Skip to content

Commit

Permalink
feat: Add support for some math functions
Browse files Browse the repository at this point in the history
  • Loading branch information
AndrewWestberg committed Jul 16, 2024
1 parent 5d00e2c commit 9a61ade
Show file tree
Hide file tree
Showing 9 changed files with 201,408 additions and 31 deletions.
56 changes: 31 additions & 25 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Based on https://github.com/actions-rs/meta/blob/master/recipes/quickstart.md

on:
push: {}
push: { }

name: Validate

Expand All @@ -11,8 +11,8 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [windows-latest, ubuntu-latest, macOS-latest]
rust: [stable]
os: [ windows-latest, ubuntu-latest, macOS-latest ]
rust: [ stable ]

runs-on: ${{ matrix.os }}

Expand All @@ -21,16 +21,17 @@ jobs:
uses: actions/checkout@v2

- name: Install stable toolchain
uses: actions-rs/toolchain@v1
uses: dtolnay/rust-toolchain@stable
with:
profile: minimal
toolchain: ${{ matrix.rust }}
override: true

- name: Run cargo check Windows
if: matrix.os == 'windows-latest'
run: cargo check --no-default-features --features num

- name: Run cargo check
uses: actions-rs/cargo@v1
with:
command: check
if: matrix.os != 'windows-latest'
run: cargo check

test:
name: Test Suite
Expand All @@ -40,16 +41,27 @@ jobs:
uses: actions/checkout@v2

- name: Install stable toolchain
uses: actions-rs/toolchain@v1
uses: dtolnay/rust-toolchain@stable
with:
profile: minimal
toolchain: stable
override: true

- name: Run cargo test
uses: actions-rs/cargo@v1
run: cargo test

test-windows:
name: Test Suite Windows
runs-on: windows-latest
steps:
- name: Checkout sources
uses: actions/checkout@v2

- name: Install stable toolchain
uses: dtolnay/rust-toolchain@stable
with:
command: test
toolchain: stable

- name: Run cargo test
run: cargo test --no-default-features --features num

lints:
name: Lints
Expand All @@ -59,21 +71,15 @@ jobs:
uses: actions/checkout@v2

- name: Install stable toolchain
uses: actions-rs/toolchain@v1
uses: dtolnay/rust-toolchain@stable
with:
profile: minimal
toolchain: stable
override: true
components: rustfmt, clippy

- name: Run cargo fmt
uses: actions-rs/cargo@v1
with:
command: fmt
args: --all -- --check
run: cargo fmt --all -- --check

- name: Run cargo clippy
uses: actions-rs/cargo@v1
with:
command: clippy
args: -- -D warnings
run: |
cargo clippy -- -D warnings
cargo clippy --no-default-features --features num -- -D warnings
12 changes: 11 additions & 1 deletion pallas-math/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,19 @@ license = "Apache-2.0"
readme = "README.md"
authors = ["Andrew Westberg <[email protected]>"]

[features]
default = ["gmp"]
gmp = ["dep:gmp-mpfr-sys"]
num = ["dep:num-bigint", "dep:num-integer", "dep:num-traits"]

[dependencies]
# rug = "1.24.1"
gmp-mpfr-sys = { version = "1.6.4", features = ["mpc"], default-features = false, optional = true }
once_cell = "1.19.0"
num-bigint = { version = "0.4.6", optional = true }
num-integer = { version = "0.1.46", optional = true }
num-traits = { version = "0.2.19", optional = true }
regex = "1.10.5"
thiserror = "1.0.61"

[dev-dependencies]
quickcheck = "1.0"
Expand Down
13 changes: 13 additions & 0 deletions pallas-math/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1 +1,14 @@
pub mod math;

// Ensure only one of `gmp` or `num` is enabled, not both.
#[cfg(all(feature = "gmp", feature = "num"))]
compile_error!("Features `gmp` and `num` are mutually exclusive.");

#[cfg(all(not(feature = "gmp"), not(feature = "num")))]
compile_error!("One of the features `gmp` or `num` must be enabled.");

#[cfg(feature = "gmp")]
pub mod math_gmp;

#[cfg(feature = "num")]
pub mod math_num;
126 changes: 121 additions & 5 deletions pallas-math/src/math.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,133 @@
/*!
# Cardano Math functions
*/
*/

