Skip to main content

formualizer_eval/arrow_store/
mod.rs

1use arrow_array::Array;
2use arrow_array::new_null_array;
3use arrow_schema::DataType;
4use std::sync::Arc;
5
6use arrow_array::builder::{BooleanBuilder, Float64Builder, StringBuilder, UInt8Builder};
7use arrow_array::{ArrayRef, BooleanArray, Float64Array, StringArray, UInt8Array, UInt32Array};
8use once_cell::sync::OnceCell;
9
10use crate::format::FormatId;
11use formualizer_common::{ExcelError, ExcelErrorKind, LiteralValue};
12use rustc_hash::FxHashMap;
13use std::collections::BTreeMap;
14
15/// Compact type tag per row (UInt8 backing)
16#[repr(u8)]
17#[derive(Copy, Clone, Debug, Eq, PartialEq)]
18pub enum TypeTag {
19    Empty = 0,
20    Number = 1,
21    Boolean = 2,
22    Text = 3,
23    Error = 4,
24    DateTime = 5, // reserved for future temporal lanes
25    Duration = 6, // reserved
26    Pending = 7,
27}
28
29impl TypeTag {
30    fn from_value(v: &LiteralValue) -> Self {
31        match v {
32            LiteralValue::Empty => TypeTag::Empty,
33            LiteralValue::Int(_) | LiteralValue::Number(_) => TypeTag::Number,
34            LiteralValue::Boolean(_) => TypeTag::Boolean,
35            LiteralValue::Text(_) => TypeTag::Text,
36            LiteralValue::Error(_) => TypeTag::Error,
37            LiteralValue::Date(_) | LiteralValue::DateTime(_) | LiteralValue::Time(_) => {
38                TypeTag::DateTime
39            }
40            LiteralValue::Duration(_) => TypeTag::Duration,
41            LiteralValue::Pending => TypeTag::Pending,
42            LiteralValue::Array(_) => TypeTag::Error, // arrays not storable in a single cell lane
43        }
44    }
45}
46
47impl TypeTag {
48    #[inline]
49    pub fn from_u8(b: u8) -> Self {
50        match b {
51            x if x == TypeTag::Empty as u8 => TypeTag::Empty,
52            x if x == TypeTag::Number as u8 => TypeTag::Number,
53            x if x == TypeTag::Boolean as u8 => TypeTag::Boolean,
54            x if x == TypeTag::Text as u8 => TypeTag::Text,
55            x if x == TypeTag::Error as u8 => TypeTag::Error,
56            x if x == TypeTag::DateTime as u8 => TypeTag::DateTime,
57            x if x == TypeTag::Duration as u8 => TypeTag::Duration,
58            x if x == TypeTag::Pending as u8 => TypeTag::Pending,
59            _ => TypeTag::Empty,
60        }
61    }
62}
63
64#[derive(Debug, Clone, Copy, Default)]
65pub struct ColumnChunkMeta {
66    pub len: usize,
67    pub non_null_num: usize,
68    pub non_null_bool: usize,
69    pub non_null_text: usize,
70    pub non_null_err: usize,
71}
72
73/// Run-end encoded per-cell format ids. Run ends are exclusive logical offsets.
74#[derive(Debug, Clone, PartialEq, Eq)]
75pub struct FormatRuns {
76    run_ends: Vec<u32>,
77    format_ids: Vec<u16>,
78}
79
80impl FormatRuns {
81    pub fn from_ids(ids: &[u16]) -> Option<Self> {
82        if ids.iter().all(|id| *id == FormatId::GENERAL.0) {
83            return None;
84        }
85        let mut run_ends = Vec::new();
86        let mut format_ids = Vec::new();
87        for (idx, id) in ids.iter().copied().enumerate() {
88            if format_ids.last().copied() != Some(id) {
89                format_ids.push(id);
90                if idx > 0 {
91                    run_ends.push(idx as u32);
92                }
93            }
94        }
95        run_ends.push(ids.len() as u32);
96        Some(Self {
97            run_ends,
98            format_ids,
99        })
100    }
101
102    #[inline]
103    pub fn get(&self, offset: usize) -> FormatId {
104        let run = self
105            .run_ends
106            .partition_point(|end| (*end as usize) <= offset);
107        self.format_ids
108            .get(run)
109            .copied()
110            .map(FormatId)
111            .unwrap_or_default()
112    }
113
114    pub fn to_ids(&self, len: usize) -> Vec<u16> {
115        (0..len).map(|offset| self.get(offset).0).collect()
116    }
117
118    /// Whether every offset in `[offset, offset + len)` is GENERAL.
119    pub(crate) fn all_general_in(&self, offset: usize, len: usize) -> bool {
120        if len == 0 {
121            return true;
122        }
123        let first = self
124            .run_ends
125            .partition_point(|end| (*end as usize) <= offset);
126        let last_off = offset + len - 1;
127        let mut run = first;
128        loop {
129            match self.format_ids.get(run) {
130                // Past the last run: the default (GENERAL).
131                None => return true,
132                Some(&id) if id != FormatId::GENERAL.0 => return false,
133                Some(_) => {}
134            }
135            match self.run_ends.get(run) {
136                Some(&end) if (end as usize) <= last_off => run += 1,
137                _ => return true,
138            }
139        }
140    }
141
142    pub fn slice(&self, offset: usize, len: usize) -> Option<Self> {
143        let ids: Vec<_> = (offset..offset.saturating_add(len))
144            .map(|i| self.get(i).0)
145            .collect();
146        Self::from_ids(&ids)
147    }
148}
149
150/// One cached merged lane: valid while the base lane (held, so its
151/// address cannot be reused) and both overlay epochs are unchanged.
152struct MergedLane<T> {
153    base: Arc<T>,
154    key: (u64, u64),
155    merged: Option<Arc<T>>,
156    /// Rows requested (and merged per call) since this key was seen.
157    pending: usize,
158}
159
160#[derive(Default)]
161struct MergedInner {
162    numbers: Option<MergedLane<Float64Array>>,
163    errors: Option<MergedLane<UInt8Array>>,
164}
165
166/// Per-chunk merged-lane cache (interior mutability: range reads are `&self`
167/// and may run on the evaluation pool). Cloning a chunk starts empty.
168#[derive(Default)]
169struct MergedLaneCache {
170    /// Boxed on first use: most chunks are never range-read repeatedly.
171    inner: std::sync::Mutex<Option<Box<MergedInner>>>,
172}
173
174impl Clone for MergedLaneCache {
175    fn clone(&self) -> Self {
176        Self::default()
177    }
178}
179
180impl std::fmt::Debug for MergedLaneCache {
181    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
182        f.write_str("MergedLaneCache")
183    }
184}
185
186impl MergedLaneCache {
187    fn lookup<T>(
188        slot: &mut Option<MergedLane<T>>,
189        base: &Arc<T>,
190        key: (u64, u64),
191        request: usize,
192        chunk_len: usize,
193        build: impl FnOnce() -> Arc<T>,
194    ) -> Option<Arc<T>> {
195        let lane = match slot {
196            Some(lane) if Arc::ptr_eq(&lane.base, base) && lane.key == key => lane,
197            _ => slot.insert(MergedLane {
198                base: base.clone(),
199                key,
200                merged: None,
201                pending: 0,
202            }),
203        };
204        if let Some(merged) = &lane.merged {
205            return Some(merged.clone());
206        }
207        lane.pending = lane.pending.saturating_add(request);
208        // Build only once the rows requested since the last change exceed
209        // two chunks: whole-chunk reads repeated once per change (a formula
210        // re-reading its column after each edit) never pay a full merge
211        // they cannot reuse.
212        if lane.pending <= 2 * chunk_len {
213            return None;
214        }
215        let merged = build();
216        lane.merged = Some(merged.clone());
217        Some(merged)
218    }
219}
220
221#[derive(Debug, Clone)]
222pub struct ColumnChunk {
223    pub numbers: Option<Arc<Float64Array>>,
224    pub booleans: Option<Arc<BooleanArray>>,
225    pub text: Option<ArrayRef>,          // Utf8 for Phase A
226    pub errors: Option<Arc<UInt8Array>>, // compact error code (UInt8)
227    pub type_tag: Arc<UInt8Array>,
228    pub formula_id: Option<Arc<UInt32Array>>, // reserved for Phase A+
229    /// Optional two-vec run-end format lane; absent means General throughout.
230    pub format: Option<FormatRuns>,
231    pub meta: ColumnChunkMeta,
232    // Lazy null providers (per-chunk)
233    lazy_null_numbers: OnceCell<Arc<Float64Array>>,
234    lazy_null_booleans: OnceCell<Arc<BooleanArray>>,
235    lazy_null_text: OnceCell<ArrayRef>,
236    lazy_null_errors: OnceCell<Arc<UInt8Array>>,
237    // Cache: lowered text lane, nulls preserved
238    lowered_text: OnceCell<ArrayRef>,
239    // Whole-chunk merged (base + overlays) lanes, reused across range reads
240    // until either overlay or the base changes (decision 20.3).
241    merged: MergedLaneCache,
242    // Phase C: per-chunk overlay (delta edits since last compaction)
243    pub overlay: Overlay,
244    // Phase 0/1: separate computed overlay (formula/spill outputs)
245    pub computed_overlay: Overlay,
246}
247
248impl ColumnChunk {
249    #[inline]
250    pub fn len(&self) -> usize {
251        self.type_tag.len()
252    }
253
254    /// `range` of the numeric lane with both overlays applied, served from
255    /// the chunk's merged-lane cache. `None` when the cache declines (the
256    /// caller then merges per call): a whole-chunk merge is built only once
257    /// requests since the last change have covered more than two chunks'
258    /// worth of rows, so the cache never costs more than the per-call merges
259    /// it replaces plus one, and reads repeated fewer than three times per
260    /// change retain nothing.
261    pub(crate) fn merged_numbers(
262        &self,
263        range: core::ops::Range<usize>,
264    ) -> Option<Arc<Float64Array>> {
265        let base = self.numbers_or_null();
266        let key = (self.overlay.epoch, self.computed_overlay.epoch);
267        let len = self.len();
268        let mut guard = self.merged.inner.lock().ok()?;
269        let inner = guard.get_or_insert_with(Default::default);
270        let entry =
271            MergedLaneCache::lookup(&mut inner.numbers, &base, key, range.len(), len, || {
272                OverlayCascade::new(&self.overlay, &self.computed_overlay)
273                    .select_numbers(0..len, &base)
274            })?;
275        Some(Arc::new(entry.slice(range.start, range.len())))
276    }
277
278    /// [`Self::merged_numbers`] for the error-code lane.
279    pub(crate) fn merged_errors(&self, range: core::ops::Range<usize>) -> Option<Arc<UInt8Array>> {
280        let base = self.errors_or_null();
281        let key = (self.overlay.epoch, self.computed_overlay.epoch);
282        let len = self.len();
283        let mut guard = self.merged.inner.lock().ok()?;
284        let inner = guard.get_or_insert_with(Default::default);
285        let entry =
286            MergedLaneCache::lookup(&mut inner.errors, &base, key, range.len(), len, || {
287                OverlayCascade::new(&self.overlay, &self.computed_overlay)
288                    .select_errors(0..len, &base)
289            })?;
290        Some(Arc::new(entry.slice(range.start, range.len())))
291    }
292    #[inline]
293    pub fn is_empty(&self) -> bool {
294        self.len() == 0
295    }
296    #[inline]
297    pub fn numbers_or_null(&self) -> Arc<Float64Array> {
298        #[cfg(test)]
299        crate::engine::range_view::range_work::record(|w| w.provider_requests[0] += 1);
300        if let Some(a) = &self.numbers {
301            return a.clone();
302        }
303        self.lazy_null_numbers
304            .get_or_init(|| {
305                #[cfg(test)]
306                crate::engine::range_view::range_work::record(|w| {
307                    w.provider_builds[0] += 1;
308                    w.provider_slots[0] += self.len();
309                });
310                let arr = new_null_array(&DataType::Float64, self.len());
311                Arc::new(arr.as_any().downcast_ref::<Float64Array>().unwrap().clone())
312            })
313            .clone()
314    }
315    #[inline]
316    pub fn booleans_or_null(&self) -> Arc<BooleanArray> {
317        #[cfg(test)]
318        crate::engine::range_view::range_work::record(|w| w.provider_requests[1] += 1);
319        if let Some(a) = &self.booleans {
320            return a.clone();
321        }
322        self.lazy_null_booleans
323            .get_or_init(|| {
324                #[cfg(test)]
325                crate::engine::range_view::range_work::record(|w| {
326                    w.provider_builds[1] += 1;
327                    w.provider_slots[1] += self.len();
328                });
329                let arr = new_null_array(&DataType::Boolean, self.len());
330                Arc::new(arr.as_any().downcast_ref::<BooleanArray>().unwrap().clone())
331            })
332            .clone()
333    }
334    #[inline]
335    pub fn errors_or_null(&self) -> Arc<UInt8Array> {
336        #[cfg(test)]
337        crate::engine::range_view::range_work::record(|w| w.provider_requests[2] += 1);
338        if let Some(a) = &self.errors {
339            return a.clone();
340        }
341        self.lazy_null_errors
342            .get_or_init(|| {
343                #[cfg(test)]
344                crate::engine::range_view::range_work::record(|w| {
345                    w.provider_builds[2] += 1;
346                    w.provider_slots[2] += self.len();
347                });
348                let arr = new_null_array(&DataType::UInt8, self.len());
349                Arc::new(arr.as_any().downcast_ref::<UInt8Array>().unwrap().clone())
350            })
351            .clone()
352    }
353    #[inline]
354    pub fn text_or_null(&self) -> ArrayRef {
355        #[cfg(test)]
356        crate::engine::range_view::range_work::record(|w| w.provider_requests[3] += 1);
357        if let Some(a) = &self.text {
358            return a.clone();
359        }
360        self.lazy_null_text
361            .get_or_init(|| {
362                #[cfg(test)]
363                crate::engine::range_view::range_work::record(|w| {
364                    w.provider_builds[3] += 1;
365                    w.provider_slots[3] += self.len();
366                });
367                new_null_array(&DataType::Utf8, self.len())
368            })
369            .clone()
370    }
371
372    /// Lowercased text lane, with nulls preserved. Cached per chunk.
373    pub fn text_lower_or_null(&self) -> ArrayRef {
374        if let Some(a) = self.lowered_text.get() {
375            return a.clone();
376        }
377        // Lowercase when text present; else return null Utf8
378        let out: ArrayRef = if let Some(txt) = &self.text {
379            let sa = txt.as_any().downcast_ref::<StringArray>().unwrap();
380            let mut b = arrow_array::builder::StringBuilder::with_capacity(sa.len(), sa.len() * 8);
381            for i in 0..sa.len() {
382                if sa.is_null(i) {
383                    b.append_null();
384                } else {
385                    b.append_value(sa.value(i).to_lowercase());
386                }
387            }
388            let lowered = b.finish();
389            Arc::new(lowered)
390        } else {
391            new_null_array(&DataType::Utf8, self.len())
392        };
393        self.lowered_text.get_or_init(|| out.clone());
394        out
395    }
396
397    /// Grow this chunk's logical length to `new_len` (padding with empty/null values).
398    ///
399    /// This is used to keep already-materialized chunks consistent when `ArrowSheet::nrows`
400    /// grows incrementally inside the current last chunk.
401    pub fn grow_len_to(&mut self, new_len: usize) {
402        let old_len = self.len();
403        if new_len <= old_len {
404            return;
405        }
406
407        // Grow type tags (pad with Empty).
408        let mut tags: Vec<u8> = self.type_tag.values().to_vec();
409        tags.resize(new_len, TypeTag::Empty as u8);
410        self.type_tag = Arc::new(UInt8Array::from(tags));
411
412        // Grow lanes when present; append nulls for new rows.
413        if let Some(a) = &self.numbers {
414            use arrow_array::builder::Float64Builder;
415            let mut b = Float64Builder::with_capacity(new_len);
416            for i in 0..old_len {
417                if a.is_null(i) {
418                    b.append_null();
419                } else {
420                    b.append_value(a.value(i));
421                }
422            }
423            for _ in old_len..new_len {
424                b.append_null();
425            }
426            self.numbers = Some(Arc::new(b.finish()));
427        }
428        if let Some(a) = &self.booleans {
429            use arrow_array::builder::BooleanBuilder;
430            let mut b = BooleanBuilder::with_capacity(new_len);
431            for i in 0..old_len {
432                if a.is_null(i) {
433                    b.append_null();
434                } else {
435                    b.append_value(a.value(i));
436                }
437            }
438            for _ in old_len..new_len {
439                b.append_null();
440            }
441            self.booleans = Some(Arc::new(b.finish()));
442        }
443        if let Some(a) = &self.errors {
444            use arrow_array::builder::UInt8Builder;
445            let mut b = UInt8Builder::with_capacity(new_len);
446            for i in 0..old_len {
447                if a.is_null(i) {
448                    b.append_null();
449                } else {
450                    b.append_value(a.value(i));
451                }
452            }
453            for _ in old_len..new_len {
454                b.append_null();
455            }
456            self.errors = Some(Arc::new(b.finish()));
457        }
458        if let Some(a) = &self.text {
459            use arrow_array::builder::StringBuilder;
460            let sa = a.as_any().downcast_ref::<StringArray>().unwrap();
461            let mut b = StringBuilder::with_capacity(new_len, 0);
462            for i in 0..old_len {
463                if sa.is_null(i) {
464                    b.append_null();
465                } else {
466                    b.append_value(sa.value(i));
467                }
468            }
469            for _ in old_len..new_len {
470                b.append_null();
471            }
472            self.text = Some(Arc::new(b.finish()) as ArrayRef);
473        }
474
475        if let Some(format) = &self.format {
476            let mut ids = format.to_ids(old_len);
477            ids.resize(new_len, FormatId::GENERAL.0);
478            self.format = FormatRuns::from_ids(&ids);
479        }
480
481        // Length-dependent caches must be dropped.
482        self.lazy_null_numbers = OnceCell::new();
483        self.lazy_null_booleans = OnceCell::new();
484        self.lazy_null_text = OnceCell::new();
485        self.lazy_null_errors = OnceCell::new();
486        self.lowered_text = OnceCell::new();
487
488        self.meta.len = new_len;
489    }
490}
491
492#[derive(Debug, Clone)]
493pub struct ArrowColumn {
494    pub chunks: Vec<ColumnChunk>,
495    pub sparse_chunks: FxHashMap<usize, ColumnChunk>,
496    pub index: u32,
497}
498
499impl ArrowColumn {
500    #[inline]
501    pub fn chunk(&self, idx: usize) -> Option<&ColumnChunk> {
502        if idx < self.chunks.len() {
503            Some(&self.chunks[idx])
504        } else {
505            self.sparse_chunks.get(&idx)
506        }
507    }
508
509    #[inline]
510    pub fn chunk_mut(&mut self, idx: usize) -> Option<&mut ColumnChunk> {
511        if idx < self.chunks.len() {
512            Some(&mut self.chunks[idx])
513        } else {
514            self.sparse_chunks.get_mut(&idx)
515        }
516    }
517
518    #[inline]
519    pub fn has_sparse_chunks(&self) -> bool {
520        !self.sparse_chunks.is_empty()
521    }
522
523    #[inline]
524    pub fn total_chunk_count(&self) -> usize {
525        self.chunks.len() + self.sparse_chunks.len()
526    }
527}
528
529#[derive(Debug, Clone)]
530pub struct ArrowSheet {
531    pub name: Arc<str>,
532    /// Serial encoding used by every temporal lane in this sheet.
533    ///
534    /// Changing this value requires re-encoding all temporal cells; engine and
535    /// workbook constructors therefore set it once when the sheet is created.
536    pub date_system: crate::engine::DateSystem,
537    pub columns: Vec<ArrowColumn>,
538    pub nrows: u32,
539    pub chunk_starts: Vec<usize>,
540    /// Preferred chunk size (rows) for capacity growth operations.
541    ///
542    /// For Arrow-ingested sheets this matches the ingest `chunk_rows`. For sparse/overlay-created
543    /// sheets this defaults to 32k to avoid creating thousands of tiny chunks during growth.
544    pub chunk_rows: usize,
545}
546
547#[derive(Debug, Default, Clone)]
548pub struct SheetStore {
549    pub sheets: Vec<ArrowSheet>,
550}
551
552impl SheetStore {
553    pub fn sheet(&self, name: &str) -> Option<&ArrowSheet> {
554        self.sheets.iter().find(|s| s.name.as_ref() == name)
555    }
556    pub fn sheet_mut(&mut self, name: &str) -> Option<&mut ArrowSheet> {
557        self.sheets.iter_mut().find(|s| s.name.as_ref() == name)
558    }
559}
560
561/// Ingestion builder that writes per-column Arrow arrays with a lane/tag design.
562pub struct IngestBuilder {
563    name: Arc<str>,
564    ncols: usize,
565    chunk_rows: usize,
566    date_system: crate::engine::DateSystem,
567
568    // Per-column active builders for current chunk
569    num_builders: Vec<Float64Builder>,
570    bool_builders: Vec<BooleanBuilder>,
571    text_builders: Vec<StringBuilder>,
572    err_builders: Vec<UInt8Builder>,
573    tag_builders: Vec<UInt8Builder>,
574    format_builders: Vec<Vec<u16>>,
575
576    // Per-column per-lane non-null counters for current chunk
577    lane_counts: Vec<LaneCounts>,
578    // Text payload bytes the previous chunk used per column; sizes the next reserve.
579    next_text_bytes: Vec<usize>,
580    // Builders were consumed by a chunk flush and must be re-created before the next row.
581    needs_provision: bool,
582
583    // Accumulated chunks
584    chunks: Vec<Vec<ColumnChunk>>, // indexed by col
585    row_in_chunk: usize,
586    total_rows: u32,
587}
588
589#[derive(Debug, Clone, Copy, Default)]
590struct LaneCounts {
591    n_num: usize,
592    n_bool: usize,
593    n_text: usize,
594    n_err: usize,
595}
596
597/// Initial lane capacity (rows) of a sheet's first ingest chunk.
598const FIRST_CHUNK_ROWS: usize = 1024;
599
600impl IngestBuilder {
601    pub fn new(
602        sheet_name: &str,
603        ncols: usize,
604        chunk_rows: usize,
605        date_system: crate::engine::DateSystem,
606    ) -> Self {
607        let mut chunks = Vec::with_capacity(ncols);
608        chunks.resize_with(ncols, Vec::new);
609        // The first chunk's lanes start small and grow: a sheet with fewer
610        // rows than a chunk (most sheets) never reserves chunk-row lanes
611        // for every column, which dominated the load peak of wide sheets.
612        // Later chunks follow a full one, so they reserve a whole chunk.
613        let cap = chunk_rows.clamp(1, FIRST_CHUNK_ROWS);
614        Self {
615            name: Arc::from(sheet_name.to_string()),
616            ncols,
617            chunk_rows: chunk_rows.max(1),
618            date_system,
619            num_builders: (0..ncols)
620                .map(|_| Float64Builder::with_capacity(cap))
621                .collect(),
622            bool_builders: (0..ncols)
623                .map(|_| BooleanBuilder::with_capacity(cap))
624                .collect(),
625            // Text payload bytes are reserved lazily: most lanes never see text,
626            // and a text lane grows its value buffer on first use.
627            text_builders: (0..ncols)
628                .map(|_| StringBuilder::with_capacity(cap, 0))
629                .collect(),
630            err_builders: (0..ncols)
631                .map(|_| UInt8Builder::with_capacity(cap))
632                .collect(),
633            tag_builders: (0..ncols)
634                .map(|_| UInt8Builder::with_capacity(cap))
635                .collect(),
636            format_builders: (0..ncols).map(|_| Vec::with_capacity(cap)).collect(),
637            lane_counts: vec![LaneCounts::default(); ncols],
638            next_text_bytes: vec![0; ncols],
639            needs_provision: false,
640            chunks,
641            row_in_chunk: 0,
642            total_rows: 0,
643        }
644    }
645
646    /// Zero-allocation row append from typed cell tokens (no LiteralValue).
647    /// Text borrows are copied into the internal StringBuilder.
648    pub fn append_row_cells<'a>(&mut self, row: &[CellIngest<'a>]) -> Result<(), ExcelError> {
649        assert_eq!(row.len(), self.ncols, "row width mismatch");
650        self.provision_if_needed();
651        for (c, cell) in row.iter().enumerate() {
652            self.format_builders[c].push(match cell {
653                CellIngest::DateSerial(serial) if serial.fract().abs() > f64::EPSILON => {
654                    FormatId::DATETIME.0
655                }
656                CellIngest::DateSerial(_) => FormatId::DATE.0,
657                CellIngest::FormattedNumber(_, id) => id.0,
658                CellIngest::DurationSerial(_) => FormatId::DURATION.0,
659                _ => FormatId::GENERAL.0,
660            });
661            match cell {
662                CellIngest::Empty => {
663                    self.tag_builders[c].append_value(TypeTag::Empty as u8);
664                    self.num_builders[c].append_null();
665                    self.bool_builders[c].append_null();
666                    self.text_builders[c].append_null();
667                    self.err_builders[c].append_null();
668                }
669                CellIngest::Number(n) => {
670                    self.tag_builders[c].append_value(TypeTag::Number as u8);
671                    self.num_builders[c].append_value(*n);
672                    self.lane_counts[c].n_num += 1;
673                    self.bool_builders[c].append_null();
674                    self.text_builders[c].append_null();
675                    self.err_builders[c].append_null();
676                }
677                CellIngest::Boolean(b) => {
678                    self.tag_builders[c].append_value(TypeTag::Boolean as u8);
679                    self.num_builders[c].append_null();
680                    self.bool_builders[c].append_value(*b);
681                    self.lane_counts[c].n_bool += 1;
682                    self.text_builders[c].append_null();
683                    self.err_builders[c].append_null();
684                }
685                CellIngest::Text(s) => {
686                    self.tag_builders[c].append_value(TypeTag::Text as u8);
687                    self.num_builders[c].append_null();
688                    self.bool_builders[c].append_null();
689                    self.text_builders[c].append_value(s);
690                    self.lane_counts[c].n_text += 1;
691                    self.err_builders[c].append_null();
692                }
693                CellIngest::ErrorCode(code) => {
694                    self.tag_builders[c].append_value(TypeTag::Error as u8);
695                    self.num_builders[c].append_null();
696                    self.bool_builders[c].append_null();
697                    self.text_builders[c].append_null();
698                    self.err_builders[c].append_value(*code);
699                    self.lane_counts[c].n_err += 1;
700                }
701                CellIngest::DateSerial(serial) | CellIngest::FormattedNumber(serial, _) => {
702                    self.tag_builders[c].append_value(TypeTag::Number as u8);
703                    self.num_builders[c].append_value(*serial);
704                    self.lane_counts[c].n_num += 1;
705                    self.bool_builders[c].append_null();
706                    self.text_builders[c].append_null();
707                    self.err_builders[c].append_null();
708                }
709                CellIngest::DurationSerial(serial) => {
710                    self.tag_builders[c].append_value(TypeTag::Number as u8);
711                    self.num_builders[c].append_value(*serial);
712                    self.lane_counts[c].n_num += 1;
713                    self.bool_builders[c].append_null();
714                    self.text_builders[c].append_null();
715                    self.err_builders[c].append_null();
716                }
717                CellIngest::Pending => {
718                    self.tag_builders[c].append_value(TypeTag::Pending as u8);
719                    self.num_builders[c].append_null();
720                    self.bool_builders[c].append_null();
721                    self.text_builders[c].append_null();
722                    self.err_builders[c].append_null();
723                }
724            }
725        }
726        self.row_in_chunk += 1;
727        self.total_rows += 1;
728        if self.row_in_chunk >= self.chunk_rows {
729            self.finish_chunk();
730        }
731        Ok(())
732    }
733
734    /// Streaming row append from an iterator of typed cell tokens.
735    /// Requires an `ExactSizeIterator` to validate row width without materializing a Vec.
736    pub fn append_row_cells_iter<'a, I>(&mut self, iter: I) -> Result<(), ExcelError>
737    where
738        I: ExactSizeIterator<Item = CellIngest<'a>>,
739    {
740        assert_eq!(iter.len(), self.ncols, "row width mismatch");
741        self.provision_if_needed();
742        for (c, cell) in iter.enumerate() {
743            self.format_builders[c].push(match cell {
744                CellIngest::DateSerial(serial) if serial.fract().abs() > f64::EPSILON => {
745                    FormatId::DATETIME.0
746                }
747                CellIngest::DateSerial(_) => FormatId::DATE.0,
748                CellIngest::FormattedNumber(_, id) => id.0,
749                CellIngest::DurationSerial(_) => FormatId::DURATION.0,
750                _ => FormatId::GENERAL.0,
751            });
752            match cell {
753                CellIngest::Empty => {
754                    self.tag_builders[c].append_value(TypeTag::Empty as u8);
755                    self.num_builders[c].append_null();
756                    self.bool_builders[c].append_null();
757                    self.text_builders[c].append_null();
758                    self.err_builders[c].append_null();
759                }
760                CellIngest::Number(n) => {
761                    self.tag_builders[c].append_value(TypeTag::Number as u8);
762                    self.num_builders[c].append_value(n);
763                    self.lane_counts[c].n_num += 1;
764                    self.bool_builders[c].append_null();
765                    self.text_builders[c].append_null();
766                    self.err_builders[c].append_null();
767                }
768                CellIngest::Boolean(b) => {
769                    self.tag_builders[c].append_value(TypeTag::Boolean as u8);
770                    self.num_builders[c].append_null();
771                    self.bool_builders[c].append_value(b);
772                    self.lane_counts[c].n_bool += 1;
773                    self.text_builders[c].append_null();
774                    self.err_builders[c].append_null();
775                }
776                CellIngest::Text(s) => {
777                    self.tag_builders[c].append_value(TypeTag::Text as u8);
778                    self.num_builders[c].append_null();
779                    self.bool_builders[c].append_null();
780                    self.text_builders[c].append_value(s);
781                    self.lane_counts[c].n_text += 1;
782                    self.err_builders[c].append_null();
783                }
784                CellIngest::ErrorCode(code) => {
785                    self.tag_builders[c].append_value(TypeTag::Error as u8);
786                    self.num_builders[c].append_null();
787                    self.bool_builders[c].append_null();
788                    self.text_builders[c].append_null();
789                    self.err_builders[c].append_value(code);
790                    self.lane_counts[c].n_err += 1;
791                }
792                CellIngest::DateSerial(serial) | CellIngest::FormattedNumber(serial, _) => {
793                    self.tag_builders[c].append_value(TypeTag::Number as u8);
794                    self.num_builders[c].append_value(serial);
795                    self.lane_counts[c].n_num += 1;
796                    self.bool_builders[c].append_null();
797                    self.text_builders[c].append_null();
798                    self.err_builders[c].append_null();
799                }
800                CellIngest::DurationSerial(serial) => {
801                    self.tag_builders[c].append_value(TypeTag::Number as u8);
802                    self.num_builders[c].append_value(serial);
803                    self.lane_counts[c].n_num += 1;
804                    self.bool_builders[c].append_null();
805                    self.text_builders[c].append_null();
806                    self.err_builders[c].append_null();
807                }
808                CellIngest::Pending => {
809                    self.tag_builders[c].append_value(TypeTag::Pending as u8);
810                    self.num_builders[c].append_null();
811                    self.bool_builders[c].append_null();
812                    self.text_builders[c].append_null();
813                    self.err_builders[c].append_null();
814                }
815            }
816        }
817        self.row_in_chunk += 1;
818        self.total_rows += 1;
819        if self.row_in_chunk >= self.chunk_rows {
820            self.finish_chunk();
821        }
822        Ok(())
823    }
824
825    /// Append a single row of values. Length must match `ncols`.
826    pub fn append_row(&mut self, row: &[LiteralValue]) -> Result<(), ExcelError> {
827        assert_eq!(row.len(), self.ncols, "row width mismatch");
828        self.provision_if_needed();
829
830        for (c, v) in row.iter().enumerate() {
831            self.format_builders[c].push(match v {
832                LiteralValue::Date(_) => FormatId::DATE.0,
833                LiteralValue::DateTime(_) => FormatId::DATETIME.0,
834                LiteralValue::Time(_) => FormatId::TIME.0,
835                LiteralValue::Duration(_) => FormatId::DURATION.0,
836                _ => FormatId::GENERAL.0,
837            });
838            let tag = match v {
839                LiteralValue::Date(_)
840                | LiteralValue::DateTime(_)
841                | LiteralValue::Time(_)
842                | LiteralValue::Duration(_) => TypeTag::Number,
843                _ => TypeTag::from_value(v),
844            } as u8;
845            self.tag_builders[c].append_value(tag);
846
847            match v {
848                LiteralValue::Empty => {
849                    self.num_builders[c].append_null();
850                    self.bool_builders[c].append_null();
851                    self.text_builders[c].append_null();
852                    self.err_builders[c].append_null();
853                }
854                LiteralValue::Int(i) => {
855                    self.num_builders[c].append_value(*i as f64);
856                    self.lane_counts[c].n_num += 1;
857                    self.bool_builders[c].append_null();
858                    self.text_builders[c].append_null();
859                    self.err_builders[c].append_null();
860                }
861                LiteralValue::Number(n) => {
862                    self.num_builders[c].append_value(*n);
863                    self.lane_counts[c].n_num += 1;
864                    self.bool_builders[c].append_null();
865                    self.text_builders[c].append_null();
866                    self.err_builders[c].append_null();
867                }
868                LiteralValue::Boolean(b) => {
869                    self.num_builders[c].append_null();
870                    self.bool_builders[c].append_value(*b);
871                    self.lane_counts[c].n_bool += 1;
872                    self.text_builders[c].append_null();
873                    self.err_builders[c].append_null();
874                }
875                LiteralValue::Text(s) => {
876                    self.num_builders[c].append_null();
877                    self.bool_builders[c].append_null();
878                    self.text_builders[c].append_value(s);
879                    self.lane_counts[c].n_text += 1;
880                    self.err_builders[c].append_null();
881                }
882                LiteralValue::Error(e) => {
883                    self.num_builders[c].append_null();
884                    self.bool_builders[c].append_null();
885                    self.text_builders[c].append_null();
886                    self.err_builders[c].append_value(map_error_code(e.kind));
887                    self.lane_counts[c].n_err += 1;
888                }
889                // Phase A: coerce temporal to serials in numeric lane with DateTime tag
890                LiteralValue::Date(d) => {
891                    let dt = d.and_hms_opt(0, 0, 0).unwrap();
892                    let serial = formualizer_common::datetime_to_serial_for(self.date_system, &dt);
893                    self.num_builders[c].append_value(serial);
894                    self.lane_counts[c].n_num += 1;
895                    self.bool_builders[c].append_null();
896                    self.text_builders[c].append_null();
897                    self.err_builders[c].append_null();
898                }
899                LiteralValue::DateTime(dt) => {
900                    let serial = formualizer_common::datetime_to_serial_for(self.date_system, dt);
901                    self.num_builders[c].append_value(serial);
902                    self.lane_counts[c].n_num += 1;
903                    self.bool_builders[c].append_null();
904                    self.text_builders[c].append_null();
905                    self.err_builders[c].append_null();
906                }
907                LiteralValue::Time(t) => {
908                    let serial = formualizer_common::time_to_fraction(t);
909                    self.num_builders[c].append_value(serial);
910                    self.lane_counts[c].n_num += 1;
911                    self.bool_builders[c].append_null();
912                    self.text_builders[c].append_null();
913                    self.err_builders[c].append_null();
914                }
915                LiteralValue::Duration(dur) => {
916                    let serial = dur.num_seconds() as f64 / 86_400.0;
917                    self.num_builders[c].append_value(serial);
918                    self.lane_counts[c].n_num += 1;
919                    self.bool_builders[c].append_null();
920                    self.text_builders[c].append_null();
921                    self.err_builders[c].append_null();
922                }
923                LiteralValue::Array(_) => {
924                    // Not allowed as a stored scalar; mark as error kind VALUE
925                    self.num_builders[c].append_null();
926                    self.bool_builders[c].append_null();
927                    self.text_builders[c].append_null();
928                    self.err_builders[c].append_value(map_error_code(ExcelErrorKind::Value));
929                    self.lane_counts[c].n_err += 1;
930                }
931                LiteralValue::Pending => {
932                    // Pending: tag only; all lanes remain null (no error)
933                    self.num_builders[c].append_null();
934                    self.bool_builders[c].append_null();
935                    self.text_builders[c].append_null();
936                    self.err_builders[c].append_null();
937                }
938            }
939        }
940
941        self.row_in_chunk += 1;
942        self.total_rows += 1;
943
944        if self.row_in_chunk >= self.chunk_rows {
945            self.finish_chunk();
946        }
947
948        Ok(())
949    }
950
951    /// Re-create full-capacity lane builders after a chunk flush. Deferred to
952    /// the next appended row so a terminal flush (partial or exactly at a chunk
953    /// boundary) never allocates builders that [`Self::finish`] would drop.
954    #[inline]
955    fn provision_if_needed(&mut self) {
956        if !self.needs_provision {
957            return;
958        }
959        self.needs_provision = false;
960        for c in 0..self.ncols {
961            self.num_builders[c] = Float64Builder::with_capacity(self.chunk_rows);
962            self.bool_builders[c] = BooleanBuilder::with_capacity(self.chunk_rows);
963            // The text payload reserve follows what this column's previous chunk used.
964            self.text_builders[c] =
965                StringBuilder::with_capacity(self.chunk_rows, self.next_text_bytes[c]);
966            self.err_builders[c] = UInt8Builder::with_capacity(self.chunk_rows);
967            self.tag_builders[c] = UInt8Builder::with_capacity(self.chunk_rows);
968            self.format_builders[c] = Vec::with_capacity(self.chunk_rows);
969        }
970    }
971
972    fn finish_chunk(&mut self) {
973        if self.row_in_chunk == 0 {
974            return;
975        }
976        // A short chunk (a small sheet, or a sheet's last chunk) copies its
977        // lanes into exact-size buffers: `finish` keeps the builder's
978        // chunk-row capacity, which made every small sheet cost the same as
979        // a full chunk per column (8 bytes x 32k rows per numeric lane).
980        let exact = self.row_in_chunk.saturating_mul(2) <= self.chunk_rows;
981        for c in 0..self.ncols {
982            let len = self.row_in_chunk;
983            let numbers_arc: Option<Arc<Float64Array>> = if self.lane_counts[c].n_num == 0 {
984                None
985            } else if exact {
986                Some(Arc::new(self.num_builders[c].finish_cloned()))
987            } else {
988                Some(Arc::new(self.num_builders[c].finish()))
989            };
990            let booleans_arc: Option<Arc<BooleanArray>> = if self.lane_counts[c].n_bool == 0 {
991                None
992            } else if exact {
993                Some(Arc::new(self.bool_builders[c].finish_cloned()))
994            } else {
995                Some(Arc::new(self.bool_builders[c].finish()))
996            };
997            let mut text_bytes = 0usize;
998            let text_ref: Option<ArrayRef> = if self.lane_counts[c].n_text == 0 {
999                None
1000            } else {
1001                let text = if exact {
1002                    self.text_builders[c].finish_cloned()
1003                } else {
1004                    self.text_builders[c].finish()
1005                };
1006                text_bytes = text.values().len();
1007                Some(Arc::new(text))
1008            };
1009            let errors_arc: Option<Arc<UInt8Array>> = if self.lane_counts[c].n_err == 0 {
1010                None
1011            } else if exact {
1012                Some(Arc::new(self.err_builders[c].finish_cloned()))
1013            } else {
1014                Some(Arc::new(self.err_builders[c].finish()))
1015            };
1016            let tags: UInt8Array = if exact {
1017                self.tag_builders[c].finish_cloned()
1018            } else {
1019                self.tag_builders[c].finish()
1020            };
1021
1022            let chunk = ColumnChunk {
1023                numbers: numbers_arc,
1024                booleans: booleans_arc,
1025                text: text_ref,
1026                errors: errors_arc,
1027                type_tag: Arc::new(tags),
1028                formula_id: None,
1029                format: FormatRuns::from_ids(&self.format_builders[c]),
1030                meta: ColumnChunkMeta {
1031                    len,
1032                    non_null_num: self.lane_counts[c].n_num,
1033                    non_null_bool: self.lane_counts[c].n_bool,
1034                    non_null_text: self.lane_counts[c].n_text,
1035                    non_null_err: self.lane_counts[c].n_err,
1036                },
1037                lazy_null_numbers: OnceCell::new(),
1038                lazy_null_booleans: OnceCell::new(),
1039                lazy_null_text: OnceCell::new(),
1040                lazy_null_errors: OnceCell::new(),
1041                lowered_text: OnceCell::new(),
1042                merged: MergedLaneCache::default(),
1043                overlay: Overlay::new(),
1044                computed_overlay: Overlay::new(),
1045            };
1046            self.chunks[c].push(chunk);
1047
1048            self.lane_counts[c] = LaneCounts::default();
1049            self.next_text_bytes[c] = text_bytes;
1050        }
1051        self.row_in_chunk = 0;
1052        self.needs_provision = true;
1053    }
1054
1055    pub fn finish(mut self) -> ArrowSheet {
1056        // flush partial chunk; builders for a next chunk are never provisioned
1057        self.finish_chunk();
1058
1059        let mut columns = Vec::with_capacity(self.ncols);
1060        for (idx, chunks) in self.chunks.into_iter().enumerate() {
1061            columns.push(ArrowColumn {
1062                chunks,
1063                sparse_chunks: FxHashMap::default(),
1064                index: idx as u32,
1065            });
1066        }
1067        // Precompute chunk starts from first column and enforce alignment across columns
1068        let mut chunk_starts: Vec<usize> = Vec::new();
1069        if let Some(col0) = columns.first() {
1070            let chunks_len0 = col0.chunks.len();
1071            for (ci, col) in columns.iter().enumerate() {
1072                if col.chunks.len() != chunks_len0 {
1073                    panic!(
1074                        "ArrowSheet chunk misalignment: column {} chunks={} != {}",
1075                        ci,
1076                        col.chunks.len(),
1077                        chunks_len0
1078                    );
1079                }
1080            }
1081            let mut cur = 0usize;
1082            for i in 0..chunks_len0 {
1083                let len_i = col0.chunks[i].type_tag.len();
1084                for (ci, col) in columns.iter().enumerate() {
1085                    let got = col.chunks[i].type_tag.len();
1086                    if got != len_i {
1087                        panic!(
1088                            "ArrowSheet chunk row-length misalignment at chunk {i}: col {ci} len={got} != {len_i}"
1089                        );
1090                    }
1091                }
1092                chunk_starts.push(cur);
1093                cur += len_i;
1094            }
1095        }
1096        ArrowSheet {
1097            name: self.name,
1098            date_system: self.date_system,
1099            columns,
1100            nrows: self.total_rows,
1101            chunk_starts,
1102            chunk_rows: self.chunk_rows,
1103        }
1104    }
1105}
1106
1107pub fn map_error_code(kind: ExcelErrorKind) -> u8 {
1108    match kind {
1109        ExcelErrorKind::Null => 1,
1110        ExcelErrorKind::Ref => 2,
1111        ExcelErrorKind::Name => 3,
1112        ExcelErrorKind::Value => 4,
1113        ExcelErrorKind::Div => 5,
1114        ExcelErrorKind::Na => 6,
1115        ExcelErrorKind::Num => 7,
1116        ExcelErrorKind::Error => 8,
1117        ExcelErrorKind::NImpl => 9,
1118        ExcelErrorKind::Spill => 10,
1119        ExcelErrorKind::Calc => 11,
1120        ExcelErrorKind::Circ => 12,
1121        ExcelErrorKind::Cancelled => 13,
1122        _ => 8,
1123    }
1124}
1125
1126pub fn unmap_error_code(code: u8) -> ExcelErrorKind {
1127    match code {
1128        1 => ExcelErrorKind::Null,
1129        2 => ExcelErrorKind::Ref,
1130        3 => ExcelErrorKind::Name,
1131        4 => ExcelErrorKind::Value,
1132        5 => ExcelErrorKind::Div,
1133        6 => ExcelErrorKind::Na,
1134        7 => ExcelErrorKind::Num,
1135        8 => ExcelErrorKind::Error,
1136        9 => ExcelErrorKind::NImpl,
1137        10 => ExcelErrorKind::Spill,
1138        11 => ExcelErrorKind::Calc,
1139        12 => ExcelErrorKind::Circ,
1140        13 => ExcelErrorKind::Cancelled,
1141        _ => ExcelErrorKind::Error,
1142    }
1143}
1144
1145// ─────────────────────────── Overlay (Phase C) ────────────────────────────
1146
1147/// Zero-allocation cell token for ingestion.
1148pub enum CellIngest<'a> {
1149    Empty,
1150    Number(f64),
1151    Boolean(bool),
1152    Text(&'a str),
1153    ErrorCode(u8),
1154    DateSerial(f64),
1155    DurationSerial(f64),
1156    FormattedNumber(f64, FormatId),
1157    Pending,
1158}
1159
1160#[derive(Debug, Clone, PartialEq)]
1161pub enum OverlayValue {
1162    Empty,
1163    Number(f64),
1164    /// Date/Time/DateTime stored as an Excel serial in the numeric lane.
1165    DateTime(f64),
1166    /// Duration stored as an Excel-style day-fraction in the numeric lane.
1167    Duration(f64),
1168    Boolean(bool),
1169    Text(Arc<str>),
1170    Error(u8),
1171    Pending,
1172}
1173
1174impl OverlayValue {
1175    pub fn from_literal_value(
1176        value: &LiteralValue,
1177        date_system: crate::engine::DateSystem,
1178    ) -> Self {
1179        match value {
1180            LiteralValue::Empty => OverlayValue::Empty,
1181            LiteralValue::Int(i) => OverlayValue::Number(*i as f64),
1182            LiteralValue::Number(n) => OverlayValue::Number(*n),
1183            LiteralValue::Boolean(b) => OverlayValue::Boolean(*b),
1184            LiteralValue::Text(s) => OverlayValue::Text(Arc::from(s.clone())),
1185            LiteralValue::Error(e) => OverlayValue::Error(map_error_code(e.kind)),
1186            LiteralValue::Date(d) => {
1187                let dt = d.and_hms_opt(0, 0, 0).unwrap();
1188                OverlayValue::DateTime(formualizer_common::datetime_to_serial_for(date_system, &dt))
1189            }
1190            LiteralValue::DateTime(dt) => {
1191                OverlayValue::DateTime(formualizer_common::datetime_to_serial_for(date_system, dt))
1192            }
1193            LiteralValue::Time(t) => {
1194                OverlayValue::DateTime(formualizer_common::time_to_fraction(t))
1195            }
1196            LiteralValue::Duration(d) => OverlayValue::Duration(d.num_seconds() as f64 / 86_400.0),
1197            LiteralValue::Pending => OverlayValue::Pending,
1198            LiteralValue::Array(_) => OverlayValue::Error(map_error_code(ExcelErrorKind::Value)),
1199        }
1200    }
1201
1202    #[inline]
1203    pub(crate) fn estimated_payload_bytes(&self) -> usize {
1204        match self {
1205            OverlayValue::Empty | OverlayValue::Pending => 0,
1206            OverlayValue::Number(_) | OverlayValue::DateTime(_) | OverlayValue::Duration(_) => {
1207                core::mem::size_of::<f64>()
1208            }
1209            OverlayValue::Boolean(_) => core::mem::size_of::<bool>(),
1210            OverlayValue::Error(_) => core::mem::size_of::<u8>(),
1211            // Deterministic estimate: count string bytes only.
1212            OverlayValue::Text(s) => s.len(),
1213        }
1214    }
1215
1216    #[inline]
1217    pub(crate) fn type_tag(&self) -> TypeTag {
1218        match self {
1219            OverlayValue::Empty => TypeTag::Empty,
1220            OverlayValue::Number(_) => TypeTag::Number,
1221            OverlayValue::DateTime(_) => TypeTag::DateTime,
1222            OverlayValue::Duration(_) => TypeTag::Duration,
1223            OverlayValue::Boolean(_) => TypeTag::Boolean,
1224            OverlayValue::Text(_) => TypeTag::Text,
1225            OverlayValue::Error(_) => TypeTag::Error,
1226            OverlayValue::Pending => TypeTag::Pending,
1227        }
1228    }
1229
1230    #[inline]
1231    pub(crate) fn numeric_lane_value(&self) -> Option<f64> {
1232        match self {
1233            OverlayValue::Number(n) | OverlayValue::DateTime(n) | OverlayValue::Duration(n) => {
1234                Some(*n)
1235            }
1236            _ => None,
1237        }
1238    }
1239
1240    #[inline]
1241    pub(crate) fn boolean_lane_value(&self) -> Option<bool> {
1242        match self {
1243            OverlayValue::Boolean(b) => Some(*b),
1244            _ => None,
1245        }
1246    }
1247
1248    #[inline]
1249    pub(crate) fn text_lane_value(&self) -> Option<&str> {
1250        match self {
1251            OverlayValue::Text(s) => Some(s.as_ref()),
1252            _ => None,
1253        }
1254    }
1255
1256    #[inline]
1257    pub(crate) fn error_lane_value(&self) -> Option<u8> {
1258        match self {
1259            OverlayValue::Error(code) => Some(*code),
1260            _ => None,
1261        }
1262    }
1263
1264    pub(crate) fn lowered_text_value(&self) -> Option<String> {
1265        match self {
1266            OverlayValue::Text(s) => Some(s.to_lowercase()),
1267            OverlayValue::Number(n) | OverlayValue::DateTime(n) | OverlayValue::Duration(n) => {
1268                Some(n.to_string())
1269            }
1270            OverlayValue::Boolean(b) => Some(if *b { "true" } else { "false" }.to_string()),
1271            OverlayValue::Empty | OverlayValue::Error(_) | OverlayValue::Pending => None,
1272        }
1273    }
1274
1275    pub(crate) fn to_literal_for(&self, date_system: crate::engine::DateSystem) -> LiteralValue {
1276        match self {
1277            OverlayValue::Empty => LiteralValue::Empty,
1278            OverlayValue::Number(n) => LiteralValue::Number(*n),
1279            OverlayValue::DateTime(serial) | OverlayValue::Duration(serial) => {
1280                let _ = date_system;
1281                LiteralValue::Number(*serial)
1282            }
1283            OverlayValue::Boolean(b) => LiteralValue::Boolean(*b),
1284            OverlayValue::Text(s) => LiteralValue::Text((**s).to_string()),
1285            OverlayValue::Error(code) => {
1286                LiteralValue::Error(ExcelError::new(unmap_error_code(*code)))
1287            }
1288            OverlayValue::Pending => LiteralValue::Pending,
1289        }
1290    }
1291
1292    #[cfg(test)]
1293    pub(crate) fn to_literal(&self) -> LiteralValue {
1294        self.to_literal_for(crate::engine::DateSystem::Excel1900)
1295    }
1296}
1297
1298#[derive(Debug, Clone)]
1299pub(crate) enum OverlayScalar<'a> {
1300    Borrowed(&'a OverlayValue),
1301    Owned(OverlayValue),
1302}
1303
1304impl<'a> OverlayScalar<'a> {
1305    #[inline]
1306    fn as_value(&self) -> &OverlayValue {
1307        match self {
1308            OverlayScalar::Borrowed(value) => value,
1309            OverlayScalar::Owned(value) => value,
1310        }
1311    }
1312
1313    #[inline]
1314    pub(crate) fn to_overlay_value(&self) -> OverlayValue {
1315        self.as_value().clone()
1316    }
1317
1318    #[inline]
1319    pub(crate) fn type_tag(&self) -> TypeTag {
1320        self.as_value().type_tag()
1321    }
1322
1323    #[inline]
1324    pub(crate) fn numeric_lane_value(&self) -> Option<f64> {
1325        self.as_value().numeric_lane_value()
1326    }
1327
1328    #[inline]
1329    pub(crate) fn boolean_lane_value(&self) -> Option<bool> {
1330        self.as_value().boolean_lane_value()
1331    }
1332
1333    #[inline]
1334    pub(crate) fn text_lane_value(&self) -> Option<&str> {
1335        self.as_value().text_lane_value()
1336    }
1337
1338    #[inline]
1339    pub(crate) fn error_lane_value(&self) -> Option<u8> {
1340        self.as_value().error_lane_value()
1341    }
1342
1343    pub(crate) fn lowered_text_value(&self) -> Option<String> {
1344        self.as_value().lowered_text_value()
1345    }
1346
1347    pub(crate) fn to_literal_for(&self, date_system: crate::engine::DateSystem) -> LiteralValue {
1348        self.as_value().to_literal_for(date_system)
1349    }
1350
1351    #[cfg(test)]
1352    pub(crate) fn to_literal(&self) -> LiteralValue {
1353        self.to_literal_for(crate::engine::DateSystem::Excel1900)
1354    }
1355}
1356
1357const OVERLAY_ENTRY_BASE_BYTES: usize = 32;
1358const OVERLAY_FRAGMENT_BASE_BYTES: usize = 48;
1359
1360#[allow(dead_code)]
1361#[derive(Debug, Clone)]
1362pub(crate) struct OverlayFragmentPayload {
1363    type_tags: Arc<UInt8Array>,
1364    numbers: Option<Arc<Float64Array>>,
1365    booleans: Option<Arc<BooleanArray>>,
1366    text: Option<ArrayRef>,
1367    errors: Option<Arc<UInt8Array>>,
1368    estimated_bytes: usize,
1369    /// Length of the lane buffers this payload views (its own length unless
1370    /// it is a zero-copy slice of a larger payload).
1371    root_len: usize,
1372}
1373
1374impl OverlayFragmentPayload {
1375    /// A slice keeps sharing its parent's buffers while it is at least a
1376    /// quarter of them (or they are tiny); smaller slices are copied so a
1377    /// split never pins more than max(4x its size, SHARE_ALWAYS_LEN). Each
1378    /// element is therefore copied O(log) times over any sequence of splits,
1379    /// and a split is O(1) otherwise.
1380    const SHARE_MIN_FRACTION: usize = 4;
1381    const SHARE_ALWAYS_LEN: usize = 64;
1382
1383    #[inline]
1384    fn shares(root_len: usize, len: usize) -> bool {
1385        root_len <= Self::SHARE_ALWAYS_LEN
1386            || len.saturating_mul(Self::SHARE_MIN_FRACTION) >= root_len
1387    }
1388
1389    #[inline]
1390    fn len(&self) -> usize {
1391        self.type_tags.len()
1392    }
1393
1394    fn slice(&self, offset: usize, len: usize) -> Self {
1395        debug_assert!(offset.saturating_add(len) <= self.len());
1396        if len == self.len() {
1397            return self.clone();
1398        }
1399        if !Self::shares(self.root_len, len) {
1400            return Self::from_values(self.values_slice(offset, len));
1401        }
1402        fn cut<A: Array + Clone + 'static>(a: &Arc<A>, offset: usize, len: usize) -> Arc<A> {
1403            let sliced = a.slice(offset, len);
1404            Arc::new(
1405                sliced
1406                    .as_any()
1407                    .downcast_ref::<A>()
1408                    .expect("slice keeps the array type")
1409                    .clone(),
1410            )
1411        }
1412        let own = self.len().max(1);
1413        Self {
1414            type_tags: cut(&self.type_tags, offset, len),
1415            numbers: self.numbers.as_ref().map(|a| cut(a, offset, len)),
1416            booleans: self.booleans.as_ref().map(|a| cut(a, offset, len)),
1417            text: self.text.as_ref().map(|a| a.slice(offset, len)),
1418            errors: self.errors.as_ref().map(|a| cut(a, offset, len)),
1419            // Proportional share of the parent's estimate: deterministic, and
1420            // the pieces of a split sum to at most the parent.
1421            estimated_bytes: self.estimated_bytes.saturating_mul(len) / own,
1422            root_len: self.root_len,
1423        }
1424    }
1425
1426    fn from_values(values: Vec<OverlayValue>) -> Self {
1427        let len = values.len();
1428        let mut tag_b = UInt8Builder::with_capacity(len);
1429        let mut nb = Float64Builder::with_capacity(len);
1430        let mut bb = BooleanBuilder::with_capacity(len);
1431        let mut sb = StringBuilder::with_capacity(len, len.saturating_mul(8));
1432        let mut eb = UInt8Builder::with_capacity(len);
1433        let mut non_num = 0usize;
1434        let mut non_bool = 0usize;
1435        let mut non_text = 0usize;
1436        let mut non_err = 0usize;
1437
1438        for value in &values {
1439            append_overlay_value_to_lane_builders(
1440                value,
1441                &mut tag_b,
1442                &mut nb,
1443                &mut bb,
1444                &mut sb,
1445                &mut eb,
1446                &mut non_num,
1447                &mut non_bool,
1448                &mut non_text,
1449                &mut non_err,
1450            );
1451        }
1452
1453        let type_tags = Arc::new(tag_b.finish());
1454        let numbers = {
1455            let a = nb.finish();
1456            (non_num > 0).then(|| Arc::new(a))
1457        };
1458        let booleans = {
1459            let a = bb.finish();
1460            (non_bool > 0).then(|| Arc::new(a))
1461        };
1462        let text = {
1463            let a = sb.finish();
1464            (non_text > 0).then(|| Arc::new(a) as ArrayRef)
1465        };
1466        let errors = {
1467            let a = eb.finish();
1468            (non_err > 0).then(|| Arc::new(a))
1469        };
1470
1471        let estimated_bytes = type_tags
1472            .get_array_memory_size()
1473            .saturating_add(
1474                numbers
1475                    .as_ref()
1476                    .map(|a| a.get_array_memory_size())
1477                    .unwrap_or(0),
1478            )
1479            .saturating_add(
1480                booleans
1481                    .as_ref()
1482                    .map(|a| a.get_array_memory_size())
1483                    .unwrap_or(0),
1484            )
1485            .saturating_add(
1486                text.as_ref()
1487                    .map(|a| a.get_array_memory_size())
1488                    .unwrap_or(0),
1489            )
1490            .saturating_add(
1491                errors
1492                    .as_ref()
1493                    .map(|a| a.get_array_memory_size())
1494                    .unwrap_or(0),
1495            );
1496
1497        Self {
1498            type_tags,
1499            numbers,
1500            booleans,
1501            text,
1502            errors,
1503            estimated_bytes,
1504            root_len: len,
1505        }
1506    }
1507
1508    fn overlay_value(&self, idx: usize) -> Option<OverlayValue> {
1509        if idx >= self.type_tags.len() || self.type_tags.is_null(idx) {
1510            return None;
1511        }
1512        match TypeTag::from_u8(self.type_tags.value(idx)) {
1513            TypeTag::Empty => Some(OverlayValue::Empty),
1514            TypeTag::Number => Some(OverlayValue::Number(self.number_at(idx)?)),
1515            TypeTag::DateTime => Some(OverlayValue::DateTime(self.number_at(idx)?)),
1516            TypeTag::Duration => Some(OverlayValue::Duration(self.number_at(idx)?)),
1517            TypeTag::Boolean => Some(OverlayValue::Boolean(self.boolean_at(idx)?)),
1518            TypeTag::Text => Some(OverlayValue::Text(Arc::from(self.text_at(idx)?))),
1519            TypeTag::Error => Some(OverlayValue::Error(self.error_at(idx)?)),
1520            TypeTag::Pending => Some(OverlayValue::Pending),
1521        }
1522    }
1523
1524    #[inline]
1525    fn get_scalar(&self, idx: usize) -> Option<OverlayScalar<'_>> {
1526        self.overlay_value(idx).map(OverlayScalar::Owned)
1527    }
1528
1529    #[inline]
1530    fn number_at(&self, idx: usize) -> Option<f64> {
1531        let arr = self.numbers.as_ref()?;
1532        (!arr.is_null(idx)).then(|| arr.value(idx))
1533    }
1534
1535    #[inline]
1536    fn boolean_at(&self, idx: usize) -> Option<bool> {
1537        let arr = self.booleans.as_ref()?;
1538        (!arr.is_null(idx)).then(|| arr.value(idx))
1539    }
1540
1541    #[inline]
1542    fn text_at(&self, idx: usize) -> Option<&str> {
1543        let arr = self.text.as_ref()?;
1544        let arr = arr.as_any().downcast_ref::<StringArray>()?;
1545        (!arr.is_null(idx)).then(|| arr.value(idx))
1546    }
1547
1548    #[inline]
1549    fn error_at(&self, idx: usize) -> Option<u8> {
1550        let arr = self.errors.as_ref()?;
1551        (!arr.is_null(idx)).then(|| arr.value(idx))
1552    }
1553
1554    #[inline]
1555    fn values_slice(&self, start: usize, len: usize) -> Vec<OverlayValue> {
1556        (start..start.saturating_add(len))
1557            .filter_map(|idx| self.overlay_value(idx))
1558            .collect()
1559    }
1560
1561    #[inline]
1562    fn estimated_bytes(&self) -> usize {
1563        self.estimated_bytes
1564    }
1565}
1566#[derive(Debug, Clone)]
1567pub(crate) enum OverlayFragment {
1568    SparseOffsets {
1569        offsets: Vec<u32>,
1570        payload: OverlayFragmentPayload,
1571    },
1572    DenseRange {
1573        start: u32,
1574        len: u32,
1575        payload: OverlayFragmentPayload,
1576    },
1577    RunRange {
1578        start: u32,
1579        len: u32,
1580        run_ends: RunEnds,
1581        payload: OverlayFragmentPayload,
1582    },
1583}
1584
1585/// Exclusive run ends of a `RunRange`, relative to the fragment start. The
1586/// buffer is shared between the pieces of a split: run `i` of a piece ends at
1587/// `ends[i] - base`, capped at the piece length.
1588#[derive(Debug, Clone)]
1589pub(crate) struct RunEnds {
1590    ends: arrow_buffer::ScalarBuffer<u32>,
1591    base: u32,
1592    len: u32,
1593}
1594
1595impl RunEnds {
1596    fn new(ends: Vec<u32>) -> Self {
1597        let len = ends.last().copied().unwrap_or(0);
1598        Self {
1599            ends: ends.into(),
1600            base: 0,
1601            len,
1602        }
1603    }
1604
1605    #[inline]
1606    fn count(&self) -> usize {
1607        self.ends.len()
1608    }
1609
1610    /// Exclusive end of run `idx`, relative to the piece start.
1611    #[inline]
1612    fn end(&self, idx: usize) -> usize {
1613        (self.ends[idx] - self.base).min(self.len) as usize
1614    }
1615
1616    /// Index of the run holding relative offset `rel`.
1617    #[inline]
1618    fn run_at(&self, rel: usize) -> usize {
1619        let key = rel as u64 + self.base as u64;
1620        self.ends.partition_point(|end| (*end as u64) <= key)
1621    }
1622
1623    /// The runs of `[rel_start, rel_end)` as `(ends, first run index, run count)`.
1624    fn slice(&self, rel_start: usize, rel_end: usize, share: bool) -> (Self, usize, usize) {
1625        let lo = self.run_at(rel_start);
1626        let hi = self.run_at(rel_end - 1) + 1;
1627        let len = (rel_end - rel_start) as u32;
1628        let ends = if share {
1629            Self {
1630                ends: self.ends.slice(lo, hi - lo),
1631                base: self.base + rel_start as u32,
1632                len,
1633            }
1634        } else {
1635            let mut ends: Vec<u32> = (lo..hi)
1636                .map(|idx| (self.end(idx) - rel_start) as u32)
1637                .collect();
1638            if let Some(last) = ends.last_mut() {
1639                *last = (*last).min(len);
1640            }
1641            Self::new(ends)
1642        };
1643        (ends, lo, hi - lo)
1644    }
1645}
1646
1647impl OverlayFragment {
1648    const MAX_SPLIT_SEGMENTS_BEFORE_SPARSE_FALLBACK: usize = 128;
1649
1650    pub(crate) fn sparse_offsets(items: Vec<(usize, OverlayValue)>) -> Option<Self> {
1651        let mut by_offset: BTreeMap<usize, OverlayValue> = BTreeMap::new();
1652        for (offset, value) in items {
1653            by_offset.insert(offset, value);
1654        }
1655        if by_offset.is_empty() {
1656            return None;
1657        }
1658
1659        let mut offsets = Vec::with_capacity(by_offset.len());
1660        let mut values = Vec::with_capacity(by_offset.len());
1661        for (offset, value) in by_offset {
1662            offsets.push(u32::try_from(offset).expect("overlay offset fits in u32"));
1663            values.push(value);
1664        }
1665
1666        Some(Self::SparseOffsets {
1667            offsets,
1668            payload: OverlayFragmentPayload::from_values(values),
1669        })
1670    }
1671
1672    pub(crate) fn sparse_offsets_if_estimated_smaller_than_points(
1673        items: Vec<(usize, OverlayValue)>,
1674        point_estimate: usize,
1675    ) -> Option<Result<Self, Vec<(usize, OverlayValue)>>> {
1676        let fragment = Self::sparse_offsets(items)?;
1677        if fragment.estimated_bytes() < point_estimate {
1678            Some(Ok(fragment))
1679        } else {
1680            Some(Err(fragment.cells()))
1681        }
1682    }
1683
1684    pub(crate) fn dense_range(start: usize, values: Vec<OverlayValue>) -> Option<Self> {
1685        let len = values.len();
1686        if len == 0 {
1687            return None;
1688        }
1689        Some(Self::DenseRange {
1690            start: u32::try_from(start).expect("overlay start fits in u32"),
1691            len: u32::try_from(len).expect("overlay length fits in u32"),
1692            payload: OverlayFragmentPayload::from_values(values),
1693        })
1694    }
1695
1696    pub(crate) fn run_range(start: usize, values: Vec<OverlayValue>) -> Option<Self> {
1697        if values.is_empty() {
1698            return None;
1699        }
1700
1701        let mut run_ends = Vec::new();
1702        let mut run_values = Vec::new();
1703        let mut current = values[0].clone();
1704        for (idx, value) in values.iter().enumerate().skip(1) {
1705            if *value != current {
1706                run_ends.push(idx);
1707                run_values.push(current);
1708                current = value.clone();
1709            }
1710        }
1711        run_ends.push(values.len());
1712        run_values.push(current);
1713
1714        Self::run_range_from_parts(start, values.len(), run_ends, run_values)
1715    }
1716
1717    fn run_range_from_parts(
1718        start: usize,
1719        len: usize,
1720        run_ends: Vec<usize>,
1721        values: Vec<OverlayValue>,
1722    ) -> Option<Self> {
1723        if len == 0 || run_ends.is_empty() || run_ends.len() != values.len() {
1724            return None;
1725        }
1726
1727        let mut merged_ends: Vec<u32> = Vec::with_capacity(run_ends.len());
1728        let mut merged_values: Vec<OverlayValue> = Vec::with_capacity(values.len());
1729        let mut prev_end = 0usize;
1730        for (end, value) in run_ends.into_iter().zip(values) {
1731            if end <= prev_end || end > len {
1732                return None;
1733            }
1734            if merged_values.last().is_some_and(|last| *last == value) {
1735                if let Some(last_end) = merged_ends.last_mut() {
1736                    *last_end = u32::try_from(end).expect("run end fits in u32");
1737                }
1738            } else {
1739                merged_ends.push(u32::try_from(end).expect("run end fits in u32"));
1740                merged_values.push(value);
1741            }
1742            prev_end = end;
1743        }
1744
1745        if prev_end != len || merged_ends.last().copied() != Some(len as u32) {
1746            return None;
1747        }
1748
1749        Some(Self::RunRange {
1750            start: u32::try_from(start).expect("overlay start fits in u32"),
1751            len: u32::try_from(len).expect("overlay length fits in u32"),
1752            run_ends: RunEnds::new(merged_ends),
1753            payload: OverlayFragmentPayload::from_values(merged_values),
1754        })
1755    }
1756
1757    #[inline]
1758    fn estimated_bytes(&self) -> usize {
1759        match self {
1760            OverlayFragment::SparseOffsets { offsets, payload } => OVERLAY_FRAGMENT_BASE_BYTES
1761                .saturating_add(offsets.len().saturating_mul(core::mem::size_of::<u32>()))
1762                .saturating_add(payload.estimated_bytes()),
1763            OverlayFragment::DenseRange { payload, .. } => {
1764                OVERLAY_FRAGMENT_BASE_BYTES.saturating_add(payload.estimated_bytes())
1765            }
1766            OverlayFragment::RunRange {
1767                run_ends, payload, ..
1768            } => OVERLAY_FRAGMENT_BASE_BYTES
1769                .saturating_add(run_ends.count().saturating_mul(core::mem::size_of::<u32>()))
1770                .saturating_add(payload.estimated_bytes()),
1771        }
1772    }
1773
1774    #[inline]
1775    fn coverage_len(&self) -> usize {
1776        match self {
1777            OverlayFragment::SparseOffsets { offsets, .. } => offsets.len(),
1778            OverlayFragment::DenseRange { len, .. } | OverlayFragment::RunRange { len, .. } => {
1779                *len as usize
1780            }
1781        }
1782    }
1783
1784    pub(crate) fn max_covered_offset(&self) -> usize {
1785        match self {
1786            OverlayFragment::SparseOffsets { offsets, .. } => {
1787                offsets.iter().copied().max().unwrap_or(0) as usize
1788            }
1789            OverlayFragment::DenseRange { start, len, .. }
1790            | OverlayFragment::RunRange { start, len, .. } => (*start as usize)
1791                .saturating_add(*len as usize)
1792                .saturating_sub(1),
1793        }
1794    }
1795
1796    fn interval_coverage(&self) -> Option<core::ops::Range<usize>> {
1797        match self {
1798            OverlayFragment::DenseRange { start, len, .. }
1799            | OverlayFragment::RunRange { start, len, .. } => {
1800                let start = *start as usize;
1801                Some(start..start.saturating_add(*len as usize))
1802            }
1803            OverlayFragment::SparseOffsets { .. } => None,
1804        }
1805    }
1806
1807    fn sparse_offsets_slice(&self) -> Option<&[u32]> {
1808        match self {
1809            OverlayFragment::SparseOffsets { offsets, .. } => Some(offsets.as_slice()),
1810            _ => None,
1811        }
1812    }
1813
1814    fn has_any_in_range(&self, range: core::ops::Range<usize>) -> bool {
1815        if range.is_empty() {
1816            return false;
1817        }
1818        match self {
1819            OverlayFragment::SparseOffsets { offsets, .. } => {
1820                let start = u32::try_from(range.start).unwrap_or(u32::MAX);
1821                let idx = offsets.partition_point(|off| *off < start);
1822                offsets
1823                    .get(idx)
1824                    .is_some_and(|off| (*off as usize) < range.end)
1825            }
1826            OverlayFragment::DenseRange { .. } | OverlayFragment::RunRange { .. } => self
1827                .interval_coverage()
1828                .is_some_and(|r| r.start < range.end && range.start < r.end),
1829        }
1830    }
1831
1832    fn intersects_fragment_exact(&self, replacement: &OverlayFragment) -> bool {
1833        if let Some(offsets) = replacement.sparse_offsets_slice() {
1834            self.intersects_sparse_offsets(offsets)
1835        } else if let Some(range) = replacement.interval_coverage() {
1836            self.intersects_interval(range)
1837        } else {
1838            false
1839        }
1840    }
1841
1842    fn intersects_interval(&self, range: core::ops::Range<usize>) -> bool {
1843        if range.is_empty() {
1844            return false;
1845        }
1846        match self {
1847            OverlayFragment::SparseOffsets { offsets, .. } => {
1848                let start = u32::try_from(range.start).unwrap_or(u32::MAX);
1849                let idx = offsets.partition_point(|off| *off < start);
1850                offsets
1851                    .get(idx)
1852                    .is_some_and(|off| (*off as usize) < range.end)
1853            }
1854            OverlayFragment::DenseRange { .. } | OverlayFragment::RunRange { .. } => self
1855                .interval_coverage()
1856                .is_some_and(|own| own.start < range.end && range.start < own.end),
1857        }
1858    }
1859
1860    fn intersects_sparse_offsets(&self, replacement_offsets: &[u32]) -> bool {
1861        if replacement_offsets.is_empty() {
1862            return false;
1863        }
1864        match self {
1865            OverlayFragment::SparseOffsets { offsets, .. } => {
1866                Self::sorted_offsets_intersect(offsets, replacement_offsets)
1867            }
1868            OverlayFragment::DenseRange { .. } | OverlayFragment::RunRange { .. } => {
1869                self.interval_coverage().is_some_and(|range| {
1870                    let start = u32::try_from(range.start).unwrap_or(u32::MAX);
1871                    let idx = replacement_offsets.partition_point(|off| *off < start);
1872                    replacement_offsets
1873                        .get(idx)
1874                        .is_some_and(|off| (*off as usize) < range.end)
1875                })
1876            }
1877        }
1878    }
1879
1880    fn sorted_offsets_intersect(a: &[u32], b: &[u32]) -> bool {
1881        let mut ai = 0usize;
1882        let mut bi = 0usize;
1883        while ai < a.len() && bi < b.len() {
1884            match a[ai].cmp(&b[bi]) {
1885                core::cmp::Ordering::Equal => return true,
1886                core::cmp::Ordering::Less => ai += 1,
1887                core::cmp::Ordering::Greater => bi += 1,
1888            }
1889        }
1890        false
1891    }
1892
1893    fn covers_offset(&self, off: usize) -> bool {
1894        self.get_scalar(off).is_some()
1895    }
1896
1897    /// Coverage test without reading the value (payload entries are never
1898    /// null, so coverage is the interval or the offset list).
1899    #[inline]
1900    fn covers_offset_fast(&self, off: usize) -> bool {
1901        match self {
1902            OverlayFragment::SparseOffsets { offsets, .. } => {
1903                u32::try_from(off).is_ok_and(|off| offsets.binary_search(&off).is_ok())
1904            }
1905            OverlayFragment::DenseRange { start, len, .. }
1906            | OverlayFragment::RunRange { start, len, .. } => {
1907                let start = *start as usize;
1908                off >= start && off - start < *len as usize
1909            }
1910        }
1911    }
1912
1913    /// Take the points this fragment covers into a rebuilt fragment of the
1914    /// same kind. `None` when it covers no point. Returns the bytes of the
1915    /// removed points (estimated like `Overlay::point_estimate`).
1916    fn fold_points(
1917        &self,
1918        points: &mut FxHashMap<usize, OverlayValue>,
1919    ) -> Option<(usize, OverlayFragment)> {
1920        let mut removed = 0usize;
1921        let mut take = |off: usize, points: &mut FxHashMap<usize, OverlayValue>| {
1922            points.remove(&off).inspect(|v| {
1923                removed = removed.saturating_add(Overlay::point_estimate(v));
1924            })
1925        };
1926        match self {
1927            OverlayFragment::SparseOffsets { offsets, payload } => {
1928                if !offsets
1929                    .iter()
1930                    .any(|off| points.contains_key(&(*off as usize)))
1931                {
1932                    return None;
1933                }
1934                let cells: Vec<_> = offsets
1935                    .iter()
1936                    .enumerate()
1937                    .filter_map(|(idx, off)| {
1938                        let off = *off as usize;
1939                        take(off, points)
1940                            .or_else(|| payload.overlay_value(idx))
1941                            .map(|v| (off, v))
1942                    })
1943                    .collect();
1944                let rebuilt = OverlayFragment::sparse_offsets(cells)?;
1945                Some((removed, rebuilt))
1946            }
1947            OverlayFragment::DenseRange { start, len, .. }
1948            | OverlayFragment::RunRange { start, len, .. } => {
1949                let start = *start as usize;
1950                let len = *len as usize;
1951                if !(start..start + len).any(|off| points.contains_key(&off)) {
1952                    return None;
1953                }
1954                let values: Vec<OverlayValue> = (start..start + len)
1955                    .map(|off| {
1956                        take(off, points).unwrap_or_else(|| {
1957                            self.get_scalar(off)
1958                                .expect("fragment covers its interval")
1959                                .to_overlay_value()
1960                        })
1961                    })
1962                    .collect();
1963                let rebuilt = if matches!(self, OverlayFragment::RunRange { .. }) {
1964                    OverlayFragment::run_range(start, values)?
1965                } else {
1966                    OverlayFragment::dense_range(start, values)?
1967                };
1968                Some((removed, rebuilt))
1969            }
1970        }
1971    }
1972
1973    fn get_scalar(&self, off: usize) -> Option<OverlayScalar<'_>> {
1974        match self {
1975            OverlayFragment::SparseOffsets { offsets, payload } => {
1976                let off = u32::try_from(off).ok()?;
1977                let idx = offsets.binary_search(&off).ok()?;
1978                payload.get_scalar(idx)
1979            }
1980            OverlayFragment::DenseRange {
1981                start,
1982                len,
1983                payload,
1984            } => {
1985                let start = *start as usize;
1986                let rel = off.checked_sub(start)?;
1987                if rel >= *len as usize {
1988                    return None;
1989                }
1990                payload.get_scalar(rel)
1991            }
1992            OverlayFragment::RunRange {
1993                start,
1994                len,
1995                run_ends,
1996                payload,
1997            } => {
1998                let start = *start as usize;
1999                let rel = off.checked_sub(start)?;
2000                if rel >= *len as usize {
2001                    return None;
2002                }
2003                payload.get_scalar(run_ends.run_at(rel))
2004            }
2005        }
2006    }
2007
2008    fn subtract_fragment(&self, replacement: &OverlayFragment) -> Vec<OverlayFragment> {
2009        if let Some(offsets) = replacement.sparse_offsets_slice() {
2010            self.subtract_sparse_offsets(offsets)
2011        } else if let Some(range) = replacement.interval_coverage() {
2012            self.subtract_interval(range)
2013        } else {
2014            vec![self.clone()]
2015        }
2016    }
2017
2018    fn subtract_offset(&self, off: usize) -> Vec<OverlayFragment> {
2019        match self {
2020            OverlayFragment::SparseOffsets { .. } => {
2021                let Ok(off) = u32::try_from(off) else {
2022                    return vec![self.clone()];
2023                };
2024                self.subtract_sparse_offsets(core::slice::from_ref(&off))
2025            }
2026            OverlayFragment::DenseRange { .. } | OverlayFragment::RunRange { .. } => {
2027                self.subtract_interval(off..off.saturating_add(1))
2028            }
2029        }
2030    }
2031
2032    fn subtract_interval(&self, replacement: core::ops::Range<usize>) -> Vec<OverlayFragment> {
2033        if replacement.is_empty() {
2034            return vec![self.clone()];
2035        }
2036
2037        match self {
2038            OverlayFragment::SparseOffsets { offsets, payload } => {
2039                let cells: Vec<_> = offsets
2040                    .iter()
2041                    .enumerate()
2042                    .filter_map(|(idx, off)| {
2043                        let off_usize = *off as usize;
2044                        (!replacement.contains(&off_usize))
2045                            .then(|| payload.overlay_value(idx).map(|value| (off_usize, value)))?
2046                    })
2047                    .collect();
2048                OverlayFragment::sparse_offsets(cells).into_iter().collect()
2049            }
2050            OverlayFragment::DenseRange { .. } => {
2051                let Some(own) = self.interval_coverage() else {
2052                    return vec![self.clone()];
2053                };
2054                if own.end <= replacement.start || replacement.end <= own.start {
2055                    return vec![self.clone()];
2056                }
2057                let cut_start = replacement.start.max(own.start);
2058                let cut_end = replacement.end.min(own.end);
2059                let mut out = Vec::with_capacity(2);
2060                if own.start < cut_start
2061                    && let Some(left) =
2062                        self.dense_segment_with_start(own.start, own.start, cut_start)
2063                {
2064                    out.push(left);
2065                }
2066                if cut_end < own.end
2067                    && let Some(right) = self.dense_segment_with_start(cut_end, cut_end, own.end)
2068                {
2069                    out.push(right);
2070                }
2071                out
2072            }
2073            OverlayFragment::RunRange { .. } => {
2074                let Some(own) = self.interval_coverage() else {
2075                    return vec![self.clone()];
2076                };
2077                if own.end <= replacement.start || replacement.end <= own.start {
2078                    return vec![self.clone()];
2079                }
2080                let cut_start = replacement.start.max(own.start);
2081                let cut_end = replacement.end.min(own.end);
2082                let mut out = Vec::with_capacity(2);
2083                if own.start < cut_start
2084                    && let Some(left) = self.run_segment_with_start(own.start, own.start, cut_start)
2085                {
2086                    out.push(left);
2087                }
2088                if cut_end < own.end
2089                    && let Some(right) = self.run_segment_with_start(cut_end, cut_end, own.end)
2090                {
2091                    out.push(right);
2092                }
2093                out
2094            }
2095        }
2096    }
2097
2098    fn subtract_sparse_offsets(&self, replacement_offsets: &[u32]) -> Vec<OverlayFragment> {
2099        if replacement_offsets.is_empty() {
2100            return vec![self.clone()];
2101        }
2102
2103        match self {
2104            OverlayFragment::SparseOffsets { offsets, payload } => {
2105                let cells: Vec<_> = offsets
2106                    .iter()
2107                    .enumerate()
2108                    .filter_map(|(idx, off)| {
2109                        replacement_offsets.binary_search(off).is_err().then(|| {
2110                            payload
2111                                .overlay_value(idx)
2112                                .map(|value| (*off as usize, value))
2113                        })?
2114                    })
2115                    .collect();
2116                OverlayFragment::sparse_offsets(cells).into_iter().collect()
2117            }
2118            OverlayFragment::DenseRange { .. } => {
2119                self.subtract_sparse_offsets_from_dense(replacement_offsets)
2120            }
2121            OverlayFragment::RunRange { .. } => {
2122                self.subtract_sparse_offsets_from_run(replacement_offsets)
2123            }
2124        }
2125    }
2126
2127    fn sparse_holes_in_interval(offsets: &[u32], range: core::ops::Range<usize>) -> Vec<usize> {
2128        if range.is_empty() {
2129            return Vec::new();
2130        }
2131        let start = u32::try_from(range.start).unwrap_or(u32::MAX);
2132        let mut idx = offsets.partition_point(|off| *off < start);
2133        let mut holes = Vec::new();
2134        let mut last = None;
2135        while let Some(off) = offsets.get(idx).copied() {
2136            let off_usize = off as usize;
2137            if off_usize >= range.end {
2138                break;
2139            }
2140            if last != Some(off_usize) {
2141                holes.push(off_usize);
2142                last = Some(off_usize);
2143            }
2144            idx += 1;
2145        }
2146        holes
2147    }
2148
2149    fn subtract_sparse_offsets_from_dense(
2150        &self,
2151        replacement_offsets: &[u32],
2152    ) -> Vec<OverlayFragment> {
2153        let Some(own) = self.interval_coverage() else {
2154            return vec![self.clone()];
2155        };
2156        let holes = Self::sparse_holes_in_interval(replacement_offsets, own.clone());
2157        if holes.is_empty() {
2158            return vec![self.clone()];
2159        }
2160        if holes.len().saturating_add(1) > Self::MAX_SPLIT_SEGMENTS_BEFORE_SPARSE_FALLBACK {
2161            return self.sparse_remainder_excluding_offsets(&holes);
2162        }
2163
2164        let mut out = Vec::with_capacity(holes.len().saturating_add(1));
2165        let mut seg_start = own.start;
2166        for hole in holes {
2167            if seg_start < hole
2168                && let Some(segment) = self.dense_segment_with_start(seg_start, seg_start, hole)
2169            {
2170                out.push(segment);
2171            }
2172            seg_start = hole.saturating_add(1);
2173        }
2174        if seg_start < own.end
2175            && let Some(segment) = self.dense_segment_with_start(seg_start, seg_start, own.end)
2176        {
2177            out.push(segment);
2178        }
2179        out
2180    }
2181
2182    fn subtract_sparse_offsets_from_run(
2183        &self,
2184        replacement_offsets: &[u32],
2185    ) -> Vec<OverlayFragment> {
2186        let Some(own) = self.interval_coverage() else {
2187            return vec![self.clone()];
2188        };
2189        let holes = Self::sparse_holes_in_interval(replacement_offsets, own.clone());
2190        if holes.is_empty() {
2191            return vec![self.clone()];
2192        }
2193        if holes.len().saturating_add(1) > Self::MAX_SPLIT_SEGMENTS_BEFORE_SPARSE_FALLBACK {
2194            return self.sparse_remainder_excluding_offsets(&holes);
2195        }
2196
2197        let mut out = Vec::with_capacity(holes.len().saturating_add(1));
2198        let mut seg_start = own.start;
2199        for hole in holes {
2200            if seg_start < hole
2201                && let Some(segment) = self.run_segment_with_start(seg_start, seg_start, hole)
2202            {
2203                out.push(segment);
2204            }
2205            seg_start = hole.saturating_add(1);
2206        }
2207        if seg_start < own.end
2208            && let Some(segment) = self.run_segment_with_start(seg_start, seg_start, own.end)
2209        {
2210            out.push(segment);
2211        }
2212        out
2213    }
2214
2215    fn sparse_remainder_excluding_offsets(&self, sorted_holes: &[usize]) -> Vec<OverlayFragment> {
2216        let cells: Vec<_> = self
2217            .cells()
2218            .into_iter()
2219            .filter(|(off, _)| sorted_holes.binary_search(off).is_err())
2220            .collect();
2221        OverlayFragment::sparse_offsets(cells).into_iter().collect()
2222    }
2223
2224    fn dense_segment_with_start(
2225        &self,
2226        new_start: usize,
2227        abs_start: usize,
2228        abs_end: usize,
2229    ) -> Option<OverlayFragment> {
2230        match self {
2231            OverlayFragment::DenseRange { start, payload, .. } => {
2232                if abs_start >= abs_end {
2233                    return None;
2234                }
2235                let base = *start as usize;
2236                let rel_start = abs_start.checked_sub(base)?;
2237                let len = abs_end
2238                    .saturating_sub(abs_start)
2239                    .min(payload.len().saturating_sub(rel_start));
2240                if len == 0 {
2241                    return None;
2242                }
2243                Some(OverlayFragment::DenseRange {
2244                    start: u32::try_from(new_start).expect("overlay start fits in u32"),
2245                    len: len as u32,
2246                    payload: payload.slice(rel_start, len),
2247                })
2248            }
2249            _ => None,
2250        }
2251    }
2252
2253    fn run_segment_with_start(
2254        &self,
2255        new_start: usize,
2256        abs_start: usize,
2257        abs_end: usize,
2258    ) -> Option<OverlayFragment> {
2259        let OverlayFragment::RunRange {
2260            start,
2261            len,
2262            run_ends,
2263            payload,
2264        } = self
2265        else {
2266            return None;
2267        };
2268        if abs_start >= abs_end {
2269            return None;
2270        }
2271        let base = *start as usize;
2272        let frag_end = base.saturating_add(*len as usize);
2273        if abs_start < base || abs_end > frag_end {
2274            return None;
2275        }
2276
2277        let rel_start = abs_start - base;
2278        let rel_end = abs_end - base;
2279        // Zero-copy piece (shared run ends and payload) unless it would pin
2280        // buffers more than 4x its size; see `OverlayFragmentPayload::slice`.
2281        let lo = run_ends.run_at(rel_start);
2282        let hi = run_ends.run_at(rel_end - 1) + 1;
2283        let share = OverlayFragmentPayload::shares(payload.root_len, hi - lo);
2284        let (new_ends, lo, count) = run_ends.slice(rel_start, rel_end, share);
2285        let payload = if share {
2286            payload.slice(lo, count)
2287        } else {
2288            OverlayFragmentPayload::from_values(payload.values_slice(lo, count))
2289        };
2290        Some(OverlayFragment::RunRange {
2291            start: u32::try_from(new_start).expect("overlay start fits in u32"),
2292            len: (rel_end - rel_start) as u32,
2293            run_ends: new_ends,
2294            payload,
2295        })
2296    }
2297
2298    fn cells(&self) -> Vec<(usize, OverlayValue)> {
2299        match self {
2300            OverlayFragment::SparseOffsets { offsets, payload } => offsets
2301                .iter()
2302                .enumerate()
2303                .filter_map(|(idx, off)| {
2304                    payload
2305                        .overlay_value(idx)
2306                        .map(|value| (*off as usize, value))
2307                })
2308                .collect(),
2309            OverlayFragment::DenseRange {
2310                start,
2311                len,
2312                payload,
2313            } => {
2314                let start = *start as usize;
2315                (0..*len as usize)
2316                    .filter_map(|idx| {
2317                        payload
2318                            .overlay_value(idx)
2319                            .map(|value| (start.saturating_add(idx), value))
2320                    })
2321                    .collect()
2322            }
2323            OverlayFragment::RunRange { start, len, .. } => {
2324                let start = *start as usize;
2325                (0..*len as usize)
2326                    .filter_map(|idx| {
2327                        self.get_scalar(start.saturating_add(idx))
2328                            .map(|value| (start.saturating_add(idx), value.to_overlay_value()))
2329                    })
2330                    .collect()
2331            }
2332        }
2333    }
2334
2335    fn slice(&self, off: usize, len: usize) -> Option<OverlayFragment> {
2336        let end = off.saturating_add(len);
2337        if len == 0 {
2338            return None;
2339        }
2340
2341        match self {
2342            OverlayFragment::SparseOffsets { offsets, payload } => {
2343                let start = u32::try_from(off).unwrap_or(u32::MAX);
2344                let lo = offsets.partition_point(|candidate| *candidate < start);
2345                let hi = offsets.partition_point(|candidate| (*candidate as usize) < end);
2346                let cells: Vec<_> = (lo..hi)
2347                    .filter_map(|idx| {
2348                        let rebased = (offsets[idx] as usize).saturating_sub(off);
2349                        payload.overlay_value(idx).map(|value| (rebased, value))
2350                    })
2351                    .collect();
2352                OverlayFragment::sparse_offsets(cells)
2353            }
2354            OverlayFragment::DenseRange { .. } => {
2355                let own = self.interval_coverage()?;
2356                let seg_start = own.start.max(off);
2357                let seg_end = own.end.min(end);
2358                if seg_start >= seg_end {
2359                    return None;
2360                }
2361                self.dense_segment_with_start(seg_start - off, seg_start, seg_end)
2362            }
2363            OverlayFragment::RunRange { .. } => {
2364                let own = self.interval_coverage()?;
2365                let seg_start = own.start.max(off);
2366                let seg_end = own.end.min(end);
2367                if seg_start >= seg_end {
2368                    return None;
2369                }
2370                self.run_segment_with_start(seg_start - off, seg_start, seg_end)
2371            }
2372        }
2373    }
2374}
2375static OVERLAY_EPOCH: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(1);
2376
2377#[inline]
2378fn next_overlay_epoch() -> u64 {
2379    OVERLAY_EPOCH.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
2380}
2381
2382impl Default for Overlay {
2383    fn default() -> Self {
2384        Self::new()
2385    }
2386}
2387
2388#[derive(Debug, Clone)]
2389pub struct Overlay {
2390    /// Content epoch: globally unique, renewed on every value mutation
2391    /// (formats excluded). Merged-lane caches key on it.
2392    epoch: u64,
2393    points: FxHashMap<usize, OverlayValue>,
2394    /// Allocated on the first format (most overlays never carry one).
2395    format_points: Option<Box<FxHashMap<usize, FormatId>>>,
2396    fragments: Vec<OverlayFragment>,
2397    /// Points whose offset a fragment also covers (the point wins). Kept
2398    /// exact so `len` stays the logical coverage; folded into the fragments
2399    /// once they are a fixed fraction of the fragment coverage.
2400    shadowed: u32,
2401    /// Sum of fragment coverage lengths (offsets are u32 within a chunk).
2402    fragment_coverage: u32,
2403    // Deterministic (and intentionally approximate) accounting of overlay memory.
2404    // This is used for budget enforcement/observability; it does not attempt to reflect
2405    // the allocator's exact overhead.
2406    estimated_bytes: usize,
2407}
2408
2409impl Overlay {
2410    // Deterministic estimate per entry to keep budget enforcement stable across platforms.
2411    // Includes key + map/node overhead (approx) and value payload bytes.
2412    const ENTRY_BASE_BYTES: usize = OVERLAY_ENTRY_BASE_BYTES;
2413
2414    pub fn new() -> Self {
2415        Self {
2416            epoch: next_overlay_epoch(),
2417            points: FxHashMap::default(),
2418            format_points: None,
2419            fragments: Vec::new(),
2420            shadowed: 0,
2421            fragment_coverage: 0,
2422            estimated_bytes: 0,
2423        }
2424    }
2425
2426    #[inline]
2427    fn point_estimate(v: &OverlayValue) -> usize {
2428        Self::ENTRY_BASE_BYTES + v.estimated_payload_bytes()
2429    }
2430
2431    #[inline]
2432    fn adjust_estimated_bytes(&mut self, delta: isize) {
2433        if delta >= 0 {
2434            self.estimated_bytes = self.estimated_bytes.saturating_add(delta as usize);
2435        } else {
2436            self.estimated_bytes = self.estimated_bytes.saturating_sub((-delta) as usize);
2437        }
2438    }
2439
2440    #[inline]
2441    pub(crate) fn get_scalar(&self, off: usize) -> Option<OverlayScalar<'_>> {
2442        self.points
2443            .get(&off)
2444            .map(OverlayScalar::Borrowed)
2445            .or_else(|| self.fragments.iter().rev().find_map(|f| f.get_scalar(off)))
2446    }
2447
2448    #[inline]
2449    pub fn get(&self, off: usize) -> Option<OverlayValue> {
2450        self.get_scalar(off).map(|value| value.to_overlay_value())
2451    }
2452
2453    #[inline]
2454    pub fn get_format(&self, off: usize) -> Option<FormatId> {
2455        self.format_points.as_ref()?.get(&off).copied()
2456    }
2457
2458    #[inline]
2459    pub(crate) fn has_formats(&self) -> bool {
2460        self.format_points.as_ref().is_some_and(|m| !m.is_empty())
2461    }
2462
2463    #[inline]
2464    pub fn set_format(&mut self, off: usize, format: Option<FormatId>) {
2465        match format.filter(|id| *id != FormatId::GENERAL) {
2466            Some(id) => {
2467                self.format_points
2468                    .get_or_insert_with(Default::default)
2469                    .insert(off, id);
2470            }
2471            None => {
2472                if let Some(map) = self.format_points.as_mut() {
2473                    map.remove(&off);
2474                }
2475            }
2476        }
2477    }
2478
2479    /// Clear computed formats in `[start, end)`. Empty lanes return in O(1);
2480    /// populated lanes pay for existing formatted entries, not range length.
2481    pub(crate) fn clear_format_range(&mut self, start: usize, end: usize) {
2482        let Some(map) = self.format_points.as_mut() else {
2483            return;
2484        };
2485        if map.is_empty() || start >= end {
2486            return;
2487        }
2488        map.retain(|off, _| *off < start || *off >= end);
2489    }
2490
2491    /// Clear exact computed-format offsets for a sparse computed write.
2492    pub(crate) fn clear_format_offsets(&mut self, offsets: &[usize]) {
2493        let Some(map) = self.format_points.as_mut() else {
2494            return;
2495        };
2496        for off in offsets {
2497            map.remove(off);
2498        }
2499    }
2500
2501    /// Set one offset. Points are read before fragments and applied after
2502    /// them, so the point shadows any fragment covering `off`: the fragment is
2503    /// left as it is (a split would copy it, O(fragment) per point write, and
2504    /// add a fragment per write). Its shadowed value stays counted in
2505    /// `estimated_bytes` until a fragment write or removal covers it.
2506    #[inline]
2507    pub(crate) fn set_scalar(&mut self, off: usize, v: OverlayValue) -> isize {
2508        self.epoch = next_overlay_epoch();
2509        let new_est = Self::point_estimate(&v);
2510        let old_est = match self.points.insert(off, v) {
2511            Some(old) => Self::point_estimate(&old),
2512            None => {
2513                if self.fragment_covers(off) {
2514                    self.shadowed += 1;
2515                }
2516                0
2517            }
2518        };
2519        let delta = new_est as isize - old_est as isize;
2520        self.adjust_estimated_bytes(delta);
2521        if self.shadowed as usize
2522            >= Self::SHADOW_FOLD_MIN.max(self.fragment_coverage as usize / Self::SHADOW_FOLD_DEN)
2523        {
2524            delta.saturating_add(self.fold_shadowed_points())
2525        } else {
2526            delta
2527        }
2528    }
2529
2530    /// Fold shadowed points once they reach `max(MIN, coverage / DEN)`: the
2531    /// fold is O(fragment coverage + points), so each point write pays O(DEN)
2532    /// amortized for it.
2533    const SHADOW_FOLD_MIN: usize = 32;
2534    const SHADOW_FOLD_DEN: usize = 8;
2535
2536    #[inline]
2537    fn fragment_covers(&self, off: usize) -> bool {
2538        self.fragments.iter().any(|f| f.covers_offset_fast(off))
2539    }
2540
2541    /// Rewrite every fragment holding a shadowed point with the point's value
2542    /// and drop those points. Returns the estimated-bytes delta.
2543    fn fold_shadowed_points(&mut self) -> isize {
2544        if self.shadowed == 0 {
2545            return 0;
2546        }
2547        let mut delta = 0isize;
2548        let mut fragments = core::mem::take(&mut self.fragments);
2549        for fragment in fragments.iter_mut() {
2550            let Some(folded) = fragment.fold_points(&mut self.points) else {
2551                continue;
2552            };
2553            let (points_removed, replacement) = folded;
2554            delta = delta.saturating_sub(points_removed as isize);
2555            delta = delta.saturating_add(
2556                replacement.estimated_bytes() as isize - fragment.estimated_bytes() as isize,
2557            );
2558            *fragment = replacement;
2559        }
2560        self.fragments = fragments;
2561        self.shadowed = 0;
2562        self.adjust_estimated_bytes(delta);
2563        delta
2564    }
2565
2566    /// Before points are dropped: how many of them are shadowed.
2567    fn shadowed_among(&self, offsets: &[usize]) -> usize {
2568        if self.shadowed == 0 {
2569            return 0;
2570        }
2571        offsets
2572            .iter()
2573            .filter(|off| self.fragment_covers(**off))
2574            .count()
2575    }
2576
2577    #[inline]
2578    pub fn set(&mut self, off: usize, v: OverlayValue) -> isize {
2579        self.set_scalar(off, v)
2580    }
2581
2582    pub(crate) fn apply_fragment(&mut self, fragment: OverlayFragment) -> isize {
2583        self.epoch = next_overlay_epoch();
2584        // A write that is small next to the existing fragments goes in as
2585        // points: carving it out of them would cost O(fragments) scans and
2586        // O(fragment) sparse rewrites per write, and add fragments that every
2587        // later read scans. The points fold back in at the shadow threshold.
2588        if fragment
2589            .coverage_len()
2590            .saturating_mul(Self::SHADOW_FOLD_DEN)
2591            < self.fragment_coverage as usize
2592        {
2593            let mut delta = 0isize;
2594            for (off, value) in fragment.cells() {
2595                delta = delta.saturating_add(self.set_scalar(off, value));
2596            }
2597            return delta;
2598        }
2599        let mut delta = self.remove_points_covered_by_fragment(&fragment);
2600        delta = delta.saturating_add(self.remove_fragments_covered_by_fragment(&fragment));
2601
2602        let fragment_est = fragment.estimated_bytes();
2603        self.fragment_coverage = self
2604            .fragment_coverage
2605            .saturating_add(fragment.coverage_len() as u32);
2606        self.fragments.push(fragment);
2607        self.adjust_estimated_bytes(fragment_est as isize);
2608        delta.saturating_add(fragment_est as isize)
2609    }
2610
2611    /// Point offsets in `range`: O(min(range, points)).
2612    fn point_offsets_in_range(&self, range: core::ops::Range<usize>) -> Vec<usize> {
2613        if range.len() < self.points.len() {
2614            range.filter(|off| self.points.contains_key(off)).collect()
2615        } else {
2616            self.points
2617                .keys()
2618                .copied()
2619                .filter(|off| range.contains(off))
2620                .collect()
2621        }
2622    }
2623
2624    /// Drop the points at `offsets` (fragments untouched). Returns the bytes
2625    /// removed; keeps `shadowed` exact.
2626    fn drop_points(&mut self, offsets: &[usize]) -> usize {
2627        let shadowed = self.shadowed_among(offsets);
2628        let mut removed = 0usize;
2629        let mut dropped = 0usize;
2630        for off in offsets {
2631            if let Some(old) = self.points.remove(off) {
2632                removed = removed.saturating_add(Self::point_estimate(&old));
2633                dropped += 1;
2634            }
2635        }
2636        debug_assert!(shadowed <= dropped);
2637        self.shadowed = self.shadowed.saturating_sub(shadowed as u32);
2638        self.estimated_bytes = self.estimated_bytes.saturating_sub(removed);
2639        removed
2640    }
2641
2642    fn remove_points_covered_by_fragment(&mut self, fragment: &OverlayFragment) -> isize {
2643        let offsets: Vec<usize> = match fragment {
2644            OverlayFragment::SparseOffsets { offsets, .. } => offsets
2645                .iter()
2646                .map(|off| *off as usize)
2647                .filter(|off| self.points.contains_key(off))
2648                .collect(),
2649            OverlayFragment::DenseRange { .. } | OverlayFragment::RunRange { .. } => fragment
2650                .interval_coverage()
2651                .map(|range| self.point_offsets_in_range(range))
2652                .unwrap_or_default(),
2653        };
2654        if offsets.is_empty() {
2655            return 0;
2656        }
2657        -(self.drop_points(&offsets) as isize)
2658    }
2659
2660    fn remove_fragments_covered_by_fragment(&mut self, replacement: &OverlayFragment) -> isize {
2661        if self.fragments.is_empty() {
2662            return 0;
2663        }
2664
2665        let mut delta: isize = 0;
2666        let mut fragments = Vec::with_capacity(self.fragments.len());
2667        for fragment in self.fragments.drain(..) {
2668            if !fragment.intersects_fragment_exact(replacement) {
2669                fragments.push(fragment);
2670                continue;
2671            }
2672
2673            let old_est = fragment.estimated_bytes();
2674            let replacements = fragment.subtract_fragment(replacement);
2675            let new_est = replacements
2676                .iter()
2677                .map(OverlayFragment::estimated_bytes)
2678                .fold(0usize, usize::saturating_add);
2679            fragments.extend(replacements);
2680            delta = delta.saturating_add(new_est as isize - old_est as isize);
2681        }
2682        self.fragments = fragments;
2683        self.refresh_fragment_coverage();
2684        self.adjust_estimated_bytes(delta);
2685        delta
2686    }
2687
2688    #[inline]
2689    fn refresh_fragment_coverage(&mut self) {
2690        self.fragment_coverage = self
2691            .fragments
2692            .iter()
2693            .map(OverlayFragment::coverage_len)
2694            .fold(0usize, usize::saturating_add) as u32;
2695    }
2696
2697    #[inline]
2698    pub(crate) fn remove_scalar(&mut self, off: usize) -> isize {
2699        self.epoch = next_overlay_epoch();
2700        let mut delta = 0isize;
2701        if self.points.contains_key(&off) {
2702            delta = delta.saturating_sub(self.drop_points(&[off]) as isize);
2703        }
2704
2705        if !self.fragments.is_empty() && self.fragment_covers(off) {
2706            let mut fragment_delta = 0isize;
2707            let mut fragments = Vec::with_capacity(self.fragments.len() + 1);
2708            for fragment in self.fragments.drain(..) {
2709                if !fragment.covers_offset_fast(off) {
2710                    fragments.push(fragment);
2711                    continue;
2712                }
2713
2714                let old_est = fragment.estimated_bytes();
2715                let replacements = fragment.subtract_offset(off);
2716                let new_est = replacements
2717                    .iter()
2718                    .map(OverlayFragment::estimated_bytes)
2719                    .fold(0usize, usize::saturating_add);
2720                fragments.extend(replacements);
2721                fragment_delta = fragment_delta.saturating_add(new_est as isize - old_est as isize);
2722            }
2723            self.fragments = fragments;
2724            self.refresh_fragment_coverage();
2725            self.adjust_estimated_bytes(fragment_delta);
2726            delta = delta.saturating_add(fragment_delta);
2727        }
2728
2729        delta
2730    }
2731
2732    #[inline]
2733    pub fn remove(&mut self, off: usize) -> isize {
2734        self.remove_scalar(off)
2735    }
2736
2737    pub(crate) fn remove_range(&mut self, range: core::ops::Range<usize>) -> isize {
2738        self.epoch = next_overlay_epoch();
2739        if range.is_empty() {
2740            return 0;
2741        }
2742
2743        let mut delta = 0isize;
2744        let offsets = self.point_offsets_in_range(range.clone());
2745        if !offsets.is_empty() {
2746            delta = delta.saturating_sub(self.drop_points(&offsets) as isize);
2747        }
2748
2749        if !self.fragments.is_empty() {
2750            let mut fragment_delta = 0isize;
2751            let mut fragments = Vec::with_capacity(self.fragments.len());
2752            for fragment in self.fragments.drain(..) {
2753                if !fragment.has_any_in_range(range.clone()) {
2754                    fragments.push(fragment);
2755                    continue;
2756                }
2757                let old_est = fragment.estimated_bytes();
2758                let replacements = fragment.subtract_interval(range.clone());
2759                let new_est = replacements
2760                    .iter()
2761                    .map(OverlayFragment::estimated_bytes)
2762                    .fold(0usize, usize::saturating_add);
2763                fragments.extend(replacements);
2764                fragment_delta = fragment_delta.saturating_add(new_est as isize - old_est as isize);
2765            }
2766            self.fragments = fragments;
2767            self.refresh_fragment_coverage();
2768            self.adjust_estimated_bytes(fragment_delta);
2769            delta = delta.saturating_add(fragment_delta);
2770        }
2771
2772        delta
2773    }
2774
2775    #[inline]
2776    pub(crate) fn clear_all(&mut self) -> usize {
2777        self.epoch = next_overlay_epoch();
2778        let freed = self.estimated_bytes;
2779        self.points.clear();
2780        self.fragments.clear();
2781        self.shadowed = 0;
2782        self.fragment_coverage = 0;
2783        self.estimated_bytes = 0;
2784        freed
2785    }
2786
2787    #[inline]
2788    pub fn clear(&mut self) -> usize {
2789        self.clear_all()
2790    }
2791
2792    /// Number of covered offsets (a shadowed point counts once).
2793    #[inline]
2794    pub fn len(&self) -> usize {
2795        (self.points.len() - self.shadowed as usize).saturating_add(self.fragment_coverage as usize)
2796    }
2797
2798    #[inline]
2799    pub fn estimated_bytes(&self) -> usize {
2800        self.estimated_bytes
2801    }
2802
2803    #[inline]
2804    pub fn is_empty(&self) -> bool {
2805        self.points.is_empty() && self.fragments.is_empty()
2806    }
2807
2808    #[inline]
2809    pub(crate) fn has_any_in_range(&self, range: core::ops::Range<usize>) -> bool {
2810        let points = if range.len() < self.points.len() {
2811            range.clone().any(|off| self.points.contains_key(&off))
2812        } else {
2813            self.points.keys().any(|k| range.contains(k))
2814        };
2815        points
2816            || self
2817                .fragments
2818                .iter()
2819                .any(|fragment| fragment.has_any_in_range(range.clone()))
2820    }
2821
2822    #[inline]
2823    pub fn any_in_range(&self, range: core::ops::Range<usize>) -> bool {
2824        self.has_any_in_range(range)
2825    }
2826
2827    pub(crate) fn slice(&self, off: usize, len: usize) -> Overlay {
2828        let mut out = Overlay::new();
2829        let end = off.saturating_add(len);
2830        for fragment in &self.fragments {
2831            if let Some(sliced) = fragment.slice(off, len) {
2832                let _ = out.apply_fragment(sliced);
2833            }
2834        }
2835        for (k, v) in self.points.iter() {
2836            if *k >= off && *k < end {
2837                let _ = out.set_scalar(*k - off, v.clone());
2838            }
2839        }
2840        for (k, format) in self.format_points.iter().flat_map(|m| m.iter()) {
2841            if *k >= off && *k < end {
2842                out.set_format(*k - off, Some(*format));
2843            }
2844        }
2845        out
2846    }
2847
2848    /// Iterate over logical `(offset, value)` pairs in the overlay.
2849    pub fn iter(&self) -> impl Iterator<Item = (usize, OverlayValue)> {
2850        let mut cells = BTreeMap::new();
2851        for fragment in &self.fragments {
2852            for (off, value) in fragment.cells() {
2853                cells.insert(off, value);
2854            }
2855        }
2856        for (off, value) in &self.points {
2857            cells.insert(*off, value.clone());
2858        }
2859        cells.into_iter()
2860    }
2861
2862    /// Iterate over physical point entries only.
2863    /// Visit the points at offsets in `range`: probing each offset when the
2864    /// range is shorter than the point map (a small read must not scan every
2865    /// point of the chunk), else scanning the map. Each offset is visited at
2866    /// most once, so the visit order does not matter to callers.
2867    #[inline]
2868    pub(crate) fn for_each_point_in_range(
2869        &self,
2870        range: core::ops::Range<usize>,
2871        mut visit: impl FnMut(usize, &OverlayValue),
2872    ) {
2873        if self.points.is_empty() {
2874            return;
2875        }
2876        if range.len() < self.points.len() {
2877            for off in range {
2878                if let Some(value) = self.points.get(&off) {
2879                    visit(off, value);
2880                }
2881            }
2882        } else {
2883            for (off, value) in &self.points {
2884                if range.contains(off) {
2885                    visit(*off, value);
2886                }
2887            }
2888        }
2889    }
2890
2891    pub(crate) fn iter_points(&self) -> impl Iterator<Item = (&usize, &OverlayValue)> {
2892        self.points.iter()
2893    }
2894}
2895
2896#[cfg(test)]
2897#[derive(Debug, Clone, Copy, Default, Eq, PartialEq)]
2898pub(crate) struct OverlayDebugStats {
2899    pub(crate) points: usize,
2900    pub(crate) sparse_fragments: usize,
2901    pub(crate) dense_fragments: usize,
2902    pub(crate) run_fragments: usize,
2903    pub(crate) covered_len: usize,
2904}
2905
2906#[cfg(test)]
2907impl Overlay {
2908    pub(crate) fn debug_stats(&self) -> OverlayDebugStats {
2909        let mut stats = OverlayDebugStats {
2910            points: self.points.len(),
2911            covered_len: self.len(),
2912            ..OverlayDebugStats::default()
2913        };
2914        for fragment in &self.fragments {
2915            match fragment {
2916                OverlayFragment::SparseOffsets { .. } => stats.sparse_fragments += 1,
2917                OverlayFragment::DenseRange { .. } => stats.dense_fragments += 1,
2918                OverlayFragment::RunRange { .. } => stats.run_fragments += 1,
2919            }
2920        }
2921        stats
2922    }
2923
2924    /// Fragments are pairwise disjoint; points may shadow fragment offsets
2925    /// (counted exactly by `shadowed`); `len` is the logical coverage.
2926    pub(crate) fn debug_is_normalized(&self) -> bool {
2927        let mut covered = std::collections::HashSet::new();
2928        for fragment in &self.fragments {
2929            for (off, _) in fragment.cells() {
2930                if !covered.insert(off) {
2931                    return false;
2932                }
2933            }
2934        }
2935        let fragment_coverage = covered.len();
2936        let mut shadowed = 0usize;
2937        for off in self.points.keys().copied() {
2938            if !covered.insert(off) {
2939                shadowed += 1;
2940            }
2941        }
2942        shadowed == self.shadowed as usize
2943            && fragment_coverage == self.fragment_coverage as usize
2944            && covered.len() == self.len()
2945    }
2946
2947    pub(crate) fn debug_recomputed_estimated_bytes(&self) -> usize {
2948        let point_bytes = self
2949            .points
2950            .values()
2951            .map(Self::point_estimate)
2952            .fold(0usize, usize::saturating_add);
2953        let fragment_bytes = self
2954            .fragments
2955            .iter()
2956            .map(OverlayFragment::estimated_bytes)
2957            .fold(0usize, usize::saturating_add);
2958        point_bytes.saturating_add(fragment_bytes)
2959    }
2960}
2961
2962#[derive(Debug, Clone, Copy, Default)]
2963#[cfg_attr(test, derive(serde::Serialize))]
2964pub(crate) struct OverlaySelectStats {
2965    pub(crate) zip_select_calls: usize,
2966    pub(crate) direct_dense_slices: usize,
2967    pub(crate) direct_run_materializations: usize,
2968    pub(crate) partial_sparse_intersections: usize,
2969    pub(crate) partial_dense_intersections: usize,
2970    pub(crate) partial_run_intersections: usize,
2971    pub(crate) partial_overlay_builds: usize,
2972    pub(crate) row_scalar_fallbacks: usize,
2973    pub(crate) point_entries_applied: usize,
2974    pub(crate) fragment_intersections: usize,
2975    /// Short point-only ranges merged per offset (no builders or zip).
2976    pub(crate) small_point_selects: usize,
2977    /// Longer point-only ranges: the base lane copied and patched per point.
2978    pub(crate) point_patch_selects: usize,
2979}
2980
2981#[cfg(test)]
2982thread_local! {
2983    static OVERLAY_SELECT_STATS: std::cell::RefCell<OverlaySelectStats> =
2984        std::cell::RefCell::new(OverlaySelectStats::default());
2985}
2986
2987#[cfg(test)]
2988pub(crate) fn reset_overlay_select_stats() {
2989    OVERLAY_SELECT_STATS.with(|stats| *stats.borrow_mut() = OverlaySelectStats::default());
2990}
2991
2992#[cfg(test)]
2993pub(crate) fn snapshot_overlay_select_stats() -> OverlaySelectStats {
2994    OVERLAY_SELECT_STATS.with(|stats| *stats.borrow())
2995}
2996
2997#[cfg(test)]
2998fn record_overlay_select_stats(f: impl FnOnce(&mut OverlaySelectStats)) {
2999    OVERLAY_SELECT_STATS.with(|stats| f(&mut stats.borrow_mut()));
3000}
3001
3002#[cfg(not(test))]
3003#[inline]
3004fn record_overlay_select_stats(_f: impl FnOnce(&mut OverlaySelectStats)) {}
3005
3006#[derive(Debug, Clone, Copy, Eq, PartialEq)]
3007enum OverlayFragmentShape {
3008    Sparse,
3009    Dense,
3010    Run,
3011}
3012
3013struct OverlaySlots<T> {
3014    present: Vec<bool>,
3015    values: Vec<Option<T>>,
3016    any_present: bool,
3017}
3018
3019impl<T> OverlaySlots<T> {
3020    fn new(len: usize) -> Self {
3021        Self {
3022            present: vec![false; len],
3023            values: (0..len).map(|_| None).collect(),
3024            any_present: false,
3025        }
3026    }
3027
3028    #[inline]
3029    fn set(&mut self, idx: usize, value: Option<T>) {
3030        if idx >= self.present.len() {
3031            return;
3032        }
3033        self.present[idx] = true;
3034        self.values[idx] = value;
3035        self.any_present = true;
3036    }
3037
3038    #[inline]
3039    fn any_present(&self) -> bool {
3040        self.any_present
3041    }
3042}
3043
3044pub(crate) struct OverlayCascade<'a> {
3045    user: &'a Overlay,
3046    computed: &'a Overlay,
3047}
3048
3049impl<'a> OverlayCascade<'a> {
3050    #[inline]
3051    pub(crate) fn new(user: &'a Overlay, computed: &'a Overlay) -> Self {
3052        Self { user, computed }
3053    }
3054
3055    #[inline]
3056    pub(crate) fn get_scalar(&self, off: usize) -> Option<OverlayScalar<'a>> {
3057        self.user
3058            .get_scalar(off)
3059            .or_else(|| self.computed.get_scalar(off))
3060    }
3061
3062    #[inline]
3063    pub(crate) fn get_format(&self, off: usize) -> Option<FormatId> {
3064        self.user
3065            .get_format(off)
3066            .or_else(|| self.computed.get_format(off))
3067    }
3068
3069    #[inline]
3070    pub(crate) fn has_any_in_range(&self, range: core::ops::Range<usize>) -> bool {
3071        self.user.has_any_in_range(range.clone()) || self.computed.has_any_in_range(range)
3072    }
3073
3074    /// For a short range that no fragment of either layer touches: per
3075    /// position `(index, user point, computed point)`.
3076    #[allow(clippy::type_complexity)]
3077    fn small_points_only(
3078        &self,
3079        range: core::ops::Range<usize>,
3080    ) -> Option<
3081        impl Iterator<Item = (usize, Option<&'a OverlayValue>, Option<&'a OverlayValue>)> + 'a,
3082    > {
3083        const SMALL: usize = 16;
3084        if range.len() > SMALL
3085            || self
3086                .user
3087                .fragments
3088                .iter()
3089                .chain(self.computed.fragments.iter())
3090                .any(|f| f.has_any_in_range(range.clone()))
3091        {
3092            return None;
3093        }
3094        let (user, computed) = (self.user, self.computed);
3095        let start = range.start;
3096        Some(range.map(move |off| {
3097            (
3098                off - start,
3099                user.points.get(&off),
3100                computed.points.get(&off),
3101            )
3102        }))
3103    }
3104
3105    /// For a range that no fragment of either layer touches: the base lane
3106    /// (aligned to `range`) copied and patched at each point (computed, then
3107    /// user), the per-slot layering below without slot vectors, builders or
3108    /// zip. A point whose lane value is absent becomes null with a default
3109    /// value slot (as a null appended to the zip's value builder). `None` when
3110    /// a fragment intersects the range; `Some(base)` when no point does.
3111    fn points_only_patch<T: arrow_array::ArrowPrimitiveType>(
3112        &self,
3113        range: core::ops::Range<usize>,
3114        base: &arrow_array::PrimitiveArray<T>,
3115        lane: impl Fn(&OverlayValue) -> Option<T::Native>,
3116    ) -> Option<Arc<arrow_array::PrimitiveArray<T>>> {
3117        if self
3118            .user
3119            .fragments
3120            .iter()
3121            .chain(self.computed.fragments.iter())
3122            .any(|f| f.has_any_in_range(range.clone()))
3123        {
3124            return None;
3125        }
3126        let len = range.len();
3127        debug_assert_eq!(base.len(), len);
3128        let mut values: Option<Vec<T::Native>> = None;
3129        let mut validity: Option<arrow_buffer::BooleanBufferBuilder> = None;
3130        for layer in [self.computed, self.user] {
3131            layer.for_each_point_in_range(range.clone(), |off, value| {
3132                record_overlay_select_stats(|stats| stats.point_entries_applied += 1);
3133                let i = off - range.start;
3134                let values = values.get_or_insert_with(|| {
3135                    // A null base keeps its validity from the first point on
3136                    // (a later point may make a null slot valid); an all-valid
3137                    // one needs a bitmap only once a point is null.
3138                    if let Some(nulls) = base.nulls() {
3139                        let mut bits = arrow_buffer::BooleanBufferBuilder::new(len);
3140                        bits.append_buffer(nulls.inner());
3141                        validity = Some(bits);
3142                    }
3143                    base.values().to_vec()
3144                });
3145                match lane(value) {
3146                    Some(v) => {
3147                        values[i] = v;
3148                        if let Some(bits) = validity.as_mut() {
3149                            bits.set_bit(i, true);
3150                        }
3151                    }
3152                    None => {
3153                        values[i] = T::Native::default();
3154                        validity
3155                            .get_or_insert_with(|| {
3156                                let mut bits = arrow_buffer::BooleanBufferBuilder::new(len);
3157                                bits.append_n(len, true);
3158                                bits
3159                            })
3160                            .set_bit(i, false);
3161                    }
3162                }
3163            });
3164        }
3165        let Some(values) = values else {
3166            return Some(Arc::new(base.clone()));
3167        };
3168        record_overlay_select_stats(|stats| stats.point_patch_selects += 1);
3169        let nulls = match validity {
3170            Some(mut bits) => Some(arrow_buffer::NullBuffer::new(bits.finish())),
3171            None => base.nulls().cloned(),
3172        }
3173        .filter(|nulls| nulls.null_count() > 0);
3174        Some(Arc::new(arrow_array::PrimitiveArray::<T>::new(
3175            values.into(),
3176            nulls,
3177        )))
3178    }
3179
3180    pub(crate) fn select_numbers(
3181        &self,
3182        range: core::ops::Range<usize>,
3183        base: &Float64Array,
3184    ) -> Arc<Float64Array> {
3185        if let Some(fragment) = self.user.full_cover_dense_fragment(range.clone()) {
3186            record_overlay_select_stats(|stats| stats.direct_dense_slices += 1);
3187            return Self::dense_numbers(fragment, range);
3188        }
3189        if let Some(fragment) = self.user.full_cover_run_fragment(range.clone()) {
3190            record_overlay_select_stats(|stats| stats.direct_run_materializations += 1);
3191            return Self::run_numbers(fragment, range);
3192        }
3193        if !self.user.has_any_in_range(range.clone()) {
3194            if let Some(fragment) = self.computed.full_cover_dense_fragment(range.clone()) {
3195                record_overlay_select_stats(|stats| stats.direct_dense_slices += 1);
3196                return Self::dense_numbers(fragment, range);
3197            }
3198            if let Some(fragment) = self.computed.full_cover_run_fragment(range.clone()) {
3199                record_overlay_select_stats(|stats| stats.direct_run_materializations += 1);
3200                return Self::run_numbers(fragment, range);
3201            }
3202        }
3203
3204        if !self.has_any_in_range(range.clone()) {
3205            return Arc::new(base.clone());
3206        }
3207        if let Some(small) = self.small_points_only(range.clone()) {
3208            record_overlay_select_stats(|stats| stats.small_point_selects += 1);
3209            // Per offset: user point, else computed point, else base (the
3210            // layering below, without the builders and zip).
3211            let values: Vec<Option<f64>> = small
3212                .map(|(i, user, computed)| match user.or(computed) {
3213                    Some(v) => {
3214                        record_overlay_select_stats(|stats| stats.point_entries_applied += 1);
3215                        v.numeric_lane_value()
3216                    }
3217                    None => base.is_valid(i).then(|| base.value(i)),
3218                })
3219                .collect();
3220            return Arc::new(Float64Array::from(values));
3221        }
3222        if let Some(patched) =
3223            self.points_only_patch(range.clone(), base, OverlayValue::numeric_lane_value)
3224        {
3225            return patched;
3226        }
3227
3228        record_overlay_select_stats(|stats| stats.partial_overlay_builds += 1);
3229        let len = range.end.saturating_sub(range.start);
3230        let mut slots = OverlaySlots::<f64>::new(len);
3231        Self::apply_number_layer(self.computed, range.clone(), &mut slots);
3232        Self::apply_number_layer(self.user, range.clone(), &mut slots);
3233        if !slots.any_present() {
3234            return Arc::new(base.clone());
3235        }
3236
3237        let mut mask_b = BooleanBuilder::with_capacity(len);
3238        let mut values_b = Float64Builder::with_capacity(len);
3239        for idx in 0..len {
3240            mask_b.append_value(slots.present[idx]);
3241            match slots.values[idx] {
3242                Some(value) => values_b.append_value(value),
3243                None => values_b.append_null(),
3244            }
3245        }
3246        record_overlay_select_stats(|stats| stats.zip_select_calls += 1);
3247        let mask = mask_b.finish();
3248        let values = values_b.finish();
3249        let zipped =
3250            crate::compute_prelude::zip_select(&mask, &values, base).expect("zip numeric overlay");
3251        Arc::new(
3252            zipped
3253                .as_any()
3254                .downcast_ref::<Float64Array>()
3255                .expect("numeric overlay zip type")
3256                .clone(),
3257        )
3258    }
3259
3260    pub(crate) fn select_booleans(
3261        &self,
3262        range: core::ops::Range<usize>,
3263        base: &BooleanArray,
3264    ) -> Arc<BooleanArray> {
3265        if let Some(fragment) = self.user.full_cover_dense_fragment(range.clone()) {
3266            record_overlay_select_stats(|stats| stats.direct_dense_slices += 1);
3267            return Self::dense_booleans(fragment, range);
3268        }
3269        if let Some(fragment) = self.user.full_cover_run_fragment(range.clone()) {
3270            record_overlay_select_stats(|stats| stats.direct_run_materializations += 1);
3271            return Self::run_booleans(fragment, range);
3272        }
3273        if !self.user.has_any_in_range(range.clone()) {
3274            if let Some(fragment) = self.computed.full_cover_dense_fragment(range.clone()) {
3275                record_overlay_select_stats(|stats| stats.direct_dense_slices += 1);
3276                return Self::dense_booleans(fragment, range);
3277            }
3278            if let Some(fragment) = self.computed.full_cover_run_fragment(range.clone()) {
3279                record_overlay_select_stats(|stats| stats.direct_run_materializations += 1);
3280                return Self::run_booleans(fragment, range);
3281            }
3282        }
3283
3284        if !self.has_any_in_range(range.clone()) {
3285            return Arc::new(base.clone());
3286        }
3287
3288        record_overlay_select_stats(|stats| stats.partial_overlay_builds += 1);
3289        let len = range.end.saturating_sub(range.start);
3290        let mut slots = OverlaySlots::<bool>::new(len);
3291        Self::apply_boolean_layer(self.computed, range.clone(), &mut slots);
3292        Self::apply_boolean_layer(self.user, range.clone(), &mut slots);
3293        if !slots.any_present() {
3294            return Arc::new(base.clone());
3295        }
3296
3297        let mut mask_b = BooleanBuilder::with_capacity(len);
3298        let mut values_b = BooleanBuilder::with_capacity(len);
3299        for idx in 0..len {
3300            mask_b.append_value(slots.present[idx]);
3301            match slots.values[idx] {
3302                Some(value) => values_b.append_value(value),
3303                None => values_b.append_null(),
3304            }
3305        }
3306        record_overlay_select_stats(|stats| stats.zip_select_calls += 1);
3307        let mask = mask_b.finish();
3308        let values = values_b.finish();
3309        let zipped =
3310            crate::compute_prelude::zip_select(&mask, &values, base).expect("zip boolean overlay");
3311        Arc::new(
3312            zipped
3313                .as_any()
3314                .downcast_ref::<BooleanArray>()
3315                .expect("boolean overlay zip type")
3316                .clone(),
3317        )
3318    }
3319
3320    pub(crate) fn select_text(
3321        &self,
3322        range: core::ops::Range<usize>,
3323        base: &StringArray,
3324    ) -> ArrayRef {
3325        if let Some(fragment) = self.user.full_cover_dense_fragment(range.clone()) {
3326            record_overlay_select_stats(|stats| stats.direct_dense_slices += 1);
3327            return Self::dense_text(fragment, range);
3328        }
3329        if let Some(fragment) = self.user.full_cover_run_fragment(range.clone()) {
3330            record_overlay_select_stats(|stats| stats.direct_run_materializations += 1);
3331            return Self::run_text(fragment, range);
3332        }
3333        if !self.user.has_any_in_range(range.clone()) {
3334            if let Some(fragment) = self.computed.full_cover_dense_fragment(range.clone()) {
3335                record_overlay_select_stats(|stats| stats.direct_dense_slices += 1);
3336                return Self::dense_text(fragment, range);
3337            }
3338            if let Some(fragment) = self.computed.full_cover_run_fragment(range.clone()) {
3339                record_overlay_select_stats(|stats| stats.direct_run_materializations += 1);
3340                return Self::run_text(fragment, range);
3341            }
3342        }
3343
3344        if !self.has_any_in_range(range.clone()) {
3345            return Arc::new(base.clone()) as ArrayRef;
3346        }
3347
3348        record_overlay_select_stats(|stats| stats.partial_overlay_builds += 1);
3349        let len = range.end.saturating_sub(range.start);
3350        let mut slots = OverlaySlots::<String>::new(len);
3351        Self::apply_text_layer(self.computed, range.clone(), &mut slots);
3352        Self::apply_text_layer(self.user, range.clone(), &mut slots);
3353        if !slots.any_present() {
3354            return Arc::new(base.clone()) as ArrayRef;
3355        }
3356
3357        let mut mask_b = BooleanBuilder::with_capacity(len);
3358        let mut values_b = StringBuilder::with_capacity(len, len.saturating_mul(8));
3359        for idx in 0..len {
3360            mask_b.append_value(slots.present[idx]);
3361            match &slots.values[idx] {
3362                Some(value) => values_b.append_value(value),
3363                None => values_b.append_null(),
3364            }
3365        }
3366        record_overlay_select_stats(|stats| stats.zip_select_calls += 1);
3367        let mask = mask_b.finish();
3368        let values = values_b.finish();
3369        crate::compute_prelude::zip_select(&mask, &values, base).expect("zip text overlay")
3370    }
3371
3372    pub(crate) fn select_errors(
3373        &self,
3374        range: core::ops::Range<usize>,
3375        base: &UInt8Array,
3376    ) -> Arc<UInt8Array> {
3377        if let Some(fragment) = self.user.full_cover_dense_fragment(range.clone()) {
3378            record_overlay_select_stats(|stats| stats.direct_dense_slices += 1);
3379            return Self::dense_errors(fragment, range);
3380        }
3381        if let Some(fragment) = self.user.full_cover_run_fragment(range.clone()) {
3382            record_overlay_select_stats(|stats| stats.direct_run_materializations += 1);
3383            return Self::run_errors(fragment, range);
3384        }
3385        if !self.user.has_any_in_range(range.clone()) {
3386            if let Some(fragment) = self.computed.full_cover_dense_fragment(range.clone()) {
3387                record_overlay_select_stats(|stats| stats.direct_dense_slices += 1);
3388                return Self::dense_errors(fragment, range);
3389            }
3390            if let Some(fragment) = self.computed.full_cover_run_fragment(range.clone()) {
3391                record_overlay_select_stats(|stats| stats.direct_run_materializations += 1);
3392                return Self::run_errors(fragment, range);
3393            }
3394        }
3395
3396        if !self.has_any_in_range(range.clone()) {
3397            return Arc::new(base.clone());
3398        }
3399        if let Some(small) = self.small_points_only(range.clone()) {
3400            record_overlay_select_stats(|stats| stats.small_point_selects += 1);
3401            let values: Vec<Option<u8>> = small
3402                .map(|(i, user, computed)| match user.or(computed) {
3403                    Some(v) => {
3404                        record_overlay_select_stats(|stats| stats.point_entries_applied += 1);
3405                        v.error_lane_value()
3406                    }
3407                    None => base.is_valid(i).then(|| base.value(i)),
3408                })
3409                .collect();
3410            return Arc::new(UInt8Array::from(values));
3411        }
3412        if let Some(patched) =
3413            self.points_only_patch(range.clone(), base, OverlayValue::error_lane_value)
3414        {
3415            return patched;
3416        }
3417
3418        record_overlay_select_stats(|stats| stats.partial_overlay_builds += 1);
3419        let len = range.end.saturating_sub(range.start);
3420        let mut slots = OverlaySlots::<u8>::new(len);
3421        Self::apply_error_layer(self.computed, range.clone(), &mut slots);
3422        Self::apply_error_layer(self.user, range.clone(), &mut slots);
3423        if !slots.any_present() {
3424            return Arc::new(base.clone());
3425        }
3426
3427        let mut mask_b = BooleanBuilder::with_capacity(len);
3428        let mut values_b = UInt8Builder::with_capacity(len);
3429        for idx in 0..len {
3430            mask_b.append_value(slots.present[idx]);
3431            match slots.values[idx] {
3432                Some(value) => values_b.append_value(value),
3433                None => values_b.append_null(),
3434            }
3435        }
3436        record_overlay_select_stats(|stats| stats.zip_select_calls += 1);
3437        let mask = mask_b.finish();
3438        let values = values_b.finish();
3439        let zipped =
3440            crate::compute_prelude::zip_select(&mask, &values, base).expect("zip error overlay");
3441        Arc::new(
3442            zipped
3443                .as_any()
3444                .downcast_ref::<UInt8Array>()
3445                .expect("error overlay zip type")
3446                .clone(),
3447        )
3448    }
3449
3450    pub(crate) fn select_type_tags(
3451        &self,
3452        range: core::ops::Range<usize>,
3453        base: &UInt8Array,
3454    ) -> Arc<UInt8Array> {
3455        if let Some(fragment) = self.user.full_cover_dense_fragment(range.clone()) {
3456            record_overlay_select_stats(|stats| stats.direct_dense_slices += 1);
3457            return Self::dense_type_tags(fragment, range);
3458        }
3459        if let Some(fragment) = self.user.full_cover_run_fragment(range.clone()) {
3460            record_overlay_select_stats(|stats| stats.direct_run_materializations += 1);
3461            return Self::run_type_tags(fragment, range);
3462        }
3463        if !self.user.has_any_in_range(range.clone()) {
3464            if let Some(fragment) = self.computed.full_cover_dense_fragment(range.clone()) {
3465                record_overlay_select_stats(|stats| stats.direct_dense_slices += 1);
3466                return Self::dense_type_tags(fragment, range);
3467            }
3468            if let Some(fragment) = self.computed.full_cover_run_fragment(range.clone()) {
3469                record_overlay_select_stats(|stats| stats.direct_run_materializations += 1);
3470                return Self::run_type_tags(fragment, range);
3471            }
3472        }
3473
3474        if !self.has_any_in_range(range.clone()) {
3475            return Arc::new(base.clone());
3476        }
3477
3478        record_overlay_select_stats(|stats| stats.partial_overlay_builds += 1);
3479        let len = range.end.saturating_sub(range.start);
3480        let mut slots = OverlaySlots::<u8>::new(len);
3481        Self::apply_type_tag_layer(self.computed, range.clone(), &mut slots);
3482        Self::apply_type_tag_layer(self.user, range.clone(), &mut slots);
3483        if !slots.any_present() {
3484            return Arc::new(base.clone());
3485        }
3486
3487        let mut mask_b = BooleanBuilder::with_capacity(len);
3488        let mut values_b = UInt8Builder::with_capacity(len);
3489        for idx in 0..len {
3490            mask_b.append_value(slots.present[idx]);
3491            match slots.values[idx] {
3492                Some(value) => values_b.append_value(value),
3493                None => values_b.append_null(),
3494            }
3495        }
3496        record_overlay_select_stats(|stats| stats.zip_select_calls += 1);
3497        let mask = mask_b.finish();
3498        let values = values_b.finish();
3499        let zipped =
3500            crate::compute_prelude::zip_select(&mask, &values, base).expect("zip type-tag overlay");
3501        Arc::new(
3502            zipped
3503                .as_any()
3504                .downcast_ref::<UInt8Array>()
3505                .expect("type-tag overlay zip type")
3506                .clone(),
3507        )
3508    }
3509
3510    pub(crate) fn select_lowered_text(
3511        &self,
3512        range: core::ops::Range<usize>,
3513        base: &StringArray,
3514    ) -> Arc<StringArray> {
3515        if let Some(fragment) = self.user.full_cover_dense_fragment(range.clone()) {
3516            record_overlay_select_stats(|stats| stats.direct_dense_slices += 1);
3517            return Self::dense_lowered_text(fragment, range);
3518        }
3519        if let Some(fragment) = self.user.full_cover_run_fragment(range.clone()) {
3520            record_overlay_select_stats(|stats| stats.direct_run_materializations += 1);
3521            return Self::run_lowered_text(fragment, range);
3522        }
3523        if !self.user.has_any_in_range(range.clone()) {
3524            if let Some(fragment) = self.computed.full_cover_dense_fragment(range.clone()) {
3525                record_overlay_select_stats(|stats| stats.direct_dense_slices += 1);
3526                return Self::dense_lowered_text(fragment, range);
3527            }
3528            if let Some(fragment) = self.computed.full_cover_run_fragment(range.clone()) {
3529                record_overlay_select_stats(|stats| stats.direct_run_materializations += 1);
3530                return Self::run_lowered_text(fragment, range);
3531            }
3532        }
3533
3534        if !self.has_any_in_range(range.clone()) {
3535            return Arc::new(base.clone());
3536        }
3537        if self.user.fragments.is_empty() && self.computed.fragments.is_empty() {
3538            return self.select_lowered_text_point_scalar(range, base);
3539        }
3540
3541        record_overlay_select_stats(|stats| stats.partial_overlay_builds += 1);
3542        let len = range.end.saturating_sub(range.start);
3543        let mut slots = OverlaySlots::<String>::new(len);
3544        Self::apply_lowered_text_layer(self.computed, range.clone(), &mut slots);
3545        Self::apply_lowered_text_layer(self.user, range.clone(), &mut slots);
3546        if !slots.any_present() {
3547            return Arc::new(base.clone());
3548        }
3549
3550        let mut mask_b = BooleanBuilder::with_capacity(len);
3551        let mut values_b = StringBuilder::with_capacity(len, len.saturating_mul(8));
3552        for idx in 0..len {
3553            mask_b.append_value(slots.present[idx]);
3554            match &slots.values[idx] {
3555                Some(value) => values_b.append_value(value),
3556                None => values_b.append_null(),
3557            }
3558        }
3559        record_overlay_select_stats(|stats| stats.zip_select_calls += 1);
3560        let mask = mask_b.finish();
3561        let values = values_b.finish();
3562        let zipped = crate::compute_prelude::zip_select(&mask, &values, base)
3563            .expect("zip lowered text overlay");
3564        Arc::new(
3565            zipped
3566                .as_any()
3567                .downcast_ref::<StringArray>()
3568                .expect("lowered text overlay zip type")
3569                .clone(),
3570        )
3571    }
3572
3573    fn select_lowered_text_point_scalar(
3574        &self,
3575        range: core::ops::Range<usize>,
3576        base: &StringArray,
3577    ) -> Arc<StringArray> {
3578        let len = range.end.saturating_sub(range.start);
3579        let mut mask_b = BooleanBuilder::with_capacity(len);
3580        let mut values_b = StringBuilder::with_capacity(len, len.saturating_mul(8));
3581        record_overlay_select_stats(|stats| stats.row_scalar_fallbacks += len);
3582        for off in range {
3583            if let Some(value) = self.get_scalar(off) {
3584                mask_b.append_value(true);
3585                if let Some(s) = value.lowered_text_value() {
3586                    values_b.append_value(&s);
3587                } else {
3588                    values_b.append_null();
3589                }
3590                record_overlay_select_stats(|stats| stats.point_entries_applied += 1);
3591            } else {
3592                mask_b.append_value(false);
3593                values_b.append_null();
3594            }
3595        }
3596        record_overlay_select_stats(|stats| stats.zip_select_calls += 1);
3597        let mask = mask_b.finish();
3598        let values = values_b.finish();
3599        let zipped = crate::compute_prelude::zip_select(&mask, &values, base)
3600            .expect("zip lowered text overlay");
3601        Arc::new(
3602            zipped
3603                .as_any()
3604                .downcast_ref::<StringArray>()
3605                .expect("lowered text overlay zip type")
3606                .clone(),
3607        )
3608    }
3609
3610    fn dense_numbers(
3611        fragment: &OverlayFragment,
3612        range: core::ops::Range<usize>,
3613    ) -> Arc<Float64Array> {
3614        let (rel_start, len, payload) = Self::dense_payload_window(fragment, range);
3615        Self::payload_numbers_slice(payload, rel_start, len)
3616    }
3617
3618    fn dense_booleans(
3619        fragment: &OverlayFragment,
3620        range: core::ops::Range<usize>,
3621    ) -> Arc<BooleanArray> {
3622        let (rel_start, len, payload) = Self::dense_payload_window(fragment, range);
3623        Self::payload_booleans_slice(payload, rel_start, len)
3624    }
3625
3626    fn dense_text(fragment: &OverlayFragment, range: core::ops::Range<usize>) -> ArrayRef {
3627        let (rel_start, len, payload) = Self::dense_payload_window(fragment, range);
3628        Self::payload_text_slice(payload, rel_start, len)
3629    }
3630
3631    fn dense_errors(fragment: &OverlayFragment, range: core::ops::Range<usize>) -> Arc<UInt8Array> {
3632        let (rel_start, len, payload) = Self::dense_payload_window(fragment, range);
3633        Self::payload_errors_slice(payload, rel_start, len)
3634    }
3635
3636    fn dense_type_tags(
3637        fragment: &OverlayFragment,
3638        range: core::ops::Range<usize>,
3639    ) -> Arc<UInt8Array> {
3640        let (rel_start, len, payload) = Self::dense_payload_window(fragment, range);
3641        Self::payload_type_tags_slice(payload, rel_start, len)
3642    }
3643
3644    fn dense_lowered_text(
3645        fragment: &OverlayFragment,
3646        range: core::ops::Range<usize>,
3647    ) -> Arc<StringArray> {
3648        let (rel_start, len, payload) = Self::dense_payload_window(fragment, range);
3649        Self::payload_lowered_text_materialize(payload, rel_start, len)
3650    }
3651
3652    fn dense_payload_window(
3653        fragment: &OverlayFragment,
3654        range: core::ops::Range<usize>,
3655    ) -> (usize, usize, &OverlayFragmentPayload) {
3656        let OverlayFragment::DenseRange { start, payload, .. } = fragment else {
3657            unreachable!("dense payload window requires DenseRange")
3658        };
3659        let rel_start = range.start.saturating_sub(*start as usize);
3660        (rel_start, range.end.saturating_sub(range.start), payload)
3661    }
3662
3663    fn run_numbers(
3664        fragment: &OverlayFragment,
3665        range: core::ops::Range<usize>,
3666    ) -> Arc<Float64Array> {
3667        let mut b = Float64Builder::with_capacity(range.end.saturating_sub(range.start));
3668        Self::for_each_run_payload_index(fragment, range, |payload, run_idx, repeat| {
3669            if let Some(value) = payload.number_at(run_idx) {
3670                for _ in 0..repeat {
3671                    b.append_value(value);
3672                }
3673            } else {
3674                for _ in 0..repeat {
3675                    b.append_null();
3676                }
3677            }
3678        });
3679        Arc::new(b.finish())
3680    }
3681
3682    fn run_booleans(
3683        fragment: &OverlayFragment,
3684        range: core::ops::Range<usize>,
3685    ) -> Arc<BooleanArray> {
3686        let mut b = BooleanBuilder::with_capacity(range.end.saturating_sub(range.start));
3687        Self::for_each_run_payload_index(fragment, range, |payload, run_idx, repeat| {
3688            if let Some(value) = payload.boolean_at(run_idx) {
3689                for _ in 0..repeat {
3690                    b.append_value(value);
3691                }
3692            } else {
3693                for _ in 0..repeat {
3694                    b.append_null();
3695                }
3696            }
3697        });
3698        Arc::new(b.finish())
3699    }
3700
3701    fn run_text(fragment: &OverlayFragment, range: core::ops::Range<usize>) -> ArrayRef {
3702        let mut b = StringBuilder::with_capacity(
3703            range.end.saturating_sub(range.start),
3704            range.end.saturating_sub(range.start).saturating_mul(8),
3705        );
3706        Self::for_each_run_payload_index(fragment, range, |payload, run_idx, repeat| {
3707            if let Some(value) = payload.text_at(run_idx) {
3708                for _ in 0..repeat {
3709                    b.append_value(value);
3710                }
3711            } else {
3712                for _ in 0..repeat {
3713                    b.append_null();
3714                }
3715            }
3716        });
3717        Arc::new(b.finish()) as ArrayRef
3718    }
3719
3720    fn run_errors(fragment: &OverlayFragment, range: core::ops::Range<usize>) -> Arc<UInt8Array> {
3721        let mut b = UInt8Builder::with_capacity(range.end.saturating_sub(range.start));
3722        Self::for_each_run_payload_index(fragment, range, |payload, run_idx, repeat| {
3723            if let Some(value) = payload.error_at(run_idx) {
3724                for _ in 0..repeat {
3725                    b.append_value(value);
3726                }
3727            } else {
3728                for _ in 0..repeat {
3729                    b.append_null();
3730                }
3731            }
3732        });
3733        Arc::new(b.finish())
3734    }
3735
3736    fn run_type_tags(
3737        fragment: &OverlayFragment,
3738        range: core::ops::Range<usize>,
3739    ) -> Arc<UInt8Array> {
3740        let mut b = UInt8Builder::with_capacity(range.end.saturating_sub(range.start));
3741        Self::for_each_run_payload_index(fragment, range, |payload, run_idx, repeat| {
3742            let tag = payload.type_tag_at(run_idx).unwrap_or(TypeTag::Empty) as u8;
3743            for _ in 0..repeat {
3744                b.append_value(tag);
3745            }
3746        });
3747        Arc::new(b.finish())
3748    }
3749
3750    fn run_lowered_text(
3751        fragment: &OverlayFragment,
3752        range: core::ops::Range<usize>,
3753    ) -> Arc<StringArray> {
3754        let mut b = StringBuilder::with_capacity(
3755            range.end.saturating_sub(range.start),
3756            range.end.saturating_sub(range.start).saturating_mul(8),
3757        );
3758        Self::for_each_run_payload_index(fragment, range, |payload, run_idx, repeat| {
3759            let value = Self::payload_lowered_text_at(payload, run_idx);
3760            if let Some(value) = value {
3761                for _ in 0..repeat {
3762                    b.append_value(&value);
3763                }
3764            } else {
3765                for _ in 0..repeat {
3766                    b.append_null();
3767                }
3768            }
3769        });
3770        Arc::new(b.finish())
3771    }
3772
3773    fn payload_numbers_slice(
3774        payload: &OverlayFragmentPayload,
3775        start: usize,
3776        len: usize,
3777    ) -> Arc<Float64Array> {
3778        if let Some(array) = &payload.numbers {
3779            let sliced = array.slice(start, len);
3780            Arc::new(
3781                sliced
3782                    .as_any()
3783                    .downcast_ref::<Float64Array>()
3784                    .unwrap()
3785                    .clone(),
3786            )
3787        } else {
3788            Self::null_numbers(len)
3789        }
3790    }
3791
3792    fn payload_booleans_slice(
3793        payload: &OverlayFragmentPayload,
3794        start: usize,
3795        len: usize,
3796    ) -> Arc<BooleanArray> {
3797        if let Some(array) = &payload.booleans {
3798            let sliced = array.slice(start, len);
3799            Arc::new(
3800                sliced
3801                    .as_any()
3802                    .downcast_ref::<BooleanArray>()
3803                    .unwrap()
3804                    .clone(),
3805            )
3806        } else {
3807            Self::null_booleans(len)
3808        }
3809    }
3810
3811    fn payload_text_slice(payload: &OverlayFragmentPayload, start: usize, len: usize) -> ArrayRef {
3812        if let Some(array) = &payload.text {
3813            array.slice(start, len)
3814        } else {
3815            new_null_array(&DataType::Utf8, len)
3816        }
3817    }
3818
3819    fn payload_errors_slice(
3820        payload: &OverlayFragmentPayload,
3821        start: usize,
3822        len: usize,
3823    ) -> Arc<UInt8Array> {
3824        if let Some(array) = &payload.errors {
3825            let sliced = array.slice(start, len);
3826            Arc::new(
3827                sliced
3828                    .as_any()
3829                    .downcast_ref::<UInt8Array>()
3830                    .unwrap()
3831                    .clone(),
3832            )
3833        } else {
3834            Self::null_errors(len)
3835        }
3836    }
3837
3838    fn payload_type_tags_slice(
3839        payload: &OverlayFragmentPayload,
3840        start: usize,
3841        len: usize,
3842    ) -> Arc<UInt8Array> {
3843        let sliced = payload.type_tags.slice(start, len);
3844        Arc::new(
3845            sliced
3846                .as_any()
3847                .downcast_ref::<UInt8Array>()
3848                .unwrap()
3849                .clone(),
3850        )
3851    }
3852
3853    fn payload_lowered_text_materialize(
3854        payload: &OverlayFragmentPayload,
3855        start: usize,
3856        len: usize,
3857    ) -> Arc<StringArray> {
3858        let mut b = StringBuilder::with_capacity(len, len.saturating_mul(8));
3859        for idx in start..start.saturating_add(len) {
3860            if let Some(value) = Self::payload_lowered_text_at(payload, idx) {
3861                b.append_value(&value);
3862            } else {
3863                b.append_null();
3864            }
3865        }
3866        Arc::new(b.finish())
3867    }
3868
3869    fn payload_lowered_text_at(payload: &OverlayFragmentPayload, idx: usize) -> Option<String> {
3870        match payload.type_tag_at(idx)? {
3871            TypeTag::Text => payload.text_at(idx).map(|value| value.to_lowercase()),
3872            TypeTag::Number | TypeTag::DateTime | TypeTag::Duration => {
3873                payload.number_at(idx).map(|value| value.to_string())
3874            }
3875            TypeTag::Boolean => payload
3876                .boolean_at(idx)
3877                .map(|value| if value { "true" } else { "false" }.to_string()),
3878            TypeTag::Empty | TypeTag::Error | TypeTag::Pending => None,
3879        }
3880    }
3881
3882    fn null_numbers(len: usize) -> Arc<Float64Array> {
3883        let arr = new_null_array(&DataType::Float64, len);
3884        Arc::new(arr.as_any().downcast_ref::<Float64Array>().unwrap().clone())
3885    }
3886
3887    fn null_booleans(len: usize) -> Arc<BooleanArray> {
3888        let arr = new_null_array(&DataType::Boolean, len);
3889        Arc::new(arr.as_any().downcast_ref::<BooleanArray>().unwrap().clone())
3890    }
3891
3892    fn null_errors(len: usize) -> Arc<UInt8Array> {
3893        let arr = new_null_array(&DataType::UInt8, len);
3894        Arc::new(arr.as_any().downcast_ref::<UInt8Array>().unwrap().clone())
3895    }
3896
3897    fn apply_number_layer(
3898        layer: &Overlay,
3899        range: core::ops::Range<usize>,
3900        slots: &mut OverlaySlots<f64>,
3901    ) {
3902        Self::apply_fragment_layer(layer, range.clone(), slots, |payload, idx| {
3903            payload.number_at(idx)
3904        });
3905        layer.for_each_point_in_range(range.clone(), |off, value| {
3906            slots.set(off - range.start, value.numeric_lane_value());
3907            record_overlay_select_stats(|stats| stats.point_entries_applied += 1);
3908        });
3909    }
3910
3911    fn apply_boolean_layer(
3912        layer: &Overlay,
3913        range: core::ops::Range<usize>,
3914        slots: &mut OverlaySlots<bool>,
3915    ) {
3916        Self::apply_fragment_layer(layer, range.clone(), slots, |payload, idx| {
3917            payload.boolean_at(idx)
3918        });
3919        layer.for_each_point_in_range(range.clone(), |off, value| {
3920            slots.set(off - range.start, value.boolean_lane_value());
3921            record_overlay_select_stats(|stats| stats.point_entries_applied += 1);
3922        });
3923    }
3924
3925    fn apply_text_layer(
3926        layer: &Overlay,
3927        range: core::ops::Range<usize>,
3928        slots: &mut OverlaySlots<String>,
3929    ) {
3930        Self::apply_fragment_layer(layer, range.clone(), slots, |payload, idx| {
3931            payload.text_at(idx).map(ToString::to_string)
3932        });
3933        layer.for_each_point_in_range(range.clone(), |off, value| {
3934            slots.set(
3935                off - range.start,
3936                value.text_lane_value().map(ToString::to_string),
3937            );
3938            record_overlay_select_stats(|stats| stats.point_entries_applied += 1);
3939        });
3940    }
3941
3942    fn apply_error_layer(
3943        layer: &Overlay,
3944        range: core::ops::Range<usize>,
3945        slots: &mut OverlaySlots<u8>,
3946    ) {
3947        Self::apply_fragment_layer(layer, range.clone(), slots, |payload, idx| {
3948            payload.error_at(idx)
3949        });
3950        layer.for_each_point_in_range(range.clone(), |off, value| {
3951            slots.set(off - range.start, value.error_lane_value());
3952            record_overlay_select_stats(|stats| stats.point_entries_applied += 1);
3953        });
3954    }
3955
3956    fn apply_type_tag_layer(
3957        layer: &Overlay,
3958        range: core::ops::Range<usize>,
3959        slots: &mut OverlaySlots<u8>,
3960    ) {
3961        Self::apply_fragment_layer(layer, range.clone(), slots, |payload, idx| {
3962            payload.type_tag_at(idx).map(|tag| tag as u8)
3963        });
3964        layer.for_each_point_in_range(range.clone(), |off, value| {
3965            slots.set(off - range.start, Some(value.type_tag() as u8));
3966            record_overlay_select_stats(|stats| stats.point_entries_applied += 1);
3967        });
3968    }
3969
3970    fn apply_lowered_text_layer(
3971        layer: &Overlay,
3972        range: core::ops::Range<usize>,
3973        slots: &mut OverlaySlots<String>,
3974    ) {
3975        Self::apply_fragment_layer(layer, range.clone(), slots, Self::payload_lowered_text_at);
3976        layer.for_each_point_in_range(range.clone(), |off, value| {
3977            slots.set(off - range.start, value.lowered_text_value());
3978            record_overlay_select_stats(|stats| stats.point_entries_applied += 1);
3979        });
3980    }
3981
3982    fn apply_fragment_layer<T>(
3983        layer: &Overlay,
3984        range: core::ops::Range<usize>,
3985        slots: &mut OverlaySlots<T>,
3986        mut value_at: impl FnMut(&OverlayFragmentPayload, usize) -> Option<T>,
3987    ) {
3988        for fragment in &layer.fragments {
3989            if !fragment.has_any_in_range(range.clone()) {
3990                continue;
3991            }
3992            Self::record_fragment_intersection(fragment);
3993            Self::for_each_fragment_payload_index(
3994                fragment,
3995                range.clone(),
3996                |out_idx, payload, payload_idx| {
3997                    slots.set(out_idx, value_at(payload, payload_idx));
3998                },
3999            );
4000        }
4001    }
4002
4003    fn record_fragment_intersection(fragment: &OverlayFragment) {
4004        let shape = match fragment {
4005            OverlayFragment::SparseOffsets { .. } => OverlayFragmentShape::Sparse,
4006            OverlayFragment::DenseRange { .. } => OverlayFragmentShape::Dense,
4007            OverlayFragment::RunRange { .. } => OverlayFragmentShape::Run,
4008        };
4009        record_overlay_select_stats(|stats| {
4010            stats.fragment_intersections += 1;
4011            match shape {
4012                OverlayFragmentShape::Sparse => stats.partial_sparse_intersections += 1,
4013                OverlayFragmentShape::Dense => stats.partial_dense_intersections += 1,
4014                OverlayFragmentShape::Run => stats.partial_run_intersections += 1,
4015            }
4016        });
4017    }
4018
4019    fn for_each_fragment_payload_index(
4020        fragment: &OverlayFragment,
4021        range: core::ops::Range<usize>,
4022        mut f: impl FnMut(usize, &OverlayFragmentPayload, usize),
4023    ) {
4024        if range.is_empty() {
4025            return;
4026        }
4027        match fragment {
4028            OverlayFragment::SparseOffsets { offsets, payload } => {
4029                let start = u32::try_from(range.start).unwrap_or(u32::MAX);
4030                let lo = offsets.partition_point(|off| *off < start);
4031                let hi = offsets.partition_point(|off| (*off as usize) < range.end);
4032                for (idx, off) in offsets.iter().enumerate().take(hi).skip(lo) {
4033                    let out_idx = (*off as usize).saturating_sub(range.start);
4034                    f(out_idx, payload, idx);
4035                }
4036            }
4037            OverlayFragment::DenseRange {
4038                start,
4039                len,
4040                payload,
4041            } => {
4042                let frag_start = *start as usize;
4043                let frag_end = frag_start.saturating_add(*len as usize);
4044                let inter_start = frag_start.max(range.start);
4045                let inter_end = frag_end.min(range.end);
4046                if inter_start >= inter_end {
4047                    return;
4048                }
4049                for abs in inter_start..inter_end {
4050                    f(abs - range.start, payload, abs - frag_start);
4051                }
4052            }
4053            OverlayFragment::RunRange {
4054                start,
4055                len,
4056                run_ends,
4057                payload,
4058            } => {
4059                let frag_start = *start as usize;
4060                let frag_end = frag_start.saturating_add(*len as usize);
4061                let inter_start = frag_start.max(range.start);
4062                let inter_end = frag_end.min(range.end);
4063                if inter_start >= inter_end {
4064                    return;
4065                }
4066                let first = run_ends.run_at(inter_start - frag_start);
4067                let mut prev_end = if first == 0 {
4068                    0
4069                } else {
4070                    run_ends.end(first - 1)
4071                };
4072                for run_idx in first..run_ends.count() {
4073                    let run_end = run_ends.end(run_idx);
4074                    let run_start_abs = frag_start.saturating_add(prev_end);
4075                    let run_end_abs = frag_start.saturating_add(run_end);
4076                    let start_abs = run_start_abs.max(inter_start);
4077                    let end_abs = run_end_abs.min(inter_end);
4078                    if start_abs < end_abs {
4079                        for abs in start_abs..end_abs {
4080                            f(abs - range.start, payload, run_idx);
4081                        }
4082                    }
4083                    prev_end = run_end;
4084                    if run_end_abs >= inter_end {
4085                        break;
4086                    }
4087                }
4088            }
4089        }
4090    }
4091
4092    fn for_each_run_payload_index(
4093        fragment: &OverlayFragment,
4094        range: core::ops::Range<usize>,
4095        mut f: impl FnMut(&OverlayFragmentPayload, usize, usize),
4096    ) {
4097        let OverlayFragment::RunRange {
4098            start,
4099            len,
4100            run_ends,
4101            payload,
4102        } = fragment
4103        else {
4104            unreachable!("run payload iteration requires RunRange")
4105        };
4106        let frag_start = *start as usize;
4107        let frag_end = frag_start.saturating_add(*len as usize);
4108        let inter_start = frag_start.max(range.start);
4109        let inter_end = frag_end.min(range.end);
4110        if inter_start >= inter_end {
4111            return;
4112        }
4113        let first = run_ends.run_at(inter_start - frag_start);
4114        let mut prev_end = if first == 0 {
4115            0
4116        } else {
4117            run_ends.end(first - 1)
4118        };
4119        for run_idx in first..run_ends.count() {
4120            let run_end = run_ends.end(run_idx);
4121            let run_start_abs = frag_start.saturating_add(prev_end);
4122            let run_end_abs = frag_start.saturating_add(run_end);
4123            let start_abs = run_start_abs.max(inter_start);
4124            let end_abs = run_end_abs.min(inter_end);
4125            if start_abs < end_abs {
4126                f(payload, run_idx, end_abs - start_abs);
4127            }
4128            prev_end = run_end;
4129            if run_end_abs >= inter_end {
4130                break;
4131            }
4132        }
4133    }
4134}
4135
4136impl OverlayFragmentPayload {
4137    #[inline]
4138    fn type_tag_at(&self, idx: usize) -> Option<TypeTag> {
4139        if idx >= self.type_tags.len() || self.type_tags.is_null(idx) {
4140            return None;
4141        }
4142        Some(TypeTag::from_u8(self.type_tags.value(idx)))
4143    }
4144}
4145
4146impl Overlay {
4147    fn full_cover_dense_fragment(
4148        &self,
4149        range: core::ops::Range<usize>,
4150    ) -> Option<&OverlayFragment> {
4151        self.full_cover_single_fragment(range, OverlayFragmentShape::Dense)
4152    }
4153
4154    fn full_cover_run_fragment(&self, range: core::ops::Range<usize>) -> Option<&OverlayFragment> {
4155        self.full_cover_single_fragment(range, OverlayFragmentShape::Run)
4156    }
4157
4158    fn full_cover_single_fragment(
4159        &self,
4160        range: core::ops::Range<usize>,
4161        shape: OverlayFragmentShape,
4162    ) -> Option<&OverlayFragment> {
4163        if range.is_empty() || self.points.keys().any(|off| range.contains(off)) {
4164            return None;
4165        }
4166        let mut found = None;
4167        for fragment in &self.fragments {
4168            if !fragment.has_any_in_range(range.clone()) {
4169                continue;
4170            }
4171            let shape_matches = matches!(
4172                (shape, fragment),
4173                (
4174                    OverlayFragmentShape::Dense,
4175                    OverlayFragment::DenseRange { .. }
4176                ) | (OverlayFragmentShape::Run, OverlayFragment::RunRange { .. })
4177            );
4178            let covers = fragment
4179                .interval_coverage()
4180                .is_some_and(|own| own.start <= range.start && range.end <= own.end);
4181            if shape_matches && covers && found.is_none() {
4182                found = Some(fragment);
4183            } else {
4184                return None;
4185            }
4186        }
4187        found
4188    }
4189}
4190fn append_overlay_value_to_lane_builders(
4191    ov: &OverlayValue,
4192    tag_b: &mut UInt8Builder,
4193    nb: &mut Float64Builder,
4194    bb: &mut BooleanBuilder,
4195    sb: &mut StringBuilder,
4196    eb: &mut UInt8Builder,
4197    non_num: &mut usize,
4198    non_bool: &mut usize,
4199    non_text: &mut usize,
4200    non_err: &mut usize,
4201) {
4202    match ov {
4203        OverlayValue::Empty => {
4204            tag_b.append_value(TypeTag::Empty as u8);
4205            nb.append_null();
4206            bb.append_null();
4207            sb.append_null();
4208            eb.append_null();
4209        }
4210        OverlayValue::Number(n) => {
4211            tag_b.append_value(TypeTag::Number as u8);
4212            nb.append_value(*n);
4213            *non_num += 1;
4214            bb.append_null();
4215            sb.append_null();
4216            eb.append_null();
4217        }
4218        OverlayValue::DateTime(serial) => {
4219            tag_b.append_value(TypeTag::DateTime as u8);
4220            nb.append_value(*serial);
4221            *non_num += 1;
4222            bb.append_null();
4223            sb.append_null();
4224            eb.append_null();
4225        }
4226        OverlayValue::Duration(serial) => {
4227            tag_b.append_value(TypeTag::Duration as u8);
4228            nb.append_value(*serial);
4229            *non_num += 1;
4230            bb.append_null();
4231            sb.append_null();
4232            eb.append_null();
4233        }
4234        OverlayValue::Boolean(b) => {
4235            tag_b.append_value(TypeTag::Boolean as u8);
4236            nb.append_null();
4237            bb.append_value(*b);
4238            *non_bool += 1;
4239            sb.append_null();
4240            eb.append_null();
4241        }
4242        OverlayValue::Text(s) => {
4243            tag_b.append_value(TypeTag::Text as u8);
4244            nb.append_null();
4245            bb.append_null();
4246            sb.append_value(s);
4247            *non_text += 1;
4248            eb.append_null();
4249        }
4250        OverlayValue::Error(code) => {
4251            tag_b.append_value(TypeTag::Error as u8);
4252            nb.append_null();
4253            bb.append_null();
4254            sb.append_null();
4255            eb.append_value(*code);
4256            *non_err += 1;
4257        }
4258        OverlayValue::Pending => {
4259            tag_b.append_value(TypeTag::Pending as u8);
4260            nb.append_null();
4261            bb.append_null();
4262            sb.append_null();
4263            eb.append_null();
4264        }
4265    }
4266}
4267
4268impl ArrowSheet {
4269    /// Create a logical sheet whose cells are initially implicit empty values.
4270    ///
4271    /// Columns start with no materialized chunks; callers can populate only touched
4272    /// column/chunk pairs via `set_sparse_overlay_value`. Missing chunks remain
4273    /// observable as empty cells through scalar and range reads.
4274    pub fn new_sparse(sheet_name: &str, ncols: usize, nrows: usize, chunk_rows: usize) -> Self {
4275        Self::new_sparse_with_date_system(
4276            sheet_name,
4277            ncols,
4278            nrows,
4279            chunk_rows,
4280            crate::engine::DateSystem::Excel1900,
4281        )
4282    }
4283
4284    /// Create a sparse sheet using an explicitly selected workbook date system.
4285    pub fn new_sparse_with_date_system(
4286        sheet_name: &str,
4287        ncols: usize,
4288        nrows: usize,
4289        chunk_rows: usize,
4290        date_system: crate::engine::DateSystem,
4291    ) -> Self {
4292        let chunk_rows = chunk_rows.max(1);
4293        let columns = (0..ncols)
4294            .map(|idx| ArrowColumn {
4295                chunks: Vec::new(),
4296                sparse_chunks: FxHashMap::default(),
4297                index: idx as u32,
4298            })
4299            .collect();
4300        let mut sheet = Self {
4301            name: Arc::from(sheet_name.to_string()),
4302            date_system,
4303            columns,
4304            nrows: 0,
4305            chunk_starts: Vec::new(),
4306            chunk_rows,
4307        };
4308        sheet.ensure_row_capacity(nrows);
4309        sheet
4310    }
4311
4312    /// Populate a single sparse cell using the overlay cascade while preserving
4313    /// implicit-empty chunks elsewhere. This is intended for sparse initial ingest,
4314    /// not for user edits; no graph/changelog side effects are triggered here.
4315    pub fn set_sparse_overlay_value(
4316        &mut self,
4317        abs_row: usize,
4318        abs_col: usize,
4319        value: OverlayValue,
4320    ) -> isize {
4321        if abs_col >= self.columns.len() {
4322            let start = self.columns.len();
4323            self.columns
4324                .extend((start..=abs_col).map(|idx| ArrowColumn {
4325                    chunks: Vec::new(),
4326                    sparse_chunks: FxHashMap::default(),
4327                    index: idx as u32,
4328                }));
4329        }
4330        if abs_row >= self.nrows as usize {
4331            self.ensure_row_capacity(abs_row + 1);
4332        }
4333        let Some((ch_idx, in_off)) = self.chunk_of_row(abs_row) else {
4334            return 0;
4335        };
4336        let Some(ch) = self.ensure_column_chunk_mut(abs_col, ch_idx) else {
4337            return 0;
4338        };
4339        ch.overlay.set(in_off, value)
4340    }
4341
4342    pub fn set_sparse_overlay_format(
4343        &mut self,
4344        abs_row: usize,
4345        abs_col: usize,
4346        format: Option<FormatId>,
4347    ) {
4348        if abs_row >= self.nrows as usize || abs_col >= self.columns.len() {
4349            return;
4350        }
4351        let Some((ch_idx, in_off)) = self.chunk_of_row(abs_row) else {
4352            return;
4353        };
4354        if let Some(ch) = self.ensure_column_chunk_mut(abs_col, ch_idx) {
4355            ch.overlay.set_format(in_off, format);
4356        }
4357    }
4358
4359    /// Clear every explicit and computed format source at a grid position.
4360    pub(crate) fn clear_format(&mut self, abs_row: usize, abs_col: usize) {
4361        if abs_row >= self.nrows as usize || abs_col >= self.columns.len() {
4362            return;
4363        }
4364        let Some((ch_idx, in_off)) = self.chunk_of_row(abs_row) else {
4365            return;
4366        };
4367        let Some(ch) = self.ensure_column_chunk_mut(abs_col, ch_idx) else {
4368            return;
4369        };
4370        ch.overlay.set_format(in_off, None);
4371        ch.computed_overlay.set_format(in_off, None);
4372        if let Some(runs) = &ch.format {
4373            let mut ids = runs.to_ids(ch.len());
4374            ids[in_off] = FormatId::GENERAL.0;
4375            ch.format = FormatRuns::from_ids(&ids);
4376        }
4377    }
4378
4379    /// Return a summary of each column's chunk counts, total rows, and lane presence.
4380    pub fn shape(&self) -> Vec<ColumnShape> {
4381        self.columns
4382            .iter()
4383            .map(|c| {
4384                let chunks = c.chunks.len();
4385                let rows = self.nrows as usize;
4386                let has_num = c.chunks.iter().any(|ch| ch.meta.non_null_num > 0);
4387                let has_bool = c.chunks.iter().any(|ch| ch.meta.non_null_bool > 0);
4388                let has_text = c.chunks.iter().any(|ch| ch.meta.non_null_text > 0);
4389                let has_err = c.chunks.iter().any(|ch| ch.meta.non_null_err > 0);
4390                ColumnShape {
4391                    index: c.index,
4392                    chunks,
4393                    rows,
4394                    has_num,
4395                    has_bool,
4396                    has_text,
4397                    has_err,
4398                }
4399            })
4400            .collect()
4401    }
4402
4403    pub fn range_view(
4404        &self,
4405        sr: usize,
4406        sc: usize,
4407        er: usize,
4408        ec: usize,
4409    ) -> crate::engine::range_view::RangeView<'_> {
4410        let r0 = er.checked_sub(sr).map(|d| d + 1).unwrap_or(0);
4411        let c0 = ec.checked_sub(sc).map(|d| d + 1).unwrap_or(0);
4412        let (rows, cols) = if r0 == 0 || c0 == 0 { (0, 0) } else { (r0, c0) };
4413        crate::engine::range_view::RangeView::new(
4414            crate::engine::range_view::RangeBacking::Borrowed(self),
4415            sr,
4416            sc,
4417            er,
4418            ec,
4419            rows,
4420            cols,
4421        )
4422    }
4423
4424    pub(crate) fn has_formats(&self) -> bool {
4425        self.columns.iter().any(|column| {
4426            column
4427                .chunks
4428                .iter()
4429                .chain(column.sparse_chunks.values())
4430                .any(|chunk| {
4431                    chunk.format.is_some()
4432                        || chunk.overlay.has_formats()
4433                        || chunk.computed_overlay.has_formats()
4434                })
4435        })
4436    }
4437
4438    /// Return the effective explicit/derived format for a cell.
4439    pub fn format_id(&self, abs_row: usize, abs_col: usize) -> Option<FormatId> {
4440        let (ch_idx, in_off) = self.chunk_of_row(abs_row)?;
4441        let ch = self.columns.get(abs_col)?.chunk(ch_idx)?;
4442        ch.overlay
4443            .get_format(in_off)
4444            .or_else(|| {
4445                ch.format
4446                    .as_ref()
4447                    .map(|runs| runs.get(in_off))
4448                    .filter(|id| *id != FormatId::GENERAL)
4449            })
4450            .or_else(|| ch.computed_overlay.get_format(in_off))
4451            .filter(|id| *id != FormatId::GENERAL)
4452    }
4453
4454    /// Fast single-cell read (0-based row/col) with overlay precedence.
4455    ///
4456    /// This avoids constructing a 1x1 RangeView and is intended for tight read loops.
4457    #[inline]
4458    pub fn get_cell_value(&self, abs_row: usize, abs_col: usize) -> LiteralValue {
4459        let sheet_rows = self.nrows as usize;
4460        if abs_row >= sheet_rows {
4461            return LiteralValue::Empty;
4462        }
4463        if abs_col >= self.columns.len() {
4464            return LiteralValue::Empty;
4465        }
4466        let Some((ch_idx, in_off)) = self.chunk_of_row(abs_row) else {
4467            return LiteralValue::Empty;
4468        };
4469        let col_ref = &self.columns[abs_col];
4470        let Some(ch) = col_ref.chunk(ch_idx) else {
4471            return LiteralValue::Empty;
4472        };
4473
4474        // Overlay takes precedence: user edits over computed over base.
4475        let cascade = OverlayCascade::new(&ch.overlay, &ch.computed_overlay);
4476        if let Some(ov) = cascade.get_scalar(in_off) {
4477            return ov.to_literal_for(self.date_system);
4478        }
4479
4480        // Read tag and route to lane.
4481        let tag_u8 = ch.type_tag.value(in_off);
4482        match TypeTag::from_u8(tag_u8) {
4483            TypeTag::Empty => LiteralValue::Empty,
4484            TypeTag::Number => {
4485                if let Some(arr) = &ch.numbers {
4486                    if arr.is_null(in_off) {
4487                        return LiteralValue::Empty;
4488                    }
4489                    LiteralValue::Number(arr.value(in_off))
4490                } else {
4491                    LiteralValue::Empty
4492                }
4493            }
4494            TypeTag::DateTime | TypeTag::Duration => {
4495                if let Some(arr) = &ch.numbers {
4496                    if arr.is_null(in_off) {
4497                        LiteralValue::Empty
4498                    } else {
4499                        LiteralValue::Number(arr.value(in_off))
4500                    }
4501                } else {
4502                    LiteralValue::Empty
4503                }
4504            }
4505            TypeTag::Boolean => {
4506                if let Some(arr) = &ch.booleans {
4507                    if arr.is_null(in_off) {
4508                        return LiteralValue::Empty;
4509                    }
4510                    LiteralValue::Boolean(arr.value(in_off))
4511                } else {
4512                    LiteralValue::Empty
4513                }
4514            }
4515            TypeTag::Text => {
4516                if let Some(arr) = &ch.text {
4517                    if arr.is_null(in_off) {
4518                        return LiteralValue::Empty;
4519                    }
4520                    let sa = arr
4521                        .as_any()
4522                        .downcast_ref::<arrow_array::StringArray>()
4523                        .unwrap();
4524                    LiteralValue::Text(sa.value(in_off).to_string())
4525                } else {
4526                    LiteralValue::Empty
4527                }
4528            }
4529            TypeTag::Error => {
4530                if let Some(arr) = &ch.errors {
4531                    if arr.is_null(in_off) {
4532                        return LiteralValue::Empty;
4533                    }
4534                    let kind = unmap_error_code(arr.value(in_off));
4535                    LiteralValue::Error(ExcelError::new(kind))
4536                } else {
4537                    LiteralValue::Empty
4538                }
4539            }
4540            TypeTag::Pending => LiteralValue::Pending,
4541        }
4542    }
4543
4544    /// Ensure capacity to address at least `target_rows` rows by extending the row chunk map.
4545    ///
4546    /// This updates `chunk_starts`/`nrows` but does **not** eagerly densify all columns with
4547    /// new empty chunks. Missing chunks are treated as all-empty and can be materialized lazily.
4548    pub fn ensure_row_capacity(&mut self, target_rows: usize) {
4549        if target_rows as u32 <= self.nrows {
4550            return;
4551        }
4552
4553        let chunk_size = self.chunk_rows.max(1);
4554
4555        // `chunk_starts` must represent fixed-size chunk boundaries based on `chunk_rows`, not
4556        // incremental growth steps. In particular, repeated calls like ensure_row_capacity(1),
4557        // ensure_row_capacity(2), ... must NOT create a new chunk per row.
4558        if self.chunk_starts.is_empty() {
4559            self.chunk_starts.push(0);
4560        }
4561
4562        // Extend chunk starts only when `target_rows` crosses a chunk boundary.
4563        // Example: chunk_size=3, target_rows=6 => chunk_starts=[0,3]
4564        let mut next_start = self
4565            .chunk_starts
4566            .last()
4567            .copied()
4568            .unwrap_or(0)
4569            .saturating_add(chunk_size);
4570        while next_start < target_rows {
4571            self.chunk_starts.push(next_start);
4572            next_start = next_start.saturating_add(chunk_size);
4573        }
4574
4575        self.nrows = target_rows as u32;
4576
4577        // Any previously-materialized chunk may have been created when the sheet had fewer rows.
4578        // When `chunk_starts` extends, chunks that used to be "last" can become interior chunks
4579        // with a larger fixed boundary. Ensure materialized chunks are grown to their current
4580        // boundary-derived length so RangeView slicing stays in-bounds.
4581        let starts = self.chunk_starts.clone();
4582        let nrows = self.nrows as usize;
4583        let required_len_for = |ch_idx: usize| -> Option<usize> {
4584            let start = *starts.get(ch_idx)?;
4585            let end = starts.get(ch_idx + 1).copied().unwrap_or(nrows);
4586            Some(end.saturating_sub(start))
4587        };
4588
4589        for col in &mut self.columns {
4590            for (idx, ch) in col.chunks.iter_mut().enumerate() {
4591                if let Some(req) = required_len_for(idx) {
4592                    ch.grow_len_to(req);
4593                }
4594            }
4595            if !col.sparse_chunks.is_empty() {
4596                let keys: Vec<usize> = col.sparse_chunks.keys().copied().collect();
4597                for idx in keys {
4598                    if let (Some(req), Some(ch)) =
4599                        (required_len_for(idx), col.sparse_chunks.get_mut(&idx))
4600                    {
4601                        ch.grow_len_to(req);
4602                    }
4603                }
4604            }
4605        }
4606    }
4607
4608    /// Ensure a mutable chunk for a given column/chunk index.
4609    ///
4610    /// If the chunk is beyond the column's dense chunk vector, it is stored in `sparse_chunks`.
4611    pub fn ensure_column_chunk_mut(
4612        &mut self,
4613        col_idx: usize,
4614        ch_idx: usize,
4615    ) -> Option<&mut ColumnChunk> {
4616        let start = *self.chunk_starts.get(ch_idx)?;
4617        let end = self
4618            .chunk_starts
4619            .get(ch_idx + 1)
4620            .copied()
4621            .unwrap_or(self.nrows as usize);
4622        let len = end.saturating_sub(start);
4623
4624        let col = self.columns.get_mut(col_idx)?;
4625        if ch_idx < col.chunks.len() {
4626            return Some(&mut col.chunks[ch_idx]);
4627        }
4628        Some(
4629            col.sparse_chunks
4630                .entry(ch_idx)
4631                .or_insert_with(|| Self::make_empty_chunk(len)),
4632        )
4633    }
4634
4635    /// Return (chunk_idx, in_chunk_offset) for absolute 0-based row.
4636    pub fn chunk_of_row(&self, abs_row: usize) -> Option<(usize, usize)> {
4637        if abs_row >= self.nrows as usize {
4638            return None;
4639        }
4640        let ch_idx = match self.chunk_starts.binary_search(&abs_row) {
4641            Ok(i) => i,
4642            Err(0) => 0,
4643            Err(i) => i - 1,
4644        };
4645        let start = self.chunk_starts[ch_idx];
4646        Some((ch_idx, abs_row - start))
4647    }
4648
4649    fn recompute_chunk_starts(&mut self) {
4650        self.chunk_starts.clear();
4651        if let Some(col0) = self.columns.first() {
4652            let mut cur = 0usize;
4653            for ch in &col0.chunks {
4654                self.chunk_starts.push(cur);
4655                cur += ch.type_tag.len();
4656            }
4657        }
4658    }
4659
4660    fn make_empty_chunk(len: usize) -> ColumnChunk {
4661        ColumnChunk {
4662            numbers: None,
4663            booleans: None,
4664            text: None,
4665            errors: None,
4666            type_tag: Arc::new(UInt8Array::from(vec![TypeTag::Empty as u8; len])),
4667            formula_id: None,
4668            format: None,
4669            meta: ColumnChunkMeta {
4670                len,
4671                non_null_num: 0,
4672                non_null_bool: 0,
4673                non_null_text: 0,
4674                non_null_err: 0,
4675            },
4676            lazy_null_numbers: OnceCell::new(),
4677            lazy_null_booleans: OnceCell::new(),
4678            lazy_null_text: OnceCell::new(),
4679            lazy_null_errors: OnceCell::new(),
4680            lowered_text: OnceCell::new(),
4681            merged: MergedLaneCache::default(),
4682            overlay: Overlay::new(),
4683            computed_overlay: Overlay::new(),
4684        }
4685    }
4686
4687    fn slice_chunk(ch: &ColumnChunk, off: usize, len: usize) -> ColumnChunk {
4688        // Slice type tags
4689        use arrow_array::Array;
4690        let type_tag: Arc<UInt8Array> = Arc::new(
4691            Array::slice(ch.type_tag.as_ref(), off, len)
4692                .as_any()
4693                .downcast_ref::<UInt8Array>()
4694                .unwrap()
4695                .clone(),
4696        );
4697        // Slice numbers if present and keep only if any non-null
4698        let numbers: Option<Arc<Float64Array>> = ch.numbers.as_ref().and_then(|a| {
4699            let sl = Array::slice(a.as_ref(), off, len);
4700            let fa = sl.as_any().downcast_ref::<Float64Array>().unwrap().clone();
4701            let nn = len.saturating_sub(fa.null_count());
4702            if nn == 0 { None } else { Some(Arc::new(fa)) }
4703        });
4704        let booleans: Option<Arc<BooleanArray>> = ch.booleans.as_ref().and_then(|a| {
4705            let sl = Array::slice(a.as_ref(), off, len);
4706            let ba = sl.as_any().downcast_ref::<BooleanArray>().unwrap().clone();
4707            let nn = len.saturating_sub(ba.null_count());
4708            if nn == 0 { None } else { Some(Arc::new(ba)) }
4709        });
4710        let text: Option<ArrayRef> = ch.text.as_ref().and_then(|a| {
4711            let sl = Array::slice(a.as_ref(), off, len);
4712            let sa = sl.as_any().downcast_ref::<StringArray>().unwrap().clone();
4713            let nn = len.saturating_sub(sa.null_count());
4714            if nn == 0 {
4715                None
4716            } else {
4717                Some(Arc::new(sa) as ArrayRef)
4718            }
4719        });
4720        let errors: Option<Arc<UInt8Array>> = ch.errors.as_ref().and_then(|a| {
4721            let sl = Array::slice(a.as_ref(), off, len);
4722            let ea = sl.as_any().downcast_ref::<UInt8Array>().unwrap().clone();
4723            let nn = len.saturating_sub(ea.null_count());
4724            if nn == 0 { None } else { Some(Arc::new(ea)) }
4725        });
4726        // Split overlays for this slice.
4727        let overlay = ch.overlay.slice(off, len);
4728        let computed_overlay = ch.computed_overlay.slice(off, len);
4729        let non_null_num = numbers.as_ref().map(|a| len - a.null_count()).unwrap_or(0);
4730        let non_null_bool = booleans.as_ref().map(|a| len - a.null_count()).unwrap_or(0);
4731        let non_null_text = text.as_ref().map(|a| len - a.null_count()).unwrap_or(0);
4732        let non_null_err = errors.as_ref().map(|a| len - a.null_count()).unwrap_or(0);
4733        ColumnChunk {
4734            numbers: numbers.clone(),
4735            booleans: booleans.clone(),
4736            text: text.clone(),
4737            errors: errors.clone(),
4738            type_tag,
4739            formula_id: None,
4740            format: ch.format.as_ref().and_then(|runs| runs.slice(off, len)),
4741            meta: ColumnChunkMeta {
4742                len,
4743                non_null_num,
4744                non_null_bool,
4745                non_null_text,
4746                non_null_err,
4747            },
4748            lazy_null_numbers: OnceCell::new(),
4749            lazy_null_booleans: OnceCell::new(),
4750            lazy_null_text: OnceCell::new(),
4751            lazy_null_errors: OnceCell::new(),
4752            lowered_text: OnceCell::new(),
4753            merged: MergedLaneCache::default(),
4754            overlay,
4755            computed_overlay,
4756        }
4757    }
4758
4759    /// Heuristic compaction: rebuilds a chunk's base arrays by applying its overlay when
4760    /// overlay density crosses thresholds. Returns true if a rebuild occurred.
4761    pub fn maybe_compact_chunk(
4762        &mut self,
4763        col_idx: usize,
4764        ch_idx: usize,
4765        abs_threshold: usize,
4766        frac_den: usize,
4767    ) -> usize {
4768        if col_idx >= self.columns.len() {
4769            return 0;
4770        }
4771
4772        let (len, tags, numbers, booleans, text, errors, non_num, non_bool, non_text, non_err) = {
4773            let Some(ch_ref) = self.columns[col_idx].chunk(ch_idx) else {
4774                return 0;
4775            };
4776            let len = ch_ref.type_tag.len();
4777            if len == 0 {
4778                return 0;
4779            }
4780
4781            let ov_len = ch_ref.overlay.len();
4782            let den = frac_den.max(1);
4783            let trig = ov_len > (len / den) || ov_len > abs_threshold;
4784            if !trig {
4785                return 0;
4786            }
4787
4788            // Rebuild: merge base lanes with overlays row-by-row.
4789            let mut tag_b = UInt8Builder::with_capacity(len);
4790            let mut nb = Float64Builder::with_capacity(len);
4791            let mut bb = BooleanBuilder::with_capacity(len);
4792            let mut sb = StringBuilder::with_capacity(len, len * 8);
4793            let mut eb = UInt8Builder::with_capacity(len);
4794            let mut non_num = 0usize;
4795            let mut non_bool = 0usize;
4796            let mut non_text = 0usize;
4797            let mut non_err = 0usize;
4798
4799            for i in 0..len {
4800                // If overlay present, use it. Otherwise, use base tag+lane.
4801                if let Some(ov) = ch_ref.overlay.get_scalar(i) {
4802                    let ov = ov.to_overlay_value();
4803                    append_overlay_value_to_lane_builders(
4804                        &ov,
4805                        &mut tag_b,
4806                        &mut nb,
4807                        &mut bb,
4808                        &mut sb,
4809                        &mut eb,
4810                        &mut non_num,
4811                        &mut non_bool,
4812                        &mut non_text,
4813                        &mut non_err,
4814                    );
4815                } else {
4816                    let tag = TypeTag::from_u8(ch_ref.type_tag.value(i));
4817                    match tag {
4818                        TypeTag::Empty => {
4819                            tag_b.append_value(TypeTag::Empty as u8);
4820                            nb.append_null();
4821                            bb.append_null();
4822                            sb.append_null();
4823                            eb.append_null();
4824                        }
4825                        TypeTag::Number | TypeTag::DateTime | TypeTag::Duration => {
4826                            tag_b.append_value(tag as u8);
4827                            if let Some(a) = &ch_ref.numbers {
4828                                let fa = a.as_any().downcast_ref::<Float64Array>().unwrap();
4829                                if fa.is_null(i) {
4830                                    nb.append_null();
4831                                } else {
4832                                    nb.append_value(fa.value(i));
4833                                    non_num += 1;
4834                                }
4835                            } else {
4836                                nb.append_null();
4837                            }
4838                            bb.append_null();
4839                            sb.append_null();
4840                            eb.append_null();
4841                        }
4842                        TypeTag::Boolean => {
4843                            tag_b.append_value(TypeTag::Boolean as u8);
4844                            nb.append_null();
4845                            if let Some(a) = &ch_ref.booleans {
4846                                let ba = a.as_any().downcast_ref::<BooleanArray>().unwrap();
4847                                if ba.is_null(i) {
4848                                    bb.append_null();
4849                                } else {
4850                                    bb.append_value(ba.value(i));
4851                                    non_bool += 1;
4852                                }
4853                            } else {
4854                                bb.append_null();
4855                            }
4856                            sb.append_null();
4857                            eb.append_null();
4858                        }
4859                        TypeTag::Text => {
4860                            tag_b.append_value(TypeTag::Text as u8);
4861                            nb.append_null();
4862                            bb.append_null();
4863                            if let Some(a) = &ch_ref.text {
4864                                let sa = a.as_any().downcast_ref::<StringArray>().unwrap();
4865                                if sa.is_null(i) {
4866                                    sb.append_null();
4867                                } else {
4868                                    sb.append_value(sa.value(i));
4869                                    non_text += 1;
4870                                }
4871                            } else {
4872                                sb.append_null();
4873                            }
4874                            eb.append_null();
4875                        }
4876                        TypeTag::Error => {
4877                            tag_b.append_value(TypeTag::Error as u8);
4878                            nb.append_null();
4879                            bb.append_null();
4880                            sb.append_null();
4881                            if let Some(a) = &ch_ref.errors {
4882                                let ea = a.as_any().downcast_ref::<UInt8Array>().unwrap();
4883                                if ea.is_null(i) {
4884                                    eb.append_null();
4885                                } else {
4886                                    eb.append_value(ea.value(i));
4887                                    non_err += 1;
4888                                }
4889                            } else {
4890                                eb.append_null();
4891                            }
4892                        }
4893                        TypeTag::Pending => {
4894                            tag_b.append_value(TypeTag::Pending as u8);
4895                            nb.append_null();
4896                            bb.append_null();
4897                            sb.append_null();
4898                            eb.append_null();
4899                        }
4900                    }
4901                }
4902            }
4903
4904            let tags = Arc::new(tag_b.finish());
4905            let numbers = {
4906                let a = nb.finish();
4907                if non_num == 0 {
4908                    None
4909                } else {
4910                    Some(Arc::new(a))
4911                }
4912            };
4913            let booleans = {
4914                let a = bb.finish();
4915                if non_bool == 0 {
4916                    None
4917                } else {
4918                    Some(Arc::new(a))
4919                }
4920            };
4921            let text = {
4922                let a = sb.finish();
4923                if non_text == 0 {
4924                    None
4925                } else {
4926                    Some(Arc::new(a) as ArrayRef)
4927                }
4928            };
4929            let errors = {
4930                let a = eb.finish();
4931                if non_err == 0 {
4932                    None
4933                } else {
4934                    Some(Arc::new(a))
4935                }
4936            };
4937
4938            (
4939                len, tags, numbers, booleans, text, errors, non_num, non_bool, non_text, non_err,
4940            )
4941        };
4942
4943        let Some(ch_mut) = self.columns[col_idx].chunk_mut(ch_idx) else {
4944            return 0;
4945        };
4946
4947        ch_mut.type_tag = tags;
4948        ch_mut.numbers = numbers;
4949        ch_mut.booleans = booleans;
4950        ch_mut.text = text;
4951        ch_mut.errors = errors;
4952        let freed = ch_mut.overlay.clear();
4953        ch_mut.lowered_text = OnceCell::new();
4954        ch_mut.meta.len = len;
4955        ch_mut.meta.non_null_num = non_num;
4956        ch_mut.meta.non_null_bool = non_bool;
4957        ch_mut.meta.non_null_text = non_text;
4958        ch_mut.meta.non_null_err = non_err;
4959        freed
4960    }
4961
4962    /// Compact a dense chunk's computed overlay into its base arrays, freeing overlay memory
4963    /// while preserving the data. Returns the number of bytes freed.
4964    ///
4965    /// This is the computed-overlay counterpart of `maybe_compact_chunk` (which compacts
4966    /// user-edit overlays). The read cascade is `overlay → computed_overlay → base`, so
4967    /// folding computed overlay entries into base arrays is transparent: the `overlay` layer
4968    /// (user edits) is left untouched and still takes precedence on reads.
4969    pub fn compact_computed_overlay_chunk(&mut self, col_idx: usize, ch_idx: usize) -> usize {
4970        if col_idx >= self.columns.len() {
4971            return 0;
4972        }
4973
4974        let (len, tags, numbers, booleans, text, errors, non_num, non_bool, non_text, non_err) = {
4975            let Some(ch_ref) = self.columns[col_idx].chunk(ch_idx) else {
4976                return 0;
4977            };
4978            let len = ch_ref.type_tag.len();
4979            if len == 0 || ch_ref.computed_overlay.is_empty() {
4980                return 0;
4981            }
4982
4983            let mut tag_b = UInt8Builder::with_capacity(len);
4984            let mut nb = Float64Builder::with_capacity(len);
4985            let mut bb = BooleanBuilder::with_capacity(len);
4986            let mut sb = StringBuilder::with_capacity(len, len * 8);
4987            let mut eb = UInt8Builder::with_capacity(len);
4988            let mut non_num = 0usize;
4989            let mut non_bool = 0usize;
4990            let mut non_text = 0usize;
4991            let mut non_err = 0usize;
4992
4993            for i in 0..len {
4994                if let Some(ov) = ch_ref.computed_overlay.get_scalar(i) {
4995                    let ov = ov.to_overlay_value();
4996                    append_overlay_value_to_lane_builders(
4997                        &ov,
4998                        &mut tag_b,
4999                        &mut nb,
5000                        &mut bb,
5001                        &mut sb,
5002                        &mut eb,
5003                        &mut non_num,
5004                        &mut non_bool,
5005                        &mut non_text,
5006                        &mut non_err,
5007                    );
5008                } else {
5009                    let tag = TypeTag::from_u8(ch_ref.type_tag.value(i));
5010                    match tag {
5011                        TypeTag::Empty => {
5012                            tag_b.append_value(TypeTag::Empty as u8);
5013                            nb.append_null();
5014                            bb.append_null();
5015                            sb.append_null();
5016                            eb.append_null();
5017                        }
5018                        TypeTag::Number | TypeTag::DateTime | TypeTag::Duration => {
5019                            tag_b.append_value(tag as u8);
5020                            if let Some(a) = &ch_ref.numbers {
5021                                let fa = a.as_any().downcast_ref::<Float64Array>().unwrap();
5022                                if fa.is_null(i) {
5023                                    nb.append_null();
5024                                } else {
5025                                    nb.append_value(fa.value(i));
5026                                    non_num += 1;
5027                                }
5028                            } else {
5029                                nb.append_null();
5030                            }
5031                            bb.append_null();
5032                            sb.append_null();
5033                            eb.append_null();
5034                        }
5035                        TypeTag::Boolean => {
5036                            tag_b.append_value(TypeTag::Boolean as u8);
5037                            nb.append_null();
5038                            if let Some(a) = &ch_ref.booleans {
5039                                let ba = a.as_any().downcast_ref::<BooleanArray>().unwrap();
5040                                if ba.is_null(i) {
5041                                    bb.append_null();
5042                                } else {
5043                                    bb.append_value(ba.value(i));
5044                                    non_bool += 1;
5045                                }
5046                            } else {
5047                                bb.append_null();
5048                            }
5049                            sb.append_null();
5050                            eb.append_null();
5051                        }
5052                        TypeTag::Text => {
5053                            tag_b.append_value(TypeTag::Text as u8);
5054                            nb.append_null();
5055                            bb.append_null();
5056                            if let Some(a) = &ch_ref.text {
5057                                let sa = a.as_any().downcast_ref::<StringArray>().unwrap();
5058                                if sa.is_null(i) {
5059                                    sb.append_null();
5060                                } else {
5061                                    sb.append_value(sa.value(i));
5062                                    non_text += 1;
5063                                }
5064                            } else {
5065                                sb.append_null();
5066                            }
5067                            eb.append_null();
5068                        }
5069                        TypeTag::Error => {
5070                            tag_b.append_value(TypeTag::Error as u8);
5071                            nb.append_null();
5072                            bb.append_null();
5073                            sb.append_null();
5074                            if let Some(a) = &ch_ref.errors {
5075                                let ea = a.as_any().downcast_ref::<UInt8Array>().unwrap();
5076                                if ea.is_null(i) {
5077                                    eb.append_null();
5078                                } else {
5079                                    eb.append_value(ea.value(i));
5080                                    non_err += 1;
5081                                }
5082                            } else {
5083                                eb.append_null();
5084                            }
5085                        }
5086                        TypeTag::Pending => {
5087                            tag_b.append_value(TypeTag::Pending as u8);
5088                            nb.append_null();
5089                            bb.append_null();
5090                            sb.append_null();
5091                            eb.append_null();
5092                        }
5093                    }
5094                }
5095            }
5096
5097            let tags = Arc::new(tag_b.finish());
5098            let numbers = {
5099                let a = nb.finish();
5100                if non_num == 0 {
5101                    None
5102                } else {
5103                    Some(Arc::new(a))
5104                }
5105            };
5106            let booleans = {
5107                let a = bb.finish();
5108                if non_bool == 0 {
5109                    None
5110                } else {
5111                    Some(Arc::new(a))
5112                }
5113            };
5114            let text = {
5115                let a = sb.finish();
5116                if non_text == 0 {
5117                    None
5118                } else {
5119                    Some(Arc::new(a) as ArrayRef)
5120                }
5121            };
5122            let errors = {
5123                let a = eb.finish();
5124                if non_err == 0 {
5125                    None
5126                } else {
5127                    Some(Arc::new(a))
5128                }
5129            };
5130
5131            (
5132                len, tags, numbers, booleans, text, errors, non_num, non_bool, non_text, non_err,
5133            )
5134        };
5135
5136        let Some(ch_mut) = self.columns[col_idx].chunk_mut(ch_idx) else {
5137            return 0;
5138        };
5139
5140        ch_mut.type_tag = tags;
5141        ch_mut.numbers = numbers;
5142        ch_mut.booleans = booleans;
5143        ch_mut.text = text;
5144        ch_mut.errors = errors;
5145        let freed = ch_mut.computed_overlay.clear();
5146        ch_mut.lowered_text = OnceCell::new();
5147        ch_mut.meta.len = len;
5148        ch_mut.meta.non_null_num = non_num;
5149        ch_mut.meta.non_null_bool = non_bool;
5150        ch_mut.meta.non_null_text = non_text;
5151        ch_mut.meta.non_null_err = non_err;
5152        freed
5153    }
5154
5155    /// Compact a sparse chunk's computed overlay into its base arrays.
5156    /// Equivalent to `compact_computed_overlay_chunk` but for sparse chunks.
5157    pub fn compact_computed_overlay_sparse_chunk(
5158        &mut self,
5159        col_idx: usize,
5160        ch_idx: usize,
5161    ) -> usize {
5162        // Sparse chunks are accessed via the same chunk/chunk_mut API,
5163        // so we delegate to the dense method which already handles both.
5164        self.compact_computed_overlay_chunk(col_idx, ch_idx)
5165    }
5166
5167    /// Insert `count` rows before absolute 0-based row `before`.
5168    pub fn insert_rows(&mut self, before: usize, count: usize) {
5169        if count == 0 {
5170            return;
5171        }
5172
5173        let total_rows = self.nrows as usize;
5174        if total_rows == 0 {
5175            self.nrows = count as u32;
5176            if self.nrows > 0 && self.chunk_starts.is_empty() {
5177                self.chunk_starts.push(0);
5178            }
5179            return;
5180        }
5181
5182        // Ensure a valid chunk map for non-empty sheets.
5183        if self.chunk_starts.is_empty() {
5184            self.chunk_starts.push(0);
5185        }
5186
5187        // "Dense" mode: every column has every chunk (legacy invariant).
5188        let dense_aligned = self
5189            .columns
5190            .iter()
5191            .all(|c| c.sparse_chunks.is_empty() && c.chunks.len() == self.chunk_starts.len());
5192
5193        let insert_at = before.min(total_rows);
5194        let (split_idx, split_off) = if insert_at == total_rows {
5195            // Append at end: split after last chunk.
5196            let last_idx = self.chunk_starts.len() - 1;
5197            let last_start = self.chunk_starts[last_idx];
5198            let last_len = total_rows.saturating_sub(last_start);
5199            (last_idx, last_len)
5200        } else {
5201            self.chunk_of_row(insert_at).unwrap_or((0, 0))
5202        };
5203
5204        if dense_aligned {
5205            // Rebuild chunks for each column (including inserted empty chunk) and recompute starts.
5206            for col in &mut self.columns {
5207                let mut new_chunks: Vec<ColumnChunk> = Vec::with_capacity(col.chunks.len() + 2);
5208                for i in 0..col.chunks.len() {
5209                    if i != split_idx {
5210                        new_chunks.push(col.chunks[i].clone());
5211                    } else {
5212                        let orig = &col.chunks[i];
5213                        let len = orig.type_tag.len();
5214                        if split_off > 0 {
5215                            new_chunks.push(Self::slice_chunk(orig, 0, split_off));
5216                        }
5217                        new_chunks.push(Self::make_empty_chunk(count));
5218                        if split_off < len {
5219                            new_chunks.push(Self::slice_chunk(orig, split_off, len - split_off));
5220                        }
5221                    }
5222                }
5223                col.chunks = new_chunks;
5224                col.sparse_chunks.clear();
5225            }
5226            self.nrows = (total_rows + count) as u32;
5227            self.recompute_chunk_starts();
5228            return;
5229        }
5230
5231        // Sparse-aware mode: `chunk_starts` is authoritative and missing chunks are treated as empty.
5232        #[derive(Clone, Copy)]
5233        enum PlanItem {
5234            Slice {
5235                old_idx: usize,
5236                off: usize,
5237                len: usize,
5238            },
5239            Empty {
5240                len: usize,
5241            },
5242        }
5243
5244        let mut plan: Vec<PlanItem> = Vec::with_capacity(self.chunk_starts.len() + 2);
5245        for old_idx in 0..self.chunk_starts.len() {
5246            let ch_start = self.chunk_starts[old_idx];
5247            let ch_end = self
5248                .chunk_starts
5249                .get(old_idx + 1)
5250                .copied()
5251                .unwrap_or(total_rows);
5252            let ch_len = ch_end.saturating_sub(ch_start);
5253            if ch_len == 0 {
5254                continue;
5255            }
5256
5257            if old_idx != split_idx {
5258                plan.push(PlanItem::Slice {
5259                    old_idx,
5260                    off: 0,
5261                    len: ch_len,
5262                });
5263                continue;
5264            }
5265
5266            let left_len = split_off.min(ch_len);
5267            let right_len = ch_len.saturating_sub(left_len);
5268            if left_len > 0 {
5269                plan.push(PlanItem::Slice {
5270                    old_idx,
5271                    off: 0,
5272                    len: left_len,
5273                });
5274            }
5275            plan.push(PlanItem::Empty { len: count });
5276            if right_len > 0 {
5277                plan.push(PlanItem::Slice {
5278                    old_idx,
5279                    off: left_len,
5280                    len: right_len,
5281                });
5282            }
5283        }
5284
5285        let mut new_starts: Vec<usize> = Vec::with_capacity(plan.len());
5286        let mut cur = 0usize;
5287        for item in &plan {
5288            let len = match *item {
5289                PlanItem::Slice { len, .. } => len,
5290                PlanItem::Empty { len } => len,
5291            };
5292            if len == 0 {
5293                continue;
5294            }
5295            new_starts.push(cur);
5296            cur = cur.saturating_add(len);
5297        }
5298
5299        debug_assert_eq!(cur, total_rows.saturating_add(count));
5300
5301        // Update sheet row layout first.
5302        self.nrows = (total_rows + count) as u32;
5303        self.chunk_starts = new_starts;
5304
5305        // Rebuild stored chunks per column using the plan.
5306        for col in &mut self.columns {
5307            let old_dense = std::mem::take(&mut col.chunks);
5308            let old_sparse = std::mem::take(&mut col.sparse_chunks);
5309            let get_old = |idx: usize| -> Option<&ColumnChunk> {
5310                if idx < old_dense.len() {
5311                    Some(&old_dense[idx])
5312                } else {
5313                    old_sparse.get(&idx)
5314                }
5315            };
5316
5317            let mut dense: Vec<ColumnChunk> = Vec::new();
5318            let mut sparse: FxHashMap<usize, ColumnChunk> = FxHashMap::default();
5319            let mut dense_prefix = true;
5320
5321            for (new_idx, item) in plan.iter().enumerate() {
5322                let produced: Option<ColumnChunk> = match *item {
5323                    PlanItem::Empty { .. } => None,
5324                    PlanItem::Slice { old_idx, off, len } => match get_old(old_idx) {
5325                        Some(orig) => {
5326                            if off == 0 && len == orig.type_tag.len() {
5327                                Some(orig.clone())
5328                            } else {
5329                                Some(Self::slice_chunk(orig, off, len))
5330                            }
5331                        }
5332                        None => None,
5333                    },
5334                };
5335
5336                if let Some(ch) = produced {
5337                    if dense_prefix && new_idx == dense.len() {
5338                        dense.push(ch);
5339                    } else {
5340                        sparse.insert(new_idx, ch);
5341                        dense_prefix = false;
5342                    }
5343                } else if dense_prefix && new_idx == dense.len() {
5344                    dense_prefix = false;
5345                }
5346            }
5347
5348            col.chunks = dense;
5349            col.sparse_chunks = sparse;
5350        }
5351    }
5352
5353    /// Delete `count` rows starting from absolute 0-based row `start`.
5354    pub fn delete_rows(&mut self, start: usize, count: usize) {
5355        if count == 0 || self.nrows == 0 {
5356            return;
5357        }
5358
5359        let total_rows = self.nrows as usize;
5360        if start >= total_rows {
5361            return;
5362        }
5363        let end = (start + count).min(total_rows);
5364        let del_len = end.saturating_sub(start);
5365        if del_len == 0 {
5366            return;
5367        }
5368
5369        // Ensure a valid chunk map for non-empty sheets.
5370        if total_rows > 0 && self.chunk_starts.is_empty() {
5371            self.chunk_starts.push(0);
5372        }
5373
5374        // "Dense" mode: every column has every chunk (legacy invariant).
5375        let dense_aligned = self
5376            .columns
5377            .iter()
5378            .all(|c| c.sparse_chunks.is_empty() && c.chunks.len() == self.chunk_starts.len());
5379
5380        if dense_aligned {
5381            // Dense rebuild by slicing out the deleted window.
5382            for col in &mut self.columns {
5383                let mut new_chunks: Vec<ColumnChunk> = Vec::new();
5384                let mut cur_start = 0usize;
5385                for ch in &col.chunks {
5386                    let len = ch.type_tag.len();
5387                    let ch_end = cur_start + len;
5388                    // No overlap
5389                    if ch_end <= start || cur_start >= end {
5390                        new_chunks.push(ch.clone());
5391                    } else {
5392                        // Overlap exists
5393                        let del_start = start.max(cur_start);
5394                        let del_end = end.min(ch_end);
5395                        let left_len = del_start.saturating_sub(cur_start);
5396                        let right_len = ch_end.saturating_sub(del_end);
5397                        if left_len > 0 {
5398                            new_chunks.push(Self::slice_chunk(ch, 0, left_len));
5399                        }
5400                        if right_len > 0 {
5401                            let off = len - right_len;
5402                            new_chunks.push(Self::slice_chunk(ch, off, right_len));
5403                        }
5404                    }
5405                    cur_start = ch_end;
5406                }
5407                col.chunks = new_chunks;
5408                col.sparse_chunks.clear();
5409            }
5410            self.nrows = (total_rows - del_len) as u32;
5411            self.recompute_chunk_starts();
5412            return;
5413        }
5414
5415        // Sparse-aware mode: `chunk_starts` is authoritative and missing chunks are treated as empty.
5416        #[derive(Clone, Copy)]
5417        enum PlanItem {
5418            Slice {
5419                old_idx: usize,
5420                off: usize,
5421                len: usize,
5422            },
5423        }
5424
5425        let mut plan: Vec<PlanItem> = Vec::with_capacity(self.chunk_starts.len());
5426        for old_idx in 0..self.chunk_starts.len() {
5427            let ch_start = self.chunk_starts[old_idx];
5428            let ch_end = self
5429                .chunk_starts
5430                .get(old_idx + 1)
5431                .copied()
5432                .unwrap_or(total_rows);
5433            let ch_len = ch_end.saturating_sub(ch_start);
5434            if ch_len == 0 {
5435                continue;
5436            }
5437
5438            // No overlap
5439            if ch_end <= start || ch_start >= end {
5440                plan.push(PlanItem::Slice {
5441                    old_idx,
5442                    off: 0,
5443                    len: ch_len,
5444                });
5445                continue;
5446            }
5447
5448            // Left remainder
5449            if start > ch_start {
5450                let left_end = start.min(ch_end);
5451                let left_len = left_end.saturating_sub(ch_start);
5452                if left_len > 0 {
5453                    plan.push(PlanItem::Slice {
5454                        old_idx,
5455                        off: 0,
5456                        len: left_len,
5457                    });
5458                }
5459            }
5460
5461            // Right remainder
5462            if end < ch_end {
5463                let right_off = end.saturating_sub(ch_start);
5464                let right_len = ch_end.saturating_sub(end);
5465                if right_len > 0 {
5466                    plan.push(PlanItem::Slice {
5467                        old_idx,
5468                        off: right_off,
5469                        len: right_len,
5470                    });
5471                }
5472            }
5473        }
5474
5475        let mut new_starts: Vec<usize> = Vec::with_capacity(plan.len());
5476        let mut cur = 0usize;
5477        for item in &plan {
5478            let len = match *item {
5479                PlanItem::Slice { len, .. } => len,
5480            };
5481            if len == 0 {
5482                continue;
5483            }
5484            new_starts.push(cur);
5485            cur = cur.saturating_add(len);
5486        }
5487
5488        debug_assert_eq!(cur, total_rows.saturating_sub(del_len));
5489
5490        // Update sheet row layout first.
5491        self.nrows = (total_rows - del_len) as u32;
5492        self.chunk_starts = new_starts;
5493
5494        // Rebuild stored chunks per column using the plan.
5495        for col in &mut self.columns {
5496            let old_dense = std::mem::take(&mut col.chunks);
5497            let old_sparse = std::mem::take(&mut col.sparse_chunks);
5498            let get_old = |idx: usize| -> Option<&ColumnChunk> {
5499                if idx < old_dense.len() {
5500                    Some(&old_dense[idx])
5501                } else {
5502                    old_sparse.get(&idx)
5503                }
5504            };
5505
5506            let mut dense: Vec<ColumnChunk> = Vec::new();
5507            let mut sparse: FxHashMap<usize, ColumnChunk> = FxHashMap::default();
5508            let mut dense_prefix = true;
5509
5510            for (new_idx, item) in plan.iter().enumerate() {
5511                let produced: Option<ColumnChunk> = match *item {
5512                    PlanItem::Slice { old_idx, off, len } => match get_old(old_idx) {
5513                        Some(orig) => {
5514                            if off == 0 && len == orig.type_tag.len() {
5515                                Some(orig.clone())
5516                            } else {
5517                                Some(Self::slice_chunk(orig, off, len))
5518                            }
5519                        }
5520                        None => None,
5521                    },
5522                };
5523
5524                if let Some(ch) = produced {
5525                    if dense_prefix && new_idx == dense.len() {
5526                        dense.push(ch);
5527                    } else {
5528                        sparse.insert(new_idx, ch);
5529                        dense_prefix = false;
5530                    }
5531                } else if dense_prefix && new_idx == dense.len() {
5532                    dense_prefix = false;
5533                }
5534            }
5535
5536            col.chunks = dense;
5537            col.sparse_chunks = sparse;
5538        }
5539    }
5540
5541    /// Insert `count` columns before absolute 0-based column `before` with empty chunks.
5542    pub fn insert_columns(&mut self, before: usize, count: usize) {
5543        if count == 0 {
5544            return;
5545        }
5546        // Determine chunk schema from first column if present
5547        let empty_col = |lens: &[usize]| -> ArrowColumn {
5548            let mut chunks = Vec::with_capacity(lens.len());
5549            for &l in lens {
5550                chunks.push(Self::make_empty_chunk(l));
5551            }
5552            ArrowColumn {
5553                chunks,
5554                sparse_chunks: FxHashMap::default(),
5555                index: 0,
5556            }
5557        };
5558        let dense_aligned = !self.columns.is_empty()
5559            && self
5560                .columns
5561                .iter()
5562                .all(|c| c.sparse_chunks.is_empty() && c.chunks.len() == self.chunk_starts.len());
5563
5564        let lens: Vec<usize> = if dense_aligned {
5565            self.columns[0]
5566                .chunks
5567                .iter()
5568                .map(|c| c.type_tag.len())
5569                .collect()
5570        } else if self.columns.is_empty() {
5571            // No columns: single chunk matching nrows if any
5572            if self.nrows > 0 {
5573                vec![self.nrows as usize]
5574            } else {
5575                Vec::new()
5576            }
5577        } else {
5578            // Sparse sheet: keep inserted columns cheap by materializing no chunks.
5579            Vec::new()
5580        };
5581        let mut cols_new: Vec<ArrowColumn> = Vec::with_capacity(self.columns.len() + count);
5582        let before_idx = before.min(self.columns.len());
5583        for (i, col) in self.columns.iter_mut().enumerate() {
5584            if i == before_idx {
5585                for _ in 0..count {
5586                    cols_new.push(empty_col(&lens));
5587                }
5588            }
5589            cols_new.push(col.clone());
5590        }
5591        if before_idx == self.columns.len() {
5592            for _ in 0..count {
5593                cols_new.push(empty_col(&lens));
5594            }
5595        }
5596        // Fix column indices
5597        for (idx, col) in cols_new.iter_mut().enumerate() {
5598            col.index = idx as u32;
5599        }
5600        self.columns = cols_new;
5601        // chunk_starts unchanged; lens were matched
5602    }
5603
5604    /// Delete `count` columns starting at absolute 0-based column `start`.
5605    pub fn delete_columns(&mut self, start: usize, count: usize) {
5606        if count == 0 || self.columns.is_empty() {
5607            return;
5608        }
5609        let end = (start + count).min(self.columns.len());
5610        if start >= end {
5611            return;
5612        }
5613        self.columns.drain(start..end);
5614        for (idx, col) in self.columns.iter_mut().enumerate() {
5615            col.index = idx as u32;
5616        }
5617    }
5618}
5619
5620#[derive(Debug, Clone, Copy)]
5621pub struct ColumnShape {
5622    pub index: u32,
5623    pub chunks: usize,
5624    pub rows: usize,
5625    pub has_num: bool,
5626    pub has_bool: bool,
5627    pub has_text: bool,
5628    pub has_err: bool,
5629}
5630
5631#[cfg(test)]
5632mod tests {
5633    use super::*;
5634
5635    /// FORM-000130: lazy next-chunk provisioning and the text reserve policy
5636    /// must not change chunk layout, lane presence, null semantics or formats,
5637    /// at exact chunk multiples, partial terminal chunks and lane transitions.
5638    #[test]
5639    fn ingest_builder_chunk_boundaries_and_lane_transitions_are_preserved() {
5640        let date = chrono::NaiveDate::from_ymd_opt(2024, 1, 15).unwrap();
5641        let value_at = |i: usize| match i % 7 {
5642            0 | 1 if i < 8 => LiteralValue::Number(i as f64),
5643            0 => LiteralValue::Text(format!("t{i}")),
5644            1 => LiteralValue::Boolean(i.is_multiple_of(2)),
5645            2 => LiteralValue::Empty,
5646            3 => LiteralValue::Error(ExcelError::new(ExcelErrorKind::Div)),
5647            4 => LiteralValue::Date(date),
5648            5 => LiteralValue::Pending,
5649            _ => LiteralValue::Int(i as i64),
5650        };
5651        // The same rows as borrowed ingest cells (the reader load paths).
5652        fn cell_at(value: LiteralValue, i: usize, text: &str) -> CellIngest<'_> {
5653            match value {
5654                LiteralValue::Number(n) => CellIngest::Number(n),
5655                LiteralValue::Text(_) => CellIngest::Text(text),
5656                LiteralValue::Boolean(b) => CellIngest::Boolean(b),
5657                LiteralValue::Empty => CellIngest::Empty,
5658                LiteralValue::Error(e) => CellIngest::ErrorCode(map_error_code(e.kind)),
5659                LiteralValue::Date(_) => CellIngest::DateSerial(i as f64 + 45_000.0),
5660                LiteralValue::Pending => CellIngest::Pending,
5661                LiteralValue::Int(n) => CellIngest::Number(n as f64),
5662                other => unreachable!("{other:?}"),
5663            }
5664        }
5665        let cases = [(0usize, 4usize), (4, 4), (8, 4), (9, 4), (3, 4), (23, 5)];
5666        for ((rows, chunk_rows), path) in cases
5667            .into_iter()
5668            .flat_map(|case| ["row", "cells", "cells_iter"].map(|path| (case, path)))
5669        {
5670            let mut ingest =
5671                IngestBuilder::new("S", 2, chunk_rows, crate::engine::DateSystem::Excel1900);
5672            for i in 0..rows {
5673                let text = format!("t{i}");
5674                let number = CellIngest::Number(i as f64);
5675                match path {
5676                    "row" => ingest
5677                        .append_row(&[value_at(i), LiteralValue::Number(i as f64)])
5678                        .unwrap(),
5679                    "cells" => ingest
5680                        .append_row_cells(&[cell_at(value_at(i), i, &text), number])
5681                        .unwrap(),
5682                    _ => ingest
5683                        .append_row_cells_iter([cell_at(value_at(i), i, &text), number].into_iter())
5684                        .unwrap(),
5685                }
5686            }
5687            let sheet = ingest.finish();
5688            let expected_chunks = rows.div_ceil(chunk_rows);
5689            assert_eq!(sheet.nrows as usize, rows);
5690            assert_eq!(sheet.chunk_starts.len(), expected_chunks);
5691            for (ci, col) in sheet.columns.iter().enumerate() {
5692                assert_eq!(col.chunks.len(), expected_chunks, "col {ci} rows {rows}");
5693                for (k, chunk) in col.chunks.iter().enumerate() {
5694                    let start = k * chunk_rows;
5695                    let len = chunk_rows.min(rows - start);
5696                    assert_eq!(chunk.meta.len, len);
5697                    assert_eq!(chunk.type_tag.len(), len);
5698                    for lane_len in [
5699                        chunk.numbers.as_ref().map(|a| a.len()),
5700                        chunk.booleans.as_ref().map(|a| a.len()),
5701                        chunk.text.as_ref().map(|a| a.len()),
5702                        chunk.errors.as_ref().map(|a| a.len()),
5703                    ]
5704                    .into_iter()
5705                    .flatten()
5706                    {
5707                        assert_eq!(lane_len, len, "materialized lanes span the chunk");
5708                    }
5709                    assert_eq!(chunk.text.is_some(), chunk.meta.non_null_text > 0);
5710                    assert_eq!(chunk.numbers.is_some(), chunk.meta.non_null_num > 0);
5711                }
5712            }
5713            for i in 0..rows {
5714                let got = sheet.get_cell_value(i, 0);
5715                match value_at(i) {
5716                    // Dates round-trip through the numeric lane with a DATE format.
5717                    LiteralValue::Date(_) => {
5718                        assert_eq!(sheet.format_id(i, 0), Some(FormatId::DATE))
5719                    }
5720                    LiteralValue::Error(e) => {
5721                        assert!(matches!(got, LiteralValue::Error(g) if g.kind == e.kind))
5722                    }
5723                    LiteralValue::Int(n) => assert_eq!(got, LiteralValue::Number(n as f64)),
5724                    expected => assert_eq!(
5725                        expected, got,
5726                        "{path} row {i} rows {rows} chunk {chunk_rows}"
5727                    ),
5728                }
5729                assert_eq!(sheet.get_cell_value(i, 1), LiteralValue::Number(i as f64));
5730            }
5731        }
5732    }
5733
5734    #[test]
5735    fn explicit_format_precedence_and_general_filter_are_stable() {
5736        let date = chrono::NaiveDate::from_ymd_opt(2024, 1, 15).unwrap();
5737        let mut ingest = IngestBuilder::new("Sheet1", 1, 16, crate::engine::DateSystem::Excel1900);
5738        ingest.append_row(&[LiteralValue::Date(date)]).unwrap();
5739        ingest.append_row(&[LiteralValue::Number(1.0)]).unwrap();
5740        let mut sheet = ingest.finish();
5741
5742        let chunk = sheet.columns[0].chunk_mut(0).unwrap();
5743        chunk.computed_overlay.set_format(0, Some(FormatId::TIME));
5744        assert_eq!(
5745            sheet.format_id(0, 0),
5746            Some(FormatId::DATE),
5747            "base explicit format must beat the derived overlay"
5748        );
5749
5750        sheet.columns[0]
5751            .chunk_mut(0)
5752            .unwrap()
5753            .overlay
5754            .set_format(0, Some(FormatId::DATETIME));
5755        assert_eq!(
5756            sheet.format_id(0, 0),
5757            Some(FormatId::DATETIME),
5758            "user explicit overlay must beat base and derived formats"
5759        );
5760        assert_eq!(
5761            sheet.format_id(1, 0),
5762            None,
5763            "General is absence, not an effective explicit format"
5764        );
5765        sheet.ensure_row_capacity(3);
5766        assert_eq!(
5767            sheet.format_id(2, 0),
5768            None,
5769            "growing a formatted chunk must fill new rows with General"
5770        );
5771    }
5772
5773    #[test]
5774    fn known_error_storage_codes_are_stable() {
5775        let cases = [
5776            (ExcelErrorKind::Null, 1),
5777            (ExcelErrorKind::Ref, 2),
5778            (ExcelErrorKind::Name, 3),
5779            (ExcelErrorKind::Value, 4),
5780            (ExcelErrorKind::Div, 5),
5781            (ExcelErrorKind::Na, 6),
5782            (ExcelErrorKind::Num, 7),
5783            (ExcelErrorKind::Error, 8),
5784            (ExcelErrorKind::NImpl, 9),
5785            (ExcelErrorKind::Spill, 10),
5786            (ExcelErrorKind::Calc, 11),
5787            (ExcelErrorKind::Circ, 12),
5788            (ExcelErrorKind::Cancelled, 13),
5789        ];
5790        for (kind, code) in cases {
5791            assert_eq!(map_error_code(kind), code, "{kind:?}");
5792            assert_eq!(unmap_error_code(code), kind, "code {code}");
5793        }
5794        assert_eq!(unmap_error_code(u8::MAX), ExcelErrorKind::Error);
5795    }
5796    use arrow_array::Array;
5797    use arrow_schema::DataType;
5798    use chrono::{Datelike, Timelike};
5799
5800    #[test]
5801    fn sparse_constructor_defaults_to_excel_1900_and_decodes_excel_1904() {
5802        let date = chrono::NaiveDate::from_ymd_opt(1904, 1, 1).unwrap();
5803        let _datetime = date.and_hms_opt(12, 0, 0).unwrap();
5804
5805        let mut default_sheet = ArrowSheet::new_sparse("Default", 1, 1, 16);
5806        assert_eq!(
5807            default_sheet.date_system,
5808            crate::engine::DateSystem::Excel1900
5809        );
5810        default_sheet.set_sparse_overlay_value(0, 0, OverlayValue::DateTime(1462.5));
5811        assert_eq!(
5812            default_sheet.get_cell_value(0, 0),
5813            LiteralValue::Number(1462.5)
5814        );
5815
5816        let mut excel_1904 = ArrowSheet::new_sparse_with_date_system(
5817            "1904",
5818            1,
5819            1,
5820            16,
5821            crate::engine::DateSystem::Excel1904,
5822        );
5823        assert_eq!(excel_1904.date_system, crate::engine::DateSystem::Excel1904);
5824        excel_1904.set_sparse_overlay_value(0, 0, OverlayValue::DateTime(0.5));
5825        assert_eq!(excel_1904.get_cell_value(0, 0), LiteralValue::Number(0.5));
5826    }
5827
5828    #[test]
5829    fn datetime_lanes_round_trip_the_sheet_date_system() {
5830        let date = chrono::NaiveDate::from_ymd_opt(2024, 1, 15).unwrap();
5831        let datetime = date.and_hms_opt(12, 30, 0).unwrap();
5832        let time = chrono::NaiveTime::from_hms_opt(12, 30, 0).unwrap();
5833
5834        for system in [
5835            crate::engine::DateSystem::Excel1900,
5836            crate::engine::DateSystem::Excel1904,
5837        ] {
5838            let values = vec![
5839                LiteralValue::Date(date),
5840                LiteralValue::DateTime(datetime),
5841                LiteralValue::Time(time),
5842            ];
5843            let mut ingest = IngestBuilder::new("Sheet1", 3, 16, system);
5844            ingest.append_row(&values).unwrap();
5845            let sheet = ingest.finish();
5846
5847            assert_eq!(sheet.date_system, system);
5848            let date_serial = formualizer_common::date_to_serial_for(system, &date);
5849            let datetime_serial = formualizer_common::datetime_to_serial_for(system, &datetime);
5850            assert_eq!(
5851                sheet.get_cell_value(0, 0),
5852                LiteralValue::Number(date_serial)
5853            );
5854            assert_eq!(
5855                sheet.get_cell_value(0, 1),
5856                LiteralValue::Number(datetime_serial)
5857            );
5858            assert_eq!(sheet.format_id(0, 0), Some(FormatId::DATE));
5859            assert_eq!(sheet.format_id(0, 1), Some(FormatId::DATETIME));
5860            assert_eq!(sheet.format_id(0, 2), Some(FormatId::TIME));
5861            let view = sheet.range_view(0, 0, 0, 1);
5862            assert_eq!(view.get_cell(0, 0), LiteralValue::Number(date_serial));
5863            assert_eq!(view.get_cell(0, 1), LiteralValue::Number(datetime_serial));
5864
5865            let mut sparse = ArrowSheet::new_sparse_with_date_system("Sparse", 1, 1, 16, system);
5866            sparse.set_sparse_overlay_value(
5867                0,
5868                0,
5869                OverlayValue::from_literal_value(&values[1], system),
5870            );
5871            assert_eq!(
5872                sparse.get_cell_value(0, 0),
5873                LiteralValue::Number(datetime_serial)
5874            );
5875        }
5876    }
5877
5878    fn add_overlay_stats(into: &mut OverlayDebugStats, next: OverlayDebugStats) {
5879        into.points += next.points;
5880        into.sparse_fragments += next.sparse_fragments;
5881        into.dense_fragments += next.dense_fragments;
5882        into.run_fragments += next.run_fragments;
5883        into.covered_len += next.covered_len;
5884    }
5885
5886    fn column_overlay_stats(
5887        sheet: &ArrowSheet,
5888        col_idx: usize,
5889        computed: bool,
5890    ) -> OverlayDebugStats {
5891        let mut stats = OverlayDebugStats::default();
5892        let Some(column) = sheet.columns.get(col_idx) else {
5893            return stats;
5894        };
5895        for chunk in &column.chunks {
5896            add_overlay_stats(
5897                &mut stats,
5898                if computed {
5899                    chunk.computed_overlay.debug_stats()
5900                } else {
5901                    chunk.overlay.debug_stats()
5902                },
5903            );
5904        }
5905        for chunk in column.sparse_chunks.values() {
5906            add_overlay_stats(
5907                &mut stats,
5908                if computed {
5909                    chunk.computed_overlay.debug_stats()
5910                } else {
5911                    chunk.overlay.debug_stats()
5912                },
5913            );
5914        }
5915        stats
5916    }
5917
5918    fn assert_column_overlays_normalized(sheet: &ArrowSheet, col_idx: usize) {
5919        let column = &sheet.columns[col_idx];
5920        for chunk in &column.chunks {
5921            assert!(chunk.overlay.debug_is_normalized());
5922            assert!(chunk.computed_overlay.debug_is_normalized());
5923            assert_eq!(
5924                chunk.overlay.estimated_bytes(),
5925                chunk.overlay.debug_recomputed_estimated_bytes()
5926            );
5927            assert_eq!(
5928                chunk.computed_overlay.estimated_bytes(),
5929                chunk.computed_overlay.debug_recomputed_estimated_bytes()
5930            );
5931        }
5932        for chunk in column.sparse_chunks.values() {
5933            assert!(chunk.overlay.debug_is_normalized());
5934            assert!(chunk.computed_overlay.debug_is_normalized());
5935            assert_eq!(
5936                chunk.overlay.estimated_bytes(),
5937                chunk.overlay.debug_recomputed_estimated_bytes()
5938            );
5939            assert_eq!(
5940                chunk.computed_overlay.estimated_bytes(),
5941                chunk.computed_overlay.debug_recomputed_estimated_bytes()
5942            );
5943        }
5944    }
5945
5946    fn column_computed_overlay_estimated_bytes(sheet: &ArrowSheet, col_idx: usize) -> usize {
5947        let Some(column) = sheet.columns.get(col_idx) else {
5948            return 0;
5949        };
5950        column
5951            .chunks
5952            .iter()
5953            .map(|chunk| chunk.computed_overlay.estimated_bytes())
5954            .chain(
5955                column
5956                    .sparse_chunks
5957                    .values()
5958                    .map(|chunk| chunk.computed_overlay.estimated_bytes()),
5959            )
5960            .fold(0usize, usize::saturating_add)
5961    }
5962
5963    #[derive(Debug, Clone, Copy)]
5964    enum Phase4ProbeFixture {
5965        PointNumeric,
5966        DenseNumeric,
5967        RunNumeric,
5968        SparseNumeric,
5969        EmptyRun,
5970        MixedDense,
5971    }
5972
5973    impl Phase4ProbeFixture {
5974        fn name(self) -> &'static str {
5975            match self {
5976                Phase4ProbeFixture::PointNumeric => "point_numeric",
5977                Phase4ProbeFixture::DenseNumeric => "dense_numeric",
5978                Phase4ProbeFixture::RunNumeric => "run_numeric",
5979                Phase4ProbeFixture::SparseNumeric => "sparse_numeric",
5980                Phase4ProbeFixture::EmptyRun => "empty_run",
5981                Phase4ProbeFixture::MixedDense => "mixed_dense",
5982            }
5983        }
5984    }
5985
5986    #[derive(Debug, serde::Serialize)]
5987    struct Phase4ProbeOp {
5988        ms: f64,
5989        segments: usize,
5990        arrays: usize,
5991        rows_scanned: usize,
5992        checksum: f64,
5993        non_null: usize,
5994    }
5995
5996    #[derive(Debug, serde::Serialize)]
5997    struct Phase4ProbeRow {
5998        fixture: &'static str,
5999        rows: usize,
6000        points: usize,
6001        sparse_fragments: usize,
6002        dense_fragments: usize,
6003        run_fragments: usize,
6004        covered_len: usize,
6005        overlay_estimated_bytes: usize,
6006        numbers: Phase4ProbeOp,
6007        type_tags: Phase4ProbeOp,
6008        lowered_text: Phase4ProbeOp,
6009        get_cell_scan: Phase4ProbeOp,
6010        select_stats: OverlaySelectStats,
6011    }
6012
6013    fn build_phase4_probe_sheet(rows: usize, fixture: Phase4ProbeFixture) -> ArrowSheet {
6014        let mut builder =
6015            IngestBuilder::new("S", 1, rows.max(1), crate::engine::DateSystem::Excel1900);
6016        for row in 0..rows {
6017            builder
6018                .append_row(&[LiteralValue::Number((row + 1) as f64)])
6019                .unwrap();
6020        }
6021        let mut sheet = builder.finish();
6022        let chunk = sheet.columns[0].chunk_mut(0).unwrap();
6023        match fixture {
6024            Phase4ProbeFixture::PointNumeric => {
6025                for row in 0..rows {
6026                    chunk
6027                        .computed_overlay
6028                        .set_scalar(row, OverlayValue::Number((row + 1) as f64));
6029                }
6030            }
6031            Phase4ProbeFixture::DenseNumeric => {
6032                chunk.computed_overlay.apply_fragment(
6033                    OverlayFragment::dense_range(
6034                        0,
6035                        (0..rows)
6036                            .map(|row| OverlayValue::Number((row + 1) as f64))
6037                            .collect(),
6038                    )
6039                    .unwrap(),
6040                );
6041            }
6042            Phase4ProbeFixture::RunNumeric => {
6043                chunk.computed_overlay.apply_fragment(
6044                    OverlayFragment::run_range(0, vec![OverlayValue::Number(1.0); rows]).unwrap(),
6045                );
6046            }
6047            Phase4ProbeFixture::SparseNumeric => {
6048                chunk.computed_overlay.apply_fragment(
6049                    OverlayFragment::sparse_offsets(
6050                        (0..rows)
6051                            .step_by(10)
6052                            .map(|row| (row, OverlayValue::Number(10.0)))
6053                            .collect(),
6054                    )
6055                    .unwrap(),
6056                );
6057            }
6058            Phase4ProbeFixture::EmptyRun => {
6059                chunk.computed_overlay.apply_fragment(
6060                    OverlayFragment::run_range(0, vec![OverlayValue::Empty; rows]).unwrap(),
6061                );
6062            }
6063            Phase4ProbeFixture::MixedDense => {
6064                let pattern = [
6065                    OverlayValue::Number(1.0),
6066                    OverlayValue::Boolean(true),
6067                    OverlayValue::Text(Arc::from("Alpha")),
6068                    OverlayValue::Empty,
6069                    OverlayValue::Error(map_error_code(ExcelErrorKind::Div)),
6070                    OverlayValue::Pending,
6071                    OverlayValue::DateTime(45000.25),
6072                    OverlayValue::Duration(0.5),
6073                ];
6074                chunk.computed_overlay.apply_fragment(
6075                    OverlayFragment::dense_range(
6076                        0,
6077                        (0..rows)
6078                            .map(|row| pattern[row % pattern.len()].clone())
6079                            .collect(),
6080                    )
6081                    .unwrap(),
6082                );
6083            }
6084        }
6085        sheet
6086    }
6087
6088    fn measure_probe_numbers(sheet: &ArrowSheet, rows: usize) -> Phase4ProbeOp {
6089        let view = sheet.range_view(0, 0, rows.saturating_sub(1), 0);
6090        let start = std::time::Instant::now();
6091        let mut segments = 0usize;
6092        let mut arrays = 0usize;
6093        let mut rows_scanned = 0usize;
6094        let mut checksum = 0.0;
6095        let mut non_null = 0usize;
6096        for segment in view.numbers_slices() {
6097            let (_row_start, row_len, cols) = segment.unwrap();
6098            segments += 1;
6099            rows_scanned += row_len;
6100            for array in cols {
6101                arrays += 1;
6102                for idx in 0..array.len() {
6103                    if array.is_valid(idx) {
6104                        checksum += array.value(idx);
6105                        non_null += 1;
6106                    }
6107                }
6108            }
6109        }
6110        Phase4ProbeOp {
6111            ms: start.elapsed().as_secs_f64() * 1000.0,
6112            segments,
6113            arrays,
6114            rows_scanned,
6115            checksum,
6116            non_null,
6117        }
6118    }
6119
6120    fn measure_probe_type_tags(sheet: &ArrowSheet, rows: usize) -> Phase4ProbeOp {
6121        let view = sheet.range_view(0, 0, rows.saturating_sub(1), 0);
6122        let start = std::time::Instant::now();
6123        let mut segments = 0usize;
6124        let mut arrays = 0usize;
6125        let mut rows_scanned = 0usize;
6126        let mut checksum = 0.0;
6127        let mut non_null = 0usize;
6128        for segment in view.type_tags_slices() {
6129            let (_row_start, row_len, cols) = segment.unwrap();
6130            segments += 1;
6131            rows_scanned += row_len;
6132            for array in cols {
6133                arrays += 1;
6134                for idx in 0..array.len() {
6135                    if array.is_valid(idx) {
6136                        checksum += array.value(idx) as f64;
6137                        non_null += 1;
6138                    }
6139                }
6140            }
6141        }
6142        Phase4ProbeOp {
6143            ms: start.elapsed().as_secs_f64() * 1000.0,
6144            segments,
6145            arrays,
6146            rows_scanned,
6147            checksum,
6148            non_null,
6149        }
6150    }
6151
6152    fn measure_probe_lowered_text(sheet: &ArrowSheet, rows: usize) -> Phase4ProbeOp {
6153        let view = sheet.range_view(0, 0, rows.saturating_sub(1), 0);
6154        let start = std::time::Instant::now();
6155        let mut segments = 0usize;
6156        let mut arrays = 0usize;
6157        let mut rows_scanned = 0usize;
6158        let mut checksum = 0.0;
6159        let mut non_null = 0usize;
6160        for segment in view.lowered_text_slices() {
6161            let (_row_start, row_len, cols) = segment.unwrap();
6162            segments += 1;
6163            rows_scanned += row_len;
6164            for array in cols {
6165                arrays += 1;
6166                for idx in 0..array.len() {
6167                    if array.is_valid(idx) {
6168                        checksum += array.value(idx).len() as f64;
6169                        non_null += 1;
6170                    }
6171                }
6172            }
6173        }
6174        Phase4ProbeOp {
6175            ms: start.elapsed().as_secs_f64() * 1000.0,
6176            segments,
6177            arrays,
6178            rows_scanned,
6179            checksum,
6180            non_null,
6181        }
6182    }
6183
6184    fn literal_probe_weight(value: LiteralValue) -> f64 {
6185        match value {
6186            LiteralValue::Empty => 0.0,
6187            LiteralValue::Int(value) => value as f64,
6188            LiteralValue::Number(value) => value,
6189            LiteralValue::Boolean(value) => {
6190                if value {
6191                    1.0
6192                } else {
6193                    0.0
6194                }
6195            }
6196            LiteralValue::Text(value) => value.len() as f64,
6197            LiteralValue::Error(_) => -1.0,
6198            LiteralValue::Date(value) => value.num_days_from_ce() as f64,
6199            LiteralValue::DateTime(value) => value.and_utc().timestamp() as f64,
6200            LiteralValue::Time(value) => value.num_seconds_from_midnight() as f64,
6201            LiteralValue::Duration(value) => value.num_seconds() as f64,
6202            LiteralValue::Array(values) => values.len() as f64,
6203            LiteralValue::Pending => -2.0,
6204        }
6205    }
6206
6207    fn measure_probe_get_cell(sheet: &ArrowSheet, rows: usize) -> Phase4ProbeOp {
6208        let view = sheet.range_view(0, 0, rows.saturating_sub(1), 0);
6209        let start = std::time::Instant::now();
6210        let mut checksum = 0.0;
6211        for row in 0..rows {
6212            checksum += literal_probe_weight(view.get_cell(row, 0));
6213        }
6214        Phase4ProbeOp {
6215            ms: start.elapsed().as_secs_f64() * 1000.0,
6216            segments: 1,
6217            arrays: 0,
6218            rows_scanned: rows,
6219            checksum,
6220            non_null: rows,
6221        }
6222    }
6223
6224    fn run_phase4_probe_fixture(rows: usize, fixture: Phase4ProbeFixture) -> Phase4ProbeRow {
6225        let sheet = build_phase4_probe_sheet(rows, fixture);
6226        assert_column_overlays_normalized(&sheet, 0);
6227        let stats = column_overlay_stats(&sheet, 0, true);
6228        reset_overlay_select_stats();
6229        let numbers = measure_probe_numbers(&sheet, rows);
6230        let type_tags = measure_probe_type_tags(&sheet, rows);
6231        let lowered_text = measure_probe_lowered_text(&sheet, rows);
6232        let select_stats = snapshot_overlay_select_stats();
6233        let get_cell_scan = measure_probe_get_cell(&sheet, rows);
6234        Phase4ProbeRow {
6235            fixture: fixture.name(),
6236            rows,
6237            points: stats.points,
6238            sparse_fragments: stats.sparse_fragments,
6239            dense_fragments: stats.dense_fragments,
6240            run_fragments: stats.run_fragments,
6241            covered_len: stats.covered_len,
6242            overlay_estimated_bytes: column_computed_overlay_estimated_bytes(&sheet, 0),
6243            numbers,
6244            type_tags,
6245            lowered_text,
6246            get_cell_scan,
6247            select_stats,
6248        }
6249    }
6250
6251    #[test]
6252    #[ignore = "manual Phase 4 observability probe; run with --ignored --nocapture"]
6253    fn phase4_overlay_rangeview_observability_probe() {
6254        let rows = std::env::var("FORMUALIZER_OVERLAY_PROBE_ROWS")
6255            .ok()
6256            .and_then(|value| value.parse::<usize>().ok())
6257            .unwrap_or(100_000)
6258            .max(1);
6259        for fixture in [
6260            Phase4ProbeFixture::PointNumeric,
6261            Phase4ProbeFixture::DenseNumeric,
6262            Phase4ProbeFixture::RunNumeric,
6263            Phase4ProbeFixture::SparseNumeric,
6264            Phase4ProbeFixture::EmptyRun,
6265            Phase4ProbeFixture::MixedDense,
6266        ] {
6267            let row = run_phase4_probe_fixture(rows, fixture);
6268            println!("{}", serde_json::to_string(&row).unwrap());
6269        }
6270    }
6271
6272    #[test]
6273    fn ingest_mixed_rows_into_lanes_and_tags() {
6274        let mut b = IngestBuilder::new("Sheet1", 1, 1024, crate::engine::DateSystem::Excel1900);
6275        let data = vec![
6276            LiteralValue::Number(42.5),                   // Number
6277            LiteralValue::Empty,                          // Empty
6278            LiteralValue::Text(String::new()),            // Empty text (Text lane)
6279            LiteralValue::Boolean(true),                  // Boolean
6280            LiteralValue::Error(ExcelError::new_value()), // Error
6281        ];
6282        for v in &data {
6283            b.append_row(std::slice::from_ref(v)).unwrap();
6284        }
6285        let sheet = b.finish();
6286        assert_eq!(sheet.nrows, 5);
6287        assert_eq!(sheet.columns.len(), 1);
6288        assert_eq!(sheet.columns[0].chunks.len(), 1);
6289        let ch = &sheet.columns[0].chunks[0];
6290
6291        // Type tags
6292        let tags = ch.type_tag.values();
6293        assert_eq!(tags.len(), 5);
6294        assert_eq!(tags[0], TypeTag::Number as u8);
6295        assert_eq!(tags[1], TypeTag::Empty as u8);
6296        assert_eq!(tags[2], TypeTag::Text as u8);
6297        assert_eq!(tags[3], TypeTag::Boolean as u8);
6298        assert_eq!(tags[4], TypeTag::Error as u8);
6299
6300        // Numbers lane validity
6301        let nums = ch.numbers.as_ref().unwrap();
6302        assert_eq!(nums.len(), 5);
6303        assert_eq!(nums.null_count(), 4);
6304        assert!(nums.is_valid(0));
6305
6306        // Booleans lane validity
6307        let bools = ch.booleans.as_ref().unwrap();
6308        assert_eq!(bools.len(), 5);
6309        assert_eq!(bools.null_count(), 4);
6310        assert!(bools.is_valid(3));
6311
6312        // Text lane validity
6313        let txt = ch.text.as_ref().unwrap();
6314        assert_eq!(txt.len(), 5);
6315        assert_eq!(txt.null_count(), 4);
6316        assert!(txt.is_valid(2)); // ""
6317
6318        // Errors lane
6319        let errs = ch.errors.as_ref().unwrap();
6320        assert_eq!(errs.len(), 5);
6321        assert_eq!(errs.null_count(), 4);
6322        assert!(errs.is_valid(4));
6323    }
6324
6325    #[test]
6326    fn range_view_get_cell_and_padding() {
6327        let mut b = IngestBuilder::new("S", 2, 2, crate::engine::DateSystem::Excel1900);
6328        b.append_row(&[LiteralValue::Number(1.0), LiteralValue::Text("".into())])
6329            .unwrap();
6330        b.append_row(&[LiteralValue::Empty, LiteralValue::Text("x".into())])
6331            .unwrap();
6332        b.append_row(&[LiteralValue::Boolean(true), LiteralValue::Empty])
6333            .unwrap();
6334        let sheet = b.finish();
6335        let rv = sheet.range_view(0, 0, 2, 1);
6336        assert_eq!(rv.dims(), (3, 2));
6337        // Inside
6338        assert_eq!(rv.get_cell(0, 0), LiteralValue::Number(1.0));
6339        assert_eq!(rv.get_cell(0, 1), LiteralValue::Text(String::new())); // empty string
6340        assert_eq!(rv.get_cell(1, 0), LiteralValue::Empty); // truly Empty
6341        assert_eq!(rv.get_cell(2, 0), LiteralValue::Boolean(true));
6342        // OOB padding
6343        assert_eq!(rv.get_cell(3, 0), LiteralValue::Empty);
6344        assert_eq!(rv.get_cell(0, 2), LiteralValue::Empty);
6345
6346        // Numbers slices should produce one 2-row and one 1-row segment
6347        let nums: Vec<_> = rv.numbers_slices().map(|r| r.unwrap()).collect();
6348        assert_eq!(nums.len(), 2);
6349        assert_eq!(nums[0].0, 0);
6350        assert_eq!(nums[0].1, 2);
6351        assert_eq!(nums[1].0, 2);
6352        assert_eq!(nums[1].1, 1);
6353    }
6354
6355    #[test]
6356    fn overlay_precedence_user_over_computed() {
6357        let mut b = IngestBuilder::new("S", 1, 8, crate::engine::DateSystem::Excel1900);
6358        b.append_row(&[LiteralValue::Number(1.0)]).unwrap();
6359        b.append_row(&[LiteralValue::Empty]).unwrap();
6360        b.append_row(&[LiteralValue::Empty]).unwrap();
6361        let mut sheet = b.finish();
6362
6363        let (ch_i, off) = sheet.chunk_of_row(0).unwrap();
6364        sheet.columns[0].chunks[ch_i]
6365            .computed_overlay
6366            .set(off, OverlayValue::Number(2.0));
6367
6368        let rv0 = sheet.range_view(0, 0, 0, 0);
6369        assert_eq!(rv0.get_cell(0, 0), LiteralValue::Number(2.0));
6370        let nums0: Vec<_> = rv0.numbers_slices().map(|r| r.unwrap()).collect();
6371        assert_eq!(nums0.len(), 1);
6372        assert_eq!(nums0[0].2[0].value(0), 2.0);
6373
6374        sheet.columns[0].chunks[ch_i]
6375            .overlay
6376            .set(off, OverlayValue::Number(3.0));
6377
6378        let rv1 = sheet.range_view(0, 0, 0, 0);
6379        assert_eq!(rv1.get_cell(0, 0), LiteralValue::Number(3.0));
6380        let nums1: Vec<_> = rv1.numbers_slices().map(|r| r.unwrap()).collect();
6381        assert_eq!(nums1.len(), 1);
6382        assert_eq!(nums1[0].2[0].value(0), 3.0);
6383    }
6384
6385    #[test]
6386    fn overlay_slice_preserves_explicit_empty_and_offsets() {
6387        let mut overlay = Overlay::new();
6388        overlay.set(2, OverlayValue::Number(2.0));
6389        overlay.set(4, OverlayValue::Empty);
6390        overlay.set(6, OverlayValue::Text(Arc::from("outside")));
6391
6392        let sliced = overlay.slice(1, 4);
6393        assert!(sliced.get_scalar(0).is_none());
6394        assert_eq!(
6395            sliced.get_scalar(1).unwrap().to_literal(),
6396            LiteralValue::Number(2.0)
6397        );
6398        assert_eq!(
6399            sliced.get_scalar(3).unwrap().to_literal(),
6400            LiteralValue::Empty
6401        );
6402        assert!(sliced.get_scalar(5).is_none());
6403    }
6404
6405    #[test]
6406    fn overlay_cascade_user_empty_masks_computed_and_base() {
6407        let mut user = Overlay::new();
6408        let mut computed = Overlay::new();
6409        computed.set(1, OverlayValue::Number(42.0));
6410        user.set(1, OverlayValue::Empty);
6411
6412        let cascade = OverlayCascade::new(&user, &computed);
6413        assert_eq!(
6414            cascade.get_scalar(1).unwrap().to_literal(),
6415            LiteralValue::Empty
6416        );
6417        assert!(cascade.has_any_in_range(1..2));
6418    }
6419
6420    #[test]
6421    fn overlay_storage_pointmap_backward_compat_get_set_remove() {
6422        let mut overlay = Overlay::new();
6423        assert!(overlay.is_empty());
6424
6425        let delta = overlay.set_scalar(1, OverlayValue::Number(10.0));
6426        assert!(delta > 0);
6427        assert_eq!(overlay.len(), 1);
6428        assert_eq!(
6429            overlay.get_scalar(1).unwrap().to_literal(),
6430            LiteralValue::Number(10.0)
6431        );
6432
6433        let replace_delta = overlay.set_scalar(1, OverlayValue::Text(Arc::from("x")));
6434        assert_ne!(replace_delta, 0);
6435        assert_eq!(overlay.len(), 1);
6436        assert_eq!(
6437            overlay.get_scalar(1).unwrap().to_literal(),
6438            LiteralValue::Text("x".into())
6439        );
6440
6441        let remove_delta = overlay.remove_scalar(1);
6442        assert!(remove_delta < 0);
6443        assert!(overlay.is_empty());
6444        assert!(overlay.get_scalar(1).is_none());
6445    }
6446
6447    #[test]
6448    fn overlay_remove_range_splits_fragments_and_points() {
6449        let mut overlay = Overlay::new();
6450        overlay.set_scalar(2, OverlayValue::Number(20.0));
6451        overlay.apply_fragment(
6452            OverlayFragment::dense_range(
6453                0,
6454                (0..6)
6455                    .map(|i| OverlayValue::Number(i as f64))
6456                    .collect::<Vec<_>>(),
6457            )
6458            .unwrap(),
6459        );
6460        overlay.set_scalar(3, OverlayValue::Number(30.0));
6461        overlay.set_scalar(8, OverlayValue::Number(80.0));
6462
6463        let delta = overlay.remove_range(2..5);
6464
6465        assert!(delta < 0);
6466        assert_eq!(
6467            overlay.get_scalar(0).unwrap().to_literal(),
6468            LiteralValue::Number(0.0)
6469        );
6470        assert_eq!(
6471            overlay.get_scalar(1).unwrap().to_literal(),
6472            LiteralValue::Number(1.0)
6473        );
6474        assert!(overlay.get_scalar(2).is_none());
6475        assert!(overlay.get_scalar(3).is_none());
6476        assert!(overlay.get_scalar(4).is_none());
6477        assert_eq!(
6478            overlay.get_scalar(5).unwrap().to_literal(),
6479            LiteralValue::Number(5.0)
6480        );
6481        assert_eq!(
6482            overlay.get_scalar(8).unwrap().to_literal(),
6483            LiteralValue::Number(80.0)
6484        );
6485        assert!(overlay.debug_is_normalized());
6486        assert_eq!(
6487            overlay.estimated_bytes(),
6488            overlay.debug_recomputed_estimated_bytes()
6489        );
6490    }
6491
6492    #[test]
6493    fn overlay_storage_no_fragments_behavior_matches_old_map() {
6494        let mut overlay = Overlay::new();
6495        overlay.set_scalar(0, OverlayValue::Number(1.0));
6496        overlay.set_scalar(3, OverlayValue::Empty);
6497
6498        assert!(overlay.has_any_in_range(0..1));
6499        assert!(!overlay.has_any_in_range(1..3));
6500        assert!(overlay.has_any_in_range(3..4));
6501
6502        let sliced = overlay.slice(2, 3);
6503        assert!(sliced.get_scalar(0).is_none());
6504        assert_eq!(
6505            sliced.get_scalar(1).unwrap().to_literal(),
6506            LiteralValue::Empty
6507        );
6508    }
6509
6510    #[test]
6511    fn overlay_cascade_user_layer_masks_computed_fragment_regardless_of_sequence() {
6512        let mut user = Overlay::new();
6513        let mut computed = Overlay::new();
6514
6515        user.set_scalar(0, OverlayValue::Number(3.0));
6516        computed.apply_fragment(
6517            OverlayFragment::dense_range(0, vec![OverlayValue::Number(2.0)]).unwrap(),
6518        );
6519
6520        let cascade = OverlayCascade::new(&user, &computed);
6521        assert_eq!(
6522            cascade.get_scalar(0).unwrap().to_literal(),
6523            LiteralValue::Number(3.0)
6524        );
6525    }
6526
6527    #[test]
6528    fn overlay_same_layer_later_point_replaces_fragment_cell() {
6529        let mut overlay = Overlay::new();
6530        overlay.apply_fragment(
6531            OverlayFragment::dense_range(
6532                0,
6533                vec![
6534                    OverlayValue::Number(1.0),
6535                    OverlayValue::Number(2.0),
6536                    OverlayValue::Number(3.0),
6537                ],
6538            )
6539            .unwrap(),
6540        );
6541
6542        overlay.set_scalar(1, OverlayValue::Number(99.0));
6543
6544        assert_eq!(
6545            overlay.get_scalar(0).unwrap().to_literal(),
6546            LiteralValue::Number(1.0)
6547        );
6548        assert_eq!(
6549            overlay.get_scalar(1).unwrap().to_literal(),
6550            LiteralValue::Number(99.0)
6551        );
6552        assert_eq!(
6553            overlay.get_scalar(2).unwrap().to_literal(),
6554            LiteralValue::Number(3.0)
6555        );
6556    }
6557
6558    #[test]
6559    fn overlay_same_layer_later_fragment_replaces_point_range() {
6560        let mut overlay = Overlay::new();
6561        overlay.set_scalar(0, OverlayValue::Number(1.0));
6562        overlay.set_scalar(1, OverlayValue::Number(2.0));
6563        overlay.set_scalar(2, OverlayValue::Number(3.0));
6564
6565        overlay.apply_fragment(
6566            OverlayFragment::dense_range(
6567                0,
6568                vec![
6569                    OverlayValue::Number(10.0),
6570                    OverlayValue::Number(20.0),
6571                    OverlayValue::Number(30.0),
6572                ],
6573            )
6574            .unwrap(),
6575        );
6576
6577        let stats = overlay.debug_stats();
6578        assert_eq!(stats.points, 0);
6579        assert_eq!(stats.dense_fragments, 1);
6580        assert!(overlay.debug_is_normalized());
6581        assert_eq!(
6582            overlay.get_scalar(0).unwrap().to_literal(),
6583            LiteralValue::Number(10.0)
6584        );
6585        assert_eq!(
6586            overlay.get_scalar(1).unwrap().to_literal(),
6587            LiteralValue::Number(20.0)
6588        );
6589        assert_eq!(
6590            overlay.get_scalar(2).unwrap().to_literal(),
6591            LiteralValue::Number(30.0)
6592        );
6593    }
6594
6595    #[test]
6596    fn overlay_sparse_far_apart_replacement_does_not_rewrite_unrelated_dense_fragment() {
6597        let mut overlay = Overlay::new();
6598        overlay.apply_fragment(
6599            OverlayFragment::dense_range(100, vec![OverlayValue::Number(1.0); 10]).unwrap(),
6600        );
6601
6602        overlay.apply_fragment(
6603            OverlayFragment::sparse_offsets(vec![
6604                (0, OverlayValue::Empty),
6605                (1000, OverlayValue::Number(1000.0)),
6606            ])
6607            .unwrap(),
6608        );
6609
6610        let stats = overlay.debug_stats();
6611        assert_eq!(stats.dense_fragments, 1);
6612        assert_eq!(stats.sparse_fragments, 1);
6613        assert_eq!(stats.run_fragments, 0);
6614        assert!(overlay.debug_is_normalized());
6615        assert_eq!(
6616            overlay.get_scalar(105).unwrap().to_literal(),
6617            LiteralValue::Number(1.0)
6618        );
6619        assert_eq!(
6620            overlay.get_scalar(0).unwrap().to_literal(),
6621            LiteralValue::Empty
6622        );
6623        assert_eq!(
6624            overlay.get_scalar(1000).unwrap().to_literal(),
6625            LiteralValue::Number(1000.0)
6626        );
6627    }
6628
6629    #[test]
6630    fn overlay_sparse_offsets_are_sorted_unique_last_write_wins() {
6631        let mut overlay = Overlay::new();
6632        overlay.apply_fragment(
6633            OverlayFragment::sparse_offsets(vec![
6634                (3, OverlayValue::Number(3.0)),
6635                (1, OverlayValue::Number(1.0)),
6636                (3, OverlayValue::Number(33.0)),
6637            ])
6638            .unwrap(),
6639        );
6640
6641        let stats = overlay.debug_stats();
6642        assert_eq!(stats.sparse_fragments, 1);
6643        assert_eq!(overlay.len(), 2);
6644        assert_eq!(
6645            overlay.get_scalar(1).unwrap().to_literal(),
6646            LiteralValue::Number(1.0)
6647        );
6648        assert_eq!(
6649            overlay.get_scalar(3).unwrap().to_literal(),
6650            LiteralValue::Number(33.0)
6651        );
6652        assert!(overlay.debug_is_normalized());
6653    }
6654
6655    #[test]
6656    fn overlay_dense_point_replacement_shadows_without_splitting() {
6657        let mut overlay = Overlay::new();
6658        overlay.apply_fragment(
6659            OverlayFragment::dense_range(
6660                0,
6661                (0..6)
6662                    .map(|i| OverlayValue::Number(i as f64))
6663                    .collect::<Vec<_>>(),
6664            )
6665            .unwrap(),
6666        );
6667
6668        overlay.set_scalar(3, OverlayValue::Number(99.0));
6669
6670        // The point shadows the fragment (no O(fragment) split per write).
6671        let stats = overlay.debug_stats();
6672        assert_eq!(stats.points, 1);
6673        assert_eq!(stats.dense_fragments, 1);
6674        assert_eq!(stats.sparse_fragments, 0);
6675        assert_eq!(stats.covered_len, 6);
6676        assert!(overlay.debug_is_normalized());
6677        assert_eq!(
6678            overlay.get_scalar(2).unwrap().to_literal(),
6679            LiteralValue::Number(2.0)
6680        );
6681        assert_eq!(
6682            overlay.get_scalar(3).unwrap().to_literal(),
6683            LiteralValue::Number(99.0)
6684        );
6685        assert_eq!(
6686            overlay.get_scalar(4).unwrap().to_literal(),
6687            LiteralValue::Number(4.0)
6688        );
6689    }
6690
6691    #[test]
6692    fn overlay_dense_fragment_replacement_splits_left_and_right_dense() {
6693        let mut overlay = Overlay::new();
6694        overlay.apply_fragment(
6695            OverlayFragment::dense_range(
6696                0,
6697                (0..8)
6698                    .map(|i| OverlayValue::Number(i as f64))
6699                    .collect::<Vec<_>>(),
6700            )
6701            .unwrap(),
6702        );
6703
6704        overlay.apply_fragment(
6705            OverlayFragment::dense_range(
6706                3,
6707                vec![OverlayValue::Number(30.0), OverlayValue::Number(40.0)],
6708            )
6709            .unwrap(),
6710        );
6711
6712        let stats = overlay.debug_stats();
6713        assert_eq!(stats.points, 0);
6714        assert_eq!(stats.dense_fragments, 3);
6715        assert_eq!(stats.sparse_fragments, 0);
6716        assert!(overlay.debug_is_normalized());
6717        assert_eq!(
6718            overlay.get_scalar(2).unwrap().to_literal(),
6719            LiteralValue::Number(2.0)
6720        );
6721        assert_eq!(
6722            overlay.get_scalar(3).unwrap().to_literal(),
6723            LiteralValue::Number(30.0)
6724        );
6725        assert_eq!(
6726            overlay.get_scalar(4).unwrap().to_literal(),
6727            LiteralValue::Number(40.0)
6728        );
6729        assert_eq!(
6730            overlay.get_scalar(5).unwrap().to_literal(),
6731            LiteralValue::Number(5.0)
6732        );
6733    }
6734
6735    #[test]
6736    fn overlay_run_point_replacement_shadows_without_splitting() {
6737        let mut overlay = Overlay::new();
6738        overlay.apply_fragment(
6739            OverlayFragment::run_range(0, vec![OverlayValue::Number(1.0); 10]).unwrap(),
6740        );
6741
6742        overlay.set_scalar(5, OverlayValue::Number(99.0));
6743
6744        let stats = overlay.debug_stats();
6745        assert_eq!(stats.points, 1);
6746        assert_eq!(stats.run_fragments, 1);
6747        assert_eq!(stats.sparse_fragments, 0);
6748        assert_eq!(stats.covered_len, 10);
6749        assert!(overlay.debug_is_normalized());
6750        assert_eq!(
6751            overlay.get_scalar(4).unwrap().to_literal(),
6752            LiteralValue::Number(1.0)
6753        );
6754        assert_eq!(
6755            overlay.get_scalar(5).unwrap().to_literal(),
6756            LiteralValue::Number(99.0)
6757        );
6758        assert_eq!(
6759            overlay.get_scalar(6).unwrap().to_literal(),
6760            LiteralValue::Number(1.0)
6761        );
6762    }
6763
6764    #[test]
6765    fn overlay_point_removal_splits_dense_and_run_zero_copy() {
6766        for run in [false, true] {
6767            let values: Vec<_> = (0..1000)
6768                .map(|i| OverlayValue::Number(if run { (i / 10) as f64 } else { i as f64 }))
6769                .collect();
6770            let fragment = if run {
6771                OverlayFragment::run_range(0, values).unwrap()
6772            } else {
6773                OverlayFragment::dense_range(0, values).unwrap()
6774            };
6775            let mut overlay = Overlay::new();
6776            overlay.apply_fragment(fragment);
6777            overlay.remove_scalar(400);
6778            overlay.remove_scalar(10);
6779            let stats = overlay.debug_stats();
6780            assert_eq!(stats.points, 0);
6781            assert_eq!(stats.covered_len, 998);
6782            assert!(overlay.debug_is_normalized());
6783            assert_eq!(
6784                overlay.estimated_bytes(),
6785                overlay.debug_recomputed_estimated_bytes()
6786            );
6787            for i in 0..1000usize {
6788                let got = overlay.get_scalar(i).map(|v| v.to_literal());
6789                if i == 400 || i == 10 {
6790                    assert!(got.is_none(), "{run} {i}");
6791                } else {
6792                    let want = if run { (i / 10) as f64 } else { i as f64 };
6793                    assert_eq!(got, Some(LiteralValue::Number(want)), "{run} {i}");
6794                }
6795            }
6796            // Pieces of a split share the parent's lanes; a small piece
6797            // (under a quarter of them) is copied.
6798            let mut pieces: Vec<_> = overlay
6799                .fragments
6800                .iter()
6801                .map(|f| {
6802                    let p = match f {
6803                        OverlayFragment::DenseRange { payload, .. }
6804                        | OverlayFragment::RunRange { payload, .. } => payload,
6805                        OverlayFragment::SparseOffsets { .. } => unreachable!(),
6806                    };
6807                    (f.coverage_len(), p.root_len)
6808                })
6809                .collect();
6810            pieces.sort();
6811            let root = if run { 100 } else { 1000 };
6812            let small = if run { 1 } else { 10 };
6813            assert_eq!(pieces[0], (10, small), "{run}");
6814            assert!(
6815                pieces[1..].iter().all(|(_, r)| *r == root),
6816                "{run} {pieces:?}"
6817            );
6818        }
6819    }
6820
6821    #[test]
6822    fn overlay_shadowed_points_fold_into_fragments() {
6823        for run in [false, true] {
6824            let n = 400usize;
6825            let base = |i: usize| if run { 7.0 } else { i as f64 };
6826            let values: Vec<_> = (0..n).map(|i| OverlayValue::Number(base(i))).collect();
6827            let fragment = if run {
6828                OverlayFragment::run_range(0, values).unwrap()
6829            } else {
6830                OverlayFragment::dense_range(0, values).unwrap()
6831            };
6832            let mut overlay = Overlay::new();
6833            overlay.apply_fragment(fragment);
6834            overlay.set_scalar(n + 5, OverlayValue::Number(-1.0));
6835            let mut delta_sum = 0isize;
6836            let start_bytes = overlay.estimated_bytes() as isize;
6837            // Threshold is max(32, 400 / 8) = 50 shadowed points.
6838            for k in 0..49usize {
6839                delta_sum += overlay.set_scalar(k * 7, OverlayValue::Number(1000.0 + k as f64));
6840            }
6841            assert_eq!(overlay.debug_stats().points, 50);
6842            assert!(overlay.debug_is_normalized());
6843            delta_sum += overlay.set_scalar(49 * 7, OverlayValue::Text(Arc::from("t")));
6844            let stats = overlay.debug_stats();
6845            assert_eq!(stats.points, 1, "only the unshadowed point stays");
6846            assert_eq!(stats.covered_len, n + 1);
6847            assert!(overlay.debug_is_normalized());
6848            assert_eq!(
6849                overlay.estimated_bytes(),
6850                overlay.debug_recomputed_estimated_bytes()
6851            );
6852            assert_eq!(overlay.estimated_bytes() as isize, start_bytes + delta_sum);
6853            for i in 0..n {
6854                let want = if i % 7 == 0 && i / 7 < 49 {
6855                    LiteralValue::Number(1000.0 + (i / 7) as f64)
6856                } else if i == 49 * 7 {
6857                    LiteralValue::Text("t".into())
6858                } else {
6859                    LiteralValue::Number(base(i))
6860                };
6861                assert_eq!(
6862                    overlay.get_scalar(i).unwrap().to_literal(),
6863                    want,
6864                    "{run} {i}"
6865                );
6866            }
6867            // Re-setting a shadowed offset, then applying a covering fragment,
6868            // keeps the shadow count exact.
6869            overlay.set_scalar(3, OverlayValue::Number(3.5));
6870            overlay.set_scalar(3, OverlayValue::Number(4.5));
6871            assert!(overlay.debug_is_normalized());
6872            overlay.apply_fragment(
6873                OverlayFragment::dense_range(0, vec![OverlayValue::Number(0.0); 8]).unwrap(),
6874            );
6875            assert!(overlay.debug_is_normalized());
6876            overlay.set_scalar(20, OverlayValue::Number(1.0));
6877            overlay.remove_range(15..25);
6878            assert!(overlay.debug_is_normalized());
6879            assert!(overlay.get_scalar(20).is_none());
6880            overlay.set_scalar(30, OverlayValue::Number(1.0));
6881            overlay.remove_scalar(30);
6882            assert!(overlay.get_scalar(30).is_none());
6883            assert!(overlay.debug_is_normalized());
6884            assert_eq!(
6885                overlay.estimated_bytes(),
6886                overlay.debug_recomputed_estimated_bytes()
6887            );
6888        }
6889    }
6890
6891    #[test]
6892    fn overlay_run_fragment_replacement_splits_left_and_right_run() {
6893        let mut overlay = Overlay::new();
6894        let values = [
6895            vec![OverlayValue::Number(1.0); 4],
6896            vec![OverlayValue::Number(2.0); 4],
6897            vec![OverlayValue::Number(3.0); 4],
6898        ]
6899        .concat();
6900        overlay.apply_fragment(OverlayFragment::run_range(0, values).unwrap());
6901
6902        overlay.apply_fragment(
6903            OverlayFragment::dense_range(
6904                5,
6905                vec![OverlayValue::Number(50.0), OverlayValue::Number(60.0)],
6906            )
6907            .unwrap(),
6908        );
6909
6910        let stats = overlay.debug_stats();
6911        assert_eq!(stats.run_fragments, 2);
6912        assert_eq!(stats.dense_fragments, 1);
6913        assert_eq!(stats.sparse_fragments, 0);
6914        assert!(overlay.debug_is_normalized());
6915        assert_eq!(
6916            overlay.get_scalar(4).unwrap().to_literal(),
6917            LiteralValue::Number(2.0)
6918        );
6919        assert_eq!(
6920            overlay.get_scalar(5).unwrap().to_literal(),
6921            LiteralValue::Number(50.0)
6922        );
6923        assert_eq!(
6924            overlay.get_scalar(6).unwrap().to_literal(),
6925            LiteralValue::Number(60.0)
6926        );
6927        assert_eq!(
6928            overlay.get_scalar(7).unwrap().to_literal(),
6929            LiteralValue::Number(2.0)
6930        );
6931    }
6932
6933    #[test]
6934    fn overlay_slice_preserves_dense_and_run_encodings() {
6935        let mut overlay = Overlay::new();
6936        overlay.apply_fragment(
6937            OverlayFragment::dense_range(
6938                10,
6939                (0..5)
6940                    .map(|i| OverlayValue::Number(i as f64))
6941                    .collect::<Vec<_>>(),
6942            )
6943            .unwrap(),
6944        );
6945        overlay.apply_fragment(
6946            OverlayFragment::run_range(
6947                20,
6948                [
6949                    vec![OverlayValue::Number(1.0); 3],
6950                    vec![OverlayValue::Number(2.0); 3],
6951                ]
6952                .concat(),
6953            )
6954            .unwrap(),
6955        );
6956
6957        let dense_slice = overlay.slice(12, 2);
6958        let dense_stats = dense_slice.debug_stats();
6959        assert_eq!(dense_stats.dense_fragments, 1);
6960        assert_eq!(dense_stats.sparse_fragments, 0);
6961        assert_eq!(
6962            dense_slice.get_scalar(0).unwrap().to_literal(),
6963            LiteralValue::Number(2.0)
6964        );
6965        assert_eq!(
6966            dense_slice.get_scalar(1).unwrap().to_literal(),
6967            LiteralValue::Number(3.0)
6968        );
6969        assert!(dense_slice.debug_is_normalized());
6970
6971        let run_slice = overlay.slice(22, 3);
6972        let run_stats = run_slice.debug_stats();
6973        assert_eq!(run_stats.run_fragments, 1);
6974        assert_eq!(run_stats.sparse_fragments, 0);
6975        assert_eq!(
6976            run_slice.get_scalar(0).unwrap().to_literal(),
6977            LiteralValue::Number(1.0)
6978        );
6979        assert_eq!(
6980            run_slice.get_scalar(1).unwrap().to_literal(),
6981            LiteralValue::Number(2.0)
6982        );
6983        assert_eq!(
6984            run_slice.get_scalar(2).unwrap().to_literal(),
6985            LiteralValue::Number(2.0)
6986        );
6987        assert!(run_slice.debug_is_normalized());
6988    }
6989
6990    #[test]
6991    fn overlay_computed_empty_run_masks_non_empty_base() {
6992        let mut b = IngestBuilder::new("S", 1, 8, crate::engine::DateSystem::Excel1900);
6993        b.append_row(&[LiteralValue::Number(1.0)]).unwrap();
6994        b.append_row(&[LiteralValue::Number(2.0)]).unwrap();
6995        b.append_row(&[LiteralValue::Number(3.0)]).unwrap();
6996        let mut sheet = b.finish();
6997
6998        let (ch_i, _) = sheet.chunk_of_row(0).unwrap();
6999        sheet.columns[0].chunks[ch_i]
7000            .computed_overlay
7001            .apply_fragment(
7002                OverlayFragment::run_range(
7003                    0,
7004                    vec![
7005                        OverlayValue::Empty,
7006                        OverlayValue::Empty,
7007                        OverlayValue::Empty,
7008                    ],
7009                )
7010                .unwrap(),
7011            );
7012
7013        assert_eq!(sheet.get_cell_value(0, 0), LiteralValue::Empty);
7014        assert_eq!(sheet.get_cell_value(1, 0), LiteralValue::Empty);
7015        assert_eq!(sheet.get_cell_value(2, 0), LiteralValue::Empty);
7016    }
7017
7018    #[test]
7019    fn overlay_fragments_reconstruct_scalars_from_typed_lanes() {
7020        let values = vec![
7021            OverlayValue::Empty,
7022            OverlayValue::Number(1.5),
7023            OverlayValue::DateTime(45000.25),
7024            OverlayValue::Duration(0.5),
7025            OverlayValue::Boolean(true),
7026            OverlayValue::Text(Arc::from("Hello")),
7027            OverlayValue::Error(map_error_code(ExcelErrorKind::Div)),
7028            OverlayValue::Pending,
7029        ];
7030
7031        let mut dense = Overlay::new();
7032        dense.apply_fragment(OverlayFragment::dense_range(0, values.clone()).unwrap());
7033        for (idx, expected) in values.iter().enumerate() {
7034            assert_eq!(
7035                dense.get_scalar(idx).unwrap().to_overlay_value(),
7036                expected.clone()
7037            );
7038        }
7039
7040        let mut sparse = Overlay::new();
7041        sparse.apply_fragment(
7042            OverlayFragment::sparse_offsets(
7043                values
7044                    .iter()
7045                    .cloned()
7046                    .enumerate()
7047                    .map(|(idx, value)| (idx * 2, value))
7048                    .collect(),
7049            )
7050            .unwrap(),
7051        );
7052        for (idx, expected) in values.iter().enumerate() {
7053            assert_eq!(
7054                sparse.get_scalar(idx * 2).unwrap().to_overlay_value(),
7055                expected.clone()
7056            );
7057        }
7058
7059        let mut run = Overlay::new();
7060        run.apply_fragment(
7061            OverlayFragment::run_range(
7062                0,
7063                vec![
7064                    OverlayValue::Number(7.0),
7065                    OverlayValue::Number(7.0),
7066                    OverlayValue::Text(Arc::from("run")),
7067                    OverlayValue::Text(Arc::from("run")),
7068                ],
7069            )
7070            .unwrap(),
7071        );
7072        assert_eq!(
7073            run.get_scalar(0).unwrap().to_overlay_value(),
7074            OverlayValue::Number(7.0)
7075        );
7076        assert_eq!(
7077            run.get_scalar(2).unwrap().to_overlay_value(),
7078            OverlayValue::Text(Arc::from("run"))
7079        );
7080    }
7081
7082    #[test]
7083    fn overlay_iter_returns_complete_logical_entries() {
7084        let mut overlay = Overlay::new();
7085        overlay.apply_fragment(
7086            OverlayFragment::dense_range(
7087                2,
7088                vec![OverlayValue::Number(2.0), OverlayValue::Number(3.0)],
7089            )
7090            .unwrap(),
7091        );
7092        overlay.set_scalar(5, OverlayValue::Text(Arc::from("point")));
7093
7094        let entries: Vec<_> = overlay.iter().collect();
7095        assert_eq!(
7096            entries,
7097            vec![
7098                (2, OverlayValue::Number(2.0)),
7099                (3, OverlayValue::Number(3.0)),
7100                (5, OverlayValue::Text(Arc::from("point"))),
7101            ]
7102        );
7103        assert_eq!(overlay.iter_points().count(), 1);
7104    }
7105
7106    #[test]
7107    fn overlay_fragment_estimates_follow_encoded_shapes() {
7108        let mut points = Overlay::new();
7109        for idx in 0..512 {
7110            points.set_scalar(idx, OverlayValue::Number(idx as f64));
7111        }
7112
7113        let mut dense = Overlay::new();
7114        dense.apply_fragment(
7115            OverlayFragment::dense_range(
7116                0,
7117                (0..512)
7118                    .map(|idx| OverlayValue::Number(idx as f64))
7119                    .collect::<Vec<_>>(),
7120            )
7121            .unwrap(),
7122        );
7123        assert_eq!(
7124            dense.estimated_bytes(),
7125            dense.debug_recomputed_estimated_bytes()
7126        );
7127        assert!(
7128            dense.estimated_bytes() < points.estimated_bytes(),
7129            "dense fragment should account like encoded lanes, not point-map entries"
7130        );
7131
7132        let mut short_run = Overlay::new();
7133        short_run.apply_fragment(
7134            OverlayFragment::run_range(0, vec![OverlayValue::Number(1.0); 8]).unwrap(),
7135        );
7136        let mut long_run = Overlay::new();
7137        long_run.apply_fragment(
7138            OverlayFragment::run_range(0, vec![OverlayValue::Number(1.0); 4096]).unwrap(),
7139        );
7140        assert_eq!(
7141            short_run.estimated_bytes(),
7142            short_run.debug_recomputed_estimated_bytes()
7143        );
7144        assert_eq!(
7145            long_run.estimated_bytes(),
7146            long_run.debug_recomputed_estimated_bytes()
7147        );
7148        assert_eq!(
7149            short_run.estimated_bytes(),
7150            long_run.estimated_bytes(),
7151            "single-run estimate should scale with run count, not covered rows"
7152        );
7153
7154        let sparse10 = OverlayFragment::sparse_offsets(
7155            (0..10)
7156                .map(|idx| (idx * 3, OverlayValue::Number(idx as f64)))
7157                .collect(),
7158        )
7159        .unwrap();
7160        let sparse20 = OverlayFragment::sparse_offsets(
7161            (0..20)
7162                .map(|idx| (idx * 3, OverlayValue::Number(idx as f64)))
7163                .collect(),
7164        )
7165        .unwrap();
7166        assert!(sparse20.estimated_bytes() > sparse10.estimated_bytes());
7167    }
7168
7169    #[test]
7170    fn overlay_estimated_bytes_stay_consistent_after_split_and_clear() {
7171        let mut overlay = Overlay::new();
7172        overlay.apply_fragment(
7173            OverlayFragment::dense_range(
7174                0,
7175                (0..16)
7176                    .map(|idx| OverlayValue::Number(idx as f64))
7177                    .collect::<Vec<_>>(),
7178            )
7179            .unwrap(),
7180        );
7181        assert_eq!(
7182            overlay.estimated_bytes(),
7183            overlay.debug_recomputed_estimated_bytes()
7184        );
7185
7186        overlay.set_scalar(8, OverlayValue::Text(Arc::from("split")));
7187        assert!(overlay.debug_is_normalized());
7188        assert_eq!(
7189            overlay.estimated_bytes(),
7190            overlay.debug_recomputed_estimated_bytes()
7191        );
7192
7193        overlay.apply_fragment(
7194            OverlayFragment::sparse_offsets(vec![
7195                (0, OverlayValue::Empty),
7196                (15, OverlayValue::Boolean(true)),
7197            ])
7198            .unwrap(),
7199        );
7200        assert!(overlay.debug_is_normalized());
7201        assert_eq!(
7202            overlay.estimated_bytes(),
7203            overlay.debug_recomputed_estimated_bytes()
7204        );
7205
7206        let freed = overlay.clear_all();
7207        assert!(freed > 0);
7208        assert_eq!(overlay.estimated_bytes(), 0);
7209        assert_eq!(overlay.debug_recomputed_estimated_bytes(), 0);
7210        assert!(overlay.is_empty());
7211    }
7212
7213    #[test]
7214    fn overlay_segment_numbers_masks_base_for_non_numeric_overlays() {
7215        let mut user = Overlay::new();
7216        user.set(1, OverlayValue::Text(Arc::from("x")));
7217        user.set(2, OverlayValue::Empty);
7218        user.set(3, OverlayValue::Error(map_error_code(ExcelErrorKind::Div)));
7219        user.set(4, OverlayValue::Pending);
7220        let computed = Overlay::new();
7221        let cascade = OverlayCascade::new(&user, &computed);
7222
7223        let base = Float64Array::from(vec![10.0, 20.0, 30.0, 40.0, 50.0]);
7224        let selected = cascade.select_numbers(0..5, &base);
7225        assert_eq!(selected.value(0), 10.0);
7226        assert!(selected.is_null(1));
7227        assert!(selected.is_null(2));
7228        assert!(selected.is_null(3));
7229        assert!(selected.is_null(4));
7230    }
7231
7232    #[test]
7233    fn overlay_segment_type_tags_preserve_temporal_tags() {
7234        let mut computed = Overlay::new();
7235        computed.set(0, OverlayValue::DateTime(45000.5));
7236        computed.set(1, OverlayValue::Duration(0.25));
7237        let user = Overlay::new();
7238        let cascade = OverlayCascade::new(&user, &computed);
7239
7240        let base = UInt8Array::from(vec![TypeTag::Empty as u8; 2]);
7241        let selected = cascade.select_type_tags(0..2, &base);
7242        assert_eq!(selected.value(0), TypeTag::DateTime as u8);
7243        assert_eq!(selected.value(1), TypeTag::Duration as u8);
7244    }
7245
7246    #[test]
7247    fn overlay_lowered_text_matches_existing_overlay_semantics() {
7248        let mut user = Overlay::new();
7249        user.set(0, OverlayValue::Text(Arc::from("HeLLo")));
7250        user.set(1, OverlayValue::Number(1.5));
7251        user.set(2, OverlayValue::Boolean(true));
7252        user.set(3, OverlayValue::Empty);
7253        let computed = Overlay::new();
7254        let cascade = OverlayCascade::new(&user, &computed);
7255
7256        let base = StringArray::from(vec![Some("A"), Some("B"), Some("C"), Some("D")]);
7257        let selected = cascade.select_lowered_text(0..4, &base);
7258        assert_eq!(selected.value(0), "hello");
7259        assert_eq!(selected.value(1), "1.5");
7260        assert_eq!(selected.value(2), "true");
7261        assert!(selected.is_null(3));
7262    }
7263
7264    fn numeric_sheet(rows: usize) -> ArrowSheet {
7265        let mut b = IngestBuilder::new("S", 1, rows.max(1), crate::engine::DateSystem::Excel1900);
7266        for row in 0..rows {
7267            b.append_row(&[LiteralValue::Number((row + 1) as f64)])
7268                .unwrap();
7269        }
7270        b.finish()
7271    }
7272
7273    fn numbers_for_range(sheet: &ArrowSheet, sr: usize, er: usize) -> Arc<Float64Array> {
7274        let view = sheet.range_view(sr, 0, er, 0);
7275        let segments: Vec<_> = view.numbers_slices().map(|res| res.unwrap()).collect();
7276        assert_eq!(segments.len(), 1);
7277        assert_eq!(segments[0].2.len(), 1);
7278        segments[0].2[0].clone()
7279    }
7280
7281    fn type_tags_for_range(sheet: &ArrowSheet, sr: usize, er: usize) -> Arc<UInt8Array> {
7282        let view = sheet.range_view(sr, 0, er, 0);
7283        let segments: Vec<_> = view.type_tags_slices().map(|res| res.unwrap()).collect();
7284        assert_eq!(segments.len(), 1);
7285        assert_eq!(segments[0].2.len(), 1);
7286        segments[0].2[0].clone()
7287    }
7288
7289    fn lowered_for_range(sheet: &ArrowSheet, sr: usize, er: usize) -> Arc<StringArray> {
7290        let view = sheet.range_view(sr, 0, er, 0);
7291        let segments: Vec<_> = view.lowered_text_slices().map(|res| res.unwrap()).collect();
7292        assert_eq!(segments.len(), 1);
7293        assert_eq!(segments[0].2.len(), 1);
7294        segments[0].2[0].clone()
7295    }
7296
7297    #[test]
7298    fn rangeview_dense_text_masks_base_numbers() {
7299        let mut sheet = numeric_sheet(4);
7300        sheet.columns[0].chunks[0].computed_overlay.apply_fragment(
7301            OverlayFragment::dense_range(
7302                0,
7303                vec![
7304                    OverlayValue::Text(Arc::from("x")),
7305                    OverlayValue::Text(Arc::from("y")),
7306                    OverlayValue::Text(Arc::from("z")),
7307                    OverlayValue::Text(Arc::from("w")),
7308                ],
7309            )
7310            .unwrap(),
7311        );
7312
7313        reset_overlay_select_stats();
7314        let numbers = numbers_for_range(&sheet, 0, 3);
7315        assert_eq!(numbers.null_count(), 4);
7316        let stats = snapshot_overlay_select_stats();
7317        assert_eq!(stats.direct_dense_slices, 1);
7318        assert_eq!(stats.zip_select_calls, 0);
7319    }
7320
7321    #[test]
7322    fn rangeview_empty_dense_masks_base_all_selectors() {
7323        let mut sheet = numeric_sheet(3);
7324        sheet.columns[0].chunks[0]
7325            .computed_overlay
7326            .apply_fragment(OverlayFragment::dense_range(0, vec![OverlayValue::Empty; 3]).unwrap());
7327
7328        reset_overlay_select_stats();
7329        let numbers = numbers_for_range(&sheet, 0, 2);
7330        let type_tags = type_tags_for_range(&sheet, 0, 2);
7331        let lowered = lowered_for_range(&sheet, 0, 2);
7332        assert_eq!(numbers.null_count(), 3);
7333        assert_eq!(lowered.null_count(), 3);
7334        assert_eq!(type_tags.values(), &[TypeTag::Empty as u8; 3]);
7335        let stats = snapshot_overlay_select_stats();
7336        assert_eq!(stats.direct_dense_slices, 3);
7337        assert_eq!(stats.zip_select_calls, 0);
7338    }
7339
7340    #[test]
7341    fn rangeview_pending_masks_base_type_tag_present_lanes_null() {
7342        let mut sheet = numeric_sheet(2);
7343        sheet.columns[0].chunks[0].computed_overlay.apply_fragment(
7344            OverlayFragment::dense_range(0, vec![OverlayValue::Pending; 2]).unwrap(),
7345        );
7346
7347        reset_overlay_select_stats();
7348        let numbers = numbers_for_range(&sheet, 0, 1);
7349        let type_tags = type_tags_for_range(&sheet, 0, 1);
7350        let lowered = lowered_for_range(&sheet, 0, 1);
7351        assert_eq!(numbers.null_count(), 2);
7352        assert_eq!(lowered.null_count(), 2);
7353        assert_eq!(type_tags.values(), &[TypeTag::Pending as u8; 2]);
7354        let stats = snapshot_overlay_select_stats();
7355        assert_eq!(stats.direct_dense_slices, 3);
7356        assert_eq!(stats.zip_select_calls, 0);
7357    }
7358
7359    #[test]
7360    fn rangeview_subrange_inside_dense_fragment_uses_direct_path() {
7361        let mut sheet = numeric_sheet(10);
7362        sheet.columns[0].chunks[0].computed_overlay.apply_fragment(
7363            OverlayFragment::dense_range(
7364                0,
7365                (0..10)
7366                    .map(|row| OverlayValue::Number((row + 10) as f64))
7367                    .collect(),
7368            )
7369            .unwrap(),
7370        );
7371
7372        reset_overlay_select_stats();
7373        let numbers = numbers_for_range(&sheet, 2, 6);
7374        assert_eq!(numbers.len(), 5);
7375        assert_eq!(numbers.value(0), 12.0);
7376        assert_eq!(numbers.value(4), 16.0);
7377        let stats = snapshot_overlay_select_stats();
7378        assert_eq!(stats.direct_dense_slices, 1);
7379        assert_eq!(stats.zip_select_calls, 0);
7380    }
7381
7382    #[test]
7383    fn rangeview_subrange_inside_run_fragment_uses_direct_path() {
7384        let mut sheet = numeric_sheet(10);
7385        sheet.columns[0].chunks[0].computed_overlay.apply_fragment(
7386            OverlayFragment::run_range(0, vec![OverlayValue::Number(7.0); 10]).unwrap(),
7387        );
7388
7389        reset_overlay_select_stats();
7390        let numbers = numbers_for_range(&sheet, 2, 6);
7391        assert_eq!(numbers.len(), 5);
7392        for idx in 0..numbers.len() {
7393            assert_eq!(numbers.value(idx), 7.0);
7394        }
7395        let stats = snapshot_overlay_select_stats();
7396        assert_eq!(stats.direct_run_materializations, 1);
7397        assert_eq!(stats.zip_select_calls, 0);
7398    }
7399
7400    #[test]
7401    fn rangeview_user_partial_wrong_type_masks_computed_numeric() {
7402        let mut sheet = numeric_sheet(5);
7403        let chunk = &mut sheet.columns[0].chunks[0];
7404        chunk.computed_overlay.apply_fragment(
7405            OverlayFragment::dense_range(
7406                0,
7407                (0..5)
7408                    .map(|row| OverlayValue::Number((row + 10) as f64))
7409                    .collect(),
7410            )
7411            .unwrap(),
7412        );
7413        chunk.overlay.apply_fragment(
7414            OverlayFragment::dense_range(2, vec![OverlayValue::Text(Arc::from("mask"))]).unwrap(),
7415        );
7416
7417        reset_overlay_select_stats();
7418        let numbers = numbers_for_range(&sheet, 0, 4);
7419        assert_eq!(numbers.value(0), 10.0);
7420        assert_eq!(numbers.value(1), 11.0);
7421        assert!(numbers.is_null(2));
7422        assert_eq!(numbers.value(3), 13.0);
7423        assert_eq!(numbers.value(4), 14.0);
7424        let stats = snapshot_overlay_select_stats();
7425        assert_eq!(stats.direct_dense_slices, 0);
7426        assert_eq!(stats.zip_select_calls, 1);
7427        assert_eq!(stats.partial_dense_intersections, 2);
7428    }
7429
7430    #[test]
7431    fn rangeview_computed_full_cover_user_no_overlap_uses_computed_direct() {
7432        let mut sheet = numeric_sheet(5);
7433        let chunk = &mut sheet.columns[0].chunks[0];
7434        chunk.computed_overlay.apply_fragment(
7435            OverlayFragment::dense_range(0, vec![OverlayValue::Number(3.0); 5]).unwrap(),
7436        );
7437        chunk
7438            .overlay
7439            .set_scalar(10, OverlayValue::Text(Arc::from("outside")));
7440
7441        reset_overlay_select_stats();
7442        let numbers = numbers_for_range(&sheet, 0, 4);
7443        assert_eq!(numbers.value(0), 3.0);
7444        assert_eq!(numbers.value(4), 3.0);
7445        let stats = snapshot_overlay_select_stats();
7446        assert_eq!(stats.direct_dense_slices, 1);
7447        assert_eq!(stats.zip_select_calls, 0);
7448    }
7449
7450    #[test]
7451    fn rangeview_user_full_cover_ignores_computed() {
7452        let mut sheet = numeric_sheet(4);
7453        let chunk = &mut sheet.columns[0].chunks[0];
7454        chunk.computed_overlay.apply_fragment(
7455            OverlayFragment::dense_range(0, vec![OverlayValue::Number(99.0); 4]).unwrap(),
7456        );
7457        chunk.overlay.apply_fragment(
7458            OverlayFragment::dense_range(0, vec![OverlayValue::Text(Arc::from("user")); 4])
7459                .unwrap(),
7460        );
7461
7462        reset_overlay_select_stats();
7463        let numbers = numbers_for_range(&sheet, 0, 3);
7464        assert_eq!(numbers.null_count(), 4);
7465        let stats = snapshot_overlay_select_stats();
7466        assert_eq!(stats.direct_dense_slices, 1);
7467        assert_eq!(stats.zip_select_calls, 0);
7468    }
7469
7470    #[test]
7471    fn rangeview_point_overlay_still_matches_legacy_scalar_path() {
7472        let mut sheet = numeric_sheet(3);
7473        sheet.columns[0].chunks[0]
7474            .computed_overlay
7475            .set_scalar(1, OverlayValue::Text(Arc::from("point")));
7476
7477        reset_overlay_select_stats();
7478        let numbers = numbers_for_range(&sheet, 0, 2);
7479        assert_eq!(numbers.value(0), 1.0);
7480        assert!(numbers.is_null(1));
7481        assert_eq!(numbers.value(2), 3.0);
7482        let stats = snapshot_overlay_select_stats();
7483        // A short point-only range is merged per offset (Program 2), not
7484        // through the builders and zip.
7485        assert_eq!(stats.small_point_selects, 1);
7486        assert_eq!(stats.zip_select_calls, 0);
7487        assert_eq!(stats.point_entries_applied, 1);
7488        assert_eq!(stats.row_scalar_fallbacks, 0);
7489    }
7490
7491    #[test]
7492    fn points_only_patch_matches_per_offset_layering() {
7493        use rand::{Rng, SeedableRng, rngs::SmallRng};
7494        let mut rng = SmallRng::seed_from_u64(0x5eed);
7495        let pick = |rng: &mut SmallRng| match rng.gen_range(0..7) {
7496            0 => OverlayValue::Number(rng.gen_range(-5.0..5.0)),
7497            1 => OverlayValue::DateTime(rng.gen_range(0.0..5.0)),
7498            2 => OverlayValue::Text(Arc::from("t")),
7499            3 => OverlayValue::Empty,
7500            4 => OverlayValue::Error(rng.gen_range(1..8)),
7501            5 => OverlayValue::Boolean(rng.gen_bool(0.5)),
7502            _ => OverlayValue::Number(-0.0),
7503        };
7504        reset_overlay_select_stats();
7505        for case in 0..400 {
7506            let n = rng.gen_range(17..300);
7507            let total = n + 10;
7508            let base_nulls = case % 2 == 0;
7509            let nums: Vec<Option<f64>> = (0..total)
7510                .map(|i| (!base_nulls || !rng.gen_bool(0.3)).then_some(i as f64 + 0.5))
7511                .collect();
7512            let errs: Vec<Option<u8>> = (0..total)
7513                .map(|i| (!base_nulls || rng.gen_bool(0.3)).then_some((i % 7) as u8 + 1))
7514                .collect();
7515            let (nums, errs) = (Float64Array::from(nums), UInt8Array::from(errs));
7516            let start = rng.gen_range(0..10);
7517            let range = start..start + n;
7518            let (mut user, mut computed) = (Overlay::new(), Overlay::new());
7519            for _ in 0..rng.gen_range(1..40) {
7520                let off = rng.gen_range(0..total);
7521                let v = pick(&mut rng);
7522                if rng.gen_bool(0.5) {
7523                    user.set_scalar(off, v);
7524                } else {
7525                    computed.set_scalar(off, v);
7526                }
7527            }
7528            let cascade = OverlayCascade::new(&user, &computed);
7529            let point = |off: usize| user.points.get(&off).or(computed.points.get(&off));
7530            let base = nums.slice(start, n);
7531            let got = cascade.select_numbers(range.clone(), &base);
7532            assert_eq!(got.len(), n);
7533            for i in 0..n {
7534                let want = match point(start + i) {
7535                    Some(v) => v.numeric_lane_value(),
7536                    None => base.is_valid(i).then(|| base.value(i)),
7537                };
7538                let have = got.is_valid(i).then(|| got.value(i));
7539                assert_eq!(
7540                    have.map(f64::to_bits),
7541                    want.map(f64::to_bits),
7542                    "case {case} row {i}"
7543                );
7544            }
7545            let base = errs.slice(start, n);
7546            let got = cascade.select_errors(range.clone(), &base);
7547            for i in 0..n {
7548                let want = match point(start + i) {
7549                    Some(v) => v.error_lane_value(),
7550                    None => base.is_valid(i).then(|| base.value(i)),
7551                };
7552                assert_eq!(
7553                    got.is_valid(i).then(|| got.value(i)),
7554                    want,
7555                    "case {case} row {i}"
7556                );
7557            }
7558        }
7559        let stats = snapshot_overlay_select_stats();
7560        assert!(stats.point_patch_selects > 400, "{stats:?}");
7561        assert_eq!(stats.zip_select_calls, 0);
7562    }
7563
7564    #[test]
7565    fn rangeview_multi_fragment_full_union_does_not_use_direct_path() {
7566        let mut sheet = numeric_sheet(4);
7567        let chunk = &mut sheet.columns[0].chunks[0];
7568        chunk.computed_overlay.apply_fragment(
7569            OverlayFragment::dense_range(0, vec![OverlayValue::Number(10.0); 2]).unwrap(),
7570        );
7571        chunk.computed_overlay.apply_fragment(
7572            OverlayFragment::dense_range(2, vec![OverlayValue::Number(20.0); 2]).unwrap(),
7573        );
7574
7575        reset_overlay_select_stats();
7576        let numbers = numbers_for_range(&sheet, 0, 3);
7577        assert_eq!(numbers.value(0), 10.0);
7578        assert_eq!(numbers.value(1), 10.0);
7579        assert_eq!(numbers.value(2), 20.0);
7580        assert_eq!(numbers.value(3), 20.0);
7581        let stats = snapshot_overlay_select_stats();
7582        assert_eq!(stats.direct_dense_slices, 0);
7583        assert_eq!(stats.zip_select_calls, 1);
7584        assert_eq!(stats.partial_dense_intersections, 2);
7585    }
7586
7587    #[test]
7588    fn rangeview_lowered_text_fragment_semantics_match_scalar_semantics() {
7589        let mut sheet = numeric_sheet(8);
7590        sheet.columns[0].chunks[0].computed_overlay.apply_fragment(
7591            OverlayFragment::dense_range(
7592                0,
7593                vec![
7594                    OverlayValue::Text(Arc::from("HeLLo")),
7595                    OverlayValue::Number(1.5),
7596                    OverlayValue::DateTime(45000.25),
7597                    OverlayValue::Duration(0.5),
7598                    OverlayValue::Boolean(true),
7599                    OverlayValue::Empty,
7600                    OverlayValue::Error(map_error_code(ExcelErrorKind::Div)),
7601                    OverlayValue::Pending,
7602                ],
7603            )
7604            .unwrap(),
7605        );
7606
7607        reset_overlay_select_stats();
7608        let lowered = lowered_for_range(&sheet, 0, 7);
7609        assert_eq!(lowered.value(0), "hello");
7610        assert_eq!(lowered.value(1), "1.5");
7611        assert_eq!(lowered.value(2), "45000.25");
7612        assert_eq!(lowered.value(3), "0.5");
7613        assert_eq!(lowered.value(4), "true");
7614        assert!(lowered.is_null(5));
7615        assert!(lowered.is_null(6));
7616        assert!(lowered.is_null(7));
7617        let stats = snapshot_overlay_select_stats();
7618        assert_eq!(stats.direct_dense_slices, 1);
7619        assert_eq!(stats.zip_select_calls, 0);
7620    }
7621
7622    #[test]
7623    fn row_chunk_slices_shape() {
7624        // chunk_rows=2 leads to two slices for 3 rows
7625        let mut b = IngestBuilder::new("S", 2, 2, crate::engine::DateSystem::Excel1900);
7626        b.append_row(&[LiteralValue::Text("a".into()), LiteralValue::Number(1.0)])
7627            .unwrap();
7628        b.append_row(&[LiteralValue::Text("b".into()), LiteralValue::Number(2.0)])
7629            .unwrap();
7630        b.append_row(&[LiteralValue::Text("c".into()), LiteralValue::Number(3.0)])
7631            .unwrap();
7632        let sheet = b.finish();
7633        let rv = sheet.range_view(0, 0, 2, 1);
7634        let slices: Vec<_> = rv.iter_row_chunks().map(|r| r.unwrap()).collect();
7635        assert_eq!(slices.len(), 2);
7636        assert_eq!(slices[0].row_start, 0);
7637        assert_eq!(slices[0].row_len, 2);
7638        assert_eq!(slices[0].cols.len(), 2);
7639        assert_eq!(slices[1].row_start, 2);
7640        assert_eq!(slices[1].row_len, 1);
7641        assert_eq!(slices[1].cols.len(), 2);
7642    }
7643
7644    #[test]
7645    fn oob_columns_are_padded() {
7646        // Build with 2 columns; request 3 columns (ec beyond last col)
7647        let mut b = IngestBuilder::new("S", 2, 2, crate::engine::DateSystem::Excel1900);
7648        b.append_row(&[LiteralValue::Number(1.0), LiteralValue::Text("a".into())])
7649            .unwrap();
7650        b.append_row(&[LiteralValue::Number(2.0), LiteralValue::Text("b".into())])
7651            .unwrap();
7652        let sheet = b.finish();
7653        // Request cols [0..=2] → 3 columns with padding
7654        let rv = sheet.range_view(0, 0, 1, 2);
7655        assert_eq!(rv.dims(), (2, 3));
7656        let slices: Vec<_> = rv.iter_row_chunks().map(|r| r.unwrap()).collect();
7657        assert!(!slices.is_empty());
7658        for cs in &slices {
7659            assert_eq!(cs.cols.len(), 3);
7660        }
7661        // Also validate typed slices return 3 entries per segment
7662        for res in rv.numbers_slices() {
7663            let (_rs, _rl, cols) = res.unwrap();
7664            assert_eq!(cols.len(), 3);
7665        }
7666        for res in rv.booleans_slices() {
7667            let (_rs, _rl, cols) = res.unwrap();
7668            assert_eq!(cols.len(), 3);
7669        }
7670        for res in rv.text_slices() {
7671            let (_rs, _rl, cols) = res.unwrap();
7672            assert_eq!(cols.len(), 3);
7673        }
7674        for res in rv.errors_slices() {
7675            let (_rs, _rl, cols) = res.unwrap();
7676            assert_eq!(cols.len(), 3);
7677        }
7678        for res in rv.lowered_text_slices() {
7679            let (_rs, _rl, cols) = res.unwrap();
7680            assert_eq!(cols.len(), 3);
7681        }
7682    }
7683
7684    #[test]
7685    fn reversed_range_is_empty() {
7686        let mut b = IngestBuilder::new("S", 1, 4, crate::engine::DateSystem::Excel1900);
7687        b.append_row(&[LiteralValue::Number(1.0)]).unwrap();
7688        b.append_row(&[LiteralValue::Number(2.0)]).unwrap();
7689        let sheet = b.finish();
7690        let rv = sheet.range_view(3, 0, 1, 0); // er < sr
7691        assert_eq!(rv.dims(), (0, 0));
7692        assert!(rv.iter_row_chunks().next().is_none());
7693        assert_eq!(rv.get_cell(0, 0), LiteralValue::Empty);
7694    }
7695
7696    #[test]
7697    fn chunk_alignment_invariant() {
7698        let mut b = IngestBuilder::new("S", 3, 2, crate::engine::DateSystem::Excel1900);
7699        // 5 rows, 2-row chunks => 3 chunks (2,2,1)
7700        for r in 0..5 {
7701            b.append_row(&[
7702                LiteralValue::Number(r as f64),
7703                LiteralValue::Text(format!("{r}")),
7704                if r % 2 == 0 {
7705                    LiteralValue::Empty
7706                } else {
7707                    LiteralValue::Boolean(true)
7708                },
7709            ])
7710            .unwrap();
7711        }
7712        let sheet = b.finish();
7713        // chunk_starts should be [0,2,4]
7714        assert_eq!(sheet.chunk_starts, vec![0, 2, 4]);
7715        // All columns must share per-chunk lengths equal to [2,2,1]
7716        let lens0: Vec<usize> = sheet.columns[0]
7717            .chunks
7718            .iter()
7719            .map(|ch| ch.type_tag.len())
7720            .collect();
7721        for col in &sheet.columns[1..] {
7722            let lens: Vec<usize> = col.chunks.iter().map(|ch| ch.type_tag.len()).collect();
7723            assert_eq!(lens, lens0);
7724        }
7725    }
7726
7727    #[test]
7728    fn chunking_splits_rows() {
7729        // Two columns, chunk size 2 → expect two chunks
7730        let mut b = IngestBuilder::new("S", 2, 2, crate::engine::DateSystem::Excel1900);
7731        let rows = vec![
7732            vec![LiteralValue::Number(1.0), LiteralValue::Text("a".into())],
7733            vec![LiteralValue::Empty, LiteralValue::Text("b".into())],
7734            vec![LiteralValue::Boolean(true), LiteralValue::Empty],
7735        ];
7736        for r in rows {
7737            b.append_row(&r).unwrap();
7738        }
7739        let sheet = b.finish();
7740        assert_eq!(sheet.columns[0].chunks.len(), 2);
7741        assert_eq!(sheet.columns[1].chunks.len(), 2);
7742        assert_eq!(sheet.columns[0].chunks[0].numbers_or_null().len(), 2);
7743        assert_eq!(sheet.columns[0].chunks[1].numbers_or_null().len(), 1);
7744    }
7745
7746    #[test]
7747    fn pending_is_not_error() {
7748        let mut b = IngestBuilder::new("S", 1, 8, crate::engine::DateSystem::Excel1900);
7749        b.append_row(&[LiteralValue::Pending]).unwrap();
7750        let sheet = b.finish();
7751        let ch = &sheet.columns[0].chunks[0];
7752        // tag is Pending
7753        assert_eq!(ch.type_tag.values()[0], super::TypeTag::Pending as u8);
7754        // errors lane is effectively null
7755        let errs = ch.errors_or_null();
7756        assert_eq!(errs.null_count(), 1);
7757    }
7758
7759    #[test]
7760    fn all_null_numeric_lane_uses_null_array() {
7761        // Only text values in first column → numbers lane should be all null with correct dtype
7762        let mut b = IngestBuilder::new("S", 1, 16, crate::engine::DateSystem::Excel1900);
7763        b.append_row(&[LiteralValue::Text("a".into())]).unwrap();
7764        b.append_row(&[LiteralValue::Text("".into())]).unwrap();
7765        b.append_row(&[LiteralValue::Text("b".into())]).unwrap();
7766        let sheet = b.finish();
7767        let ch = &sheet.columns[0].chunks[0];
7768        let nums = ch.numbers_or_null();
7769        assert_eq!(nums.len(), 3);
7770        assert_eq!(nums.null_count(), 3);
7771        assert_eq!(nums.data_type(), &DataType::Float64);
7772    }
7773
7774    #[test]
7775    fn row_insert_delete_across_chunk_boundaries_with_overlays() {
7776        // Build 1 column, chunk size 4, 10 rows -> chunks at [0..4],[4..8],[8..10]
7777        let mut b = IngestBuilder::new("S", 1, 4, crate::engine::DateSystem::Excel1900);
7778        for _ in 0..10 {
7779            b.append_row(&[LiteralValue::Empty]).unwrap();
7780        }
7781        let mut sheet = b.finish();
7782        // Add overlays at row 3 and row 4
7783        {
7784            let (c0, o0) = sheet.chunk_of_row(3).unwrap();
7785            sheet.columns[0].chunks[c0]
7786                .overlay
7787                .set(o0, OverlayValue::Number(30.0));
7788            let (c1, o1) = sheet.chunk_of_row(4).unwrap();
7789            sheet.columns[0].chunks[c1]
7790                .overlay
7791                .set(o1, OverlayValue::Number(40.0));
7792        }
7793        // Insert 2 rows before row 4 (at chunk boundary)
7794        sheet.insert_rows(4, 2);
7795        assert_eq!(sheet.nrows, 12);
7796        // Validate overlays moved correctly: 3 stays, 4 becomes Empty, 6 has 40
7797        let av = sheet.range_view(0, 0, (sheet.nrows - 1) as usize, 0);
7798        assert_eq!(av.get_cell(3, 0), LiteralValue::Number(30.0));
7799        assert_eq!(av.get_cell(4, 0), LiteralValue::Empty);
7800        assert_eq!(av.get_cell(6, 0), LiteralValue::Number(40.0));
7801
7802        // Now delete 3 rows starting at 3: removes rows 3,4,5 → moves 40.0 from 6 → 3
7803        sheet.delete_rows(3, 3);
7804        assert_eq!(sheet.nrows, 9);
7805        let av2 = sheet.range_view(0, 0, (sheet.nrows - 1) as usize, 0);
7806        assert_eq!(av2.get_cell(3, 0), LiteralValue::Number(40.0));
7807        // All columns share chunk lengths; chunk_starts monotonic and cover nrows
7808        let lens0: Vec<usize> = sheet.columns[0]
7809            .chunks
7810            .iter()
7811            .map(|ch| ch.type_tag.len())
7812            .collect();
7813        for col in &sheet.columns {
7814            let lens: Vec<usize> = col.chunks.iter().map(|ch| ch.type_tag.len()).collect();
7815            assert_eq!(lens, lens0);
7816        }
7817        // chunk_starts should be monotonic and final chunk end == nrows
7818        assert!(sheet.chunk_starts.windows(2).all(|w| w[0] < w[1]));
7819        let last_start = *sheet.chunk_starts.last().unwrap_or(&0);
7820        let last_len = sheet.columns[0]
7821            .chunks
7822            .last()
7823            .map(|c| c.type_tag.len())
7824            .unwrap_or(0);
7825        assert_eq!(last_start + last_len, sheet.nrows as usize);
7826    }
7827
7828    #[test]
7829    fn row_insert_delete_preserves_user_dense_fragments() {
7830        let mut b = IngestBuilder::new("S", 1, 4, crate::engine::DateSystem::Excel1900);
7831        for _ in 0..10 {
7832            b.append_row(&[LiteralValue::Empty]).unwrap();
7833        }
7834        let mut sheet = b.finish();
7835
7836        let (ch_idx, off) = sheet.chunk_of_row(1).unwrap();
7837        sheet.columns[0]
7838            .chunk_mut(ch_idx)
7839            .unwrap()
7840            .overlay
7841            .apply_fragment(
7842                OverlayFragment::dense_range(
7843                    off,
7844                    vec![
7845                        OverlayValue::Number(10.0),
7846                        OverlayValue::Number(20.0),
7847                        OverlayValue::Number(30.0),
7848                    ],
7849                )
7850                .unwrap(),
7851            );
7852
7853        let before = column_overlay_stats(&sheet, 0, false);
7854        assert_eq!(before.dense_fragments, 1);
7855        assert_eq!(before.sparse_fragments, 0);
7856        assert_column_overlays_normalized(&sheet, 0);
7857
7858        sheet.insert_rows(2, 2);
7859        assert_eq!(sheet.nrows, 12);
7860        let av = sheet.range_view(0, 0, (sheet.nrows - 1) as usize, 0);
7861        assert_eq!(av.get_cell(1, 0), LiteralValue::Number(10.0));
7862        assert_eq!(av.get_cell(2, 0), LiteralValue::Empty);
7863        assert_eq!(av.get_cell(3, 0), LiteralValue::Empty);
7864        assert_eq!(av.get_cell(4, 0), LiteralValue::Number(20.0));
7865        assert_eq!(av.get_cell(5, 0), LiteralValue::Number(30.0));
7866        let after_insert = column_overlay_stats(&sheet, 0, false);
7867        assert_eq!(after_insert.sparse_fragments, 0);
7868        assert!(after_insert.dense_fragments >= 2);
7869        assert_column_overlays_normalized(&sheet, 0);
7870
7871        sheet.delete_rows(2, 2);
7872        assert_eq!(sheet.nrows, 10);
7873        let av = sheet.range_view(0, 0, (sheet.nrows - 1) as usize, 0);
7874        assert_eq!(av.get_cell(1, 0), LiteralValue::Number(10.0));
7875        assert_eq!(av.get_cell(2, 0), LiteralValue::Number(20.0));
7876        assert_eq!(av.get_cell(3, 0), LiteralValue::Number(30.0));
7877        let after_delete = column_overlay_stats(&sheet, 0, false);
7878        assert_eq!(after_delete.sparse_fragments, 0);
7879        assert!(after_delete.dense_fragments >= 1);
7880        assert_column_overlays_normalized(&sheet, 0);
7881    }
7882
7883    #[test]
7884    fn row_insert_delete_preserves_computed_empty_run_fragments() {
7885        let mut b = IngestBuilder::new("S", 1, 4, crate::engine::DateSystem::Excel1900);
7886        for row in 0..8 {
7887            b.append_row(&[LiteralValue::Number((row + 1) as f64)])
7888                .unwrap();
7889        }
7890        let mut sheet = b.finish();
7891
7892        let (ch_idx, off) = sheet.chunk_of_row(1).unwrap();
7893        sheet.columns[0]
7894            .chunk_mut(ch_idx)
7895            .unwrap()
7896            .computed_overlay
7897            .apply_fragment(
7898                OverlayFragment::run_range(
7899                    off,
7900                    vec![
7901                        OverlayValue::Empty,
7902                        OverlayValue::Empty,
7903                        OverlayValue::Empty,
7904                    ],
7905                )
7906                .unwrap(),
7907            );
7908
7909        let before = column_overlay_stats(&sheet, 0, true);
7910        assert_eq!(before.run_fragments, 1);
7911        assert_eq!(before.sparse_fragments, 0);
7912        assert_column_overlays_normalized(&sheet, 0);
7913
7914        sheet.insert_rows(2, 1);
7915        assert_eq!(sheet.nrows, 9);
7916        let av = sheet.range_view(0, 0, (sheet.nrows - 1) as usize, 0);
7917        assert_eq!(av.get_cell(1, 0), LiteralValue::Empty);
7918        assert_eq!(av.get_cell(2, 0), LiteralValue::Empty);
7919        assert_eq!(av.get_cell(3, 0), LiteralValue::Empty);
7920        assert_eq!(av.get_cell(4, 0), LiteralValue::Empty);
7921        assert_eq!(av.get_cell(5, 0), LiteralValue::Number(5.0));
7922        let after_insert = column_overlay_stats(&sheet, 0, true);
7923        assert_eq!(after_insert.sparse_fragments, 0);
7924        assert!(after_insert.run_fragments >= 2);
7925        assert_column_overlays_normalized(&sheet, 0);
7926
7927        sheet.delete_rows(2, 1);
7928        assert_eq!(sheet.nrows, 8);
7929        let av = sheet.range_view(0, 0, (sheet.nrows - 1) as usize, 0);
7930        assert_eq!(av.get_cell(1, 0), LiteralValue::Empty);
7931        assert_eq!(av.get_cell(2, 0), LiteralValue::Empty);
7932        assert_eq!(av.get_cell(3, 0), LiteralValue::Empty);
7933        assert_eq!(av.get_cell(4, 0), LiteralValue::Number(5.0));
7934        let after_delete = column_overlay_stats(&sheet, 0, true);
7935        assert_eq!(after_delete.sparse_fragments, 0);
7936        assert!(after_delete.run_fragments >= 1);
7937        assert_column_overlays_normalized(&sheet, 0);
7938    }
7939
7940    #[test]
7941    fn column_insert_delete_retains_chunk_alignment() {
7942        let mut b = IngestBuilder::new("S", 3, 3, crate::engine::DateSystem::Excel1900);
7943        for _ in 0..5 {
7944            b.append_row(&[
7945                LiteralValue::Empty,
7946                LiteralValue::Empty,
7947                LiteralValue::Empty,
7948            ])
7949            .unwrap();
7950        }
7951        let mut sheet = b.finish();
7952        // Record reference chunk lengths of first column
7953        let ref_lens: Vec<usize> = sheet.columns[0]
7954            .chunks
7955            .iter()
7956            .map(|ch| ch.type_tag.len())
7957            .collect();
7958        // Insert 2 columns before index 1
7959        sheet.insert_columns(1, 2);
7960        assert_eq!(sheet.columns.len(), 5);
7961        for col in &sheet.columns {
7962            let lens: Vec<usize> = col.chunks.iter().map(|ch| ch.type_tag.len()).collect();
7963            assert_eq!(lens, ref_lens);
7964        }
7965        let starts_before = sheet.chunk_starts.clone();
7966        // Delete 2 columns starting at index 2 → back to 3 columns
7967        sheet.delete_columns(2, 2);
7968        assert_eq!(sheet.columns.len(), 3);
7969        for col in &sheet.columns {
7970            let lens: Vec<usize> = col.chunks.iter().map(|ch| ch.type_tag.len()).collect();
7971            assert_eq!(lens, ref_lens);
7972        }
7973        // chunk_starts unchanged by column operations
7974        assert_eq!(sheet.chunk_starts, starts_before);
7975    }
7976
7977    #[test]
7978    fn multiple_adjacent_row_ops_overlay_mixed_types() {
7979        use formualizer_common::ExcelErrorKind;
7980        // Two columns to ensure alignment preserved across columns
7981        let mut b = IngestBuilder::new("S", 2, 3, crate::engine::DateSystem::Excel1900);
7982        for _ in 0..9 {
7983            b.append_row(&[LiteralValue::Empty, LiteralValue::Empty])
7984                .unwrap();
7985        }
7986        let mut sheet = b.finish();
7987        // Overlays at rows (0-based): 2->Number, 3->Text, 5->Boolean, 6->Error, 8->Empty
7988        // Column 0 only
7989        let set_ov = |sh: &mut ArrowSheet, row: usize, ov: OverlayValue| {
7990            let (ch_i, off) = sh.chunk_of_row(row).unwrap();
7991            let _ = sh.columns[0].chunks[ch_i].overlay.set(off, ov);
7992        };
7993        set_ov(&mut sheet, 2, OverlayValue::Number(12.5));
7994        set_ov(&mut sheet, 3, OverlayValue::Text(Arc::from("hello")));
7995        set_ov(&mut sheet, 5, OverlayValue::Boolean(true));
7996        set_ov(
7997            &mut sheet,
7998            6,
7999            OverlayValue::Error(map_error_code(ExcelErrorKind::Div)),
8000        );
8001        set_ov(&mut sheet, 8, OverlayValue::Empty);
8002
8003        // Insert 1 row before index 3
8004        sheet.insert_rows(3, 1);
8005        // Expected new positions: 2->2 (unchanged), 3->4, 5->6, 6->7, 8->9
8006        let av1 = sheet.range_view(0, 0, (sheet.nrows - 1) as usize, 0);
8007        assert_eq!(av1.get_cell(2, 0), LiteralValue::Number(12.5));
8008        assert_eq!(av1.get_cell(4, 0), LiteralValue::Text("hello".into()));
8009        assert_eq!(av1.get_cell(6, 0), LiteralValue::Boolean(true));
8010        match av1.get_cell(7, 0) {
8011            LiteralValue::Error(e) => assert_eq!(e.kind, ExcelErrorKind::Div),
8012            other => panic!("expected error at row 7, got {other:?}"),
8013        }
8014        assert_eq!(av1.get_cell(9, 0), LiteralValue::Empty);
8015
8016        // Insert 2 rows before index 4 (adjacent to previous region)
8017        sheet.insert_rows(4, 2);
8018        // Now positions: 2->2, 4->6, 6->8, 7->9, 9->11
8019        let av2 = sheet.range_view(0, 0, (sheet.nrows - 1) as usize, 0);
8020        assert_eq!(av2.get_cell(2, 0), LiteralValue::Number(12.5));
8021        assert_eq!(av2.get_cell(6, 0), LiteralValue::Text("hello".into()));
8022        assert_eq!(av2.get_cell(8, 0), LiteralValue::Boolean(true));
8023        match av2.get_cell(9, 0) {
8024            LiteralValue::Error(e) => assert_eq!(e.kind, ExcelErrorKind::Div),
8025            other => panic!("expected error at row 9, got {other:?}"),
8026        }
8027        assert_eq!(av2.get_cell(11, 0), LiteralValue::Empty);
8028
8029        // Delete 2 rows starting at index 6 → removes the text at 6 and one empty row
8030        sheet.delete_rows(6, 2);
8031        let av3 = sheet.range_view(0, 0, (sheet.nrows - 1) as usize, 0);
8032        // Remaining expected: 2->Number 12.5, 6 (was 8)->true, 7 (was 9)->#DIV/0!, 9 (was 11)->Empty
8033        assert_eq!(av3.get_cell(2, 0), LiteralValue::Number(12.5));
8034        assert_eq!(av3.get_cell(6, 0), LiteralValue::Boolean(true));
8035        match av3.get_cell(7, 0) {
8036            LiteralValue::Error(e) => assert_eq!(e.kind, ExcelErrorKind::Div),
8037            other => panic!("expected error at row 8, got {other:?}"),
8038        }
8039        assert_eq!(av3.get_cell(9, 0), LiteralValue::Empty);
8040
8041        // Alignment checks
8042        let lens0: Vec<usize> = sheet.columns[0]
8043            .chunks
8044            .iter()
8045            .map(|ch| ch.type_tag.len())
8046            .collect();
8047        for col in &sheet.columns {
8048            let lens: Vec<usize> = col.chunks.iter().map(|ch| ch.type_tag.len()).collect();
8049            assert_eq!(lens, lens0);
8050        }
8051        // chunk_starts monotonically increasing and cover nrows
8052        assert!(sheet.chunk_starts.windows(2).all(|w| w[0] < w[1]));
8053        let last_start = *sheet.chunk_starts.last().unwrap_or(&0);
8054        let last_len = sheet.columns[0]
8055            .chunks
8056            .last()
8057            .map(|c| c.type_tag.len())
8058            .unwrap_or(0);
8059        assert_eq!(last_start + last_len, sheet.nrows as usize);
8060    }
8061
8062    #[test]
8063    fn multiple_adjacent_column_ops_alignment() {
8064        // Start with 2 columns, chunk_rows=2, rows=5
8065        let mut b = IngestBuilder::new("S", 2, 2, crate::engine::DateSystem::Excel1900);
8066        for _ in 0..5 {
8067            b.append_row(&[LiteralValue::Empty, LiteralValue::Empty])
8068                .unwrap();
8069        }
8070        let mut sheet = b.finish();
8071        let ref_lens: Vec<usize> = sheet.columns[0]
8072            .chunks
8073            .iter()
8074            .map(|ch| ch.type_tag.len())
8075            .collect();
8076        // Insert 1 at start, then 2 at index 2 → columns = 5
8077        sheet.insert_columns(0, 1);
8078        sheet.insert_columns(2, 2);
8079        assert_eq!(sheet.columns.len(), 5);
8080        for col in &sheet.columns {
8081            let lens: Vec<usize> = col.chunks.iter().map(|ch| ch.type_tag.len()).collect();
8082            assert_eq!(lens, ref_lens);
8083        }
8084        let starts_before = sheet.chunk_starts.clone();
8085        // Delete 1 at index 1, then 2 at the end if available
8086        sheet.delete_columns(1, 1);
8087        let remain = sheet.columns.len();
8088        if remain >= 3 {
8089            sheet.delete_columns(remain - 2, 2);
8090        }
8091        for col in &sheet.columns {
8092            let lens: Vec<usize> = col.chunks.iter().map(|ch| ch.type_tag.len()).collect();
8093            assert_eq!(lens, ref_lens);
8094        }
8095        assert_eq!(sheet.chunk_starts, starts_before);
8096    }
8097
8098    #[test]
8099    fn overlays_on_multiple_columns_row_col_ops() {
8100        // 3 columns, chunk_rows=3, rows=6 → chunks [0..3), [3..6)
8101        let mut b = IngestBuilder::new("S", 3, 3, crate::engine::DateSystem::Excel1900);
8102        for _ in 0..6 {
8103            b.append_row(&[
8104                LiteralValue::Empty,
8105                LiteralValue::Empty,
8106                LiteralValue::Empty,
8107            ])
8108            .unwrap();
8109        }
8110        let mut sheet = b.finish();
8111        // Overlays at row2 and row3 across columns with different types
8112        let set_ov = |sh: &mut ArrowSheet, col: usize, row: usize, ov: OverlayValue| {
8113            let (ch_i, off) = sh.chunk_of_row(row).unwrap();
8114            let _ = sh.columns[col].chunks[ch_i].overlay.set(off, ov);
8115        };
8116        set_ov(&mut sheet, 0, 2, OverlayValue::Number(12.0));
8117        set_ov(&mut sheet, 1, 2, OverlayValue::Text(Arc::from("xx")));
8118        set_ov(&mut sheet, 2, 2, OverlayValue::Boolean(true));
8119        set_ov(&mut sheet, 0, 3, OverlayValue::Number(33.0));
8120        set_ov(&mut sheet, 1, 3, OverlayValue::Text(Arc::from("yy")));
8121        set_ov(&mut sheet, 2, 3, OverlayValue::Boolean(false));
8122
8123        // Insert a row at boundary (before row index 3)
8124        sheet.insert_rows(3, 1);
8125        // Now original row>=3 shift down by 1
8126        let av = sheet.range_view(0, 0, (sheet.nrows - 1) as usize, 2);
8127        // Row 2 values unchanged
8128        assert_eq!(av.get_cell(2, 0), LiteralValue::Number(12.0));
8129        assert_eq!(av.get_cell(2, 1), LiteralValue::Text("xx".into()));
8130        assert_eq!(av.get_cell(2, 2), LiteralValue::Boolean(true));
8131        // Row 3 became Empty (inserted)
8132        assert_eq!(av.get_cell(3, 0), LiteralValue::Empty);
8133        // Row 4 holds old row 3 overlays
8134        assert_eq!(av.get_cell(4, 0), LiteralValue::Number(33.0));
8135        assert_eq!(av.get_cell(4, 1), LiteralValue::Text("yy".into()));
8136        assert_eq!(av.get_cell(4, 2), LiteralValue::Boolean(false));
8137
8138        // Delete column 1 (middle), values shift left
8139        sheet.delete_columns(1, 1);
8140        let av2 = sheet.range_view(0, 0, (sheet.nrows - 1) as usize, 1);
8141        assert_eq!(av2.get_cell(2, 0), LiteralValue::Number(12.0));
8142        // Column 1 now was old column 2
8143        assert_eq!(av2.get_cell(2, 1), LiteralValue::Boolean(true));
8144        assert_eq!(av2.get_cell(4, 0), LiteralValue::Number(33.0));
8145        assert_eq!(av2.get_cell(4, 1), LiteralValue::Boolean(false));
8146
8147        // Alignment preserved
8148        let lens0: Vec<usize> = sheet.columns[0]
8149            .chunks
8150            .iter()
8151            .map(|ch| ch.type_tag.len())
8152            .collect();
8153        for col in &sheet.columns {
8154            let lens: Vec<usize> = col.chunks.iter().map(|ch| ch.type_tag.len()).collect();
8155            assert_eq!(lens, lens0);
8156        }
8157    }
8158
8159    #[test]
8160    fn effective_slices_overlay_precedence_numbers_text() {
8161        // 1 column, chunk_rows=3, rows=6. Base numbers in lane; overlays include text on row1 and number on row4.
8162        let mut b = IngestBuilder::new("S", 1, 3, crate::engine::DateSystem::Excel1900);
8163        for i in 0..6 {
8164            b.append_row(&[LiteralValue::Number((i + 1) as f64)])
8165                .unwrap();
8166        }
8167        let mut sheet = b.finish();
8168        // Overlays: row1 -> Text("X"), row4 -> Number(99)
8169        let (c1, o1) = sheet.chunk_of_row(1).unwrap();
8170        sheet.columns[0].chunks[c1]
8171            .overlay
8172            .set(o1, OverlayValue::Text(Arc::from("X")));
8173        let (c4, o4) = sheet.chunk_of_row(4).unwrap();
8174        sheet.columns[0].chunks[c4]
8175            .overlay
8176            .set(o4, OverlayValue::Number(99.0));
8177
8178        let av = sheet.range_view(0, 0, 5, 0);
8179        // Validate numbers_slices: row1 should be null (text overlay), row4 should be 99.0, others base
8180        let mut numeric: Vec<Option<f64>> = vec![None; 6];
8181        for res in av.numbers_slices() {
8182            let (row_start, row_len, cols) = res.unwrap();
8183            let a = &cols[0];
8184            for i in 0..row_len {
8185                let idx = row_start + i;
8186                numeric[idx] = if a.is_null(i) { None } else { Some(a.value(i)) };
8187            }
8188        }
8189        assert_eq!(numeric[0], Some(1.0));
8190        assert_eq!(numeric[1], None); // overshadowed by text overlay
8191        assert_eq!(numeric[2], Some(3.0));
8192        assert_eq!(numeric[3], Some(4.0));
8193        assert_eq!(numeric[4], Some(99.0));
8194        assert_eq!(numeric[5], Some(6.0));
8195
8196        // Validate text_slices: row1 has "X", others null
8197        let mut texts: Vec<Option<String>> = vec![None; 6];
8198        for res in av.text_slices() {
8199            let (row_start, row_len, cols) = res.unwrap();
8200            let a = cols[0].as_any().downcast_ref::<StringArray>().unwrap();
8201            for i in 0..row_len {
8202                let idx = row_start + i;
8203                texts[idx] = if a.is_null(i) {
8204                    None
8205                } else {
8206                    Some(a.value(i).to_string())
8207                };
8208            }
8209        }
8210        assert_eq!(texts[1].as_deref(), Some("X"));
8211        assert!(texts[0].is_none());
8212        assert!(texts[2].is_none());
8213        assert!(texts[3].is_none());
8214        assert!(texts[4].is_none());
8215        assert!(texts[5].is_none());
8216    }
8217
8218    #[test]
8219    fn effective_slices_overlay_precedence_booleans() {
8220        // Base booleans over 1 column; overlays include boolean and non-boolean types.
8221        let mut b = IngestBuilder::new("S", 1, 4, crate::engine::DateSystem::Excel1900);
8222        for i in 0..6 {
8223            let v = if i % 2 == 0 {
8224                LiteralValue::Boolean(true)
8225            } else {
8226                LiteralValue::Boolean(false)
8227            };
8228            b.append_row(&[v]).unwrap();
8229        }
8230        let mut sheet = b.finish();
8231        // Overlays: row1 -> Boolean(true), row2 -> Text("T")
8232        let (c1, o1) = sheet.chunk_of_row(1).unwrap();
8233        sheet.columns[0].chunks[c1]
8234            .overlay
8235            .set(o1, OverlayValue::Boolean(true));
8236        let (c2, o2) = sheet.chunk_of_row(2).unwrap();
8237        sheet.columns[0].chunks[c2]
8238            .overlay
8239            .set(o2, OverlayValue::Text(Arc::from("T")));
8240
8241        let av = sheet.range_view(0, 0, 5, 0);
8242        // Validate booleans_slices: row1 should be true (overlay), row2 should be null (text overlay), others base
8243        let mut bools: Vec<Option<bool>> = vec![None; 6];
8244        for res in av.booleans_slices() {
8245            let (row_start, row_len, cols) = res.unwrap();
8246            let a = &cols[0];
8247            for i in 0..row_len {
8248                let idx = row_start + i;
8249                bools[idx] = if a.is_null(i) { None } else { Some(a.value(i)) };
8250            }
8251        }
8252        assert_eq!(bools[0], Some(true));
8253        assert_eq!(bools[1], Some(true)); // overlay to true
8254        assert_eq!(bools[2], None); // overshadowed by text overlay
8255        // spot-check others remain base
8256        assert_eq!(bools[3], Some(false));
8257    }
8258
8259    #[test]
8260    fn effective_slices_overlay_precedence_errors() {
8261        // Base numbers; overlay an error at one row and ensure errors_slices reflect it.
8262        let mut b = IngestBuilder::new("S", 1, 3, crate::engine::DateSystem::Excel1900);
8263        for i in 0..6 {
8264            b.append_row(&[LiteralValue::Number((i + 1) as f64)])
8265                .unwrap();
8266        }
8267        let mut sheet = b.finish();
8268        // Overlay error at row 4
8269        let (c4, o4) = sheet.chunk_of_row(4).unwrap();
8270        sheet.columns[0].chunks[c4]
8271            .overlay
8272            .set(o4, OverlayValue::Error(map_error_code(ExcelErrorKind::Div)));
8273
8274        let av = sheet.range_view(0, 0, 5, 0);
8275        let mut errs: Vec<Option<u8>> = vec![None; 6];
8276        for res in av.errors_slices() {
8277            let (row_start, row_len, cols) = res.unwrap();
8278            let a = &cols[0];
8279            for i in 0..row_len {
8280                let idx = row_start + i;
8281                errs[idx] = if a.is_null(i) { None } else { Some(a.value(i)) };
8282            }
8283        }
8284        assert_eq!(errs[4], Some(map_error_code(ExcelErrorKind::Div)));
8285        assert!(errs[3].is_none());
8286    }
8287}