private Predicates getPredicates(Step step, Traversal.Admin traversal) {
    Predicates predicates = new Predicates();
    Step<?, ?> nextStep = step.getNextStep();

    while (true) {
      if (nextStep instanceof HasContainerHolder) {
        HasContainerHolder hasContainerHolder = (HasContainerHolder) nextStep;
        boolean skip = false;
        for (HasContainer has : hasContainerHolder.getHasContainers())
          if (has.getPredicate().getTraversals().size() > 0) skip = true;

        if (!skip) {
          hasContainerHolder.getHasContainers().forEach((has) -> predicates.hasContainers.add(has));
          nextStep.getLabels().forEach(label -> predicates.labels.add(label.toString()));
          traversal.removeStep(nextStep);
        }
      } else if (nextStep instanceof RangeGlobalStep) {
        RangeGlobalStep rangeGlobalStep = (RangeGlobalStep) nextStep;
        predicates.limitLow = rangeGlobalStep.getLowRange();
        predicates.limitHigh = rangeGlobalStep.getHighRange();
        traversal.removeStep(nextStep);
      } else return predicates;

      nextStep = nextStep.getNextStep();
    }
  }
 private static Set<String> getLabels(
     final Set<String> labels, final Traversal.Admin<?, ?> traversal) {
   for (final Step<?, ?> step : traversal.getSteps()) {
     labels.addAll(step.getLabels());
     if (step instanceof TraversalParent) {
       ((TraversalParent) step).getLocalChildren().forEach(child -> getLabels(labels, child));
       ((TraversalParent) step).getGlobalChildren().forEach(child -> getLabels(labels, child));
     }
   }
   return labels;
 }
 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;
 }
 @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;
   }
 }