use num::One;
use std::fmt::Debug;
use std::marker::PhantomData;
use std::ops::Range;
use crate::deser::fits::keywords::MocDim;
use crate::idx::Idx;
pub trait Bounded<T> {
fn upper_bound_exclusive() -> T;
}
impl<T, Q> Bounded<T> for Q
where
T: Idx,
Q: MocQty<T>,
{
fn upper_bound_exclusive() -> T {
Self::n_cells_max()
}
}
pub trait MocableQty: 'static + PartialEq + Eq + Send + Sync + Clone + Debug {
const N_RESERVED_BITS: u8 = 2;
const NAME: &'static str;
const PREFIX: char;
const DIM: u8;
const N_D0_CELLS: u8;
const N_D0_BITS: u8 = n_bits_to_code_from_0_to_n_exclusive(Self::N_D0_CELLS);
const LEVEL_MASK: u8 = (1 << Self::DIM) - 1;
const MOC_DIM: MocDim;
const HAS_COOSYS: bool;
const HAS_TIMESYS: bool;
const HAS_FREQSYS: bool;
fn mult_by_dim<T: Idx>(v: T) -> T;
fn div_by_dim<T: Idx>(v: T) -> T;
#[inline(always)]
fn shift(delta_depth: u8) -> u8 {
Self::mult_by_dim(delta_depth)
}
}
const fn n_bits_to_code_from_0_to_n_exclusive(n: u8) -> u8 {
let n_bits_in_u8 = u8::N_BITS as u32; let index_max = n - 1;
(n_bits_in_u8 - index_max.leading_zeros()) as u8
}
pub trait MocQty<T>: MocableQty
where
T: Idx,
{
const MAX_DEPTH: u8 = (T::N_BITS - (Self::N_RESERVED_BITS + Self::N_D0_BITS)) / Self::DIM;
const MAX_SHIFT: u32 = (Self::DIM * Self::MAX_DEPTH) as u32;
fn n_cells_max() -> T {
let nd0: T = Self::N_D0_CELLS.into();
nd0.unsigned_shl(Self::MAX_SHIFT)
}
fn n_cells(depth: u8) -> T {
let nd0: T = Self::N_D0_CELLS.into();
nd0.unsigned_shl(Self::shift(depth) as u32)
}
fn delta_depth_max_from_n_bits(n_bits: u8) -> u8 {
Self::delta_depth_max_from_n_bits_unchecked(n_bits).min(Self::MAX_DEPTH)
}
fn delta_depth_max_from_n_bits_unchecked(n_bits: u8) -> u8 {
n_bits >> (Self::DIM - 1)
}
fn delta_with_depth_max(depth: u8) -> u8 {
Self::MAX_DEPTH - depth
}
fn shift_from_depth_max(depth: u8) -> u8 {
Self::shift(Self::delta_with_depth_max(depth))
}
#[inline(always)]
fn get_msb(x: T) -> u32 {
T::N_BITS as u32 - x.leading_zeros() - 1
}
#[inline(always)]
fn get_lsb(x: T) -> u32 {
x.trailing_zeros()
}
#[inline(always)]
fn compute_min_depth(x: T) -> u8 {
let dd = Self::div_by_dim(x.trailing_zeros() as u8).min(Self::MAX_DEPTH);
Self::MAX_DEPTH - dd
}
#[inline(always)]
fn from_uniq_gen(uniq: T) -> (u8, T) {
let depth = Self::div_by_dim(T::N_BITS - uniq.leading_zeros() as u8 - 1 - Self::N_D0_BITS);
let idx = uniq & !Self::sentinel_bit(depth);
(depth, idx)
}
#[inline(always)]
fn to_uniq_gen(depth: u8, idx: T) -> T {
Self::sentinel_bit(depth) | idx
}
#[inline(always)]
fn sentinel_bit(depth: u8) -> T {
T::one()
.unsigned_shl(Self::N_D0_BITS as u32)
.unsigned_shl(Self::shift(depth) as u32)
}
#[inline(always)]
fn uniq_gen_to_range(uniq: T) -> Range<T> {
let (depth, pix) = Self::from_uniq_gen(uniq);
let tdd = ((Self::MAX_DEPTH - depth) << 1) as u32;
Range {
start: pix.unsigned_shl(tdd),
end: (pix + One::one()).unsigned_shl(tdd),
}
}
fn to_zuniq(depth: u8, idx: T) -> T {
let zuniq = (idx << 1) | T::one();
zuniq.unsigned_shl(Self::shift_from_depth_max(depth) as u32)
}
fn from_zuniq(zuniq: T) -> (u8, T) {
let n_trailing_zero = zuniq.trailing_zeros() as u8;
let delta_depth = Self::div_by_dim(n_trailing_zero);
let depth = Self::MAX_DEPTH - delta_depth;
let idx = zuniq >> (n_trailing_zero + 1) as usize;
(depth, idx)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Hpx<T: Idx>(std::marker::PhantomData<T>);
impl<T: Idx> MocableQty for Hpx<T> {
const NAME: &'static str = "HPX";
const PREFIX: char = 's';
const DIM: u8 = 2;
const N_D0_CELLS: u8 = 12;
const MOC_DIM: MocDim = MocDim::Space;
const HAS_COOSYS: bool = true;
const HAS_TIMESYS: bool = false;
const HAS_FREQSYS: bool = false;
#[inline(always)]
fn mult_by_dim<U: Idx>(v: U) -> U {
v << 1
}
#[inline(always)]
fn div_by_dim<U: Idx>(v: U) -> U {
v >> 1
}
}
impl<T> MocQty<T> for Hpx<T> where T: Idx {}
impl<T: Idx> Hpx<T> {
#[inline(always)]
pub fn from_uniq_hpx(uniq: T) -> (u8, T) {
let depth = (Self::get_msb(uniq) - 2) >> 1;
let idx = uniq - Self::four_shl_twice_depth(depth);
(depth as u8, idx)
}
#[inline(always)]
pub fn uniq_hpx(depth: u8, idx: T) -> T {
idx + Self::four_shl_twice_depth(depth as u32)
}
#[inline(always)]
pub fn four_shl_twice_depth(depth: u32) -> T {
T::one().unsigned_shl(2).unsigned_shl(depth << 1)
}
#[inline(always)]
pub fn uniq_hpx_to_range(uniq: T) -> Range<T> {
let (depth, pix) = Self::from_uniq_hpx(uniq);
let tdd = ((Self::MAX_DEPTH - depth) << 1) as u32;
Range {
start: pix.unsigned_shl(tdd),
end: (pix + One::one()).unsigned_shl(tdd),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Time<T: Idx>(PhantomData<T>);
impl<T: Idx> MocableQty for Time<T> {
const NAME: &'static str = "TIME";
const PREFIX: char = 't';
const DIM: u8 = 1;
const N_D0_CELLS: u8 = 2;
const MOC_DIM: MocDim = MocDim::Time;
const HAS_COOSYS: bool = false;
const HAS_TIMESYS: bool = true;
const HAS_FREQSYS: bool = false;
#[inline(always)]
fn mult_by_dim<U: Idx>(v: U) -> U {
v
}
#[inline(always)]
fn div_by_dim<U: Idx>(v: U) -> U {
v
}
}
impl<T> MocQty<T> for Time<T> where T: Idx {}
pub const F64_SIGN_BIT_MASK: u64 = 0x8000000000000000;
pub const F64_BUT_SIGN_BIT_MASK: u64 = 0x7FFFFFFFFFFFFFFF;
pub const F64_EXPONENT_BIT_MASK: u64 = 0x7FF << 52;
pub const F64_BUT_EXPONENT_BIT_MASK: u64 = !F64_EXPONENT_BIT_MASK;
pub const F64_MANTISSA_BIT_MASK: u64 = !(0xFFF << 52);
pub const F64_BUT_MANTISSA_BIT_MASK: u64 = 0xFFF << 52;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Frequency<T: Idx>(PhantomData<T>);
impl<T: Idx> MocableQty for Frequency<T> {
const N_RESERVED_BITS: u8 = 4; const NAME: &'static str = "FREQUENCY";
const PREFIX: char = 'f';
const DIM: u8 = 1;
const N_D0_CELLS: u8 = 2;
const MOC_DIM: MocDim = MocDim::Frequency;
const HAS_COOSYS: bool = false;
const HAS_TIMESYS: bool = false;
const HAS_FREQSYS: bool = true;
#[inline(always)]
fn mult_by_dim<U: Idx>(v: U) -> U {
v
}
#[inline(always)]
fn div_by_dim<U: Idx>(v: U) -> U {
v
}
}
impl<T> MocQty<T> for Frequency<T> where T: Idx {}
impl<T: Idx> Frequency<T> {
pub fn freq2hash(freq: f64) -> T {
const FREQ_MIN: f64 = 5.048_709_793_414_476e-29; const FREQ_MAX: f64 = 5.846_006_549_323_611e48; assert!(
FREQ_MIN <= freq,
"Wrong frequency in Hz. Expected: >= {}. Actual: {}",
FREQ_MIN,
freq
);
assert!(
freq <= FREQ_MAX,
"Wrong frequency in Hz. Expected: < {}. Actual: {}",
FREQ_MAX,
freq
);
let freq_bits = freq.to_bits();
assert_eq!(freq_bits & F64_SIGN_BIT_MASK, 0); let exponent = (freq_bits & F64_EXPONENT_BIT_MASK) >> 52;
assert!((929..=1184).contains(&exponent), "Exponent: {}", exponent); let exponent = (exponent - 929) << 52;
let freq_hash_dmax = (freq_bits & F64_BUT_EXPONENT_BIT_MASK) | exponent;
T::from_u64_idx(freq_hash_dmax)
}
pub fn hash2freq(hash: T) -> f64 {
let freq_hash = hash.to_u64_idx();
let exponent = (freq_hash & F64_EXPONENT_BIT_MASK) >> 52;
assert!(
exponent <= 256,
"Exponent: {}. Hash: {}. Hash bits: {:064b}",
exponent,
freq_hash,
freq_hash
);
let exponent = (exponent + 929) << 52;
let freq_bits = (freq_hash & F64_BUT_EXPONENT_BIT_MASK) | exponent;
f64::from_bits(freq_bits)
}
}
#[cfg(test)]
mod tests {
use crate::qty::{Frequency, Hpx, MocQty, MocableQty, Time};
#[test]
fn test_hpx_uniq_ext() {
println!("{:?}", Hpx::<u32>::from_uniq_hpx(96));
}
#[test]
fn test_hpx_uniq() {
for depth in 0..8 {
for idx in 0..Hpx::<u32>::n_cells(depth) {
assert_eq!(
(depth, idx),
Hpx::<u32>::from_uniq_hpx(Hpx::<u32>::uniq_hpx(depth, idx))
);
}
}
for depth in 0..8 {
for idx in 0..Hpx::<u64>::n_cells(depth) {
assert_eq!(
(depth, idx),
Hpx::<u64>::from_uniq_hpx(Hpx::<u64>::uniq_hpx(depth, idx))
);
}
}
assert_eq!(Hpx::<u64>::DIM, 2);
assert_eq!(Hpx::<u64>::N_D0_CELLS, 12);
assert_eq!(Hpx::<u64>::N_D0_BITS, 4);
assert_eq!(Hpx::<u64>::LEVEL_MASK, 3);
assert_eq!(Hpx::<u64>::shift(1), 2);
assert_eq!(Hpx::<u64>::shift(10), 20);
assert_eq!(Hpx::<u64>::MAX_DEPTH, 29);
assert_eq!(Hpx::<u64>::MAX_SHIFT, 58);
assert_eq!(Hpx::<u64>::n_cells_max(), 12 * 4_u64.pow(29));
}
#[test]
fn test_hpx_zuniq() {
for depth in 0..8 {
for idx in 0..Hpx::<u64>::n_cells(depth) {
assert_eq!(
(depth, idx),
Hpx::<u64>::from_zuniq(Hpx::<u64>::to_zuniq(depth, idx))
);
}
}
}
#[test]
fn test_hpx() {
assert_eq!(Hpx::<u64>::DIM, 2);
assert_eq!(Hpx::<u64>::N_D0_CELLS, 12);
assert_eq!(Hpx::<u64>::N_D0_BITS, 4);
assert_eq!(Hpx::<u64>::LEVEL_MASK, 3);
assert_eq!(Hpx::<u64>::shift(1), 2);
assert_eq!(Hpx::<u64>::shift(10), 20);
assert_eq!(Hpx::<u64>::MAX_DEPTH, 29);
assert_eq!(Hpx::<u64>::MAX_SHIFT, 58);
assert_eq!(Hpx::<u64>::n_cells_max(), 12 * 4_u64.pow(29));
assert_eq!(Hpx::<u32>::MAX_DEPTH, 13);
}
#[test]
fn test_time() {
assert_eq!(Time::<u64>::DIM, 1);
assert_eq!(Time::<u64>::N_D0_CELLS, 2);
assert_eq!(Time::<u64>::N_D0_BITS, 1);
assert_eq!(Time::<u64>::LEVEL_MASK, 1);
assert_eq!(Time::<u64>::shift(1), 1);
assert_eq!(Time::<u64>::shift(10), 10);
assert_eq!(Time::<u64>::MAX_DEPTH, 61);
assert_eq!(Time::<u64>::MAX_SHIFT, 61);
assert_eq!(Time::<u64>::n_cells_max(), 2_u64.pow(62));
}
#[test]
fn test_freq() {
assert_eq!(Frequency::<u64>::DIM, 1);
assert_eq!(Frequency::<u64>::N_D0_CELLS, 2);
assert_eq!(Frequency::<u64>::N_D0_BITS, 1);
assert_eq!(Frequency::<u64>::LEVEL_MASK, 1);
assert_eq!(Frequency::<u64>::shift(1), 1);
assert_eq!(Frequency::<u64>::shift(10), 10);
assert_eq!(Frequency::<u64>::MAX_DEPTH, 59);
assert_eq!(Frequency::<u64>::MAX_SHIFT, 59);
assert_eq!(Frequency::<u64>::n_cells_max(), 2_u64.pow(60));
let freq_hz = 0.1;
assert_eq!(
Frequency::<u64>::hash2freq(Frequency::<u64>::freq2hash(freq_hz)),
freq_hz
);
let freq_hz = 1.125697115656943e-18;
assert_eq!(
Frequency::<u64>::hash2freq(Frequency::<u64>::freq2hash(freq_hz)),
freq_hz
);
let freq_hz = 1.12569711565245e+44;
assert_eq!(
Frequency::<u64>::hash2freq(Frequency::<u64>::freq2hash(freq_hz)),
freq_hz
);
let freq_hz = 5.048709793414476e-29;
assert_eq!(
Frequency::<u64>::hash2freq(Frequency::<u64>::freq2hash(freq_hz)),
freq_hz
);
let freq_hz = 5.846006549323610e+48;
assert_eq!(
Frequency::<u64>::hash2freq(Frequency::<u64>::freq2hash(freq_hz)),
freq_hz
);
}
}