%% -*- Prolog -*- /* This code implements hash-arrays. It requires the hash key to be a ground term. It relies on dynamic array code. */ :- source. :- yap_flag(unknown,error). :- style_check(all). :- module(b_hash, [ b_hash_new/1, b_hash_new/2, b_hash_new/4, b_hash_lookup/3, b_hash_update/3, b_hash_update/4, b_hash_insert_new/4, b_hash_insert/4, b_hash_size/2, b_hash_code/2, is_b_hash/1, b_hash_to_list/2, b_hash_values_to_list/2, b_hash_keys_to_list/2 ]). :- use_module(library(terms), [ term_hash/4 ]). :- meta_predicate(b_hash_new(-,+,3,2)). array_default_size(2048). is_b_hash(V) :- var(V), !, fail. is_b_hash(hash(_,_,_,_,_)). b_hash_new(hash(Keys, Vals, Size, N, _, _)) :- array_default_size(Size), array(Keys, Size), array(Vals, Size), create_mutable(0, N). b_hash_new(hash(Keys, Vals, Size, N, _, _), Size) :- array(Keys, Size), array(Vals, Size), create_mutable(0, N). b_hash_new(hash(Keys,Vals, Size, N, HashF, CmpF), Size, HashF, CmpF) :- array(Keys, Size), array(Vals, Size), create_mutable(0, N). b_hash_size(hash(_, _, Size, _, _, _), Size). b_hash_lookup(Key, Val, hash(Keys, Vals, Size, _, F, CmpF)):- hash_f(Key, Size, Index, F), fetch_key(Keys, Index, Size, Key, CmpF, ActualIndex), array_element(Vals, ActualIndex, Mutable), get_mutable(Val, Mutable). fetch_key(Keys, Index, Size, Key, CmpF, ActualIndex) :- array_element(Keys, Index, El), nonvar(El), ( cmp_f(CmpF, El, Key) -> Index = ActualIndex ; I1 is (Index+1) mod Size, fetch_key(Keys, I1, Size, Key, CmpF, ActualIndex) ). b_hash_update(Hash, Key, NewVal):- Hash = hash(Keys, Vals, Size, _, F, CmpF), hash_f(Key,Size,Index,F), fetch_key(Keys, Index, Size, Key, CmpF, ActualIndex), array_element(Vals, ActualIndex, Mutable), update_mutable(NewVal, Mutable). b_hash_update(Hash, Key, OldVal, NewVal):- Hash = hash(Keys, Vals, Size, _, F, CmpF), hash_f(Key,Size,Index,F), fetch_key(Keys, Index, Size, Key, CmpF, ActualIndex), array_element(Vals, ActualIndex, Mutable), get_mutable(OldVal, Mutable), update_mutable(NewVal, Mutable). b_hash_insert(Hash, Key, NewVal, NewHash):- Hash = hash(Keys, Vals, Size, N, F, CmpF), hash_f(Key,Size,Index,F), find_or_insert(Keys, Index, Size, N, CmpF, Vals, Key, NewVal, Hash, NewHash). find_or_insert(Keys, Index, Size, N, CmpF, Vals, Key, NewVal, Hash, NewHash) :- array_element(Keys, Index, El), ( var(El) -> add_element(Keys, Index, Size, N, Vals, Key, NewVal, Hash, NewHash) ; cmp_f(CmpF, El, Key) -> % do rb_update array_element(Vals, Index, Mutable), update_mutable(NewVal, Mutable), Hash = NewHash ; I1 is (Index+1) mod Size, find_or_insert(Keys, I1, Size, N, CmpF, Vals, Key, NewVal, Hash, NewHash) ). b_hash_insert_new(Hash, Key, NewVal, NewHash):- Hash = hash(Keys, Vals, Size, N, F, CmpF), hash_f(Key,Size,Index,F), find_or_insert_new(Keys, Index, Size, N, CmpF, Vals, Key, NewVal, Hash, NewHash). find_or_insert_new(Keys, Index, Size, N, CmpF, Vals, Key, NewVal, Hash, NewHash) :- array_element(Keys, Index, El), ( var(El) -> add_element(Keys, Index, Size, N, Vals, Key, NewVal, Hash, NewHash) ; cmp_f(CmpF, El, Key) -> fail ; I1 is (Index+1) mod Size, find_or_insert_new(Keys, I1, Size, N, CmpF, Vals, Key, NewVal, Hash, NewHash) ). add_element(Keys, Index, Size, N, Vals, Key, NewVal, Hash, NewHash) :- get_mutable(NEls, N), NN is NEls+1, update_mutable(NN, N), array_element(Keys, Index, Key), update_mutable(NN, N), array_element(Vals, Index, Mutable), create_mutable(NewVal, Mutable), ( NN > Size/3 -> expand_array(Hash, NewHash) ; Hash = NewHash ). expand_array(Hash, NewHash) :- Hash == NewHash, !, Hash = hash(Keys, Vals, Size, _X, F, _CmpF), new_size(Size, NewSize), array(NewKeys, NewSize), array(NewVals, NewSize), copy_hash_table(Size, Keys, Vals, F, NewSize, NewKeys, NewVals), /* overwrite in place */ setarg(1, Hash, NewKeys), setarg(2, Hash, NewVals), setarg(3, Hash, NewSize). expand_array(Hash, hash(NewKeys, NewVals, NewSize, X, F, CmpF)) :- Hash = hash(Keys, Vals, Size, X, F, CmpF), new_size(Size, NewSize), array(NewKeys, NewSize), array(NewVals, NewSize), copy_hash_table(Size, Keys, Vals, F, NewSize, NewKeys, NewVals). new_size(Size, NewSize) :- Size > 1048576, !, NewSize is Size+1048576. new_size(Size, NewSize) :- NewSize is Size*2. copy_hash_table(0, _, _, _, _, _, _) :- !. copy_hash_table(I1, Keys, Vals, F, Size, NewKeys, NewVals) :- I is I1-1, array_element(Keys, I, Key), nonvar(Key), !, array_element(Vals, I, Val), insert_el(Key, Val, Size, F, NewKeys, NewVals), copy_hash_table(I, Keys, Vals, F, Size, NewKeys, NewVals). copy_hash_table(I1, Keys, Vals, F, Size, NewKeys, NewVals) :- I is I1-1, copy_hash_table(I, Keys, Vals, F, Size, NewKeys, NewVals). insert_el(Key, Val, Size, F, NewKeys, NewVals) :- hash_f(Key,Size,Index, F), find_free(Index, Size, NewKeys, TrueIndex), array_element(NewKeys, TrueIndex, Key), array_element(NewVals, TrueIndex, Val). find_free(Index, Size, Keys, NewIndex) :- array_element(Keys, Index, El), ( var(El) -> NewIndex = Index ; I1 is (Index+1) mod Size, find_free(I1, Size, Keys, NewIndex) ). hash_f(Key, Size, Index, F) :- var(F), !, term_hash(Key,-1,Size,Index). hash_f(Key, Size, Index, F) :- call(F, Key, Size, Index). cmp_f(F, A, B) :- var(F), !, A == B. cmp_f(F, A, B) :- call(F, A, B). b_hash_to_list(hash(Keys, Vals, _, _, _, _), LKeyVals) :- Keys =.. (_.LKs), Vals =.. (_.LVs), mklistpairs(LKs, LVs, LKeyVals). b_hash_keys_to_list(hash(Keys, _, _, _, _, _), LKeys) :- Keys =.. (_.LKs), mklistels(LKs, LKeys). b_hash_values_to_list(hash(_, Vals, _, _, _, _), LVals) :- Vals =.. (_.LVs), mklistvals(LVs, LVals). mklistpairs([], [], []). mklistpairs(V.LKs, _.LVs, KeyVals) :- var(V), !, mklistpairs(LKs, LVs, KeyVals). mklistpairs(K.LKs, V.LVs, (K-VV).KeyVals) :- get_mutable(VV, V), mklistpairs(LKs, LVs, KeyVals). mklistels([], []). mklistels(V.Els, NEls) :- var(V), !, mklistels(Els, NEls). mklistels(K.Els, K.NEls) :- mklistels(Els, NEls). mklistvals([], []). mklistvals(V.Vals, NVals) :- var(V), !, mklistvals(Vals, NVals). mklistvals(K.Vals, KK.NVals) :- get_mutable(KK, K), mklistvals(Vals, NVals).