-module(viva_math@common). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/viva_math/common.gleam"). -export([clamp/3, clamp_unit/1, clamp_bipolar/1, lerp/3, inverse_lerp/3, sigmoid/2, sigmoid_standard/1, softmax/1, safe_div/3, smoothstep/3, exponential_decay/3, inverse_decay/3, inverse_sqrt_decay/3, deterministic_noise/2, wiener_increment/3]). -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( " Common mathematical utilities for VIVA.\n" "\n" " Functions not found in gleam_community_maths:\n" " - clamp, lerp, sigmoid, softmax, safe_div\n" ). -file("src/viva_math/common.gleam", 19). ?DOC( " Clamp a value to a range [min, max].\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " clamp(5.0, 0.0, 10.0) // -> 5.0\n" " clamp(-1.0, 0.0, 10.0) // -> 0.0\n" " clamp(15.0, 0.0, 10.0) // -> 10.0\n" " ```\n" ). -spec clamp(float(), float(), float()) -> float(). clamp(Value, Min, Max) -> _pipe = Value, _pipe@1 = gleam@float:max(_pipe, Min), gleam@float:min(_pipe@1, Max). -file("src/viva_math/common.gleam", 34). ?DOC( " Clamp a value to the unit interval [0, 1].\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " clamp_unit(0.5) // -> 0.5\n" " clamp_unit(-0.5) // -> 0.0\n" " clamp_unit(1.5) // -> 1.0\n" " ```\n" ). -spec clamp_unit(float()) -> float(). clamp_unit(Value) -> clamp(Value, +0.0, 1.0). -file("src/viva_math/common.gleam", 48). ?DOC( " Clamp a value to the bipolar interval [-1, 1].\n" " Used for PAD dimensions.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " clamp_bipolar(0.5) // -> 0.5\n" " clamp_bipolar(-1.5) // -> -1.0\n" " clamp_bipolar(1.5) // -> 1.0\n" " ```\n" ). -spec clamp_bipolar(float()) -> float(). clamp_bipolar(Value) -> clamp(Value, -1.0, 1.0). -file("src/viva_math/common.gleam", 64). ?DOC( " Linear interpolation between two values.\n" "\n" " lerp(a, b, 0.0) = a\n" " lerp(a, b, 1.0) = b\n" " lerp(a, b, 0.5) = (a + b) / 2\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " lerp(0.0, 10.0, 0.5) // -> 5.0\n" " lerp(0.0, 10.0, 0.25) // -> 2.5\n" " ```\n" ). -spec lerp(float(), float(), float()) -> float(). lerp(A, B, T) -> A + (T * (B - A)). -file("src/viva_math/common.gleam", 77). ?DOC( " Inverse linear interpolation - find t given value in range [a, b].\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " inverse_lerp(0.0, 10.0, 5.0) // -> Ok(0.5)\n" " inverse_lerp(0.0, 10.0, 2.5) // -> Ok(0.25)\n" " inverse_lerp(0.0, 0.0, 5.0) // -> Error(Nil) (division by zero)\n" " ```\n" ). -spec inverse_lerp(float(), float(), float()) -> {ok, float()} | {error, nil}. inverse_lerp(A, B, Value) -> Range = B - A, case Range =:= +0.0 of true -> {error, nil}; false -> {ok, case Range of +0.0 -> +0.0; -0.0 -> -0.0; Gleam@denominator -> (Value - A) / Gleam@denominator end} end. -file("src/viva_math/common.gleam", 97). ?DOC( " Sigmoid function: 1 / (1 + exp(-k * x))\n" "\n" " Maps any real number to (0, 1).\n" " k controls steepness (k=1 is standard sigmoid).\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " sigmoid(0.0, 1.0) // -> 0.5\n" " sigmoid(100.0, 1.0) // -> ~1.0\n" " sigmoid(-100.0, 1.0) // -> ~0.0\n" " ```\n" ). -spec sigmoid(float(), float()) -> float(). sigmoid(X, K) -> Neg_kx = +0.0 - (K * X), case (1.0 + gleam_community@maths:exponential(Neg_kx)) of +0.0 -> +0.0; -0.0 -> -0.0; Gleam@denominator -> 1.0 / Gleam@denominator end. -file("src/viva_math/common.gleam", 103). ?DOC(" Standard sigmoid with k=1.\n"). -spec sigmoid_standard(float()) -> float(). sigmoid_standard(X) -> sigmoid(X, 1.0). -file("src/viva_math/common.gleam", 118). ?DOC( " Softmax function: converts a list of values to probabilities.\n" "\n" " softmax([x1, x2, ...]) = [exp(x1)/sum, exp(x2)/sum, ...]\n" " where sum = exp(x1) + exp(x2) + ...\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " softmax([1.0, 2.0, 3.0]) // -> [0.09, 0.24, 0.67] (approx)\n" " softmax([0.0, 0.0]) // -> [0.5, 0.5]\n" " ```\n" ). -spec softmax(list(float())) -> {ok, list(float())} | {error, nil}. softmax(Values) -> case Values of [] -> {error, nil}; _ -> Max_val = gleam@list:fold(Values, +0.0, fun gleam@float:max/2), Exps = gleam@list:map( Values, fun(X) -> gleam_community@maths:exponential(X - Max_val) end ), Sum = gleam@list:fold(Exps, +0.0, fun(Acc, X@1) -> Acc + X@1 end), case Sum =:= +0.0 of true -> {error, nil}; false -> {ok, gleam@list:map(Exps, fun(X@2) -> case Sum of +0.0 -> +0.0; -0.0 -> -0.0; Gleam@denominator -> X@2 / Gleam@denominator end end)} end end. -file("src/viva_math/common.gleam", 147). ?DOC( " Safe division with default value on division by zero.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " safe_div(10.0, 2.0, 0.0) // -> 5.0\n" " safe_div(10.0, 0.0, -1.0) // -> -1.0\n" " ```\n" ). -spec safe_div(float(), float(), float()) -> float(). safe_div(A, B, Default) -> case B =:= +0.0 of true -> Default; false -> case B of +0.0 -> +0.0; -0.0 -> -0.0; Gleam@denominator -> A / Gleam@denominator end end. -file("src/viva_math/common.gleam", 164). ?DOC( " Smooth step function (Hermite interpolation).\n" " Returns 0 if x < edge0, 1 if x > edge1, smooth transition otherwise.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " smoothstep(0.0, 1.0, 0.5) // -> 0.5 (roughly)\n" " smoothstep(0.0, 1.0, 0.0) // -> 0.0\n" " smoothstep(0.0, 1.0, 1.0) // -> 1.0\n" " ```\n" ). -spec smoothstep(float(), float(), float()) -> float(). smoothstep(Edge0, Edge1, X) -> T = clamp_unit(case (Edge1 - Edge0) of +0.0 -> +0.0; -0.0 -> -0.0; Gleam@denominator -> (X - Edge0) / Gleam@denominator end), (T * T) * (3.0 - (2.0 * T)). -file("src/viva_math/common.gleam", 177). ?DOC( " Exponential decay: value * exp(-rate * time)\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " exponential_decay(1.0, 0.5, 1.0) // -> ~0.606\n" " exponential_decay(1.0, 0.0, 10.0) // -> 1.0 (no decay)\n" " ```\n" ). -spec exponential_decay(float(), float(), float()) -> float(). exponential_decay(Value, Rate, Time) -> Neg_rt = +0.0 - (Rate * Time), Value * gleam_community@maths:exponential(Neg_rt). -file("src/viva_math/common.gleam", 237). ?DOC( " Inverse decay rate (pattern from viva_glyph/association.gleam).\n" " η(t) = η₀ / (1 + t/τ)\n" "\n" " Used for learning rate decay, consolidation, etc.\n" ). -spec inverse_decay(float(), float(), float()) -> float(). inverse_decay(Base_rate, T, Tau) -> case Tau =< +0.0 of true -> Base_rate; false -> case (1.0 + (case Tau of +0.0 -> +0.0; -0.0 -> -0.0; Gleam@denominator -> T / Gleam@denominator end)) of +0.0 -> +0.0; -0.0 -> -0.0; Gleam@denominator@1 -> Base_rate / Gleam@denominator@1 end end. -file("src/viva_math/common.gleam", 246). ?DOC( " Inverse square root decay.\n" " η(t) = η₀ / √(1 + t/τ)\n" ). -spec inverse_sqrt_decay(float(), float(), float()) -> float(). inverse_sqrt_decay(Base_rate, T, Tau) -> case Tau =< +0.0 of true -> Base_rate; false -> case gleam_community@maths:nth_root(1.0 + (case Tau of +0.0 -> +0.0; -0.0 -> -0.0; Gleam@denominator -> T / Gleam@denominator end), 2) of {ok, Sqrt_val} -> case Sqrt_val of +0.0 -> +0.0; -0.0 -> -0.0; Gleam@denominator@1 -> Base_rate / Gleam@denominator@1 end; {error, _} -> Base_rate end end. -file("src/viva_math/common.gleam", 259). -spec int_to_float(integer()) -> float(). int_to_float(N) -> case N of 0 -> +0.0; 1 -> 1.0; _ -> case N < 0 of true -> +0.0 - int_to_float(0 - N); false -> Half = N div 2, Remainder = N - (Half * 2), (int_to_float(Half) * 2.0) + int_to_float(Remainder) end end. -file("src/viva_math/common.gleam", 204). ?DOC( " Deterministic pseudo-random noise generator.\n" " Uses hash-based seeding for reproducibility (no external RNG needed).\n" "\n" " Returns value in [-1, 1] range.\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " deterministic_noise(0, 42) // -> some value in [-1, 1]\n" " deterministic_noise(0, 42) // -> same value (deterministic)\n" " ```\n" ). -spec deterministic_noise(integer(), integer()) -> float(). deterministic_noise(Step, Seed) -> Hash = (((Step * 31) + (Seed * 17)) + 7919) rem 1000, (int_to_float(Hash) - 500.0) / 500.0. -file("src/viva_math/common.gleam", 225). ?DOC( " Generate Wiener process increment (discrete approximation).\n" " dW ≈ √dt × N(0,1) where N is approximated by deterministic noise.\n" "\n" " Used for stochastic differential equations.\n" "\n" " ## Parameters\n" " - step: Current time step (for determinism)\n" " - seed: Random seed\n" " - dt: Time step size\n" "\n" " ## Examples\n" "\n" " ```gleam\n" " wiener_increment(0, 42, 0.01) // -> small noise scaled by √dt\n" " ```\n" ). -spec wiener_increment(integer(), integer(), float()) -> float(). wiener_increment(Step, Seed, Dt) -> Noise = deterministic_noise(Step, Seed), case gleam_community@maths:nth_root(Dt, 2) of {ok, Sqrt_dt} -> Noise * Sqrt_dt; {error, _} -> +0.0 end.