diff -r 1f19c6bbf07b -r 3868555d135c src/direct_product.rs --- a/src/direct_product.rs Sun Apr 27 20:29:43 2025 -0500 +++ b/src/direct_product.rs Fri May 15 14:46:30 2026 -0500 @@ -6,11 +6,11 @@ */ use crate::euclidean::Euclidean; -use crate::instance::{Decomposition, DecompositionMut, Instance, InstanceMut, MyCow}; -use crate::linops::AXPY; +use crate::instance::{Decomposition, DecompositionMut, Instance, InstanceMut, MyCow, Ownable}; +use crate::linops::{VectorSpace, AXPY}; use crate::loc::Loc; use crate::mapping::Space; -use crate::norms::{HasDual, Norm, NormExponent, Normed, PairNorm, L2}; +use crate::norms::{Dist, HasDual, Norm, NormExponent, Normed, PairNorm, L2}; use crate::types::{Float, Num}; use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}; use serde::{Deserialize, Serialize}; @@ -39,239 +39,290 @@ } } -macro_rules! impl_binop { - (($a : ty, $b : ty), $trait : ident, $fn : ident, $refl:ident, $refr:ident) => { - impl_binop!(@doit: $a, $b, $trait, $fn; - maybe_lifetime!($refl, &'l Pair<$a,$b>), - (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: $a:ty, $b:ty, - $trait:ident, $fn:ident; - $self:ty, ($aself:ty, $bself:ty); - $in:ty, ($ain:ty, $bin:ty); - $refl:ident, $refr:ident) => { - impl<'l, 'r, 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(maybe_ref!($refl, self.0).$fn(maybe_ref!($refr, y.0)), - maybe_ref!($refl, self.1).$fn(maybe_ref!($refr, y.1))) +macro_rules! impl_unary { + ($trait:ident, $fn:ident) => { + impl $trait for Pair + where + A: $trait, + B: $trait, + { + type Output = Pair; + fn $fn(self) -> Self::Output { + let Pair(a, b) = self; + Pair(a.$fn(), b.$fn()) } } + + // Compiler overflow + // impl<'a, A, B> $trait for &'a Pair + // where + // &'a A: $trait, + // &'a B: $trait, + // { + // type Output = Pair<<&'a A as $trait>::Output, <&'a B as $trait>::Output>; + // fn $fn(self) -> Self::Output { + // let Pair(ref a, ref b) = self; + // Pair(a.$fn(), b.$fn()) + // } + // } }; } -macro_rules! impl_assignop { - (($a : ty, $b : ty), $trait : ident, $fn : ident, $refr:ident) => { - impl_assignop!(@doit: $a, $b, - $trait, $fn; - maybe_lifetime!($refr, &'r Pair), - (maybe_lifetime!($refr, &'r Ai), - maybe_lifetime!($refr, &'r Bi)); - $refr); - }; - (@doit: $a : ty, $b : ty, - $trait:ident, $fn:ident; - $in:ty, ($ain:ty, $bin:ty); - $refr:ident) => { - impl<'r, Ai, Bi> $trait<$in> - for Pair<$a,$b> - 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)); +impl_unary!(Neg, neg); + +macro_rules! impl_binary { + ($trait:ident, $fn:ident) => { + impl $trait> for Pair + where + A: $trait, + B: $trait, + { + type Output = Pair; + fn $fn(self, Pair(c, d): Pair) -> Self::Output { + let Pair(a, b) = self; + Pair(a.$fn(c), b.$fn(d)) } } - } -} -macro_rules! impl_scalarop { - (($a : ty, $b : ty), $field : ty, $trait : ident, $fn : ident, $refl:ident) => { - impl_scalarop!(@doit: $field, - $trait, $fn; - maybe_lifetime!($refl, &'l Pair<$a,$b>), - (maybe_lifetime!($refl, &'l $a), - maybe_lifetime!($refl, &'l $b)); - $refl); - }; - (@doit: $field : ty, - $trait:ident, $fn:ident; - $self:ty, ($aself:ty, $bself:ty); - $refl:ident) => { - // Scalar as Rhs - impl<'l> $trait<$field> - for $self - where $aself: $trait<$field>, - $bself: $trait<$field> { - type Output = Pair<<$aself as $trait<$field>>::Output, - <$bself as $trait<$field>>::Output>; - #[inline] - fn $fn(self, a : $field) -> Self::Output { - Pair(maybe_ref!($refl, self.0).$fn(a), - maybe_ref!($refl, self.1).$fn(a)) + impl<'a, A, B, C, D> $trait> for &'a Pair + where + &'a A: $trait, + &'a B: $trait, + { + type Output = Pair<<&'a A as $trait>::Output, <&'a B as $trait>::Output>; + fn $fn(self, Pair(c, d): Pair) -> Self::Output { + let Pair(ref a, ref b) = self; + Pair(a.$fn(c), b.$fn(d)) } } - } -} -// Not used due to compiler overflow -#[allow(unused_macros)] -macro_rules! impl_scalarlhs_op { - (($a : ty, $b : ty), $field : ty, $trait:ident, $fn:ident, $refr:ident) => { - impl_scalarlhs_op!(@doit: $trait, $fn, - maybe_lifetime!($refr, &'r Pair<$a,$b>), - (maybe_lifetime!($refr, &'r $a), - maybe_lifetime!($refr, &'r $b)); - $refr, $field); - }; - (@doit: $trait:ident, $fn:ident, - $in:ty, ($ain:ty, $bin:ty); - $refr:ident, $field:ty) => { - impl<'r> $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(self.$fn(maybe_ref!($refr, x.0)), - self.$fn(maybe_ref!($refr, x.1))) + impl<'a, 'b, A, B, C, D> $trait<&'b Pair> for &'a Pair + where + &'a A: $trait<&'b C>, + &'a B: $trait<&'b D>, + { + type Output = Pair<<&'a A as $trait<&'b C>>::Output, <&'a B as $trait<&'b D>>::Output>; + fn $fn(self, Pair(ref c, ref d): &'b Pair) -> Self::Output { + let Pair(ref a, ref b) = self; + Pair(a.$fn(c), b.$fn(d)) + } + } + + impl<'b, A, B, C, D> $trait<&'b Pair> for Pair + where + A: $trait<&'b C>, + B: $trait<&'b D>, + { + type Output = Pair<>::Output, >::Output>; + fn $fn(self, Pair(ref c, ref d): &'b Pair) -> Self::Output { + let Pair(a, b) = self; + Pair(a.$fn(c), b.$fn(d)) } } }; } -macro_rules! impl_scalar_assignop { - (($a : ty, $b : ty), $field : ty, $trait : ident, $fn : ident) => { - impl<'r> $trait<$field> for Pair<$a, $b> +impl_binary!(Add, add); +impl_binary!(Sub, sub); + +macro_rules! impl_scalar { + ($trait:ident, $fn:ident) => { + impl $trait for Pair + where + A: $trait, + B: $trait, + { + type Output = Pair; + fn $fn(self, t: F) -> Self::Output { + let Pair(a, b) = self; + Pair(a.$fn(t), b.$fn(t)) + } + } + + impl<'a, A, B, F: Num> $trait for &'a Pair where - $a: $trait<$field>, - $b: $trait<$field>, + &'a A: $trait, + &'a B: $trait, + { + type Output = Pair<<&'a A as $trait>::Output, <&'a B as $trait>::Output>; + fn $fn(self, t: F) -> Self::Output { + let Pair(ref a, ref b) = self; + Pair(a.$fn(t), b.$fn(t)) + } + } + + // impl<'a, 'b, A, B> $trait<&'b $F> for &'a Pair + // where + // &'a A: $trait<&'b $F>, + // &'a B: $trait<&'b $F>, + // { + // type Output = + // Pair<<&'a A as $trait<&'b $F>>::Output, <&'a B as $trait<&'b $F>>::Output>; + // fn $fn(self, t: &'b $F) -> Self::Output { + // let Pair(ref a, ref b) = self; + // Pair(a.$fn(t), b.$fn(t)) + // } + // } + + // impl<'b, A, B> $trait<&'b $F> for Pair + // where + // A: $trait<&'b $F>, + // B: $trait<&'b $F>, + // { + // type Output = Pair<>::Output, >::Output>; + // fn $fn(self, t: &'b $F) -> Self::Output { + // let Pair(a, b) = self; + // Pair(a.$fn(t), b.$fn(t)) + // } + // } + }; +} + +impl_scalar!(Mul, mul); +impl_scalar!(Div, div); + +macro_rules! impl_scalar_lhs { + ($trait:ident, $fn:ident, $F:ty) => { + impl $trait> for $F + where + $F: $trait + $trait, { - #[inline] - fn $fn(&mut self, a: $field) -> () { - self.0.$fn(a); - self.1.$fn(a); + type Output = Pair<<$F as $trait>::Output, <$F as $trait>::Output>; + fn $fn(self, Pair(a, b): Pair) -> Self::Output { + Pair(self.$fn(a), self.$fn(b)) + } + } + + // Compiler overflow: + // + // impl<'a, A, B> $trait<&'a Pair> for $F + // where + // $F: $trait<&'a A> + $trait<&'a B>, + // { + // type Output = Pair<<$F as $trait<&'a A>>::Output, <$F as $trait<&'a B>>::Output>; + // fn $fn(self, Pair(a, b): &'a Pair) -> Self::Output { + // Pair(self.$fn(a), self.$fn(b)) + // } + // } + }; +} + +impl_scalar_lhs!(Mul, mul, f32); +impl_scalar_lhs!(Mul, mul, f64); +impl_scalar_lhs!(Div, div, f32); +impl_scalar_lhs!(Div, div, f64); + +macro_rules! impl_binary_mut { + ($trait:ident, $fn:ident) => { + impl<'a, A, B, C, D> $trait> for Pair + where + A: $trait, + B: $trait, + { + fn $fn(&mut self, Pair(c, d): Pair) { + let Pair(ref mut a, ref mut b) = self; + a.$fn(c); + b.$fn(d); + } + } + + impl<'a, 'b, A, B, C, D> $trait<&'b Pair> for Pair + where + A: $trait<&'b C>, + B: $trait<&'b D>, + { + fn $fn(&mut self, Pair(ref c, ref d): &'b Pair) { + let Pair(ref mut a, ref mut b) = self; + a.$fn(c); + b.$fn(d); } } }; } -macro_rules! impl_unaryop { - (($a : ty, $b : ty), $trait:ident, $fn:ident, $refl:ident) => { - impl_unaryop!(@doit: $trait, $fn; - maybe_lifetime!($refl, &'l Pair<$a,$b>), - (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> $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(maybe_ref!($refl, self.0).$fn(), - maybe_ref!($refl, self.1).$fn()) +impl_binary_mut!(AddAssign, add_assign); +impl_binary_mut!(SubAssign, sub_assign); + +macro_rules! impl_scalar_mut { + ($trait:ident, $fn:ident) => { + impl<'a, A, B, F: Num> $trait for Pair + where + A: $trait, + B: $trait, + { + fn $fn(&mut self, t: F) { + let Pair(ref mut a, ref mut b) = self; + a.$fn(t); + b.$fn(t); } } + }; +} + +impl_scalar_mut!(MulAssign, mul_assign); +impl_scalar_mut!(DivAssign, div_assign); + +/// Trait for ownable-by-consumption objects +impl Ownable for Pair +where + A: Ownable, + B: Ownable, +{ + type OwnedVariant = Pair; + + #[inline] + fn into_owned(self) -> Self::OwnedVariant { + Pair(self.0.into_owned(), self.1.into_owned()) + } + + #[inline] + fn clone_owned(&self) -> Self::OwnedVariant { + Pair(self.0.clone_owned(), self.1.clone_owned()) + } + + #[inline] + fn cow_owned<'b>(self) -> MyCow<'b, Self::OwnedVariant> + where + Self: 'b, + { + MyCow::Owned(self.into_owned()) + } + + #[inline] + fn ref_cow_owned<'b>(&'b self) -> MyCow<'b, Self::OwnedVariant> + where + Self: 'b, + { + MyCow::Owned(self.clone_owned()) } } -#[macro_export] -macro_rules! impl_pair_vectorspace_ops { - (($a:ty, $b:ty), $field:ty) => { - impl_pair_vectorspace_ops!(@binary, ($a, $b), Add, add); - impl_pair_vectorspace_ops!(@binary, ($a, $b), Sub, sub); - impl_pair_vectorspace_ops!(@assign, ($a, $b), AddAssign, add_assign); - impl_pair_vectorspace_ops!(@assign, ($a, $b), SubAssign, sub_assign); - impl_pair_vectorspace_ops!(@scalar, ($a, $b), $field, Mul, mul); - impl_pair_vectorspace_ops!(@scalar, ($a, $b), $field, Div, div); - // Compiler overflow - // $( - // impl_pair_vectorspace_ops!(@scalar_lhs, ($a, $b), $field, $impl_scalarlhs_op, Mul, mul); - // )* - impl_pair_vectorspace_ops!(@scalar_assign, ($a, $b), $field, MulAssign, mul_assign); - impl_pair_vectorspace_ops!(@scalar_assign, ($a, $b), $field, DivAssign, div_assign); - impl_pair_vectorspace_ops!(@unary, ($a, $b), Neg, neg); - }; - (@binary, ($a : ty, $b : ty), $trait : ident, $fn : ident) => { - impl_binop!(($a, $b), $trait, $fn, ref, ref); - impl_binop!(($a, $b), $trait, $fn, ref, noref); - impl_binop!(($a, $b), $trait, $fn, noref, ref); - impl_binop!(($a, $b), $trait, $fn, noref, noref); - }; - (@assign, ($a : ty, $b : ty), $trait : ident, $fn :ident) => { - impl_assignop!(($a, $b), $trait, $fn, ref); - impl_assignop!(($a, $b), $trait, $fn, noref); - }; - (@scalar, ($a : ty, $b : ty), $field : ty, $trait : ident, $fn :ident) => { - impl_scalarop!(($a, $b), $field, $trait, $fn, ref); - impl_scalarop!(($a, $b), $field, $trait, $fn, noref); - }; - (@scalar_lhs, ($a : ty, $b : ty), $field : ty, $trait : ident, $fn : ident) => { - impl_scalarlhs_op!(($a, $b), $field, $trait, $fn, ref); - impl_scalarlhs_op!(($a, $b), $field, $trait, $fn, noref); - }; - (@scalar_assign, ($a : ty, $b : ty), $field : ty, $trait : ident, $fn : ident) => { - impl_scalar_assignop!(($a, $b), $field, $trait, $fn); - }; - (@unary, ($a : ty, $b : ty), $trait : ident, $fn : ident) => { - impl_unaryop!(($a, $b), $trait, $fn, ref); - impl_unaryop!(($a, $b), $trait, $fn, noref); - }; -} - -impl_pair_vectorspace_ops!((f32, f32), f32); -impl_pair_vectorspace_ops!((f64, f64), f64); - -type PairOutput = Pair<>::Output, >::Output>; - -impl Euclidean for Pair +/// We only support 'closed' `Euclidean` `Pair`s, as more general ones cause +/// compiler overflows. +impl Euclidean for Pair where A: Euclidean, B: Euclidean, - F: Float, - PairOutput: Euclidean, - Self: Sized - + Mul> - + MulAssign - + Div> - + DivAssign - + Add> - + Sub> - + for<'b> Add<&'b Self, Output = PairOutput> - + for<'b> Sub<&'b Self, Output = PairOutput> - + AddAssign - + for<'b> AddAssign<&'b Self> - + SubAssign - + for<'b> SubAssign<&'b Self> - + Neg>, + // //Pair: Euclidean, + // Self: Sized + // + Mul + // + MulAssign + // + Div + // + DivAssign + // + Add + // + Sub + // + for<'b> Add<&'b Self, Output = Self::OwnedEuclidean> + // + for<'b> Sub<&'b Self, Output = Self::OwnedEuclidean> + // + AddAssign + // + for<'b> AddAssign<&'b Self> + // + SubAssign + // + for<'b> SubAssign<&'b Self> + // + Neg, { - type Output = PairOutput; + type PrincipalE = Pair; fn dot>(&self, other: I) -> F { - let Pair(u, v) = other.decompose(); - self.0.dot(u) + self.1.dot(v) + other.eval_decompose(|Pair(u, v)| self.0.dot(u) + self.1.dot(v)) } fn norm2_squared(&self) -> F { @@ -279,45 +330,59 @@ } fn dist2_squared>(&self, other: I) -> F { - let Pair(u, v) = other.decompose(); - self.0.dist2_squared(u) + self.1.dist2_squared(v) + other.eval_decompose(|Pair(u, v)| self.0.dist2_squared(u) + self.1.dist2_squared(v)) } } -impl AXPY> for Pair +impl VectorSpace for Pair +where + A: VectorSpace, + B: VectorSpace, + F: Num, +{ + type Field = F; + type PrincipalV = Pair; + + /// Return a similar zero as `self`. + fn similar_origin(&self) -> Self::PrincipalV { + Pair(self.0.similar_origin(), self.1.similar_origin()) + } + + // #[inline] + // fn into_owned(self) -> Self::Owned { + // Pair(self.0.into_owned(), self.1.into_owned()) + // } +} + +impl AXPY> for Pair where U: Space, V: Space, - A: AXPY, - B: AXPY, + A: AXPY, + B: AXPY, F: Num, - Self: MulAssign, - Pair: MulAssign, - Pair: AXPY>, + // Self: MulAssign + DivAssign, + // Pair: MulAssign + DivAssign, { - type Owned = Pair; - fn axpy>>(&mut self, α: F, x: I, β: F) { - let Pair(u, v) = x.decompose(); - self.0.axpy(α, u, β); - self.1.axpy(α, v, β); + x.eval_decompose(|Pair(u, v)| { + self.0.axpy(α, u, β); + self.1.axpy(α, v, β); + }) } fn copy_from>>(&mut self, x: I) { - let Pair(u, v) = x.decompose(); - self.0.copy_from(u); - self.1.copy_from(v); + x.eval_decompose(|Pair(u, v)| { + self.0.copy_from(u); + self.1.copy_from(v); + }) } fn scale_from>>(&mut self, α: F, x: I) { - let Pair(u, v) = x.decompose(); - self.0.scale_from(α, u); - self.1.scale_from(α, v); - } - - /// Return a similar zero as `self`. - fn similar_origin(&self) -> Self::Owned { - Pair(self.0.similar_origin(), self.1.similar_origin()) + x.eval_decompose(|Pair(u, v)| { + self.0.scale_from(α, u); + self.1.scale_from(α, v); + }) } /// Set self to zero. @@ -332,6 +397,7 @@ pub struct PairDecomposition(D, Q); impl Space for Pair { + type Principal = Pair; type Decomp = PairDecomposition; } @@ -367,25 +433,16 @@ V: Instance, { #[inline] - fn decompose<'b>( - self, - ) -> as Decomposition>>::Decomposition<'b> + fn eval_ref<'b, R>(&'b self, f: impl FnOnce(Pair, Q::Reference<'b>>) -> R) -> R where + Pair: 'b, Self: 'b, - Pair: 'b, { - Pair(self.0.decompose(), self.1.decompose()) + self.0.eval_ref(|a| self.1.eval_ref(|b| f(Pair(a, b)))) } #[inline] - fn ref_instance( - &self, - ) -> as Decomposition>>::Reference<'_> { - Pair(self.0.ref_instance(), self.1.ref_instance()) - } - - #[inline] - fn cow<'b>(self) -> MyCow<'b, Pair> + fn cow<'b>(self) -> MyCow<'b, Pair> where Self: 'b, { @@ -393,9 +450,17 @@ } #[inline] - fn own(self) -> Pair { + fn own(self) -> Pair { Pair(self.0.own(), self.1.own()) } + + #[inline] + fn decompose<'b>(self) -> Pair, Q::Decomposition<'b>> + where + Self: 'b, + { + Pair(self.0.decompose(), self.1.decompose()) + } } impl<'a, A, B, U, V, D, Q> Instance, PairDecomposition> for &'a Pair @@ -409,29 +474,16 @@ &'a U: Instance, &'a V: Instance, { - #[inline] - fn decompose<'b>( - self, - ) -> as Decomposition>>::Decomposition<'b> + fn eval_ref<'b, R>(&'b self, f: impl FnOnce(Pair, Q::Reference<'b>>) -> R) -> R where + Pair: 'b, Self: 'b, - Pair: 'b, { - Pair( - D::lift(self.0.ref_instance()), - Q::lift(self.1.ref_instance()), - ) + self.0.eval_ref(|a| self.1.eval_ref(|b| f(Pair(a, b)))) } #[inline] - fn ref_instance( - &self, - ) -> as Decomposition>>::Reference<'_> { - Pair(self.0.ref_instance(), self.1.ref_instance()) - } - - #[inline] - fn cow<'b>(self) -> MyCow<'b, Pair> + fn cow<'b>(self) -> MyCow<'b, Pair> where Self: 'b, { @@ -439,10 +491,19 @@ } #[inline] - fn own(self) -> Pair { + fn own(self) -> Pair { let Pair(ref u, ref v) = self; Pair(u.own(), v.own()) } + + #[inline] + fn decompose<'b>(self) -> Pair, Q::Decomposition<'b>> + where + Self: 'b, + { + let Pair(u, v) = self; + Pair(u.decompose(), v.decompose()) + } } impl DecompositionMut> for PairDecomposition @@ -492,18 +553,63 @@ } } -impl Norm> for Pair +impl Norm, F> for Pair +where + F: Num, + ExpA: NormExponent, + ExpB: NormExponent, + ExpJ: NormExponent, + A: Norm, + B: Norm, + Loc<2, F>: Norm, +{ + fn norm(&self, PairNorm(expa, expb, expj): PairNorm) -> F { + Loc([self.0.norm(expa), self.1.norm(expb)]).norm(expj) + } +} + +impl Dist, F> for Pair where F: Num, ExpA: NormExponent, ExpB: NormExponent, ExpJ: NormExponent, - A: Norm, - B: Norm, - Loc: Norm, + A: Dist, + B: Dist, + Loc<2, F>: Norm, { - fn norm(&self, PairNorm(expa, expb, expj): PairNorm) -> F { - Loc([self.0.norm(expa), self.1.norm(expb)]).norm(expj) + fn dist>( + &self, + x: I, + PairNorm(expa, expb, expj): PairNorm, + ) -> F { + x.eval_decompose(|Pair(x1, x2)| { + Loc([self.0.dist(x1, expa), self.1.dist(x2, expb)]).norm(expj) + }) + } +} + +impl Norm for Pair +where + F: Num, + A: Norm, + B: Norm, + Loc<2, F>: Norm, +{ + fn norm(&self, _: L2) -> F { + Loc([self.0.norm(L2), self.1.norm(L2)]).norm(L2) + } +} + +impl Dist for Pair +where + F: Num, + A: Dist, + B: Dist, + Loc<2, F>: Norm, +{ + fn dist>(&self, x: I, _: L2) -> F { + x.eval_decompose(|Pair(x1, x2)| Loc([self.0.dist(x1, L2), self.1.dist(x2, L2)]).norm(L2)) } } @@ -531,4 +637,72 @@ B: HasDual, { type DualSpace = Pair; + + fn dual_origin(&self) -> ::PrincipalV { + Pair(self.0.dual_origin(), self.1.dual_origin()) + } } + +#[cfg(feature = "pyo3")] +mod python { + use super::Pair; + use pyo3::conversion::FromPyObject; + use pyo3::types::{PyAny, PyTuple}; + use pyo3::{Borrowed, Bound, IntoPyObject, PyErr, Python}; + + impl<'py, A, B> IntoPyObject<'py> for Pair + where + A: IntoPyObject<'py>, + B: IntoPyObject<'py>, + { + type Target = PyTuple; + type Error = PyErr; + type Output = Bound<'py, Self::Target>; + + fn into_pyobject(self, py: Python<'py>) -> Result { + (self.0, self.1).into_pyobject(py) + } + } + + /* + impl<'a, 'py, A, B> IntoPyObject<'py> for &'a mut Pair + where + &'a mut A: IntoPyObject<'py>, + &'a mut B: IntoPyObject<'py>, + { + type Target = PyTuple; + type Error = PyErr; + type Output = Bound<'py, Self::Target>; + + fn into_pyobject(self, py: Python<'py>) -> Result { + (&mut self.0, &mut self.1).into_pyobject(py) + } + } + + impl<'a, 'py, A, B> IntoPyObject<'py> for &'a Pair + where + &'a A: IntoPyObject<'py>, + &'a B: IntoPyObject<'py>, + { + type Target = PyTuple; + type Error = PyErr; + type Output = Bound<'py, Self::Target>; + + fn into_pyobject(self, py: Python<'py>) -> Result { + (&self.0, &self.1).into_pyobject(py) + } + } + */ + + impl<'a, 'py, A, B> FromPyObject<'a, 'py> for Pair + where + A: Clone + FromPyObject<'a, 'py>, + B: Clone + FromPyObject<'a, 'py>, + { + type Error = PyErr; + + fn extract(ob: Borrowed<'a, 'py, PyAny>) -> Result { + FromPyObject::extract(ob).map(|(a, b)| Pair(a, b)) + } + } +}