Skip to main content

nodedb_array/coord/
encode.rs

1// SPDX-License-Identifier: Apache-2.0
2
3//! High-level coordinate-prefix encoding.
4//!
5//! Combines [`super::normalize`] with [`super::hilbert`] /
6//! [`super::zorder`] and dispatches off the schema's
7//! [`crate::schema::CellOrder`] for cell-prefix encoding, or
8//! [`crate::schema::TileOrder`] for tile-prefix encoding (tile coords
9//! are cell coords integer-divided by `tile_extents`).
10
11use super::{hilbert, normalize, zorder};
12use crate::error::ArrayResult;
13use crate::schema::ArraySchema;
14use crate::schema::cell_order::{CellOrder, TileOrder};
15use crate::types::coord::value::CoordValue;
16
17/// Encode a Hilbert prefix for one cell coordinate using the schema's
18/// configured per-dim bit budget.
19pub fn encode_hilbert_prefix(schema: &ArraySchema, coord: &[CoordValue]) -> ArrayResult<u64> {
20    let bits = normalize::bits_per_dim(schema.arity());
21    let normalized = normalize::normalize_coord(schema, coord, bits)?;
22    hilbert::encode(&normalized, bits)
23}
24
25/// Encode a Z-order prefix for one cell coordinate.
26pub fn encode_zorder_prefix(schema: &ArraySchema, coord: &[CoordValue]) -> ArrayResult<u64> {
27    let bits = normalize::bits_per_dim(schema.arity());
28    let normalized = normalize::normalize_coord(schema, coord, bits)?;
29    zorder::encode(&normalized, bits)
30}
31
32/// Encode whichever space-filling curve the schema declares for cells.
33pub fn encode_cell_prefix(schema: &ArraySchema, coord: &[CoordValue]) -> ArrayResult<u64> {
34    match schema.cell_order {
35        CellOrder::Hilbert | CellOrder::RowMajor | CellOrder::ColMajor => {
36            encode_hilbert_prefix(schema, coord)
37        }
38        CellOrder::ZOrder => encode_zorder_prefix(schema, coord),
39    }
40}
41
42/// Encode whichever curve the schema declares for tile boundaries.
43pub fn encode_tile_prefix_with_order(
44    schema: &ArraySchema,
45    tile_indices: &[u64],
46    bits: u32,
47) -> ArrayResult<u64> {
48    match schema.tile_order {
49        TileOrder::Hilbert | TileOrder::RowMajor | TileOrder::ColMajor => {
50            hilbert::encode(tile_indices, bits)
51        }
52        TileOrder::ZOrder => zorder::encode(tile_indices, bits),
53    }
54}
55
56#[cfg(test)]
57mod tests {
58    use super::*;
59    use crate::schema::ArraySchemaBuilder;
60    use crate::schema::attr_spec::{AttrSpec, AttrType};
61    use crate::schema::dim_spec::{DimSpec, DimType};
62    use crate::types::domain::{Domain, DomainBound};
63
64    fn schema() -> ArraySchema {
65        ArraySchemaBuilder::new("g")
66            .dim(DimSpec::new(
67                "x",
68                DimType::Int64,
69                Domain::new(DomainBound::Int64(0), DomainBound::Int64(31)),
70            ))
71            .dim(DimSpec::new(
72                "y",
73                DimType::Int64,
74                Domain::new(DomainBound::Int64(0), DomainBound::Int64(31)),
75            ))
76            .attr(AttrSpec::new("v", AttrType::Int64, false))
77            .tile_extents(vec![8, 8])
78            .build()
79            .unwrap()
80    }
81
82    #[test]
83    fn hilbert_prefix_distinct_for_distinct_cells() {
84        let s = schema();
85        let a = encode_hilbert_prefix(&s, &[CoordValue::Int64(0), CoordValue::Int64(0)]).unwrap();
86        let b = encode_hilbert_prefix(&s, &[CoordValue::Int64(31), CoordValue::Int64(31)]).unwrap();
87        assert_ne!(a, b);
88    }
89
90    #[test]
91    fn zorder_prefix_distinct_for_distinct_cells() {
92        let s = schema();
93        let a = encode_zorder_prefix(&s, &[CoordValue::Int64(0), CoordValue::Int64(0)]).unwrap();
94        let b = encode_zorder_prefix(&s, &[CoordValue::Int64(31), CoordValue::Int64(31)]).unwrap();
95        assert_ne!(a, b);
96    }
97
98    #[test]
99    fn cell_prefix_dispatches_on_order() {
100        let s = schema();
101        let p = encode_cell_prefix(&s, &[CoordValue::Int64(7), CoordValue::Int64(13)]);
102        assert!(p.is_ok());
103    }
104}