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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
//! Semantically enriched value mutation.

/// This trait adds ergonomic convenience functions to `bool`.
pub trait PowerBool {
    /// Set the value to `true`, and say if it changed.
    #[inline(always)]
    fn raise(&mut self) -> bool {
        self.set(true)
    }

    /// Set the value to `false`, and say if it changed.
    #[inline(always)]
    fn lower(&mut self) -> bool {
        self.set(false)
    }

    /// Set the value, and say if it changed.
    ///
    /// Don't use a boolean literal with this function, `val` should be an expression:
    ///
    /// * Instead of `set(true)`, use `raise()`.
    /// * Instead of `set(false)`, use `lower()`.
    #[inline(always)]
    fn set(&mut self, val: bool) -> bool;

    /// Returns `true` if the value was `false`. This is not the same as `set()`.
    ///
    /// ```
    /// use powerbool::PowerBool;
    /// let list = vec![0.23, 3.4, 8.0, 9.6];
    /// let mut found = false;
    /// for x in list {
    ///     if found.trip((x as i32 as f32) == x) {
    ///         println!("Alert! There's an integer hiding in our number!");
    ///         return;
    ///     }
    /// }
    /// println!("Nobody here but us floats.");
    /// # panic!();
    /// ```
    #[inline(always)]
    fn trip(&mut self, val: bool) -> bool;

    /// Makes sure the value is `false`, and return `true` if it didn`t change.
    /// (Am I kicking a dead horse?)
    #[inline(always)]
    fn kick(&mut self) -> bool {
        !self.set(false)
    }

    /// Makes sure the value is `true`, and return `true` if it didn't change.
    /// (The opposite of `kick`: am I punching a sleeping horse?)
    #[inline(always)]
    fn punch(&mut self) -> bool {
        !self.set(true)
    }
}

impl PowerBool for bool {
    #[inline(always)]
    fn set(&mut self, val: bool) -> bool {
        let ret = *self != val;
        *self = val;
        ret
    }
    
    #[inline(always)]
    fn trip(&mut self, val: bool) -> bool {
        let ret = val && !*self;
        if ret {
            *self = true;
        }
        ret
    }
}

#[cfg(test)]
mod test_powerbool {
    //! It would be silly to have two names for the same truth table.
    //!
    //! But some things are acceptable:
    //!
    //!  * Eliminating a bool literal
    //!  * Eliminating a return value negation
    //!  * Eliminating a following value change
    //!
    //! What's not okay?
    //!
    //!  * `foo()` being logically identical to `bar()`. (Unless there's some kind of semantic/usage
    //!  distinction. But maybe that point we'd want actual new types.)
    use super::PowerBool;

    fn truth_table_row<F: Fn(&mut bool) -> bool>(before: bool, f: F, returns: bool, after: bool) {
        let mut v = before;
        assert_eq!(f(&mut v), returns);
        assert_eq!(v, after);
    }

    #[test]
    fn set() {
        fn set_true(v: &mut bool) -> bool { v.set(true) }
        fn set_false(v: &mut bool) -> bool { v.set(false) }
        truth_table_row(true, set_true, false, true);
        truth_table_row(true, set_false, true, false);
        truth_table_row(false, set_true, true, true);
        truth_table_row(false, set_false, false, false);
    }

    #[test]
    fn raise() {
        truth_table_row(true, bool::raise, false, true);
        truth_table_row(false, bool::raise, true, true);
    }

    #[test]
    fn lower() {
        truth_table_row(true, bool::lower, true, false);
        truth_table_row(false, bool::lower, false, false);
    }

    #[test]
    fn trip() {
        fn trip_true(v: &mut bool) -> bool { v.trip(true) }
        fn trip_false(v: &mut bool) -> bool { v.trip(false) }
        truth_table_row(true, trip_true, false, true);
        truth_table_row(true, trip_false, false, true);
        truth_table_row(false, trip_true, true, true);
        truth_table_row(false, trip_false, false, false);
    }

    #[test]
    fn kick() {
        truth_table_row(false, bool::kick, true, false);
        truth_table_row(true, bool::kick, false, false);
    }

    #[test]
    fn punch() {
        truth_table_row(false, bool::punch, false, true);
        truth_table_row(true, bool::punch, true, true);
    }
}

pub trait Change {
    /// If this value does not equal the other value, become that value and return true.
    #[inline(always)]
    fn change(&mut self, v: Self) -> bool;
}
impl<T: PartialEq> Change for T {
    #[inline(always)]
    fn change(&mut self, v: Self) -> bool {
        if self != &v {
            *self = v;
            true
        } else {
            false
        }
    }
}

#[cfg(test)]
mod test_change {
    use super::Change;

    #[test]
    fn works() {
        let mut x = 1;
        assert!(x.change(2));
    }

    #[test]
    fn still_works() {
        let mut x = 1;
        assert!(!x.change(1));
    }
}