// ==++==
//
//   Copyright (c) Microsoft Corporation.  All rights reserved.
//
// ==--==
//=============================================================================
//
// Class: BitArray
//
// Purpose: The BitArray class manages a compact array of bit values.
//
//=============================================================================  
namespace System.Collections
{
    // @Consider: is _ShrinkThreshold (256) ints the correct threshold for shrinking?
    using System;
    using System.Diagnostics;

    // A vector of bits.  Use this to store bits efficiently, without having to do bit
    // shifting yourself.
    //| <include path='docs/doc[@for="BitArray"]/*' />
    public sealed class BitArray : ICollection, ICloneable {
        private BitArray() {
        }

        //=========================================================================
        // Allocates space to hold length bit values. All of the values in the bit
        // array are set to false.
        //
        // Exceptions: ArgumentException if length < 0.
        //=========================================================================  
        //| <include path='docs/doc[@for="BitArray.BitArray"]/*' />
        public BitArray(int length)
            : this(length, false) {
        }

        //=========================================================================
        // Allocates space to hold length bit values. All of the values in the bit
        // array are set to defaultValue.
        //
        // Exceptions: ArgumentOutOfRangeException if length < 0.
        //=========================================================================  
        //| <include path='docs/doc[@for="BitArray.BitArray1"]/*' />
        public BitArray(int length, bool defaultValue) {
            if (length < 0) {
                throw new ArgumentOutOfRangeException("ArgumentOutOfRange_NeedNonNegNum");
            }

            m_array = new int[(length + 31) / 32];
            m_length = length;

            int fillValue = defaultValue ? unchecked(((int)0xffffffff)) : 0;
            for (int i = 0; i < m_array.Length; i++) {
                m_array[i] = fillValue;
            }

            _version = 0;
        }

        //=========================================================================
        // Allocates space to hold the bit values in bytes. bytes[0] represents
        // bits 0 - 7, bytes[1] represents bits 8 - 15, etc. The LSB of each byte
        // represents the lowest index value; bytes[0] & 1 represents bit 0,
        // bytes[0] & 2 represents bit 1, bytes[0] & 4 represents bit 2, etc.
        //
        // Exceptions: ArgumentException if bytes == null.
        //=========================================================================  
        //| <include path='docs/doc[@for="BitArray.BitArray2"]/*' />
        public BitArray(byte[] bytes) {
            if (bytes == null) {
                throw new ArgumentNullException("bytes");
            }

            m_array = new int[(bytes.Length + 3) / 4];
            m_length = bytes.Length * 8;

            int i = 0;
            int j = 0;
            while (bytes.Length - j >= 4) {
                m_array[i++] = (bytes[j] & 0xff) |
                              ((bytes[j + 1] & 0xff) << 8) |
                              ((bytes[j + 2] & 0xff) << 16) |
                              ((bytes[j + 3] & 0xff) << 24);
                j += 4;
            }

            Debug.Assert(bytes.Length - j >= 0, "BitArray byteLength problem");
            Debug.Assert(bytes.Length - j < 4, "BitArray byteLength problem #2");

            switch (bytes.Length - j) {
                case 3:
                    m_array[i] = ((bytes[j + 2] & 0xff) << 16);
                    goto case 2;
                    // fall through
                case 2:
                    m_array[i] |= ((bytes[j + 1] & 0xff) << 8);
                    goto case 1;
                    // fall through
                case 1:
                    m_array[i] |= (bytes[j] & 0xff);
            break;
            }

            _version = 0;
        }

        //| <include path='docs/doc[@for="BitArray.BitArray3"]/*' />
        public BitArray(bool[] values) {
            if (values == null) {
                throw new ArgumentNullException("values");
            }

            m_array = new int[(values.Length + 31) / 32];
            m_length = values.Length;

            for (int i = 0; i < values.Length; i++) {
                if (values[i])
                    m_array[i/32] |= (1 << (i%32));
            }

            _version = 0;

        }

        //=========================================================================
        // Allocates space to hold the bit values in values. values[0] represents
        // bits 0 - 31, values[1] represents bits 32 - 63, etc. The LSB of each
        // integer represents the lowest index value; values[0] & 1 represents bit
        // 0, values[0] & 2 represents bit 1, values[0] & 4 represents bit 2, etc.
        //
        // Exceptions: ArgumentException if values == null.
        //=========================================================================  
        //| <include path='docs/doc[@for="BitArray.BitArray4"]/*' />
        public BitArray(int[] values) {
            if (values == null) {
                throw new ArgumentNullException("values");
            }

            m_array = new int[values.Length];
            m_length = values.Length * 32;

            Array.Copy(values, m_array, values.Length);

            _version = 0;
        }

