-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(FOX) :: {entry, binary(), FOX}. -opaque map_(FOY) :: {map, list(gleam@option:option(entry(FOY))), integer(), integer(), integer()}. -type rehash_data(FOZ) :: {rehash_data, list(gleam@option:option(entry(FOZ))), list({{integer(), integer()}, entry(FOZ)}), 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 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_(FQC)) -> list(FQC). 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_(FQF)) -> list({binary(), FQF}). 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_(FQI), fun((FQI) -> 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(FQK))), integer(), gleam@option:option(entry(FQK)) ) -> list(gleam@option:option(entry(FQK))). 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_(FQT), integer(), FQT) -> {gleam@option:option(FQT), map_(FQT)}. 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 gleam@list:at(erlang:element(2, Map), Position) of {ok, none} -> {Position, false}; {error, nil} -> {Position, false}; {ok, {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_(FRD), binary(), integer(), integer(), fun((integer(), FRD) -> FRF) ) -> gleam@option:option(FRF). find_key(Map, Key, Last_position, Position, Ret_fn) -> case gleam@list:at(erlang:element(2, Map), Position) of {ok, none} -> none; {error, nil} -> none; {ok, {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(), FRH) -> FRH. ret_value(_, Value) -> Value. -spec ret_exists(integer(), any()) -> boolean(). ret_exists(_, _) -> true. -spec ret_index_and_value(integer(), FRJ) -> {integer(), FRJ}. ret_index_and_value(Index, Value) -> {Index, Value}. -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_(FPQ), binary()) -> gleam@option:option(FPQ). get(Map, Key) -> {Hash, _} = calc_hash(erlang:element(3, Map), Key), case gleam@list:at(erlang:element(2, Map), Hash) of {ok, none} -> none; {error, nil} -> none; {ok, {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 gleam@list:at(erlang:element(2, Map), Hash) of {ok, none} -> false; {error, nil} -> false; {ok, {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_(FPV), binary()) -> {gleam@option:option(FPV), map_(FPV)}. remove(Map, Key) -> {Hash, _} = calc_hash(erlang:element(3, Map), Key), case gleam@list:at(erlang:element(2, Map), Hash) of {ok, none} -> {none, Map}; {error, nil} -> {none, Map}; {ok, {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) -> gleam@list: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_(FPG)) -> map_(FPG). 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_(FRK), integer()) -> map_(FRK). 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) -> gleam@int:compare( erlang:element(1, (erlang:element(1, I2))), erlang:element(1, (erlang:element(1, I1))) ) end ) end, Proc_list = gleam@list:fold( Entries, {rehash_data, [], [], New_size, 0}, fun(Acc@1, En@1) -> case erlang:element(4, Acc@1) =:= erlang:element( 1, (erlang:element(1, En@1)) ) of true -> erlang:setelement( 3, Acc@1, [En@1 | erlang:element(3, Acc@1)] ); false -> {rehash_data, gleam@list:append( [{some, erlang:element(2, En@1)} | gleam@list:repeat( none, (erlang:element(4, Acc@1) - erlang:element( 1, (erlang:element(1, En@1)) )) - 1 )], erlang:element(2, Acc@1) ), erlang:element(3, Acc@1), erlang:element(1, (erlang:element(1, En@1))), 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_(FPN), binary(), FPN) -> map_(FPN). put(Map, Key, Value) -> {Hash, Original_hash} = calc_hash(erlang:element(3, Map), Key), case gleam@list:at(erlang:element(2, Map), Hash) of {ok, {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_(FQX), integer()) -> {map_(FQX), 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.