use diskann_utils::views::{Init, Matrix};
use crate::graph::AdjacencyList;
#[derive(Debug, Clone, Copy)]
pub enum Grid {
One,
Two,
Three,
Four,
}
impl Grid {
pub fn data(self, size: usize) -> Matrix<f32> {
Self::data_as(self, size, |i: usize| i as f32)
}
pub fn data_as<F, R>(self, size: usize, mut f: F) -> Matrix<R>
where
F: FnMut(usize) -> R,
{
match self {
Self::One => {
let mut i = 0;
let init = Init(|| {
let this = f(i);
i += 1;
this
});
Matrix::new(init, size, 1)
}
Self::Two => {
let mut v = [0; 2];
let mut i = 0;
let init = Init(|| {
let value = f(v[i]);
i += 1;
if i == 2 {
i = 0;
increment(&mut v, size);
}
value
});
Matrix::new(init, size.pow(self.dim().into()), 2)
}
Self::Three => {
let mut v = [0; 3];
let mut i = 0;
let init = Init(|| {
let value = f(v[i]);
i += 1;
if i == 3 {
i = 0;
increment(&mut v, size);
}
value
});
Matrix::new(init, size.pow(self.dim().into()), 3)
}
Self::Four => {
let mut v = [0; 4];
let mut i = 0;
let init = Init(|| {
let value = f(v[i]);
i += 1;
if i == 4 {
i = 0;
increment(&mut v, size);
}
value
});
Matrix::new(init, size.pow(self.dim().into()), 4)
}
}
}
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: Matrix<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
.row_iter()
.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());
}
}