        //=========================================================================
        // Allocates a new BitArray with the same length and bit values as bits.
        //
        // Exceptions: ArgumentException if bits == null.
        //=========================================================================  
        //| <include path='docs/doc[@for="BitArray.BitArray5"]/*' />
        public BitArray(BitArray bits) {
            if (bits == null) {
                throw new ArgumentNullException("bits");
            }

            m_array = new int[(bits.m_length + 31) / 32];
            m_length = bits.m_length;

            Array.Copy(bits.m_array, m_array, (bits.m_length + 31) / 32);

            _version = bits._version;
        }

        //| <include path='docs/doc[@for="BitArray.this"]/*' />
        public bool this[int index] {
                get {
                    return Get(index);
                }
                set {
                    Set(index,value);
                }
         }

        //=========================================================================
        // Returns the bit value at position index.
        //
        // Exceptions: ArgumentOutOfRangeException if index < 0 or
        //             index >= GetLength().
        //=========================================================================  
        //| <include path='docs/doc[@for="BitArray.Get"]/*' />
        public bool Get(int index) {
            if (index < 0 || index >= m_length) {
                throw new ArgumentOutOfRangeException("index", "ArgumentOutOfRange_Index");
            }

            return (m_array[index / 32] & (1 << (index % 32))) != 0;
        }

        //=========================================================================
        // Sets the bit value at position index to value.
        //
        // Exceptions: ArgumentOutOfRangeException if index < 0 or
        //             index >= GetLength().
        //=========================================================================  
        //| <include path='docs/doc[@for="BitArray.Set"]/*' />
        public void Set(int index, bool value) {
            if (index < 0 || index >= m_length) {
                throw new ArgumentOutOfRangeException("index", "ArgumentOutOfRange_Index");
            }

            if (value) {
                m_array[index / 32] |= (1 << (index % 32));
            }
            else {
                m_array[index / 32] &= ~(1 << (index % 32));
            }

            _version++;
        }

        //=========================================================================
        // Sets all the bit values to value.
        //=========================================================================  
        //| <include path='docs/doc[@for="BitArray.SetAll"]/*' />
        public void SetAll(bool value) {
            int fillValue = value ? unchecked(((int)0xffffffff)) : 0;
            int ints = (m_length + 31) / 32;
            for (int i = 0; i < ints; i++) {
                m_array[i] = fillValue;
            }

            _version++;
        }

        //=========================================================================
        // Returns a reference to the current instance ANDed with value.
        //
        // Exceptions: ArgumentException if value == null or
        //             value.Length != this.Length.
        //=========================================================================  
        //| <include path='docs/doc[@for="BitArray.And"]/*' />
        public BitArray And(BitArray value) {
            if (value == null)
                throw new ArgumentNullException("value");
            if (m_length != value.m_length)
                throw new ArgumentException("Arg_ArrayLengthsDiffer");

            int ints = (m_length + 31) / 32;
            for (int i = 0; i < ints; i++) {
                m_array[i] &= value.m_array[i];
            }

            _version++;
            return this;
        }

        //=========================================================================
        // Returns a reference to the current instance ORed with value.
        //
        // Exceptions: ArgumentException if value == null or
        //             value.Length != this.Length.
        //=========================================================================  
        //| <include path='docs/doc[@for="BitArray.Or"]/*' />
        public BitArray Or(BitArray value) {
            if (value == null)
                throw new ArgumentNullException("value");
            if (m_length != value.m_length)
                throw new ArgumentException("Arg_ArrayLengthsDiffer");

            int ints = (m_length + 31) / 32;
            for (int i = 0; i < ints; i++) {
                m_array[i] |= value.m_array[i];
            }

            _version++;
            return this;
        }

        //=========================================================================
        // Returns a reference to the current instance XORed with value.
        //
        // Exceptions: ArgumentException if value == null or
        //             value.Length != this.Length.
        //=========================================================================  
        //| <include path='docs/doc[@for="BitArray.Xor"]/*' />
        public BitArray Xor(BitArray value) {
            if (value == null)
                throw new ArgumentNullException("value");
            if (m_length != value.m_length)
                throw new ArgumentException("Arg_ArrayLengthsDiffer");

            int ints = (m_length + 31) / 32;
            for (int i = 0; i < ints; i++) {
                m_array[i] ^= value.m_array[i];
            }

            _version++;
            return this;
        }

