1
//! Miscellaneous utilities
2

            
3
use crate::internal_prelude::*;
4

            
5
pub mod bool_indexed_array;
6
pub(crate) use bool_indexed_array::*;
7

            
8
/// Wrapper for `todo!` which avoids daft warnings everywhere
9
///
10
/// TOOD DIRAUTH abolish `todo` wrapper, getting rid of panics.
11
pub(super) fn todo<T>() -> T {
12
    todo!()
13
}
14

            
15
/// What `RangeInclusive::map` ought to be
16
///
17
/// Open-coding this at the call site would risk accidental change of the range type,
18
/// changing inclusiveness, etc.  This function has the same range type as argument and return.
19
18
pub(crate) fn map_range<T, U>(
20
18
    r: &RangeInclusive<T>,
21
18
    mut f: impl FnMut(&T) -> U,
22
18
) -> RangeInclusive<U> {
23
18
    f(r.start())..=f(r.end())
24
18
}
25

            
26
/// Is `predicate` true for more than half the values in `iterator`?
27
//
28
// The signature of this function is complicated, compared to the alternative
29
//   fn is_for_more_than_half_of(some: usize, all: usize)
30
// but makes it impossible to accidentally swap the arguments, or get the wrong
31
// total value, or some such.
32
53
pub(super) fn is_true_for_more_than_half_of<I, E>(
33
53
    iterator: impl IntoIterator<Item = I>,
34
53
    predicate: impl Fn(I) -> Result<bool, E>,
35
53
) -> Result<bool, E>
36
53
where
37
53
    E: From<Bug>,
38
{
39
53
    let mut n = 0;
40
53
    let mut y = 0;
41
230
    for i in iterator {
42
230
        n.try_add_assign(1)?;
43
230
        if predicate(i)? {
44
195
            y.try_add_assign(1)?;
45
35
        }
46
    }
47
53
    Ok(y > n / 2)
48
53
}
49

            
50
#[ext(name = IteratorExtUnzip)]
51
pub(crate) impl<AI, BI, I> I
52
where
53
    I: Iterator<Item = (Option<AI>, Option<BI>)>,
54
{
55
    /// Collect and filter an iterator yielding tuples of options into two collections.
56
    ///
57
    /// Takes an iterator yielding `(Option<AI>, Option<AB>)` and collects the
58
    /// `Some`s into a tuple of the two collections, of `AI` and `BI` respectively.
59
    //
60
    // TODO DIRAUTH consider moving this to tor-basic-utils or itertools or something.
61
    fn filter_collect_unzip<AC, BC>(self) -> (AC, BC)
62
    where
63
        AC: Default + Extend<AI>,
64
        BC: Default + Extend<BI>,
65
    {
66
        let mut ac = AC::default();
67
        let mut bc = BC::default();
68
        for (ao, bo) in self {
69
            ac.extend(ao);
70
            bc.extend(bo);
71
        }
72
        (ac, bc)
73
    }
74
}
75

            
76
#[ext(name = IteratorExt)]
77
pub(crate) impl<I> I
78
where
79
    I: Iterator,
80
{
81
    /// Filter items using a fallible predicate
82
    ///
83
    /// Returns an iterator of `Result`.
84
    //
85
    // TODO DIRAUTH consider moving this to tor-basic-utils or itertools or something.
86
5
    fn try_filter<'r, F, E>(self, mut f: F) -> impl Iterator<Item = Result<I::Item, E>> + 'r
87
5
    where
88
5
        I: 'r,
89
5
        F: FnMut(&I::Item) -> Result<bool, E> + 'r,
90
    {
91
37
        self.filter_map(move |item| match f(&item) {
92
3
            Ok(false) => None,
93
34
            Ok(true) => Some(Ok(item)),
94
            Err(e) => Some(Err(e)),
95
37
        })
96
5
    }
97
}
98

            
99
#[ext(name = TryAddAssign)]
100
pub(crate) impl<T: num_traits::CheckedAdd> T {
101
    /// `+=` but throws bug on overflow.
102
    //
103
    // `try` rather than `checked` because `checked_add_assign` ought to return `Option<()>`
104
    // by analogy with `.checked_add`.
105
425
    fn try_add_assign(&mut self, v: Self) -> Result<(), Bug> {
106
425
        *self = self
107
425
            .checked_add(&v)
108
425
            .ok_or_else(|| internal!("addition overflow"))?;
109
425
        Ok(())
110
425
    }
111
}
112

            
113
#[cfg(test)]
114
mod test {
115
    // @@ begin test lint list maintained by maint/add_warning @@
116
    #![allow(clippy::bool_assert_comparison)]
117
    #![allow(clippy::clone_on_copy)]
118
    #![allow(clippy::dbg_macro)]
119
    #![allow(clippy::mixed_attributes_style)]
120
    #![allow(clippy::print_stderr)]
121
    #![allow(clippy::print_stdout)]
122
    #![allow(clippy::single_char_pattern)]
123
    #![allow(clippy::unwrap_used)]
124
    #![allow(clippy::unchecked_time_subtraction)]
125
    #![allow(clippy::useless_vec)]
126
    #![allow(clippy::needless_pass_by_value)]
127
    #![allow(clippy::string_slice)] // See arti#2571
128
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
129
    use super::*;
130

            
131
    #[test]
132
    fn is_true_for_more_than_half_of_t() -> Result<(), Bug> {
133
        // This test case is quite like is_more_than_half_all_auths in framework.rs
134

            
135
        let check = |n_all: usize, minimum_that_is_more_than_half| {
136
            for t in 0..=n_all {
137
                let cheat = Cell::new(t);
138
                // returns true t times, and then returns false
139
                let predicate = |_| {
140
                    Ok::<_, Bug>(match cheat.get().checked_sub(1) {
141
                        None => false,
142
                        Some(less) => {
143
                            cheat.set(less);
144
                            true
145
                        }
146
                    })
147
                };
148
                assert_eq! {
149
                    is_true_for_more_than_half_of(0..n_all, predicate)?,
150
                    t >= minimum_that_is_more_than_half,
151
                }
152
            }
153
            Ok::<_, Bug>(())
154
        };
155

            
156
        check(0, 1)?;
157
        check(1, 1)?;
158
        check(2, 2)?;
159
        check(3, 2)?;
160
        check(4, 3)?;
161
        check(5, 3)?;
162

            
163
        Ok(())
164
    }
165
}