123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
/*******************************************************************************

        copyright:      Copyright (c) 2009 Kris Bell. All rights reserved

        license:        BSD style: $(LICENSE)

        version:        Sept 2009: Initial release

        since:          0.99.9

        author:         Kris

*******************************************************************************/

module tango.util.container.more.BitSet;

private import core.bitop;

/******************************************************************************

        A fixed or dynamic set of bits. Note that this does no memory 
        allocation of its own when Size != 0, and does heap allocation 
        when Size is zero. Thus you can have a fixed-size low-overhead 
        'instance, or a heap oriented instance. The latter has support
        for resizing, whereas the former does not.

        Note that leveraging intrinsics is slower when using dmd ...

******************************************************************************/

struct BitSet (int Count=0) 
{               
        private enum           width = size_t.sizeof * 8;
        
        const bool opBinary(immutable(char)[] s : "&")(size_t i)
        {
            return and(i);
        }
        
        void opOpAssign(immutable(char)[] s : "|")(size_t i)
        {
            or(i);
        }
        
        void opOpAssign(immutable(char)[] s : "^")(size_t i)
        {
            xor(i);
        }

        static if (Count == 0)
                   private size_t[] bits;
               else
                  private size_t [(Count+width-1)/width] bits;

        /**********************************************************************

                Set the indexed bit, resizing as necessary for heap-based
                instances (IndexOutOfBounds for statically-sized instances)

        **********************************************************************/

        void add (size_t i)
        {
                static if (Count == 0)
                           size (i);
                or (i);
        }

        /**********************************************************************

                Test whether the indexed bit is enabled 

        **********************************************************************/

        const bool has (size_t i)
        {
                auto idx = i / width;
                return idx < bits.length && (bits[idx] & (1 << (i % width))) != 0;
                //return idx < bits.length && bt(&bits[idx], i % width) != 0;
        }

        /**********************************************************************

                Like get() but a little faster for when you know the range
                is valid

        **********************************************************************/

        const bool and (size_t i)
        {
                return (bits[i / width] & (1 << (i % width))) != 0;
                //return bt(&bits[i / width], i % width) != 0;
        }

        /**********************************************************************

                Turn on an indexed bit

        **********************************************************************/

        void or (size_t i)
        {
                bits[i / width] |= (1 << (i % width));
                //bts (&bits[i / width], i % width);
        }
        
        /**********************************************************************

                Invert an indexed bit

        **********************************************************************/

        void xor (size_t i)
        {
                bits[i / width] ^= (1 << (i % width));
                //btc (&bits[i / width], i % width);
        }
        
        /**********************************************************************

                Clear an indexed bit

        **********************************************************************/

        void clr (size_t i)
        {
                bits[i / width] &= ~(1 << (i % width));
                //btr (&bits[i / width], i % width);
        }

        /**********************************************************************

                Clear all bits

        **********************************************************************/

        BitSet* clr ()
        {
                bits[] = 0;
                return &this;
        }
        
        /**********************************************************************

                Clone this BitSet and return it

        **********************************************************************/

        @property const BitSet dup ()
        {
                BitSet x;
                static if (Count == 0)
                           x.bits.length = this.bits.length;
                x.bits[] = bits[];
                return x;
        }

        /**********************************************************************

                Return the number of bits we have room for

        **********************************************************************/

        @property const size_t size ()
        {
                return width * bits.length;
        }

        /**********************************************************************

                Expand to include the indexed bit (dynamic only)

        **********************************************************************/

        static if (Count == 0) BitSet* size (size_t i)
        {
                i = i / width;
                if (i >= bits.length)
                    bits.length = i + 1;
                return &this;
        }
}