1 /******************************************************************************* 2 * src/SortArray.h 3 * 4 * SortArray represents a simple array, which is displayed by WSortView to the 5 * user is real-time. 6 * 7 ******************************************************************************* 8 * Copyright (C) 2014 Timo Bingmann <tb@panthema.net> 9 * 10 * This program is free software: you can redistribute it and/or modify it 11 * under the terms of the GNU General Public License as published by the Free 12 * Software Foundation, either version 3 of the License, or (at your option) 13 * any later version. 14 * 15 * This program is distributed in the hope that it will be useful, but WITHOUT 16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 17 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 18 * more details. 19 * 20 * You should have received a copy of the GNU General Public License along with 21 * this program. If not, see <http://www.gnu.org/licenses/>. 22 ******************************************************************************/ 23 24 #ifndef SORT_ARRAY_HEADER 25 #define SORT_ARRAY_HEADER 26 27 #include <vector> 28 #include <algorithm> 29 #include <stdlib.h> 30 31 #include <wx/log.h> 32 #include <wx/thread.h> 33 #include <wx/ctrlsub.h> 34 35 // ---------------------------------------------------------------------------- 36 37 /// custom assertion (also active in release mode) 38 #define ASSERT(cond) do { if (!(cond)) { \ 39 wxLogError(_("Assertion failed:\n%s in %s:%d"), \ 40 _T(#cond), _T(__FILE__), __LINE__); \ 41 wxLog::FlushActive(); \ 42 abort(); \ 43 } } while(0) 44 45 // ---------------------------------------------------------------------------- 46 47 /// globally count the number of comparisons done on value_type 48 extern size_t g_compare_count; 49 50 /// globally count the number of array access 51 extern size_t g_access_count; 52 53 // custom struct for array items, which allows detailed counting of comparisons. 54 class ArrayItem 55 { 56 public: 57 typedef int value_type; 58 59 protected: 60 value_type value; 61 62 public: ArrayItem()63 ArrayItem() {} 64 ArrayItem(const value_type & d)65 explicit ArrayItem(const value_type& d) : value(d) {} 66 ArrayItem(const ArrayItem & v)67 ArrayItem(const ArrayItem& v) : value(v.value) {} 68 69 // ArrayItem has no implicit data cast, because most sorting algorithms use 70 // comparisons. However, radix sort and similar use the following data 71 // accessor. To add sound for these, we use a separate callback. get()72 const value_type& get() const 73 { OnAccess(*this); return value; } 74 75 static void OnAccess(const ArrayItem& a); 76 77 // for direct data access by visualizer get_direct()78 const value_type& get_direct() const 79 { return value; } 80 81 // *** comparisons 82 83 bool operator== (const ArrayItem& v) const 84 { OnComparison(*this,v); return (value == v.value); } 85 86 bool operator!= (const ArrayItem& v) const 87 { OnComparison(*this,v); return (value != v.value); } 88 89 bool operator< (const ArrayItem& v) const 90 { OnComparison(*this,v); return (value < v.value); } 91 92 bool operator<= (const ArrayItem& v) const 93 { OnComparison(*this,v); return (value <= v.value); } 94 95 bool operator> (const ArrayItem& v) const 96 { OnComparison(*this,v); return (value > v.value); } 97 98 bool operator>= (const ArrayItem& v) const 99 { OnComparison(*this,v); return (value >= v.value); } 100 101 // ternary comparison which counts just one cmp(const ArrayItem & v)102 int cmp(const ArrayItem& v) const 103 { 104 OnComparison(*this,v); 105 return (value == v.value ? 0 : value < v.value ? -1 : +1); 106 } 107 108 // *** comparisons without sound, counting or delay 109 equal_direct(const ArrayItem & v)110 bool equal_direct(const ArrayItem& v) const 111 { return (value == v.value); } 112 less_direct(const ArrayItem & v)113 bool less_direct(const ArrayItem& v) const 114 { return (value < v.value); } 115 greater_direct(const ArrayItem & v)116 bool greater_direct(const ArrayItem& v) const 117 { return (value > v.value); } 118 119 static void OnComparison(const ArrayItem& a, const ArrayItem& b); 120 }; 121 122 // ---------------------------------------------------------------------------- 123 124 class SortDelay 125 { 126 public: 127 128 /// central access function called by each array access of the algorithms 129 virtual void OnAccess() = 0; 130 }; 131 132 // ---------------------------------------------------------------------------- 133 134 class SortArray 135 { 136 protected: 137 // *** Displayed Array Data 138 139 /// the array data 140 std::vector<ArrayItem> m_array; 141 142 /// maximum value in array for scaling display 143 ArrayItem::value_type m_array_max; 144 145 /// disable calculating of inversions 146 bool m_calc_inversions; 147 148 /// the number of inversions in the array order 149 ssize_t m_inversions; 150 151 /// access touch color 152 struct Access 153 { 154 unsigned int index; 155 unsigned short color; 156 unsigned short sustain; 157 unsigned short priority; 158 159 Access(size_t i=0, unsigned short c=1, 160 unsigned short s=0, unsigned short p=0) indexAccess161 : index(i), color(c), sustain(s), priority(p) { } 162 }; 163 164 /// position of very last get/set accesses (two for swaps) 165 Access m_access1, m_access2; 166 167 /// array of get/set accesses since last paint event 168 std::vector<Access> m_access_list; 169 170 /// custom markers in the array, set by algorithm 171 std::vector<unsigned char> m_mark; 172 173 /// custom watched index pointers in the array, set by algorithm 174 std::vector< std::pair<volatile ssize_t*,unsigned char> > m_watch; 175 176 /// flag for sorted array 177 bool m_is_sorted; 178 179 /// pointer to delay function 180 SortDelay* m_delay; 181 182 public: 183 /// mutex for accesses and watch items 184 wxMutex m_mutex; 185 186 // *** Array Functions 187 188 public: 189 /// constructor 190 SortArray(); 191 192 /// Set pointer to delay functional SetSortDelay(SortDelay * delay)193 void SetSortDelay(SortDelay* delay) { m_delay = delay; } 194 195 /// called by main when an algorithm starts 196 void OnAlgoLaunch(const struct AlgoEntry& ae); 197 198 /// turn on/off calculation of inversions 199 void SetCalcInversions(bool on); 200 201 /// toggle boolean to calculate inversions 202 void ToggleCalcInversions(); 203 204 /// fill the array with one of the predefined data templates 205 void FillData(unsigned int schema, size_t arraysize); 206 207 /// fill an array of strings with the list of predefined data templates 208 static void FillInputlist(wxArrayString& list); 209 210 /// return whether the array was sorted IsSorted()211 bool IsSorted() const { return m_is_sorted; } 212 213 /// central access function called by each array access of the algorithms 214 void OnAccess(); 215 216 /// check array after sorting algorithm 217 bool CheckSorted(); 218 219 /// return the number of inversions in the array GetInversions()220 ssize_t GetInversions() const 221 { return m_inversions; } 222 223 /// calculate the number of runs in the array 224 size_t GetRuns() const; 225 226 public: 227 /// reset the array to the given size 228 void ResetArray(size_t size); 229 230 /// called when the data fill function is finished 231 void FinishFill(); 232 233 /// save access to array, forwards to sound system 234 void SaveAccess(size_t i); 235 236 /// check if index matches one of the watched pointers 237 short InAccessList(ssize_t idx); 238 239 /// check if index matches one of the watched pointers 240 unsigned short InWatchList(ssize_t idx) const; 241 242 /// Calculate the current color of the index i 243 int GetIndexColor(size_t idx); 244 245 /// recalculate the number of inversions (in quadratic time) 246 void RecalcInversions(); 247 248 // update inversion count by calculating delta linearly for a swap 249 void UpdateInversions(size_t i, size_t j); 250 251 public: 252 /// return array size size()253 size_t size() const { return m_array.size(); } 254 255 /// return highest element value in array array_max()256 const ArrayItem::value_type& array_max() const 257 { return m_array_max; } 258 259 /// Return an item of the array (bypassing sound, counting and delay) direct(size_t i)260 const ArrayItem& direct(size_t i) const 261 { 262 ASSERT(i < m_array.size()); 263 return m_array[i]; 264 } 265 266 /// Return an item of the array (yields counting and delay) 267 const ArrayItem& operator[](size_t i) 268 { 269 ASSERT(i < m_array.size()); 270 271 if (m_access1.index != i) 272 { 273 { 274 wxMutexLocker lock(m_mutex); 275 ASSERT(lock.IsOk()); 276 277 m_access1 = i; 278 m_access_list.push_back(i); 279 } 280 281 // skip wait for duplicate accesses 282 OnAccess(); 283 } 284 285 return m_array[i]; 286 } 287 288 /// Return a mutable item of the array (yields counting and delay) get_mutable(size_t i)289 ArrayItem& get_mutable(size_t i) 290 { 291 ASSERT(i < m_array.size()); 292 293 if (m_access1.index != i) 294 { 295 { 296 wxMutexLocker lock(m_mutex); 297 ASSERT(lock.IsOk()); 298 299 m_access1 = i; 300 m_access_list.push_back(i); 301 } 302 303 // skip wait for duplicate accesses 304 OnAccess(); 305 } 306 307 RecalcInversions(); 308 return m_array[i]; 309 } 310 311 /// Return an item of the array (yields delay, but no counting) get_nocount(size_t i)312 const ArrayItem& get_nocount(size_t i) 313 { 314 ASSERT(i < m_array.size()); 315 316 if (m_access1.index != i) 317 { 318 { 319 wxMutexLocker lock(m_mutex); 320 ASSERT(lock.IsOk()); 321 322 m_access1 = i; 323 m_access_list.push_back(i); 324 } 325 326 // skip wait for duplicate accesses 327 --g_access_count; 328 OnAccess(); 329 } 330 331 return m_array[i]; 332 } 333 334 /// Set an item of the array: first set then yield sound, counting and delay. set(size_t i,const ArrayItem & v)335 void set(size_t i, const ArrayItem& v) 336 { 337 ASSERT(i < m_array.size()); 338 339 { 340 wxMutexLocker lock(m_mutex); 341 ASSERT(lock.IsOk()); 342 343 m_access1 = i; 344 m_access_list.push_back(i); 345 346 m_array[i] = v; 347 } 348 349 RecalcInversions(); 350 OnAccess(); 351 } 352 353 /// Special function to swap the value in the array, this method provides a 354 /// special visualization for this operation. swap(size_t i,size_t j)355 void swap(size_t i, size_t j) 356 { 357 ASSERT(i < m_array.size()); 358 ASSERT(j < m_array.size()); 359 360 { 361 wxMutexLocker lock(m_mutex); 362 ASSERT(lock.IsOk()); 363 364 m_access1 = i; 365 m_access2 = j; 366 367 m_access_list.push_back(i); 368 m_access_list.push_back(j); 369 } 370 371 UpdateInversions(i, j); // update inversion count 372 373 OnAccess(); 374 std::swap(m_array[i], m_array[j]); 375 OnAccess(); 376 m_access2 = -1; 377 } 378 379 /// Touch an item of the array: set color till next frame is outputted. 380 void touch(size_t i, int color = 2, 381 unsigned short sustain = 0, unsigned short priority = 0) 382 { 383 ASSERT(i < m_array.size()); 384 385 { 386 wxMutexLocker lock(m_mutex); 387 ASSERT(lock.IsOk()); 388 389 m_access1 = Access(i, color, sustain, priority); 390 m_access_list.push_back( Access(i, color, sustain, priority) ); 391 } 392 } 393 394 /// Mark an array index with a color. 395 void mark(size_t i, int color = 2) 396 { 397 ASSERT(i < m_array.size()); 398 m_mark[i] = color; 399 } 400 401 /// Swap color for two array indexes. mark_swap(size_t i,size_t j)402 void mark_swap(size_t i, size_t j) 403 { 404 ASSERT(i < m_array.size()); 405 ASSERT(j < m_array.size()); 406 407 std::swap(m_mark[i], m_mark[j]); 408 } 409 410 /// Unmark an array index. unmark(size_t i)411 void unmark(size_t i) 412 { 413 ASSERT(i < m_array.size()); 414 m_mark[i] = 0; 415 } 416 417 /// Unmark all array indexes. unmark_all()418 void unmark_all() 419 { 420 m_access1 = m_access2 = -1; 421 std::fill(m_mark.begin(), m_mark.end(), 0); 422 423 wxMutexLocker lock(m_mutex); 424 ASSERT(lock.IsOk()); 425 m_access_list.clear(); 426 } 427 428 /// Highly experimental method to _track_ array live indexes. For this, the 429 /// index must be marked volatile!. 430 void watch(volatile ssize_t* idxptr, unsigned char color = 2) 431 { 432 wxMutexLocker lock(m_mutex); 433 ASSERT(lock.IsOk()); 434 435 m_watch.push_back( std::make_pair(idxptr,color) ); 436 } 437 438 /// Release all tracked live array indexes. unwatch_all()439 void unwatch_all() 440 { 441 wxMutexLocker lock(m_mutex); 442 ASSERT(lock.IsOk()); 443 444 m_watch.clear(); 445 } 446 }; 447 448 // ---------------------------------------------------------------------------- 449 450 #endif // SORT_ARRAY_HEADER 451