Number/Unary.bruijn


# MIT License, Copyright (c) 2022 Marvin Borner
# with implicit help from Justine Tunney and John Tromp
# classic Church style numerals

:import std/Logic .
:import std/Combinator .
:import std/Pair .

# id for church numerals
# generic base for dec/fib/fac/etc.
uid [[[extract (2 inc init)]]] ⧗ Unary → Unary
	extract &i
	inc [&(0 2)]
	init &0

:test (uid (+0u)) ((+0u))
:test (uid (+1u)) ((+1u))
:test (uid (+5u)) ((+5u))

# returns true if a unary number is zero
zero? [0 [(+0u)] true] ⧗ Unary → Boolean

=?‣ zero?

:test (=?(+0u)) (true)
:test (=?(+42u)) (false)

# returns remainder of integer division
mod [[[[3 &k (3 [3 [[[0 (2 (5 1)) 1]]] [1] 1] [1]) ki]]]] ⧗ Unary → Unary → Unary

…%… mod

:test ((+10u) % (+3u)) ((+1u))
:test ((+3u) % (+5u)) ((+3u))

# returns true if the number is even (remainder mod 2 == 0)
even? [=?(0 % (+2u))] ⧗ Unary → Boolean

=²?‣ even?

:test (=²?(+0u)) (true)
:test (=²?(+1u)) (false)
:test (=²?(+41u)) (false)
:test (=²?(+42u)) (true)

# subtracts 1 from a unary number
dec [[[extract (2 inc const)]]] ⧗ Unary → Unary
	extract &i
	inc [&(0 2)]
	const [1]

--‣ dec

:test (--(+0u)) ((+0u))
:test (--(+1u)) ((+0u))
:test (--(+42u)) ((+41u))

# adds 1 to a unary number
inc [[[1 (2 1 0)]]] ⧗ Unary → Unary

++‣ inc

:test (++(+0u)) ((+1u))
:test (++(+1u)) ((+2u))
:test (++(+42u)) ((+43u))

# adds two unary numbers
add [[[[3 1 (2 1 0)]]]] ⧗ Unary → Unary → Unary

…+… add

:test ((+0u) + (+2u)) ((+2u))
:test ((+5u) + (+3u)) ((+8u))

# subtracts two unary numbers
sub [[0 dec 1]] ⧗ Unary → Unary → Unary

…-… sub

:test ((+2u) - (+2u)) ((+0u))
:test ((+5u) - (+3u)) ((+2u))

# returns true if number is greater than other number
gt? not! ∘∘ (zero? ∘∘ sub) ⧗ Unary → Unary → Boolean

…>?… gt?

:test ((+1u) >? (+2u)) (false)
:test ((+2u) >? (+2u)) (false)
:test ((+3u) >? (+2u)) (true)

# returns true if two unary numbers are equal
eq? [[=?(1 - 0) ⋀? =?(0 - 1)]] ⧗ Unary → Unary → Boolean

…=?… eq?

:test ((+1u) =? (+0u)) (false)
:test ((+1u) =? (+1u)) (true)
:test ((+1u) =? (+2u)) (false)
:test ((+42u) =? (+42u)) (true)

# returns eq, lt, gt depending on comparison of two numbers
compare-case [[[[[go (1 - 0) (0 - 1)]]]]] ⧗ a → b → c → Unary → Unary → d
	go [[=?0 (=?1 6 5) 4]]

# ============================================================================ #
# most relevant functions are defined - we can now derive from Generic/Number! #
# ============================================================================ #

:input std/Generic/Number

# multiplies two unary numbers
mul …∘… ⧗ Unary → Unary → Unary

…⋅… mul

:test ((+0u) ⋅ (+2u)) ((+0u))
:test ((+2u) ⋅ (+3u)) ((+6u))

# divs two unary numbers
div [[[[3 t [1] (3 [3 t [3 (0 1)] i] 0)]]]] ⧗ Unary → Unary → Unary

…/… div

:test ((+8u) / (+4u)) ((+2u))
:test ((+2u) / (+1u)) ((+2u))
:test ((+2u) / (+2u)) ((+1u))
:test ((+2u) / (+3u)) ((+0u))

# slower div (more obvious impl)
div* [z rec ++0] ⧗ Unary → Unary → Unary
	rec [[[[[[=?0 ((+0u) 2 1) (2 (5 0 3 2 1))] (3 - 2)]]]]]

# exponentiates two unary number
# doesn't give correct results for x^0
pow* [[1 0]] ⧗ Unary → Unary → Unary

# exponentiates two unary numbers
pow [[0 [[3 (1 0)]] pow*]] ⧗ Unary → Unary → Unary

…^… pow

:test ((+2u) ^ (+3u)) ((+8u))
:test ((+3u) ^ (+2u)) ((+9u))

# also note that
# [0 ..i.. 0] (+nu) = n^..i..^n

# fibonacci sequence
# index +1 vs std/Math fib
fib [0 [[[2 0 [2 (1 0)]]]] [[1]] [0]] ⧗ Unary → Unary

:test (fib (+6u)) ((+8u))

# factorial function
fac [[1 [[0 (1 [[2 1 (1 0)]])]] [1] i]] ⧗ Unary → Unary

:test (fac (+3u)) ((+6u))

# hyperfactorial function
hyperfac [[1 [[(0 0) (1 [[2 1 (1 0)]])]] [1] i]] ⧗ Unary → Unary

:test (hyperfac (+3u)) ((+108u))