Skip to content

Commit eb805ad

Browse files
authored
Merge pull request #20 from cmccomb/master
Preparing v0.2.2
2 parents d344ab3 + c34ca7d commit eb805ad

10 files changed

+812
-116
lines changed

Cargo.toml

+18-18
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "rhai-sci"
3-
version = "0.2.1"
3+
version = "0.2.2"
44
edition = "2021"
55
authors = ["Chris McComb <[email protected]>"]
66
description = "Scientific computing in the Rhai scripting language"
@@ -22,29 +22,29 @@ rand = ["randlib"]
2222

2323
[dependencies]
2424
rhai = ">=1.8.0"
25-
nalgebralib = { version = "0.32.1", optional = true, package = "nalgebra" }
26-
polars = { version = "0.27.2", optional = true }
27-
url = { version = "2.2.2", optional = true }
28-
temp-file = { version = "0.1.6", optional = true }
25+
nalgebralib = { version = "0.33.2", optional = true, package = "nalgebra" }
26+
polars = { version = "0.45.1", optional = true }
27+
url = { version = ">=2.0.0", optional = true }
28+
temp-file = { version = "0.1.9", optional = true }
2929
csv-sniffer = { version = "0.3.1", optional = true }
30-
minreq = { version = "2.6.0", features = ["json-using-serde", "https"], optional = true }
31-
randlib = { version = "0.8", optional = true, package = "rand" }
32-
smartstring = "1.0.1"
30+
minreq = { version = "2.13.0", features = ["json-using-serde", "https"], optional = true }
31+
randlib = { version = "0.8.5", optional = true, package = "rand" }
32+
smartstring = ">=1.0"
3333
linregress = { version = "0.5.0", optional = true }
3434

3535
[build-dependencies]
3636
rhai = ">=1.8.0"
37-
nalgebralib = { version = "0.32.1", optional = true, package = "nalgebra" }
38-
polars = { version = "0.27.2", optional = true }
39-
url = { version = "2.2.2", optional = true }
40-
temp-file = { version = "0.1.6", optional = true }
37+
nalgebralib = { version = "0.33.2", optional = true, package = "nalgebra" }
38+
polars = { version = "0.45.1", optional = true }
39+
url = { version = ">=2.0.0", optional = true }
40+
temp-file = { version = "0.1.9", optional = true }
4141
csv-sniffer = { version = "0.3.1", optional = true }
42-
minreq = { version = "2.6.0", features = ["json-using-serde", "https"], optional = true }
43-
randlib = { version = "0.8", optional = true, package = "rand" }
44-
serde_json = "1.0.82"
45-
serde = "1.0.140"
46-
smartstring = "1.0.1"
47-
linregress = { version = "0.5.0", optional = true }
42+
minreq = { version = "2.13.0", features = ["json-using-serde", "https"], optional = true }
43+
randlib = { version = "0.8.5", optional = true, package = "rand" }
44+
serde_json = ">=1.0.0"
45+
serde = ">=1.0.0"
46+
smartstring = ">=1.0.0"
47+
linregress = { version = "0.5.4", optional = true }
4848

4949
[package.metadata.docs.rs]
5050
all-features = true

README.md

+12-16
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,22 @@
44

55
# About `rhai-sci`
66

