# 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)) # folds unary number left foldl [[1 [0 1]]] ⧗ Unary → a :test (foldl [[1 (1 (1 0))]]) ([[0 1 1 1]]) # unary infinity ∞ [[[0 0] [2 (0 0)]]] # 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 # more efficient primality test, based on Tromp's characteristic sequence # golfed to 208 bit prime? [0 [0 ki] [0 ki [0 ki (sieve y s0)]] k] ⧗ Unary → Boolean sieve [0 [[[0 k ([3 0 (4 0)] [[[[0 2 (1 (5 3))]]]])]]]] s0 [[[[0 ki (1 3)]]]] :test (prime? (+2u)) ([[1]]) :test (prime? (+3u)) ([[1]]) :test (prime? (+4u)) ([[0]]) :test (prime? (+5u)) ([[1]]) :test (prime? (+6u)) ([[0]])