#region Copyright 2009-2014 by Roger Knapp, Licensed under the Apache License, Version 2.0
/* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#endregion
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
namespace CSharpTest.Net.Collections
{
///
/// An ordinal list is a list optimized to store lists of integer data that can then be manipulated
/// as a set with intersect/union etc. Each integer stored is translated to a bit offset and thus
/// cann't be stored more than once or in any particular order. Note: adding the value int.Max will
/// allocate int.Max/8 bytes of memory, so this is best used with ordinal indexes into a list that
/// is smaller than 8,388,608 (one megabyte of bits). Pre-allocate with Ceiling = max for better
/// performance, or add the integers in reverse order (highest to lowest).
///
public class OrdinalList : ICollection, ICollection, IEnumerable, ICloneable
{
#region Static cache for Count { get; }
static readonly byte[] BitCount;
static OrdinalList()
{
BitCount = new byte[byte.MaxValue + 1];
for (int i = 0; i <= byte.MaxValue; i++)
{
if ((i & 0x0001) != 0) BitCount[i]++;
if ((i & 0x0002) != 0) BitCount[i]++;
if ((i & 0x0004) != 0) BitCount[i]++;
if ((i & 0x0008) != 0) BitCount[i]++;
if ((i & 0x0010) != 0) BitCount[i]++;
if ((i & 0x0020) != 0) BitCount[i]++;
if ((i & 0x0040) != 0) BitCount[i]++;
if ((i & 0x0080) != 0) BitCount[i]++;
}
}
#endregion
byte[] _bits;
/// Constructs an empty OrdinalList
public OrdinalList()
{
Clear();
}
/// Constructs an OrdinalList from a set of bits represeting the ordinals
public OrdinalList(byte[] fromBits)
{
Clear();
_bits = (byte[])fromBits.Clone();
}
/// Constructs an OrdinalList from the integer ordinals provided
public OrdinalList(IEnumerable contents)
{
Clear();
AddRange(contents);
}
/// Empty the OrdinalList
public void Clear()
{
_bits = new byte[0];
}
/// Semi-expensive, returns the count of ordinals in the collection
public int Count
{
get
{
int count = 0;
foreach (byte b in _bits)
count += BitCount[b];
return count;
}
}
///
/// Gets or sets the maximum inclusive ordinal that can be stored in the memory currently
/// allocated, ranges from -1 to int.MaxValue
///
public int Ceiling
{
get { return (int)((((long)_bits.Length) << 3) - 1); }
set { AllocFor(value); }
}
private void AllocFor(int max)
{
if (max >= 0)
max = 1 + (max >> 3);
else if (max == -1)
max = 0;
else
throw new ArgumentOutOfRangeException();
if (max > _bits.Length)
Array.Resize(ref _bits, max);
}
/// Adds a range of integer ordinals into the collection
public void AddRange(IEnumerable contents)
{
int max = int.MinValue;
foreach (int i in contents)
max = Math.Max(i, max);
if (max == int.MinValue)
return;//empty
//pre-alloc/adjust array
AllocFor(max);
foreach (int item in contents)
{
int offset = item >> 3;
int bit = 1 << (item & 0x07);
_bits[offset] |= unchecked((byte)bit);
}
}
/// Adds an integer ordinal into the collection
public void Add(int item)
{
AllocFor(item);
int offset = item >> 3;
int bit = 1 << (item & 0x07);
_bits[offset] |= unchecked((byte)bit);
}
/// Removes an ordinal from the collection
public bool Remove(int item)
{
int offset = item >> 3;
int bit = 1 << (item & 0x07);
if (offset < _bits.Length)
{
if (0 != (_bits[offset] & unchecked((byte)bit)))
{
_bits[offset] &= unchecked((byte)~bit);
return true;
}
}
return false;
}
/// Returns true if the ordinal is in the collection
public bool Contains(int item)
{
int offset = item >> 3;
int bit = 1 << (item & 0x07);
if (offset < _bits.Length && (_bits[offset] & bit) == bit)
return true;
return false;
}
/// Extracts the ordinals into an array
public void CopyTo(int[] array, int arrayIndex)
{
foreach(int ordinal in this)
array[arrayIndex++] = ordinal;
}
/// Returns false
public bool IsReadOnly
{
get { return false; }
}
/// Returns the array of ordinals that have been added.
public int[] ToArray() { return new List(this).ToArray(); }
/// Returns the complete set of raw bytes for storage and reconstitution
public byte[] ToByteArray() { return (byte[])_bits.Clone(); }
#region Set Operations
/// Returns the 1's compliment (inverts) of the list up to Ceiling
public OrdinalList Invert(int ceiling)
{
unchecked
{
byte[] copy = new byte[_bits.Length];
for (int i = 0; i < _bits.Length; i++)
copy[i] = (byte)~_bits[i];
OrdinalList result = new OrdinalList();
result._bits = copy;
result.Ceiling = ceiling;
int limit = result.Ceiling;
for (int i = Ceiling; i < limit; i++)
result.Add(i);
for (int i = ceiling + 1; i <= limit; i++)
result.Remove(i);
return result;
}
}
/// Returns the set of items that are in both this set and the provided set
/// { 1, 2, 3 }.IntersectWith({ 2, 3, 4 }) == { 2, 3 }
public OrdinalList IntersectWith(OrdinalList other)
{
byte[] small, big;
big = _bits.Length > other._bits.Length ? _bits : other._bits;
small = _bits.Length > other._bits.Length ? other._bits : _bits;
byte[] newbits = (byte[])small.Clone();
for (int i = 0; i < small.Length; i++)
newbits[i] &= big[i];
OrdinalList result = new OrdinalList();
result._bits = newbits;
return result;
}
/// Returns the set of items that are in either this set or the provided set
/// { 1, 2, 3 }.UnionWith({ 2, 3, 4 }) == { 1, 2, 3, 4 }
public OrdinalList UnionWith(OrdinalList other)
{
byte[] small, big;
big = _bits.Length > other._bits.Length ? _bits : other._bits;
small = _bits.Length > other._bits.Length ? other._bits : _bits;
byte[] newbits = (byte[])big.Clone();
for (int i = 0; i < small.Length; i++)
newbits[i] |= small[i];
OrdinalList result = new OrdinalList();
result._bits = newbits;
return result;
}
#endregion
#region ICollection Members
void ICollection.CopyTo(Array array, int index)
{
if (array is int[])
this.CopyTo((int[])array, index);
else if (array is byte[])
_bits.CopyTo(array, index);
else
throw new ArgumentException();
}
bool ICollection.IsSynchronized
{
get { return false; }
}
object ICollection.SyncRoot
{
get { return this; }
}
/// Returns an enumeration of the ordinal values
public IEnumerable EnumerateFrom(int startAt)
{
return EnumerateRange(startAt, int.MaxValue);
}
/// Returns an enumeration of the ordinal values
public IEnumerable EnumerateRange(int startAt, int endAt)
{
int ordinal = startAt & ~0x07;
//foreach (byte i in _bits)
for(int ix = startAt >> 3; ix < _bits.Length; ix++)
{
if (_bits[ix] != 0)
{
int i = _bits[ix];
if ((i & 0x0001) != 0) yield return ordinal + 0;
if ((i & 0x0002) != 0) yield return ordinal + 1;
if ((i & 0x0004) != 0) yield return ordinal + 2;
if ((i & 0x0008) != 0) yield return ordinal + 3;
if ((i & 0x0010) != 0) yield return ordinal + 4;
if ((i & 0x0020) != 0) yield return ordinal + 5;
if ((i & 0x0040) != 0) yield return ordinal + 6;
if ((i & 0x0080) != 0) yield return ordinal + 7;
}
ordinal += 8;
if (ordinal > endAt)
break;
}
}
/// Returns an enumeration of the ordinal values
public IEnumerator GetEnumerator()
{
return EnumerateFrom(0).GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return EnumerateFrom(0).GetEnumerator();
}
#endregion
object ICloneable.Clone() { return Clone(); }
///
/// Creates a new object that is a copy of the current instance.
///
public OrdinalList Clone()
{
OrdinalList copy = new OrdinalList();
copy._bits = (byte[])_bits.Clone();
return copy;
}
}
}