#![doc = include_str!("../README.MD")]
#![warn(missing_docs)]
#![deny(warnings)]
#![allow(private_bounds)]
use std::{
collections::HashMap,
fmt::{Debug, Formatter},
ops::Deref,
sync::{atomic::AtomicBool, Arc, Mutex, RwLock, RwLockReadGuard},
};
use linked_hash_map::LinkedHashMap;
pub(crate) fn next_id() -> u64 {
static ID_GENERATOR: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
ID_GENERATOR.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
}
pub trait ComputationValueType {
type Type: 'static + Send + Sync;
}
trait ComputationValueInterface: ComputationValueType {
fn id(&self) -> u64;
fn prepare_value(&self) -> (impl ComputationGetter<Self::Type>, Arc<Dependency>);
}
trait ComputationGetter<T>: Sync + Send + 'static {
type ValueRef<'a>: Deref<Target = T>;
fn value_ref<'a>(&'a self) -> Self::ValueRef<'a>;
}
impl<T> ComputationGetter<T> for RwLock<T>
where
T: Sync + Send + 'static,
{
type ValueRef<'a> = RwLockReadGuard<'a, T>;
fn value_ref<'a>(&'a self) -> Self::ValueRef<'a> {
self.read().unwrap()
}
}
impl<C, T> ComputationGetter<T> for Arc<C>
where
C: ComputationGetter<T>,
{
type ValueRef<'a> = C::ValueRef<'a>;
fn value_ref<'a>(&'a self) -> Self::ValueRef<'a> {
self.deref().value_ref()
}
}
macro_rules! generate_traits_for_computation_getters {
($name:tt) => {
impl<T> PartialEq for $name<T>
where
T: PartialEq + Send + Sync,
{
fn eq(&self, other: &Self) -> bool {
self.value_ref().eq(&other.value_ref())
}
}
impl<T> PartialOrd for $name<T>
where
T: PartialOrd + Send + Sync,
{
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.value_ref().partial_cmp(&other.value_ref())
}
}
};
}
generate_traits_for_computation_getters!(Computation);
generate_traits_for_computation_getters!(VecComputation);
generate_traits_for_computation_getters!(Variable);
struct Dependency {
key: u64,
dependencies: RwLock<HashMap<u64, Arc<Dependency>>>,
dependents: RwLock<HashMap<u64, Arc<Dependency>>>,
outdated: AtomicBool,
}
impl Debug for Dependency {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("")
.field("key", &self.key)
.field("dependencies", &self.dependencies)
.field("dependends count", &self.dependents.read().unwrap().len())
.finish()
}
}
impl Dependency {
fn new() -> Arc<Self> {
Arc::new(Self {
key: next_id(),
dependencies: Default::default(),
dependents: Default::default(),
outdated: AtomicBool::new(false),
})
}
fn notify_all(&self) {
self.outdated
.store(true, std::sync::atomic::Ordering::Relaxed);
for (_, dep) in self.dependencies.read().unwrap().iter() {
dep.notify_all();
}
}
fn insert_dependency(&self, d: Arc<Dependency>) {
self.dependencies.write().unwrap().insert(d.key, d);
}
fn insert_dependent(&self, d: Arc<Dependency>) {
self.dependents.write().unwrap().insert(d.key, d);
}
fn remove_dependency(&self, key: u64) {
if let Some(dep) = self.dependencies.write().unwrap().remove(&key) {
dep.dependents.write().unwrap().remove(&self.key);
}
}
}
trait ComputationBuilder<T> {
type FunctorArgument<'a>;
fn prepare<'b>(
&self,
dependency: &Arc<Dependency>,
formula: impl for<'a> Fn(Self::FunctorArgument<'a>) -> T + 'b + Send + Sync,
) -> impl Fn() -> T + 'b + Send + Sync;
}
trait ComputationVecBuilder<T> {
fn prepare(
&self,
dependency: &Arc<Dependency>,
) -> LinkedHashMap<u64, Box<dyn VariableReader<T>>>;
}
macro_rules! computation_builder {
($($type_idx: tt),*) => {
paste::paste!{
impl<'b, T, $( [<VT $type_idx>], )*> ComputationBuilder<T>
for ($(&'b [<VT $type_idx>], )*)
where
$(
[<VT $type_idx>]: ComputationValueInterface,
)*
{
type FunctorArgument<'a> = ( $(&'a [<VT $type_idx>]::Type, )*);
fn prepare<'c>(
&self,
dependency: &Arc<Dependency>,
formula: impl for<'a> Fn(Self::FunctorArgument<'a>) -> T + 'c + Send + Sync,
) -> impl Fn() -> T + 'c + Send + Sync {
$(
let ([<v $type_idx>], [<dep $type_idx>]) = self.$type_idx.prepare_value();
[<dep $type_idx>].insert_dependency(dependency.clone());
dependency.insert_dependent([<dep $type_idx>]);
)*
move || {
$(
let [<v $type_idx>] = [<v $type_idx>].value_ref();
)*
formula(($( [<v $type_idx>].deref(), )*))
}
}
}
impl<'b, T, $( [<VT $type_idx>], )*> ComputationVecBuilder<T>
for ($(&'b [<VT $type_idx>], )*)
where
$(
[<VT $type_idx>]: ComputationValueInterface<Type = T>,
)*
T: Sync + Send + 'static,
{
fn prepare(&self,
dependency: &Arc<Dependency>) -> LinkedHashMap<u64, Box<dyn VariableReader<T>>>
{
let mut variables: LinkedHashMap<u64, Box<dyn VariableReader<T>>> = Default::default();
$(
let ([<v $type_idx>], [<dep $type_idx>]) = self.$type_idx.prepare_value();
let id = [<dep $type_idx>].key;
[<dep $type_idx>].insert_dependency(dependency.clone());
dependency.insert_dependent([<dep $type_idx>]);
let [<v $type_idx>]: Box<dyn VariableReader<T>> = Box::new([<v $type_idx>]);
variables.insert(id, [<v $type_idx>]);
)*
variables
}
}
}
};
}
macro_rules! computation_builder_reversed {
({} {$($reversed:tt),*}) => {
computation_builder!($($reversed),*);
};
({$first:tt $(, $rest:tt)*} {$($reversed:tt),*}) => {
computation_builder_reversed!({$($rest),*} {$first $(, $reversed)*}); };
}
macro_rules! computation_builders {
($first_type_idx: tt) => {
computation_builder!($first_type_idx);
};
($first_type_idx: tt, $($type_idx: tt),*) => {
computation_builder_reversed!({$($type_idx),*} {$first_type_idx});
computation_builders!($($type_idx),*);
}
}
computation_builders!(20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0);
impl<'b, T> ComputationVecBuilder<T> for ()
where
T: Sync + Send + 'static,
{
fn prepare(&self, _: &Arc<Dependency>) -> LinkedHashMap<u64, Box<dyn VariableReader<T>>> {
Default::default()
}
}
impl<T, VT> ComputationBuilder<T> for Vec<&VT>
where
VT: ComputationValueInterface<Type = T>,
T: Sync + Send + 'static,
{
type FunctorArgument<'a> = Vec<&'a VT::Type>;
fn prepare<'b>(
&self,
dependency: &Arc<Dependency>,
formula: impl for<'a> Fn(Self::FunctorArgument<'a>) -> T + 'b + Send + Sync,
) -> impl Fn() -> T + 'b + Send + Sync {
let variables = <Vec<&VT> as ComputationVecBuilder<T>>::prepare(self, dependency);
move || {
let unlocked_vars: Vec<_> = variables.iter().map(|(_, v)| v.read_variable()).collect();
let values: Vec<&T> = unlocked_vars.iter().map(|uv| -> &T { uv }).collect();
formula(values)
}
}
}
impl<T, VT> ComputationVecBuilder<T> for Vec<&VT>
where
VT: ComputationValueInterface<Type = T>,
T: Sync + Send + 'static,
{
fn prepare(
&self,
dependency: &Arc<Dependency>,
) -> LinkedHashMap<u64, Box<dyn VariableReader<T>>> {
self.into_iter()
.map(|v| {
let (getter, v_dependency) = v.prepare_value();
let id = v_dependency.key;
v_dependency.insert_dependency(dependency.clone());
dependency.insert_dependent(v_dependency);
let vm: Box<dyn VariableReader<T>> = Box::new(getter);
(id, vm)
})
.collect()
}
}
struct ComputationData<T>
where
T: Send + Sync + 'static,
{
result: Arc<RwLock<T>>,
formula: Arc<Mutex<Box<dyn Fn() -> T + Sync + Send + 'static>>>,
dependency: Arc<Dependency>,
}
impl<T> Debug for ComputationData<T>
where
T: Debug + Sync + Send + 'static,
{
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("").field("result", &self.result).finish()
}
}
impl<T> Clone for ComputationData<T>
where
T: Send + Sync + 'static,
{
fn clone(&self) -> Self {
Self {
result: self.result.clone(),
formula: self.formula.clone(),
dependency: self.dependency.clone(),
}
}
}
#[derive(Debug)]
pub struct Computation<T>
where
T: Send + Sync + 'static,
{
data: ComputationData<T>,
}
impl<T> ComputationValueType for Computation<T>
where
T: Send + Sync + 'static,
{
type Type = T;
}
impl<T> ComputationValueInterface for Computation<T>
where
T: Send + Sync + 'static,
{
fn id(&self) -> u64 {
self.data.dependency.key
}
fn prepare_value(&self) -> (impl ComputationGetter<Self::Type>, Arc<Dependency>) {
(self.data.clone(), self.data.dependency.clone())
}
}
impl<T> ComputationGetter<T> for ComputationData<T>
where
T: Sync + Send + 'static,
{
type ValueRef<'a> = RwLockReadGuard<'a, T>;
fn value_ref<'a>(&'a self) -> Self::ValueRef<'a> {
if self
.dependency
.outdated
.load(std::sync::atomic::Ordering::Relaxed)
{
self.dependency
.outdated
.store(false, std::sync::atomic::Ordering::Relaxed);
*self.result.write().unwrap() = (self.formula.lock().unwrap())();
}
self.result.read().unwrap()
}
}
impl<T> Computation<T>
where
T: Sync + Send + 'static,
{
pub fn new<TVars>(
formula: impl for<'a> Fn(TVars::FunctorArgument<'a>) -> T + 'static + Send + Sync,
tvars: TVars,
) -> Self
where
TVars: ComputationBuilder<T>,
{
let dependency = Dependency::new();
let formula = tvars.prepare(&dependency, formula);
let result = Arc::new(RwLock::new((formula)()));
Self {
data: ComputationData::<T> {
result,
formula: Arc::new(Mutex::new(Box::new(formula))),
dependency,
},
}
}
pub fn value_ref<'a>(&'a self) -> impl Deref<Target = T> + 'a {
self.data.value_ref()
}
}
trait VariableReader<C>: Send + Sync + 'static
where
C: Send + Sync + 'static,
{
fn read_variable(&self) -> Box<dyn Deref<Target = C> + '_>;
}
impl<T, CG> VariableReader<T> for CG
where
CG: ComputationGetter<T>,
T: Send + Sync + 'static,
{
fn read_variable(&self) -> Box<dyn Deref<Target = T> + '_> {
Box::new(self.value_ref())
}
}
struct VecComputationData<T, C>
where
T: Send + Sync + 'static,
C: Send + Sync + 'static,
{
result: Arc<RwLock<T>>,
variables: Arc<RwLock<LinkedHashMap<u64, Box<dyn VariableReader<C>>>>>,
formula: Arc<Mutex<Box<dyn for<'a> Fn(Vec<&'a C>) -> T + Send + Sync>>>,
dependency: Arc<Dependency>,
}
impl<T, C> Debug for VecComputationData<T, C>
where
T: Send + Sync + 'static + Debug,
C: Send + Sync + 'static + Debug,
{
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("")
.field("result", &self.result)
.field("dependency", &self.dependency)
.finish()
}
}
impl<T, C> Clone for VecComputationData<T, C>
where
T: Send + Sync + 'static,
C: Send + Sync + 'static,
{
fn clone(&self) -> Self {
Self {
result: self.result.clone(),
variables: self.variables.clone(),
formula: self.formula.clone(),
dependency: self.dependency.clone(),
}
}
}
impl<T, C> ComputationGetter<T> for VecComputationData<T, C>
where
T: Sync + Send + 'static,
C: Send + Sync + 'static,
{
type ValueRef<'a> = RwLockReadGuard<'a, T>;
fn value_ref<'a>(&'a self) -> Self::ValueRef<'a> {
if self
.dependency
.outdated
.load(std::sync::atomic::Ordering::Relaxed)
{
self.dependency
.outdated
.store(false, std::sync::atomic::Ordering::Relaxed);
let variables = self.variables.read().unwrap();
let unlocked_vars: Vec<_> = variables.iter().map(|(_, v)| v.read_variable()).collect();
let values: Vec<&C> = unlocked_vars.iter().map(|uv| -> &C { uv }).collect();
*self.result.write().unwrap() = (self.formula.lock().unwrap())(values);
}
self.result.read().unwrap()
}
}
#[derive(Debug)]
pub struct VecComputation<T, C = T>
where
T: Send + Sync + 'static,
C: Send + Sync + 'static,
{
data: VecComputationData<T, C>,
}
impl<T, C> ComputationValueType for VecComputation<T, C>
where
T: Send + Sync + 'static,
C: Send + Sync + 'static,
{
type Type = T;
}
impl<T, C> ComputationValueInterface for VecComputation<T, C>
where
T: Send + Sync + 'static,
C: Send + Sync + 'static,
{
fn id(&self) -> u64 {
self.data.dependency.key
}
fn prepare_value(&self) -> (impl ComputationGetter<Self::Type>, Arc<Dependency>) {
(self.data.clone(), self.data.dependency.clone())
}
}
impl<T, C> VecComputation<T, C>
where
T: Send + Sync + 'static,
C: Send + Sync + 'static,
{
pub fn new<TVars>(
formula: impl for<'a> Fn(Vec<&'a C>) -> T + 'static + Send + Sync,
tvars: TVars,
) -> Self
where
TVars: ComputationVecBuilder<C>,
{
let dependency = Dependency::new();
let variables = tvars.prepare(&dependency);
let result = Arc::new(RwLock::new({
let unlocked_vars: Vec<_> = variables.iter().map(|(_, v)| v.read_variable()).collect();
let values: Vec<&C> = unlocked_vars.iter().map(|uv| -> &C { uv }).collect();
(formula)(values)
}));
let variables = Arc::new(RwLock::new(variables));
Self {
data: VecComputationData::<T, C> {
result,
variables,
formula: Arc::new(Mutex::new(Box::new(formula))),
dependency,
},
}
}
pub fn value_ref<'a>(&'a self) -> impl Deref<Target = T> + 'a {
self.data.value_ref()
}
pub fn push(&mut self, var: &impl ComputationValueInterface<Type = C>) {
let (getter, dep) = var.prepare_value();
let id = dep.key;
dep.insert_dependency(self.data.dependency.clone());
self.data.dependency.insert_dependent(dep);
self.data
.variables
.write()
.unwrap()
.insert(id, Box::new(getter));
self.data.dependency.notify_all();
}
pub fn remove(&mut self, var: &impl ComputationValueInterface) {
let id = var.id();
self.data.variables.write().unwrap().remove(&id);
self.data.dependency.remove_dependency(id);
self.data.dependency.notify_all();
}
}
impl<T, C> VecComputation<T, C>
where
T: Default + std::ops::Add<Output = T> + Sync + Send,
C: Into<T> + Clone + Sync + Send,
{
pub fn sum<TVars>(tvars: TVars) -> Self
where
TVars: ComputationVecBuilder<C>,
{
Self::new(
|a| {
a.iter()
.fold(T::default(), |a, b| a + <C as Into<T>>::into((**b).clone()))
as T
},
tvars,
)
}
}
impl<T, C> VecComputation<T, C>
where
T: Default + for<'a> std::ops::Add<&'a T, Output = T> + Sync + Send,
for<'a> &'a C: Into<&'a T>,
C: Sync + Send,
{
pub fn sum_ref<TVars>(tvars: TVars) -> Self
where
TVars: ComputationVecBuilder<C>,
{
Self::new(
|a| {
a.iter()
.fold(T::default(), |a, b| a + <&C as Into<&T>>::into(*b)) as T
},
tvars,
)
}
}
impl<T, C> VecComputation<T, C>
where
T: Default
+ std::ops::Add<Output = T>
+ TryFrom<usize>
+ std::ops::Div<Output = T>
+ Sync
+ Send,
C: Into<T> + Clone + Sync + Send,
<T as TryFrom<usize>>::Error: Debug,
{
pub fn average<TVars>(tvars: TVars) -> Self
where
TVars: ComputationVecBuilder<C>,
{
Self::new(
|a| {
a.iter()
.fold(T::default(), |a, b| a + <C as Into<T>>::into((**b).clone()))
/ (<usize as TryInto<T>>::try_into(a.len()).expect("usize overflow"))
},
tvars,
)
}
}
#[derive(Debug)]
pub struct Variable<T>
where
T: Send + Sync,
{
t: Arc<RwLock<T>>,
dependency: Arc<Dependency>,
}
impl<T> Default for Variable<T>
where
T: Default + Send + Sync,
{
fn default() -> Self {
Self {
t: Arc::<RwLock<T>>::default(),
dependency: Dependency::new(),
}
}
}
impl<T> From<T> for Variable<T>
where
T: Send + Sync,
{
fn from(value: T) -> Self {
Self::new(value)
}
}
impl<T> ComputationValueType for Variable<T>
where
T: Send + Sync + 'static,
{
type Type = T;
}
impl<T> ComputationValueInterface for Variable<T>
where
T: Send + Sync + 'static,
{
fn id(&self) -> u64 {
self.dependency.key
}
fn prepare_value(&self) -> (impl ComputationGetter<Self::Type>, Arc<Dependency>) {
(self.t.clone(), self.dependency.clone())
}
}
impl<T> Variable<T>
where
T: Send + Sync,
{
pub fn new(t: T) -> Self {
Self {
t: Arc::new(RwLock::new(t)),
dependency: Dependency::new(),
}
}
pub fn set(&self, t: T) {
*self.t.write().unwrap() = t;
self.dependency.notify_all();
}
pub fn value_ref(&self) -> RwLockReadGuard<'_, T> {
self.t.read().unwrap()
}
pub fn update(&self, f: impl Fn(&mut T)) {
f(&mut self.t.write().unwrap());
self.dependency.notify_all();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn static_computation() {
let v1 = Variable::<u32>::new(1);
let v2 = Variable::<u32>::new(2);
let v3 = Variable::<u32>::new(3);
let c1 = Computation::<u32>::new(|(a, b, c)| a + b + c, (&v1, &v2, &v3));
assert_eq!(*c1.value_ref(), 6);
v1.set(4);
assert_eq!(*c1.value_ref(), 9);
v3.update(|x| *x -= 3);
assert_eq!(*c1.value_ref(), 6);
}
#[test]
fn heterogenous_computation() {
let v1 = Variable::<u16>::new(1);
let v2 = Variable::<u32>::new(2);
let v3 = Variable::<f32>::new(3.0);
let c1 = Computation::<f64>::new(
|(a, b, c)| *a as f64 + *b as f64 + *c as f64,
(&v1, &v2, &v3),
);
assert_eq!(*c1.value_ref(), 6.0);
v1.set(4);
assert_eq!(*c1.value_ref(), 9.0);
v3.update(|x| *x -= 3.0);
assert_eq!(*c1.value_ref(), 6.0);
}
#[test]
fn intermediary_computation() {
let v1 = Variable::<u16>::new(1);
let v2 = Variable::<u32>::new(2);
let v3 = Variable::<f32>::new(3.0);
let c1 = Computation::<f64>::new(
|(a, b, c)| *a as f64 + *b as f64 + *c as f64,
(&v1, &v2, &v3),
);
assert_eq!(*c1.value_ref(), 6.0);
let v4 = Variable::<f64>::new(5.0);
let c2 = Computation::<f64>::new(|(a, b)| a + b, (&c1, &v4));
assert_eq!(*c2.value_ref(), 11.0);
v1.set(4);
assert_eq!(*c1.value_ref(), 9.0);
assert_eq!(*c2.value_ref(), 14.0);
v3.update(|x| *x -= 3.0);
assert_eq!(*c1.value_ref(), 6.0);
assert_eq!(*c2.value_ref(), 11.0);
v4.set(-1.0);
assert_eq!(*c1.value_ref(), 6.0);
assert_eq!(*c2.value_ref(), 5.0);
v1.set(0);
assert_eq!(*c2.value_ref(), 1.0);
}
#[test]
fn vec_computation() {
let v1 = Variable::<u32>::new(1);
let v2 = Variable::<u32>::new(2);
let v3 = Variable::<u32>::new(3);
let mut c1 = VecComputation::<u32>::average(vec![&v1, &v2, &v3]);
assert_eq!(*c1.value_ref(), 2);
let v4 = Variable::<u32>::new(10);
c1.push(&v4);
assert_eq!(*c1.value_ref(), 4);
c1.remove(&v1);
c1.remove(&v3);
assert_eq!(*c1.value_ref(), 6);
v1.set(4);
v3.set(2);
assert_eq!(*c1.value_ref(), 6);
v4.set(2);
assert_eq!(*c1.value_ref(), 2);
v2.set(0);
assert_eq!(*c1.value_ref(), 1);
let c2 = VecComputation::<u32>::sum(vec![&v1, &v2, &v3]);
assert_eq!(*c2.value_ref(), 6);
}
#[test]
fn vec_intermediary_computation() {
let v1 = Variable::<u32>::new(1);
let v2 = Variable::<u32>::new(2);
let v3 = Variable::<u32>::new(3);
let mut c1 = VecComputation::<u32>::average(vec![&v1, &v2, &v3]);
assert_eq!(*c1.value_ref(), 2);
let v5 = Variable::<f32>::new(4.0);
let c2 = Computation::<f32>::new(|(a, b)| *a as f32 + b, (&c1, &v5));
assert_eq!(*c2.value_ref(), 6.0);
let v6 = Variable::<f32>::new(2.0);
let mut c3 = VecComputation::<f32>::sum((&c2, &v6));
let v4 = Variable::<u32>::new(10);
c1.push(&v4);
assert_eq!(*c1.value_ref(), 4);
assert_eq!(*c2.value_ref(), 8.0);
assert_eq!(*c3.value_ref(), 10.0);
c1.remove(&v1);
c1.remove(&v3);
assert_eq!(*c1.value_ref(), 6);
assert_eq!(*c2.value_ref(), 10.0);
assert_eq!(*c3.value_ref(), 12.0);
v1.set(4);
v3.set(2);
assert_eq!(*c1.value_ref(), 6);
assert_eq!(*c2.value_ref(), 10.0);
assert_eq!(*c3.value_ref(), 12.0);
v4.set(2);
assert_eq!(*c1.value_ref(), 2);
assert_eq!(*c2.value_ref(), 6.0);
assert_eq!(*c3.value_ref(), 8.0);
v2.set(0);
assert_eq!(*c1.value_ref(), 1);
assert_eq!(*c2.value_ref(), 5.0);
assert_eq!(*c3.value_ref(), 7.0);
let v7 = Variable::<f32>::new(1.0);
c3.push(&v7);
assert_eq!(*c1.value_ref(), 1);
assert_eq!(*c2.value_ref(), 5.0);
assert_eq!(*c3.value_ref(), 8.0);
c3.remove(&v6);
assert_eq!(*c1.value_ref(), 1);
assert_eq!(*c2.value_ref(), 5.0);
assert_eq!(*c3.value_ref(), 6.0);
c3.remove(&c2);
assert_eq!(*c1.value_ref(), 1);
assert_eq!(*c2.value_ref(), 5.0);
assert_eq!(*c3.value_ref(), 1.0);
}
#[test]
fn test_traits() {
let v1: Variable<i32> = 0.into();
let v2: Variable<i32> = 1.into();
let c1 = Computation::new(|(v1, v2)| *v1 + *v2, (&v1, &v2));
let c2 = Computation::new(|(v1, v2)| *v1 - *v2, (&v1, &v2));
let c3 = VecComputation::new(|v| *v[0] + *v[1], (&v1, &v2));
let c4 = VecComputation::new(|v| *v[0] - *v[1], vec![&v1, &v2]);
let c5 = VecComputation::new(|v| *v[0] - *v[1], (&v1, &v2));
assert_eq!(*c1.value_ref(), 1);
assert_eq!(*c2.value_ref(), -1);
assert_eq!(*c3.value_ref(), 1);
assert_eq!(*c4.value_ref(), -1);
assert_eq!(*c5.value_ref(), -1);
assert!(v1 != v2);
assert!(v1 < v2);
assert!(c1 != c2);
assert!(c1 > c2);
assert!(c3 != c4);
assert!(c3 > c4);
assert!(c3 != c5);
assert!(c3 > c5);
assert!(c4 == c5);
}
#[test]
fn test_vec_order() {
let v1: Variable<i32> = 0.into();
let v2: Variable<i32> = 1.into();
let v3: Variable<i32> = 2.into();
let v4: Variable<i32> = 3.into();
let v5: Variable<i32> = 4.into();
let v6: Variable<i32> = 5.into();
let mut c1 = VecComputation::new(|v| v.windows(2).all(|w| w[0] < w[1]), (&v1, &v2));
assert_eq!(*c1.value_ref(), true);
c1.push(&v3);
c1.push(&v4);
assert_eq!(*c1.value_ref(), true);
c1.remove(&v4);
assert_eq!(*c1.value_ref(), true);
c1.push(&v5);
assert_eq!(*c1.value_ref(), true);
c1.push(&v6);
assert_eq!(*c1.value_ref(), true);
c1.remove(&v3);
assert_eq!(*c1.value_ref(), true);
c1.remove(&v6);
assert_eq!(*c1.value_ref(), true);
c1.push(&v4);
assert_eq!(*c1.value_ref(), false);
c1.push(&v6);
assert_eq!(*c1.value_ref(), false);
c1.remove(&v4);
assert_eq!(*c1.value_ref(), true);
}
}