use super::*;
pub const fn nside_max() -> u64 {
1 << 29
}
pub const fn n_hash(nside: u32) -> u64 {
let nside = nside as u64;
12 * nside * nside
}
pub const fn n_isolatitude_rings(nside: u32) -> u32 {
(nside << 2) - 1
}
#[inline]
pub const fn first_hash_in_eqr(nside: u32) -> u64 {
triangular_number_x4(nside as u64)
}
#[inline]
pub const fn first_hash_on_npc_eqr_transition(nside: u32) -> u64 {
triangular_number_x4((nside - 1) as u64)
}
#[inline]
pub const fn first_hash_on_eqr_spc_transition(nside: u32) -> u64 {
let n = nside as u64;
(n * (5 * n - 1)) << 1
}
#[inline]
pub const fn first_hash_in_spc(nside: u32) -> u64 {
let n = nside as u64;
(n * (5 * n + 1)) << 1
}
#[inline]
pub(crate) const fn triangular_number_x4(n: u64) -> u64 {
(n * (n + 1)) << 1
}
pub fn hash(nside: u32, lon: f64, lat: f64) -> u64 {
hash_with_dldh(nside, lon, lat).0
}
pub fn hash_with_dxdy(nside: u32, lon: f64, lat: f64) -> (u64, f64, f64) {
let (h, dl, dh) = hash_with_dldh(nside, lon, lat);
let (dx, dy) = dldh_to_dxdy(dl, dh);
(h, dx, dy)
}
fn hash_with_dldh(nside: u32, lon: f64, lat: f64) -> (u64, f64, f64) {
let nside = nside as u64;
let half_nside = 0.5 * (nside as f64);
let mut xy = proj(lon, lat);
xy.0 = ensures_x_is_positive(xy.0);
let mut dl = half_nside * xy.0;
debug_assert!(0.0 <= dl && dl < 4.0 * nside as f64);
let mut dh = half_nside * (xy.1 + 3.0);
debug_assert!(0.0 <= dh && dh <= 2.5 * nside as f64);
let mut i_ring = (dh as u64) << 1;
let mut i_in_ring = dl as u64;
dl -= i_in_ring as f64;
debug_assert!((0.0..1.0).contains(&dl));
dh -= (i_ring >> 1) as f64;
debug_assert!((0.0..1.0).contains(&dh));
deal_with_1x1_box(dl, dh, &mut i_ring, &mut i_in_ring);
if i_ring >= 5 * nside {
return (i_in_ring / nside, 1.0, 1.0);
}
i_ring = 5 * nside - 1 - i_ring;
let hash = if i_ring < nside {
let off = nside - 1 - i_ring;
i_in_ring -= (off >> 1) + (off & 1) + off * (i_in_ring / nside);
triangular_number_x4(i_ring) + i_in_ring
} else if i_ring >= 3 * nside {
let off = i_ring + 1 - 3 * nside;
i_in_ring -= (off >> 1) + (off & 1) + off * (i_in_ring / nside);
n_hash(nside as u32) - triangular_number_x4(n_isolatitude_rings(nside as u32) as u64 - i_ring)
+ i_in_ring
} else {
first_hash_in_eqr(nside as u32)
+ (i_ring - nside) * (nside << 2)
+ if i_in_ring == nside << 2 {
0
} else {
i_in_ring
}
};
(hash, dl, dh)
}
fn deal_with_1x1_box(dl: f64, dh: f64, i_ring: &mut u64, i_in_ring: &mut u64) {
debug_assert!((0.0..1.0).contains(&dl));
debug_assert!((0.0..1.0).contains(&dh));
let in1 = (dl <= dh) as u64;
debug_assert!(in1 == 0 || in1 == 1);
let in2 = (dl >= 1.0 - dh) as u64;
debug_assert!(in2 == 0 || in2 == 1);
*i_ring += in1 + in2;
*i_in_ring += in2 >> in1; }
#[allow(dead_code)]
fn build_hash(nside: u32, i_ring: u32, mut i_in_ring: u32) -> u64 {
debug_assert!(i_ring < n_isolatitude_rings(nside));
debug_assert!(i_in_ring <= n_isolatitude_rings(nside));
if i_ring < nside {
let off = nside - 1 - i_ring;
i_in_ring -= (off >> 1) + (off & 1) + off * (i_in_ring / nside);
triangular_number_x4(i_ring as u64) + i_in_ring as u64
} else if i_ring >= 3 * nside {
let off = i_ring + 1 - 3 * nside;
i_in_ring -= (off >> 1) + (off & 1) + off * (i_in_ring / nside);
n_hash(nside) - triangular_number_x4((n_isolatitude_rings(nside) - i_ring) as u64)
+ i_in_ring as u64
} else {
first_hash_in_eqr(nside)
+ (i_ring - nside) as u64 * (nside << 2) as u64
+ if i_in_ring == nside << 2 {
0
} else {
i_in_ring
} as u64
}
}
fn dldh_to_dxdy(dl: f64, dh: f64) -> (f64, f64) {
let dx = dh + dl - 1.0;
let dy = dh - dl;
(
dx + ((dx < 0.0) as u8) as f64,
dy + ((dy < 0.0) as u8) as f64,
)
}
#[inline]
fn is_hash(nside: u32, hash: u64) -> bool {
hash < n_hash(nside)
}
#[inline]
fn check_hash(nside: u32, hash: u64) {
assert!(is_hash(nside, hash), "Wrong hash value: too large.");
}
pub fn center_of_projected_cell(nside: u32, hash: u64) -> (f64, f64) {
check_hash(nside, hash);
if hash < first_hash_on_npc_eqr_transition(nside) {
let i_ring = (((1 + (hash << 1)) as f64).sqrt() as u64 - 1) >> 1;
let n_in_ring = i_ring + 1;
let i_in_ring = hash - triangular_number_x4(i_ring);
let q = i_in_ring / n_in_ring;
debug_assert!(q < 4);
let off_in_d0h = nside as u64 - i_ring;
let i_in_d0h_ring = i_in_ring - q * n_in_ring;
let x = ((i_in_d0h_ring) << 1) as f64 + off_in_d0h as f64;
let y = 1.0 + (nside as u64 - 1 - i_ring) as f64 / (nside as f64);
((q << 1) as f64 + x as f64 / nside as f64, y)
} else if hash >= first_hash_in_spc(nside) {
let hash = n_hash(nside) - 1 - hash; let i_ring = (((1 + (hash << 1)) as f64).sqrt() as u64 - 1) >> 1;
let n_in_ring = i_ring + 1;
let i_in_ring = ((n_in_ring << 2) - 1) - (hash - triangular_number_x4(i_ring));
let q = i_in_ring / n_in_ring;
let off_in_d0h = nside as u64 - i_ring;
let i_in_d0h_ring = i_in_ring - q * n_in_ring;
let x = ((i_in_d0h_ring) << 1) as f64 + off_in_d0h as f64;
let y = 1.0 + (nside as u64 - 1 - i_ring) as f64 / (nside as f64);
((q << 1) as f64 + x as f64 / nside as f64, -y)
} else {
let nsidex4 = (nside << 2) as u64;
let i_ring = (hash - first_hash_on_npc_eqr_transition(nside)) / nsidex4;
let i_in_ring = (hash - first_hash_on_npc_eqr_transition(nside)) - i_ring * nsidex4;
let x = ((i_in_ring << 1) + ((i_ring + 1) & 1)) as f64 / (nside as f64);
let y = (nside as i64 - i_ring as i64) as f64 / (nside as f64);
(x, y)
}
}
pub fn center(nside: u32, hash: u64) -> (f64, f64) {
let (x, y) = center_of_projected_cell(nside, hash);
super::unproj(x, y)
}
pub fn sph_coo(nside: u32, hash: u64, dx: f64, dy: f64) -> (f64, f64) {
assert!((0.0..1.0).contains(&dx));
assert!((0.0..1.0).contains(&dy));
let (mut x, mut y) = center_of_projected_cell(nside, hash);
x += (dx - dy) / (nside as f64);
y += (dx + dy - 1.0) / (nside as f64);
super::unproj(ensures_x_is_positive(x), y)
}
#[inline]
pub fn vertices(nside: u32, hash: u64) -> [(f64, f64); 4] {
let one_over_nside = 1.0 / (nside as f64);
let (x, y) = center_of_projected_cell(nside, hash);
[
super::unproj(x, y - one_over_nside), super::unproj(x + one_over_nside, y), super::unproj(x, y + one_over_nside), super::unproj(ensures_x_is_positive(x - one_over_nside), y), ]
}
#[cfg(test)]
mod tests {
use crate::ring::*;
#[test]
fn test_hash() {
let nside = 1;
assert_eq!(hash(nside, 40.0_f64.to_radians(), 45.0_f64.to_radians()), 0);
assert_eq!(
hash(nside, 130.0_f64.to_radians(), 45.0_f64.to_radians()),
1
);
assert_eq!(
hash(nside, 220.0_f64.to_radians(), 45.0_f64.to_radians()),
2
);
assert_eq!(
hash(nside, 310.0_f64.to_radians(), 45.0_f64.to_radians()),
3
);
assert_eq!(hash(nside, 0.0_f64.to_radians(), 0.0_f64.to_radians()), 4);
assert_eq!(hash(nside, 90.0_f64.to_radians(), 0.0_f64.to_radians()), 5);
assert_eq!(hash(nside, 180.0_f64.to_radians(), 0.0_f64.to_radians()), 6);
assert_eq!(hash(nside, 270.0_f64.to_radians(), 0.0_f64.to_radians()), 7);
assert_eq!(
hash(nside, 40.0_f64.to_radians(), -45.0_f64.to_radians()),
8
);
assert_eq!(
hash(nside, 130.0_f64.to_radians(), -45.0_f64.to_radians()),
9
);
assert_eq!(
hash(nside, 220.0_f64.to_radians(), -45.0_f64.to_radians()),
10
);
assert_eq!(
hash(nside, 310.0_f64.to_radians(), -45.0_f64.to_radians()),
11
);
assert_eq!(hash(nside, 40.0_f64.to_radians(), 90.0_f64.to_radians()), 0);
assert_eq!(
hash(nside, 130.0_f64.to_radians(), 90.0_f64.to_radians()),
1
);
assert_eq!(
hash(nside, 220.0_f64.to_radians(), 90.0_f64.to_radians()),
2
);
assert_eq!(
hash(nside, 310.0_f64.to_radians(), 90.0_f64.to_radians()),
3
);
assert_eq!(
hash(nside, 40.0_f64.to_radians(), -90.0_f64.to_radians()),
8
);
assert_eq!(
hash(nside, 130.0_f64.to_radians(), -90.0_f64.to_radians()),
9
);
assert_eq!(
hash(nside, 220.0_f64.to_radians(), -90.0_f64.to_radians()),
10
);
assert_eq!(
hash(nside, 310.0_f64.to_radians(), -90.0_f64.to_radians()),
11
);
assert_eq!(hash(nside, 44.0_f64.to_radians(), 0.0_f64.to_radians()), 4);
assert_eq!(hash(nside, 46.0_f64.to_radians(), 0.0_f64.to_radians()), 5);
assert_eq!(hash(nside, 45.0_f64.to_radians(), 1.0_f64.to_radians()), 0);
assert_eq!(hash(nside, 45.0_f64.to_radians(), -1.0_f64.to_radians()), 8);
}
#[test]
fn test_hash_2() {
for depth in 0..10 {
let nside = nside(depth);
let layer = nested::get(depth);
for icell in 0..layer.n_hash() {
let (lon, lat) = layer.center(icell);
assert_eq!(hash(nside, lon, lat), layer.to_ring(icell));
}
}
}
#[test]
fn test_hash_3() {
let nside = 4_u32;
let lon = 4.71238898;
let lat = -1.15965846;
let h = hash(nside, lon, lat);
println!("h: {}", &h);
}
#[test]
fn test_deal_with_1x1_box() {
let mut i_ring = 0;
let mut i_in_ring = 0;
deal_with_1x1_box(0.0, 0.0, &mut i_ring, &mut i_in_ring);
assert_eq!(i_ring, 1);
assert_eq!(i_in_ring, 0);
i_ring = 0;
i_in_ring = 0;
deal_with_1x1_box(0.5, 0.0, &mut i_ring, &mut i_in_ring);
assert_eq!(i_ring, 0);
assert_eq!(i_in_ring, 0);
i_ring = 0;
i_in_ring = 0;
deal_with_1x1_box(0.9, 0.5, &mut i_ring, &mut i_in_ring);
assert_eq!(i_ring, 1);
assert_eq!(i_in_ring, 1);
i_ring = 0;
i_in_ring = 0;
deal_with_1x1_box(0.5, 0.9, &mut i_ring, &mut i_in_ring);
assert_eq!(i_ring, 2);
assert_eq!(i_in_ring, 0);
i_ring = 0;
i_in_ring = 0;
deal_with_1x1_box(0.5, 0.5, &mut i_ring, &mut i_in_ring);
assert_eq!(i_ring, 2);
assert_eq!(i_in_ring, 0);
i_ring = 0;
i_in_ring = 0;
deal_with_1x1_box(0.25, 0.75, &mut i_ring, &mut i_in_ring);
assert_eq!(i_ring, 2);
assert_eq!(i_in_ring, 0);
i_ring = 0;
i_in_ring = 0;
deal_with_1x1_box(0.75, 0.75, &mut i_ring, &mut i_in_ring);
assert_eq!(i_ring, 2);
assert_eq!(i_in_ring, 0);
i_ring = 0;
i_in_ring = 0;
deal_with_1x1_box(0.75, 0.25, &mut i_ring, &mut i_in_ring);
assert_eq!(i_ring, 1);
assert_eq!(i_in_ring, 1);
i_ring = 0;
i_in_ring = 0;
deal_with_1x1_box(0.25, 0.25, &mut i_ring, &mut i_in_ring);
assert_eq!(i_ring, 1);
assert_eq!(i_in_ring, 0);
}
#[test]
fn test_dldh_to_dxdy() {
assert_eq!(dldh_to_dxdy(0.0, 0.0), (0.0, 0.0));
assert_eq!(dldh_to_dxdy(0.5, 0.0), (0.5, 0.5));
assert_eq!(dldh_to_dxdy(0.0, 0.5), (0.5, 0.5));
assert_eq!(dldh_to_dxdy(0.5, 1.0), (0.5, 0.5));
assert_eq!(dldh_to_dxdy(1.0, 0.5), (0.5, 0.5));
assert_eq!(dldh_to_dxdy(0.25, 0.25), (0.5, 0.0));
assert_eq!(dldh_to_dxdy(0.75, 0.25), (0.0, 0.5));
assert_eq!(dldh_to_dxdy(0.75, 0.75), (0.5, 0.0));
assert_eq!(dldh_to_dxdy(0.25, 0.75), (0.0, 0.5));
}
#[test]
fn test_center() {
let nside = 2;
let ipix = 33;
println!("{:?}", center_of_projected_cell(nside, ipix));
let (lon, lat) = center(nside, ipix);
println!("(lon: {}, lat: {})", lon.to_degrees(), lat.to_degrees());
}
#[test]
fn test_center_2() {
let nside = 2;
assert_eq!(center_of_projected_cell(nside, 2), (5.0, 1.5));
assert_eq!(center_of_projected_cell(nside, 46), (5.0, -1.5));
}
#[test]
fn test_center_3() {
let nside = 4;
let l2 = nested::get(2);
let ipix = 7;
assert_eq!(
center_of_projected_cell(nside, ipix),
l2.center_of_projected_cell(l2.from_ring(ipix))
);
let ipix = 183;
assert_eq!(
center_of_projected_cell(nside, ipix),
l2.center_of_projected_cell(l2.from_ring(ipix))
);
}
}