1 /*******************************************************************************
2  * src/SortArray.cpp
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 #include "SortArray.h"
25 #include "SortAlgo.h"
26 
27 #include <algorithm>
28 
29 extern void SoundAccess(size_t i);
30 
31 // *****************************************************************************
32 // *** Comparisons of ArrayItems
33 
34 size_t g_compare_count = 0;
35 
36 size_t g_access_count = 0;
37 
OnAccess(const ArrayItem & a)38 void ArrayItem::OnAccess(const ArrayItem& a)
39 {
40     SoundAccess(a.get_direct());
41 }
42 
OnComparison(const ArrayItem & a,const ArrayItem & b)43 void ArrayItem::OnComparison(const ArrayItem& a, const ArrayItem& b)
44 {
45     ++g_compare_count;
46 
47     SoundAccess(a.get_direct());
48     SoundAccess(b.get_direct());
49 }
50 
51 // *****************************************************************************
52 // *** SortArray
53 
SortArray()54 SortArray::SortArray()
55     : m_calc_inversions(false),
56       m_delay(NULL)
57 {
58 }
59 
OnAlgoLaunch(const AlgoEntry & ae)60 void SortArray::OnAlgoLaunch(const AlgoEntry& ae)
61 {
62     if (size() <= ae.inversion_count_limit)
63     {
64         m_calc_inversions = true;
65         RecalcInversions();
66     }
67     else
68     {
69         m_calc_inversions = false;
70         m_inversions = -1;
71     }
72 }
73 
ResetArray(size_t size)74 void SortArray::ResetArray(size_t size)
75 {
76     m_array.resize(size, ArrayItem(0));
77     m_mark.resize(size);
78 }
79 
FinishFill()80 void SortArray::FinishFill()
81 {
82     ASSERT(m_array.size() > 0);
83 
84     // calculate max value in array
85     m_array_max = m_array[0].get_direct();
86     for (size_t i = 1; i < size(); ++i)
87     {
88         if (m_array_max < m_array[i].get_direct())
89             m_array_max = m_array[i].get_direct();
90     }
91 
92     // reset access markers
93     unmark_all();
94     unwatch_all();
95 
96     // reset counters and info
97     m_is_sorted = false;
98     g_access_count = 0;
99     g_compare_count = 0;
100     m_calc_inversions = true;
101 
102     RecalcInversions();
103 }
104 
FillInputlist(wxArrayString & list)105 void SortArray::FillInputlist(wxArrayString& list)
106 {
107     list.Add(_("Random Shuffle"));
108     list.Add(_("Ascending"));
109     list.Add(_("Descending"));
110     list.Add(_("Shuffled Cubic"));
111     list.Add(_("Shuffled Quintic"));
112     list.Add(_("Shuffled n-2 Equal"));
113 }
114 
FillData(unsigned int schema,size_t arraysize)115 void SortArray::FillData(unsigned int schema, size_t arraysize)
116 {
117     if (arraysize == 0) arraysize = 1;
118 
119     ResetArray(arraysize);
120 
121     if (schema == 0) // Shuffle of [1,n]
122     {
123         for (size_t i = 0; i < m_array.size(); ++i)
124             m_array[i] = ArrayItem(i+1);
125 
126         std::random_shuffle(m_array.begin(), m_array.end());
127     }
128     else if (schema == 1) // Ascending [1,n]
129     {
130         for (size_t i = 0; i < m_array.size(); ++i)
131             m_array[i] = ArrayItem(i+1);
132     }
133     else if (schema == 2) // Descending [1,n]
134     {
135         for (size_t i = 0; i < m_array.size(); ++i)
136             m_array[i] = ArrayItem(m_array.size() - i);
137     }
138     else if (schema == 3) // Cubic skew of [1,n]
139     {
140         for (size_t i = 0; i < m_array.size(); ++i)
141         {
142             // normalize to [-1,+1]
143             double x = (2.0 * (double)i / m_array.size()) - 1.0;
144             // calculate x^3
145             double v = x * x * x;
146             // normalize to array size
147             double w = (v + 1.0) / 2.0 * arraysize + 1;
148             // decrease resolution for more equal values
149             w /= 3.0;
150             m_array[i] = ArrayItem(w + 1);
151         }
152 
153         std::random_shuffle(m_array.begin(), m_array.end());
154     }
155     else if (schema == 4) // Quintic skew of [1,n]
156     {
157         for (size_t i = 0; i < m_array.size(); ++i)
158         {
159             // normalize to [-1,+1]
160             double x = (2.0 * (double)i / m_array.size()) - 1.0;
161             // calculate x^5
162             double v = x * x * x * x * x;
163             // normalize to array size
164             double w = (v + 1.0) / 2.0 * arraysize + 1;
165             // decrease resolution for more equal values
166             w /= 3.0;
167             m_array[i] = ArrayItem(w + 1);
168         }
169 
170         std::random_shuffle(m_array.begin(), m_array.end());
171     }
172     else if (schema == 5) // shuffled n-2 equal values in [1,n]
173     {
174         m_array[0] = ArrayItem(1);
175         for (size_t i = 1; i < m_array.size()-1; ++i)
176         {
177             m_array[i] = ArrayItem( arraysize / 2 + 1 );
178         }
179         m_array[m_array.size()-1] = ArrayItem(arraysize);
180 
181         std::random_shuffle(m_array.begin(), m_array.end());
182     }
183     else // fallback
184     {
185         return FillData(0, arraysize);
186     }
187 
188     FinishFill();
189 }
190 
OnAccess()191 void SortArray::OnAccess()
192 {
193     ++g_access_count;
194 
195     if (m_delay)
196         m_delay->OnAccess();
197 }
198 
CheckSorted()199 bool SortArray::CheckSorted()
200 {
201     unmark_all();
202     // needed because iterator instrumentated algorithms may have changed the array
203     RecalcInversions();
204 
205     ArrayItem prev = get_nocount(0);
206     mark(0);
207 
208     bool is_sorted = true;
209 
210     for (size_t i = 1; i < size(); ++i)
211     {
212         ArrayItem key = get_nocount(i);
213         g_compare_count--; // dont count the following comparison
214         if (!(prev <= key)) {
215             wxLogError(_T("Result of sorting algorithm is incorrect!"));
216             is_sorted = false;
217             break;
218         }
219         mark(i);
220         prev = key;
221     }
222 
223     unmark_all();
224 
225     return (m_is_sorted = is_sorted);
226 }
227 
SetCalcInversions(bool on)228 void SortArray::SetCalcInversions(bool on)
229 {
230     // toggle boolean
231     m_calc_inversions = on;
232 
233     if (!m_calc_inversions)
234         m_inversions = -1;
235 }
236 
ToggleCalcInversions()237 void SortArray::ToggleCalcInversions()
238 {
239     // toggle boolean
240     SetCalcInversions(!m_calc_inversions);
241 }
242 
RecalcInversions()243 void SortArray::RecalcInversions()
244 {
245     if (!m_calc_inversions) {
246         m_inversions = -1;
247         return;
248     }
249 
250     unsigned int inversions = 0;
251 
252     for (size_t i = 0; i < size(); ++i)
253     {
254         const ArrayItem& a = direct(i);
255 
256         for (size_t j = i+1; j < size(); ++j)
257         {
258             const ArrayItem& b = direct(j);
259 
260             if ( a.greater_direct(b) )
261             {
262                 inversions++;
263             }
264         }
265     }
266 
267     m_inversions = inversions;
268 }
269 
UpdateInversions(size_t i,size_t j)270 void SortArray::UpdateInversions(size_t i, size_t j)
271 {
272     if (!m_calc_inversions) {
273         m_inversions = -1;
274         return;
275     }
276     if (m_inversions < 0) return RecalcInversions();
277 
278     if (i == j) return;
279 
280     unsigned int lo = i, hi = j;
281     if (lo > hi) std::swap(lo, hi);
282 
283     const ArrayItem& ilo = m_array[lo];
284     const ArrayItem& ihi = m_array[hi];
285     int invdelta = 0;
286 
287     for (size_t k = lo + 1; k < hi; ++k)
288     {
289         if (m_array[k].less_direct(ilo))
290             invdelta--;
291         if (m_array[k].greater_direct(ilo))
292             invdelta++;
293 
294         if (m_array[k].less_direct(ihi))
295             invdelta++;
296         if (m_array[k].greater_direct(ihi))
297             invdelta--;
298     }
299 
300     if (ilo.less_direct(ihi))
301         invdelta++;
302     if (ilo.greater_direct(ihi))
303         invdelta--;
304 
305     m_inversions += invdelta;
306 }
307 
GetRuns() const308 size_t SortArray::GetRuns() const
309 {
310     unsigned int runs = 1;
311 
312     for (size_t i = 1; i < size(); ++i)
313     {
314         const ArrayItem& a = direct(i-1);
315         const ArrayItem& b = direct(i);
316 
317         if ( a.greater_direct(b) )
318         {
319             runs++;
320         }
321     }
322 
323     return runs;
324 }
325 
InAccessList(ssize_t idx)326 short SortArray::InAccessList(ssize_t idx)
327 {
328     if (idx < 0) return -1;
329 
330     signed color = -1;
331     signed priority = -1;
332 
333     for (std::vector<Access>::iterator it = m_access_list.begin();
334          it != m_access_list.end(); )
335     {
336         if (it->index != (size_t)idx) {
337             ++it;
338             continue;
339         }
340 
341         if (it->priority >= priority)
342         {
343             priority = it->priority;
344             color = it->color;
345         }
346 
347         if (it->sustain == 0) {
348             if (it->index == m_access1.index ||
349                 it->index == m_access2.index)
350             {
351                 ++it;
352             }
353             else
354             {
355                 it = m_access_list.erase(it);
356             }
357         }
358         else {
359             it->sustain--;
360             ++it;
361         }
362     }
363 
364     return color;
365 }
366 
InWatchList(ssize_t idx) const367 unsigned short SortArray::InWatchList(ssize_t idx) const
368 {
369     for (size_t i = 0; i < m_watch.size(); ++i)
370     {
371         if (m_watch[i].first == NULL) continue;
372 
373         // compare watched value
374         if (*m_watch[i].first != idx) continue;
375 
376         return m_watch[i].second;
377     }
378     return 0;
379 }
380 
GetIndexColor(size_t idx)381 int SortArray::GetIndexColor(size_t idx)
382 {
383     int clr;
384 
385     // select color
386     if (idx == m_access1.index)
387     {
388         clr = m_access1.color;
389     }
390     else if (idx == m_access2.index)
391     {
392         clr = m_access2.color;
393     }
394     else if ( (clr = InWatchList(idx)) != 0 )
395     {
396         // clr already set
397     }
398     else if (m_mark[idx] != 0)
399     {
400         clr = m_mark[idx];
401     }
402     else if ( (clr = InAccessList(idx)) >= 0 )
403     {
404     }
405     else
406     {
407         clr = 0;
408     }
409 
410     return clr;
411 }
412 
413 // *****************************************************************************
414