Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BigUint - ln #1682

Merged
merged 21 commits into from
Jun 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
519 changes: 500 additions & 19 deletions Cargo.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ members = [

"tools/mxpy-snippet-generator",
"tools/payload-macro-generator",
"tools/plotter",

"vm",

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ pub trait BigIntMethods {
}

#[endpoint]
fn biguint_overwrite_u64(&self, bu: BigUint, small: u64) -> BigUint {
fn biguint_overwrite_u64(&self, mut bu: BigUint, small: u64) -> BigUint {
bu.overwrite_u64(small);
bu
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,17 @@ fn big_float_overflow_test_rs() {
assert_eq!(forth, &BigUint::from(2184473079534488064u64));
}

#[test]
fn big_float_ln_test_rs() {
let x = BigFloat::<StaticApi>::from(23i64);
let ln_x = x.ln();
assert_eq!(ln_x.to_managed_decimal(9usize).to_string(), "3.135514648");
assert!(ln_x.is_close(
&BigFloat::from_frac(3135514648, 1_000_000_000), // 3.135514648
&BigFloat::from_frac(1, 1_000_000_000)
));
}

#[test]
fn big_float_new_from_big_int_rs() {
world().run("scenarios/big_float_new_from_big_int.scen.json");
Expand Down
43 changes: 39 additions & 4 deletions framework/base/src/types/managed/basic/big_float.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,23 +93,27 @@ big_float_conv_num! {i16}
big_float_conv_num! {i8}

impl<M: ManagedTypeApi> BigFloat<M> {
#[inline]
pub fn neg(&self) -> Self {
let new_bf_handle: M::BigFloatHandle =
use_raw_handle(M::static_var_api_impl().next_handle());
M::managed_type_impl().bf_neg(new_bf_handle.clone(), self.handle.clone());
BigFloat::from_handle(new_bf_handle)
}

#[inline]
pub fn abs(&self) -> Self {
let new_bf_handle: M::BigFloatHandle =
use_raw_handle(M::static_var_api_impl().next_handle());
M::managed_type_impl().bf_abs(new_bf_handle.clone(), self.handle.clone());
BigFloat::from_handle(new_bf_handle)
}

pub fn from_big_uint(big_uint: &BigUint<M>) -> Self {
let new_bf_handle: M::BigFloatHandle =
use_raw_handle(M::static_var_api_impl().next_handle());
M::managed_type_impl().bf_set_bi(new_bf_handle.clone(), big_uint.handle.clone());
BigFloat::from_handle(new_bf_handle)
}

#[inline]
pub fn from_big_int(big_int: &BigInt<M>) -> Self {
let new_bf_handle: M::BigFloatHandle =
use_raw_handle(M::static_var_api_impl().next_handle());
Expand Down Expand Up @@ -168,9 +172,40 @@ impl<M: ManagedTypeApi> BigFloat<M> {
(self * denominator).trunc()
}

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

pub fn ln(&self) -> Self {
// find the highest power of 2 less than or equal to self
let trunc_val = self.trunc();
let trunc_val_unsigned = trunc_val
.into_big_uint()
.unwrap_or_sc_panic("log argument must be positive");
let bit_log2 = trunc_val_unsigned.log2(); // aproximate, based on position of the most significant bit
let divisor = BigFloat::from(1 << bit_log2);
let x = self / &divisor; // normalize to [1.0, 2.0]

debug_assert!(x >= 1);
debug_assert!(x <= 2);

const DENOMINATOR: i64 = 1_000_000_000;

let mut result = BigFloat::from_frac(-56570851, DENOMINATOR); // -0.056570851
result *= &x;
result += BigFloat::from_frac(447179550, DENOMINATOR); // 0.44717955
result *= &x;
result += BigFloat::from_frac(-1469956800, DENOMINATOR); // -1.4699568
result *= &x;
result += BigFloat::from_frac(2821202600, DENOMINATOR); // 2.8212026
result *= &x;
result += BigFloat::from_frac(-1741793900, DENOMINATOR); // -1.7417939

let ln_of_2 = BigFloat::from_frac(693147180, DENOMINATOR); // 0.69314718
result += BigFloat::from(bit_log2 as i32) * ln_of_2;

result
}
}

impl<M: ManagedTypeApi> BigFloat<M> {
Expand Down
7 changes: 7 additions & 0 deletions framework/base/src/types/managed/basic/big_float_cmp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ use crate::api::{use_raw_handle, BigFloatApiImpl, ManagedTypeApi, StaticVarApiIm

use super::{BigFloat, BigInt};

impl<M: ManagedTypeApi> BigFloat<M> {
pub fn is_close(&self, other: &Self, abs_tolerance: &Self) -> bool {
// TODO: temp handles
&(self - other).abs() <= abs_tolerance
}
}

impl<M: ManagedTypeApi> PartialEq for BigFloat<M> {
#[inline]
fn eq(&self, other: &Self) -> bool {
Expand Down
58 changes: 55 additions & 3 deletions framework/base/src/types/managed/basic/big_uint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@ use crate::{
DecodeErrorHandler, EncodeErrorHandler, NestedDecode, NestedDecodeInput, NestedEncode,
NestedEncodeOutput, TopDecode, TopDecodeInput, TopEncode, TopEncodeOutput, TryStaticCast,
},
contract_base::ErrorHelper,
formatter::{hex_util::encode_bytes_as_hex, FormatBuffer, FormatByteReceiver, SCDisplay},
proxy_imports::ManagedRef,
types::{heap::BoxedBytes, ManagedBuffer, ManagedBufferCachedBuilder, ManagedType},
types::{
heap::BoxedBytes, ConstDecimals, Decimals, ManagedBuffer, ManagedBufferCachedBuilder,
ManagedDecimal, ManagedRef, ManagedType,
},
};

use super::cast_to_i64::cast_to_i64;
Expand Down Expand Up @@ -176,7 +179,7 @@ impl<M: ManagedTypeApi> BigUint<M> {
}

#[inline]
pub fn overwrite_u64(&self, value: u64) {
pub fn overwrite_u64(&mut self, value: u64) {
Self::set_value(self.handle.clone(), value);
}

Expand Down Expand Up @@ -236,6 +239,55 @@ impl<M: ManagedTypeApi> BigUint<M> {
let api = M::managed_type_impl();
api.bi_log2(self.handle.clone())
}

/// Natural logarithm of a number.
///
/// Returns `None` for 0.
pub fn ln(&self) -> Option<ManagedDecimal<M, ConstDecimals<9>>> {
if self == &0u32 {
return None;
}

let bit_log2 = self.log2(); // aproximate, based on position of the most significant bit
let scaling_factor_9 = ConstDecimals::<9>.scaling_factor();
let divisor = BigUint::from(1u64) << bit_log2 as usize;
let normalized = self * &*scaling_factor_9 / divisor;

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

const DENOMINATOR: i64 = 1_000_000_000;

// x normalized to [1.0, 2.0]
debug_assert!(x >= DENOMINATOR);
debug_assert!(x <= 2 * DENOMINATOR);

let mut result: i64 = -56570851; // -0.056570851
result *= x;
result /= DENOMINATOR;
result += 447179550; // 0.44717955
result *= x;
result /= DENOMINATOR;
result += -1469956800; // -1.4699568
result *= x;
result /= DENOMINATOR;
result += 2821202600; // 2.8212026
result *= x;
result /= DENOMINATOR;
result += -1741793900; // -1.7417939

const LN_OF_2_SCALE_9: i64 = 693147180; // 0.69314718
result += bit_log2 as i64 * LN_OF_2_SCALE_9;

debug_assert!(result > 0);

let mut result_bi = normalized; // reuse handle
result_bi.overwrite_u64(result as u64);

Some(ManagedDecimal::const_decimals_from_raw(result_bi))
}
}

impl<M: ManagedTypeApi> Clone for BigUint<M> {
Expand Down
39 changes: 36 additions & 3 deletions framework/base/src/types/managed/wrapped/managed_decimal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ use crate::{
const_handles, use_raw_handle, BigFloatApiImpl, BigIntApiImpl, ManagedTypeApi,
StaticVarApiImpl,
},
formatter::{FormatBuffer, FormatByteReceiver, SCDisplay},
types::{BigFloat, BigUint},
};

use alloc::string::ToString;
use multiversx_sc_codec::{
DecodeError, DecodeErrorHandler, EncodeErrorHandler, NestedDecode, NestedDecodeInput,
NestedEncode, NestedEncodeOutput, TopDecode, TopDecodeInput, TopEncode, TopEncodeOutput,
Expand All @@ -17,7 +19,7 @@ use core::{
ops::{Add, Deref, Div, Mul, Sub},
};

use super::ManagedRef;
use super::{ManagedBufferCachedBuilder, ManagedRef};

fn scaling_factor<M: ManagedTypeApi>(
num_decimals: NumDecimals,
Expand Down Expand Up @@ -71,7 +73,7 @@ impl<const DECIMALS: NumDecimals> Decimals for ConstDecimals<DECIMALS> {
}
}

#[derive(Debug, Clone)]
#[derive(Clone)]
pub struct ManagedDecimal<M: ManagedTypeApi, D: Decimals> {
data: BigUint<M>,
decimals: D,
Expand All @@ -94,6 +96,10 @@ impl<M: ManagedTypeApi, D: Decimals> ManagedDecimal<M, D> {
self.decimals.num_decimals()
}

pub fn scaling_factor(&self) -> ManagedRef<'static, M, BigUint<M>> {
self.decimals.scaling_factor()
}

pub fn rescale<T: Decimals>(self, scale_to: T) -> ManagedDecimal<M, T>
where
M: ManagedTypeApi,
Expand Down Expand Up @@ -126,7 +132,7 @@ impl<M: ManagedTypeApi, D: Decimals> ManagedDecimal<M, D> {
}

pub fn from_big_float<T: Decimals>(
big_float: BigFloat<M>,
big_float: &BigFloat<M>,
num_decimals: T,
) -> ManagedDecimal<M, T> {
let scaling_factor: &BigUint<M> = &num_decimals.scaling_factor();
Expand Down Expand Up @@ -402,3 +408,30 @@ impl<M: ManagedTypeApi, const DECIMALS: NumDecimals> TypeAbi
false
}
}
impl<M: ManagedTypeApi, D: Decimals> SCDisplay for ManagedDecimal<M, D> {
fn fmt<F: FormatByteReceiver>(&self, f: &mut F) {
let sf = self.decimals.scaling_factor();
let temp = &self.data / sf.deref();
temp.fmt(f);
f.append_bytes(b".");
let temp = &self.data % sf.deref();
temp.fmt(f);
}
}

impl<M: ManagedTypeApi, D: Decimals> core::fmt::Display for ManagedDecimal<M, D> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let mut result = ManagedBufferCachedBuilder::<M>::new_from_slice(&[]);
result.append_display(self);
core::fmt::Display::fmt(&result.into_managed_buffer(), f)
}
}

impl<M: ManagedTypeApi, D: Decimals> core::fmt::Debug for ManagedDecimal<M, D> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("ManagedDecimal")
.field("handle", &self.data.handle.clone())
.field("number", &self.to_string())
.finish()
}
}
4 changes: 2 additions & 2 deletions framework/scenario/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ serde_json = "1.0"
pathdiff = "0.2.1"
itertools = "0.12.0"
colored = "2.0"
clap = { version = "4.4.7", features = ["derive"] }
tokio = { version = "1.24", features = ["full"] }
unwrap-infallible = "0.1.5"

[features]
default = ["wasm-incopatible"]
wasm-incopatible = ["multiversx-chain-vm/wasm-incopatible"]
run-go-tests = []

[dependencies.multiversx-sc]
Expand Down
22 changes: 22 additions & 0 deletions framework/scenario/tests/big_uint_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use multiversx_sc::types::BigUint;
use multiversx_sc_scenario::api::StaticApi;

fn assert_big_uint_ln(x: u32, ln_str: &str) {
let x = BigUint::<StaticApi>::from(x);
let ln_x = x.ln();
assert_eq!(ln_x.unwrap().to_string(), ln_str);
}

#[test]
fn test_big_uint_ln() {
assert_big_uint_ln(23, "3.135514649"); // vs. 3.1354942159291497 first 6 decimals are ok

assert_big_uint_ln(1, "0.60599");
assert_big_uint_ln(2, "0.693207779"); // vs. 0.6931471805599453
assert_big_uint_ln(3, "1.98595430"); // vs. 1.0986122886681096
assert_big_uint_ln(4, "1.386354959"); // vs. 1.3862943611198906
assert_big_uint_ln(5, "1.609481340"); // vs. 1.6094379124341003
assert_big_uint_ln(6, "1.791742610"); // vs. 1.791759469228055

assert_big_uint_ln(1000, "6.907784913"); // vs. 6.907755278982137
}
8 changes: 3 additions & 5 deletions framework/scenario/tests/managed_decimal_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,9 @@ pub fn test_managed_decimal() {
);

let float_1 = BigFloat::<StaticApi>::from_frac(3i64, 2i64);
let fixed_float_1 = ManagedDecimal::<StaticApi, ConstDecimals<1>>::from_big_float(
float_1.clone(),
ConstDecimals::<1>,
);
let fixed_float_2 = ManagedDecimal::<StaticApi, NumDecimals>::from_big_float(float_1, 1usize);
let fixed_float_1 =
ManagedDecimal::<StaticApi, ConstDecimals<1>>::from_big_float(&float_1, ConstDecimals::<1>);
let fixed_float_2 = ManagedDecimal::<StaticApi, NumDecimals>::from_big_float(&float_1, 1usize);

assert_eq!(
fixed_float_1,
Expand Down
3 changes: 3 additions & 0 deletions tools/plotter/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/target
.*.sw*
.vscode/*
28 changes: 28 additions & 0 deletions tools/plotter/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
[package]
name = "sc-plotter-wasm"
version = "0.1.0"
authors = ["Hao Hou <haohou302@gmail.com>"]
edition = "2018"
publish = false

[lib]
crate-type=["cdylib"]

[dependencies]
plotters = "^0.3.2"
wasm-bindgen = "0.2.78"
wee_alloc = "0.4.5"
web-sys = { version = "0.3.39", features = ["HtmlCanvasElement"] }
plotters-canvas = "^0.3.0"

[profile.release]
lto = true

[dependencies.multiversx-sc]
version = "=0.50.3"
path = "../../framework/base"

[dependencies.multiversx-sc-scenario]
version = "=0.50.3"
path = "../../framework/scenario"
default-features = false
Loading
Loading