7-
This crate provides some basic scientific computing utilities for the [`Rhai`](https://rhai.rs/) scripting language, inspired by languages
8-
like MATLAB, Octave, and R. For a complete API reference, check [the docs](https://docs.rs/rhai-sci).
7+
This crate provides some basic scientific computing utilities for the [`Rhai`](https://rhai.rs/) scripting language,
8+
inspired by languages like MATLAB, Octave, and R. For a complete API reference,
9+
check [the docs](https://docs.rs/rhai-sci).
910

1011
# Install
1112

1213
To use the latest released version of `rhai-sci`, add this to your `Cargo.toml`:
1314

1415
```toml
15-
rhai-sci = "0.2.1"
16-
```
17-
18-
To use the bleeding edge instead, add this:
19-
20-
```toml
21-
rhai-sci = { git = "https://github.com/cmccomb/rhai-sci" }
16+
rhai-sci = "0.2.2"
2217
```
2318

2419
# Usage
2520

26-
Using this crate is pretty simple! If you just want to evaluate a single line of [`Rhai`](https://rhai.rs/), then you only need:
21+
Using this crate is pretty simple! If you just want to evaluate a single line of [`Rhai`](https://rhai.rs/), then you
22+
only need:
2723

2824
```rust
2925
use rhai::INT;
@@ -49,9 +45,9 @@ let value = engine.eval::<INT>("argmin([43, 42, -500])").unwrap();
4945

5046
# Features
5147

52-
| Feature | Default | Description |
53-
| ----------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
54-
| `metadata` | Disabled | Enables exporting function metadata and is ___necessary for running doc-tests on Rhai examples___. |
55-
| `io` | Enabled | Enables the [`read_matrix`](#read_matrixfile_path-string---array) function but pulls in several additional dependencies (`polars`, `url`, `temp-file`, `csv-sniffer`, `minreq`). |
56-
| `nalgebra` | Enabled | Enables several functions ([`regress`](#regressx-array-y-array---map), [`inv`](#invmatrix-array---array), [`mtimes`](#mtimesmatrix1-array-matrix2-array---array), [`horzcat`](#horzcatmatrix1-array-matrix2-array---array), [`vertcat`](#vertcatmatrix1-array-matrix2-array---array), [`repmat`](#repmatmatrix-array-nx-i64-ny-i64---array), [`svd`](#svdmatrix-array---map), [`hessenberg`](#hessenbergmatrix-array---map), and [`qr`](#qrmatrix-array---map)) but brings in the `nalgebra` and `linregress` crates. |
57-
| `rand` | Enabled | Enables the [`rand`](#rand) function for generating random FLOAT values and random matrices, but brings in the `rand` crate. |
48+
| Feature | Default | Description |
49+
|------------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
50+
| `metadata` | Disabled | Enables exporting function metadata and is ___necessary for running doc-tests on Rhai examples___. |
51+
| `io` | Enabled | Enables the [`read_matrix`](#read_matrixfile_path-string---array) function but pulls in several additional dependencies (`polars`, `url`, `temp-file`, `csv-sniffer`, `minreq`). |
52+
| `nalgebra` | Enabled | Enables several functions ([`regress`](#regressx-array-y-array---map), [`inv`](#invmatrix-array---array), [`mtimes`](#mtimesmatrix1-array-matrix2-array---array), [`horzcat`](#horzcatmatrix1-array-matrix2-array---array), [`vertcat`](#vertcatmatrix1-array-matrix2-array---array), [`repmat`](#repmatmatrix-array-nx-i64-ny-i64---array), [`svd`](#svdmatrix-array---map), [`hessenberg`](#hessenbergmatrix-array---map), and [`qr`](#qrmatrix-array---map)) but brings in the `nalgebra` and `linregress` crates. |
53+
| `rand` | Enabled | Enables the [`rand`](#rand) function for generating random FLOAT values and random matrices, but brings in the `rand` crate. |

build.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ fn main() {
6363
combine_with_exported_module!(&mut lib, "rhai_sci_sets", set_functions);
6464
combine_with_exported_module!(&mut lib, "rhai_sci_moving", moving_functions);
6565
combine_with_exported_module!(&mut lib, "rhai_sci_validate", validation_functions);
66+
combine_with_exported_module!(&mut lib, "rhai_sci_trig", trig_functions);
6667
engine.register_global_module(rhai::Shared::new(lib));
6768

6869
// Extract metadata
@@ -84,7 +85,7 @@ fn main() {
8485
let function = function.clone();
8586
// Pull out basic info
8687
let name = function.name;
87-
if !name.starts_with("anon") && !name.starts_with("_") {
88+
if !name.starts_with("anon") && !name.starts_with("_") && !name.starts_with("$CONSTANTS$"){
8889
let signature = function
8990
.signature
9091
.replace("Result<", "")
@@ -219,6 +220,7 @@ mod functions {
219220
include!("src/moving.rs");
220221
include!("src/validate.rs");
221222
include!("src/patterns.rs");
223+
include!("src/trig.rs");
222224
}
223225

224226
#[cfg(feature = "metadata")]

src/assertions.rs

+79-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ use rhai::plugin::*;
22

33
#[export_module]
44
pub mod assert_functions {
5-
use rhai::{Dynamic, EvalAltResult, Position};
5+
use rhai::{Array, Dynamic, EvalAltResult, Position, FLOAT};
6+
7+
use crate::if_list_convert_to_vec_float_and_do;
68

79
/// Assert that a statement is true and throw an error if it is not.
810
/// ```typescript
@@ -88,4 +90,80 @@ pub mod assert_functions {
8890
.into())
8991
}
9092
}
93+
94+
/// Assert that two floats are approximately equal (within `eps`) and return an error if they
95+
/// are not.
96+
/// ```typescript
97+
/// assert_approx_eq(2.0, 2.000000000000000001, 1e-10);
98+
/// ```
99+
#[rhai_fn(name = "assert_approx_eq", return_raw)]
100+
pub fn assert_approx_eq(
101+
lhs: FLOAT,
102+
rhs: FLOAT,
103+
eps: FLOAT,
104+
) -> Result<bool, Box<EvalAltResult>> {
105+
if (lhs - rhs).abs() < eps {
106+
Ok(true)
107+
} else {
108+
println!("LHS: {:?}", lhs);
109+
println!("RHS: {:?}", rhs);
110+
Err(EvalAltResult::ErrorArithmetic(
111+
"The left-hand side and right-hand side are not equal".to_string(),
112+
Position::NONE,
113+
)
114+
.into())
115+
}
116+
}
117+
118+
/// Assert that two floats are approximately equal and return an error if they
119+
/// are not. Use the default tolerance of 1e-10 for the comparison.
120+
/// ```typescript
121+
/// assert_approx_eq(2.0, 2.000000000000000001);
122+
/// ```
123+
#[rhai_fn(name = "assert_approx_eq", return_raw)]
124+
pub fn assert_approx_eq_with_default(
125+
lhs: FLOAT,
126+
rhs: FLOAT,
127+
) -> Result<bool, Box<EvalAltResult>> {
128+
assert_approx_eq(lhs, rhs, 1e-10)
129+
}
130+
131+
/// Assert that two arrays are approximately equal (within `eps`) and return an error if they
132+
/// are not.
133+
/// ```typescript
134+
/// assert_approx_eq([2.0, 2.0], [2.0, 2.000000000000000001], 1e-10);
135+
/// ```
136+
#[rhai_fn(name = "assert_approx_eq", return_raw)]
137+
pub fn assert_approx_eq_list(
138+
lhs: Array,
139+
rhs: Array,
140+
eps: FLOAT,
141+
) -> Result<bool, Box<EvalAltResult>> {
142+
if_list_convert_to_vec_float_and_do(&mut rhs.clone(), |rhs_as_vec_float| {
143+
if_list_convert_to_vec_float_and_do(&mut lhs.clone(), |lhs_as_vec_float| {
144+
let mut result = Ok(true);
145+
for i in 0..rhs_as_vec_float.len() {
146+
result = result.and(assert_approx_eq(
147+
lhs_as_vec_float[i],
148+
rhs_as_vec_float[i],
149+
eps,
150+
))
151+
}
152+
result
153+
})
154+
})
155+
}
156+
157+
/// Assert that two arrays are approximately equal and return an error if they
158+
/// are not. Use the default tolerance of 1e-10 for the comparison.
159+
/// ```typescript
160+
/// assert_approx_eq([2.0, 2.0], [2.0, 2.000000000000000001]);
161+
/// ```
162+
#[rhai_fn(name = "assert_approx_eq", return_raw)]
163+
pub fn assert_approx_eq_list_with_default(
164+
lhs: Array,
165+
rhs: Array,
166+
) -> Result<bool, Box<EvalAltResult>> {
167+
assert_approx_eq_list(lhs, rhs, 1e-10)
168+
}
91169
}

src/lib.rs

+14-11
Original file line numberDiff line numberDiff line change
@@ -6,28 +6,30 @@
66
#![doc = include_str!("../docs/highlight.html")]
77

88
mod patterns;
9-
use patterns::*;
9+
pub use patterns::*;
1010
use rhai::{def_package, packages::Package, plugin::*, Engine, EvalAltResult};
1111
mod matrices_and_arrays;
12-
use matrices_and_arrays::matrix_functions;
12+
pub use matrices_and_arrays::matrix_functions;
1313
mod statistics;
14-
use statistics::stats;
14+
pub use statistics::stats;
1515
mod misc;
16-
use misc::misc_functions;
16+
pub use misc::misc_functions;
1717
mod cumulative;
18-
use cumulative::cum_functions;
18+
pub use cumulative::cum_functions;
1919
mod integration_and_differentiation;
20-
use integration_and_differentiation::int_and_diff;
20+
pub use integration_and_differentiation::int_and_diff;
2121
mod assertions;
22-
use assertions::assert_functions;
22+
pub use assertions::assert_functions;
2323
mod constants;
24-
use constants::constant_definitions;
24+
pub use constants::constant_definitions;
2525
mod moving;
26-
use moving::moving_functions;
26+
pub use moving::moving_functions;
2727
mod sets;
28-
use sets::set_functions;
28+
pub use sets::set_functions;
2929
mod validate;
30-
use validate::validation_functions;
30+
pub use validate::validation_functions;
31+
mod trig;
32+
pub use trig::trig_functions;
3133

3234
def_package! {
3335
/// Package for scientific computing
@@ -43,6 +45,7 @@ def_package! {
4345
combine_with_exported_module!(lib, "rhai_sci_sets", set_functions);
4446
combine_with_exported_module!(lib, "rhai_sci_moving", moving_functions);
4547
combine_with_exported_module!(lib, "rhai_sci_validation", validation_functions);
48+
combine_with_exported_module!(lib, "rhai_sci_trig", trig_functions);
4649
}
4750
}
4851

src/matrices_and_arrays.rs

+39-12
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use rhai::plugin::*;
44
#[export_module]
55
pub mod matrix_functions {
66
use crate::{
7-
if_int_convert_to_float_and_do, if_int_do_else_if_array_do, if_list_do,
7+
array_to_vec_float, if_int_convert_to_float_and_do, if_int_do_else_if_array_do, if_list_do,
88
if_matrix_convert_to_vec_array_and_do,
99
};
1010
#[cfg(feature = "nalgebra")]
@@ -357,9 +357,28 @@ pub mod matrix_functions {
357357
flatten(matrix).len() as INT
358358
}
359359

360+
/// Returns the number of non-zero elements in a matrix, passed by reference.
361+
/// ```typescript
362+
/// let matrix = ones(4, 6);
363+
/// let n = nnz(matrix);
364+
/// assert_eq(n, 24);
365+
/// ```
366+
/// ```typescript
367+
/// let matrix = eye(4);
368+
/// let n = nnz(matrix);
369+
/// assert_eq(n, 4);
370+
/// ```
371+
#[rhai_fn(name = "nnz", pure)]
372+
pub fn nnz_by_reference(matrix: &mut Array) -> INT {
373+
array_to_vec_float(&mut flatten(matrix))
374+
.iter()
375+
.filter(|&n| *n > 0.0)
376+
.count() as INT
377+
}
378+
360379
#[cfg(all(feature = "io"))]
361380
pub mod read_write {
362-
use polars::prelude::{CsvReader, DataType, SerReader};
381+
use polars::prelude::{CsvReadOptions, DataType, SerReader};
363382
use rhai::{Array, Dynamic, EvalAltResult, ImmutableString, FLOAT};
364383

365384
/// Reads a numeric csv file from a url
@@ -387,11 +406,13 @@ pub mod matrix_functions {
387406

388407
let file_path_as_str = file_path.as_str();
389408

390-
match CsvReader::from_path(file_path_as_str) {
391-
Ok(csv) => {
392-
let x = csv
393-
.infer_schema(Some(10))
394-
.has_header(
409+
// Determine path is url
410+
let path_is_url = url::Url::parse(file_path_as_str);
411+
412+
match path_is_url {
413+
Err(_) => {
414+
let x = CsvReadOptions::default()
415+
.with_has_header(
395416
csv_sniffer::Sniffer::new()
396417
.sniff_path(file_path_as_str)
397418
.map_err(|err| {
@@ -404,14 +425,21 @@ pub mod matrix_functions {
404425
.header
405426
.has_header_row,
406427
)
407-
.finish()
428+
.try_into_reader_with_file_path(Some(file_path_as_str.into()))
408429
.map_err(|err| {
409430
EvalAltResult::ErrorSystem(
410431
format!("Cannot read file as CSV: {file_path_as_str}"),
411432
err.into(),
412433
)
413434
})?
414-
.drop_nulls(None)
435+
.finish()
436+
.map_err(|err| {
437+
EvalAltResult::ErrorSystem(
438+
format!("Cannot read file: {file_path_as_str}"),
439+
err.into(),
440+
)
441+
})?
442+
.drop_nulls::<String>(None)
415443
.map_err(|err| {
416444
EvalAltResult::ErrorSystem(
417445
format!("Cannot remove null values from file: {file_path_as_str}"),
@@ -420,7 +448,6 @@ pub mod matrix_functions {
420448
})?;
421449

422450
// Convert into vec of vec
423-
424451
let mut final_output = vec![];
425452
for series in x.get_columns() {
426453
let col: Vec<FLOAT> = series
@@ -454,7 +481,7 @@ pub mod matrix_functions {
454481

455482
Ok(matrix_as_array)
456483
}
457-
Err(_) => {
484+
Ok(_) => {
458485
if let Ok(_) = url::Url::parse(file_path_as_str) {
459486
let file_contents =
460487
minreq::get(file_path_as_str).send().map_err(|err| {
@@ -753,7 +780,7 @@ pub mod matrix_functions {
753780
output
754781
}
755782

756-
/// Returns the contents of an multidimensional array as a 1-D array.
783+
/// Returns the contents of a multidimensional array as a 1-D array.
757784
/// ```typescript
758785
/// let matrix = ones(3, 5);
759786
/// let flat = flatten(matrix);

0 commit comments

Comments
 (0)