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 [false] 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 not! true] ⧗ 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]]
<?>‣ &compare-case
# ============================================================================ #
# 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))
# Wilson's theorem
# very inefficient but very golfable
prime? [=?(++(fac --0) % 0)] ⧗ Unary → Boolean