1use once_cell::sync::Lazy;
27use std::collections::HashMap;
28use std::fmt::Debug;
29use std::sync::Mutex;
30use std::time::Instant;
31
32pub mod prelude;
33
34#[derive(Debug, Clone)]
40pub struct Config {
41 pub warmup_runs: u32,
43 pub cache_enabled: bool,
45 pub debug: bool,
47 pub smart_detection: bool,
49}
50
51impl Default for Config {
52 fn default() -> Self {
53 Self {
54 warmup_runs: 3,
55 cache_enabled: true,
56 debug: false,
57 smart_detection: true,
58 }
59 }
60}
61
62impl Config {
63 pub fn new() -> Self {
64 Self::default()
65 }
66 pub fn with_warmup(mut self, runs: u32) -> Self {
67 self.warmup_runs = runs;
68 self
69 }
70 pub fn with_cache(mut self, enabled: bool) -> Self {
71 self.cache_enabled = enabled;
72 self
73 }
74 pub fn with_debug(mut self, enabled: bool) -> Self {
75 self.debug = enabled;
76 self
77 }
78 pub fn with_smart_detection(mut self, enabled: bool) -> Self {
79 self.smart_detection = enabled;
80 self
81 }
82}
83
84#[derive(Debug, Clone)]
86pub struct SelectResult<O> {
87 pub output: O,
89 pub winner: String,
91 pub time_ns: u64,
93 pub timings: Vec<AlgoTiming>,
95 pub pattern: Option<DataPattern>,
97}
98
99#[derive(Debug, Clone)]
101pub struct AlgoTiming {
102 pub name: String,
103 pub time_ns: u64,
104}
105
106#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
112pub enum DataPattern {
113 Sorted,
115 ReverseSorted,
117 NearlySorted,
119 Random,
121 FewUnique,
123 Unknown,
125}
126
127impl DataPattern {
128 pub fn recommended_sort(&self) -> Vec<&'static str> {
130 match self {
131 DataPattern::Sorted | DataPattern::NearlySorted => {
132 vec!["insertionsort", "quicksort"]
133 }
134 DataPattern::ReverseSorted => {
135 vec!["insertionsort"]
136 }
137 DataPattern::FewUnique => {
138 vec!["radixsort", "insertionsort"]
139 }
140 DataPattern::Random => {
141 vec!["quicksort", "mergesort", "heapsort"]
142 }
143 DataPattern::Unknown => {
144 vec!["quicksort", "mergesort", "heapsort", "insertionsort"]
145 }
146 }
147 }
148}
149
150pub fn detect_pattern(data: &[i64]) -> DataPattern {
152 if data.is_empty() {
153 return DataPattern::Unknown;
154 }
155
156 let n = data.len();
157
158 let use_full_check = n <= 1000;
160 let sample_size = n.min(1000);
161 let step = if use_full_check { 1 } else { n / sample_size };
162
163 let mut unique_count = 0usize;
165 let mut seen = std::collections::HashSet::new();
166 for i in (0..n).step_by(step) {
167 if seen.insert(data[i]) {
168 unique_count += 1;
169 if unique_count > sample_size / 2 {
170 break; }
172 }
173 }
174
175 let checked = (n / step).max(1);
176 let unique_ratio = unique_count as f64 / checked as f64;
177
178 if unique_ratio < 0.30 {
179 return DataPattern::FewUnique;
180 }
181
182 let mut sorted_count = 0usize;
184 let mut reverse_count = 0usize;
185 let mut sorted_checked = 0usize;
186
187 for i in (0..n.saturating_sub(1)).step_by(step) {
188 if data[i] <= data[i + 1] {
189 sorted_count += 1;
190 }
191 if data[i] >= data[i + 1] {
192 reverse_count += 1;
193 }
194 sorted_checked += 1;
195 }
196
197 if sorted_checked == 0 {
198 sorted_checked = 1;
199 }
200 let sorted_ratio = sorted_count as f64 / sorted_checked as f64;
201 let reverse_ratio = reverse_count as f64 / sorted_checked as f64;
202
203 if sorted_ratio > 0.99 {
204 return DataPattern::Sorted;
205 }
206
207 if reverse_ratio > 0.99 {
208 return DataPattern::ReverseSorted;
209 }
210
211 if sorted_ratio > 0.80 {
212 return DataPattern::NearlySorted;
213 }
214
215 DataPattern::Random
216}
217
218pub fn pattern_name(pattern: &DataPattern) -> &'static str {
220 match pattern {
221 DataPattern::Sorted => "sorted",
222 DataPattern::ReverseSorted => "reverse sorted",
223 DataPattern::NearlySorted => "nearly sorted",
224 DataPattern::Random => "random",
225 DataPattern::FewUnique => "few unique",
226 DataPattern::Unknown => "unknown",
227 }
228}
229
230#[derive(Debug, Clone)]
236struct CacheEntry {
237 winner: String,
238 count: u32,
239}
240
241static ALGORITHM_CACHE: Lazy<Mutex<HashMap<String, CacheEntry>>> =
243 Lazy::new(|| Mutex::new(HashMap::new()));
244
245pub fn get_cached(pattern: &DataPattern) -> Option<String> {
247 let cache = ALGORITHM_CACHE.lock().ok()?;
248 let key = pattern_name(pattern);
249 cache.get(key).map(|e| e.winner.clone())
250}
251
252pub fn cache_winner(pattern: &DataPattern, winner: &str) {
254 if let Ok(mut cache) = ALGORITHM_CACHE.lock() {
255 let key = pattern_name(pattern);
256 cache.insert(
257 key.to_string(),
258 CacheEntry {
259 winner: winner.to_string(),
260 count: 1,
261 },
262 );
263 }
264}
265
266pub fn clear_cache() {
268 if let Ok(mut cache) = ALGORITHM_CACHE.lock() {
269 cache.clear();
270 }
271}
272
273pub fn cache_stats() -> (usize, Vec<(String, String, u32)>) {
275 let cache = match ALGORITHM_CACHE.lock() {
276 Ok(c) => c,
277 Err(c) => c.into_inner(),
278 };
279 let entries: Vec<_> = cache
280 .iter()
281 .map(|(k, v)| (k.clone(), v.winner.clone(), v.count))
282 .collect();
283 (cache.len(), entries)
284}
285
286pub mod sort {
291 pub fn quicksort(data: &mut [i64]) {
295 if data.len() <= 1 {
296 return;
297 }
298 let pivot = data.len() / 2;
299 data.swap(pivot, data.len() - 1);
300 let mut i = 0;
301 for j in 0..data.len() - 1 {
302 if data[j] <= data[data.len() - 1] {
303 data.swap(i, j);
304 i += 1;
305 }
306 }
307 data.swap(i, data.len() - 1);
308 quicksort(&mut data[..i]);
309 quicksort(&mut data[i + 1..]);
310 }
311
312 pub fn mergesort(data: &mut [i64]) {
314 if data.len() <= 1 {
315 return;
316 }
317 let mid = data.len() / 2;
318 let mut left = data[..mid].to_vec();
319 let mut right = data[mid..].to_vec();
320 mergesort(&mut left);
321 mergesort(&mut right);
322
323 let mut i = 0;
324 let mut j = 0;
325 let mut k = 0;
326 while i < left.len() && j < right.len() {
327 if left[i] <= right[j] {
328 data[k] = left[i];
329 i += 1;
330 } else {
331 data[k] = right[j];
332 j += 1;
333 }
334 k += 1;
335 }
336 while i < left.len() {
337 data[k] = left[i];
338 i += 1;
339 k += 1;
340 }
341 while j < right.len() {
342 data[k] = right[j];
343 j += 1;
344 k += 1;
345 }
346 }
347
348 pub fn heapsort(data: &mut [i64]) {
350 let n = data.len();
351 for i in (0..n / 2).rev() {
352 heapify(data, n, i);
353 }
354 for i in (1..n).rev() {
355 data.swap(0, i);
356 heapify(data, i, 0);
357 }
358 }
359
360 fn heapify(data: &mut [i64], heap_size: usize, root: usize) {
361 let mut largest = root;
362 let left = 2 * root + 1;
363 let right = 2 * root + 2;
364
365 if left < heap_size && data[left] > data[largest] {
366 largest = left;
367 }
368 if right < heap_size && data[right] > data[largest] {
369 largest = right;
370 }
371
372 if largest != root {
373 data.swap(root, largest);
374 heapify(data, heap_size, largest);
375 }
376 }
377
378 pub fn insertionsort(data: &mut [i64]) {
380 for i in 1..data.len() {
381 let mut j = i;
382 while j > 0 && data[j - 1] > data[j] {
383 data.swap(j - 1, j);
384 j -= 1;
385 }
386 }
387 }
388
389 pub fn radixsort(data: &mut [i64]) {
391 if data.is_empty() {
392 return;
393 }
394
395 let max = *data.iter().max().unwrap_or(&0);
396 if max < 0 {
397 return;
398 }
399
400 let mut exp = 1;
401 while max / exp > 0 {
402 counting_sort(data, exp);
403 exp *= 10;
404 }
405 }
406
407 fn counting_sort(data: &mut [i64], exp: i64) {
408 let n = data.len();
409 let mut output = vec![0i64; n];
410 let mut count = [0i64; 10];
411
412 for x in data.iter() {
413 let digit = ((x / exp) % 10) as usize;
414 count[digit] += 1;
415 }
416
417 for i in 1..10 {
418 count[i] += count[i - 1];
419 }
420
421 for i in (0..n).rev() {
422 let digit = ((data[i] / exp) % 10) as usize;
423 count[digit] -= 1;
424 output[count[digit] as usize] = data[i];
425 }
426
427 data.copy_from_slice(&output);
428 }
429}
430
431pub mod search {
436 pub fn linear(data: &[i64], target: i64) -> Option<usize> {
440 data.iter().position(|&x| x == target)
441 }
442
443 pub fn binary(data: &[i64], target: i64) -> Option<usize> {
445 if !is_sorted(data) {
446 return None;
447 }
448 binary_helper(data, target, 0, data.len())
449 }
450
451 fn binary_helper(data: &[i64], target: i64, left: usize, right: usize) -> Option<usize> {
452 if left >= right {
453 return None;
454 }
455 let mid = left + (right - left) / 2;
456 if data[mid] == target {
457 Some(mid)
458 } else if data[mid] < target {
459 binary_helper(data, target, mid + 1, right)
460 } else {
461 binary_helper(data, target, left, mid)
462 }
463 }
464
465 pub fn interpolation(data: &[i64], target: i64) -> Option<usize> {
467 if data.is_empty() || !is_sorted(data) {
468 return None;
469 }
470 if data[0] == target {
471 return Some(0);
472 }
473 if target < data[0] || target > data[data.len() - 1] {
474 return None;
475 }
476
477 let low = 0;
478 let high = data.len() - 1;
479
480 let pos = low
481 + (((target - data[low]) * (high - low) as i64) / (data[high] - data[low])) as usize;
482
483 if data[pos] == target {
484 Some(pos)
485 } else if data[pos] < target {
486 interpolation(&data[pos + 1..], target).map(|i| pos + 1 + i)
487 } else {
488 interpolation(&data[low..pos], target)
489 }
490 }
491
492 fn is_sorted(data: &[i64]) -> bool {
493 data.windows(2).all(|w| w[0] <= w[1])
494 }
495}
496
497pub mod hash {
502 pub fn fnv(data: &[u8]) -> u64 {
506 let mut hash: u64 = 0xcbf29ce484222325;
507 for &byte in data {
508 hash ^= byte as u64;
509 hash = hash.wrapping_mul(0x100000001b3);
510 }
511 hash
512 }
513
514 pub fn djb2(data: &[u8]) -> u64 {
516 let mut hash: u64 = 5381;
517 for &byte in data {
518 hash = ((hash << 5).wrapping_add(hash)).wrapping_add(byte as u64);
519 }
520 hash
521 }
522
523 pub fn simple(data: &[u8]) -> u64 {
525 let mut hash: u64 = 0;
526 for (i, &byte) in data.iter().enumerate() {
527 hash = hash.wrapping_add((byte as u64).wrapping_mul(31_u64.wrapping_pow(i as u32)));
528 }
529 hash
530 }
531}
532
533pub struct SortFamily;
539
540impl SortFamily {
541 pub fn standard() -> Vec<(&'static str, AlgoFn)> {
543 vec![
544 ("quicksort", sort::quicksort),
545 ("mergesort", sort::mergesort),
546 ("heapsort", sort::heapsort),
547 ("insertionsort", sort::insertionsort),
548 ]
549 }
550
551 pub fn all() -> Vec<(&'static str, AlgoFn)> {
553 let mut all = Self::standard();
554 all.push(("radixsort", sort::radixsort));
555 all
556 }
557}
558
559pub type SearchFn = fn(&[i64], i64) -> Option<usize>;
561
562pub struct SearchFamily;
564
565impl SearchFamily {
566 pub fn standard() -> Vec<(&'static str, SearchFn)> {
568 vec![
569 ("linear", search::linear),
570 ("binary", search::binary),
571 ("interpolation", search::interpolation),
572 ]
573 }
574}
575
576pub type HashFn = fn(&[u8]) -> u64;
578
579pub struct HashFamily;
581
582impl HashFamily {
583 pub fn standard() -> Vec<(&'static str, HashFn)> {
585 vec![
586 ("fnv", hash::fnv),
587 ("djb2", hash::djb2),
588 ("simple", hash::simple),
589 ]
590 }
591}
592
593pub mod algo {
595 use super::*;
596 pub fn select(
597 algos: Vec<(&str, AlgoFn)>,
598 data: &mut [i64],
599 config: Config,
600 ) -> SelectResult<Vec<i64>> {
601 super::select(algos, data, config)
602 }
603}
604
605pub type AlgoFn = fn(&mut [i64]);
611
612pub fn select(
614 algos: Vec<(&str, AlgoFn)>,
615 data: &mut [i64],
616 config: Config,
617) -> SelectResult<Vec<i64>> {
618 let pattern = if config.smart_detection {
620 Some(detect_pattern(data))
621 } else {
622 None
623 };
624
625 if config.debug {
626 if let Some(p) = &pattern {
627 println!("Detected pattern: {:?}", p);
628 }
629 }
630
631 if config.cache_enabled {
633 if let Some(p) = &pattern {
634 if let Some(cached_winner) = get_cached(p) {
635 for (name, algo_fn) in &algos {
637 if name == &cached_winner {
638 let mut d = data.to_vec();
639 let start = Instant::now();
640 algo_fn(&mut d);
641 let elapsed = start.elapsed().as_nanos() as u64;
642
643 if config.debug {
644 println!("Using cached winner: {}", cached_winner);
645 }
646
647 return SelectResult {
648 output: d,
649 winner: cached_winner.to_string(),
650 time_ns: elapsed,
651 timings: vec![AlgoTiming {
652 name: cached_winner.to_string(),
653 time_ns: elapsed,
654 }],
655 pattern,
656 };
657 }
658 }
659 }
660 }
661 }
662
663 let mut timings = Vec::new();
665 let mut best_time = u64::MAX;
666 let mut best_name = "";
667 let mut best_result = data.to_vec();
668
669 for (name, algo_fn) in &algos {
670 let mut d = data.to_vec();
671
672 for _ in 0..config.warmup_runs {
674 let mut warmup = data.to_vec();
675 algo_fn(&mut warmup);
676 }
677
678 let start = Instant::now();
680 algo_fn(&mut d);
681 let elapsed = start.elapsed().as_nanos() as u64;
682
683 timings.push(AlgoTiming {
684 name: name.to_string(),
685 time_ns: elapsed,
686 });
687
688 if config.debug {
689 println!(" {}: {}ns", name, elapsed);
690 }
691
692 if elapsed < best_time {
693 best_time = elapsed;
694 best_name = name;
695 best_result = d;
696 }
697 }
698
699 if config.cache_enabled {
701 if let Some(p) = &pattern {
702 cache_winner(p, best_name);
703 }
704 }
705
706 SelectResult {
707 output: best_result,
708 winner: best_name.to_string(),
709 time_ns: best_time,
710 timings,
711 pattern,
712 }
713}
714
715pub fn select_search(data: &[i64], target: i64) -> (Option<usize>, String, u64) {
717 let algos = [
718 ("linear", search::linear as fn(&[i64], i64) -> Option<usize>),
719 ("binary", search::binary),
720 ("interpolation", search::interpolation),
721 ];
722
723 let mut best_time = u64::MAX;
724 let mut best_result = None;
725 let mut best_name = "";
726
727 for (name, algo_fn) in &algos {
728 let start = Instant::now();
729 let result = algo_fn(data, target);
730 let elapsed = start.elapsed().as_nanos() as u64;
731
732 if elapsed < best_time {
733 best_time = elapsed;
734 best_result = result;
735 best_name = name;
736 }
737 }
738
739 (best_result, best_name.to_string(), best_time)
740}
741
742pub fn select_hash(data: &[u8]) -> (u64, String, u64) {
744 let algos = [
745 ("fnv", hash::fnv as fn(&[u8]) -> u64),
746 ("djb2", hash::djb2),
747 ("simple", hash::simple),
748 ];
749
750 let mut best_time = u64::MAX;
751 let mut best_result = 0u64;
752 let mut best_name = "";
753
754 for (name, algo_fn) in &algos {
755 let start = Instant::now();
756 let result = algo_fn(data);
757 let elapsed = start.elapsed().as_nanos() as u64;
758
759 if elapsed < best_time {
760 best_time = elapsed;
761 best_result = result;
762 best_name = name;
763 }
764 }
765
766 (best_result, best_name.to_string(), best_time)
767}