@@ -171,10 +171,11 @@ where
171171 }
172172 self [ i] . clone ( )
173173 } else {
174- // Adapt pivot sampling to relative sought rank and switch from dual-pivot to
175- // single-pivot partitioning for extreme sought ranks.
174+ // Sorted sample of 5 equally spaced elements around the center.
176175 let mut sample = [ 0 ; 5 ] ;
177176 sample_mut ( self , & mut sample) ;
177+ // Adapt pivot sampling to relative sought rank and switch from dual-pivot to
178+ // single-pivot partitioning for extreme sought ranks.
178179 let sought_rank = i as f64 / n as f64 ;
179180 if ( 0.036 ..=0.964 ) . contains ( & sought_rank) {
180181 let ( lower_index, upper_index) = if sought_rank <= 0.5 {
@@ -400,11 +401,80 @@ fn _get_many_from_sorted_mut_unchecked<A>(
400401 return ;
401402 }
402403
403- // Since there is no single sought rank to adapt pivot sampling to, the recommended skewed pivot
404- // sampling of dual-pivot Quicksort is used.
404+ // Sorted sample of 5 equally spaced elements around the center.
405405 let mut sample = [ 0 ; 5 ] ;
406406 sample_mut ( & mut array, & mut sample) ;
407- let ( lower_index, upper_index) = ( sample[ 0 ] , sample[ 2 ] ) ; // (0, 1, 2)
407+ let ( lower_index, upper_index) = if indexes. len ( ) == 1 {
408+ // Adapt pivot sampling to relative sought rank and switch from dual-pivot to single-pivot
409+ // partitioning for extreme sought ranks.
410+ let sought_rank = indexes[ 0 ] as f64 / n as f64 ;
411+ if ( 0.036 ..=0.964 ) . contains ( & sought_rank) {
412+ if sought_rank <= 0.5 {
413+ if sought_rank <= 0.153 {
414+ ( 0 , 1 ) // (0, 0, 3)
415+ } else {
416+ ( 0 , 2 ) // (0, 1, 2)
417+ }
418+ } else {
419+ if sought_rank <= 0.847 {
420+ ( 2 , 4 ) // (2, 1, 0)
421+ } else {
422+ ( 3 , 4 ) // (3, 0, 0)
423+ }
424+ }
425+ } else {
426+ let pivot_index = if sought_rank <= 0.5 {
427+ 0 // (0, 4)
428+ } else {
429+ 4 // (4, 0)
430+ } ;
431+
432+ // We partition the array with respect to the pivot value. The pivot value moves to the
433+ // new `pivot_index`.
434+ //
435+ // Elements strictly less than the pivot value have indexes < `pivot_index`.
436+ //
437+ // Elements greater than or equal the pivot value have indexes > `pivot_index`.
438+ let pivot_index = array. partition_mut ( sample[ pivot_index] ) ;
439+ let ( found_exact, split_index) = match indexes. binary_search ( & pivot_index) {
440+ Ok ( index) => ( true , index) ,
441+ Err ( index) => ( false , index) ,
442+ } ;
443+ let ( lower_indexes, upper_indexes) = indexes. split_at_mut ( split_index) ;
444+ let ( lower_values, upper_values) = values. split_at_mut ( split_index) ;
445+ let ( upper_indexes, upper_values) = if found_exact {
446+ upper_values[ 0 ] = array[ pivot_index] . clone ( ) ; // Write exactly found value.
447+ ( & mut upper_indexes[ 1 ..] , & mut upper_values[ 1 ..] )
448+ } else {
449+ ( upper_indexes, upper_values)
450+ } ;
451+
452+ // We search recursively for the values corresponding to indexes strictly less than
453+ // `pivot_index` in the lower partition.
454+ _get_many_from_sorted_mut_unchecked (
455+ array. slice_axis_mut ( Axis ( 0 ) , Slice :: from ( ..pivot_index) ) ,
456+ lower_indexes,
457+ lower_values,
458+ ) ;
459+
460+ // We search recursively for the values corresponding to indexes greater than or equal
461+ // `pivot_index` in the upper partition. Since only the upper partition of the array is
462+ // passed in, the indexes need to be shifted by length of the lower partition.
463+ upper_indexes. iter_mut ( ) . for_each ( |x| * x -= pivot_index + 1 ) ;
464+ _get_many_from_sorted_mut_unchecked (
465+ array. slice_axis_mut ( Axis ( 0 ) , Slice :: from ( pivot_index + 1 ..) ) ,
466+ upper_indexes,
467+ upper_values,
468+ ) ;
469+
470+ return ;
471+ }
472+ } else {
473+ // Since there is no single sought rank to adapt pivot sampling to, the recommended skewed
474+ // pivot sampling of dual-pivot Quicksort is used in the assumption that multiple indexes
475+ // change characteristics from Quickselect towards Quicksort.
476+ ( 0 , 2 ) // (0, 1, 2)
477+ } ;
408478
409479 // We partition the array with respect to the two pivot values. The pivot values move to the new
410480 // `lower_index` and `upper_index`.
@@ -414,8 +484,9 @@ fn _get_many_from_sorted_mut_unchecked<A>(
414484 // Elements greater than or equal the lower pivot value and less than or equal the upper pivot
415485 // value have indexes > `lower_index` and < `upper_index`.
416486 //
417- // Elements less than or equal the upper pivot value have indexes > `upper_index`.
418- let ( lower_index, upper_index) = array. dual_partition_mut ( lower_index, upper_index) ;
487+ // Elements greater than or equal the upper pivot value have indexes > `upper_index`.
488+ let ( lower_index, upper_index) =
489+ array. dual_partition_mut ( sample[ lower_index] , sample[ upper_index] ) ;
419490
420491 // We use a divide-and-conquer strategy, splitting the indexes we are searching for (`indexes`)
421492 // and the corresponding portions of the output slice (`values`) into partitions with respect to
0 commit comments