module es {
    /**
     * 这个类可以从两方面来考虑。你可以把它看成一个位向量或者一组非负整数。这个名字有点误导人。
     *
     * 它是由一个位向量实现的，但同样可以把它看成是一个非负整数的集合;集合中的每个整数由对应索引处的集合位表示。该结构的大小由集合中的最大整数决定。
     */
    export class BitSet {
        private static LONG_MASK: number = 0x3f;
        private _bits: number[];

        constructor(nbits: number = 64) {
            let length = nbits >> 6;
            if ((nbits & BitSet.LONG_MASK) != 0)
                length++;

            this._bits = new Array(length);
        }

        public and(bs: BitSet) {
            let max = Math.min(this._bits.length, bs._bits.length);
            let i;
            for (let i = 0; i < max; ++i)
                this._bits[i] &= bs._bits[i];

            while (i < this._bits.length)
                this._bits[i++] = 0;
        }

        public andNot(bs: BitSet) {
            let i = Math.min(this._bits.length, bs._bits.length);
            while (--i >= 0)
                this._bits[i] &= ~bs._bits[i];
        }

        public cardinality(): number {
            let card = 0;
            for (let i = this._bits.length - 1; i >= 0; i--) {
                let a = this._bits[i];

                if (a == 0)
                    continue;

                if (a == -1) {
                    card += 64;
                    continue;
                }

                a = ((a >> 1) & 0x5555555555555555) + (a & 0x5555555555555555);
                a = ((a >> 2) & 0x3333333333333333) + (a & 0x3333333333333333);
                let b = ((a >> 32) + a);
                b = ((b >> 4) & 0x0f0f0f0f) + (b & 0x0f0f0f0f);
                b = ((b >> 8) & 0x00ff00ff) + (b & 0x00ff00ff);
                card += ((b >> 16) & 0x0000ffff) + (b & 0x0000ffff);
            }

            return card;
        }

        public clear(pos?: number) {
            if (pos != undefined) {
                let offset = pos >> 6;
                this.ensure(offset);
                this._bits[offset] &= ~(1 << pos);
            } else {
                for (let i = 0; i < this._bits.length; i++)
                    this._bits[i] = 0;
            }
        }

        public get(pos: number): boolean {
            let offset = pos >> 6;
            if (offset >= this._bits.length)
                return false;

            return (this._bits[offset] & (1 << pos)) != 0;
        }

        public intersects(set: BitSet) {
            let i = Math.min(this._bits.length, set._bits.length);
            while (--i >= 0) {
                if ((this._bits[i] & set._bits[i]) != 0)
                    return true;
            }

            return false;
        }

        public isEmpty(): boolean {
            for (let i = this._bits.length - 1; i >= 0; i--) {
                if (this._bits[i])
                    return false;
            }

            return true;
        }

        public nextSetBit(from: number) {
            let offset = from >> 6;
            let mask = 1 << from;
            while (offset < this._bits.length) {
                let h = this._bits[offset];
                do {
                    if ((h & mask) != 0)
                        return from;

                    mask <<= 1;
                    from++;
                } while (mask != 0);

                mask = 1;
                offset++;
            }

            return -1;
        }

        public set(pos: number, value: boolean = true) {
            if (value) {
                let offset = pos >> 6;
                this.ensure(offset);
                this._bits[offset] |= 1 << pos;
            } else {
                this.clear(pos);
            }
        }

        private ensure(lastElt: number) {
            if (lastElt >= this._bits.length) {
                let nd = new Number[lastElt + 1];
                nd = this._bits.copyWithin(0, 0, this._bits.length);
                this._bits = nd;
            }
        }
    }
}
