Packages

Core mathematical functions for VIVA - sentient digital life. PAD emotions, Cusp catastrophe, Free Energy Principle, attractor dynamics.

Current section

Files

Jump to
viva_math src viva_math@complex.erl
Raw

src/viva_math@complex.erl

-module(viva_math@complex).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/viva_math/complex.gleam").
-export([zero/0, one/0, i/0, real/1, from_polar/2, add/2, sub/2, mul/2, 'div'/2, neg/1, conjugate/1, scale/2, magnitude/1, magnitude_squared/1, phase/1, to_polar/1, exp/1, log/1, sqrt/1, pow_int/2, pow/2, sin/1, cos/1, tan/1, is_close/3]).
-export_type([complex/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" Complex numbers `a + bi`.\n"
"\n"
" Standard algebra plus a small set of transcendental functions sufficient\n"
" for FFT, eigenvalue work and signal processing.\n"
"\n"
" All operations are pure and total — division by zero returns the zero\n"
" complex by convention (consistent with `gleam/float`).\n"
).
-type complex() :: {complex, float(), float()}.
-file("src/viva_math/complex.gleam", 21).
?DOC(" 0 + 0i\n").
-spec zero() -> complex().
zero() ->
{complex, +0.0, +0.0}.
-file("src/viva_math/complex.gleam", 26).
?DOC(" 1 + 0i\n").
-spec one() -> complex().
one() ->
{complex, 1.0, +0.0}.
-file("src/viva_math/complex.gleam", 31).
?DOC(" 0 + 1i (the imaginary unit)\n").
-spec i() -> complex().
i() ->
{complex, +0.0, 1.0}.
-file("src/viva_math/complex.gleam", 36).
?DOC(" Build from a real number (im = 0).\n").
-spec real(float()) -> complex().
real(X) ->
{complex, X, +0.0}.
-file("src/viva_math/complex.gleam", 41).
?DOC(" Build from magnitude `r` and phase `theta` (polar form).\n").
-spec from_polar(float(), float()) -> complex().
from_polar(R, Theta) ->
{complex, R * math:cos(Theta), R * math:sin(Theta)}.
-file("src/viva_math/complex.gleam", 49).
-spec add(complex(), complex()) -> complex().
add(A, B) ->
{complex,
erlang:element(2, A) + erlang:element(2, B),
erlang:element(3, A) + erlang:element(3, B)}.
-file("src/viva_math/complex.gleam", 53).
-spec sub(complex(), complex()) -> complex().
sub(A, B) ->
{complex,
erlang:element(2, A) - erlang:element(2, B),
erlang:element(3, A) - erlang:element(3, B)}.
-file("src/viva_math/complex.gleam", 57).
-spec mul(complex(), complex()) -> complex().
mul(A, B) ->
{complex,
(erlang:element(2, A) * erlang:element(2, B)) - (erlang:element(3, A) * erlang:element(
3,
B
)),
(erlang:element(2, A) * erlang:element(3, B)) + (erlang:element(3, A) * erlang:element(
2,
B
))}.
-file("src/viva_math/complex.gleam", 61).
-spec 'div'(complex(), complex()) -> complex().
'div'(A, B) ->
Denom = (erlang:element(2, B) * erlang:element(2, B)) + (erlang:element(
3,
B
)
* erlang:element(3, B)),
case Denom =:= +0.0 of
true ->
zero();
false ->
{complex, case Denom of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator -> ((erlang:element(2, A) * erlang:element(
2,
B
))
+ (erlang:element(3, A) * erlang:element(3, B)))
/ Gleam@denominator
end, case Denom of
+0.0 -> +0.0;
-0.0 -> -0.0;
Gleam@denominator@1 -> ((erlang:element(3, A) * erlang:element(
2,
B
))
- (erlang:element(2, A) * erlang:element(3, B)))
/ Gleam@denominator@1
end}
end.
-file("src/viva_math/complex.gleam", 74).
-spec neg(complex()) -> complex().
neg(Z) ->
{complex, +0.0 - erlang:element(2, Z), +0.0 - erlang:element(3, Z)}.
-file("src/viva_math/complex.gleam", 78).
-spec conjugate(complex()) -> complex().
conjugate(Z) ->
{complex, erlang:element(2, Z), +0.0 - erlang:element(3, Z)}.
-file("src/viva_math/complex.gleam", 82).
-spec scale(complex(), float()) -> complex().
scale(Z, S) ->
{complex, erlang:element(2, Z) * S, erlang:element(3, Z) * S}.
-file("src/viva_math/complex.gleam", 91).
?DOC(" |z| = √(re² + im²). Uses `hypot` to avoid overflow.\n").
-spec magnitude(complex()) -> float().
magnitude(Z) ->
viva_math@scalar:hypot(erlang:element(2, Z), erlang:element(3, Z)).
-file("src/viva_math/complex.gleam", 96).
?DOC(" |z|² (cheaper than `magnitude` when comparing).\n").
-spec magnitude_squared(complex()) -> float().
magnitude_squared(Z) ->
(erlang:element(2, Z) * erlang:element(2, Z)) + (erlang:element(3, Z) * erlang:element(
3,
Z
)).
-file("src/viva_math/complex.gleam", 101).
?DOC(" Phase angle in radians, range (-π, π].\n").
-spec phase(complex()) -> float().
phase(Z) ->
math:atan2(erlang:element(3, Z), erlang:element(2, Z)).
-file("src/viva_math/complex.gleam", 106).
?DOC(" Polar decomposition: returns `(magnitude, phase)`.\n").
-spec to_polar(complex()) -> {float(), float()}.
to_polar(Z) ->
{magnitude(Z), phase(Z)}.
-file("src/viva_math/complex.gleam", 115).
?DOC(" exp(z) = e^a · (cos(b) + i·sin(b))\n").
-spec exp(complex()) -> complex().
exp(Z) ->
Ea = math:exp(erlang:element(2, Z)),
{complex,
Ea * math:cos(erlang:element(3, Z)),
Ea * math:sin(erlang:element(3, Z))}.
-file("src/viva_math/complex.gleam", 121).
?DOC(" log(z) = ln|z| + i·arg(z). Branch cut along the negative real axis.\n").
-spec log(complex()) -> complex().
log(Z) ->
{complex, math:log(magnitude(Z)), phase(Z)}.
-file("src/viva_math/complex.gleam", 126).
?DOC(" Principal square root √z.\n").
-spec sqrt(complex()) -> complex().
sqrt(Z) ->
R = magnitude(Z),
New_re = math:sqrt((R + erlang:element(2, Z)) / 2.0),
Sign = case erlang:element(3, Z) < +0.0 of
true ->
-1.0;
false ->
1.0
end,
New_im = Sign * math:sqrt((R - erlang:element(2, Z)) / 2.0),
{complex, New_re, New_im}.
-file("src/viva_math/complex.gleam", 146).
-spec pow_int_loop(complex(), integer(), complex()) -> complex().
pow_int_loop(Base, N, Acc) ->
case N of
0 ->
Acc;
_ ->
case N rem 2 of
0 ->
pow_int_loop(mul(Base, Base), N div 2, Acc);
_ ->
pow_int_loop(mul(Base, Base), N div 2, mul(Acc, Base))
end
end.
-file("src/viva_math/complex.gleam", 138).
?DOC(" z^n for integer n via repeated multiplication. Handles negative exponents.\n").
-spec pow_int(complex(), integer()) -> complex().
pow_int(Z, N) ->
case N of
0 ->
one();
N@1 when N@1 < 0 ->
'div'(one(), pow_int(Z, 0 - N@1));
_ ->
pow_int_loop(Z, N, one())
end.
-file("src/viva_math/complex.gleam", 158).
?DOC(" Real exponent z^x via `exp(x · log(z))`.\n").
-spec pow(complex(), float()) -> complex().
pow(Z, X) ->
exp(scale(log(Z), X)).
-file("src/viva_math/complex.gleam", 163).
?DOC(" sin(z) = sin(a)·cosh(b) + i·cos(a)·sinh(b)\n").
-spec sin(complex()) -> complex().
sin(Z) ->
{complex,
math:sin(erlang:element(2, Z)) * math:cosh(erlang:element(3, Z)),
math:cos(erlang:element(2, Z)) * math:sinh(erlang:element(3, Z))}.
-file("src/viva_math/complex.gleam", 168).
?DOC(" cos(z) = cos(a)·cosh(b) - i·sin(a)·sinh(b)\n").
-spec cos(complex()) -> complex().
cos(Z) ->
{complex,
math:cos(erlang:element(2, Z)) * math:cosh(erlang:element(3, Z)),
+0.0 - (math:sin(erlang:element(2, Z)) * math:sinh(erlang:element(3, Z)))}.
-file("src/viva_math/complex.gleam", 173).
?DOC(" tan(z) = sin(z) / cos(z)\n").
-spec tan(complex()) -> complex().
tan(Z) ->
'div'(sin(Z), cos(Z)).
-file("src/viva_math/complex.gleam", 182).
?DOC(" Approximate equality.\n").
-spec is_close(complex(), complex(), float()) -> boolean().
is_close(A, B, Tol) ->
(gleam@float:absolute_value(erlang:element(2, A) - erlang:element(2, B)) =< Tol)
andalso (gleam@float:absolute_value(
erlang:element(3, A) - erlang:element(3, B)
)
=< Tol).