eytzinger_interpolation/lib.rs
1//! This crate implements the "eytzinger" (aka BFS) array layout where
2//! a binary search tree is stored by layer (instead of as a sorted array).
3//! This can have significant performance benefits
4//! (see [Khuong, Paul-Virak, and Pat Morin. "Array layouts for comparison-based searching."][1]).
5//!
6//! # Usage
7//!
8//! ```
9//! use eytzinger_interpolation::SliceExt;
10//! let mut data = [0, 1, 2, 3, 4, 5, 6];
11//! data.eytzingerize(&mut eytzinger_interpolation::permutation::InplacePermutator);
12//! assert_eq!(data, [3, 1, 5, 0, 2, 4, 6]);
13//! assert_eq!(data.eytzinger_search(&5), Some(2));
14//! assert_eq!(data.eytzinger_search_by(|x| x.cmp(&6)), Some(6));
15//! ```
16//!
17//! [1]: https://arxiv.org/pdf/1509.05053.pdf
18
19#![warn(missing_docs, missing_debug_implementations)]
20
21use permutation::*;
22use std::borrow::Borrow;
23use std::cmp::{Ord, Ordering};
24
25/// The basic building blocks this crate is made of.
26pub mod foundation {
27 /// Given an array size (`n`), tree layer index (`ipk`) and element index (`li`),
28 /// this function computes the index of this value in a sorted array.
29 ///
30 /// This is basically the core of this crate, everything else is trivial.
31 /// Also, you usually want to use `PermutationGenerator` instead for convenience.
32 ///
33 /// # How it works
34 ///
35 /// This computes the magic function:
36 ///
37 /// ```text
38 /// f(n, k) = 2^floor(log2(n+1)) * 2k - max(0, (1+k) * 2^floor(log2(n+1)) - (n + 1)) - 1
39 /// (where n ∈ ℕ, k ∈ [0, 1])
40 /// ```
41 ///
42 /// Because this is integer math: `k = zk * 2^-ipk`
43 /// And because we only care about certain values of zk: `zk = li * 2 + 1`
44 ///
45 /// Even though I discovered this on my own and am quite certain that it's correct,
46 /// I only have a very vague feeling about **why** it works. If you want to understand this
47 /// (you really don't!), have a look at this sequence:
48 ///
49 /// ```text
50 /// a_n = (2n - 2^floor(log2(2n)) + 1) / 2^floor(log2(2n))
51 /// (1/2, 1/4, 3/4, 1/8, 3/8, 5/8, 7/8, 1/16, 3/16, 5/16, 7/16, 9/16, 11/16, 13/16, 15/16, ...)
52 /// ```
53 ///
54 /// The basic idea is that this sequence basically establishes a mapping between a sorted array
55 /// and an eytzinger array: If you look at the plot sideways (literally), you can see the tree
56 /// with all its layers. The only secret sauce here is `2^floor(log2(x))` which is elementary to
57 /// get exponentially growing windows (to build tree layers).
58 #[inline]
59 pub fn get_permutation_element_by_node(n: usize, ipk: usize, li: usize) -> usize {
60 let zk = li * 2 + 1;
61 // k = zk * 2^-ipk
62
63 let last_power_of_two = (n + 2).next_power_of_two() / 2;
64
65 let y = (last_power_of_two >> (ipk - 1)) * zk;
66 let kp = y >> 1;
67 let x = kp + last_power_of_two; // (1+k) * last_power_of_two
68 let x = x.saturating_sub(n + 1);
69
70 //println!("n={} x={} y={} z={} kp={} lpot={}", n, x,y,z, kp, last_power_of_two);
71 y - x - 1
72 }
73
74 /// Converts an index in an eytzinger array to the corresponding tree coordinates `(ipk, li)`.
75 #[inline]
76 pub fn index_to_node(i: usize) -> (usize, usize) {
77 let ipk = (i + 2).next_power_of_two().trailing_zeros() as usize;
78 let li = i + 1 - (1 << (ipk - 1));
79 (ipk, li)
80 }
81
82 /// Given an array size (`n`) and an index into the eytzinger array (`ì`),
83 /// this function computes the index of this value in a sorted array.
84 ///
85 /// This is simply `index_to_node` + `get_permutation_element_by_node`.
86 #[inline]
87 pub fn get_permutation_element(n: usize, i: usize) -> usize {
88 let (ipk, li) = index_to_node(i);
89 get_permutation_element_by_node(n, ipk, li)
90 }
91}
92
93/// Abstractions around applying generic permutations using generic implementations.
94///
95/// You should pick one that matches your use case.
96pub mod permutation {
97 use std::iter::{Cloned, Enumerate};
98 use std::slice::Iter;
99
100 /// A generic permutation.
101 pub trait Permutation {
102 /// An iterator through the permutation.
103 /// This may be more efficient than indexing a counter.
104 type Iter: Iterator<Item = usize>;
105
106 /// Get an iterator.
107 fn iterable(&self) -> Self::Iter;
108 /// Index into this permutation.
109 fn index(&self, i: usize) -> usize;
110 }
111
112 impl<'a> Permutation for &'a [usize] {
113 type Iter = Cloned<Iter<'a, usize>>;
114
115 #[inline]
116 fn iterable(&self) -> Self::Iter {
117 self.iter().cloned()
118 }
119
120 #[inline]
121 fn index(&self, i: usize) -> usize {
122 self[i]
123 }
124 }
125
126 /// A generic permutator.
127 pub trait Permutator<T, P: ?Sized + Permutation> {
128 /// Applies the given permutation to the given array.
129 fn permute(&mut self, data: &mut [T], permutation: &P);
130 }
131
132 /// Simple permutator that does not allocate.
133 ///
134 /// Worst-case runtime is in `O(n^2)`, so you should only use this for small permutations.
135 #[derive(Clone, Copy, Debug, Default)]
136 pub struct InplacePermutator;
137
138 impl<T, P: ?Sized + Permutation> Permutator<T, P> for InplacePermutator {
139 #[inline]
140 fn permute(&mut self, data: &mut [T], permutation: &P) {
141 for (i, mut p) in permutation.iterable().enumerate() {
142 while p < i {
143 p = permutation.index(p);
144 }
145
146 if p > i {
147 data.swap(i, p);
148 }
149 }
150 }
151 }
152
153 /// Simple permutator that stack-allocates a copy of the data (using recursion).
154 ///
155 /// Worst-case runtime is `O(n)`, but this takes `O(n)` stack space so it WILL NOT work for large permutations.
156 #[derive(Clone, Copy, Debug, Default)]
157 pub struct StackCopyPermutator;
158
159 fn recursive_permute<T: Clone, P: ?Sized + Permutation>(
160 data: &mut [T],
161 permutation: &mut Enumerate<P::Iter>,
162 ) {
163 if let Some((i, p)) = permutation.next() {
164 let item = data[p].clone();
165 recursive_permute::<T, P>(data, permutation);
166 data[i] = item;
167 }
168 }
169
170 impl<T: Clone, P: ?Sized + Permutation> Permutator<T, P> for StackCopyPermutator {
171 #[inline]
172 fn permute(&mut self, data: &mut [T], permutation: &P) {
173 let mut iter = permutation.iterable().enumerate();
174 recursive_permute::<T, P>(data, &mut iter);
175 }
176 }
177
178 /// Simple permutator that heap-allocates a copy of the data.
179 ///
180 /// Worst-case runtime is `O(n)`, taking `O(n)` heap space in a reusable buffer.
181 /// This is an acceptable permutator for large permutations, provided that the data
182 /// is (efficiently) cloneable.
183 #[derive(Debug, Default)]
184 pub struct HeapCopyPermutator<T> {
185 buffer: Vec<T>,
186 }
187
188 impl<T: Clone, P: ?Sized + Permutation> Permutator<T, P> for HeapCopyPermutator<T> {
189 #[inline]
190 fn permute(&mut self, data: &mut [T], permutation: &P) {
191 self.buffer.clear();
192 self.buffer
193 .extend(permutation.iterable().map(|i| data[i].clone()));
194 for (i, t) in self.buffer.drain(..).enumerate() {
195 data[i] = t;
196 }
197 }
198 }
199
200 /// Permutator that uses an auxiliary heap buffer to ensure linear runtime.
201 ///
202 /// Worst-case runtime is `O(n)`, taking `O(n)` heap space in a reusable buffer.
203 /// The buffer we allocate uses exactly `sizeof(usize) * data.len()` bytes.
204 /// This is a decent permutator for large permutations.
205 #[derive(Debug, Default)]
206 #[cfg(feature = "heap-permutator")]
207 pub struct HeapPermutator {
208 buffer: Vec<Option<nonmax::NonMaxUsize>>,
209 }
210
211 #[cfg(feature = "heap-permutator")]
212 impl<T, P: ?Sized + Permutation> Permutator<T, P> for HeapPermutator {
213 #[inline]
214 fn permute(&mut self, data: &mut [T], permutation: &P) {
215 use std::convert::TryInto;
216 self.buffer.clear();
217 self.buffer.resize(data.len(), None);
218 for mut i in 0..data.len() {
219 let mut j = permutation.index(i);
220 if j < i {
221 j = self.buffer[j].take().unwrap().get();
222 }
223 data.swap(i, j);
224 if let Some(x) = self.buffer[i].take() {
225 i = x.get();
226 }
227
228 if j != i {
229 self.buffer[j] = Some(i.try_into().unwrap());
230 self.buffer[i] = Some(j.try_into().unwrap());
231 }
232 }
233 }
234 }
235
236 /// Permutator that uses an auxiliary heap buffer to ensure linear runtime.
237 ///
238 /// Worst-case runtime is `O(n)`, worst-case memory usage is also `O(n)`.
239 /// The difference to `HeapPermutator` is that this implementation uses a `HashMap`
240 /// instead of a `Vec` for storage. Hence, the exact the memory usage can no longer be
241 /// predicted - but in return, it should be lower in most cases.
242 /// This is a decent permutator for large permutations.
243 #[derive(Debug, Default)]
244 #[cfg(feature = "heap-permutator-sparse")]
245 pub struct SparseHeapPermutator {
246 buffer: std::collections::HashMap<usize, usize, nohash_hasher::BuildNoHashHasher<usize>>,
247 }
248
249 #[cfg(feature = "heap-permutator-sparse")]
250 impl<T, P: ?Sized + Permutation> Permutator<T, P> for SparseHeapPermutator {
251 #[inline]
252 fn permute(&mut self, data: &mut [T], permutation: &P) {
253 self.buffer.clear();
254 for mut i in 0..data.len() {
255 let mut j = permutation.index(i);
256 if j < i {
257 j = self.buffer.remove(&j).unwrap();
258 }
259 data.swap(i, j);
260 if let Some(x) = self.buffer.remove(&i) {
261 i = x;
262 }
263
264 if j != i {
265 self.buffer.insert(j, i);
266 self.buffer.insert(i, j);
267 }
268 }
269 }
270 }
271}
272
273/// Generates a permutation that transforms a sorted array into an eytzinger array.
274///
275/// This is an iterator which yields a permutation (indexes into the sorted array)
276/// in the order of an eytzinger array.
277#[derive(Clone, Debug)]
278pub struct PermutationGenerator {
279 size: usize,
280 ipk: usize,
281 li: usize,
282}
283
284impl PermutationGenerator {
285 /// Generate a new permutation for a sorted array of a given size.
286 #[inline]
287 pub fn new(size: usize) -> PermutationGenerator {
288 PermutationGenerator {
289 size,
290 ipk: 1,
291 li: 0,
292 }
293 }
294}
295
296impl Iterator for PermutationGenerator {
297 type Item = usize;
298
299 #[inline]
300 fn next(&mut self) -> Option<usize> {
301 let k2 = 1 << (self.ipk - 1);
302
303 if k2 + self.li - 1 >= self.size {
304 return None;
305 }
306
307 if self.li >= k2 {
308 self.li = 0;
309 self.ipk += 1;
310 }
311
312 let li = self.li;
313 self.li += 1;
314 Some(foundation::get_permutation_element_by_node(
315 self.size, self.ipk, li,
316 ))
317 }
318
319 #[inline]
320 fn size_hint(&self) -> (usize, Option<usize>) {
321 let k2 = 1 << (self.ipk - 1);
322 let size = self.size - (k2 + self.li - 1);
323 (size, Some(size))
324 }
325}
326impl ExactSizeIterator for PermutationGenerator {}
327
328impl Permutation for PermutationGenerator {
329 type Iter = PermutationGenerator;
330
331 #[inline]
332 fn iterable(&self) -> PermutationGenerator {
333 self.clone()
334 }
335 #[inline]
336 fn index(&self, i: usize) -> usize {
337 foundation::get_permutation_element(self.size, i)
338 }
339}
340
341/// Converts a sorted array to its eytzinger representation.
342///
343/// # Example
344///
345/// ```rust
346/// let mut data = [0, 1, 2, 3, 4, 5, 6];
347/// eytzinger_interpolation::eytzingerize(&mut data, &mut eytzinger_interpolation::permutation::InplacePermutator);
348/// assert_eq!(data, [3, 1, 5, 0, 2, 4, 6]);
349/// ```
350#[inline]
351pub fn eytzingerize<T, P: Permutator<T, PermutationGenerator>>(data: &mut [T], permutator: &mut P) {
352 let len = data.len();
353 permutator.permute(data, &PermutationGenerator::new(len))
354}
355
356/// Eytzinger extension methods for slices.
357pub trait SliceExt<T> {
358 /// Converts an already sorted array to its eytzinger representation.
359 ///
360 /// # Example
361 ///
362 /// ```rust
363 /// use eytzinger_interpolation::SliceExt;
364 /// let mut data = [0, 1, 2, 3, 4, 5, 6];
365 /// data.eytzingerize(&mut eytzinger_interpolation::permutation::InplacePermutator);
366 /// assert_eq!(data, [3, 1, 5, 0, 2, 4, 6]);
367 /// ```
368 fn eytzingerize<P: Permutator<T, PermutationGenerator>>(&mut self, permutator: &mut P);
369
370 /// Binary searches this eytzinger slice for a given element.
371 ///
372 /// If the value is found then `Some` is returned, containing the index of the matching element;
373 /// if the value is not found then `None` is returned.
374 ///
375 /// # Example
376 ///
377 /// ```rust
378 /// use eytzinger_interpolation::SliceExt;
379 /// let s = [3, 1, 5, 0, 2, 4, 6];
380 /// assert_eq!(s.eytzinger_search(&5), Some(2));
381 /// assert_eq!(s.eytzinger_search(&6), Some(6));
382 /// assert_eq!(s.eytzinger_search(&7), None);
383 /// ```
384 fn eytzinger_search<Q: ?Sized>(&self, x: &Q) -> Option<usize>
385 where
386 Q: Ord,
387 T: Borrow<Q>;
388
389 /// Binary searches this eytzinger slice with a comparator function.
390 ///
391 /// The comparator function should implement an order consistent with the sort order
392 /// of the underlying eytzinger slice, returning an order code that indicates whether
393 /// its argument is `Less`, `Equal` or `Greater` than the desired target.
394 ///
395 /// If a matching value is found then `Some` is returned, containing the index of the
396 /// matching element; if no match is found then `None` is returned.
397 ///
398 /// # Examples
399 ///
400 /// ```rust
401 /// use eytzinger_interpolation::SliceExt;
402 /// let s = [3, 1, 5, 0, 2, 4, 6];
403 /// assert_eq!(s.eytzinger_search_by(|x| x.cmp(&5)), Some(2));
404 /// assert_eq!(s.eytzinger_search_by(|x| x.cmp(&6)), Some(6));
405 /// assert_eq!(s.eytzinger_search_by(|x| x.cmp(&7)), None);
406 /// ```
407 fn eytzinger_search_by<'a, F>(&'a self, f: F) -> Option<usize>
408 where
409 F: FnMut(&'a T) -> Ordering,
410 T: 'a;
411
412 /// Binary searches this sorted slice with a key extraction function.
413 ///
414 /// Assumes that the slice is eytzinger-sorted by the key, for instance with
415 /// `slice::sort_by_key` combined with `eytzinger::eytzingerize` using the
416 /// same key extraction function.
417 ///
418 /// If a matching value is found then `Some` is returned, containing the index of the
419 /// matching element; if no match is found then `None` is returned.
420 ///
421 /// # Examples
422 ///
423 /// ```rust
424 /// use eytzinger_interpolation::SliceExt;
425 /// let s = [(3, 'd'), (1, 'b'), (5, 'f'), (0, 'a'), (2, 'c'), (4, 'e'), (6, 'g')];
426 /// assert_eq!(s.eytzinger_search_by_key(&'f', |&(_, b)| b), Some(2));
427 /// assert_eq!(s.eytzinger_search_by_key(&'g', |&(_, b)| b), Some(6));
428 /// assert_eq!(s.eytzinger_search_by_key(&'x', |&(_, b)| b), None);
429 /// ```
430 fn eytzinger_search_by_key<'a, B, F, Q: ?Sized>(&'a self, b: &Q, f: F) -> Option<usize>
431 where
432 B: Borrow<Q>,
433 F: FnMut(&'a T) -> B,
434 Q: Ord,
435 T: 'a;
436
437 /// Binary searches this eytzinger slice with a comparator function for interpolation.
438 ///
439 /// Called "interpolative" search because it's useful for when you need to interpolate to a value between two values in the array.
440 ///
441 /// The comparator function should implement an order consistent with the sort order
442 /// of the underlying eytzinger slice, returning an order code that indicates whether
443 /// its argument is `Less`, `Equal` or `Greater` than the desired target.
444 ///
445 /// The first return value is the index of the highest value that's less than or equal to the target value.
446 /// The second return value is the index of the lowest value that's greater than to the target value.
447 ///
448 /// # Examples
449 ///
450 /// ```rust
451 /// use eytzinger_interpolation::SliceExt;
452 /// let s = [3, 1, 5, 0, 2, 4, 6];
453 /// assert_eq!(s.eytzinger_interpolative_search_by(|x| x.cmp(&3)), (Some(0_usize), Some(5_usize)));
454 /// assert_eq!(s.eytzinger_interpolative_search_by(|x| x.cmp(&5)), (Some(2_usize), Some(6_usize)));
455 /// assert_eq!(s.eytzinger_interpolative_search_by(|x| x.cmp(&6)), (Some(6_usize), None));
456 /// assert_eq!(s.eytzinger_interpolative_search_by(|x| x.cmp(&7)), (Some(6_usize), None));
457 /// assert_eq!(s.eytzinger_interpolative_search_by(|x| x.cmp(&0)), (Some(3_usize), Some(1_usize)));
458 /// assert_eq!(s.eytzinger_interpolative_search_by(|x| x.cmp(&-1)), (None, Some(3_usize)));
459 /// assert_eq!(s.eytzinger_interpolative_search_by(|x| (*x as f32).partial_cmp(&3.5).unwrap()), (Some(0_usize), Some(5_usize)));
460 /// ```
461 fn eytzinger_interpolative_search_by<'a, F>(&'a self, f: F) -> (Option<usize>, Option<usize>)
462 where
463 F: FnMut(&'a T) -> Ordering,
464 T: 'a;
465
466 /// Binary searches this eytzinger slice for interpolation.
467 ///
468 /// The first return value is the index of the highest value that's less than or equal to the target value.
469 /// The second return value is the index of the lowest value that's greater than to the target value.
470 ///
471 /// # Examples
472 ///
473 /// ```rust
474 /// use eytzinger_interpolation::SliceExt;
475 /// let s = [3, 1, 5, 0, 2, 4, 6];
476 /// assert_eq!(s.eytzinger_interpolative_search(&3), (Some(0_usize), Some(5_usize)));
477 /// assert_eq!(s.eytzinger_interpolative_search(&5), (Some(2_usize), Some(6_usize)));
478 /// assert_eq!(s.eytzinger_interpolative_search(&6), (Some(6_usize), None));
479 /// assert_eq!(s.eytzinger_interpolative_search(&7), (Some(6_usize), None));
480 /// assert_eq!(s.eytzinger_interpolative_search(&0), (Some(3_usize), Some(1_usize)));
481 /// assert_eq!(s.eytzinger_interpolative_search(&-1), (None, Some(3_usize)));
482 /// ```
483 fn eytzinger_interpolative_search<Q: ?Sized>(&self, x: &Q) -> (Option<usize>, Option<usize>)
484 where
485 Q: Ord,
486 T: Borrow<Q>;
487
488 /// Binary searches this sorted slice with a key extraction function for interpolation.
489 ///
490 /// Assumes that the slice is eytzinger-sorted by the key, for instance with
491 /// `slice::sort_by_key` combined with `eytzinger::eytzingerize` using the
492 /// same key extraction function.
493 ///
494 /// The first return value is the index of the highest value that's less than or equal to the target value.
495 /// The second return value is the index of the lowest value that's greater than to the target value.
496 ///
497 /// # Examples
498 ///
499 /// ```rust
500 /// use eytzinger_interpolation::SliceExt;
501 /// let s = [(3, 'd'), (1, 'b'), (5, 'f'), (0, 'a'), (2, 'c'), (4, 'e'), (6, 'g')];
502 /// assert_eq!(s.eytzinger_interpolative_search_by_key(&'d', |&(_, b)| b), (Some(0), Some(5)));
503 /// assert_eq!(s.eytzinger_interpolative_search_by_key(&'f', |&(_, b)| b), (Some(2), Some(6)));
504 /// assert_eq!(s.eytzinger_interpolative_search_by_key(&'g', |&(_, b)| b), (Some(6), None));
505 /// assert_eq!(s.eytzinger_interpolative_search_by_key(&'x', |&(_, b)| b), (Some(6), None));
506 /// ```
507 fn eytzinger_interpolative_search_by_key<'a, B, F, Q: ?Sized>(&'a self, b: &Q, f: F) -> (Option<usize>, Option<usize>)
508 where
509 B: Borrow<Q>,
510 F: FnMut(&'a T) -> B,
511 Q: Ord,
512 T: 'a;
513
514}
515
516/// Binary searches this eytzinger slice with a comparator function.
517///
518/// Called "interpolative" search because it's useful for when you need to interpolate to a value between two values in the array.
519///
520/// The comparator function should implement an order consistent with the sort order
521/// of the underlying eytzinger slice, returning an order code that indicates whether
522/// its argument is `Less`, `Equal` or `Greater` than the desired target.
523///
524/// The first return value is the index of the highest value that's less than or equal to the target value.
525/// The second return value is the index of the lowest value that's greater than to the target value.
526///
527/// # Examples
528///
529/// ```rust
530/// use eytzinger_interpolation::eytzinger_interpolative_search_by;
531/// let s = [3, 1, 5, 0, 2, 4, 6];
532/// assert_eq!(eytzinger_interpolative_search_by(&s, |x| x.cmp(&3)), (Some(0_usize), Some(5_usize)));
533/// assert_eq!(eytzinger_interpolative_search_by(&s, |x| x.cmp(&5)), (Some(2_usize), Some(6_usize)));
534/// assert_eq!(eytzinger_interpolative_search_by(&s, |x| x.cmp(&6)), (Some(6_usize), None));
535/// assert_eq!(eytzinger_interpolative_search_by(&s, |x| x.cmp(&7)), (Some(6_usize), None));
536/// assert_eq!(eytzinger_interpolative_search_by(&s, |x| x.cmp(&0)), (Some(3_usize), Some(1_usize)));
537/// assert_eq!(eytzinger_interpolative_search_by(&s, |x| x.cmp(&-1)), (None, Some(3_usize)));
538/// assert_eq!(eytzinger_interpolative_search_by(&s, |x| (*x as f32).partial_cmp(&3.5).unwrap()), (Some(0_usize), Some(5_usize)));
539/// ```
540#[inline]
541pub fn eytzinger_interpolative_search_by<'a, T: 'a, F>(
542 data: &'a [T],
543 mut f: F,
544) -> (Option<usize>, Option<usize>)
545where
546 F: FnMut(&'a T) -> Ordering,
547{
548 let mut lte: Option<usize> = None; // Index of highest value <= target
549 let mut gt: Option<usize> = None; // Index of lowest value > target
550 let mut i = 0;
551
552 while i < data.len() {
553 let v = &data[i];
554 match f(v) {
555 Ordering::Less | Ordering::Equal => {
556 // Current value <= target
557 lte = Some(i);
558 // Go right to find larger values (potentially better lte or gt)
559 i = 2 * i + 2;
560 }
561 Ordering::Greater => {
562 // Current value > target
563 // This is a candidate for gt (lowest value > target)
564 gt = Some(i);
565 // Go left to find smaller values (potentially lte or better gt)
566 i = 2 * i + 1;
567 }
568 }
569 }
570
571 (lte, gt)
572}
573
574/// Binary searches this eytzinger slice with a comparator function.
575///
576/// The comparator function should implement an order consistent with the sort order
577/// of the underlying eytzinger slice, returning an order code that indicates whether
578/// its argument is `Less`, `Equal` or `Greater` than the desired target.
579///
580/// If a matching value is found then `Some` is returned, containing the index of the
581/// matching element; if no match is found then `None` is returned.
582///
583/// # Examples
584///
585/// ```rust
586/// use eytzinger_interpolation::eytzinger_search_by;
587/// let s = [3, 1, 5, 0, 2, 4, 6];
588/// assert_eq!(eytzinger_search_by(&s, |x| x.cmp(&3)), Some(0));
589/// assert_eq!(eytzinger_search_by(&s, |x| x.cmp(&5)), Some(2));
590/// assert_eq!(eytzinger_search_by(&s, |x| x.cmp(&6)), Some(6));
591/// assert_eq!(eytzinger_search_by(&s, |x| x.cmp(&7)), None);
592/// ```
593#[inline]
594pub fn eytzinger_search_by<'a, T: 'a, F>(data: &'a [T], f: F) -> Option<usize>
595where
596 F: FnMut(&'a T) -> Ordering,
597{
598 eytzinger_search_by_impl(data, f)
599}
600
601#[inline]
602#[cfg(not(feature = "branchless"))]
603fn eytzinger_search_by_impl<'a, T: 'a, F>(data: &'a [T], mut f: F) -> Option<usize>
604where
605 F: FnMut(&'a T) -> Ordering,
606{
607 let mut i = 0;
608 loop {
609 match data.get(i) {
610 Some(ref v) => {
611 match f(v) {
612 Ordering::Equal => return Some(i),
613 o => {
614 // I was hoping the optimizer could handle this but it can't
615 // So here goes the evil hack: Ordering is -1/0/1
616 // So we use this dirty trick to map this to +2/X/+1
617 let o = o as usize;
618 let o = (o >> 1) & 1;
619 i = 2 * i + 1 + o;
620 }
621 };
622 }
623 None => return None,
624 }
625 }
626}
627
628#[inline]
629#[cfg(feature = "branchless")]
630fn eytzinger_search_by_impl<'a, T: 'a, F>(data: &'a [T], mut f: F) -> Option<usize>
631where
632 F: FnMut(&'a T) -> Ordering,
633{
634 let mut i = 0;
635 while i < data.len() {
636 let v = &data[i]; // this range check is optimized out :D
637 i = match f(v) {
638 Ordering::Greater | Ordering::Equal => 2 * i + 1,
639 Ordering::Less => 2 * i + 2,
640 };
641 }
642
643 // magic from the paper to fix up the (incomplete) final tree layer
644 // (only difference is that we recheck f() because this is exact search)
645 let p = i + 1;
646 let j = p >> (1 + (!p).trailing_zeros());
647 if j != 0 && (f(&data[j - 1]) == Ordering::Equal) {
648 Some(j - 1)
649 } else {
650 None
651 }
652}
653
654impl<T> SliceExt<T> for [T] {
655 #[inline]
656 fn eytzingerize<P: Permutator<T, PermutationGenerator>>(&mut self, permutator: &mut P) {
657 eytzingerize(self, permutator)
658 }
659
660 #[inline]
661 fn eytzinger_search<Q: ?Sized>(&self, x: &Q) -> Option<usize>
662 where
663 Q: Ord,
664 T: Borrow<Q>,
665 {
666 self.eytzinger_search_by(|e| e.borrow().cmp(x))
667 }
668
669 #[inline]
670 fn eytzinger_search_by<'a, F>(&'a self, f: F) -> Option<usize>
671 where
672 F: FnMut(&'a T) -> Ordering,
673 T: 'a,
674 {
675 eytzinger_search_by(self, f)
676 }
677
678 #[inline]
679 fn eytzinger_search_by_key<'a, B, F, Q: ?Sized>(&'a self, b: &Q, mut f: F) -> Option<usize>
680 where
681 B: Borrow<Q>,
682 F: FnMut(&'a T) -> B,
683 Q: Ord,
684 T: 'a,
685 {
686 self.eytzinger_search_by(|k| f(k).borrow().cmp(b))
687 }
688
689 #[inline]
690 fn eytzinger_interpolative_search_by<'a, F>(&'a self, f: F) -> (Option<usize>, Option<usize>)
691 where
692 F: FnMut(&'a T) -> Ordering,
693 T: 'a,
694 {
695 eytzinger_interpolative_search_by(self, f)
696 }
697
698 #[inline]
699 fn eytzinger_interpolative_search<Q: ?Sized>(&self, x: &Q) -> (Option<usize>, Option<usize>)
700 where
701 Q: Ord,
702 T: Borrow<Q>,
703 {
704 self.eytzinger_interpolative_search_by(|e| e.borrow().cmp(x))
705 }
706
707 #[inline]
708 fn eytzinger_interpolative_search_by_key<'a, B, F, Q: ?Sized>(&'a self, b: &Q, mut f: F) -> (Option<usize>, Option<usize>)
709 where
710 B: Borrow<Q>,
711 F: FnMut(&'a T) -> B,
712 Q: Ord,
713 T: 'a,
714 {
715 self.eytzinger_interpolative_search_by(|k| f(k).borrow().cmp(b))
716 }
717}
718
719#[cfg(test)]
720#[macro_use]
721extern crate quickcheck;
722#[cfg(test)]
723mod tests {
724 use super::foundation::*;
725 use super::*;
726
727 #[test]
728 fn magic() {
729 for (i, &v) in [0, 1, 1, 2, 3, 3, 3, 4, 5, 6, 7, 7, 7, 7, 7, 8]
730 .iter()
731 .enumerate()
732 {
733 assert_eq!(get_permutation_element_by_node(i + 1, 1, 0), v);
734 }
735 for (i, &v) in [0, 0, 1, 1, 1, 1, 2, 3, 3].iter().enumerate() {
736 assert_eq!(get_permutation_element_by_node(i + 2, 2, 0), v);
737 }
738 for (i, &v) in [2, 3, 4, 5, 5, 6, 7, 8].iter().enumerate() {
739 assert_eq!(get_permutation_element_by_node(i + 3, 2, 1), v);
740 }
741 for (i, &v) in [0, 0, 0, 0, 1, 1, 1].iter().enumerate() {
742 assert_eq!(get_permutation_element_by_node(i + 4, 3, 0), v);
743 }
744 }
745
746 const REF_PERMUTATIONS: &[&'static [usize]] = &[
747 &[],
748 &[0],
749 &[1, 0],
750 &[1, 0, 2],
751 &[2, 1, 3, 0],
752 &[3, 1, 4, 0, 2],
753 &[3, 1, 5, 0, 2, 4],
754 &[3, 1, 5, 0, 2, 4, 6],
755 &[4, 2, 6, 1, 3, 5, 7, 0],
756 &[5, 3, 7, 1, 4, 6, 8, 0, 2],
757 &[6, 3, 8, 1, 5, 7, 9, 0, 2, 4],
758 &[7, 3, 0xb, 1, 5, 9, 0xd, 0, 2, 4, 6, 8, 0xa, 0xc, 0xe],
759 ];
760
761 #[test]
762 fn reference_permutations() {
763 for &array in REF_PERMUTATIONS {
764 let permut: Vec<_> = PermutationGenerator::new(array.len()).collect();
765 assert_eq!(array, permut.as_slice());
766 }
767 }
768
769 #[test]
770 fn eytzingerize_simple() {
771 let mut permutator = InplacePermutator;
772 for &array in REF_PERMUTATIONS {
773 let mut payload: Vec<_> = (0..array.len()).collect();
774 eytzingerize(payload.as_mut_slice(), &mut permutator);
775 assert_eq!(payload, array);
776 }
777 }
778
779 const NODE_INDEXES: &[(usize, usize)] = &[
780 (1, 0),
781 (2, 0),
782 (2, 1),
783 (3, 0),
784 (3, 1),
785 (3, 2),
786 (3, 3),
787 (4, 0),
788 (4, 1),
789 (4, 2),
790 (4, 3),
791 (4, 4),
792 (4, 5),
793 (4, 6),
794 (4, 7),
795 ];
796
797 #[test]
798 fn calc_index() {
799 for (i, &x) in NODE_INDEXES.iter().enumerate() {
800 assert_eq!(x, index_to_node(i));
801 }
802 }
803
804 #[test]
805 fn simple_inplace_permutation() {
806 let permutation: &[usize] = &[4, 2, 3, 0, 1];
807 let mut data = [1, 2, 3, 4, 5];
808 InplacePermutator.permute(&mut data, &permutation);
809 assert_eq!(data, [5, 3, 4, 1, 2]);
810 }
811
812 #[test]
813 #[cfg(feature = "heap-permutator")]
814 fn simple_heap_permutation() {
815 let permutation: &[usize] = &[4, 2, 3, 0, 1];
816 let mut data = [1, 2, 3, 4, 5];
817 HeapPermutator::default().permute(&mut data, &permutation);
818 assert_eq!(data, [5, 3, 4, 1, 2]);
819 }
820
821 #[test]
822 #[cfg(feature = "heap-permutator-sparse")]
823 fn simple_heap_permutation_sparse() {
824 let permutation: &[usize] = &[4, 2, 3, 0, 1];
825 let mut data = [1, 2, 3, 4, 5];
826 SparseHeapPermutator::default().permute(&mut data, &permutation);
827 assert_eq!(data, [5, 3, 4, 1, 2]);
828 }
829
830 #[test]
831 fn simple_heap_copy_permutation() {
832 let permutation: &[usize] = &[4, 2, 3, 0, 1];
833 let mut data = [1, 2, 3, 4, 5];
834 HeapCopyPermutator::default().permute(&mut data, &permutation);
835 assert_eq!(data, [5, 3, 4, 1, 2]);
836 }
837
838 #[test]
839 fn search_negative() {
840 let data: &[i32] = &[6, 2, 10, 0, 4, 8, 12];
841 for i in -10..20 {
842 let expected = data.iter().position(|&x| x == i);
843 assert_eq!(expected, data.eytzinger_search(&i));
844 }
845 }
846
847 fn test_permutation<P: Default>(junk: Vec<usize>) -> bool
848 where
849 for<'a> P: Permutator<usize, &'a [usize]>,
850 {
851 // first create a permutation from the random array
852 let mut perm: Vec<_> = (0..junk.len()).collect();
853 perm.sort_by_key(|&i| junk[i]);
854
855 // now test
856 let mut data: Vec<_> = (0..perm.len()).collect();
857 P::default().permute(data.as_mut_slice(), &perm.as_slice());
858 data == perm
859 }
860
861 quickcheck! {
862 fn inplace_permutation(junk: Vec<usize>) -> bool {
863 test_permutation::<InplacePermutator>(junk)
864 }
865
866 fn stack_permutation(junk: Vec<usize>) -> bool {
867 test_permutation::<StackCopyPermutator>(junk)
868 }
869
870 #[cfg(feature = "heap-permutator")]
871 fn heap_permutation(junk: Vec<usize>) -> bool {
872 test_permutation::<HeapPermutator>(junk)
873 }
874
875 #[cfg(feature = "heap-permutator-sparse")]
876 fn heap_permutation_sparse(junk: Vec<usize>) -> bool {
877 test_permutation::<SparseHeapPermutator>(junk)
878 }
879
880 fn heap_copy_permutation(junk: Vec<usize>) -> bool {
881 test_permutation::<HeapCopyPermutator<_>>(junk)
882 }
883
884 fn eytzinger_tree_invariants(length: usize) -> bool {
885 let perm: Vec<_> = PermutationGenerator::new(length).collect();
886
887 let mut todo = Vec::new();
888 todo.push((0, 0..length));
889 let mut checked = 0;
890 while let Some((i, range)) = todo.pop() {
891 match perm.get(i) {
892 Some(&v) => {
893 if !(range.start <= v && v < range.end) { return false; }
894 todo.push((2 * (i + 1) - 1, range.start..v));
895 todo.push((2 * (i + 1), v..range.end));
896 checked += 1;
897 }
898 None => continue,
899 }
900 }
901
902 checked == length
903 }
904
905 fn search_works(data: Vec<usize>) -> bool {
906 let mut data = data;
907 data.sort();
908 data.dedup();
909 data.eytzingerize(&mut InplacePermutator);
910
911 data.iter().enumerate().all(|(i, v)| data.eytzinger_search(v) == Some(i))
912 }
913
914 fn interpolative_search_bounds_correct(data: Vec<i32>) -> bool {
915 let mut data = data;
916 data.sort();
917 data.dedup();
918 if data.is_empty() { return true; }
919
920 let sorted = data.clone();
921 data.eytzingerize(&mut InplacePermutator);
922
923 // Test every value in the array
924 for &target in &sorted {
925 let (lte, gt) = data.eytzinger_interpolative_search(&target);
926
927 // lte should be <= target
928 if let Some(idx) = lte {
929 if data[idx] > target { return false; }
930 }
931
932 // gt should be > target
933 if let Some(idx) = gt {
934 if data[idx] <= target { return false; }
935 }
936
937 // At least one should be Some
938 if lte.is_none() && gt.is_none() { return false; }
939 }
940
941 // Test values between elements
942 for window in sorted.windows(2) {
943 let between = (window[0] + window[1]) / 2;
944 if between != window[0] && between != window[1] {
945 let (lte, gt) = data.eytzinger_interpolative_search(&between);
946
947 if let Some(idx) = lte {
948 if data[idx] > between { return false; }
949 }
950
951 if let Some(idx) = gt {
952 if data[idx] <= between { return false; }
953 }
954 }
955 }
956
957 true
958 }
959 }
960
961 #[test]
962 fn interpolative_search_empty() {
963 let data: &[i32] = &[];
964 assert_eq!(data.eytzinger_interpolative_search(&0), (None, None));
965 }
966
967 #[test]
968 fn interpolative_search_single() {
969 let data = [42];
970 assert_eq!(data.eytzinger_interpolative_search(&42), (Some(0), None));
971 assert_eq!(data.eytzinger_interpolative_search(&41), (None, Some(0)));
972 assert_eq!(data.eytzinger_interpolative_search(&43), (Some(0), None));
973 }
974
975 #[test]
976 fn interpolative_search_all_equal() {
977 let mut data = [5, 5, 5, 5];
978 data.eytzingerize(&mut InplacePermutator);
979 let (lte, _gt) = data.eytzinger_interpolative_search(&5);
980 // Should find at least one value equal to 5
981 assert!(lte.is_some());
982 if let Some(idx) = lte {
983 assert_eq!(data[idx], 5);
984 }
985 }
986
987 #[test]
988 fn interpolative_search_below_min() {
989 let mut data = [10, 20, 30, 40, 50];
990 data.eytzingerize(&mut InplacePermutator);
991 let (lte, gt) = data.eytzinger_interpolative_search(&5);
992 assert_eq!(lte, None);
993 assert!(gt.is_some());
994 if let Some(idx) = gt {
995 assert!(data[idx] >= 10);
996 }
997 }
998
999 #[test]
1000 fn interpolative_search_above_max() {
1001 let mut data = [10, 20, 30, 40, 50];
1002 data.eytzingerize(&mut InplacePermutator);
1003 let (lte, gt) = data.eytzinger_interpolative_search(&100);
1004 assert!(lte.is_some());
1005 assert_eq!(gt, None);
1006 if let Some(idx) = lte {
1007 assert!(data[idx] <= 50);
1008 }
1009 }
1010
1011 #[test]
1012 fn interpolative_search_between_values() {
1013 let mut data = [10, 20, 30, 40, 50];
1014 data.eytzingerize(&mut InplacePermutator);
1015 let (lte, gt) = data.eytzinger_interpolative_search(&25);
1016
1017 assert!(lte.is_some());
1018 assert!(gt.is_some());
1019
1020 if let (Some(lte_idx), Some(gt_idx)) = (lte, gt) {
1021 assert!(data[lte_idx] <= 25);
1022 assert!(data[gt_idx] > 25);
1023 }
1024 }
1025}