Skip to content

Commit

Permalink
ManagedBuffer - ln cleanup, conversions
Browse files Browse the repository at this point in the history
  • Loading branch information
andrei-marinica committed Jun 27, 2024
1 parent f4e0024 commit 137861f
Show file tree
Hide file tree
Showing 11 changed files with 225 additions and 194 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ pub trait BigFloatWrappedOperators: big_float_operators::BigFloatOperators {
&self,
a: BigInt,
precision: usize,
) -> ManagedDecimal<Self::Api, usize> {
) -> ManagedDecimalSigned<Self::Api, usize> {
let number = self.ln_big_float_ref(&BigFloat::from(a));
number.to_managed_decimal(precision)
}
Expand Down
1 change: 1 addition & 0 deletions framework/base/src/err_msg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ pub const VALUE_EXCEEDS_SLICE: &str = "value exceeds target slice";
pub const CAST_TO_I64_ERROR: &str = "cast to i64 error";
pub const BIG_UINT_EXCEEDS_SLICE: &str = "big uint as_bytes exceed target slice";
pub const BIG_UINT_SUB_NEGATIVE: &str = "cannot subtract because result would be negative";
pub const BIG_UINT_NEGATIVE: &str = "cannot convert to unsigned, number is negative";

pub const DESERIALIZATION_INVALID_BYTE: &str = "call data deserialization error: not a valid byte";
pub const DESERIALIZATION_NOT_32_BYTES: &str =
Expand Down
37 changes: 32 additions & 5 deletions framework/base/src/types/managed/basic/big_float.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ use crate::{
api::{
use_raw_handle, BigFloatApiImpl, ManagedTypeApi, ManagedTypeApiImpl, Sign, StaticVarApiImpl,
},
types::{BigInt, BigUint, Decimals, ManagedDecimal, ManagedType},
contract_base::ErrorHelper,
types::{BigInt, BigUint, Decimals, ManagedDecimalSigned, ManagedType},
};
use alloc::string::String;

Expand Down Expand Up @@ -175,8 +176,8 @@ impl<M: ManagedTypeApi> BigFloat<M> {
(self * denominator).trunc()
}

pub fn to_managed_decimal<T: Decimals>(&self, decimals: T) -> ManagedDecimal<M, T> {
ManagedDecimal::<M, T>::from_big_float(self, decimals)
pub fn to_managed_decimal<T: Decimals>(&self, decimals: T) -> ManagedDecimalSigned<M, T> {
ManagedDecimalSigned::<M, T>::from_big_float(self, decimals)
}

/// Computes the natural logarithm of the current number.
Expand Down Expand Up @@ -242,9 +243,7 @@ impl<M: ManagedTypeApi> BigFloat<M> {

result
}
}

