src/qrkit@internal@reed_solomon.erl

-module(qrkit@internal@reed_solomon).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/qrkit/internal/reed_solomon.gleam").
-export([gf_exp/1, gf_multiply/2, generator_polynomial/1, encode/2]).

-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(false).

-file("src/qrkit/internal/reed_solomon.gleam", 38).
?DOC(false).
-spec normalise_exponent(integer()) -> integer().
normalise_exponent(Exponent) ->
    case Exponent < 0 of
        true ->
            normalise_exponent(Exponent + 255);

        false ->
            case Exponent >= 255 of
                true ->
                    normalise_exponent(Exponent - 255);

                false ->
                    Exponent
            end
    end.

-file("src/qrkit/internal/reed_solomon.gleam", 49).
?DOC(false).
-spec gf_exp_loop(integer(), integer()) -> integer().
gf_exp_loop(Exponent, Value) ->
    gleam@bool:guard(
        Exponent =< 0,
        Value,
        fun() ->
            Shifted = erlang:'bsl'(Value, 1),
            Reduced = case erlang:'band'(Shifted, 16#100) /= 0 of
                true ->
                    erlang:'bxor'(Shifted, 16#11D);

                false ->
                    Shifted
            end,
            gf_exp_loop(Exponent - 1, erlang:'band'(Reduced, 16#FF))
        end
    ).

-file("src/qrkit/internal/reed_solomon.gleam", 34).
?DOC(false).
-spec gf_exp(integer()) -> integer().
gf_exp(Exponent) ->
    gf_exp_loop(normalise_exponent(Exponent), 1).

-file("src/qrkit/internal/reed_solomon.gleam", 59).
?DOC(false).
-spec gf_multiply_loop(integer(), integer(), integer()) -> integer().
gf_multiply_loop(A, B, Acc) ->
    gleam@bool:guard(
        B =:= 0,
        Acc,
        fun() ->
            Next_acc = case erlang:'band'(B, 1) =:= 1 of
                true ->
                    erlang:'bxor'(Acc, A);

                false ->
                    Acc
            end,
            Shifted = erlang:'bsl'(A, 1),
            Next_a = case erlang:'band'(Shifted, 16#100) /= 0 of
                true ->
                    erlang:'bxor'(Shifted, 16#11D);

                false ->
                    Shifted
            end,
            gf_multiply_loop(
                erlang:'band'(Next_a, 16#FF),
                erlang:'bsr'(B, 1),
                Next_acc
            )
        end
    ).

-file("src/qrkit/internal/reed_solomon.gleam", 30).
?DOC(false).
-spec gf_multiply(integer(), integer()) -> integer().
gf_multiply(A, B) ->
    gf_multiply_loop(A, B, 0).

-file("src/qrkit/internal/reed_solomon.gleam", 97).
?DOC(false).
-spec do_poly_row(
    list(integer()),
    integer(),
    integer(),
    integer(),
    list(integer())
) -> list(integer()).
do_poly_row(Right, Left_coefficient, Left_index, Right_index, Acc) ->
    case Right of
        [] ->
            Acc;

        [Coefficient | Rest] ->
            Index = Left_index + Right_index,
            Previous = qrkit@internal@util:at_or(Acc, Index, 0),
            Value = erlang:'bxor'(
                Previous,
                gf_multiply(Left_coefficient, Coefficient)
            ),
            do_poly_row(
                Rest,
                Left_coefficient,
                Left_index,
                Right_index + 1,
                qrkit@internal@util:replace_at(Acc, Index, Value)
            )
    end.

-file("src/qrkit/internal/reed_solomon.gleam", 82).
?DOC(false).
-spec do_poly_multiply(
    list(integer()),
    list(integer()),
    integer(),
    list(integer())
) -> list(integer()).
do_poly_multiply(Left, Right, Left_index, Acc) ->
    case Left of
        [] ->
            Acc;

        [Coefficient | Rest] ->
            Next = do_poly_row(Right, Coefficient, Left_index, 0, Acc),
            do_poly_multiply(Rest, Right, Left_index + 1, Next)
    end.

-file("src/qrkit/internal/reed_solomon.gleam", 77).
?DOC(false).
-spec poly_multiply(list(integer()), list(integer())) -> list(integer()).
poly_multiply(Left, Right) ->
    Size = (erlang:length(Left) + erlang:length(Right)) - 1,
    do_poly_multiply(Left, Right, 0, qrkit@internal@util:repeat(0, Size)).

-file("src/qrkit/internal/reed_solomon.gleam", 13).
?DOC(false).
-spec do_generator_polynomial(integer(), list(integer())) -> list(integer()).
do_generator_polynomial(Degree, Polynomial) ->
    gleam@bool:guard(
        Degree =< 0,
        Polynomial,
        fun() ->
            Exponent = erlang:length(Polynomial) - 1,
            Factor = [1, gf_exp(Exponent)],
            do_generator_polynomial(
                Degree - 1,
                poly_multiply(Polynomial, Factor)
            )
        end
    ).

-file("src/qrkit/internal/reed_solomon.gleam", 9).
?DOC(false).
-spec generator_polynomial(integer()) -> list(integer()).
generator_polynomial(Degree) ->
    do_generator_polynomial(Degree, [1]).

-file("src/qrkit/internal/reed_solomon.gleam", 140).
?DOC(false).
-spec do_poly_mod_step(
    list(integer()),
    list(integer()),
    integer(),
    integer(),
    list(integer())
) -> list(integer()).
do_poly_mod_step(Dividend, Divisor, Lead, Index, Acc) ->
    case {Dividend, Divisor} of
        {[Left | Left_rest], [Right | Right_rest]} ->
            Value = erlang:'bxor'(Left, gf_multiply(Right, Lead)),
            do_poly_mod_step(
                Left_rest,
                Right_rest,
                Lead,
                Index + 1,
                [Value | Acc]
            );

        {Remaining, []} ->
            _pipe = lists:reverse(Acc),
            lists:append(_pipe, Remaining);

        {[], _} ->
            lists:reverse(Acc)
    end.

-file("src/qrkit/internal/reed_solomon.gleam", 157).
?DOC(false).
-spec trim_leading_zeros(list(integer())) -> list(integer()).
trim_leading_zeros(Values) ->
    case Values of
        [0 | Rest] ->
            trim_leading_zeros(Rest);

        _ ->
            Values
    end.

-file("src/qrkit/internal/reed_solomon.gleam", 125).
?DOC(false).
-spec poly_mod(list(integer()), list(integer())) -> list(integer()).
poly_mod(Dividend, Divisor) ->
    case erlang:length(Dividend) < erlang:length(Divisor) of
        true ->
            trim_leading_zeros(Dividend);

        false ->
            case Dividend of
                [] ->
                    [];

                [Lead | _] ->
                    Reduced = do_poly_mod_step(Dividend, Divisor, Lead, 0, []),
                    poly_mod(trim_leading_zeros(Reduced), Divisor)
            end
    end.

-file("src/qrkit/internal/reed_solomon.gleam", 21).
?DOC(false).
-spec encode(list(integer()), integer()) -> list(integer()).
encode(Data, Degree) ->
    Generator = generator_polynomial(Degree),
    Padded = lists:append(Data, qrkit@internal@util:repeat(0, Degree)),
    Remainder = poly_mod(Padded, Generator),
    Padding = qrkit@internal@util:repeat(0, Degree - erlang:length(Remainder)),
    lists:append(Padding, Remainder).