diff --git a/src/main/java/xyz/zhouxy/plusone/commons/math/Interval.java b/src/main/java/xyz/zhouxy/plusone/commons/math/Interval.java new file mode 100644 index 0000000..e454fb1 --- /dev/null +++ b/src/main/java/xyz/zhouxy/plusone/commons/math/Interval.java @@ -0,0 +1,62 @@ +package xyz.zhouxy.plusone.commons.math; + +import java.util.Optional; + +import javax.annotation.Nonnull; + +import com.google.common.base.Preconditions; + +import xyz.zhouxy.plusone.commons.util.Numbers; + +public class Interval> { + @Nonnull + private final IntervalType intervalType; + private final T lowerBound; + private final T upperBound; + + public Interval(@Nonnull IntervalType intervalType, T lowerBound, T upperBound) { + Preconditions.checkNotNull(intervalType); + if (intervalType.isLeftClosed()) { + Preconditions.checkArgument(lowerBound != null, + "The lower bound cannot be null, when the interval is left-closed."); + } + if (intervalType.isRightClosed()) { + Preconditions.checkArgument(upperBound != null, + "The upper bound cannot be null, when the interval is right-closed."); + } + if (lowerBound != null && upperBound != null) { + Preconditions.checkArgument(lowerBound.compareTo(upperBound) <= 0, + "The lower bound must less than the upper bound."); + } + this.intervalType = intervalType; + this.lowerBound = lowerBound; + this.upperBound = upperBound; + } + + @Nonnull + public IntervalType getIntervalType() { + return intervalType; + } + + @Nonnull + public Optional getLowerBound() { + return Optional.ofNullable(lowerBound); + } + + @Nonnull + public Optional getUpperBound() { + return Optional.ofNullable(upperBound); + } + + public boolean isLeftClosed() { + return this.intervalType.isLeftClosed(); + } + + public boolean isRightClosed() { + return this.intervalType.isRightClosed(); + } + + public boolean validValue(@Nonnull T value) { + return Numbers.between(value, this.lowerBound, this.upperBound, this.intervalType); + } +} diff --git a/src/main/java/xyz/zhouxy/plusone/commons/math/IntervalType.java b/src/main/java/xyz/zhouxy/plusone/commons/math/IntervalType.java new file mode 100644 index 0000000..e32a3fe --- /dev/null +++ b/src/main/java/xyz/zhouxy/plusone/commons/math/IntervalType.java @@ -0,0 +1,32 @@ +package xyz.zhouxy.plusone.commons.math; + +public enum IntervalType { + /** 开区间。(a,b)={x|a < x < b} */ + OPEN(false, false), + /** 闭区间。[a,b]={x|a ≤ x ≤ b} */ + CLOSED(true, true), + /** 左闭右开区间。[a,b)={x|a ≤ x < b} */ + CLOSED_OPEN(true, false), + /** 左开右闭区间。(a,b]={x|a < x ≤ b} */ + OPEN_CLOSED(false, true); + + private final boolean leftClosed; + private final boolean rightClosed; + + IntervalType(boolean leftClosed, boolean rightClosed) { + this.leftClosed = leftClosed; + this.rightClosed = rightClosed; + } + + public final boolean isLeftClosed() { + return leftClosed; + } + + public final boolean isRightClosed() { + return rightClosed; + } + + public final > Interval buildInterval(T left, T right) { + return new Interval<>(this, left, right); + } +} diff --git a/src/main/java/xyz/zhouxy/plusone/commons/util/Numbers.java b/src/main/java/xyz/zhouxy/plusone/commons/util/Numbers.java index 3c499f0..cd66aa0 100644 --- a/src/main/java/xyz/zhouxy/plusone/commons/util/Numbers.java +++ b/src/main/java/xyz/zhouxy/plusone/commons/util/Numbers.java @@ -18,6 +18,12 @@ package xyz.zhouxy.plusone.commons.util; import java.math.BigDecimal; +import javax.annotation.Nonnull; + +import com.google.common.base.Preconditions; + +import xyz.zhouxy.plusone.commons.math.IntervalType; + /** * Numbers * @@ -77,28 +83,93 @@ public class Numbers { // between - public static boolean between(short value, short min, short max) { - return value >= min && value < max; + public static boolean between(int value, int min, int max) { + return between(value, min, max, IntervalType.CLOSED_OPEN); } - public static boolean between(int value, int min, int max) { - return value >= min && value < max; + public static boolean between(int value, int min, int max, IntervalType intervalType) { + final IntervalType intervalTypeToUse = intervalType != null + ? intervalType + : IntervalType.CLOSED_OPEN; + switch (intervalTypeToUse) { + case OPEN: + return min < value && value < max; + case CLOSED: + return min <= value && value <= max; + case OPEN_CLOSED: + return min < value && value <= max; + case CLOSED_OPEN: + default: + return min <= value && value < max; + } } public static boolean between(long value, long min, long max) { - return value >= min && value < max; + return between(value, min, max, IntervalType.CLOSED_OPEN); } - public static boolean between(float value, float min, float max) { - return value >= min && value < max; + public static boolean between(long value, long min, long max, IntervalType intervalType) { + final IntervalType intervalTypeToUse = intervalType != null + ? intervalType + : IntervalType.CLOSED_OPEN; + switch (intervalTypeToUse) { + case OPEN: + return min < value && value < max; + case CLOSED: + return min <= value && value <= max; + case OPEN_CLOSED: + return min < value && value <= max; + case CLOSED_OPEN: + default: + return min <= value && value < max; + } } public static boolean between(double value, double min, double max) { - return value >= min && value < max; + return between(value, min, max, IntervalType.CLOSED_OPEN); } - public static boolean between(BigDecimal value, BigDecimal min, BigDecimal max) { - return BigDecimals.ge(value, min) && BigDecimals.lt(value, max); + public static boolean between(double value, double min, double max, IntervalType intervalType) { + final IntervalType intervalTypeToUse = intervalType != null + ? intervalType + : IntervalType.CLOSED_OPEN; + switch (intervalTypeToUse) { + case OPEN: + return min < value && value < max; + case CLOSED: + return min <= value && value <= max; + case OPEN_CLOSED: + return min < value && value <= max; + case CLOSED_OPEN: + default: + return min <= value && value < max; + } + } + + public static > boolean between(@Nonnull T value, T min, T max) { + return between(value, min, max, IntervalType.CLOSED_OPEN); + } + + public static > boolean between(@Nonnull T value, T min, T max, IntervalType intervalType) { + Preconditions.checkArgument(value != null, "The value to valid connot be null."); + IntervalType intervalTypeToUse = intervalType != null + ? intervalType + : IntervalType.CLOSED_OPEN; + switch (intervalTypeToUse) { + case OPEN: + return (min == null || min.compareTo(value) < 0) + && (max == null || value.compareTo(max) < 0); + case CLOSED: + return (min == null || min.compareTo(value) <= 0) + && (max == null || value.compareTo(max) <= 0); + case OPEN_CLOSED: + return (min == null || min.compareTo(value) < 0) + && (max == null || value.compareTo(max) <= 0); + case CLOSED_OPEN: + default: + return (min == null || min.compareTo(value) <= 0) + && (max == null || value.compareTo(max) < 0); + } } private Numbers() {