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