1#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
10pub struct SymbolId(pub u32);
11
12#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
14pub enum Dim {
15 Static(usize),
17 Symbolic(SymbolId),
19}
20
21impl Dim {
22 pub fn as_static(self) -> Option<usize> {
24 match self {
25 Dim::Static(n) => Some(n),
26 Dim::Symbolic(_) => None,
27 }
28 }
29
30 pub fn is_static(self) -> bool {
32 matches!(self, Dim::Static(_))
33 }
34}
35
36impl From<usize> for Dim {
37 fn from(n: usize) -> Self {
38 Dim::Static(n)
39 }
40}
41
42impl From<SymbolId> for Dim {
43 fn from(s: SymbolId) -> Self {
44 Dim::Symbolic(s)
45 }
46}
47
48pub type Shape = Vec<Dim>;
61
62pub fn static_shape(dims: impl IntoIterator<Item = usize>) -> Shape {
64 dims.into_iter().map(Dim::Static).collect()
65}
66
67pub fn as_static_shape(shape: &[Dim]) -> Option<Vec<usize>> {
69 shape.iter().map(|d| d.as_static()).collect()
70}
71
72pub fn is_fully_static(shape: &[Dim]) -> bool {
74 shape.iter().all(|d| d.is_static())
75}
76
77#[derive(Clone, Debug, PartialEq, Eq, Default)]
80pub struct SymbolConstraints {
81 pub id: Option<SymbolId>,
82 pub name: Option<String>,
84 pub min: Option<usize>,
86 pub max: Option<usize>,
88 pub divisible_by: Option<usize>,
90}
91
92impl SymbolConstraints {
93 pub fn new(id: SymbolId, name: Option<String>) -> Self {
95 Self {
96 id: Some(id),
97 name,
98 ..Default::default()
99 }
100 }
101
102 pub fn accepts(&self, value: usize) -> bool {
104 self.min.is_none_or(|lo| value >= lo)
105 && self.max.is_none_or(|hi| value <= hi)
106 && self
107 .divisible_by
108 .is_none_or(|m| m != 0 && value.is_multiple_of(m))
109 }
110}
111
112#[cfg(test)]
113mod tests {
114 use super::*;
115
116 #[test]
117 fn static_shape_helpers() {
118 let s = static_shape([2, 3, 4]);
119 assert_eq!(s.len(), 3);
120 assert!(is_fully_static(&s));
121 assert_eq!(as_static_shape(&s), Some(vec![2, 3, 4]));
122 }
123
124 #[test]
125 fn symbolic_shape_is_not_static() {
126 let s = vec![Dim::Symbolic(SymbolId(0)), Dim::Static(768)];
127 assert!(!is_fully_static(&s));
128 assert_eq!(as_static_shape(&s), None);
129 assert_eq!(s[1].as_static(), Some(768));
130 }
131
132 #[test]
133 fn dim_conversions() {
134 assert_eq!(Dim::from(5usize), Dim::Static(5));
135 assert_eq!(Dim::from(SymbolId(7)), Dim::Symbolic(SymbolId(7)));
136 }
137
138 #[test]
139 fn constraints_accept() {
140 let c = SymbolConstraints {
141 id: Some(SymbolId(0)),
142 name: Some("seq".into()),
143 min: Some(1),
144 max: Some(2048),
145 divisible_by: Some(8),
146 };
147 assert!(c.accepts(8));
148 assert!(c.accepts(2048));
149 assert!(!c.accepts(0)); assert!(!c.accepts(4096)); assert!(!c.accepts(12)); }
153}