Skip to main content

yui_core/conc/lc/
lc_key.rs

1//! Key types for [`Lc`](super::Lc): the trait [`LcKey`] and the two
2//! constructions [`AsKey`] (wrap an arbitrary element as a key) and
3//! [`EitherKey`] (disjoint union of two key sets).
4
5use std::ops::Mul;
6use derive_more::Display;
7use itertools::Either;
8
9use crate::lc::Lc;
10use crate::abst::{MathType, IndexType, Ring, RingOps};
11
12/// Marker trait for types usable as keys in [`Lc`](super::Lc) — i.e.
13/// elements that are hashable and totally ordered.
14pub trait LcKey: MathType + IndexType {}
15
16/// An [`LcKey`] whose product is formed by reference — the term-wise
17/// multiplication in [`Lc`](super::Lc)'s ring structure.
18///
19/// note: `Mul for &Lc` bounds on this trait, not `for<'x> &'x X: Mul`
20/// directly — since `Lc` is never an `LcKey`, the solver can't chase
21/// `&Lc<Lc<…>>: Mul` into an infinite recursion.
22pub trait LcMulKey: LcKey {
23    fn mul_ref(&self, rhs: &Self) -> Self;
24}
25
26impl<X> LcMulKey for X
27where X: LcKey, for<'x> &'x X: Mul<Output = X> {
28    fn mul_ref(&self, rhs: &Self) -> Self {
29        self * rhs
30    }
31}
32
33/// Wraps an arbitrary element `T` so it can be used as an [`LcKey`].
34///
35/// Used to build the [free module](crate::abst::RMod) over any `T: IndexType`.
36#[derive(Debug, Display, Default, Hash, PartialEq, Eq, Clone, PartialOrd, Ord)]
37#[display("<{}>", _0)]
38#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
39#[cfg_attr(feature = "serde", serde(transparent))]
40pub struct AsKey<T>(pub T) where T: IndexType;
41
42impl<T> From<T> for AsKey<T>
43where T: IndexType {
44    fn from(value: T) -> Self {
45        Self(value)
46    }
47}
48
49impl<T> MathType for AsKey<T>
50where T: IndexType {
51    fn math_symbol() -> String {
52        let full_name = std::any::type_name::<T>();
53        let name = full_name.split("::").last().unwrap_or(full_name);
54        format!("Free<{}>", name)
55    }
56}
57
58impl<T> LcKey for AsKey<T>
59where T: IndexType {}
60
61/// A disjoint union `X ⊔ Y` of two key sets, used to form direct sums of
62/// linear combinations, e.g. the basis for `Lc<X, R> ⊕ Lc<Y, R> ≅ Lc<EitherKey<X, Y>, R>`.
63#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
64pub struct EitherKey<X, Y>(Either<X, Y>) where X: LcKey, Y: LcKey;
65
66impl<X, Y> EitherKey<X, Y> where X: LcKey, Y: LcKey {
67    pub fn from_left(x: X) -> Self {
68        Self(Either::Left(x))
69    }
70
71    pub fn from_right(y: Y) -> Self {
72        Self(Either::Right(y))
73    }
74
75    pub fn entity(&self) -> Either<&X, &Y> {
76        match &self.0 {
77            Either::Left(x) => Either::Left(x),
78            Either::Right(y) => Either::Right(y),
79        }
80    }
81
82    pub fn is_left(&self) -> bool {
83        matches!(self.0, Either::Left(_))
84    }
85
86    pub fn is_right(&self) -> bool {
87        matches!(self.0, Either::Right(_))
88    }
89
90    pub fn inner(&self) -> &Either<X, Y> {
91        &self.0
92    }
93
94    pub fn into_left(self) -> X {
95        let Either::Left(x) = self.0 else {
96            panic!();
97        };
98        x
99    }
100
101    pub fn into_right(self) -> Y {
102        let Either::Right(y) = self.0 else {
103            panic!();
104        };
105        y
106    }
107}
108
109impl<X, Y> From<Either<X, Y>> for EitherKey<X, Y> where X: LcKey, Y: LcKey {
110    fn from(e: Either<X, Y>) -> Self {
111        Self(e)
112    }
113}
114
115impl<X, Y> From<EitherKey<X, Y>> for Either<X, Y> where X: LcKey, Y: LcKey {
116    fn from(e: EitherKey<X, Y>) -> Self {
117        e.0
118    }
119}
120
121impl<X, Y> Default for EitherKey<X, Y> where X: LcKey, Y: LcKey {
122    fn default() -> Self {
123        Self(Either::Left(X::default()))
124    }
125}
126
127impl <X, Y> std::fmt::Display for EitherKey<X, Y> where X: LcKey, Y: LcKey {
128    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
129        match &self.0 {
130            Either::Left(x)  => std::fmt::Display::fmt(x, f),
131            Either::Right(y) => std::fmt::Display::fmt(y, f),
132        }
133    }
134}
135
136impl<X, Y> MathType for EitherKey<X, Y> where X: LcKey, Y: LcKey {
137    fn math_symbol() -> String {
138        if X::math_symbol() == Y::math_symbol() {
139            X::math_symbol()
140        } else {
141            format!("E({},{})", X::math_symbol(), Y::math_symbol())
142        }
143    }
144}
145
146impl <X, Y> LcKey for EitherKey<X, Y> where X: LcKey, Y: LcKey {
147}
148
149pub fn split_lr<X, Y, R>(z: &Lc<EitherKey<X, Y>, R>) -> (Lc<X, R>, Lc<Y, R>)
150where X: LcKey, Y: LcKey, R: Ring, for<'x> &'x R: RingOps<R>{
151    let mut x = vec![];
152    let mut y = vec![];
153    for (e, r) in z.iter() {
154        if e.is_left() {
155            x.push((e.clone().into_left(), r.clone()));
156        } else {
157            y.push((e.clone().into_right(), r.clone()));
158        }
159    }
160    (Lc::from_iter(x), Lc::from_iter(y))
161}
162
163#[cfg(test)]
164mod tests {
165    use super::*;
166    use crate::lc::AsKey; // Assuming Free is defined in the crate
167
168    #[test]
169    fn test_either_key() {
170        type T = EitherKey<AsKey<i32>, AsKey<String>>;
171        let a = T::from_left(AsKey(42));
172        let b = T::from_right(AsKey("hello".to_string()));
173
174        assert_eq!(a.to_string(), "<42>");
175        assert_eq!(b.to_string(), "<hello>");
176    }
177}