partial_function

[Done] Partial Function library for Rust.
git clone https://git.jojolepro.com/partial_function.git
Log | Files | Refs | README | LICENSE

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 }