1#![allow(clippy::needless_pass_by_value)]
4
5use rustc_hash::FxHashMap;
6use std::{fmt, hash::Hash};
7
8use crate::{
9 IndexedDomain, IndexedValue, ToIndex, bitset::BitSet, pointer::PointerFamily, set::IndexSet,
10};
11
12pub struct IndexMatrix<'a, R, C: IndexedValue + 'a, S: BitSet, P: PointerFamily<'a>> {
17 pub(crate) matrix: FxHashMap<R, IndexSet<'a, C, S, P>>,
18 empty_set: IndexSet<'a, C, S, P>,
19 col_domain: P::Pointer<IndexedDomain<C>>,
20}
21
22impl<'a, R, C, S, P> IndexMatrix<'a, R, C, S, P>
23where
24 R: PartialEq + Eq + Hash + Clone,
25 C: IndexedValue + 'a,
26 S: BitSet,
27 P: PointerFamily<'a>,
28{
29 pub fn new(col_domain: &P::Pointer<IndexedDomain<C>>) -> Self {
31 IndexMatrix {
32 matrix: FxHashMap::default(),
33 empty_set: IndexSet::new(col_domain),
34 col_domain: col_domain.clone(),
35 }
36 }
37
38 pub(crate) fn ensure_row(&mut self, row: R) -> &mut IndexSet<'a, C, S, P> {
39 self.matrix
40 .entry(row)
41 .or_insert_with(|| self.empty_set.clone())
42 }
43
44 pub fn insert<M>(&mut self, row: R, col: impl ToIndex<C, M>) -> bool {
46 let col = col.to_index(&self.col_domain);
47 self.ensure_row(row).insert(col)
48 }
49
50 pub fn union_into_row(&mut self, into: R, from: &IndexSet<'a, C, S, P>) -> bool {
52 self.ensure_row(into).union_changed(from)
53 }
54
55 pub fn union_rows(&mut self, from: R, to: R) -> bool {
57 if from == to {
58 return false;
59 }
60
61 self.ensure_row(from.clone());
62 self.ensure_row(to.clone());
63
64 let [Some(from), Some(to)] =
66 (unsafe { self.matrix.get_disjoint_unchecked_mut([&from, &to]) })
67 else {
68 unreachable!()
69 };
70 to.union_changed(from)
71 }
72
73 pub fn row(&self, row: &R) -> impl Iterator<Item = &C> {
75 self.matrix.get(row).into_iter().flat_map(IndexSet::iter)
76 }
77
78 pub fn rows(&self) -> impl ExactSizeIterator<Item = (&R, &IndexSet<'a, C, S, P>)> {
80 self.matrix.iter()
81 }
82
83 pub fn row_set(&self, row: &R) -> &IndexSet<'a, C, S, P> {
85 self.matrix.get(row).unwrap_or(&self.empty_set)
86 }
87
88 pub fn clear_row(&mut self, row: &R) {
90 self.matrix.remove(row);
91 }
92
93 pub fn col_domain(&self) -> &P::Pointer<IndexedDomain<C>> {
95 &self.col_domain
96 }
97}
98
99impl<'a, R, C, S, P> IndexMatrix<'a, R, C, S, P>
100where
101 R: PartialEq + Eq + Hash + Clone + 'a,
102 C: IndexedValue + 'a,
103 S: BitSet,
104 P: PointerFamily<'a>,
105{
106 pub fn transpose<T, M>(
111 &self,
112 row_domain: &P::Pointer<IndexedDomain<T>>,
113 ) -> IndexMatrix<'a, C::Index, T, S, P>
114 where
115 T: IndexedValue + 'a,
116 R: ToIndex<T, M>,
117 {
118 let mut mtx = IndexMatrix::new(row_domain);
119 for (row, cols) in self.rows() {
120 for col in cols.indices() {
121 mtx.insert(col, row.clone());
122 }
123 }
124 mtx
125 }
126}
127
128impl<'a, R, C, S, P> PartialEq for IndexMatrix<'a, R, C, S, P>
129where
130 R: PartialEq + Eq + Hash + Clone,
131 C: IndexedValue + 'a,
132 S: BitSet,
133 P: PointerFamily<'a>,
134{
135 fn eq(&self, other: &Self) -> bool {
136 self.matrix == other.matrix
137 }
138}
139
140impl<'a, R, C, S, P> Eq for IndexMatrix<'a, R, C, S, P>
141where
142 R: PartialEq + Eq + Hash + Clone,
143 C: IndexedValue + 'a,
144 S: BitSet,
145 P: PointerFamily<'a>,
146{
147}
148
149impl<'a, R, C, S, P> Clone for IndexMatrix<'a, R, C, S, P>
150where
151 R: PartialEq + Eq + Hash + Clone,
152 C: IndexedValue + 'a,
153 S: BitSet,
154 P: PointerFamily<'a>,
155{
156 fn clone(&self) -> Self {
157 Self {
158 matrix: self.matrix.clone(),
159 empty_set: self.empty_set.clone(),
160 col_domain: self.col_domain.clone(),
161 }
162 }
163
164 fn clone_from(&mut self, source: &Self) {
165 for col in self.matrix.values_mut() {
166 col.clear();
167 }
168
169 for (row, col) in &source.matrix {
170 self.ensure_row(row.clone()).clone_from(col);
171 }
172
173 self.empty_set.clone_from(&source.empty_set);
174 self.col_domain.clone_from(&source.col_domain);
175 }
176}
177
178impl<'a, R, C, S, P> fmt::Debug for IndexMatrix<'a, R, C, S, P>
179where
180 R: PartialEq + Eq + Hash + Clone + fmt::Debug,
181 C: IndexedValue + fmt::Debug + 'a,
182 S: BitSet,
183 P: PointerFamily<'a>,
184{
185 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
186 f.debug_map().entries(self.rows()).finish()
187 }
188}
189
190#[cfg(test)]
191mod test {
192 use crate::{IndexedDomain, test_utils::TestIndexMatrix};
193 use std::rc::Rc;
194
195 fn mk(s: &str) -> String {
196 s.to_string()
197 }
198
199 #[test]
200 fn test_indexmatrix() {
201 let col_domain = Rc::new(IndexedDomain::from_iter([mk("a"), mk("b"), mk("c")]));
202 let mut mtx = TestIndexMatrix::new(&col_domain);
203 mtx.insert(0, mk("b"));
204 mtx.insert(1, mk("c"));
205 assert_eq!(mtx.row(&0).collect::<Vec<_>>(), vec!["b"]);
206 assert_eq!(mtx.row(&1).collect::<Vec<_>>(), vec!["c"]);
207
208 assert!(mtx.union_rows(0, 1));
209 assert_eq!(mtx.row(&1).collect::<Vec<_>>(), vec!["b", "c"]);
210 }
211}