diff options
Diffstat (limited to 'matrix-lang/src/value/comp.rs')
-rw-r--r-- | matrix-lang/src/value/comp.rs | 373 |
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}")), + } + } +} |