-module(svg_path@root).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/svg_path/root.gleam").
-export([default_options/0, bisect_with/4, bisect/3]).
-export_type([options/0, error/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(
" Bracketed root-finding helpers for scalar functions.\n"
"\n"
" This module intentionally keeps the numerical method small and explicit.\n"
" `bisect` implements the standard bisection method: for a continuous\n"
" function `f` and a bracket `[a, b]` where `f(a)` and `f(b)` have opposite\n"
" signs, repeatedly halve the interval until the root estimate is within the\n"
" requested tolerance.\n"
).
-type options() :: {options, float(), integer()}.
-type error() :: {invalid_tolerance, float()} |
{invalid_max_iterations, integer()} |
{not_bracketed, float(), float(), float(), float()} |
{max_iterations_reached, float(), float()}.
-file("src/svg_path/root.gleam", 36).
?DOC(" Return the default bisection options.\n").
-spec default_options() -> options().
default_options() ->
{options, 0.000000001, 100}.
-file("src/svg_path/root.gleam", 160).
-spec same_sign(float(), float()) -> boolean().
same_sign(A, B) ->
((A < +0.0) andalso (B < +0.0)) orelse ((A > +0.0) andalso (B > +0.0)).
-file("src/svg_path/root.gleam", 156).
-spec is_close_to_zero(float(), float()) -> boolean().
is_close_to_zero(Value, Tolerance) ->
gleam@float:absolute_value(Value) =< Tolerance.
-file("src/svg_path/root.gleam", 103).
-spec bisect_loop(
fun((float()) -> float()),
float(),
float(),
float(),
float(),
integer()
) -> {ok, float()} | {error, error()}.
bisect_loop(F, Left, Left_value, Right, Tolerance, Remaining_iterations) ->
Midpoint = Left + ((Right - Left) / 2.0),
Midpoint_value = F(Midpoint),
case is_close_to_zero(Midpoint_value, Tolerance) orelse (((Right - Left) / 2.0)
=< Tolerance) of
true ->
{ok, Midpoint};
false ->
case Remaining_iterations =< 1 of
true ->
{error, {max_iterations_reached, Midpoint, Midpoint_value}};
false ->
case same_sign(Left_value, Midpoint_value) of
true ->
bisect_loop(
F,
Midpoint,
Midpoint_value,
Right,
Tolerance,
Remaining_iterations - 1
);
false ->
bisect_loop(
F,
Left,
Left_value,
Midpoint,
Tolerance,
Remaining_iterations - 1
)
end
end
end.
-file("src/svg_path/root.gleam", 149).
-spec ordered_bracket(float(), float()) -> {float(), float()}.
ordered_bracket(Left, Right) ->
case Left =< Right of
true ->
{Left, Right};
false ->
{Right, Left}
end.
-file("src/svg_path/root.gleam", 58).
?DOC(
" Find a root of `f` in a bracket using explicit options.\n"
"\n"
" The returned value is an approximation. Convergence succeeds when either\n"
" `abs(f(estimate)) <= tolerance` or the current bracket width is no larger\n"
" than `tolerance`.\n"
).
-spec bisect_with(fun((float()) -> float()), float(), float(), options()) -> {ok,
float()} |
{error, error()}.
bisect_with(F, Left, Right, Options) ->
case erlang:element(2, Options) =< +0.0 of
true ->
{error, {invalid_tolerance, erlang:element(2, Options)}};
false ->
case erlang:element(3, Options) =< 0 of
true ->
{error,
{invalid_max_iterations, erlang:element(3, Options)}};
false ->
{Left@1, Right@1} = ordered_bracket(Left, Right),
Left_value = F(Left@1),
Right_value = F(Right@1),
case is_close_to_zero(
Left_value,
erlang:element(2, Options)
) of
true ->
{ok, Left@1};
false ->
case is_close_to_zero(
Right_value,
erlang:element(2, Options)
) of
true ->
{ok, Right@1};
false ->
case same_sign(Left_value, Right_value) of
true ->
{error,
{not_bracketed,
Left@1,
Right@1,
Left_value,
Right_value}};
false ->
bisect_loop(
F,
Left@1,
Left_value,
Right@1,
erlang:element(2, Options),
erlang:element(3, Options)
)
end
end
end
end
end.
-file("src/svg_path/root.gleam", 45).
?DOC(
" Find a root of `f` in a bracket using default options.\n"
"\n"
" `f(from)` and `f(to)` must have opposite signs, unless either endpoint is\n"
" already within tolerance of zero. `from` may be greater than `to`; the\n"
" bracket is normalized before solving.\n"
).
-spec bisect(fun((float()) -> float()), float(), float()) -> {ok, float()} |
{error, error()}.
bisect(F, Left, Right) ->
bisect_with(F, Left, Right, default_options()).