diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ea497845..5d4734b5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -81,6 +81,12 @@ jobs: - name: Check no_std with rand run: cargo check --target riscv32imac-unknown-none-elf --no-default-features --features rand --lib + - name: Check no_std with nalgebra + run: cargo check --target riscv32imac-unknown-none-elf --no-default-features --features nalgebra --lib + + - name: Check no_std with nalgebra and rand + run: cargo check --target riscv32imac-unknown-none-elf --no-default-features --features nalgebra,rand --lib + features: needs: [clippy, fmt] runs-on: ubuntu-latest @@ -103,6 +109,16 @@ jobs: uses: dtolnay/rust-toolchain@stable with: targets: wasm32-unknown-unknown, wasm32-wasip1 + - name: Install Wasmtime + uses: bytecodealliance/actions/wasmtime/setup@v1 + with: + version: "46.0.1" + + - name: Build no_std runtime verification + run: cargo build --manifest-path tests/no_std/Cargo.toml --target wasm32-unknown-unknown --locked + + - name: Run no_std calculations + run: wasmtime run --invoke verify tests/no_std/target/wasm32-unknown-unknown/debug/statrs_no_std_runtime.wasm - name: Check WebAssembly without a host RNG run: cargo check --target wasm32-unknown-unknown --no-default-features --features std,nalgebra,rand --lib diff --git a/.gitignore b/.gitignore index fe9e65ec..63d4e441 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ tests/*.dat # Generated by Cargo /target/ +/tests/no_std/target/ *.lock #editor specific diff --git a/Cargo.lock.MSRV b/Cargo.lock.MSRV index cba8a7b7..14aab2d5 100644 --- a/Cargo.lock.MSRV +++ b/Cargo.lock.MSRV @@ -277,24 +277,36 @@ name = "glam" version = "0.30.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19fc433e8437a212d1b6f1e68c7824af3aed907da60afa994e7f542d18d12aa9" +dependencies = [ + "libm", +] [[package]] name = "glam" version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "556f6b2ea90b8d15a74e0e7bb41671c9bdf38cd9f78c284d750b9ce58a2b5be7" +dependencies = [ + "libm", +] [[package]] name = "glam" version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f70749695b063ecbf6b62949ccccde2e733ec3ecbbd71d467dca4e5c6c97cca0" +dependencies = [ + "libm", +] [[package]] name = "glam" version = "0.33.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f22fb22f065b308be0d8724e3706c7fa3fc2a6c7d6899df4cad7860e7a75436" +dependencies = [ + "libm", +] [[package]] name = "half" diff --git a/Cargo.toml b/Cargo.toml index be611f5d..fb77d16b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,11 +32,10 @@ required-features = ["rand", "std", "kde"] [features] default = ["std", "nalgebra", "rand"] std = ["approx/std", "num-traits/std", "nalgebra?/std", "rand?/std"] -# at the moment, all nalgebra features needs std -nalgebra = ["dep:nalgebra", "std"] +nalgebra = ["dep:nalgebra", "nalgebra/alloc", "nalgebra/libm"] rand = ["dep:rand", "nalgebra?/rand-no-std", "rand?/std_rng"] # kd-tree backed density estimation (src/density), implemented in terms of nalgebra vectors -kde = ["dep:kdtree", "nalgebra"] +kde = ["dep:kdtree", "nalgebra", "std"] [dependencies] approx = { version = "0.5.0", default-features = false } diff --git a/README.md b/README.md index 33c15649..540f2642 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,36 @@ statrs = "*" # replace * by the latest version of the crate. For examples, view [the docs](https://docs.rs/statrs/*/statrs/). +### `no_std` + +Disable the default features to use `statrs` without `std`: + +```toml +[dependencies] +statrs = { version = "*", default-features = false } +``` + +No separate Cargo feature is required for allocation-backed APIs. To enable +matrix-backed distributions without `std`, enable `nalgebra` directly: + +```toml +[dependencies] +statrs = { version = "*", default-features = false, features = ["nalgebra"] } +``` + +Heap-backed APIs use Rust's `alloc` crate. A `no_std` application that calls +these APIs must provide and initialize a global allocator suitable for its +target: + +```rust,ignore +#[global_allocator] +static ALLOCATOR: PlatformAllocator = PlatformAllocator::new(/* heap memory */); +``` + +The allocator type, heap memory, and initialization are platform-specific. +`statrs` does not provide or select an allocator. +When `std` is enabled, its global allocator is used and no setup is required. + ### WebAssembly randomness On `wasm32-unknown-unknown`, sampling works with an explicitly seeded random diff --git a/src/density/mod.rs b/src/density/mod.rs index b5c7896f..cfb51cbc 100644 --- a/src/density/mod.rs +++ b/src/density/mod.rs @@ -14,6 +14,7 @@ pub mod kde; pub mod knn; +use alloc::vec::Vec; use kdtree::{ErrorKind, KdTree, distance::squared_euclidean}; use thiserror::Error; diff --git a/src/distribution/categorical.rs b/src/distribution/categorical.rs index 03801afa..6cf6da8f 100644 --- a/src/distribution/categorical.rs +++ b/src/distribution/categorical.rs @@ -1,6 +1,9 @@ use crate::distribution::{Discrete, DiscreteCDF}; use crate::statistics::*; +use alloc::vec::Vec; use core::f64; +#[cfg(not(feature = "std"))] +use num_traits::Float as _; /// Implements the /// [Categorical](https://en.wikipedia.org/wiki/Categorical_distribution) diff --git a/src/distribution/dirichlet.rs b/src/distribution/dirichlet.rs index 5f1697b4..dde19a0b 100644 --- a/src/distribution/dirichlet.rs +++ b/src/distribution/dirichlet.rs @@ -2,8 +2,11 @@ use crate::distribution::Continuous; use crate::function::gamma; use crate::prec; use crate::statistics::*; +use alloc::{vec, vec::Vec}; use core::f64; use nalgebra::{Dim, Dyn, OMatrix, OVector}; +#[cfg(not(feature = "std"))] +use num_traits::Float as _; /// Implements the /// [Dirichlet](https://en.wikipedia.org/wiki/Dirichlet_distribution) diff --git a/src/distribution/empirical.rs b/src/distribution/empirical.rs index 99f1bd99..6075a454 100644 --- a/src/distribution/empirical.rs +++ b/src/distribution/empirical.rs @@ -1,9 +1,9 @@ use crate::distribution::ContinuousCDF; use crate::statistics::*; +use alloc::collections::btree_map::{BTreeMap, Entry}; use core::convert::Infallible; use core::ops::Bound; use non_nan::NonNan; -use std::collections::btree_map::{BTreeMap, Entry}; mod non_nan { use core::cmp::Ordering; diff --git a/src/distribution/mod.rs b/src/distribution/mod.rs index be948218..dc56bc7b 100644 --- a/src/distribution/mod.rs +++ b/src/distribution/mod.rs @@ -8,7 +8,6 @@ use num_traits::NumAssignOps; pub use self::bernoulli::Bernoulli; pub use self::beta::{Beta, BetaError}; pub use self::binomial::{Binomial, BinomialError}; -#[cfg(feature = "std")] pub use self::categorical::{Categorical, CategoricalError}; pub use self::cauchy::{Cauchy, CauchyError}; pub use self::chi::{Chi, ChiError}; @@ -17,7 +16,6 @@ pub use self::dirac::{Dirac, DiracError}; #[cfg(feature = "nalgebra")] pub use self::dirichlet::{Dirichlet, DirichletError}; pub use self::discrete_uniform::{DiscreteUniform, DiscreteUniformError}; -#[cfg(feature = "std")] pub use self::empirical::Empirical; pub use self::erlang::Erlang; pub use self::exponential::{Exp, ExpError}; @@ -48,7 +46,6 @@ pub use self::weibull::{Weibull, WeibullError}; mod bernoulli; mod beta; mod binomial; -#[cfg(feature = "std")] mod categorical; mod cauchy; mod chi; @@ -58,7 +55,6 @@ mod dirac; #[cfg_attr(docsrs, doc(cfg(feature = "nalgebra")))] mod dirichlet; mod discrete_uniform; -#[cfg(feature = "std")] mod empirical; mod erlang; mod exponential; diff --git a/src/distribution/multinomial.rs b/src/distribution/multinomial.rs index 3504ee21..5eaab233 100644 --- a/src/distribution/multinomial.rs +++ b/src/distribution/multinomial.rs @@ -1,7 +1,10 @@ use crate::distribution::Discrete; use crate::function::factorial; use crate::statistics::*; +use alloc::vec::Vec; use nalgebra::{Dim, Dyn, OMatrix, OVector}; +#[cfg(not(feature = "std"))] +use num_traits::Float as _; /// Implements the /// [Multinomial](https://en.wikipedia.org/wiki/Multinomial_distribution) diff --git a/src/distribution/multivariate_normal.rs b/src/distribution/multivariate_normal.rs index e35a0fbd..cba8c892 100644 --- a/src/distribution/multivariate_normal.rs +++ b/src/distribution/multivariate_normal.rs @@ -1,8 +1,11 @@ use crate::distribution::Continuous; use crate::statistics::{Max, MeanN, Min, Mode, VarianceN}; +use alloc::vec::Vec; use core::f64; use core::f64::consts::{E, PI}; use nalgebra::{Cholesky, Const, DMatrix, DVector, Dim, DimMin, Dyn, OMatrix, OVector}; +#[cfg(not(feature = "std"))] +use num_traits::Float as _; /// Computes both the normalization and exponential argument in the normal /// distribution, returning `None` on dimension mismatch. diff --git a/src/distribution/multivariate_students_t.rs b/src/distribution/multivariate_students_t.rs index 2ad69880..2423b8c2 100644 --- a/src/distribution/multivariate_students_t.rs +++ b/src/distribution/multivariate_students_t.rs @@ -1,8 +1,11 @@ use crate::distribution::Continuous; use crate::function::gamma; use crate::statistics::{Max, MeanN, Min, Mode, VarianceN}; +use alloc::vec::Vec; use core::f64::consts::PI; use nalgebra::{Cholesky, Const, DMatrix, Dim, DimMin, Dyn, OMatrix, OVector}; +#[cfg(not(feature = "std"))] +use num_traits::Float as _; /// Implements the [Multivariate Student's t-distribution](https://en.wikipedia.org/wiki/Multivariate_t-distribution) /// distribution using the "nalgebra" crate for matrix operations. diff --git a/src/generate.rs b/src/generate.rs index 4249df61..d417f54d 100644 --- a/src/generate.rs +++ b/src/generate.rs @@ -1,6 +1,7 @@ //! Provides utility functions for generating data sequences use crate::euclid::Modulus; +use alloc::{vec, vec::Vec}; use core::f64::consts; #[cfg(not(feature = "std"))] use num_traits::Float as _; @@ -15,7 +16,6 @@ use num_traits::Float as _; /// let x = generate::log_spaced(5, 0.0, 4.0); /// assert_eq!(x, [1.0, 10.0, 100.0, 1000.0, 10000.0]); /// ``` -#[cfg(feature = "std")] pub fn log_spaced(length: usize, start_exp: f64, stop_exp: f64) -> Vec { match length { 0 => Vec::new(), diff --git a/src/lib.rs b/src/lib.rs index 8307ed18..1c9bfeaa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -54,6 +54,8 @@ #![cfg_attr(docsrs, feature(doc_cfg))] #![cfg_attr(not(feature = "std"), no_std)] +extern crate alloc; + pub mod consts; #[cfg(feature = "kde")] #[cfg_attr(docsrs, doc(cfg(feature = "kde")))] diff --git a/src/statistics/order_statistics.rs b/src/statistics/order_statistics.rs index 76855435..0991202c 100644 --- a/src/statistics/order_statistics.rs +++ b/src/statistics/order_statistics.rs @@ -1,5 +1,5 @@ -#[cfg(feature = "std")] use super::RankTieBreaker; +use alloc::vec::Vec; /// The `OrderStatistics` trait provides statistical utilities /// having to do with ordering. All the algorithms are in-place thus requiring @@ -213,6 +213,5 @@ pub trait OrderStatistics { /// assert_eq!(y.clone().ranks(RankTieBreaker::Min), [1.0, 4.0, 2.0, /// 2.0]); /// ``` - #[cfg(feature = "std")] fn ranks(&mut self, tie_breaker: RankTieBreaker) -> Vec; } diff --git a/src/statistics/slice_statistics.rs b/src/statistics/slice_statistics.rs index 96b83e7b..586d146f 100644 --- a/src/statistics/slice_statistics.rs +++ b/src/statistics/slice_statistics.rs @@ -1,4 +1,5 @@ use crate::statistics::*; +use alloc::{vec, vec::Vec}; use core::ops::{Index, IndexMut}; #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] @@ -197,7 +198,6 @@ impl + AsRef<[f64]>> OrderStatistics for Data { self.upper_quartile() - self.lower_quartile() } - #[cfg(feature = "std")] fn ranks(&mut self, tie_breaker: RankTieBreaker) -> Vec { let n = self.len(); let mut ranks: Vec = vec![0.0; n]; @@ -392,7 +392,6 @@ impl + AsRef<[f64]> + Clone> Median for Data { } } -#[cfg(feature = "std")] fn handle_rank_ties( ranks: &mut [f64], index: &[(usize, &f64)], diff --git a/src/stats_tests/anderson_darling.rs b/src/stats_tests/anderson_darling.rs index 1a48b22a..a4856a52 100644 --- a/src/stats_tests/anderson_darling.rs +++ b/src/stats_tests/anderson_darling.rs @@ -1,4 +1,7 @@ use crate::distribution::ContinuousCDF; +use alloc::vec::Vec; +#[cfg(not(feature = "std"))] +use num_traits::Float as _; #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] #[non_exhaustive] @@ -27,7 +30,7 @@ pub fn anderson_darling>( if n == 0 { return Err(AndersonDarlingError::SampleSizeInvalid); } - let mut f_obs = f_obs.to_vec(); + let mut f_obs = Vec::from(f_obs); f_obs.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap()); let n_float = n as f64; diff --git a/src/stats_tests/chisquare.rs b/src/stats_tests/chisquare.rs index 12b8995e..1f0fe5ed 100644 --- a/src/stats_tests/chisquare.rs +++ b/src/stats_tests/chisquare.rs @@ -2,6 +2,8 @@ use crate::distribution::{ChiSquared, ContinuousCDF}; use crate::prec; +#[cfg(not(feature = "std"))] +use num_traits::Float as _; /// Represents the errors that can occur when computing the chisquare function #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] diff --git a/src/stats_tests/f_oneway.rs b/src/stats_tests/f_oneway.rs index 65ee61fa..b560e82c 100644 --- a/src/stats_tests/f_oneway.rs +++ b/src/stats_tests/f_oneway.rs @@ -3,6 +3,9 @@ use crate::distribution::{ContinuousCDF, FisherSnedecor}; use crate::stats_tests::NaNPolicy; +use alloc::vec::Vec; +#[cfg(not(feature = "std"))] +use num_traits::Float as _; /// Represents the errors that occur when computing the f_oneway function #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] diff --git a/src/stats_tests/ks_test.rs b/src/stats_tests/ks_test.rs index c0963da0..9978e625 100644 --- a/src/stats_tests/ks_test.rs +++ b/src/stats_tests/ks_test.rs @@ -1,9 +1,12 @@ //! Provides the [Kolmogorov-Smirnov (KS) test](https://en.wikipedia.org/wiki/Kolmogorov–Smirnov_test) and related //! functions +use alloc::{vec, vec::Vec}; use core::f64; use core::iter::zip; +#[cfg(not(feature = "std"))] +use num_traits::Float as _; use num_traits::clamp; use crate::distribution::ContinuousCDF; diff --git a/src/stats_tests/mannwhitneyu.rs b/src/stats_tests/mannwhitneyu.rs index fd778082..85f4c21d 100644 --- a/src/stats_tests/mannwhitneyu.rs +++ b/src/stats_tests/mannwhitneyu.rs @@ -1,6 +1,9 @@ //! Provides the [Mann-Whitney U test](https://en.wikipedia.org/wiki/Mann–Whitney_U_test#) and related //! functions +use alloc::{vec, vec::Vec}; +#[cfg(not(feature = "std"))] +use num_traits::Float as _; use num_traits::clamp; use crate::distribution::{ContinuousCDF, Normal}; diff --git a/src/stats_tests/mod.rs b/src/stats_tests/mod.rs index 342c5e18..2163c67d 100644 --- a/src/stats_tests/mod.rs +++ b/src/stats_tests/mod.rs @@ -1,17 +1,10 @@ -#[cfg(feature = "std")] pub mod anderson_darling; -#[cfg(feature = "std")] pub mod chisquare; -#[cfg(feature = "std")] pub mod f_oneway; pub mod fisher; -#[cfg(feature = "std")] pub mod ks_test; -#[cfg(feature = "std")] pub mod mannwhitneyu; -#[cfg(feature = "std")] pub mod skewtest; -#[cfg(feature = "std")] pub mod ttest_onesample; /// Specifies an [alternative hypothesis](https://en.wikipedia.org/wiki/Alternative_hypothesis) diff --git a/src/stats_tests/skewtest.rs b/src/stats_tests/skewtest.rs index ec5f0fe0..31221f88 100644 --- a/src/stats_tests/skewtest.rs +++ b/src/stats_tests/skewtest.rs @@ -3,6 +3,9 @@ use crate::distribution::{ContinuousCDF, Normal}; use crate::stats_tests::{Alternative, NaNPolicy}; +use alloc::vec::Vec; +#[cfg(not(feature = "std"))] +use num_traits::Float as _; /// Represents the errors that can occur when computing the skewtest function #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] diff --git a/src/stats_tests/ttest_onesample.rs b/src/stats_tests/ttest_onesample.rs index 050cf9f6..9b571aad 100644 --- a/src/stats_tests/ttest_onesample.rs +++ b/src/stats_tests/ttest_onesample.rs @@ -3,6 +3,9 @@ use crate::distribution::{ContinuousCDF, StudentsT}; use crate::stats_tests::{Alternative, NaNPolicy}; +use alloc::vec::Vec; +#[cfg(not(feature = "std"))] +use num_traits::Float as _; /// Represents the errors that can occur when computing the ttest_onesample function #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] diff --git a/tests/no_std/Cargo.lock b/tests/no_std/Cargo.lock new file mode 100644 index 00000000..b6ecbf65 --- /dev/null +++ b/tests/no_std/Cargo.lock @@ -0,0 +1,244 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits", +] + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "dlmalloc" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad5208a115eaba24916f7456929832e310a81518c641f93fee4f89aa93aa3675" +dependencies = [ + "cfg-if", + "libc", + "windows-sys", +] + +[[package]] +name = "glam" +version = "0.30.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19fc433e8437a212d1b6f1e68c7824af3aed907da60afa994e7f542d18d12aa9" +dependencies = [ + "libm", +] + +[[package]] +name = "glam" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556f6b2ea90b8d15a74e0e7bb41671c9bdf38cd9f78c284d750b9ce58a2b5be7" +dependencies = [ + "libm", +] + +[[package]] +name = "glam" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f70749695b063ecbf6b62949ccccde2e733ec3ecbbd71d467dca4e5c6c97cca0" +dependencies = [ + "libm", +] + +[[package]] +name = "glam" +version = "0.33.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f22fb22f065b308be0d8724e3706c7fa3fc2a6c7d6899df4cad7860e7a75436" +dependencies = [ + "libm", +] + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + +[[package]] +name = "nalgebra" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adc43a60c217b0c6ff46e47f26911015ad8d2e5a8be1af668c67e370d99a4346" +dependencies = [ + "approx", + "glam 0.30.10", + "glam 0.31.1", + "glam 0.32.1", + "glam 0.33.2", + "num-complex", + "num-rational", + "num-traits", + "simba", + "typenum", +] + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "proc-macro2" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "simba" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f45c644a9f3a386f9288625d9f0c1e999e1acf07a37df35d0516c7f199d9cb2" +dependencies = [ + "approx", + "num-complex", + "num-traits", +] + +[[package]] +name = "statrs" +version = "0.19.0" +dependencies = [ + "approx", + "nalgebra", + "num-traits", + "thiserror", +] + +[[package]] +name = "statrs-no-std-runtime" +version = "0.0.0" +dependencies = [ + "dlmalloc", + "statrs", +] + +[[package]] +name = "syn" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5edbec4ed188954a10c12c038215f8ce7606b2d5c973cd8dc43e8795065c5f2f" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "2.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09a43598840e33d5b0331f38c5e30d13bb11c11210a4b58f0d9b18a5a5eefcd9" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43cbfe0cf76104d42a574802844187e84a305e531ed54455f11fbde0f10541cd" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] diff --git a/tests/no_std/Cargo.toml b/tests/no_std/Cargo.toml new file mode 100644 index 00000000..a8ea7b67 --- /dev/null +++ b/tests/no_std/Cargo.toml @@ -0,0 +1,21 @@ +[workspace] + +[package] +name = "statrs-no-std-runtime" +version = "0.0.0" +edition = "2024" +publish = false +rust-version = "1.89.0" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +dlmalloc = { version = "0.2.14", features = ["global"] } +statrs = { path = "../..", default-features = false, features = ["nalgebra"] } + +[profile.dev] +panic = "abort" + +[profile.release] +panic = "abort" diff --git a/tests/no_std/src/lib.rs b/tests/no_std/src/lib.rs new file mode 100644 index 00000000..2b83b8bb --- /dev/null +++ b/tests/no_std/src/lib.rs @@ -0,0 +1,60 @@ +#![no_std] + +extern crate alloc; + +use alloc::vec; +use statrs::distribution::{Categorical, Continuous, Empirical, Multinomial, MultivariateNormal}; +use statrs::generate::log_spaced; +use statrs::statistics::{Data, Distribution, MeanN, OrderStatistics, RankTieBreaker}; +use statrs::stats_tests::{NaNPolicy, f_oneway::f_oneway}; + +#[global_allocator] +static ALLOCATOR: dlmalloc::GlobalDlmalloc = dlmalloc::GlobalDlmalloc; + +#[panic_handler] +fn panic(_: &core::panic::PanicInfo<'_>) -> ! { + core::arch::wasm32::unreachable() +} + +fn assert_close(actual: f64, expected: f64, tolerance: f64) { + assert!((actual - expected).abs() <= tolerance); +} + +#[unsafe(no_mangle)] +pub extern "C" fn verify() { + let categorical = Categorical::new(&[1.0, 2.0, 3.0]).unwrap(); + assert_close(categorical.mean().unwrap(), 4.0 / 3.0, 1e-12); + + let empirical = vec![1.0, 2.0, 3.0].into_iter().collect::(); + assert_close(empirical.mean().unwrap(), 2.0, 1e-12); + + assert_eq!(log_spaced(3, 0.0, 2.0), [1.0, 10.0, 100.0]); + + let mut data = Data::new([3.0, 1.0, 2.0, 2.0]); + assert_eq!(data.ranks(RankTieBreaker::Average), [4.0, 1.0, 2.5, 2.5]); + + let multinomial = Multinomial::new(vec![1.0, 3.0], 4).unwrap(); + let multinomial_mean = multinomial.mean().unwrap(); + assert_close(multinomial_mean[0], 1.0, 1e-12); + assert_close(multinomial_mean[1], 3.0, 1e-12); + + let normal = MultivariateNormal::new(vec![0.0, 0.0], vec![1.0, 0.0, 0.0, 1.0]).unwrap(); + let point = vec![0.0, 0.0].into(); + assert_close( + normal.pdf(&point), + 1.0 / (2.0 * core::f64::consts::PI), + 1e-12, + ); + + let (statistic, p_value) = f_oneway( + vec![ + vec![6.0, 8.0, 4.0, 5.0, 3.0, 4.0], + vec![8.0, 12.0, 9.0, 11.0, 6.0, 8.0], + vec![13.0, 9.0, 11.0, 8.0, 7.0, 12.0], + ], + NaNPolicy::Error, + ) + .unwrap(); + assert_close(statistic, 9.264705882352942, 1e-12); + assert_close(p_value, 0.0023987773293929317, 1e-12); +}