# MIT License, Copyright (c) 2022 Marvin Borner

:input std/Number

:import std/List .

# adds all values in list
sum foldl add (+0) ⧗ (List Number) → Number

∑‣ sum

:test (∑((+1) : ((+2) : {}(+3)))) ((+6))

# returns max value of list
lmax foldl1 max ⧗ (List Number) → Number

:test (lmax ((+1) : ((+3) : {}(+2)))) ((+3))

# returns min value of list
lmin foldl1 min ⧗ (List Number) → Number

:test (lmin ((+2) : ((+1) : {}(+0)))) ((+0))

# list from num to num
{…→…} z [[[rec]]] ⧗ Number → Number → (List Number)
	rec (1 =? ++0) case-end case-list
		case-list 1 : (2 ++1 0)
		case-end empty

:test ({ (+0) → (+2) }) ((+0) : ((+1) : {}(+2)))

# equivalent of mathematical sum function
∑…→…|… z [[[[[rec]]]]] (+0) ⧗ Number → Number → (Number → Number) → Number
	rec (2 =? ++1) case-end case-sum
		case-sum 4 (3 + (0 2)) ++2 1 0
		case-end 3

:test (∑ (+1) → (+3) | ++‣) ((+9))

# multiplies all values in list
product foldl mul (+1) ⧗ (List Number) → Number

∏‣ product

:test (∏((+1) : ((+2) : {}(+3)))) ((+6))

# equivalent of mathematical product function
∏…→…|… z [[[[[rec]]]]] (+1) ⧗ Number → Number → (Number → Number) → Number
	rec (2 =? ++1) case-end case-sum
		case-sum 4 (3 ⋅ (0 2)) ++2 1 0
		case-end 3

:test (∏ (+1) → (+3) | ++‣) ((+24))

# greatest common divisor
gcd z [[[(1 =? 0) case-eq ((1 >? 0) case-gre case-les)]]] ⧗ Number → Number → Number
	case-eq 1
	case-gre 2 (1 - 0) 0
	case-les 2 1 (0 - 1)

:test ((gcd (+2) (+4)) =? (+2)) (true)
:test ((gcd (+10) (+5)) =? (+5)) (true)
:test ((gcd (+3) (+8)) =? (+1)) (true)

# power function
pow […!!… (iterate (…⋅… 0) (+1))] ⧗ Number → Number → Number

…**… pow

:test (((+2) ** (+3)) =? (+8)) (true)

# power function using ternary exponentiation (TODO: fix, wrong..)
pow* z [[[rec]]] ⧗ Number → Number → Number
	rec =?0 case-end case-pow
		case-pow =?(lst 0) ³(2 1 /³0) (³(2 1 /³0) ⋅ 1)
			³‣ [0 ⋅ 0 ⋅ 0]
		case-end (+1)

# prime number sequence
primes nub ((…≠?… (+1)) ∘∘ gcd) (iterate ++‣ (+2)) ⧗ (List Number)

# factorial function
# TODO: faster fac?
fac [∏ (+1) → 0 | i] ⧗ Number → Number

:test ((fac (+3)) =? (+6)) (true)

# hyper factorial function
fac! [∏ (+1) → 0 | [0 ** 0]] ⧗ Number → Number

:test ((fac! (+2)) =? (+4)) (true)
:test ((fac! (+3)) =? (+108)) (true)

# calculates a powertower
# also: [[foldr pow (+1) (replicate 0 1)]]
powertower z [[[rec]]] ⧗ Number → Number → Number
	rec =?0 case-end case-rec
		case-end (+1)
		case-rec 1 ** (2 1 --0)

:test ((powertower (+2) (+1)) =? (+2)) (true)
:test ((powertower (+2) (+2)) =? (+4)) (true)
:test ((powertower (+2) (+3)) =? (+16)) (true)
:test ((powertower (+2) (+4)) =? (+65536)) (true)

# knuth's up-arrow notation
# arrow count → base → exponent
arrow z [[[[rec]]]] ⧗ Number → Number → Number → Number
	rec =?2 case-end case-rec
		case-end 1 ⋅ 0
		case-rec foldr (3 --2) 1 (replicate --0 1)

:test ((arrow (+1) (+1) (+1)) =? (+1)) (true)
:test ((arrow (+1) (+2) (+4)) =? (+16)) (true)
:test ((arrow (+2) (+2) (+4)) =? (+65536)) (true)

# fibonacci sequence
# TODO: faster fib?
fibs head <$> (iterate [~0 : (^0 + ~0)] ((+0) : (+1))) ⧗ (List Number)

fib [fibs !! ++0] ⧗ Number

:test (fib (+5)) ((+8))

# integer logarithm
log z [[[[rec]]]] (+1) ⧗ Number → Number → Number
	rec [((3 ≤? 1) ⋀? (1 <? 0)) case-end case-rec] (2 ⋅ 1)
		case-end (+0)
		case-rec ++(4 0 2 1)

:test ((log (+2) (+1)) =? (+0)) (true)
:test ((log (+2) (+2)) =? (+1)) (true)
:test ((log (+2) (+3)) =? (+1)) (true)
:test ((log (+2) (+4)) =? (+2)) (true)
:test ((log (+2) (+32)) =? (+5)) (true)
:test ((log (+2) (+48)) =? (+5)) (true)

# iterated logarithm
# note that log* 1 is defined as 1
log* [z [[rec]] --0] ⧗ Number → Number
	rec (0 ≤? (+1)) case-end case-rec
		case-end (+1)
		case-rec ++(1 (log (+2) 0))

:test ((log* (+1)) =? (+1)) (true)
:test ((log* (+2)) =? (+1)) (true)
:test ((log* (+3)) =? (+2)) (true)
:test ((log* (+4)) =? (+2)) (true)
:test ((log* (+5)) =? (+3)) (true)
:test ((log* (+16)) =? (+3)) (true)
:test ((log* (+17)) =? (+4)) (true)
:test ((log* (+65536)) =? (+4)) (true)
:test ((log* (+65537)) =? (+5)) (true)

# pascal triangle
# TODO: something is wrong in here
pascal iterate [zip-with …+… ({}(+0) ++ 0) (0 ; (+0))] ({}(+1))

# π as a list of decimal digits
# translation of unbounded spigot algorithm by Jeremy Gibbons
# TODO: faster!
#     → BBP/Bellard's formula with ternary base?
#       TODO: |log|, better primes/mod/div
π g (+1) (+180) (+60) (+2) ⧗ (List Number)
	g z [[[[[calc]]]]]
		calc b : (4 q r t i)
			a ↑⁰(↑⁺0 ⋅ (↑⁰0 + (+2)))
			b (3 ⋅ ↑⁰(↑⁻(↑⁻0)) + ((+5) ⋅ 2)) / ((+5) ⋅ 1)
			q (+10) ⋅ 3 ⋅ 0 ⋅ --((+2) ⋅ 0)
			r (+10) ⋅ a ⋅ (3 ⋅ ((+5) ⋅ 0 - (+2)) + 2 - (b ⋅ 1))
			t 1 ⋅ a
			i ++0