@Test
 public void shouldCalculateStartLabelCorrectly() {
   Traversal.Admin<?, ?> traversal =
       match(
               where(and(as("a").out("created").as("b"), as("b").in("created").count().is(eq(3)))),
               as("a").both().as("b"),
               where(as("b").in()))
           .asAdmin();
   assertEquals(
       "a",
       MatchStep.Helper.computeStartLabel(
           ((MatchStep<?, ?>) traversal.getStartStep()).getGlobalChildren()));
   /////
   traversal =
       match(
               where("a", P.neq("c")),
               as("a").out("created").as("b"),
               or(
                   as("a").out("knows").has("name", "vadas"),
                   as("a").in("knows").and().as("a").has(T.label, "person")),
               as("b").in("created").as("c"),
               as("b").in("created").count().is(P.gt(1)))
           .asAdmin();
   assertEquals(
       "a",
       MatchStep.Helper.computeStartLabel(
           ((MatchStep<?, ?>) traversal.getStartStep()).getGlobalChildren()));
 }
 @Test
 public void testPreCompilationOfWherePredicate() {
   final List<Traversal.Admin<?, ?>> traversals =
       Arrays.asList(
           __.match(as("a").out().as("b"), as("c").where(P.neq("d"))).asAdmin(),
           __.match(as("a").out().as("b"), where("c", P.neq("d"))).asAdmin());
   assertEquals(
       1,
       new HashSet<>(traversals)
           .size()); // the two patterns should pre-compile to the same traversal
   traversals.forEach(
       traversal -> {
         MatchStep<?, ?> matchStep = (MatchStep<?, ?>) traversal.getStartStep();
         // assertFalse(matchStep.getStartLabel().isPresent());
         assertEquals(2, matchStep.getGlobalChildren().size());
         Traversal.Admin<Object, Object> pattern = matchStep.getGlobalChildren().get(0);
         assertEquals(
             "a", ((MatchStep.MatchStartStep) pattern.getStartStep()).getSelectKey().get());
         assertEquals(VertexStep.class, pattern.getStartStep().getNextStep().getClass());
         assertEquals("b", ((MatchStep.MatchEndStep) pattern.getEndStep()).getMatchKey().get());
         //
         pattern = matchStep.getGlobalChildren().get(1);
         assertEquals(MatchStep.MatchStartStep.class, pattern.getStartStep().getClass());
         assertEquals(
             "c", ((MatchStep.MatchStartStep) pattern.getStartStep()).getSelectKey().get());
         assertEquals(WherePredicateStep.class, pattern.getStartStep().getNextStep().getClass());
         assertEquals(
             MatchStep.MatchEndStep.class,
             pattern.getStartStep().getNextStep().getNextStep().getClass());
         assertFalse(
             ((WherePredicateStep<?>) pattern.getStartStep().getNextStep())
                 .getStartKey()
                 .isPresent());
         assertEquals(
             "d",
             ((WherePredicateStep<?>) pattern.getStartStep().getNextStep())
                 .getPredicate()
                 .get()
                 .getOriginalValue());
       });
 }
 private static Set<Scoping.Variable> getVariableLocations(
     final Set<Scoping.Variable> variables, final Traversal.Admin<?, ?> traversal) {
   if (variables.size() == 2)
     return variables; // has both START and END so no need to compute further
   final Step<?, ?> startStep = traversal.getStartStep();
   if (StartStep.isVariableStartStep(startStep)) variables.add(Scoping.Variable.START);
   else if (startStep instanceof WherePredicateStep) {
     if (((WherePredicateStep) startStep).getStartKey().isPresent())
       variables.add(Scoping.Variable.START);
   } else if (startStep instanceof WhereTraversalStep.WhereStartStep) {
     if (!((WhereTraversalStep.WhereStartStep) startStep).getScopeKeys().isEmpty())
       variables.add(Scoping.Variable.START);
   } else if (startStep instanceof MatchStep.MatchStartStep) {
     if (((MatchStep.MatchStartStep) startStep).getSelectKey().isPresent())
       variables.add(Scoping.Variable.START);
   } else if (startStep instanceof MatchStep) {
     ((MatchStep<?, ?>) startStep)
         .getGlobalChildren()
         .forEach(child -> TraversalHelper.getVariableLocations(variables, child));
   } else if (startStep instanceof ConnectiveStep
       || startStep instanceof NotStep
       || startStep instanceof WhereTraversalStep)
     ((TraversalParent) startStep)
         .getLocalChildren()
         .forEach(child -> TraversalHelper.getVariableLocations(variables, child));
   ///
   final Step<?, ?> endStep = traversal.getEndStep();
   if (endStep instanceof WherePredicateStep) {
     if (((WherePredicateStep) endStep).getStartKey().isPresent())
       variables.add(Scoping.Variable.END);
   } else if (endStep instanceof WhereTraversalStep.WhereEndStep) {
     if (!((WhereTraversalStep.WhereEndStep) endStep).getScopeKeys().isEmpty())
       variables.add(Scoping.Variable.END);
   } else if (endStep instanceof MatchStep.MatchEndStep) {
     if (((MatchStep.MatchEndStep) endStep).getMatchKey().isPresent())
       variables.add(Scoping.Variable.END);
   } else if (!endStep.getLabels().isEmpty()) variables.add(Scoping.Variable.END);
   ///
   return variables;
 }
 @Test
 public void testPreCompilationOfStartAndEnds() {
   final Traversal.Admin<?, ?> traversal =
       __.match(as("a").out().as("b"), as("c").path().as("d")).asAdmin();
   final MatchStep<?, ?> matchStep = (MatchStep<?, ?>) traversal.getStartStep();
   assertEquals(MatchStep.class, traversal.getStartStep().getClass());
   assertEquals(2, matchStep.getGlobalChildren().size());
   Traversal.Admin<Object, Object> pattern = matchStep.getGlobalChildren().get(0);
   assertEquals("a", ((MatchStep.MatchStartStep) pattern.getStartStep()).getSelectKey().get());
   assertEquals(VertexStep.class, pattern.getStartStep().getNextStep().getClass());
   assertEquals("b", ((MatchStep.MatchEndStep) pattern.getEndStep()).getMatchKey().get());
   //
   pattern = matchStep.getGlobalChildren().get(1);
   assertEquals("c", ((MatchStep.MatchStartStep) pattern.getStartStep()).getSelectKey().get());
   assertEquals(PathStep.class, pattern.getStartStep().getNextStep().getClass());
   assertEquals("d", ((MatchStep.MatchEndStep) pattern.getEndStep()).getMatchKey().get());
 }
 @Override
 public void apply(final Traversal.Admin<?, ?> traversal) {
   final TraversalParent parent = traversal.getParent();
   int size = traversal.getSteps().size();
   Step prev = null;
   for (int i = 0; i < size; i++) {
     final Step curr = traversal.getSteps().get(i);
     if (curr instanceof CountGlobalStep && i < size - 1) {
       final Step next = traversal.getSteps().get(i + 1);
       if (next instanceof IsStep
           && !(prev
               instanceof
               RangeGlobalStep)) { // if a RangeStep was provided, assume that the user knows what
                                   // he's doing
         final IsStep isStep = (IsStep) next;
         final P isStepPredicate = isStep.getPredicate();
         Long highRange = null;
         boolean useNotStep = false;
         for (P p :
             isStepPredicate instanceof ConnectiveP
                 ? ((ConnectiveP<?>) isStepPredicate).getPredicates()
                 : Collections.singletonList(isStepPredicate)) {
           final Object value = p.getValue();
           final BiPredicate predicate = p.getBiPredicate();
           if (value instanceof Number) {
             final long highRangeOffset =
                 INCREASED_OFFSET_SCALAR_PREDICATES.contains(predicate) ? 1L : 0L;
             final Long highRangeCandidate = ((Number) value).longValue() + highRangeOffset;
             final boolean update = highRange == null || highRangeCandidate > highRange;
             if (update) {
               if (parent instanceof EmptyStep) {
                 useNotStep = true;
               } else {
                 if (parent instanceof RepeatStep) {
                   final RepeatStep repeatStep = (RepeatStep) parent;
                   useNotStep =
                       Objects.equals(traversal, repeatStep.getUntilTraversal())
                           || Objects.equals(traversal, repeatStep.getEmitTraversal());
                 } else {
                   useNotStep = parent instanceof FilterStep || parent instanceof SideEffectStep;
                 }
               }
               highRange = highRangeCandidate;
               useNotStep &=
                   curr.getLabels().isEmpty()
                       && next.getLabels().isEmpty()
                       && next.getNextStep() instanceof EmptyStep
                       && ((highRange <= 1L && predicate.equals(Compare.lt))
                           || (highRange == 1L
                               && (predicate.equals(Compare.eq)
                                   || predicate.equals(Compare.lte))));
             }
           } else {
             final Long highRangeOffset = RANGE_PREDICATES.get(predicate);
             if (value instanceof Collection && highRangeOffset != null) {
               final Object high = Collections.max((Collection) value);
               if (high instanceof Number) {
                 final Long highRangeCandidate = ((Number) high).longValue() + highRangeOffset;
                 final boolean update = highRange == null || highRangeCandidate > highRange;
                 if (update) highRange = highRangeCandidate;
               }
             }
           }
         }
         if (highRange != null) {
           if (useNotStep) {
             traversal.asAdmin().removeStep(next); // IsStep
             traversal.asAdmin().removeStep(curr); // CountStep
             size -= 2;
             if (prev != null) {
               final Traversal.Admin inner = __.start().asAdmin();
               TraversalHelper.insertAfterStep(prev, inner.getStartStep(), inner);
               TraversalHelper.replaceStep(prev, new NotStep<>(traversal, inner), traversal);
             } else {
               traversal.asAdmin().addStep(new NotStep<>(traversal, __.identity()));
             }
           } else {
             TraversalHelper.insertBeforeStep(
                 new RangeGlobalStep<>(traversal, 0L, highRange), curr, traversal);
           }
           i++;
         }
       }
     }
     prev = curr;
   }
 }
 @Test
 public void testPreCompilationOfOr() {
   final List<Traversal.Admin<?, ?>> traversals =
       Arrays.asList(
           __.match(as("a").out().as("b"), or(as("c").path().as("d"), as("e").coin(0.5).as("f")))
               .asAdmin(),
           __.match(as("a").out().as("b"), as("c").path().as("d").or().as("e").coin(0.5).as("f"))
               .asAdmin());
   assertEquals(
       1,
       new HashSet<>(traversals)
           .size()); // the two patterns should pre-compile to the same traversal
   traversals.forEach(
       traversal -> {
         final MatchStep<?, ?> matchStep = (MatchStep<?, ?>) traversal.getStartStep();
         assertEquals(2, matchStep.getGlobalChildren().size());
         Traversal.Admin<Object, Object> pattern = matchStep.getGlobalChildren().get(0);
         assertEquals(
             "a", ((MatchStep.MatchStartStep) pattern.getStartStep()).getSelectKey().get());
         assertEquals(VertexStep.class, pattern.getStartStep().getNextStep().getClass());
         assertEquals("b", ((MatchStep.MatchEndStep) pattern.getEndStep()).getMatchKey().get());
         //
         pattern = matchStep.getGlobalChildren().get(1);
         assertEquals(MatchStep.class, pattern.getStartStep().getClass());
         assertEquals(
             ConnectiveStep.Connective.OR,
             ((MatchStep<?, ?>) pattern.getStartStep()).getConnective());
         assertEquals(
             "c",
             ((MatchStep.MatchStartStep)
                     ((MatchStep<?, ?>) pattern.getStartStep())
                         .getGlobalChildren()
                         .get(0)
                         .getStartStep())
                 .getSelectKey()
                 .get());
         assertEquals(
             PathStep.class,
             ((MatchStep<?, ?>) pattern.getStartStep())
                 .getGlobalChildren()
                 .get(0)
                 .getStartStep()
                 .getNextStep()
                 .getClass());
         assertEquals(
             "d",
             ((MatchStep.MatchEndStep)
                     ((MatchStep<?, ?>) pattern.getStartStep())
                         .getGlobalChildren()
                         .get(0)
                         .getEndStep())
                 .getMatchKey()
                 .get());
         assertEquals(
             "e",
             ((MatchStep.MatchStartStep)
                     ((MatchStep<?, ?>) pattern.getStartStep())
                         .getGlobalChildren()
                         .get(1)
                         .getStartStep())
                 .getSelectKey()
                 .get());
         assertEquals(
             CoinStep.class,
             ((MatchStep<?, ?>) pattern.getStartStep())
                 .getGlobalChildren()
                 .get(1)
                 .getStartStep()
                 .getNextStep()
                 .getClass());
         assertEquals(
             "f",
             ((MatchStep.MatchEndStep)
                     ((MatchStep<?, ?>) pattern.getStartStep())
                         .getGlobalChildren()
                         .get(1)
                         .getEndStep())
                 .getMatchKey()
                 .get());
       });
 }
 @Test
 public void testComputerAwareCountMatchAlgorithm() {
   // MAKE SURE OLAP JOBS ARE BIASED TOWARDS STAR GRAPH DATA
   final Consumer doNothing = s -> {};
   Traversal.Admin<?, ?> traversal =
       __.match(
               as("a").sideEffect(doNothing).as("b"), // 1
               as("b").sideEffect(doNothing).as("c"), // 2
               as("a").sideEffect(doNothing).as("d"), // 5
               as("c").sideEffect(doNothing).as("e"), // 4
               as("c").sideEffect(doNothing).as("f")) // 3
           .asAdmin();
   traversal.applyStrategies(); // necessary to enure step ids are unique
   MatchStep.CountMatchAlgorithm countMatchAlgorithm = new MatchStep.CountMatchAlgorithm();
   countMatchAlgorithm.initialize(
       TraversalEngine.Type.COMPUTER,
       ((MatchStep<?, ?>) traversal.getStartStep()).getGlobalChildren());
   Traversal.Admin<Object, Object> firstPattern =
       ((MatchStep<?, ?>) traversal.getStartStep()).getGlobalChildren().get(0);
   Traversal.Admin<Object, Object> secondPattern =
       ((MatchStep<?, ?>) traversal.getStartStep()).getGlobalChildren().get(1);
   Traversal.Admin<Object, Object> thirdPattern =
       ((MatchStep<?, ?>) traversal.getStartStep()).getGlobalChildren().get(2);
   Traversal.Admin<Object, Object> forthPattern =
       ((MatchStep<?, ?>) traversal.getStartStep()).getGlobalChildren().get(3);
   Traversal.Admin<Object, Object> fifthPattern =
       ((MatchStep<?, ?>) traversal.getStartStep()).getGlobalChildren().get(4);
   countMatchAlgorithm
       .bundles
       .stream()
       .forEach(bundle -> assertEquals(0.0d, bundle.multiplicity, 0.0d));
   assertEquals(
       MatchStep.TraversalType.MATCH_TRAVERSAL,
       countMatchAlgorithm.getBundle(firstPattern).traversalType);
   assertEquals(
       MatchStep.TraversalType.MATCH_TRAVERSAL,
       countMatchAlgorithm.getBundle(secondPattern).traversalType);
   assertEquals(
       MatchStep.TraversalType.MATCH_TRAVERSAL,
       countMatchAlgorithm.getBundle(thirdPattern).traversalType);
   assertEquals(
       MatchStep.TraversalType.MATCH_TRAVERSAL,
       countMatchAlgorithm.getBundle(forthPattern).traversalType);
   assertEquals(
       MatchStep.TraversalType.MATCH_TRAVERSAL,
       countMatchAlgorithm.getBundle(fifthPattern).traversalType);
   assertEquals(firstPattern, countMatchAlgorithm.bundles.get(0).traversal);
   assertEquals(secondPattern, countMatchAlgorithm.bundles.get(1).traversal);
   assertEquals(thirdPattern, countMatchAlgorithm.bundles.get(2).traversal);
   assertEquals(forthPattern, countMatchAlgorithm.bundles.get(3).traversal);
   assertEquals(fifthPattern, countMatchAlgorithm.bundles.get(4).traversal);
   // MAKE THE SECOND PATTERN EXPENSIVE
   countMatchAlgorithm.recordStart(EmptyTraverser.instance(), secondPattern);
   countMatchAlgorithm.recordEnd(EmptyTraverser.instance(), secondPattern);
   countMatchAlgorithm.recordEnd(EmptyTraverser.instance(), secondPattern);
   countMatchAlgorithm.recordEnd(EmptyTraverser.instance(), secondPattern);
   countMatchAlgorithm.recordEnd(EmptyTraverser.instance(), secondPattern);
   countMatchAlgorithm.recordEnd(EmptyTraverser.instance(), secondPattern);
   countMatchAlgorithm.recordEnd(EmptyTraverser.instance(), secondPattern);
   // MAKE THE THIRD PATTERN MORE EXPENSIVE THAN FORTH
   countMatchAlgorithm.recordStart(EmptyTraverser.instance(), thirdPattern);
   countMatchAlgorithm.recordEnd(EmptyTraverser.instance(), thirdPattern);
   countMatchAlgorithm.recordEnd(EmptyTraverser.instance(), thirdPattern);
   countMatchAlgorithm.recordEnd(EmptyTraverser.instance(), thirdPattern);
   // MAKE THE FORTH PATTERN EXPENSIVE
   countMatchAlgorithm.recordStart(EmptyTraverser.instance(), forthPattern);
   countMatchAlgorithm.recordEnd(EmptyTraverser.instance(), forthPattern);
   countMatchAlgorithm.recordEnd(EmptyTraverser.instance(), forthPattern);
   //
   Traverser.Admin traverser =
       B_LP_O_P_S_SE_SL_TraverserGenerator.instance().generate(1, EmptyStep.instance(), 1l);
   traverser.addLabels(Collections.singleton("a"));
   assertEquals(firstPattern, countMatchAlgorithm.apply(traverser));
   traverser = traverser.split(1, EmptyStep.instance());
   traverser.getTags().add(firstPattern.getStartStep().getId());
   traverser.addLabels(Collections.singleton("b"));
   //
   assertEquals(secondPattern, countMatchAlgorithm.apply(traverser));
   traverser = traverser.split(1, EmptyStep.instance());
   traverser.getTags().add(secondPattern.getStartStep().getId());
   traverser.addLabels(Collections.singleton("c"));
   //
   assertEquals(fifthPattern, countMatchAlgorithm.apply(traverser));
   traverser = traverser.split(1, EmptyStep.instance());
   traverser.getTags().add(fifthPattern.getStartStep().getId());
   traverser.addLabels(Collections.singleton("f"));
   //
   assertEquals(forthPattern, countMatchAlgorithm.apply(traverser));
   traverser = traverser.split(1, EmptyStep.instance());
   traverser.getTags().add(forthPattern.getStartStep().getId());
   traverser.addLabels(Collections.singleton("e"));
   //
   assertEquals(thirdPattern, countMatchAlgorithm.apply(traverser));
   traverser = traverser.split(1, EmptyStep.instance());
   traverser.getTags().add(thirdPattern.getStartStep().getId());
   traverser.addLabels(Collections.singleton("d"));
 }
  @Test
  public void testCountMatchAlgorithm() {
    // MAKE SURE THE SORT ORDER CHANGES AS MORE RESULTS ARE RETURNED BY ONE OR THE OTHER TRAVERSAL
    Traversal.Admin<?, ?> traversal =
        __.match(as("a").out().as("b"), as("c").in().as("d")).asAdmin();
    MatchStep.CountMatchAlgorithm countMatchAlgorithm = new MatchStep.CountMatchAlgorithm();
    countMatchAlgorithm.initialize(
        TraversalEngine.Type.STANDARD,
        ((MatchStep<?, ?>) traversal.getStartStep()).getGlobalChildren());
    Traversal.Admin<Object, Object> firstPattern =
        ((MatchStep<?, ?>) traversal.getStartStep()).getGlobalChildren().get(0);
    Traversal.Admin<Object, Object> secondPattern =
        ((MatchStep<?, ?>) traversal.getStartStep()).getGlobalChildren().get(1);
    //
    assertEquals(2, countMatchAlgorithm.bundles.size());
    countMatchAlgorithm
        .bundles
        .stream()
        .forEach(bundle -> assertEquals(0.0d, bundle.multiplicity, 0.0d));
    assertEquals(firstPattern, countMatchAlgorithm.bundles.get(0).traversal);
    assertEquals(secondPattern, countMatchAlgorithm.bundles.get(1).traversal);
    countMatchAlgorithm.recordStart(EmptyTraverser.instance(), firstPattern);
    countMatchAlgorithm.recordStart(EmptyTraverser.instance(), firstPattern);
    countMatchAlgorithm.recordStart(EmptyTraverser.instance(), secondPattern);
    countMatchAlgorithm.recordStart(EmptyTraverser.instance(), secondPattern);
    countMatchAlgorithm.recordStart(EmptyTraverser.instance(), secondPattern);
    assertEquals(firstPattern, countMatchAlgorithm.bundles.get(0).traversal);
    assertEquals(secondPattern, countMatchAlgorithm.bundles.get(1).traversal);
    assertEquals(
        MatchStep.TraversalType.MATCH_TRAVERSAL,
        countMatchAlgorithm.getBundle(firstPattern).traversalType);
    assertEquals(
        MatchStep.TraversalType.MATCH_TRAVERSAL,
        countMatchAlgorithm.getBundle(secondPattern).traversalType);
    assertEquals(2l, countMatchAlgorithm.getBundle(firstPattern).startsCount);
    assertEquals(3l, countMatchAlgorithm.getBundle(secondPattern).startsCount);
    assertEquals(0l, countMatchAlgorithm.getBundle(firstPattern).endsCount);
    assertEquals(0l, countMatchAlgorithm.getBundle(secondPattern).endsCount);
    ////
    countMatchAlgorithm.recordEnd(EmptyTraverser.instance(), firstPattern);
    countMatchAlgorithm.recordEnd(EmptyTraverser.instance(), secondPattern);
    countMatchAlgorithm.recordEnd(EmptyTraverser.instance(), secondPattern);
    countMatchAlgorithm.recordEnd(EmptyTraverser.instance(), secondPattern);
    assertEquals(2l, countMatchAlgorithm.getBundle(firstPattern).startsCount);
    assertEquals(3l, countMatchAlgorithm.getBundle(secondPattern).startsCount);
    assertEquals(1l, countMatchAlgorithm.getBundle(firstPattern).endsCount);
    assertEquals(3l, countMatchAlgorithm.getBundle(secondPattern).endsCount);
    assertEquals(0.5d, countMatchAlgorithm.getBundle(firstPattern).multiplicity, 0.01d);
    assertEquals(1.0d, countMatchAlgorithm.getBundle(secondPattern).multiplicity, 0.01d);
    assertEquals(firstPattern, countMatchAlgorithm.bundles.get(0).traversal);
    assertEquals(secondPattern, countMatchAlgorithm.bundles.get(1).traversal);
    // CHECK RE-SORTING AS MORE DATA COMES IN
    countMatchAlgorithm.recordEnd(EmptyTraverser.instance(), firstPattern);
    countMatchAlgorithm.recordEnd(EmptyTraverser.instance(), firstPattern);
    assertEquals(1.5d, countMatchAlgorithm.getBundle(firstPattern).multiplicity, 0.01d);
    assertEquals(1.0d, countMatchAlgorithm.getBundle(secondPattern).multiplicity, 0.01d);
    assertEquals(secondPattern, countMatchAlgorithm.bundles.get(0).traversal);
    assertEquals(firstPattern, countMatchAlgorithm.bundles.get(1).traversal);

    ///////  MAKE SURE WHERE PREDICATE TRAVERSALS ARE ALWAYS FIRST AS THEY ARE SIMPLY .hasNext()
    // CHECKS
    traversal =
        __.match(as("a").out().as("b"), as("c").in().as("d"), where("a", P.eq("b"))).asAdmin();
    countMatchAlgorithm = new MatchStep.CountMatchAlgorithm();
    countMatchAlgorithm.initialize(
        TraversalEngine.Type.STANDARD,
        ((MatchStep<?, ?>) traversal.getStartStep()).getGlobalChildren());
    assertEquals(3, countMatchAlgorithm.bundles.size());
    firstPattern = ((MatchStep<?, ?>) traversal.getStartStep()).getGlobalChildren().get(0);
    secondPattern = ((MatchStep<?, ?>) traversal.getStartStep()).getGlobalChildren().get(1);
    Traversal.Admin<Object, Object> thirdPattern =
        ((MatchStep<?, ?>) traversal.getStartStep()).getGlobalChildren().get(2);
    ///
    countMatchAlgorithm
        .bundles
        .stream()
        .forEach(bundle -> assertEquals(0.0d, bundle.multiplicity, 0.0d));
    assertEquals(
        MatchStep.TraversalType.MATCH_TRAVERSAL,
        countMatchAlgorithm.getBundle(firstPattern).traversalType);
    assertEquals(
        MatchStep.TraversalType.MATCH_TRAVERSAL,
        countMatchAlgorithm.getBundle(secondPattern).traversalType);
    assertEquals(
        MatchStep.TraversalType.WHERE_PREDICATE,
        countMatchAlgorithm.getBundle(thirdPattern).traversalType);
    assertEquals(firstPattern, countMatchAlgorithm.bundles.get(0).traversal);
    assertEquals(secondPattern, countMatchAlgorithm.bundles.get(1).traversal);
    assertEquals(thirdPattern, countMatchAlgorithm.bundles.get(2).traversal);
    countMatchAlgorithm.recordStart(EmptyTraverser.instance(), firstPattern);
    countMatchAlgorithm.recordEnd(EmptyTraverser.instance(), firstPattern);
    assertEquals(1l, countMatchAlgorithm.getBundle(firstPattern).startsCount);
    assertEquals(0l, countMatchAlgorithm.getBundle(secondPattern).startsCount);
    assertEquals(0l, countMatchAlgorithm.getBundle(thirdPattern).startsCount);
    assertEquals(1l, countMatchAlgorithm.getBundle(firstPattern).endsCount);
    assertEquals(0l, countMatchAlgorithm.getBundle(secondPattern).endsCount);
    assertEquals(0l, countMatchAlgorithm.getBundle(thirdPattern).endsCount);
    assertEquals(1.0d, countMatchAlgorithm.getBundle(firstPattern).multiplicity, 0.01d);
    assertEquals(0.0d, countMatchAlgorithm.getBundle(secondPattern).multiplicity, 0.01d);
    assertEquals(0.0d, countMatchAlgorithm.getBundle(thirdPattern).multiplicity, 0.01d);
    assertEquals(thirdPattern, countMatchAlgorithm.bundles.get(0).traversal);
    assertEquals(secondPattern, countMatchAlgorithm.bundles.get(1).traversal);
    assertEquals(firstPattern, countMatchAlgorithm.bundles.get(2).traversal);
    //
    countMatchAlgorithm.recordStart(EmptyTraverser.instance(), thirdPattern);
    countMatchAlgorithm.recordStart(EmptyTraverser.instance(), thirdPattern);
    countMatchAlgorithm.recordStart(EmptyTraverser.instance(), thirdPattern);
    countMatchAlgorithm.recordEnd(EmptyTraverser.instance(), thirdPattern);
    countMatchAlgorithm.recordEnd(EmptyTraverser.instance(), thirdPattern);
    countMatchAlgorithm.recordEnd(EmptyTraverser.instance(), thirdPattern);
    countMatchAlgorithm.recordEnd(EmptyTraverser.instance(), thirdPattern);
    countMatchAlgorithm.recordEnd(EmptyTraverser.instance(), thirdPattern);
    countMatchAlgorithm.recordEnd(EmptyTraverser.instance(), thirdPattern);
    assertEquals(1l, countMatchAlgorithm.getBundle(firstPattern).startsCount);
    assertEquals(0l, countMatchAlgorithm.getBundle(secondPattern).startsCount);
    assertEquals(3l, countMatchAlgorithm.getBundle(thirdPattern).startsCount);
    assertEquals(1l, countMatchAlgorithm.getBundle(firstPattern).endsCount);
    assertEquals(0l, countMatchAlgorithm.getBundle(secondPattern).endsCount);
    assertEquals(6l, countMatchAlgorithm.getBundle(thirdPattern).endsCount);
    assertEquals(1.0d, countMatchAlgorithm.getBundle(firstPattern).multiplicity, 0.01d);
    assertEquals(0.0d, countMatchAlgorithm.getBundle(secondPattern).multiplicity, 0.01d);
    assertEquals(2.0d, countMatchAlgorithm.getBundle(thirdPattern).multiplicity, 0.01d);
    assertEquals(thirdPattern, countMatchAlgorithm.bundles.get(0).traversal);
    assertEquals(secondPattern, countMatchAlgorithm.bundles.get(1).traversal);
    assertEquals(firstPattern, countMatchAlgorithm.bundles.get(2).traversal);
    //
    countMatchAlgorithm.recordStart(EmptyTraverser.instance(), secondPattern);
    countMatchAlgorithm.recordStart(EmptyTraverser.instance(), secondPattern);
    countMatchAlgorithm.recordStart(EmptyTraverser.instance(), secondPattern);
    countMatchAlgorithm.recordStart(EmptyTraverser.instance(), secondPattern);
    countMatchAlgorithm.recordEnd(EmptyTraverser.instance(), secondPattern);
    countMatchAlgorithm.recordEnd(EmptyTraverser.instance(), secondPattern);
    countMatchAlgorithm.recordEnd(EmptyTraverser.instance(), secondPattern);
    countMatchAlgorithm.recordEnd(EmptyTraverser.instance(), secondPattern);
    countMatchAlgorithm.recordEnd(EmptyTraverser.instance(), secondPattern);
    countMatchAlgorithm.recordEnd(EmptyTraverser.instance(), secondPattern);
    assertEquals(1l, countMatchAlgorithm.getBundle(firstPattern).startsCount);
    assertEquals(4l, countMatchAlgorithm.getBundle(secondPattern).startsCount);
    assertEquals(3l, countMatchAlgorithm.getBundle(thirdPattern).startsCount);
    assertEquals(1l, countMatchAlgorithm.getBundle(firstPattern).endsCount);
    assertEquals(6l, countMatchAlgorithm.getBundle(secondPattern).endsCount);
    assertEquals(6l, countMatchAlgorithm.getBundle(thirdPattern).endsCount);
    assertEquals(1.0d, countMatchAlgorithm.getBundle(firstPattern).multiplicity, 0.01d);
    assertEquals(1.5d, countMatchAlgorithm.getBundle(secondPattern).multiplicity, 0.01d);
    assertEquals(2.0d, countMatchAlgorithm.getBundle(thirdPattern).multiplicity, 0.01d);
    assertEquals(thirdPattern, countMatchAlgorithm.bundles.get(0).traversal);
    assertEquals(firstPattern, countMatchAlgorithm.bundles.get(1).traversal);
    assertEquals(secondPattern, countMatchAlgorithm.bundles.get(2).traversal);
  }