summaryrefslogtreecommitdiff
path: root/matrix-stdlib/src
diff options
context:
space:
mode:
authorFreya Murphy <freya@freyacat.org>2024-02-27 20:42:10 -0500
committerFreya Murphy <freya@freyacat.org>2024-02-27 20:42:10 -0500
commit508c4fa1b89ee31ca8b005bf146301c14afa7779 (patch)
tree7685f1b624cd2159ce415a0e8d4d262c81799dac /matrix-stdlib/src
parentsort n rand (diff)
downloadmatrix-508c4fa1b89ee31ca8b005bf146301c14afa7779.tar.gz
matrix-508c4fa1b89ee31ca8b005bf146301c14afa7779.tar.bz2
matrix-508c4fa1b89ee31ca8b005bf146301c14afa7779.zip
more mat, sys, and os stdlib functions, better matrix printing, other fixes
Diffstat (limited to 'matrix-stdlib/src')
-rw-r--r--matrix-stdlib/src/math.rs181
-rw-r--r--matrix-stdlib/src/sys.rs195
2 files changed, 350 insertions, 26 deletions
diff --git a/matrix-stdlib/src/math.rs b/matrix-stdlib/src/math.rs
index 3226af5..3f33951 100644
--- a/matrix-stdlib/src/math.rs
+++ b/matrix-stdlib/src/math.rs
@@ -100,7 +100,7 @@ fn mat_get_non_zero_pivot_row(
Ok(())
}
-fn mat_rref(mat: Matrix) -> Result<Matrix> {
+fn mat_rref(mat: Matrix, full_rref: bool) -> Result<Matrix> {
let mut mat = mat;
let Some(start) = mat_find_non_zero_col(&mat) else {
return Ok(mat)
@@ -112,7 +112,8 @@ fn mat_rref(mat: Matrix) -> Result<Matrix> {
if mat.get(pivot_row, col)?.is_zero() {
break
}
- for row in 0..mat.codomain {
+ let min = if full_rref { 0 } else { col };
+ for row in min..mat.codomain {
if row == pivot_row { continue; };
let scale = mat.get(row, col)?;
mat_gauss_row_operation(row, pivot_row, scale, &mut mat)?;
@@ -127,9 +128,20 @@ fn rref(_: VmArgs, args: Vec<Value>) -> Result<Value> {
let mat = match value {
Value::Matrix(m) => m,
Value::List(l) => Matrix::from_list(l.to_vec()).into(),
- _ => return error!("trans must be given a matrix")
+ _ => return error!("rref must be given a matrix")
};
- Ok(Value::Matrix(mat_rref(mat.into_inner())?.into()))
+ Ok(Value::Matrix(mat_rref(mat.into_inner(), true)?.into()))
+}
+
+#[native_func(1)]
+fn mat_ref(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [value] = unpack_args!(args);
+ let mat = match value {
+ Value::Matrix(m) => m,
+ Value::List(l) => Matrix::from_list(l.to_vec()).into(),
+ _ => return error!("ref must be given a matrix")
+ };
+ Ok(Value::Matrix(mat_rref(mat.into_inner(), false)?.into()))
}
fn mat_det(mat: Matrix) -> Result<Value> {
@@ -236,7 +248,7 @@ fn inv(_: VmArgs, args: Vec<Value>) -> Result<Value> {
let mat = mat.into_inner();
let ident = mat_ident(mat.domain);
let joined = mat.join_right(&ident)?;
- let refed = mat_rref(joined)?;
+ let refed = mat_rref(joined, true)?;
let (new_ident, new_inv) = mat_splith(refed);
if new_ident == ident {
@@ -394,6 +406,17 @@ fn complex(_: VmArgs, args: Vec<Value>) -> Result<Value> {
}
#[native_func(1)]
+fn mat(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ use Value as V;
+ let [value] = unpack_args!(args);
+ match value {
+ V::List(l) => Ok(V::Matrix(Matrix::from_list(l.to_vec()).into())),
+ V::Matrix(m) => Ok(V::Matrix(m)),
+ arg => error!("cannot cast {arg} to mat")
+ }
+}
+
+#[native_func(1)]
fn numer(_: VmArgs, args: Vec<Value>) -> Result<Value> {
use Value as V;
let [value] = unpack_args!(args);
@@ -437,6 +460,140 @@ fn im(_: VmArgs, args: Vec<Value>) -> Result<Value> {
}
}
+#[native_func(1)]
+fn cis(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [value] = unpack_args!(args);
+ match value.promote_trig() {
+ Value::Float(f) => Ok(Value::Complex(Complex64::cis(f))),
+ Value::Complex(c) => Ok((Value::Complex(Complex64::cis(c.re)) * Value::Float((-c.im).exp()))?),
+ _ => error!("cis can only take floats")
+ }
+}
+
+#[native_func(1)]
+fn is_finite(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ use Value as V;
+ let [value] = unpack_args!(args);
+ match value {
+ V::Int(_) | V::Ratio(_) => Ok(V::Bool(true)),
+ V::Float(f) => Ok(V::Bool(f.is_finite())),
+ V::Complex(c) => Ok(V::Bool(c.is_finite())),
+ _ => error!("is_finite can only take a valid number")
+ }
+}
+
+#[native_func(1)]
+fn is_infinite(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ use Value as V;
+ let [value] = unpack_args!(args);
+ match value {
+ V::Int(_) | V::Ratio(_) => Ok(V::Bool(false)),
+ V::Float(f) => Ok(V::Bool(f.is_infinite())),
+ V::Complex(c) => Ok(V::Bool(c.is_infinite())),
+ _ => error!("is_infinite can only take a valid number")
+ }
+}
+
+#[native_func(1)]
+fn is_nan(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ use Value as V;
+ let [value] = unpack_args!(args);
+ match value {
+ V::Int(_) | V::Ratio(_) => Ok(V::Bool(false)),
+ V::Float(f) => Ok(V::Bool(f.is_nan())),
+ V::Complex(c) => Ok(V::Bool(c.is_nan())),
+ _ => error!("is_nan can only take a valid number")
+ }
+}
+
+#[native_func(1)]
+fn is_normal(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ use Value as V;
+ let [value] = unpack_args!(args);
+ match value {
+ V::Int(_) | V::Ratio(_) => Ok(V::Bool(true)),
+ V::Float(f) => Ok(V::Bool(f.is_normal())),
+ V::Complex(c) => Ok(V::Bool(c.is_normal())),
+ _ => error!("is_normal can only take a valid number")
+ }
+}
+
+#[native_func(1)]
+fn is_subnormal(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ use Value as V;
+ let [value] = unpack_args!(args);
+ match value {
+ V::Int(_) | V::Ratio(_) => Ok(V::Bool(false)),
+ V::Float(f) => Ok(V::Bool(f.is_subnormal())),
+ _ => error!("is_subnormal can only take subnormal")
+ }
+}
+
+#[native_func(1)]
+fn is_sign_positive(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ use Value as V;
+ let [value] = unpack_args!(args);
+ match value {
+ V::Int(i) => Ok(V::Bool(i > 0)),
+ V::Ratio(r) => Ok(V::Bool(*r.numer() > 0)),
+ V::Float(f) => Ok(V::Bool(f.is_sign_positive())),
+ _ => error!("is_sign_positive can only take a real number")
+ }
+}
+
+#[native_func(1)]
+fn is_sign_negative(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ use Value as V;
+ let [value] = unpack_args!(args);
+ match value {
+ V::Int(i) => Ok(V::Bool(i < 0)),
+ V::Ratio(r) => Ok(V::Bool(*r.numer() < 0)),
+ V::Float(f) => Ok(V::Bool(f.is_sign_negative())),
+ _ => error!("is_sign_negative can only take a real number")
+ }
+}
+
+#[native_func(1)]
+fn is_zero(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ use Value as V;
+ let [value] = unpack_args!(args);
+ match value {
+ V::Int(i) => Ok(V::Bool(i == 0)),
+ V::Ratio(r) => Ok(V::Bool(*r.numer() == 0 && *r.denom() != 0)),
+ V::Float(f) => Ok(V::Bool(f == 0.0)),
+ V::Complex(c) => Ok(V::Bool(c.re == 0.0 && c.im == 0.0)),
+ _ => error!("is_zero can only take a valid number")
+ }
+}
+
+#[native_func(2)]
+fn mat_joinh(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [l, r] = unpack_args!(args);
+ let (l, r) = match (l, r) {
+ (Value::List(l), Value::List(r)) => (Matrix::from_list(l.to_vec()), Matrix::from_list(r.to_vec())),
+ (Value::List(l), Value::Matrix(r)) => (Matrix::from_list(l.to_vec()), r.into_inner()),
+ (Value::Matrix(l), Value::List(r)) => (l.into_inner(), Matrix::from_list(r.to_vec())),
+ (Value::Matrix(l), Value::Matrix(r)) => (l.into_inner(), r.into_inner()),
+ _ => return error!("mat_joinh takes two matrices")
+ };
+ let mat = l.join_right(&r)?;
+ Ok(Value::Matrix(mat.into()))
+}
+
+#[native_func(2)]
+fn mat_joinv(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [l, r] = unpack_args!(args);
+ let (l, r) = match (l, r) {
+ (Value::List(l), Value::List(r)) => (Matrix::from_list(l.to_vec()), Matrix::from_list(r.to_vec())),
+ (Value::List(l), Value::Matrix(r)) => (Matrix::from_list(l.to_vec()), r.into_inner()),
+ (Value::Matrix(l), Value::List(r)) => (l.into_inner(), Matrix::from_list(r.to_vec())),
+ (Value::Matrix(l), Value::Matrix(r)) => (l.into_inner(), r.into_inner()),
+ _ => return error!("mat_joinv takes two matrices")
+ };
+ let mat = l.join_bottom(&r)?;
+ Ok(Value::Matrix(mat.into()))
+}
+
mathr!(floor);
mathr!(ceil);
mathr!(round);
@@ -465,10 +622,13 @@ trigf!(to_radians, rad);
pub fn load(vm: &mut Vm) {
vm.load_global_fn(trans(), "trans");
+ vm.load_global_fn(mat_ref(), "ref");
vm.load_global_fn(rref(), "rref");
vm.load_global_fn(det(), "det");
vm.load_global_fn(ident(), "ident");
vm.load_global_fn(inv(), "inv");
+ vm.load_global_fn(mat_joinh(), "mat_joinh");
+ vm.load_global_fn(mat_joinv(), "mat_joinv");
vm.load_global(Value::Float(PI), "pi");
vm.load_global(Value::Float(TAU), "tau");
@@ -481,6 +641,7 @@ pub fn load(vm: &mut Vm) {
vm.load_global_fn(ratio(), "ratio");
vm.load_global_fn(float(), "float");
vm.load_global_fn(complex(), "complex");
+ vm.load_global_fn(mat(), "mat");
vm.load_global_fn(abs(), "abs");
vm.load_global_fn(sign(), "sign");
vm.load_global_fn(floor(), "floor");
@@ -510,9 +671,19 @@ pub fn load(vm: &mut Vm) {
vm.load_global_fn(atanh(), "atanh");
vm.load_global_fn(deg(), "deg");
vm.load_global_fn(rad(), "rad");
+ vm.load_global_fn(cis(), "cis");
vm.load_global_fn(denom(), "denom");
vm.load_global_fn(numer(), "numer");
vm.load_global_fn(re(), "re");
vm.load_global_fn(im(), "im");
+
+ vm.load_global_fn(is_finite(), "is_finite");
+ vm.load_global_fn(is_infinite(), "is_infinite");
+ vm.load_global_fn(is_nan(), "is_nan");
+ vm.load_global_fn(is_zero(), "is_zero");
+ vm.load_global_fn(is_normal(), "is_normal");
+ vm.load_global_fn(is_subnormal(), "is_subnormal");
+ vm.load_global_fn(is_sign_negative(), "is_sign_negative");
+ vm.load_global_fn(is_sign_positive(), "is_sign_positive");
}
diff --git a/matrix-stdlib/src/sys.rs b/matrix-stdlib/src/sys.rs
index e91e635..d30226f 100644
--- a/matrix-stdlib/src/sys.rs
+++ b/matrix-stdlib/src/sys.rs
@@ -1,7 +1,8 @@
-use std::{process::{exit, Command, Stdio}, env, rc::Rc, io::Read};
+use std::{process::{exit, Command, Stdio, Child}, env, rc::Rc, io::{Read, self}, cell::RefCell, fs::{File, self}, os::fd::FromRawFd, sync::OnceLock, path::PathBuf};
use matrix::{vm::Vm, value::{Value, ValueMap}, unpack_args, Result, gc::Gc};
use matrix_macros::native_func;
+use os_info::Info;
use crate::{VmArgs, error};
#[native_func(1)]
@@ -35,26 +36,7 @@ fn env(_: VmArgs, args: Vec<Value>) -> Result<Value> {
}
}
-#[native_func(2)]
-fn exec(_: VmArgs, args: Vec<Value>) -> Result<Value> {
- let [cmd, args] = unpack_args!(args);
- let (cmd, args) = match (cmd, args) {
- (Value::String(s), Value::List(l)) => (s, l.into_inner()),
- _ => return error!("exec requires a string cmd and string argument list")
- };
- let mut sargs = Vec::new();
- for arg in args {
- let Value::String(arg) = arg else {
- return error!("exec requires a string cmd and string argument list")
- };
- sargs.push(arg.to_string());
- };
- let cmd = Command::new(cmd.to_string())
- .args(sargs)
- .stdin(Stdio::piped())
- .stdout(Stdio::piped())
- .stderr(Stdio::piped())
- .spawn();
+fn exec_impl(cmd: io::Result<Child>) -> Result<Value> {
let mut child = match cmd {
Ok(c) => c,
Err(e) => return error!("error executing command: {e}")
@@ -91,9 +73,180 @@ fn exec(_: VmArgs, args: Vec<Value>) -> Result<Value> {
Ok(Value::Table(res.into()))
}
+#[native_func(2)]
+fn exec(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [cmd, args] = unpack_args!(args);
+ let (cmd, args) = match (cmd, args) {
+ (Value::String(s), Value::List(l)) => (s, l.into_inner()),
+ _ => return error!("exec requires a string cmd and string argument list")
+ };
+ let mut sargs = Vec::new();
+ for arg in args {
+ let Value::String(arg) = arg else {
+ return error!("exec requires a string cmd and string argument list")
+ };
+ sargs.push(arg.to_string());
+ };
+ let cmd = Command::new(cmd.to_string())
+ .args(sargs)
+ .stdin(Stdio::piped())
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped())
+ .spawn();
+
+ exec_impl(cmd)
+}
+
+#[native_func(1)]
+fn system(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [cmd] = unpack_args!(args);
+ let Value::String(cmd) = cmd else {
+ return error!("system requires a full command argument string")
+ };
+ let sh = String::from("/bin/sh");
+ let args = vec!["-c".to_string(), cmd.to_string()];
+
+ let cmd = Command::new(sh)
+ .args(args)
+ .stdin(Stdio::piped())
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped())
+ .spawn();
+
+ exec_impl(cmd)
+}
+
+#[native_func(1)]
+fn systemi(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [cmd] = unpack_args!(args);
+ let Value::String(cmd) = cmd else {
+ return error!("systemi requires a full command argument string")
+ };
+ let sh = String::from("/bin/sh");
+ let args = vec!["-c".to_string(), cmd.to_string()];
+
+ let cmd = Command::new(sh)
+ .args(args)
+ .stdin(Stdio::inherit())
+ .stdout(Stdio::inherit())
+ .stderr(Stdio::inherit())
+ .spawn();
+
+ exec_impl(cmd)
+}
+
+fn stdin() -> Value {
+ let f = unsafe { File::from_raw_fd(0) };
+ Value::File(Rc::new(RefCell::new(f)))
+}
+
+fn stdout() -> Value {
+ let f = unsafe { File::from_raw_fd(1) };
+ Value::File(Rc::new(RefCell::new(f)))
+}
+
+fn stderr() -> Value {
+ let f = unsafe { File::from_raw_fd(2) };
+ Value::File(Rc::new(RefCell::new(f)))
+}
+
+const OS_INFO: OnceLock<Info> = OnceLock::new();
+
+#[native_func(0)]
+fn os_type(_: VmArgs, _: Vec<Value>) -> Result<Value> {
+ Ok(Value::String(OS_INFO.get_or_init(|| os_info::get()).os_type().to_string().into()))
+}
+
+#[native_func(0)]
+fn os_version(_: VmArgs, _: Vec<Value>) -> Result<Value> {
+ Ok(Value::String(OS_INFO.get_or_init(|| os_info::get()).version().to_string().into()))
+}
+
+#[native_func(0)]
+fn os_edition(_: VmArgs, _: Vec<Value>) -> Result<Value> {
+ Ok(Value::String(OS_INFO.get_or_init(|| os_info::get()).edition().unwrap_or("Unknown").into()))
+}
+
+#[native_func(0)]
+fn os_bitness(_: VmArgs, _: Vec<Value>) -> Result<Value> {
+ Ok(Value::String(OS_INFO.get_or_init(|| os_info::get()).bitness().to_string().into()))
+}
+
+#[native_func(0)]
+fn os_arch(_: VmArgs, _: Vec<Value>) -> Result<Value> {
+ Ok(Value::String(OS_INFO.get_or_init(|| os_info::get()).architecture().unwrap_or("Unknown").into()))
+}
+
+#[native_func(0)]
+fn cwd(_: VmArgs, _: Vec<Value>) -> Result<Value> {
+ match env::current_dir() {
+ Ok(v) => Ok(Value::String(v.into_os_string().into_string().unwrap_or(String::new()).into())),
+ Err(e) => error!("cant get cwd: {e}")
+ }
+}
+
+#[native_func(1)]
+fn basename(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [value] = unpack_args!(args);
+ let Value::String(value) = value else {
+ return error!("basename requires a string path")
+ };
+ let path = PathBuf::from(value.to_string());
+ match path.file_name() {
+ Some(p) => Ok(Value::String(p.to_str().unwrap().into())),
+ None => Ok(Value::String(value.into()))
+ }
+}
+
+#[native_func(1)]
+fn dirname(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [value] = unpack_args!(args);
+ let Value::String(value) = value else {
+ return error!("basename requires a string path")
+ };
+ let path = PathBuf::from(value.to_string());
+ let parent = match path.parent() {
+ Some(p) => p,
+ None => path.as_path()
+ };
+ let str = parent.as_os_str().to_str().unwrap();
+ match str {
+ "" => Ok(Value::String(".".into())),
+ s => Ok(Value::String(s.into()))
+ }
+}
+
+#[native_func(1)]
+fn realpath(_: VmArgs, args: Vec<Value>) -> Result<Value> {
+ let [value] = unpack_args!(args);
+ let Value::String(value) = value else {
+ return error!("basename requires a string path")
+ };
+ let path = match fs::canonicalize(value.as_ref()) {
+ Ok(p) => p,
+ Err(e) => return error!("could not get realpath: {e}")
+ };
+ Ok(Value::String(path.to_str().unwrap().into()))
+}
+
+
pub fn load(vm: &mut Vm) {
vm.load_global_fn(sys_exit(), "exit");
vm.load_global_fn(argv(), "argv");
vm.load_global_fn(exec(), "exec");
+ vm.load_global_fn(system(), "system");
+ vm.load_global_fn(systemi(), "systemi");
vm.load_global_fn(env(), "env");
+ vm.load_global(stdin(), "stdin");
+ vm.load_global(stdout(), "stdout");
+ vm.load_global(stderr(), "stderr");
+ vm.load_global_fn(os_type(), "os_type");
+ vm.load_global_fn(os_version(), "os_version");
+ vm.load_global_fn(os_edition(), "os_edition");
+ vm.load_global_fn(os_bitness(), "os_bitness");
+ vm.load_global_fn(os_arch(), "os_arch");
+ vm.load_global_fn(cwd(), "cwd");
+ vm.load_global_fn(basename(), "basename");
+ vm.load_global_fn(dirname(), "dirname");
+ vm.load_global_fn(realpath(), "realpath");
}