use super::Modulus;
use crate::{
error::MathError, integer::Z, macros::for_others::implement_empty_trait_owned_ref, traits::*,
};
use flint_sys::{
fmpz::{fmpz, fmpz_clear, fmpz_cmp_si},
fmpz_mod::fmpz_mod_ctx_init,
};
use std::{mem::MaybeUninit, rc::Rc, str::FromStr};
impl Modulus {
pub(crate) fn from_fmpz_ref(value: &fmpz) -> Result<Self, MathError> {
if (unsafe { fmpz_cmp_si(value, 1) } <= 0) {
let z = Z::from(value);
return Err(MathError::InvalidModulus(z.to_string()));
}
let mut ctx = MaybeUninit::uninit();
unsafe {
fmpz_mod_ctx_init(ctx.as_mut_ptr(), value);
Ok(Self {
modulus: Rc::new(ctx.assume_init()),
})
}
}
}
trait IntoModulus {}
implement_empty_trait_owned_ref!(IntoModulus for Z fmpz u8 u16 u32 u64 i8 i16 i32 i64);
impl<Integer: AsInteger + IntoModulus> From<Integer> for Modulus {
fn from(value: Integer) -> Self {
match value.get_fmpz_ref() {
Some(val) => Modulus::from_fmpz_ref(val).unwrap(),
None => unsafe {
let mut value = value.into_fmpz();
let out = Modulus::from_fmpz_ref(&value);
fmpz_clear(&mut value);
out.unwrap()
},
}
}
}
impl From<&Modulus> for Modulus {
fn from(value: &Modulus) -> Self {
value.clone()
}
}
impl FromStr for Modulus {
type Err = MathError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let z = Z::from_str(s)?;
Modulus::from_fmpz_ref(&z.value)
}
}
#[cfg(test)]
mod test_from {
use super::*;
#[test]
#[allow(clippy::useless_conversion)]
fn available() {
let _ = Modulus::from(i8::MAX);
let _ = Modulus::from(i16::MAX);
let _ = Modulus::from(i32::MAX);
let _ = Modulus::from(i64::MAX);
let _ = Modulus::from(&i8::MAX);
let _ = Modulus::from(&i16::MAX);
let _ = Modulus::from(&i32::MAX);
let _ = Modulus::from(&i64::MAX);
let _ = Modulus::from(u8::MAX);
let _ = Modulus::from(u16::MAX);
let _ = Modulus::from(u32::MAX);
let _ = Modulus::from(u64::MAX);
let _ = Modulus::from(&u8::MAX);
let _ = Modulus::from(&u16::MAX);
let _ = Modulus::from(&u32::MAX);
let _ = Modulus::from(&u64::MAX);
let _ = Modulus::from(Z::from(10));
let _ = Modulus::from(&Z::from(10));
let z = Z::from(42);
let _ = Modulus::from(&z.value);
let modulus = Modulus::from(z.value);
let _ = Modulus::from(&modulus);
let _ = Modulus::from(modulus);
}
#[test]
#[should_panic]
fn invalid_modulus() {
let _ = Modulus::from(1);
}
#[test]
fn large() {
let modulus = Modulus::from(i64::MAX);
assert_eq!(Z::from(modulus), Z::from(i64::MAX));
}
#[test]
fn small() {
let modulus = Modulus::from(2);
assert_eq!(Z::from(modulus), Z::from(2));
}
}
#[cfg(test)]
mod test_from_fmpz_ref {
use super::*;
use crate::integer::Z;
#[test]
fn working_example() {
let z = Z::from(100);
assert!(Modulus::from_fmpz_ref(&z.value).is_ok());
}
#[test]
fn large_modulus() {
let z = &Z::from(u64::MAX);
assert!(Modulus::from_fmpz_ref(&z.value).is_ok());
}
#[test]
fn negative_modulus() {
let z = &Z::from(-42);
assert!(Modulus::from_fmpz_ref(&z.value).is_err());
}
#[test]
fn zero_modulus() {
let z = &Z::ZERO;
assert!(Modulus::from_fmpz_ref(&z.value).is_err());
}
}
#[cfg(test)]
mod test_from_str {
use super::Modulus;
use std::str::FromStr;
#[test]
fn working_example() {
assert!(Modulus::from_str("42").is_ok());
}
#[test]
fn large_value() {
assert!(Modulus::from_str(&"1".repeat(65)).is_ok());
}
#[test]
fn false_format_whitespaces() {
assert!(Modulus::from_str("4 2").is_err());
}
#[test]
fn false_format_symbols() {
assert!(Modulus::from_str("b a").is_err());
}
#[test]
fn false_sign() {
assert!(Modulus::from_str("-42").is_err());
}
}