home *** CD-ROM | disk | FTP | other *** search
- //=============================================================================
- //
- // Copyright (C) 1995 by Paul S. McCarthy and Eric Sunshine.
- // Written by Paul S. McCarthy and Eric Sunshine.
- // All Rights Reserved.
- //
- // This notice may not be removed from this source code.
- //
- // This object is included in the MiscKit by permission from the authors
- // and its use is governed by the MiscKit license, found in the file
- // "License.rtf" in the MiscKit distribution. Please refer to that file
- // for a list of all applicable permissions and restrictions.
- //
- //=============================================================================
- //-----------------------------------------------------------------------------
- // MiscSparseSet.cc
- //
- // This object impelements a sparse set. The set is represented by an
- // array of ranges kept in sorted ascending order.
- //
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- // $Id: MiscSparseSet.cc,v 1.1 95/09/27 12:21:21 zarnuk Exp $
- // $Log: MiscSparseSet.cc,v $
- // Revision 1.1 95/09/27 12:21:21 zarnuk
- // Initial revision
- //
- //-----------------------------------------------------------------------------
- #ifdef __GNUC__
- # pragma implementation
- #endif
- #import "MiscSparseSet.h"
-
- extern "C" {
- #import <assert.h>
- #import <stdlib.h>
- #import <string.h>
- }
-
- unsigned int const INITIAL_CAPACITY = 16;
-
-
- //-----------------------------------------------------------------------------
- // sort
- //-----------------------------------------------------------------------------
- static inline void sort( int& low, int& high )
- {
- if (high < low) { int t = low; low = high; high = t; }
- }
-
-
- //=============================================================================
- // IMPLEMENTATION
- //=============================================================================
- //-----------------------------------------------------------------------------
- // expand(uint)
- //-----------------------------------------------------------------------------
- void MiscSparseSet::expand( unsigned int new_capacity )
- {
- if (new_capacity > capacity)
- {
- capacity = new_capacity;
- int const BYTES = capacity * sizeof(*array);
- if (array == 0)
- array = (Range*) malloc( BYTES );
- else
- array = (Range*) realloc( array, BYTES );
- }
- }
-
-
- //-----------------------------------------------------------------------------
- // expand
- //-----------------------------------------------------------------------------
- void MiscSparseSet::expand()
- {
- if (lim >= capacity)
- expand( capacity == 0 ? INITIAL_CAPACITY : capacity << 1 );
- }
-
-
- //-----------------------------------------------------------------------------
- // bsearch
- //-----------------------------------------------------------------------------
- int MiscSparseSet::bsearch( int x ) const
- {
- int lo = 0;
- int hi = numRanges() - 1;
- while (lo <= hi)
- {
- int const mid = (lo + hi) >> 1;
- Range const& r = array[mid];
- if (x > r.max_val)
- lo = mid + 1;
- else if (x < r.min_val)
- hi = mid - 1;
- else
- return mid;
- }
- return ~lo;
- }
-
-
- //-----------------------------------------------------------------------------
- // fixCursor
- //
- // If cursor is out of bounds, make it INVALID. If cursor points to a
- // slot not in this set then adjust it to point to a valid slot.
- //-----------------------------------------------------------------------------
- void MiscSparseSet::fixCursor()
- {
- if (lim == 0 || cursor < 0)
- {
- clearCursor();
- }
- else
- {
- int i = bsearch( cursor );
- if (i < 0)
- {
- i = ~i;
- cursor = (i > 0 ? array[i - 1].max_val : array[i].min_val);
- }
- }
- }
-
-
- //-----------------------------------------------------------------------------
- // insertAt
- //-----------------------------------------------------------------------------
- void MiscSparseSet::insertAt( unsigned int x, unsigned int lo,
- unsigned int hi )
- {
- assert( x <= lim );
- expand();
- if (x < lim)
- memmove( array + x + 1, array + x, (lim - x) * sizeof(*array) );
- lim++;
- array[ x ].min_val = lo;
- array[ x ].max_val = hi;
- }
-
-
- //-----------------------------------------------------------------------------
- // deleteAt
- //-----------------------------------------------------------------------------
- void MiscSparseSet::deleteAt( unsigned int x, unsigned int slots )
- {
- assert( x < lim );
- assert( slots > 0 );
- int const s = lim - (x + slots);
- assert( s >= 0 );
- lim -= slots;
- if (x < lim)
- memmove( array + x, array + x + slots, s * sizeof(*array));
- }
-
-
- //-----------------------------------------------------------------------------
- // Constructor [Copy]
- //-----------------------------------------------------------------------------
- MiscSparseSet::MiscSparseSet( MiscSparseSet const& s ) :
- lim(s.lim), capacity(s.lim), array(0), cursor(INVALID)
- {
- if (capacity > 0)
- {
- int const BYTES = capacity * sizeof(*array);
- array = (Range*) malloc( BYTES );
- memcpy( array, s.array, BYTES );
- }
- fixCursor();
- }
-
-
- //-----------------------------------------------------------------------------
- // Destructor
- //-----------------------------------------------------------------------------
- MiscSparseSet::~MiscSparseSet()
- {
- if (array != 0)
- free( array );
- }
-
-
- //-----------------------------------------------------------------------------
- // operator=
- //-----------------------------------------------------------------------------
- MiscSparseSet& MiscSparseSet::operator=( MiscSparseSet const& s )
- {
- if (&s != this)
- {
- if (s.isEmpty())
- {
- empty();
- }
- else
- {
- expand( s.lim );
- lim = s.lim;
- memcpy( array, s.array, lim * sizeof(*array) );
- cursor = s.cursor;
- fixCursor();
- }
- }
- return *this;
- }
-
-
- //-----------------------------------------------------------------------------
- // operator==
- //-----------------------------------------------------------------------------
- bool MiscSparseSet::operator==( MiscSparseSet const& s ) const
- {
- return (&s == this || (lim == s.lim && (lim == 0 ||
- memcmp( array, s.array, lim * sizeof(*array) ) == 0)));
- }
-
-
- //-----------------------------------------------------------------------------
- // merge
- //
- // Cases when merging coords into adjacent ranges:
- //
- // CASE
- // *1* If x is the only coord between two ranges then expand range 1 to
- // encompass all, and delete range 2.
- // *2* If x is directly adjacent to the range preceeding it then expand
- // that range to encompass x.
- // *3* If x is directly adjacent to the range following it then expand
- // that range to encompass x.
- //-----------------------------------------------------------------------------
- int MiscSparseSet::merge( int x, unsigned int at )
- {
- assert( at <= lim );
- assert( at == lim || array[at].min_val > x );
- assert( at == 0 || x > array[at - 1].max_val );
- int ret = ~at;
- if (at > 0 && at < lim &&
- x - 1 == array[at - 1].max_val && x + 1 == array[at].min_val) // *1*
- {
- ret = at - 1;
- array[ret].max_val = array[at].max_val;
- deleteAt( at, 1 );
- }
- else if (at > 0 && x - 1 == array[at - 1].max_val) // *2*
- {
- ret = at - 1;
- array[ret].max_val = x;
- }
- else if (at < lim && x + 1 == array[at].min_val) // *3*
- {
- ret = at;
- array[ret].min_val = x;
- }
- return ret;
- }
-
-
- //-----------------------------------------------------------------------------
- // add
- // Cases after merging coords into existing adjacent ranges if possible:
- //
- // CASE
- // *1* Both in same new range
- // ->> insert new range
- // *2* Both in different new ranges
- // ->> extend low range, purge excess high ranges
- // *3* High in new range only
- // ->> extend low range, purge excess high ranges
- // *4* Low in new range only
- // ->> extend high range, purge excess low ranges
- // *5* Both in different existing ranges
- // ->> extend low range, purge excess high ranges
- // *6* Both in same existing range
- // ->> we're done!
- //
- //-----------------------------------------------------------------------------
- void MiscSparseSet::add( int low, int high )
- {
- sort( low, high );
- int rLow = bsearch( low );
- if (rLow < 0) rLow = merge( low, ~rLow );
- int rHigh = bsearch( high );
- if (rHigh < 0) rHigh = merge( high, ~rHigh );
-
- if (rLow < 0 && rLow == rHigh) // *1*
- {
- insertAt( ~rLow, low, high );
- }
- else if (rLow < 0 && rHigh < 0) // *2*
- {
- array[ ~rLow ].min_val = low;
- array[ ~rLow ].max_val = high;
- if (~rHigh - ~rLow > 1)
- deleteAt( ~rLow + 1, ~rHigh - ~rLow - 1 );
- }
- else if (rHigh < 0) // *3*
- {
- array[ rLow ].max_val = high;
- if (~rHigh - rLow > 1)
- deleteAt( rLow + 1, ~rHigh - rLow - 1);
- }
- else if (rLow < 0) // *4*
- {
- array[ rHigh ].min_val = low;
- if (~rLow < rHigh)
- deleteAt( ~rLow, rHigh - ~rLow );
- }
- else if (rLow < rHigh) // *5*
- {
- array[ rLow ].max_val = high;
- deleteAt( rLow + 1, rHigh - rLow );
- } // *6*
- setCursor( high );
- }
-
-
- //-----------------------------------------------------------------------------
- // slice
- // Four cases when slicing coords off of ranges:
- //
- // CASE
- // *1* If x is the only coord in the range then purge the range.
- // *2* If x is the min_val of the range then increment min_val.
- // *3* If x is the max_val of the range then decrement max_val.
- // *4* If x is not at an extreme of the range, leave it alone.
- //-----------------------------------------------------------------------------
- int MiscSparseSet::slice( int x, unsigned int at )
- {
- assert( at < lim );
- assert( array[at].min_val <= x );
- assert( x <= array[at].max_val );
- int ret = at;
- if (x == array[at].min_val && x == array[at].max_val) // *1*
- {
- ret = ~at;
- deleteAt( at, 1 );
- }
- else if (x == array[at].min_val) // *2*
- {
- ret = ~at;
- array[at].min_val++;
- }
- else if (x == array[at].max_val) // *3*
- {
- ret = ~(at + 1);
- array[at].max_val--;
- } // *4*
- return ret;
- }
-
-
- //-----------------------------------------------------------------------------
- // remove
- // Cases after slicing extremity coords off of ranges if possible:
- //
- // CASE
- // *1* Both in same range
- // ->> split into two ranges, slice max & min
- // *2* Both in different ranges
- // ->> slice max & min, purge excess mid ranges
- // *3* Low in range only
- // ->> slice max, purge excess high ranges
- // *4* High in range only
- // ->> slice min, purge excess low ranges
- // *5* Both outside of different ranges
- // ->> purge excess mid ranges
- // *6* Both outside of same range
- // ->> we're done!
- //-----------------------------------------------------------------------------
- void MiscSparseSet::remove( int low, int high )
- {
- sort( low, high );
- int rLow = bsearch( low );
- if (rLow >= 0) rLow = slice( low, rLow );
- int rHigh = bsearch( high );
- if (rHigh >= 0) rHigh = slice( high, rHigh );
-
- if (rLow >= 0 && rLow == rHigh) // *1*
- {
- insertAt( rLow + 1, high + 1, array[ rLow ].max_val );
- array[ rLow ].max_val = low - 1;
- }
- else if (rLow >= 0 && rHigh >= 0) // *2*
- {
- array[ rLow ].max_val = low - 1;
- array[ rHigh ].min_val = high + 1;
- if (rHigh - rLow > 1)
- deleteAt( rLow + 1, rHigh - rLow - 1 );
- }
- else if (rLow >= 0) // *3*
- {
- array[ rLow ].max_val = low - 1;
- if (~rHigh - rLow > 1)
- deleteAt( rLow + 1, ~rHigh - rLow -1 );
- }
- else if (rHigh >= 0) // *4*
- {
- array[ rHigh ].min_val = high + 1;
- if (rHigh > ~rLow)
- deleteAt( ~rLow, rHigh - ~rLow );
- }
- else if (~rLow < ~rHigh) // *5*
- {
- deleteAt( ~rLow, ~rHigh - ~rLow );
- } // *6*
- fixCursor();
- }
-
-
- //-----------------------------------------------------------------------------
- // toggle
- //-----------------------------------------------------------------------------
- void MiscSparseSet::toggle( int x )
- {
- if (contains( x ))
- remove( x );
- else
- add( x );
- setCursor( x );
- }
-
-
- //-----------------------------------------------------------------------------
- // shiftUpAt
- //-----------------------------------------------------------------------------
- void MiscSparseSet::shiftUpAt( int x )
- {
- for (unsigned int i = lim; i-- > 0; )
- {
- Range& r = array[i];
- if (r.max_val >= x)
- {
- r.max_val++;
- if (r.min_val >= x)
- r.min_val++;
- }
- else
- break;
- }
-
- if (cursor >= x)
- cursor++;
-
- if (contains(x))
- remove(x); // Newly inserted slots are not selected.
- }
-
-
- //-----------------------------------------------------------------------------
- // shiftDownAt
- //-----------------------------------------------------------------------------
- void MiscSparseSet::shiftDownAt( int x )
- {
- if (contains(x))
- remove(x);
-
- for (unsigned int i = lim; i-- > 0; )
- {
- Range& r = array[i];
- if (r.max_val > x)
- {
- r.max_val--;
- if (r.min_val > x)
- r.min_val--;
- }
- else
- break;
- }
-
- if (cursor > x)
- {
- cursor--;
- fixCursor();
- }
- }
-
-
- //-----------------------------------------------------------------------------
- // count
- //-----------------------------------------------------------------------------
- unsigned int MiscSparseSet::count() const
- {
- unsigned int total = 0;
- for (int i = lim; i-- > 0; )
- total += (array[i].max_val - array[i].min_val + 1);
- return total;
- }
-
-
- //-----------------------------------------------------------------------------
- // getRangeAt
- //-----------------------------------------------------------------------------
- void MiscSparseSet::getRangeAt( unsigned int i,
- int& lo, int& hi ) const
- {
- lo = hi = INVALID;
- if (i < lim)
- {
- lo = array[i].min_val;
- hi = array[i].max_val;
- }
- }
-
-
- //-----------------------------------------------------------------------------
- // getTotalRange
- //-----------------------------------------------------------------------------
- void MiscSparseSet::getTotalRange( int& lo, int& hi ) const
- {
- lo = hi = INVALID;
- if (lim > 0)
- {
- lo = array[0].min_val;
- hi = array[lim].max_val;
- }
- }
-