Browse Source

add SparseArray

David Rose 19 years ago
parent
commit
9f198514ce

+ 3 - 0
panda/src/putil/Sources.pp

@@ -53,6 +53,7 @@
     nonDeletor.h \
     pta_double.h \
     pta_float.h pta_int.h \
+    sparseArray.I sparseArray.h \
     string_utils.I string_utils.N string_utils.h \
     timedCycle.I timedCycle.h typedWritable.I \
     typedWritable.h typedWritableReferenceCount.I \
@@ -94,6 +95,7 @@
     nonDeletor.cxx \
     pta_double.cxx pta_float.cxx \
     pta_int.cxx pta_ushort.cxx \
+    sparseArray.cxx \
     string_utils.cxx timedCycle.cxx typedWritable.cxx \
     typedWritableReferenceCount.cxx updateSeq.cxx \
     uniqueIdAllocator.cxx \
@@ -145,6 +147,7 @@
     nonDeletor.h \
     pta_double.h \
     pta_float.h pta_int.h pta_ushort.h \
+    sparseArray.I sparseArray.h \
     string_utils.I \
     string_utils.h timedCycle.I timedCycle.h typedWritable.I \
     typedWritable.h typedWritableReferenceCount.I \

+ 158 - 1
panda/src/putil/bitArray.cxx

@@ -17,9 +17,31 @@
 ////////////////////////////////////////////////////////////////////
 
 #include "bitArray.h"
+#include "sparseArray.h"
 
 TypeHandle BitArray::_type_handle;
 
