use std::collections::{BTreeMap, HashMap};
use icu_casemap::CaseMapperBorrowed;
use crate::casefold::simple_fold;
use crate::depgraph::RangeRef;
use crate::workbook::Workbook;
struct SheetRows {
total: u64,
rows: BTreeMap<u32, Vec<u32>>,
}
#[doc(hidden)]
pub struct AuthoredCellIndex {
sheets: HashMap<String, SheetRows>,
}
impl AuthoredCellIndex {
#[doc(hidden)]
pub fn build(workbook: &Workbook) -> Self {
let folder = CaseMapperBorrowed::new();
let mut sheets = HashMap::with_capacity(workbook.sheets().len());
for sheet in workbook.sheets() {
let mut rows: BTreeMap<u32, Vec<u32>> = BTreeMap::new();
for (addr, _) in sheet.iter() {
rows.entry(addr.row).or_default().push(addr.column);
}
for columns in rows.values_mut() {
columns.sort_unstable();
}
sheets
.entry(simple_fold(&folder, sheet.name()))
.or_insert_with(|| SheetRows {
total: sheet.len() as u64,
rows,
});
}
Self { sheets }
}
#[doc(hidden)]
pub fn range_has_unauthored_cell(&self, r: &RangeRef) -> bool {
self.range_has_unauthored_cell_examined(r).0
}
#[doc(hidden)]
pub fn range_has_unauthored_cell_examined(&self, r: &RangeRef) -> (bool, usize) {
let mut examined = 0usize;
if r.start.row > r.end.row || r.start.column > r.end.column {
return (true, examined);
}
let Some(sheet) = self.sheets.get(&r.sheet) else {
return (true, examined);
};
let height = u64::from(r.end.row - r.start.row) + 1;
let width = u64::from(r.end.column - r.start.column) + 1;
let area = height.saturating_mul(width);
if area > sheet.total {
return (true, examined);
}
let mut expected = u64::from(r.start.row);
for (&row, columns) in sheet.rows.range(r.start.row..=r.end.row) {
if u64::from(row) != expected {
return (true, examined); }
let lo = partition_point(columns, &mut examined, |c| c < r.start.column);
let hi = partition_point(columns, &mut examined, |c| c <= r.end.column);
if (hi - lo) as u64 != width {
return (true, examined);
}
expected += 1;
}
(expected <= u64::from(r.end.row), examined)
}
}
fn partition_point(columns: &[u32], examined: &mut usize, keep: impl Fn(u32) -> bool) -> usize {
let (mut lo, mut hi) = (0usize, columns.len());
while lo < hi {
let mid = lo + (hi - lo) / 2;
*examined += 1;
if keep(columns[mid]) {
lo = mid + 1;
} else {
hi = mid;
}
}
lo
}