multiversx_sc/types/managed/wrapped/managed_decimal/
managed_decimal_logarithm.rs

1use super::decimals::{Decimals, LnDecimals};
2use super::ManagedDecimalSigned;
3use super::{ManagedDecimal, NumDecimals};
4
5use crate::types::ManagedRef;
6use crate::{
7    api::ManagedTypeApi,
8    contract_base::ErrorHelper,
9    types::{BigInt, BigUint, Sign},
10};
11
12fn compute_ln<M: ManagedTypeApi>(
13    data: &BigUint<M>,
14    num_decimals: NumDecimals,
15) -> Option<ManagedDecimalSigned<M, LnDecimals>> {
16    // start with approximation, based on position of the most significant bit
17    let Some(log2_floor) = data.log2_floor() else {
18        // means the input was zero
19        return None;
20    };
21
22    let scaling_factor_9 = LnDecimals::new().scaling_factor();
23    let divisor = BigUint::from(1u64) << log2_floor as usize;
24    let normalized = data * &*scaling_factor_9 / divisor;
25
26    let x = normalized
27        .to_u64()
28        .unwrap_or_else(|| ErrorHelper::<M>::signal_error_with_message("ln internal error"))
29        as i64;
30
31    let mut result = crate::types::math_util::logarithm_i64::ln_polynomial(x);
32    crate::types::math_util::logarithm_i64::ln_add_bit_log2(&mut result, log2_floor);
33
34    debug_assert!(result > 0);
35
36    crate::types::math_util::logarithm_i64::ln_sub_decimals(&mut result, num_decimals);
37
38    Some(ManagedDecimalSigned::from_raw_units(
39        BigInt::from(result),
40        LnDecimals::new(),
41    ))
42}
43
44fn compute_log2<M: ManagedTypeApi>(
45    data: &BigUint<M>,
46    num_decimals: NumDecimals,
47) -> Option<ManagedDecimalSigned<M, LnDecimals>> {
48    // start with approximation, based on position of the most significant bit
49    let Some(log2_floor) = data.log2_floor() else {
50        // means the input was zero
51        return None;
52    };
53
54    let scaling_factor_9 = LnDecimals::new().scaling_factor();
55    let divisor = BigUint::from(1u64) << log2_floor as usize;
56    let normalized = data * &*scaling_factor_9 / divisor;
57
58    let x = normalized
59        .to_u64()
60        .unwrap_or_else(|| ErrorHelper::<M>::signal_error_with_message("log2 internal error"))
61        as i64;
62
63    let mut result = crate::types::math_util::logarithm_i64::log2_polynomial(x);
64    crate::types::math_util::logarithm_i64::log2_add_bit_log2(&mut result, log2_floor);
65
66    debug_assert!(result > 0);
67
68    crate::types::math_util::logarithm_i64::log2_sub_decimals(&mut result, num_decimals);
69
70    Some(ManagedDecimalSigned::from_raw_units(
71        BigInt::from(result),
72        LnDecimals::new(),
73    ))
74}
75
76impl<M: ManagedTypeApi, D: Decimals> ManagedDecimal<M, D> {
77    /// Natural logarithm of a number.
78    ///
79    /// Returns `None` for 0.
80    ///
81    /// Even though 9 decimals are returned, only around 6 decimals are actually useful.
82    pub fn ln(&self) -> Option<ManagedDecimalSigned<M, LnDecimals>> {
83        compute_ln(&self.data, self.decimals.num_decimals())
84    }
85
86    /// Base 2 logarithm of a number.
87    ///
88    /// Returns `None` for 0.
89    ///
90    /// Even though 9 decimals are returned, only around 6 decimals are actually useful.
91    pub fn log2(&self) -> Option<ManagedDecimalSigned<M, LnDecimals>> {
92        compute_log2(&self.data, self.decimals.num_decimals())
93    }
94}
95
96impl<M: ManagedTypeApi, D: Decimals> ManagedDecimalSigned<M, D> {
97    /// Natural logarithm of a number.
98    ///
99    /// Returns `None` for 0.
100    ///
101    /// Even though 9 decimals are returned, only around 6 decimals are actually useful.
102    pub fn ln(&self) -> Option<ManagedDecimalSigned<M, LnDecimals>> {
103        if self.sign() != Sign::Plus {
104            return None;
105        }
106
107        let bu = unsafe { ManagedRef::wrap_handle(self.data.handle.clone()) };
108        compute_ln(&bu, self.decimals.num_decimals())
109    }
110
111    /// Base 2 logarithm of a number.
112    ///
113    /// Returns `None` for 0.
114    ///
115    /// Even though 9 decimals are returned, only around 6 decimals are actually useful.
116    pub fn log2(&self) -> Option<ManagedDecimalSigned<M, LnDecimals>> {
117        if self.sign() != Sign::Plus {
118            return None;
119        }
120
121        let bu = unsafe { ManagedRef::wrap_handle(self.data.handle.clone()) };
122        compute_log2(&bu, self.decimals.num_decimals())
123    }
124}