All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hfg.math.Range Maven / Gradle / Ivy

There is a newer version: 20240423
Show newest version
package com.hfg.math;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import com.hfg.exception.ProgrammingException;
import com.hfg.util.CompareUtil;
import com.hfg.util.collection.CollectionUtil;

//------------------------------------------------------------------------------
/**
 Generic range object.
 

@author J. Alex Taylor, hairyfatguy.com */ //------------------------------------------------------------------------------ // com.hfg Library // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com // [email protected] //------------------------------------------------------------------------------ public class Range implements Cloneable, Comparable> { private T mStart; private T mEnd; // Cached values private Boolean mEndValueIsExclusive; //########################################################################### // CONSTRUCTORS //########################################################################### //-------------------------------------------------------------------------- public Range() { } //-------------------------------------------------------------------------- public Range(T inStart, T inEnd) { mStart = inStart; mEnd = inEnd; } //########################################################################### // PUBLIC METHODS //########################################################################### //-------------------------------------------------------------------------- public Range setStart(T inValue) { mStart = inValue; return this; } //-------------------------------------------------------------------------- public T getStart() { return mStart; } //-------------------------------------------------------------------------- public Range setEnd(T inValue) { mEnd = inValue; return this; } //-------------------------------------------------------------------------- public T getEnd() { return mEnd; } //-------------------------------------------------------------------------- @Override public String toString() { return String.format("[%s, %s]", getStart() != null ? getStart().toString() : null, getEnd() != null ? getEnd().toString() : null); } //--------------------------------------------------------------------------- @Override public Range clone() { Range cloneObj; try { cloneObj = (Range) super.clone(); } catch (CloneNotSupportedException e) { throw new ProgrammingException(e); } return cloneObj; } //-------------------------------------------------------------------------- @Override public int hashCode() { int hashCode = (getStart() != null ? getStart().hashCode() : 0); hashCode += 31 * (getEnd() != null ? getEnd().hashCode() : 0); return hashCode; } //-------------------------------------------------------------------------- @Override public boolean equals(Object inObj2) { boolean result = false; if (inObj2 != null && inObj2 instanceof Range) { result = (0 == compareTo((Range)inObj2)); } return result; } //-------------------------------------------------------------------------- @Override public int compareTo(Range inObj2) { int result = 0; if (null == inObj2) { result = 1; } else { // Some custom work here to ensure that null starts sort first and // null ends sort last if (getStart() != null) { if (inObj2.getStart() != null) { result = CompareUtil.compare(getStart(), inObj2.getStart()); } } else if (inObj2.getStart() != null) { result = -1; } if (0 == result) { if (getEnd() != null) { if (inObj2.getEnd() != null) { result = CompareUtil.compare(getEnd(), inObj2.getEnd()); } } else if (inObj2.getEnd() != null) { result = 1; } } } return result; } //-------------------------------------------------------------------------- public Double length() { Double length = null; if (getStart() != null && getEnd() != null) { length = getEnd().doubleValue() - getStart().doubleValue(); // Is the end value inclusive? if (! endValueIsExclusive()) { length += 1; } } return length; } //-------------------------------------------------------------------------- public boolean contains(Number inValue) { return ((inValue != null && ((getStart() != null && inValue.doubleValue() >= getStart().doubleValue() && getEnd() != null && (inValue.doubleValue() < getEnd().doubleValue() || (! endValueIsExclusive() && inValue.doubleValue() == getEnd().doubleValue()))) || (null == getStart() && getEnd() != null && (inValue.doubleValue() < getEnd().doubleValue() || (! endValueIsExclusive() && inValue.doubleValue() == getEnd().doubleValue()))) || (null == getEnd() && getStart() != null && inValue.doubleValue() >= getStart().doubleValue())) || (null == getStart() && null == getEnd())) || (null == getStart() && null == getEnd())); } //-------------------------------------------------------------------------- public boolean contains(int inValue) { return ((getStart() != null && inValue >= getStart().doubleValue() && getEnd() != null && (inValue < getEnd().doubleValue() || (! endValueIsExclusive() && inValue == getEnd().doubleValue()))) || (null == getStart() && getEnd() != null && (inValue < getEnd().doubleValue() || (! endValueIsExclusive() && inValue == getEnd().doubleValue()))) || (null == getEnd() && getStart() != null && inValue >= getStart().doubleValue()) || (null == getStart() && null == getEnd())); } //-------------------------------------------------------------------------- public boolean contains(float inValue) { return ((getStart() != null && inValue >= getStart().doubleValue() && getEnd() != null && (inValue < getEnd().doubleValue() || (! endValueIsExclusive() && inValue == getEnd().doubleValue()))) || (null == getStart() && getEnd() != null && (inValue < getEnd().doubleValue() || (! endValueIsExclusive() && inValue == getEnd().doubleValue()))) || (null == getEnd() && getStart() != null && inValue >= getStart().doubleValue()) || (null == getStart() && null == getEnd())); } //-------------------------------------------------------------------------- public boolean contains(double inValue) { return ((getStart() != null && inValue >= getStart().doubleValue() && getEnd() != null && (inValue < getEnd().doubleValue() || (! endValueIsExclusive() && inValue == getEnd().doubleValue()))) || (null == getStart() && getEnd() != null && (inValue < getEnd().doubleValue() || (! endValueIsExclusive() && inValue == getEnd().doubleValue()))) || (null == getEnd() && getStart() != null && inValue >= getStart().doubleValue()) || (null == getStart() && null == getEnd())); } //-------------------------------------------------------------------------- public boolean contains(Range inRange2) { return (contains(inRange2.getStart().doubleValue()) && contains(inRange2.getEnd().doubleValue())); } //--------------------------------------------------------------------------- /** Collapses a collection of specified ranges.

