root/trunk/dep/include/g3dlite/G3D/Array.h @ 2

Revision 2, 33.0 kB (checked in by yumileroy, 17 years ago)

[svn] * Proper SVN structure

Original author: Neo2003
Date: 2008-10-02 16:23:55-05:00

Line 
1/**
2  @file Array.h
3 
4  @maintainer Morgan McGuire, graphics3d.com
5  @cite Portions written by Aaron Orenstein, a@orenstein.name
6 
7  @created 2001-03-11
8  @edited  2007-05-12
9
10  Copyright 2000-2007, Morgan McGuire.
11  All rights reserved.
12 */
13
14#ifndef G3D_ARRAY_H
15#define G3D_ARRAY_H
16
17#include "G3D/platform.h"
18#include "G3D/debug.h"
19#include "G3D/System.h"
20#ifdef G3D_DEBUG
21//   For formatting error messages
22#    include "G3D/format.h"
23#endif
24#include <vector>
25#include <algorithm>
26
27#ifdef G3D_WIN32
28#   include <new>
29   
30#   pragma warning (push)
31    // debug information too long
32#   pragma warning( disable : 4312)
33#   pragma warning( disable : 4786)
34#endif
35
36
37namespace G3D {
38
39/**
40 Constant for passing to Array::resize
41 */
42const bool DONT_SHRINK_UNDERLYING_ARRAY = false;
43
44/** Constant for Array::sort */
45const int SORT_INCREASING = 1;
46/** Constant for Array::sort */
47const int SORT_DECREASING = -1;
48
49/**
50 Dynamic 1D array. 
51
52 Objects must have a default constructor (constructor that
53 takes no arguments) in order to be used with this template.
54 You will get the error "no appropriate default constructor found"
55 if they do not.
56
57 Do not use with objects that overload placement <code>operator new</code>,
58 since the speed of Array is partly due to pooled allocation.
59
60 If SSE is defined Arrays allocate the first element aligned to
61 16 bytes.
62
63
64 Array is highly optimized compared to std::vector. 
65 Array operations are less expensive than on std::vector and for large
66 amounts of data, Array consumes only 1.5x the total size of the
67 data, while std::vector consumes 2.0x.  The default
68 array takes up zero heap space.  The first resize (or append)
69 operation grows it to a reasonable internal size so it is efficient
70 to append to small arrays.  Memory is allocated using
71 System::alignedMalloc, which produces pointers aligned to 16-byte
72 boundaries for use with SSE instructions and uses pooled storage for
73 fast allocation.  When Array needs to copy
74 data internally on a resize operation it correctly invokes copy
75 constructors of the elements (the MSVC6 implementation of
76 std::vector uses realloc, which can create memory leaks for classes
77 containing references and pointers).  Array provides a guaranteed
78 safe way to access the underlying data as a flat C array --
79 Array::getCArray.  Although (T*)std::vector::begin() can be used for
80 this purpose, it is not guaranteed to succeed on all platforms.
81
82 To serialize an array, see G3D::serialize.
83
84 Do not subclass an Array.
85 */
86template <class T>
87class Array {
88private:
89    /** 0...num-1 are initialized elements, num...numAllocated-1 are not */
90    T*              data;
91
92    int             num;
93    int             numAllocated;
94
95    void init(int n, int a) {
96        debugAssert(n <= a);
97        debugAssert(n >= 0);
98        this->num = 0;
99        this->numAllocated = 0;
100        data = NULL;
101        if (a > 0) {
102            resize(n);
103        } else {
104            data = NULL;
105        }
106    }
107
108    void _copy(const Array &other) {
109        init(other.num, other.num);
110        for (int i = 0; i < num; i++) {
111            data[i] = other.data[i];
112        }
113    }
114
115    /**
116     Returns true iff address points to an element of this array.
117     Used by append.
118     */
119    inline bool inArray(const T* address) {
120        return (address >= data) && (address < data + num);
121    }
122
123
124    /** Only compiled if you use the sort procedure. */
125    static bool __cdecl compareGT(const T& a, const T& b) {
126        return a > b;
127    }
128
129
130    /**
131     Allocates a new array of size numAllocated (not a parameter to the method)
132     and then copies at most oldNum elements from the old array to it.  Destructors are
133     called for oldNum elements of the old array.
134     */
135    void realloc(int oldNum) {
136         T* oldData = data;
137         
138         // The allocation is separate from the constructor invocation because we don't want
139         // to pay for the cost of constructors until the newly allocated
140         // elements are actually revealed to the application.  They
141         // will be constructed in the resize() method.
142
143         data = (T*)System::alignedMalloc(sizeof(T) * numAllocated, 16);
144
145         // Call the copy constructors
146         {const int N = iMin(oldNum, numAllocated);
147          const T* end = data + N;
148          T* oldPtr = oldData;
149          for (T* ptr = data; ptr < end; ++ptr, ++oldPtr) {
150
151             // Use placement new to invoke the constructor at the location
152             // that we determined.  Use the copy constructor to make the assignment.
153             const T* constructed = new (ptr) T(*oldPtr);
154
155             (void)constructed;
156             debugAssertM(constructed == ptr, 
157                 "new returned a different address than the one provided by Array.");
158         }}
159
160         // Call destructors on the old array (if there is no destructor, this will compile away)
161         {const T* end = oldData + oldNum;
162          for (T* ptr = oldData; ptr < end; ++ptr) {
163              ptr->~T();
164         }}
165
166
167         System::alignedFree(oldData);
168    }
169
170public:
171
172    /**
173     C++ STL style iterator variable.  Call begin() to get
174     the first iterator, pre-increment (++i) the iterator to get to
175     the next value.  Use dereference (*i) to access the element.
176     */
177    typedef T* Iterator;
178    typedef const T* ConstIterator;
179
180    /**
181     C++ STL style iterator method.  Returns the first iterator element.
182     Do not change the size of the array while iterating.
183     */
184    Iterator begin() {
185        return data;
186    }
187
188    ConstIterator begin() const {
189        return data;
190    }
191    /**
192     C++ STL style iterator method.  Returns one after the last iterator
193     element.
194     */
195    ConstIterator end() const {
196        return data + num;
197    }
198
199    Iterator end() {
200        return data + num;
201    }
202
203   /**
204    The array returned is only valid until the next append() or resize call, or
205        the Array is deallocated.
206    */
207   T* getCArray() {
208       return data;
209   }
210
211   /**
212    The array returned is only valid until the next append() or resize call, or
213        the Array is deallocated.
214    */
215   const T* getCArray() const {
216       return data;
217   }
218
219   /** Creates a zero length array (no heap allocation occurs until resize). */
220   Array() {
221       init(0, 0);
222   }
223
224   /**
225    Creates an array of size.
226    */
227   Array(int size) {
228       init(size, size);
229   }
230
231   /**
232    Copy constructor
233    */
234   Array(const Array& other) {
235       _copy(other);
236   }
237
238   /**
239    Destructor does not delete() the objects if T is a pointer type
240    (e.g. T = int*) instead, it deletes the <B>pointers themselves</B> and
241    leaves the objects.  Call deleteAll if you want to dealocate
242    the objects referenced.  Do not call deleteAll if <CODE>T</CODE> is not a pointer
243    type (e.g. do call Array<Foo*>::deleteAll, do <B>not</B> call Array<Foo>::deleteAll).
244    */
245   ~Array() {
246       // Invoke the destructors on the elements
247       for (int i = 0; i < num; i++) {
248           (data + i)->~T();
249       }
250       
251       System::alignedFree(data);
252       // Set to 0 in case this Array is global and gets referenced during app exit
253       data = NULL;
254           num = 0;
255       numAllocated = 0;
256   }
257
258
259   /**
260    Removes all elements.  Use resize(0, false) or fastClear if you want to
261    remove all elements without deallocating the underlying array
262    so that future append() calls will be faster.
263    */
264   void clear() {
265       resize(0);
266   }
267
268   /** resize(0, false) */
269   void fastClear() {
270       resize(0, false);
271   }
272
273   /**
274    Assignment operator.
275    */
276   Array& operator=(const Array& other) {
277       resize(other.num);
278       for (int i = 0; i < num; ++i) {
279           data[i] = other[i];
280       }
281       return *this;
282   }
283
284   Array& operator=(const std::vector<T>& other) {
285       resize((int)other.size());
286       for (int i = 0; i < num; ++i) {
287           data[i] = other[i];
288       }
289       return *this;
290   }
291
292   /**
293    Number of elements in the array.
294    */
295   inline int size() const {
296      return num;
297   }
298
299   /**
300    Number of elements in the array.  (Same as size; this is just
301    here for convenience).
302    */
303   inline int length() const {
304      return size();
305   }
306
307   /**
308    Swaps element index with the last element in the array then
309    shrinks the array by one.
310    */
311   void fastRemove(int index) {
312       debugAssert(index >= 0);
313       debugAssert(index < num);
314       data[index] = data[num - 1];
315       resize(size() - 1);
316   }
317
318   /**
319    Resizes, calling the default constructor for
320    newly created objects and shrinking the underlying
321    array as needed (and calling destructors as needed).
322    */
323   void resize(int n) {
324      resize(n, true);
325   }
326
327   /** Resizes without shrinking the underlying array */
328   void fastResize(int n) {
329      resize(n, false);
330   }
331
332
333   /**
334    Inserts at the specified index and shifts all other elements up by one.
335    */
336   void insert(int n, const T& value) {
337       // Add space for the extra element
338       resize(num + 1, false);
339
340       for (int i = num - 1; i > n; --i) {
341           data[i] = data[i - 1];
342       }
343       data[n] = value;
344   }
345
346    /** @param shrinkIfNecessary if false, memory will never be
347      reallocated when the array shrinks.  This makes resizing much
348      faster but can waste memory. */
349   void resize(int n, bool shrinkIfNecessary) {
350      int oldNum = num;
351      num = n;
352
353      // Call the destructors on newly hidden elements if there are any
354      for (int i = num; i < oldNum; ++i) {
355          (data + i)->~T();
356      }
357
358      // Once allocated, always maintain 10 elements or 32 bytes, whichever is higher.
359      static const int minSize = iMax(10, 32 / sizeof(T));
360
361      if (num > numAllocated) {
362          // Grow the underlying array
363
364          if (numAllocated == 0) {
365              // First allocation; grow to exactly the size requested to avoid wasting space.
366              numAllocated = n;
367              debugAssert(oldNum == 0);
368              realloc(oldNum);
369          } else {
370         
371              if (num < minSize) {
372                  // Grow to at least the minimum size
373                  numAllocated = minSize;
374
375              } else {
376
377                  // Increase the underlying size of the array.  Grow aggressively
378                  // up to 64k, less aggressively up to 400k, and then grow relatively
379                  // slowly (1.5x per resize) to avoid excessive space consumption.
380                  //
381                  // These numbers are tweaked according to performance tests.
382
383                  float growFactor = 3.0;
384
385                  size_t oldSizeBytes = numAllocated * sizeof(T);
386                  if (oldSizeBytes > 400000) {
387                      // Avoid bloat
388                      growFactor = 1.5;
389                  } else if (oldSizeBytes > 64000) {
390                      // This is what std:: uses at all times
391                      growFactor = 2.0;
392                  }
393
394                  numAllocated = (num - numAllocated) + (int)(numAllocated * growFactor);
395
396                  if (numAllocated < minSize) {
397                      numAllocated = minSize;
398                  }
399              }
400
401              realloc(oldNum);
402          }
403
404      } else if ((num <= numAllocated / 3) && shrinkIfNecessary && (num > minSize)) {
405          // Shrink the underlying array
406
407          // Only copy over old elements that still remain after resizing
408          // (destructors were called for others if we're shrinking)
409          realloc(iMin(num, oldNum));
410
411      }
412
413      // Call the constructors on newly revealed elements.
414      // Do not use parens because we don't want the intializer
415      // invoked for POD types.
416      for (int i = oldNum; i < num; ++i) {
417          new (data + i) T;
418      }
419   }
420
421    /**
422     Add an element to the end of the array.  Will not shrink the underlying array
423     under any circumstances.  It is safe to append an element that is already
424     in the array.
425     */
426    inline void append(const T& value) {
427       
428        if (num < numAllocated) {
429            // This is a simple situation; just stick it in the next free slot using
430            // the copy constructor.
431            new (data + num) T(value);
432            ++num;
433        } else if (inArray(&value)) {
434            // The value was in the original array; resizing
435            // is dangerous because it may move the value
436            // we have a reference to.
437            T tmp = value;
438            append(tmp);
439        } else {
440            // Here we run the empty initializer where we don't have to, but
441            // this simplifies the computation.
442            resize(num + 1, DONT_SHRINK_UNDERLYING_ARRAY);
443            data[num - 1] = value;
444        }
445    }
446
447
448    inline void append(const T& v1, const T& v2) {
449        if (inArray(&v1) || inArray(&v2)) {
450            T t1 = v1;
451            T t2 = v2;
452            append(t1, t2);
453        } else if (num + 1 < numAllocated) {
454            // This is a simple situation; just stick it in the next free slot using
455            // the copy constructor.
456            new (data + num) T(v1);
457            new (data + num + 1) T(v2);
458            num += 2;
459        } else {
460            resize(num + 2, DONT_SHRINK_UNDERLYING_ARRAY);
461            data[num - 2] = v1;
462            data[num - 1] = v2;
463        }
464    }
465
466
467    inline void append(const T& v1, const T& v2, const T& v3) {
468        if (inArray(&v1) || inArray(&v2) || inArray(&v3)) {
469            T t1 = v1;
470            T t2 = v2;
471            T t3 = v3;
472            append(t1, t2, t3);
473        } else if (num + 2 < numAllocated) {
474            // This is a simple situation; just stick it in the next free slot using
475            // the copy constructor.
476            new (data + num) T(v1);
477            new (data + num + 1) T(v2);
478            new (data + num + 2) T(v3);
479            num += 3;
480        } else {
481            resize(num + 3, DONT_SHRINK_UNDERLYING_ARRAY);
482            data[num - 3] = v1;
483            data[num - 2] = v2;
484            data[num - 1] = v3;
485        }
486    }
487
488
489    inline void append(const T& v1, const T& v2, const T& v3, const T& v4) {
490        if (inArray(&v1) || inArray(&v2) || inArray(&v3) || inArray(&v4)) {
491            T t1 = v1;
492            T t2 = v2;
493            T t3 = v3;
494            T t4 = v4;
495            append(t1, t2, t3, t4);
496        } else if (num + 3 < numAllocated) {
497            // This is a simple situation; just stick it in the next free slot using
498            // the copy constructor.
499            new (data + num) T(v1);
500            new (data + num + 1) T(v2);
501            new (data + num + 2) T(v3);
502            new (data + num + 3) T(v4);
503            num += 4;
504        } else {
505            resize(num + 4, DONT_SHRINK_UNDERLYING_ARRAY);
506            data[num - 4] = v1;
507            data[num - 3] = v2;
508            data[num - 2] = v3;
509            data[num - 1] = v4;
510        }
511    }
512
513    /**
514     Returns true if the given element is in the array.
515     */
516    bool contains(const T& e) const {
517        for (int i = 0; i < size(); ++i) {
518            if ((*this)[i] == e) {
519                return true;
520            }
521        }
522
523        return false;
524    }
525
526   /**
527    Append the elements of array.  Cannot be called with this array
528    as an argument.
529    */
530   void append(const Array<T>& array) {
531       debugAssert(this != &array);
532       int oldNum = num;
533       int arrayLength = array.length();
534
535       resize(num + arrayLength, false);
536
537       for (int i = 0; i < arrayLength; i++) {
538           data[oldNum + i] = array.data[i];
539       }
540   }
541
542   /**
543    Pushes a new element onto the end and returns its address.
544    This is the same as A.resize(A.size() + 1, false); A.last()
545    */
546   inline T& next() {
547       resize(num + 1, false);
548       return last();
549   }
550
551   /**
552    Pushes an element onto the end (appends)
553    */
554   inline void push(const T& value) {
555       append(value);
556   }
557
558   inline void push(const Array<T>& array) {
559       append(array);
560   }
561
562   /** Alias to provide std::vector compatibility */
563   inline void push_back(const T& v) {
564       push(v);
565   }
566
567   /** "The member function removes the last element of the controlled sequence, which must be non-empty."
568        For compatibility with std::vector. */
569   inline void pop_back() {
570       pop();
571   }
572
573   /**
574      "The member function returns the storage currently allocated to hold the controlled
575       sequence, a value at least as large as size()"
576       For compatibility with std::vector.
577   */
578   int capacity() const {
579       return numAllocated;
580   }
581
582   /**
583      "The member function returns a reference to the first element of the controlled sequence,
584       which must be non-empty."
585       For compatibility with std::vector.
586   */
587   T& front() {
588       return (*this)[0];
589   }
590
591   /**
592      "The member function returns a reference to the first element of the controlled sequence,
593       which must be non-empty."
594       For compatibility with std::vector.
595   */
596   const T& front() const {
597       return (*this)[0];
598   }
599
600   /**
601    Removes the last element and returns it.  By default, shrinks the underlying array.
602    */
603   inline T pop(bool shrinkUnderlyingArrayIfNecessary = true) {
604       debugAssert(num > 0);
605       T temp = data[num - 1];
606       resize(num - 1, shrinkUnderlyingArrayIfNecessary);
607       return temp;
608   }
609
610   /** Pops the last element and discards it without returning anything.  Faster than pop.
611      By default, does not shrink the underlying array.*/
612   inline void popDiscard(bool shrinkUnderlyingArrayIfNecessary = false) {
613       debugAssert(num > 0);
614       resize(num - 1, shrinkUnderlyingArrayIfNecessary);
615   }
616
617
618   /**
619    "The member function swaps the controlled sequences between *this and str."
620    Note that this is slower than the optimal std implementation.
621
622    For compatibility with std::vector.
623    */
624   void swap(Array<T>& str) {
625       Array<T> temp = str;
626       str = *this;
627       *this = temp;
628   }
629
630
631   /**
632    Performs bounds checks in debug mode
633    */
634   inline T& operator[](int n) {
635        debugAssertM((n >= 0) && (n < num), format("Array index out of bounds. n = %d, size() = %d", n, num));
636        debugAssert(data!=NULL);
637        return data[n];
638   }
639
640   inline T& operator[](unsigned int n) {
641        debugAssertM(((int)n < num), format("Array index out of bounds. n = %d, size() = %d", n, num));
642        return data[n];
643   }
644
645   /**
646    Performs bounds checks in debug mode
647    */
648    inline const T& operator[](int n) const {
649        debugAssert((n >= 0) && (n < num));
650        debugAssert(data!=NULL);
651        return data[n];
652    }
653
654    inline const T& operator[](unsigned int n) const {
655        debugAssert((n < (unsigned int)num));
656        debugAssert(data!=NULL);
657        return data[n];
658    }
659
660    inline T& randomElement() {
661        debugAssert(num > 0);
662        debugAssert(data!=NULL);
663        return data[iRandom(0, num - 1)];
664    }
665
666    inline const T& randomElement() const {
667        debugAssert(num > 0);
668        debugAssert(data!=NULL);
669        return data[iRandom(0, num - 1)];
670    }
671
672    /**
673    Returns the last element, performing a check in
674    debug mode that there is at least one element.
675    */
676    inline const T& last() const {
677        debugAssert(num > 0);
678        debugAssert(data!=NULL);
679        return data[num - 1];
680    }
681
682    /** Returns element lastIndex() */
683    inline T& last() {
684        debugAssert(num > 0);
685        debugAssert(data!=NULL);
686        return data[num - 1];
687    }
688
689    /** Returns <i>size() - 1</i> */
690    inline int lastIndex() const {
691        debugAssertM(num > 0, "Array is empty");
692        return num - 1;
693    }
694
695    inline int firstIndex() const {
696        debugAssertM(num > 0, "Array is empty");
697        return 0;
698    }
699
700    /** Returns element firstIndex(), performing a check in debug mode to ensure that there is at least one */
701    inline T& first() {
702        debugAssertM(num > 0, "Array is empty");
703        return data[0];
704    }
705
706    inline const T& first() const {
707        debugAssertM(num > 0, "Array is empty");
708        return data[0];
709    }
710
711    /** Returns iFloor(size() / 2), throws an assertion in debug mode if the array is empty */
712    inline int middleIndex() const {
713        debugAssertM(num > 0, "Array is empty");
714        return num >> 1;
715    }
716
717    /** Returns element middleIndex() */
718    inline const T& middle() const {
719        debugAssertM(num > 0, "Array is empty");
720        return data[num >> 1];   
721    }
722
723    /** Returns element middleIndex() */
724    inline T& middle() {
725        debugAssertM(num > 0, "Array is empty");
726        return data[num >> 1];   
727    }
728
729    /**
730    Calls delete on all objects[0...size-1]
731    and sets the size to zero.
732    */
733    void deleteAll() {
734        for (int i = 0; i < num; i++) {
735            delete data[i];
736        }
737        resize(0);
738    }
739
740    /**
741     Returns the index of (the first occurance of) an index or -1 if
742     not found.
743     */
744    int findIndex(const T& value) const {
745        for (int i = 0; i < num; ++i) {
746            if (data[i] == value) {
747                return i;
748            }
749        }
750        return -1;
751    }
752
753    /**
754     Finds an element and returns the iterator to it.  If the element
755     isn't found then returns end().
756     */
757    Iterator find(const T& value) {
758        for (int i = 0; i < num; ++i) {
759            if (data[i] == value) {
760                return data + i;
761            }
762        }
763        return end();
764    }
765
766    ConstIterator find(const T& value) const {
767        for (int i = 0; i < num; ++i) {
768            if (data[i] == value) {
769                return data + i;
770            }
771        }
772        return end();
773    }
774
775    /**
776     Removes count elements from the array
777     referenced either by index or Iterator.
778     */
779    void remove(Iterator element, int count = 1) {
780        debugAssert((element >= begin()) && (element < end()));
781        debugAssert((count > 0) && (element + count) <= end());
782        Iterator last = end() - count;
783
784        while(element < last) {
785            element[0] = element[count];
786            ++element;
787        }
788       
789        resize(num - count);
790    }
791
792    void remove(int index, int count = 1) {
793        debugAssert((index >= 0) && (index < num));
794        debugAssert((count > 0) && (index + count <= num));
795       
796        remove(begin() + index, count);
797    }
798
799    /**
800     Reverse the elements of the array in place.
801     */
802    void reverse() {
803        T temp;
804       
805        int n2 = num / 2;
806        for (int i = 0; i < n2; ++i) {
807            temp = data[num - 1 - i];
808            data[num - 1 - i] = data[i];
809            data[i] = temp;
810        }
811    }
812
813    /**
814     Sort using a specific less-than function, e.g.:
815
816  <PRE>
817    bool __cdecl myLT(const MyClass& elem1, const MyClass& elem2) {
818        return elem1.x < elem2.x;
819    }
820    </PRE>
821
822  Note that for pointer arrays, the <CODE>const</CODE> must come
823  <I>after</I> the class name, e.g., <CODE>Array<MyClass*></CODE> uses:
824
825  <PRE>
826    bool __cdecl myLT(MyClass*const& elem1, MyClass*const& elem2) {
827        return elem1->x < elem2->x;
828    }
829    </PRE>
830     */
831    void sort(bool (__cdecl *lessThan)(const T& elem1, const T& elem2)) {
832        std::sort(data, data + num, lessThan);
833    }
834
835
836    /**
837     Sorts the array in increasing order using the > or < operator.  To
838     invoke this method on Array<T>, T must override those operator.
839     You can overide these operators as follows:
840     <code>
841        bool T::operator>(const T& other) const {
842           return ...;
843        }
844        bool T::operator<(const T& other) const {
845           return ...;
846        }
847     </code>
848     */
849    void sort(int direction = SORT_INCREASING) {
850        if (direction == SORT_INCREASING) {
851            std::sort(data, data + num);
852        } else {
853            std::sort(data, data + num, compareGT);
854        }
855    }
856
857    /**
858     Sorts elements beginIndex through and including endIndex.
859     */
860    void sortSubArray(int beginIndex, int endIndex, int direction = SORT_INCREASING) {
861        if (direction == SORT_INCREASING) {
862            std::sort(data + beginIndex, data + endIndex + 1);
863        } else {
864            std::sort(data + beginIndex, data + endIndex + 1, compareGT);
865        }
866    }
867
868    void sortSubArray(int beginIndex, int endIndex, bool (__cdecl *lessThan)(const T& elem1, const T& elem2)) {
869        std::sort(data + beginIndex, data + endIndex + 1, lessThan);
870    }
871
872    /**
873     The StrictWeakOrdering can be either a class that overloads the function call operator() or
874     a function pointer of the form <code>bool (__cdecl *lessThan)(const T& elem1, const T& elem2)</code>
875     */
876    template<typename StrictWeakOrdering>
877    void sortSubArray(int beginIndex, int endIndex, StrictWeakOrdering& lessThan) {
878        std::sort(data + beginIndex, data + endIndex + 1, lessThan);
879    }
880
881    /** Uses < and == to evaluate operator(); this is the default comparator for Array::partition. */
882    class DefaultComparator {
883    public:
884        inline int operator()(const T& A, const T& B) const {
885            if (A < B) {
886                return 1;
887            } else if (A == B) {
888                return 0;
889            } else {
890                return -1;
891            }
892        }
893    };
894
895    /** The output arrays are resized with fastClear() so that if they are already of the same size
896        as this array no memory is allocated during partitioning.
897       
898        @param comparator A function, or class instance with an overloaded operator() that compares
899        two elements of type <code>T</code> and returns 0 if they are equal, -1 if the second is smaller,
900        and 1 if the first is smaller (i.e., following the conventions of std::string::compare).  For example:
901
902        <pre>
903        int compare(int A, int B) {
904            if (A < B) {
905                return 1;
906            } else if (A == B) {
907                return 0;
908            } else {
909                return -1;
910            }
911        }
912        </pre>
913        */
914    template<typename Comparator>
915    void partition(
916        const T& partitionElement, 
917        Array<T>& ltArray,
918        Array<T>& eqArray,
919        Array<T>& gtArray,
920        const Comparator& comparator) const {
921
922        // Make sure all arrays are independent
923        debugAssert(&ltArray != this);
924        debugAssert(&eqArray != this);
925        debugAssert(&gtArray != this);
926        debugAssert(&ltArray != &eqArray);
927        debugAssert(&ltArray != &gtArray);
928        debugAssert(&eqArray != &gtArray);
929
930        // Clear the arrays
931        ltArray.fastClear();
932        eqArray.fastClear();
933        gtArray.fastClear();
934
935        // Form a table of buckets for lt, eq, and gt
936        Array<T>* bucket[3] = {&ltArray, &eqArray, &gtArray};
937
938        for (int i = 0; i < num; ++i) {
939            int c = comparator(partitionElement, data[i]);
940            debugAssertM(c >= -1 && c <= 1, "Comparator returned an illegal value.");
941
942            // Insert into the correct bucket, 0, 1, or 2
943            bucket[c + 1]->append(data[i]);
944        }
945    }
946
947    /**
948      Uses < and == on elements to perform a partition.  See partition().
949     */
950    void partition(
951        const T& partitionElement, 
952        Array<T>& ltArray,
953        Array<T>& eqArray,
954        Array<T>& gtArray) const {
955
956        partition(partitionElement, ltArray, eqArray, gtArray, typename Array<T>::DefaultComparator());
957    }
958
959    /**
960     Paritions the array into those below the median, those above the median, and those elements
961     equal to the median in expected O(n) time using quickselect.  If the array has an even
962     number of different elements, the median for partition purposes is the largest value
963     less than the median.
964
965     @param tempArray used for working scratch space
966     @param comparator see parition() for a discussion.*/
967    template<typename Comparator>
968    void medianPartition(
969        Array<T>&           ltMedian, 
970        Array<T>&           eqMedian, 
971        Array<T>&           gtMedian,
972        Array<T>&           tempArray,
973        const Comparator&   comparator) const {
974
975        ltMedian.fastClear();
976        eqMedian.fastClear();
977        gtMedian.fastClear();
978
979        // Handle trivial cases first
980        switch (size()) {
981        case 0:
982            // Array is empty; no parition is possible
983            return;
984
985        case 1:
986            // One element
987            eqMedian.append(first());
988            return;
989
990        case 2:
991            {
992                // Two element array; median is the smaller
993                int c = comparator(first(), last());
994               
995                switch (c) {
996                case -1:
997                    // first was bigger
998                    eqMedian.append(last());
999                    gtMedian.append(first());
1000                    break;
1001
1002                case 0:
1003                    // Both equal to the median
1004                    eqMedian.append(first(), last());
1005                    break;
1006
1007                case 1:
1008                    // Last was bigger
1009                    eqMedian.append(first());
1010                    gtMedian.append(last());
1011                    break;
1012                }
1013            }
1014            return;
1015        }
1016
1017        // All other cases use a recursive randomized median
1018
1019        // Number of values less than all in the current arrays       
1020        int ltBoost = 0;
1021
1022        // Number of values greater than all in the current arrays       
1023        int gtBoost = 0;
1024
1025        // For even length arrays, force the gt array to be one larger than the
1026        // lt array: 
1027        //  [1 2 3] size = 3, choose half = (s + 1) /2
1028        //
1029        int lowerHalfSize, upperHalfSize;
1030        if (isEven(size())) {
1031            lowerHalfSize = size() / 2;
1032            upperHalfSize = lowerHalfSize + 1;
1033        } else {
1034            lowerHalfSize = upperHalfSize = (size() + 1) / 2;
1035        }
1036        const T* xPtr = NULL;
1037
1038        // Maintain pointers to the arrays; we'll switch these around during sorting
1039        // to avoid copies.
1040        const Array<T>* source = this;
1041        Array<T>* lt     = &ltMedian;
1042        Array<T>* eq     = &eqMedian;
1043        Array<T>* gt     = &gtMedian;
1044        Array<T>* extra  = &tempArray;
1045
1046        while (true) {
1047            // Choose a random element -- choose the middle element; this is theoretically
1048            // suboptimal, but for loosly sorted array is actually the best strategy
1049
1050            xPtr = &(source->middle());
1051            if (source->size() == 1) {
1052                // Done; there's only one element left
1053                break;
1054            }
1055            const T& x = *xPtr;
1056
1057            // Note: partition (fast) clears the arrays for us
1058            source->partition(x, *lt, *eq, *gt, comparator);
1059
1060            int L = lt->size() + ltBoost + eq->size();
1061            int U = gt->size() + gtBoost + eq->size();
1062            if ((L >= lowerHalfSize) &&
1063                (U >= upperHalfSize)) {
1064
1065                // x must be the partition median                   
1066                break;
1067
1068            } else if (L < lowerHalfSize) {
1069
1070                // x must be smaller than the median.  Recurse into the 'gt' array.
1071                ltBoost += lt->size() + eq->size();
1072
1073                // The new gt array will be the old source array, unless
1074                // that was the this pointer (i.e., unless we are on the
1075                // first iteration)
1076                Array<T>* newGt = (source == this) ? extra : const_cast<Array<T>*>(source);
1077               
1078                // Now set up the gt array as the new source
1079                source = gt;
1080                gt = newGt;
1081
1082            } else {
1083
1084                // x must be bigger than the median.  Recurse into the 'lt' array.
1085                gtBoost += gt->size() + eq->size();
1086
1087                // The new lt array will be the old source array, unless
1088                // that was the this pointer (i.e., unless we are on the
1089                // first iteration)
1090                Array<T>* newLt = (source == this) ? extra : const_cast<Array<T>*>(source);
1091               
1092                // Now set up the lt array as the new source
1093                source = lt;
1094                lt = newLt;
1095            }
1096        }
1097
1098        // Now that we know the median, make a copy of it (since we're about to destroy the array that it
1099        // points into).
1100        T median = *xPtr;
1101        xPtr = NULL;
1102
1103        // Partition the original array (note that this fast clears for us)
1104        partition(median, ltMedian, eqMedian, gtMedian, comparator);
1105    }
1106
1107    /**
1108      Computes a median partition using the default comparator and a dynamically allocated temporary
1109      working array.  If the median is not in the array, it is chosen to be the largest value smaller
1110      than the true median.
1111     */
1112    void medianPartition(
1113        Array<T>&           ltMedian, 
1114        Array<T>&           eqMedian, 
1115        Array<T>&           gtMedian) const {
1116
1117        Array<T> temp;
1118        medianPartition(ltMedian, eqMedian, gtMedian, temp, DefaultComparator());
1119    }
1120
1121
1122    /** Redistributes the elements so that the new order is statistically independent
1123        of the original order. O(n) time.*/
1124    void randomize() {
1125        T temp;
1126
1127        for (int i = size() - 1; i >= 0; --i) {
1128            int x = iRandom(0, i);
1129
1130            temp = data[i];
1131            data[i] = data[x];
1132            data[x] = temp;
1133        }
1134    }
1135
1136
1137};
1138
1139
1140/** Array::contains for C-arrays */
1141template<class T> bool contains(const T* array, int len, const T& e) {
1142    for (int i = len - 1; i >= 0; --i) {
1143        if (array[i] == e) {
1144            return true;
1145        }
1146    }
1147    return false;
1148}
1149
1150} // namespace
1151
1152#endif
1153
1154#ifdef G3D_WIN32
1155#   pragma warning (push)
1156#endif
Note: See TracBrowser for help on using the browser.