impl<M: ManagedTypeApi> BigFloat<M> {
#[inline]
pub fn zero() -> Self {
BigFloat::from_handle(M::managed_type_impl().bf_new_zero())
Expand Down Expand Up @@ -304,6 +303,34 @@ impl<M: ManagedTypeApi> BigFloat<M> {
}
}

impl<M: ManagedTypeApi> From<f64> for BigFloat<M> {
fn from(x: f64) -> Self {
const PREC: i64 = 1_000_000_000;
Self::from_frac((x * PREC as f64) as i64, PREC)
}
}

impl<M: ManagedTypeApi> From<f32> for BigFloat<M> {
fn from(x: f32) -> Self {
Self::from(x as f64)
}
}

impl<M: ManagedTypeApi> BigFloat<M> {
/// Warning: cannot be used in contracts. It is only meant to simplify certain tests.
///
/// It might also not be optimal with respect to precision.
pub fn to_f64(&self) -> f64 {
const PREC: i64 = 1_000_000_000;
let mut rescaled = Self::from(PREC);
rescaled *= self;
let ln_units = rescaled.trunc().to_i64().unwrap_or_else(|| {
ErrorHelper::<M>::signal_error_with_message("BigFloat out of precision range")
});
ln_units as f64 / PREC as f64
}
}

impl<M: ManagedTypeApi> Clone for BigFloat<M> {
fn clone(&self) -> Self {
let new_handle: M::BigFloatHandle = use_raw_handle(M::static_var_api_impl().next_handle());
Expand Down
28 changes: 25 additions & 3 deletions framework/base/src/types/managed/basic/big_int.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ use core::{convert::TryInto, marker::PhantomData};
use crate::{
abi::{TypeAbiFrom, TypeName},
api::{
const_handles, use_raw_handle, BigIntApiImpl, HandleConstraints, ManagedBufferApiImpl,
ManagedTypeApi, ManagedTypeApiImpl, RawHandle, StaticVarApiImpl,
const_handles, use_raw_handle, BigIntApiImpl, ErrorApiImpl, HandleConstraints,
ManagedBufferApiImpl, ManagedTypeApi, ManagedTypeApiImpl, RawHandle, StaticVarApiImpl,
},
codec::{
DecodeErrorHandler, EncodeErrorHandler, NestedDecode, NestedDecodeInput, NestedEncode,
NestedEncodeOutput, TopDecode, TopDecodeInput, TopEncode, TopEncodeOutput, TryStaticCast,
},
err_msg,
formatter::{hex_util::encode_bytes_as_hex, FormatByteReceiver, SCDisplay},
types::{heap::BoxedBytes, BigUint, ManagedBuffer, ManagedOption, ManagedType, Sign},
};
Expand Down Expand Up @@ -246,14 +247,35 @@ impl<M: ManagedTypeApi> BigInt<M> {
(self.sign(), self.magnitude())
}

/// Converts to an unsigned `BigUint`, without performing any checks.
///
/// # Safety
///
/// If the number is negative, undefined behavior might occur further down the execution.
pub unsafe fn into_big_uint_unchecked(self) -> BigUint<M> {
BigUint::from_handle(self.handle)
}

/// Converts this `BigInt` into a `BigUint`, if it's not negative.
pub fn into_big_uint(self) -> ManagedOption<M, BigUint<M>> {
if let Sign::Minus = self.sign() {
ManagedOption::none()
} else {
ManagedOption::some(BigUint::from_handle(self.handle))
ManagedOption::some(unsafe { self.into_big_uint_unchecked() })
}
}

fn check_non_negative(&self) {
if M::managed_type_impl().bi_sign(self.handle.clone()) == crate::api::Sign::Minus {
M::error_api_impl().signal_error(err_msg::BIG_UINT_SUB_NEGATIVE.as_bytes());
}
}

/// Converts to an unsigned `BigUint`. Stops execution if number is negative.
pub fn into_big_uint_or_fail(self) -> BigUint<M> {
self.check_non_negative();
unsafe { self.into_big_uint_unchecked() }
}
}

impl<M: ManagedTypeApi> TryStaticCast for BigInt<M> {}
Expand Down
4 changes: 4 additions & 0 deletions framework/base/src/types/managed/wrapped/big_uint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ impl<M: ManagedTypeApi> BigUint<M> {
pub fn as_big_int(&self) -> &BigInt<M> {
&self.value
}

pub fn into_big_int(self) -> BigInt<M> {
self.value
}
}

macro_rules! big_uint_conv_num {
Expand Down
56 changes: 18 additions & 38 deletions framework/base/src/types/managed/wrapped/managed_decimal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ pub use managed_decimal_signed::ManagedDecimalSigned;

use crate::{
abi::{TypeAbi, TypeAbiFrom, TypeName},
api::{const_handles, use_raw_handle, BigFloatApiImpl, ManagedTypeApi},
api::ManagedTypeApi,
formatter::{FormatBuffer, FormatByteReceiver, SCDisplay},
types::{BigFloat, BigUint},
types::BigUint,
};

use alloc::string::ToString;
Expand Down Expand Up @@ -85,31 +85,24 @@ impl<M: ManagedTypeApi, D: Decimals> ManagedDecimal<M, D> {
ManagedDecimal::from_raw_units(self.rescale_data(scale_to_num_decimals), scale_to)
}

pub fn to_big_float(&self) -> BigFloat<M> {
let result = BigFloat::from_big_uint(&self.data);
let temp_handle: M::BigFloatHandle = use_raw_handle(const_handles::BIG_FLOAT_TEMPORARY);
let denominator = self.decimals.scaling_factor::<M>();
M::managed_type_impl().bf_set_bi(temp_handle.clone(), denominator.handle);
M::managed_type_impl().bf_div(result.handle.clone(), result.handle.clone(), temp_handle);
result
pub fn into_signed(self) -> ManagedDecimalSigned<M, D> {
ManagedDecimalSigned {
data: self.data.into_big_int(),
decimals: self.decimals,
}
}
}

pub fn from_big_float<T: Decimals>(
big_float: &BigFloat<M>,
num_decimals: T,
) -> ManagedDecimal<M, T> {
let scaling_factor: &BigUint<M> = &num_decimals.scaling_factor();
let magnitude = big_float.magnitude();

let scaled = &BigFloat::from(scaling_factor) * &magnitude;
let fixed_big_int = scaled.trunc();

ManagedDecimal::from_raw_units(
fixed_big_int
.into_big_uint()
.unwrap_or_sc_panic("failed to cast BigInt to BigUint"),
num_decimals,
)
impl<M: ManagedTypeApi, const DECIMALS: NumDecimals> From<BigUint<M>>
for ManagedDecimal<M, ConstDecimals<DECIMALS>>
{
fn from(mut value: BigUint<M>) -> Self {
let decimals = ConstDecimals;
value *= decimals.scaling_factor().deref();
ManagedDecimal {
data: value,
decimals,
}
}
}

Expand Down Expand Up @@ -251,19 +244,6 @@ impl<M: ManagedTypeApi> TopDecode for ManagedDecimal<M, NumDecimals> {
}
}

impl<M: ManagedTypeApi, const DECIMALS: NumDecimals> From<BigUint<M>>
for ManagedDecimal<M, ConstDecimals<DECIMALS>>
{
fn from(mut value: BigUint<M>) -> Self {
let decimals = ConstDecimals;
value *= decimals.scaling_factor().deref();
ManagedDecimal {
data: value,
decimals,
}
}
}

impl<M: ManagedTypeApi> TypeAbiFrom<Self> for ManagedDecimal<M, NumDecimals> {}

impl<M: ManagedTypeApi> TypeAbi for ManagedDecimal<M, NumDecimals> {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,117 +1,69 @@
pub use super::decimals::{ConstDecimals, Decimals, NumDecimals};
pub use super::managed_decimal_signed::ManagedDecimalSigned;
use super::ManagedDecimal;
use super::decimals::{ConstDecimals, Decimals};
use super::ManagedDecimalSigned;
use super::{ManagedDecimal, NumDecimals};

use crate::proxy_imports::ManagedType;
use crate::{
abi::{TypeAbi, TypeAbiFrom, TypeName},
api::{const_handles, use_raw_handle, BigFloatApiImpl, ManagedTypeApi},
api::ManagedTypeApi,
contract_base::ErrorHelper,
formatter::{FormatBuffer, FormatByteReceiver, SCDisplay},
types::{BigFloat, BigUint},
types::{BigInt, BigUint, Sign},
};

impl<M: ManagedTypeApi, D: Decimals> ManagedDecimal<M, D> {
/// Natural logarithm of a number.
///
/// Returns `None` for 0.
///
/// TODO: TEMP impl.
pub fn ln(&self) -> Option<i64> {
let bit_log2 = self.data.log2(); // aproximate, based on position of the most significant bit
if bit_log2 == u32::MAX {
// means the input was zero, TODO: change log2 return type
return None;
}
fn compute_ln<M: ManagedTypeApi>(
data: &BigUint<M>,
num_decimals: NumDecimals,
) -> Option<ManagedDecimalSigned<M, ConstDecimals<9>>> {
let bit_log2 = data.log2(); // aproximate, based on position of the most significant bit
if bit_log2 == u32::MAX {
// means the input was zero, TODO: change log2 return type
return None;
}

let scaling_factor_9 = ConstDecimals::<9>.scaling_factor();
let divisor = BigUint::from(1u64) << bit_log2 as usize;
let normalized = &self.data * &*scaling_factor_9 / divisor;
let scaling_factor_9 = ConstDecimals::<9>.scaling_factor();
let divisor = BigUint::from(1u64) << bit_log2 as usize;
let normalized = data * &*scaling_factor_9 / divisor;

let x = normalized
.to_u64()
.unwrap_or_else(|| ErrorHelper::<M>::signal_error_with_message("ln internal error"))
as i64;
let x = normalized
.to_u64()
.unwrap_or_else(|| ErrorHelper::<M>::signal_error_with_message("ln internal error"))
as i64;

let mut result = crate::types::math_util::logarithm_i64::ln_polynomial(x);
crate::types::math_util::logarithm_i64::ln_add_bit_log2(&mut result, bit_log2);
let mut result = crate::types::math_util::logarithm_i64::ln_polynomial(x);
crate::types::math_util::logarithm_i64::ln_add_bit_log2(&mut result, bit_log2);

debug_assert!(result > 0);
debug_assert!(result > 0);

crate::types::math_util::logarithm_i64::ln_sub_decimals(
&mut result,
self.decimals.num_decimals(),
);
crate::types::math_util::logarithm_i64::ln_sub_decimals(&mut result, num_decimals);

Some(result)
}
Some(ManagedDecimalSigned::from_raw_units(
BigInt::from(result),
ConstDecimals,
))
}

impl<M: ManagedTypeApi, const DECIMALS: NumDecimals> ManagedDecimal<M, ConstDecimals<DECIMALS>> {
// pub fn log(
// &self,
// target_base: &ManagedDecimal<M, D>,
// precision: D,
// ) -> ManagedDecimal<M, NumDecimals> {
// let num_decimals = precision.num_decimals();
// // should verify >= 1
// let one = ManagedDecimal::from_raw_units(BigUint::from(1u64), 0usize);
// one.rescale(self.scale());
// assert!(self >= &one, "wrong input for self");
// one.rescale(target_base.scale());
// assert!(target_base >= &one, "wrong input for target base");

// self.ln(&precision)
// * ManagedDecimal::from_raw_units(BigUint::from(num_decimals), num_decimals)
// / target_base.ln(&precision)
// //this should be done with precision
// }

pub fn ln_temp<const PREC: usize>(
self,
precision: ConstDecimals<PREC>,
) -> ManagedDecimal<M, ConstDecimals<PREC>> {
let num_decimals = self.decimals.num_decimals() as u32;
// find the highest power of 2 less than or equal to self
let log2 = self.data.log2() - num_decimals * BigUint::<M>::from(10u64).log2(); // most significant bit for the actual number
let divisor = 1 << log2;
let divisor_scaled =
BigUint::<M>::from(divisor as u64) * self.decimals.scaling_factor().clone_value();
let _normalized = self.data / divisor_scaled; // normalize to [1.0, 2.0]
let x_dec = ManagedDecimal::<M, ConstDecimals<0>>::const_decimals_from_raw(_normalized);
let x = x_dec.rescale(precision.clone());

// approximating polynom to get the result
let mut result = ManagedDecimal::<M, ConstDecimals<9>>::const_decimals_from_raw(
BigUint::from(56570851u64), // 0.056570851, 9 decimals˝
)
.mul_with_precision(x.clone(), precision.clone());
result = ManagedDecimal::<M, ConstDecimals<8>>::const_decimals_from_raw(BigUint::from(
44717955u64, // 0.44717955, 8 decimals
))
.rescale(precision.clone())
- result;
result = result.mul_with_precision(x.clone(), precision.clone());
result -= ManagedDecimal::<M, ConstDecimals<7>>::const_decimals_from_raw(BigUint::from(
14699568u64, // 1.4699568, 7 decimals
))
.rescale(precision.clone());
result = result.mul_with_precision(x.clone(), precision.clone());
result += ManagedDecimal::<M, ConstDecimals<7>>::const_decimals_from_raw(BigUint::from(
28212026u64, // 2.8212026, 7 decimals
))
.rescale(precision.clone());
result = result.mul_with_precision(x.clone(), precision.clone());
result -= ManagedDecimal::<M, ConstDecimals<7>>::const_decimals_from_raw(BigUint::from(
17417939u64, // 1.7417939, 7 decimals
))
.rescale(precision.clone());
impl<M: ManagedTypeApi, D: Decimals> ManagedDecimal<M, D> {
/// Natural logarithm of a number.
///
/// Returns `None` for 0.
///
/// Even though 9 decimals are returned, only around 6 decimals are actually useful.
pub fn ln(&self) -> Option<ManagedDecimalSigned<M, ConstDecimals<9>>> {
compute_ln(&self.data, self.decimals.num_decimals())
}
}

let log_2 =
ManagedDecimal::<M, ConstDecimals<0>>::const_decimals_from_raw(BigUint::from(log2));
let ln_of_2 = ManagedDecimal::<M, ConstDecimals<8>>::const_decimals_from_raw(
BigUint::from(69314718u64),
); // 0.69314718 8 decimals
impl<M: ManagedTypeApi, D: Decimals> ManagedDecimalSigned<M, D> {
/// Natural logarithm of a number.
///
/// Returns `None` for 0.
///
/// Even though 9 decimals are returned, only around 6 decimals are actually useful.
pub fn ln(&self) -> Option<ManagedDecimalSigned<M, ConstDecimals<9>>> {
if self.sign() != Sign::Plus {
return None;
}

result + log_2.mul_with_precision(ln_of_2, precision.clone())
let bu = BigUint::from_handle(self.data.handle.clone());
compute_ln(&bu, self.decimals.num_decimals())
}
}
Loading

0 comments on commit 137861f

Please sign in to comment.