diff --git a/examples/numerics.rs b/examples/numerics.rs index 780a688..1c3d216 100644 --- a/examples/numerics.rs +++ b/examples/numerics.rs @@ -6,7 +6,7 @@ fn main() { println!("Example 1:\nRecycled add of Vec and Vec"); println!("{} + {}", x, y); - println!("{}\n", x + y); + println!("{}\n", (x + y).unwrap()); use r::object::OptionNA::*; // Some() and NA let x = Vector::from(vec![Some(5_i32), NA, Some(1_i32)]); @@ -14,5 +14,5 @@ fn main() { println!("Example 2:\nAdd of Vec and Vec"); println!("{} + {}", x, y); - println!("{}\n", x + y); + println!("{}\n", (x + y).unwrap()); } diff --git a/src/CHANGELOG.md b/src/CHANGELOG.md index 60deb53..d17608e 100644 --- a/src/CHANGELOG.md +++ b/src/CHANGELOG.md @@ -5,6 +5,14 @@ * Named vectors were added and can e.g. be constructed via `[a = 1, b = 2]` * The `is_null()` primitive was added * Setting a list value to `null` actually sets it to `null` and does not remove it. +* Stricter recycling rule are enforced (@98): + Vectorized operations on two vectors `v1` and `v2` now requires either of: + * One of the vectors has length 1 and the other vector's length is not zero. + * The vectors have the same length. + + Otherwise an error is thrown. +* The `typeof()` primitive was added +* Type stability for numeric operations (@69) ## Internals diff --git a/src/callable/builtins.rs b/src/callable/builtins.rs index cb84727..ee64df9 100644 --- a/src/callable/builtins.rs +++ b/src/callable/builtins.rs @@ -55,6 +55,7 @@ pub static BUILTIN: LazyLock>> = LazyLock ("runif", Box::new(PrimitiveRunif) as Box), ("substitute", Box::new(PrimitiveSubstitute) as Box), ("sum", Box::new(PrimitiveSum) as Box), + ("typeof", Box::new(PrimitiveTypeOf) as Box), // builtins end ]) }); diff --git a/src/callable/primitive/mod.rs b/src/callable/primitive/mod.rs index 3e69895..5d68f2e 100644 --- a/src/callable/primitive/mod.rs +++ b/src/callable/primitive/mod.rs @@ -32,3 +32,5 @@ mod length; pub use length::PrimitiveLength; mod is_null; pub use is_null::PrimitiveIsNull; +mod type_reflection; +pub use type_reflection::PrimitiveTypeOf; diff --git a/src/callable/primitive/type_reflection.rs b/src/callable/primitive/type_reflection.rs new file mode 100644 index 0000000..ccbf9e6 --- /dev/null +++ b/src/callable/primitive/type_reflection.rs @@ -0,0 +1,46 @@ +use r_derive::*; + +use crate::callable::core::*; +use crate::formals; +use crate::lang::*; +use crate::object::*; + +#[doc(alias = "typeof")] +#[builtin(sym = "typeof")] +#[derive(Debug, Clone, PartialEq)] +pub struct PrimitiveTypeOf; + +formals!(PrimitiveTypeOf, "(x,)"); + +impl Callable for PrimitiveTypeOf { + fn call_matched(&self, args: List, _ellipsis: List, stack: &mut CallStack) -> EvalResult { + let mut args = Obj::List(args); + let x = args.try_get_named("x")?.force(stack)?; + + let t = match x { + Obj::Null => "null", + Obj::Vector(v) => match v { + Vector::Character(_) => "character", + Vector::Integer(_) => "integer", + Vector::Double(_) => "double", + Vector::Logical(_) => "logical", + }, + Obj::List(_) => "list", + Obj::Expr(_) => "expression", + Obj::Promise(..) => "promise", + Obj::Function(..) => "function", + Obj::Environment(..) => "environment", + }; + EvalResult::Ok(Obj::Vector(Vector::Character(vec![t.to_string()].into()))) + } +} + +#[cfg(test)] + +mod tests { + use crate::{r, r_expect}; + #[test] + fn character() { + r_expect!(typeof("a") == "character") + } +} diff --git a/src/error.rs b/src/error.rs index 4499f9d..4a64b08 100644 --- a/src/error.rs +++ b/src/error.rs @@ -30,6 +30,8 @@ pub enum Error { CannotBeCoercedTo(&'static str), InvalidRange, + NonRecyclableLengths(usize, usize), + // destructuring CannotBeDestructuredIntoList, @@ -76,6 +78,9 @@ impl Error { Error::NotInterpretableAsLogical => { "argument is not interpretable as logical".to_string() } + Error::NonRecyclableLengths(l, r) => { + format!("Vector lengths {l} and {r} cannot be recycled.") + } Error::ConditionIsNotScalar => "the condition has length > 1".to_string(), Error::CannotBeCoercedToCharacter => { "object cannot be coerced to type 'character'".to_string() diff --git a/src/lang.rs b/src/lang.rs index 9e440bf..c37c92e 100644 --- a/src/lang.rs +++ b/src/lang.rs @@ -108,20 +108,20 @@ impl Obj { Obj::List(mut l) => { match value.clone() { Obj::List(r) => { - l.assign(r); + l.assign(r)?; } Obj::Vector(r) => match r { Vector::Integer(r) => { - l.assign(r); + l.assign(r)?; } Vector::Character(r) => { - l.assign(r); + l.assign(r)?; } Vector::Logical(r) => { - l.assign(r); + l.assign(r)?; } Vector::Double(r) => { - l.assign(r); + l.assign(r)?; } }, _ => return Err(err.into()), @@ -428,9 +428,9 @@ fn display_list(x: &List, f: &mut fmt::Formatter<'_>, bc: Option) -> fmt impl std::ops::Add for Obj { type Output = EvalResult; - fn add(self, rhs: Self) -> Self::Output { - match (self.as_double()?, rhs.as_double()?) { - (Obj::Vector(l), Obj::Vector(r)) => Ok(Obj::Vector(l + r)), + fn add(self, rhs: Self) -> EvalResult { + match (self, rhs) { + (Obj::Vector(l), Obj::Vector(r)) => Ok(Obj::Vector((l + r)?)), _ => internal_err!(), } } @@ -439,9 +439,9 @@ impl std::ops::Add for Obj { impl std::ops::Sub for Obj { type Output = EvalResult; - fn sub(self, rhs: Self) -> Self::Output { - match (self.as_double()?, rhs.as_double()?) { - (Obj::Vector(l), Obj::Vector(r)) => Ok(Obj::Vector(l - r)), + fn sub(self, rhs: Self) -> EvalResult { + match (self, rhs) { + (Obj::Vector(l), Obj::Vector(r)) => Ok(Obj::Vector((l - r)?)), _ => internal_err!(), } } @@ -451,8 +451,8 @@ impl std::ops::Neg for Obj { type Output = EvalResult; fn neg(self) -> Self::Output { - match self.as_double()? { - Obj::Vector(x) => Ok(Obj::Vector(-x)), + match self { + Obj::Vector(x) => Ok(Obj::Vector((-x)?)), _ => internal_err!(), } } @@ -462,8 +462,8 @@ impl std::ops::Not for Obj { type Output = EvalResult; fn not(self) -> Self::Output { - match self.as_logical()? { - Obj::Vector(x) => Ok(Obj::Vector(!x)), + match self { + Obj::Vector(x) => Ok(Obj::Vector((!x)?)), _ => internal_err!(), } } @@ -473,8 +473,8 @@ impl std::ops::Mul for Obj { type Output = EvalResult; fn mul(self, rhs: Self) -> Self::Output { - match (self.as_double()?, rhs.as_double()?) { - (Obj::Vector(l), Obj::Vector(r)) => Ok(Obj::Vector(l * r)), + match (self, rhs) { + (Obj::Vector(l), Obj::Vector(r)) => Ok(Obj::Vector((l * r)?)), _ => internal_err!(), } } @@ -484,8 +484,8 @@ impl std::ops::Div for Obj { type Output = EvalResult; fn div(self, rhs: Self) -> Self::Output { - match (self.as_double()?, rhs.as_double()?) { - (Obj::Vector(l), Obj::Vector(r)) => Ok(Obj::Vector(l / r)), + match (self, rhs) { + (Obj::Vector(l), Obj::Vector(r)) => Ok(Obj::Vector((l / r)?)), _ => internal_err!(), } } @@ -495,8 +495,8 @@ impl super::object::Pow for Obj { type Output = EvalResult; fn power(self, rhs: Self) -> Self::Output { - match (self.as_double()?, rhs.as_double()?) { - (Obj::Vector(l), Obj::Vector(r)) => Ok(Obj::Vector(l.power(r))), + match (self, rhs) { + (Obj::Vector(l), Obj::Vector(r)) => Ok(Obj::Vector(l.power(r)?)), _ => internal_err!(), } } @@ -506,8 +506,8 @@ impl std::ops::Rem for Obj { type Output = EvalResult; fn rem(self, rhs: Self) -> Self::Output { - match (self.as_double()?, rhs.as_double()?) { - (Obj::Vector(l), Obj::Vector(r)) => Ok(Obj::Vector(l % r)), + match (self, rhs) { + (Obj::Vector(l), Obj::Vector(r)) => Ok(Obj::Vector((l % r)?)), _ => internal_err!(), } } @@ -517,8 +517,8 @@ impl std::ops::BitOr for Obj { type Output = EvalResult; fn bitor(self, rhs: Self) -> Self::Output { - match (self.as_logical()?, rhs.as_logical()?) { - (Obj::Vector(l), Obj::Vector(r)) => Ok(Obj::Vector(l | r)), + match (self, rhs) { + (Obj::Vector(l), Obj::Vector(r)) => Ok(Obj::Vector((l | r)?)), _ => internal_err!(), } } @@ -528,8 +528,8 @@ impl std::ops::BitAnd for Obj { type Output = EvalResult; fn bitand(self, rhs: Self) -> Self::Output { - match (self.as_logical()?, rhs.as_logical()?) { - (Obj::Vector(l), Obj::Vector(r)) => Ok(Obj::Vector(l & r)), + match (self, rhs) { + (Obj::Vector(l), Obj::Vector(r)) => Ok(Obj::Vector((l & r)?)), _ => internal_err!(), } } @@ -539,28 +539,28 @@ impl VecPartialCmp for Obj { type Output = EvalResult; fn vec_gt(self, rhs: Self) -> Self::Output { match (self.as_vector()?, rhs.as_vector()?) { - (Obj::Vector(l), Obj::Vector(r)) => Ok(Obj::Vector(l.vec_gt(r))), + (Obj::Vector(l), Obj::Vector(r)) => Ok(Obj::Vector(l.vec_gt(r)?)), _ => internal_err!(), } } fn vec_gte(self, rhs: Self) -> Self::Output { match (self.as_vector()?, rhs.as_vector()?) { - (Obj::Vector(l), Obj::Vector(r)) => Ok(Obj::Vector(l.vec_gte(r))), + (Obj::Vector(l), Obj::Vector(r)) => Ok(Obj::Vector(l.vec_gte(r)?)), _ => internal_err!(), } } fn vec_lt(self, rhs: Self) -> Self::Output { match (self.as_vector()?, rhs.as_vector()?) { - (Obj::Vector(l), Obj::Vector(r)) => Ok(Obj::Vector(l.vec_lt(r))), + (Obj::Vector(l), Obj::Vector(r)) => Ok(Obj::Vector(l.vec_lt(r)?)), _ => internal_err!(), } } fn vec_lte(self, rhs: Self) -> Self::Output { match (self.as_vector()?, rhs.as_vector()?) { - (Obj::Vector(l), Obj::Vector(r)) => Ok(Obj::Vector(l.vec_lte(r))), + (Obj::Vector(l), Obj::Vector(r)) => Ok(Obj::Vector(l.vec_lte(r)?)), _ => internal_err!(), } } @@ -572,7 +572,7 @@ impl VecPartialCmp for Obj { (lhs @ Obj::Function(..), rhs @ Obj::Function(..)) => Ok((lhs == rhs).into()), (lhs @ Obj::Environment(_), rhs @ Obj::Environment(_)) => Ok((lhs == rhs).into()), (lhs, rhs) => match (lhs.as_vector()?, rhs.as_vector()?) { - (Obj::Vector(l), Obj::Vector(r)) => Ok(Obj::Vector(l.vec_eq(r))), + (Obj::Vector(l), Obj::Vector(r)) => Ok(Obj::Vector(l.vec_eq(r)?)), _ => internal_err!(), }, } @@ -585,7 +585,7 @@ impl VecPartialCmp for Obj { (lhs @ Obj::Function(..), rhs @ Obj::Function(..)) => Ok((lhs != rhs).into()), (lhs @ Obj::Environment(_), rhs @ Obj::Environment(_)) => Ok((lhs != rhs).into()), (lhs, rhs) => match (lhs.as_vector()?, rhs.as_vector()?) { - (Obj::Vector(l), Obj::Vector(r)) => Ok(Obj::Vector(l.vec_neq(r))), + (Obj::Vector(l), Obj::Vector(r)) => Ok(Obj::Vector(l.vec_neq(r)?)), _ => internal_err!(), }, } @@ -1271,7 +1271,7 @@ mod test { r_expect! {{" f = fn(x) x a = f((y = 2)) - a == 2 + a == 2 "}} } @@ -1280,7 +1280,7 @@ mod test { r_expect! {{" f = fn(x) x f((y = 2)) - y == 2 + y == 2 "}} } diff --git a/src/object/vector/coercion.rs b/src/object/vector/coercion.rs index 934e5d5..705c30e 100644 --- a/src/object/vector/coercion.rs +++ b/src/object/vector/coercion.rs @@ -328,20 +328,6 @@ pub trait CommonCmp: Sized { fn into_common(self) -> (Self::Common, Self::Common); } -impl CommonCmp for (OptionNA, OptionNA) -where - (U, V): CommonCmp>, -{ - type Common = OptionNA; - fn into_common(self) -> (OptionNA, OptionNA) { - use OptionNA::*; - match self { - (Some(l), Some(r)) => (l, r).into_common(), - _ => (NA, NA), - } - } -} - #[macro_export] macro_rules! register { ( diff --git a/src/object/vector/core.rs b/src/object/vector/core.rs index 8784799..1966e45 100644 --- a/src/object/vector/core.rs +++ b/src/object/vector/core.rs @@ -182,16 +182,16 @@ impl Vector { Error::Other("Cannot assign to a vector from a different type".to_string()).into(); match (self, other) { (Vector::Double(l), Obj::Vector(Vector::Double(r))) => { - Ok(Obj::Vector(Vector::from(l.assign(r)))) + Ok(Obj::Vector(Vector::from(l.assign(r)?))) } (Vector::Integer(l), Obj::Vector(Vector::Integer(r))) => { - Ok(Obj::Vector(Vector::from(l.assign(r)))) + Ok(Obj::Vector(Vector::from(l.assign(r)?))) } (Vector::Logical(l), Obj::Vector(Vector::Logical(r))) => { - Ok(Obj::Vector(Vector::from(l.assign(r)))) + Ok(Obj::Vector(Vector::from(l.assign(r)?))) } (Vector::Character(l), Obj::Vector(Vector::Character(r))) => { - Ok(Obj::Vector(Vector::from(l.assign(r)))) + Ok(Obj::Vector(Vector::from(l.assign(r)?))) } _ => Err(err), } @@ -311,6 +311,12 @@ impl TryInto for Vector { } } +impl From for Result { + fn from(x: Vector) -> Self { + Ok(x) + } +} + impl From>> for Vector { fn from(x: CowObj>) -> Self { Vector::Character(x.into()) @@ -654,119 +660,120 @@ impl std::ops::Not for Logical { } impl std::ops::Neg for Vector { - type Output = Vector; + type Output = Result; fn neg(self) -> Self::Output { use Vector::*; match self { - Double(x) => Double(x.neg()), - Integer(x) => Integer(x.neg()), - Logical(x) => Integer(x.neg()), + Double(x) => x.neg().map(|x| x.into()), + Integer(x) => x.neg().map(|x| x.into()), + Logical(x) => x.neg().map(|x| x.into()), _ => todo!(), } } } impl std::ops::Not for Vector { - type Output = Vector; + type Output = Result; fn not(self) -> Self::Output { use Vector::*; match self { - Logical(x) => (!x).into(), + Logical(x) => (!x).map(|x| x.into()), _ => todo!(), } } } impl std::ops::Add for Vector { - type Output = Vector; - fn add(self, rhs: Self) -> Self::Output { + type Output = Result; + fn add(self, other: Self) -> Self::Output { use Vector::*; - match (self, rhs) { - (Double(l), Double(r)) => (l + r).into(), - (Double(l), Integer(r)) => (l + r).into(), - (Double(l), Logical(r)) => (l + r).into(), - (Integer(l), Double(r)) => (l + r).into(), - (Integer(l), Integer(r)) => (l + r).into(), - (Integer(l), Logical(r)) => (l + r).into(), - (Logical(l), Double(r)) => (l + r).into(), - (Logical(l), Integer(r)) => (l + r).into(), - (Logical(l), Logical(r)) => (l + r).into(), + match (self, other) { + (Double(l), Double(r)) => (l + r).map(|x| x.into()), + (Double(l), Integer(r)) => (l + r).map(|x| x.into()), + (Double(l), Logical(r)) => (l + r).map(|x| x.into()), + (Integer(l), Double(r)) => (l + r).map(|x| x.into()), + (Integer(l), Integer(r)) => (l + r).map(|x| x.into()), + (Integer(l), Logical(r)) => (l + r).map(|x| x.into()), + (Logical(l), Double(r)) => (l + r).map(|x| x.into()), + (Logical(l), Integer(r)) => (l + r).map(|x| x.into()), + (Logical(l), Logical(r)) => (l + r).map(|x| x.into()), + // Add more combinations if necessary _ => todo!(), } } } impl std::ops::Sub for Vector { - type Output = Vector; + type Output = Result; fn sub(self, rhs: Self) -> Self::Output { use Vector::*; match (self, rhs) { - (Double(l), Double(r)) => (l - r).into(), - (Double(l), Integer(r)) => (l - r).into(), - (Double(l), Logical(r)) => (l - r).into(), - (Integer(l), Double(r)) => (l - r).into(), - (Integer(l), Integer(r)) => (l - r).into(), - (Integer(l), Logical(r)) => (l - r).into(), - (Logical(l), Double(r)) => (l - r).into(), - (Logical(l), Integer(r)) => (l - r).into(), - (Logical(l), Logical(r)) => (l - r).into(), + (Double(l), Double(r)) => (l - r).map(|x| x.into()), + (Double(l), Integer(r)) => (l - r).map(|x| x.into()), + (Double(l), Logical(r)) => (l - r).map(|x| x.into()), + (Integer(l), Double(r)) => (l - r).map(|x| x.into()), + (Integer(l), Integer(r)) => (l - r).map(|x| x.into()), + (Integer(l), Logical(r)) => (l - r).map(|x| x.into()), + (Logical(l), Double(r)) => (l - r).map(|x| x.into()), + (Logical(l), Integer(r)) => (l - r).map(|x| x.into()), + (Logical(l), Logical(r)) => (l - r).map(|x| x.into()), _ => todo!(), } } } impl std::ops::Mul for Vector { - type Output = Vector; + type Output = Result; fn mul(self, rhs: Self) -> Self::Output { use Vector::*; match (self, rhs) { - (Double(l), Double(r)) => (l * r).into(), - (Double(l), Integer(r)) => (l * r).into(), - (Double(l), Logical(r)) => (l * r).into(), - (Integer(l), Double(r)) => (l * r).into(), - (Integer(l), Integer(r)) => (l * r).into(), - (Integer(l), Logical(r)) => (l * r).into(), - (Logical(l), Double(r)) => (l * r).into(), - (Logical(l), Integer(r)) => (l * r).into(), - (Logical(l), Logical(r)) => (l * r).into(), + (Double(l), Double(r)) => (l * r).map(|x| x.into()), + (Double(l), Integer(r)) => (l * r).map(|x| x.into()), + (Double(l), Logical(r)) => (l * r).map(|x| x.into()), + (Integer(l), Double(r)) => (l * r).map(|x| x.into()), + (Integer(l), Integer(r)) => (l * r).map(|x| x.into()), + (Integer(l), Logical(r)) => (l * r).map(|x| x.into()), + (Logical(l), Double(r)) => (l * r).map(|x| x.into()), + (Logical(l), Integer(r)) => (l * r).map(|x| x.into()), + (Logical(l), Logical(r)) => (l * r).map(|x| x.into()), _ => todo!(), } } } impl std::ops::Div for Vector { - type Output = Vector; + type Output = Result; fn div(self, rhs: Self) -> Self::Output { use Vector::*; match (self, rhs) { - (Double(l), Double(r)) => (l / r).into(), - (Double(l), Integer(r)) => (l / r).into(), - (Double(l), Logical(r)) => (l / r).into(), - (Integer(l), Double(r)) => (l / r).into(), - (Integer(l), Integer(r)) => (l / r).into(), - (Integer(l), Logical(r)) => (l / r).into(), - (Logical(l), Double(r)) => (l / r).into(), - (Logical(l), Integer(r)) => (l / r).into(), - (Logical(l), Logical(r)) => (l / r).into(), + (Double(l), Double(r)) => (l / r).map(|x| x.into()), + (Double(l), Integer(r)) => (l / r).map(|x| x.into()), + (Double(l), Logical(r)) => (l / r).map(|x| x.into()), + (Integer(l), Double(r)) => (l / r).map(|x| x.into()), + (Integer(l), Integer(r)) => (l / r).map(|x| x.into()), + (Integer(l), Logical(r)) => (l / r).map(|x| x.into()), + (Logical(l), Double(r)) => (l / r).map(|x| x.into()), + (Logical(l), Integer(r)) => (l / r).map(|x| x.into()), + (Logical(l), Logical(r)) => (l / r).map(|x| x.into()), _ => todo!(), } } } impl Pow for Vector { - type Output = Vector; + type Output = Result; fn power(self, rhs: Self) -> Self::Output { use Vector::*; match (self, rhs) { - (Double(l), Double(r)) => l.power(r).into(), - (Double(l), Integer(r)) => l.power(r).into(), - (Double(l), Logical(r)) => l.power(r).into(), - (Integer(l), Double(r)) => l.power(r).into(), - (Integer(l), Integer(r)) => l.power(r).into(), - (Integer(l), Logical(r)) => l.power(r).into(), - (Logical(l), Double(r)) => l.power(r).into(), - (Logical(l), Integer(r)) => l.power(r).into(), - (Logical(l), Logical(r)) => l.power(r).into(), + (Double(l), Double(r)) => l.power(r).map(|x| x.into()), + (Double(l), Integer(r)) => l.power(r).map(|x| x.into()), + (Double(l), Logical(r)) => l.power(r).map(|x| x.into()), + (Integer(l), Double(r)) => l.power(r).map(|x| x.into()), + (Integer(l), Integer(r)) => l.power(r).map(|x| x.into()), + (Integer(l), Logical(r)) => l.power(r).map(|x| x.into()), + (Logical(l), Double(r)) => l.power(r).map(|x| x.into()), + (Logical(l), Integer(r)) => l.power(r).map(|x| x.into()), + (Logical(l), Logical(r)) => l.power(r).map(|x| x.into()), _ => todo!(), } } @@ -783,205 +790,205 @@ pub trait VecPartialCmp { } impl VecPartialCmp for Vector { - type Output = Vector; + type Output = Result; fn vec_gt(self, rhs: Self) -> Self::Output { use Vector::*; match (self, rhs) { - (Double(l), Double(r)) => l.vec_gt(r).into(), - (Double(l), Integer(r)) => l.vec_gt(r).into(), - (Double(l), Logical(r)) => l.vec_gt(r).into(), - (Double(l), Character(r)) => l.vec_gt(r).into(), - (Integer(l), Double(r)) => l.vec_gt(r).into(), - (Integer(l), Integer(r)) => l.vec_gt(r).into(), - (Integer(l), Logical(r)) => l.vec_gt(r).into(), - (Integer(l), Character(r)) => l.vec_gt(r).into(), - (Logical(l), Double(r)) => l.vec_gt(r).into(), - (Logical(l), Integer(r)) => l.vec_gt(r).into(), - (Logical(l), Logical(r)) => l.vec_gt(r).into(), - (Logical(l), Character(r)) => l.vec_gt(r).into(), - (Character(l), Double(r)) => l.vec_gt(r).into(), - (Character(l), Integer(r)) => l.vec_gt(r).into(), - (Character(l), Logical(r)) => l.vec_gt(r).into(), - (Character(l), Character(r)) => l.vec_gt(r).into(), + (Double(l), Double(r)) => l.vec_gt(r).map(|x| x.into()), + (Double(l), Integer(r)) => l.vec_gt(r).map(|x| x.into()), + (Double(l), Logical(r)) => l.vec_gt(r).map(|x| x.into()), + (Double(l), Character(r)) => l.vec_gt(r).map(|x| x.into()), + (Integer(l), Double(r)) => l.vec_gt(r).map(|x| x.into()), + (Integer(l), Integer(r)) => l.vec_gt(r).map(|x| x.into()), + (Integer(l), Logical(r)) => l.vec_gt(r).map(|x| x.into()), + (Integer(l), Character(r)) => l.vec_gt(r).map(|x| x.into()), + (Logical(l), Double(r)) => l.vec_gt(r).map(|x| x.into()), + (Logical(l), Integer(r)) => l.vec_gt(r).map(|x| x.into()), + (Logical(l), Logical(r)) => l.vec_gt(r).map(|x| x.into()), + (Logical(l), Character(r)) => l.vec_gt(r).map(|x| x.into()), + (Character(l), Double(r)) => l.vec_gt(r).map(|x| x.into()), + (Character(l), Integer(r)) => l.vec_gt(r).map(|x| x.into()), + (Character(l), Logical(r)) => l.vec_gt(r).map(|x| x.into()), + (Character(l), Character(r)) => l.vec_gt(r).map(|x| x.into()), } } fn vec_gte(self, rhs: Self) -> Self::Output { use Vector::*; match (self, rhs) { - (Double(l), Double(r)) => l.vec_gte(r).into(), - (Double(l), Integer(r)) => l.vec_gte(r).into(), - (Double(l), Logical(r)) => l.vec_gte(r).into(), - (Double(l), Character(r)) => l.vec_gte(r).into(), - (Integer(l), Double(r)) => l.vec_gte(r).into(), - (Integer(l), Integer(r)) => l.vec_gte(r).into(), - (Integer(l), Logical(r)) => l.vec_gte(r).into(), - (Integer(l), Character(r)) => l.vec_gte(r).into(), - (Logical(l), Double(r)) => l.vec_gte(r).into(), - (Logical(l), Integer(r)) => l.vec_gte(r).into(), - (Logical(l), Logical(r)) => l.vec_gte(r).into(), - (Logical(l), Character(r)) => l.vec_gte(r).into(), - (Character(l), Double(r)) => l.vec_gte(r).into(), - (Character(l), Integer(r)) => l.vec_gte(r).into(), - (Character(l), Logical(r)) => l.vec_gte(r).into(), - (Character(l), Character(r)) => l.vec_gte(r).into(), + (Double(l), Double(r)) => l.vec_gte(r).map(|x| x.into()), + (Double(l), Integer(r)) => l.vec_gte(r).map(|x| x.into()), + (Double(l), Logical(r)) => l.vec_gte(r).map(|x| x.into()), + (Double(l), Character(r)) => l.vec_gte(r).map(|x| x.into()), + (Integer(l), Double(r)) => l.vec_gte(r).map(|x| x.into()), + (Integer(l), Integer(r)) => l.vec_gte(r).map(|x| x.into()), + (Integer(l), Logical(r)) => l.vec_gte(r).map(|x| x.into()), + (Integer(l), Character(r)) => l.vec_gte(r).map(|x| x.into()), + (Logical(l), Double(r)) => l.vec_gte(r).map(|x| x.into()), + (Logical(l), Integer(r)) => l.vec_gte(r).map(|x| x.into()), + (Logical(l), Logical(r)) => l.vec_gte(r).map(|x| x.into()), + (Logical(l), Character(r)) => l.vec_gte(r).map(|x| x.into()), + (Character(l), Double(r)) => l.vec_gte(r).map(|x| x.into()), + (Character(l), Integer(r)) => l.vec_gte(r).map(|x| x.into()), + (Character(l), Logical(r)) => l.vec_gte(r).map(|x| x.into()), + (Character(l), Character(r)) => l.vec_gte(r).map(|x| x.into()), } } fn vec_lt(self, rhs: Self) -> Self::Output { use Vector::*; match (self, rhs) { - (Double(l), Double(r)) => l.vec_lt(r).into(), - (Double(l), Integer(r)) => l.vec_lt(r).into(), - (Double(l), Logical(r)) => l.vec_lt(r).into(), - (Double(l), Character(r)) => l.vec_lt(r).into(), - (Integer(l), Double(r)) => l.vec_lt(r).into(), - (Integer(l), Integer(r)) => l.vec_lt(r).into(), - (Integer(l), Logical(r)) => l.vec_lt(r).into(), - (Integer(l), Character(r)) => l.vec_lt(r).into(), - (Logical(l), Double(r)) => l.vec_lt(r).into(), - (Logical(l), Integer(r)) => l.vec_lt(r).into(), - (Logical(l), Logical(r)) => l.vec_lt(r).into(), - (Logical(l), Character(r)) => l.vec_lt(r).into(), - (Character(l), Double(r)) => l.vec_lt(r).into(), - (Character(l), Integer(r)) => l.vec_lt(r).into(), - (Character(l), Logical(r)) => l.vec_lt(r).into(), - (Character(l), Character(r)) => l.vec_lt(r).into(), + (Double(l), Double(r)) => l.vec_lt(r).map(|x| x.into()), + (Double(l), Integer(r)) => l.vec_lt(r).map(|x| x.into()), + (Double(l), Logical(r)) => l.vec_lt(r).map(|x| x.into()), + (Double(l), Character(r)) => l.vec_lt(r).map(|x| x.into()), + (Integer(l), Double(r)) => l.vec_lt(r).map(|x| x.into()), + (Integer(l), Integer(r)) => l.vec_lt(r).map(|x| x.into()), + (Integer(l), Logical(r)) => l.vec_lt(r).map(|x| x.into()), + (Integer(l), Character(r)) => l.vec_lt(r).map(|x| x.into()), + (Logical(l), Double(r)) => l.vec_lt(r).map(|x| x.into()), + (Logical(l), Integer(r)) => l.vec_lt(r).map(|x| x.into()), + (Logical(l), Logical(r)) => l.vec_lt(r).map(|x| x.into()), + (Logical(l), Character(r)) => l.vec_lt(r).map(|x| x.into()), + (Character(l), Double(r)) => l.vec_lt(r).map(|x| x.into()), + (Character(l), Integer(r)) => l.vec_lt(r).map(|x| x.into()), + (Character(l), Logical(r)) => l.vec_lt(r).map(|x| x.into()), + (Character(l), Character(r)) => l.vec_lt(r).map(|x| x.into()), } } fn vec_lte(self, rhs: Self) -> Self::Output { use Vector::*; match (self, rhs) { - (Double(l), Double(r)) => l.vec_lte(r).into(), - (Double(l), Integer(r)) => l.vec_lte(r).into(), - (Double(l), Logical(r)) => l.vec_lte(r).into(), - (Double(l), Character(r)) => l.vec_lte(r).into(), - (Integer(l), Double(r)) => l.vec_lte(r).into(), - (Integer(l), Integer(r)) => l.vec_lte(r).into(), - (Integer(l), Logical(r)) => l.vec_lte(r).into(), - (Integer(l), Character(r)) => l.vec_lte(r).into(), - (Logical(l), Double(r)) => l.vec_lte(r).into(), - (Logical(l), Integer(r)) => l.vec_lte(r).into(), - (Logical(l), Logical(r)) => l.vec_lte(r).into(), - (Logical(l), Character(r)) => l.vec_lte(r).into(), - (Character(l), Double(r)) => l.vec_lte(r).into(), - (Character(l), Integer(r)) => l.vec_lte(r).into(), - (Character(l), Logical(r)) => l.vec_lte(r).into(), - (Character(l), Character(r)) => l.vec_lte(r).into(), + (Double(l), Double(r)) => l.vec_lte(r).map(|x| x.into()), + (Double(l), Integer(r)) => l.vec_lte(r).map(|x| x.into()), + (Double(l), Logical(r)) => l.vec_lte(r).map(|x| x.into()), + (Double(l), Character(r)) => l.vec_lte(r).map(|x| x.into()), + (Integer(l), Double(r)) => l.vec_lte(r).map(|x| x.into()), + (Integer(l), Integer(r)) => l.vec_lte(r).map(|x| x.into()), + (Integer(l), Logical(r)) => l.vec_lte(r).map(|x| x.into()), + (Integer(l), Character(r)) => l.vec_lte(r).map(|x| x.into()), + (Logical(l), Double(r)) => l.vec_lte(r).map(|x| x.into()), + (Logical(l), Integer(r)) => l.vec_lte(r).map(|x| x.into()), + (Logical(l), Logical(r)) => l.vec_lte(r).map(|x| x.into()), + (Logical(l), Character(r)) => l.vec_lte(r).map(|x| x.into()), + (Character(l), Double(r)) => l.vec_lte(r).map(|x| x.into()), + (Character(l), Integer(r)) => l.vec_lte(r).map(|x| x.into()), + (Character(l), Logical(r)) => l.vec_lte(r).map(|x| x.into()), + (Character(l), Character(r)) => l.vec_lte(r).map(|x| x.into()), } } fn vec_eq(self, rhs: Self) -> Self::Output { use Vector::*; match (self, rhs) { - (Double(l), Double(r)) => l.vec_eq(r).into(), - (Double(l), Integer(r)) => l.vec_eq(r).into(), - (Double(l), Logical(r)) => l.vec_eq(r).into(), - (Double(l), Character(r)) => l.vec_eq(r).into(), - (Integer(l), Double(r)) => l.vec_eq(r).into(), - (Integer(l), Integer(r)) => l.vec_eq(r).into(), - (Integer(l), Logical(r)) => l.vec_eq(r).into(), - (Integer(l), Character(r)) => l.vec_eq(r).into(), - (Logical(l), Double(r)) => l.vec_eq(r).into(), - (Logical(l), Integer(r)) => l.vec_eq(r).into(), - (Logical(l), Logical(r)) => l.vec_eq(r).into(), - (Logical(l), Character(r)) => l.vec_eq(r).into(), - (Character(l), Double(r)) => l.vec_eq(r).into(), - (Character(l), Integer(r)) => l.vec_eq(r).into(), - (Character(l), Logical(r)) => l.vec_eq(r).into(), - (Character(l), Character(r)) => l.vec_eq(r).into(), + (Double(l), Double(r)) => l.vec_eq(r).map(|x| x.into()), + (Double(l), Integer(r)) => l.vec_eq(r).map(|x| x.into()), + (Double(l), Logical(r)) => l.vec_eq(r).map(|x| x.into()), + (Double(l), Character(r)) => l.vec_eq(r).map(|x| x.into()), + (Integer(l), Double(r)) => l.vec_eq(r).map(|x| x.into()), + (Integer(l), Integer(r)) => l.vec_eq(r).map(|x| x.into()), + (Integer(l), Logical(r)) => l.vec_eq(r).map(|x| x.into()), + (Integer(l), Character(r)) => l.vec_eq(r).map(|x| x.into()), + (Logical(l), Double(r)) => l.vec_eq(r).map(|x| x.into()), + (Logical(l), Integer(r)) => l.vec_eq(r).map(|x| x.into()), + (Logical(l), Logical(r)) => l.vec_eq(r).map(|x| x.into()), + (Logical(l), Character(r)) => l.vec_eq(r).map(|x| x.into()), + (Character(l), Double(r)) => l.vec_eq(r).map(|x| x.into()), + (Character(l), Integer(r)) => l.vec_eq(r).map(|x| x.into()), + (Character(l), Logical(r)) => l.vec_eq(r).map(|x| x.into()), + (Character(l), Character(r)) => l.vec_eq(r).map(|x| x.into()), } } fn vec_neq(self, rhs: Self) -> Self::Output { use Vector::*; match (self, rhs) { - (Double(l), Double(r)) => l.vec_neq(r).into(), - (Double(l), Integer(r)) => l.vec_neq(r).into(), - (Double(l), Logical(r)) => l.vec_neq(r).into(), - (Double(l), Character(r)) => l.vec_neq(r).into(), - (Integer(l), Double(r)) => l.vec_neq(r).into(), - (Integer(l), Integer(r)) => l.vec_neq(r).into(), - (Integer(l), Logical(r)) => l.vec_neq(r).into(), - (Integer(l), Character(r)) => l.vec_neq(r).into(), - (Logical(l), Double(r)) => l.vec_neq(r).into(), - (Logical(l), Integer(r)) => l.vec_neq(r).into(), - (Logical(l), Logical(r)) => l.vec_neq(r).into(), - (Logical(l), Character(r)) => l.vec_neq(r).into(), - (Character(l), Double(r)) => l.vec_neq(r).into(), - (Character(l), Integer(r)) => l.vec_neq(r).into(), - (Character(l), Logical(r)) => l.vec_neq(r).into(), - (Character(l), Character(r)) => l.vec_neq(r).into(), + (Double(l), Double(r)) => l.vec_neq(r).map(|x| x.into()), + (Double(l), Integer(r)) => l.vec_neq(r).map(|x| x.into()), + (Double(l), Logical(r)) => l.vec_neq(r).map(|x| x.into()), + (Double(l), Character(r)) => l.vec_neq(r).map(|x| x.into()), + (Integer(l), Double(r)) => l.vec_neq(r).map(|x| x.into()), + (Integer(l), Integer(r)) => l.vec_neq(r).map(|x| x.into()), + (Integer(l), Logical(r)) => l.vec_neq(r).map(|x| x.into()), + (Integer(l), Character(r)) => l.vec_neq(r).map(|x| x.into()), + (Logical(l), Double(r)) => l.vec_neq(r).map(|x| x.into()), + (Logical(l), Integer(r)) => l.vec_neq(r).map(|x| x.into()), + (Logical(l), Logical(r)) => l.vec_neq(r).map(|x| x.into()), + (Logical(l), Character(r)) => l.vec_neq(r).map(|x| x.into()), + (Character(l), Double(r)) => l.vec_neq(r).map(|x| x.into()), + (Character(l), Integer(r)) => l.vec_neq(r).map(|x| x.into()), + (Character(l), Logical(r)) => l.vec_neq(r).map(|x| x.into()), + (Character(l), Character(r)) => l.vec_neq(r).map(|x| x.into()), } } } impl std::ops::Rem for Vector { - type Output = Vector; + type Output = Result; fn rem(self, rhs: Self) -> Self::Output { use Vector::*; match (self, rhs) { - (Double(l), Double(r)) => l.rem(r).into(), - (Double(l), Integer(r)) => l.rem(r).into(), - (Double(l), Logical(r)) => l.rem(r).into(), - (Integer(l), Double(r)) => l.rem(r).into(), - (Integer(l), Integer(r)) => l.rem(r).into(), - (Integer(l), Logical(r)) => l.rem(r).into(), - (Logical(l), Double(r)) => l.rem(r).into(), - (Logical(l), Integer(r)) => l.rem(r).into(), - (Logical(l), Logical(r)) => l.rem(r).into(), + (Double(l), Double(r)) => l.rem(r).map(|x| x.into()), + (Double(l), Integer(r)) => l.rem(r).map(|x| x.into()), + (Double(l), Logical(r)) => l.rem(r).map(|x| x.into()), + (Integer(l), Double(r)) => l.rem(r).map(|x| x.into()), + (Integer(l), Integer(r)) => l.rem(r).map(|x| x.into()), + (Integer(l), Logical(r)) => l.rem(r).map(|x| x.into()), + (Logical(l), Double(r)) => l.rem(r).map(|x| x.into()), + (Logical(l), Integer(r)) => l.rem(r).map(|x| x.into()), + (Logical(l), Logical(r)) => l.rem(r).map(|x| x.into()), _ => todo!(), } } } impl std::ops::BitOr for Vector { - type Output = Vector; + type Output = Result; fn bitor(self, rhs: Self) -> Self::Output { use Vector::*; match (self, rhs) { - (Double(l), Double(r)) => l.bitor(r).into(), - (Double(l), Integer(r)) => l.bitor(r).into(), - (Double(l), Logical(r)) => l.bitor(r).into(), - (Double(l), Character(r)) => l.bitor(r).into(), - (Integer(l), Double(r)) => l.bitor(r).into(), - (Integer(l), Integer(r)) => l.bitor(r).into(), - (Integer(l), Logical(r)) => l.bitor(r).into(), - (Integer(l), Character(r)) => l.bitor(r).into(), - (Logical(l), Double(r)) => l.bitor(r).into(), - (Logical(l), Integer(r)) => l.bitor(r).into(), - (Logical(l), Logical(r)) => l.bitor(r).into(), - (Logical(l), Character(r)) => l.bitor(r).into(), - (Character(l), Double(r)) => l.bitor(r).into(), - (Character(l), Integer(r)) => l.bitor(r).into(), - (Character(l), Logical(r)) => l.bitor(r).into(), - (Character(l), Character(r)) => l.bitor(r).into(), + (Double(l), Double(r)) => l.bitor(r).map(|x| x.into()), + (Double(l), Integer(r)) => l.bitor(r).map(|x| x.into()), + (Double(l), Logical(r)) => l.bitor(r).map(|x| x.into()), + (Double(l), Character(r)) => l.bitor(r).map(|x| x.into()), + (Integer(l), Double(r)) => l.bitor(r).map(|x| x.into()), + (Integer(l), Integer(r)) => l.bitor(r).map(|x| x.into()), + (Integer(l), Logical(r)) => l.bitor(r).map(|x| x.into()), + (Integer(l), Character(r)) => l.bitor(r).map(|x| x.into()), + (Logical(l), Double(r)) => l.bitor(r).map(|x| x.into()), + (Logical(l), Integer(r)) => l.bitor(r).map(|x| x.into()), + (Logical(l), Logical(r)) => l.bitor(r).map(|x| x.into()), + (Logical(l), Character(r)) => l.bitor(r).map(|x| x.into()), + (Character(l), Double(r)) => l.bitor(r).map(|x| x.into()), + (Character(l), Integer(r)) => l.bitor(r).map(|x| x.into()), + (Character(l), Logical(r)) => l.bitor(r).map(|x| x.into()), + (Character(l), Character(r)) => l.bitor(r).map(|x| x.into()), } } } impl std::ops::BitAnd for Vector { - type Output = Vector; + type Output = Result; fn bitand(self, rhs: Self) -> Self::Output { use Vector::*; match (self, rhs) { - (Double(l), Double(r)) => l.bitand(r).into(), - (Double(l), Integer(r)) => l.bitand(r).into(), - (Double(l), Logical(r)) => l.bitand(r).into(), - (Double(l), Character(r)) => l.bitand(r).into(), - (Integer(l), Double(r)) => l.bitand(r).into(), - (Integer(l), Integer(r)) => l.bitand(r).into(), - (Integer(l), Logical(r)) => l.bitand(r).into(), - (Integer(l), Character(r)) => l.bitand(r).into(), - (Logical(l), Double(r)) => l.bitand(r).into(), - (Logical(l), Integer(r)) => l.bitand(r).into(), - (Logical(l), Logical(r)) => l.bitand(r).into(), - (Logical(l), Character(r)) => l.bitand(r).into(), - (Character(l), Double(r)) => l.bitand(r).into(), - (Character(l), Integer(r)) => l.bitand(r).into(), - (Character(l), Logical(r)) => l.bitand(r).into(), - (Character(l), Character(r)) => l.bitand(r).into(), + (Double(l), Double(r)) => l.bitand(r).map(|x| x.into()), + (Double(l), Integer(r)) => l.bitand(r).map(|x| x.into()), + (Double(l), Logical(r)) => l.bitand(r).map(|x| x.into()), + (Double(l), Character(r)) => l.bitand(r).map(|x| x.into()), + (Integer(l), Double(r)) => l.bitand(r).map(|x| x.into()), + (Integer(l), Integer(r)) => l.bitand(r).map(|x| x.into()), + (Integer(l), Logical(r)) => l.bitand(r).map(|x| x.into()), + (Integer(l), Character(r)) => l.bitand(r).map(|x| x.into()), + (Logical(l), Double(r)) => l.bitand(r).map(|x| x.into()), + (Logical(l), Integer(r)) => l.bitand(r).map(|x| x.into()), + (Logical(l), Logical(r)) => l.bitand(r).map(|x| x.into()), + (Logical(l), Character(r)) => l.bitand(r).map(|x| x.into()), + (Character(l), Double(r)) => l.bitand(r).map(|x| x.into()), + (Character(l), Integer(r)) => l.bitand(r).map(|x| x.into()), + (Character(l), Logical(r)) => l.bitand(r).map(|x| x.into()), + (Character(l), Character(r)) => l.bitand(r).map(|x| x.into()), } } } @@ -999,4 +1006,57 @@ mod tests { x[[1]] == 10 & x[[2]] == 2 "}} } + #[test] + fn comparison_conversion() { + // double + r_expect!(2 == 2); + r_expect!(2 == 2L); + r_expect!(1 == true); + r_expect!(2 != false); + r_expect!(2 != true); + r_expect!(0 == false); + // integer + r_expect!(2L == 2L); + r_expect!(2L == 2); + r_expect!(2L != true); + r_expect!(0L == false); + r_expect!(1L == true); + // logical + r_expect!(true == 1); + r_expect!(true == 1L); + r_expect!(false == 0L); + r_expect!(false == 0); + r_expect! {{r#""true" == true"#}} + // character + r_expect!("a" == "a"); + r_expect!("a" != "b"); + r_expect!("1" == 1); + r_expect!("1" == 1L); + r_expect!("1" != 2L); + r_expect!("1" != 2); + + // metaprogramming objects + r_expect!(environment() == environment()); + r_expect!(quote(1) == quote(1)); + + // length > 1 also works + r_expect! {{" + x = [1L, 2L] + y = [1L, 2L] + z = x == y + z[1] && z[2] + "}} + } + #[test] + // test that types are as expected + fn type_stability_num_ops() { + r_expect! {{r#" + typeof(1L + 1L) == "integer" + typeof(1 + 1) == "double" + typeof(1L + 1) == "double" + typeof(1 + 1L) == "double" + typeof(true + true) == "integer" + typeof(true + false) == "integer" + "#}} + } } diff --git a/src/object/vector/iterators.rs b/src/object/vector/iterators.rs index bb33e9d..b4e6e37 100644 --- a/src/object/vector/iterators.rs +++ b/src/object/vector/iterators.rs @@ -2,30 +2,6 @@ use std::ops::Deref; use super::coercion::{CoercibleInto, CommonNum, MinimallyNumeric}; -/// Zip iterators into recycling vectors, extending to longest length -/// -/// This operation does not do any vector length matching, elements will be -/// recycled even if they do not repeat an even number of times. -/// -/// ```rust -/// use r::object::iterators::zip_recycle; -/// -/// let x = vec![1, 2, 3, 4]; -/// let y = vec![2, 4]; -/// let z: Vec<_> = zip_recycle(x.into_iter(), y.into_iter()).collect(); -/// ```` -/// -pub fn zip_recycle(l: L, r: R) -> impl Iterator -where - L: ExactSizeIterator + Iterator + Clone, - R: ExactSizeIterator + Iterator + Clone, -{ - let l = l.into_iter(); - let r = r.into_iter(); - let n = std::cmp::max(l.len(), r.len()); - l.cycle().zip(r.cycle()).take(n) -} - /// Map an iterator of pairs into a pair of common numeric types /// /// Accept an iterator of pairs of numeric (or numeric-coercible) values and diff --git a/src/object/vector/rep.rs b/src/object/vector/rep.rs index 8fec737..f790dc7 100644 --- a/src/object/vector/rep.rs +++ b/src/object/vector/rep.rs @@ -1,8 +1,8 @@ use std::cell::{Ref, RefCell, RefMut}; use std::fmt::{Debug, Display}; +use std::iter::repeat; use super::coercion::{AtomicMode, CoercibleInto, CommonCmp, CommonNum, MinimallyNumeric}; -use super::iterators::{map_common_numeric, zip_recycle}; use super::reptype::{ IntoIterableRefNames, IntoIterableRefPairs, IntoIterableRefValues, IterablePairs, IterableValues, Naming, RepType, @@ -10,6 +10,7 @@ use super::reptype::{ use super::subset::Subset; use super::types::*; use super::{OptionNA, Pow, VecPartialCmp}; +use crate::error::Error; use crate::lang::Signal; use crate::object::{CowObj, Obj, Subsets, ViewMut}; @@ -64,6 +65,18 @@ impl Rep where T: Clone + Default, { + /// Return the only value if the vector has length 1. + pub fn as_scalar(&self) -> Option { + let mut into_iter = self.values_ref(); + let mut iter = into_iter.iter(); + if let Some(x) = iter.next() { + if iter.next().is_none() { + return Some(x.clone()); + } + }; + None + } + pub fn borrow(&self) -> Ref> { self.0.borrow() } @@ -236,12 +249,17 @@ where self.borrow().push_named(name, value) } - pub fn assign(&mut self, value: Rep) -> Self + /// Assign to the vector, often with a view through a Subset. + /// An error is thrown if the lengths are not compatible. + pub fn assign(&mut self, value: Rep) -> Result where T: From + Clone, R: Clone + Default, { - self.0.borrow_mut().assign(value.0.into_inner()).into() + self.0 + .borrow_mut() + .assign(value.0.into_inner()) + .map(|x| x.into()) } /// Test the mode of the internal vector type /// @@ -339,27 +357,6 @@ where { self.as_mode::() } - - /// Apply over the vector contents to produce a vector of [std::cmp::Ordering] - /// - /// This function is used primarily in support of the implementation of - /// vectorized comparison operators and likely does not need to be used - /// outside of that context. - /// - /// See [crate::object::vector::VecPartialCmp] for vectorized comparison - /// operator implementations. - /// - pub fn vectorized_partial_cmp(self, other: Rep) -> Vec> - where - T: AtomicMode + Default + Clone + CoercibleInto, - R: AtomicMode + Default + Clone + CoercibleInto, - (T, R): CommonCmp, - C: PartialOrd, - { - self.0 - .into_inner() - .vectorized_partial_cmp(other.0.into_inner()) - } } impl Default for Rep @@ -371,6 +368,15 @@ where } } +impl From> for Rep +where + T: Clone + Default, +{ + fn from(rep: Vec) -> Self { + Rep(RefCell::new(RepType::from(CowObj::from(rep)))) + } +} + impl From>> for Rep where T: Clone + Default, @@ -416,48 +422,24 @@ impl From> for Rep { } } -impl From>> for Rep { - fn from(value: Vec>) -> Self { - Rep(RefCell::new(value.into())) - } -} - impl From> for Rep { fn from(value: Vec) -> Self { Rep(RefCell::new(value.into())) } } -impl From>> for Rep { - fn from(value: Vec>) -> Self { - Rep(RefCell::new(value.into())) - } -} - impl From> for Rep { fn from(value: Vec) -> Self { Rep(RefCell::new(value.into())) } } -impl From>> for Rep { - fn from(value: Vec>) -> Self { - Rep(RefCell::new(value.into())) - } -} - impl From> for Rep { fn from(value: Vec) -> Self { Rep(RefCell::new(value.into())) } } -impl From>> for Rep { - fn from(value: Vec>) -> Self { - Rep(RefCell::new(value.into())) - } -} - impl From> for Rep { fn from(value: Vec) -> Self { Rep(RefCell::new(value.into())) @@ -599,10 +581,13 @@ where RepType: From>, O: Clone, { - type Output = Rep; + type Output = Result, Signal>; fn neg(self) -> Self::Output { - let result = -(self.0.into_inner()); - Rep(RefCell::new(result)) + let result: Vec = self + .iter_values() + .map(|x| -(CoercibleInto::::coerce_into(x))) + .collect(); + Ok(Rep(RefCell::new(result.into()))) } } @@ -611,26 +596,13 @@ where L: AtomicMode + Default + Clone + MinimallyNumeric + CoercibleInto, R: AtomicMode + Default + Clone + MinimallyNumeric + CoercibleInto, (LNum, RNum): CommonNum, - C: Clone + std::ops::Add, - RepType: From>, + C: Clone + std::ops::Add + Default, + Rep: From>, + O: Clone + Default, { - type Output = Rep; + type Output = Result, Signal>; fn add(self, rhs: Rep) -> Self::Output { - let lc = self.inner().clone(); - let lb = lc.borrow(); - let lhs = lb.iter(); - - let rc = rhs.inner().clone(); - let rb = rc.borrow(); - let rhs = rb.iter(); - - let result = RepType::from( - map_common_numeric(zip_recycle(lhs, rhs)) - .map(|(l, r)| l + r) - .collect::>(), - ); - - Rep(RefCell::new(result)) + try_binary_num_op(self, rhs, |x, y| x + y) } } @@ -639,15 +611,13 @@ where L: AtomicMode + Default + Clone + MinimallyNumeric + CoercibleInto, R: AtomicMode + Default + Clone + MinimallyNumeric + CoercibleInto, (LNum, RNum): CommonNum, - C: std::ops::Sub, - RepType: From>, - O: Clone, - C: Clone, + C: Clone + std::ops::Sub + Default, + Rep: From>, + O: Clone + Default, { - type Output = Rep; + type Output = Result, Signal>; fn sub(self, rhs: Rep) -> Self::Output { - let result = (self.0.into_inner()) - (rhs.0.into_inner()); - Rep(RefCell::new(result)) + try_binary_num_op(self, rhs, |x, y| x - y) } } @@ -656,17 +626,13 @@ where L: AtomicMode + Default + Clone + MinimallyNumeric + CoercibleInto, R: AtomicMode + Default + Clone + MinimallyNumeric + CoercibleInto, (LNum, RNum): CommonNum, - C: std::ops::Mul, - RepType: From>, - O: Clone, - C: Clone, + C: Clone + std::ops::Mul + Default, + Rep: From>, + O: Clone + Default, { - type Output = Rep; + type Output = Result, Signal>; fn mul(self, rhs: Rep) -> Self::Output { - use std::ops::Mul; - let result = Mul::mul(self.0.into_inner(), rhs.0.into_inner()); - - Rep(RefCell::new(result)) + try_binary_num_op(self, rhs, |x, y| x * y) } } @@ -675,15 +641,13 @@ where L: AtomicMode + Default + Clone + MinimallyNumeric + CoercibleInto, R: AtomicMode + Default + Clone + MinimallyNumeric + CoercibleInto, (LNum, RNum): CommonNum, - C: std::ops::Div, - RepType: From>, - O: Clone, - C: Clone, + C: Clone + std::ops::Div + Default, + Rep: From>, + O: Clone + Default, { - type Output = Rep; + type Output = Result, Signal>; fn div(self, rhs: Rep) -> Self::Output { - let result = (self.0.into_inner()) / (rhs.0.into_inner()); - Rep(RefCell::new(result)) + try_binary_num_op(self, rhs, |x, y| x / y) } } @@ -692,18 +656,13 @@ where L: AtomicMode + Default + Clone + MinimallyNumeric + CoercibleInto, R: AtomicMode + Default + Clone + MinimallyNumeric + CoercibleInto, (LNum, RNum): CommonNum, - C: std::ops::Rem, - RepType: From>, - L: Clone, - R: Clone, - C: Clone, - O: Clone, + C: Clone + std::ops::Rem + Default, + Rep: From>, + O: Clone + Default, { - type Output = Rep; + type Output = Result, Signal>; fn rem(self, rhs: Rep) -> Self::Output { - pub use std::ops::Rem; - let result = Rem::rem(self.0.into_inner(), rhs.0.into_inner()); - Rep(RefCell::new(result)) + try_binary_num_op(self, rhs, |x, y| x % y) } } @@ -711,154 +670,240 @@ impl Pow> for Rep where L: AtomicMode + Default + Clone + MinimallyNumeric + CoercibleInto, R: AtomicMode + Default + Clone + MinimallyNumeric + CoercibleInto, - LNum: Pow, - RepType: From>, + (LNum, RNum): CommonNum, + O: Pow, + Rep: From>, + O: Default, L: Clone, R: Clone, O: Clone, { - type Output = Rep; + type Output = Result, Signal>; fn power(self, rhs: Rep) -> Self::Output { - let result = Pow::power(self.0.into_inner(), rhs.0.into_inner()); - Rep(RefCell::new(result)) + try_binary_num_op(self, rhs, |x, y| Pow::power(x, y)) } } -impl std::ops::BitOr> for Rep +impl std::ops::BitOr> for Rep where L: AtomicMode + Default + Clone + CoercibleInto, R: AtomicMode + Default + Clone + CoercibleInto, - Logical: std::ops::BitOr, - RepType: From>, - L: Clone, - R: Clone, - O: Clone, { - type Output = Rep; + type Output = Result, Signal>; fn bitor(self, rhs: Rep) -> Self::Output { - let result: RepType = (self.0.into_inner()) | (rhs.0.into_inner()); - Rep(RefCell::new(result)) + try_binary_lgl_op(self, rhs, |x, y| x | y) } } -impl std::ops::BitAnd> for Rep +impl std::ops::BitAnd> for Rep where L: AtomicMode + Default + Clone + CoercibleInto, R: AtomicMode + Default + Clone + CoercibleInto, - Logical: std::ops::BitAnd, - RepType: From>, - L: Clone, - R: Clone, - O: Clone, { - type Output = Rep; + type Output = Result, Signal>; fn bitand(self, rhs: Rep) -> Self::Output { - let result: RepType = (self.0.into_inner()) & (rhs.0.into_inner()); - Rep(RefCell::new(result)) + try_binary_lgl_op(self, rhs, |x, y| x & y) } } -impl std::ops::Not for Rep +impl std::ops::Not for Rep where L: AtomicMode + Default + Clone + CoercibleInto, - Logical: std::ops::Not, - RepType: From>, - O: Clone, { - type Output = Rep; + type Output = Result, Signal>; fn not(self) -> Self::Output { - let result: RepType = !self.0.into_inner(); - Rep(RefCell::new(result)) + let result: Vec = self + .iter_values() + .map(|x| !(CoercibleInto::::coerce_into(x))) + .collect(); + Ok(Rep(RefCell::new(result.into()))) } } impl VecPartialCmp> for Rep where - L: AtomicMode + Default + Clone + CoercibleInto, - R: AtomicMode + Default + Clone + CoercibleInto, + L: AtomicMode + Default + Clone + CoercibleInto + Clone, + R: AtomicMode + Default + Clone + CoercibleInto + Clone, (L, R): CommonCmp, - C: PartialOrd, - L: Clone, - R: Clone, - C: Clone, + C: PartialOrd + Clone + Default, { - type Output = Rep; + type Output = Result, Signal>; fn vec_gt(self, rhs: Rep) -> Self::Output { use std::cmp::Ordering::*; - self.vectorized_partial_cmp(rhs) - .into_iter() - .map(|i| match i { - Some(Greater) => OptionNA::Some(true), - Some(_) => OptionNA::Some(false), - None => OptionNA::NA, - }) - .collect::>() - .into() + try_binary_cmp_op(self, rhs, |i| match i { + Some(Greater) => OptionNA::Some(true), + Some(_) => OptionNA::Some(false), + None => OptionNA::NA, + }) } fn vec_gte(self, rhs: Rep) -> Self::Output { use std::cmp::Ordering::*; - self.vectorized_partial_cmp(rhs) - .into_iter() - .map(|i| match i { - Some(Greater | Equal) => OptionNA::Some(true), - Some(_) => OptionNA::Some(false), - None => OptionNA::NA, - }) - .collect::>() - .into() + try_binary_cmp_op(self, rhs, |i| match i { + Some(Greater | Equal) => OptionNA::Some(true), + Some(_) => OptionNA::Some(false), + None => OptionNA::NA, + }) } fn vec_lt(self, rhs: Rep) -> Self::Output { use std::cmp::Ordering::*; - self.vectorized_partial_cmp(rhs) - .into_iter() - .map(|i| match i { - Some(Less) => OptionNA::Some(true), - Some(_) => OptionNA::Some(false), - None => OptionNA::NA, - }) - .collect::>() - .into() + try_binary_cmp_op(self, rhs, |i| match i { + Some(Less) => OptionNA::Some(true), + Some(_) => OptionNA::Some(false), + None => OptionNA::NA, + }) } fn vec_lte(self, rhs: Rep) -> Self::Output { use std::cmp::Ordering::*; - self.vectorized_partial_cmp(rhs) - .into_iter() - .map(|i| match i { - Some(Less | Equal) => OptionNA::Some(true), - Some(_) => OptionNA::Some(false), - None => OptionNA::NA, - }) - .collect::>() - .into() + try_binary_cmp_op(self, rhs, |i| match i { + Some(Less | Equal) => OptionNA::Some(true), + Some(_) => OptionNA::Some(false), + None => OptionNA::NA, + }) } fn vec_eq(self, rhs: Rep) -> Self::Output { use std::cmp::Ordering::*; - self.vectorized_partial_cmp(rhs) - .into_iter() - .map(|i| match i { - Some(Equal) => OptionNA::Some(true), - Some(_) => OptionNA::Some(false), - None => OptionNA::NA, - }) - .collect::>() - .into() + try_binary_cmp_op(self, rhs, |i| match i { + Some(Equal) => OptionNA::Some(true), + Some(_) => OptionNA::Some(false), + None => OptionNA::NA, + }) } fn vec_neq(self, rhs: Rep) -> Self::Output { use std::cmp::Ordering::*; - self.vectorized_partial_cmp(rhs) - .into_iter() - .map(|i| match i { - Some(Equal) => OptionNA::Some(false), - Some(_) => OptionNA::Some(true), - None => OptionNA::NA, - }) - .collect::>() - .into() + try_binary_cmp_op(self, rhs, |i| match i { + Some(Equal) => OptionNA::Some(false), + Some(_) => OptionNA::Some(true), + None => OptionNA::NA, + }) + } +} + +/// This function applies a function `g` to pairs from lhs and rhs. +/// The function returns an error when the lengths are not compatible. +fn try_recycle_then(lhs: Rep, rhs: Rep, g: F) -> Result, Signal> +where + L: Clone + Default, + R: Clone + Default, + Rep: From>, + O: Clone + Default, + A: Clone, + F: Fn(L, R) -> O, +{ + match (lhs.as_scalar(), rhs.as_scalar()) { + (Some(l), Some(r)) => { + let result: Vec = vec![g(l, r)]; + Ok(Rep::from(result)) + } + (Some(l), None) => { + let result: Vec = repeat(l) + .zip(rhs.iter_values()) + .map(|(l, r)| g(l, r)) + .collect(); + if result.is_empty() { + return Err(Signal::Error(Error::NonRecyclableLengths(1, 0))); + } + Ok(Rep::from(result)) + } + (None, Some(r)) => { + let result: Vec = lhs + .iter_values() + .zip(repeat(r)) + .map(|(l, r)| g(l, r)) + .collect(); + if result.is_empty() { + return Err(Signal::Error(Error::NonRecyclableLengths(0, 1))); + } + Ok(Rep::from(result)) + } + (None, None) => { + let mut lc = lhs.iter_values(); + let mut rc = rhs.iter_values(); + + let max_size = std::cmp::max(lc.size_hint().0, rc.size_hint().0); + + let mut result: Vec = Vec::with_capacity(max_size); + + loop { + match (lc.next(), rc.next()) { + (Some(l), Some(r)) => result.push(g(l, r)), + (Some(_), None) => { + return Err(Signal::Error(Error::NonRecyclableLengths( + result.len() + 1 + lc.count(), + result.len(), + ))); + } + (None, Some(_)) => { + return Err(Signal::Error(Error::NonRecyclableLengths( + result.len(), + result.len() + 1 + rc.count(), + ))); + } + (None, None) => return Ok(Rep::from(result)), + } + } + } } } + +fn try_binary_num_op( + lhs: Rep, + rhs: Rep, + f: F, +) -> Result, Signal> +where + L: Default + Clone + MinimallyNumeric + CoercibleInto, + R: Default + Clone + MinimallyNumeric + CoercibleInto, + C: Default + Clone, + (LNum, RNum): CommonNum, + Rep: From>, + O: Clone + Default, + F: Fn(C, C) -> O, + C: Clone + Default, +{ + try_recycle_then(lhs, rhs, |x, y| { + let (c1, c2) = ( + CoercibleInto::::coerce_into(x), + CoercibleInto::::coerce_into(y), + ) + .into_common(); + f(c1, c2) + }) +} + +// FIXME(performance): equality with references for characters +fn try_binary_cmp_op(lhs: Rep, rhs: Rep, f: F) -> Result, Signal> +where + L: AtomicMode + Default + Clone + CoercibleInto + Clone, + R: AtomicMode + Default + Clone + CoercibleInto + Clone, + (L, R): CommonCmp, + C: PartialOrd + Clone + Default, + F: Fn(Option) -> Logical, +{ + try_recycle_then(lhs, rhs, |x, y| { + let c1: C = x.coerce_into(); + let c2: C = y.coerce_into(); + let ordering = c1.partial_cmp(&c2); + f(ordering) + }) +} + +pub fn try_binary_lgl_op(lhs: Rep, rhs: Rep, f: F) -> Result, Signal> +where + L: AtomicMode + Default + Clone + CoercibleInto, + R: AtomicMode + Default + Clone + CoercibleInto, + F: Fn(Logical, Logical) -> Logical, +{ + try_recycle_then(lhs, rhs, |x, y| { + let (c1, c2) = ( + CoercibleInto::::coerce_into(x), + CoercibleInto::::coerce_into(y), + ); + f(c1, c2) + }) +} diff --git a/src/object/vector/reptype.rs b/src/object/vector/reptype.rs index 237e6f7..1b9f2f2 100644 --- a/src/object/vector/reptype.rs +++ b/src/object/vector/reptype.rs @@ -1,11 +1,10 @@ use std::fmt::Debug; -use super::coercion::{AtomicMode, CoercibleInto, CommonCmp, CommonNum, MinimallyNumeric}; -use super::iterators::{map_common_numeric, zip_recycle}; +use super::coercion::{AtomicMode, CoercibleInto}; use super::subset::Subset; use super::subsets::Subsets; use super::types::*; -use super::{OptionNA, Pow, VecPartialCmp}; +use super::OptionNA; use crate::error::Error; use crate::lang::Signal; use crate::object::{CowObj, ViewMut}; @@ -447,6 +446,14 @@ impl RepType { } } + pub fn iter_subset_indices_exact(&self) -> ExactIterSubsetIndices { + // TODO(performance): Avoid the vector allocation + let iter = self.iter_subset_indices(); + let len = iter.count(); + let iter = self.iter_subset_indices(); + ExactIterSubsetIndices { iter, len } + } + pub fn iter_subset_indices(&self) -> Box>> { match self.clone() { RepType::Subset(vals, subsets, maybe_naming) => { @@ -591,14 +598,44 @@ impl RepType { /// Assignment to a vector from another. The aggregate subsetted indices /// are iterated over while performing the assignment. /// - pub fn assign(&mut self, value: RepType) -> Self + pub fn assign(&mut self, value: RepType) -> Result where T: Clone + Default + From, R: Default + Clone, { - // TODO(feature): here we should also throw an error if the recycling rules are violated. - let l_indices = self.iter_subset_indices(); - let r_indices = value.iter_subset_indices(); + let l_indices = self.iter_subset_indices_exact(); + let mut r_indices = value.iter_subset_indices_exact(); + + // TODO(performance): When we clone the interior data of self (to which we write) + // we don't have to perform recycling checks + // and just start iterating. We can always discard the result afterwards again + // Maybe implement filter_exact on (named)subsets + if r_indices.len() == 1 { + // get the element from reptype value + let index = r_indices + .next() + .expect("index should exist") + .expect("No NA for subsetting"); + let elem = value.get_inner(index).expect("element should exist"); + match (self, value) { + (RepType::Subset(lv, ls, ln), RepType::Subset(..)) => { + lv.with_inner_mut(|lvb| { + for li in l_indices { + lvb[li.unwrap()] = elem.clone().into(); + } + }); + return Ok(RepType::Subset(lv.clone(), ls.clone(), ln.clone())); + } + } + } + + if l_indices.len() != r_indices.len() { + return Err(Signal::Error(Error::NonRecyclableLengths( + l_indices.len(), + r_indices.len(), + ))); + } + match (self, value) { (RepType::Subset(lv, ls, ln), RepType::Subset(rv, ..)) => { lv.with_inner_mut(|lvb| { @@ -614,7 +651,7 @@ impl RepType { } }); - RepType::Subset(lv.clone(), ls.clone(), ln.clone()) + Ok(RepType::Subset(lv.clone(), ls.clone(), ln.clone())) } } } @@ -741,30 +778,6 @@ impl RepType { self.as_mode::() } - pub fn vectorized_partial_cmp(self, other: RepType) -> Vec> - where - T: AtomicMode + Default + Clone + CoercibleInto, - R: AtomicMode + Default + Clone + CoercibleInto, - (T, R): CommonCmp, - C: PartialOrd, - { - let lc = self.inner().clone(); - let lb = lc.borrow(); - let lhs = lb.iter(); - - let rc = other.inner().clone(); - let rb = rc.borrow(); - let rhs = rb.iter(); - - zip_recycle(lhs, rhs) - .map(|(l, r)| { - let lc = CoercibleInto::::coerce_into(l.clone()); - let rc = CoercibleInto::::coerce_into(r.clone()); - lc.partial_cmp(&rc) - }) - .collect() - } - pub fn get_inner(&self, index: usize) -> Option { match self { RepType::Subset(v, subsets, maybe_naming) => { @@ -780,6 +793,24 @@ impl RepType { } } +pub struct ExactIterSubsetIndices { + iter: Box>>, + len: usize, +} + +impl ExactSizeIterator for ExactIterSubsetIndices { + fn len(&self) -> usize { + self.len + } +} + +impl Iterator for ExactIterSubsetIndices { + type Item = Option; + fn next(&mut self) -> Option { + self.iter.next() + } +} + impl TryInto for RepType> where OptionNA: AtomicMode + Clone + CoercibleInto>, @@ -893,344 +924,10 @@ where } } -impl std::ops::Neg for RepType -where - L: AtomicMode + Default + Clone + MinimallyNumeric + CoercibleInto, - LNum: std::ops::Neg, - RepType: From>, - O: Clone, -{ - type Output = RepType; - fn neg(self) -> Self::Output { - RepType::from( - self.inner() - .iter() - .map(|l| CoercibleInto::::coerce_into(l.clone()).neg()) - .collect::>(), - ) - } -} - -impl std::ops::Add> for RepType -where - L: AtomicMode + Default + Clone + MinimallyNumeric + CoercibleInto, - R: AtomicMode + Default + Clone + MinimallyNumeric + CoercibleInto, - (LNum, RNum): CommonNum, - C: Clone + std::ops::Add, - RepType: From>, -{ - type Output = RepType; - fn add(self, rhs: RepType) -> Self::Output { - let lc = self.inner().clone(); - let lb = lc.borrow(); - let lhs = lb.iter(); - - let rc = rhs.inner().clone(); - let rb = rc.borrow(); - let rhs = rb.iter(); - - RepType::from( - map_common_numeric(zip_recycle(lhs, rhs)) - .map(|(l, r)| l + r) - .collect::>(), - ) - } -} - -impl std::ops::Sub> for RepType -where - L: AtomicMode + Default + Clone + MinimallyNumeric + CoercibleInto, - R: AtomicMode + Default + Clone + MinimallyNumeric + CoercibleInto, - (LNum, RNum): CommonNum, - C: std::ops::Sub + Clone, - RepType: From>, - O: Clone, -{ - type Output = RepType; - fn sub(self, rhs: RepType) -> Self::Output { - let lc = self.inner().clone(); - let lb = lc.borrow(); - let lhs = lb.iter(); - - let rc = rhs.inner().clone(); - let rb = rc.borrow(); - let rhs = rb.iter(); - - RepType::from( - map_common_numeric(zip_recycle(lhs, rhs)) - .map(|(l, r)| l - r) - .collect::>(), - ) - } -} - -impl std::ops::Mul> for RepType -where - L: AtomicMode + Default + Clone + MinimallyNumeric + CoercibleInto, - R: AtomicMode + Default + Clone + MinimallyNumeric + CoercibleInto, - (LNum, RNum): CommonNum, - C: std::ops::Mul + Clone, - RepType: From>, -{ - type Output = RepType; - fn mul(self, rhs: RepType) -> Self::Output { - let lc = self.inner().clone(); - let lb = lc.borrow(); - let lhs = lb.iter(); - - let rc = rhs.inner().clone(); - let rb = rc.borrow(); - let rhs = rb.iter(); - - RepType::from( - map_common_numeric(zip_recycle(lhs, rhs)) - .map(|(l, r)| l * r) - .collect::>(), - ) - } -} - -impl std::ops::Div> for RepType -where - L: AtomicMode + Default + Clone + MinimallyNumeric + CoercibleInto, - R: AtomicMode + Default + Clone + MinimallyNumeric + CoercibleInto, - (LNum, RNum): CommonNum, - C: std::ops::Div + Clone, - RepType: From>, - O: Clone, -{ - type Output = RepType; - fn div(self, rhs: RepType) -> Self::Output { - let lc = self.inner().clone(); - let lb = lc.borrow(); - let lhs = lb.iter(); - - let rc = rhs.inner().clone(); - let rb = rc.borrow(); - let rhs = rb.iter(); - - RepType::from( - map_common_numeric(zip_recycle(lhs, rhs)) - .map(|(l, r)| l / r) - .collect::>(), - ) - } -} - -impl std::ops::Rem> for RepType -where - L: AtomicMode + Default + Clone + MinimallyNumeric + CoercibleInto, - R: AtomicMode + Default + Clone + MinimallyNumeric + CoercibleInto, - (LNum, RNum): CommonNum, - C: std::ops::Rem + Clone, - O: Clone, - RepType: From>, -{ - type Output = RepType; - fn rem(self, rhs: RepType) -> Self::Output { - let lc = self.inner().clone(); - let lb = lc.borrow(); - let lhs = lb.iter(); - - let rc = rhs.inner().clone(); - let rb = rc.borrow(); - let rhs = rb.iter(); - - RepType::from( - map_common_numeric(zip_recycle(lhs.into_iter(), rhs.into_iter())) - .map(|(l, r)| l.rem(r)) - .collect::>(), - ) - } -} - -impl Pow> for RepType -where - L: AtomicMode + Default + Clone + MinimallyNumeric + CoercibleInto, - R: AtomicMode + Default + Clone + MinimallyNumeric + CoercibleInto, - LNum: Pow, - RepType: From>, - O: Clone, -{ - type Output = RepType; - fn power(self, rhs: RepType) -> Self::Output { - let lc = self.inner().clone(); - let lb = lc.borrow(); - let lhs = lb.iter(); - - let rc = rhs.inner().clone(); - let rb = rc.borrow(); - let rhs = rb.iter(); - - RepType::from( - zip_recycle(lhs, rhs) - .map(|(l, r)| l.clone().coerce_into().power(r.clone().coerce_into())) - .collect::>(), - ) - } -} - -impl std::ops::BitOr> for RepType -where - L: AtomicMode + Default + Clone + CoercibleInto, - R: AtomicMode + Default + Clone + CoercibleInto, - Logical: std::ops::BitOr, - RepType: From>, - O: Clone, -{ - type Output = RepType; - fn bitor(self, rhs: RepType) -> Self::Output { - let lc = self.inner().clone(); - let lb = lc.borrow(); - let lhs = lb.iter(); - - let rc = rhs.inner().clone(); - let rb = rc.borrow(); - let rhs = rb.iter(); - - RepType::from( - zip_recycle(lhs, rhs) - .map(|(l, r)| l.clone().coerce_into().bitor(r.clone().coerce_into())) - .collect::>(), - ) - } -} - -impl std::ops::BitAnd> for RepType -where - L: AtomicMode + Default + Clone + CoercibleInto, - R: AtomicMode + Default + Clone + CoercibleInto, - Logical: std::ops::BitAnd, - RepType: From>, - O: Clone, -{ - type Output = RepType; - fn bitand(self, rhs: RepType) -> Self::Output { - let lc = self.inner().clone(); - let lb = lc.borrow(); - let lhs = lb.iter(); - - let rc = rhs.inner().clone(); - let rb = rc.borrow(); - let rhs = rb.iter(); - - RepType::from( - zip_recycle(lhs, rhs) - .map(|(l, r)| l.clone().coerce_into().bitand(r.clone().coerce_into())) - .collect::>(), - ) - } -} - -impl std::ops::Not for RepType -where - L: AtomicMode + Default + Clone + CoercibleInto, - Logical: std::ops::Not, - RepType: From>, - O: Clone, -{ - type Output = RepType; - fn not(self) -> Self::Output { - RepType::from( - self.inner() - .iter() - .map(|l| CoercibleInto::::coerce_into(l.clone()).not()) - .collect::>(), - ) - } -} - -impl VecPartialCmp> for RepType -where - L: AtomicMode + Default + Clone + CoercibleInto, - R: AtomicMode + Default + Clone + CoercibleInto, - (L, R): CommonCmp, - C: PartialOrd, -{ - type Output = RepType; - - fn vec_gt(self, rhs: RepType) -> Self::Output { - use std::cmp::Ordering::*; - self.vectorized_partial_cmp(rhs) - .into_iter() - .map(|i| match i { - Some(Greater) => OptionNA::Some(true), - Some(_) => OptionNA::Some(false), - None => OptionNA::NA, - }) - .collect::>() - .into() - } - - fn vec_gte(self, rhs: RepType) -> Self::Output { - use std::cmp::Ordering::*; - self.vectorized_partial_cmp(rhs) - .into_iter() - .map(|i| match i { - Some(Greater | Equal) => OptionNA::Some(true), - Some(_) => OptionNA::Some(false), - None => OptionNA::NA, - }) - .collect::>() - .into() - } - - fn vec_lt(self, rhs: RepType) -> Self::Output { - use std::cmp::Ordering::*; - self.vectorized_partial_cmp(rhs) - .into_iter() - .map(|i| match i { - Some(Less) => OptionNA::Some(true), - Some(_) => OptionNA::Some(false), - None => OptionNA::NA, - }) - .collect::>() - .into() - } - - fn vec_lte(self, rhs: RepType) -> Self::Output { - use std::cmp::Ordering::*; - self.vectorized_partial_cmp(rhs) - .into_iter() - .map(|i| match i { - Some(Less | Equal) => OptionNA::Some(true), - Some(_) => OptionNA::Some(false), - None => OptionNA::NA, - }) - .collect::>() - .into() - } - - fn vec_eq(self, rhs: RepType) -> Self::Output { - use std::cmp::Ordering::*; - self.vectorized_partial_cmp(rhs) - .into_iter() - .map(|i| match i { - Some(Equal) => OptionNA::Some(true), - Some(_) => OptionNA::Some(false), - None => OptionNA::NA, - }) - .collect::>() - .into() - } - - fn vec_neq(self, rhs: RepType) -> Self::Output { - use std::cmp::Ordering::*; - self.vectorized_partial_cmp(rhs) - .into_iter() - .map(|i| match i { - Some(Equal) => OptionNA::Some(false), - Some(_) => OptionNA::Some(true), - None => OptionNA::NA, - }) - .collect::>() - .into() - } -} - #[cfg(test)] mod test { use super::OptionNA::*; + use crate::object::rep::Rep; use crate::object::reptype::RepType; use crate::object::{types::*, OptionNA, VecPartialCmp}; use crate::r; @@ -1238,11 +935,11 @@ mod test { #[test] fn vector_add() { - let x = RepType::from((1..=10).collect::>()); - let y = RepType::from(vec![2, 5, 6, 2, 3]); + let x = Rep::::from((1..=5).collect::>()); + let y = Rep::::from(vec![2, 5, 6, 2, 3]); - let z = x + y; - assert_eq!(z, RepType::from(vec![3, 7, 9, 6, 8, 8, 12, 14, 11, 13])); + let z = (x + y).unwrap(); + assert_eq!(z, Rep::from(vec![3, 7, 9, 6, 8])); let expected_type = RepType::::new(); assert!(z.is_same_type_as(&expected_type)); @@ -1251,25 +948,11 @@ mod test { #[test] fn vector_mul() { - let x = RepType::from((1..=10).collect::>()); - let y = RepType::from(vec![Some(2), NA, Some(6), NA, Some(3)]); + let x = Rep::::from((1..=5).collect::>()); + let y = Rep::::from(vec![Some(2), NA, Some(6), NA, Some(3)]); - let z = x * y; - assert_eq!( - z, - RepType::from(vec![ - Some(2), - NA, - Some(18), - NA, - Some(15), - Some(12), - NA, - Some(48), - NA, - Some(30) - ]) - ); + let z = (x * y).unwrap(); + assert_eq!(z, Rep::from(vec![Some(2), NA, Some(18), NA, Some(15),])); let expected_type = RepType::::new(); assert!(z.is_same_type_as(&expected_type)); @@ -1281,10 +964,10 @@ mod test { // expect that f32's do not get coerced into an OptionNA:: instead // using std::f32::NAN as NA representation. - let x = RepType::from(vec![Some(0_f64), NA, Some(10_f64)]); - let y = RepType::from(vec![100, 10]); + let x = Rep::::from(vec![Some(0_f64), NA, Some(10_f64)]); + let y = Rep::::from(vec![100, 10, 1]); - let z = x * y; + let z = (x * y).unwrap(); // assert_eq!(z, Vector::from(vec![0_f32, std::f32::NAN, 1_000_f32])); // comparing floats is error prone @@ -1298,11 +981,11 @@ mod test { // expect that f32's do not get coerced into an OptionNA:: instead // using std::f32::NAN as NA representation. - let x = RepType::from(vec![Some(0_f64), NA, Some(10_f64)]); - let y = RepType::from(vec![100, 10]); + let x = Rep::::from(vec![Some(0_f64), NA, Some(10_f64)]); + let y = Rep::::from(vec![100, 10, 1]); - let z = x & y; - assert_eq!(z, RepType::from(vec![Some(false), NA, Some(true)])); + let z = (x & y).unwrap(); + assert_eq!(z, Rep::from(vec![Some(false), NA, Some(true)])); let expected_type = RepType::::new(); assert!(z.is_same_type_as(&expected_type)); @@ -1314,11 +997,11 @@ mod test { // expect that f32's do not get coerced into an instead // using std::f32::NAN as NA representation. - let x = RepType::from(vec![Some(0_f64), NA, Some(10000_f64)]); - let y = RepType::from(vec![100, 10]); + let x = Rep::from(vec![Some(0_f64), NA, Some(10000_f64)]); + let y = Rep::::from(vec![100, 10, 1]); - let z = x.vec_gt(y); - assert_eq!(z, RepType::from(vec![Some(false), NA, Some(true)])); + let z = x.vec_gt(y).unwrap(); + assert_eq!(z, Rep::from(vec![Some(false), NA, Some(true)])); let expected_type = RepType::::new(); assert!(z.is_same_type_as(&expected_type)); @@ -1522,4 +1205,87 @@ mod test { ); assert_eq!(x.next(), None); } + + use crate::error::Error; + use crate::lang::Signal; + + #[test] + fn assign_recycle_incompatible() { + let mut x = Rep::::from(vec![1, 2, 3]); + let y = Rep::::from(vec![99, 99]); + let result = x.assign(y); + assert_eq!( + result.unwrap_err(), + Signal::Error(Error::NonRecyclableLengths(3, 2)) + ); + } + #[test] + fn assign_recycle_length_one() { + let x = Rep::::from(vec![1, 2, 3]); + let y = Rep::::from(vec![99]); + let mut xview = x.subset(vec![0, 1].into()); + let _ = xview.assign(y).unwrap(); + let result_vec: Vec<_> = x.iter_values().collect(); + assert_eq!(result_vec, vec![Some(99), Some(99), Some(3)]) + } + #[test] + fn non_recyclable_lengths_3_2() { + let x = Rep::::from(vec![1, 2, 3]); + let y = Rep::::from(vec![99, 99]); + let result = x + y; + assert_eq!( + result.unwrap_err(), + Signal::Error(Error::NonRecyclableLengths(3, 2)) + ); + } + #[test] + fn non_recyclable_lengths_4_2() { + let x = Rep::::from(vec![1, 2, 3, 4]); + let y = Rep::::from(vec![99, 99]); + let result = x + y; + assert_eq!( + result.unwrap_err(), + Signal::Error(Error::NonRecyclableLengths(4, 2)) + ); + } + #[test] + fn non_recyclable_lengths_2_3() { + let x = Rep::::from(vec![1, 2]); + let y = Rep::::from(vec![99, 99, 99]); + let result = x + y; + assert_eq!( + result.unwrap_err(), + Signal::Error(Error::NonRecyclableLengths(2, 3)) + ); + } + #[test] + fn non_recyclable_lengths_2_4() { + let x = Rep::::from(vec![1, 2]); + let y = Rep::::from(vec![99, 99, 99, 99]); + let result = x + y; + assert_eq!( + result.unwrap_err(), + Signal::Error(Error::NonRecyclableLengths(2, 4)) + ); + } + #[test] + fn non_recyclable_lengths_0_1() { + let x = Rep::::from(Vec::::new()); + let y = Rep::::from(vec![99]); + let result = x + y; + assert_eq!( + result.unwrap_err(), + Signal::Error(Error::NonRecyclableLengths(0, 1)) + ); + } + #[test] + fn non_recyclable_lengths_1_0() { + let x = Rep::::from(vec![99]); + let y = Rep::::from(Vec::::new()); + let result = x + y; + assert_eq!( + result.unwrap_err(), + Signal::Error(Error::NonRecyclableLengths(1, 0)) + ); + } }