use super::interval_tree::IntervalTree;
use super::vertex::VertexId;
use formualizer_common::Coord as AbsCoord;
use std::collections::HashSet;
#[derive(Debug, Default)]
pub struct SheetIndex {
row_tree: IntervalTree<VertexId>,
col_tree: IntervalTree<VertexId>,
}
impl SheetIndex {
pub fn new() -> Self {
Self {
row_tree: IntervalTree::new(),
col_tree: IntervalTree::new(),
}
}
pub fn build_from_sorted(&mut self, items: &[(AbsCoord, VertexId)]) {
self.add_vertices_batch(items);
}
pub fn add_vertex(&mut self, coord: AbsCoord, vertex_id: VertexId) {
let row = coord.row();
let col = coord.col();
self.row_tree
.entry(row, row)
.or_insert_with(HashSet::new)
.insert(vertex_id);
self.col_tree
.entry(col, col)
.or_insert_with(HashSet::new)
.insert(vertex_id);
}
pub fn add_vertices_batch(&mut self, items: &[(AbsCoord, VertexId)]) {
if items.is_empty() {
return;
}
if self.row_tree.is_empty() && self.col_tree.is_empty() {
let mut row_items: Vec<(u32, HashSet<VertexId>)> = Vec::with_capacity(items.len());
let mut col_items: Vec<(u32, HashSet<VertexId>)> = Vec::with_capacity(items.len());
use rustc_hash::FxHashMap;
let mut row_map: FxHashMap<u32, HashSet<VertexId>> = FxHashMap::default();
let mut col_map: FxHashMap<u32, HashSet<VertexId>> = FxHashMap::default();
for (coord, vid) in items {
row_map.entry(coord.row()).or_default().insert(*vid);
col_map.entry(coord.col()).or_default().insert(*vid);
}
row_items.reserve(row_map.len());
for (k, v) in row_map.into_iter() {
row_items.push((k, v));
}
col_items.reserve(col_map.len());
for (k, v) in col_map.into_iter() {
col_items.push((k, v));
}
self.row_tree.bulk_build_points(row_items);
self.col_tree.bulk_build_points(col_items);
return;
}
for (coord, vid) in items {
let row = coord.row();
let col = coord.col();
self.row_tree
.entry(row, row)
.or_insert_with(HashSet::new)
.insert(*vid);
self.col_tree
.entry(col, col)
.or_insert_with(HashSet::new)
.insert(*vid);
}
}
pub fn remove_vertex(&mut self, coord: AbsCoord, vertex_id: VertexId) {
let row = coord.row();
let col = coord.col();
if let Some(vertices) = self.row_tree.get_mut(row, row) {
vertices.remove(&vertex_id);
}
if let Some(vertices) = self.col_tree.get_mut(col, col) {
vertices.remove(&vertex_id);
}
}
pub fn update_vertex(&mut self, old_coord: AbsCoord, new_coord: AbsCoord, vertex_id: VertexId) {
self.remove_vertex(old_coord, vertex_id);
self.add_vertex(new_coord, vertex_id);
}
pub fn vertices_in_row_range(&self, start: u32, end: u32) -> Vec<VertexId> {
let mut result = HashSet::new();
for (_low, _high, values) in self.row_tree.query(start, end) {
result.extend(values.iter());
}
result.into_iter().collect()
}
pub fn vertices_in_col_range(&self, start: u32, end: u32) -> Vec<VertexId> {
let mut result = HashSet::new();
for (_low, _high, values) in self.col_tree.query(start, end) {
result.extend(values.iter());
}
result.into_iter().collect()
}
pub fn vertices_in_rect(
&self,
start_row: u32,
end_row: u32,
start_col: u32,
end_col: u32,
) -> Vec<VertexId> {
let row_vertices: HashSet<_> = self
.vertices_in_row_range(start_row, end_row)
.into_iter()
.collect();
let col_vertices: HashSet<_> = self
.vertices_in_col_range(start_col, end_col)
.into_iter()
.collect();
row_vertices.intersection(&col_vertices).copied().collect()
}
pub fn len(&self) -> usize {
let mut unique: HashSet<VertexId> = HashSet::new();
for (_low, _high, values) in self.row_tree.query(0, u32::MAX) {
unique.extend(values.iter());
}
unique.len()
}
pub fn is_empty(&self) -> bool {
self.row_tree.is_empty()
}
pub fn clear(&mut self) {
self.row_tree = IntervalTree::new();
self.col_tree = IntervalTree::new();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_add_and_query_single_vertex() {
let mut index = SheetIndex::new();
let coord = AbsCoord::new(5, 10);
let vertex_id = VertexId(1024);
index.add_vertex(coord, vertex_id);
let row_results = index.vertices_in_row_range(5, 5);
assert_eq!(row_results, vec![vertex_id]);
let col_results = index.vertices_in_col_range(10, 10);
assert_eq!(col_results, vec![vertex_id]);
let row_results = index.vertices_in_row_range(3, 7);
assert_eq!(row_results, vec![vertex_id]);
}
#[test]
fn test_remove_vertex() {
let mut index = SheetIndex::new();
let coord = AbsCoord::new(5, 10);
let vertex_id = VertexId(1024);
index.add_vertex(coord, vertex_id);
assert_eq!(index.len(), 1);
index.remove_vertex(coord, vertex_id);
assert_eq!(index.len(), 0);
let row_results = index.vertices_in_row_range(5, 5);
assert!(row_results.is_empty());
}
#[test]
fn test_update_vertex_position() {
let mut index = SheetIndex::new();
let old_coord = AbsCoord::new(5, 10);
let new_coord = AbsCoord::new(15, 20);
let vertex_id = VertexId(1024);
index.add_vertex(old_coord, vertex_id);
index.update_vertex(old_coord, new_coord, vertex_id);
let old_row_results = index.vertices_in_row_range(5, 5);
assert!(old_row_results.is_empty());
let new_row_results = index.vertices_in_row_range(15, 15);
assert_eq!(new_row_results, vec![vertex_id]);
let new_col_results = index.vertices_in_col_range(20, 20);
assert_eq!(new_col_results, vec![vertex_id]);
}
#[test]
fn test_range_queries() {
let mut index = SheetIndex::new();
for row in 0..10 {
for col in 0..5 {
let coord = AbsCoord::new(row, col);
let vertex_id = VertexId(1024 + row * 5 + col);
index.add_vertex(coord, vertex_id);
}
}
let row_results = index.vertices_in_row_range(3, 5);
assert_eq!(row_results.len(), 15);
let col_results = index.vertices_in_col_range(1, 2);
assert_eq!(col_results.len(), 20);
let rect_results = index.vertices_in_rect(3, 5, 1, 2);
assert_eq!(rect_results.len(), 6);
}
#[test]
fn test_sparse_sheet_efficiency() {
let mut index = SheetIndex::new();
index.add_vertex(AbsCoord::new(100, 5), VertexId(1024));
index.add_vertex(AbsCoord::new(50_000, 10), VertexId(1025));
index.add_vertex(AbsCoord::new(100_000, 15), VertexId(1026));
index.add_vertex(AbsCoord::new(500_000, 20), VertexId(1027));
index.add_vertex(AbsCoord::new(999_999, 25), VertexId(1028));
assert_eq!(index.len(), 5);
let high_rows = index.vertices_in_row_range(100_000, u32::MAX);
assert_eq!(high_rows.len(), 3);
let col_range = index.vertices_in_col_range(10, 20);
assert_eq!(col_range.len(), 3); }
#[test]
fn test_shift_operation_query() {
let mut index = SheetIndex::new();
for row in [10, 20, 30, 40, 50] {
index.add_vertex(AbsCoord::new(row, 0), VertexId(1024 + row));
}
let vertices_to_shift = index.vertices_in_row_range(25, u32::MAX);
assert_eq!(vertices_to_shift.len(), 3);
for col in 1..=3 {
index.add_vertex(AbsCoord::new(5, col), VertexId(2000 + col));
}
let vertices_to_delete = index.vertices_in_col_range(1, 3);
assert_eq!(vertices_to_delete.len(), 3);
}
#[test]
fn test_viewport_query() {
let mut index = SheetIndex::new();
for row in (0..10000).step_by(100) {
for col in 0..10 {
index.add_vertex(AbsCoord::new(row, col), VertexId(row * 10 + col));
}
}
let viewport = index.vertices_in_rect(500, 1500, 2, 7);
assert_eq!(viewport.len(), 66);
}
}