Skip to main content

runlength_arrow/
rle.rs

1use std::sync::Arc;
2
3use arrow_array::cast::AsArray;
4use arrow_array::{
5    Array, ArrayRef, ArrowPrimitiveType, BooleanArray, Int64Array, PrimitiveArray, UInt64Array,
6    new_empty_array,
7};
8use arrow_cast::cast;
9use arrow_schema::{ArrowError, DataType};
10use arrow_select::concat::concat;
11use arrow_select::filter::filter;
12use arrow_select::take::take;
13use runlength::{Keep, Plan, Rle, RleLike, merge_plan, plan};
14
15use crate::batch::ArrowRleBatch;
16use crate::error::{Error, Result};
17use crate::identical::identical_neighbours;
18use crate::kernels::Kernel;
19
20/// A run-length encoded sequence whose values are an Arrow array of any type.
21///
22/// Like [`runlength::Rle`], it stores cumulative run ends and one value per
23/// run, starts at position 0, and has no zero-length runs. Values may contain
24/// nulls; a null is a value like any other and merges with neighbouring nulls.
25#[derive(Clone, Debug)]
26pub struct ArrowRle {
27    pub(crate) ends: Vec<i64>,
28    pub(crate) values: ArrayRef,
29}
30
31fn validate_ends(ends: &[i64]) -> Result<()> {
32    let mut prev = 0;
33    for (index, &end) in ends.iter().enumerate() {
34        if end <= prev {
35            return Err(runlength::Error::InvalidEnds { index }.into());
36        }
37        prev = end;
38    }
39    Ok(())
40}
41
42fn check_same_len(what: &'static str, left: usize, right: usize) -> Result<()> {
43    if left != right {
44        return Err(runlength::Error::LengthMismatch { what, left, right }.into());
45    }
46    Ok(())
47}
48
49/// The fill value operators use for a value type: zero for numbers, `false`
50/// for booleans, and `None` for other types (which then require equal lengths).
51pub fn default_fill(data_type: &DataType) -> Option<ArrayRef> {
52    if data_type == &DataType::Boolean {
53        return Some(Arc::new(BooleanArray::from(vec![false])));
54    }
55    if data_type.is_numeric() {
56        return cast(&Int64Array::from(vec![0]), data_type).ok();
57    }
58    None
59}
60
61/// Gathers `values[idx]`, where an index equal to `values.len()` means `fill`.
62pub(crate) fn gather(
63    values: &ArrayRef,
64    idx: &[usize],
65    fill: Option<&dyn Array>,
66) -> Result<ArrayRef> {
67    let n = values.len();
68    if idx.len() == n && idx.iter().enumerate().all(|(k, &i)| k == i) {
69        return Ok(values.clone());
70    }
71    // A plan only needs the fill value at its tail.
72    let source = if idx.last() == Some(&n) {
73        let fill = fill.expect("the length check guarantees a fill value past the end");
74        concat(&[values.as_ref(), fill])?
75    } else {
76        values.clone()
77    };
78    let indices = UInt64Array::from_iter_values(idx.iter().map(|&i| i as u64));
79    Ok(take(source.as_ref(), &indices, None)?)
80}
81
82/// An owned reference to a borrowed array.
83pub(crate) fn array_ref(array: &dyn Array) -> ArrayRef {
84    arrow_array::make_array(array.to_data())
85}
86
87pub(crate) fn check_fill(fill: Option<&dyn Array>, data_type: &DataType) -> Result<()> {
88    if let Some(fill) = fill {
89        if fill.len() != 1 || fill.data_type() != data_type {
90            return Err(Error::InvalidFill {
91                expected: data_type.clone(),
92                actual: fill.data_type().clone(),
93                len: fill.len(),
94            });
95        }
96    }
97    Ok(())
98}
99
100impl ArrowRle {
101    /// Builds a sequence from cumulative run ends and one value per run.
102    pub fn try_new(ends: Vec<i64>, values: ArrayRef) -> Result<Self> {
103        check_same_len("ends and values", ends.len(), values.len())?;
104        validate_ends(&ends)?;
105        Ok(ArrowRle { ends, values })
106    }
107
108    /// Builds a sequence from run lengths and one value per run. Zero-length
109    /// runs are dropped; neighbouring identical values are kept as they are.
110    pub fn from_runs(runs: &[i64], values: ArrayRef) -> Result<Self> {
111        check_same_len("runs and values", runs.len(), values.len())?;
112        let mut ends = Vec::with_capacity(runs.len());
113        let mut total: i64 = 0;
114        for (index, &run) in runs.iter().enumerate() {
115            if run < 0 {
116                return Err(runlength::Error::NegativeRun { index, run }.into());
117            }
118            if run > 0 {
119                total = total.checked_add(run).ok_or(runlength::Error::Overflow)?;
120                ends.push(total);
121            }
122        }
123        let values = if ends.len() < runs.len() {
124            let keep = BooleanArray::from_iter(runs.iter().map(|&r| Some(r > 0)));
125            filter(values.as_ref(), &keep)?
126        } else {
127            values
128        };
129        Ok(ArrowRle { ends, values })
130    }
131
132    /// Encodes a dense array, merging identical neighbours.
133    pub fn from_dense(values: ArrayRef) -> Result<Self> {
134        let ends = (1..=values.len() as i64).collect();
135        ArrowRle { ends, values }.merge()
136    }
137
138    /// An empty sequence of the given value type.
139    pub fn empty(data_type: &DataType) -> Self {
140        ArrowRle {
141            ends: Vec::new(),
142            values: new_empty_array(data_type),
143        }
144    }
145
146    /// Converts a core sequence of primitive values.
147    pub fn from_rle<T: ArrowPrimitiveType>(rle: &Rle<T::Native>) -> Self {
148        ArrowRle {
149            ends: rle.ends().to_vec(),
150            values: Arc::new(PrimitiveArray::<T>::from_iter_values(
151                rle.values().iter().copied(),
152            )),
153        }
154    }
155
156    /// Converts to a core sequence of primitive values. The values must be of
157    /// type `T` and contain no nulls.
158    pub fn to_rle<T: ArrowPrimitiveType>(&self) -> Result<Rle<T::Native>> {
159        let values = self
160            .values
161            .as_primitive_opt::<T>()
162            .ok_or_else(|| Error::UnsupportedType(self.values.data_type().clone()))?;
163        if values.null_count() > 0 {
164            return Err(Error::UnexpectedNulls);
165        }
166        Ok(Rle::from_ends(self.ends.clone(), values.values().to_vec())?)
167    }
168
169    /// The cumulative end of each run.
170    pub fn ends(&self) -> &[i64] {
171        &self.ends
172    }
173
174    /// One value per run.
175    pub fn values(&self) -> &ArrayRef {
176        &self.values
177    }
178
179    pub fn data_type(&self) -> &DataType {
180        self.values.data_type()
181    }
182
183    /// The number of positions the sequence covers.
184    pub fn len(&self) -> i64 {
185        self.ends.last().copied().unwrap_or(0)
186    }
187
188    pub fn is_empty(&self) -> bool {
189        self.ends.is_empty()
190    }
191
192    pub fn num_runs(&self) -> usize {
193        self.ends.len()
194    }
195
196    /// The length of each run.
197    pub fn runs(&self) -> Vec<i64> {
198        let mut prev = 0;
199        self.ends
200            .iter()
201            .map(|&end| {
202                let run = end - prev;
203                prev = end;
204                run
205            })
206            .collect()
207    }
208
209    pub fn into_parts(self) -> (Vec<i64>, ArrayRef) {
210        (self.ends, self.values)
211    }
212
213    /// The index of the run containing `pos`, if `pos` is in range.
214    pub fn run_index(&self, pos: i64) -> Option<usize> {
215        (0..self.len())
216            .contains(&pos)
217            .then(|| self.ends.partition_point(|&end| end <= pos))
218    }
219
220    /// The positions `start..end` as a new sequence starting at 0, clamped to
221    /// the sequence. The values are a zero-copy slice.
222    pub fn slice(&self, start: i64, end: i64) -> ArrowRle {
223        let (start, end) = (start.max(0), end.min(self.len()));
224        if start >= end {
225            return ArrowRle::empty(self.data_type());
226        }
227        let first = self.ends.partition_point(|&e| e <= start);
228        let last = self.ends.partition_point(|&e| e < end);
229        let mut ends: Vec<i64> = self.ends[first..last].iter().map(|&e| e - start).collect();
230        ends.push(end - start);
231        ArrowRle {
232            ends,
233            values: self.values.slice(first, last - first + 1),
234        }
235    }
236
237    /// Many slices at once, as a batch with one key column, `slice`, holding
238    /// the slice number. Each interval needs `0 <= start <= end`.
239    pub fn getitems(&self, starts: &[i64], ends: &[i64]) -> Result<ArrowRleBatch> {
240        check_same_len("starts and ends", starts.len(), ends.len())?;
241        let mut offsets = Vec::with_capacity(starts.len() + 1);
242        offsets.push(0);
243        let mut out_ends = Vec::new();
244        let mut run_ids = Vec::new();
245        for (index, (&start, &end)) in starts.iter().zip(ends).enumerate() {
246            if start < 0 || end < start {
247                return Err(runlength::Error::InvalidInterval { index, start, end }.into());
248            }
249            let (start, end) = (start, end.min(self.len()));
250            if start < end {
251                let first = self.ends.partition_point(|&e| e <= start);
252                let last = self.ends.partition_point(|&e| e < end);
253                out_ends.extend(self.ends[first..last].iter().map(|&e| e - start));
254                out_ends.push(end - start);
255                run_ids.extend(first as u64..=last as u64);
256            }
257            offsets.push(out_ends.len());
258        }
259        let values = take(self.values.as_ref(), &UInt64Array::from(run_ids), None)?;
260        let keys = UInt64Array::from_iter_values(0..starts.len() as u64);
261        ArrowRleBatch::from_parts_unchecked(
262            crate::batch::single_key_column("slice", Arc::new(keys)),
263            offsets,
264            out_ends,
265            values,
266        )
267    }
268
269    /// Expands the sequence into one value per position.
270    pub fn to_dense(&self) -> Result<ArrayRef> {
271        let mut indices = Vec::with_capacity(self.len() as usize);
272        for (i, run) in self.runs().into_iter().enumerate() {
273            indices.extend(std::iter::repeat_n(i as u64, run as usize));
274        }
275        Ok(take(
276            self.values.as_ref(),
277            &UInt64Array::from(indices),
278            None,
279        )?)
280    }
281
282    /// The plan for combining this sequence with `other`. See [`Plan`].
283    pub fn plan_with(&self, other: &ArrowRle) -> Plan {
284        plan(&self.ends, &other.ends)
285    }
286
287    /// For each pair of neighbouring runs, whether their values are identical.
288    pub fn identical_neighbours(&self) -> Result<Vec<bool>> {
289        identical_neighbours(self.values.as_ref())
290    }
291
292    /// Whether no two neighbouring runs hold identical values.
293    pub fn is_merged(&self) -> Result<bool> {
294        Ok(!self.identical_neighbours()?.into_iter().any(|x| x))
295    }
296
297    /// Merges neighbouring runs with identical values.
298    pub fn merge(self) -> Result<Self> {
299        let mask = self.identical_neighbours()?;
300        self.merge_mask(&mask, Keep::First)
301    }
302
303    /// Merges run `i + 1` into run `i` wherever `mask[i]` is true, keeping the
304    /// first or last value of each merged group.
305    pub fn merge_mask(self, mask: &[bool], keep: Keep) -> Result<Self> {
306        let groups = merge_plan(&self.ends, mask)?;
307        if groups.num_groups() == self.ends.len() {
308            return Ok(self);
309        }
310        let kept = groups.offsets.windows(2).map(|w| match keep {
311            Keep::First => w[0] as u64,
312            Keep::Last => w[1] as u64 - 1,
313        });
314        let values = take(
315            self.values.as_ref(),
316            &UInt64Array::from_iter_values(kept),
317            None,
318        )?;
319        Ok(ArrowRle {
320            ends: groups.ends,
321            values,
322        })
323    }
324
325    /// Combines two sequences position by position and merges the result.
326    ///
327    /// `f` receives the values of both sides gathered onto the plan's
328    /// segments (two arrays of equal length) and must return one value per
329    /// segment; any Arrow kernel fits. The shorter side is padded with its
330    /// fill value, a single value of the same type; with no fill value,
331    /// unequal lengths are an error.
332    pub fn zip_with<F>(
333        &self,
334        other: &ArrowRle,
335        fill_a: Option<&dyn Array>,
336        fill_b: Option<&dyn Array>,
337        f: F,
338    ) -> Result<ArrowRle>
339    where
340        F: FnOnce(&dyn Array, &dyn Array) -> std::result::Result<ArrayRef, ArrowError>,
341    {
342        self.zip_with_raw(other, fill_a, fill_b, f)?.merge()
343    }
344
345    /// Like [`zip_with`](ArrowRle::zip_with) but without merging the result.
346    pub fn zip_with_raw<F>(
347        &self,
348        other: &ArrowRle,
349        fill_a: Option<&dyn Array>,
350        fill_b: Option<&dyn Array>,
351        f: F,
352    ) -> Result<ArrowRle>
353    where
354        F: FnOnce(&dyn Array, &dyn Array) -> std::result::Result<ArrayRef, ArrowError>,
355    {
356        check_fill(fill_a, self.data_type())?;
357        check_fill(fill_b, other.data_type())?;
358        let (len_a, len_b) = (self.len(), other.len());
359        if (len_a < len_b && fill_a.is_none()) || (len_b < len_a && fill_b.is_none()) {
360            return Err(runlength::Error::UnequalLength {
361                left: len_a,
362                right: len_b,
363            }
364            .into());
365        }
366        let p = self.plan_with(other);
367        let a = gather(&self.values, &p.idx_a, fill_a)?;
368        let b = gather(&other.values, &p.idx_b, fill_b)?;
369        let values = f(a.as_ref(), b.as_ref())?;
370        if values.len() != p.ends.len() {
371            return Err(Error::WrongLength {
372                expected: p.ends.len(),
373                actual: values.len(),
374            });
375        }
376        Ok(ArrowRle {
377            ends: p.ends,
378            values,
379        })
380    }
381
382    /// Applies an Arrow kernel to the run values and merges the result.
383    pub fn map<F>(&self, f: F) -> Result<ArrowRle>
384    where
385        F: FnOnce(&dyn Array) -> std::result::Result<ArrayRef, ArrowError>,
386    {
387        let values = f(self.values.as_ref())?;
388        if values.len() != self.values.len() {
389            return Err(Error::WrongLength {
390                expected: self.values.len(),
391                actual: values.len(),
392            });
393        }
394        ArrowRle {
395            ends: self.ends.clone(),
396            values,
397        }
398        .merge()
399    }
400
401    /// Casts the values to `data_type` and merges any neighbours that become
402    /// identical (for example when floats are truncated to integers).
403    pub fn cast(&self, data_type: &DataType) -> Result<ArrowRle> {
404        if self.data_type() == data_type {
405            return Ok(self.clone());
406        }
407        let values = cast(self.values.as_ref(), data_type)?;
408        ArrowRle {
409            ends: self.ends.clone(),
410            values,
411        }
412        .merge()
413    }
414
415    /// Combines with `other` using [`default_fill`] on each side, and merges.
416    pub fn zip_with_default(&self, other: &ArrowRle, f: Kernel) -> Result<ArrowRle> {
417        self.op(other, f)
418    }
419
420    fn op(&self, other: &ArrowRle, f: Kernel) -> Result<ArrowRle> {
421        let fill_a = default_fill(self.data_type());
422        let fill_b = default_fill(other.data_type());
423        self.zip_with(other, fill_a.as_deref(), fill_b.as_deref(), f)
424    }
425}
426
427crate::kernels::kernel_methods!(
428    ArrowRle,
429    " The shorter side is padded with [`default_fill`]."
430);
431
432impl PartialEq for ArrowRle {
433    /// Whether both sequences represent the same values at every position
434    /// (by [`identical_neighbours`]'s rule), however the runs are split.
435    fn eq(&self, other: &Self) -> bool {
436        if self.len() != other.len() || self.data_type() != other.data_type() {
437            return false;
438        }
439        let p = self.plan_with(other);
440        let (Ok(a), Ok(b)) = (
441            gather(&self.values, &p.idx_a, None),
442            gather(&other.values, &p.idx_b, None),
443        ) else {
444            return false;
445        };
446        // Interleave a and b so each pair becomes a neighbouring pair.
447        let n = a.len();
448        let indices: Vec<(usize, usize)> = (0..n).flat_map(|k| [(0, k), (1, k)]).collect();
449        let Ok(pairs) = arrow_select::interleave::interleave(&[a.as_ref(), b.as_ref()], &indices)
450        else {
451            return false;
452        };
453        identical_neighbours(pairs.as_ref())
454            .map(|mask| mask.into_iter().step_by(2).all(|x| x))
455            .unwrap_or(false)
456    }
457}