        //=========================================================================
        // Inverts all the bit values. On/true bit values are converted to
        // off/false. Off/false bit values are turned on/true. The current instance
        // is updated and returned.
        //=========================================================================  
        //| <include path='docs/doc[@for="BitArray.Not"]/*' />
        public BitArray Not() {
            int ints = (m_length + 31) / 32;
            for (int i = 0; i < ints; i++) {
                m_array[i] = ~m_array[i];
            }

            _version++;
            return this;
        }


        //| <include path='docs/doc[@for="BitArray.Length"]/*' />
        public int Length {
            get { return m_length; }
            set {
                if (value < 0) {
                    throw new ArgumentOutOfRangeException("ArgumentOutOfRange_NeedNonNegNum");
                }

                int newints = (value + 31) / 32;
                if (newints > m_array.Length || newints + _ShrinkThreshold < m_array.Length) {
                    // grow or shrink (if wasting more than _ShrinkThreshold ints)
                    int[] newarray = new int[newints];
                    Array.Copy(m_array, newarray, newints > m_array.Length ? m_array.Length : newints);
                    m_array = newarray;
                }

                if (value > m_length) {
                    // clear high bit values in the last int
                    int last = ((m_length + 31) / 32) - 1;
                    int bits = m_length % 32;
                    if (bits > 0) {
                        m_array[last] &= (1 << bits) - 1;
                    }

                    // clear remaining int values
                    Array.Clear(m_array, last + 1, newints - last - 1);
                }

                m_length = value;
                _version++;
            }
        }

        // ICollection implementation
        //| <include path='docs/doc[@for="BitArray.CopyTo"]/*' />
        public void CopyTo(Object[] array, int index)
        {
            if (array == null)
                throw new ArgumentNullException("array");

            if (index < 0)
                throw new ArgumentOutOfRangeException("ArgumentOutOfRange_NeedNonNegNum");

            throw new ArgumentException("Arg_BitArrayTypeUnsupported");
        }

        //| <include path='docs/doc[@for="BitArray.Count"]/*' />
        public int Count
        {
            get
            {
                return m_length;
            }
        }

        //| <include path='docs/doc[@for="BitArray.Clone"]/*' />
        public Object Clone()
        {
            BitArray bitArray = new BitArray(m_array);
            bitArray._version = _version;
            bitArray.m_length = m_length;
            return bitArray;
        }

        //| <include path='docs/doc[@for="BitArray.SyncRoot"]/*' />
        public Object SyncRoot
        {
             get
             {
                return this;
             }
        }

        //| <include path='docs/doc[@for="BitArray.IsReadOnly"]/*' />
        public bool IsReadOnly
        {
            get
            {
                return false;
            }
        }


        //| <include path='docs/doc[@for="BitArray.IsSynchronized"]/*' />
        public bool IsSynchronized
        {
            get
            {
                return false;
            }
        }

        //| <include path='docs/doc[@for="BitArray.GetEnumerator"]/*' />
        public IEnumerator GetEnumerator()
        {
            return new BitArrayEnumeratorSimple(this);
        }

          // For a straightforward enumeration of the entire ArrayList,
        // this is faster, because it's smaller.  Patrick showed
        // this with a benchmark.
        private class BitArrayEnumeratorSimple : IEnumerator//, ICloneable
        {
            private BitArray bitarray;
            private int index;
            private int version;
            private bool currentElement;

            internal BitArrayEnumeratorSimple(BitArray bitarray) {
                this.bitarray = bitarray;
                this.index = -1;
                version = bitarray._version;
            }

/*
            public Object Clone() {
                return MemberwiseClone();
            }
*/

            public virtual bool MoveNext() {
                if (version != bitarray._version) throw new InvalidOperationException("InvalidOperation_EnumFailedVersion");
                if (index < (bitarray.Count - 1)) {
                    index++;
                    currentElement = bitarray.Get(index);
                    return true;
                }
                else
                    index = bitarray.Count;

                return false;
            }

            public virtual Object Current {
                get {
                    if (index == -1)
                        throw new InvalidOperationException("InvalidOperation_EnumNotStarted");
                    if (index >= bitarray.Count)
                        throw new InvalidOperationException("InvalidOperation_EnumEnded");
                    return currentElement;
                }
            }

            public void Reset() {
                if (version != bitarray._version) throw new InvalidOperationException("InvalidOperation_EnumFailedVersion");
                index = -1;
            }
        }

        private int[] m_array;
        private int m_length;
        private int _version;

        private const int _ShrinkThreshold = 256;
    }

}
