Skip to content

Commit

Permalink
fixed log code for SoroNum<U256|I256>
Browse files Browse the repository at this point in the history
  • Loading branch information
rahul-soshte committed Apr 10, 2024
1 parent 3b761a7 commit 42d3cb5
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 2 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
workspace = { members = ["example"] }
[package]
name = "soroban-math"
version = "0.2.0"
version = "0.2.1"
edition = "2021"
description = "Fixed-Point Math Library for soroban smart contracts with advanced math and high precision"
license = "Apache-2.0"
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Add this to your Cargo.toml:

```toml
[dependencies]
soroban-math = "0.2.0"
soroban-math = "0.2.1"
```

And this to your code:
Expand Down
5 changes: 5 additions & 0 deletions example/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,10 @@ impl SorobanMathExample {
let num1 = SoroNum { value: a };
num1.sqrt(&e).value().clone()
}

pub fn log_2_u256(a: U256) -> u32 {
let num = SoroNum { value: a };
num.log2().unwrap()
}
}
mod test;
2 changes: 2 additions & 0 deletions example/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,7 @@ fn test() {
let root = client.root(&U256::from_u32(&env, 16_u32));
assert_eq!(root, U256::from_u32(&env, 4_u32));

let log2_u128 = client.log_2_u256(&U256::from_u128(&env, 1024));
assert_eq!(log2_u128, 10_u32);

}
55 changes: 55 additions & 0 deletions example/test_snapshots/test/test.1.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
[],
[],
[],
[],
[]
],
"ledger": {
Expand Down Expand Up @@ -366,6 +367,60 @@
}
},
"failed_call": false
},
{
"event": {
"ext": "v0",
"contract_id": null,
"type_": "diagnostic",
"body": {
"v0": {
"topics": [
{
"symbol": "fn_call"
},
{
"bytes": "0000000000000000000000000000000000000000000000000000000000000001"
},
{
"symbol": "log_2_u256"
}
],
"data": {
"u256": {
"hi_hi": 0,
"hi_lo": 0,
"lo_hi": 0,
"lo_lo": 1024
}
}
}
}
},
"failed_call": false
},
{
"event": {
"ext": "v0",
"contract_id": "0000000000000000000000000000000000000000000000000000000000000001",
"type_": "diagnostic",
"body": {
"v0": {
"topics": [
{
"symbol": "fn_return"
},
{
"symbol": "log_2_u256"
}
],
"data": {
"u32": 10
}
}
}
},
"failed_call": false
}
]
}
74 changes: 74 additions & 0 deletions src/log.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use soroban_sdk::{I256, U256};

use crate::SoroNum;

pub trait Logarithm {
Expand Down Expand Up @@ -44,3 +46,75 @@ impl Logarithm for SoroNum<u128> {
Some(count)
}
}

impl Logarithm for SoroNum<U256> {
fn log2(&self) -> Option<u32> {
let bytes = self.value.to_be_bytes();
let mut non_zero_byte = 0u8;
let mut non_zero_index = 0usize;
for (i, byte) in bytes.iter().enumerate() {
if byte != 0 {
non_zero_byte = byte;
non_zero_index = i;
break;
}
}

// If the entire U256 is zero, return None
if non_zero_byte == 0 {
return None;
}

// Calculate log2 based on the position of the first non-zero byte
let bits_from_most_significant_byte = 255 - (non_zero_index as u32 * 8);
let leading_zeros_in_byte = non_zero_byte.leading_zeros();
Some(bits_from_most_significant_byte - leading_zeros_in_byte)
}

fn log10(&self) -> Option<u32> {
// Direct calculation or iterative division by 10 isn't feasible for U256,
// so we'll use an approximation based on significant digits and `log2`.
self.log2().map(|log2_val| {
// Since log2(10) is approximately 3.32193, we use a ratio of 10:3 for approximation.
// This is a simplification for integer arithmetic, acknowledging potential rounding errors.
log2_val / 3
})
}
}

impl Logarithm for SoroNum<I256> {
fn log2(&self) -> Option<u32> {
let bytes = self.value.to_be_bytes();
let mut non_zero_byte = 0u8;
let mut non_zero_index = 0usize;
for (i, byte) in bytes.iter().enumerate() {
if byte != 0 {
non_zero_byte = byte;
non_zero_index = i;
break;
}
}

// If the entire I256 is zero, or negative, return None
if non_zero_byte == 0 {
return None;
}

// Calculate log2 based on the position of the first non-zero byte
let bits_from_most_significant_byte = 255 - (non_zero_index as u32 * 8);
let leading_zeros_in_byte = non_zero_byte.leading_zeros();
Some(bits_from_most_significant_byte - leading_zeros_in_byte)
}

fn log10(&self) -> Option<u32> {
// Similar approach as U256, ensuring we only apply this to non-negative values.
if self.value.to_i128().map_or(true, |v| v <= 0) {
None
} else {
self.log2().map(|log2_val| {
// Using the same approximation ratio as for U256.
log2_val / 3
})
}
}
}

0 comments on commit 42d3cb5

Please sign in to comment.