lib.rs (6287B)
1 #[macro_use] 2 extern crate derive_new; 3 4 use std::cmp::Ordering; 5 6 /// A regular function that is only defined between lower and higher. 7 /// If two functions intersect their higher and lower bounds respectively. 8 /// The second will take precedence where f(lower). 9 #[derive(new)] 10 pub struct DualBoundedFunction<B, O> { 11 /// The stored function f(x) = ??? 12 pub func: Box<dyn Fn(B) -> O>, 13 /// The lower bound of the function. 14 pub lower: B, 15 /// The higher bound of the function. 16 pub higher: B, 17 } 18 19 /// Define a functions defined by multiple functions parts. 20 /// See BoundedFunction. 21 /// Uses bounds as [lower,higher], 22 /// except in the case of a lower bound overlapping a higher bound. 23 /// In this case, the lower bound always take precedence. 24 pub struct PartialFunction<B, O> { 25 funcs: Vec<DualBoundedFunction<B, O>>, 26 } 27 28 impl<B: PartialOrd, O> PartialFunction<B, O> { 29 /// Creates a new PartialFunctionBuilder 30 pub fn new() -> PartialFunctionBuilder<B, O> { 31 PartialFunctionBuilder::new() 32 } 33 34 /// Evaluates the partial function. 35 /// Returns None if no function is defined. 36 pub fn eval(&self, x: B) -> Option<O> { 37 let iter = self.funcs.iter().enumerate(); 38 for (i, bounded) in iter { 39 let next = self.funcs.get(i + 1); 40 if (x >= bounded.lower && x < bounded.higher) 41 || (next.is_none() && x == bounded.higher) 42 || (next.is_some() && next.unwrap().lower != bounded.higher) 43 { 44 let f = &bounded.func; 45 return Some(f(x)); 46 } 47 } 48 None 49 } 50 } 51 52 /// A builder to create an immutable PartialFunction. 53 #[derive(new)] 54 pub struct PartialFunctionBuilder<B, O> { 55 #[new(default)] 56 funcs: Vec<DualBoundedFunction<B, O>>, 57 } 58 59 impl<B: PartialOrd, O> PartialFunctionBuilder<B, O> { 60 /// Adds a bounded function bounded between [lower,higher[ of function func. 61 pub fn with(mut self, lower: B, higher: B, func: Box<dyn Fn(B) -> O>) -> Self { 62 debug_assert!(self.can_insert(&lower, &higher)); 63 let f = DualBoundedFunction { 64 func: func, 65 lower: lower, 66 higher: higher, 67 }; 68 self.funcs.push(f); 69 self 70 } 71 72 /// Check if you can safely insert into the function list for the specified bounds. 73 pub fn can_insert(&self, lower: &B, higher: &B) -> bool { 74 !self.funcs.iter().any(|b| { 75 (lower >= &b.lower && lower < &b.higher) 76 || (higher > &b.lower && higher <= &b.higher) 77 || (lower <= &b.lower && higher >= &b.higher) 78 }) 79 } 80 81 /// Builds the PartialFunction from the functions added using with. 82 pub fn build(mut self) -> PartialFunction<B, O> { 83 self.funcs.sort_by(|a, b| { 84 a.lower 85 .partial_cmp(&b.lower) 86 .unwrap_or(a.higher.partial_cmp(&b.higher).unwrap_or(Ordering::Equal)) 87 }); 88 PartialFunction { funcs: self.funcs } 89 } 90 } 91 92 /// A lower bounded function is a function that is valid from [x..infinite[, or until it hits another function's start. 93 #[derive(new)] 94 struct LowerBoundedFunction<B, O> { 95 /// The stored function f(x) = ??? 96 pub func: Box<dyn Fn(B) -> O>, 97 /// The lower bound of the function. 98 pub lower: B, 99 } 100 101 /// A lower partial function is a function that is defined by segments valid from [x..infinite[, or until it hits another function's start. 102 /// It starts searching at -infinity and goes up to infinity, and takes the last seen function that contains the desired invariable value (x). 103 /// 104 /// Example: 105 /// [0..infinity[ = 5 106 /// [1..infinity[ = 10 107 /// 108 /// f(0.5) = 5 109 /// f(1) = 10 110 /// f(70) = 10 111 pub struct LowerPartialFunction<B, O> 112 where 113 B: PartialOrd, 114 { 115 funcs: Vec<LowerBoundedFunction<B, O>>, 116 } 117 118 impl<B, O> LowerPartialFunction<B, O> 119 where 120 B: PartialOrd, 121 { 122 /// Creates a new LowerPartialFunctionBuilder. 123 pub fn new() -> LowerPartialFunctionBuilder<B, O> { 124 LowerPartialFunctionBuilder::new() 125 } 126 127 /// Evaluates the partial function. 128 /// Returns None if no function is defined for the searched invariable value (x). 129 pub fn eval(&self, x: B) -> Option<O> { 130 let iter = self.funcs.iter().enumerate(); 131 for (i, bounded) in iter { 132 let next = self.funcs.get(i + 1); 133 if x >= bounded.lower && ((next.is_some() && next.unwrap().lower > x) || next.is_none()) 134 { 135 let f = &bounded.func; 136 return Some(f(x)); 137 } 138 } 139 None 140 } 141 } 142 143 /// A builder to create an immutable PartialFunction. 144 #[derive(new)] 145 pub struct LowerPartialFunctionBuilder<B, O> { 146 #[new(default)] 147 funcs: Vec<LowerBoundedFunction<B, O>>, 148 } 149 150 impl<B: PartialOrd, O> LowerPartialFunctionBuilder<B, O> { 151 /// Adds a bounded function bounded between [lower,higher[ of function func. 152 pub fn with(mut self, lower: B, func: Box<dyn Fn(B) -> O>) -> Self { 153 debug_assert!(self.can_insert(&lower)); 154 let f = LowerBoundedFunction { func, lower }; 155 self.funcs.push(f); 156 self 157 } 158 159 /// Check if you can safely insert into the function list for the specified bounds. 160 pub fn can_insert(&self, lower: &B) -> bool { 161 !self.funcs.iter().any(|b| lower == &b.lower) 162 } 163 164 /// Builds the PartialFunction from the functions added using with. 165 pub fn build(mut self) -> LowerPartialFunction<B, O> { 166 self.funcs 167 .sort_by(|a, b| a.lower.partial_cmp(&b.lower).unwrap_or(Ordering::Equal)); 168 LowerPartialFunction { funcs: self.funcs } 169 } 170 } 171 172 /// More convenient syntax to create a partial function 173 #[macro_export] 174 macro_rules! partfn { 175 ( $( [$start:expr, $end:expr]: $var:ident -> $f:expr,)* ) => { 176 { 177 let mut func = PartialFunction::new(); 178 $( func = func.with($start, $end, Box::new(|$var| $f)); )* 179 func.build() 180 } 181 }; 182 } 183 184 /// More convenient syntax to create a lower partial function 185 #[macro_export] 186 macro_rules! lowpartfn { 187 ( $( [$bound:expr]: $var:ident -> $f:expr,)* ) => { 188 { 189 let mut func = LowerPartialFunction::new(); 190 $( func = func.with($bound, Box::new(|$var| $f)); )* 191 func.build() 192 } 193 }; 194 }