diff --git a/Cargo.toml b/Cargo.toml index 71458f2..c162822 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rhai-sci" -version = "0.2.1" +version = "0.2.2" edition = "2021" authors = ["Chris McComb "] description = "Scientific computing in the Rhai scripting language" @@ -22,29 +22,29 @@ rand = ["randlib"] [dependencies] rhai = ">=1.8.0" -nalgebralib = { version = "0.32.1", optional = true, package = "nalgebra" } -polars = { version = "0.27.2", optional = true } -url = { version = "2.2.2", optional = true } -temp-file = { version = "0.1.6", optional = true } +nalgebralib = { version = "0.33.2", optional = true, package = "nalgebra" } +polars = { version = "0.45.1", optional = true } +url = { version = ">=2.0.0", optional = true } +temp-file = { version = "0.1.9", optional = true } csv-sniffer = { version = "0.3.1", optional = true } -minreq = { version = "2.6.0", features = ["json-using-serde", "https"], optional = true } -randlib = { version = "0.8", optional = true, package = "rand" } -smartstring = "1.0.1" +minreq = { version = "2.13.0", features = ["json-using-serde", "https"], optional = true } +randlib = { version = "0.8.5", optional = true, package = "rand" } +smartstring = ">=1.0" linregress = { version = "0.5.0", optional = true } [build-dependencies] rhai = ">=1.8.0" -nalgebralib = { version = "0.32.1", optional = true, package = "nalgebra" } -polars = { version = "0.27.2", optional = true } -url = { version = "2.2.2", optional = true } -temp-file = { version = "0.1.6", optional = true } +nalgebralib = { version = "0.33.2", optional = true, package = "nalgebra" } +polars = { version = "0.45.1", optional = true } +url = { version = ">=2.0.0", optional = true } +temp-file = { version = "0.1.9", optional = true } csv-sniffer = { version = "0.3.1", optional = true } -minreq = { version = "2.6.0", features = ["json-using-serde", "https"], optional = true } -randlib = { version = "0.8", optional = true, package = "rand" } -serde_json = "1.0.82" -serde = "1.0.140" -smartstring = "1.0.1" -linregress = { version = "0.5.0", optional = true } +minreq = { version = "2.13.0", features = ["json-using-serde", "https"], optional = true } +randlib = { version = "0.8.5", optional = true, package = "rand" } +serde_json = ">=1.0.0" +serde = ">=1.0.0" +smartstring = ">=1.0.0" +linregress = { version = "0.5.4", optional = true } [package.metadata.docs.rs] all-features = true diff --git a/README.md b/README.md index 06f890a..45be01d 100644 --- a/README.md +++ b/README.md @@ -4,26 +4,22 @@ # About `rhai-sci` -This crate provides some basic scientific computing utilities for the [`Rhai`](https://rhai.rs/) scripting language, inspired by languages -like MATLAB, Octave, and R. For a complete API reference, check [the docs](https://docs.rs/rhai-sci). +This crate provides some basic scientific computing utilities for the [`Rhai`](https://rhai.rs/) scripting language, +inspired by languages like MATLAB, Octave, and R. For a complete API reference, +check [the docs](https://docs.rs/rhai-sci). # Install To use the latest released version of `rhai-sci`, add this to your `Cargo.toml`: ```toml -rhai-sci = "0.2.1" -``` - -To use the bleeding edge instead, add this: - -```toml -rhai-sci = { git = "https://github.com/cmccomb/rhai-sci" } +rhai-sci = "0.2.2" ``` # Usage -Using this crate is pretty simple! If you just want to evaluate a single line of [`Rhai`](https://rhai.rs/), then you only need: +Using this crate is pretty simple! If you just want to evaluate a single line of [`Rhai`](https://rhai.rs/), then you +only need: ```rust use rhai::INT; @@ -49,9 +45,9 @@ let value = engine.eval::("argmin([43, 42, -500])").unwrap(); # Features -| Feature | Default | Description | -| ----------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `metadata` | Disabled | Enables exporting function metadata and is ___necessary for running doc-tests on Rhai examples___. | -| `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`). | -| `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. | -| `rand` | Enabled | Enables the [`rand`](#rand) function for generating random FLOAT values and random matrices, but brings in the `rand` crate. | +| Feature | Default | Description | +|------------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `metadata` | Disabled | Enables exporting function metadata and is ___necessary for running doc-tests on Rhai examples___. | +| `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`). | +| `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. | +| `rand` | Enabled | Enables the [`rand`](#rand) function for generating random FLOAT values and random matrices, but brings in the `rand` crate. | diff --git a/build.rs b/build.rs index 6c73726..4c52a2a 100644 --- a/build.rs +++ b/build.rs @@ -63,6 +63,7 @@ fn main() { combine_with_exported_module!(&mut lib, "rhai_sci_sets", set_functions); combine_with_exported_module!(&mut lib, "rhai_sci_moving", moving_functions); combine_with_exported_module!(&mut lib, "rhai_sci_validate", validation_functions); + combine_with_exported_module!(&mut lib, "rhai_sci_trig", trig_functions); engine.register_global_module(rhai::Shared::new(lib)); // Extract metadata @@ -84,7 +85,7 @@ fn main() { let function = function.clone(); // Pull out basic info let name = function.name; - if !name.starts_with("anon") && !name.starts_with("_") { + if !name.starts_with("anon") && !name.starts_with("_") && !name.starts_with("$CONSTANTS$"){ let signature = function .signature .replace("Result<", "") @@ -219,6 +220,7 @@ mod functions { include!("src/moving.rs"); include!("src/validate.rs"); include!("src/patterns.rs"); + include!("src/trig.rs"); } #[cfg(feature = "metadata")] diff --git a/src/assertions.rs b/src/assertions.rs index 29552a3..487353a 100644 --- a/src/assertions.rs +++ b/src/assertions.rs @@ -2,7 +2,9 @@ use rhai::plugin::*; #[export_module] pub mod assert_functions { - use rhai::{Dynamic, EvalAltResult, Position}; + use rhai::{Array, Dynamic, EvalAltResult, Position, FLOAT}; + + use crate::if_list_convert_to_vec_float_and_do; /// Assert that a statement is true and throw an error if it is not. /// ```typescript @@ -88,4 +90,80 @@ pub mod assert_functions { .into()) } } + + /// Assert that two floats are approximately equal (within `eps`) and return an error if they + /// are not. + /// ```typescript + /// assert_approx_eq(2.0, 2.000000000000000001, 1e-10); + /// ``` + #[rhai_fn(name = "assert_approx_eq", return_raw)] + pub fn assert_approx_eq( + lhs: FLOAT, + rhs: FLOAT, + eps: FLOAT, + ) -> Result> { + if (lhs - rhs).abs() < eps { + Ok(true) + } else { + println!("LHS: {:?}", lhs); + println!("RHS: {:?}", rhs); + Err(EvalAltResult::ErrorArithmetic( + "The left-hand side and right-hand side are not equal".to_string(), + Position::NONE, + ) + .into()) + } + } + + /// Assert that two floats are approximately equal and return an error if they + /// are not. Use the default tolerance of 1e-10 for the comparison. + /// ```typescript + /// assert_approx_eq(2.0, 2.000000000000000001); + /// ``` + #[rhai_fn(name = "assert_approx_eq", return_raw)] + pub fn assert_approx_eq_with_default( + lhs: FLOAT, + rhs: FLOAT, + ) -> Result> { + assert_approx_eq(lhs, rhs, 1e-10) + } + + /// Assert that two arrays are approximately equal (within `eps`) and return an error if they + /// are not. + /// ```typescript + /// assert_approx_eq([2.0, 2.0], [2.0, 2.000000000000000001], 1e-10); + /// ``` + #[rhai_fn(name = "assert_approx_eq", return_raw)] + pub fn assert_approx_eq_list( + lhs: Array, + rhs: Array, + eps: FLOAT, + ) -> Result> { + if_list_convert_to_vec_float_and_do(&mut rhs.clone(), |rhs_as_vec_float| { + if_list_convert_to_vec_float_and_do(&mut lhs.clone(), |lhs_as_vec_float| { + let mut result = Ok(true); + for i in 0..rhs_as_vec_float.len() { + result = result.and(assert_approx_eq( + lhs_as_vec_float[i], + rhs_as_vec_float[i], + eps, + )) + } + result + }) + }) + } + + /// Assert that two arrays are approximately equal and return an error if they + /// are not. Use the default tolerance of 1e-10 for the comparison. + /// ```typescript + /// assert_approx_eq([2.0, 2.0], [2.0, 2.000000000000000001]); + /// ``` + #[rhai_fn(name = "assert_approx_eq", return_raw)] + pub fn assert_approx_eq_list_with_default( + lhs: Array, + rhs: Array, + ) -> Result> { + assert_approx_eq_list(lhs, rhs, 1e-10) + } } diff --git a/src/lib.rs b/src/lib.rs index 3cf175d..48228b3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,28 +6,30 @@ #![doc = include_str!("../docs/highlight.html")] mod patterns; -use patterns::*; +pub use patterns::*; use rhai::{def_package, packages::Package, plugin::*, Engine, EvalAltResult}; mod matrices_and_arrays; -use matrices_and_arrays::matrix_functions; +pub use matrices_and_arrays::matrix_functions; mod statistics; -use statistics::stats; +pub use statistics::stats; mod misc; -use misc::misc_functions; +pub use misc::misc_functions; mod cumulative; -use cumulative::cum_functions; +pub use cumulative::cum_functions; mod integration_and_differentiation; -use integration_and_differentiation::int_and_diff; +pub use integration_and_differentiation::int_and_diff; mod assertions; -use assertions::assert_functions; +pub use assertions::assert_functions; mod constants; -use constants::constant_definitions; +pub use constants::constant_definitions; mod moving; -use moving::moving_functions; +pub use moving::moving_functions; mod sets; -use sets::set_functions; +pub use sets::set_functions; mod validate; -use validate::validation_functions; +pub use validate::validation_functions; +mod trig; +pub use trig::trig_functions; def_package! { /// Package for scientific computing @@ -43,6 +45,7 @@ def_package! { combine_with_exported_module!(lib, "rhai_sci_sets", set_functions); combine_with_exported_module!(lib, "rhai_sci_moving", moving_functions); combine_with_exported_module!(lib, "rhai_sci_validation", validation_functions); + combine_with_exported_module!(lib, "rhai_sci_trig", trig_functions); } } diff --git a/src/matrices_and_arrays.rs b/src/matrices_and_arrays.rs index b308d00..c7f845b 100644 --- a/src/matrices_and_arrays.rs +++ b/src/matrices_and_arrays.rs @@ -4,7 +4,7 @@ use rhai::plugin::*; #[export_module] pub mod matrix_functions { use crate::{ - if_int_convert_to_float_and_do, if_int_do_else_if_array_do, if_list_do, + array_to_vec_float, if_int_convert_to_float_and_do, if_int_do_else_if_array_do, if_list_do, if_matrix_convert_to_vec_array_and_do, }; #[cfg(feature = "nalgebra")] @@ -357,9 +357,28 @@ pub mod matrix_functions { flatten(matrix).len() as INT } + /// Returns the number of non-zero elements in a matrix, passed by reference. + /// ```typescript + /// let matrix = ones(4, 6); + /// let n = nnz(matrix); + /// assert_eq(n, 24); + /// ``` + /// ```typescript + /// let matrix = eye(4); + /// let n = nnz(matrix); + /// assert_eq(n, 4); + /// ``` + #[rhai_fn(name = "nnz", pure)] + pub fn nnz_by_reference(matrix: &mut Array) -> INT { + array_to_vec_float(&mut flatten(matrix)) + .iter() + .filter(|&n| *n > 0.0) + .count() as INT + } + #[cfg(all(feature = "io"))] pub mod read_write { - use polars::prelude::{CsvReader, DataType, SerReader}; + use polars::prelude::{CsvReadOptions, DataType, SerReader}; use rhai::{Array, Dynamic, EvalAltResult, ImmutableString, FLOAT}; /// Reads a numeric csv file from a url @@ -387,11 +406,13 @@ pub mod matrix_functions { let file_path_as_str = file_path.as_str(); - match CsvReader::from_path(file_path_as_str) { - Ok(csv) => { - let x = csv - .infer_schema(Some(10)) - .has_header( + // Determine path is url + let path_is_url = url::Url::parse(file_path_as_str); + + match path_is_url { + Err(_) => { + let x = CsvReadOptions::default() + .with_has_header( csv_sniffer::Sniffer::new() .sniff_path(file_path_as_str) .map_err(|err| { @@ -404,14 +425,21 @@ pub mod matrix_functions { .header .has_header_row, ) - .finish() + .try_into_reader_with_file_path(Some(file_path_as_str.into())) .map_err(|err| { EvalAltResult::ErrorSystem( format!("Cannot read file as CSV: {file_path_as_str}"), err.into(), ) })? - .drop_nulls(None) + .finish() + .map_err(|err| { + EvalAltResult::ErrorSystem( + format!("Cannot read file: {file_path_as_str}"), + err.into(), + ) + })? + .drop_nulls::(None) .map_err(|err| { EvalAltResult::ErrorSystem( format!("Cannot remove null values from file: {file_path_as_str}"), @@ -420,7 +448,6 @@ pub mod matrix_functions { })?; // Convert into vec of vec - let mut final_output = vec![]; for series in x.get_columns() { let col: Vec = series @@ -454,7 +481,7 @@ pub mod matrix_functions { Ok(matrix_as_array) } - Err(_) => { + Ok(_) => { if let Ok(_) = url::Url::parse(file_path_as_str) { let file_contents = minreq::get(file_path_as_str).send().map_err(|err| { @@ -753,7 +780,7 @@ pub mod matrix_functions { output } - /// Returns the contents of an multidimensional array as a 1-D array. + /// Returns the contents of a multidimensional array as a 1-D array. /// ```typescript /// let matrix = ones(3, 5); /// let flat = flatten(matrix); diff --git a/src/misc.rs b/src/misc.rs index a648483..37c2b6a 100644 --- a/src/misc.rs +++ b/src/misc.rs @@ -5,6 +5,10 @@ pub mod misc_functions { use crate::{if_list_convert_to_vec_float_and_do, if_list_do_int_or_do_float}; use rhai::{Array, Dynamic, EvalAltResult, Position, FLOAT}; + /// Infinity + #[allow(non_upper_case_globals)] + pub const inf: FLOAT = FLOAT::INFINITY; + /// Returns a random number between zero and one. /// ```typescript /// let r = rand(); diff --git a/src/patterns.rs b/src/patterns.rs index f0b8a70..baef622 100644 --- a/src/patterns.rs +++ b/src/patterns.rs @@ -1,8 +1,4 @@ use rhai::{Array, Dynamic, EvalAltResult, Position, FLOAT, INT}; -#[cfg(feature = "smartcore")] -use smartcorelib::linalg::basic::{ - arrays::Array as scArray, arrays::Array2 as scArray2, matrix::DenseMatrix, -}; /// Matrix compatibility conditions #[allow(dead_code)] @@ -68,6 +64,7 @@ where } } +/// Does a function if the input is a list, otherwise throws an error. pub fn if_list_do(arr: &mut Array, mut f: F) -> Result> where F: FnMut(&mut Array) -> Result>, @@ -96,6 +93,8 @@ where .and_then(f) } +/// If the input is an int, convert to a float and do the function. if the input is a float already, +/// the function is still performed. pub fn if_int_convert_to_float_and_do(x: Dynamic, mut f: F) -> Result> where F: FnMut(FLOAT) -> Result>, @@ -182,6 +181,7 @@ where } } +/// If the input is a pub fn if_matrix_convert_to_vec_array_and_do( matrix: &mut Array, mut f: F, @@ -204,42 +204,6 @@ where } } -#[cfg(feature = "smartcore")] -pub fn if_matrix_convert_to_dense_matrix_and_do( - matrix: &mut Array, - mut f: F, -) -> Result> -where - F: FnMut(DenseMatrix) -> Result>, -{ - if crate::validation_functions::is_matrix(matrix) { - let matrix_as_vec = matrix - .clone() - .iter() - .map(|x| { - x.clone() - .into_array() - .unwrap() - .iter() - .map(|y| { - if y.is::() { - y.clone().as_float().unwrap() - } else { - y.clone().as_int().unwrap() as FLOAT - } - }) - .collect::>() - }) - .collect::>>(); - f(DenseMatrix::from_2d_vec(&matrix_as_vec)) - } else { - Err( - EvalAltResult::ErrorArithmetic(format!("The input must be a matrix."), Position::NONE) - .into(), - ) - } -} - pub fn if_int_do_else_if_array_do( d: Dynamic, mut f_int: FA, @@ -268,20 +232,6 @@ pub fn array_to_vec_int(arr: &mut Array) -> Vec { .collect::>() } -#[cfg(feature = "smartcore")] -pub fn dense_matrix_to_vec_dynamic(dm: DenseMatrix) -> Vec { - let mut output = vec![]; - for idx in 0..dm.shape().0 { - output.push(Dynamic::from_array( - dm.get_row(idx) - .iterator(0) - .map(|x| Dynamic::from_float(x.clone())) - .collect::>(), - )); - } - output -} - pub fn array_to_vec_float(arr: &mut Array) -> Vec { arr.into_iter() .map(|el| el.as_float().unwrap()) diff --git a/src/statistics.rs b/src/statistics.rs index db7308c..4228785 100644 --- a/src/statistics.rs +++ b/src/statistics.rs @@ -542,9 +542,9 @@ pub mod stats { /// [0.8], /// [2.1]]; /// let b = regress(x, y); - /// assert_eq(b, #{"parameters": [5.551115123125783e-16, 1.0000000000000002], - /// "pvalues": [1.0, 0.1091825535092476], - /// "standard_errors": [0.1118033988749896, 0.17320508075688787]}); + /// assert_eq(b, #{"parameters": [-2.220446049250313e-16, 1.0000000000000002], + /// "pvalues": [1.0, 0.10918255350924745], + /// "standard_errors": [0.11180339887498947, 0.17320508075688767]}); /// ``` #[cfg(feature = "nalgebra")] #[rhai_fn(name = "regress", return_raw, pure)] diff --git a/src/trig.rs b/src/trig.rs new file mode 100644 index 0000000..3ca82ac --- /dev/null +++ b/src/trig.rs @@ -0,0 +1,636 @@ +use rhai::plugin::*; + +#[export_module] +pub mod trig_functions { + use crate::if_int_convert_to_float_and_do; + use rhai::{Array, Dynamic, EvalAltResult, Position, FLOAT, INT}; + + /// Converts the argument from degrees to radians + /// ```typescript + /// assert_eq(deg2rad(180.0), pi); + /// ``` + #[rhai_fn(name = "deg2rad")] + pub fn deg2rad(degrees: FLOAT) -> FLOAT { + degrees * std::f64::consts::PI / 180.0 + } + + /// Converts the argument from radians to degrees + /// ```typescript + /// assert_eq(rad2deg(pi), 180.0); + /// ``` + #[rhai_fn(name = "rad2deg")] + pub fn rad2deg(radians: FLOAT) -> FLOAT { + radians * 180.0 / std::f64::consts::PI + } + + /// Convert the argument from 3D Cartesian coordinates to polar coordinates. + /// ```typescript + /// assert_eq(cart2pol(1.0, 1.0, 1.0), [pi/4, sqrt(2.0), 1.0]) + /// ``` + #[rhai_fn(name = "cart2pol")] + pub fn cart2pol3d(x: FLOAT, y: FLOAT, z: FLOAT) -> Array { + vec![ + Dynamic::from(y.atan2(x)), + Dynamic::from(y.hypot(x)), + Dynamic::from(z), + ] + } + + /// Convert the argument from 2D Cartesian coordinates to polar coordinates. + /// ```typescript + /// assert_eq(cart2pol(1.0, 1.0), [pi/4, sqrt(2.0)]) + /// ``` + #[rhai_fn(name = "cart2pol")] + pub fn cart2pol2d(x: FLOAT, y: FLOAT) -> Array { + vec![Dynamic::from(y.atan2(x)), Dynamic::from(y.hypot(x))] + } + + /// Convert the argument from 3D polar coordinates to Cartesian coordinates. + /// ```typescript + /// assert_approx_eq(pol2cart(pi/4, sqrt(2.0), 1.0), [1.0, 1.0, 1.0]) + /// ``` + #[rhai_fn(name = "pol2cart")] + pub fn pol2cart3d(theta: FLOAT, r: FLOAT, z: FLOAT) -> Array { + vec![ + Dynamic::from(r * theta.cos()), + Dynamic::from(r * theta.sin()), + Dynamic::from(z), + ] + } + + /// Convert the argument from 2D polar coordinates to Cartesian coordinates. + /// ```typescript + /// assert_approx_eq(pol2cart(pi/4, sqrt(2.0)), [1.0, 1.0]) + /// ``` + #[rhai_fn(name = "pol2cart")] + pub fn pol2cart2d(theta: FLOAT, r: FLOAT) -> Array { + vec![ + Dynamic::from(r * theta.cos()), + Dynamic::from(r * theta.sin()), + ] + } + + /// Convert the argument from 3D Cartesian coordinates to spherical coordinates. + /// ```typescript + /// assert_approx_eq(cart2sph(1.0, 0.0, 1.0), [0.0, pi/4, sqrt(2.0)]) + /// ``` + #[rhai_fn(name = "cart2sph")] + pub fn cart2sph(x: FLOAT, y: FLOAT, z: FLOAT) -> Array { + vec![ + Dynamic::from(y.atan2(x)), + Dynamic::from(z.atan2(y.hypot(x))), + Dynamic::from(hypot3(x, y, z)), + ] + } + + /// Convert the argument from spherical coordinates to 3D Cartesian coordinates. + /// ```typescript + /// assert_approx_eq(sph2cart(0.0, pi/4, sqrt(2.0)), [1.0, 0.0, 1.0]) + /// ``` + #[rhai_fn(name = "sph2cart")] + pub fn sph2cart(azimuth: FLOAT, elevation: FLOAT, r: FLOAT) -> Array { + vec![ + Dynamic::from(r * elevation.cos() * azimuth.cos()), + Dynamic::from(r * elevation.cos() * azimuth.sin()), + Dynamic::from(r * elevation.sin()), + ] + } + + /// Extends the built-in hypot function to compute distance in 3D cartesian space + /// ```typescript + /// assert_eq(hypot(2.0, 3.0, 6.0), 7.0); + /// ``` + #[rhai_fn(name = "hypot")] + pub fn hypot3(x: FLOAT, y: FLOAT, z: FLOAT) -> FLOAT { + (x.powf(2.0) + y.powf(2.0) + z.powf(2.0)).sqrt() + } + + /// Returns the sine of an argument given in degrees + /// ```typescript + /// assert_eq(sind(0.0), 0.0); + /// ``` + /// ```typescript + /// assert_eq(sind(90.0), 1.0); + /// ``` + /// ```typescript + /// assert_approx_eq(sind(180.0), 0.0); + /// ``` + /// ```typescript + /// assert_eq(sind(270.0), -1.0); + /// ``` + #[rhai_fn(name = "sind")] + pub fn sind(degrees: FLOAT) -> FLOAT { + FLOAT::sin(deg2rad(degrees)) + } + + /// Returns the inverse sine in degrees + /// ```typescript + /// assert_eq(asind(-1.0), -90.0); + /// ``` + /// ```typescript + /// assert_eq(asind(0.0), 0.0); + /// ``` + /// ```typescript + /// assert_eq(asind(1.0), 90.0); + /// ``` + #[rhai_fn(name = "asind")] + pub fn asind(x: FLOAT) -> FLOAT { + rad2deg(FLOAT::asin(x)) + } + + /// Returns the hyperbolic sine of the argument given in degrees + /// ```typescript + /// assert_eq(sinhd(0.0), 0.0) + /// ``` + /// ```typescript + /// assert_eq(sinhd(10.0), sinh(10.0*pi/180.0)) + /// ``` + #[rhai_fn(name = "sinhd")] + pub fn sinhd(degrees: FLOAT) -> FLOAT { + FLOAT::sinh(deg2rad(degrees)) + } + + /// Returns the inverse hyperbolic sine in degrees + /// ```typescript + /// assert_eq(asinhd(0.0), 0.0) + /// ``` + /// ```typescript + /// assert_eq(asinhd(10.0), 180.0/pi*asinh(10.0)) + /// ``` + #[rhai_fn(name = "asinhd")] + pub fn asinhd(x: FLOAT) -> FLOAT { + rad2deg(FLOAT::asinh(x)) + } + + /// Returns the cosine of an argument given in degrees + /// ```typescript + /// assert_eq(cosd(0.0), 1.0); + /// ``` + /// ```typescript + /// assert_approx_eq(cosd(90.0), 0.0); + /// ``` + /// ```typescript + /// assert_eq(cosd(180.0), -1.0); + /// ``` + /// ```typescript + /// assert_approx_eq(cosd(270.0), 0.0); + /// ``` + #[rhai_fn(name = "cosd")] + pub fn cosd(degrees: FLOAT) -> FLOAT { + FLOAT::cos(deg2rad(degrees)) + } + + /// Returns the inverse cosine in degrees + /// ```typescript + /// assert_eq(acosd(-1.0), 180.0); + /// ``` + /// ```typescript + /// assert_eq(acosd(0.0), 90.0); + /// ``` + /// ```typescript + /// assert_eq(acosd(1.0), 0.0); + /// ``` + #[rhai_fn(name = "acosd")] + pub fn acosd(x: FLOAT) -> FLOAT { + rad2deg(FLOAT::acos(x)) + } + + /// Returns the hyperbolic cosine of the argument given in degrees + /// ```typescript + /// assert_eq(coshd(0.0), 1.0) + /// ``` + /// ```typescript + /// assert_eq(coshd(10.0), cosh(10.0*pi/180.0)) + /// ``` + #[rhai_fn(name = "coshd")] + pub fn coshd(degrees: FLOAT) -> FLOAT { + FLOAT::cosh(deg2rad(degrees)) + } + + /// Returns the inverse hyperbolic cosine in degrees + /// ```typescript + /// assert_eq(acoshd(1.0), 0.0) + /// ``` + /// ```typescript + /// assert_eq(acoshd(10.0), 180.0/pi*acosh(10.0)) + /// ``` + #[rhai_fn(name = "acoshd")] + pub fn acoshd(x: FLOAT) -> FLOAT { + rad2deg(FLOAT::acosh(x)) + } + + /// Returns the tangent of an argument given in degrees + /// ```typescript + /// assert_approx_eq(tand(-45.0), -1.0); + /// ``` + /// ```typescript + /// assert_eq(tand(0.0), 0.0); + /// ``` + /// ```typescript + /// assert_approx_eq(tand(45.0), 1.0); + /// ``` + #[rhai_fn(name = "tand")] + pub fn tand(degrees: FLOAT) -> FLOAT { + FLOAT::tan(deg2rad(degrees)) + } + + /// Returns the inverse tangent in degrees + /// ```typescript + /// assert_approx_eq(atand(-1.0), -45.0); + /// ``` + /// ```typescript + /// assert_eq(atand(0.0), 0.0); + /// ``` + /// ```typescript + /// assert_approx_eq(atand(1.0), 45.0); + /// ``` + #[rhai_fn(name = "atand")] + pub fn atand(x: FLOAT) -> FLOAT { + rad2deg(FLOAT::atan(x)) + } + + /// Returns the inverse tangent in degrees , taking two arguments as input. + /// ```typescript + /// assert_approx_eq(atand(-1.0, 1.0), -45.0); + /// ``` + /// ```typescript + /// assert_eq(atand(0.0, 1.0), 0.0); + /// ``` + /// ```typescript + /// assert_approx_eq(atand(1.0, 1.0), 45.0); + /// ``` + #[rhai_fn(name = "atand")] + pub fn atand2(x: FLOAT, y: FLOAT) -> FLOAT { + rad2deg(FLOAT::atan2(x, y)) + } + + /// Returns the hyperbolic tangent of the argument given in degrees + /// ```typescript + /// assert_eq(tanhd(0.0), 0.0) + /// ``` + /// ```typescript + /// assert_eq(tanhd(10.0), tanh(10.0*pi/180.0)) + /// ``` + #[rhai_fn(name = "tanhd")] + pub fn tanhd(degrees: FLOAT) -> FLOAT { + FLOAT::tanh(deg2rad(degrees)) + } + + /// Returns the inverse hyperbolic tangent in degrees + /// ```typescript + /// assert_eq(atanhd(0.0), 0.0) + /// ``` + /// ```typescript + /// assert_eq(atanhd(10.0), 180.0/pi*atanh(10.0)) + /// ``` + #[rhai_fn(name = "atanhd")] + pub fn atanhd(x: FLOAT) -> FLOAT { + rad2deg(FLOAT::atanh(x)) + } + + /// Returns the cosecant of the argument given in radians + /// ```typescript + /// assert_eq(csc(-pi/2), -1.0) + /// ``` + /// ```typescript + /// assert_eq(csc(0.0), inf) + /// ``` + /// ```typescript + /// assert_eq(csc(pi/2), 1.0) + /// ``` + #[rhai_fn(name = "csc")] + pub fn csc(radians: FLOAT) -> FLOAT { + 1.0 / FLOAT::sin(radians) + } + + /// Returns the cosecant of the argument given in degrees + /// ```typescript + /// assert_eq(cscd(-90.0), -1.0) + /// ``` + /// ```typescript + /// assert_eq(cscd(0.0), inf) + /// ``` + /// ```typescript + /// assert_eq(cscd(90.0), 1.0) + /// ``` + #[rhai_fn(name = "cscd")] + pub fn cscd(degrees: FLOAT) -> FLOAT { + 1.0 / FLOAT::sin(deg2rad(degrees)) + } + + /// Returns the inverse cosecant in radians + /// ```typescript + /// assert_eq(acsc(-1.0), -pi/2) + /// ``` + /// ```typescript + /// assert_eq(acsc(inf), 0.0) + /// ``` + /// ```typescript + /// assert_eq(acsc(1.0), pi/2) + /// ``` + #[rhai_fn(name = "acsc")] + pub fn acsc(x: FLOAT) -> FLOAT { + FLOAT::asin(1.0 / x) + } + + /// Returns the inverse cosecant in degrees + /// ```typescript + /// assert_eq(acscd(-1.0), -90.0) + /// ``` + /// ```typescript + /// assert_eq(acscd(inf), 0.0) + /// ``` + /// ```typescript + /// assert_eq(acscd(1.0), 90.0) + /// ``` + #[rhai_fn(name = "acscd")] + pub fn acscd(x: FLOAT) -> FLOAT { + rad2deg(FLOAT::asin(1.0 / x)) + } + + /// Returns the hyperbolic cosecant of the argument given in radians + /// ```typescript + /// assert_eq(csch(0.0), inf) + /// ``` + /// ```typescript + /// assert_eq(csch(10.0), 1.0/sinh(10.0)) + /// ``` + /// ```typescript + /// assert_eq(csch(pi/2), 1.0/sinh(pi/2)) + /// ``` + #[rhai_fn(name = "csch")] + pub fn csch(radians: FLOAT) -> FLOAT { + 1.0 / FLOAT::sinh(radians) + } + + /// Returns the hyperbolic cosecant of the argument given in degrees + /// ```typescript + /// assert_eq(cschd(0.0), inf) + /// ``` + /// ```typescript + /// assert_eq(cschd(10.0), 1.0/sinhd(10.0)) + /// ``` + /// ```typescript + /// assert_eq(cschd(90.0), 1.0/sinhd(90.0)) + /// ``` + #[rhai_fn(name = "cschd")] + pub fn cschd(degrees: FLOAT) -> FLOAT { + 1.0 / FLOAT::sinh(deg2rad(degrees)) + } + + /// Returns the inverse hyperbolic cosecant in radians + /// ```typescript + /// assert_eq(acsch(inf), 0.0) + /// ``` + /// ```typescript + /// assert_eq(acsch(1.0), asinh(1.0)) + /// ``` + /// ```typescript + /// assert_eq(acsch(-1.0), asinh(-1.0)) + /// ``` + #[rhai_fn(name = "acsch")] + pub fn acsch(x: FLOAT) -> FLOAT { + FLOAT::asinh(1.0 / x) + } + + /// Returns the inverse hyperbolic cosecant in degrees + /// ```typescript + /// assert_eq(acschd(inf), 0.0) + /// ``` + /// ```typescript + /// assert_eq(acschd(1.0), asinhd(1.0)) + /// ``` + /// ```typescript + /// assert_eq(acschd(-1.0), asinhd(-1.0)) + /// ``` + #[rhai_fn(name = "acschd")] + pub fn acschd(x: FLOAT) -> FLOAT { + rad2deg(FLOAT::asinh(1.0 / x)) + } + + /// Returns the secant of the argument given in radians + /// ```typescript + /// assert_eq(sec(0.0), 1.0); + /// ``` + /// ```typescript + /// assert_eq(sec(pi/2), 1/cos(pi/2)); + /// ``` + /// ```typescript + /// assert_eq(sec(pi), -1.0); + #[rhai_fn(name = "sec")] + pub fn sec(radians: FLOAT) -> FLOAT { + 1.0 / FLOAT::cos(radians) + } + + /// Returns the secant of the argument given in degrees + /// ```typescript + /// assert_eq(secd(0.0), 1.0); + /// ``` + /// ```typescript + /// assert_eq(secd(90.0), 1/cosd(90.0)); + /// ``` + /// ```typescript + /// assert_eq(secd(180.0), -1.0); + /// ``` + #[rhai_fn(name = "secd")] + pub fn secd(degrees: FLOAT) -> FLOAT { + 1.0 / FLOAT::cos(deg2rad(degrees)) + } + + /// Returns the inverse secant in radians + /// ```typescript + /// assert_eq(asec(1.0), 0.0); + /// ``` + /// ```typescript + /// assert_eq(asec(-1.0), pi); + /// ``` + /// ```typescript + /// assert_eq(asec(0.5), acos(2.0)); + /// ``` + #[rhai_fn(name = "asec")] + pub fn asec(x: FLOAT) -> FLOAT { + FLOAT::acos(1.0 / x) + } + + /// Returns the inverse secant in degrees + /// ```typescript + /// assert_eq(asecd(1.0), 0.0); + /// ``` + /// ```typescript + /// assert_eq(asecd(-1.0), 180.0); + /// ``` + /// ```typescript + /// assert_eq(asecd(0.5), acosd(2.0)); + /// ``` + #[rhai_fn(name = "asecd")] + pub fn asecd(x: FLOAT) -> FLOAT { + rad2deg(FLOAT::acos(1.0 / x)) + } + + /// Returns the hyperbolic secant of the argument given in radians + /// ```typescript + /// assert_eq(sech(0.0), 1.0); + /// ``` + /// ```typescript + /// assert_eq(sech(10.0), 1.0/cosh(10.0)); + /// ``` + /// ```typescript + /// assert_eq(sech(pi/2), 1.0/cosh(pi/2)); + /// ``` + #[rhai_fn(name = "sech")] + pub fn sech(radians: FLOAT) -> FLOAT { + 1.0 / FLOAT::cosh(radians) + } + /// Returns the hyperbolic secant of the argument given in degrees + /// ```typescript + /// assert_eq(sechd(0.0), 1.0); + /// ``` + /// ```typescript + /// assert_eq(sechd(10.0), 1.0/coshd(10.0)); + /// ``` + /// ```typescript + /// assert_eq(sechd(90.0), 1.0/coshd(90.0)); + /// ``` + #[rhai_fn(name = "sechd")] + pub fn sechd(degrees: FLOAT) -> FLOAT { + 1.0 / FLOAT::cosh(deg2rad(degrees)) + } + + /// Returns the inverse hyperbolic secant in radians + /// ```typescript + /// assert_eq(asech(1.0), 0.0); + /// ``` + /// ```typescript + /// assert_eq(asech(0.5), acosh(2.0)); + /// ``` + /// ```typescript + /// assert_eq(asech(0.1), acosh(10.0)); + /// ``` + #[rhai_fn(name = "asech")] + pub fn asech(x: FLOAT) -> FLOAT { + FLOAT::acosh(1.0 / x) + } + + /// Returns the inverse hyperbolic secant of the argument in degrees + /// ```typescript + /// assert_eq(asechd(1.0), 0.0); + /// ``` + #[rhai_fn(name = "asechd")] + pub fn asechd(x: FLOAT) -> FLOAT { + rad2deg(FLOAT::acosh(1.0 / x)) + } + + /// Returns the cotangent of the argument given in radians + /// ```typescript + /// assert_approx_eq(cot(pi/4), 1.0, 1e-10); + /// ``` + /// ```typescript + /// assert_approx_eq(cot(pi/2), 0.0, 1e-10); + /// ``` + /// ```typescript + /// assert_approx_eq(cot(3*pi/4), -1.0, 1e-10); + /// ``` + #[rhai_fn(name = "cot")] + pub fn cot(radians: FLOAT) -> FLOAT { + 1.0 / FLOAT::tan(radians) + } + + /// Returns the cotangent of the argument given in degrees + /// ```typescript + /// assert_approx_eq(cotd(45.0), 1.0, 1e-10); + /// ``` + /// ```typescript + /// assert_approx_eq(cotd(90.0), 0.0, 1e-10); + /// ``` + /// ```typescript + /// assert_approx_eq(cotd(135.0), -1.0, 1e-10); + /// ``` + #[rhai_fn(name = "cotd")] + pub fn cotd(degrees: FLOAT) -> FLOAT { + 1.0 / FLOAT::tan(deg2rad(degrees)) + } + + /// Returns the inverse of the cotangent in radians + /// ```typescript + /// assert_eq(acot(1.0), pi/4); + /// ``` + /// ```typescript + /// assert_eq(acot(-1.0), -pi/4); + /// ``` + /// ```typescript + /// assert_eq(acot(0.0), pi/2); + /// ``` + #[rhai_fn(name = "acot")] + pub fn acot(x: FLOAT) -> FLOAT { + FLOAT::atan(1.0 / x) + } + + /// Returns the inverse of the cotangent in degrees + /// ```typescript + /// assert_eq(acotd(1.0), 45.0); + /// ``` + /// ```typescript + /// assert_eq(acotd(-1.0), -45.0); + /// ``` + /// ```typescript + /// assert_eq(acotd(0.0), 90.0); + /// ``` + #[rhai_fn(name = "acotd")] + pub fn acotd(x: FLOAT) -> FLOAT { + rad2deg(FLOAT::atan(1.0 / x)) + } + + /// Returns the hyperbolic cotangent of the argument given in radians + /// ```typescript + /// assert_approx_eq(coth(1.0), cosh(1.0)/sinh(1.0), 1e-10); + /// ``` + /// ```typescript + /// assert_approx_eq(coth(0.5), cosh(0.5)/sinh(0.5), 1e-10); + /// ``` + /// ```typescript + /// assert_approx_eq(coth(0.1), cosh(0.1)/sinh(0.1), 1e-10); + /// ``` + #[rhai_fn(name = "coth")] + pub fn coth(radians: FLOAT) -> FLOAT { + 1.0 / FLOAT::tanh(radians) + } + + /// Returns the hyperbolic cotangent of the argument given in degrees + /// ```typescript + /// assert_approx_eq(cothd(1.0), coshd(1.0)/sinhd(1.0), 1e-10); + /// ``` + /// ```typescript + /// assert_approx_eq(cothd(0.5), coshd(0.5)/sinhd(0.5), 1e-10); + /// ``` + /// ```typescript + /// assert_approx_eq(cothd(0.1), coshd(0.1)/sinhd(0.1), 1e-10); + /// ``` + #[rhai_fn(name = "cothd")] + pub fn cothd(degrees: FLOAT) -> FLOAT { + 1.0 / FLOAT::tanh(deg2rad(degrees)) + } + + /// Returns the inverse hyperbolic cotangent of the argument in radians + /// ```typescript + /// assert_eq(acoth(1.0), atanh(1.0)); + /// ``` + /// ```typescript + /// assert_eq(acoth(-1.0), atanh(-1.0)); + /// ``` + #[rhai_fn(name = "acoth")] + pub fn acoth(x: FLOAT) -> FLOAT { + FLOAT::atanh(1.0 / x) + } + + /// Returns the inverse hyperbolic cotangent of the argument in degrees + /// ```typescript + /// assert_eq(acothd(1.0), atanhd(1.0)); + /// ``` + /// ```typescript + /// assert_eq(acothd(-1.0), atanhd(-1.0)); + /// ``` + #[rhai_fn(name = "acothd")] + pub fn acothd(x: FLOAT) -> FLOAT { + rad2deg(FLOAT::atanh(1.0 / x)) + } +}