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