| 1 | % (c) 2009-2019 Lehrstuhl fuer Softwaretechnik und Programmiersprachen, | |
| 2 | % Heinrich Heine Universitaet Duesseldorf | |
| 3 | % This software is licenced under EPL 1.0 (http://www.eclipse.org/org/documents/epl-v10.html) | |
| 4 | ||
| 5 | :- module(avl_tools,[avl_height_less_than/2, | |
| 6 | avl_height_compare/3, | |
| 7 | avl_height_compare_up_to/5, | |
| 8 | quick_avl_approximate_size/2, avl_fetch_pair/3, | |
| 9 | avl_delete_pair/4, | |
| 10 | avl_apply/5, | |
| 11 | avl_image_interval/4]). | |
| 12 | ||
| 13 | :- use_module(module_information,[module_info/2]). | |
| 14 | :- module_info(group,kernel). | |
| 15 | :- module_info(description,'This module provides AVL-tree utilities used by the kernel.'). | |
| 16 | ||
| 17 | :- use_module(library(avl)). | |
| 18 | ||
| 19 | :- use_module(error_manager). | |
| 20 | :- use_module(self_check). | |
| 21 | :- use_module(kernel_waitflags,[add_wd_error_span/4]). | |
| 22 | ||
| 23 | % ------------------------------- | |
| 24 | ||
| 25 | test_avl_set(node(((int(2),int(3)),int(6)),true,0,node(((int(1),int(2)),int(2)),true,0,empty,empty),node(((int(3),int(4)),int(12)),true,0,empty,empty))). | |
| 26 | ||
| 27 | :- assert_must_succeed(( avl_tools:test_avl_set(A), avl_tools:avl_height_less_than(A,3) )). | |
| 28 | :- assert_must_succeed(( avl_tools:avl_height_less_than(empty,1) )). | |
| 29 | :- assert_must_fail(( avl_tools:test_avl_set(A), avl_tools:avl_height_less_than(A,2) )). | |
| 30 | :- assert_must_fail(( avl_tools:avl_height_less_than(empty,0) )). | |
| 31 | ||
| 32 | % a custom version of avl_height; advantage it will stop when reaching MaxHeight | |
| 33 | ||
| 34 | avl_height_less_than(empty, MaxHeight) :- MaxHeight>0. | |
| 35 | avl_height_less_than(node(_,_,B,L,R), H0) :- H0>1, | |
| 36 | H1 is H0-1, | |
| 37 | ( B >= 0 -> avl_height_less_than(R, H1) | |
| 38 | ; avl_height_less_than(L, H1) | |
| 39 | ). | |
| 40 | ||
| 41 | ||
| 42 | % efficient way of comparing AVL heights without having to fully traverse larger AVL | |
| 43 | ||
| 44 | :- assert_must_succeed(( avl_tools:test_avl_set(A), avl_tools:avl_height_compare(A,A,eq) )). | |
| 45 | :- assert_must_succeed(( avl_tools:test_avl_set(A), avl_tools:avl_height_compare(empty,A,lt) )). | |
| 46 | :- assert_must_succeed(( avl_tools:test_avl_set(A), avl_tools:avl_height_compare(A,empty,gt) )). | |
| 47 | ||
| 48 | avl_height_compare(A,B,Res) :- avl_height_compare_up_to(A,B,0,0,_,Res). | |
| 49 | ||
| 50 | :- assert_must_succeed(( avl_tools:test_avl_set(A), avl_tools:avl_height_compare_up_to(empty,A,2,0,lt) )). | |
| 51 | :- assert_must_succeed(( avl_tools:test_avl_set(A), avl_tools:avl_height_compare_up_to(empty,A,3,0,eq) )). | |
| 52 | ||
| 53 | % avl_height_compare_up_to(Avl1,Avl2,MaxDiff,ResSz,ResCmp) -> ResSz: Height of smaller AVL, ResCmp = eq,lt,gt | |
| 54 | avl_height_compare_up_to(Avl1,Avl2,MaxDiff,ResSz,ResCmp) :- | |
| 55 | avl_height_compare_up_to(Avl1,Avl2,MaxDiff,0,ResSz,ResCmp). | |
| 56 | ||
| 57 | avl_height_compare_up_to(empty,empty,_,Sz,Sz,eq). | |
| 58 | avl_height_compare_up_to(empty,node(_,_,B,L,R),MaxDiff,Sz,Sz,Res) :- | |
| 59 | (avl_height_less_than(node(_,_,B,L,R),MaxDiff) -> Res=eq ; Res=lt). | |
| 60 | avl_height_compare_up_to(node(_,_,B,L,R),empty,MaxDiff,Sz,Sz,Res) :- | |
| 61 | (avl_height_less_than(node(_,_,B,L,R),MaxDiff) -> Res=eq ; Res=gt). | |
| 62 | avl_height_compare_up_to(node(_,_,B1,L1,R1),node(_,_,B2,L2,R2),MaxDiff,AccSz,ResSz,Res) :- | |
| 63 | ( B1 >= 0 -> A1=R1 ; A1=L1), | |
| 64 | ( B2 >= 0 -> A2=R2 ; A2=L2), | |
| 65 | Acc1 is AccSz+1, | |
| 66 | avl_height_compare_up_to(A1,A2,MaxDiff,Acc1,ResSz,Res). | |
| 67 | ||
| 68 | % ------------------------------- | |
| 69 | ||
| 70 | % compute an upper bound for the size of an AVL based on Height (log runtime): | |
| 71 | quick_avl_approximate_size(AVL,Size) :- avl_height(AVL,Height), Size is integer(2**Height-1). | |
| 72 | % a lower bound could be computed by =POWER(2,(HEIGHT+0.3277)/1.4405)-2 (page 460, Knuth 3) | |
| 73 | ||
| 74 | ||
| 75 | % ------------------------------- | |
| 76 | ||
| 77 | ||
| 78 | % a version of avl_fetch that looks for a pair in the AVL tree whose | |
| 79 | % first component is Key; it assumes that the term ordering gives the | |
| 80 | % first argument higher priority than the second one. | |
| 81 | % TO DO ?: extend to records: decompose((K,KY),K,KY). decompose(rec([H|T]),H,T). | |
| 82 | avl_fetch_pair(Key, node((K,KY),_,_,L,R),Res) :- | |
| 83 | ? | my_compare(O, Key, K), %((O='=',nonvar(Res)) -> print(comp(Res,KY)),nl ; true), % see TODO below |
| 84 | ? | avl_fetch_pair_1(O, Key, L, R, KY, Res). |
| 85 | ||
| 86 | % order of clauses relevant so that safe_avl_member returns elements in order ! | |
| 87 | avl_fetch_pair_1(<, Key, node((K,KY),_,_,L,R), _, _, Res) :- | |
| 88 | ? | my_compare(O, Key, K), %((O='=',nonvar(Res)) -> print(comp(Res,KY)),nl ; true), % see TODO below |
| 89 | ? | avl_fetch_pair_1(O, Key, L, R, KY, Res). |
| 90 | avl_fetch_pair_1(=, _Key, _L, _R, KY, KY). | |
| 91 | avl_fetch_pair_1(>, Key, _, node((K,KY),_,_,L,R),_,Res) :- | |
| 92 | ? | my_compare(O, Key, K), %((O='=',nonvar(Res)) -> print(comp(Res,KY)),nl ; true), % see TODO below |
| 93 | ? | avl_fetch_pair_1(O, Key, L, R, KY, Res). |
| 94 | ||
| 95 | my_compare(O,K1,K2) :- compare(OK,K1,K2), | |
| 96 | (OK=('=') -> true /* leave O free variable */ | |
| 97 | % TO DO: think about comparing KY and Res above | |
| 98 | ; O=OK). | |
| 99 | ||
| 100 | :- assert_must_succeed(( avl_tools:test_avl_set(A), avl_tools:avl_delete_pair((int(3),int(4)),A,true,AA), avl:avl_size(AA,2) )). | |
| 101 | :- assert_must_fail(( avl_tools:test_avl_set(A), avl_tools:avl_delete_pair((int(1),int(3)),A,true,_) )). | |
| 102 | :- assert_must_succeed(( avl_tools:test_avl_set(A), avl_tools:avl_delete_pair((int(1),int(2)),A,true,AA), | |
| 103 | avl_tools:avl_delete_pair((int(2),int(3)),AA,true,AAA), avl:avl_size(AAA,1) )). | |
| 104 | ||
| 105 | % an adaptation of avl_delete which deletes a pair just knowing the first argument of the pair | |
| 106 | % like avl_fetch_pair it assumes term ordering gives precedence to first argument | |
| 107 | ||
| 108 | avl_delete_pair(Key, AVL0, Val, AVL) :- | |
| 109 | avl_delete_pair5(AVL0, Key, Val, AVL, _). | |
| 110 | ||
| 111 | avl_delete_pair5(node(K,V,B,L,R), Key, Val, AVL, Delta) :- | |
| 112 | K = (KX,_KY), % crucial difference | |
| 113 | compare(C, Key, KX), | |
| 114 | avl_delete_pair(C, Key, Val, AVL, Delta, K, V, B, L, R). | |
| 115 | ||
| 116 | avl_delete_pair(<, Key, Val, AVL, Delta, K, V, B, L, R) :- | |
| 117 | avl_delete_pair5(L, Key, Val, L1, D1), | |
| 118 | B1 is B+D1, | |
| 119 | avl:avl(B1, K, V, L1, R, AVL), | |
| 120 | avl:avl_shrinkage(AVL, D1, Delta). | |
| 121 | avl_delete_pair(=, _, Val, AVL, Delta, _, Val, B, L, R) :- | |
| 122 | ( L == empty -> AVL = R, Delta = 1 | |
| 123 | ; R == empty -> AVL = L, Delta = 1 | |
| 124 | ; avl:avl_del_max(L, K, V, L1, D1), | |
| 125 | B1 is B+D1, | |
| 126 | avl:avl(B1, K, V, L1, R, AVL), | |
| 127 | avl:avl_shrinkage(AVL, D1, Delta) | |
| 128 | ). | |
| 129 | avl_delete_pair(>, Key, Val, AVL, Delta, K, V, B, L, R) :- | |
| 130 | avl_delete_pair5(R, Key, Val, R1, D1), | |
| 131 | B1 is B-D1, | |
| 132 | avl:avl(B1, K, V, L, R1, AVL), | |
| 133 | avl:avl_shrinkage(AVL, D1, Delta). | |
| 134 | ||
| 135 | ||
| 136 | % ------------------------------- | |
| 137 | ||
| 138 | ||
| 139 | :- load_files(library(system), [when(compile_time), imports([environ/2])]). | |
| 140 | % avl_apply | |
| 141 | % similar to avl_fetch_pair: but checks whether we have a function | |
| 142 | % and whether the function is defined for the key | |
| 143 | :- if(environ(no_wd_checking,true)). | |
| 144 | /* faster version without WD-checking : */ | |
| 145 | :- nl,print('DISABLING WD-CHECKING FOR FUNCTION APPLICATION!'),nl,nl. | |
| 146 | avl_apply(Key, node((K,KY),_,_,L,R),Res,_Span,_WF) :- | |
| 147 | compare(O, Key, K), | |
| 148 | avl_apply_1(O, Key, L, R, KY, Res). | |
| 149 | avl_apply_1(<, Key, node((K,KY),_,_,L,R), _, _, Res) :- | |
| 150 | compare(O, Key, K), | |
| 151 | avl_apply_1(O, Key, L, R, KY, Res). | |
| 152 | avl_apply_1(=, _Key, _Left,_Right, KY, KY). | |
| 153 | avl_apply_1(>, Key, _, node((K,KY),_,_,L,R),_,Res) :- | |
| 154 | compare(O, Key, K), | |
| 155 | avl_apply_1(O, Key, L, R, KY, Res). | |
| 156 | :- else. | |
| 157 | /* normal version with WD -checking : */ | |
| 158 | avl_apply(Key, node((K,KY),_,_,L,R),Res,Span,WF) :- | |
| 159 | compare(O, Key, K), | |
| 160 | avl_apply_1(O, Key, L, R, KY, Res,Span,WF). | |
| 161 | ||
| 162 | avl_apply_1(<, Key, NODE, _, _, Res,Span,WF) :- | |
| 163 | (NODE=node((K,KY),_,_,L,R) | |
| 164 | -> compare(O, Key, K), | |
| 165 | avl_apply_1(O, Key, L, R, KY, Res,Span,WF) | |
| 166 | ; add_wd_error_span('function applied outside of domain (#6): ','@fun'(Key,avl_set(NODE)),Span,WF) | |
| 167 | ). | |
| 168 | avl_apply_1(>, Key, _, NODE,_,Res,Span,WF) :- | |
| 169 | (NODE=node((K,KY),_,_,L,R) | |
| 170 | -> compare(O, Key, K), | |
| 171 | avl_apply_1(O, Key, L, R, KY, Res,Span,WF) | |
| 172 | ; add_wd_error_span('function applied outside of domain (#7): ','@fun'(Key,avl_set(NODE)),Span,WF) | |
| 173 | ). | |
| 174 | %%avl_apply_1(=, Key, _Left,_Right, KY, KY, _Span,_WF). | |
| 175 | avl_apply_1(=, Key, Left,Right, KY, Res,Span,WF) :- | |
| 176 | ((Left = node((Key,KY2),_,_,_,_) ; | |
| 177 | Right = node((Key,KY2),_,_,_,_)) % this is only a partial quick test; the next & previous elements could be deeper in the tree | |
| 178 | % TO DO: use optimized version of avl_fetch((Key,KY2),Left) ; avl_fetch((Key,KY2),Right) if preferences:preference(find_abort_values,true), | |
| 179 | -> add_wd_error_span('function application used for relation: ','@rel'(Key,KY,KY2),Span,WF) | |
| 180 | % we do not instantiate Res in this case | |
| 181 | ; Res=KY). | |
| 182 | ||
| 183 | :- endif. | |
| 184 | ||
| 185 | ||
| 186 | % ------------------------------- | |
| 187 | ||
| 188 | ||
| 189 | ||
| 190 | % similar to avl_apply but uses interval as lookup key | |
| 191 | avl_image_interval(From,To, node((K,KY),_,_,L,R),Res) :- | |
| 192 | ? | comp_interval(O, From,To, K), |
| 193 | ? | avl_image_interval_1(O, From,To, L, R, KY, Res). |
| 194 | avl_image_interval_1(<, From,To, NODE, _, _, Res) :- | |
| 195 | ? | NODE=node((K,KY),_,_,L,R),comp_interval(O, From,To, K), |
| 196 | ? | avl_image_interval_1(O, From,To, L, R, KY, Res). |
| 197 | avl_image_interval_1(=, _From,_To, _Left,_Right, KY, Res) :- Res=KY. | |
| 198 | avl_image_interval_1(>, From,To, _, NODE,_,Res) :- | |
| 199 | ? | NODE=node((K,KY),_,_,L,R), |
| 200 | ? | comp_interval(O, From,To, K), |
| 201 | ? | avl_image_interval_1(O, From,To, L, R, KY, Res). |
| 202 | ||
| 203 | comp_interval(O,From,To,int(Key)) :- | |
| 204 | ? | ( number(From),Key<From -> O = ('>') % could be minus_inf |
| 205 | ? | ; number(To), Key>To -> O = ('<') |
| 206 | ? | ; O = ('<') ; O = ('=') ; O = ('>') |
| 207 | ). | |
| 208 | % ------------------------------- |