|
|
1.1 root 1:
2: /* The SortController class is the "brains" of the sorting operations. As the
3: * shepherd of a flock of sorts, it keeps an array of Sort objects. The
4: * SortController starts off the sort trials when the SortApp gets the "go!"
5: * signal. The SortController generates the data and forks off the sort
6: * threads. It keeps track of when the sorts finish, starts the next sort
7: * when the sorts are running in series, and lets the SortApp know when all
8: * the sorts have finished. Most of the controls of the Parameters panel send
9: * their target-action methods to the SortController and it sends the changes
10: * on to the sorts as necessary. The SortController is also the text delegate
11: * for the various text fields and forms of the Parameters panel. It does
12: * entry checking to make sure the user enters reasonable values.
13: *
14: * Author: Julie Zelenski, NeXT Developer Support
15: * You may freely copy, distribute and reuse the code in this example.
16: * NeXT disclaims any warranty of any kind, expressed or implied, as to
17: * its fitness for any particular use.
18: */
19:
20: #import <appkit/appkit.h>
21: #import "SortController.h"
22: #import "BubbleSort.h"
23: #import "InsertionSort.h"
24: #import "MergeSort.h"
25: #import "QuickSort.h"
26: #import "SelectionSort.h"
27: #import "ShellSort.h"
28: #import "SortApp.h"
29: #import "SortView.h"
30: #import <libc.h>
31: #import <math.h>
32: #import <mach/cthreads.h>
33:
34:
35: extern BOOL Abort; // global variable to signal abort
36:
37:
38: @implementation SortController:Object
39:
40:
41: - init;
42: /* The initialization method is called once, when unarchiving the nib section.
43: * There is only one instantiation of the SortConroller object. This method
44: * creates a new sort object for each algorithm and initializes some instance
45: * variables.
46: */
47: {
48: [super init];
49: srandom(time(0)); // Seed random for data generating
50: // Create on of each sort object
51: sort[BUBBLE_SORT] = [[BubbleSort allocFromZone:[self zone]] init];
52: sort[INSERTION_SORT] = [[InsertionSort allocFromZone:[self zone]] init];
53: sort[MERGE_SORT] = [[MergeSort allocFromZone:[self zone]] init];
54: sort[QUICK_SORT] = [[QuickSort allocFromZone:[self zone]] init];
55: sort[SELECTION_SORT] = [[SelectionSort allocFromZone:[self zone]] init];
56: sort[SHELL_SORT] = [[ShellSort allocFromZone:[self zone]] init];
57: sortOn[QUICK_SORT] = sortOn[INSERTION_SORT] = sortOn[SHELL_SORT] = YES;
58: sortOn[BUBBLE_SORT] = sortOn[SELECTION_SORT] = sortOn[MERGE_SORT] = NO;
59: numSorts = 3;
60: parallel = YES;
61: numElements = 50;
62: percentSorted = 0;
63: return self;
64: }
65:
66: #define SIZE 1 // tags for different text fields
67: #define PERCENT 2
68: #define COMPARE 3
69: #define MOVE 4
70: #define FCALL 5
71:
72:
73: /* TEXT DELEGATE METHODS */
74:
75:
76: - (BOOL)textWillEnd:textObject;
77: /* Checks to make sure that the entry in each of the text fields is reasonable.
78: * Different values are reasonable based on what the field is. You can get the
79: * field or form being edited by asking the text object for its delegate. If
80: * the value is unreasonable, reject it, and set the value back to the closest
81: * reasonable value. Then it sends the action to the target so that change is
82: * recorded.
83: */
84: {
85: BOOL entryOK = YES;
86: id form;
87: int value;
88:
89: form = [textObject delegate];
90: value = [[form selectedCell] intValue];
91:
92: switch ([form selectedTag]) {
93: case SIZE: entryOK = (value > 0 );
94: if (value>5000) [[form selectedCell] setIntValue:5000];
95: break;
96: case PERCENT: entryOK = ((value >= 0) && (value <= 100));
97: break;
98: case FCALL:
99: case MOVE:
100: case COMPARE: entryOK = (value >= 0);
101: break;
102: }
103: if (entryOK) [form sendAction:[form action] to:[form target]];
104: return (!entryOK);
105: }
106:
107:
108:
109: /* TARGET-ACTION METHODS (for controls in the Sort Parameters panel) */
110:
111: - changeAnimate:sender
112: /* Changes whether the sorts highlight their comparisions. SortController
113: * simply passes this method onto each of the sorts.
114: */
115: { int c;
116: BOOL value = (BOOL)[sender state];
117:
118: for (c = 0; c < NUM_SORT_TYPES; c++)
119: [sort[c] setAnimate:value];
120: return self;
121: }
122:
123:
124: - changeParallel:sender
125: /* Changes whether the sorts run in parallel or series. SortController needs
126: * this to know whether to launch all sort threads at once, or to wait until
127: * one has finished to start the next sort.
128: */
129: {
130: parallel = (BOOL)[sender selectedTag];
131: return self;
132: }
133:
134:
135: - changeSpeed:sender;
136: /* Changes at what speed the sorts are running. SortController simply passes
137: * this method onto each of the sorts.
138: */
139: {
140: int c, speed;
141: speed = [(Slider *)sender maxValue] - [sender intValue];
142: for (c = 0; c < NUM_SORT_TYPES; c++)
143: [sort[c] setSpeed:speed];
144: return self;
145: }
146:
147:
148: - changeSortOn:sender
149: /* Changes whether a selected sort is set to run or not. Increments or
150: * decrements the number of sorts in this trial as appropriate.
151: */
152: { BOOL on;
153:
154: on = (BOOL)[[sender selectedCell] state];
155: sortOn[[sender selectedTag]] = on;
156: if (on)
157: numSorts++;
158: else
159: numSorts--;
160: return self;
161: }
162:
163:
164:
165: - changeDataSet:sender;
166: /* Changes the size or percent sorted of a data set, uses tag to determine
167: * which.
168: */
169: {
170: switch ([sender selectedTag]) {
171: case SIZE: numElements = [sender intValue];
172: break;
173: case PERCENT: percentSorted = [sender intValue];
174: break;
175: }
176: return self;
177: }
178:
179:
180: - changeTickValue:sender;
181: /* Changes the tick cost of a certain operation, uses tag to determine which.
182: * SortController simply passes this method onto each of the sorts.
183: */
184: {
185: int c, value;
186:
187: value = [sender intValue];
188: for (c = 0; c < NUM_SORT_TYPES; c++)
189: [sort[c] setTicks:value for:[sender selectedTag]];
190: return self;
191: }
192:
193:
194:
195:
196: /* PRIVATE METHODS */
197:
198:
199: - setSortWindow:anObject
200: /* The sortWindow is where all the animation takes place.
201: */
202: {
203: sortWindow = anObject;
204: [[sortWindow contentView] setFlipped:YES];
205: return self;
206: }
207:
208:
209: - setUpWindow;
210: /* This is the method that adjusts the sorting window for a new trial. It
211: * resizes the window to fit all the needed sort views and then adds those
212: * sortviews as subviews of the content view. It removes any sortviews of
213: * sorts that aren't scheduled to run in this trial.
214: */
215: {
216: NXPoint pt = {2.0,2.0};
217: NXRect windowRect,headerRect;
218: float newHeight;
219: int c;
220:
221: [sortWindow disableFlushWindow];
222: [sortWindow getFrame:&windowRect];
223: [windowHeader getFrame:&headerRect];
224: newHeight = headerRect.size.height + numSorts*(VIEW_HEIGHT+2) + 24;
225: windowRect.origin.y += windowRect.size.height - newHeight;
226: windowRect.size.height = newHeight;
227: [sortWindow placeWindow:&windowRect];
228: [windowHeader moveTo:headerRect.origin.x :-3];
229: pt.y = headerRect.size.height;
230: for (c = 0; (c < NUM_SORT_TYPES); c++) {
231: if (sortOn[c]) {
232: [[sort[c] view] moveTo:pt.x :pt.y];
233: [[sortWindow contentView] addSubview:[sort[c] view]];
234: pt.y += VIEW_HEIGHT+2.0;
235: } else {
236: [[sort[c] view] removeFromSuperview];
237: }
238: }
239: [sortWindow display];
240: [[[sortWindow reenableFlushWindow] flushWindow] orderFront:self];
241: return self;
242: }
243:
244:
245: - (int *)newDataSet
246: /* This is the method to generate a new data set. Basically, it cycles
247: * through a loop numElements times, getting a new number for each position
248: * of the array. For completely random data, each element is randomly
249: * generated. For partially sorted data, only some of the elements are random,
250: * others are inserted in sorted position. Each time through the loop, a
251: * random number is first generated to determine whether this array element
252: * will be random. If the random number is greater than the percent sorted,
253: * it will generate a random number for that array element, otherwise it will
254: * assign an element in sorted order to that array element. */
255: {
256: int max,c,*data;
257:
258: data = (int *)calloc(numElements,sizeof(int));
259: max = floor((VIEW_HEIGHT-15.0)/(ceil((float)numElements/(VIEW_WIDTH-6))));
260: for (c = 0; c < numElements ;c++) {
261: cthread_yield(); // give the interface a change
262: if (((random() % 100) + 1) > percentSorted)
263: data[c] = (random() % (int)(max-1))+ 1;
264: else
265: data[c] = (int)(c*(max/(float)numElements)) +1;
266: }
267: return data;
268: }
269:
270:
271: - setUpData;
272: /* This method sends a message to itself to generate a new data set and
273: * then passes that data set along to the sorts who are scheduled to run
274: * so they can make a copy of it. The sorts will set up their sortviews
275: * and draw the data set. (by messaging back to main thread)
276: */
277: {
278: int c, *address;
279:
280: address = [self newDataSet];
281: for (c = 0; c < NUM_SORT_TYPES; c++) {
282: if (sortOn[c])
283: [sort[c] setSize:numElements address:address];
284: }
285: cfree(address);
286: return self;
287: }
288:
289:
290: static any_t sortInThread(aSort)
291: id aSort;
292: {
293: return [aSort sort];
294: }
295:
296:
297: - doSort;
298: /* This method is called to run a complete trial. It is launched as a thread,
299: * so from here on out, I can no longer safely draw and must message to the
300: * main thread. This method generates the data for the trial, sets up all
301: * the sort views, starts the tick counter, and then launches the sort threads.
302: */
303: { int c;
304:
305: [self setUpData];
306: if (!Abort)
307: [NXApp startTickCounter];
308: for (c = 0; c < NUM_SORT_TYPES; c++) {
309: if (sortOn[c]) {
310: cthread_detach(cthread_fork(sortInThread,sort[c]));
311: if (!parallel)
312: break;
313: }
314: }
315: return self;
316: }
317:
318:
319: static any_t doSort(self)
320: id self;
321: {
322: return [self doSort];
323: }
324:
325:
326:
327: /* PUBLIC METHODS */
328:
329: - (BOOL)startSort;
330: /* The SortApp object calls this method after the Go button is clicked.
331: * If there are no sorts to run or there is no data set, this method returns
332: * NO to the SortApp. Otherwise, I resize and set up the sorting window and
333: * launch a thread to "doSort" which generates the data and launches the sort
334: * threads. By launching a thread, this method will return almost immediately,
335: * so the user isn't locked out while I do the work.
336: */
337: {
338: if (!numSorts || !numElements)
339: return NO;
340: else {
341: sortsFinished = 0;
342: [self setUpWindow];
343: cthread_detach(cthread_fork(doSort,self));
344: return YES;
345: }
346: }
347:
348:
349: - sortFinished:(int)sortNum;
350: /* When a sort finishes, it messages back to the main thread who does the final
351: * display. The SortApp then calls this method so the SortController can
352: * launch the next sort if necessary (i.e. if the sorts are running in series).
353: * The SortController also keeps track of how many sorts still need to finish.
354: * If all sorts have finished, the SortController lets the SortApp know, so it
355: * can clean up, stop the tick counter and re-enable all the controls in the
356: * Parameters panel.
357: */
358: {
359: sortsFinished++;
360: if (sortsFinished == numSorts) // if all sorts finished,
361: [NXApp allFinished]; // let SortApp know this
362: else if (!parallel) { // if in series, launch next sort
363: while (!sortOn[++sortNum]) ;
364: [NXApp startTickCounter];
365: cthread_detach(cthread_fork(sortInThread,sort[sortNum]));
366: }
367: return self;
368: }
369:
370:
371: - findSortWithNum:(int)sortNum;
372: /* Simply returns the sort of the specified number. Called by the SortApp
373: * who has only the number of the sort, but needs the actual sort object.
374: */
375: {
376: return sort[sortNum];
377: }
378:
379:
380: @end
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.