multiversx_sc/types/managed/wrapped/
big_uint.rs

1use core::convert::TryInto;
2
3use crate::{
4    abi::{TypeAbi, TypeAbiFrom, TypeName},
5    api::{
6        const_handles, use_raw_handle, BigIntApiImpl, HandleConstraints, ManagedBufferApiImpl,
7        ManagedTypeApi, ManagedTypeApiImpl, RawHandle,
8    },
9    codec::{
10        DecodeErrorHandler, EncodeErrorHandler, NestedDecode, NestedDecodeInput, NestedEncode,
11        NestedEncodeOutput, TopDecode, TopDecodeInput, TopEncode, TopEncodeOutput, TryStaticCast,
12    },
13    contract_base::ErrorHelper,
14    formatter::{hex_util::encode_bytes_as_hex, FormatBuffer, FormatByteReceiver, SCDisplay},
15    types::{
16        heap::BoxedBytes, BigInt, Decimals, LnDecimals, ManagedBuffer, ManagedBufferCachedBuilder,
17        ManagedDecimal, ManagedRef, ManagedType,
18    },
19};
20
21#[repr(transparent)]
22pub struct BigUint<M: ManagedTypeApi> {
23    pub(crate) value: BigInt<M>,
24}
25
26impl<M: ManagedTypeApi> ManagedType<M> for BigUint<M> {
27    type OwnHandle = M::BigIntHandle;
28
29    unsafe fn from_handle(handle: M::BigIntHandle) -> Self {
30        BigUint {
31            value: BigInt::from_handle(handle),
32        }
33    }
34
35    fn get_handle(&self) -> M::BigIntHandle {
36        self.value.handle.clone()
37    }
38
39    unsafe fn forget_into_handle(self) -> Self::OwnHandle {
40        self.value.forget_into_handle()
41    }
42
43    fn transmute_from_handle_ref(handle_ref: &M::BigIntHandle) -> &Self {
44        unsafe { core::mem::transmute(handle_ref) }
45    }
46
47    fn transmute_from_handle_ref_mut(handle_ref: &mut M::BigIntHandle) -> &mut Self {
48        unsafe { core::mem::transmute(handle_ref) }
49    }
50}
51
52impl<M: ManagedTypeApi> From<u128> for BigUint<M> {
53    fn from(value: u128) -> Self {
54        BigUint::from_bytes_be(&value.to_be_bytes()[..])
55    }
56}
57
58impl<M: ManagedTypeApi> TypeAbiFrom<u128> for BigUint<M> {}
59
60impl<M: ManagedTypeApi> From<ManagedBuffer<M>> for BigUint<M> {
61    #[inline]
62    fn from(item: ManagedBuffer<M>) -> Self {
63        BigUint::from_bytes_be_buffer(&item)
64    }
65}
66
67impl<M: ManagedTypeApi> From<&ManagedBuffer<M>> for BigUint<M> {
68    #[inline]
69    fn from(item: &ManagedBuffer<M>) -> Self {
70        BigUint::from_bytes_be_buffer(item)
71    }
72}
73
74impl<M: ManagedTypeApi> BigUint<M> {
75    /// Creates a new object, without initializing it.
76    ///
77    /// ## Safety
78    ///
79    /// The value needs to be initialized after creation, otherwise the VM will halt the first time the value is attempted to be read.
80    pub unsafe fn new_uninit() -> Self {
81        BigUint {
82            value: BigInt::new_uninit(),
83        }
84    }
85
86    pub(crate) fn set_value<T>(handle: M::BigIntHandle, value: T)
87    where
88        T: TryInto<i64> + num_traits::Unsigned,
89    {
90        BigInt::<M>::set_value(handle, value);
91    }
92
93    pub(crate) fn new_from_num<T>(value: T) -> Self
94    where
95        T: TryInto<i64> + num_traits::Unsigned,
96    {
97        unsafe {
98            let result = Self::new_uninit();
99            Self::set_value(result.get_handle(), value);
100            result
101        }
102    }
103
104    pub(crate) fn make_temp<T>(handle: RawHandle, value: T) -> M::BigIntHandle
105    where
106        T: TryInto<i64> + num_traits::Unsigned,
107    {
108        let temp: M::BigIntHandle = use_raw_handle(handle);
109        Self::set_value(temp.clone(), value);
110        temp
111    }
112
113    pub fn as_big_int(&self) -> &BigInt<M> {
114        &self.value
115    }
116
117    pub fn into_big_int(self) -> BigInt<M> {
118        self.value
119    }
120}
121
122macro_rules! big_uint_conv_num {
123    ($num_ty:ty) => {
124        impl<M: ManagedTypeApi> From<$num_ty> for BigUint<M> {
125            #[inline]
126            fn from(value: $num_ty) -> Self {
127                Self::new_from_num(value)
128            }
129        }
130
131        impl<M: ManagedTypeApi> TypeAbiFrom<$num_ty> for BigUint<M> {}
132    };
133}
134
135big_uint_conv_num! {u64}
136big_uint_conv_num! {u32}
137big_uint_conv_num! {usize}
138big_uint_conv_num! {u16}
139big_uint_conv_num! {u8}
140
141#[cfg(feature = "num-bigint")]
142impl<M: ManagedTypeApi> TypeAbiFrom<crate::codec::num_bigint::BigUint> for BigUint<M> {}
143#[cfg(feature = "num-bigint")]
144impl<M: ManagedTypeApi> TypeAbiFrom<BigUint<M>> for crate::codec::num_bigint::BigUint {}
145
146impl<M> TypeAbiFrom<Self> for BigUint<M> where M: ManagedTypeApi {}
147impl<M> TypeAbiFrom<&Self> for BigUint<M> where M: ManagedTypeApi {}
148
149impl<M: ManagedTypeApi> TypeAbi for BigUint<M> {
150    #[cfg(feature = "num-bigint")]
151    type Unmanaged = crate::codec::num_bigint::BigUint;
152
153    #[cfg(not(feature = "num-bigint"))]
154    type Unmanaged = Self;
155
156    fn type_name() -> TypeName {
157        TypeName::from("BigUint")
158    }
159
160    fn type_name_rust() -> TypeName {
161        TypeName::from("BigUint<$API>")
162    }
163}
164
165#[cfg(feature = "num-bigint")]
166impl<M: ManagedTypeApi> From<&crate::codec::num_bigint::BigUint> for BigUint<M> {
167    fn from(alloc_big_uint: &crate::codec::num_bigint::BigUint) -> Self {
168        BigUint::from_bytes_be(alloc_big_uint.to_bytes_be().as_slice())
169    }
170}
171#[cfg(feature = "num-bigint")]
172impl<M: ManagedTypeApi> From<crate::codec::num_bigint::BigUint> for BigUint<M> {
173    fn from(alloc_big_uint: crate::codec::num_bigint::BigUint) -> Self {
174        BigUint::from(&alloc_big_uint)
175    }
176}
177#[cfg(feature = "num-bigint")]
178impl<M: ManagedTypeApi> BigUint<M> {
179    pub fn to_alloc(&self) -> crate::codec::num_bigint::BigUint {
180        crate::codec::num_bigint::BigUint::from_bytes_be(self.to_bytes_be().as_slice())
181    }
182}
183
184impl<M: ManagedTypeApi> Default for BigUint<M> {
185    #[inline]
186    fn default() -> Self {
187        Self::zero()
188    }
189}
190
191/// More conversions here.
192impl<M: ManagedTypeApi> BigUint<M> {
193    #[inline]
194    pub fn zero() -> Self {
195        unsafe {
196            let result = Self::new_uninit();
197            M::managed_type_impl().bi_set_int64(result.get_handle(), 0);
198            result
199        }
200    }
201
202    pub fn zero_ref() -> ManagedRef<'static, M, BigUint<M>> {
203        let handle: M::BigIntHandle = use_raw_handle(const_handles::BIG_INT_CONST_ZERO);
204        M::managed_type_impl().bi_set_int64(handle.clone(), 0);
205        unsafe { ManagedRef::wrap_handle(handle) }
206    }
207
208    #[inline]
209    pub fn to_u64(&self) -> Option<u64> {
210        let api = M::managed_type_impl();
211        api.bi_to_i64(self.value.handle.clone()).map(|bi| bi as u64)
212    }
213
214    #[inline]
215    pub fn overwrite_u64(&mut self, value: u64) {
216        Self::set_value(self.value.handle.clone(), value);
217    }
218
219    pub fn from_bytes_be(bytes: &[u8]) -> Self {
220        let mb_handle: M::ManagedBufferHandle = use_raw_handle(const_handles::MBUF_TEMPORARY_1);
221        M::managed_type_impl().mb_overwrite(mb_handle.clone(), bytes);
222        unsafe {
223            let result = Self::new_uninit();
224            M::managed_type_impl().mb_to_big_int_unsigned(mb_handle, result.get_handle());
225            result
226        }
227    }
228
229    pub fn to_bytes_be(&self) -> BoxedBytes {
230        let mb_handle: M::ManagedBufferHandle = use_raw_handle(const_handles::MBUF_TEMPORARY_1);
231        M::managed_type_impl()
232            .mb_from_big_int_unsigned(self.value.handle.clone(), mb_handle.clone());
233        M::managed_type_impl().mb_to_boxed_bytes(mb_handle)
234    }
235
236    pub fn from_bytes_be_buffer(managed_buffer: &ManagedBuffer<M>) -> Self {
237        unsafe {
238            let result = BigUint::new_uninit();
239            M::managed_type_impl()
240                .mb_to_big_int_unsigned(managed_buffer.handle.clone(), result.get_handle());
241            result
242        }
243    }
244
245    pub fn to_bytes_be_buffer(&self) -> ManagedBuffer<M> {
246        unsafe {
247            let result = ManagedBuffer::new_uninit();
248            M::managed_type_impl().mb_from_big_int_unsigned(self.get_handle(), result.get_handle());
249            result
250        }
251    }
252}
253
254impl<M: ManagedTypeApi> BigUint<M> {
255    pub fn sqrt(&self) -> Self {
256        unsafe {
257            let result = BigUint::new_uninit();
258            M::managed_type_impl().bi_sqrt(result.get_handle(), self.get_handle());
259            result
260        }
261    }
262
263    pub fn pow(&self, exp: u32) -> Self {
264        let big_int_temp_1 = BigUint::<M>::make_temp(const_handles::BIG_INT_TEMPORARY_1, exp);
265        unsafe {
266            let result = BigUint::new_uninit();
267            M::managed_type_impl().bi_pow(result.get_handle(), self.get_handle(), big_int_temp_1);
268            result
269        }
270    }
271
272    /// The whole part of the base-2 logarithm.
273    ///
274    /// Obtained by counting the significant bits.
275    /// More specifically, the log2 floor is the position of the most significant bit minus one.
276    ///
277    /// Will return `None` for the number zero (the logarithm in this case would approach -inf).
278    pub fn log2_floor(&self) -> Option<u32> {
279        let api = M::managed_type_impl();
280        let result = api.bi_log2(self.value.handle.clone());
281        if result < 0 {
282            None
283        } else {
284            Some(result as u32)
285        }
286    }
287
288    /// Natural logarithm of a number.
289    ///
290    /// Returns `None` for 0.
291    pub fn ln(&self) -> Option<ManagedDecimal<M, LnDecimals>> {
292        // start with approximation, based on position of the most significant bit
293        let Some(log2_floor) = self.log2_floor() else {
294            // means the input was zero
295            return None;
296        };
297
298        let scaling_factor_9 = LnDecimals::new().scaling_factor();
299        let divisor = BigUint::from(1u64) << log2_floor as usize;
300        let normalized = self * &*scaling_factor_9 / divisor;
301
302        let x = normalized
303            .to_u64()
304            .unwrap_or_else(|| ErrorHelper::<M>::signal_error_with_message("ln internal error"))
305            as i64;
306
307        let mut result = crate::types::math_util::logarithm_i64::ln_polynomial(x);
308        crate::types::math_util::logarithm_i64::ln_add_bit_log2(&mut result, log2_floor);
309
310        debug_assert!(result > 0);
311
312        let mut result_bi = normalized; // reuse handle
313        result_bi.overwrite_u64(result as u64);
314
315        Some(ManagedDecimal::const_decimals_from_raw(result_bi))
316    }
317}
318
319impl<M: ManagedTypeApi> Clone for BigUint<M> {
320    fn clone(&self) -> Self {
321        unsafe { self.as_big_int().clone().into_big_uint_unchecked() }
322    }
323}
324
325impl<M: ManagedTypeApi> TryStaticCast for BigUint<M> {}
326
327impl<M: ManagedTypeApi> TopEncode for BigUint<M> {
328    #[inline]
329    fn top_encode_or_handle_err<O, H>(&self, output: O, h: H) -> Result<(), H::HandledErr>
330    where
331        O: TopEncodeOutput,
332        H: EncodeErrorHandler,
333    {
334        if O::supports_specialized_type::<Self>() {
335            output.set_specialized(self, h)
336        } else {
337            output.set_slice_u8(self.to_bytes_be().as_slice());
338            Ok(())
339        }
340    }
341}
342
343impl<M: ManagedTypeApi> NestedEncode for BigUint<M> {
344    fn dep_encode_or_handle_err<O, H>(&self, dest: &mut O, h: H) -> Result<(), H::HandledErr>
345    where
346        O: NestedEncodeOutput,
347        H: EncodeErrorHandler,
348    {
349        self.to_bytes_be_buffer().dep_encode_or_handle_err(dest, h)
350    }
351}
352
353impl<M: ManagedTypeApi> NestedDecode for BigUint<M> {
354    fn dep_decode_or_handle_err<I, H>(input: &mut I, h: H) -> Result<Self, H::HandledErr>
355    where
356        I: NestedDecodeInput,
357        H: DecodeErrorHandler,
358    {
359        if I::supports_specialized_type::<Self>() {
360            input.read_specialized((), h)
361        } else {
362            let boxed_bytes = BoxedBytes::dep_decode_or_handle_err(input, h)?;
363            Ok(Self::from_bytes_be(boxed_bytes.as_slice()))
364        }
365    }
366}
367
368impl<M: ManagedTypeApi> TopDecode for BigUint<M> {
369    fn top_decode_or_handle_err<I, H>(input: I, h: H) -> Result<Self, H::HandledErr>
370    where
371        I: TopDecodeInput,
372        H: DecodeErrorHandler,
373    {
374        if I::supports_specialized_type::<Self>() {
375            input.into_specialized(h)
376        } else {
377            let boxed_bytes = BoxedBytes::top_decode_or_handle_err(input, h)?;
378            Ok(Self::from_bytes_be(boxed_bytes.as_slice()))
379        }
380    }
381}
382
383impl<M: ManagedTypeApi> SCDisplay for BigUint<M> {
384    fn fmt<F: FormatByteReceiver>(&self, f: &mut F) {
385        let str_handle: M::ManagedBufferHandle = use_raw_handle(const_handles::MBUF_TEMPORARY_1);
386        M::managed_type_impl().bi_to_string(self.value.handle.clone(), str_handle.clone());
387        let cast_handle = str_handle.cast_or_signal_error::<M, _>();
388        let wrap_cast = unsafe { ManagedRef::wrap_handle(cast_handle) };
389        f.append_managed_buffer(&wrap_cast);
390    }
391}
392
393impl<M: ManagedTypeApi> BigUint<M> {
394    /// Creates to a managed buffer containing the textual representation of the number.
395    pub fn to_display(&self) -> ManagedBuffer<M> {
396        let mut result = ManagedBufferCachedBuilder::new_from_slice(&[]);
397        result.append_display(self);
398        result.into_managed_buffer()
399    }
400}
401
402impl<M: ManagedTypeApi> core::fmt::Debug for BigUint<M> {
403    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
404        f.debug_struct("BigUint")
405            .field("handle", &self.value.handle.clone())
406            .field(
407                "hex-value-be",
408                &encode_bytes_as_hex(self.to_bytes_be().as_slice()),
409            )
410            .finish()
411    }
412}