diff --git a/Cargo.toml b/Cargo.toml index f1fa723..ed305dd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,9 @@ [package] name = "orx-imp-vec" -version = "2.7.0" +version = "2.8.0" edition = "2021" authors = ["orxfun "] -description = "`ImpVec`, stands for immutable push vector 👿, is a data structure which allows appending elements with a shared reference." +description = "`ImpVec` stands for immutable push vector 👿, it is a data structure which allows appending elements with a shared reference." license = "MIT" repository = "https://github.com/orxfun/orx-imp-vec/" keywords = ["vec", "pinned", "bag", "container", "split"] diff --git a/README.md b/README.md index ec427c7..eca0fec 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![orx-imp-vec crate](https://img.shields.io/crates/v/orx-imp-vec.svg)](https://crates.io/crates/orx-imp-vec) [![orx-imp-vec documentation](https://docs.rs/orx-imp-vec/badge.svg)](https://docs.rs/orx-imp-vec) -`ImpVec`, stands for immutable push vector 👿, is a data structure which allows appending elements with a shared reference. +`ImpVec` stands for immutable push vector 👿, it is a data structure which allows appending elements with a shared reference. Specifically, it extends vector capabilities with the following two methods: * [`fn imp_push(&self, value: T)`](https://docs.rs/orx-imp-vec/latest/orx_imp_vec/struct.ImpVec.html#method.imp_push) @@ -196,6 +196,9 @@ assert_eq!( +You may find another demonstration where an `ImpVec` mimics a scope in the [system_of_linear_inequalities.rs](https://github.com/orxfun/orx-imp-vec/blob/main/examples/system_of_linear_inequalities.rs) example. + +Finally, you may find the initial motivation of this crate and the `ImpVec` type in [imp-vec-motivation](https://orxfun.github.io/orxfun-notes/#/imp-vec-motivation-2024-10-03) article. ## Safety diff --git a/examples/system_of_linear_inequalities.rs b/examples/system_of_linear_inequalities.rs new file mode 100644 index 0000000..7a2c7d6 --- /dev/null +++ b/examples/system_of_linear_inequalities.rs @@ -0,0 +1,207 @@ +use orx_imp_vec::*; +use std::{ + fmt::{Display, Formatter, Result}, + ops::{Add, Index, Mul}, +}; + +/// # Scope +/// +/// It is a bag of things, +/// analogous to variables, expressions, etc. defined +/// in the scope of a code block. +#[derive(Default)] +struct Scope<'a> { + vectors: ImpVec>, + exprs: ImpVec>, + terms: ImpVec>, + vars: ImpVec>, +} + +impl<'a> Scope<'a> { + fn same_scope_as(&self, other: &Self) -> bool { + self as *const Self == other as *const Self + } +} + +impl<'a> Scope<'a> { + fn new_var_vec(&'a self, symbol: &str) -> &'a Vector<'a> { + self.vectors.imp_push_get_ref(Vector { + scope: self, + symbol: symbol.to_string(), + }) + } +} + +/// # VarVec +struct Vector<'a> { + scope: &'a Scope<'a>, + symbol: String, +} + +impl<'a> Display for Vector<'a> { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + write!(f, "{}", self.symbol) + } +} + +impl<'a> Index for &'a Vector<'a> { + type Output = Var<'a>; + + fn index(&self, index: usize) -> &Self::Output { + self.scope.vars.imp_push_get_ref(Var { + scope: self.scope, + var_vec: self, + index, + }) + } +} + +/// # Expr +struct Expr<'a> { + scope: &'a Scope<'a>, + terms: Vec<&'a Term<'a>>, +} + +impl<'a> Display for Expr<'a> { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + let mut terms = self.terms.iter(); + if let Some(term) = terms.next() { + write!(f, "{}", term)?; + for term in terms { + write!(f, " + {}", term)?; + } + } + Ok(()) + } +} + +impl<'a> Add<&'a Term<'a>> for &'a Expr<'a> { + type Output = &'a Expr<'a>; + + fn add(self, rhs: &'a Term<'a>) -> Self::Output { + assert!(self.scope.same_scope_as(rhs.scope)); + + let mut terms = self.terms.clone(); + terms.push(rhs); + self.scope.exprs.imp_push_get_ref(Expr { + scope: self.scope, + terms, + }) + } +} + +/// # Term +#[derive(Clone, Copy)] +struct Term<'a> { + scope: &'a Scope<'a>, + coef: i64, + var: Var<'a>, +} + +impl<'a> Display for Term<'a> { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + write!(f, "{}.{}", self.coef, self.var) + } +} + +impl<'a> Add<&'a Term<'a>> for &'a Term<'a> { + type Output = &'a Expr<'a>; + + fn add(self, rhs: &'a Term<'a>) -> Self::Output { + assert!(self.scope.same_scope_as(rhs.scope)); + + self.scope.exprs.imp_push_get_ref(Expr { + scope: self.scope, + terms: vec![self, rhs], + }) + } +} + +/// # Var +#[derive(Clone, Copy)] +struct Var<'a> { + scope: &'a Scope<'a>, + var_vec: &'a Vector<'a>, + index: usize, +} + +impl<'a> Display for Var<'a> { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + write!(f, "{}[{}]", self.var_vec, self.index) + } +} + +impl<'a> Mul> for i64 { + type Output = &'a Term<'a>; + + fn mul(self, rhs: Var<'a>) -> Self::Output { + rhs.scope.terms.imp_push(Term { + scope: rhs.scope, + coef: self, + var: rhs, + }); + &rhs.scope.terms[rhs.scope.terms.len() - 1] + } +} + +#[allow(unused_variables)] +fn main() { + /// Breakdown of types + fn break_down_of_types() { + let scope = Scope::default(); + + let x: &Vector = scope.new_var_vec("x"); + + let x0: Var = x[0]; + let x1: Var = x[1]; + + let t1: &Term = 3 * x[0]; + let t2: &Term = 4 * x[1]; + + let le: &Expr = t1 + t2; // or + let le: &Expr = 3 * x[0] + 4 * x[1]; + } + + /// Challenge #1 + /// x: VarVec being a symbolic vector of variables, + /// we need x[i] operator to create a new value of the scalar type + /// Var and return a reference to it for an arbitrary index i. + fn challenge1() { + let scope = Scope::default(); + + let x: &Vector = scope.new_var_vec("x"); + + let x0: Var = x[0]; + let x1: Var = x[1]; + } + + /// Challenge #2 + /// It is very important to have the symbolic types Var and Term + /// to implement the Copy trait to achieve the desired expressive api. + fn challenge2() { + let scope = Scope::default(); + + let x: &Vector = scope.new_var_vec("x"); + + let t = 42 * x[0]; + let e1 = t + 3 * x[1]; + let e2 = t + 2 * x[0]; + } + + /// The Goal + fn the_goal() { + let scope = Scope::default(); + + let x = scope.new_var_vec("x"); + + let le = 3 * x[0] + 4 * x[1]; + + assert_eq!(&le.to_string(), "3.x[0] + 4.x[1]"); + println!("{}", le); + } + + break_down_of_types(); + challenge1(); + challenge2(); + the_goal(); +} diff --git a/examples/vector_var_imp_vec.rs b/examples/vector_var_imp_vec.rs new file mode 100644 index 0000000..80948ff --- /dev/null +++ b/examples/vector_var_imp_vec.rs @@ -0,0 +1,66 @@ +use orx_imp_vec::*; +use std::fmt::{Display, Formatter, Result}; +use std::ops::Index; + +struct Vector<'a> { + symbol: String, + created_vars: ImpVec>, +} + +impl<'a> Vector<'a> { + fn new(symbol: &str) -> Self { + Self { + symbol: symbol.into(), + created_vars: Default::default(), + } + } +} + +impl<'a> Index for &'a Vector<'a> { + type Output = Var<'a>; + + fn index(&self, index: usize) -> &Self::Output { + let var = Var { + index, + vector: self, + }; + self.created_vars.imp_push_get_ref(var) + } +} + +#[derive(Clone, Copy)] +struct Var<'a> { + vector: &'a Vector<'a>, + index: usize, +} + +impl<'a> Display for Var<'a> { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + write!(f, "{}[{}]", &self.vector.symbol, self.index) + } +} + +fn main() { + let x = &Vector::new("x"); + + // good + + let x0: Var = x[0]; + assert_eq!(x0.to_string(), "x[0]"); + + // also good + + let vars1: Vec = (0..1000).map(|i| x[i]).collect(); + + for (i, x) in vars1.iter().enumerate() { + assert_eq!(x.to_string(), format!("x[{}]", i)); + } + + // still good + + let vars2: Vec<&Var> = (0..1000).map(|i| &x[i]).collect(); + + for (i, x) in vars2.iter().enumerate() { + assert_eq!(x.to_string(), format!("x[{}]", i)); + } +} diff --git a/examples/vector_var_unsafe_cell_vec.rs b/examples/vector_var_unsafe_cell_vec.rs new file mode 100644 index 0000000..98aa72e --- /dev/null +++ b/examples/vector_var_unsafe_cell_vec.rs @@ -0,0 +1,68 @@ +use std::fmt::{Display, Formatter, Result}; +use std::{cell::UnsafeCell, ops::Index}; + +struct Vector<'a> { + symbol: String, + created_vars: UnsafeCell>>, +} + +impl<'a> Vector<'a> { + fn new(symbol: &str) -> Self { + Self { + symbol: symbol.into(), + created_vars: Default::default(), + } + } +} + +impl<'a> Index for &'a Vector<'a> { + type Output = Var<'a>; + + fn index(&self, index: usize) -> &Self::Output { + let var = Var { + index, + vector: self, + }; + + let cache = unsafe { &mut *self.created_vars.get() }; + cache.push(var); + &cache[cache.len() - 1] + } +} + +#[derive(Clone, Copy)] +struct Var<'a> { + vector: &'a Vector<'a>, + index: usize, +} + +impl<'a> Display for Var<'a> { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + write!(f, "{}[{}]", &self.vector.symbol, self.index) + } +} + +fn main() { + let x = &Vector::new("x"); + + // good + + let x0: Var = x[0]; + assert_eq!(x0.to_string(), "x[0]"); + + // also good + + let vars1: Vec = (0..1000).map(|i| x[i]).collect(); + + for (i, x) in vars1.iter().enumerate() { + assert_eq!(x.to_string(), format!("x[{}]", i)); + } + + // ¯\_(ツ)_/¯ UNDEFINED BEHAVIOR !! + + let vars2: Vec<&Var> = (0..1000).map(|i| &x[i]).collect(); + + for (i, x) in vars2.iter().enumerate() { + assert_eq!(x.to_string(), format!("x[{}]", i)); + } +} diff --git a/src/imp_vec.rs b/src/imp_vec.rs index 7af1f57..384b727 100644 --- a/src/imp_vec.rs +++ b/src/imp_vec.rs @@ -2,7 +2,7 @@ use core::{cell::UnsafeCell, marker::PhantomData}; use orx_pinned_vec::PinnedVec; use orx_split_vec::SplitVec; -/// `ImpVec`, standing for immutable push vector 👿, is a data structure which allows appending elements with a shared reference. +/// `ImpVec`, stands for immutable push vector 👿, is a data structure which allows appending elements with a shared reference. /// /// Specifically, it extends vector capabilities with the following two methods: /// * `fn imp_push(&self, value: T)` @@ -171,6 +171,36 @@ impl> ImpVec { self.pinned_mut().push(value); } + /// Pushes the `value` to the vector and returns a reference to it. + /// + /// It is the composition of [`vec.imp_push(value)`] call followed by `&vec[vec.len() - 1]`. + /// + /// [`vec.imp_push(value)`]: crate::ImpVec::imp_push + /// + /// # Examples + /// + /// This method provides a shorthand for the following common use case. + /// + /// ``` + /// use orx_imp_vec::*; + /// + /// let vec = ImpVec::new(); + /// + /// vec.imp_push('a'); + /// let a = &vec[vec.len() - 1]; + /// assert_eq!(a, &'a'); + /// + /// // or with imp_push_get_ref + /// + /// let b = vec.imp_push_get_ref('b'); + /// assert_eq!(b, &'b'); + /// ``` + pub fn imp_push_get_ref(&self, value: T) -> &T { + let pinned = self.pinned_mut(); + pinned.push(value); + &pinned[pinned.len() - 1] + } + /// Extends the vector with the given `slice`. /// This method differs from the `extend_from_slice` method with the required reference. /// Unlike `extend_from_slice`, `imp_extend_from_slice` allows to push the element with a shared reference.