package kr.debop4j.core.collection; import com.google.common.base.Objects; import com.google.common.collect.Lists; import lombok.Getter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Iterator; import java.util.List; /** * 특정 범위에 해당하는 숫자들을 열거하도록 합니다. * * @author 배성혁 ( sunghyouk.bae@gmail.com ) * @since 13. 1. 11 */ public abstract class NumberRange<T extends Number> implements Iterable<T> { private static final Logger log = LoggerFactory.getLogger(NumberRange.class); private static final boolean isTraceEnabled = log.isTraceEnabled(); private static final boolean isDebugEnabled = log.isDebugEnabled(); private NumberRange() { } /** * get int range. * * @param fromInclude the from include * @param toExclude the to exclude * @param step the step * @return the int range */ public static IntRange range(int fromInclude, int toExclude, int step) { return new IntRange(fromInclude, toExclude, step); } /** * get int range. * * @param fromInclude the from include * @param toExclude the to exclude * @return the int range */ public static IntRange range(int fromInclude, int toExclude) { int step = fromInclude <= toExclude ? 1 : -1; return range(fromInclude, toExclude, step); } /** * get int range. * * @param count the count * @return the int range */ public static IntRange range(int count) { return range(0, count); } /** * Range long range. * * @param fromInclude the from include * @param toExclude the to exclude * @param step the step * @return the long range */ public static LongRange range(long fromInclude, long toExclude, long step) { return new LongRange(fromInclude, toExclude, step); } /** * get long range. * * @param fromInclude the from include * @param toExclude the to exclude * @return the long range */ public static LongRange range(long fromInclude, long toExclude) { long step = fromInclude <= toExclude ? 1L : -1L; return range(fromInclude, toExclude, step); } /** * get long range. * * @param count the count * @return the long range */ public static LongRange range(long count) { return range(0L, count); } /** * Partition list. * * @param range the range * @param partitionCount the partition count * @return the list */ public static List<IntRange> partition(IntRange range, int partitionCount) { if (isTraceEnabled) log.trace("partition... range=[{}], partitionCount=[{}]", range, partitionCount); int rangeSize = range.size(); int stepSign = range.getStep(); int step = range.getStep(); int partitionSize = rangeSize / partitionCount; int remainder = rangeSize % partitionCount; List<IntRange> partitions = Lists.newLinkedList(); int fromInclude = range.fromInclude; for (int i = 0; i < partitionCount; i++) { int toExclude = fromInclude + (partitionSize + ((remainder > 0) ? 1 : 0)) * stepSign; if (remainder > 0) remainder--; toExclude = (step > 0) ? Math.min(toExclude, range.getToExclude()) : Math.max(toExclude, range.getToExclude()); IntRange partition = range(fromInclude, toExclude, step); partitions.add(partition); fromInclude = toExclude; if (isTraceEnabled) log.trace("Partition 추가 = [{}]", partition); } return partitions; } /** * Partition list. * * @param fromInclude the from include * @param toExclude the to exclude * @param step the step * @param partitionCount the partition count * @return the list */ public static List<IntRange> partition(int fromInclude, int toExclude, int step, int partitionCount) { return partition(range(fromInclude, toExclude, step), partitionCount); } /** * Partition list. * * @param fromInclude the from include * @param toExclude the to exclude * @param partitionCount the partition count * @return the list */ public static List<IntRange> partition(int fromInclude, int toExclude, int partitionCount) { return partition(range(fromInclude, toExclude), partitionCount); } /** * Partition list. * * @param count the count * @param partitionCount the partition count * @return the list */ public static List<IntRange> partition(int count, int partitionCount) { return partition(range(count), partitionCount); } /** * Partition list. * * @param range the range * @param partitionCount the partition count * @return the list */ public static List<LongRange> partition(LongRange range, int partitionCount) { long rangeSize = range.size(); long stepSign = range.getStep(); long step = range.getStep(); long partitionSize = rangeSize / partitionCount; long remainder = rangeSize % partitionCount; List<LongRange> partitions = Lists.newLinkedList(); long fromInclude = range.fromInclude; for (int i = 0; i < partitionCount; i++) { long toExclude = fromInclude + (partitionSize + ((remainder > 0) ? 1 : 0)) * stepSign; if (remainder > 0) remainder--; toExclude = (step > 0) ? Math.min(toExclude, range.getToExclude()) : Math.max(toExclude, range.getToExclude()); LongRange partition = range(fromInclude, toExclude, step); partitions.add(partition); fromInclude = toExclude; if (isTraceEnabled) log.trace("Partition 추가 = [{}]", partition); } return partitions; } /** * Partition list. * * @param fromInclude the from include * @param toExclude the to exclude * @param step the step * @param partitionCount the partition count * @return the list */ public static List<LongRange> partition(long fromInclude, long toExclude, int step, int partitionCount) { return partition(range(fromInclude, toExclude, step), partitionCount); } /** * Partition list. * * @param fromInclude the from include * @param toExclude the to exclude * @param partitionCount the partition count * @return the list */ public static List<LongRange> partition(long fromInclude, long toExclude, int partitionCount) { return partition(range(fromInclude, toExclude), partitionCount); } /** * Partition list. * * @param count the count * @param partitionCount the partition count * @return the list */ public static List<LongRange> partition(long count, int partitionCount) { return partition(range(count), partitionCount); } /** The type Int range. */ public static class IntRange extends NumberRange<Integer> implements Iterator<Integer> { @Getter int fromInclude, toExclude, step; @Getter int current; /** * Instantiates a new Int range. * * @param fromInclude the from include * @param toExclude the to exclude * @param step the step */ public IntRange(int fromInclude, int toExclude, int step) { this.fromInclude = fromInclude; this.toExclude = toExclude; this.step = step; this.current = this.fromInclude; if (isTraceEnabled) log.trace("create IntRange=[{}]", this); } /** * Size int. * * @return the int */ public int size() { return (toExclude - fromInclude) / step; } /** * Gets step sign. * * @return the step sign */ public int getStepSign() { return (step > 0) ? 1 : -1; } @Override public boolean hasNext() { return step > 0 ? current < toExclude : current > toExclude; } public Integer next() { int result = this.current; this.current += this.step; return result; } /** Reset void. */ public void reset() { this.current = this.fromInclude; } @Override public void remove() { throw new UnsupportedOperationException(); } @Override public Iterator<Integer> iterator() { return this; } @Override public String toString() { return Objects.toStringHelper(this) .add("fromInclude", fromInclude) .add("toExclude", toExclude) .add("step", step) .add("size", size()) .add("current", current) .toString(); } } /** The type Long range. */ public static class LongRange extends NumberRange<Long> implements Iterator<Long> { @Getter long fromInclude, toExclude, step; @Getter long current; /** * Instantiates a new Long range. * * @param fromInclude the from include * @param toExclude the to exclude * @param step the step */ public LongRange(long fromInclude, long toExclude, long step) { this.fromInclude = fromInclude; this.toExclude = toExclude; this.step = step; this.current = this.fromInclude; if (isTraceEnabled) log.trace("create IntRange=[{}]", this); } /** * Size long. * * @return the long */ public Long size() { return (toExclude - fromInclude) / step; } /** * Gets step sign. * * @return the step sign */ public Long getStepSign() { return (step > 0) ? 1L : -1L; } @Override public boolean hasNext() { return step > 0 ? current < toExclude : current > toExclude; } public Long next() { long result = this.current; this.current += this.step; return result; } /** Reset */ public void reset() { this.current = this.fromInclude; } @Override public void remove() { throw new UnsupportedOperationException(); } @Override public Iterator<Long> iterator() { return this; } @Override public String toString() { return Objects.toStringHelper(this) .add("fromInclude", fromInclude) .add("toExclude", toExclude) .add("step", step) .add("size", size()) .add("current", current) .toString(); } } }