-module(glib@map). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]). -export([size/1, is_empty/1, keys/1, values/1, entries/1, to_string/2, get/2, contains_key/2, remove/2, list_size/1, full_count/1, new_with_size_and_load/2, new_with_size/1, new/0, clear/1, put/3]). -export_type([entry/1, map_/1, rehash_data/1]). -type entry(GBH) :: {entry, binary(), GBH}. -opaque map_(GBI) :: {map, list(gleam@option:option(entry(GBI))), integer(), integer(), integer()}. -type rehash_data(GBJ) :: {rehash_data, list(gleam@option:option(entry(GBJ))), list({{integer(), integer()}, entry(GBJ)}), integer(), integer()}. -spec size(map_(any())) -> integer(). size(Map) -> erlang:element(5, Map). -spec is_empty(map_(any())) -> boolean(). is_empty(Map) -> size(Map) =:= 0. -spec get_at(list(GBX), integer()) -> GBX. get_at(List, Pos) -> _assert_subject = erlang:element(2, gleam@list:split(List, Pos)), [R | _] = case _assert_subject of [_ | _] -> _assert_subject; _assert_fail -> erlang:error(#{gleam_error => let_assert, message => <<"Assertion pattern match failed"/utf8>>, value => _assert_fail, module => <<"glib/map"/utf8>>, function => <<"get_at"/utf8>>, line => 110}) end, R. -spec keys(map_(any())) -> list(binary()). keys(Map) -> gleam@list:filter_map(erlang:element(2, Map), fun(E) -> case E of none -> {error, nil}; {some, En} -> {ok, erlang:element(2, En)} end end). -spec values(map_(GCO)) -> list(GCO). values(Map) -> gleam@list:filter_map(erlang:element(2, Map), fun(E) -> case E of none -> {error, nil}; {some, En} -> {ok, erlang:element(3, En)} end end). -spec entries(map_(GCR)) -> list({binary(), GCR}). entries(Map) -> gleam@list:filter_map(erlang:element(2, Map), fun(E) -> case E of none -> {error, nil}; {some, En} -> {ok, {erlang:element(2, En), erlang:element(3, En)}} end end). -spec to_string(map_(GCU), fun((GCU) -> binary())) -> binary(). to_string(Map, Value_to_string) -> <<<<"{"/utf8, (gleam@string:join( gleam@list:filter_map( erlang:element(2, Map), fun(Opt) -> case Opt of none -> {error, Opt}; {some, E} -> {ok, <<<<<<<<"\""/utf8, (erlang:element(2, E))/binary>>/binary, "\""/utf8>>/binary, ":"/utf8>>/binary, (Value_to_string(erlang:element(3, E)))/binary>>} end end ), <<","/utf8>> ))/binary>>/binary, "}"/utf8>>. -spec insert_at( list(gleam@option:option(entry(GCW))), integer(), gleam@option:option(entry(GCW)) ) -> list(gleam@option:option(entry(GCW))). insert_at(Map_list, At, Entry) -> {Split_left, Split_right} = gleam@list:split(Map_list, At), gleam@list:concat([Split_left, [Entry], case Split_right of [] -> []; [_ | Right] -> Right end]). -spec do_remove(map_(GDF), integer(), GDF) -> {gleam@option:option(GDF), map_(GDF)}. do_remove(Map, Index, Value) -> New_map = {map, insert_at(erlang:element(2, Map), Index, none), erlang:element(3, Map), erlang:element(4, Map), erlang:element(5, Map) - 1}, {{some, Value}, New_map}. -spec find_gap(map_(any()), binary(), integer(), integer()) -> {integer(), boolean()}. find_gap(Map, Key, Last_position, Position) -> case get_at(erlang:element(2, Map), Position) of none -> {Position, false}; {some, E} -> case erlang:element(2, E) =:= Key of true -> {Position, true}; false -> case Position of Position@1 when Position@1 =:= Last_position -> {-1, false}; 0 -> find_gap( Map, Key, Last_position, erlang:element(3, Map) - 1 ); Position@2 -> find_gap(Map, Key, Last_position, Position@2 - 1) end end end. -spec find_key( map_(GDP), binary(), integer(), integer(), fun((integer(), GDP) -> GDR) ) -> gleam@option:option(GDR). find_key(Map, Key, Last_position, Position, Ret_fn) -> case get_at(erlang:element(2, Map), Position) of none -> none; {some, E} -> case erlang:element(2, E) =:= Key of true -> {some, Ret_fn(Position, erlang:element(3, E))}; false -> case Position of Position@1 when Position@1 =:= Last_position -> none; 0 -> find_key( Map, Key, Last_position, erlang:element(3, Map) - 1, Ret_fn ); Position@2 -> find_key( Map, Key, Last_position, Position@2 - 1, Ret_fn ) end end end. -spec ret_value(integer(), GDT) -> GDT. ret_value(_, Value) -> Value. -spec ret_exists(integer(), any()) -> boolean(). ret_exists(_, _) -> true. -spec ret_index_and_value(integer(), GDV) -> {integer(), GDV}. ret_index_and_value(Index, Value) -> {Index, Value}. -spec prepend_none(integer(), list(gleam@option:option(entry(GDZ)))) -> list(gleam@option:option(entry(GDZ))). prepend_none(Times, Acc) -> case Times =< 0 of true -> Acc; false -> prepend_none(Times - 1, [none | Acc]) end. -spec do_repeat(GEE, integer(), list(GEE)) -> list(GEE). do_repeat(A, Times, Acc) -> case Times =< 0 of true -> Acc; false -> do_repeat(A, Times, [A | Acc]) end. -spec fix_hash(integer(), integer()) -> integer(). fix_hash(Map_size, Hash) -> case Map_size of 0 -> 0; Gleam@denominator -> begin _pipe = Hash, gleam@int:absolute_value(_pipe) end rem Gleam@denominator end. -spec calc_hash(integer(), binary()) -> {integer(), integer()}. calc_hash(Map_size, Key) -> Hash_value = glib@hash:hash(Key), {fix_hash(Map_size, Hash_value), Hash_value}. -spec get(map_(GCC), binary()) -> gleam@option:option(GCC). get(Map, Key) -> {Hash, _} = calc_hash(erlang:element(3, Map), Key), case get_at(erlang:element(2, Map), Hash) of none -> none; {some, E} -> case erlang:element(2, E) =:= Key of true -> {some, erlang:element(3, E)}; false -> find_key(Map, Key, case erlang:element(3, Map) of 0 -> 0; Gleam@denominator -> (Hash + 1) rem Gleam@denominator end, Hash, fun ret_value/2) end end. -spec contains_key(map_(any()), binary()) -> boolean(). contains_key(Map, Key) -> {Hash, _} = calc_hash(erlang:element(3, Map), Key), case get_at(erlang:element(2, Map), Hash) of none -> false; {some, E} -> case erlang:element(2, E) =:= Key of true -> true; false -> gleam@option:unwrap( find_key(Map, Key, case erlang:element(3, Map) of 0 -> 0; Gleam@denominator -> (Hash + 1) rem Gleam@denominator end, Hash, fun ret_exists/2), false ) end end. -spec remove(map_(GCH), binary()) -> {gleam@option:option(GCH), map_(GCH)}. remove(Map, Key) -> {Hash, _} = calc_hash(erlang:element(3, Map), Key), case get_at(erlang:element(2, Map), Hash) of none -> {none, Map}; {some, E} -> case erlang:element(2, E) =:= Key of true -> do_remove(Map, Hash, erlang:element(3, E)); false -> Item = find_key(Map, Key, case erlang:element(3, Map) of 0 -> 0; Gleam@denominator -> (Hash + 1) rem Gleam@denominator end, Hash, fun ret_index_and_value/2), case Item of none -> {none, Map}; {some, {Index, Value}} -> do_remove(Map, Index, Value) end end end. -spec list_size(map_(any())) -> integer(). list_size(Map) -> erlang:length(erlang:element(2, Map)). -spec full_count(map_(any())) -> integer(). full_count(Map) -> gleam@list:fold(erlang:element(2, Map), 0, fun(Acc, E) -> case E of none -> Acc; {some, _} -> Acc + 1 end end). -spec new_with_size_and_load(integer(), float()) -> map_(any()). new_with_size_and_load(Size, Load) -> Load@1 = case (Load >= 1.0) orelse (Load < +0.0) of true -> 0.75; false -> Load end, Size@1 = case Size < 1 of true -> 1; false -> Size end, {map, gleam@list:repeat(none, Size@1), Size@1, gleam@float:round(Load@1 * 100.0), 0}. -spec new_with_size(integer()) -> map_(any()). new_with_size(Size) -> new_with_size_and_load(Size, 0.75). -spec new() -> map_(any()). new() -> new_with_size(11). -spec clear(map_(GBQ)) -> map_(GBQ). clear(Previous_map) -> new_with_size_and_load( erlang:element(3, Previous_map), gleam@int:to_float(erlang:element(4, Previous_map)) / 100.0 ). -spec optimised_rehash(map_(GDW), integer()) -> map_(GDW). optimised_rehash(Map, New_size) -> Entries = begin _pipe = gleam@list:fold( erlang:element(2, Map), [], fun(Acc, En) -> case En of {some, Entry} -> [{calc_hash(New_size, erlang:element(2, Entry)), Entry} | Acc]; none -> Acc end end ), gleam@list:sort( _pipe, fun(I1, I2) -> {{Hash1, _}, _} = I1, {{Hash2, _}, _} = I2, gleam@int:compare(Hash2, Hash1) end ) end, Proc_list = gleam@list:fold( Entries, {rehash_data, [], [], New_size, 0}, fun(Acc@1, En@1) -> {{Hash, _}, _} = En@1, case erlang:element(4, Acc@1) =:= Hash of true -> erlang:setelement( 3, Acc@1, [En@1 | erlang:element(3, Acc@1)] ); false -> {rehash_data, [{some, erlang:element(2, En@1)} | prepend_none( (erlang:element(4, Acc@1) - Hash) - 1, erlang:element(2, Acc@1) )], erlang:element(3, Acc@1), Hash, erlang:element(5, Acc@1) + 1} end end ), It = case erlang:element(4, Proc_list) =:= 0 of true -> gleam@iterator:empty(); false -> gleam@iterator:range(erlang:element(4, Proc_list) - 1, 0) end, Res_list = gleam@iterator:fold( It, Proc_list, fun(Acc@2, _) -> erlang:setelement(2, Acc@2, [none | erlang:element(2, Acc@2)]) end ), _pipe@1 = erlang:element(3, Res_list), gleam@list:fold( _pipe@1, {map, erlang:element(2, Res_list), New_size, erlang:element(4, Map), erlang:element(5, Res_list)}, fun(Acc@3, En@2) -> Entry@1 = erlang:element(2, En@2), put(Acc@3, erlang:element(2, Entry@1), erlang:element(3, Entry@1)) end ). -spec put(map_(GBZ), binary(), GBZ) -> map_(GBZ). put(Map, Key, Value) -> {Hash, Original_hash} = calc_hash(erlang:element(3, Map), Key), case get_at(erlang:element(2, Map), Hash) of {some, E} when erlang:element(2, E) =:= Key -> {map, insert_at( erlang:element(2, Map), Hash, {some, {entry, Key, Value}} ), erlang:element(3, Map), erlang:element(4, Map), erlang:element(5, Map)}; _ -> {Map@1, New_hash} = check_capacity(Map, Original_hash), New_hash@1 = gleam@option:unwrap(New_hash, Hash), {Position, Overwrite} = find_gap( Map@1, Key, case erlang:element(3, Map@1) of 0 -> 0; Gleam@denominator -> (New_hash@1 + 1) rem Gleam@denominator end, New_hash@1 ), erlang:setelement( 5, erlang:setelement( 2, Map@1, insert_at( erlang:element(2, Map@1), Position, {some, {entry, Key, Value}} ) ), case Overwrite of true -> erlang:element(5, Map@1); false -> erlang:element(5, Map@1) + 1 end ) end. -spec check_capacity(map_(GDJ), integer()) -> {map_(GDJ), gleam@option:option(integer())}. check_capacity(Map, Original_hash) -> case erlang:element(5, Map) >= ((erlang:element(3, Map) * erlang:element( 4, Map )) div 100) of true -> New_map = optimised_rehash(Map, (erlang:element(3, Map) * 2) + 1), {New_map, {some, fix_hash(erlang:element(3, New_map), Original_hash)}}; _ -> {Map, none} end.