]>
git.proxmox.com Git - pxar.git/blob - src/binary_tree_array.rs
1 //! Helpers to generate a binary search tree stored in an array from a
4 //! Specifically, for any given sorted array 'input' permute the
5 //! array so that the following rule holds:
7 //! For each array item with index i, the item at 2i+1 is smaller and
8 //! the item 2i+2 is larger.
10 //! This structure permits efficient (meaning: O(log(n)) binary
11 //! searches: start with item i=0 (i.e. the root of the BST), compare
12 //! the value with the searched item, if smaller proceed at item
13 //! 2i+1, if larger proceed at item 2i+2, and repeat, until either
14 //! the item is found, or the indexes grow beyond the array size,
15 //! which means the entry does not exist.
17 //! Effectively this implements bisection, but instead of jumping
18 //! around wildly in the array during a single search we only search
19 //! with strictly monotonically increasing indexes.
21 //! Algorithm is from casync (camakebst.c), simplified and optimized
22 //! for rust. Permutation function originally by L. Bressel, 2017. We
23 //! pass permutation info to user provided callback, which actually
24 //! implements the data copy.
26 //! The Wikipedia Artikel for [Binary
27 //! Heap](https://en.wikipedia.org/wiki/Binary_heap) gives a short
28 //! intro howto store binary trees using an array.
30 #![deny(missing_docs)]
32 use std
::cmp
::Ordering
;
34 #[allow(clippy::many_single_char_names)]
35 fn copy_inner
<F
: FnMut(usize, usize)>(
37 // we work on input array input[o..o+n]
45 let t
= p
+ (p
>> 1) - 1;
48 // |...........p.............t....n........(2p)|
51 // |...........p.....n.......t.............(2p)|
55 (copy_func
)(o
+ m
, i
);
58 copy_inner(copy_func
, m
, o
, e
- 1, i
* 2 + 1);
62 copy_inner(copy_func
, n
- m
- 1, o
+ m
+ 1, e
- 1, i
* 2 + 2);
66 /// This function calls the provided `copy_func()` with the permutaion information required to
67 /// build a binary search tree array.
70 /// # use pxar::binary_tree_array;
72 /// # const EXPECTED: &[(usize, usize)] = &[(3, 0), (1, 1), (0, 3), (2, 4), (4, 2)];
73 /// binary_tree_array::copy(5, |src, dest| {
74 /// # assert_eq!((src, dest), EXPECTED[i]);
76 /// println!("Copy {} to {}", src, dest);
80 /// This will produce the folowing output:
90 /// So this generates the following permuation: `[3,1,4,0,2]`.
91 pub fn copy
<F
>(n
: usize, mut copy_func
: F
)
93 F
: FnMut(usize, usize),
99 let e
= (64 - n
.leading_zeros() - 1) as usize; // fast log2(n)
101 copy_inner(&mut copy_func
, n
, 0, e
, 0);
104 /// This function searches for the index where the comparison by the provided
105 /// `compare()` function returns `Ordering::Equal`.
106 /// The order of the comparison matters (noncommutative) and should be search
107 /// value compared to value at given index as shown in the examples.
108 /// The parameter `skip` defines the number of matches to ignore while
109 /// searching before returning the index in order to lookup duplicate entries in
113 /// # use pxar::binary_tree_array;
114 /// let mut vals = vec![0,1,2,2,2,3,4,5,6,6,7,8,8,8];
116 /// let clone = vals.clone();
117 /// binary_tree_array::copy(vals.len(), |s, d| {
118 /// vals[d] = clone[s];
120 /// let should_be = vec![5,2,8,1,3,6,8,0,2,2,4,6,7,8];
121 /// assert_eq!(vals, should_be);
125 /// let idx = binary_tree_array::search_by(&vals, 0, skip, |el| find.cmp(el));
126 /// assert_eq!(idx, Some(2));
130 /// let idx = binary_tree_array::search_by(&vals, 2, skip, |el| find.cmp(el));
131 /// assert_eq!(idx, Some(6));
135 /// let idx = binary_tree_array::search_by(&vals, 6, skip, |el| find.cmp(el));
136 /// assert_eq!(idx, Some(13));
140 /// let idx = binary_tree_array::search_by(&vals, 0, skip, |el| find.cmp(el));
141 /// assert!(idx.is_none());
145 /// // if start index is equal to the array length, `None` is returned.
146 /// let idx = binary_tree_array::search_by(&vals, vals.len(), skip, |el| find.cmp(el));
147 /// assert!(idx.is_none());
149 /// // if start index is larger than length, `None` is returned.
150 /// let idx = binary_tree_array::search_by(&vals, vals.len() + 1, skip, |el| find.cmp(el));
151 /// assert!(idx.is_none());
153 pub fn search_by
<F
, T
>(tree
: &[T
], start
: usize, skip
: usize, f
: F
) -> Option
<usize>
155 F
: Copy
+ Fn(&T
) -> Ordering
,
159 while i
< tree
.len() {
161 Ordering
::Less
=> i
= 2 * i
+ 1,
162 Ordering
::Greater
=> i
= 2 * i
+ 2,
163 Ordering
::Equal
if skip
== 0 => return Some(i
),
166 return search_by(tree
, i
, skip
- 1, f
)
167 .or_else(move || search_by(tree
, i
+ 1, skip
- 1, f
));
176 fn test_binary_search_tree() {
177 fn run_test(len
: usize) -> Vec
<usize> {
178 const MARKER
: usize = 0xfffffff;
179 let mut output
= vec
![];
184 assert
!(output
[d
] == MARKER
);
188 println
!("GOT:{}:{:?}", len
, output
);
191 assert
!(output
[i
] != MARKER
);
196 assert
!(run_test(0).len() == 0);
197 assert
!(run_test(1) == [0]);
198 assert
!(run_test(2) == [1, 0]);
199 assert
!(run_test(3) == [1, 0, 2]);
200 assert
!(run_test(4) == [2, 1, 3, 0]);
201 assert
!(run_test(5) == [3, 1, 4, 0, 2]);
202 assert
!(run_test(6) == [3, 1, 5, 0, 2, 4]);
203 assert
!(run_test(7) == [3, 1, 5, 0, 2, 4, 6]);
204 assert
!(run_test(8) == [4, 2, 6, 1, 3, 5, 7, 0]);
205 assert
!(run_test(9) == [5, 3, 7, 1, 4, 6, 8, 0, 2]);
206 assert
!(run_test(10) == [6, 3, 8, 1, 5, 7, 9, 0, 2, 4]);
207 assert
!(run_test(11) == [7, 3, 9, 1, 5, 8, 10, 0, 2, 4, 6]);
208 assert
!(run_test(12) == [7, 3, 10, 1, 5, 9, 11, 0, 2, 4, 6, 8]);
209 assert
!(run_test(13) == [7, 3, 11, 1, 5, 9, 12, 0, 2, 4, 6, 8, 10]);
210 assert
!(run_test(14) == [7, 3, 11, 1, 5, 9, 13, 0, 2, 4, 6, 8, 10, 12]);
211 assert
!(run_test(15) == [7, 3, 11, 1, 5, 9, 13, 0, 2, 4, 6, 8, 10, 12, 14]);
212 assert
!(run_test(16) == [8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15, 0]);
213 assert
!(run_test(17) == [9, 5, 13, 3, 7, 11, 15, 1, 4, 6, 8, 10, 12, 14, 16, 0, 2]);
215 for len
in 18..1000 {