2
2
import ArrayTracer from '../../components/DataStructures/Array/Array1DTracer' ;
3
3
import MaskTracer from '../../components/DataStructures/Mask/MaskTracer' ;
4
4
import { areExpanded } from './collapseChunkPlugin' ;
5
+ import { createPopper } from '@popperjs/core' ;
5
6
6
- const BITS = 2 ;
7
+ // radix must be a power of two; we use radix 4 here but code should work
8
+ // with another radix except vis.mask.setAddBase4() would need to be
9
+ // generalised and that call deleted if radix = 2
10
+ const RADIX_BITS = 2 ;
11
+ const RADIX = 1 << RADIX_BITS ;
7
12
8
13
const SRS_BOOKMARKS = {
9
14
radix_sort : 1 ,
@@ -84,9 +89,31 @@ export default {
84
89
run ( chunker , { nodes } ) {
85
90
let A = [ ...nodes ] ;
86
91
const n = A . length ;
92
+ // Stuff for poppers, copied from MSDRadixSort.js - search for
93
+ // POPPERS: in that code for more details, though it's done
94
+ // slightly differently here:
95
+ // The first time the first chunk is called, poppers are created
96
+ // (after a delay - see MSDRadixSort.js). Subsequent times the
97
+ // first chunk is called we reset the innerHTML so the contents
98
+ // is correct (it may have changed during execution).
99
+ // Similarly, when the temp array is copied back to A we update
100
+ // the innerHTML.
101
+ let floatingBoxes = new Array ( n ) ; // XXX popper instances (rename)
102
+ let DELAY_POPPER_CREATE = 700 ;
103
+ let DELAY_POPPER_RESET = 800 ;
104
+ let DELAY_POPPER_UPDATE = 800 ;
105
+
106
+ let bits ; // number of bits used (depends on max data value)
107
+
108
+ // poppers show binary plus whatever radix we use
109
+ // XXX if radix === 2, best avoid duplication
110
+ const popperContent = ( n ) => {
111
+ return n . toString ( 2 ) . padStart ( bits , "0" ) + "<br>"
112
+ + n . toString ( RADIX ) . padStart ( bits / RADIX_BITS , "0" ) ;
113
+ }
87
114
88
- const countingSort = ( A , k , n , bits ) => {
89
- const count = Array . apply ( null , Array ( 1 << bits ) ) . map ( ( ) => 0 ) ;
115
+ const countingSort = ( A , k , n , radixBits ) => {
116
+ const count = Array . apply ( null , Array ( 1 << radixBits ) ) . map ( ( ) => 0 ) ;
90
117
let lastBit = - 1 ;
91
118
92
119
chunker . add ( SRS_BOOKMARKS . zero_counts ,
@@ -118,7 +145,7 @@ export default {
118
145
[ i , lastBit , count ]
119
146
) ;
120
147
121
- const bit = bitsAtIndex ( A [ i ] , k , bits ) ;
148
+ const bit = bitsAtIndex ( A [ i ] , k , radixBits ) ;
122
149
count [ bit ] ++ ;
123
150
124
151
chunker . add ( SRS_BOOKMARKS . add_to_count ,
@@ -216,7 +243,7 @@ export default {
216
243
} ,
217
244
[ num , i , bit , count , sortedA ]
218
245
) ;
219
- bit = bitsAtIndex ( num , k , bits ) ;
246
+ bit = bitsAtIndex ( num , k , radixBits ) ;
220
247
count [ bit ] -- ;
221
248
chunker . add ( SRS_BOOKMARKS . dec_count ,
222
249
( vis , num , i , bit , count , sortedA ) => {
@@ -242,62 +269,101 @@ export default {
242
269
}
243
270
244
271
chunker . add ( SRS_BOOKMARKS . copy ,
245
- ( vis , array , n , countLength ) => {
272
+ ( vis , array , n , countLength , bits ) => {
246
273
setArray ( vis . array , array ) ;
247
274
248
275
if ( isCountExpanded ( ) ) {
249
276
setArray ( vis . tempArray , Array . apply ( null , Array ( n ) ) . map ( ( ) => undefined ) ) ;
250
277
setArray ( vis . countArray , Array . apply ( null , Array ( countLength ) ) . map ( ( ) => undefined ) ) ;
251
278
}
279
+ // update contents of all poppers
280
+ setTimeout ( ( ) => {
281
+ for ( let idx = 0 ; idx < array . length ; idx ++ ) {
282
+ const popper = document . getElementById ( 'float_box_' + idx ) ;
283
+ popper . innerHTML = popperContent ( array [ idx ] ) ;
284
+ }
285
+ } , DELAY_POPPER_UPDATE ) ;
252
286
} ,
253
- [ sortedA , n , count . length ]
287
+ [ sortedA , n , count . length , bits ]
254
288
) ;
255
289
256
290
return sortedA ;
257
291
} ;
258
292
259
293
let maxNumber = Math . max ( ...A ) ;
260
- let maxBit = - 1 ;
294
+ let savedMax = maxNumber ;
295
+ let maxBit = 0 ;
261
296
262
297
while ( maxNumber > 0 ) {
263
298
maxNumber = Math . floor ( maxNumber / 2 ) ;
264
299
maxBit ++ ;
265
300
}
266
301
267
- let bits = 1 ;
268
-
269
- while ( bits < maxBit ) {
270
- bits *= 2 ;
271
- }
302
+ bits = maxBit + maxBit % RADIX_BITS ; // bits is multiple of radix
303
+ floatingBoxes . fill ( null ) ; // poppers: see MSDRadixSort.js
272
304
273
305
chunker . add ( SRS_BOOKMARKS . radix_sort ,
274
- ( vis , array ) => {
306
+ ( vis , array , bits ) => {
307
+ // XXX assumes radix 4
275
308
vis . mask . setAddBase4 ( true ) ; // add Base 4 display
276
309
setArray ( vis . array , array ) ;
277
310
278
311
if ( isCountExpanded ( ) ) {
279
- setArray ( vis . countArray , Array . apply ( null , Array ( 1 << BITS ) ) . map ( ( ) => undefined ) ) ;
312
+ setArray ( vis . countArray , Array . apply ( null , Array ( 1 << RADIX_BITS ) ) . map ( ( ) => undefined ) ) ;
280
313
setArray ( vis . tempArray , Array . apply ( null , Array ( n ) ) . map ( ( ) => undefined ) ) ;
281
314
}
315
+ // create poppers or reset poppers if they already exist
316
+ for ( let idx = 0 ; idx < array . length ; idx ++ ) {
317
+ if ( floatingBoxes [ idx ] === null ) {
318
+ setTimeout ( ( ) => {
319
+ const popper = document . getElementById ( 'float_box_' + idx ) ;
320
+ const slot = document . getElementById ( 'chain_' + idx ) ;
321
+ floatingBoxes [ idx ] = createPopper ( slot , popper , {
322
+ placement : "right-start" ,
323
+ strategy : "fixed" ,
324
+ modifiers : [
325
+ {
326
+ removeOnDestroy : true , // doesn't work well?
327
+ name : 'preventOverflow' ,
328
+ options : {
329
+ // XXX popper_boundary not defined for 1D
330
+ // array - maybe it should be??
331
+ boundary : document . getElementById ( 'popper_boundary' ) ,
332
+ } ,
333
+ } ,
334
+ ]
335
+ } ) ;
336
+ popper . innerHTML = popperContent ( array [ idx ] ) ;
337
+ } , DELAY_POPPER_CREATE ) ;
338
+ } else {
339
+ setTimeout ( ( ) => {
340
+ const popper = document . getElementById ( 'float_box_' + idx ) ;
341
+ popper . innerHTML = popperContent ( array [ idx ] ) ;
342
+ floatingBoxes [ idx ] . forceUpdate ( ) ;
343
+ } , DELAY_POPPER_RESET )
344
+ }
345
+ }
282
346
} ,
283
- [ nodes ]
347
+ [ nodes , bits ]
284
348
) ;
285
349
286
350
chunker . add ( SRS_BOOKMARKS . max_number ,
287
- ( vis , bits ) => {
351
+ ( vis , bits , max ) => {
352
+ // XXX could highlight max in array also?
288
353
vis . mask . setMaxBits ( bits ) ;
354
+ updateBinary ( vis , max ) ;
289
355
} ,
290
- [ bits ]
356
+ [ bits , savedMax ]
291
357
) ;
292
358
293
- for ( let k = 0 ; k < bits / BITS ; k ++ ) {
359
+ for ( let k = 0 ; k < bits / RADIX_BITS ; k ++ ) {
294
360
chunker . add ( SRS_BOOKMARKS . counting_sort_for_loop ,
295
361
vis => {
296
- updateMask ( vis , k , BITS ) ;
362
+ updateMask ( vis , k , RADIX_BITS ) ;
297
363
}
298
364
) ;
299
365
300
- A = countingSort ( A , k , n , BITS ) ;
366
+ A = countingSort ( A , k , n , RADIX_BITS ) ;
301
367
302
368
// chunker.add(SRS_BOOKMARKS.counting_sort);
303
369
}
0 commit comments