Math/Real.bruijn


# some ideas by u/DaVinci103
# MIT License, Copyright (c) 2024 Marvin Borner

:import std/Logic .
:import std/Combinator .
:import std/Pair .
:import std/Math/Rational Q
:import std/Math N

# a Real is just a Number → Rational!

# converts a balanced ternary number to a real number
number→real [[Q.number→rational 1]] ⧗ Number → Real

:test (number→real (+5)) ((+5.0r))

# returns true if two real numbers are equal approximately
approx-eq? [[[Q.eq? (1 2) (0 2)]]] ⧗ Number → Real → Real → Boolean

# TODO: bigger value??
…≈?… approx-eq? (+100)

# returns true if a real number is greater than another
gt? [[[Q.gt? (1 2) (0 2)]]] ⧗ Number → Real → Real → Boolean

…>?… gt?

# returns true if a real number is less than another
lt? [[[Q.lt? (1 2) (0 2)]]] ⧗ Number → Real → Real → Boolean

…<?… lt?

# returns true if a real number is positive
positive? [\(gt? 0) (+0.0r)] ⧗ Number → Real → Boolean

>?‣ positive?

# returns true if a real number is negative
negative? [\(lt? 0) (+0.0r)] ⧗ Number → Real → Boolean

<?‣ negative?

# adds two real numbers
add φ Q.add ⧗ Real → Real → Real

…+… add

:test ((+1.0r) + (+0.0r) ≈? (+1.0r)) (true)
:test ((+0.0r) + (-1.0r) ≈? (-1.0r)) (true)

# subtracts two real numbers
sub φ Q.sub ⧗ Real → Real → Real

…-… sub

:test ((+1.0r) - (+0.5r) ≈? (+0.5r)) (true)
:test ((+0.0r) - (-1.0r) ≈? (+1.0r)) (true)

# multiplies two real numbers
mul φ Q.mul ⧗ Real → Real → Real

…⋅… mul

:test ((+5.0r) ⋅ (+5.0r) ≈? (+25.0r)) (true)
:test ((+1.8r) ⋅ (+1.2r) ≈? (+2.16r)) (true)

# divides two real numbers
div φ Q.div ⧗ Real → Real → Real

…/… div

:test ((+8.0r) / (+4.0r) ≈? (+2.0r)) (true)
:test ((+18.0r) / (+12.0r) ≈? (+1.5r)) (true)

# negates a real number
negate b Q.negate ⧗ Real → Real

-‣ negate

:test (-(+0.0r) ≈? (+0.0r)) (true)
:test (-(+4.2r) ≈? (-4.2r)) (true)
:test (-(-4.2r) ≈? (+4.2r)) (true)

# inverts a real number
invert b Q.invert ⧗ Real → Real

~‣ invert

:test (~(+0.5r) ≈? (+2.0r)) (true)
:test (~(-0.5r) ≈? (-2.0r)) (true)

# finds smallest equivalent representation of a real number
compress b Q.compress ⧗ Real → Real

%‣ compress

:test (%[(+4) : (+1)] ≈? (+2.0r)) (true)
:test (%[(+4) : (+7)] ≈? (+0.5r)) (true)

# increments a real number
inc add (+1.0r) ⧗ Real → Real

++‣ inc

# decrements a real number
dec \sub (+1.0r) ⧗ Real → Real

--‣ dec

# ---

:import std/List L

# e^x using Taylor expansion
# tex: \sum_{n=0}^\infty \frac{x^n}{n!}
unary-exp [[0 &[[[[[[[2 1 0 (Q.add 4 (1 : N.--0)) N.++3]] pow fac]]]]] start [[[[1]]]]]] ⧗ Unary → Real
	pow N.mul 6 4
	fac N.mul 1 3
	start [0 (+1) (+1) (+1.0q) (+1)]

# equivalent to unary-exp but with ternary index using infinite list iteration
exp [[L.nth-iterate &[[[[op]]]] start 0 [[[[1]]]] 0]] ⧗ Real → Real
	start [0 (+1.0r) (+1.0r) (+1.0r) (+1)]
	op [[[2 1 0 (4 + (1 / 0)) N.++3]] pow fac]
		pow 6 ⋅ 4
		fac (number→real 1) ⋅ 3

# power function: real^number
pow-n [L.nth-iterate (mul 0) (+1.0r)] ⧗ Real → Number → Real

# e^x using infinite limit
# tex: \lim_{n\to\infty}(1+x/n)^n
lim-exp [[pow-n ++(1 / (number→real 0)) 0]] ⧗ Real → Real

# natural logarithm using Taylor expansion
# tex: \sum_{n=0}^\infty\frac{2}{2n+1}(\frac{x-1}{x+1})^{2n+1}
# error: O(((x-1)/2)^{2n+1})
ln [[[L.nth-iterate &[[[op]]] start 1] (--1 / ++1 0) [[[1]]]]] ⧗ Real → Real
	start [0 1 (+0.0q) (+0)]
	op [0 pow (Q.add 2 go) N.++1]
		pow Q.mul 3 (Q.mul 4 4)
		go Q.mul ((+2) : (N.mul (+2) 1)) 3

