use core::fmt::{Debug, Display, Formatter};
use core::ops::{Add, Div, Mul, Neg, Sub};
use itertools::Itertools;
type Word = u64;
const WORDLEVELS: usize = 6;
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct FiniteNimber(FiniteNimberEnum);
#[derive(Clone, PartialEq, Eq, Hash)]
enum FiniteNimberEnum {
Single(Word),
Vector(Vec<Word>),
}
#[derive(Clone, Copy)]
enum FiniteNimberRef<'a> {
Single(Word),
Slice(&'a [Word]),
}
impl Debug for FiniteNimber {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), core::fmt::Error> {
match &self.0 {
FiniteNimberEnum::Single(v) => {
write!(f, "FiniteNimber(0x{:016x})", v)
}
FiniteNimberEnum::Vector(s) => {
write!(f, "FiniteNimber[")?;
let mut sep: &str = "";
for v in s {
write!(f, "{}0x{:016x}", sep, v)?;
sep = ", ";
}
write!(f, "]")
}
}
}
}
impl Debug for FiniteNimberRef<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), core::fmt::Error> {
match self {
FiniteNimberRef::Single(v) => {
write!(f, "FiniteNimberRef(0x{:16x})", v)
}
FiniteNimberRef::Slice(s) => {
write!(f, "FiniteNimberRef[")?;
let mut sep: &str = "";
for v in *s {
write!(f, "{}0x{:016x}", sep, v)?;
sep = ", ";
}
write!(f, "]")
}
}
}
}
impl Display for FiniteNimber {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), core::fmt::Error> {
let r = self.to_ref();
if !f.alternate() && r.level() < WORDLEVELS && r.low_word() < 10 {
write!(f, "*{}", r.low_word())
} else {
write!(f, "*0x")?;
let mut started = false;
for v in r.as_slice().iter().rev() {
if started {
write!(f, "{:016x}", v)?;
} else if *v != 0 {
write!(f, "{:x}", v)?;
started = true;
}
}
if !started {
write!(f, "0")?;
}
Ok(())
}
}
}
impl Default for FiniteNimber {
fn default() -> Self {
Self(FiniteNimberEnum::Single(0))
}
}
impl<'a> FiniteNimberRef<'a> {
fn zero() -> Self {
FiniteNimberRef::Single(0)
}
fn low_word(self) -> Word {
match self {
FiniteNimberRef::Single(v) => v,
FiniteNimberRef::Slice(s) => {
*(s.first().expect("FiniteNimberRef::Slice is never empty"))
}
}
}
fn as_slice<'b, 'c>(&'b self) -> &'c [Word]
where
'a: 'c,
'b: 'c,
{
match self {
FiniteNimberRef::Single(v) => core::slice::from_ref(v),
FiniteNimberRef::Slice(s) => s,
}
}
fn level(self) -> usize {
fn word_level(w: Word) -> usize {
let log_w: u32 =
(Word::BITS - 1).saturating_sub(w.leading_zeros());
u32::BITS.saturating_sub(log_w.leading_zeros()) as usize
}
fn usize_level(w: usize) -> usize {
let log_w: u32 = (usize::BITS - 1) - w.leading_zeros();
log_w as usize
}
match self {
FiniteNimberRef::Single(v) => word_level(v),
FiniteNimberRef::Slice(s) => {
let w = s
.len()
.checked_sub(1)
.expect("slice representation must always be non-empty");
match w {
0 => word_level(s[0]),
w => usize_level(w) + (WORDLEVELS + 1),
}
}
}
}
fn split(
self,
level: usize,
) -> (FiniteNimberRef<'a>, FiniteNimberRef<'a>) {
match level.checked_sub(WORDLEVELS) {
None => {
let bits = 1 << (level as Word);
let mask = (1 << bits) - 1;
let v = self.low_word();
return (
FiniteNimberRef::Single(v & mask),
FiniteNimberRef::Single((v >> bits) & mask),
);
}
Some(wordlevel) => match self {
FiniteNimberRef::Single(v) => {
(FiniteNimberRef::Single(v), Self::zero())
}
FiniteNimberRef::Slice(s) => {
match wordlevel
.try_into()
.ok()
.and_then(|w| 1usize.checked_shl(w))
{
Some(words) => {
let mut iter = s.chunks(words);
let lo = iter.next().expect(
"FiniteNimberRef::Slice is never empty",
);
match iter.next() {
Some(hi) => (
FiniteNimberRef::Slice(lo),
FiniteNimberRef::Slice(hi),
),
None => {
(FiniteNimberRef::Slice(lo), Self::zero())
}
}
}
None => (self.clone(), Self::zero()),
}
}
},
}
}
fn join(self, hi: FiniteNimberRef<'_>, level: usize) -> FiniteNimber {
match level.checked_sub(WORDLEVELS) {
None => {
let bits = 1 << (level as Word);
let mask = (1 << bits) - 1;
let vlo = self.low_word();
let vhi = hi.low_word();
((vlo & mask) | ((vhi & mask) << bits)).into()
}
Some(wordlevel) => {
match wordlevel
.try_into()
.ok()
.and_then(|w| 1usize.checked_shl(w))
{
Some(words) => {
let slo = self.as_slice();
let shi = hi.as_slice();
let padding = words.saturating_sub(slo.len());
let vec: Vec<_> = slo
.iter()
.cloned()
.take(words)
.chain(core::iter::repeat(0).take(padding))
.chain(shi.iter().cloned().take(words))
.collect();
FiniteNimber::from(vec)
}
None => FiniteNimber::from(self.as_slice()),
}
}
}
}
}
impl FiniteNimber {
fn to_ref(&self) -> FiniteNimberRef {
match &self.0 {
FiniteNimberEnum::Single(w) => FiniteNimberRef::Single(*w),
FiniteNimberEnum::Vector(v) => match v.len() {
0 => FiniteNimberRef::zero(),
_ => FiniteNimberRef::Slice(&v),
},
}
}
fn test_bit(&self, bit: usize) -> bool {
let word = bit >> WORDLEVELS;
let shift = (bit as u32) & (Word::BITS - 1);
((self.as_slice().get(word).cloned().unwrap_or(0) >> shift) & 1) != 0
}
fn sort_pair(a: Self, b: Self) -> (Self, Self) {
let misordered = a
.as_slice()
.iter()
.cloned()
.zip_longest(b.as_slice().iter().cloned())
.rev()
.map(|pair| {
let (left, right) = pair.or(0, 0);
if left != right {
Some(left > right)
} else {
None
}
})
.find_map(|v| v)
.unwrap_or(false);
if misordered {
(b, a)
} else {
(a, b)
}
}
}
impl<'a> From<&'a FiniteNimber> for FiniteNimberRef<'a> {
fn from(n: &'a FiniteNimber) -> Self {
n.to_ref()
}
}
impl From<FiniteNimberRef<'_>> for FiniteNimber {
fn from(n: FiniteNimberRef) -> Self {
match n {
FiniteNimberRef::Single(w) => Self(FiniteNimberEnum::Single(w)),
FiniteNimberRef::Slice(v) => FiniteNimber::from(v),
}
}
}
impl From<Word> for FiniteNimber {
fn from(val: Word) -> FiniteNimber {
Self(FiniteNimberEnum::Single(val))
}
}
impl From<Vec<Word>> for FiniteNimber {
fn from(mut vec: Vec<Word>) -> FiniteNimber {
match vec
.iter()
.enumerate()
.rev()
.find(|(_index, word)| **word != 0)
{
None => Self(FiniteNimberEnum::Single(0)),
Some((0, w)) => Self(FiniteNimberEnum::Single(*w)),
Some((pos, _)) => {
vec.truncate(pos + 1);
Self(FiniteNimberEnum::Vector(vec))
}
}
}
}
impl From<&[Word]> for FiniteNimber {
fn from(slice: &[Word]) -> FiniteNimber {
match slice
.iter()
.enumerate()
.rev()
.find(|(_index, word)| **word != 0)
{
None => Self(FiniteNimberEnum::Single(0)),
Some((0, w)) => Self(FiniteNimberEnum::Single(*w)),
Some((pos, _)) => {
Self(FiniteNimberEnum::Vector(slice[..=pos].into()))
}
}
}
}
impl<const N: usize> From<&[Word; N]> for FiniteNimber {
fn from(array: &[Word; N]) -> FiniteNimber {
FiniteNimber::from(array as &[Word])
}
}
macro_rules! impl_binop_wrappers {
($trait:tt, $fn:ident, $assigntrait:tt, $assignfn:ident, $comment:literal) => {
impl $trait<FiniteNimberRef<'_>> for FiniteNimber {
type Output = FiniteNimber;
fn $fn(self, other: FiniteNimberRef<'_>) -> FiniteNimber {
let aref: FiniteNimberRef = (&self).into();
let bref: FiniteNimberRef = other;
$trait::$fn(aref, bref)
}
}
impl $trait<FiniteNimber> for FiniteNimberRef<'_> {
type Output = FiniteNimber;
fn $fn(self, other: FiniteNimber) -> FiniteNimber {
let aref: FiniteNimberRef = self;
let bref: FiniteNimberRef = (&other).into();
$trait::$fn(aref, bref)
}
}
impl $trait<&FiniteNimber> for &FiniteNimber {
type Output = FiniteNimber;
#[doc=$comment]
fn $fn(self, other: &FiniteNimber) -> FiniteNimber {
let aref: FiniteNimberRef = self.into();
let bref: FiniteNimberRef = other.into();
$trait::$fn(aref, bref)
}
}
impl $trait<&FiniteNimber> for FiniteNimber {
type Output = FiniteNimber;
#[doc=$comment]
fn $fn(self, other: &FiniteNimber) -> FiniteNimber {
let aref: FiniteNimberRef = (&self).into();
let bref: FiniteNimberRef = other.into();
$trait::$fn(aref, bref)
}
}
impl $trait<FiniteNimber> for &FiniteNimber {
type Output = FiniteNimber;
#[doc=$comment]
fn $fn(self, other: FiniteNimber) -> FiniteNimber {
let aref: FiniteNimberRef = self.into();
let bref: FiniteNimberRef = (&other).into();
$trait::$fn(aref, bref)
}
}
impl $trait<FiniteNimber> for FiniteNimber {
type Output = FiniteNimber;
#[doc=$comment]
fn $fn(self, other: FiniteNimber) -> FiniteNimber {
let aref: FiniteNimberRef = (&self).into();
let bref: FiniteNimberRef = (&other).into();
$trait::$fn(aref, bref)
}
}
impl core::ops::$assigntrait<&FiniteNimber> for FiniteNimber {
#[doc=$comment]
fn $assignfn(&mut self, other: &FiniteNimber) {
let aref: FiniteNimberRef = (&self as &FiniteNimber).into();
let bref: FiniteNimberRef = other.into();
*self = $trait::$fn(aref, bref);
}
}
impl core::ops::$assigntrait<FiniteNimber> for FiniteNimber {
#[doc=$comment]
fn $assignfn(&mut self, other: FiniteNimber) {
let aref: FiniteNimberRef = (&self as &FiniteNimber).into();
let bref: FiniteNimberRef = (&other).into();
*self = $trait::$fn(aref, bref);
}
}
};
}
impl<'a, 'b> Add<FiniteNimberRef<'a>> for FiniteNimberRef<'b> {
type Output = FiniteNimber;
fn add(self, other: FiniteNimberRef<'a>) -> FiniteNimber {
let vec: Vec<_> = self
.as_slice()
.iter()
.cloned()
.zip_longest(other.as_slice().iter().cloned())
.map(|pair| pair.reduce(|a, b| a ^ b))
.collect();
FiniteNimber::from(vec)
}
}
impl<'a, 'b> Sub<FiniteNimberRef<'a>> for FiniteNimberRef<'b> {
type Output = FiniteNimber;
fn sub(self, other: FiniteNimberRef<'a>) -> FiniteNimber {
self + other
}
}
impl<'a> FiniteNimberRef<'a> {
fn mul_by_h(self, level: usize) -> FiniteNimber {
match level.checked_sub(1) {
Some(sublevel) => {
let (lo, hi) = self.split(sublevel);
hi.mul_by_h(sublevel)
.to_ref()
.mul_by_h(sublevel)
.to_ref()
.join(
(lo + hi).to_ref().mul_by_h(sublevel).to_ref(),
sublevel,
)
}
None => self.into(),
}
}
fn square_recurse(self, level: usize) -> FiniteNimber {
match level.checked_sub(1) {
Some(sublevel) => {
let (lo, hi) = self.split(sublevel);
let lo2 = lo.square_recurse(sublevel);
let hi2 = hi.square_recurse(sublevel);
(lo2 + hi2.to_ref().mul_by_h(sublevel))
.to_ref()
.join(hi2.to_ref(), sublevel)
}
None => self.into(),
}
}
}
impl<'a, 'b> Mul<FiniteNimberRef<'a>> for FiniteNimberRef<'b> {
type Output = FiniteNimber;
fn mul(self, other: FiniteNimberRef<'a>) -> FiniteNimber {
let slevel = self.level();
let olevel = other.level();
let (a, b, alevel, blevel) = if slevel > olevel {
(other, self, olevel, slevel)
} else {
(self, other, slevel, olevel)
};
if alevel < blevel {
let sublevel = blevel - 1;
let (blo, bhi) = b.split(sublevel);
(blo * a).to_ref().join((bhi * a).to_ref(), sublevel)
} else {
match alevel.checked_sub(1) {
Some(sublevel) => {
let (alo, ahi) = a.split(sublevel);
let (blo, bhi) = b.split(sublevel);
let karatsuba = (alo + ahi) * (blo + bhi);
let albl = alo * blo;
let ahbh = ahi * bhi;
(&albl + ahbh.to_ref().mul_by_h(sublevel))
.to_ref()
.join((karatsuba + &albl).to_ref(), sublevel)
}
None => FiniteNimber::from(a.low_word() & b.low_word()),
}
}
}
}
impl<'a> FiniteNimberRef<'a> {
fn inverse_recurse(self, level: usize) -> Option<FiniteNimber> {
match level.checked_sub(1) {
Some(sublevel) => {
let (lo, hi) = self.split(sublevel);
let sum = lo + hi;
let sq = hi.square_recurse(sublevel);
let det = lo * sum.to_ref() + sq.to_ref().mul_by_h(sublevel);
let detinv = det.to_ref().inverse_recurse(sublevel)?;
Some(
(detinv.to_ref() * sum)
.to_ref()
.join((detinv.to_ref() * hi).to_ref(), sublevel),
)
}
None => match self.low_word() {
0 => None,
v => Some(FiniteNimber::from(v)),
},
}
}
fn inverse(&self) -> Option<FiniteNimber> {
self.inverse_recurse(self.level())
}
}
impl<'a, 'b> Div<FiniteNimberRef<'a>> for FiniteNimberRef<'b> {
type Output = FiniteNimber;
fn div(self, other: FiniteNimberRef<'a>) -> FiniteNimber {
let inverse = other.inverse().expect("Division by zero");
self * inverse.to_ref()
}
}
impl<'a> FiniteNimberRef<'a> {
fn sqrt_recurse(self, level: usize) -> FiniteNimber {
match level.checked_sub(1) {
Some(sublevel) => {
let (lo, hi) = self.split(sublevel);
let hi_root = hi.sqrt_recurse(sublevel);
let sum = hi.mul_by_h(sublevel) + lo;
let sum_root = sum.to_ref().sqrt_recurse(sublevel);
sum_root.to_ref().join(hi_root.to_ref(), sublevel)
}
None => self.into(),
}
}
}
impl<'a> FiniteNimberRef<'a> {
fn quadratic1_recurse(self, level: usize) -> FiniteNimber {
match level.checked_sub(1) {
Some(sublevel) => {
let (clo, chi) = self.split(sublevel);
let mut xhi = chi.quadratic1_recurse(sublevel);
let mut c2 =
(xhi.to_ref() + chi).to_ref().mul_by_h(sublevel) + clo;
let hbit = (1 << sublevel) - 1;
if c2.test_bit(hbit) {
let one = FiniteNimber::from(1);
c2 += one.to_ref().mul_by_h(sublevel); xhi += one;
}
let xlo = c2.to_ref().quadratic1_recurse(sublevel);
xlo.to_ref().join(xhi.to_ref(), sublevel)
}
None => FiniteNimber::from(0),
}
}
}
impl FiniteNimber {
pub fn as_slice(&self) -> &[Word] {
match &self.0 {
FiniteNimberEnum::Single(w) => core::slice::from_ref(w),
FiniteNimberEnum::Vector(v) => &v,
}
}
pub fn inverse(&self) -> Option<FiniteNimber> {
self.to_ref().inverse()
}
pub fn square(&self) -> FiniteNimber {
let r = self.to_ref();
r.square_recurse(r.level())
}
pub fn sqrt(&self) -> FiniteNimber {
let r = self.to_ref();
r.sqrt_recurse(r.level())
}
pub fn quadratic1(&self) -> (FiniteNimber, FiniteNimber) {
let r = self.to_ref();
let level = r.level();
let start_level = if self.test_bit((1 << level) - 1) {
level + 1
} else {
level
};
let x0 = r.quadratic1_recurse(start_level);
let x1 = &x0 + FiniteNimber::from(1);
Self::sort_pair(x0, x1)
}
pub fn quadratic2(b: &Self, c: &Self) -> (FiniteNimber, FiniteNimber) {
if b == &FiniteNimber::from(0) {
let x = c.sqrt();
(x.clone(), x)
} else {
let (x0, x1) = (c / b.square()).quadratic1();
Self::sort_pair(x0 * b, x1 * b)
}
}
pub fn quadratic3(
a: &Self,
b: &Self,
c: &Self,
) -> Option<(FiniteNimber, FiniteNimber)> {
let ainv = a.inverse()?;
Some(Self::quadratic2(&(b * &ainv), &(c * &ainv)))
}
}
impl_binop_wrappers!(
Add,
add,
AddAssign,
add_assign,
r##"
Addition of finite nimbers behaves like bitwise XOR on unsigned
integers.
"##
);
impl_binop_wrappers!(
Sub,
sub,
SubAssign,
sub_assign,
r##"
Subtraction of finite nimbers behaves just like addition, i.e. like
bitwise XOR on unsigned integers. This is due to the field of nimbers
having characteristic 2, i.e. it is such that `x + x = 0` for any `x`,
so that `-x` is the same as `x`.
"##
);
impl_binop_wrappers!(
Mul,
mul,
MulAssign,
mul_assign,
r##"
Multiplication of finite nimbers has the property that if the two
input nimbers both fitted in 2^n bits, then so does the output.
"##
);
impl_binop_wrappers!(
Div,
div,
DivAssign,
div_assign,
r##"
Division of finite nimbers has the property that if the two input
nimbers both fitted in 2^n bits, then so does the output.
Dividing by the zero nimber causes a panic.
"##
);
impl Neg for FiniteNimber {
type Output = Self;
fn neg(self) -> Self {
self
}
}
impl<'a> Neg for &'a FiniteNimber {
type Output = &'a FiniteNimber;
fn neg(self) -> Self {
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn split() {
let a = FiniteNimber::from(&[
0x8786858483828180,
0x9796959493929190,
0xa7a6a5a4a3a2a1a0,
0xb7b6b5b4b3b2b1b0,
0xc7c6c5c4c3c2c1c0,
0xd7d6d5d4d3d2d1d0,
]);
let (loref, hiref) = a.to_ref().split(4);
let lo: FiniteNimber = loref.into();
let hi: FiniteNimber = hiref.into();
assert_eq!(lo, FiniteNimber::from(&[0x8180]));
assert_eq!(hi, FiniteNimber::from(&[0x8382]));
let (loref, hiref) = a.to_ref().split(5);
let lo: FiniteNimber = loref.into();
let hi: FiniteNimber = hiref.into();
assert_eq!(lo, FiniteNimber::from(&[0x83828180]));
assert_eq!(hi, FiniteNimber::from(&[0x87868584]));
let (loref, hiref) = a.to_ref().split(6);
let lo: FiniteNimber = loref.into();
let hi: FiniteNimber = hiref.into();
assert_eq!(lo, FiniteNimber::from(&[0x8786858483828180]));
assert_eq!(hi, FiniteNimber::from(&[0x9796959493929190]));
let (loref, hiref) = a.to_ref().split(7);
let lo: FiniteNimber = loref.into();
let hi: FiniteNimber = hiref.into();
assert_eq!(
lo,
FiniteNimber::from(&[0x8786858483828180, 0x9796959493929190])
);
assert_eq!(
hi,
FiniteNimber::from(&[0xa7a6a5a4a3a2a1a0, 0xb7b6b5b4b3b2b1b0,])
);
let (loref, hiref) = a.to_ref().split(8);
let lo: FiniteNimber = loref.into();
let hi: FiniteNimber = hiref.into();
assert_eq!(
lo,
FiniteNimber::from(&[
0x8786858483828180,
0x9796959493929190,
0xa7a6a5a4a3a2a1a0,
0xb7b6b5b4b3b2b1b0
])
);
assert_eq!(
hi,
FiniteNimber::from(&[0xc7c6c5c4c3c2c1c0, 0xd7d6d5d4d3d2d1d0])
);
let (loref, hiref) = a.to_ref().split(9);
let lo: FiniteNimber = loref.into();
let hi: FiniteNimber = hiref.into();
assert_eq!(
lo,
FiniteNimber::from(&[
0x8786858483828180,
0x9796959493929190,
0xa7a6a5a4a3a2a1a0,
0xb7b6b5b4b3b2b1b0,
0xc7c6c5c4c3c2c1c0,
0xd7d6d5d4d3d2d1d0
])
);
assert_eq!(hi, FiniteNimber::from(&[0]));
let a = FiniteNimber::from(&[0xFF]);
let (loref, hiref) = a.to_ref().split(0);
let lo: FiniteNimber = loref.into();
let hi: FiniteNimber = hiref.into();
assert_eq!(lo, FiniteNimber::from(&[1]));
assert_eq!(hi, FiniteNimber::from(&[1]));
let (loref, hiref) = a.to_ref().split(1);
let lo: FiniteNimber = loref.into();
let hi: FiniteNimber = hiref.into();
assert_eq!(lo, FiniteNimber::from(&[3]));
assert_eq!(hi, FiniteNimber::from(&[3]));
let (loref, hiref) = a.to_ref().split(2);
let lo: FiniteNimber = loref.into();
let hi: FiniteNimber = hiref.into();
assert_eq!(lo, FiniteNimber::from(&[0xf]));
assert_eq!(hi, FiniteNimber::from(&[0xf]));
}
#[test]
fn join() {
let a = FiniteNimber::from(0x5);
let b = FiniteNimber::from(0xa);
assert_eq!(a.to_ref().join(b.to_ref(), 0), FiniteNimber::from(0b01));
assert_eq!(b.to_ref().join(a.to_ref(), 0), FiniteNimber::from(0b10));
assert_eq!(a.to_ref().join(b.to_ref(), 1), FiniteNimber::from(0b1001));
assert_eq!(b.to_ref().join(a.to_ref(), 1), FiniteNimber::from(0b0110));
assert_eq!(a.to_ref().join(b.to_ref(), 2), FiniteNimber::from(0xa5));
assert_eq!(b.to_ref().join(a.to_ref(), 2), FiniteNimber::from(0x5a));
assert_eq!(
a.to_ref().join(b.to_ref(), 5),
FiniteNimber::from(0xa00000005)
);
assert_eq!(
a.to_ref().join(b.to_ref(), 6),
FiniteNimber::from(&[0x5, 0xa])
);
assert_eq!(
a.to_ref().join(b.to_ref(), 8),
FiniteNimber::from(&[0x5, 0, 0, 0, 0xa])
);
}
#[test]
fn levels() {
assert_eq!(FiniteNimber::from(&[]).to_ref().level(), 0);
assert_eq!(FiniteNimber::from(&[0]).to_ref().level(), 0);
assert_eq!(FiniteNimber::from(&[1]).to_ref().level(), 0);
assert_eq!(FiniteNimber::from(&[2]).to_ref().level(), 1);
assert_eq!(FiniteNimber::from(&[3]).to_ref().level(), 1);
assert_eq!(FiniteNimber::from(&[4]).to_ref().level(), 2);
assert_eq!(FiniteNimber::from(&[0xf]).to_ref().level(), 2);
assert_eq!(FiniteNimber::from(&[0x10]).to_ref().level(), 3);
assert_eq!(FiniteNimber::from(&[0xff]).to_ref().level(), 3);
assert_eq!(FiniteNimber::from(&[0x0100]).to_ref().level(), 4);
assert_eq!(FiniteNimber::from(&[0xffff]).to_ref().level(), 4);
assert_eq!(FiniteNimber::from(&[0x00010000]).to_ref().level(), 5);
assert_eq!(FiniteNimber::from(&[0xffffffff]).to_ref().level(), 5);
assert_eq!(
FiniteNimber::from(&[0x0000000100000000]).to_ref().level(),
6
);
assert_eq!(
FiniteNimber::from(&[0xffffffffffffffff]).to_ref().level(),
6
);
assert_eq!(FiniteNimber::from(&[0x55, 0, 0, 0]).to_ref().level(), 3);
assert_eq!(FiniteNimber::from(&[0x55, 1, 0, 0]).to_ref().level(), 7);
assert_eq!(FiniteNimber::from(&[0x55, 1, 1, 0]).to_ref().level(), 8);
assert_eq!(FiniteNimber::from(&[0x55, 1, 1, 1]).to_ref().level(), 8);
assert_eq!(
FiniteNimber::from(&[0x55, 1, 1, 1, 1]).to_ref().level(),
9
);
assert_eq!(
FiniteNimber::from(&[1, 1, 1, 1, 1, 1, 1, 1])
.to_ref()
.level(),
9
);
assert_eq!(
FiniteNimber::from(&[1, 1, 1, 1, 1, 1, 1, 1, 1])
.to_ref()
.level(),
10
);
}
#[test]
fn addition() {
let a = FiniteNimber::from(0b1100);
let b = FiniteNimber::from(0b1010);
assert_eq!(a + b, FiniteNimber::from(0b0110));
let a = FiniteNimber::from(&[1, 1, 0, 0]);
let b = FiniteNimber::from(&[1, 0, 1, 0]);
assert_eq!(a + b, FiniteNimber::from(&[0, 1, 1 ]));
let a = FiniteNimber::from(&[1, 1, 1, 1]);
let b = FiniteNimber::from(2);
assert_eq!(a + b, FiniteNimber::from(&[3, 1, 1, 1]));
}
#[test]
fn mul_by_h() {
let a = FiniteNimber::from(1);
assert_eq!(a.to_ref().mul_by_h(0), FiniteNimber::from(1));
assert_eq!(a.to_ref().mul_by_h(1), FiniteNimber::from(2));
assert_eq!(a.to_ref().mul_by_h(2), FiniteNimber::from(8));
assert_eq!(a.to_ref().mul_by_h(3), FiniteNimber::from(128));
assert_eq!(
a.to_ref().mul_by_h(8),
FiniteNimber::from(&[0, 0, 0, 0x8000000000000000])
);
assert_eq!(
a.to_ref().mul_by_h(3).to_ref().mul_by_h(3),
FiniteNimber::from(0xde)
);
assert_eq!(
a.to_ref().mul_by_h(8).to_ref().mul_by_h(8),
FiniteNimber::from(&[
0xa92181714b010a1a,
0x4a88a921e2208b6b,
0xe3a92850a9218171,
0xde4ae3a94a88a921
])
);
}
#[test]
fn mul() {
assert_eq!(
FiniteNimber::from(0xd) * FiniteNimber::from(0xb),
FiniteNimber::from(0x5)
);
assert_eq!(
FiniteNimber::from(0x123456789abcdef0) * FiniteNimber::from(0x76),
FiniteNimber::from(0x84946b8cf1e11ef9)
);
assert_eq!(
FiniteNimber::from(&[
0x3f84d5b5b5470917,
0xc0ac29b7c97c50dd,
0xbe5466cf34e90c6c,
0x452821e638d01377,
0x082efa98ec4e6c89,
0xa4093822299f31d0,
0x13198a2e03707344,
0x243f6a8885a308d3,
]) * FiniteNimber::from(&[
0x4f7c7b5757f59584,
0xda06c80abb1185eb,
0xf4bf8d8d8c31d763,
0x324e7738926cfbe5,
0xa784d9045190cfef,
0x62e7160f38b4da56,
0xbf7158809cf4f3c7,
0xb7e151628aed2a6a,
]),
FiniteNimber::from(&[
0x72890f944be121d8,
0xe6429b02014feb2e,
0x2454070e4408eff8,
0x298c025ec5ac4190,
0xba6895a109cfcf6d,
0xcfb08c013e9dd19c,
0x5eeb02eaf7ae9ea0,
0x59cbe194a9599171,
])
);
}
#[test]
fn square() {
assert_eq!(
FiniteNimber::from(0x80).square(),
FiniteNimber::from(0xde)
);
assert_eq!(
FiniteNimber::from(0x8000).square(),
FiniteNimber::from(0xde4a)
);
assert_eq!(
FiniteNimber::from(&[
0x3f84d5b5b5470917,
0xc0ac29b7c97c50dd,
0xbe5466cf34e90c6c,
0x452821e638d01377,
0x082efa98ec4e6c89,
0xa4093822299f31d0,
0x13198a2e03707344,
0x243f6a8885a308d3,
])
.square(),
FiniteNimber::from(&[
0xd0e74a0945d35342,
0xfa91473032b5438a,
0xe17bdc047300f99d,
0x1a51cba4adb8ddb9,
0xdc06e76f54f372c0,
0xe74d7b7c65542a19,
0xffe69bcb391c628b,
0x32ae6e49dcd65156,
])
);
}
#[test]
fn div() {
assert_eq!(
FiniteNimber::from(1) / FiniteNimber::from(2),
FiniteNimber::from(3)
);
assert_eq!(
FiniteNimber::from(&[
0x72890f944be121d8,
0xe6429b02014feb2e,
0x2454070e4408eff8,
0x298c025ec5ac4190,
0xba6895a109cfcf6d,
0xcfb08c013e9dd19c,
0x5eeb02eaf7ae9ea0,
0x59cbe194a9599171,
]) / FiniteNimber::from(&[
0x3f84d5b5b5470917,
0xc0ac29b7c97c50dd,
0xbe5466cf34e90c6c,
0x452821e638d01377,
0x082efa98ec4e6c89,
0xa4093822299f31d0,
0x13198a2e03707344,
0x243f6a8885a308d3,
]),
FiniteNimber::from(&[
0x4f7c7b5757f59584,
0xda06c80abb1185eb,
0xf4bf8d8d8c31d763,
0x324e7738926cfbe5,
0xa784d9045190cfef,
0x62e7160f38b4da56,
0xbf7158809cf4f3c7,
0xb7e151628aed2a6a,
])
);
}
#[test]
#[should_panic]
fn div0() {
let _ = FiniteNimber::from(0x1234) / FiniteNimber::from(0);
}
#[test]
fn sqrt() {
assert_eq!(FiniteNimber::from(0xde).sqrt(), FiniteNimber::from(0x80));
assert_eq!(
FiniteNimber::from(0xde4a).sqrt(),
FiniteNimber::from(0x8000)
);
assert_eq!(
FiniteNimber::from(&[
0xd0e74a0945d35342,
0xfa91473032b5438a,
0xe17bdc047300f99d,
0x1a51cba4adb8ddb9,
0xdc06e76f54f372c0,
0xe74d7b7c65542a19,
0xffe69bcb391c628b,
0x32ae6e49dcd65156,
])
.sqrt(),
FiniteNimber::from(&[
0x3f84d5b5b5470917,
0xc0ac29b7c97c50dd,
0xbe5466cf34e90c6c,
0x452821e638d01377,
0x082efa98ec4e6c89,
0xa4093822299f31d0,
0x13198a2e03707344,
0x243f6a8885a308d3,
])
);
}
#[test]
fn quadratic() {
assert_eq!(
FiniteNimber::from(0).quadratic1(),
(FiniteNimber::from(0), FiniteNimber::from(1))
);
assert_eq!(
FiniteNimber::from(1).quadratic1(),
(FiniteNimber::from(2), FiniteNimber::from(3))
);
assert_eq!(
FiniteNimber::from(0x8).quadratic1(),
(FiniteNimber::from(0x10), FiniteNimber::from(0x11))
);
assert_eq!(
FiniteNimber::from(0x80).quadratic1(),
(FiniteNimber::from(0x100), FiniteNimber::from(0x101))
);
assert_eq!(
FiniteNimber::from(0x80000000).quadratic1(),
(
FiniteNimber::from(0x100000000),
FiniteNimber::from(0x100000001)
)
);
assert_eq!(
FiniteNimber::from(0x23456789).quadratic1(),
(
FiniteNimber::from(0x4aec00e4),
FiniteNimber::from(0x4aec00e5)
)
);
assert_eq!(
FiniteNimber::from(0xabcdef01).quadratic1(),
(
FiniteNimber::from(0x15b7ac2e8),
FiniteNimber::from(0x15b7ac2e9)
)
);
let c = FiniteNimber::from(0x1234);
let (x0, x1) = FiniteNimber::quadratic2(&FiniteNimber::from(0), &c);
assert_eq!(x0, x1);
assert_eq!(x0, c.sqrt());
assert_eq!(
FiniteNimber::quadratic3(&FiniteNimber::from(0), &c, &c),
None
);
let b = FiniteNimber::from(&[
0x6a784d9045190cfe,
0x762e7160f38b4da5,
0xabf7158809cf4f3c,
0x2b7e151628aed2a6,
]);
let c = FiniteNimber::from(&[
0x0082efa98ec4e6c8,
0x4a4093822299f31d,
0x313198a2e0370734,
0x3243f6a8885a308d,
]);
let (x0, x1) = FiniteNimber::quadratic2(&b, &c);
assert_eq!(
(&x0, &x1),
(
&FiniteNimber::from(&[
0x7392f8dbead36e27,
0x36f5dc49b334524e,
0xee0d9a634c2c0d97,
0x54405edcf7af4140
]),
&FiniteNimber::from(&[
0x19eab54bafca62d9,
0x40dbad2940bf1feb,
0x45fa8feb45e342ab,
0x7f3e4bcadf0193e6
])
)
);
assert_eq!(x0.square() + &b * &x0 + &c, FiniteNimber::from(0));
assert_eq!(x1.square() + &b * &x1 + &c, FiniteNimber::from(0));
let (x0, x1) = FiniteNimber::quadratic2(&c, &b);
assert_eq!(
(&x0, &x1),
(
&FiniteNimber::from(&[
0xfc7a27ac1e033e3b,
0x1796bec3e478bf4b,
0xb3befb47f55d2ca4,
0x9d9791d73ee591e3,
0x0082efa98ec4e6c8,
0x4a4093822299f31d,
0x313198a2e0370734,
0x3243f6a8885a308d
]),
&FiniteNimber::from(&[
0xfcf8c80590c7d8f3,
0x5dd62d41c6e14c56,
0x828f63e5156a2b90,
0xafd4677fb6bfa16e,
0x0082efa98ec4e6c8,
0x4a4093822299f31d,
0x313198a2e0370734,
0x3243f6a8885a308d
])
)
);
assert_eq!(x0.square() + &c * &x0 + &b, FiniteNimber::from(0));
assert_eq!(x1.square() + &c * &x1 + &b, FiniteNimber::from(0));
let a = FiniteNimber::from(&[
0x1f86c6a11d0c18e9,
0x41082276bf3a2725,
0x5f39cc0605cedc83,
0x19e3779b97f4a7c1,
]);
let (x0, x1) = FiniteNimber::quadratic3(&a, &b, &c).unwrap();
assert_eq!(
(&x0, &x1),
(
&FiniteNimber::from(&[
0x9158f8e5521e4d7f,
0x14039613d6356d58,
0x32c757ff646c969f,
0xe491100b296059ec
]),
&FiniteNimber::from(&[
0xcfe6ae8db299c447,
0x5bcae8a4aab7a309,
0x28fb6b4774cab164,
0xfcb0c6016cb46b22
])
)
);
assert_eq!(&a * x0.square() + &b * &x0 + &c, FiniteNimber::from(0));
assert_eq!(&a * x1.square() + &b * &x1 + &c, FiniteNimber::from(0));
}
}