1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
use crate::Search;
use core::{
    cmp::{Ordering, Ordering::*},
    ops::{Add, Div, Range, RangeInclusive, Sub},
};

impl<T> Search<T> for RangeInclusive<T>
where
    T: PartialOrd + Copy + From<u8> + Add<Output = T> + Sub<Output = T> + Div<Output = T>    

{
    /// Binary search within an inclusive range.  
    /// When the target is missing, returns the insert position as `Err<T>`. 
    /// Same as `std::slice::binary_search_by()` but does not need explicit data.
    /// The probing of any data is done by the comparator closure.
    fn binary_by(self, mut cmpr: impl FnMut(T) -> Ordering) -> Result<T, T> {
        let mut lo = *self.start(); // initial low index
        let mut hi = *self.end();   // initial high index
        match cmpr(lo) {
            Equal => return Ok(lo),
            Greater => return Err(lo),
            _ => match cmpr(hi) {
                Equal => return Ok(hi),
                Less => return Err(hi+1.into()),
                _ => ()
            }            
        }; 
        loop {            
            let mid = lo + (hi - lo) / 2.into(); // binary chop with truncation
            if mid == lo {
                // interval is exhausted without a match, hi is the insert position
                return Err(hi);
            };
            // still some interval left
            match cmpr(mid) {
                Less => lo = mid,
                Greater => hi = mid,
                Equal => {
                    // the first hit
                    return Ok(mid);
                }
            } 
        }
    }

    /// Binary search for an index of any item matching the target within an open interval
    /// specified in the input inclusive range.
    /// Closure `cmpr` probes some ordered data and compares it against some target.
    /// This code is agnostic about the type of the target (and the data).
    /// Descending order data can be handled by reversing the order of the comparison operands in the call.
    /// Returns the index of the first hit that is PartiallyEqual to the target and its last search envelope `lo..hi`.  
    /// When the target is not found, then `(ip, lo..ip)` is returned, where ip is the target's insert position.
    /// The (indexing) range values can be of any generic type T, satisfying the listed trait bounds.
    /// Typically usize for searching in-memory, u128 for searching disks or internet,
    /// or f64 for numerically solving nonlinear equations.
    fn binary_any(&self, mut cmpr: impl FnMut(T) -> Ordering) -> (T, Range<T>) {
        let mut lo = *self.start(); // initial low index
        let mut hi = *self.end();   // initial high index
        loop {            
            let mid = lo + (hi - lo) / 2.into(); // binary chop with truncation
            if mid == lo {
                // interval is exhausted without a match, hi is the insert position
                return (hi, lo..hi);
            };
            // still some interval left
            match cmpr(mid) {
                Less => lo = mid,
                Greater => hi = mid,
                Equal => {
                    // the first match hit
                    return (mid, lo..hi);
                }
            } 
        }
    }

    /// General Binary Search for finding all the matches.
    /// Searches within the specified RangeInclusive<T> index.
    /// The (indexing) range values can be of any generic type T (satisfying the listed bounds):
    /// usize for indexing in-memory, u128 for searching whole disks or internet,
    /// f64 for solving equations which might not converge using other methods.
    /// Comparator closure `cmpr` is comparing data against a target captured from its environment.
    /// Using closures enables custom comparisons of user's own data types.
    /// This code is also agnostic about the type of the target (and of the data).
    /// When the target is in order before self.start, empty `self.start..self.start` range is returned.
    /// When the target is in order after self.end, `self.end..self.end` is returned.
    /// When target is not found, then `ip..ip` is returned, where ip is its insert position.
    /// Otherwise the range of all consecutive values PartiallyEqual to the target is returned.
    fn binary_all(&self, mut cmpr: impl FnMut(T) -> Ordering) -> Range<T> {
        fn cmp_then<T>(
            cmpr: &mut impl FnMut(T) -> Ordering,
            then: Ordering,
        ) -> impl FnMut(T) -> Ordering + '_ {
            move |probe| cmpr(probe).then(then)
        }
        let lo = *self.start(); // initial low index
        let ihi = *self.end();  // initial high index
        let hi = ihi + 1.into();
        if self.is_empty() {
            return lo..hi;
        };

        // Checking end cases
        match cmpr(lo) {
            Greater => {
                return lo..lo;
            } // item is before the range
            Equal => {
                if cmpr(ihi) == Equal {
                    // all in range match
                    return lo..hi;
                };
                let (lor, _) = self.binary_any(cmp_then(&mut cmpr, Less));
                return lo..lor + 1.into();
            }
            _ => (),
        };
        match cmpr(ihi) {
            Less => {
                return hi..hi;
            } // item is after the range
            Equal => {
                let (lor, _) = self.binary_any(cmp_then(&mut cmpr, Greater));
                return lor..hi;
            }
            _ => (),
        };
        // lo and hi will now never be equal to target
        // Binary search for any match, with the given closure
        let (hit, lastrange) = self.binary_any(&mut cmpr);
        // Not found, return empty range with sort position
        if hit == lastrange.end {
            return hit..hit;
        };
        // Search down in the last interval for the start of the matching range
        let (lowend, _) = (lastrange.start..=hit).binary_any(cmp_then(&mut cmpr, Greater));
        // Search up in the last interval for the end of the matching range
        let (highend, _) = (hit..=lastrange.end).binary_any(cmp_then(&mut cmpr, Less));
        lowend..highend
    }
}