class IndexFn implements Iterable<Integer> {

  IndexFn section(List<Range> ranges) throws InvalidRangeException {
    Preconditions.checkArgument(ranges.size() == rank);
    for (int ii = 0; ii < rank; ii++) {
      Range r = ranges.get(ii);
      if (r == null)
        continue;
      if (r == Range.VLEN)
        continue;
      if ((r.first() < 0) || (r.first() >= shape[ii]))
        throw new InvalidRangeException("Bad range starting value at index " + ii + " == " + r.first());
      if ((r.last() < 0) || (r.last() >= shape[ii]))
        throw new InvalidRangeException("Bad range ending value at index " + ii + " == " + r.last());
    }

    // allocate
    IndexFn.Builder newindex = builder(rank);
    newindex.offset = offset;
    int[] newstride = new int[rank];

    // calc shape, size, and index transformations
    // calc strides into original (backing) store
    for (int ii = 0; ii < rank; ii++) {
      Range r = ranges.get(ii);
      if (r == null) { // null range means use the whole original dimension
        newindex.shape[ii] = shape[ii];
        newstride[ii] = stride[ii];
      } else {
        newindex.shape[ii] = r.length();
        newstride[ii] = stride[ii] * r.stride();
        newindex.offset += stride[ii] * r.first();
      }
    }
    newindex.setStride(newstride);

    // if equal, then its not a real subset, so can still use fastIterator
    newindex.canonicalOrder = canonicalOrder && (Arrays.computeSize(newindex.shape) == size);
    return newindex.build();
  }

  private  IndexFn(Builder builder);

}