|
|
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.