package hep.aida.ref.pdf;

import hep.aida.IRangeSet;

import java.util.ArrayList;
import java.util.Random;

/**
 *
 * This class handles multiple ranges along one axis.
 * Overlapping ranges are merged in one bigger range.
 *
 * @author The FreeHEP team @ SLAC.
 *
 */
public class RangeSet implements IRangeSet {

    private ArrayList set;
    private boolean rangeChanged;
    private Random r = new Random();
    
    private ArrayList listeners = new ArrayList();

    /**
     * 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) {
	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 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));
            notifyRangeSetChanged();
            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);
            notifyRangeSetChanged();
            return;
	}

	// New range is above highest valid point
	if (((Range) set.get(set.size()-1)).upperBound()xMax) {
	    insert(0, new Range(xMin, xMax));
            notifyRangeSetChanged();
	    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));
            notifyRangeSetChanged();
	    return;
	}

	// New range bridges several ranges - replace them with one big range
	if (range2) {
	    remove(r1, r2);
	    insert(r1, new Range(min, max));
            notifyRangeSetChanged();
	    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) {
            notifyRangeSetChanged();
	    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();
            notifyRangeSetChanged();
	    return;
	}

	// New exclude range is above highest valid point - do nothing
	if (((Range) set.get(set.size()-1)).upperBound()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);
                notifyRangeSetChanged();
		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);

	}
        notifyRangeSetChanged();    
    }

    /**
     * Set full range in all axes.
     */
    public void includeAll() {
	rangeChanged = true;
	set = new ArrayList();

	Range r = new Range();
	set.add(r);
        notifyRangeSetChanged();
    }
    
    /**
     * Set empty range in all axes.
     */
    public void excludeAll() {
	rangeChanged = true;
	set = new ArrayList();
        notifyRangeSetChanged();
    }
 
    public boolean isInRange(double point) {
	if (set.size() == 0) return false;
	for (int i=0; ipoint) { 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 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; iindex2) tmp.add((Range) set.get(i));
	
	//System.out.println("remove: Size: "+tmp.size());
	set = tmp;
    }

    void addRangeSetListener( RangeSetListener listener ) {
        listeners.add(listener);
    }
    
    void removeRangeSetListener( RangeSetListener listener ) {
        listeners.remove(listener);
    }
    
    void notifyRangeSetChanged() {
        for ( int i = 0; i < listeners.size(); i++ ) {
            RangeSetListener listener = (RangeSetListener) listeners.get(i);
            listener.rangeSetChanged();
        }
    }
    
    // Test RangeSet here
    public static void main(String[] args) {
	RangeSet set = new RangeSet();

	set.print();

	set.excludeAll();
	set.print();

	set.include(5.5, 7.6);
	set.print();

	set.include(14.5, 17.6);
	set.print();

	set.include(9.5, 12.6);
	set.print();
    
	set.include(6.5, 10.6);
	set.print();
    
	set.include(1.5, 3.6);
	set.print();

	set.include(5.1, 18.6);
	set.print();

	System.out.println("\n ****************** Start Excluding ******************\n");

	set.exclude(14.2, 16.7);
	set.print();

	set.exclude(7.2, 10.3);
	set.print();

	set.exclude(4.9, 11.4);
	set.print();

	set.exclude(2.1, 12.1);
	set.print();

	set.exclude(17.0, 18.0);
	set.print();

    }

    private void print() {
	System.out.println("\n\n Size: "+set.size());
	for (int i=0; i 

Ads help maintain this website.