docs: protected static <T extends IPAddress, S extends IPAddressSegment> Iterator<T> iterator(
  			T lower,
  			T upper,
  			AddressCreator<T, ?, ?, S> creator,
  			SegFunction<T, S> segProducer,
  			SegFunction<S, Iterator<S>> segmentIteratorProducer,
  			SegValueComparator<T> segValueComparator,
  			int networkSegmentIndex,
  			int hostSegmentIndex,
  			SegFunction<S, Iterator<S>> prefixedSegIteratorProducer) {
  		int divCount = lower.getDivisionCount();
  		
  		// at any given point in time, this list provides an iterator for the segment at each index
  		ArrayList<Supplier<Iterator<S>>> segIteratorProducerList = new ArrayList<Supplier<Iterator<S>>>(divCount);
  		
  		// at any given point in time, finalValue[i] is true if and only if we have reached the very last value for segment i - 1
  		// when that happens, the next iterator for the segment at index i will be the last
  		boolean finalValue[] = new boolean[divCount + 1];
  		
  		// here is how the segment iterators will work:
  		// the low and high values at each segment are low, high
  		// the maximum possible valoues for any segment are min, max
  		// we first find the first k >= 0 such that low != high for the segment at index k
  		
  		//	the initial set of iterators at each index are as follows:
  		//    for i < k finalValue[i] is set to true right away.
  		//		we create an iterator from seg = new Seg(low)
  		//    for i == k we create a wrapped iterator from Seg(low, high), wrapper will set finalValue[i] once we reach the final value of the iterator
  		//    for i > k we create an iterator from Seg(low, max)
  		// 
  		// after the initial iterator has been supplied, any further iterator supplied for the same segment is as follows:
  		//    for i <= k, there was only one iterator, there will be no further iterator
  		//    for i > k,
  		//	  	if i == 0 or of if flagged[i - 1] is true, we create a wrapped iterator from Seg(low, high), wrapper will set finalValue[i] once we reach the final value of the iterator
  		//      otherwise we create an iterator from Seg(min, max)
  		//
  		// By following these rules, we iterator through all possible addresses	
  		boolean notDiffering = true;
  		finalValue[0] = true;
  		S allSegShared = null;
  		for(int i = 0; i < divCount; i++) {
  			SegFunction<S, Iterator<S>> segIteratorProducer;
  			if(prefixedSegIteratorProducer != null && i >= networkSegmentIndex) {
  				segIteratorProducer = prefixedSegIteratorProducer;
  			} else {
  				segIteratorProducer = segmentIteratorProducer;
  			}
  			S lowerSeg = segProducer.apply(lower, i);
  			int indexi = i;
  			if(notDiffering) {
  				notDiffering = segValueComparator.apply(lower, upper, i);
  				if(notDiffering) {
  					// there is only one iterator and it produces only one value
  					finalValue[i + 1] = true;
  					Iterator<S> iterator = segIteratorProducer.apply(lowerSeg, i);
  					segIteratorProducerList.add(() -> iterator);
  				} else {
  					// in the first differing segment the only iterator will go from segment value of lower address to segment value of upper address
  					Iterator<S> iterator = segIteratorProducer.apply(
  							creator.createSegment(lowerSeg.getSegmentValue(), upper.getSegment(i).getSegmentValue(), null), i);
  					
  					// the wrapper iterator detects when the iterator has reached its final value
  					Iterator<S> wrappedFinalIterator = new Iterator<S>() {
  						@Override
  						public boolean hasNext() {
  							return iterator.hasNext();
  						}
  
  						@Override
  						public S next() {
  							S next = iterator.next();
  							if(!iterator.hasNext()) {
  								finalValue[indexi + 1] = true;
  							}
  							return next;
  						}
  					};
  					segIteratorProducerList.add(() -> wrappedFinalIterator);
  				}
  			} else {
  				// in the second and all following differing segments, rather than go from segment value of lower address to segment value of upper address
  				// we go from segment value of lower address to the max seg value the first time through
  				// then we go from the min value of the seg to the max seg value each time until the final time,
  				// the final time we go from the min value to the segment value of upper address
  				// we know it is the final time through when the previous iterator has reached its final value, which we track
  				
  				// the first iterator goes from the segment value of lower address to the max value of the segment
  				Iterator<S> firstIterator = segIteratorProducer.apply(creator.createSegment(lowerSeg.getSegmentValue(), lower.getMaxSegmentValue(), null), i);
  				
  				// the final iterator goes from 0 to the segment value of our upper address
  				Iterator<S> finalIterator = segIteratorProducer.apply(creator.createSegment(0, upper.getSegment(i).getSegmentValue(), null), i);
  				
  				// the wrapper iterator detects when the final iterator has reached its final value
  				Iterator<S> wrappedFinalIterator = new Iterator<S>() {
  					@Override
  					public boolean hasNext() {
  						return finalIterator.hasNext();
  					}
  
  					@Override
  					public S next() {
  						S next = finalIterator.next();
  						if(!finalIterator.hasNext()) {
  							finalValue[indexi + 1] = true;
  						}
  						return next;
  					}
  				};
  				if(allSegShared == null) {
  					allSegShared = creator.createSegment(0, lower.getMaxSegmentValue(), null);
  				}
  				// all iterators after the first iterator and before the final iterator go from 0 the max segment value,
  				// and there will be many such iterators
  				S allSeg = allSegShared;
  				Supplier<Iterator<S>> finalIteratorProducer = () -> finalValue[indexi] ?  wrappedFinalIterator : segIteratorProducer.apply(allSeg, indexi);
  				segIteratorProducerList.add(() -> {
  					//the first time through, we replace the iterator producer so the first iterator used only once
  					segIteratorProducerList.set(indexi, finalIteratorProducer);
  					return firstIterator;
  				});
  			}
  		}
  		IntFunction<Iterator<S>> iteratorProducer = iteratorIndex -> segIteratorProducerList.get(iteratorIndex).get();
  		return IPAddressSection.iterator(null, creator,
  				IPAddressSection.iterator(
  						lower.getSegmentCount(),
  						creator,
  						iteratorProducer, 
  						networkSegmentIndex,
  						hostSegmentIndex,
  						iteratorProducer)
  			);
  	}