private void partitionRange(FeatureRange range, long from, long to, int arity, boolean isNeg) {
        int fromRemainder = abs((int) (from % arity));  // from is only negative when using LLONG_MIN.
        // operate on exclusive upper bound.
        int toRemainder = ((int) ((to - arity + 1) % arity) + arity) % arity;  // avoid overflow of (to + 1)
        long fromVal = from - fromRemainder;
        long toVal = to - toRemainder;  // use inclusive upper bound here to avoid overflow problems.
        long fromValDividedByArity = abs(fromVal / arity);
        if (fromVal - 1 == toVal) {  // (toVal + 1) might cause overflow
            addEdgePartition(range, fromVal, fromRemainder, toRemainder - 1, isNeg);
            return;
        } else {
            if (fromRemainder != 0) {
                addEdgePartition(range, fromVal, fromRemainder, -1, isNeg);
                fromValDividedByArity += 1;
            }
            if (toRemainder != 0) {
                addEdgePartition(range, toVal + 1, -1, toRemainder - 1, isNeg);
            }
        }
        makePartitions(range, fromValDividedByArity, abs((toVal - (arity - 1)) / arity) + 1, arity, arity, isNeg);
    }
Partitions all the feature ranges according to the arity and bounds
set in the PredicateOptions, and updates the ranges with a set of
partitions and edge partitions.
This is required to be able to store a range in the PredicateIndex.

@author Magnar Nedland
@author bjorncs
predicate-search-core/src/main/java/com/yahoo/search/predicate/optimization/ComplexNodeTransformer.java