SortUtils.js 3.0 KB

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