1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
//! A representation of a fraction used in Fractran program execution.

use super::primebasis::Divides;
use std::fmt;
use std::ops::{Div, Mul};

/// Wrapper trait for the various things that numbers in Fractran programs need
/// to do. `PrimeBasis` satisfies this, as does `u64`.
pub trait FractranNat:
    Into<u64>
    + Mul<Self, Output = Self>
    + Div<Self, Output = Self>
    + Divides
    + Clone
    + std::fmt::Debug
    + Sized
{
}
impl<T> FractranNat for T where
    T: Into<u64>
        + Mul<Self, Output = Self>
        + Div<Self, Output = Self>
        + Divides
        + Clone
        + std::fmt::Debug
        + Sized
{
}

/// A fraction in Fractran, with a nonzero numerator and denominator.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Fraction<T: FractranNat> {
    /// The fraction numerator: must be nonzero.
    num: T,
    /// The fraction denominator: must be nonzero.
    denom: T,
}

/// The result of executing an instruction step in Fractran. `Changed` means
/// that the input did divide the fraction, and `Unchanged` means it did not.
/// Note that the result of a step can be `Changed` and still equal to the
/// original state, if the fraction used was 1/1.
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub(crate) enum StepResult<T: FractranNat> {
    /// The same state as before, without any multiplication.
    Unchanged(T),
    /// The product of the former state and the fraction at this step.
    Changed(T),
}

impl<T: FractranNat> Fraction<T> {
    /// Creates a new `Fraction` with the given numerator and denominator,
    /// panicking if either input is zero.
    pub fn new(num: T, denom: T) -> Fraction<T> {
        if num.clone().into() == 0_u64 || denom.clone().into() == 0_u64 {
            panic!("Cannot have fraction with zero on either side!");
        } else {
            Fraction { num, denom }
        }
    }
    /// Computes the only operation Fractran has: for this fraction `f` and some
    /// input `n`, returns `StepResult::Changed(nf)` if `nf` is integral and
    /// `StepResult::Unchanged(n)` otherwise. Note that, for example, 1/1
    /// doesn't change the actual state, but it will still return `Changed`
    /// because the multiplication was performed.
    pub(crate) fn exec(&self, input: T) -> StepResult<T> {
        let new_num = input.clone() * self.clone().num;
        if self.denom.divides(&new_num) {
            StepResult::Changed(new_num / self.clone().denom)
        } else {
            StepResult::Unchanged(input)
        }
    }
}

impl<T: FractranNat + fmt::Display> fmt::Display for Fraction<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{} / {}", self.num, self.denom)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_exec() {
        assert_eq!(
            Fraction::new(1_u64, 2_u64).exec(2_u64),
            StepResult::Changed(1_u64)
        );
        assert_eq!(
            Fraction::new(1_u64, 2_u64).exec(1_u64),
            StepResult::Unchanged(1_u64)
        );
        assert_eq!(
            Fraction::new(6_u64, 7_u64).exec(28_u64),
            StepResult::Changed(24_u64)
        );
    }
}