use std::convert::{Into, TryFrom};
use std::format;
use std::ops::{Div, Mul, Rem};
use itertools::EitherOrBoth;
use itertools::Itertools;
use thiserror::Error;
use super::PRIMES;
#[derive(Error, Debug, PartialEq)]
pub enum Error {
#[error("Register overflow: input {0} has prime factor larger than {}",
PRIMES.last().unwrap())]
RegisterOverflow(u64),
#[error("Zero is meaningless in FRACTRAN programs, cannot be stored")]
NumIsZero,
}
pub trait Divides {
fn divides(&self, rhs: &Self) -> bool;
}
impl<T> Divides for T
where
T: Rem<Self, Output = Self> + Into<u64> + Eq + Copy,
{
fn divides(&self, rhs: &Self) -> bool {
(*rhs % *self).into() == 0
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct PrimeBasis {
pub exps: Vec<u32>,
}
impl PrimeBasis {
pub fn try_new(num: u64) -> Result<PrimeBasis, Error> {
if num == 0 {
return Err(Error::NumIsZero);
}
let mut exps = vec![];
let mut exp;
let mut curr = num;
for prime in &*PRIMES {
if curr == 1 {
return Ok(PrimeBasis { exps });
}
exp = 0;
while curr % prime == 0 {
curr /= prime;
exp += 1;
}
exps.push(exp);
}
Err(Error::RegisterOverflow(num))
}
pub fn value(&self) -> u64 {
self.exps
.iter()
.zip(&*PRIMES)
.fold(1, |acc, (&exp, p)| acc * p.pow(exp))
}
}
impl std::fmt::Display for PrimeBasis {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let godel_str = self
.exps
.iter()
.zip(&*PRIMES)
.map(|(&exp, p)| {
if exp != 0 {
format!("{}^{}", p, exp)
} else {
String::new()
}
})
.filter(|s| s != "")
.join(" ✕ ");
if godel_str == "" {
write!(f, "PrimeBasis(1)")
} else {
write!(f, "PrimeBasis({})", godel_str)
}
}
}
impl Mul for PrimeBasis {
type Output = PrimeBasis;
fn mul(self, rhs: Self) -> Self::Output {
PrimeBasis {
exps: self
.exps
.into_iter()
.zip_longest(rhs.exps)
.map(|pair| pair.reduce(|a, b| a + b))
.collect(),
}
}
}
impl Div for PrimeBasis {
type Output = PrimeBasis;
fn div(self, rhs: Self) -> Self::Output {
if !rhs.divides(&self) {
panic!("Can't divide {} by {}", self, rhs);
} else {
PrimeBasis {
exps: self
.exps
.into_iter()
.zip_longest(rhs.exps)
.map(|pair| pair.reduce(|a, b| a - b))
.collect(),
}
}
}
}
impl Divides for PrimeBasis {
fn divides(&self, rhs: &Self) -> bool {
self.exps.iter().zip_longest(&rhs.exps).all(|pair| {
match pair {
EitherOrBoth::Left(a) => *a == 0,
EitherOrBoth::Right(_) => true,
EitherOrBoth::Both(a, b) => a <= b,
}
})
}
}
impl TryFrom<u64> for PrimeBasis {
type Error = Error;
fn try_from(value: u64) -> Result<Self, Self::Error> {
Self::try_new(value)
}
}
impl Into<u64> for PrimeBasis {
fn into(self) -> u64 {
self.value()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn new(num: u64) -> PrimeBasis {
PrimeBasis::try_new(num).unwrap()
}
#[test]
fn test_basis_nums() {
assert_eq!(new(200).exps, vec![3, 0, 2]);
assert_eq!(new(1).exps, vec![]);
assert_eq!(PrimeBasis::try_new(0), Err(Error::NumIsZero));
}
#[test]
fn test_tryfrom_u64() {
let nums: Vec<u64> = vec![1, 2, 3, 5, 10, 20, 60, 2520, 70000];
for num in nums {
let converted: u64 = PrimeBasis::try_from(num).unwrap().into();
assert_eq!(num, converted);
}
}
#[test]
fn test_into_u64() {
let nums: Vec<u64> = vec![1, 2, 3, 5, 10, 20, 60, 2520, 70000];
for num in nums {
let converted: u64 = new(num).into();
assert_eq!(num, converted);
}
}
#[test]
fn test_mul() {
let nums1: Vec<u64> = vec![1, 2, 5, 8, 100, 256, 2520];
let nums2: Vec<u64> = vec![1, 5, 7, 11, 102, 353, 1000];
for num1 in &nums1 {
for num2 in &nums2 {
let pb1 = PrimeBasis::try_new(*num1).unwrap();
let pb2 = PrimeBasis::try_new(*num2).unwrap();
let ans: u64 = (pb1 * pb2).into();
assert_eq!(ans, num1 * num2);
}
}
}
#[test]
fn test_divides() {
let help_div = |a, b| {
let pb1 = new(a);
let pb2 = new(b);
pb1.divides(&pb2)
};
assert_eq!(help_div(7, 28), true);
assert_eq!(help_div(32, 128), true);
assert_eq!(help_div(40, 40), true);
assert_eq!(help_div(1, 28), true);
assert_eq!(help_div(1, 1), true);
assert_eq!(help_div(70, 7), false);
assert_eq!(help_div(2, 7), false);
assert_eq!(help_div(100, 250), false);
}
}