use std::f32::consts::{PI};
use crate::nside;
pub const TRANSITION_Z: f32 = 2.0 / 3.0;
pub const TRANSITION_Z_INV: f32 = 3.0 / 2.0;
pub fn proj_gpu(x: f32, y: f32, z: f32) -> (f32, f32) {
assert!((-1.0..=1.0).contains(&x));
assert!((-1.0..=1.0).contains(&y));
assert!((-1.0..=1.0).contains(&z));
debug_assert!(1.0 - (x * x + y * y + z * z) < 1e-5, "{}", 1.0 - (x * x + y * y + z * z));
if z > TRANSITION_Z {
let (x_pm1, offset) = xpm1_and_offset(x, y);
let sqrt_3_one_min_z = (3.0 * one_minus_z_pos(x, y, z)).sqrt();
(
(x_pm1 * sqrt_3_one_min_z) + offset as f32,
2.0 - sqrt_3_one_min_z
)
} else if z < -TRANSITION_Z {
let (x_pm1, offset) = xpm1_and_offset(x, y);
let sqrt_3_one_min_z = (3.0 * one_minus_z_neg(x, y, z)).sqrt();
(
(x_pm1 * sqrt_3_one_min_z) + offset as f32,
-2.0 + sqrt_3_one_min_z
)
} else {
(
y.atan2(x) * 4.0 / PI,
z * TRANSITION_Z_INV
)
}
}
#[allow(clippy::many_single_char_names)]
pub fn hash_with_dxdy(depth: u8, x: f32, y: f32, z: f32) -> (u32, f32, f32) {
assert!(depth <= 14);
assert!((-1.0..=1.0).contains(&x));
assert!((-1.0..=1.0).contains(&y));
assert!((-1.0..=1.0).contains(&z));
debug_assert!(1.0 - (x * x + y * y + z * z) < 1e-5, "{}", 1.0 - (x * x + y * y + z * z));
let nside = nside(depth);
let half_nside = nside as f32 * 0.5;
let (x_pm1, q) = xpm1_and_q(x, y);
let (d0h, x_in_d0c, y_in_d0c) = if z > TRANSITION_Z {
let sqrt_3_one_min_z = (3.0 * one_minus_z_pos(x, y, z)).sqrt();
let (x_proj, y_proj) = (x_pm1 * sqrt_3_one_min_z, 2.0 - sqrt_3_one_min_z);
let d0h = q;
(d0h, x_proj, y_proj)
} else if z < -TRANSITION_Z {
let sqrt_3_one_min_z = (3.0 * one_minus_z_neg(x, y, z)).sqrt();
let (x_proj, y_proj) = (x_pm1 * sqrt_3_one_min_z, sqrt_3_one_min_z);
let d0h = q + 8;
(d0h, x_proj, y_proj)
} else {
let y_pm1 = z * TRANSITION_Z_INV;
let q01 = (x_pm1 > y_pm1) as u8; debug_assert!(q01 == 0 || q01 == 1);
let q12 = (x_pm1 >= -y_pm1) as u8; debug_assert!(q12 == 0 || q12 == 1);
let q03 = 1 - q12;
let q1 = q01 & q12; debug_assert!( q1 == 0 || q1 == 1);
let x_proj = x_pm1 - ((q01 + q12) as i8 - 1) as f32;
let y_proj = y_pm1 + (q01 + q03) as f32;
let d0h = ((q01 + q03) << 2) + ((q + q1) & 3);
(d0h, x_proj, y_proj)
};
let x = half_nside * (y_in_d0c + x_in_d0c); debug_assert!((0.0 - 1e-5) < x && x < (nside as f32 + 1e-5), "x: {}, x_proj: {}; y_proj: {}", &x, &x_in_d0c, &y_in_d0c);
let y = half_nside * (y_in_d0c - x_in_d0c); debug_assert!((0.0 - 1e-5) < x && y < (nside as f32 + 1e-5), "y: {}", &y);
let mut i = x as u32;
let mut j = y as u32;
if i == nside { i -= 1; } if j == nside { j -= 1; } (
((d0h as u32) << (depth << 1)) | ij2z(i, j),
(x - (i as f32)),
(y - (j as f32)),
)
}
fn xpm1_and_q(x: f32, y: f32) -> (f32, u8) {
let x_neg = (x < 0.0) as u8; debug_assert!(x_neg <= 1);
let y_neg = (y < 0.0) as u8; debug_assert!(y_neg <= 1);
let q = (x_neg + y_neg) | (y_neg << 1); debug_assert!(y_neg <= 3);
let lon = y.abs().atan2(x.abs()); debug_assert!((0.0..=PI / 2.0).contains(&lon));
let x02 = lon * 4.0 / PI; debug_assert!((0.0..=2.0).contains(&x02));
if x_neg != y_neg { (1.0 - x02, q)
} else {
(x02 - 1.0, q)
}
}
fn xpm1_and_offset(x: f32, y: f32) -> (f32, i8) {
let x_neg = (x < 0.0) as i8; debug_assert!(x_neg == 0 || x_neg == 1);
let y_neg = (y < 0.0) as i8; debug_assert!(y_neg == 0 || y_neg == 1);
let offset = ((-y_neg) << 2) + 1 + ((x_neg ^ y_neg) << 1);
let lon = y.abs().atan2(x.abs()); debug_assert!((0.0..=PI / 2.0).contains(&lon));
let x02 = lon * 4.0 / PI; debug_assert!((0.0..=2.0).contains(&x02));
if x_neg != y_neg { (1.0 - x02, offset)
} else {
(x02 - 1.0, offset)
}
}
fn one_minus_z_pos(x: f32, y: f32, z: f32) -> f32 {
debug_assert!(z > 0.0);
let d2: f32 = x * x + y * y;
if d2 < 1e-1 { d2 * (0.5 + d2 * (0.125 + d2 * (0.0625 + d2 * (0.0390625 + d2 * 0.02734375))))
} else {
1.0 - z
}
}
fn one_minus_z_neg(x: f32, y: f32, z: f32) -> f32 {
debug_assert!(z < 0.0);
let d2: f32 = x * x + y * y; if d2 < 1e-1 { d2 * (0.5 + d2 * (0.125 + d2 * (0.0625 + d2 * (0.0390625 + d2 * 0.02734375))))
} else {
z + 1.0
}
}
fn ij2z(mut i: u32, mut j: u32) -> u32 {
i |= j << 16;
j = (i ^ (i >> 8)) & 0x0000FF00_u32; i = i ^ j ^ (j << 8);
j = (i ^ (i >> 4)) & 0x00F000F0_u32; i = i ^ j ^ (j << 4);
j = (i ^ (i >> 2)) & 0x0C0C0C0C_u32; i = i ^ j ^ (j << 2);
j = (i ^ (i >> 1)) & 0x22222222_u32; i = i ^ j ^ (j << 1);
i
}
#[cfg(test)]
mod tests {
use crate::nested;
use crate::proj;
use super::*;
#[test]
fn testok_hash_gpu_spe_1() {
const DELTA_DEPTH: u8 = 1;
let depth = 13; let layer = nested::get(depth + DELTA_DEPTH);
let h = 259907375;
let (ra, de) = layer.center(h);
println!("{:?}", proj(ra, de));
let (sl, cl) = ra.sin_cos();
let (sb, cb) = de.sin_cos();
let (x, y, z) = (cb * cl, cb * sl, sb);
let (nh, dx, dy) = hash_with_dxdy(depth, x as f32, y as f32, z as f32);
assert_eq!((h >> (DELTA_DEPTH << 1)) as u32, nh);
let prec = 1.0 / 512.0; match h & 3 {
0 => {
assert!((dx - 0.25).abs() <= prec, "h: {}; ra: {}; dec: {}; dx: {}; dy: {}; prec: {}", h, ra.to_degrees(), de.to_degrees(), dx, dy, (dx - 0.25).abs()); assert!((dy - 0.25).abs() <= prec, "h: {}; ra: {}; dec: {}; dx: {}; dy: {}; prec: {}", h, ra.to_degrees(), de.to_degrees(), dx, dy, (dy - 0.25).abs()); },
1 => {
assert!((dx - 0.75).abs() <= prec, "h: {}; ra: {}; dec: {}; dx: {}; dy: {}; prec: {}", h, ra.to_degrees(), de.to_degrees(), dx, dy, (dx - 0.75).abs());
assert!((dy - 0.25).abs() <= prec, "h: {}; ra: {}; dec: {}; dx: {}; dy: {}; prec: {}", h, ra.to_degrees(), de.to_degrees(), dx, dy, (dy - 0.25).abs());
},
2 => {
assert!((dx - 0.25).abs() <= prec, "h: {}; ra: {}; dec: {}; dx: {}; dy: {}; prec: {}", h, ra.to_degrees(), de.to_degrees(), dx, dy, (dx - 0.25).abs());
assert!((dy - 0.75).abs() <= prec, "h: {}; ra: {}; dec: {}; dx: {}; dy: {}; prec: {}", h, ra.to_degrees(), de.to_degrees(), dx, dy, (dy - 0.75).abs());
},
3 => {
assert!((dx - 0.75).abs() <= prec, "h: {}; ra: {}; dec: {}; dx: {}; dy: {}; prec: {}", h, ra.to_degrees(), de.to_degrees(), dx, dy, (dx - 0.75).abs());
assert!((dy - 0.75).abs() <= prec, "h: {}; ra: {}; dec: {}; dx: {}; dy: {}; prec: {}", h, ra.to_degrees(), de.to_degrees(), dx, dy, (dy - 0.75).abs());
},
_ => unreachable!(),
}
}
#[test]
fn testok_hash_gpu_spe_2() {
const DELTA_DEPTH: u8 = 1;
let depth = 13; let layer = nested::get(depth + DELTA_DEPTH);
let h = 430712695;
let (ra, de) = layer.center(h);
println!("RA: {}; Dec: {}", ra.to_degrees(), de.to_degrees());
println!("Proj: {:?}", proj(ra, de));
let (sl, cl) = ra.sin_cos();
let (sb, cb) = de.sin_cos();
let (x, y, z) = (cb * cl, cb * sl, sb);
let (nh, dx, dy) = hash_with_dxdy(depth, x as f32, y as f32, z as f32);
assert_eq!((h >> (DELTA_DEPTH << 1)) as u32, nh);
let prec = 1.0 / 512.0; match h & 3 {
0 => {
assert!((dx - 0.25).abs() <= prec, "h: {}; ra: {}; dec: {}; dx: {}; dy: {}; prec: {}", h, ra.to_degrees(), de.to_degrees(), dx, dy, (dx - 0.25).abs()); assert!((dy - 0.25).abs() <= prec, "h: {}; ra: {}; dec: {}; dx: {}; dy: {}; prec: {}", h, ra.to_degrees(), de.to_degrees(), dx, dy, (dy - 0.25).abs()); },
1 => {
assert!((dx - 0.75).abs() <= prec, "h: {}; ra: {}; dec: {}; dx: {}; dy: {}; prec: {}", h, ra.to_degrees(), de.to_degrees(), dx, dy, (dx - 0.75).abs());
assert!((dy - 0.25).abs() <= prec, "h: {}; ra: {}; dec: {}; dx: {}; dy: {}; prec: {}", h, ra.to_degrees(), de.to_degrees(), dx, dy, (dy - 0.25).abs());
},
2 => {
assert!((dx - 0.25).abs() <= prec, "h: {}; ra: {}; dec: {}; dx: {}; dy: {}; prec: {}", h, ra.to_degrees(), de.to_degrees(), dx, dy, (dx - 0.25).abs());
assert!((dy - 0.75).abs() <= prec, "h: {}; ra: {}; dec: {}; dx: {}; dy: {}; prec: {}", h, ra.to_degrees(), de.to_degrees(), dx, dy, (dy - 0.75).abs());
},
3 => {
assert!((dx - 0.75).abs() <= prec, "h: {}; ra: {}; dec: {}; dx: {}; dy: {}; prec: {}", h, ra.to_degrees(), de.to_degrees(), dx, dy, (dx - 0.75).abs());
assert!((dy - 0.75).abs() <= prec, "h: {}; ra: {}; dec: {}; dx: {}; dy: {}; prec: {}", h, ra.to_degrees(), de.to_degrees(), dx, dy, (dy - 0.75).abs());
},
_ => unreachable!(),
}
}
#[test]
fn testok_hash_gpu_sys() {
const DELTA_DEPTH: u8 = 1;
let depth = 4; let layer = nested::get(depth + DELTA_DEPTH);
let mut d0h = 0;
for h in 0..layer.n_hash() {
if (h >> ((depth + 1) << 1)) != d0h {
d0h += 1;
println!("New base cell: {}", d0h);
}
let (ra, de) = layer.center(h);
let (sl, cl) = ra.sin_cos();
let (sb, cb) = de.sin_cos();
let (x, y, z) = (cb * cl, cb * sl, sb);
let (nh, dx, dy) = hash_with_dxdy(depth, x as f32, y as f32, z as f32);
assert_eq!((h >> (DELTA_DEPTH << 1)) as u32, nh);
let prec = 1.0 / 512.0; match h & 3 {
0 => {
assert!((dx - 0.25).abs() <= prec, "h: {}; ra: {}; dec: {}; dx: {}; dy: {}; prec: {}", h, ra.to_degrees(), de.to_degrees(), dx, dy, (dx - 0.25).abs()); assert!((dy - 0.25).abs() <= prec, "h: {}; ra: {}; dec: {}; dx: {}; dy: {}; prec: {}", h, ra.to_degrees(), de.to_degrees(), dx, dy, (dy - 0.25).abs()); },
1 => {
assert!((dx - 0.75).abs() <= prec, "h: {}; ra: {}; dec: {}; dx: {}; dy: {}; prec: {}", h, ra.to_degrees(), de.to_degrees(), dx, dy, (dx - 0.75).abs());
assert!((dy - 0.25).abs() <= prec, "h: {}; ra: {}; dec: {}; dx: {}; dy: {}; prec: {}", h, ra.to_degrees(), de.to_degrees(), dx, dy, (dy - 0.25).abs());
},
2 => {
assert!((dx - 0.25).abs() <= prec, "h: {}; ra: {}; dec: {}; dx: {}; dy: {}; prec: {}", h, ra.to_degrees(), de.to_degrees(), dx, dy, (dx - 0.25).abs());
assert!((dy - 0.75).abs() <= prec, "h: {}; ra: {}; dec: {}; dx: {}; dy: {}; prec: {}", h, ra.to_degrees(), de.to_degrees(), dx, dy, (dy - 0.75).abs());
},
3 => {
assert!((dx - 0.75).abs() <= prec, "h: {}; ra: {}; dec: {}; dx: {}; dy: {}; prec: {}", h, ra.to_degrees(), de.to_degrees(), dx, dy, (dx - 0.75).abs());
assert!((dy - 0.75).abs() <= prec, "h: {}; ra: {}; dec: {}; dx: {}; dy: {}; prec: {}", h, ra.to_degrees(), de.to_degrees(), dx, dy, (dy - 0.75).abs());
},
_ => unreachable!(),
}
}
}
}