1use crate::error::{Error, Kind};
3use crate::hashing::{HashMap, HashMapExt, HashSet, HashSetExt};
4use std::{borrow::Cow, fmt, sync::Arc};
5
6pub const MAX_RANK: usize = 128;
8pub const MAX_GENERATED_ELEMENTS: usize = 1_000_000_000;
10
11#[inline]
13pub fn count(shape: &[usize]) -> usize { match *shape { [] => 1, [n] => n, [m, n] => m * n, _ => shape.iter().product() } }
14
15#[inline]
17pub fn element_count(shape: &[usize]) -> Result<usize, Error> {
18 if shape.len() > MAX_RANK { return Err(Error::new(Kind::Limit, format!("an array has at most {MAX_RANK} axes"))); }
19 shape.iter().try_fold(1usize, |n, &d| n.checked_mul(d)).or_else(|| shape.contains(&0).then_some(0))
20 .ok_or_else(|| Error::new(Kind::Limit, "an array of this shape has too many items to count"))
21}
22
23#[inline]
25pub fn generated_len(shape: &[usize]) -> Result<usize, Error> {
26 let len = element_count(shape)?;
27 if len > MAX_GENERATED_ELEMENTS { return Err(Error::new(Kind::Limit, format!("an array has at most {MAX_GENERATED_ELEMENTS} items"))); }
28 crate::memory::admit(len)?;
29 Ok(len)
30}
31
32#[inline]
34pub fn strides(shape: &[usize]) -> Vec<usize> {
35 let mut strides = vec![1; shape.len()];
36 for a in (1..shape.len()).rev() { strides[a - 1] = strides[a] * shape[a]; }
37 strides
38}
39
40pub fn exclusive_sums(sizes: impl IntoIterator<Item = usize>) -> Vec<usize> {
42 let mut at = 0;
43 sizes.into_iter().map(|n| {
44 let start = at;
45 at += n;
46 start
47 }).collect()
48}
49
50#[inline]
52pub fn coordinates(mut flat: usize, shape: &[usize]) -> impl Iterator<Item = (usize, usize)> + '_ {
53 shape.iter().enumerate().rev().map(move |(axis, &len)| {
54 let coordinate = flat % len;
55 flat /= len;
56 (axis, coordinate)
57 })
58}
59
60#[inline]
62pub fn position(n: i64, len: usize) -> Option<usize> { let i = if n < 0 { len as i64 + n } else { n }; usize::try_from(i).ok().filter(|&i| i < len) }
63
64#[derive(Clone, Copy, Debug)]
66pub struct Lanes { pub outer: usize, pub len: usize, pub inner: usize }
67
68impl Lanes {
69 #[inline]
71 pub fn new(shape: &[usize], axis: usize) -> Result<Self, Error> {
72 let &len = shape.get(axis).ok_or_else(|| Error::new(Kind::Rank, format!("axis {axis} is outside an array of rank {}", shape.len())))?;
73 Ok(Self { outer: element_count(&shape[..axis])?, len, inner: element_count(&shape[axis + 1..])? })
74 }
75 #[inline]
77 pub fn offset(&self, i: usize, j: usize, k: usize) -> usize { (i * self.len + j) * self.inner + k }
78 #[inline]
80 pub fn dims(&self) -> [usize; 3] { [self.outer, self.len, self.inner] }
81}
82
83pub fn agreed<'a, T: PartialEq + Clone + 'a>(values: impl IntoIterator<Item = Option<&'a T>>) -> Option<T> {
85 let mut found = None;
86 for value in values.into_iter().flatten() {
87 if found.is_some_and(|f| f != value) { return None; }
88 found = Some(value);
89 }
90 found.cloned()
91}
92
93pub fn repeated_name(names: &[Option<Arc<str>>]) -> Option<&Arc<str>> { let mut seen = HashSet::new(); names.iter().flatten().find(|n| !seen.insert(*n)) }
95
96#[inline]
98pub fn hidden(name: &str) -> bool { name.starts_with('_') }
99
100pub const FRESH: char = '·';
102
103pub fn shown(name: &str) -> Cow<'_, str> {
105 match name.rfind(FRESH) { Some(i) if hidden(name) => format!("{}…", &name[..i + FRESH.len_utf8()]).into(), _ => name.into() }
106}
107
108#[inline]
110pub fn names_conflict(a: Option<&str>, b: Option<&str>) -> bool { matches!((a, b), (Some(a), Some(b)) if a != b && hidden(a) == hidden(b)) }
111
112#[derive(Clone)]
114pub struct Shape(Repr);
115
116#[derive(Clone)]
117enum Repr { Small(u8, [usize; 2]), Large(Box<[usize]>) }
118
119impl Shape {
120 pub const EMPTY: Self = Self(Repr::Small(0, [0; 2]));
122 fn spliced(parts: [&[usize]; 3]) -> Self {
124 let len: usize = parts.iter().map(|p| p.len()).sum();
125 if len > 2 { return Self(Repr::Large(parts.concat().into_boxed_slice())); }
126 let mut small = [0; 2];
127 for (slot, &n) in small.iter_mut().zip(parts.into_iter().flatten()) { *slot = n; }
128 Self(Repr::Small(len as u8, small))
129 }
130}
131impl Default for Shape {
132 #[inline]
133 fn default() -> Self { Self::EMPTY }
134}
135impl std::ops::Deref for Shape {
136 type Target = [usize];
137 #[inline]
138 fn deref(&self) -> &[usize] { match &self.0 { Repr::Small(n, lengths) => &lengths[..usize::from(*n)], Repr::Large(lengths) => &lengths[..] } }
139}
140impl std::ops::DerefMut for Shape {
141 #[inline]
142 fn deref_mut(&mut self) -> &mut [usize] {
143 match &mut self.0 { Repr::Small(n, lengths) => &mut lengths[..usize::from(*n)], Repr::Large(lengths) => &mut lengths[..] }
144 }
145}
146impl PartialEq for Shape {
147 #[inline]
148 fn eq(&self, other: &Self) -> bool { **self == **other }
149}
150impl Eq for Shape {}
151impl fmt::Debug for Shape { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&**self, f) } }
152impl From<&[usize]> for Shape {
153 #[inline]
154 fn from(lengths: &[usize]) -> Self {
155 match *lengths {
156 [] => Self::EMPTY, [a] => Self(Repr::Small(1, [a, 0])), [a, b] => Self(Repr::Small(2, [a, b])), _ => Self(Repr::Large(lengths.into()))
157 }
158 }
159}
160impl From<Vec<usize>> for Shape {
161 #[inline]
162 fn from(lengths: Vec<usize>) -> Self { if lengths.len() > 2 { Self(Repr::Large(lengths.into_boxed_slice())) } else { Self::from(lengths.as_slice()) } }
163}
164impl<const N: usize> From<[usize; N]> for Shape { fn from(lengths: [usize; N]) -> Self { Self::from(lengths.as_slice()) } }
165impl FromIterator<usize> for Shape {
166 fn from_iter<I: IntoIterator<Item = usize>>(lengths: I) -> Self {
167 let mut lengths = lengths.into_iter();
168 let mut small = [0; 2];
169 for n in 0..2 {
170 let Some(len) = lengths.next() else { return Self(Repr::Small(n, small)) };
171 small[usize::from(n)] = len;
172 }
173 match lengths.next() {
174 None => Self(Repr::Small(2, small)), Some(third) => Self(Repr::Large(small.into_iter().chain([third]).chain(lengths).collect()))
175 }
176 }
177}
178
179#[derive(Clone, Debug, Default)]
181pub struct Keys { names: Vec<Option<Arc<str>>>, index: HashMap<Arc<str>, usize> }
182
183impl PartialEq for Keys { fn eq(&self, other: &Self) -> bool { self.names == other.names } }
185impl Eq for Keys {}
186
187#[derive(Clone, Debug)]
189pub enum Alignment {
190 Same,
192 Leading,
194 Table(Vec<Option<usize>>),
196}
197
198impl Keys {
199 pub fn partial(names: Vec<Option<Arc<str>>>) -> Result<Arc<Self>, Error> {
201 let mut index = HashMap::with_capacity(names.len());
202 for (i, name) in names.iter().enumerate() {
203 let Some(name) = name else { continue };
204 if index.insert(name.clone(), i).is_some() { return Err(duplicate(name)); }
205 }
206 Ok(Arc::new(Self { names, index }))
207 }
208 #[inline]
209 pub fn names(&self) -> &[Option<Arc<str>>] { &self.names }
210 #[inline]
211 pub fn len(&self) -> usize { self.names.len() }
212 #[inline]
213 pub fn is_empty(&self) -> bool { self.names.is_empty() }
214 #[inline]
216 pub fn complete(&self) -> bool { self.index.len() == self.names.len() }
217 #[inline]
219 pub fn blank(&self) -> bool { self.index.is_empty() && !self.is_empty() }
220 #[inline]
221 pub fn position(&self, name: &str) -> Option<usize> { self.index.get(name).copied() }
222 pub fn extend(&mut self, names: &[Option<Arc<str>>]) -> Result<(), Error> {
224 let mut new = HashSet::new();
225 if let Some(name) = names.iter().flatten().find(|n| self.position(n).is_some() || !new.insert(*n)) { return Err(duplicate(name)); }
226 for name in names {
227 if let Some(name) = name { self.index.insert(name.clone(), self.names.len()); }
228 self.names.push(name.clone());
229 }
230 Ok(())
231 }
232 pub fn align(&self, wanted: &Self) -> Vec<Option<usize>> {
234 let mut gaps = self.names.iter().enumerate().filter(|(_, n)| n.is_none()).map(|(i, _)| i);
235 wanted.names.iter().map(|n| match n { Some(n) => self.position(n), None => gaps.next() }).collect()
236 }
237 pub fn positions(&self, wanted: &Self, subset: bool) -> Result<Vec<usize>, Error> {
239 let kind = if subset { Kind::Index } else { Kind::Length };
240 if !subset && self.len() != wanted.len() {
241 return Err(Error::new(kind, format!("keyed axes of lengths {} and {} don't pair", self.len(), wanted.len())));
242 }
243 self.align(wanted).into_iter().zip(&wanted.names).map(|(p, name)| {
244 p.ok_or_else(|| match name {
245 Some(name) => Error::new(kind, format!("missing key: {}", shown(name))), None => Error::new(kind, "too few positions without keys")
246 })
247 }).collect()
248 }
249 pub fn select(&self, positions: impl IntoIterator<Item = Option<usize>>) -> Result<Arc<Self>, Error> {
251 Self::partial(positions.into_iter().map(|i| i.and_then(|i| self.names[i].clone())).collect())
252 }
253 pub fn aligned(self: &Arc<Self>, wanted: &Arc<Self>) -> Alignment {
255 if Arc::ptr_eq(self, wanted) || self == wanted { Alignment::Same }
256 else if wanted.names.starts_with(&self.names) { Alignment::Leading }
257 else { Alignment::Table(self.align(wanted)) }
258 }
259 pub fn union(self: &Arc<Self>, other: &Arc<Self>) -> Result<(Arc<Self>, [Alignment; 2]), Error> {
261 if Arc::ptr_eq(self, other) { return Ok((self.clone(), [Alignment::Same, Alignment::Same])); }
262 let (a, b) = (self.len() - self.index.len(), other.len() - other.index.len());
263 if a != b { return Err(Error::new(Kind::Length, format!("axes with {a} and {b} positions without keys don't align"))); }
264 if self == other { return Ok((self.clone(), [Alignment::Same, Alignment::Same])); }
265 if other.names.starts_with(&self.names) { return Ok((other.clone(), [Alignment::Leading, Alignment::Same])); }
266 if self.names.starts_with(&other.names) { return Ok((self.clone(), [Alignment::Same, Alignment::Leading])); }
267 let places = self.align(other);
268 let mut table = vec![None; self.len()];
270 for (j, &p) in places.iter().enumerate() { match p { Some(p) => table[p] = Some(j), None => table.push(Some(j)) } }
271 if table.len() == self.len() { return Ok((self.clone(), [Alignment::Same, Alignment::Table(table)])); }
272 let keys = Self::partial(self.names.iter().chain(other.names.iter().zip(&places).filter(|(_, p)| p.is_none()).map(|(k, _)| k)).cloned().collect())?;
273 Ok((keys, [Alignment::Leading, Alignment::Table(table)]))
274 }
275}
276
277fn duplicate(key: &str) -> Error { Error::new(Kind::Domain, format!("duplicate key: {}", shown(key))) }
279
280#[derive(Clone, Debug, Default, PartialEq, Eq)]
282pub struct Layout { shape: Shape, meta: Option<Box<Meta>> }
283
284#[derive(Clone, Debug, Default, PartialEq, Eq)]
286struct Meta { keys: Vec<Option<Arc<Keys>>>, names: Vec<Option<Arc<str>>> }
287
288impl<S: Into<Shape>> From<S> for Layout {
289 #[inline]
290 fn from(shape: S) -> Self { Self { shape: shape.into(), meta: None } }
291}
292impl Layout {
293 pub const EMPTY: Self = Self { shape: Shape::EMPTY, meta: None };
295 #[inline]
296 pub fn shape(&self) -> &[usize] { &self.shape }
297 #[inline]
299 pub fn count(&self) -> usize { count(&self.shape) }
300 #[inline]
302 pub fn names(&self) -> &[Option<Arc<str>>] { self.meta.as_ref().map_or(&[], |l| &l.names) }
303 #[inline]
304 pub fn name(&self, axis: usize) -> Option<&Arc<str>> { self.names().get(axis).and_then(Option::as_ref) }
305 #[inline]
307 pub fn axis_named(&self, name: &str) -> Option<usize> { self.names().iter().position(|n| n.as_deref() == Some(name)) }
308 fn meta_mut(&mut self) -> &mut Meta { self.meta.get_or_insert_with(Default::default) }
309 fn tidy(mut self) -> Self { if self.meta.as_deref() == Some(&Meta::default()) { self.meta = None; } self }
311 fn edit(mut self, empty: bool, set: impl FnOnce(&mut Meta)) -> Self {
313 if empty && self.meta.is_none() { return self; }
314 set(self.meta_mut());
315 self.tidy()
316 }
317 pub fn with_names(self, names: Vec<Option<Arc<str>>>) -> Result<Self, Error> {
319 if !names.is_empty() && names.len() != self.shape.len() {
320 return Err(Error::new(Kind::Rank, format!("{} names for {} axes", names.len(), self.shape.len())));
321 }
322 if let Some(name) = repeated_name(&names) { return Err(Error::new(Kind::Domain, format!("the result would have two axes named {}", shown(name)))); }
323 Ok(self.inherit_names(names))
324 }
325 pub fn inherit_names(self, names: Vec<Option<Arc<str>>>) -> Self {
327 assert!(names.is_empty() || names.len() == self.shape.len());
328 let empty = names.iter().all(Option::is_none);
329 self.edit(empty, |m| m.names = if empty { vec![] } else { names })
330 }
331 pub fn unique_names(mut self) -> Self {
333 let Some(meta) = self.meta.as_mut() else { return self };
334 let mut counts = HashMap::new();
335 for name in meta.names.iter().flatten() { *counts.entry(name.clone()).or_insert(0) += 1; }
336 for name in &mut meta.names { if name.as_ref().is_some_and(|n| counts[n] > 1) { *name = None; } }
337 if meta.names.iter().all(Option::is_none) { meta.names.clear(); }
338 self.tidy()
339 }
340 #[inline]
341 fn key_list(&self) -> &[Option<Arc<Keys>>] { self.meta.as_ref().map_or(&[], |l| &l.keys) }
342 #[inline]
343 pub fn has_keys(&self) -> bool { !self.key_list().is_empty() }
344 #[inline]
346 pub fn has_names(&self) -> bool { !self.names().is_empty() }
347 #[inline]
349 pub fn plain(&self) -> bool { self.meta.is_none() }
350 #[inline]
351 pub fn keys(&self, axis: usize) -> Option<&Arc<Keys>> { self.key_list().get(axis).and_then(Option::as_ref) }
352 pub fn all_keys(&self) -> Vec<Option<Arc<Keys>>> { (0..self.shape.len()).map(|a| self.keys(a).cloned()).collect() }
354 pub fn matches(&self, other: &Self) -> bool {
356 self.shape == other.shape && (0..self.shape.len()).all(|a| {
357 !names_conflict(self.name(a).map(AsRef::as_ref), other.name(a).map(AsRef::as_ref)) && match (self.keys(a), other.keys(a)) {
358 (Some(x), Some(y)) => !y.align(x).contains(&None), _ => true
359 }
360 })
361 }
362 pub fn without_keys(self, axis: usize) -> Self {
364 if self.keys(axis).is_none() { return self; }
365 let mut keys = self.all_keys();
366 keys[axis] = None;
367 self.with_keys(keys).unwrap()
368 }
369 pub fn with_keys(self, keys: Vec<Option<Arc<Keys>>>) -> Result<Self, Error> {
371 if !keys.is_empty() && keys.len() != self.shape.len() {
372 return Err(Error::new(Kind::Rank, format!("keys for {} axes of an array with {} axes", keys.len(), self.shape.len())));
373 }
374 if let Some((k, n)) = keys.iter().zip(self.shape.iter()).find_map(|(k, &n)| k.as_ref().filter(|k| k.len() != n).map(|k| (k.len(), n))) {
375 return Err(Error::new(Kind::Length, format!("{k} keys for an axis of length {n}")));
376 }
377 let keys: Vec<_> = keys.into_iter().map(|k| k.filter(|k| !k.blank())).collect();
378 let empty = keys.iter().all(Option::is_none);
379 Ok(self.edit(empty, |m| m.keys = if empty { vec![] } else { keys }))
380 }
381 fn gathered<'a>(sources: impl Iterator<Item = (&'a Self, usize)> + Clone) -> Self {
383 let shape: Shape = sources.clone().map(|(l, a)| l.shape[a]).collect();
384 if sources.clone().all(|(l, _)| l.plain()) { return Self::from(shape); }
385 let keys = sources.clone().map(|(l, a)| l.keys(a).cloned()).collect();
386 let names = sources.map(|(l, a)| l.name(a).cloned()).collect();
387 Self::from(shape).with_keys(keys).unwrap().inherit_names(names)
388 }
389 pub fn axes(&self, axes: impl Iterator<Item = usize> + Clone) -> Self { Self::gathered(axes.map(|a| (self, a))) }
391 pub fn concat(&self, other: &Self) -> Self { Self::gathered((0..self.shape.len()).map(|a| (self, a)).chain((0..other.shape.len()).map(|a| (other, a)))) }
393 pub fn contracted(&self, right: &Self) -> Self { self.axes(0..self.shape.len().saturating_sub(1)).concat(&right.axes(1..right.shape.len())) }
395 pub fn merged(&self, axes: std::ops::Range<usize>, len: usize) -> Self {
397 let shape = Shape::spliced([&self.shape[..axes.start], &[len], &self.shape[axes.end..]]);
398 if self.plain() { return Self::from(shape); }
399 let source = |a: usize| match a.cmp(&axes.start) {
400 std::cmp::Ordering::Less => Some(a), std::cmp::Ordering::Equal => None, std::cmp::Ordering::Greater => Some(a - 1 + axes.len())
401 };
402 let keys = (0..shape.len()).map(|a| source(a).and_then(|s| self.keys(s).cloned())).collect();
403 let names = (0..shape.len()).map(|a| source(a).and_then(|s| self.name(s).cloned())).collect();
404 Self::from(shape).with_keys(keys).unwrap().inherit_names(names)
405 }
406 pub fn select(&self, axis: usize, positions: impl ExactSizeIterator<Item = Option<usize>>) -> Result<Self, Error> {
408 let mut layout = self.clone();
409 layout.shape[axis] = positions.len();
410 let Some(keys) = self.keys(axis) else { return Ok(layout) };
411 let mut all = self.all_keys();
412 all[axis] = keys.select(positions).ok();
413 layout.with_keys(all)
414 }
415 pub fn append(&mut self, len: usize, names: Option<&[Option<Arc<str>>]>) -> Result<(), Error> {
417 let (old, rank) = (self.shape[0], self.shape.len());
418 if let Some(names) = names {
419 match self.meta.as_mut().and_then(|m| m.keys.first_mut()).and_then(Option::as_mut) {
420 Some(keys) => Arc::make_mut(keys).extend(names)?,
421 None => {
422 let keys = Keys::partial(std::iter::repeat_n(None, old).chain(names.iter().cloned()).collect())?;
423 let meta = self.meta_mut();
424 if meta.keys.is_empty() { meta.keys = vec![None; rank]; }
425 meta.keys[0] = Some(keys);
426 }
427 }
428 }
429 self.shape[0] += len;
430 Ok(())
431 }
432}