use term::*;
use term::Error::*;
use booleans::*;
use pair::pair;
use combinators::{i, k, z};
pub fn zero() -> Term { abs(abs(Var(1))) }
pub fn is_zero() -> Term {
abs(
app!(Var(1), abs(fls()), tru())
)
}
pub fn one() -> Term {
abs(abs(
app(Var(2), Var(1))
))
}
pub fn succ() -> Term {
abs(abs(abs(
app(Var(2), app!(Var(3), Var(2), Var(1)))
)))
}
pub fn plus() -> Term {
abs(abs(abs(abs(
app!(Var(4), Var(2), app!(Var(3), Var(2), Var(1)))
))))
}
pub fn mult() -> Term {
abs(abs(abs(
app(Var(3), app(Var(2), Var(1)))
)))
}
pub fn pow() -> Term {
abs(abs(
app!(is_zero(), Var(1), one(), app(Var(1), Var(2)))
))
}
pub fn pred() -> Term {
abs(abs(abs(
app!(
Var(3),
abs(abs(app(Var(1), app(Var(2), Var(4))))),
abs(Var(2)),
abs(Var(1))
)
)))
}
pub fn sub() -> Term {
abs(abs(
app!(Var(1), pred(), Var(2))
))
}
pub fn lt() -> Term {
abs(abs(
app(not(), app!(leq(), Var(1), Var(2)))
))
}
pub fn leq() -> Term {
abs(abs(
app(is_zero(), app!(sub(), Var(2), Var(1)))
))
}
pub fn eq() -> Term {
abs(abs(
app!(
and(),
app!(leq(), Var(2), Var(1)),
app!(leq(), Var(1), Var(2))
)
))
}
pub fn neq() -> Term {
abs(abs(
app!(
or(),
app(not(), app!(leq(), Var(2), Var(1))),
app(not(), app!(leq(), Var(1), Var(2)))
)
))
}
pub fn geq() -> Term {
abs(abs(
app!(leq(), Var(1), Var(2))
))
}
pub fn gt() -> Term {
abs(abs(
app(not(), app!(leq(), Var(2), Var(1)))
))
}
pub fn div() -> Term {
app!(
z(),
abs(abs(abs(abs(
app!(
lt(),
Var(2),
Var(1),
abs(app!(pair(), Var(4), Var(3))),
abs(app!(
Var(5),
app(succ(), Var(4)),
app!(sub(), Var(3), Var(2)),
Var(2)
)),
i()
)
)))),
zero()
)
}
pub fn quot() -> Term {
app(
z(),
abs(abs(abs(
app!(
lt(),
Var(2),
Var(1),
abs(zero()),
abs(app(
succ(),
app!(
Var(4),
app!(sub(), Var(3), Var(2)),
Var(2)
)
)),
i()
)
)))
)
}
pub fn rem() -> Term {
app(
z(),
abs(abs(abs(
app!(
lt(),
Var(2),
Var(1),
abs(Var(3)),
abs(
app!(
Var(4),
app!(sub(), Var(3), Var(2)),
Var(2)
)
),
i()
)
)))
)
}
pub fn fac() -> Term {
abs(
app!(
Var(1),
abs(abs(abs(
app!(
Var(3),
app!(mult(), Var(2), Var(1)),
app!(succ(), Var(1))
)
))),
k(),
one(),
one()
)
)
}
impl Term {
pub fn value(&self) -> Result<usize, Error> {
if let Ok(ref inner) = self.unabs_ref().and_then(|t| t.unabs_ref()) {
Ok(try!(inner._value()))
} else {
Err(NotANum)
}
}
fn _value(&self) -> Result<usize, Error> {
if let Ok((lhs, rhs)) = self.unapp_ref() {
if *lhs == Var(2) {
Ok(1 + try!(rhs._value()))
} else {
Err(NotANum)
}
} else if *self == Var(1) {
Ok(0)
} else {
Err(NotANum)
}
}
pub fn is_cnum(&self) -> bool { self.value().is_ok() }
}
impl From<usize> for Term {
fn from(n: usize) -> Self {
let mut inner = Var(1);
let mut count = n;
while count > 0 {
inner = Var(2).app(inner);
count -= 1;
}
abs(abs(inner))
}
}
#[cfg(test)]
mod tests {
use super::*;
use reduction::beta;
use reduction::Order::*;
use combinators::c;
#[test]
fn church_invalid_nums() {
assert_eq!( tru().is_cnum(), false);
assert_eq!( Var(1).is_cnum(), false);
assert_eq!(abs(Var(1)).is_cnum(), false);
}
#[test]
fn church_number_values() {
for n in 0..10 { assert_eq!(Term::from(n).value(), Ok(n)) }
}
#[test]
fn church_successor() {
assert_eq!(beta(app!(succ(), 0.into()), NOR, 0, false), 1.into());
assert_eq!(beta(app!(succ(), 1.into()), NOR, 0, false), 2.into());
assert_eq!(beta(app!(succ(), 2.into()), NOR, 0, false), 3.into());
assert_eq!(beta(app!(succ(), 0.into()), HNO, 0, false), 1.into());
assert_eq!(beta(app!(succ(), 1.into()), HNO, 0, false), 2.into());
assert_eq!(beta(app!(succ(), 2.into()), HNO, 0, false), 3.into());
assert_eq!(beta(app!(succ(), 0.into()), APP, 0, false), 1.into());
assert_eq!(beta(app!(succ(), 1.into()), APP, 0, false), 2.into());
assert_eq!(beta(app!(succ(), 2.into()), APP, 0, false), 3.into());
assert_eq!(beta(app!(succ(), 0.into()), HAP, 0, false), 1.into());
assert_eq!(beta(app!(succ(), 1.into()), HAP, 0, false), 2.into());
assert_eq!(beta(app!(succ(), 2.into()), HAP, 0, false), 3.into());
}
#[test]
fn church_predecessor() {
assert_eq!(beta(app!(pred(), 0.into()), NOR, 0, false), 0.into());
assert_eq!(beta(app!(pred(), 1.into()), NOR, 0, false), 0.into());
assert_eq!(beta(app!(pred(), 5.into()), NOR, 0, false), 4.into());
assert_eq!(beta(app!(pred(), 0.into()), HNO, 0, false), 0.into());
assert_eq!(beta(app!(pred(), 1.into()), HNO, 0, false), 0.into());
assert_eq!(beta(app!(pred(), 5.into()), HNO, 0, false), 4.into());
assert_eq!(beta(app!(pred(), 0.into()), APP, 0, false), 0.into());
assert_eq!(beta(app!(pred(), 1.into()), APP, 0, false), 0.into());
assert_eq!(beta(app!(pred(), 5.into()), APP, 0, false), 4.into());
assert_eq!(beta(app!(pred(), 0.into()), HAP, 0, false), 0.into());
assert_eq!(beta(app!(pred(), 1.into()), HAP, 0, false), 0.into());
assert_eq!(beta(app!(pred(), 5.into()), HAP, 0, false), 4.into());
}
#[test]
fn church_plus_sub_equivalents() {
assert_eq!(beta(app!( plus(), 1.into()), NOR, 0, false), succ());
assert_eq!(beta(app!(c(), sub(), 1.into()), NOR, 0, false), pred());
assert_eq!(beta(app!( plus(), 1.into()), HNO, 0, false), succ());
assert_eq!(beta(app!(c(), sub(), 1.into()), HNO, 0, false), pred());
assert_eq!(beta(app!( plus(), 1.into()), APP, 0, false), succ());
assert_eq!(beta(app!(c(), sub(), 1.into()), APP, 0, false), pred());
assert_eq!(beta(app!( plus(), 1.into()), HAP, 0, false), succ());
assert_eq!(beta(app!(c(), sub(), 1.into()), HAP, 0, false), pred());
}
#[test]
fn church_multiplication() {
assert_eq!(beta(app!(mult(), 3.into(), 4.into()), NOR, 0, false), 12.into());
assert_eq!(beta(app!(mult(), 1.into(), 3.into()), NOR, 0, false), 3.into());
assert_eq!(beta(app!(mult(), 3.into(), 1.into()), NOR, 0, false), 3.into());
assert_eq!(beta(app!(mult(), 5.into(), 0.into()), NOR, 0, false), 0.into());
assert_eq!(beta(app!(mult(), 0.into(), 5.into()), NOR, 0, false), 0.into());
assert_eq!(beta(app!(mult(), 3.into(), 4.into()), HNO, 0, false), 12.into());
assert_eq!(beta(app!(mult(), 1.into(), 3.into()), HNO, 0, false), 3.into());
assert_eq!(beta(app!(mult(), 3.into(), 1.into()), HNO, 0, false), 3.into());
assert_eq!(beta(app!(mult(), 5.into(), 0.into()), HNO, 0, false), 0.into());
assert_eq!(beta(app!(mult(), 0.into(), 5.into()), HNO, 0, false), 0.into());
assert_eq!(beta(app!(mult(), 3.into(), 4.into()), APP, 0, false), 12.into());
assert_eq!(beta(app!(mult(), 1.into(), 3.into()), APP, 0, false), 3.into());
assert_eq!(beta(app!(mult(), 3.into(), 1.into()), APP, 0, false), 3.into());
assert_eq!(beta(app!(mult(), 5.into(), 0.into()), APP, 0, false), 0.into());
assert_eq!(beta(app!(mult(), 0.into(), 5.into()), APP, 0, false), 0.into());
assert_eq!(beta(app!(mult(), 3.into(), 4.into()), HAP, 0, false), 12.into());
assert_eq!(beta(app!(mult(), 1.into(), 3.into()), HAP, 0, false), 3.into());
assert_eq!(beta(app!(mult(), 3.into(), 1.into()), HAP, 0, false), 3.into());
assert_eq!(beta(app!(mult(), 5.into(), 0.into()), HAP, 0, false), 0.into());
assert_eq!(beta(app!(mult(), 0.into(), 5.into()), HAP, 0, false), 0.into());
}
#[test]
fn church_exponentiation() {
assert_eq!(beta(app!(pow(), 2.into(), 4.into()), NOR, 0, false), 16.into());
assert_eq!(beta(app!(pow(), 1.into(), 3.into()), NOR, 0, false), 1.into());
assert_eq!(beta(app!(pow(), 3.into(), 1.into()), NOR, 0, false), 3.into());
assert_eq!(beta(app!(pow(), 5.into(), 0.into()), NOR, 0, false), 1.into());
assert_eq!(beta(app!(pow(), 0.into(), 5.into()), NOR, 0, false), 0.into());
assert_eq!(beta(app!(pow(), 2.into(), 4.into()), HNO, 0, false), 16.into());
assert_eq!(beta(app!(pow(), 1.into(), 3.into()), HNO, 0, false), 1.into());
assert_eq!(beta(app!(pow(), 3.into(), 1.into()), HNO, 0, false), 3.into());
assert_eq!(beta(app!(pow(), 5.into(), 0.into()), HNO, 0, false), 1.into());
assert_eq!(beta(app!(pow(), 0.into(), 5.into()), HNO, 0, false), 0.into());
assert_eq!(beta(app!(pow(), 2.into(), 4.into()), APP, 0, false), 16.into());
assert_eq!(beta(app!(pow(), 1.into(), 3.into()), APP, 0, false), 1.into());
assert_eq!(beta(app!(pow(), 3.into(), 1.into()), APP, 0, false), 3.into());
assert_eq!(beta(app!(pow(), 5.into(), 0.into()), APP, 0, false), 1.into());
assert_eq!(beta(app!(pow(), 0.into(), 5.into()), APP, 0, false), 0.into());
assert_eq!(beta(app!(pow(), 2.into(), 4.into()), HAP, 0, false), 16.into());
assert_eq!(beta(app!(pow(), 1.into(), 3.into()), HAP, 0, false), 1.into());
assert_eq!(beta(app!(pow(), 3.into(), 1.into()), HAP, 0, false), 3.into());
assert_eq!(beta(app!(pow(), 5.into(), 0.into()), HAP, 0, false), 1.into());
assert_eq!(beta(app!(pow(), 0.into(), 5.into()), HAP, 0, false), 0.into());
}
#[test]
fn church_division() {
assert_eq!(beta(app!(div(), 2.into(), 2.into()), NOR, 0, false), (1.into(), 0.into()).into());
assert_eq!(beta(app!(div(), 3.into(), 2.into()), NOR, 0, false), (1.into(), 1.into()).into());
assert_eq!(beta(app!(div(), 5.into(), 2.into()), NOR, 0, false), (2.into(), 1.into()).into());
assert_eq!(beta(app!(div(), 2.into(), 1.into()), NOR, 0, false), (2.into(), 0.into()).into());
assert_eq!(beta(app!(div(), 0.into(), 3.into()), NOR, 0, false), (0.into(), 0.into()).into());
assert_eq!(beta(app!(div(), 2.into(), 2.into()), HNO, 0, false), (1.into(), 0.into()).into());
assert_eq!(beta(app!(div(), 3.into(), 2.into()), HNO, 0, false), (1.into(), 1.into()).into());
assert_eq!(beta(app!(div(), 5.into(), 2.into()), HNO, 0, false), (2.into(), 1.into()).into());
assert_eq!(beta(app!(div(), 2.into(), 1.into()), HNO, 0, false), (2.into(), 0.into()).into());
assert_eq!(beta(app!(div(), 0.into(), 3.into()), HNO, 0, false), (0.into(), 0.into()).into());
assert_eq!(beta(app!(div(), 2.into(), 2.into()), HAP, 0, false), (1.into(), 0.into()).into());
assert_eq!(beta(app!(div(), 3.into(), 2.into()), HAP, 0, false), (1.into(), 1.into()).into());
assert_eq!(beta(app!(div(), 5.into(), 2.into()), HAP, 0, false), (2.into(), 1.into()).into());
assert_eq!(beta(app!(div(), 2.into(), 1.into()), HAP, 0, false), (2.into(), 0.into()).into());
assert_eq!(beta(app!(div(), 0.into(), 3.into()), HAP, 0, false), (0.into(), 0.into()).into());
}
#[test]
fn church_quotient() {
assert_eq!(beta(app!(quot(), 2.into(), 2.into()), NOR, 0, false), 1.into());
assert_eq!(beta(app!(quot(), 3.into(), 2.into()), NOR, 0, false), 1.into());
assert_eq!(beta(app!(quot(), 5.into(), 2.into()), NOR, 0, false), 2.into());
assert_eq!(beta(app!(quot(), 2.into(), 1.into()), NOR, 0, false), 2.into());
assert_eq!(beta(app!(quot(), 0.into(), 3.into()), NOR, 0, false), 0.into());
assert_eq!(beta(app!(quot(), 2.into(), 2.into()), HNO, 0, false), 1.into());
assert_eq!(beta(app!(quot(), 3.into(), 2.into()), HNO, 0, false), 1.into());
assert_eq!(beta(app!(quot(), 5.into(), 2.into()), HNO, 0, false), 2.into());
assert_eq!(beta(app!(quot(), 2.into(), 1.into()), HNO, 0, false), 2.into());
assert_eq!(beta(app!(quot(), 0.into(), 3.into()), HNO, 0, false), 0.into());
assert_eq!(beta(app!(quot(), 2.into(), 2.into()), HAP, 0, false), 1.into());
assert_eq!(beta(app!(quot(), 3.into(), 2.into()), HAP, 0, false), 1.into());
assert_eq!(beta(app!(quot(), 5.into(), 2.into()), HAP, 0, false), 2.into());
assert_eq!(beta(app!(quot(), 2.into(), 1.into()), HAP, 0, false), 2.into());
assert_eq!(beta(app!(quot(), 0.into(), 3.into()), HAP, 0, false), 0.into());
}
#[test]
fn church_remainder() {
assert_eq!(beta(app!(rem(), 2.into(), 2.into()), NOR, 0, false), 0.into());
assert_eq!(beta(app!(rem(), 3.into(), 2.into()), NOR, 0, false), 1.into());
assert_eq!(beta(app!(rem(), 2.into(), 5.into()), NOR, 0, false), 2.into());
assert_eq!(beta(app!(rem(), 2.into(), 1.into()), NOR, 0, false), 0.into());
assert_eq!(beta(app!(rem(), 0.into(), 3.into()), NOR, 0, false), 0.into());
assert_eq!(beta(app!(rem(), 2.into(), 2.into()), HNO, 0, false), 0.into());
assert_eq!(beta(app!(rem(), 3.into(), 2.into()), HNO, 0, false), 1.into());
assert_eq!(beta(app!(rem(), 2.into(), 5.into()), HNO, 0, false), 2.into());
assert_eq!(beta(app!(rem(), 2.into(), 1.into()), HNO, 0, false), 0.into());
assert_eq!(beta(app!(rem(), 0.into(), 3.into()), HNO, 0, false), 0.into());
assert_eq!(beta(app!(rem(), 2.into(), 2.into()), HAP, 0, false), 0.into());
assert_eq!(beta(app!(rem(), 3.into(), 2.into()), HAP, 0, false), 1.into());
assert_eq!(beta(app!(rem(), 2.into(), 5.into()), HAP, 0, false), 2.into());
assert_eq!(beta(app!(rem(), 2.into(), 1.into()), HAP, 0, false), 0.into());
assert_eq!(beta(app!(rem(), 0.into(), 3.into()), HAP, 0, false), 0.into());
}
#[test]
fn church_fac() {
assert_eq!(beta(app!(fac(), 0.into()), NOR, 0, false), 1.into());
assert_eq!(beta(app!(fac(), 1.into()), NOR, 0, false), 1.into());
assert_eq!(beta(app!(fac(), 2.into()), NOR, 0, false), 2.into());
assert_eq!(beta(app!(fac(), 3.into()), NOR, 0, false), 6.into());
assert_eq!(beta(app!(fac(), 0.into()), HNO, 0, false), 1.into());
assert_eq!(beta(app!(fac(), 1.into()), HNO, 0, false), 1.into());
assert_eq!(beta(app!(fac(), 2.into()), HNO, 0, false), 2.into());
assert_eq!(beta(app!(fac(), 3.into()), HNO, 0, false), 6.into());
assert_eq!(beta(app!(fac(), 0.into()), HAP, 0, false), 1.into());
assert_eq!(beta(app!(fac(), 1.into()), HAP, 0, false), 1.into());
assert_eq!(beta(app!(fac(), 2.into()), HAP, 0, false), 2.into());
assert_eq!(beta(app!(fac(), 3.into()), HAP, 0, false), 6.into());
}
}