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