use super::array::WasmArray;
use crate::array::Array;
use crate::stats::{corrcoef, cov, histogram, percentile, Statistics};
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn mean(arr: &WasmArray) -> f64 {
arr.mean()
}
#[wasm_bindgen]
pub fn median(arr: &WasmArray) -> f64 {
arr.percentile(0.5)
}
#[wasm_bindgen]
pub fn variance(arr: &WasmArray) -> f64 {
arr.var()
}
#[wasm_bindgen]
pub fn std_dev(arr: &WasmArray) -> f64 {
arr.std()
}
#[wasm_bindgen]
pub fn minimum(arr: &WasmArray) -> f64 {
arr.min()
}
#[wasm_bindgen]
pub fn maximum(arr: &WasmArray) -> f64 {
arr.max()
}
#[wasm_bindgen]
pub fn compute_percentile(arr: &WasmArray, q: f64) -> Result<f64, JsValue> {
if !(0.0..=1.0).contains(&q) {
return Err(JsValue::from_str("Percentile must be between 0.0 and 1.0"));
}
Ok(arr.percentile(q))
}
#[wasm_bindgen]
pub fn compute_histogram(arr: &WasmArray, bins: usize) -> Result<HistogramResult, JsValue> {
if bins == 0 {
return Err(JsValue::from_str("Number of bins must be greater than 0"));
}
let arr_vec = arr.to_vec();
let arr_shape = arr.shape();
let inner = Array::from_vec(arr_vec).reshape(&arr_shape);
histogram(&inner, bins, None, None)
.map(|(counts, bin_edges)| HistogramResult {
counts: WasmArray::from_array(counts),
bin_edges: WasmArray::from_array(bin_edges),
})
.map_err(|e| JsValue::from_str(&format!("Histogram computation error: {}", e)))
}
#[wasm_bindgen]
pub struct HistogramResult {
counts: WasmArray,
bin_edges: WasmArray,
}
#[wasm_bindgen]
impl HistogramResult {
#[wasm_bindgen(getter)]
pub fn counts(&self) -> WasmArray {
let counts_vec = self.counts.to_vec();
let counts_shape = self.counts.shape();
WasmArray::from_array(Array::from_vec(counts_vec).reshape(&counts_shape))
}
#[wasm_bindgen(getter)]
pub fn bin_edges(&self) -> WasmArray {
let edges_vec = self.bin_edges.to_vec();
let edges_shape = self.bin_edges.shape();
WasmArray::from_array(Array::from_vec(edges_vec).reshape(&edges_shape))
}
}
#[wasm_bindgen]
pub fn correlation(x: &WasmArray, y: Option<WasmArray>) -> Result<WasmArray, JsValue> {
let x_vec = x.to_vec();
let x_shape = x.shape();
let x_inner = Array::from_vec(x_vec).reshape(&x_shape);
let y_inner = y.as_ref().map(|y_arr| {
let y_vec = y_arr.to_vec();
let y_shape = y_arr.shape();
Array::from_vec(y_vec).reshape(&y_shape)
});
corrcoef(&x_inner, y_inner.as_ref(), None)
.map(WasmArray::from_array)
.map_err(|e| JsValue::from_str(&format!("Correlation computation error: {}", e)))
}
#[wasm_bindgen]
pub fn covariance(x: &WasmArray, y: Option<WasmArray>) -> Result<WasmArray, JsValue> {
let x_vec = x.to_vec();
let x_shape = x.shape();
let x_inner = Array::from_vec(x_vec).reshape(&x_shape);
let y_inner = y.as_ref().map(|y_arr| {
let y_vec = y_arr.to_vec();
let y_shape = y_arr.shape();
Array::from_vec(y_vec).reshape(&y_shape)
});
cov(&x_inner, y_inner.as_ref(), None, None, None)
.map(WasmArray::from_array)
.map_err(|e| JsValue::from_str(&format!("Covariance computation error: {}", e)))
}
#[wasm_bindgen]
pub fn sum(arr: &WasmArray) -> f64 {
arr.sum()
}
#[wasm_bindgen]
pub fn product(arr: &WasmArray) -> f64 {
let arr_vec = arr.to_vec();
arr_vec.iter().product()
}
impl WasmArray {
pub(crate) fn percentile(&self, q: f64) -> f64 {
let arr_vec = self.to_vec();
let arr_shape = self.shape();
let inner = Array::from_vec(arr_vec).reshape(&arr_shape);
inner.percentile(q)
}
pub(crate) fn var(&self) -> f64 {
let m = self.mean();
let arr_vec = self.to_vec();
let sum_sq_diff: f64 = arr_vec.iter().map(|&x| (x - m).powi(2)).sum();
sum_sq_diff / (arr_vec.len() as f64)
}
pub(crate) fn std(&self) -> f64 {
self.var().sqrt()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_mean() {
let arr =
WasmArray::from_vec(&[1.0, 2.0, 3.0, 4.0, 5.0], &[5]).expect("from_vec should succeed");
assert_eq!(mean(&arr), 3.0);
}
#[test]
fn test_median() {
let arr =
WasmArray::from_vec(&[1.0, 2.0, 3.0, 4.0, 5.0], &[5]).expect("from_vec should succeed");
assert_eq!(median(&arr), 3.0);
}
#[test]
fn test_variance() {
let arr =
WasmArray::from_vec(&[1.0, 2.0, 3.0, 4.0, 5.0], &[5]).expect("from_vec should succeed");
let var = variance(&arr);
assert!((var - 2.0).abs() < 1e-10);
}
#[test]
fn test_std_dev() {
let arr =
WasmArray::from_vec(&[1.0, 2.0, 3.0, 4.0, 5.0], &[5]).expect("from_vec should succeed");
let std = std_dev(&arr);
assert!((std - 1.4142135623730951).abs() < 1e-10);
}
#[test]
fn test_min_max() {
let arr =
WasmArray::from_vec(&[3.0, 1.0, 4.0, 1.0, 5.0], &[5]).expect("from_vec should succeed");
assert_eq!(minimum(&arr), 1.0);
assert_eq!(maximum(&arr), 5.0);
}
#[test]
fn test_percentile() {
let arr =
WasmArray::from_vec(&[1.0, 2.0, 3.0, 4.0, 5.0], &[5]).expect("from_vec should succeed");
let p25 = compute_percentile(&arr, 0.25).expect("percentile should succeed");
let p75 = compute_percentile(&arr, 0.75).expect("percentile should succeed");
assert!((1.0..=3.0).contains(&p25));
assert!((3.0..=5.0).contains(&p75));
}
#[test]
fn test_sum_product() {
let arr =
WasmArray::from_vec(&[1.0, 2.0, 3.0, 4.0, 5.0], &[5]).expect("from_vec should succeed");
assert_eq!(sum(&arr), 15.0);
assert_eq!(product(&arr), 120.0);
}
}