use crate::cbig::CBig;
use crate::cmp::lex_cmp;
use _num_modular::{FixedMersenneInt, ModularInteger};
use core::cmp::Ordering;
use core::hash::Hasher;
use dashu_float::round::Round;
use dashu_int::Word;
use num_order::{NumHash, NumOrd};
#[inline]
fn bterm(b: i128) -> i128 {
type MInt = FixedMersenneInt<127, 1>;
const M127U: u128 = i128::MAX as u128;
const PROOT: u128 = i32::MAX as u128;
if b >= 0 {
let pb = MInt::new(b as u128, &M127U) * PROOT;
-((pb * pb).residue() as i128)
} else {
let pb = MInt::new((-b) as u128, &M127U) * PROOT;
(pb * pb).residue() as i128
}
}
impl<R1: Round, R2: Round, const B: Word> NumOrd<CBig<R2, B>> for CBig<R1, B> {
#[inline]
fn num_cmp(&self, other: &CBig<R2, B>) -> Ordering {
lex_cmp(&self.re, &self.im, &other.re, &other.im)
}
#[inline]
fn num_partial_cmp(&self, other: &CBig<R2, B>) -> Option<Ordering> {
Some(self.num_cmp(other))
}
}
impl<R: Round, const B: Word> NumHash for CBig<R, B> {
fn num_hash<H: Hasher>(&self, state: &mut H) {
let a = self.re().num_hash_residue();
let b = self.im().num_hash_residue();
a.wrapping_add(bterm(b)).num_hash(state)
}
}
#[cfg(test)]
mod tests {
use super::*;
use dashu_float::round::mode;
type C = CBig<mode::HalfAway, 10>;
#[test]
fn num_ord_agrees_with_ord() {
let a = C::from_parts(1.into(), 9.into());
let b = C::from_parts(2.into(), 0.into());
assert_eq!(a.num_cmp(&b), a.cmp(&b));
assert!(a.num_lt(&b));
}
#[test]
fn num_hash_consistent_with_eq() {
fn hash_of<T: NumHash>(v: &T) -> u64 {
use std::hash::DefaultHasher;
let mut h = DefaultHasher::new();
v.num_hash(&mut h);
std::hash::Hasher::finish(&h)
}
let a = C::from_parts(3.into(), 4.into());
let b = C::from_parts(3.into(), 4.into());
assert_eq!(hash_of(&a), hash_of(&b));
}
fn residue<T: NumHash>(v: &T) -> i128 {
struct Collector(i128);
impl core::hash::Hasher for Collector {
fn write_i128(&mut self, v: i128) {
self.0 = v;
}
fn write(&mut self, _: &[u8]) {}
fn finish(&self) -> u64 {
0
}
}
let mut c = Collector(0);
v.num_hash(&mut c);
c.0
}
#[test]
fn cbig_num_hash_matches_num_complex() {
use dashu_float::FBig;
use num_complex_v04::Complex64;
type CF = CBig<mode::Zero, 2>;
for (re, im) in [
(3.0_f64, 4.0),
(1.0, 0.0),
(0.0, 1.0),
(-2.0, 0.5),
(0.0, 0.0),
(1.5, -2.25),
(100.0, -0.0625),
] {
let z = CF::from_parts(FBig::try_from(re).unwrap(), FBig::try_from(im).unwrap());
let expected = residue(&Complex64::new(re, im));
assert_eq!(
residue(&z),
expected,
"CBig num_hash disagrees with num-complex hash for {re}+{im}i"
);
}
}
}