package hep.aida.ref.function; /* * RangeSet.java * * Created on September, 2002, 3:23 PM */ import hep.aida.IRangeSet; import java.util.ArrayList; import java.util.Random; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * * This class handles multiple ranges along one axis. * Overlapping ranges are merged in one bigger range. * * @author Tony Johnson, Victor Serbo, Max Turri * */ public class RangeSet implements IRangeSet { private static Pattern pattern = Pattern.compile("\\s*\\(\\s*([\\-?|\\+?]?\\d*.?\\d*)\\s*,\\s*([\\-?|\\+?]?\\d*.?\\d*)\\s*\\)\\s*"); private ArrayList set; private boolean rangeChanged; private Random r = new Random(); /** * Creates RangeSet with (-infinity, +infinity) valid interval, borders excluded */ public RangeSet() { rangeChanged = true; set = new ArrayList(); Range r = new Range(); set.add(r); } /* * Creates RangeSet with a valid interval (xMin, xMax), borders excluded. */ public RangeSet(double xMin, double xMax) { rangeChanged = true; set = new ArrayList(); Range r = new Range(xMin, xMax);; set.add(r); } /* * Creates RangeSet with valid intervals: * (xMin[0], xMax[0]), (xMin[1], xMax[1]), ... , borders excluded. */ public RangeSet(double[] xMin, double[] xMax) { buildRange(xMin, xMax); } /** * Create a RangeSet from a String. * Ranges can be specified as: * (,-9),(-1.,0.),(2.,3.),(5,) * */ public RangeSet(String range) { Matcher matcher = pattern.matcher(range); int count = 0; while (matcher.find()) count++; double[] xMax = new double[count]; double[] xMin = new double[count]; matcher.reset(); count = 0; while (matcher.find()) { String maxStr = matcher.group(2); if ( maxStr.equals("") ) xMax[count] = Double.POSITIVE_INFINITY; else xMax[count] = Double.parseDouble(maxStr); String minStr = matcher.group(1); if ( minStr.equals("") ) xMin[count] = Double.NEGATIVE_INFINITY; else xMin[count] = Double.parseDouble(minStr); count++; } buildRange(xMin, xMax); } private void buildRange(double[] xMin, double[] xMax) { rangeChanged = true; if (xMin.length != xMax.length) throw new IllegalArgumentException("xMin and xMax arrays must have the same size."); set = new ArrayList(); Range r = new Range(xMin[0], xMax[0]); set.add(r); if (xMin.length > 1) for (int i=1; i<xMin.length; i++) { include(xMin[i], xMax[i]); } } public double PLUS_INF() { return Double.POSITIVE_INFINITY; } public double MINUS_INF() { return Double.NEGATIVE_INFINITY; } /** Returns current number of disjoint ranges (non-overlapping intervals) */ public int size() { return set.size(); } public double length() { if ( size() == 0 ) throw new RuntimeException("No length for this Range set"); double length = 0; for (int i=0; i<size(); i++) length += ((Range) set.get(i)).upperBound() - ((Range) set.get(i)).lowerBound(); return length; } public double generatePoint() { // double rnd = r.nextDouble(); double rnd = Math.random(); double l = length(); double pl = 0; for (int i=0; i<size(); i++) { double rub = ((Range) set.get(i)).upperBound(); double rlb = ((Range) set.get(i)).lowerBound(); pl += rub - rlb; if ( rnd < pl/l ) return rlb + ((pl/l)-rnd)*l; } throw new RuntimeException("Problem with generating a point in the RangeSet"); } /** Return array of lower or upper Bounds for the current set of ranges */ public double[] lowerBounds() { if (size() == 0) return null; double[] tmp = new double[size()]; for (int i=0; i<size(); i++) tmp[i] = ((Range) set.get(i)).lowerBound(); return tmp; } public double[] upperBounds() { if (size() == 0) return null; double[] tmp = new double[size()]; for (int i=0; i<size(); i++) tmp[i] = ((Range) set.get(i)).upperBound(); return tmp; } /** * Set the range from xMin to xMax * This method first does "excludeAll", then "includeRange" */ /* public void setRange(double xMin, double xMax) { rangeChanged = true; set = new ArrayList(); Range r = new Range(xMin, xMax); set.add(r); } */ /** * Additive include/exclude of the range in given axis. */ public void include(double xMin, double xMax) { rangeChanged = true; if (xMin > xMax) throw new IllegalArgumentException("xMax must be bigger tnan xMin."); int r1 = -1; int r2 = -1; boolean range1 = true; boolean range2 = true; double min = Double.NaN; double max = Double.NaN; // No ranges added before if (set.size() == 0) { set.add(new Range(xMin, xMax)); return; } // New range cover all valid intervals or more if (((Range) set.get(set.size()-1)).upperBound()<=xMax && ((Range) set.get(0)).lowerBound()>=xMin) { //setRange(xMin, xMax); excludeAll(); include(xMin, xMax); return; } // New range is above highest valid point if (((Range) set.get(set.size()-1)).upperBound()<xMin) { set.add(new Range(xMin, xMax)); return; } // New range is below lowest valid point if (((Range) set.get(0)).lowerBound()>xMax) { insert(0, new Range(xMin, xMax)); return; } // All other cases range1 = true; r1 = inRange(xMin); if (r1<0) { range1 = false; r1 = inGap(xMin); min = xMin; } else { min = ((Range) set.get(r1)).lowerBound(); } range2 = true; r2 = inRange(xMax); if (r2<0) { range2 = false; r2 = inGap(xMax); max = xMax; } else { max = ((Range) set.get(r2)).upperBound(); } // New range is inside one of existing ranges - do nothing if (range1 && range2 && (r1 == r2)) return; // New range edges fall in the gap between existing ranges if (!range1 && !range2 && (r1 == r2)) { insert(r1, new Range(min, max)); return; } // New range bridges several ranges - replace them with one big range if (range2) { remove(r1, r2); insert(r1, new Range(min, max)); return; } else if (!range2) { remove(r1, r2-1); insert(r1, new Range(min, max)); return; } } public void exclude(double xMin, double xMax) { rangeChanged = true; if (xMin > xMax) throw new IllegalArgumentException("xMax must be bigger than xMin."); int r1 = -1; int r2 = -1; boolean range1 = true; boolean range2 = true; double min = Double.NaN; double max = Double.NaN; // No ranges added before - everything is already excluded, do nothing if (set.size() == 0) { return; } // Exclude range cover all valid intervals or more - exclude all if (((Range) set.get(set.size()-1)).upperBound()<=xMax && ((Range) set.get(0)).lowerBound()>=xMin) { excludeAll(); return; } // New exclude range is above highest valid point - do nothing if (((Range) set.get(set.size()-1)).upperBound()<xMin) { return; } // New exclude range is below lowest valid point - do nothing if (((Range) set.get(0)).lowerBound()>xMax) { return; } // All other cases range1 = true; r1 = inRange(xMin); if (r1<0) { range1 = false; r1 = inGap(xMin); min = xMin; } else { min = ((Range) set.get(r1)).lowerBound(); } range2 = true; r2 = inRange(xMax); if (r2<0) { range2 = false; r2 = inGap(xMax); max = xMax; } else { max = ((Range) set.get(r2)).upperBound(); } // New exclude range starts and ends in the gap if (!range1 && !range2) { if (r1 == r2) return; else { remove(r1, r2-1); return; } } else { // New exclude range starts on the valid include range if (range1) { insert(r1, new Range(min, xMin)); r1++; r2++; } // New exclude range ends on the valid include range if (range2) { insert(r2+1, new Range(xMax, max)); } else r2--; remove(r1, r2); } } /** * Set full range in all axes. */ public void includeAll() { rangeChanged = true; set = new ArrayList(); Range r = new Range(); set.add(r); } /** * Set empty range in all axes. */ public void excludeAll() { rangeChanged = true; set = new ArrayList(); } public boolean isInRange(double point) { if (set.size() == 0) return false; for (int i=0; i<set.size(); i++) { if (((Range) set.get(i)).isInRange(point)) return true; } return false; } private int inRange(double point) { //System.out.println("inRange point="+point); if (set.size() == 0) return -1; for (int i=0; i<set.size(); i++) { if (((Range) set.get(i)).isInRange(point)) return i; } return -1; } private int inGap(double point) { //System.out.println("inGap point="+point+" set.size() = "+set.size()); if (set.size() == 0) return -1; if (((Range) set.get(0)).lowerBound()>point) return 0; if (((Range) set.get(set.size()-1)).upperBound()<point) return set.size(); for (int i=0; i<set.size()-1; i++) { //System.out.println(" upper: "+((Range) set.get(i)).upperBound()); //System.out.println(" lower: "+((Range) set.get(i+1)).lowerBound()); if (((Range) set.get(i)).upperBound()<point && ((Range) set.get(i+1)).lowerBound()>point) { return i+1; } } //System.out.println(" Gap End"); return -1; } /* * Inserts Range into the specified position of the Set * Indexing runs [0, set.size()] */ private void insert(int index, Range r) { if (index>set.size() || index<0 ) throw new IllegalArgumentException("Wrong index: "+index+", Set size="+set.size()); //System.out.println("insert1:Size: "+set.size()+" index="+index); if (index == set.size()) { set.add(r); return; } ArrayList tmp = new ArrayList(); for (int i=0; i<set.size()+1; i++) { if (i < index) tmp.add((Range) set.get(i)); else if (i == index) tmp.add(r); else if (i > index) tmp.add((Range) set.get(i-1)); } //System.out.println("insert2:Size: "+tmp.size()); set = tmp; } /* * Removes ranges from the set, (index1 - index2), inclusive. * Indexing runs [0, set.size()-1] */ private void remove(int index1, int index2) { //System.out.println("remove: Size: "+set.size()+" index1="+index1+", index2="+index2); if (index1>index2 || index1>=set.size() || index2>=set.size() || index1<0 || index2<0) throw new IllegalArgumentException("Wrong index: index1="+index1+ ", index2="+index2+", Set size="+set.size()); ArrayList tmp = new ArrayList(); for (int i=0; i<set.size(); i++) if (i<index1 || i>index2) tmp.add((Range) set.get(i)); //System.out.println("remove: Size: "+tmp.size()); set = tmp; } private void print() { System.out.println("\n\n Size: "+set.size()); for (int i=0; i<set.size(); i++) { System.out.println(i + "\t lowerBound = " + ((Range) set.get(i)).lowerBound() + ",\t upperBound = " + ((Range) set.get(i)).upperBound()); } System.out.println(""); for (int i=0; i<20; i++) { System.out.println(i +"\t "+ isInRange(i*1.)); } } }