pub fn add(a: i32, b: i32) -> i32 {
a + b
use std::fmt::{Debug, Display};
use std::ops::Neg;

use thiserror::Error;

#[derive(Debug, Error)]
pub enum Error {
#[error("error in regex")]
RegexFailure(#[from] regex::Error),

#[error("string contained a nul byte")]
NulFailure(#[from] std::ffi::NulError),
}

pub const DEFAULT_PRECISION: u64 = 34;

pub trait FixedPrecision:
Neg + Display + Clone + PartialEq + Debug + From<u64> + From<i64>
{
/// Creates a new fixed point number with the given precision
fn new(precision: u64) -> Self;

/// Creates a new fixed point number from an integer string. Precision tells us how many decimals
fn from_str(s: &str, precision: u64) -> Result<Self, Error>;

/// Returns the precision of the fixed point number
fn precision(&self) -> u64;

/// Performs the 'exp' approximation. First does the scaling of 'x' to [0,1]
/// and then calls the continued fraction approximation function.
fn exp(&self) -> Self;

/// Entry point for 'ln' approximation. First does the necessary scaling, and
/// then calls the continued fraction calculation. For any value outside the
/// domain, i.e., 'x in (-inf,0]', the function returns '-INFINITY'.
fn ln(&self) -> Self;
}

#[cfg(test)]
mod tests {
use std::fs::File;
use std::io::BufRead;
use std::path::PathBuf;

#[cfg(feature = "gmp")]
use crate::math_gmp::Decimal;
#[cfg(feature = "num")]
use crate::math_num::Decimal;

use super::*;

#[test]
fn test_add() {
assert_eq!(add(1, 2), 3);
fn test_fixed_precision() {
let fp: Decimal = Decimal::new(34);
assert_eq!(fp.precision(), 34);
assert_eq!(fp.to_string(), "0.0000000000000000000000000000000000");
}

#[test]
fn test_fixed_precision_eq() {
let fp1: Decimal = Decimal::new(34);
let fp2: Decimal = Decimal::new(34);
assert_eq!(fp1, fp2);
}

#[test]
fn test_fixed_precision_from_str() {
let fp: Decimal = Decimal::from_str("1234567890123456789012345678901234", 34).unwrap();
assert_eq!(fp.precision(), 34);
assert_eq!(fp.to_string(), "0.1234567890123456789012345678901234");

let fp: Decimal = Decimal::from_str("-1234567890123456789012345678901234", 30).unwrap();
assert_eq!(fp.precision(), 30);
assert_eq!(fp.to_string(), "-1234.567890123456789012345678901234");

let fp: Decimal = Decimal::from_str("-1234567890123456789012345678901234", 34).unwrap();
assert_eq!(fp.precision(), 34);
assert_eq!(fp.to_string(), "-0.1234567890123456789012345678901234");
}

#[test]
fn test_fixed_precision_exp() {
let fp: Decimal = Decimal::from(1u64);
assert_eq!(fp.to_string(), "1.0000000000000000000000000000000000");
let exp_fp = fp.exp();
assert_eq!(exp_fp.to_string(), "2.7182818284590452353602874043083282");
}

#[test]
fn golden_tests() {
let mut data_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
data_path.push("tests/data/golden_tests.txt");

// read each line of golden_tests.txt
let file = File::open(data_path).expect("golden_tests.txt: file not found");
let reader = std::io::BufReader::new(file);

// read each line of golden_tests_result.txt
let mut data_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
data_path.push("tests/data/golden_tests_result.txt");
let file = File::open(data_path).expect("golden_tests_result.txt: file not found");
let result_reader = std::io::BufReader::new(file);

for (test_line, result_line) in reader.lines().zip(result_reader.lines()) {
let test_line = test_line.expect("failed to read line");
// println!("test_line: {}", test_line);
let mut parts = test_line.split_whitespace();
let x = Decimal::from_str(parts.next().unwrap(), DEFAULT_PRECISION)
.expect("failed to parse x");
let a = Decimal::from_str(parts.next().unwrap(), DEFAULT_PRECISION)
.expect("failed to parse a");
let _b = Decimal::from_str(parts.next().unwrap(), DEFAULT_PRECISION)
.expect("failed to parse b");
let result_line = result_line.expect("failed to read line");
// println!("result_line: {}", result_line);
let mut result_parts = result_line.split_whitespace();
let expected_exp_x = result_parts.next().expect("expected_exp_x not found");
let expected_ln_a = result_parts.next().expect("expected_ln_a not found");

// calculate exp' x
let exp_x = x.exp();
assert_eq!(exp_x.to_string(), expected_exp_x);

// calculate ln' a, print -ln' a
let ln_a = a.ln();
assert_eq!((-ln_a).to_string(), expected_ln_a);
}
}
}
Loading

0 comments on commit 9a61ade

Please sign in to comment.