Skip to main content

oximo_core/
set.rs

1use std::marker::PhantomData;
2use std::ops::Mul;
3
4use num_traits::PrimInt;
5use rayon::prelude::*;
6use smol_str::SmolStr;
7
8// Note: I used `Box<[IndexKey]>` over:
9//   - `Vec<IndexKey>`: saves one word
10//     (no capacity field) since tuples are never mutated after construction.
11//
12//   - `SmallVec<[IndexKey; N]>` is rejected by the compiler: recursive size
13//      cycle
14
15/// Heap-allocated tuple key payload. Immutable after construction.
16pub type IndexTuple = Box<[IndexKey]>;
17
18/// Runtime representation of a [`Set`]. The key type is tracked only at the type
19/// level (via [`Set`]'s phantom parameter), so the stored representation is
20/// identical for every `K` and carries no per-key type information.
21#[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/// A single contiguous integer axis of a dense index grid.
39/// Carried by [`Set`] (see its internal `axes`) so an [`crate::IndexedVar`]
40/// built over a range can store its scalars densely and map
41/// a key to a flat offset without hashing.
42#[derive(Clone, Copy, Debug, PartialEq, Eq)]
43pub struct Axis {
44    pub start: i64,
45    pub len: usize,
46}
47
48/// A finite, ordered index set, parameterized by the type `K` its keys decode to.
49///
50/// `K` is a phantom marker: the runtime representation is the same erased
51/// payload regardless of `K`, so carrying the key type costs nothing at runtime.
52/// It exists so the `variable!`/`constraint!`/`sum!` macros can infer the
53/// closure parameter type from the set instead of requiring an annotation.
54///
55/// Supports integer ranges (`K = usize`), string lists (`K = String`), and
56/// arbitrary tuple lists (built via [`Set::product`] / the `&a * &b` operator.
57///
58/// `axes` is `Some` exactly when the set is a dense integer grid
59/// and records the per-axis extents.
60/// It is `None` for string sets, sparse/`from_ints` sets, and any
61/// `filter`ed set.
62pub struct Set<K = IndexKey> {
63    repr: SetRepr,
64    axes: Option<Box<[Axis]>>,
65    _k: PhantomData<fn() -> K>,
66}
67
68// Manual `Clone`/`Debug` so they hold for every `K`
69impl<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    /// Per-axis extents when this set is a dense integer grid (a range or a
91    /// product of ranges), else `None`. Read by the `IndexedVar` builder to pick
92    /// dense vs sparse storage.
93    pub(crate) fn axes(&self) -> Option<&[Axis]> {
94        self.axes.as_deref()
95    }
96
97    /// Build a tuple set directly from an iterator of keys.
98    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    /// Filter keys with a predicate. Preserves the original variant where
107    /// possible.
108    #[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                        if f(&key) {
122                            match key {
123                                IndexKey::Str(owned) => Some(owned),
124                                _ => unreachable!(),
125                            }
126                        } else {
127                            None
128                        }
129                    })
130                    .collect(),
131            ),
132            SetRepr::Tuples(v) => SetRepr::Tuples(
133                v.iter()
134                    .filter_map(|t| {
135                        let key = IndexKey::Tuple(t.clone());
136                        f(&key).then(|| match key {
137                            IndexKey::Tuple(owned) => owned,
138                            _ => unreachable!(),
139                        })
140                    })
141                    .collect(),
142            ),
143        };
144        Self::from_repr(repr)
145    }
146
147    /// Filter keys with a predicate, consuming the set.
148    #[must_use]
149    pub fn into_filter<F>(self, mut f: F) -> Self
150    where
151        F: FnMut(&IndexKey) -> bool,
152    {
153        let repr = match self.repr {
154            SetRepr::Range(v) => {
155                SetRepr::Range(v.into_iter().filter(|i| f(&IndexKey::Int(*i))).collect())
156            }
157            SetRepr::Strings(v) => SetRepr::Strings(
158                v.into_iter()
159                    .filter_map(|s| {
160                        let key = IndexKey::Str(s);
161                        let keep = f(&key);
162                        match key {
163                            IndexKey::Str(owned) => keep.then_some(owned),
164                            _ => unreachable!(),
165                        }
166                    })
167                    .collect(),
168            ),
169            SetRepr::Tuples(v) => SetRepr::Tuples(
170                v.into_iter()
171                    .filter_map(|t| {
172                        let key = IndexKey::Tuple(t);
173                        let keep = f(&key);
174                        match key {
175                            IndexKey::Tuple(owned) => keep.then_some(owned),
176                            _ => unreachable!(),
177                        }
178                    })
179                    .collect(),
180            ),
181        };
182        Self::from_repr(repr)
183    }
184
185    /// Filter keys with a predicate over the typed, by-value decoded key.
186    ///
187    /// Unlike [`Self::filter`] (which hands the closure a raw [`IndexKey`]), the
188    /// key is decoded to `K` first, so a product set yields native tuples and no
189    /// manual `as_tuple().unwrap()` unpacking is needed. The receiver's `K` pins
190    /// the closure parameter, so it usually needs no annotation.
191    ///
192    /// ```
193    /// use oximo_core::Set;
194    /// let plants = Set::strings(["seattle", "san-diego"]);
195    /// // No-self-loop arcs; keys decoded to `(String, String)`.
196    /// let arcs = (&plants * &plants).filter_typed(|(p, q)| p != q);
197    /// assert_eq!(arcs.len(), 2);
198    /// ```
199    #[must_use]
200    pub fn filter_typed<F>(&self, mut pred: F) -> Self
201    where
202        K: FromIndexKey,
203        F: FnMut(K) -> bool,
204    {
205        self.filter(|k| pred(K::from_index_key(k)))
206    }
207
208    /// Borrowed-key filter that passes [`IndexKeyRef`] to the predicate.
209    /// Use when the set is borrowed and the filtered fraction is small or tuple
210    /// arity is large.
211    #[must_use]
212    pub fn filter_ref<F>(&self, mut f: F) -> Self
213    where
214        F: FnMut(IndexKeyRef<'_>) -> bool,
215    {
216        let repr = match &self.repr {
217            SetRepr::Range(v) => {
218                SetRepr::Range(v.iter().copied().filter(|i| f(IndexKeyRef::Int(*i))).collect())
219            }
220            SetRepr::Strings(v) => {
221                SetRepr::Strings(v.iter().filter(|s| f(IndexKeyRef::Str(s))).cloned().collect())
222            }
223            SetRepr::Tuples(v) => {
224                SetRepr::Tuples(v.iter().filter(|t| f(IndexKeyRef::Tuple(t))).cloned().collect())
225            }
226        };
227        Self::from_repr(repr)
228    }
229
230    /// Consuming variant of [`Self::filter_ref`].
231    #[must_use]
232    pub fn into_filter_ref<F>(self, mut f: F) -> Self
233    where
234        F: FnMut(IndexKeyRef<'_>) -> bool,
235    {
236        let repr = match self.repr {
237            SetRepr::Range(v) => {
238                SetRepr::Range(v.into_iter().filter(|i| f(IndexKeyRef::Int(*i))).collect())
239            }
240            SetRepr::Strings(v) => {
241                SetRepr::Strings(v.into_iter().filter(|s| f(IndexKeyRef::Str(s))).collect())
242            }
243            SetRepr::Tuples(v) => SetRepr::Tuples(
244                v.into_iter().filter(|t| f(IndexKeyRef::Tuple(t.as_ref()))).collect(),
245            ),
246        };
247        Self::from_repr(repr)
248    }
249
250    /// Consuming typed filter that moves the set (see [`Self::into_filter`]).
251    #[must_use]
252    pub fn into_filter_typed<F>(self, mut pred: F) -> Self
253    where
254        K: FromIndexKey,
255        F: FnMut(K) -> bool,
256    {
257        self.into_filter(|k| pred(K::from_index_key(k)))
258    }
259
260    pub fn len(&self) -> usize {
261        self.repr.len()
262    }
263
264    pub fn is_empty(&self) -> bool {
265        self.len() == 0
266    }
267
268    /// Whether the backing representation is the integer-range variant.
269    pub fn is_range(&self) -> bool {
270        matches!(self.repr, SetRepr::Range(_))
271    }
272
273    /// Whether the backing representation is the string-list variant.
274    pub fn is_strings(&self) -> bool {
275        matches!(self.repr, SetRepr::Strings(_))
276    }
277
278    /// Whether the backing representation is the tuple-list variant.
279    pub fn is_tuples(&self) -> bool {
280        matches!(self.repr, SetRepr::Tuples(_))
281    }
282
283    /// Cartesian product of two sets. Inner tuple keys are flattened so a
284    /// product `(a * b) * c` yields 3-element tuples, not nested 2-tuples.
285    ///
286    /// # Panics
287    /// Panics if `a.len() * b.len()` overflows `usize`.
288    #[must_use]
289    pub fn product<B>(a: &Set<K>, b: &Set<B>) -> Set<<K as KeyCat<B>>::Out>
290    where
291        K: KeyCat<B>,
292    {
293        let a_len = a.len();
294        let b_len = b.len();
295        let total = a_len.checked_mul(b_len).expect("Set::product size overflow");
296
297        let axes = match (a.axes(), b.axes()) {
298            (Some(aa), Some(bb)) => {
299                let mut v = Vec::with_capacity(aa.len() + bb.len());
300                v.extend_from_slice(aa);
301                v.extend_from_slice(bb);
302                Some(v.into_boxed_slice())
303            }
304            _ => None,
305        };
306
307        // Below this size, rayon dispatch overhead may dominate, so we stay serial.
308        // TODO: benchmark and tune this threshold.
309        const PAR_THRESHOLD: usize = 4096;
310        let out: Vec<IndexTuple> = if total < PAR_THRESHOLD {
311            let mut out = Vec::with_capacity(total);
312            for ka in a {
313                for kb in b {
314                    let mut parts: Vec<IndexKey> = Vec::new();
315                    push_flat(&mut parts, ka.clone());
316                    push_flat(&mut parts, kb);
317                    out.push(parts.into_boxed_slice());
318                }
319            }
320            out
321        } else {
322            let a_keys: Vec<IndexKey> = a.iter().collect();
323            let b_keys: Vec<IndexKey> = b.iter().collect();
324            (0..total)
325                .into_par_iter()
326                .map(|i| {
327                    let mut parts: Vec<IndexKey> = Vec::new();
328                    push_flat(&mut parts, a_keys[i / b_len].clone());
329                    push_flat(&mut parts, b_keys[i % b_len].clone());
330                    parts.into_boxed_slice()
331                })
332                .collect()
333        };
334
335        match axes {
336            Some(axes) => Set::from_repr_with_axes(SetRepr::Tuples(out), axes),
337            None => Set::from_repr(SetRepr::Tuples(out)),
338        }
339    }
340}
341
342impl Set<usize> {
343    /// Build an integer index set from a range over any primitive integer
344    /// type. Accepts `Range<i64>`, `Range<i32>`, `Range<usize>`, etc.
345    ///
346    /// The keys decode to `usize`.
347    /// Negative elements are accepted into the payload but panic when
348    /// decoded to `usize`.
349    ///
350    /// # Panics
351    /// Panics if either range bound does not fit in `i64`.
352    #[must_use]
353    pub fn range<T: PrimInt>(r: std::ops::Range<T>) -> Self {
354        let start = r.start.to_i64().expect("range start out of i64 range");
355        let end = r.end.to_i64().expect("range end out of i64 range");
356        Self::dense_i64(start, end)
357    }
358
359    /// Build a dense contiguous integer set from an `i64` half-open range,
360    /// recording the single axis so the resulting [`IndexedVar`] stores densely.
361    /// Shared by [`Self::range`] and the `RangeInclusive` `IntoSet` path.
362    pub(crate) fn dense_i64(start: i64, end: i64) -> Self {
363        let vals: Vec<i64> = (start..end).collect();
364        let len = vals.len();
365        Self::from_repr_with_axes(SetRepr::Range(vals), Box::from([Axis { start, len }]))
366    }
367
368    /// Build an integer index set from an iterator of any primitive integer
369    /// type. Useful when keys are sparse or computed.
370    ///
371    /// # Panics
372    /// Panics if any element does not fit in `i64`.
373    pub fn from_ints<T, I>(iter: I) -> Self
374    where
375        T: PrimInt,
376        I: IntoIterator<Item = T>,
377    {
378        Self::from_repr(SetRepr::Range(
379            iter.into_iter().map(|v| v.to_i64().expect("element out of i64 range")).collect(),
380        ))
381    }
382}
383
384impl Set<String> {
385    pub fn strings<I, S>(iter: I) -> Self
386    where
387        I: IntoIterator<Item = S>,
388        S: Into<SmolStr>,
389    {
390        Self::from_repr(SetRepr::Strings(iter.into_iter().map(Into::into).collect()))
391    }
392}
393
394fn push_flat(dst: &mut Vec<IndexKey>, k: IndexKey) {
395    match k {
396        IndexKey::Tuple(inner) => dst.extend(inner.into_vec()),
397        other => dst.push(other),
398    }
399}
400
401fn make_tuple<I: IntoIterator<Item = IndexKey>>(items: I) -> IndexTuple {
402    let mut v: Vec<IndexKey> = Vec::new();
403    for k in items {
404        push_flat(&mut v, k);
405    }
406    v.into_boxed_slice()
407}
408
409impl<A, B> Mul<&Set<B>> for &Set<A>
410where
411    A: KeyCat<B>,
412{
413    type Output = Set<<A as KeyCat<B>>::Out>;
414    fn mul(self, rhs: &Set<B>) -> Self::Output {
415        Set::product(self, rhs)
416    }
417}
418
419/// Type-level concatenation of index key types, mirroring the runtime tuple
420/// flattening in [`Set::product`]. The arity ceiling is 4, matching the
421/// [`FromIndexKey`]/`From<(...)>` tuple implementations.
422#[diagnostic::on_unimplemented(
423    message = "cannot form a Cartesian product index key from `{Self}` and `{Rhs}`",
424    label = "no product key for `{Self}` * `{Rhs}`",
425    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"
426)]
427pub trait KeyCat<Rhs> {
428    type Out;
429}
430
431/// Marker for non-tuple ("scalar") index key types. Lets [`KeyCat`] distinguish
432/// the scalar base case from the tuple-extension cases without overlap.
433pub trait ScalarKey {}
434impl ScalarKey for usize {}
435impl ScalarKey for i32 {}
436impl ScalarKey for i64 {}
437impl ScalarKey for String {}
438impl ScalarKey for IndexKey {}
439
440impl<A: ScalarKey, B: ScalarKey> KeyCat<B> for A {
441    type Out = (A, B);
442}
443
444impl<A, B, C: ScalarKey> KeyCat<C> for (A, B) {
445    type Out = (A, B, C);
446}
447
448impl<A, B, C, D: ScalarKey> KeyCat<D> for (A, B, C) {
449    type Out = (A, B, C, D);
450}
451
452// Right-associated / tuple-on-the-right products (`a * (b * c)`,
453// `(a * b) * (c * d)`). The macros only ever left-fold with a scalar right
454// operand, but `Set::product` flattens both sides, so we keep manual
455// products associative up to the arity-4 ceiling.
456impl<A: ScalarKey, B, C> KeyCat<(B, C)> for A {
457    type Out = (A, B, C);
458}
459
460impl<A: ScalarKey, B, C, D> KeyCat<(B, C, D)> for A {
461    type Out = (A, B, C, D);
462}
463
464impl<A, B, C, D> KeyCat<(C, D)> for (A, B) {
465    type Out = (A, B, C, D);
466}
467
468/// A serializable index key from a [`Set`].
469#[derive(Clone, Debug, PartialEq, Eq, Hash)]
470pub enum IndexKey {
471    Int(i64),
472    Str(SmolStr),
473    Tuple(IndexTuple),
474}
475
476impl IndexKey {
477    /// Build a tuple key from any iterable of convertible items. Nested tuple
478    /// keys are flattened.
479    pub fn tuple<I, T>(iter: I) -> Self
480    where
481        I: IntoIterator<Item = T>,
482        T: Into<IndexKey>,
483    {
484        Self::Tuple(make_tuple(iter.into_iter().map(Into::into)))
485    }
486
487    pub fn as_i64(&self) -> Option<i64> {
488        if let Self::Int(v) = self { Some(*v) } else { None }
489    }
490
491    pub fn as_str(&self) -> Option<&str> {
492        if let Self::Str(s) = self { Some(s.as_str()) } else { None }
493    }
494
495    pub fn as_tuple(&self) -> Option<&[IndexKey]> {
496        if let Self::Tuple(t) = self { Some(&t[..]) } else { None }
497    }
498}
499
500/// Borrowed view of an [`IndexKey`].
501#[derive(Clone, Copy, Debug, PartialEq, Eq)]
502pub enum IndexKeyRef<'a> {
503    Int(i64),
504    Str(&'a SmolStr),
505    Tuple(&'a [IndexKey]),
506}
507
508impl<'a> IndexKeyRef<'a> {
509    pub fn as_i64(self) -> Option<i64> {
510        if let Self::Int(v) = self { Some(v) } else { None }
511    }
512    pub fn as_str(self) -> Option<&'a str> {
513        if let Self::Str(s) = self { Some(s.as_str()) } else { None }
514    }
515    pub fn as_tuple(self) -> Option<&'a [IndexKey]> {
516        if let Self::Tuple(t) = self { Some(t) } else { None }
517    }
518}
519
520/// Typed projection of a borrowed [`IndexKeyRef`].
521/// Like [`FromIndexKey`] but decodes from a borrow.
522pub trait FromIndexKeyRef<'a>: Sized {
523    fn from_index_key_ref(k: IndexKeyRef<'a>) -> Self;
524}
525
526impl<'a> FromIndexKeyRef<'a> for IndexKey {
527    fn from_index_key_ref(k: IndexKeyRef<'a>) -> Self {
528        match k {
529            IndexKeyRef::Int(v) => IndexKey::Int(v),
530            IndexKeyRef::Str(s) => IndexKey::Str(s.clone()),
531            IndexKeyRef::Tuple(t) => IndexKey::Tuple(t.to_vec().into_boxed_slice()),
532        }
533    }
534}
535
536impl<'a> FromIndexKeyRef<'a> for i64 {
537    fn from_index_key_ref(k: IndexKeyRef<'a>) -> Self {
538        k.as_i64().unwrap_or_else(|| panic!("expected Int key, got {k:?}"))
539    }
540}
541
542impl<'a> FromIndexKeyRef<'a> for i32 {
543    fn from_index_key_ref(k: IndexKeyRef<'a>) -> Self {
544        let v = i64::from_index_key_ref(k);
545        i32::try_from(v).unwrap_or_else(|_| panic!("key {v} out of i32 range"))
546    }
547}
548
549impl<'a> FromIndexKeyRef<'a> for usize {
550    fn from_index_key_ref(k: IndexKeyRef<'a>) -> Self {
551        let v = i64::from_index_key_ref(k);
552        usize::try_from(v).unwrap_or_else(|_| panic!("key {v} out of usize range"))
553    }
554}
555
556impl<'a> FromIndexKeyRef<'a> for String {
557    fn from_index_key_ref(k: IndexKeyRef<'a>) -> Self {
558        k.as_str().unwrap_or_else(|| panic!("expected Str key, got {k:?}")).to_owned()
559    }
560}
561
562impl<'a> FromIndexKeyRef<'a> for &'a str {
563    fn from_index_key_ref(k: IndexKeyRef<'a>) -> Self {
564        k.as_str().unwrap_or_else(|| panic!("expected Str key, got {k:?}"))
565    }
566}
567
568impl<'a> FromIndexKeyRef<'a> for &'a SmolStr {
569    fn from_index_key_ref(k: IndexKeyRef<'a>) -> Self {
570        match k {
571            IndexKeyRef::Str(s) => s,
572            _ => panic!("expected Str key, got {k:?}"),
573        }
574    }
575}
576
577fn tuple_parts_ref<'a>(k: IndexKeyRef<'a>, expected: usize) -> &'a [IndexKey] {
578    let p = k.as_tuple().unwrap_or_else(|| panic!("expected Tuple key, got {k:?}"));
579    assert_eq!(p.len(), expected, "expected tuple of arity {expected}, got arity {}", p.len());
580    p
581}
582
583impl<'a, A, B> FromIndexKeyRef<'a> for (A, B)
584where
585    A: FromIndexKeyRef<'a>,
586    B: FromIndexKeyRef<'a>,
587{
588    fn from_index_key_ref(k: IndexKeyRef<'a>) -> Self {
589        let p = tuple_parts_ref(k, 2);
590        (
591            A::from_index_key_ref(IndexKeyRef::from(&p[0])),
592            B::from_index_key_ref(IndexKeyRef::from(&p[1])),
593        )
594    }
595}
596
597impl<'a, A, B, C> FromIndexKeyRef<'a> for (A, B, C)
598where
599    A: FromIndexKeyRef<'a>,
600    B: FromIndexKeyRef<'a>,
601    C: FromIndexKeyRef<'a>,
602{
603    fn from_index_key_ref(k: IndexKeyRef<'a>) -> Self {
604        let p = tuple_parts_ref(k, 3);
605        (
606            A::from_index_key_ref(IndexKeyRef::from(&p[0])),
607            B::from_index_key_ref(IndexKeyRef::from(&p[1])),
608            C::from_index_key_ref(IndexKeyRef::from(&p[2])),
609        )
610    }
611}
612
613impl<'a, A, B, C, D> FromIndexKeyRef<'a> for (A, B, C, D)
614where
615    A: FromIndexKeyRef<'a>,
616    B: FromIndexKeyRef<'a>,
617    C: FromIndexKeyRef<'a>,
618    D: FromIndexKeyRef<'a>,
619{
620    fn from_index_key_ref(k: IndexKeyRef<'a>) -> Self {
621        let p = tuple_parts_ref(k, 4);
622        (
623            A::from_index_key_ref(IndexKeyRef::from(&p[0])),
624            B::from_index_key_ref(IndexKeyRef::from(&p[1])),
625            C::from_index_key_ref(IndexKeyRef::from(&p[2])),
626            D::from_index_key_ref(IndexKeyRef::from(&p[3])),
627        )
628    }
629}
630
631impl<'a> From<&'a IndexKey> for IndexKeyRef<'a> {
632    fn from(k: &'a IndexKey) -> Self {
633        match k {
634            IndexKey::Int(v) => IndexKeyRef::Int(*v),
635            IndexKey::Str(s) => IndexKeyRef::Str(s),
636            IndexKey::Tuple(t) => IndexKeyRef::Tuple(t),
637        }
638    }
639}
640
641impl From<i64> for IndexKey {
642    fn from(v: i64) -> Self {
643        Self::Int(v)
644    }
645}
646
647impl From<i32> for IndexKey {
648    fn from(v: i32) -> Self {
649        Self::Int(i64::from(v))
650    }
651}
652
653impl From<usize> for IndexKey {
654    fn from(v: usize) -> Self {
655        Self::Int(i64::try_from(v).expect("usize -> i64 overflow"))
656    }
657}
658
659impl From<&str> for IndexKey {
660    fn from(s: &str) -> Self {
661        Self::Str(SmolStr::new(s))
662    }
663}
664
665impl From<String> for IndexKey {
666    fn from(s: String) -> Self {
667        Self::Str(SmolStr::from(s))
668    }
669}
670
671impl From<&String> for IndexKey {
672    fn from(s: &String) -> Self {
673        Self::Str(SmolStr::new(s.as_str()))
674    }
675}
676
677// Reference conversions.
678impl From<&usize> for IndexKey {
679    fn from(v: &usize) -> Self {
680        Self::from(*v)
681    }
682}
683
684impl From<&i64> for IndexKey {
685    fn from(v: &i64) -> Self {
686        Self::Int(*v)
687    }
688}
689
690impl From<&i32> for IndexKey {
691    fn from(v: &i32) -> Self {
692        Self::Int(i64::from(*v))
693    }
694}
695
696impl From<&&str> for IndexKey {
697    fn from(s: &&str) -> Self {
698        Self::Str(SmolStr::new(*s))
699    }
700}
701
702impl From<&&String> for IndexKey {
703    fn from(s: &&String) -> Self {
704        Self::Str(SmolStr::new(s.as_str()))
705    }
706}
707
708impl<A, B> From<(A, B)> for IndexKey
709where
710    A: Into<IndexKey>,
711    B: Into<IndexKey>,
712{
713    fn from(t: (A, B)) -> Self {
714        Self::Tuple(make_tuple([t.0.into(), t.1.into()]))
715    }
716}
717
718impl<A, B, C> From<(A, B, C)> for IndexKey
719where
720    A: Into<IndexKey>,
721    B: Into<IndexKey>,
722    C: Into<IndexKey>,
723{
724    fn from(t: (A, B, C)) -> Self {
725        Self::Tuple(make_tuple([t.0.into(), t.1.into(), t.2.into()]))
726    }
727}
728
729impl<A, B, C, D> From<(A, B, C, D)> for IndexKey
730where
731    A: Into<IndexKey>,
732    B: Into<IndexKey>,
733    C: Into<IndexKey>,
734    D: Into<IndexKey>,
735{
736    fn from(t: (A, B, C, D)) -> Self {
737        Self::Tuple(make_tuple([t.0.into(), t.1.into(), t.2.into(), t.3.into()]))
738    }
739}
740
741/// Typed projection of an [`IndexKey`]. Implementations panic when the
742/// key's shape does not match the target type, the same contract as
743/// [`crate::indexed::IndexedVar`] indexing on a missing key.
744///
745/// Used by the indexed-family `constraint!` macro (and similar rule helpers) to
746/// give the closure typed indices directly:
747///
748/// ```ignore
749/// constraint!(m, supply[(p, m) in &plants * &markets], {
750///     // p, m are native String, no manual unpack
751///     ...
752/// });
753/// ```
754#[diagnostic::on_unimplemented(
755    message = "`{Self}` is not a valid index key type",
756    label = "cannot be decoded from an index key",
757    note = "index keys decode to `usize`, `i64`, `i32`, `String`, `IndexKey`, or a tuple of those up to arity 4",
758    note = "annotate the binding to one of these (e.g. `for k: usize in set`) or match the `Set`'s key type"
759)]
760pub trait FromIndexKey: Sized {
761    fn from_index_key(k: &IndexKey) -> Self;
762}
763
764impl FromIndexKey for IndexKey {
765    fn from_index_key(k: &IndexKey) -> Self {
766        k.clone()
767    }
768}
769
770impl FromIndexKey for i64 {
771    fn from_index_key(k: &IndexKey) -> Self {
772        k.as_i64().unwrap_or_else(|| panic!("expected Int key, got {k:?}"))
773    }
774}
775
776impl FromIndexKey for i32 {
777    fn from_index_key(k: &IndexKey) -> Self {
778        let v = i64::from_index_key(k);
779        i32::try_from(v).unwrap_or_else(|_| panic!("key {v} out of i32 range"))
780    }
781}
782
783impl FromIndexKey for usize {
784    fn from_index_key(k: &IndexKey) -> Self {
785        let v = i64::from_index_key(k);
786        usize::try_from(v).unwrap_or_else(|_| panic!("key {v} out of usize range"))
787    }
788}
789
790impl FromIndexKey for String {
791    fn from_index_key(k: &IndexKey) -> Self {
792        k.as_str().unwrap_or_else(|| panic!("expected Str key, got {k:?}")).to_owned()
793    }
794}
795
796fn tuple_parts<'a>(k: &'a IndexKey, expected: usize) -> &'a [IndexKey] {
797    let p = k.as_tuple().unwrap_or_else(|| panic!("expected Tuple key, got {k:?}"));
798    assert_eq!(p.len(), expected, "expected tuple of arity {expected}, got arity {}", p.len());
799    p
800}
801
802impl<A, B> FromIndexKey for (A, B)
803where
804    A: FromIndexKey,
805    B: FromIndexKey,
806{
807    fn from_index_key(k: &IndexKey) -> Self {
808        let p = tuple_parts(k, 2);
809        (A::from_index_key(&p[0]), B::from_index_key(&p[1]))
810    }
811}
812
813impl<A, B, C> FromIndexKey for (A, B, C)
814where
815    A: FromIndexKey,
816    B: FromIndexKey,
817    C: FromIndexKey,
818{
819    fn from_index_key(k: &IndexKey) -> Self {
820        let p = tuple_parts(k, 3);
821        (A::from_index_key(&p[0]), B::from_index_key(&p[1]), C::from_index_key(&p[2]))
822    }
823}
824
825impl<A, B, C, D> FromIndexKey for (A, B, C, D)
826where
827    A: FromIndexKey,
828    B: FromIndexKey,
829    C: FromIndexKey,
830    D: FromIndexKey,
831{
832    fn from_index_key(k: &IndexKey) -> Self {
833        let p = tuple_parts(k, 4);
834        (
835            A::from_index_key(&p[0]),
836            B::from_index_key(&p[1]),
837            C::from_index_key(&p[2]),
838            D::from_index_key(&p[3]),
839        )
840    }
841}
842
843impl<'a, K> IntoIterator for &'a Set<K> {
844    type Item = IndexKey;
845    type IntoIter = SetIter<'a>;
846    fn into_iter(self) -> Self::IntoIter {
847        self.iter()
848    }
849}
850
851impl<K> Set<K> {
852    pub fn iter(&self) -> SetIter<'_> {
853        SetIter { repr: &self.repr, pos: 0 }
854    }
855
856    pub fn par_iter(&self) -> impl ParallelIterator<Item = IndexKey> + '_ {
857        (0..self.len()).into_par_iter().map(|i| match &self.repr {
858            SetRepr::Range(v) => IndexKey::Int(v[i]),
859            SetRepr::Strings(v) => IndexKey::Str(v[i].clone()),
860            SetRepr::Tuples(v) => IndexKey::Tuple(v[i].clone()),
861        })
862    }
863}
864
865#[derive(Debug)]
866pub struct SetIter<'a> {
867    repr: &'a SetRepr,
868    pos: usize,
869}
870
871impl<'a> Iterator for SetIter<'a> {
872    type Item = IndexKey;
873
874    fn size_hint(&self) -> (usize, Option<usize>) {
875        let remaining = self.repr.len() - self.pos;
876        (remaining, Some(remaining))
877    }
878
879    fn next(&mut self) -> Option<Self::Item> {
880        let out = match self.repr {
881            SetRepr::Range(v) => v.get(self.pos).copied().map(IndexKey::Int),
882            SetRepr::Strings(v) => v.get(self.pos).cloned().map(IndexKey::Str),
883            SetRepr::Tuples(v) => v.get(self.pos).cloned().map(IndexKey::Tuple),
884        };
885        if out.is_some() {
886            self.pos += 1;
887        }
888        out
889    }
890}