summaryrefslogtreecommitdiff
path: root/matrix-lang/src/value/comp.rs
diff options
context:
space:
mode:
Diffstat (limited to 'matrix-lang/src/value/comp.rs')
-rw-r--r--matrix-lang/src/value/comp.rs373
1 files changed, 373 insertions, 0 deletions
diff --git a/matrix-lang/src/value/comp.rs b/matrix-lang/src/value/comp.rs
new file mode 100644
index 0000000..3557927
--- /dev/null
+++ b/matrix-lang/src/value/comp.rs
@@ -0,0 +1,373 @@
+use std::{ops::{Add, Sub, Mul, Div, Shl, Shr, BitOr, BitAnd, BitXor, Neg, Not}, cmp::Ordering};
+use crate::prelude::*;
+
+fn ratio_to_f64(r: Rational64) -> f64 {
+ *r.numer() as f64 / *r.denom() as f64
+}
+
+macro_rules! error {
+ ($($arg:tt)*) => {
+ exception!(VALUE_EXCEPTION, $($arg)*)
+ };
+}
+
+///
+/// MATH OPERATIONS
+///
+
+fn ipow(n: i64, d: i64, p: i64) -> Result<(i64, i64)> {
+ Ok(match (n, d, p) {
+ (0, _, 0) => Err(error!("cannot exponent 0 ** 0"))?,
+ (0, _, _) => (0, 1),
+ (_, _, 0) => (1, 1),
+ (1, 1, _) => (1, 1),
+ (n, d, p) if p < 0 => (d.pow((-p) as u32), n.pow((-p) as u32)),
+ (n, d, p) => (n.pow(p as u32), d.pow(p as u32)),
+ })
+}
+
+fn promote(a: Value, b: Value) -> (Value, Value) {
+ use Value as V;
+ match (&a, &b) {
+ (V::Int(x), V::Ratio(..)) => (V::Ratio((*x).into()), b),
+ (V::Int(x), V::Float(..)) => (V::Float(*x as f64), b),
+ (V::Int(x), V::Complex(..)) => (V::Complex((*x as f64).into()), b),
+ (V::Ratio(x), V::Float(..)) => (V::Float(ratio_to_f64(*x)), b),
+ (V::Ratio(x), V::Complex(..)) => (V::Complex(ratio_to_f64(*x).into()), b),
+ (V::Float(x), V::Complex(..)) => (V::Complex((*x).into()), b),
+ (V::Ratio(..), V::Int(y)) => (a, V::Ratio((*y).into())),
+ (V::Float(..), V::Int(y)) => (a, V::Float(*y as f64)),
+ (V::Complex(..), V::Int(y)) => (a, V::Complex((*y as f64).into())),
+ (V::Float(..), V::Ratio(y)) => (a, V::Float(ratio_to_f64(*y))),
+ (V::Complex(..), V::Ratio(y)) => (a, V::Complex(ratio_to_f64(*y).into())),
+ (V::Complex(..), V::Float(y)) => (a, V::Complex((*y).into())),
+ (V::List(l1), V::List(l2)) if l1.len() > 0 && l2.len() > 0
+ => (V::Matrix(Matrix::from_list(l1.to_vec()).into()), V::Matrix(Matrix::from_list(l2.to_vec()).into())),
+ (_, V::List(l)) if l.len() > 0
+ => (a, V::Matrix(Matrix::from_list(l.to_vec()).into())),
+ (V::List(l), _) if l.len() > 0
+ => (V::Matrix(Matrix::from_list(l.to_vec()).into()), b),
+ _ => (a, b),
+ }
+}
+
+
+impl Add for Value {
+ type Output = Result<Self>;
+ fn add(self, rhs: Self) -> Self::Output {
+ use Value::*;
+ match promote(self, rhs) {
+ (Int(x), Int(y)) => Ok(Int(x + y)),
+ (Float(x), Float(y)) => Ok(Float(x + y)),
+ (Ratio(x), Ratio(y)) => Ok(Ratio(x + y)),
+ (Complex(x), Complex(y)) => Ok(Complex(x + y)),
+ (Matrix(x), Matrix(y)) => Ok(Matrix((x + y)?.into())),
+ (Matrix(x), r) => Ok(Matrix(x.increment(r)?.into())),
+ (l, Matrix(y)) => Ok(Matrix(y.increment(l)?.into())),
+ (String(str), value) => Ok(String(Rc::from(
+ format!("{str}{value}")
+ ))),
+ (value, String(str)) => Ok(String(Rc::from(
+ format!("{value}{str}")
+ ))),
+ (List(mut l1), List(l2)) => {
+ l1.extend_from_slice(&l2);
+ Ok(List(l1))
+ },
+ (l, r) => Err(error!("cannot add {l:?} + {r:?}"))
+ }
+ }
+}
+
+impl Sub for Value {
+ type Output = Result<Self>;
+ fn sub(self, rhs: Self) -> Self::Output {
+ use Value::*;
+ match promote(self, rhs) {
+ (Int(x), Int(y)) => Ok(Int(x - y)),
+ (Float(x), Float(y)) => Ok(Float(x - y)),
+ (Ratio(x), Ratio(y)) => Ok(Ratio(x - y)),
+ (Complex(x), Complex(y)) => Ok(Complex(x - y)),
+ (Matrix(x), Matrix(y)) => Ok(Matrix((x - y)?.into())),
+ (Matrix(x), r) => Ok(Matrix(x.decrement(r)?.into())),
+ (l, r) => Err(error!("cannot subtract {l:?} - {r:?}"))
+ }
+ }
+}
+
+impl Mul for Value {
+ type Output = Result<Self>;
+ fn mul(self, rhs: Value) -> Self::Output {
+ use Value::*;
+ match promote(self, rhs) {
+ (Int(x), Int(y)) => Ok(Int(x * y)),
+ (Float(x), Float(y)) => Ok(Float(x * y)),
+ (Ratio(x), Ratio(y)) => Ok(Ratio(x * y)),
+ (Complex(x), Complex(y)) => Ok(Complex(x * y)),
+ (Matrix(x), Matrix(y)) => Ok(Matrix((x * y)?.into())),
+ (Matrix(x), r) => Ok(Matrix(x.scale(r)?.into())),
+ (l, Matrix(y)) => Ok(Matrix(y.scale(l)?.into())),
+ (l, r) => Err(error!("cannot multiply {l:?} * {r:?}"))
+ }
+ }
+}
+
+impl Div for Value {
+ type Output = Result<Self>;
+ fn div(self, rhs: Value) -> Self::Output {
+ use Value::*;
+ match promote(self, rhs) {
+ (Int(_), Int(0)) => Err(error!("cannot divide by zero")),
+ (Int(x), Int(y)) => Ok(Ratio(Rational64::new(x, y))),
+ (Float(x), Float(y)) => Ok(Float(x / y)),
+ (Ratio(x), Ratio(y)) => Ok(Ratio(x / y)),
+ (Complex(x), Complex(y)) => Ok(Complex(x / y)),
+ (l, r) => Err(error!("cannot divide {l:?} / {r:?}"))
+ }
+ }
+}
+
+impl BitOr for Value {
+ type Output = Result<Self>;
+ fn bitor(self, rhs: Value) -> Self::Output {
+ use Value::*;
+ match promote(self, rhs) {
+ (Int(x), Int(y)) => Ok(Int(x | y)),
+ (l, r) => Err(error!("cannot bitwise or {l:?} | {r:?}"))
+ }
+ }
+}
+
+impl BitAnd for Value {
+ type Output = Result<Self>;
+ fn bitand(self, rhs: Value) -> Self::Output {
+ use Value::*;
+ match promote(self, rhs) {
+ (Int(x), Int(y)) => Ok(Int(x & y)),
+ (l, r) => Err(error!("cannot bitwise and {l:?} & {r:?}"))
+ }
+ }
+}
+
+impl BitXor for Value {
+ type Output = Result<Self>;
+ fn bitxor(self, rhs: Value) -> Self::Output {
+ use Value::*;
+ match promote(self, rhs) {
+ (Int(x), Int(y)) => Ok(Int(x ^ y)),
+ (l, r) => Err(error!("cannot bitwise xor {l:?} ^ {r:?}"))
+ }
+ }
+}
+
+impl Shl for Value {
+ type Output = Result<Self>;
+ fn shl(self, rhs: Value) -> Self::Output {
+ use Value::*;
+ match promote(self, rhs) {
+ (Int(x), Int(y)) => Ok(Int(x << y)),
+ (l, r) => Err(error!("cannot bitwise shift left {l:?} << {r:?}"))
+ }
+ }
+}
+
+impl Shr for Value {
+ type Output = Result<Self>;
+ fn shr(self, rhs: Value) -> Self::Output {
+ use Value::*;
+ match promote(self, rhs) {
+ (Int(x), Int(y)) => Ok(Int(x >> y)),
+ (l, r) => Err(error!("cannot bitwise shift right {l:?} >> {r:?}"))
+ }
+ }
+}
+
+impl PartialEq for Value {
+ fn eq(&self, other: &Self) -> bool {
+ use Value::*;
+ match (self, other) {
+ (Nil, Nil) => true,
+ (Bool(a), Bool(b)) => a == b,
+ (Int(a), Int(b)) => *a == *b,
+ (Ratio(a), Ratio(b)) => *a == *b,
+ (Float(a), Float(b)) => *a == *b,
+ (Complex(a), Complex(b)) => *a == *b,
+ (Int(a), Ratio(b)) => Rational64::from(*a) == *b,
+ (Ratio(a), Int(b)) => *a == Rational64::from(*b),
+ (Int(a), Float(b)) => *a as f64 == *b,
+ (Float(a), Int(b)) => *a == *b as f64,
+ (Int(a), Complex(b)) => Complex64::from(*a as f64) == *b,
+ (Complex(a), Int(b)) => *a == Complex64::from(*b as f64),
+ (Ratio(a), Float(b)) => ratio_to_f64(*a) == *b,
+ (Float(a), Ratio(b)) => *a == ratio_to_f64(*b),
+ (Ratio(a), Complex(b)) => Complex64::from(ratio_to_f64(*a)) == *b,
+ (Complex(a), Ratio(b)) => *a == Complex64::from(ratio_to_f64(*b)),
+ (Float(a), Complex(b)) => Complex64::from(*a) == *b,
+ (Complex(a), Float(b)) => *a == Complex64::from(*b),
+ (String(a), String(b)) => *a == *b,
+ (List(a), List(b)) => *a == *b,
+ (Matrix(a), Matrix(b)) => a == b,
+ _ => false,
+ }
+ }
+}
+
+impl PartialOrd for Value {
+ fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+ use Value::*;
+ match (self, other) {
+ (Nil, Nil) => Some(Ordering::Equal),
+ (Bool(a), Bool(b)) => a.partial_cmp(b),
+ (Int(a), Int(b)) => a.partial_cmp(b),
+ (Ratio(a), Ratio(b)) => a.partial_cmp(b),
+ (Float(a), Float(b)) => a.partial_cmp(b),
+ (Int(a), Ratio(b)) => Rational64::from(*a).partial_cmp(b),
+ (Ratio(a), Int(b)) => a.partial_cmp(&Rational64::from(*b)),
+ (Int(a), Float(b)) => (*a as f64).partial_cmp(b),
+ (Float(a), Int(b)) => a.partial_cmp(&(*b as f64)),
+ (Ratio(a), Float(b)) => ratio_to_f64(*a).partial_cmp(b),
+ (Float(a), Ratio(b)) => a.partial_cmp(&ratio_to_f64(*b)),
+ (String(a), String(b)) => a.partial_cmp(b),
+ (List(a), List(b)) => a.partial_cmp(b),
+ (Matrix(a), Matrix(b)) => a.values.partial_cmp(&b.values),
+ _ => None,
+ }
+ }
+}
+
+impl Neg for Value {
+ type Output = Value;
+
+ fn neg(self) -> Self::Output {
+ use Value::*;
+ match self {
+ Bool(b) => Bool(!b),
+ Int(i) => Int(-i),
+ Float(f) => Float(-f),
+ Ratio(r) => Ratio(-r),
+ Complex(c) => Complex(-c),
+ _ => return Float(f64::NAN)
+ }
+ }
+}
+
+impl Not for Value {
+ type Output = bool;
+
+ fn not(self) -> Self::Output {
+ use Value as V;
+ match self {
+ V::Nil => true,
+ V::Bool(b) => !b,
+ V::Int(i) => i == 0,
+ V::Float(f) => f == 0.0,
+ V::Ratio(r) => *(r.numer()) == 0 || *(r.denom()) == 0,
+ V::Complex(c) => !c.is_normal(),
+ V::Regex(_) => false,
+ V::List(_) => false,
+ V::Matrix(_) => false,
+ V::Table(_) => false,
+ V::String(s) => s.as_ref() == "",
+ V::Function(_) => false,
+ V::Iter(_) => false,
+ V::Range(_) => false,
+ V::File(_) => false,
+ V::Exception(_) => false,
+ }
+ }
+}
+
+
+impl Value {
+
+ pub fn modulo(self, rhs: Value) -> Result<Self> {
+ use Value::*;
+ match promote(self, rhs) {
+ (Int(x), Int(y)) => Ok(Int(x % y)),
+ (Float(x), Float(y)) => Ok(Float(x % y)),
+ (Ratio(x), Ratio(y)) => Ok(Ratio(x % y)),
+ (Complex(x), Complex(y)) => Ok(Complex(x % y)),
+ (l, r) => Err(error!("cannot modulo: {l:?} % {r:?}"))
+ }
+ }
+
+ pub fn pow(self, rhs: Value) -> Result<Self> {
+ use Value::*;
+ if let (Ratio(x), Int(y)) = (&self, &rhs) {
+ return Ok(Ratio(ipow(*(*x).numer(), *(*x).denom(), *y)?.into()));
+ }
+ match promote(self, rhs) {
+ (Int(x), Int(y)) => Ok(Ratio(ipow(x, 1, y)?.into())),
+ (Float(x), Float(y)) => Ok(Float(x.powf(y))),
+ (Ratio(x), Ratio(y)) => Ok(Float(ratio_to_f64(x).powf(ratio_to_f64(y)))),
+ (Complex(x), Complex(y)) => Ok(Complex(x.powc(y))),
+ (l, r) => Err(error!("cannot exponent: {l:?} ** {r:?}"))
+ }
+ }
+
+ pub fn floaty(self) -> Self {
+ use Value as V;
+ match self {
+ V::Int(i) => V::Float(i as f64),
+ V::Ratio(r) => V::Float(ratio_to_f64(r)),
+ a => a
+ }
+ }
+
+ pub fn is_zero(&self) -> bool {
+ use Value as V;
+ match self {
+ V::Int(i) => *i == 0,
+ V::Float(f) => *f == 0.0 || *f == -0.0,
+ V::Ratio(r) => *r.numer() == 0,
+ _ => false,
+ }
+ }
+
+ pub fn binary_op(op: BinaryOp, lhs: Value, rhs: Value) -> Result<Self> {
+ use BinaryOp::*;
+ match op {
+ Add => lhs + rhs,
+ Subtract => lhs - rhs,
+ Multiply => lhs * rhs,
+ Divide => lhs / rhs,
+ Modulo => lhs.modulo(rhs),
+ Power => lhs.pow(rhs),
+ BitwiseAnd => lhs & rhs,
+ BitwiseOr => lhs | rhs,
+ BitwiseXor => lhs ^ rhs,
+ BitwiseShiftLeft => lhs << rhs,
+ BitwiseShiftRight => lhs >> rhs,
+ Equals => Ok(Self::Bool(lhs == rhs)),
+ NotEquals => Ok(Self::Bool(lhs != rhs)),
+ GreaterEquals => Ok(Self::Bool(lhs >= rhs)),
+ LessEquals => Ok(Self::Bool(lhs <= rhs)),
+ GreaterThan => Ok(Self::Bool(lhs > rhs)),
+ LessThan => Ok(Self::Bool(lhs < rhs)),
+ Range | RangeEq => {
+ let Value::Int(lhs) = lhs else {
+ return Err(error!("range can only take [Int]'s"))
+ };
+ let Value::Int(rhs) = rhs else {
+ return Err(error!("range can only take [Int]'s"))
+ };
+ Ok(Self::Range(Rc::new((lhs, rhs, op == RangeEq))))
+ },
+ }
+ }
+
+ pub fn unary_op(op: UnaryOp, val: Value) -> Value {
+ use UnaryOp::*;
+ match op {
+ Negate => -val,
+ Not => Self::Bool(!val),
+ }
+ }
+
+ pub fn to_regex(value: &str) -> Result<Self> {
+ match Regex::new(value) {
+ Ok(r) => Ok(Self::Regex(r.into())),
+ Err(e) => Err(error!("{e}")),
+ }
+ }
+}