:test (Q.eq? (ln (+2.0r) (+2)) ((+168) : (+242))) (true)

derive [[[[((3 (2 + 0)) - (3 2)) / 0] ((+1.0r) / conv) 0]]] ⧗ (Real → Real) → (Real → Real)
	conv number→real (N.pow (+2) (N.mul (+2) 0))

# power function: real^real
pow [[exp (0 ⋅ (ln 1))]] ⧗ Real → Real → Real

…**… pow

# square root by x^{0.5}
sqrt* \pow (+0.5r) ⧗ Real → Real

# Newton's/Heron's method, quadratic convergence
# tex: x_{n+1}=\frac{x_n+a/x_n}{2}
sqrt [[y [[[N.=?0 guess go]]] (1 0) 0]] ⧗ Real → Real
	guess (+1.0q)
	go [Q.div (Q.add 0 (Q.div 2 0)) (+2.0q)] (2 1 N.--0)

# Newton's/Heron's method, quadratic convergence
# tex: x_{n+1}=\frac{3x_n+a/(x_n^2)}{4}
cbrt [[y [[[N.=?0 guess go]]] (1 0) 0]] ⧗ Real → Real
	guess (+1.0q)
	go [Q.div (Q.add (Q.mul (+3.0q) 0) (Q.div 2 (Q.mul 0 0))) (+4.0q)] (2 1 N.--0)

# hypotenuse
hypot [[sqrt ((0 ⋅ 0) + (1 ⋅ 1))]] ⧗ Real → Real → Real

# tex: \sum_{n=0}^\infty\frac{2^n n!^2}{(2n+1)!}
π/2 [L.nth-iterate &[[[[[op]]]]] start 0 [[[[[3]]]]]] ⧗ Real
	start [0 (+1) (+0.0q) (+1) (+1) (+1)]
	op [0 N.++5 (Q.add 4 ((N.mul 3 2) : N.--1)) num-pow num-fac denom]
		num-pow N.mul 3 (+2)
		num-fac N.mul 2 (N.mul 5 5)
		denom [N.mul 2 (N.mul 0 N.++0)] (N.mul (+2) 5)

# ratio of circle's circumference to its diameter
π π/2 ⋅ (+2.0r) ⧗ Real

# Gauss-Legendre, quadratic convergence
# Chudnovsky would be even faster but is ugly (i.e. magic numbers)
π-gauss [L.nth-iterate &[[[[op]]]] start 0 final]
	start [0 (+1.0q) Q.~(sqrt (+2.0r) 1) (+0.25q) (+1.0q)]
	op [[1 0 b t p] a]
		a Q.div (Q.add 4 3) (+2.0q)
		b sqrt [Q.mul 6 5] 6
		t Q.sub 3 (Q.mul 2 (Q.pow-n (Q.sub 5 0) (+2)))
		p Q.mul (+2.0q) 2
	final [[[[Q.div (Q.pow-n (Q.add 3 2) (+2)) (Q.mul (+4.0q) 1)]]]]

# golden ratio from direct formula
φ ++(sqrt (+5.0r)) / (+2.0r)

# conjugate golden ratio
ψ -(~φ)

# golden ratio from fibonacci convergence
φ* ++[(L.nth-iterate &[[0 : (N.add 1 0)]] ((+0) : (+1)) 0) [[1 : N.--0]]]

# real fibonacci
fib [((pow φ 0) - (pow ψ 0)) / (sqrt (+5.0r))]

# arctan by Taylor expansion, only for |x|<=1
# tex: \sum_{n=0}^\infty(-1)^n \frac{x^{2n+1}}{2n+1}
atan* [[[L.nth-iterate &[[[[op]]]] start 1] (1 0) [[[[3]]]]]] ⧗ Real → Real
	start [0 1 [[0]] (Q.pow-n 1 (+3)) (+3.0q)]
	op [0 ((3 Q.add Q.sub) 4 (Q.div 2 1)) \3 num denom]
		num Q.mul 2 (Q.mul 5 5)
		denom Q.add 1 (+2.0q)

# actual arctan for arbitrary x
atan [[[fallback] (1 0)]] ⧗ Real → Real
	normal Q.sub π/2-pm conj-atan
		π/2-pm Q.mul (Q.>?0 (+1.0q) (-1.0q)) (π/2 1)
		conj-atan atan* [Q.div (+1.0q) 1] 1
	fallback Q.lt? Q.|0 (+1.0) (atan* [1] 1) normal

# 2-argument arctan
atan2 [[[[[go]] (2 0) (1 0)]]] ⧗ Real → Real → Real
	go Q.add a (Q.mul b (Q.add c d))
		z (+0.0q)
		a Q.=?0 z (atan [Q.div 2 1] 2)
		b Q.sub (+1.0q) (Q.<?1 (+2.0q) z)
		c Q.<?0 (π 2) z
		d Q.=?0 (π/2 2) z