+////////////////////////////////////////////////////////////////////
+//     Function: BitArray::Constructor (from SparseArray)
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+BitArray::
+BitArray(const SparseArray &from) {
+  _highest_bits = 0;
+
+  int num_subranges = from.get_num_subranges();
+  for (int i = 0; i < num_subranges; ++i) {
+    int begin = from.get_subrange_begin(i);
+    int end = from.get_subrange_end(i);
+    set_range(begin, end - begin);
+  }
+
+  if (from.is_inverse()) {
+    invert_in_place();
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: BitArray::is_zero
 //       Access: Published
@@ -37,10 +59,145 @@ is_zero() const {
   // Start from the high end, since that's more likely to be nonzero.
   Array::reverse_iterator ai;
   for (ai = _array.rbegin(); ai != _array.rend(); ++ai) {
-    if ((*ai) != 0) {
+    if (!(*ai).is_zero()) {
+      return false;
+    }
+  }
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BitArray::is_all_on
+//       Access: Published
+//  Description: Returns true if the entire bitmask is one, false
+//               otherwise.
+////////////////////////////////////////////////////////////////////
+bool BitArray::
+is_all_on() const {
+  if (!_highest_bits) {
+    // If all the infinite highest bits are not set, certainly the
+    // bitmask is not all on.
+    return false;
+  }
+
+  Array::reverse_iterator ai;
+  for (ai = _array.rbegin(); ai != _array.rend(); ++ai) {
+    if (!(*ai).is_all_on()) {
+      return false;
+    }
+  }
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BitArray::has_any_of
+//       Access: Published
+//  Description: Returns true if any bit in the indicated range is
+//               set, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool BitArray::
+has_any_of(int low_bit, int size) const {
+  if ((low_bit + size - 1) / num_bits_per_word >= get_num_words()) {
+    // This range touches the highest bits.
+    if (_highest_bits) {
+      return true;
+    }
+  }
+
+  int w = low_bit / num_bits_per_word;
+  int b = low_bit % num_bits_per_word;
+
+  if (w >= get_num_words()) {
+    // This range is entirely among the highest bits.
+    return _highest_bits;
+  }
+  if (b + size <= num_bits_per_word) {
+    // The whole thing fits within one word of the array.
+    return get_word(w).has_any_of(b, size);
+  }
+
+  int num_high_bits = num_bits_per_word - b;
+  if (_array[w].has_any_of(b, num_high_bits)) {
+    return true;
+  }
+  size -= num_high_bits;
+  ++w;
+
+  while (size > 0) {
+    if (size <= num_bits_per_word) {
+      // The remainder fits within one word of the array.
+      return _array[w].has_any_of(0, size);
+    }
+
+    // Keep going.
+    if (!_array[w].is_zero()) {
+      return true;
+    }
+    size -= num_bits_per_word;
+    ++w;
+
+    if (w >= get_num_words()) {
+      // Now we're up to the highest bits.
+      return _highest_bits;
+    }
+  }
+
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BitArray::has_all_of
+//       Access: Published
+//  Description: Returns true if all bits in the indicated range are
+//               set, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool BitArray::
+has_all_of(int low_bit, int size) const {
+  if ((low_bit + size - 1) / num_bits_per_word >= get_num_words()) {
+    // This range touches the highest bits.
+    if (!_highest_bits) {
       return false;
     }
   }
+
+  int w = low_bit / num_bits_per_word;
+  int b = low_bit % num_bits_per_word;
+
+  if (w >= get_num_words()) {
+    // This range is entirely among the highest bits.
+    return _highest_bits;
+  }
+  if (b + size <= num_bits_per_word) {
+    // The whole thing fits within one word of the array.
+    return get_word(w).has_all_of(b, size);
+  }
+
+  int num_high_bits = num_bits_per_word - b;
+  if (!_array[w].has_all_of(b, num_high_bits)) {
+    return false;
+  }
+  size -= num_high_bits;
+  ++w;
+
+  while (size > 0) {
+    if (size <= num_bits_per_word) {
+      // The remainder fits within one word of the array.
+      return _array[w].has_all_of(0, size);
+    }
+
+    // Keep going.
+    if (!_array[w].is_all_on()) {
+      return false;
+    }
+    size -= num_bits_per_word;
+    ++w;
+
+    if (w >= get_num_words()) {
+      // Now we're up to the highest bits.
+      return _highest_bits;
+    }
+  }
+
   return true;
 }
 

+ 6 - 1
panda/src/putil/bitArray.h

@@ -29,6 +29,8 @@
 
 #include "checksumHashGenerator.h"
 
+class SparseArray;
+
 ////////////////////////////////////////////////////////////////////
 //       Class : BitArray
 // Description : A dynamic array with an unlimited number of bits.
@@ -44,11 +46,11 @@ public:
   enum { num_bits_per_word = MaskType::num_bits };
 
 PUBLISHED:
-
   INLINE BitArray();
   INLINE BitArray(WordType init_value);
   INLINE BitArray(const BitArray &copy);
   INLINE BitArray &operator = (const BitArray &copy);
+  BitArray(const SparseArray &from);
 
   INLINE static BitArray all_on();
   INLINE static BitArray all_off();
@@ -69,9 +71,12 @@ PUBLISHED:
   INLINE void set_bit_to(int index, bool value);
   INLINE bool get_highest_bits() const;
   bool is_zero() const;
+  bool is_all_on() const;
 
   INLINE WordType extract(int low_bit, int size) const;
   INLINE void store(WordType value, int low_bit, int size);
+  bool has_any_of(int low_bit, int size) const;
+  bool has_all_of(int low_bit, int size) const;
   void set_range(int low_bit, int size);
   void clear_range(int low_bit, int size);
   INLINE void set_range_to(bool value, int low_bit, int size);

+ 38 - 0
panda/src/putil/bitMask.I

@@ -273,6 +273,18 @@ is_zero() const {
   return (_word == 0);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: BitMask::is_all_on
+//       Access: Published
+//  Description: Returns true if the entire bitmask is one, false
+//               otherwise.
+////////////////////////////////////////////////////////////////////
+template<class WType, int nbits>
+INLINE bool BitMask<WType, nbits>::
+is_all_on() const {
+  return (~_word == 0);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: BitMask::extract
 //       Access: Published
@@ -300,6 +312,32 @@ store(WordType value, int low_bit, int size) {
   _word = (_word & ~mask) | ((value << low_bit) & mask);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: BitMask::has_any_of
+//       Access: Published
+//  Description: Returns true if any bit in the indicated range is
+//               set, false otherwise.
+////////////////////////////////////////////////////////////////////
+template<class WType, int nbits>
+INLINE bool BitMask<WType, nbits>::
+has_any_of(int low_bit, int size) const {
+  WordType mask = BitMask<WType, nbits>::range(low_bit, size)._word;
+  return (_word & mask) != 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BitMask::has_all_of
+//       Access: Published
+//  Description: Returns true if all bits in the indicated range are
+//               set, false otherwise.
+////////////////////////////////////////////////////////////////////
+template<class WType, int nbits>
+INLINE bool BitMask<WType, nbits>::
+has_all_of(int low_bit, int size) const {
+  WordType mask = BitMask<WType, nbits>::range(low_bit, size)._word;
+  return (_word & mask) == mask;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: BitMask::set_range
 //       Access: Published

+ 3 - 0
panda/src/putil/bitMask.h

@@ -64,9 +64,12 @@ PUBLISHED:
   INLINE void clear_bit(int index);
   INLINE void set_bit_to(int index, bool value);
   INLINE bool is_zero() const;
+  INLINE bool is_all_on() const;
 
   INLINE WordType extract(int low_bit, int size) const;
   INLINE void store(WordType value, int low_bit, int size);
+  INLINE bool has_any_of(int low_bit, int size) const;
+  INLINE bool has_all_of(int low_bit, int size) const;
   INLINE void set_range(int low_bit, int size);
   INLINE void clear_range(int low_bit, int size);
   INLINE void set_range_to(bool value, int low_bit, int size);

+ 2 - 0
panda/src/putil/config_util.cxx

@@ -32,6 +32,7 @@
 #include "namable.h"
 #include "nodeCachedReferenceCount.h"
 #include "referenceCount.h"
+#include "sparseArray.h"
 #include "typedObject.h"
 #include "typedReferenceCount.h"
 #include "typedWritable.h"
@@ -94,6 +95,7 @@ ConfigureFn(config_util) {
   Namable::init_type();
   NodeCachedReferenceCount::init_type();
   ReferenceCount::init_type();
+  SparseArray::init_type();
   TypedObject::init_type();
   TypedReferenceCount::init_type();
   TypedWritable::init_type();

+ 1 - 0
panda/src/putil/putil_composite2.cxx

@@ -13,6 +13,7 @@
 #include "pta_float.cxx"
 #include "pta_int.cxx"
 #include "pta_ushort.cxx"
+#include "sparseArray.cxx"
 #include "string_utils.cxx"
 #include "timedCycle.cxx"
 #include "typedWritable.cxx"

+ 578 - 0
panda/src/putil/sparseArray.I

@@ -0,0 +1,578 @@
+// Filename: sparseArray.I
+// Created by:  drose (14Feb07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::Constructor
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE SparseArray::
+SparseArray() : _inverse(false) {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::Copy Constructor
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE SparseArray::
+SparseArray(const SparseArray &copy) :
+  _subranges(copy._subranges),
+  _inverse(copy._inverse)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::Copy Assignment Operator
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE SparseArray &SparseArray::
+operator = (const SparseArray &copy) {
+  _subranges = copy._subranges;
+  _inverse = copy._inverse;
+  return *this;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::Named all_on constructor
+//       Access: Published, Static
+//  Description: Returns a SparseArray with an infinite array of bits,
+//               all on.
+////////////////////////////////////////////////////////////////////
+INLINE SparseArray SparseArray::
+all_on() {
+  SparseArray result;
+  result._inverse = true;
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::Named all_on constructor
+//       Access: Published, Static
+//  Description: Returns a SparseArray whose bits are all off.
+////////////////////////////////////////////////////////////////////
+INLINE SparseArray SparseArray::
+all_off() {
+  return SparseArray();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::Named lower_on constructor
+//       Access: Published, Static
+//  Description: Returns a SparseArray whose lower on_bits bits are on.
+////////////////////////////////////////////////////////////////////
+INLINE SparseArray SparseArray::
+lower_on(int on_bits) {
+  SparseArray result;
+  result.set_range(0, on_bits);
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::Named bit constructor
+//       Access: Published, Static
+//  Description: Returns a SparseArray with only the indicated bit on.
+////////////////////////////////////////////////////////////////////
+INLINE SparseArray SparseArray::
+bit(int index) {
+  SparseArray result;
+  result.set_bit(index);
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::Named range constructor
+//       Access: Published, Static
+//  Description: Returns a SparseArray whose size bits, beginning at
+//               low_bit, are on.
+////////////////////////////////////////////////////////////////////
+INLINE SparseArray SparseArray::
+range(int low_bit, int size) {
+  SparseArray result;
+  result.set_range(low_bit, size);
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::Destructor
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE SparseArray::
+~SparseArray() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::has_max_num_bits
+//       Access: Published, Static
+//  Description: Returns true if there is a maximum number of bits
+//               that may be stored in this structure, false
+//               otherwise.  If this returns true, the number may be
+//               queried in get_max_num_bits().
+//
+//               This method always returns false.  The SparseArray has
+//               no maximum number of bits.  This method is defined so
+//               generic programming algorithms can use BitMask or
+//               SparseArray interchangeably.
+////////////////////////////////////////////////////////////////////
+INLINE bool SparseArray::
+has_max_num_bits() {
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::get_max_num_bits
+//       Access: Published, Static
+//  Description: If get_max_num_bits() returned true, this method may
+//               be called to return the maximum number of bits that
+//               may be stored in this structure.  It is an error to
+//               call this if get_max_num_bits() return false.
+//
+//               It is always an error to call this method.  The
+//               SparseArray has no maximum number of bits.  This method
+//               is defined so generic programming algorithms can use
+//               BitMask or SparseArray interchangeably.
+////////////////////////////////////////////////////////////////////
+INLINE int SparseArray::
+get_max_num_bits() {
+  nassertr(false, 0);
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::get_num_bits
+//       Access: Published
+//  Description: Returns the current number of possibly different bits
+//               in this array.  There are actually an infinite number
+//               of bits, but every bit higher than this bit will have
+//               the same value, either 0 or 1 (see
+//               get_highest_bits()).
+//
+//               This number may grow and/or shrink automatically as
+//               needed.
+////////////////////////////////////////////////////////////////////
+INLINE int SparseArray::
+get_num_bits() const {
+  if (_subranges.empty()) {
+    return 0;
+  } else {
+    Subranges::const_iterator si = _subranges.begin() + _subranges.size() - 1;
+    return (*si)._end;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::get_bit
+//       Access: Published
+//  Description: Returns true if the nth bit is set, false if it is
+//               cleared.  It is valid for n to increase beyond
+//               get_num_bits(), but the return value get_num_bits()
+//               will always be the same.
+////////////////////////////////////////////////////////////////////
+INLINE bool SparseArray::
+get_bit(int index) const {
+  return has_any_of(index, 1);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::set_bit
+//       Access: Published
+//  Description: Sets the nth bit on.  If n >= get_num_bits(), this
+//               automatically extends the array.
+////////////////////////////////////////////////////////////////////
+INLINE void SparseArray::
+set_bit(int index) {
+  set_range(index, 1);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::clear_bit
+//       Access: Published
+//  Description: Sets the nth bit off.  If n >= get_num_bits(), this
+//               automatically extends the array.
+////////////////////////////////////////////////////////////////////
+INLINE void SparseArray::
+clear_bit(int index) {
+  clear_range(index, 1);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::set_bit_to
+//       Access: Published
+//  Description: Sets the nth bit either on or off, according to the
+//               indicated bool value.
+////////////////////////////////////////////////////////////////////
+INLINE void SparseArray::
+set_bit_to(int index, bool value) {
+  if (value) {
+    set_bit(index);
+  } else {
+    clear_bit(index);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::get_highest_bits
+//       Access: Published
+//  Description: Returns true if the infinite set of bits beyond
+//               get_num_bits() are all on, or false of they are all
+//               off.
+////////////////////////////////////////////////////////////////////
+INLINE bool SparseArray::
+get_highest_bits() const {
+  return _inverse;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::is_zero
+//       Access: Published
+//  Description: Returns true if the entire bitmask is zero, false
+//               otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool SparseArray::
+is_zero() const {
+  if (_inverse) {
+    return false;
+  } else {
+    return _subranges.empty();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::is_all_on
+//       Access: Published
+//  Description: Returns true if the entire bitmask is one, false
+//               otherwise.
+////////////////////////////////////////////////////////////////////
+bool SparseArray::
+is_all_on() const {
+  if (_inverse) {
+    return _subranges.empty();
+  } else {
+    return false;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::has_any_of
+//       Access: Published
+//  Description: Returns true if any bit in the indicated range is
+//               set, false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool SparseArray::
+has_any_of(int low_bit, int size) const {
+  if (_inverse) {
+    return !do_has_all(low_bit, low_bit + size);
+  } else {
+    return do_has_any(low_bit, low_bit + size);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::has_all_of
+//       Access: Published
+//  Description: Returns true if all bits in the indicated range are
+//               set, false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool SparseArray::
+has_all_of(int low_bit, int size) const {
+  if (_inverse) {
+    return !do_has_any(low_bit, low_bit + size);
+  } else {
+    return do_has_all(low_bit, low_bit + size);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::set_range
+//       Access: Published
+//  Description: Sets the indicated range of bits on.
+////////////////////////////////////////////////////////////////////
+INLINE void SparseArray::
+set_range(int low_bit, int size) {
+  if (_inverse) {
+    return do_remove_range(low_bit, low_bit + size);
+  } else {
+    return do_add_range(low_bit, low_bit + size);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::clear_range
+//       Access: Published
+//  Description: Sets the indicated range of bits off.
+////////////////////////////////////////////////////////////////////
+INLINE void SparseArray::
+clear_range(int low_bit, int size) {
+  if (_inverse) {
+    return do_add_range(low_bit, low_bit + size);
+  } else {
+    return do_remove_range(low_bit, low_bit + size);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::set_range_to
+//       Access: Published
+//  Description: Sets the indicated range of bits to either on or off.
+////////////////////////////////////////////////////////////////////
+INLINE void SparseArray::
+set_range_to(bool value, int low_bit, int size) {
+  if (value) {
+    set_range(low_bit, size);
+  } else {
+    clear_range(low_bit, size);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::invert_in_place
+//       Access: Published
+//  Description: Inverts all the bits in the SparseArray.  This is
+//               equivalent to array = ~array.
+////////////////////////////////////////////////////////////////////
+void SparseArray::
+invert_in_place() {
+  _inverse = !_inverse;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::clear
+//       Access: Published
+//  Description: Sets all the bits in the SparseArray off.
+////////////////////////////////////////////////////////////////////
+void SparseArray::
+clear() {
+  _subranges.clear();
+  _inverse = false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::operator ==
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE bool SparseArray::
+operator == (const SparseArray &other) const {
+  return compare_to(other) == 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::operator !=
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE bool SparseArray::
+operator != (const SparseArray &other) const {
+  return compare_to(other) != 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::operator <
+//       Access: Published
+//  Description: Returns true if the unsigned integer which is
+//               represented by this SparseArray is less than that of the
+//               other one, false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool SparseArray::
+operator < (const SparseArray &other) const {
+  return compare_to(other) < 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::operator &
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE SparseArray SparseArray::
+operator & (const SparseArray &other) const {
+  SparseArray result(*this);
+  result &= other;
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::operator |
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE SparseArray SparseArray::
+operator | (const SparseArray &other) const {
+  SparseArray result(*this);
+  result |= other;
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::operator ^
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE SparseArray SparseArray::
+operator ^ (const SparseArray &other) const {
+  SparseArray result(*this);
+  result ^= other;
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::operator ~
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE SparseArray SparseArray::
+operator ~ () const {
+  SparseArray result(*this);
+  result.invert_in_place();
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::operator <<
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE SparseArray SparseArray::
+operator << (int shift) const {
+  SparseArray result(*this);
+  result <<= shift;
+  return result;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::operator >>
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE SparseArray SparseArray::
+operator >> (int shift) const {
+  SparseArray result(*this);
+  result >>= shift;
+  return result;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::operator <<=
+//       Access: Published
+//  Description: Logical left shift.  Since negative bit positions
+//               have meaning in a SparseArray, real bit values are
+//               rotated in on the left (not necessarily zero).
+////////////////////////////////////////////////////////////////////
+void SparseArray::
+operator <<= (int shift) {
+  do_shift(shift);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::operator >>=
+//       Access: Published
+//  Description: Logical right shift.  The rightmost bits become
+//               negative, but are not lost; they will reappear into
+//               the zero position if the array is later left-shifted.
+////////////////////////////////////////////////////////////////////
+void SparseArray::
+operator >>= (int shift) {
+  do_shift(-shift);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::is_inverse
+//       Access: Published
+//  Description: If this is true, the SparseArray is actually defined
+//               as a list of subranges of integers that are *not* in
+//               the set.  If this is false (the default), then the
+//               subranges define the integers that *are* in the set.
+//               This affects the interpretation of the values
+//               returned by iterating through get_num_subranges().
+////////////////////////////////////////////////////////////////////
+INLINE bool SparseArray::
+is_inverse() const {
+  return _inverse;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::get_num_subranges
+//       Access: Published
+//  Description: Returns the number of separate subranges stored in
+//               the SparseArray.  You can use this limit to iterate
+//               through the subranges, calling get_subrange_begin()
+//               and get_subrange_end() for each one.
+//
+//               Also see is_inverse().
+////////////////////////////////////////////////////////////////////
+INLINE int SparseArray::
+get_num_subranges() const {
+  return _subranges.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::get_subrange_begin
+//       Access: Published
+//  Description: Returns the first numeric element in the nth
+//               subrange.
+//
+//               Also see is_inverse().
+////////////////////////////////////////////////////////////////////
+INLINE int SparseArray::
+get_subrange_begin(int n) const {
+  nassertr(n >= 0 && n < (int)_subranges.size(), 0);
+  return _subranges[n]._begin;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::get_subrange_end
+//       Access: Published
+//  Description: Returns the last numeric element, plus one, in the
+//               nth subrange.
+//
+//               Also see is_inverse().
+////////////////////////////////////////////////////////////////////
+INLINE int SparseArray::
+get_subrange_end(int n) const {
+  nassertr(n >= 0 && n < (int)_subranges.size(), 0);
+  return _subranges[n]._end;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::Subrange::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE SparseArray::Subrange::
+Subrange(int begin, int end) :
+  _begin(begin),
+  _end(end)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::Subrange::operator <
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE bool SparseArray::Subrange::
+operator < (const SparseArray::Subrange &other) const {
+  // We compare the end values, rather than the begin values, to make
+  // lower_bound() sensibly return a possible intersection with the
+  // indicated Subrange.
+  return _end < other._end;
+}
+

+ 517 - 0
panda/src/putil/sparseArray.cxx

@@ -0,0 +1,517 @@
+// Filename: sparseArray.cxx
+// Created by:  drose (14Feb07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "sparseArray.h"
+#include "bitArray.h"
+
+TypeHandle SparseArray::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::Constructor (from BitArray)
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+SparseArray::
+SparseArray(const BitArray &from) {
+  bool empty_bit = from.get_highest_bits();
+  _inverse = empty_bit;
+
+  int begin = 0;
+  bool current_state = from.get_bit(0);
+  int i = 0;
+
+  // By including get_num_bits()--one more than the last bit--in this
+  // traversal, we guarantee that we will end on the empty_bit state
+  // (because the last bit we visit will be one of the highest_bits).
+  while (i <= from.get_num_bits()) {
+    if (from.get_bit(i) != current_state) {
+      // End of a run.
+      if (current_state != empty_bit) {
+        Subrange range(begin, i);
+        _subranges.push_back(range);
+      }
+      begin = i;
+      current_state = !current_state;
+    }
+    ++i;
+  }
+
+  nassertv(current_state == empty_bit);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::has_bits_in_common
+//       Access: Published
+//  Description: Returns true if this SparseArray has any "one" bits in
+//               common with the other one, false otherwise.
+//
+//               This is equivalent to (array & other) != 0, but may
+//               be faster.
+////////////////////////////////////////////////////////////////////
+bool SparseArray::
+has_bits_in_common(const SparseArray &other) const {
+  if (_inverse && other._inverse) {
+    // Yup, in fact we have an infinite number of bits in common.
+    return true;
+  }
+
+  if (_inverse != other._inverse) {
+    // We'll handle this tricky case the lazy way.
+    return !(*this & other).is_zero();
+  }
+
+  // Actually, we'll handle this easy case the lazy way too.  Maybe
+  // later we'll do this smarter.
+  return !(*this & other).is_zero();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::output
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void SparseArray::
+output(ostream &out) const {
+  out << "[ ";
+  if (_inverse) {
+    out << "all except: ";
+  }
+  Subranges::const_iterator si;
+  for (si = _subranges.begin(); si != _subranges.end(); ++si) {
+    if ((*si)._end == (*si)._begin + 1) {
+      // A single element.
+      out << (*si)._begin << ", ";
+    } else {
+      // A range of elements.
+      out << (*si)._begin << "-" << ((*si)._end - 1) << ", ";
+    }
+  }
+  out << "]";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::compare_to
+//       Access: Published
+//  Description: Returns a number less than zero if this SparseArray
+//               sorts before the indicated other SparseArray, greater
+//               than zero if it sorts after, or 0 if they are
+//               equivalent.  This is based on the same ordering
+//               defined by operator <.
+////////////////////////////////////////////////////////////////////
+int SparseArray::
+compare_to(const SparseArray &other) const {
+  if (_inverse != other._inverse) {
+    return _inverse ? 1 : -1;
+  }
+
+  Subranges::const_reverse_iterator ai = _subranges.rbegin();
+  Subranges::const_reverse_iterator bi = other._subranges.rbegin();
+
+  while (ai != _subranges.rend() && bi != other._subranges.rend()) {
+    if ((*ai)._end < (*bi)._end) {
+      // B is higher.
+      return -1;
+    } else if ((*bi)._end < (*ai)._end) {
+      // A is higher.
+      return 1;
+    } else if ((*ai)._begin < (*bi)._begin) {
+      // A is higher.
+      return 1;
+    } else if ((*bi)._begin < (*ai)._begin) {
+      // B is higher.
+      return -1;
+    }
+
+    --ai;
+    --bi;
+  }
+
+  if (ai != _subranges.rend()) {
+    // A is higher.
+    return 1;
+  }
+  if (bi != other._subranges.rend()) {
+    // B is higher.
+    return -1;
+  }
+
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::operator &=
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+void SparseArray::
+operator &= (const SparseArray &other) {
+  // We do this the slow and stupid way.  This could be done much
+  // better with a little effort, but I'm not at all sure it's worth
+  // the effort.  If you need fast boolean operations, you should
+  // probably be using a BitArray.
+
+  if (_inverse && other._inverse) {
+    do_union(other);
+
+  } else if (!_inverse && !other._inverse) {
+    do_intersection(other);
+
+  } else if (_inverse && !other._inverse) {
+    // a & b == b & a
+    (*this) = other & (*this);
+
+  } else if (!_inverse && other._inverse) {
+    do_intersection_neg(other);
+
+  } else {
+    // TODO.
+    nassertv(false);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::operator |=
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+void SparseArray::
+operator |= (const SparseArray &other) {
+  // We do this the slow and stupid way.  This could be done much
+  // better with a little effort, but I'm not at all sure it's worth
+  // the effort.  If you need fast boolean operations, you should
+  // probably be using a BitArray.
+
+  if (_inverse && other._inverse) {
+    do_intersection(other);
+
+  } else if (!_inverse && !other._inverse) {
+    do_union(other);
+
+  } else if (_inverse && !other._inverse) {
+    do_intersection_neg(other);
+
+  } else if (!_inverse && other._inverse) {
+    // a | b == b | a
+    (*this) = other | (*this);
+
+  } else {
+    nassertv(false);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::operator ^=
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+void SparseArray::
+operator ^= (const SparseArray &other) {
+  // We do this the slow and stupid way.  This could be done much
+  // better with a little effort, but I'm not at all sure it's worth
+  // the effort.  If you need fast boolean operations, you should
+  // probably be using a BitArray.
+
+  (*this) = ((*this) | other) & ~((*this) & other);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::do_add_range
+//       Access: Private
+//  Description: Adds the consecutive range of integers beginning at
+//               begin, but not including end, to the array.  If this
+//               range overlaps with another range already in the
+//               array, the result is the union.
+////////////////////////////////////////////////////////////////////
+void SparseArray::
+do_add_range(int begin, int end) {
+  if (begin >= end) {
+    // Empty range.
+    return;
+  }
+
+  Subrange range(begin, end);
+  Subranges::iterator si = _subranges.lower_bound(range);
+  if (si == _subranges.end()) {
+    if (!_subranges.empty()) {
+      si = _subranges.begin() + _subranges.size() - 1;
+      if ((*si)._end >= begin) {
+        // The new range expands the last element of the array to the right.
+        (*si)._end = end;
+        // It might also expand it to the left; fall through.
+      } else {
+        // The new range is completely after the last element of the array.
+        _subranges.push_back(range);
+        return;
+      }
+
+    } else {
+      // The new range is completely after the last element of the array.
+      _subranges.push_back(range);
+      return;
+    }
+  }
+
+  nassertv((*si)._end >= end);
+
+  if ((*si)._begin > end) {
+    if (si != _subranges.begin()) {
+      Subranges::iterator si2 = si;
+      --si2;
+      if ((*si2)._end >= begin) {
+        // The new range expands an element within the array to the
+        // right (but does not intersect the next element).
+        (*si2)._end = end;
+        // It might also expand it to the left; fall through.
+        si = si2;
+      } else {
+        // The new range does not intersect any elements in the array.
+        _subranges.insert_unverified(si, range);
+        return;
+      }
+    } else {
+      // The new range does not intersect any elements in the array.
+      _subranges.insert_unverified(si, range);
+      return;
+    }
+  }
+
+  // Check if the new range overlaps with any elements to the left.
+  while (si != _subranges.begin()) {
+    Subranges::iterator si2 = si;
+    --si2;
+    if ((*si2)._end >= begin) {
+      // The new range straddles two elements, so they get combined.
+      (*si2)._end = (*si)._end;
+      _subranges.erase(si);
+    }
+    si = si2;
+  }
+
+  if ((*si)._begin > begin) {
+    // The new range expands an element to the left.
+    (*si)._begin = begin;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::do_remove_range
+//       Access: Private
+//  Description: Removes the consecutive range of integers beginning
+//               at begin, but not including end, from the array.
+////////////////////////////////////////////////////////////////////
+void SparseArray::
+do_remove_range(int begin, int end) {
+  if (begin >= end) {
+    // Empty range.
+    return;
+  }
+
+  Subrange range(begin, end);
+  Subranges::iterator si = _subranges.lower_bound(range);
+  if (si == _subranges.end()) {
+    if (!_subranges.empty()) {
+      si = _subranges.begin() + _subranges.size() - 1;
+      if ((*si)._end >= begin) {
+        // The new range shortens the last element of the array on the right.
+        end = min(end, (*si)._begin);
+        (*si)._end = end;
+        // It might also shorten it on the left; fall through.
+      } else {
+        // The new range is completely after the last element of the array.
+        return;
+      }
+
+    } else {
+      // The new range is completely after the last element of the array.
+      return;
+    }
+  }
+
+  nassertv((*si)._end >= end);
+
+  if ((*si)._begin > end) {
+    if (si != _subranges.begin()) {
+      Subranges::iterator si2 = si;
+      --si2;
+      if ((*si2)._end >= begin) {
+        // The new range shortens an element within the array on the
+        // right (but does not intersect the next element).
+        end = min(end, (*si2)._begin);
+        (*si2)._end = end;
+        // It might also shorten it on the left; fall through.
+        si = si2;
+      } else {
+        // The new range does not intersect any elements in the array.
+        return;
+      }
+    } else {
+      // The new range does not intersect any elements in the array.
+      return;
+    }
+  }
+
+
+  if (end < (*si)._end) {
+    // We must split an element into two.
+    Subrange left_range((*si)._begin, begin);
+    (*si)._begin = end;
+    si = _subranges.insert_unverified(si, left_range);
+  }
+
+  // Check if the new range removes any elements to the left.
+  while (begin <= (*si)._begin) {
+    if (si == _subranges.begin()) {
+      _subranges.erase(si);
+      return;
+    }
+    Subranges::iterator si2 = si;
+    --si2;
+    _subranges.erase(si);
+    si = si2;
+  }
+
+  (*si)._end = min((*si)._end, begin);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::do_has_any
+//       Access: Private
+//  Description: Returns true if any of the consecutive range of
+//               integers beginning at begin, but not including end,
+//               appear in the array.  Note that this will return
+//               false for an empty range.
+////////////////////////////////////////////////////////////////////
+bool SparseArray::
+do_has_any(int begin, int end) const {
+  if (begin >= end) {
+    // Empty range.
+    return false;
+  }
+
+  Subrange range(begin, end);
+  Subranges::const_iterator si = _subranges.lower_bound(range);
+  if (si != _subranges.end() && end > (*si)._begin) {
+    return true;
+  }
+  if (si != _subranges.begin()) {
+    --si;
+    if (begin < (*si)._end) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::do_has_all
+//       Access: Private
+//  Description: Returns true if all of the consecutive range of
+//               integers beginning at begin, but not including end,
+//               appear in the array.  Note that this will return
+//               true for an empty range.
+////////////////////////////////////////////////////////////////////
+bool SparseArray::
+do_has_all(int begin, int end) const {
+  if (begin >= end) {
+    // Empty range.
+    return true;
+  }
+
+  Subrange range(begin, end);
+  Subranges::const_iterator si = _subranges.lower_bound(range);
+  if (si != _subranges.end() && begin >= (*si)._begin) {
+    return true;
+  }
+
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::do_intersection
+//       Access: Private
+//  Description: Removes from this array all of the elements that do
+//               not appear in the other one.
+////////////////////////////////////////////////////////////////////
+void SparseArray::
+do_intersection(const SparseArray &other) {
+  if (_subranges.empty()) {
+    return;
+  }
+  if (other._subranges.empty()) {
+    _subranges.clear();
+    return;
+  }
+
+  int my_begin = (*_subranges.begin())._begin;
+  int other_begin = (*other._subranges.begin())._begin;
+  do_remove_range(my_begin, other_begin);
+
+  for (size_t i = 0; i < other._subranges.size() - 1; ++i) {
+    do_remove_range(other._subranges[i]._end, other._subranges[i + 1]._begin);
+  }
+
+  int my_end = (*(_subranges.begin() + _subranges.size() - 1))._end;
+  int other_end = (*(other._subranges.begin() + other._subranges.size() - 1))._end;
+  do_remove_range(other_end, my_end);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::do_union
+//       Access: Private
+//  Description: Adds to this array all of the elements that also
+//               appear in the other one.
+////////////////////////////////////////////////////////////////////
+void SparseArray::
+do_union(const SparseArray &other) {
+  Subranges::const_iterator si;
+  for (si = other._subranges.begin(); si != other._subranges.end(); ++si) {
+    do_add_range((*si)._begin, (*si)._end);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::do_intersection_neg
+//       Access: Private
+//  Description: Removes from this array all of the elements that also
+//               appear in the other one.
+////////////////////////////////////////////////////////////////////
+void SparseArray::
+do_intersection_neg(const SparseArray &other) {
+  Subranges::const_iterator si;
+  for (si = other._subranges.begin(); si != other._subranges.end(); ++si) {
+    do_remove_range((*si)._begin, (*si)._end);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SparseArray::do_shift
+//       Access: Private
+//  Description: Shifts all the elements in the array by the indicated
+//               amount.
+////////////////////////////////////////////////////////////////////
+void SparseArray::
+do_shift(int offset) {
+  if (offset != 0) {
+    Subranges::iterator si;
+    for (si = _subranges.begin(); si != _subranges.end(); ++si) {
+      (*si)._begin += offset;
+      (*si)._end += offset;
+    }
+  }
+}

+ 168 - 0
panda/src/putil/sparseArray.h

@@ -0,0 +1,168 @@
+// Filename: sparseArray.h
+// Created by:  drose (14Feb07)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef SPARSEARRAY_H
+#define SPARSEARRAY_H
+
+#include "pandabase.h"
+#include "ordered_vector.h"
+
+class BitArray;
+
+////////////////////////////////////////////////////////////////////
+//       Class : SparseArray
+// Description : This class records a set of integers, where each
+//               integer is either present or not present in the set.
+//
+//               It is similar in principle and in interface to a
+//               BitArray (which can be thought of as a set of
+//               integers, one integer corresponding to each different
+//               bit position), but the SparseArray is implemented as
+//               a list of min/max subrange lists, rather than as a
+//               bitmask.  
+//
+//               This makes it particularly efficient for storing sets
+//               which consist of large sections of consecutively
+//               included or consecutively excluded elements, with
+//               arbitrarily large integers, but particularly
+//               inefficient for doing boolean operations such as & or
+//               |.
+//
+//               Also, unlike BitArray, the SparseArray can store
+//               negative integers.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA SparseArray {
+PUBLISHED:
+  INLINE SparseArray();
+  INLINE SparseArray(const SparseArray &copy);
+  INLINE SparseArray &operator = (const SparseArray &copy);
+  SparseArray(const BitArray &from);
+
+  INLINE static SparseArray all_on();
+  INLINE static SparseArray all_off();
+  INLINE static SparseArray lower_on(int on_bits);
+  INLINE static SparseArray bit(int index);
+  INLINE static SparseArray range(int low_bit, int size);
+
+  INLINE ~SparseArray();
+
+  INLINE static bool has_max_num_bits();
+  INLINE static int get_max_num_bits();
+
+  INLINE int get_num_bits() const;
+  INLINE bool get_bit(int index) const;
+  INLINE void set_bit(int index);
+  INLINE void clear_bit(int index);
+  INLINE void set_bit_to(int index, bool value);
+  INLINE bool get_highest_bits() const;
+  INLINE bool is_zero() const;
+  INLINE bool is_all_on() const;
+
+  INLINE bool has_any_of(int low_bit, int size) const;
+  INLINE bool has_all_of(int low_bit, int size) const;
+  INLINE void set_range(int low_bit, int size);
+  INLINE void clear_range(int low_bit, int size);
+  INLINE void set_range_to(bool value, int low_bit, int size);
+
+  INLINE void invert_in_place();
+  bool has_bits_in_common(const SparseArray &other) const;
+  INLINE void clear();
+
+  void output(ostream &out) const;
+
+  INLINE bool operator == (const SparseArray &other) const;
+  INLINE bool operator != (const SparseArray &other) const;
+  INLINE bool operator < (const SparseArray &other) const;
+  int compare_to(const SparseArray &other) const;
+
+  INLINE SparseArray
+  operator & (const SparseArray &other) const;
+
+  INLINE SparseArray
+  operator | (const SparseArray &other) const;
+
+  INLINE SparseArray
+  operator ^ (const SparseArray &other) const;
+
+  INLINE SparseArray
+  operator ~ () const;
+
+  INLINE SparseArray
+  operator << (int shift) const;
+
+  INLINE SparseArray
+  operator >> (int shift) const;
+
+  void operator &= (const SparseArray &other);
+  void operator |= (const SparseArray &other);
+  void operator ^= (const SparseArray &other);
+  INLINE void operator <<= (int shift);
+  INLINE void operator >>= (int shift);
+
+  INLINE bool is_inverse() const;
+  INLINE int get_num_subranges() const;
+  INLINE int get_subrange_begin(int n) const;
+  INLINE int get_subrange_end(int n) const;
+
+private:
+  void do_add_range(int begin, int end);
+  void do_remove_range(int begin, int end);
+  bool do_has_any(int begin, int end) const;
+  bool do_has_all(int begin, int end) const;
+
+  void do_intersection(const SparseArray &other);
+  void do_union(const SparseArray &other);
+  void do_intersection_neg(const SparseArray &other);
+  void do_shift(int offset);
+
+  // The SparseArray is implemented as a set of non-overlapping
+  // Subranges.
+  class Subrange {
+  public:
+    INLINE Subrange(int begin, int end);
+    INLINE bool operator < (const Subrange &other) const;
+
+    int _begin, _end;
+  };
+
+  typedef ov_set<Subrange> Subranges;
+  Subranges _subranges;
+  bool _inverse;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    register_type(_type_handle, "SparseArray");
+  }
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "sparseArray.I"
+
+INLINE ostream &
+operator << (ostream &out, const SparseArray &array) {
+  array.output(out);
+  return out;
+}
+
+#endif
+