-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]). -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).