tachyon_math_library/functions/
exp.rs

1use crate::{FunctionData, FunctionDataAccessors, FunctionLogic, FunctionType, Interpolation, ValueCode, LOAD_ERROR_TOLERANCE};
2use anchor_lang::prelude::*;
3
4use num_traits::Inv;
5use rust_decimal::{Decimal, MathematicalOps};
6
7use crate::error::ErrorCode;
8
9pub struct Exp {}
10
11impl FunctionLogic for Exp {
12    const FUNCTION_TYPE: FunctionType = FunctionType::Exp;
13
14    fn validate_load(x_in: Decimal, y_in: Decimal) -> Result<(Decimal, ValueCode)> {
15        // it's easier to calculate an accurate ln(x) then an accurate exp(x), so compare x to ln(y) instead of y to exp(x)
16        // this would result in higher error tolerance, but is an acceptable tradeoff, since the difficulty in calculating an accurate exp(x) on-chain is the reason for taking pre-calculated off-chain inputs as arguments
17        // this on-chain verification is primarily to catch any bugs/index mix-up in the off-chain loading code
18        let diff = Self::proportion_difference(x_in, y_in.ln())?;
19
20        if diff > LOAD_ERROR_TOLERANCE {
21            return err!(ErrorCode::InvalidValue);
22        }
23
24        Ok((y_in, ValueCode::Valid))
25    }
26
27    fn eval(fd: &FunctionData, x_in: Decimal, interp: Interpolation, saturating: bool) -> Result<Decimal> {
28        let mut x = x_in;
29
30        // e^-x = 1/e^x, so only cover positive values of x and invert if necessary
31        let is_negative = x.is_sign_negative();
32        if is_negative {
33            x.set_sign_positive(true);
34        }
35
36        // grab the domain end
37        let domain_end = fd.get_domain_end()?;
38
39        // test for out of domain bounds
40        if x > domain_end {
41            if saturating {
42                x = domain_end;
43            } else {
44                return err!(ErrorCode::OutOfDomainBounds);
45            }
46        }
47
48        let mut y = Self::interpolate(fd, x, interp)?;
49
50        // invert if sign was negative, as noted above
51        if is_negative {
52            y = y.inv();
53        }
54
55        Ok(y)
56    }
57}