src/qrkit@internal@mask.erl

-module(qrkit@internal@mask).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/qrkit/internal/mask.gleam").
-export([penalty_n4/1, mask_at/3, apply/2, penalty_n1/1, penalty_n2/1, penalty_n3/1, penalty_total/1, best_mask/1]).

-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/mask.gleam", 112).
?DOC(false).
-spec run_penalty(integer()) -> integer().
run_penalty(Run_length) ->
    case Run_length >= 5 of
        true ->
            (3 + Run_length) - 5;

        false ->
            0
    end.

-file("src/qrkit/internal/mask.gleam", 95).
?DOC(false).
-spec line_penalty_loop(list(boolean()), boolean(), integer(), integer()) -> integer().
line_penalty_loop(Values, Last, Run_length, Penalty) ->
    case Values of
        [] ->
            Penalty + run_penalty(Run_length);

        [Value | Rest] ->
            case Value =:= Last of
                true ->
                    line_penalty_loop(Rest, Last, Run_length + 1, Penalty);

                false ->
                    line_penalty_loop(
                        Rest,
                        Value,
                        1,
                        Penalty + run_penalty(Run_length)
                    )
            end
    end.

-file("src/qrkit/internal/mask.gleam", 88).
?DOC(false).
-spec line_penalty(list(boolean())) -> integer().
line_penalty(Values) ->
    case Values of
        [] ->
            0;

        [First | Rest] ->
            line_penalty_loop(Rest, First, 1, 0)
    end.

-file("src/qrkit/internal/mask.gleam", 187).
?DOC(false).
-spec penalty_n4(qrkit@internal@matrix:matrix()) -> integer().
penalty_n4(Target) ->
    Dark = qrkit@internal@matrix:dark_count(Target),
    Total = qrkit@internal@matrix:total_cells(Target),
    Percentage = case Total of
        0 -> 0;
        Gleam@denominator -> Dark * 100 div Gleam@denominator
    end,
    Deviation = Percentage - 50,
    Absolute = case Deviation < 0 of
        true ->
            0 - Deviation;

        false ->
            Deviation
    end,
    Lower_bucket = Absolute div 5,
    Lower_bucket * 10.

-file("src/qrkit/internal/mask.gleam", 200).
?DOC(false).
-spec mask_at(integer(), integer(), integer()) -> boolean().
mask_at(Mask, Row, Col) ->
    case Mask of
        0 ->
            ((Row + Col) rem 2) =:= 0;

        1 ->
            (Row rem 2) =:= 0;

        2 ->
            (Col rem 3) =:= 0;

        3 ->
            ((Row + Col) rem 3) =:= 0;

        4 ->
            (((Row div 2) + (Col div 3)) rem 2) =:= 0;

        5 ->
            (((Row * Col) rem 2) + ((Row * Col) rem 3)) =:= 0;

        6 ->
            ((((Row * Col) rem 2) + ((Row * Col) rem 3)) rem 2) =:= 0;

        _ ->
            ((((Row * Col) rem 3) + ((Row + Col) rem 2)) rem 2) =:= 0
    end.

-file("src/qrkit/internal/mask.gleam", 11).
?DOC(false).
-spec do_apply(integer(), qrkit@internal@matrix:matrix(), integer(), integer()) -> qrkit@internal@matrix:matrix().
do_apply(Mask, Target, Row, Col) ->
    case Row >= qrkit@internal@matrix:height(Target) of
        true ->
            Target;

        false ->
            case Col >= qrkit@internal@matrix:width(Target) of
                true ->
                    do_apply(Mask, Target, Row + 1, 0);

                false ->
                    case qrkit@internal@matrix:is_reserved(Target, Row, Col) of
                        true ->
                            do_apply(Mask, Target, Row, Col + 1);

                        false ->
                            do_apply(
                                Mask,
                                qrkit@internal@matrix:'xor'(
                                    Target,
                                    Row,
                                    Col,
                                    mask_at(Mask, Row, Col)
                                ),
                                Row,
                                Col + 1
                            )
                    end
            end
    end.

-file("src/qrkit/internal/mask.gleam", 7).
?DOC(false).
-spec apply(integer(), qrkit@internal@matrix:matrix()) -> qrkit@internal@matrix:matrix().
apply(Mask, Target) ->
    do_apply(Mask, Target, 0, 0).

-file("src/qrkit/internal/mask.gleam", 213).
?DOC(false).
-spec row_values(qrkit@internal@matrix:matrix(), integer()) -> list(boolean()).
row_values(Target, Row) ->
    _pipe = qrkit@internal@util:range(
        0,
        qrkit@internal@matrix:width(Target) - 1
    ),
    gleam@list:map(
        _pipe,
        fun(Col) -> qrkit@internal@matrix:get(Target, Row, Col) end
    ).

-file("src/qrkit/internal/mask.gleam", 218).
?DOC(false).
-spec column_values(qrkit@internal@matrix:matrix(), integer()) -> list(boolean()).
column_values(Target, Col) ->
    _pipe = qrkit@internal@util:range(
        0,
        qrkit@internal@matrix:height(Target) - 1
    ),
    gleam@list:map(
        _pipe,
        fun(Row) -> qrkit@internal@matrix:get(Target, Row, Col) end
    ).

-file("src/qrkit/internal/mask.gleam", 70).
?DOC(false).
-spec penalty_runs(
    qrkit@internal@matrix:matrix(),
    integer(),
    integer(),
    integer()
) -> integer().
penalty_runs(Target, Row, Col_points, Row_points) ->
    case Row >= qrkit@internal@matrix:height(Target) of
        true ->
            Col_points + Row_points;

        false ->
            penalty_runs(
                Target,
                Row + 1,
                Col_points + line_penalty(column_values(Target, Row)),
                Row_points + line_penalty(row_values(Target, Row))
            )
    end.

