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