From 14b749d2ed379368b8c18c0229c78071b20156c2 Mon Sep 17 00:00:00 2001 From: TriMill Date: Tue, 20 Sep 2022 21:30:13 -0400 Subject: [PATCH] more functions --- complexpr/Cargo.toml | 4 +- complexpr/src/stdlib/iter.rs | 20 +++++++ complexpr/src/stdlib/math.rs | 82 ++++++++++++++++++++++++++- complexpr/src/stdlib/mod.rs | 107 ++++++++++++++++++++++++++++++++++- examples/prime.cxpr | 7 +++ 5 files changed, 216 insertions(+), 4 deletions(-) create mode 100644 examples/prime.cxpr diff --git a/complexpr/Cargo.toml b/complexpr/Cargo.toml index 02ff50c..69a6074 100644 --- a/complexpr/Cargo.toml +++ b/complexpr/Cargo.toml @@ -7,6 +7,6 @@ edition = "2021" num-complex = "0.4.2" num-rational = "0.4.1" num-traits = "0.2.15" -strum_macros = "0.24" -strum = { version = "0.24", features = ["derive"] } +strum_macros = "0.24.3" +strum = { version = "0.24.1", features = ["derive"] } paste = "1.0.9" diff --git a/complexpr/src/stdlib/iter.rs b/complexpr/src/stdlib/iter.rs index c052ee1..0888506 100644 --- a/complexpr/src/stdlib/iter.rs +++ b/complexpr/src/stdlib/iter.rs @@ -7,6 +7,9 @@ pub fn load(env: &mut Environment) { declare_fn!(env, skip, 2); declare_fn!(env, forall, 2); declare_fn!(env, exists, 2); + declare_fn!(env, nth, 2); + declare_fn!(env, last, 1); + declare_fn!(env, void, 1); } fn fn_take(args: Vec) -> Result { @@ -76,3 +79,20 @@ fn fn_exists(args: Vec) -> Result { } Ok(Value::Bool(false)) } + +fn fn_nth(args: Vec) -> Result { + match &args[0] { + Value::Int(n) if *n < 0 => Err("First argument to nth must be positive".into()), + Value::Int(n) => args[1].iter()?.nth(*n as usize).unwrap_or(Ok(Value::Nil)), + _ => Err("First argument to nth must be an integer".into()) + } +} + +fn fn_last(args: Vec) -> Result { + args[0].iter()?.last().unwrap_or(Ok(Value::Nil)) +} + +fn fn_void(args: Vec) -> Result { + for _ in args[0].iter()? {} + Ok(Value::Nil) +} diff --git a/complexpr/src/stdlib/math.rs b/complexpr/src/stdlib/math.rs index 1343924..fcab6f6 100644 --- a/complexpr/src/stdlib/math.rs +++ b/complexpr/src/stdlib/math.rs @@ -1,6 +1,6 @@ use std::cmp::Ordering; -use num_traits::{ToPrimitive, Pow}; +use num_traits::{ToPrimitive, Pow, Signed}; use crate::{value::{Value, Complex, Rational}, RuntimeError, env::Environment, declare_fn}; @@ -25,6 +25,7 @@ pub fn load(env: &mut Environment) { declare_fn!(env, im, 1); declare_fn!(env, min, 2); declare_fn!(env, max, 2); + declare_fn!(env, abs, 1); declare_fn!(env, floor, 1); declare_fn!(env, ceil, 1); declare_fn!(env, round, 1); @@ -43,6 +44,8 @@ pub fn load(env: &mut Environment) { declare_fn!(env, atanh, 1); declare_fn!(env, exp, 1); declare_fn!(env, "log", fn_ln, 1); + declare_fn!(env, is_prime, 1); + declare_fn!(env, factors, 1); } // @@ -98,6 +101,19 @@ fn fn_max(args: Vec) -> Result { } } +fn fn_abs(args: Vec) -> Result { + match args[0] { + Value::Int(n) => Ok(Value::Int(n.abs())), + Value::Float(f) => Ok(Value::Float(f.abs())), + Value::Rational(r) => Ok(Value::Rational(r.abs())), + _ => Err("Argument to floor must be real".into()), + } +} + +// +// Rounding +// + fn fn_floor(args: Vec) -> Result { match args[0] { Value::Int(n) => Ok(Value::Int(n)), @@ -195,3 +211,67 @@ transcendental!{ acosh } transcendental!{ atanh } transcendental!{ exp } transcendental!{ ln } + +// +// Factorization +// + +fn fn_is_prime(args: Vec) -> Result { + let n = match &args[0] { + Value::Int(n) => *n, + _ => return Err("Argument to is_prime must be an integer".into()) + }; + match n { + _ if n <= 1 => Ok(Value::Bool(false)), + 2 | 3 => Ok(Value::Bool(true)), + _ if (n % 2 == 0) || (n % 3 == 0) => Ok(Value::Bool(false)), + _ => { + let mut i = 5; + while i*i <= n { + if n % i == 0 { + return Ok(Value::Bool(false)); + } + i += 2; + if n % i == 0 { + return Ok(Value::Bool(false)); + } + i += 4; + } + Ok(Value::Bool(true)) + } + } +} + +fn fn_factors(args: Vec) -> Result { + let mut n = match &args[0] { + Value::Int(n) => n.abs(), + _ => return Err("Argument to is_prime must be an integer".into()) + }; + if n <= 1 { + return Ok(Value::from(vec![])); + } + let mut factors = vec![]; + while n % 2 == 0 { + factors.push(Value::Int(2)); + n >>= 1; + } + while n % 3 == 0 { + factors.push(Value::Int(3)); + n /= 3; + } + let mut i = 5; + while n != 1 { + while n % i == 0 { + factors.push(Value::Int(i)); + n /= i; + } + i += 2; + while n % i == 0 { + factors.push(Value::Int(i)); + n /= i; + } + i += 4; + } + + Ok(Value::from(factors)) +} diff --git a/complexpr/src/stdlib/mod.rs b/complexpr/src/stdlib/mod.rs index 53a04e1..c04e750 100644 --- a/complexpr/src/stdlib/mod.rs +++ b/complexpr/src/stdlib/mod.rs @@ -21,15 +21,23 @@ macro_rules! declare_fn { pub fn load(env: &mut Environment) { declare_fn!(env, "type", fn_type, 1); declare_fn!(env, type_eq, 1); + declare_fn!(env, copy, 1); declare_fn!(env, str, 1); declare_fn!(env, repr, 1); declare_fn!(env, ord, 1); declare_fn!(env, chr, 1); declare_fn!(env, range, 2); + declare_fn!(env, count_by, 2); declare_fn!(env, has, 2); declare_fn!(env, len, 1); declare_fn!(env, time, 0); - declare_fn!(env, list, 1); + declare_fn!(env, list, 1); + declare_fn!(env, push, 2); + declare_fn!(env, pop, 1); + declare_fn!(env, append, 2); + declare_fn!(env, insert, 3); + declare_fn!(env, remove, 2); + } fn fn_type(args: Vec) -> Result { @@ -44,6 +52,16 @@ fn fn_type_eq(args: Vec) -> Result { })) } +fn fn_copy(args: Vec) -> Result { + Ok(match &args[0] { + Value::List(l) => Value::from(l.borrow().clone()), + Value::Map(m) => Value::from(m.borrow().clone()), + // Value::Func(f) => Value::Func(f.make_copy()) // TODO copy functions + Value::Data(_) => todo!(), + a => a.clone(), + }) +} + fn fn_str(args: Vec) -> Result { Ok(Value::String(args[0].to_string())) } @@ -105,6 +123,26 @@ fn fn_range(args: Vec) -> Result { Ok(Value::Func(mk_range_inner(*start, *end, delta))) } +fn mk_countby_inner(start: i64, delta: i64) -> Func { + let counter = RefCell::new(start); + Func::BuiltinClosure { + arg_count: 0, + func: Rc::new(move |_| { + let res = *counter.borrow(); + *counter.borrow_mut() += delta; + Ok(Value::Int(res)) + }) + } +} + +fn fn_count_by(args: Vec) -> Result { + let (start, delta) = match (&args[0], &args[1]) { + (Value::Int(a), Value::Int(b)) => (a, b), + _ => return Err("Both arguments to count_by must be integers".into()) + }; + Ok(Value::Func(mk_countby_inner(*start, *delta))) +} + fn fn_len(args: Vec) -> Result { Ok(Value::Int(args[0].len().map_err(RuntimeError::new_no_pos)? as i64)) } @@ -127,3 +165,70 @@ fn fn_list(args: Vec) -> Result { for v in a { res.push(v?); } Ok(Value::from(res)) } + +fn fn_push(args: Vec) -> Result { + if let Value::List(l) = &args[0] { + l.as_ref().borrow_mut().push(args[1].clone()); + Ok(Value::Nil) + } else{ + Err("First argument to push must be a list".into()) + } +} + +fn fn_pop(args: Vec) -> Result { + if let Value::List(l) = &args[0] { + l.as_ref().borrow_mut().pop().ok_or("Pop on empty list".into()) + } else{ + Err("First argument to pop must be a list".into()) + } +} + +fn fn_append(args: Vec) -> Result { + match (&args[0], &args[1]) { + (Value::List(a), Value::List(b)) => { + let mut newvals = b.borrow().clone(); + a.borrow_mut().append(&mut newvals); + Ok(Value::Nil) + }, + _ => Err("Both arguments to append must be lists".into()) + } +} + +fn fn_insert(args: Vec) -> Result { + match (&args[0], &args[1]) { + (Value::List(a), Value::Int(i)) => { + if *i < 0 { + return Err(format!("List index {} cannot be negative", i).into()) + } + if *i > a.borrow().len() as i64 { + return Err(format!("List index {} not valid for list of length {}", i, a.borrow().len()).into()) + } + a.borrow_mut().insert(*i as usize, args[2].clone()); + Ok(Value::Nil) + }, + (Value::Map(a), b) => { + Ok(a.borrow_mut().insert(b.clone(), args[3].clone()).unwrap_or(Value::Nil)) + }, + (Value::List(_), _) => Err("Second argument to insert must be an integer when the first is a list".into()), + _ => Err("First argument to insert must be a list or map".into()), + } +} + +fn fn_remove(args: Vec) -> Result { + match (&args[0], &args[1]) { + (Value::List(a), Value::Int(i)) => { + if *i < 0 { + return Err(format!("List index {} cannot be negative", i).into()) + } + if *i >= a.borrow().len() as i64 { + return Err(format!("List index {} not valid for list of length {}", i, a.borrow().len()).into()) + } + Ok(a.borrow_mut().remove(*i as usize)) + }, + (Value::Map(a), b) => { + Ok(a.borrow_mut().remove(b).unwrap_or(Value::Nil)) + }, + (Value::List(_), _) => Err("Second argument to remove must be an integer when the first is a list".into()), + _ => Err("First argument to remove must be a list or map".into()), + } +} diff --git a/examples/prime.cxpr b/examples/prime.cxpr new file mode 100644 index 0000000..cd5b236 --- /dev/null +++ b/examples/prime.cxpr @@ -0,0 +1,7 @@ +# compute the primorial of n - the product of all primes <= n +fn primorial(n) { + return range(2,n+1) |? is_prime |// fn(x,y) { return x*y; }; +} + +println(primorial(10)); # 2*3*5*7 = 210 +println(primorial(20)); # 2*3*5*7*11*13*17*19 = 9699690