Ex: given three ranges: [1,4], [2,10], and [14,15] the result would be two ranges: [1,10] and [14,15].

@param inOrigList a collection of Ranges to be combined. @return a list of Ranges that represents the union of the specified Ranges. */ public static List> union(Collection> inOrigList) { List> condensedRanges = new ArrayList<>(inOrigList); boolean overlap = true; while (overlap) { overlap = false; for (int i = 0; i < condensedRanges.size() - 1; i++) { Range range1 = condensedRanges.get(i); for (int j = i + 1; j < condensedRanges.size(); j++) { Range range2 = condensedRanges.get(j); if (range1.intersects(range2) || (! range1.endValueIsExclusive() && ((range1.getStart() != null && range2.getEnd() != null && 1 == range1.getStart().longValue() - range2.getEnd().longValue()) || (range1.getEnd() != null && range2.getStart() != null && 1 == range2.getStart().longValue() - range1.getEnd().longValue())))) { T condensedStart = null; if (range1.getStart() != null && range2.getStart() != null) { int startComparison = CompareUtil.compare(range1.getStart(), range2.getStart()); condensedStart = startComparison > 0 ? range2.getStart() : range1.getStart(); } T condensedEnd = null; if (range1.getEnd() != null && range2.getEnd() != null) { int endComparison = CompareUtil.compare(range1.getEnd(), range2.getEnd()); condensedEnd = endComparison > 0 ? range1.getEnd() : range2.getEnd(); } Range condensedRange = new Range(condensedStart, condensedEnd); condensedRanges.remove(j); condensedRanges.remove(i); condensedRanges.add(condensedRange); overlap = true; break; } } if (overlap) { break; } } } Collections.sort(condensedRanges); return condensedRanges; } //--------------------------------------------------------------------------- /** Returns a single Range that encompasses the collection of input ranges and ignores internal gaps. @param inOrigList a collection of input ranges @return a single Range that encompasses the input ranges and ignores internal gaps */ public static Range superUnion(Collection> inOrigList) { Range superUnion = null; if (CollectionUtil.hasValues(inOrigList)) { for (Range range : inOrigList) { if (null == superUnion) { superUnion = range; } else { superUnion = superUnion.superUnion(range); } } } return superUnion; } //--------------------------------------------------------------------------- /** Returns a single Range that encompasses both input ranges and ignores internal gaps. @param inRange2 the second Range to union with the current range @return a single Range that encompasses both input ranges and ignores internal gaps */ public Range superUnion(Range inRange2) { T start = null; T end = null; if (inRange2 != null) { if (getStart() != null && inRange2.getStart() != null) { start = (getStart().doubleValue() < inRange2.getStart().doubleValue() ? getStart() : inRange2.getStart()); } if (getEnd() != null && inRange2.getEnd() != null) { end = (getEnd().doubleValue() > inRange2.getEnd().doubleValue() ? getEnd() : inRange2.getEnd()); } } return new Range<>(start, end); } //--------------------------------------------------------------------------- public List> subtract(Range inRange2) { List> resultRanges = new ArrayList<>(2); Range intersection = intersection(inRange2); if (intersection != null) { // Right chunk? Range leftChunk; if (null == getStart()) { if (intersection.getStart() != null) { leftChunk = new Range<>(); if (endValueIsExclusive()) { leftChunk.setEnd(intersection.getStart()); } else { if (intersection.getStart() instanceof Long) { leftChunk.setEnd((T) new Long(intersection.getStart().longValue() - 1)); } else if (intersection.getStart() instanceof Integer) { leftChunk.setEnd((T) new Integer(intersection.getStart().intValue() - 1)); } } resultRanges.add(leftChunk); } } else if (CompareUtil.compare(intersection.getStart(), getStart()) > 0) { leftChunk = new Range<>(); leftChunk.setStart(getStart()); if (endValueIsExclusive()) { leftChunk.setEnd(intersection.getStart()); } else { if (intersection.getStart() instanceof Long) { leftChunk.setEnd((T) new Long(intersection.getStart().longValue() - 1)); } else if (intersection.getStart() instanceof Integer) { leftChunk.setEnd((T) new Integer(intersection.getStart().intValue() - 1)); } } resultRanges.add(leftChunk); } // Right chunk? Range rightChunk; if (null == getEnd()) { if (intersection.getEnd() != null) { rightChunk = new Range<>(); if (endValueIsExclusive()) { rightChunk.setStart(intersection.getEnd()); } else { if (intersection.getEnd() instanceof Long) { rightChunk.setStart((T) new Long(intersection.getEnd().longValue() + 1)); } else if (intersection.getEnd() instanceof Integer) { rightChunk.setStart((T) new Integer(intersection.getEnd().intValue() + 1)); } } resultRanges.add(rightChunk); } } else if (CompareUtil.compare(intersection.getEnd(), getEnd()) < 0) { rightChunk = new Range<>(); rightChunk.setEnd(getEnd()); if (endValueIsExclusive()) { rightChunk.setStart(intersection.getEnd()); } else { if (intersection.getStart() instanceof Long) { rightChunk.setStart((T) new Long(intersection.getEnd().longValue() + 1)); } else if (intersection.getStart() instanceof Integer) { rightChunk.setStart((T) new Integer(intersection.getEnd().intValue() + 1)); } } resultRanges.add(rightChunk); } } else { // There was no intersection, hence return the orig. resultRanges.add(this); } return resultRanges; } //--------------------------------------------------------------------------- public boolean endValueIsExclusive() { if (null == mEndValueIsExclusive) { // We can't interrogate T directly so we have to look at the values. T end = getEnd(); if (end != null) { mEndValueIsExclusive = (end instanceof Double || end instanceof Float || end instanceof BigDecimal); } else { T start = getStart(); if (start != null) { mEndValueIsExclusive = (start instanceof Double || start instanceof Float || start instanceof BigDecimal); } } } return mEndValueIsExclusive; } //-------------------------------------------------------------------------- public Range intersection(Range inRange2) { Range intersection = null; if (inRange2 != null) { if (getStart() != null) { if (inRange2.getStart() != null) { if (getEnd() != null) { if (inRange2.getEnd() != null) { if (getStart().doubleValue() <= inRange2.getEnd().doubleValue() && getEnd().doubleValue() >= inRange2.getStart().doubleValue()) { // ----- // ----- intersection = new Range() .setStart(getStart().doubleValue() < inRange2.getStart().doubleValue() ? inRange2.getStart() : getStart()) .setEnd(getEnd().doubleValue() > inRange2.getEnd().doubleValue() ? inRange2.getEnd() : getEnd()); } } else // Range 2 extends infinitely to the right { // ----- // -----> if (inRange2.contains(getStart().doubleValue())) { intersection = new Range() .setStart(getStart().doubleValue() < inRange2.getStart().doubleValue() ? inRange2.getStart() : getStart()) .setEnd(getEnd()); } } } else if (inRange2.getEnd() != null) // Range 1 extends infinitely to the right { // -----> // ----- if (getStart().doubleValue() <= inRange2.getEnd().doubleValue()) { intersection = new Range() .setStart(getStart().doubleValue() < inRange2.getStart().doubleValue() ? inRange2.getStart() : getStart()) .setEnd(inRange2.getEnd()); } } else { // Both ranges extend infinitely to the right // -----> // -------> intersection = new Range() .setStart(getStart().doubleValue() > inRange2.getStart().doubleValue() ? inRange2.getStart() : getStart()) .setEnd(getStart().doubleValue() > inRange2.getStart().doubleValue() ? getStart() : inRange2.getStart()); } } else // Range 2 extends infinitely to the left { if (getEnd() != null) { if (inRange2.getEnd() != null) { // ----- // <------- if (getStart().doubleValue() <= inRange2.getEnd().doubleValue()) { intersection = new Range() .setStart(getStart()) .setEnd(getEnd().doubleValue() > inRange2.getEnd().doubleValue() ? inRange2.getEnd() : getEnd()); } } else // Range 2 extends infinitely to the left & right { // ----- // <-------> intersection = clone(); } } else if (inRange2.getEnd() != null) // Range 1 extends infinitely to the right { // -----> // <--- if (inRange2.contains(getStart().doubleValue())) { intersection = new Range() .setStart(getStart()) .setEnd(inRange2.getEnd()); } } else { // Both ranges extend infinitely to the right // -----> // <-------> intersection = new Range() .setStart(getStart()); } } } else if (inRange2.getStart() != null) { if (getEnd() != null) { if (inRange2.getEnd() != null) { // <----- // ------- if (inRange2.getStart().doubleValue() <= getEnd().doubleValue()) { intersection = new Range() .setStart(inRange2.getStart()) .setEnd(getEnd().doubleValue() <= inRange2.getEnd().doubleValue() ? getEnd() : inRange2.getEnd()); } } else { // <----- // -------> if (inRange2.getStart().doubleValue() <= getEnd().doubleValue()) { intersection = new Range() .setStart(inRange2.getStart()) .setEnd(getEnd()); } } } else { if (inRange2.getEnd() != null) { // <-----> // --- intersection = inRange2.clone(); } else { // <-----> // ---> intersection = new Range() .setStart(inRange2.getStart()); } } } else { if (getEnd() != null) { if (inRange2.getEnd() != null) { // <----- // <--- intersection = new Range() .setEnd(getEnd().doubleValue() <= inRange2.getEnd().doubleValue() ? getEnd() : inRange2.getEnd()); } else { // <----- // <--------> intersection = clone(); } } else { if (inRange2.getEnd() != null) { // <-----> // <--- intersection = inRange2.clone(); } else { // <-----> // <-----> intersection = clone(); } } } } return intersection; } //-------------------------------------------------------------------------- public boolean intersects(Range inRange2) { boolean result = false; if (getStart() != null) { if (inRange2.getStart() != null) { if (getEnd() != null) { if (inRange2.getEnd() != null) { if (endValueIsExclusive()) { result = getStart().doubleValue() < inRange2.getEnd().doubleValue() && getEnd().doubleValue() > inRange2.getStart().doubleValue(); } else { result = getStart().doubleValue() <= inRange2.getEnd().doubleValue() && getEnd().doubleValue() >= inRange2.getStart().doubleValue(); } } else // Range 2 extends infinitely to the right { result = inRange2.getStart().doubleValue() <= getEnd().doubleValue(); } } else if (inRange2.getEnd() != null) { // Range 1 extends infinitely to the right result = inRange2.getEnd().doubleValue() >= getStart().doubleValue(); } else { // Both ranges extend infinitely to the right result = true; } } else // Range 2 extends infinitely to the left { if (inRange2.getEnd() != null) { if (endValueIsExclusive()) { result = getStart().doubleValue() < inRange2.getEnd().doubleValue(); } else { result = getStart().doubleValue() <= inRange2.getEnd().doubleValue(); } } else { // Range2 is infinite result = true; } } } else if (inRange2.getStart() != null) { if (getEnd() != null) { if (endValueIsExclusive()) { result = inRange2.getStart().doubleValue() < getEnd().doubleValue(); } else { result = inRange2.getStart().doubleValue() <= getEnd().doubleValue(); } } else { result = true; } } else { // Both ranges extend infinitely to the left result = true; } return result; } //-------------------------------------------------------------------------- public void swapStartEndValues() { T tmp = getEnd(); setEnd(getStart()); setStart(tmp); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy