balancer_maths_rust/common/
maths.rs

1//! Mathematical utilities for fixed-point arithmetic
2
3use crate::common::constants::{FOUR_WAD, MAX_POW_RELATIVE_ERROR, TWO_WAD, WAD};
4use crate::common::errors::PoolError;
5use crate::common::log_exp_math;
6use num_bigint::BigInt;
7use num_traits::{One, Zero};
8
9/// Multiply two BigInts and round up
10pub fn mul_up_fixed(a: &BigInt, b: &BigInt) -> Result<BigInt, PoolError> {
11    let product = a * b;
12    if product.is_zero() {
13        return Ok(BigInt::zero());
14    }
15    let result = (product - BigInt::one()) / &*WAD + BigInt::one();
16    Ok(result)
17}
18
19/// Divide two BigInts and round up
20pub fn div_up_fixed(a: &BigInt, b: &BigInt) -> Result<BigInt, PoolError> {
21    if a.is_zero() {
22        return Ok(BigInt::zero());
23    }
24    if b.is_zero() {
25        return Err(PoolError::MathOverflow);
26    }
27
28    let a_inflated = a * &*WAD;
29    let result = (&a_inflated - BigInt::one()) / b + BigInt::one();
30    Ok(result)
31}
32
33/// Multiply two BigInts and round down
34pub fn mul_down_fixed(a: &BigInt, b: &BigInt) -> Result<BigInt, PoolError> {
35    let product = a * b;
36    let result = product / &*WAD;
37    Ok(result)
38}
39
40/// Divide two BigInts and round down
41pub fn div_down_fixed(a: &BigInt, b: &BigInt) -> Result<BigInt, PoolError> {
42    if a.is_zero() {
43        return Ok(BigInt::zero());
44    }
45    if b.is_zero() {
46        return Err(PoolError::MathOverflow);
47    }
48
49    let a_inflated = a * &*WAD;
50    let result = a_inflated / b;
51    Ok(result)
52}
53
54/// Divide and round up (raw division)
55pub fn div_up(a: &BigInt, b: &BigInt) -> Result<BigInt, PoolError> {
56    if b.is_zero() {
57        return Ok(BigInt::zero());
58    }
59    let result = BigInt::one() + (a - &BigInt::one()) / b;
60    Ok(result)
61}
62
63/// Multiply and divide with up rounding
64pub fn mul_div_up_fixed(a: &BigInt, b: &BigInt, c: &BigInt) -> Result<BigInt, PoolError> {
65    let product = a * b;
66    let result = (&product - &BigInt::one()) / c + &BigInt::one();
67    Ok(result)
68}
69
70/// Calculate power with down rounding (default version 0)
71pub fn pow_down_fixed(base: &BigInt, exponent: &BigInt) -> Result<BigInt, PoolError> {
72    pow_down_fixed_with_version(base, exponent, 0)
73}
74
75/// Calculate power with down rounding with explicit version
76pub fn pow_down_fixed_with_version(
77    base: &BigInt,
78    exponent: &BigInt,
79    version: u32,
80) -> Result<BigInt, PoolError> {
81    if exponent == &*WAD && version != 1 {
82        return Ok(base.clone());
83    }
84    if exponent == &*TWO_WAD && version != 1 {
85        return mul_up_fixed(base, base);
86    }
87    if exponent == &*FOUR_WAD && version != 1 {
88        let square = mul_up_fixed(base, base)?;
89        return mul_up_fixed(&square, &square);
90    }
91
92    let raw = log_exp_math::pow(base, exponent)?;
93    let max_error = mul_up_fixed(&raw, &MAX_POW_RELATIVE_ERROR)? + &BigInt::one();
94
95    if raw < max_error {
96        return Ok(BigInt::zero());
97    }
98
99    Ok(raw - max_error)
100}
101
102/// Calculate power with up rounding (default version 0)
103pub fn pow_up_fixed(base: &BigInt, exponent: &BigInt) -> Result<BigInt, PoolError> {
104    pow_up_fixed_with_version(base, exponent, 0)
105}
106
107/// Calculate power with up rounding with explicit version
108pub fn pow_up_fixed_with_version(
109    base: &BigInt,
110    exponent: &BigInt,
111    version: u32,
112) -> Result<BigInt, PoolError> {
113    if exponent == &*WAD && version != 1 {
114        return Ok(base.clone());
115    }
116    if exponent == &*TWO_WAD && version != 1 {
117        return mul_up_fixed(base, base);
118    }
119    if exponent == &*FOUR_WAD && version != 1 {
120        let square = mul_up_fixed(base, base)?;
121        return mul_up_fixed(&square, &square);
122    }
123
124    let raw = log_exp_math::pow(base, exponent)?;
125    let max_error = mul_up_fixed(&raw, &MAX_POW_RELATIVE_ERROR)? + &BigInt::one();
126
127    Ok(raw + max_error)
128}
129
130/// Calculate complement (1 - x) with fixed-point arithmetic
131pub fn complement_fixed(x: &BigInt) -> Result<BigInt, PoolError> {
132    if x < &*WAD {
133        Ok(&*WAD - x)
134    } else {
135        Ok(BigInt::zero())
136    }
137}