package jamaica;

/**
 * The Range class representing a linear set of integers (usually indexes)
 *
 * Range is basically a triple (number,base,stride), where number
 * means the number of integers, base is the start integer, and
 * stride is the distance between to consecutive integers.
 *
 * @author Hanhua Feng - hanhua@cs.columbia.edu
 * @version $Id: Range.java,v 1.11 2003/05/13 00:13:13 hanhua Exp $
 */
public class Range implements Cloneable {
    private int number;
    private int base;
    private int stride;

    /** constructor
     * @param first   the start integer of a range
     * @param last    the end integer of a range
     */
    public Range( int first, int last ) {
        this.base = first;
        number = last - base + 1;
        if ( number < 0 )
        {
            number = -number;
            stride = -1;
        }
        else
            stride = 1;
    }

    /** constructor
     * @param first   the start integer of a range
     * @param size    the number of integers in the range
     * @param interval the distance between two consecutive integers
     */
    public Range( int first, int size, int interval ) {
        this.base = first;
        this.number = size;
        this.stride = interval;
    }

    /** the first integer of the range
     */
    public final int first() {
        return base;
    }

    /** the number of integers in the range
     */
    public final int size() {
        return number;
    }

    /** the last integer of the range
     */
    public final int last() {
        return base + (number-1) * stride;
    }

    /** the next integer of the current
     * @param n    current integer
     */
    public final int next( int n ) {
        return n + stride;
    }

    /** the distance between two consecutive integers
     */
    public final int interval() {
        return stride;
    }

    /** does a range contain a specified integer?
     * @param n    the specified integer
     * @return     a boolean value indicating the result
     */
    public final boolean contain( int n ) {
        n -= base;
        if ( stride >= 0 )
            return n >= 0 && n < number * stride && 0 == n % stride;
        return n <= 0 && n > number * stride && 0 == (-n) % (-stride);
    }

    /** does a range is in bounded region
     * @param lower   the lower bound
     * @param upper   the upper bound
     * @return        a boolean value indicating the result
     */
    public final boolean in( int lower, int upper ) {
        if ( stride >= 0 )
            return base >= lower && ( number - 1 ) * stride + base <= upper;
        return base <= upper && ( number - 1 ) * stride + base >= lower;
    }

    /** The smallest (not necessarily the first) integer in a range 
     */
    public int inf() {
        if ( stride >= 0 )
            return base;
        return base + (number-1) * stride;
    }

    /** The largest (not necessarily the last) integer in a range 
     */
    public int sup() {
        if ( stride <= 0 )
            return base;
        return base + (number-1) * stride;
    }

    /** is a range empty? (contains zero integers)
     * @return     a boolean value indicating the result
     */
    public boolean empty() {
        return number <= 0;
    }

    // interesting methods starting here, although not implemented

    /** intersection of two ranges.
     *  Note: the intersection of two ranges are still a range
     *  NOT implemented yet
     */
    public Range intersect( Range x ) {
        throw new UnsupportedOperationException( "intersect" );
    }

    /** get the closure range of the union of two ranges.
     * Note: union of ranges is usually not a range, but there is
     * a minimum range, namely closure range, covering it.
     * NOT implemented yet
     */
    public Range unionClosure( Range x ) {
        throw new UnsupportedOperationException( "unionClosure" );
    }

    /** subtract another range from this range
     * The subtraction is still a range.
     * NOT implemented yet
     */
    public Range sub( Range x ) {
        throw new UnsupportedOperationException( "sub" );
    }

    /** convert a range to string for printing
     */
    public String toString() {
        if ( 1 == stride )
        {
            if ( 1 == number )
                return Integer.toString( base );
            return Integer.toString( base ) + ":" + last();
        }

        return Integer.toString( base ) + ":"
            + ( stride>=0 ? "+" + stride : "-" + (-stride) )
            + ":" + number;
    }
}

