pub mod fast_hilbert {
use num::BigUint;
use crate::interleaver::Interleaver;
pub fn transpose(hilbert_index : &BigUint, bits : usize, dimensions : usize) -> Vec<u32>
{
uninterleave(hilbert_index, bits, dimensions, true)
}
pub fn unpack_big_integer(n : &BigUint, num_bits : usize) -> Vec<u8>
{
let mut unpacked = n.to_radix_le(2);
let unpacked_len = unpacked.len();
for _ in unpacked_len .. num_bits {
unpacked.push(0)
}
unpacked
}
pub fn uninterleave(gray_code : &BigUint, bit_depth : usize, dimensions : usize, reverse_order : bool) -> Vec<u32>
{
let mut axes_vector = vec![0_u32; dimensions];
let num_bits = dimensions * bit_depth;
let bits = unpack_big_integer(gray_code, num_bits);
let mut bit_index = 0;
let start_dimension : i32 = if reverse_order { dimensions as i32 - 1 } else { 0 };
let stop_dimension : i32 = if reverse_order { -1 } else { dimensions as i32 };
let dim_increment : i32 = if reverse_order { -1 } else { 1 };
for bit_number in 0 .. bit_depth
{
let mut dimension = start_dimension;
while dimension != stop_dimension {
axes_vector[dimension as usize] = axes_vector[dimension as usize] | ((bits[bit_index] as u32) << bit_number);
bit_index += 1;
dimension += dim_increment;
}
}
axes_vector
}
pub fn untranspose(transposed_index : &[u32], bits : usize, interleaver_option : Option<&Interleaver>) -> BigUint
{
let interleaved_bytes : Vec<u8> = interleave_be(transposed_index, bits, interleaver_option);
BigUint::from_bytes_be(&interleaved_bytes)
}
pub fn interleave_be(vector : &[u32], bit_depth : usize, interleaver_option : Option<&Interleaver>) -> Vec<u8>
{
if let Some(interleaver) = interleaver_option {
return interleaver.interleave(vector);
}
let dimensions = vector.len(); let bytes_needed = (bit_depth * dimensions + 7) >> 3;
let mut byte_vector = vec![0_u8; bytes_needed];
let num_bits = dimensions * bit_depth;
let pad_bits = bytes_needed * 8 - num_bits;
for i_bit in 0 .. num_bits
{
let i_from_uint_vector = i_bit % dimensions; let i_from_uint_bit = bit_depth - (i_bit / dimensions) - 1; let i_to_byte_vector = (i_bit + pad_bits) >> 3; let i_to_byte_bit = 0x7 - ((i_bit + pad_bits) & 0x7);
let bit : u8 = (((vector[i_from_uint_vector] >> i_from_uint_bit) & 1_u32) << i_to_byte_bit) as u8;
byte_vector[i_to_byte_vector] |= bit;
}
byte_vector
}
#[allow(non_snake_case)]
pub fn hilbert_inverse_transform(transposed_index : &[u32], bits : usize) -> Vec<u32>
{
let mut X : Vec<u32> = transposed_index.to_vec();
let n = X.len(); let N : u32 = 2_u32 << (bits - 1);
let (mut P, mut Q, mut t) : (u32, u32, u32);
t = X[n - 1] >> 1;
for i in (1..n).rev() {
X[i] ^= X[i - 1];
}
X[0] ^= t;
Q = 2;
while Q != N {
P = Q - 1;
for i in (0..n).rev() {
if (X[i] & Q) != 0_u32 {
X[0] ^= P; }
else
{
t = (X[0] ^ X[i]) & P;
X[0] ^= t;
X[i] ^= t;
}
}
Q <<= 1;
} X
}
pub fn hilbert_axes(hilbert_index : &BigUint, bits : usize, dimensions : usize) -> Vec<u32>
{
let transposed = transpose(hilbert_index, bits, dimensions);
hilbert_inverse_transform(&transposed, bits)
}
#[allow(non_snake_case)]
pub fn hilbert_index_transposed(hilbert_axes : &[u32], bits : usize) -> Vec<u32>
{
let mut X = hilbert_axes.to_vec();
let n = hilbert_axes.len(); let M : u32 = 1_u32 << (bits - 1);
let (mut P, mut Q, mut t) : (u32, u32, u32);
Q = M;
while Q > 1
{
P = Q - 1;
let mut X_0 = X[0];
if (X_0 & Q) != 0 {
X_0 ^= P; }
for i in 1..n
{
let X_i = X[i];
if (X_i & Q) != 0 {
X_0 ^= P; }
else
{
t = (X_0 ^ X_i) & P;
X_0 ^= t;
X[i] = X_i ^ t;
}
}
X[0] = X_0;
Q >>= 1;
} let mut X_i_minus_1 = X[0];
for i in 1..n {
X[i] ^= X_i_minus_1;
X_i_minus_1 = X[i];
}
t = 0;
Q = M;
while Q > 1 {
if (X[n - 1] & Q) != 0 {
t ^= Q - 1;
}
Q >>= 1;
}
for i in 0..n {
X[i] ^= t;
}
X
}
pub fn hilbert_index(hilbert_axes : &[u32], bits : usize, interleaver_option : Option<&Interleaver>) -> BigUint
{
let transposed_index = hilbert_index_transposed(hilbert_axes, bits);
let index = untranspose(&transposed_index, bits, interleaver_option);
index
}
}
#[cfg(test)]
mod tests {
use num::BigUint;
use std::ops::Sub;
#[allow(unused_imports)]
use spectral::prelude::*;
use super::fast_hilbert;
#[test]
fn unpack_big_integer() {
let big : BigUint = 329_u32.into(); let actual_bit_array = fast_hilbert::unpack_big_integer(&big, 12);
let expected_bit_array : Vec<u8> = vec![1,0,0,1,0,0,1,0,1,0,0,0];
asserting!("Correct number of bits").that(&actual_bit_array.len()).is_equal_to(12);
asserting!("Correct ordering of bits").that(&actual_bit_array).is_equal_to(expected_bit_array);
}
#[test]
fn uninterleave() {
let gray_code : BigUint = 25676_u32.into();
let actual_axes = fast_hilbert::uninterleave(&gray_code, 5, 3, true);
let expected_axes : Vec<u32> = vec![17, 24, 6];
asserting("Correct uninterleave result").that(&actual_axes).is_equal_to(expected_axes);
}
#[test]
fn uninterleave_2_dims_3_bits() {
let gray_code : BigUint = 8_u32.into();
let actual_axes = fast_hilbert::uninterleave(&gray_code, 3, 2, true);
let expected_axes : Vec<u32> = vec![2, 0];
asserting("Correct uninterleave result").that(&actual_axes).is_equal_to(expected_axes);
}
#[test]
fn untranspose() {
let axes : Vec<u32> = vec![17, 24, 6];
let actual = fast_hilbert::untranspose(&axes, 5, None);
let expected : BigUint = 25676_u32.into();
asserting("Correct untranspose result").that(&actual).is_equal_to(expected);
}
#[test]
fn interleave_be() {
let axes : Vec<u32> = vec![17, 24, 6];
let actual = fast_hilbert::interleave_be(&axes, 5, None);
let expected : Vec<u8> = vec![100,76];
asserting("Correct interleave result").that(&actual).is_equal_to(expected);
}
fn abs_difference<T: Sub<Output = T> + Ord>(x: T, y: T) -> T {
if x < y {
y - x
} else {
x - y
}
}
fn verify_difference(vec1 : &[u32], vec2 : &[u32]) -> Result<usize, String> {
let mut changed_dimension = None;
for dimension in 0..(vec1.len()) {
let diff = abs_difference(vec1[dimension], vec2[dimension]);
if diff != 0 {
if changed_dimension.is_some() {
return Err(format!("More than one dimension changed: {} and {}. Vectors: {:?} and {:?}", dimension, changed_dimension.unwrap(), vec1, vec2));
}
else {
changed_dimension = Some(dimension);
if diff != 1 {
return Err(format!("Dimension {} changed by {} instead of 1", dimension, diff));
}
}
}
}
match changed_dimension {
Some(dim) => Ok(dim),
None => Err("No dimensions changed".to_string())
}
}
#[test]
fn hilbert_round_trip(){
let bits = 5;
let dimensions = 3;
let mut previous : Option<(Vec<u32>, Option<usize>)> = None;
let mut same_dimension_changed_twice = false;
let mut same_dimension_changed_thrice = false;
for i in 0..32768_u32 {
let expected_hilbert_index : BigUint = i.into();
let coordinates = fast_hilbert::hilbert_axes(&expected_hilbert_index, bits, dimensions);
let actual_hilbert_index = fast_hilbert::hilbert_index(&coordinates, bits, None);
asserting(&format!("Invertible for i = {}", i)).that(&actual_hilbert_index).is_equal_to(expected_hilbert_index);
previous = match previous {
None => Some((coordinates.clone(), None)),
Some((previous_coordinates, None)) => {
same_dimension_changed_twice = false;
same_dimension_changed_thrice = false;
match verify_difference(&coordinates, &previous_coordinates) {
Ok(changed_dim) => {
Some((coordinates, Some(changed_dim)))
},
Err(msg) => {
panic!("Error: {} for Hilbert index = {}", msg, i);
}
}
},
Some((previous_coordinates, Some(previous_changed_dim))) => {
match verify_difference(&coordinates, &previous_coordinates) {
Ok(changed_dim) => {
if previous_changed_dim == changed_dim {
asserting(&format!("Dimension {} changed more than thrice in a row at i = {}", previous_changed_dim, i))
.that(&same_dimension_changed_thrice).is_equal_to(false);
if same_dimension_changed_twice {
same_dimension_changed_thrice = true;
}
else {
same_dimension_changed_twice = true;
}
}
else {
same_dimension_changed_twice = false;
same_dimension_changed_thrice = false;
}
Some((coordinates, Some(changed_dim)))
},
Err(msg) => {
asserting(&format!("Error: {} for Hilbert index = {}", msg, i)).that(&true).is_equal_to(false);
panic!("Should never reach this line");
}
}
}
}
}
}
#[test]
fn exact_2_dimensions_3_bits() {
let bits : usize = 3;
let dimensions : usize = 2;
let num_points : usize = 1 << (bits * dimensions);
let expected : Vec<Vec<u32>> = vec![
vec![0,0], vec![0,1], vec![1,1], vec![1,0], vec![2,0], vec![3,0],
vec![3,1], vec![2,1], vec![2,2], vec![3,2], vec![3,3], vec![2,3],
vec![1,3], vec![1,2], vec![0,2], vec![0,3], vec![0,4], vec![1,4],
vec![1,5], vec![0,5], vec![0,6], vec![0,7], vec![1,7], vec![1,6],
vec![2,6], vec![2,7], vec![3,7], vec![3,6], vec![3,5], vec![2,5],
vec![2,4], vec![3,4], vec![4,4], vec![5,4], vec![5,5], vec![4,5],
vec![4,6], vec![4,7], vec![5,7], vec![5,6], vec![6,6], vec![6,7],
vec![7,7], vec![7,6], vec![7,5], vec![6,5], vec![6,4], vec![7,4],
vec![7,3], vec![7,2], vec![6,2], vec![6,3], vec![5,3], vec![4,3],
vec![4,2], vec![5,2], vec![5,1], vec![4,1], vec![4,0], vec![5,0],
vec![6,0], vec![6,1], vec![7,1], vec![7,0]
];
for i in 0..num_points {
let hilbert_index : BigUint = i.into();
let coordinates = fast_hilbert::hilbert_axes(&hilbert_index, bits, dimensions);
asserting(&format!("Hilbert Index = {}. Expected {:?}. Actual {:?}", i, expected[i], coordinates)).that(&coordinates).is_equal_to(expected[i].clone());
}
}
#[test]
fn single_transform_dim2_bits3_index8() {
let bits : usize = 3;
let dimensions : usize = 2;
let index = 8_u32;
let hilbert_index : BigUint = index.into();
let actual_point = fast_hilbert::hilbert_axes(&hilbert_index, bits, dimensions);
let expected_point = vec![2,2];
asserting(&format!("Hilbert Index = {}. Expected {:?}. Actual {:?}", index, expected_point, actual_point)).that(&actual_point).is_equal_to(expected_point);
}
fn dump_hilbert(dimensions : usize, bits : usize, from : u32, to : u32){
println!("{} dimensions, {} bits, i = {} to {}", dimensions, bits, from, to);
for i in from..to {
let expected_hilbert_index : BigUint = i.into();
let coordinates = fast_hilbert::hilbert_axes(&expected_hilbert_index, bits, dimensions);
println!("{} => {:?}", i, coordinates);
}
}
#[test]
#[ignore]
fn show_hilbert(){
dump_hilbert(3, 5, 0, 40);
panic!("show_hilbert");
}
}