SortUtils.js 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. // Hybrid radix sort from
  2. // - https://gist.github.com/sciecode/93ed864dd77c5c8803c6a86698d68dab
  3. // - https://github.com/mrdoob/three.js/pull/27202#issuecomment-1817640271
  4. const POWER = 3;
  5. const BIT_MAX = 32;
  6. const BIN_BITS = 1 << POWER;
  7. const BIN_SIZE = 1 << BIN_BITS;
  8. const BIN_MAX = BIN_SIZE - 1;
  9. const ITERATIONS = BIT_MAX / BIN_BITS;
  10. const bins = new Array( ITERATIONS );
  11. const bins_buffer = new ArrayBuffer( ( ITERATIONS + 1 ) * BIN_SIZE * 4 );
  12. let c = 0;
  13. for ( let i = 0; i < ( ITERATIONS + 1 ); i ++ ) {
  14. bins[ i ] = new Uint32Array( bins_buffer, c, BIN_SIZE );
  15. c += BIN_SIZE * 4;
  16. }
  17. const defaultGet = ( el ) => el;
  18. export const radixSort = ( arr, opt ) => {
  19. const len = arr.length;
  20. const options = opt || {};
  21. const aux = options.aux || new arr.constructor( len );
  22. const get = options.get || defaultGet;
  23. const data = [ arr, aux ];
  24. let compare, accumulate, recurse;
  25. if ( options.reversed ) {
  26. compare = ( a, b ) => a < b;
  27. accumulate = ( bin ) => {
  28. for ( let j = BIN_SIZE - 2; j >= 0; j -- )
  29. bin[ j ] += bin[ j + 1 ];
  30. };
  31. recurse = ( cache, depth, start ) => {
  32. let prev = 0;
  33. for ( let j = BIN_MAX; j >= 0; j -- ) {
  34. const cur = cache[ j ], diff = cur - prev;
  35. if ( diff != 0 ) {
  36. if ( diff > 32 )
  37. radixSortBlock( depth + 1, start + prev, diff );
  38. else
  39. insertionSortBlock( depth + 1, start + prev, diff );
  40. prev = cur;
  41. }
  42. }
  43. };
  44. } else {
  45. compare = ( a, b ) => a > b;
  46. accumulate = ( bin ) => {
  47. for ( let j = 1; j < BIN_SIZE; j ++ )
  48. bin[ j ] += bin[ j - 1 ];
  49. };
  50. recurse = ( cache, depth, start ) => {
  51. let prev = 0;
  52. for ( let j = 0; j < BIN_SIZE; j ++ ) {
  53. const cur = cache[ j ], diff = cur - prev;
  54. if ( diff != 0 ) {
  55. if ( diff > 32 )
  56. radixSortBlock( depth + 1, start + prev, diff );
  57. else
  58. insertionSortBlock( depth + 1, start + prev, diff );
  59. prev = cur;
  60. }
  61. }
  62. };
  63. }
  64. const insertionSortBlock = ( depth, start, len ) => {
  65. const a = data[ depth & 1 ];
  66. const b = data[ ( depth + 1 ) & 1 ];
  67. for ( let j = start + 1; j < start + len; j ++ ) {
  68. const p = a[ j ], t = get( p );
  69. let i = j;
  70. while ( i > 0 ) {
  71. if ( compare( get( a[ i - 1 ] ), t ) )
  72. a[ i ] = a[ -- i ];
  73. else
  74. break;
  75. }
  76. a[ i ] = p;
  77. }
  78. if ( ( depth & 1 ) == 1 ) {
  79. for ( let i = start; i < start + len; i ++ )
  80. b[ i ] = a[ i ];
  81. }
  82. };
  83. const radixSortBlock = ( depth, start, len ) => {
  84. const a = data[ depth & 1 ];
  85. const b = data[ ( depth + 1 ) & 1 ];
  86. const shift = ( 3 - depth ) << POWER;
  87. const end = start + len;
  88. const cache = bins[ depth ];
  89. const bin = bins[ depth + 1 ];
  90. bin.fill( 0 );
  91. for ( let j = start; j < end; j ++ )
  92. bin[ ( get( a[ j ] ) >> shift ) & BIN_MAX ] ++;
  93. accumulate( bin );
  94. cache.set( bin );
  95. for ( let j = end - 1; j >= start; j -- )
  96. b[ start + -- bin[ ( get( a[ j ] ) >> shift ) & BIN_MAX ] ] = a[ j ];
  97. if ( depth == ITERATIONS - 1 ) return;
  98. recurse( cache, depth, start );
  99. };
  100. radixSortBlock( 0, 0, len );
  101. };