| 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 | |
|---|
| 37 | namespace G3D { |
|---|
| 38 | |
|---|
| 39 | /** |
|---|
| 40 | Constant for passing to Array::resize |
|---|
| 41 | */ |
|---|
| 42 | const bool DONT_SHRINK_UNDERLYING_ARRAY = false; |
|---|
| 43 | |
|---|
| 44 | /** Constant for Array::sort */ |
|---|
| 45 | const int SORT_INCREASING = 1; |
|---|
| 46 | /** Constant for Array::sort */ |
|---|
| 47 | const 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 | */ |
|---|
| 86 | template <class T> |
|---|
| 87 | class Array { |
|---|
| 88 | private: |
|---|
| 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 | |
|---|
| 170 | public: |
|---|
| 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(<Array != this); |
|---|
| 924 | debugAssert(&eqArray != this); |
|---|
| 925 | debugAssert(>Array != this); |
|---|
| 926 | debugAssert(<Array != &eqArray); |
|---|
| 927 | debugAssert(<Array != >Array); |
|---|
| 928 | debugAssert(&eqArray != >Array); |
|---|
| 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] = {<Array, &eqArray, >Array}; |
|---|
| 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 = <Median; |
|---|
| 1042 | Array<T>* eq = &eqMedian; |
|---|
| 1043 | Array<T>* gt = >Median; |
|---|
| 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 */ |
|---|
| 1141 | template<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 |
|---|