use diskann_utils::views::rowmajor::{self, Matrix};
use crate::graph::AdjacencyList;
#[derive(Debug, Clone, Copy)]
pub enum Grid {
One,
Two,
Three,
Four,
}
impl Grid {
pub fn data(self, size: usize) -> rowmajor::Owned<f32> {
Self::data_as(self, size, |i: usize| i as f32)
}
pub fn data_as<F, R>(self, size: usize, mut f: F) -> rowmajor::Owned<R>
where
F: FnMut(usize) -> R,
{
match self {
Self::One => rowmajor::Owned::from_fn(size, 1, |rc| f(rc.row)),
Self::Two => {
let mut v = [0; 2];
rowmajor::Owned::from_fn(size.pow(self.dim().into()), 2, |rc| {
let value = f(v[rc.col]);
if rc.col == 1 {
increment(&mut v, size);
}
value
})
}
Self::Three => {
let mut v = [0; 3];
rowmajor::Owned::from_fn(size.pow(self.dim().into()), 3, |rc| {
let value = f(v[rc.col]);
if rc.col == 2 {
increment(&mut v, size);
}
value
})
}
Self::Four => {
let mut v = [0; 4];
rowmajor::Owned::from_fn(size.pow(self.dim().into()), 4, |rc| {
let value = f(v[rc.col]);
if rc.col == 3 {
increment(&mut v, size);
}
value
})
}
}
}
pub fn neighbors(self, size: usize) -> Vec<AdjacencyList<u32>> {
let mut lists: Vec<AdjacencyList<u32>> = Vec::new();
let size: u32 = size as u32;
match self {
Self::One => {
for i in 0..size {
let mut list = AdjacencyList::with_capacity(2);
if i > 0 {
list.push(i - 1);
}
if i < size - 1 {
list.push(i + 1);
}
lists.push(list);
}
}
Self::Two => {
let map = |i, j| (i * size) + j;
for i in 0..size {
for j in 0..size {
let mut list = AdjacencyList::new();
if i > 0 {
list.push(map(i - 1, j));
}
if i < size - 1 {
list.push(map(i + 1, j));
}
if j > 0 {
list.push(map(i, j - 1));
}
if j < size - 1 {
list.push(map(i, j + 1));
}
lists.push(list);
}
}
}
Self::Three => {
let map = |i, j, k| (i * size * size) + (j * size) + k;
for i in 0..size {
for j in 0..size {
for k in 0..size {
let mut list = AdjacencyList::new();
if i > 0 {
list.push(map(i - 1, j, k));
}
if i < size - 1 {
list.push(map(i + 1, j, k));
}
if j > 0 {
list.push(map(i, j - 1, k));
}
if j < size - 1 {
list.push(map(i, j + 1, k));
}
if k > 0 {
list.push(map(i, j, k - 1));
}
if k < size - 1 {
list.push(map(i, j, k + 1));
}
lists.push(list);
}
}
}
}
Self::Four => {
let map =
|i, j, k, l| (i * size * size * size) + (j * size * size) + (k * size) + l;
for i in 0..size {
for j in 0..size {
for k in 0..size {
for l in 0..size {
let mut list = AdjacencyList::new();
if i > 0 {
list.push(map(i - 1, j, k, l));
}
if i < size - 1 {
list.push(map(i + 1, j, k, l));
}
if j > 0 {
list.push(map(i, j - 1, k, l));
}
if j < size - 1 {
list.push(map(i, j + 1, k, l));
}
if k > 0 {
list.push(map(i, j, k - 1, l));
}
if k < size - 1 {
list.push(map(i, j, k + 1, l));
}
if l > 0 {
list.push(map(i, j, k, l - 1));
}
if l < size - 1 {
list.push(map(i, j, k, l + 1));
}
lists.push(list);
}
}
}
}
}
};
lists
}
pub fn start_point(self, size: usize) -> Vec<f32> {
Self::start_point_as(self, size, |i: usize| i as f32)
}
pub fn start_point_as<F, R>(self, size: usize, mut f: F) -> Vec<R>
where
F: FnMut(usize) -> R,
{
(0..self.dim()).map(|_| f(size)).collect()
}
pub fn dim(self) -> u8 {
match self {
Self::One => 1,
Self::Two => 2,
Self::Three => 3,
Self::Four => 4,
}
}
pub fn from_dim(dim: usize) -> Option<Self> {
match dim {
1 => Some(Self::One),
3 => Some(Self::Three),
4 => Some(Self::Four),
_ => None,
}
}
pub fn num_points(self, size: usize) -> usize {
size.pow(self.dim().into())
}
#[inline(never)]
pub(super) fn setup(self, size: usize, start_id: u32) -> Setup {
let num_points = self.num_points(size);
Setup {
start_point: self.start_point(size),
start_id,
start_neighbors: AdjacencyList::from_iter_unique(std::iter::once(
num_points as u32 - 1,
)),
data: self.data(size),
neighbors: self.neighbors(size),
}
}
}
fn increment<const N: usize>(array: &mut [usize; N], modulo: usize) {
const {
assert!(
N != 0,
"this algorithm doesn't work on 0 dimensional arrays"
)
};
let mut i = N - 1;
loop {
let v = &mut array[i];
*v += 1;
if *v == modulo {
*v = 0;
match i.checked_sub(1) {
Some(j) => i = j,
None => return,
}
} else {
return;
}
}
}
#[derive(Debug)]
pub(super) struct Setup {
start_point: Vec<f32>,
start_id: u32,
start_neighbors: AdjacencyList<u32>,
data: rowmajor::Owned<f32>,
neighbors: Vec<AdjacencyList<u32>>,
}
impl Setup {
pub(super) fn start_point(&self) -> Vec<f32> {
self.start_point.clone()
}
pub(super) fn start_id(&self) -> u32 {
self.start_id
}
pub(super) fn start_neighbors(&self) -> impl Iterator<Item = (u32, AdjacencyList<u32>)> {
std::iter::once((self.start_id(), self.start_neighbors.clone()))
}
pub(super) fn setup(&self) -> impl Iterator<Item = (u32, Vec<f32>, AdjacencyList<u32>)> {
let mut i = 0u32;
self.data
.rows()
.zip(self.neighbors.iter())
.map(move |(data, neighbors)| {
let id = i;
i = i.wrapping_add(1);
(id, data.into(), neighbors.clone())
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_increment_2() {
let mut v = [0, 0];
increment(&mut v, 3);
assert_eq!(v, [0, 1]);
increment(&mut v, 3);
assert_eq!(v, [0, 2]);
increment(&mut v, 3);
assert_eq!(v, [1, 0]);
increment(&mut v, 3);
assert_eq!(v, [1, 1]);
increment(&mut v, 3);
assert_eq!(v, [1, 2]);
increment(&mut v, 3);
assert_eq!(v, [2, 0]);
increment(&mut v, 3);
assert_eq!(v, [2, 1]);
increment(&mut v, 3);
assert_eq!(v, [2, 2]);
increment(&mut v, 3);
assert_eq!(v, [0, 0]);
}
#[test]
fn test_increment_3() {
let mut v = [0, 0, 0];
increment(&mut v, 2);
assert_eq!(v, [0, 0, 1]);
increment(&mut v, 2);
assert_eq!(v, [0, 1, 0]);
increment(&mut v, 2);
assert_eq!(v, [0, 1, 1]);
increment(&mut v, 2);
assert_eq!(v, [1, 0, 0]);
increment(&mut v, 2);
assert_eq!(v, [1, 0, 1]);
increment(&mut v, 2);
assert_eq!(v, [1, 1, 0]);
increment(&mut v, 2);
assert_eq!(v, [1, 1, 1]);
increment(&mut v, 2);
assert_eq!(v, [0, 0, 0]);
}
#[test]
fn test_from_dim_roundtrip() {
for grid in [Grid::One, Grid::Three, Grid::Four] {
assert_eq!(
Grid::from_dim(grid.dim() as usize).unwrap().dim(),
grid.dim()
);
}
}
#[test]
fn test_from_dim_unsupported() {
assert!(Grid::from_dim(2).is_none());
assert!(Grid::from_dim(5).is_none());
}
}