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