1use serde::{Deserialize, Serialize};
11
12#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
19pub struct Compact2DArray<T> {
20 data: Vec<T>,
22 indptr: Vec<i32>,
24}
25
26impl<T> Default for Compact2DArray<T> {
27 fn default() -> Self {
28 Self::new()
29 }
30}
31
32impl<T> Compact2DArray<T> {
33 pub fn new() -> Self {
35 Self {
36 data: Vec::new(),
37 indptr: vec![0],
38 }
39 }
40
41 pub fn len(&self) -> i32 {
43 self.indptr.len() as i32 - 1
44 }
45
46 pub fn is_empty(&self) -> bool {
48 self.len() == 0
49 }
50
51 pub fn row(&self, i: i32) -> &[T] {
56 assert!(
57 i >= 0 && i < self.len(),
58 "Compact2DArray index {i} is out of bound"
59 );
60 let start = self.indptr[i as usize] as usize;
61 let end = self.indptr[i as usize + 1] as usize;
62 &self.data[start..end]
63 }
64
65 pub fn back(&self) -> &[T] {
70 assert!(!self.is_empty(), "Compact2DArray is empty");
71 self.row(self.len() - 1)
72 }
73
74 pub fn iter(&self) -> impl Iterator<Item = &[T]> {
76 (0..self.len()).map(move |i| self.row(i))
77 }
78
79 pub fn data(&self) -> &[T] {
81 &self.data
82 }
83
84 pub fn indptr(&self) -> &[i32] {
86 &self.indptr
87 }
88
89 pub fn push_row(&mut self, new_data: &[T]) -> i32
91 where
92 T: Clone,
93 {
94 self.data.extend_from_slice(new_data);
95 self.indptr.push(self.data.len() as i32);
96 self.indptr.len() as i32 - 2
97 }
98
99 pub fn push_row_owned(&mut self, new_data: Vec<T>) -> i32 {
102 self.data.extend(new_data);
103 self.indptr.push(self.data.len() as i32);
104 self.indptr.len() as i32 - 2
105 }
106
107 pub fn push_row_non_contiguous(&mut self, first: T, rest: &[T]) -> i32
111 where
112 T: Clone,
113 {
114 self.data.push(first);
115 self.data.extend_from_slice(rest);
116 self.indptr.push(self.data.len() as i32);
117 self.indptr.len() as i32 - 2
118 }
119
120 pub fn push_in_latest_row(&mut self, new_data: T) {
125 assert!(
126 self.indptr.len() > 1,
127 "Cannot push back in an empty Compact2DArray"
128 );
129 self.data.push(new_data);
130 *self.indptr.last_mut().unwrap() += 1;
131 }
132
133 pub fn pop_rows(&mut self, cnt: i32) {
138 assert!(
139 cnt >= 0 && cnt <= self.len(),
140 "Cannot pop {cnt} rows from a Compact2DArray with {} rows",
141 self.len()
142 );
143 let new_indptr_len = self.indptr.len() - cnt as usize;
144 self.indptr.truncate(new_indptr_len);
145 let new_data_len = *self.indptr.last().unwrap() as usize;
146 self.data.truncate(new_data_len);
147 }
148}
149
150#[cfg(test)]
151mod tests {
152 use super::*;
153
154 #[test]
155 fn empty_array() {
156 let arr: Compact2DArray<i32> = Compact2DArray::new();
157 assert_eq!(arr.len(), 0);
158 assert!(arr.is_empty());
159 assert_eq!(arr.indptr(), &[0]);
160 }
161
162 #[test]
163 fn push_and_read_rows() {
164 let mut arr = Compact2DArray::new();
165 assert_eq!(arr.push_row(&[1, 2, 3]), 0);
166 assert_eq!(arr.push_row(&[4]), 1);
167 assert_eq!(arr.push_row(&[]), 2);
168 assert_eq!(arr.push_row(&[5, 6]), 3);
169 assert_eq!(arr.len(), 4);
170 assert_eq!(arr.row(0), &[1, 2, 3]);
171 assert_eq!(arr.row(1), &[4]);
172 assert_eq!(arr.row(2), &[] as &[i32]);
173 assert_eq!(arr.row(3), &[5, 6]);
174 assert_eq!(arr.data(), &[1, 2, 3, 4, 5, 6]);
175 assert_eq!(arr.indptr(), &[0, 3, 4, 4, 6]);
176 }
177
178 #[test]
179 fn push_row_owned_matches_push_row() {
180 let mut arr = Compact2DArray::new();
181 arr.push_row_owned(vec![7, 8]);
182 arr.push_row_owned(vec![9]);
183 assert_eq!(arr.row(0), &[7, 8]);
184 assert_eq!(arr.row(1), &[9]);
185 }
186
187 #[test]
188 fn back_returns_last_row() {
189 let mut arr = Compact2DArray::new();
190 arr.push_row(&[1]);
191 arr.push_row(&[2, 3]);
192 assert_eq!(arr.back(), &[2, 3]);
193 }
194
195 #[test]
196 fn push_non_contiguous() {
197 let mut arr = Compact2DArray::new();
198 assert_eq!(arr.push_row_non_contiguous(10, &[20, 30]), 0);
199 assert_eq!(arr.row(0), &[10, 20, 30]);
200 assert_eq!(arr.push_row_non_contiguous(40, &[]), 1);
201 assert_eq!(arr.row(1), &[40]);
202 }
203
204 #[test]
205 fn push_in_latest_row() {
206 let mut arr = Compact2DArray::new();
207 arr.push_row(&[1, 2]);
208 arr.push_in_latest_row(3);
209 arr.push_in_latest_row(4);
210 assert_eq!(arr.row(0), &[1, 2, 3, 4]);
211 assert_eq!(arr.len(), 1);
212 }
213
214 #[test]
215 fn pop_rows() {
216 let mut arr = Compact2DArray::new();
217 arr.push_row(&[1, 2]);
218 arr.push_row(&[3]);
219 arr.push_row(&[4, 5, 6]);
220 arr.pop_rows(2);
221 assert_eq!(arr.len(), 1);
222 assert_eq!(arr.row(0), &[1, 2]);
223 assert_eq!(arr.data(), &[1, 2]);
224 arr.pop_rows(1);
225 assert!(arr.is_empty());
226 }
227
228 #[test]
229 fn iter_yields_all_rows() {
230 let mut arr = Compact2DArray::new();
231 arr.push_row(&[1]);
232 arr.push_row(&[2, 3]);
233 let rows: Vec<Vec<i32>> = arr.iter().map(|r| r.to_vec()).collect();
234 assert_eq!(rows, vec![vec![1], vec![2, 3]]);
235 }
236
237 #[test]
238 #[should_panic(expected = "out of bound")]
239 fn row_out_of_bounds_panics() {
240 let mut arr = Compact2DArray::new();
241 arr.push_row(&[1]);
242 arr.row(1);
243 }
244
245 #[test]
246 #[should_panic(expected = "Cannot push back in an empty")]
247 fn push_in_latest_row_empty_panics() {
248 let mut arr: Compact2DArray<i32> = Compact2DArray::new();
249 arr.push_in_latest_row(1);
250 }
251
252 #[test]
253 fn serde_round_trip() {
254 let mut arr = Compact2DArray::new();
255 arr.push_row(&[1, 2, 3]);
256 arr.push_row(&[4, 5]);
257 let json = serde_json::to_string(&arr).unwrap();
258 let back: Compact2DArray<i32> = serde_json::from_str(&json).unwrap();
259 assert_eq!(arr, back);
260 }
261}