Skip to main content

dodecet_encoder/
array.rs

1//! # DodecetArray: Fixed-size arrays of dodecets
2//!
3//! Provides fixed-size arrays optimized for geometric operations.
4
5use crate::Dodecet;
6use std::ops::{Deref, DerefMut};
7
8/// A fixed-size array of dodecets
9///
10/// # Example
11///
12/// ```rust
13/// use dodecet_encoder::{Dodecet, DodecetArray};
14///
15/// let arr = DodecetArray::<3>::from_slice(&[0x123, 0x456, 0x789]);
16/// assert_eq!(arr[0], Dodecet::from_hex(0x123));
17/// ```
18#[derive(Debug, Clone, PartialEq, Eq)]
19pub struct DodecetArray<const N: usize> {
20    data: [Dodecet; N],
21}
22
23impl<const N: usize> DodecetArray<N> {
24    /// Create a new dodecet array initialized to zeros
25    ///
26    /// # Example
27    ///
28    /// ```rust
29    /// use dodecet_encoder::{DodecetArray, Dodecet};
30    ///
31    /// let arr: DodecetArray<3> = DodecetArray::new();
32    /// assert!(arr.iter().all(|d| d.is_zero()));
33    /// ```
34    pub fn new() -> Self
35    where
36        [Dodecet; N]: Sized,
37    {
38        DodecetArray {
39            data: [Dodecet::from_hex(0); N],
40        }
41    }
42
43    /// Create from a slice of u16 values
44    ///
45    /// # Panics
46    /// Panics if slice length != N or any value > 4095
47    ///
48    /// # Example
49    ///
50    /// ```rust
51    /// use dodecet_encoder::{DodecetArray, Dodecet};
52    ///
53    /// let arr = DodecetArray::<3>::from_slice(&[0x123, 0x456, 0x789]);
54    /// ```
55    pub fn from_slice(values: &[u16]) -> Self
56    where
57        [Dodecet; N]: Sized,
58    {
59        assert_eq!(values.len(), N, "Slice length must match array size");
60
61        let data: [Dodecet; N] = values
62            .iter()
63            .map(|&v| Dodecet::from_hex(v))
64            .collect::<Vec<_>>()
65            .try_into()
66            .unwrap();
67
68        DodecetArray { data }
69    }
70
71    /// Create from dodecets
72    ///
73    /// # Example
74    ///
75    /// ```rust
76    /// use dodecet_encoder::{Dodecet, DodecetArray};
77    ///
78    /// let arr = DodecetArray::<3>::from_dodecets([
79    ///     Dodecet::from_hex(0x123),
80    ///     Dodecet::from_hex(0x456),
81    ///     Dodecet::from_hex(0x789),
82    /// ]);
83    /// ```
84    pub fn from_dodecets(data: [Dodecet; N]) -> Self {
85        DodecetArray { data }
86    }
87
88    /// Get inner array
89    pub fn as_inner(&self) -> &[Dodecet; N] {
90        &self.data
91    }
92
93    /// Get mutable inner array
94    pub fn as_inner_mut(&mut self) -> &mut [Dodecet; N] {
95        &mut self.data
96    }
97
98    /// Convert to hex string (concatenated)
99    ///
100    /// # Example
101    ///
102    /// ```rust
103    /// use dodecet_encoder::{DodecetArray, Dodecet};
104    ///
105    /// let arr = DodecetArray::<2>::from_slice(&[0x123, 0x456]);
106    /// assert_eq!(arr.to_hex_string(), "123456");
107    /// ```
108    pub fn to_hex_string(&self) -> String {
109        self.data
110            .iter()
111            .map(|d| d.to_hex_string())
112            .collect::<Vec<_>>()
113            .join("")
114    }
115
116    /// Parse from hex string
117    ///
118    /// # Example
119    ///
120    /// ```rust
121    /// use dodecet_encoder::{DodecetArray, Dodecet};
122    ///
123    /// let arr = DodecetArray::<2>::from_hex_str("123456").unwrap();
124    /// assert_eq!(arr[0].value(), 0x123);
125    /// assert_eq!(arr[1].value(), 0x456);
126    /// ```
127    pub fn from_hex_str(s: &str) -> crate::Result<Self>
128    where
129        [Dodecet; N]: Sized,
130    {
131        let expected_len = N * 3;
132        if s.len() != expected_len {
133            return Err(crate::DodecetError::InvalidHex);
134        }
135
136        let mut data = [Dodecet::from_hex(0); N];
137
138        for (i, chunk) in s.as_bytes().chunks(3).enumerate() {
139            let chunk_str = std::str::from_utf8(chunk).unwrap();
140            data[i] = Dodecet::from_hex_str(chunk_str)?;
141        }
142
143        Ok(DodecetArray { data })
144    }
145
146    /// Map each dodecet through a function
147    ///
148    /// # Example
149    ///
150    /// ```rust
151    /// use dodecet_encoder::{DodecetArray, Dodecet};
152    ///
153    /// let arr = DodecetArray::<3>::from_slice(&[0x100, 0x200, 0x300]);
154    /// let doubled = arr.map(|d| Dodecet::from_hex(d.value() * 2));
155    /// assert_eq!(doubled[0].value(), 0x200);
156    /// ```
157    pub fn map<F>(self, f: F) -> Self
158    where
159        F: FnMut(Dodecet) -> Dodecet,
160    {
161        DodecetArray {
162            data: self.data.map(f),
163        }
164    }
165
166    /// Zip with another array
167    ///
168    /// # Example
169    ///
170    /// ```rust
171    /// use dodecet_encoder::{DodecetArray, Dodecet};
172    ///
173    /// let a = DodecetArray::<3>::from_slice(&[0x100, 0x200, 0x300]);
174    /// let b = DodecetArray::<3>::from_slice(&[0x001, 0x002, 0x003]);
175    /// let sum = a.zip_map(b, |x, y| x + y);
176    /// assert_eq!(sum[0].value(), 0x101);
177    /// ```
178    pub fn zip_map<F>(self, other: Self, mut f: F) -> Self
179    where
180        F: FnMut(Dodecet, Dodecet) -> Dodecet,
181    {
182        DodecetArray {
183            data: self
184                .data
185                .into_iter()
186                .zip(other.data)
187                .map(|(a, b)| f(a, b))
188                .collect::<Vec<_>>()
189                .try_into()
190                .unwrap(),
191        }
192    }
193
194    /// Sum all dodecets
195    ///
196    /// # Example
197    ///
198    /// ```rust
199    /// use dodecet_encoder::{DodecetArray, Dodecet};
200    ///
201    /// let arr = DodecetArray::<3>::from_slice(&[0x100, 0x200, 0x300]);
202    /// assert_eq!(arr.sum().value(), 0x600);
203    /// ```
204    pub fn sum(self) -> Dodecet {
205        self.data.into_iter().fold(Dodecet::from_hex(0), |acc, d| acc + d)
206    }
207
208    /// Get the average
209    ///
210    /// # Example
211    ///
212    /// ```rust
213    /// use dodecet_encoder::{DodecetArray, Dodecet};
214    ///
215    /// let arr = DodecetArray::<3>::from_slice(&[0x000, 0x003, 0x006]);
216    /// let avg = arr.average();
217    /// assert_eq!(avg.value(), 0x003);
218    /// ```
219    pub fn average(&self) -> Dodecet {
220        let sum: u32 = self.data.iter().map(|d| d.value() as u32).sum();
221        Dodecet::from_hex((sum / N as u32) as u16)
222    }
223}
224
225impl<const N: usize> Default for DodecetArray<N>
226where
227    [Dodecet; N]: Sized,
228{
229    fn default() -> Self {
230        Self::new()
231    }
232}
233
234impl<const N: usize> Deref for DodecetArray<N> {
235    type Target = [Dodecet; N];
236
237    fn deref(&self) -> &Self::Target {
238        &self.data
239    }
240}
241
242impl<const N: usize> DerefMut for DodecetArray<N> {
243    fn deref_mut(&mut self) -> &mut Self::Target {
244        &mut self.data
245    }
246}
247
248impl<const N: usize> From<[u16; N]> for DodecetArray<N> {
249    fn from(values: [u16; N]) -> Self {
250        DodecetArray::from_slice(&values)
251    }
252}
253
254impl<const N: usize> From<[Dodecet; N]> for DodecetArray<N> {
255    fn from(data: [Dodecet; N]) -> Self {
256        DodecetArray { data }
257    }
258}
259
260impl<const N: usize> std::fmt::Display for DodecetArray<N> {
261    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
262        write!(f, "[")?;
263        for (i, d) in self.data.iter().enumerate() {
264            if i > 0 {
265                write!(f, ", ")?;
266            }
267            write!(f, "{}", d)?;
268        }
269        write!(f, "]")
270    }
271}
272
273#[cfg(test)]
274mod tests {
275    use super::*;
276
277    #[test]
278    fn test_creation() {
279        let arr: DodecetArray<3> = DodecetArray::new();
280        assert_eq!(arr.len(), 3);
281        assert!(arr.iter().all(|d| d.is_zero()));
282    }
283
284    #[test]
285    fn test_from_slice() {
286        let arr = DodecetArray::<3>::from_slice(&[0x123, 0x456, 0x789]);
287        assert_eq!(arr[0].value(), 0x123);
288        assert_eq!(arr[1].value(), 0x456);
289        assert_eq!(arr[2].value(), 0x789);
290    }
291
292    #[test]
293    fn test_indexing() {
294        let mut arr = DodecetArray::<3>::from_slice(&[0x123, 0x456, 0x789]);
295        assert_eq!(arr[0].value(), 0x123);
296
297        arr[1] = Dodecet::from_hex(0xABC);
298        assert_eq!(arr[1].value(), 0xABC);
299    }
300
301    #[test]
302    fn test_hex_string() {
303        let arr = DodecetArray::<2>::from_slice(&[0x123, 0x456]);
304        assert_eq!(arr.to_hex_string(), "123456");
305
306        let arr2 = DodecetArray::<2>::from_hex_str("123456").unwrap();
307        assert_eq!(arr2[0].value(), 0x123);
308        assert_eq!(arr2[1].value(), 0x456);
309    }
310
311    #[test]
312    fn test_map() {
313        let arr = DodecetArray::<3>::from_slice(&[0x100, 0x200, 0x300]);
314        let doubled = arr.map(|d| Dodecet::from_hex(d.value() * 2));
315        assert_eq!(doubled[0].value(), 0x200);
316        assert_eq!(doubled[1].value(), 0x400);
317        assert_eq!(doubled[2].value(), 0x600);
318    }
319
320    #[test]
321    fn test_zip_map() {
322        let a = DodecetArray::<3>::from_slice(&[0x100, 0x200, 0x300]);
323        let b = DodecetArray::<3>::from_slice(&[0x001, 0x002, 0x003]);
324        let sum = a.zip_map(b, |x, y| x + y);
325        assert_eq!(sum[0].value(), 0x101);
326        assert_eq!(sum[1].value(), 0x202);
327        assert_eq!(sum[2].value(), 0x303);
328    }
329
330    #[test]
331    fn test_sum() {
332        let arr = DodecetArray::<3>::from_slice(&[0x100, 0x200, 0x300]);
333        assert_eq!(arr.sum().value(), 0x600);
334    }
335
336    #[test]
337    fn test_average() {
338        let arr = DodecetArray::<3>::from_slice(&[0x000, 0x003, 0x006]);
339        assert_eq!(arr.average().value(), 0x003);
340    }
341
342    #[test]
343    fn test_display() {
344        let arr = DodecetArray::<3>::from_slice(&[0x123, 0x456, 0x789]);
345        assert_eq!(format!("{}", arr), "[0x123, 0x456, 0x789]");
346    }
347}