diff -r 5e3c1874797d -r 1b3b1687b9ed src/direct_product.rs
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/direct_product.rs Fri Dec 13 22:37:12 2024 -0500
@@ -0,0 +1,228 @@
+/*!
+Direct products of the form $A \times B$.
+
+TODO: This could be easily much more generic if `derive_more` could derive arithmetic
+operations on references.
+*/
+
+use core::ops::{Mul,MulAssign,Div,DivAssign,Add,AddAssign,Sub,SubAssign,Neg};
+use std::clone::Clone;
+use serde::{Serialize, Deserialize};
+use crate::types::{Num, Float};
+use crate::{maybe_lifetime, maybe_ref, impl_vectorspace_ops};
+use crate::euclidean::{Dot, Euclidean};
+
+#[derive(Debug,Clone,PartialEq,Eq,Serialize,Deserialize)]
+pub struct Pair (pub A, pub B);
+
+impl Pair {
+ pub fn new(a : A, b : B) -> Pair { Pair{ 0 : a, 1 : b } }
+}
+
+impl From<(A,B)> for Pair {
+ #[inline]
+ fn from((a, b) : (A, B)) -> Pair { Pair{ 0 : a, 1 : b } }
+}
+
+macro_rules! impl_binop {
+ ($trait : ident, $fn : ident, $refl:ident, $refr:ident) => {
+ impl_binop!(@doit: $trait, $fn;
+ maybe_lifetime!($refl, &'l Pair),
+ (maybe_lifetime!($refl, &'l A), maybe_lifetime!($refl, &'l B));
+ maybe_lifetime!($refr, &'r Pair),
+ (maybe_lifetime!($refr, &'r Ai), maybe_lifetime!($refr, &'r Bi));
+ $refl, $refr);
+ };
+
+ (@doit: $trait:ident, $fn:ident;
+ $self:ty, ($aself:ty, $bself:ty);
+ $in:ty, ($ain:ty, $bin:ty);
+ $refl:ident, $refr:ident) => {
+ impl<'l, 'r, A, B, Ai, Bi> $trait<$in>
+ for $self
+ where $aself: $trait<$ain>,
+ $bself: $trait<$bin> {
+ type Output = Pair<<$aself as $trait<$ain>>::Output,
+ <$bself as $trait<$bin>>::Output>;
+
+ #[inline]
+ fn $fn(self, y : $in) -> Self::Output {
+ Pair { 0 : maybe_ref!($refl, self.0).$fn(maybe_ref!($refr, y.0)),
+ 1 : maybe_ref!($refl, self.1).$fn(maybe_ref!($refr, y.1)) }
+ }
+ }
+ };
+}
+
+macro_rules! impl_assignop {
+ ($trait : ident, $fn : ident, $refr:ident) => {
+ impl_assignop!(@doit: $trait, $fn;
+ maybe_lifetime!($refr, &'r Pair),
+ (maybe_lifetime!($refr, &'r Ai), maybe_lifetime!($refr, &'r Bi));
+ $refr);
+ };
+ (@doit: $trait:ident, $fn:ident;
+ $in:ty, ($ain:ty, $bin:ty);
+ $refr:ident) => {
+ impl<'r, A, B, Ai, Bi> $trait<$in>
+ for Pair
+ where A: $trait<$ain>,
+ B: $trait<$bin> {
+ #[inline]
+ fn $fn(&mut self, y : $in) -> () {
+ self.0.$fn(maybe_ref!($refr, y.0));
+ self.1.$fn(maybe_ref!($refr, y.1));
+ }
+ }
+ }
+}
+
+macro_rules! impl_scalarop {
+ ($trait : ident, $fn : ident, $refl:ident) => {
+ impl_scalarop!(@doit: $trait, $fn;
+ maybe_lifetime!($refl, &'l Pair),
+ (maybe_lifetime!($refl, &'l A), maybe_lifetime!($refl, &'l B));
+ $refl);
+ };
+ (@doit: $trait:ident, $fn:ident;
+ $self:ty, ($aself:ty, $bself:ty);
+ $refl:ident) => {
+ // Scalar as Rhs
+ impl<'l, F : Num, A, B> $trait
+ for $self
+ where $aself: $trait,
+ $bself: $trait {
+ type Output = Pair<<$aself as $trait>::Output,
+ <$bself as $trait>::Output>;
+ #[inline]
+ fn $fn(self, a : F) -> Self::Output {
+ Pair{ 0 : maybe_ref!($refl, self.0).$fn(a),
+ 1 : maybe_ref!($refl, self.1).$fn(a)}
+ }
+ }
+ }
+}
+
+// Not used due to compiler overflow
+#[allow(unused_macros)]
+macro_rules! impl_scalarlhs_op {
+ ($trait:ident, $fn:ident, $refr:ident, $field:ty) => {
+ impl_scalarlhs_op!(@doit: $trait, $fn,
+ maybe_lifetime!($refr, &'r Pair),
+ (maybe_lifetime!($refr, &'r Ai), maybe_lifetime!($refr, &'r Bi));
+ $refr, $field);
+ };
+ (@doit: $trait:ident, $fn:ident,
+ $in:ty, ($ain:ty, $bin:ty);
+ $refr:ident, $field:ty) => {
+ impl<'r, Ai, Bi> $trait<$in>
+ for $field
+ where $field : $trait<$ain>
+ + $trait<$bin> {
+ type Output = Pair<<$field as $trait<$ain>>::Output,
+ <$field as $trait<$bin>>::Output>;
+ #[inline]
+ fn $fn(self, x : $in) -> Self::Output {
+ Pair{ 0 : self.$fn(maybe_ref!($refr, x.0)),
+ 1 : self.$fn(maybe_ref!($refr, x.1))}
+ }
+ }
+ };
+}
+
+macro_rules! impl_scalar_assignop {
+ ($trait : ident, $fn : ident) => {
+ impl<'r, F : Num, A, B> $trait
+ for Pair
+ where A: $trait, B: $trait {
+ #[inline]
+ fn $fn(&mut self, a : F) -> () {
+ self.0.$fn(a);
+ self.1.$fn(a);
+ }
+ }
+ }
+}
+
+macro_rules! impl_unaryop {
+ ($trait:ident, $fn:ident, $refl:ident) => {
+ impl_unaryop!(@doit: $trait, $fn;
+ maybe_lifetime!($refl, &'l Pair),
+ (maybe_lifetime!($refl, &'l A), maybe_lifetime!($refl, &'l B));
+ $refl);
+ };
+ (@doit: $trait:ident, $fn:ident;
+ $self:ty, ($aself:ty, $bself:ty);
+ $refl : ident) => {
+ impl<'l, A, B> $trait
+ for $self
+ where $aself: $trait,
+ $bself: $trait {
+ type Output = Pair<<$aself as $trait>::Output,
+ <$bself as $trait>::Output>;
+ #[inline]
+ fn $fn(self) -> Self::Output {
+ Pair{ 0 : maybe_ref!($refl, self.0).$fn(),
+ 1 : maybe_ref!($refl, self.1).$fn()}
+ }
+ }
+ }
+}
+
+
+impl_vectorspace_ops!(impl_binop, impl_assignop, impl_scalarop, impl_scalarlhs_op,
+ impl_scalar_assignop, impl_unaryop);
+
+
+impl Dot, F> for Pair
+where
+ A : Dot,
+ B : Dot,
+ F : Num
+{
+
+ fn dot(&self, Pair(ref u, ref v) : &Pair) -> F {
+ self.0.dot(u) + self.1.dot(v)
+ }
+}
+
+impl Euclidean for Pair
+where
+ A : Euclidean,
+ B : Euclidean,
+ F : Float
+{
+ type Output = Pair<>::Output, >::Output>;
+
+ fn similar_origin(&self) -> >::Output {
+ Pair(self.0.similar_origin(), self.1.similar_origin())
+ }
+
+ fn dist2_squared(&self, Pair(ref u, ref v) : &Self) -> F {
+ self.0.dist2_squared(u) + self.1.dist2_squared(v)
+ }
+}
+
+use crate::linops::AXPY;
+
+impl AXPY> for Pair
+where
+ A : AXPY,
+ B : AXPY,
+ F : Num
+{
+ fn axpy(&mut self, α : F, x : &Pair, β : F) {
+ self.0.axpy(α, &x.0, β);
+ self.1.axpy(α, &x.1, β);
+ }
+
+ fn copy_from(&mut self, x : &Pair,) {
+ self.0.copy_from(&x.0);
+ self.1.copy_from(&x.1);
+ }
+
+ fn scale_from(&mut self, α : F, x : &Pair) {
+ self.0.scale_from(α, &x.0);
+ self.1.scale_from(α, &x.1);
+ }
+}
\ No newline at end of file