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
#[macro_use]
extern crate derive_new;

use std::cmp::Ordering;

/// A regular function that is only defined between lower and higher.
/// If two functions intersect their higher and lower bounds respectively.
/// The second will take precedence where f(lower).
#[derive(new)]
pub struct DualBoundedFunction<B, O> {
    /// The stored function f(x) = ???
    pub func: Box<dyn Fn(B) -> O>,
    /// The lower bound of the function.
    pub lower: B,
    /// The higher bound of the function.
    pub higher: B,
}

/// Define a functions defined by multiple functions parts.
/// See BoundedFunction.
/// Uses bounds as [lower,higher],
/// except in the case of a lower bound overlapping a higher bound.
/// In this case, the lower bound always take precedence.
pub struct PartialFunction<B, O> {
    funcs: Vec<DualBoundedFunction<B, O>>,
}

impl<B: PartialOrd, O> PartialFunction<B, O> {
    /// Creates a new PartialFunctionBuilder
    pub fn new() -> PartialFunctionBuilder<B, O> {
        PartialFunctionBuilder::new()
    }

    /// Evaluates the partial function.
    /// Returns None if no function is defined.
    pub fn eval(&self, x: B) -> Option<O> {
        let iter = self.funcs.iter().enumerate();
        for (i, bounded) in iter {
            let next = self.funcs.get(i + 1);
            if (x >= bounded.lower && x < bounded.higher)
                || (next.is_none() && x == bounded.higher)
                || (next.is_some() && next.unwrap().lower != bounded.higher)
            {
                let f = &bounded.func;
                return Some(f(x));
            }
        }
        None
    }
}

/// A builder to create an immutable PartialFunction.
#[derive(new)]
pub struct PartialFunctionBuilder<B, O> {
    #[new(default)]
    funcs: Vec<DualBoundedFunction<B, O>>,
}

impl<B: PartialOrd, O> PartialFunctionBuilder<B, O> {
    /// Adds a bounded function bounded between [lower,higher[ of function func.
    pub fn with(mut self, lower: B, higher: B, func: Box<dyn Fn(B) -> O>) -> Self {
        debug_assert!(self.can_insert(&lower, &higher));
        let f = DualBoundedFunction {
            func: func,
            lower: lower,
            higher: higher,
        };
        self.funcs.push(f);
        self
    }

    /// Check if you can safely insert into the function list for the specified bounds.
    pub fn can_insert(&self, lower: &B, higher: &B) -> bool {
        !self.funcs.iter().any(|b| {
            (lower >= &b.lower && lower < &b.higher)
                || (higher > &b.lower && higher <= &b.higher)
                || (lower <= &b.lower && higher >= &b.higher)
        })
    }

    /// Builds the PartialFunction from the functions added using with.
    pub fn build(mut self) -> PartialFunction<B, O> {
        self.funcs.sort_by(|a, b| {
            a.lower
                .partial_cmp(&b.lower)
                .unwrap_or(a.higher.partial_cmp(&b.higher).unwrap_or(Ordering::Equal))
        });
        PartialFunction { funcs: self.funcs }
    }
}

/// A lower bounded function is a function that is valid from [x..infinite[, or until it hits another function's start.
#[derive(new)]
struct LowerBoundedFunction<B, O> {
    /// The stored function f(x) = ???
    pub func: Box<dyn Fn(B) -> O>,
    /// The lower bound of the function.
    pub lower: B,
}

/// A lower partial function is a function that is defined by segments valid from [x..infinite[, or until it hits another function's start.
/// It starts searching at -infinity and goes up to infinity, and takes the last seen function that contains the desired invariable value (x).
///
/// Example:
/// [0..infinity[ = 5
/// [1..infinity[ = 10
///
/// f(0.5) = 5
/// f(1) = 10
/// f(70) = 10
pub struct LowerPartialFunction<B, O>
where
    B: PartialOrd,
{
    funcs: Vec<LowerBoundedFunction<B, O>>,
}

impl<B, O> LowerPartialFunction<B, O>
where
    B: PartialOrd,
{
    /// Creates a new LowerPartialFunctionBuilder.
    pub fn new() -> LowerPartialFunctionBuilder<B, O> {
        LowerPartialFunctionBuilder::new()
    }

    /// Evaluates the partial function.
    /// Returns None if no function is defined for the searched invariable value (x).
    pub fn eval(&self, x: B) -> Option<O> {
        let iter = self.funcs.iter().enumerate();
        for (i, bounded) in iter {
            let next = self.funcs.get(i + 1);
            if x >= bounded.lower && ((next.is_some() && next.unwrap().lower > x) || next.is_none())
            {
                let f = &bounded.func;
                return Some(f(x));
            }
        }
        None
    }
}

/// A builder to create an immutable PartialFunction.
#[derive(new)]
pub struct LowerPartialFunctionBuilder<B, O> {
    #[new(default)]
    funcs: Vec<LowerBoundedFunction<B, O>>,
}

impl<B: PartialOrd, O> LowerPartialFunctionBuilder<B, O> {
    /// Adds a bounded function bounded between [lower,higher[ of function func.
    pub fn with(mut self, lower: B, func: Box<dyn Fn(B) -> O>) -> Self {
        debug_assert!(self.can_insert(&lower));
        let f = LowerBoundedFunction { func, lower };
        self.funcs.push(f);
        self
    }

    /// Check if you can safely insert into the function list for the specified bounds.
    pub fn can_insert(&self, lower: &B) -> bool {
        !self.funcs.iter().any(|b| lower == &b.lower)
    }

    /// Builds the PartialFunction from the functions added using with.
    pub fn build(mut self) -> LowerPartialFunction<B, O> {
        self.funcs
            .sort_by(|a, b| a.lower.partial_cmp(&b.lower).unwrap_or(Ordering::Equal));
        LowerPartialFunction { funcs: self.funcs }
    }
}