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.divisible_by.is_none_or(|m| m != 0 && value.is_multiple_of(m))
107 }
108}
109
110#[cfg(test)]
111mod tests {
112 use super::*;
113
114 #[test]
115 fn static_shape_helpers() {
116 let s = static_shape([2, 3, 4]);
117 assert_eq!(s.len(), 3);
118 assert!(is_fully_static(&s));
119 assert_eq!(as_static_shape(&s), Some(vec![2, 3, 4]));
120 }
121
122 #[test]
123 fn symbolic_shape_is_not_static() {
124 let s = vec![Dim::Symbolic(SymbolId(0)), Dim::Static(768)];
125 assert!(!is_fully_static(&s));
126 assert_eq!(as_static_shape(&s), None);
127 assert_eq!(s[1].as_static(), Some(768));
128 }
129
130 #[test]
131 fn dim_conversions() {
132 assert_eq!(Dim::from(5usize), Dim::Static(5));
133 assert_eq!(Dim::from(SymbolId(7)), Dim::Symbolic(SymbolId(7)));
134 }
135
136 #[test]
137 fn constraints_accept() {
138 let c = SymbolConstraints {
139 id: Some(SymbolId(0)),
140 name: Some("seq".into()),
141 min: Some(1),
142 max: Some(2048),
143 divisible_by: Some(8),
144 };
145 assert!(c.accepts(8));
146 assert!(c.accepts(2048));
147 assert!(!c.accepts(0)); assert!(!c.accepts(4096)); assert!(!c.accepts(12)); }
151}