1use std::marker::PhantomData;
2use std::ops::Index;
3
4use oximo_expr::Expr;
5use rustc_hash::FxHashMap;
6
7use crate::set::{Axis, FromIndexKey, IndexKey};
8
9#[derive(Clone)]
15pub(crate) enum Storage<'a> {
16 Dense { data: Vec<Expr<'a>>, keys: Vec<IndexKey>, axes: Box<[Axis]> },
17 Sparse(FxHashMap<IndexKey, Expr<'a>>),
18}
19
20mod sealed {
21 pub trait Sealed {}
22}
23
24#[doc(hidden)]
31pub trait Family: sealed::Sealed {
32 const NAME: &'static str;
34}
35
36#[doc(hidden)]
38#[derive(Debug)]
39pub struct VarFamily;
40
41#[doc(hidden)]
43#[derive(Debug)]
44pub struct ParamFamily;
45
46impl sealed::Sealed for VarFamily {}
47impl sealed::Sealed for ParamFamily {}
48impl Family for VarFamily {
49 const NAME: &'static str = "IndexedVar";
50}
51impl Family for ParamFamily {
52 const NAME: &'static str = "IndexedParam";
53}
54
55pub struct IndexedFamily<'a, K = IndexKey, F = VarFamily> {
66 pub(crate) storage: Storage<'a>,
67 pub(crate) _marker: PhantomData<fn() -> (K, F)>,
68}
69
70pub type IndexedVar<'a, K = IndexKey> = IndexedFamily<'a, K, VarFamily>;
72
73pub type IndexedParam<'a, K = IndexKey> = IndexedFamily<'a, K, ParamFamily>;
77
78impl<'a, K, F> Clone for IndexedFamily<'a, K, F> {
79 fn clone(&self) -> Self {
80 Self { storage: self.storage.clone(), _marker: PhantomData }
81 }
82}
83
84impl<'a, K, F: Family> std::fmt::Debug for IndexedFamily<'a, K, F> {
85 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
86 f.debug_struct(F::NAME).field("len", &self.len()).field("dense", &self.is_dense()).finish()
87 }
88}
89
90impl<'a, K, F> IndexedFamily<'a, K, F> {
91 pub fn len(&self) -> usize {
92 match &self.storage {
93 Storage::Dense { data, .. } => data.len(),
94 Storage::Sparse(m) => m.len(),
95 }
96 }
97
98 pub fn is_empty(&self) -> bool {
99 self.len() == 0
100 }
101
102 pub fn is_dense(&self) -> bool {
105 matches!(self.storage, Storage::Dense { .. })
106 }
107
108 pub fn shape(&self) -> Option<Box<[usize]>> {
110 match &self.storage {
111 Storage::Dense { axes, .. } => Some(axes.iter().map(|a| a.len).collect()),
112 Storage::Sparse(_) => None,
113 }
114 }
115
116 pub fn iter(&self) -> impl Iterator<Item = (&IndexKey, &Expr<'a>)> + '_ {
117 let it: Box<dyn Iterator<Item = (&IndexKey, &Expr<'a>)>> = match &self.storage {
118 Storage::Dense { data, keys, .. } => Box::new(keys.iter().zip(data.iter())),
119 Storage::Sparse(m) => Box::new(m.iter()),
120 };
121 it
122 }
123
124 pub fn get<Q: Into<IndexKey>>(&self, key: Q) -> Option<Expr<'a>> {
125 match &self.storage {
126 Storage::Sparse(m) => m.get(&key.into()).copied(),
127 Storage::Dense { data, axes, .. } => {
128 grid_offset(axes, &key.into()).map(|off| data[off])
129 }
130 }
131 }
132
133 pub fn at<const N: usize>(&self, coords: [usize; N]) -> Expr<'a> {
140 *self.get_ref(&coords).expect("indexed family: coordinates not present")
141 }
142
143 pub fn get_at<const N: usize>(&self, coords: [usize; N]) -> Option<Expr<'a>> {
145 self.get_ref(&coords).copied()
146 }
147
148 fn get_ref(&self, coords: &[usize]) -> Option<&Expr<'a>> {
149 match &self.storage {
150 Storage::Dense { data, axes, .. } => {
151 grid_offset_coords(axes, coords).map(|off| &data[off])
152 }
153 Storage::Sparse(m) => m.get(&coords_to_key(coords)),
154 }
155 }
156}
157
158impl<'a, K: FromIndexKey, F> IndexedFamily<'a, K, F> {
159 pub fn keys(&self) -> impl Iterator<Item = (K, Expr<'a>)> + '_ {
161 self.iter().map(|(k, e)| (K::from_index_key(k), *e))
162 }
163}
164
165impl<'a, K, F, Q: Into<IndexKey>> Index<Q> for IndexedFamily<'a, K, F> {
166 type Output = Expr<'a>;
167 fn index(&self, key: Q) -> &Self::Output {
168 match &self.storage {
169 Storage::Sparse(m) => m.get(&key.into()).expect("indexed family: key not present"),
170 Storage::Dense { data, axes, .. } => {
171 let off = grid_offset(axes, &key.into()).expect("indexed family: key not present");
172 &data[off]
173 }
174 }
175 }
176}
177
178impl<'a, K, F> Index<&IndexKey> for IndexedFamily<'a, K, F> {
179 type Output = Expr<'a>;
180 fn index(&self, key: &IndexKey) -> &Self::Output {
181 match &self.storage {
182 Storage::Sparse(m) => m.get(key).expect("indexed family: key not present"),
183 Storage::Dense { data, axes, .. } => {
184 let off = grid_offset(axes, key).expect("indexed family: key not present");
185 &data[off]
186 }
187 }
188 }
189}
190
191impl<'a, K, F, const N: usize> Index<[usize; N]> for IndexedFamily<'a, K, F> {
192 type Output = Expr<'a>;
193 fn index(&self, coords: [usize; N]) -> &Self::Output {
194 self.get_ref(&coords).expect("indexed family: coordinates not present")
195 }
196}
197
198pub(crate) fn build_storage<'a>(
201 keys: Vec<IndexKey>,
202 axes: Option<Box<[Axis]>>,
203 mut make: impl FnMut(&IndexKey) -> Expr<'a>,
204) -> Storage<'a> {
205 if let Some(axes) = axes {
206 let total = keys.len();
207 let mut data: Vec<Option<Expr<'a>>> = vec![None; total];
208 let mut kept: Vec<Option<IndexKey>> = vec![None; total];
209 for key in keys {
210 let expr = make(&key);
211 let off = grid_offset(&axes, &key).expect("dense grid key out of range");
212 data[off] = Some(expr);
213 kept[off] = Some(key);
214 }
215 let data = data.into_iter().map(|o| o.expect("dense grid had a gap")).collect();
216 let kept = kept.into_iter().map(|o| o.expect("dense grid had a gap")).collect();
217 Storage::Dense { data, keys: kept, axes }
218 } else {
219 let mut entries = FxHashMap::default();
220 for key in keys {
221 let expr = make(&key);
222 entries.insert(key, expr);
223 }
224 Storage::Sparse(entries)
225 }
226}
227
228fn axis_index(a: &Axis, v: i64) -> Option<usize> {
230 let d = v.checked_sub(a.start)?;
231 let u = usize::try_from(d).ok()?;
232 (u < a.len).then_some(u)
233}
234
235pub(crate) fn grid_offset(axes: &[Axis], key: &IndexKey) -> Option<usize> {
238 match (axes, key) {
239 ([a], IndexKey::Int(v)) => axis_index(a, *v),
240 (axes, IndexKey::Tuple(parts)) if parts.len() == axes.len() => {
241 let mut off = 0usize;
242 for (a, p) in axes.iter().zip(parts.iter()) {
243 off = off.checked_mul(a.len)?.checked_add(axis_index(a, p.as_i64()?)?)?;
244 }
245 Some(off)
246 }
247 _ => None,
248 }
249}
250
251fn grid_offset_coords(axes: &[Axis], coords: &[usize]) -> Option<usize> {
253 if coords.len() != axes.len() {
254 return None;
255 }
256 let mut off = 0usize;
257 for (a, &c) in axes.iter().zip(coords) {
258 off = off * a.len + axis_index(a, i64::try_from(c).ok()?)?;
259 }
260 Some(off)
261}
262
263fn coords_to_key(coords: &[usize]) -> IndexKey {
266 if let [single] = coords {
267 IndexKey::from(*single)
268 } else {
269 IndexKey::Tuple(coords.iter().map(|&c| IndexKey::from(c)).collect())
270 }
271}