#[derive(Debug, Default, Clone, Copy)]
pub struct OctahedronToolBox {
quantization_bits: i32,
max_quantized_value: i32,
max_value: i32,
dequantization_scale: f32,
center_value: i32,
}
impl OctahedronToolBox {
pub fn new() -> Self {
Self::default()
}
pub fn set_quantization_bits(&mut self, q: i32) -> bool {
if !(2..=30).contains(&q) {
return false;
}
self.quantization_bits = q;
self.max_quantized_value = (1 << q) - 1;
self.max_value = self.max_quantized_value - 1;
self.dequantization_scale = 2.0 / self.max_value as f32;
self.center_value = self.max_value / 2;
true
}
pub fn is_initialized(&self) -> bool {
self.quantization_bits != -1
}
pub fn quantization_bits(&self) -> i32 {
self.quantization_bits
}
pub fn max_quantized_value(&self) -> i32 {
self.max_quantized_value
}
pub fn max_value(&self) -> i32 {
self.max_value
}
pub fn center_value(&self) -> i32 {
self.center_value
}
pub fn is_in_diamond(&self, s: i32, t: i32) -> bool {
debug_assert!(s <= self.center_value);
debug_assert!(t <= self.center_value);
debug_assert!(s >= -self.center_value);
debug_assert!(t >= -self.center_value);
let st = s.unsigned_abs() + t.unsigned_abs();
st <= self.center_value as u32
}
pub fn invert_diamond(&self, s: &mut i32, t: &mut i32) {
debug_assert!(*s <= self.center_value);
debug_assert!(*t <= self.center_value);
debug_assert!(*s >= -self.center_value);
debug_assert!(*t >= -self.center_value);
let sign_s: i32;
let sign_t: i32;
if *s >= 0 && *t >= 0 {
sign_s = 1;
sign_t = 1;
} else if *s <= 0 && *t <= 0 {
sign_s = -1;
sign_t = -1;
} else {
sign_s = if *s > 0 { 1 } else { -1 };
sign_t = if *t > 0 { 1 } else { -1 };
}
let corner_point_s = (sign_s * self.center_value) as u32;
let corner_point_t = (sign_t * self.center_value) as u32;
let mut us = *s as u32;
let mut ut = *t as u32;
us = us.wrapping_add(us).wrapping_sub(corner_point_s);
ut = ut.wrapping_add(ut).wrapping_sub(corner_point_t);
if sign_s * sign_t >= 0 {
let temp = us;
us = (-(ut as i32)) as u32;
ut = (-(temp as i32)) as u32;
} else {
std::mem::swap(&mut us, &mut ut);
}
us = us.wrapping_add(corner_point_s);
ut = ut.wrapping_add(corner_point_t);
*s = us as i32;
*t = ut as i32;
*s /= 2;
*t /= 2;
}
pub fn invert_direction(&self, s: &mut i32, t: &mut i32) {
*s *= -1;
*t *= -1;
self.invert_diamond(s, t);
}
pub fn mod_max(&self, x: i32) -> i32 {
if x > self.center_value {
return x - self.max_quantized_value;
}
if x < -self.center_value {
return x + self.max_quantized_value;
}
x
}
pub fn mod_max_positive(&self, x: i32) -> i32 {
x & self.max_quantized_value
}
pub fn make_positive(&self, x: i32) -> i32 {
debug_assert!(x <= self.center_value * 2);
if x < 0 {
return x + self.max_quantized_value;
}
x
}
pub fn canonicalize_octahedral_coords(&self, s: i32, t: i32) -> (i32, i32) {
let mut s = s;
let mut t = t;
let is_corner =
(s == 0 && (t == 0 || t == self.max_value)) || (s == self.max_value && t == 0);
if is_corner {
s = self.max_value;
t = self.max_value;
} else if s == 0 && t > self.center_value {
t = self.center_value - (t - self.center_value);
} else if s == self.max_value && t < self.center_value {
t = self.center_value + (self.center_value - t);
} else if t == self.max_value && s < self.center_value {
s = self.center_value + (self.center_value - s);
} else if t == 0 && s > self.center_value {
s = self.center_value - (s - self.center_value);
}
(s, t)
}
pub fn canonicalize_integer_vector(&self, vec: &mut [i32; 3]) {
let abs_sum = (vec[0].abs() as i64) + (vec[1].abs() as i64) + (vec[2].abs() as i64);
if abs_sum == 0 {
vec[0] = self.center_value;
vec[1] = 0;
vec[2] = 0;
} else {
vec[0] = ((vec[0] as i64 * self.center_value as i64) / abs_sum) as i32;
vec[1] = ((vec[1] as i64 * self.center_value as i64) / abs_sum) as i32;
if vec[2] >= 0 {
vec[2] = self.center_value - vec[0].abs() - vec[1].abs();
} else {
vec[2] = -(self.center_value - vec[0].abs() - vec[1].abs());
}
}
}
pub fn integer_vector_to_quantized_octahedral_coords(&self, int_vec: &[i32; 3]) -> (i32, i32) {
let abs_sum = int_vec[0].abs() + int_vec[1].abs() + int_vec[2].abs();
debug_assert_eq!(abs_sum, self.center_value);
let s;
let t;
if int_vec[0] >= 0 {
s = int_vec[1] + self.center_value;
t = int_vec[2] + self.center_value;
} else {
if int_vec[1] < 0 {
s = int_vec[2].abs();
} else {
s = self.max_value - int_vec[2].abs();
}
if int_vec[2] < 0 {
t = int_vec[1].abs();
} else {
t = self.max_value - int_vec[1].abs();
}
}
self.canonicalize_octahedral_coords(s, t)
}
pub fn float_vector_to_quantized_octahedral_coords(&self, vector: &[f32; 3]) -> (i32, i32) {
let abs_sum = vector[0].abs() + vector[1].abs() + vector[2].abs();
let mut scaled_vector = [0.0; 3];
if abs_sum > 1e-6 {
let scale = 1.0 / abs_sum;
scaled_vector[0] = vector[0] * scale;
scaled_vector[1] = vector[1] * scale;
scaled_vector[2] = vector[2] * scale;
} else {
scaled_vector[0] = 1.0;
scaled_vector[1] = 0.0;
scaled_vector[2] = 0.0;
}
let mut int_vec = [0; 3];
int_vec[0] = (scaled_vector[0] * self.center_value as f32 + 0.5).floor() as i32;
int_vec[1] = (scaled_vector[1] * self.center_value as f32 + 0.5).floor() as i32;
int_vec[2] = self.center_value - int_vec[0].abs() - int_vec[1].abs();
if int_vec[2] < 0 {
if int_vec[1] > 0 {
int_vec[1] += int_vec[2];
} else {
int_vec[1] -= int_vec[2];
}
int_vec[2] = 0;
}
if scaled_vector[2] < 0.0 {
int_vec[2] *= -1;
}
self.integer_vector_to_quantized_octahedral_coords(&int_vec)
}
pub fn quantized_octahedral_coords_to_unit_vector(&self, s: i32, t: i32) -> [f32; 3] {
let in_s_scaled = s as f32 * self.dequantization_scale - 1.0;
let in_t_scaled = t as f32 * self.dequantization_scale - 1.0;
let mut y = in_s_scaled;
let mut z = in_t_scaled;
let x = 1.0 - y.abs() - z.abs();
if x < 0.0 {
let x_offset = -x;
y += if y < 0.0 { x_offset } else { -x_offset };
z += if z < 0.0 { x_offset } else { -x_offset };
}
let norm_squared = x * x + y * y + z * z;
if norm_squared < 1e-6 {
[0.0, 0.0, 0.0]
} else {
let d = 1.0 / norm_squared.sqrt();
[x * d, y * d, z * d]
}
}
pub fn quantized_octahedral_coords_to_unit_vector_legacy(&self, s: i32, t: i32) -> [f32; 3] {
let max_quantized_value = self.max_value as f32;
let in_s = s as f32 / max_quantized_value;
let in_t = t as f32 / max_quantized_value;
let mut out_s = in_s;
let mut out_t = in_t;
let mut spt = out_s + out_t;
let mut smt = out_s - out_t;
let mut x_sign = 1.0;
if !(0.5..=1.5).contains(&spt) || !(-0.5..=0.5).contains(&smt) {
x_sign = -1.0;
if spt <= 0.5 {
out_s = 0.5 - in_t;
out_t = 0.5 - in_s;
} else if spt >= 1.5 {
out_s = 1.5 - in_t;
out_t = 1.5 - in_s;
} else if smt <= -0.5 {
out_s = in_t - 0.5;
out_t = in_s + 0.5;
} else {
out_s = in_t + 0.5;
out_t = in_s - 0.5;
}
spt = out_s + out_t;
smt = out_s - out_t;
}
let y = 2.0 * out_s - 1.0;
let z = 2.0 * out_t - 1.0;
let x = (2.0 * spt - 1.0)
.min(3.0 - 2.0 * spt)
.min(2.0 * smt + 1.0)
.min(1.0 - 2.0 * smt)
* x_sign;
let norm_squared = x * x + y * y + z * z;
if norm_squared < 1e-6 {
[0.0, 0.0, 0.0]
} else {
let d = 1.0 / norm_squared.sqrt();
[x * d, y * d, z * d]
}
}
}