1use std::marker::PhantomData;
2use std::ops::Mul;
3
4use num_traits::PrimInt;
5use rayon::prelude::*;
6use smol_str::SmolStr;
7
8pub type IndexTuple = Box<[IndexKey]>;
17
18#[derive(Clone, Debug)]
22enum SetRepr {
23 Range(Vec<i64>),
24 Strings(Vec<SmolStr>),
25 Tuples(Vec<IndexTuple>),
26}
27
28impl SetRepr {
29 fn len(&self) -> usize {
30 match self {
31 Self::Range(v) => v.len(),
32 Self::Strings(v) => v.len(),
33 Self::Tuples(v) => v.len(),
34 }
35 }
36}
37
38#[derive(Clone, Copy, Debug, PartialEq, Eq)]
43pub struct Axis {
44 pub start: i64,
45 pub len: usize,
46}
47
48pub struct Set<K = IndexKey> {
63 repr: SetRepr,
64 axes: Option<Box<[Axis]>>,
65 _k: PhantomData<fn() -> K>,
66}
67
68impl<K> Clone for Set<K> {
70 fn clone(&self) -> Self {
71 Self { repr: self.repr.clone(), axes: self.axes.clone(), _k: PhantomData }
72 }
73}
74
75impl<K> std::fmt::Debug for Set<K> {
76 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
77 std::fmt::Debug::fmt(&self.repr, f)
78 }
79}
80
81impl<K> Set<K> {
82 fn from_repr(repr: SetRepr) -> Self {
83 Self { repr, axes: None, _k: PhantomData }
84 }
85
86 fn from_repr_with_axes(repr: SetRepr, axes: Box<[Axis]>) -> Self {
87 Self { repr, axes: Some(axes), _k: PhantomData }
88 }
89
90 pub(crate) fn axes(&self) -> Option<&[Axis]> {
94 self.axes.as_deref()
95 }
96
97 pub fn tuples<I, T>(iter: I) -> Self
99 where
100 I: IntoIterator<Item = T>,
101 T: Into<IndexTuple>,
102 {
103 Self::from_repr(SetRepr::Tuples(iter.into_iter().map(Into::into).collect()))
104 }
105
106 #[must_use]
109 pub fn filter<F>(&self, mut f: F) -> Self
110 where
111 F: FnMut(&IndexKey) -> bool,
112 {
113 let repr = match &self.repr {
114 SetRepr::Range(v) => {
115 SetRepr::Range(v.iter().copied().filter(|i| f(&IndexKey::Int(*i))).collect())
116 }
117 SetRepr::Strings(v) => SetRepr::Strings(
118 v.iter()
119 .filter_map(|s| {
120 let key = IndexKey::Str(s.clone());
121 f(&key).then(|| s.clone())
122 })
123 .collect(),
124 ),
125 SetRepr::Tuples(v) => SetRepr::Tuples(
126 v.iter()
127 .filter_map(|t| {
128 let key = IndexKey::Tuple(t.clone());
129 f(&key).then(|| match key {
130 IndexKey::Tuple(owned) => owned,
131 _ => unreachable!(),
132 })
133 })
134 .collect(),
135 ),
136 };
137 Self::from_repr(repr)
138 }
139
140 #[must_use]
155 pub fn filter_typed<F>(&self, mut pred: F) -> Self
156 where
157 K: FromIndexKey,
158 F: FnMut(K) -> bool,
159 {
160 self.filter(|k| pred(K::from_index_key(k)))
161 }
162
163 pub fn len(&self) -> usize {
164 self.repr.len()
165 }
166
167 pub fn is_empty(&self) -> bool {
168 self.len() == 0
169 }
170
171 pub fn is_range(&self) -> bool {
173 matches!(self.repr, SetRepr::Range(_))
174 }
175
176 pub fn is_strings(&self) -> bool {
178 matches!(self.repr, SetRepr::Strings(_))
179 }
180
181 pub fn is_tuples(&self) -> bool {
183 matches!(self.repr, SetRepr::Tuples(_))
184 }
185
186 #[must_use]
192 pub fn product<B>(a: &Set<K>, b: &Set<B>) -> Set<<K as KeyCat<B>>::Out>
193 where
194 K: KeyCat<B>,
195 {
196 let a_len = a.len();
197 let b_len = b.len();
198 let total = a_len.checked_mul(b_len).expect("Set::product size overflow");
199
200 let axes = match (a.axes(), b.axes()) {
201 (Some(aa), Some(bb)) => {
202 let mut v = Vec::with_capacity(aa.len() + bb.len());
203 v.extend_from_slice(aa);
204 v.extend_from_slice(bb);
205 Some(v.into_boxed_slice())
206 }
207 _ => None,
208 };
209
210 const PAR_THRESHOLD: usize = 4096;
213 let out: Vec<IndexTuple> = if total < PAR_THRESHOLD {
214 let mut out = Vec::with_capacity(total);
215 for ka in a {
216 for kb in b {
217 let mut parts: Vec<IndexKey> = Vec::new();
218 push_flat(&mut parts, ka.clone());
219 push_flat(&mut parts, kb);
220 out.push(parts.into_boxed_slice());
221 }
222 }
223 out
224 } else {
225 let a_keys: Vec<IndexKey> = a.iter().collect();
226 let b_keys: Vec<IndexKey> = b.iter().collect();
227 (0..total)
228 .into_par_iter()
229 .map(|i| {
230 let mut parts: Vec<IndexKey> = Vec::new();
231 push_flat(&mut parts, a_keys[i / b_len].clone());
232 push_flat(&mut parts, b_keys[i % b_len].clone());
233 parts.into_boxed_slice()
234 })
235 .collect()
236 };
237
238 match axes {
239 Some(axes) => Set::from_repr_with_axes(SetRepr::Tuples(out), axes),
240 None => Set::from_repr(SetRepr::Tuples(out)),
241 }
242 }
243}
244
245impl Set<usize> {
246 #[must_use]
256 pub fn range<T: PrimInt>(r: std::ops::Range<T>) -> Self {
257 let start = r.start.to_i64().expect("range start out of i64 range");
258 let end = r.end.to_i64().expect("range end out of i64 range");
259 Self::dense_i64(start, end)
260 }
261
262 pub(crate) fn dense_i64(start: i64, end: i64) -> Self {
266 let vals: Vec<i64> = (start..end).collect();
267 let len = vals.len();
268 Self::from_repr_with_axes(SetRepr::Range(vals), Box::from([Axis { start, len }]))
269 }
270
271 pub fn from_ints<T, I>(iter: I) -> Self
277 where
278 T: PrimInt,
279 I: IntoIterator<Item = T>,
280 {
281 Self::from_repr(SetRepr::Range(
282 iter.into_iter().map(|v| v.to_i64().expect("element out of i64 range")).collect(),
283 ))
284 }
285}
286
287impl Set<String> {
288 pub fn strings<I, S>(iter: I) -> Self
289 where
290 I: IntoIterator<Item = S>,
291 S: Into<SmolStr>,
292 {
293 Self::from_repr(SetRepr::Strings(iter.into_iter().map(Into::into).collect()))
294 }
295}
296
297fn push_flat(dst: &mut Vec<IndexKey>, k: IndexKey) {
298 match k {
299 IndexKey::Tuple(inner) => dst.extend(inner.into_vec()),
300 other => dst.push(other),
301 }
302}
303
304fn make_tuple<I: IntoIterator<Item = IndexKey>>(items: I) -> IndexTuple {
305 let mut v: Vec<IndexKey> = Vec::new();
306 for k in items {
307 push_flat(&mut v, k);
308 }
309 v.into_boxed_slice()
310}
311
312impl<A, B> Mul<&Set<B>> for &Set<A>
313where
314 A: KeyCat<B>,
315{
316 type Output = Set<<A as KeyCat<B>>::Out>;
317 fn mul(self, rhs: &Set<B>) -> Self::Output {
318 Set::product(self, rhs)
319 }
320}
321
322#[diagnostic::on_unimplemented(
326 message = "cannot form a Cartesian product index key from `{Self}` and `{Rhs}`",
327 label = "no product key for `{Self}` * `{Rhs}`",
328 note = "`&a * &b` composes scalar keys (`usize`/`i64`/`i32`/`String`) into flat tuples up to arity 4. A 5th axis or a non-scalar operand is unsupported"
329)]
330pub trait KeyCat<Rhs> {
331 type Out;
332}
333
334pub trait ScalarKey {}
337impl ScalarKey for usize {}
338impl ScalarKey for i32 {}
339impl ScalarKey for i64 {}
340impl ScalarKey for String {}
341impl ScalarKey for IndexKey {}
342
343impl<A: ScalarKey, B: ScalarKey> KeyCat<B> for A {
344 type Out = (A, B);
345}
346
347impl<A, B, C: ScalarKey> KeyCat<C> for (A, B) {
348 type Out = (A, B, C);
349}
350
351impl<A, B, C, D: ScalarKey> KeyCat<D> for (A, B, C) {
352 type Out = (A, B, C, D);
353}
354
355impl<A: ScalarKey, B, C> KeyCat<(B, C)> for A {
360 type Out = (A, B, C);
361}
362
363impl<A: ScalarKey, B, C, D> KeyCat<(B, C, D)> for A {
364 type Out = (A, B, C, D);
365}
366
367impl<A, B, C, D> KeyCat<(C, D)> for (A, B) {
368 type Out = (A, B, C, D);
369}
370
371#[derive(Clone, Debug, PartialEq, Eq, Hash)]
373pub enum IndexKey {
374 Int(i64),
375 Str(SmolStr),
376 Tuple(IndexTuple),
377}
378
379impl IndexKey {
380 pub fn tuple<I, T>(iter: I) -> Self
383 where
384 I: IntoIterator<Item = T>,
385 T: Into<IndexKey>,
386 {
387 Self::Tuple(make_tuple(iter.into_iter().map(Into::into)))
388 }
389
390 pub fn as_i64(&self) -> Option<i64> {
391 if let Self::Int(v) = self { Some(*v) } else { None }
392 }
393
394 pub fn as_str(&self) -> Option<&str> {
395 if let Self::Str(s) = self { Some(s.as_str()) } else { None }
396 }
397
398 pub fn as_tuple(&self) -> Option<&[IndexKey]> {
399 if let Self::Tuple(t) = self { Some(&t[..]) } else { None }
400 }
401}
402
403impl From<i64> for IndexKey {
404 fn from(v: i64) -> Self {
405 Self::Int(v)
406 }
407}
408
409impl From<i32> for IndexKey {
410 fn from(v: i32) -> Self {
411 Self::Int(i64::from(v))
412 }
413}
414
415impl From<usize> for IndexKey {
416 fn from(v: usize) -> Self {
417 Self::Int(i64::try_from(v).expect("usize -> i64 overflow"))
418 }
419}
420
421impl From<&str> for IndexKey {
422 fn from(s: &str) -> Self {
423 Self::Str(SmolStr::new(s))
424 }
425}
426
427impl From<String> for IndexKey {
428 fn from(s: String) -> Self {
429 Self::Str(SmolStr::from(s))
430 }
431}
432
433impl From<&String> for IndexKey {
434 fn from(s: &String) -> Self {
435 Self::Str(SmolStr::new(s.as_str()))
436 }
437}
438
439impl From<&usize> for IndexKey {
441 fn from(v: &usize) -> Self {
442 Self::from(*v)
443 }
444}
445
446impl From<&i64> for IndexKey {
447 fn from(v: &i64) -> Self {
448 Self::Int(*v)
449 }
450}
451
452impl From<&i32> for IndexKey {
453 fn from(v: &i32) -> Self {
454 Self::Int(i64::from(*v))
455 }
456}
457
458impl From<&&str> for IndexKey {
459 fn from(s: &&str) -> Self {
460 Self::Str(SmolStr::new(*s))
461 }
462}
463
464impl From<&&String> for IndexKey {
465 fn from(s: &&String) -> Self {
466 Self::Str(SmolStr::new(s.as_str()))
467 }
468}
469
470impl<A, B> From<(A, B)> for IndexKey
471where
472 A: Into<IndexKey>,
473 B: Into<IndexKey>,
474{
475 fn from(t: (A, B)) -> Self {
476 Self::Tuple(make_tuple([t.0.into(), t.1.into()]))
477 }
478}
479
480impl<A, B, C> From<(A, B, C)> for IndexKey
481where
482 A: Into<IndexKey>,
483 B: Into<IndexKey>,
484 C: Into<IndexKey>,
485{
486 fn from(t: (A, B, C)) -> Self {
487 Self::Tuple(make_tuple([t.0.into(), t.1.into(), t.2.into()]))
488 }
489}
490
491impl<A, B, C, D> From<(A, B, C, D)> for IndexKey
492where
493 A: Into<IndexKey>,
494 B: Into<IndexKey>,
495 C: Into<IndexKey>,
496 D: Into<IndexKey>,
497{
498 fn from(t: (A, B, C, D)) -> Self {
499 Self::Tuple(make_tuple([t.0.into(), t.1.into(), t.2.into(), t.3.into()]))
500 }
501}
502
503#[diagnostic::on_unimplemented(
517 message = "`{Self}` is not a valid index key type",
518 label = "cannot be decoded from an index key",
519 note = "index keys decode to `usize`, `i64`, `i32`, `String`, `IndexKey`, or a tuple of those up to arity 4",
520 note = "annotate the binding to one of these (e.g. `for k: usize in set`) or match the `Set`'s key type"
521)]
522pub trait FromIndexKey: Sized {
523 fn from_index_key(k: &IndexKey) -> Self;
524}
525
526impl FromIndexKey for IndexKey {
527 fn from_index_key(k: &IndexKey) -> Self {
528 k.clone()
529 }
530}
531
532impl FromIndexKey for i64 {
533 fn from_index_key(k: &IndexKey) -> Self {
534 k.as_i64().unwrap_or_else(|| panic!("expected Int key, got {k:?}"))
535 }
536}
537
538impl FromIndexKey for i32 {
539 fn from_index_key(k: &IndexKey) -> Self {
540 let v = i64::from_index_key(k);
541 i32::try_from(v).unwrap_or_else(|_| panic!("key {v} out of i32 range"))
542 }
543}
544
545impl FromIndexKey for usize {
546 fn from_index_key(k: &IndexKey) -> Self {
547 let v = i64::from_index_key(k);
548 usize::try_from(v).unwrap_or_else(|_| panic!("key {v} out of usize range"))
549 }
550}
551
552impl FromIndexKey for String {
553 fn from_index_key(k: &IndexKey) -> Self {
554 k.as_str().unwrap_or_else(|| panic!("expected Str key, got {k:?}")).to_owned()
555 }
556}
557
558fn tuple_parts<'a>(k: &'a IndexKey, expected: usize) -> &'a [IndexKey] {
559 let p = k.as_tuple().unwrap_or_else(|| panic!("expected Tuple key, got {k:?}"));
560 assert_eq!(p.len(), expected, "expected tuple of arity {expected}, got arity {}", p.len());
561 p
562}
563
564impl<A, B> FromIndexKey for (A, B)
565where
566 A: FromIndexKey,
567 B: FromIndexKey,
568{
569 fn from_index_key(k: &IndexKey) -> Self {
570 let p = tuple_parts(k, 2);
571 (A::from_index_key(&p[0]), B::from_index_key(&p[1]))
572 }
573}
574
575impl<A, B, C> FromIndexKey for (A, B, C)
576where
577 A: FromIndexKey,
578 B: FromIndexKey,
579 C: FromIndexKey,
580{
581 fn from_index_key(k: &IndexKey) -> Self {
582 let p = tuple_parts(k, 3);
583 (A::from_index_key(&p[0]), B::from_index_key(&p[1]), C::from_index_key(&p[2]))
584 }
585}
586
587impl<A, B, C, D> FromIndexKey for (A, B, C, D)
588where
589 A: FromIndexKey,
590 B: FromIndexKey,
591 C: FromIndexKey,
592 D: FromIndexKey,
593{
594 fn from_index_key(k: &IndexKey) -> Self {
595 let p = tuple_parts(k, 4);
596 (
597 A::from_index_key(&p[0]),
598 B::from_index_key(&p[1]),
599 C::from_index_key(&p[2]),
600 D::from_index_key(&p[3]),
601 )
602 }
603}
604
605impl<'a, K> IntoIterator for &'a Set<K> {
606 type Item = IndexKey;
607 type IntoIter = SetIter<'a>;
608 fn into_iter(self) -> Self::IntoIter {
609 self.iter()
610 }
611}
612
613impl<K> Set<K> {
614 pub fn iter(&self) -> SetIter<'_> {
615 SetIter { repr: &self.repr, pos: 0 }
616 }
617
618 pub fn par_iter(&self) -> impl ParallelIterator<Item = IndexKey> + '_ {
619 match &self.repr {
620 SetRepr::Range(v) => v.par_iter().map(|i| IndexKey::Int(*i)).collect::<Vec<_>>(),
621 SetRepr::Strings(v) => {
622 v.par_iter().map(|s| IndexKey::Str(s.clone())).collect::<Vec<_>>()
623 }
624 SetRepr::Tuples(v) => {
625 v.par_iter().map(|t| IndexKey::Tuple(t.clone())).collect::<Vec<_>>()
626 }
627 }
628 .into_par_iter()
629 }
630}
631
632#[derive(Debug)]
633pub struct SetIter<'a> {
634 repr: &'a SetRepr,
635 pos: usize,
636}
637
638impl<'a> Iterator for SetIter<'a> {
639 type Item = IndexKey;
640 fn next(&mut self) -> Option<Self::Item> {
641 let out = match self.repr {
642 SetRepr::Range(v) => v.get(self.pos).copied().map(IndexKey::Int),
643 SetRepr::Strings(v) => v.get(self.pos).cloned().map(IndexKey::Str),
644 SetRepr::Tuples(v) => v.get(self.pos).cloned().map(IndexKey::Tuple),
645 };
646 if out.is_some() {
647 self.pos += 1;
648 }
649 out
650 }
651}