Skip to main content

multiversx_sc/math/
linear_interpolation.rs

1use core::ops::{Add, Div, Mul, Sub};
2
3/// Error returned when `current_in` is outside the `[min_in, max_in]` range.
4#[derive(Debug)]
5pub struct LinearInterpolationInvalidValuesError;
6
7/// Computes a linearly interpolated output value for a given input within a known range.
8///
9/// Given an input range `[min_in, max_in]` and a corresponding output range `[min_out, max_out]`,
10/// maps `current_in` proportionally to its position in the output range.
11///
12/// Formula:
13/// ```text
14/// out = (min_out * (max_in - current_in) + max_out * (current_in - min_in)) / (max_in - min_in)
15/// ```
16///
17/// Returns [`Err(LinearInterpolationInvalidValuesError)`] if `current_in` is outside `[min_in, max_in]`.
18///
19/// See also: <https://en.wikipedia.org/wiki/Linear_interpolation>
20pub fn linear_interpolation<T>(
21    min_in: T,
22    max_in: T,
23    current_in: T,
24    min_out: T,
25    max_out: T,
26) -> Result<T, LinearInterpolationInvalidValuesError>
27where
28    T: Add<Output = T> + Sub<Output = T> + Mul<Output = T> + Div<Output = T> + PartialOrd + Clone,
29{
30    if min_in > max_in || current_in < min_in || current_in > max_in {
31        return Err(LinearInterpolationInvalidValuesError);
32    }
33
34    let min_out_weighted = min_out * (max_in.clone() - current_in.clone());
35    let max_out_weighted = max_out * (current_in - min_in.clone());
36    let in_diff = max_in - min_in;
37
38    let result = (min_out_weighted + max_out_weighted) / in_diff;
39    Ok(result)
40}