5 // Ben Maurer (bmaurer@users.sourceforge.net)
6 // Modified by Jon Trowbridge (trow@novell.com)
12 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 using System
.Collections
;
37 namespace Beagle
.Util
{
39 public class BetterBitArray
: ICollection
, ICloneable
{
45 public BetterBitArray (BetterBitArray bits
)
48 throw new ArgumentNullException ("bits");
50 _length
= bits
._length
;
51 _array
= new int [(_length
+ 31) / 32];
53 Array
.Copy(bits
._array
, _array
, _array
.Length
);
56 public BetterBitArray (bool [] values
)
59 throw new ArgumentNullException ("values");
61 _length
= values
.Length
;
62 _array
= new int [(_length
+ 31) / 32];
64 for (int i
= 0; i
< values
.Length
; i
++)
65 this [i
] = values
[i
];
68 public BetterBitArray (byte [] bytes
)
71 throw new ArgumentNullException ("bytes");
73 _length
= bytes
.Length
* 8;
74 _array
= new int [(_length
+ 31) / 32];
76 for (int i
= 0; i
< bytes
.Length
; i
++)
77 setByte (i
, bytes
[i
]);
80 public BetterBitArray (int [] values
)
83 throw new ArgumentNullException ("values");
85 int arrlen
= values
.Length
;
87 _array
= new int [arrlen
];
88 Array
.Copy (values
, _array
, arrlen
);
91 public BetterBitArray (int length
)
94 throw new ArgumentOutOfRangeException ("length");
97 _array
= new int [(_length
+ 31) / 32];
98 _contains_true
= ContainsTrueState
.No
; // better
99 _cached_true_count
= 0; // better
102 public BetterBitArray (int length
, bool defaultValue
) : this (length
)
105 for (int i
= 0; i
< _array
.Length
; i
++)
107 _contains_true
= ContainsTrueState
.Yes
; // better
108 _cached_true_count
= length
; // better
110 _contains_true
= ContainsTrueState
.No
;
111 _cached_true_count
= 0;
115 private BetterBitArray (int [] array
, int length
)
121 #region Utility Methods
123 byte getByte (int byteIndex
)
125 int index
= byteIndex
/ 4;
126 int shift
= (byteIndex
% 4) * 8;
128 int theByte
= _array
[index
] & (0xff << shift
);
130 return (byte)((theByte
>> shift
) & 0xff);
133 void setByte (int byteIndex
, byte value)
135 int index
= byteIndex
/ 4;
136 int shift
= (byteIndex
% 4) * 8;
139 _array
[index
] &= ~
(0xff << shift
);
140 // or in the new byte
141 _array
[index
] |= value << shift
;
146 void checkOperand (BetterBitArray operand
)
149 throw new ArgumentNullException ();
150 if (operand
._length
!= _length
)
151 throw new ArgumentException ();
156 get { return _length; }
159 public bool IsReadOnly
{
160 get { return false; }
163 public bool IsSynchronized
{
164 get { return false; }
167 public bool this [int index
] {
168 get { return Get (index); }
169 set { Set (index, value); }
173 get { return _length; }
176 throw new ArgumentOutOfRangeException ();
179 if (_length
!= newLen
) {
180 int numints
= (newLen
+ 31) / 32;
181 int [] newArr
= new int [numints
];
182 int copylen
= (numints
> _array
.Length
) ? _array
.Length
: numints
;
183 Array
.Copy (_array
, newArr
, copylen
);
185 // set the internal state
193 public object SyncRoot
{
197 public object Clone ()
199 // LAMESPEC: docs say shallow, MS makes deep.
200 return new BetterBitArray (this);
203 public void CopyTo (Array array
, int index
)
206 throw new ArgumentNullException ("array");
208 throw new ArgumentOutOfRangeException ("index");
211 throw new ArgumentException ("array", "Array rank must be 1");
213 if (index
>= array
.Length
)
214 throw new ArgumentException ("index", "index is greater than array.Length");
216 // in each case, check to make sure enough space in array
218 if (array
is bool []) {
219 if (array
.Length
- index
< _length
)
220 throw new ArgumentException ();
222 bool [] barray
= (bool []) array
;
224 // Copy the bits into the array
225 for (int i
= 0; i
< _length
; i
++)
226 barray
[index
+ i
] = this [i
];
228 } else if (array
is byte []) {
229 int numbytes
= (_length
+ 7) / 8;
231 if ((array
.Length
- index
) < numbytes
)
232 throw new ArgumentException ();
234 byte [] barray
= (byte []) array
;
235 // Copy the bytes into the array
236 for (int i
= 0; i
< numbytes
; i
++)
237 barray
[index
+ i
] = getByte (i
);
239 } else if (array
is int []) {
241 Array
.Copy (_array
, 0, array
, index
, (_length
+ 31) / 32);
244 throw new ArgumentException ("array", "Unsupported type");
248 public BetterBitArray
Not ()
250 int ints
= (_length
+ 31) / 32;
251 for (int i
= 0; i
< ints
; i
++)
252 _array
[i
] = ~_array
[i
];
255 _contains_true
= ContainsTrueState
.Maybe
; // better
256 _cached_true_count
= -1; // better
260 public BetterBitArray
And (BetterBitArray
value)
262 checkOperand (value);
264 int ints
= (_length
+ 31) / 32;
265 for (int i
= 0; i
< ints
; i
++)
266 _array
[i
] &= value._array
[i
];
269 _contains_true
= ContainsTrueState
.Maybe
; // better
270 _cached_true_count
= -1; // better
274 public BetterBitArray
AndNot (BetterBitArray
value)
276 checkOperand (value);
278 int ints
= (_length
+ 31) / 32;
279 for (int i
= 0; i
< ints
; i
++)
280 _array
[i
] &= ~
value._array
[i
];
283 _contains_true
= ContainsTrueState
.Maybe
; // better
284 _cached_true_count
= -1; // better
288 public BetterBitArray
Or (BetterBitArray
value)
290 checkOperand (value);
292 int ints
= (_length
+ 31) / 32;
293 for (int i
= 0; i
< ints
; i
++)
294 _array
[i
] |= value._array
[i
];
297 if (_contains_true
== ContainsTrueState
.Yes
298 || value._contains_true
== ContainsTrueState
.Yes
)
299 _contains_true
= ContainsTrueState
.Yes
;
301 _contains_true
= ContainsTrueState
.Maybe
; // better
302 _cached_true_count
= -1; // better
306 public BetterBitArray
Xor (BetterBitArray
value)
308 checkOperand (value);
310 int ints
= (_length
+ 31) / 32;
311 for (int i
= 0; i
< ints
; i
++)
312 _array
[i
] ^
= value._array
[i
];
315 _contains_true
= ContainsTrueState
.Maybe
; // better
316 _cached_true_count
= -1; // better
320 public bool Get (int index
)
322 if (index
< 0 || index
>= _length
)
323 throw new ArgumentOutOfRangeException ();
327 rv
= (_array
[index
/ 32] & (1 << (index
% 32))) != 0;
329 _contains_true
= ContainsTrueState
.Yes
;
333 public void Set (int index
, bool value)
335 if (index
< 0 || index
>= _length
)
336 throw new ArgumentOutOfRangeException ();
339 if (_cached_true_count
!= -1) {
341 old
= (_array
[index
/ 32] & (1 << (index
% 32))) != 0;
343 --_cached_true_count
;
345 ++_cached_true_count
;
349 _array
[index
/ 32] |= (1 << (index
% 32));
350 _contains_true
= ContainsTrueState
.Yes
; // better
352 _array
[index
/ 32] &= ~
(1 << (index
% 32));
353 if (_contains_true
== ContainsTrueState
.Yes
) // better
354 _contains_true
= ContainsTrueState
.Maybe
;
360 public void SetAll (bool value)
363 for (int i
= 0; i
< _array
.Length
; i
++)
365 _contains_true
= ContainsTrueState
.Yes
; // better
366 _cached_true_count
= _length
;
369 Array
.Clear (_array
, 0, _array
.Length
);
370 _contains_true
= ContainsTrueState
.No
; // better
371 _cached_true_count
= 0; // better
377 public IEnumerator
GetEnumerator ()
379 return new BetterBitArrayEnumerator (this);
383 class BetterBitArrayEnumerator
: IEnumerator
, ICloneable
{
385 BetterBitArray _bitArray
;
387 int _index
, _version
;
389 public object Clone () {
390 return MemberwiseClone ();
393 public BetterBitArrayEnumerator (BetterBitArray ba
)
397 _version
= ba
._version
;
400 public object Current
{
403 throw new InvalidOperationException ("Enum not started");
404 if (_index
>= _bitArray
.Count
)
405 throw new InvalidOperationException ("Enum Ended");
411 public bool MoveNext ()
415 if (_index
< (_bitArray
.Count
- 1)) {
416 _current
= _bitArray
[++_index
];
420 _index
= _bitArray
.Count
;
433 if (_version
!= _bitArray
._version
)
434 throw new InvalidOperationException ();
438 ////////////////////////////////////////////////////////////////////////////
441 // The "Better" parts are below
444 static private int [] true_count
= new int [256];
446 static BetterBitArray ()
448 for (int i
= 0; i
< 256; ++i
) {
456 true_count
[i
] = count
;
460 private enum ContainsTrueState
{
466 // Just in case, start in an ambiguous state.
467 private ContainsTrueState _contains_true
= ContainsTrueState
.Maybe
;
468 private int _cached_true_count
= -1;
470 // Returns the number of array elements that are set to true.
471 public int TrueCount
{
473 if (_cached_true_count
< 0) {
474 _cached_true_count
= 0;
476 for (int i
= 0; i
< _array
.Length
; ++i
) {
478 byte b1
= (byte) (x
& 0xff);
479 byte b2
= (byte) ((x
& (0xff << 8)) >> 8);
480 byte b3
= (byte) ((x
& (0xff << 16)) >> 16);
481 byte b4
= (byte) ((x
& (0xff << 24)) >> 24);
482 _cached_true_count
+= true_count
[b1
];
483 _cached_true_count
+= true_count
[b2
];
484 _cached_true_count
+= true_count
[b3
];
485 _cached_true_count
+= true_count
[b4
];
489 if (_cached_true_count
== 0)
490 _contains_true
= ContainsTrueState
.No
;
492 _contains_true
= ContainsTrueState
.Yes
;
495 int paranoid_check
= 0;
496 for (int i
= 0; i
< Count
; ++i
)
499 if (paranoid_check
!= _cached_true_count
)
500 Logger
.Log
.Error ("TrueCount mismatch: {0} vs {1}", _cached_true_count
, paranoid_check
);
502 return _cached_true_count
;
506 // Returns the index of the next 'true' value greater than or equal to
507 // start. If there is no such value, we return an index greater than
508 // or equal to array.Count.
509 // FIXME: It would be slightly nicer to provide some sort of enumerator
510 // that returned the indexes of the bits that are set.
511 public int GetNextTrueIndex (int start
)
513 if (start
>= _length
|| _contains_true
== ContainsTrueState
.No
)
522 while (i
< _array
.Length
) {
525 array_value
= _array
[i
];
527 if (array_value
!= 0) {
529 array_value
= array_value
>> offset
;
530 while (offset
< 32) {
531 if ((array_value
& 1) != 0) {
532 if (i
* 32 + offset
< _length
)
533 _contains_true
= ContainsTrueState
.Yes
;
535 _contains_true
= ContainsTrueState
.No
;
536 return i
* 32 + offset
;
539 array_value
= array_value
>> 1;
548 _contains_true
= ContainsTrueState
.No
;
554 // A version of GetNextTrueIndex for walking
555 // backwards across the array.
556 public int GetPreviousTrueIndex (int start
)
558 if (start
< 0 || _contains_true
== ContainsTrueState
.No
)
560 else if (start
>= _length
)
570 array_value
= _array
[i
];
572 if (array_value
!= 0) {
575 while (offset
>= 0) {
576 if ((array_value
& mask
) != 0)
577 return i
* 32 + offset
;
591 public bool ContainsTrue ()
593 if (_contains_true
== ContainsTrueState
.Maybe
) {
594 if (GetNextTrueIndex (0) < _length
)
595 _contains_true
= ContainsTrueState
.Yes
;
597 _contains_true
= ContainsTrueState
.No
;
600 return _contains_true
== ContainsTrueState
.Yes
;
606 Random rng
= new Random ();
608 for (int trial
= 0; trial
< 10000; ++trial
) {
612 BetterBitArray ba
= new BetterBitArray (rng
.Next (100000) + 1);
614 Hashtable true_hash
= new Hashtable ();
615 int N
= 1 + rng
.Next (10000);
616 for (int k
= 0; k
< N
; ++k
) {
617 int j
= rng
.Next (ba
.Count
);
619 true_hash
[j
] = true;
623 while (i
< ba
.Count
) {
624 i
= ba
.GetNextTrueIndex (i
);
626 if (true_hash
.Contains (i
)) {
627 true_hash
.Remove (i
);
629 Console
.WriteLine ("Spurious true at {0}", i
);
637 if (true_hash
.Count
> 0) {
638 Console
.WriteLine ("Missed some trues:");
639 foreach (int k
in true_hash
.Values
)
640 Console
.WriteLine (" {0}", k
);
644 Console
.WriteLine ("Trial #{0}: {1}", trial
+1,
645 failed
? "FAILED" : "ok");