-file("src/qrkit/internal/mask.gleam", 66).
?DOC(false).
-spec penalty_n1(qrkit@internal@matrix:matrix()) -> integer().
penalty_n1(Target) ->
    penalty_runs(Target, 0, 0, 0).

-file("src/qrkit/internal/mask.gleam", 223).
?DOC(false).
-spec bool_to_int(boolean()) -> integer().
bool_to_int(Value) ->
    case Value of
        true ->
            1;

        false ->
            0
    end.

-file("src/qrkit/internal/mask.gleam", 123).
?DOC(false).
-spec do_penalty_n2(
    qrkit@internal@matrix:matrix(),
    integer(),
    integer(),
    integer()
) -> integer().
do_penalty_n2(Target, Row, Col, Acc) ->
    case Row >= (qrkit@internal@matrix:height(Target) - 1) of
        true ->
            Acc * 3;

        false ->
            case Col >= (qrkit@internal@matrix:width(Target) - 1) of
                true ->
                    do_penalty_n2(Target, Row + 1, 0, Acc);

                false ->
                    Dark_total = ((bool_to_int(
                        qrkit@internal@matrix:get(Target, Row, Col)
                    )
                    + bool_to_int(
                        qrkit@internal@matrix:get(Target, Row, Col + 1)
                    ))
                    + bool_to_int(
                        qrkit@internal@matrix:get(Target, Row + 1, Col)
                    ))
                    + bool_to_int(
                        qrkit@internal@matrix:get(Target, Row + 1, Col + 1)
                    ),
                    Next_acc = case (Dark_total =:= 0) orelse (Dark_total =:= 4) of
                        true ->
                            Acc + 1;

                        false ->
                            Acc
                    end,
                    do_penalty_n2(Target, Row, Col + 1, Next_acc)
            end
    end.

-file("src/qrkit/internal/mask.gleam", 119).
?DOC(false).
-spec penalty_n2(qrkit@internal@matrix:matrix()) -> integer().
penalty_n2(Target) ->
    do_penalty_n2(Target, 0, 0, 0).

-file("src/qrkit/internal/mask.gleam", 178).
?DOC(false).
-spec take_bits(list(boolean()), integer(), integer()) -> integer().
take_bits(Values, Count, Acc) ->
    case {Values, Count} of
        {_, 0} ->
            Acc;

        {[Value | Rest], _} ->
            take_bits(Rest, Count - 1, (Acc * 2) + bool_to_int(Value));

        {[], _} ->
            Acc
    end.

-file("src/qrkit/internal/mask.gleam", 159).
?DOC(false).
-spec finder_like_penalty(list(boolean())) -> integer().
finder_like_penalty(Values) ->
    case Values of
        [] ->
            0;

        _ ->
            case erlang:length(Values) < 11 of
                true ->
                    0;

                false ->
                    Bits = take_bits(Values, 11, 0),
                    Rest = gleam@list:drop(Values, 1),
                    Points = case (Bits =:= 16#5D0) orelse (Bits =:= 16#05D) of
                        true ->
                            1;

                        false ->
                            0
                    end,
                    Points + finder_like_penalty(Rest)
            end
    end.

-file("src/qrkit/internal/mask.gleam", 145).
?DOC(false).
-spec penalty_n3(qrkit@internal@matrix:matrix()) -> integer().
penalty_n3(Target) ->
    Rows = begin
        _pipe = qrkit@internal@util:range(
            0,
            qrkit@internal@matrix:height(Target) - 1
        ),
        gleam@list:fold(
            _pipe,
            0,
            fun(Acc, Row) ->
                Acc + finder_like_penalty(row_values(Target, Row))
            end
        )
    end,
    Columns = begin
        _pipe@1 = qrkit@internal@util:range(
            0,
            qrkit@internal@matrix:width(Target) - 1
        ),
        gleam@list:fold(
            _pipe@1,
            0,
            fun(Acc@1, Col) ->
                Acc@1 + finder_like_penalty(column_values(Target, Col))
            end
        )
    end,
    (Rows + Columns) * 40.

-file("src/qrkit/internal/mask.gleam", 59).
?DOC(false).
-spec penalty_total(qrkit@internal@matrix:matrix()) -> integer().
penalty_total(Target) ->
    ((penalty_n1(Target) + penalty_n2(Target)) + penalty_n3(Target)) + penalty_n4(
        Target
    ).

-file("src/qrkit/internal/mask.gleam", 41).
?DOC(false).
-spec do_best_mask(
    qrkit@internal@matrix:matrix(),
    integer(),
    integer(),
    integer()
) -> integer().
do_best_mask(Target, Mask, Best_mask, Best_penalty) ->
    case Mask > 7 of
        true ->
            Best_mask;

        false ->
            Penalty = penalty_total(apply(Mask, Target)),
            case (Mask =:= 0) orelse (Penalty < Best_penalty) of
                true ->
                    do_best_mask(Target, Mask + 1, Mask, Penalty);

                false ->
                    do_best_mask(Target, Mask + 1, Best_mask, Best_penalty)
            end
    end.

-file("src/qrkit/internal/mask.gleam", 37).
?DOC(false).
-spec best_mask(qrkit@internal@matrix:matrix()) -> integer().
best_mask(Target) ->
    do_best_mask(Target, 0, 0, 0).