Skip to main content

yui_core/conc/lc/
lc_data.rs

1//! Internal storage for [`super::Lc`]: a 0/1/many specialization of a
2//! `(key, coefficient)` table that avoids `HashMap` allocation for the
3//! empty and single-term cases.
4
5use std::collections::hash_map;
6use rustc_hash::FxHashMap;
7use crate::abst::{Ring, RingOps};
8
9use super::lc_key::LcKey;
10
11/// Invariants:
12/// - `Single(_, r)` always has `r ≠ 0`.
13/// - `Many(m)` always has `m.len() >= 2` and contains no zero values.
14///
15/// The `*_unreduced` methods may break these invariants; every other mutating
16/// method restores them. [`LcData::reduce`] restores them on demand.
17#[derive(PartialEq, Eq, Clone, Default, Debug)]
18#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
19pub(super) enum LcData<X, R>
20where
21    X: LcKey,
22    R: Ring, for<'x> &'x R: RingOps<R>
23{
24    #[default]
25    Zero,
26    Single(X, R),
27    Many(FxHashMap<X, R>),
28}
29
30impl<X, R> LcData<X, R>
31where
32    X: LcKey,
33    R: Ring, for<'x> &'x R: RingOps<R>
34{
35    fn new_many() -> FxHashMap<X, R> {
36        FxHashMap::default()
37    }
38
39    pub(super) fn len(&self) -> usize {
40        match self {
41            Self::Zero => 0,
42            Self::Single(_, _) => 1,
43            Self::Many(m) => m.len(),
44        }
45    }
46
47    pub(super) fn is_empty(&self) -> bool {
48        matches!(self, Self::Zero)
49    }
50
51    pub(super) fn get(&self, x: &X) -> Option<&R> {
52        match self {
53            Self::Zero => None,
54            Self::Single(k, v) => (k == x).then_some(v),
55            Self::Many(m) => m.get(x),
56        }
57    }
58
59    pub(super) fn iter(&self) -> LcDataIter<'_, X, R> {
60        match self {
61            Self::Zero => LcDataIter::Zero,
62            Self::Single(x, r) => LcDataIter::Single(Some((x, r))),
63            Self::Many(m) => LcDataIter::Many(m.iter()),
64        }
65    }
66
67    /// Insert/combine a `(key, coef)` pair, preserving canonical form for
68    /// `Zero`/`Single` transitions. For `Many`, may leave a zero coefficient
69    /// in the map; caller must invoke [`Self::reduce`] afterwards.
70    pub(super) fn add_pair_unreduced(&mut self, x: X, r: R) {
71        if r.is_zero() { return; }
72        match self {
73            Self::Zero => {
74                *self = Self::Single(x, r);
75            }
76            Self::Single(x0, r0) if x0 == &x => {
77                r0.add_assign(r);
78            }
79            Self::Single(_, _) => {
80                let Self::Single(x0, r0) = std::mem::take(self) else { unreachable!() };
81                let mut m = Self::new_many();
82                m.insert(x0, r0);
83                m.insert(x, r);
84                *self = Self::Many(m);
85            }
86            Self::Many(m) => {
87                if let Some(v) = m.get_mut(&x) {
88                    v.add_assign(r);
89                } else {
90                    m.insert(x, r);
91                }
92            }
93        }
94    }
95
96    /// Same, but the key is cloned only when it is not already present.
97    pub(super) fn add_pair_ref_unreduced(&mut self, x: &X, r: R) {
98        if r.is_zero() { return; }
99        match self {
100            Self::Single(x0, r0) if x0 == x => {
101                r0.add_assign(r);
102                return;
103            }
104            Self::Many(m) => {
105                if let Some(r0) = m.get_mut(x) {
106                    r0.add_assign(r);
107                    return;
108                }
109            }
110            _ => {}
111        }
112        self.add_pair_unreduced(x.clone(), r);
113    }
114
115    /// Drop zero-valued terms, then collapse `Many → Single/Zero` if fewer than two remain.
116    pub(super) fn reduce(&mut self) {
117        match self {
118            Self::Zero => {}
119            Self::Single(_, r) => {
120                if r.is_zero() {
121                    *self = Self::Zero;
122                }
123            }
124            Self::Many(m) => {
125                m.retain(|_, r| !r.is_zero());
126                match m.len() {
127                    0 => *self = Self::Zero,
128                    1 => {
129                        let (x, r) = std::mem::take(m).drain().next().unwrap();
130                        *self = Self::Single(x, r);
131                    }
132                    _ => {}
133                }
134            }
135        }
136    }
137
138    /// In-place coefficient mapping. Result is canonicalized.
139    pub(super) fn map_coeffs_in_place<F>(&mut self, f: F)
140    where F: Fn(R) -> R {
141        match self {
142            Self::Zero => {}
143            Self::Single(_, _) => {
144                let Self::Single(x, r) = std::mem::take(self) else { unreachable!() };
145                let r = f(r);
146                if !r.is_zero() {
147                    *self = Self::Single(x, r);
148                }
149            }
150            Self::Many(m) => {
151                let taken = std::mem::take(m);
152                for (k, v) in taken {
153                    let v = f(v);
154                    if !v.is_zero() {
155                        m.insert(k, v);
156                    }
157                }
158                self.reduce();
159            }
160        }
161    }
162}
163
164impl<X, R> IntoIterator for LcData<X, R>
165where
166    X: LcKey,
167    R: Ring, for<'x> &'x R: RingOps<R>
168{
169    type Item = (X, R);
170    type IntoIter = LcDataIntoIter<X, R>;
171    fn into_iter(self) -> Self::IntoIter {
172        match self {
173            Self::Zero => LcDataIntoIter::Zero,
174            Self::Single(x, r) => LcDataIntoIter::Single(Some((x, r))),
175            Self::Many(m) => LcDataIntoIter::Many(m.into_iter()),
176        }
177    }
178}
179
180/// Iterator returned by [`super::Lc::iter`].
181pub enum LcDataIter<'a, X, R> {
182    Zero,
183    Single(Option<(&'a X, &'a R)>),
184    Many(hash_map::Iter<'a, X, R>),
185}
186
187impl<'a, X, R> Iterator for LcDataIter<'a, X, R> {
188    type Item = (&'a X, &'a R);
189    fn next(&mut self) -> Option<Self::Item> {
190        match self {
191            Self::Zero => None,
192            Self::Single(opt) => opt.take(),
193            Self::Many(it) => it.next(),
194        }
195    }
196}
197
198/// Owning iterator returned by `<super::Lc as IntoIterator>::into_iter`.
199pub enum LcDataIntoIter<X, R> {
200    Zero,
201    Single(Option<(X, R)>),
202    Many(hash_map::IntoIter<X, R>),
203}
204
205impl<X, R> Iterator for LcDataIntoIter<X, R> {
206    type Item = (X, R);
207    fn next(&mut self) -> Option<Self::Item> {
208        match self {
209            Self::Zero => None,
210            Self::Single(opt) => opt.take(),
211            Self::Many(it) => it.next(),
212        }
213    }
214}