package com.colloquial.arithcode.ppm;

/** A set of bytes.  Supports add operations, containment queries,
 * and may be cleared.
 *
 * @author <a href="http://www.colloquial.com/carp/">Bob Carpenter</a>
 * @version 1.1
 * @since 1.0
 */
public final class ByteSet {
    
    /** Construct a byte set.
     */
    public ByteSet() {
        // clear(); implied
    }

    /** Remove all of the bytes from this set.
     */
    public void clear() {
        _bits1 = 0;
        _bits2 = 0;
        _bits3 = 0;
        _bits4 = 0;
    }

    /* Adds a byte, specified as an integer, to this set.
     * @param i Byte to add to this set.
     * @since 1.1
     */
    public void add(int i) { add(Converter.integerToByte(i)); }

    /** Add a byte to this set.
     * @param b Byte to add to this set.
     */
    public void add(byte b) {
        if      (b >  63) _bits4 |= ((long) 1) << (- 64 + (int) b);
        else if (b >=  0) _bits3 |= ((long) 1) << (/* 0 +*/(int) b);
        else if (b > -65) _bits2 |= ((long) 1) << (  64 + (int) b); 
        else              _bits1 |= ((long) 1) << ( 128 + (int) b);
    }

    /** Removes a byte, specified as an integer, from this set.
     * @param i Integer specification of byte to remove from this set.
     * @since 1.1
     */
    public void remove(int i) {  add(Converter.integerToByte(i)); }

    /** Removes a byte from this set.
     * @param b Byte to remove from this set.
     * @since 1.1
     */
    public void remove(byte b) {
        if      (b >  63) _bits4 &= ~(((long) 1) << (- 64 + (int) b));
        else if (b >=  0) _bits3 &= ~(((long) 1) << (/* 0 +*/(int) b));
        else if (b > -65) _bits2 &= ~(((long) 1) << (  64 + (int) b)); 
        else              _bits1 &= ~(((long) 1) << ( 128 + (int) b));

    }

    /** Adds all the members of specified set to this set. The result is
     * that this set's value is the union of its previous value with the
     * specified set.  The argument set is unchanged.
     * @param that Byte set to add to this set.
     * @since 1.1
     */
    public void add(ByteSet that) {
        _bits1 |= that._bits1;
        _bits2 |= that._bits2;
        _bits3 |= that._bits3;
        _bits4 |= that._bits4;
    }

    /** Removes all the members of specified set from this set. The result is
     * that this set's value is the complement of its previous value with the
     * specified set.  The argument set is unchanged.
     * @param that Byte set to remove from this set.
     * @since 1.1
     */
    public void remove(ByteSet that) {
        _bits1 &= ~that._bits1;
        _bits2 &= ~that._bits2;
        _bits3 &= ~that._bits3;
        _bits4 &= ~that._bits4;
    }

    /** Removes all the elements of this set that are not in the specified
     * set.  The result is that this set's value is the intersection of its
     * previous value with the specified set.  The argument set is unchagned.
     * @param that Byte set to restrict this set to.
     * @since 1.1
     */
    public void restrict(ByteSet that) {
        _bits1 &= that._bits1;
        _bits2 &= that._bits2;
        _bits3 &= that._bits3;
        _bits4 &= that._bits4;
    }

    /** Returns <code>true</code> if byte specified as an integer
     * is a member of this set.  Conversion is done by casting.
     * @param i Integer representation of byte to be tested for membership.
     * @return <code>true</code> if the specified byte is a member of this set.
     * @since 1.0
     */
    public boolean contains(int i) {
        return contains(Converter.integerToByte(i));
    }

    /** Returns <code>true</code> if specified byte is a member
     * of this set.
     * @param b Byte to test for membership in this set.
     * @return <code>true</code> if the specified byte is a member of this set.
     */
    public boolean contains(byte b) {
        // built-in ASCII order preference in first two test cases
        // moving 0 inside ternary is *much* slower -- must be set up for ints, not bools
        if (b > 63) return 0 != (_bits4 & ((long) 1) << (- 64 + (int) b));
        if (b >= 0) return 0 != (_bits3 & ((long) 1) << ((int) b));
        if (b > -65) return 0 != (_bits2 & ((long) 1) << (  64 + (int) b));
        return 0 != ((_bits1 & ((long) 1) << ( 128 + (int) b)));
    }

    /** Returns number of elements in this set.
     * @return Number of elements in this set.
     */
    public int size() {
        int sum = 0;
        for (int i = 0; i < 256; ++i) if (contains(i)) ++sum;
        return sum;
    }

    /** An empty set.  Unsafe, because nothing prevents
     * the addition of elements. So it's kept to the package.
     * The decision to do it this way was to keep ByteSet itself a final
     * class for the sake of efficiency.
     */
    static final ByteSet EMPTY_SET = new ByteSet(); 

    /** Representation of bytes -128..-65.
     * Longs are a real kick in the butt for 1.4 non-server.
     */
    private long _bits1; // -128..-65 

    /** Representation of bytes -64..-1.
     */
    private long _bits2; 

    /** Representation of bytes // 0..63.
     */
    private long _bits3; 

    /** Representation of bytes 64..127.
     */
    private long _bits4; 

}
        
