#![deny(missing_docs)]
#![allow(clippy::needless_return)]
#[cfg(doctest)]
#[macro_use]
extern crate doc_comment;
#[cfg(doctest)]
doctest!("../README.md", readme);
use std::str::FromStr;
use num_complex::{Complex, ParseComplexError};
use num_integer::Integer;
use num_traits::{Num, One, PrimInt, Signed, Zero};
mod ops;
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct GaussianInt<T: PrimInt + Integer>(pub Complex<T>);
#[macro_export]
macro_rules! gaussint {
($a:expr,$b:expr) => {
GaussianInt::new($a, $b)
};
($n:expr) => {
GaussianInt::new($n, 0)
};
}
impl<T: PrimInt + Integer> GaussianInt<T> {
pub fn new(r: T, i: T) -> Self {
Self(Complex::new(r, i))
}
pub fn congruent(&self, other: Self, modulus: Self) -> bool {
(*self - other) % modulus == Self::zero()
}
}
impl<T: PrimInt + Integer + Signed> GaussianInt<T> {
pub fn conj(&self) -> Self {
Self::new(self.0.re, -self.0.im)
}
pub fn norm(&self) -> usize {
let a = self.0.re;
let b = self.0.im;
(T::pow(a, 2) + T::pow(b, 2)).to_usize().unwrap()
}
pub fn divides(&self, other: Self) -> bool {
*self != Self::zero() && (other % *self) == Self::zero()
}
pub fn is_rational(&self) -> bool {
self.0.im == T::zero()
}
pub fn is_gaussian_prime(&self) -> bool {
let a = self.0.re;
let b = self.0.im;
match (a.abs().to_isize().unwrap(), b.abs().to_isize().unwrap()) {
(0, 0) => return false,
(1, 1) => return true,
(-1, -1) => return true,
(2, 0) => return false,
(0, 2) => return false,
_ => {}
}
let condition_1 = match (a.is_zero(), b.is_zero()) {
(true, false) => {
let other = b.abs().to_u64().unwrap();
primal::is_prime(other) && (other - 3) % 4 == 0
}
(false, true) => {
let other = a.abs().to_u64().unwrap();
primal::is_prime(other) && (other - 3) % 4 == 0
}
_ => false,
};
let condition_2 = match (a.is_zero(), b.is_zero()) {
(false, false) => {
let a = a.abs().to_u64().unwrap();
let b = b.abs().to_u64().unwrap();
let sum_of_squares = u64::pow(a, 2) + u64::pow(b, 2);
let sum_of_squares_is_4n_plus_3 = (sum_of_squares - 3) % 4 == 0;
primal::is_prime(sum_of_squares) && !sum_of_squares_is_4n_plus_3
}
_ => false,
};
condition_1 || condition_2
}
pub fn units() -> [Self; 4] {
[
Self::one(), -Self::one(), Self::new(T::zero(), T::one()), Self::new(T::zero(), -T::one()), ]
}
pub fn is_associated(&self, other: Self) -> bool {
for u in GaussianInt::units() {
if (*self * u) == other {
return true;
}
}
false
}
pub fn is_even(&self) -> bool {
let modulus = Self::new(T::one(), T::one());
self.congruent(Self::zero(), modulus)
}
pub fn is_odd(&self) -> bool {
let modulus = Self::new(T::one(), T::one());
self.congruent(Self::one(), modulus)
}
}
impl<T: PrimInt + Integer + Signed> GaussianInt<T>
where
f64: From<T>,
{
pub fn to_polar(&self) -> (f64, f64) {
let a = self.0.re;
let b = self.0.im;
let a: f64 = a.into();
let b: f64 = b.into();
Complex::new(a, b).to_polar()
}
}
pub fn get_g_ints(n: isize) -> impl Iterator<Item = GaussianInt<isize>> + 'static {
let mut integers: Vec<GaussianInt<_>> = vec![];
for a in -n..=n {
for b in -n..=n {
let z = GaussianInt::new(a, b);
integers.push(z);
}
}
integers.into_iter()
}
pub fn get_pos_g_ints(n: isize) -> impl Iterator<Item = GaussianInt<isize>> + 'static {
let mut pos_integers: Vec<GaussianInt<_>> = vec![];
for a in 0..=n {
for b in -n..=n {
let z = GaussianInt::new(a, b);
pos_integers.push(z);
}
}
pos_integers.into_iter()
}
pub fn get_g_primes(n: isize) -> impl Iterator<Item = GaussianInt<isize>> + 'static {
let set = get_g_ints(n);
let mut primes: Vec<GaussianInt<_>> = vec![];
for z in set {
if z.is_gaussian_prime() {
primes.push(z);
}
}
primes.into_iter()
}
pub fn get_pos_g_primes(n: isize) -> impl Iterator<Item = GaussianInt<isize>> + 'static {
let set = get_pos_g_ints(n);
let mut pos_primes: Vec<GaussianInt<_>> = vec![];
for z in set {
if z.is_gaussian_prime() {
pos_primes.push(z);
}
}
pos_primes.into_iter()
}
impl<T: PrimInt + Integer> One for GaussianInt<T> {
fn one() -> Self {
GaussianInt::new(T::one(), T::zero())
}
}
impl<T: PrimInt + Integer> Zero for GaussianInt<T> {
fn zero() -> Self {
GaussianInt::new(T::zero(), T::zero())
}
fn is_zero(&self) -> bool {
*self == GaussianInt::zero()
}
}
impl<T: PrimInt + Integer> From<Complex<T>> for GaussianInt<T> {
fn from(z: Complex<T>) -> Self {
Self(z)
}
}
impl<T: PrimInt + Integer> From<GaussianInt<T>> for isize {
fn from(g: GaussianInt<T>) -> Self {
g.0.re.to_isize().unwrap()
}
}
impl<T> FromStr for GaussianInt<T>
where
T: PrimInt + Integer + FromStr + Num + Clone,
{
type Err = ParseComplexError<T::Err>;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self::from(Complex::from_str(s)?))
}
}
impl<T: PrimInt + Integer> std::fmt::Display for GaussianInt<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let one = T::one();
let zero = T::zero();
let im_is_neg_one = self.0.im + one == zero;
if self.0.im == zero {
return write!(f, "{}", self.0.re.to_isize().unwrap());
}
if self.0.re == zero && self.0.im == T::one() {
return write!(f, "i");
}
if self.0.re == zero && im_is_neg_one {
return write!(f, "-i");
}
if self.0.re == zero && !im_is_neg_one {
return write!(f, "{}i", self.0.im.to_isize().unwrap());
}
if self.0.im == one {
return write!(f, "{}+i", self.0.re.to_isize().unwrap());
}
if self.0.im < zero {
if self.0.re == zero && im_is_neg_one {
return write!(f, "-i");
}
if self.0.im + one == zero {
write!(f, "{}-i", self.0.re.to_isize().unwrap(),)
} else {
return write!(
f,
"{}{}i",
self.0.re.to_isize().unwrap(),
self.0.im.to_isize().unwrap()
);
}
} else {
return write!(
f,
"{}+{}i",
self.0.re.to_isize().unwrap(),
self.0.im.to_isize().unwrap()
);
}
}
}
#[cfg(test)]
mod tests;