Files
mercury/library/rbtree.m
2023-02-21 00:40:31 +11:00

1151 lines
37 KiB
Mathematica

%---------------------------------------------------------------------------%
% vim: ft=mercury ts=4 sw=4 et
%---------------------------------------------------------------------------%
% Copyright (C) 1995-2000, 2003-2007, 2011 The University of Melbourne.
% Copyright (C) 2014-2018 The Mercury team.
% This file is distributed under the terms specified in COPYING.LIB.
%---------------------------------------------------------------------------%
%
% File: rbtree.m.
% Main author: petdr.
% Stability: medium.
%
% Contains an implementation of red black trees.
%
% *** Exit conditions of main predicates ***
% insert:
% fails if key already in tree.
% update:
% changes value of key already in tree. fails if key doesn't exist.
% transform_value:
% looks up an existing value in the tree, applies a transformation to the
% value and then updates the value. fails if the key doesn't exist.
% set:
% inserts or updates. Never fails.
%
% insert_duplicate:
% inserts duplicate keys into the tree, never fails. Search doesn't
% yet support looking for duplicates.
%
% delete:
% deletes a node from the tree if it exists.
% remove:
% fails if node to remove doesn't exist in the tree.
%
% lookup:
% Throws an exception if key looked up doesn't exist.
% search:
% Fails if key looked up doesn't exist.
%
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
:- module rbtree.
:- interface.
:- import_module assoc_list.
:- import_module list.
%---------------------------------------------------------------------------%
:- type rbtree(Key, Value).
% Initialise the data structure.
%
:- func init = rbtree(K, V).
:- pred init(rbtree(K, V)::uo) is det.
% Check whether a tree is empty.
%
:- pred is_empty(rbtree(K, V)::in) is semidet.
% Initialise an rbtree containing the given key-value pair.
%
:- func singleton(K, V) = rbtree(K, V).
% Inserts a new key-value pair into the tree.
% Fails if key already in the tree.
%
:- pred insert(K::in, V::in, rbtree(K, V)::in, rbtree(K, V)::out) is semidet.
% Updates the value associated with a key.
% Fails if the key does not exist.
%
:- pred update(K::in, V::in, rbtree(K, V)::in, rbtree(K, V)::out) is semidet.
% Update the value at the given key by applying the supplied
% transformation to it. Fails if the key is not found. This is faster
% than first searching for the value and then updating it.
%
:- pred transform_value(pred(V, V)::in(pred(in, out) is det), K::in,
rbtree(K, V)::in, rbtree(K, V)::out) is semidet.
% Sets a value regardless of whether key exists or not.
%
:- func set(rbtree(K, V), K, V) = rbtree(K, V).
:- pred set(K::in, V::in, rbtree(K, V)::in, rbtree(K, V)::out) is det.
% Insert a duplicate key into the tree.
%
:- func insert_duplicate(rbtree(K, V), K, V) = rbtree(K, V).
:- pred insert_duplicate(K::in, V::in,
rbtree(K, V)::in, rbtree(K, V)::out) is det.
:- pred member(rbtree(K, V)::in, K::out, V::out) is nondet.
% Search for a key-value pair using the key.
% Fails if the key does not exist.
%
:- pred search(rbtree(K, V)::in, K::in, V::out) is semidet.
% Lookup the value associated with a key.
% Throws an exception if the key does not exist.
%
:- func lookup(rbtree(K, V), K) = V.
:- pred lookup(rbtree(K, V)::in, K::in, V::out) is det.
% Search for a key-value pair using the key. If there is no entry
% for the given key, returns the pair for the next lower key instead.
% Fails if there is no key with the given or lower value.
%
:- pred lower_bound_search(rbtree(K, V)::in, K::in, K::out, V::out)
is semidet.
% Search for a key-value pair using the key. If there is no entry
% for the given key, returns the pair for the next lower key instead.
% Throws an exception if there is no key with the given or lower value.
%
:- pred lower_bound_lookup(rbtree(K, V)::in, K::in, K::out, V::out) is det.
% Search for a key-value pair using the key. If there is no entry
% for the given key, returns the pair for the next higher key instead.
% Fails if there is no key with the given or higher value.
%
:- pred upper_bound_search(rbtree(K, V)::in, K::in, K::out, V::out)
is semidet.
% Search for a key-value pair using the key. If there is no entry
% for the given key, returns the pair for the next higher key instead.
% Throws an exception if there is no key with the given or higher value.
%
:- pred upper_bound_lookup(rbtree(K, V)::in, K::in, K::out, V::out) is det.
% Delete the key-value pair associated with a key.
% Does nothing if the key does not exist.
%
:- func delete(rbtree(K, V), K) = rbtree(K, V).
:- pred delete(K::in, rbtree(K, V)::in, rbtree(K, V)::out) is det.
% Remove the key-value pair associated with a key.
% Fails if the key does not exist.
%
:- pred remove(K::in, V::out, rbtree(K, V)::in, rbtree(K, V)::out)
is semidet.
% Deletes the node with the minimum key from the tree,
% and returns the key and value fields.
%
:- pred remove_smallest(K::out, V::out,
rbtree(K, V)::in, rbtree(K, V)::out) is semidet.
% Deletes the node with the maximum key from the tree,
% and returns the key and value fields.
%
:- pred remove_largest(K::out, V::out,
rbtree(K, V)::in, rbtree(K, V)::out) is semidet.
% Returns an in-order list of all the keys in the rbtree.
%
:- func keys(rbtree(K, V)) = list(K).
:- pred keys(rbtree(K, V)::in, list(K)::out) is det.
% Returns a list of values such that the keys associated with the
% values are in-order.
%
:- func values(rbtree(K, V)) = list(V).
:- pred values(rbtree(K, V)::in, list(V)::out) is det.
% Count the number of elements in the tree.
%
:- func count(rbtree(K, V)) = int.
:- pred count(rbtree(K, V)::in, int::out) is det.
:- func assoc_list_to_rbtree(assoc_list(K, V)) = rbtree(K, V).
:- pred assoc_list_to_rbtree(assoc_list(K, V)::in, rbtree(K, V)::out) is det.
:- func from_assoc_list(assoc_list(K, V)) = rbtree(K, V).
:- func rbtree_to_assoc_list(rbtree(K, V)) = assoc_list(K, V).
:- pred rbtree_to_assoc_list(rbtree(K, V)::in, assoc_list(K, V)::out)
is det.
:- func to_assoc_list(rbtree(K, V)) = assoc_list(K, V).
:- func foldl(func(K, V, T) = T, rbtree(K, V), T) = T.
:- pred foldl(pred(K, V, T, T), rbtree(K, V), T, T).
:- mode foldl(pred(in, in, in, out) is det, in, in, out) is det.
:- mode foldl(pred(in, in, mdi, muo) is det, in, mdi, muo) is det.
:- mode foldl(pred(in, in, di, uo) is det, in, di, uo) is det.
:- mode foldl(pred(in, in, in, out) is semidet, in, in, out)
is semidet.
:- mode foldl(pred(in, in, mdi, muo) is semidet, in, mdi, muo)
is semidet.
:- mode foldl(pred(in, in, di, uo) is semidet, in, di, uo)
is semidet.
:- pred foldl2(pred(K, V, T, T, U, U), rbtree(K, V), T, T, U, U).
:- mode foldl2(pred(in, in, in, out, in, out) is det,
in, in, out, in, out) is det.
:- mode foldl2(pred(in, in, in, out, mdi, muo) is det,
in, in, out, mdi, muo) is det.
:- mode foldl2(pred(in, in, in, out, di, uo) is det,
in, in, out, di, uo) is det.
:- mode foldl2(pred(in, in, di, uo, di, uo) is det,
in, di, uo, di, uo) is det.
:- mode foldl2(pred(in, in, in, out, in, out) is semidet,
in, in, out, in, out) is semidet.
:- mode foldl2(pred(in, in, in, out, mdi, muo) is semidet,
in, in, out, mdi, muo) is semidet.
:- mode foldl2(pred(in, in, in, out, di, uo) is semidet,
in, in, out, di, uo) is semidet.
:- pred foldl3(pred(K, V, T, T, U, U, W, W), rbtree(K, V),
T, T, U, U, W, W).
:- mode foldl3(pred(in, in, in, out, in, out, in, out) is det,
in, in, out, in, out, in, out) is det.
:- mode foldl3(pred(in, in, in, out, in, out, in, out) is semidet,
in, in, out, in, out, in, out) is semidet.
:- mode foldl3(pred(in, in, in, out, in, out, di, uo) is det,
in, in, out, in, out, di, uo) is det.
:- mode foldl3(pred(in, in, in, out, di, uo, di, uo) is det,
in, in, out, di, uo, di, uo) is det.
:- mode foldl3(pred(in, in, di, uo, di, uo, di, uo) is det,
in, di, uo, di, uo, di, uo) is det.
:- pred foldl_values(pred(V, A, A), rbtree(K, V), A, A).
:- mode foldl_values(pred(in, in, out) is det, in, in, out) is det.
:- mode foldl_values(pred(in, mdi, muo) is det, in, mdi, muo) is det.
:- mode foldl_values(pred(in, di, uo) is det, in, di, uo) is det.
:- mode foldl_values(pred(in, in, out) is semidet, in, in, out) is semidet.
:- mode foldl_values(pred(in, mdi, muo) is semidet, in, mdi, muo) is semidet.
:- mode foldl_values(pred(in, di, uo) is semidet, in, di, uo) is semidet.
:- pred foldl2_values(pred(V, A, A, B, B), rbtree(K, V), A, A, B, B).
:- mode foldl2_values(pred(in, in, out, in, out) is det, in, in, out,
in, out) is det.
:- mode foldl2_values(pred(in, in, out, mdi, muo) is det, in, in, out,
mdi, muo) is det.
:- mode foldl2_values(pred(in, in, out, di, uo) is det, in, in, out,
di, uo) is det.
:- mode foldl2_values(pred(in, in, out, in, out) is semidet, in, in, out,
in, out) is semidet.
:- mode foldl2_values(pred(in, in, out, mdi, muo) is semidet, in, in, out,
mdi, muo) is semidet.
:- mode foldl2_values(pred(in, in, out, di, uo) is semidet, in, in, out,
di, uo) is semidet.
:- func map_values(func(K, V) = W, rbtree(K, V)) = rbtree(K, W).
:- pred map_values(pred(K, V, W), rbtree(K, V), rbtree(K, W)).
:- mode map_values(pred(in, in, out) is det, in, out) is det.
:- mode map_values(pred(in, in, out) is semidet, in, out) is semidet.
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
:- implementation.
:- import_module bool.
:- import_module int.
:- import_module maybe.
:- import_module pair.
:- import_module require.
%---------------------------------------------------------------------------%
% Special conditions that must be satisfied by Red-Black trees:
% * The root node must be black.
% * There can never be 2 red nodes in a row.
:- type rbtree(K, V)
---> empty
; red(K, V, rbtree(K, V), rbtree(K, V))
; black(K, V, rbtree(K, V), rbtree(K, V)).
%---------------------------------------------------------------------------%
init = RBT :-
rbtree.init(RBT).
init(empty).
is_empty(Tree) :-
Tree = empty.
singleton(K, V) = black(K, V, empty, empty).
%---------------------------------------------------------------------------%
insert(K, V, empty, black(K, V, empty, empty)).
insert(_K, _V, red(_, _, _, _), _Tree) :-
error($pred, "root node cannot be red!").
insert(K, V, black(K0, V0, L0, R0), Tree) :-
rbtree.insert_2(black(K0, V0, L0, R0), K, V, Tree0),
% Ensure that the root of the tree is black.
(
Tree0 = black(_, _, _, _),
Tree = Tree0
;
Tree0 = red(K1, V1, L1, R1),
Tree = black(K1, V1, L1, R1)
;
Tree0 = empty,
error($pred, "new tree is empty")
).
% insert_2:
%
% We traverse down the tree until we find the correct spot to insert.
% Then as we fall back out of the recursions we look for possible
% rotation cases.
%
:- pred insert_2(rbtree(K, V)::in, K::in, V::in, rbtree(K, V)::out)
is semidet.
% Red node always inserted at the bottom as it will be rotated into the
% correct place as we move back up the tree.
insert_2(empty, K, V, red(K, V, empty, empty)).
insert_2(red(K0, V0, L0, R0), K, V, Tree) :-
% Work out which side of the rbtree to insert.
compare(Result, K, K0),
(
Result = (<),
rbtree.insert_2(L0, K, V, NewL),
Tree = red(K0, V0, NewL, R0)
;
Result = (>),
rbtree.insert_2(R0, K, V, NewR),
Tree = red(K0, V0, L0, NewR)
;
Result = (=),
fail
).
% Only ever need to look for a possible rotation if we are in a black node.
% The rotation criteria is when there is 2 red nodes in a row.
insert_2(black(K0, V0, L0, R0), K, V, Tree) :-
( if
L0 = red(LK, LV, LL, LR),
R0 = red(RK, RV, RL, RR)
then
% On the way down the rbtree we split any 4-nodes we find.
% This converts the current node to a red node, so we call
% the red node version of rbtree.insert_2/4.
L = black(LK, LV, LL, LR),
R = black(RK, RV, RL, RR),
Tree0 = red(K0, V0, L, R),
rbtree.insert_2(Tree0, K, V, Tree)
else
% Work out which side of the rbtree to insert.
compare(Result, K, K0),
(
Result = (<),
rbtree.insert_2(L0, K, V, NewL),
( if
% Only need to start looking for a rotation case
% if the current node is black(known), and its
% new child red.
NewL = red(LK, LV, LL, LR)
then
% Check to see if a grandchild is red and if so rotate.
( if LL = red(_LLK, _LLV, _LLL, _LLR) then
TreeR = red(K0, V0, LR, R0),
Tree = black(LK, LV, LL, TreeR)
else if LR = red(LRK, LRV, LRL, LRR) then
TreeL = red(LK, LV, LL, LRL),
TreeR = red(K0, V0, LRR, R0),
Tree = black(LRK, LRV, TreeL, TreeR)
else
Tree = black(K0, V0, NewL, R0)
)
else
Tree = black(K0, V0, NewL, R0)
)
;
Result = (>),
rbtree.insert_2(R0, K, V, NewR),
( if
% Only need to start looking for a rotation case
% if the current node is black(known), and its
% new child red.
NewR = red(RK, RV, RL, RR)
then
% Check to see if a grandchild is red and if so rotate.
( if RL = red(RLK, RLV, RLL, RLR) then
TreeL = red(K0, V0, L0, RLL),
TreeR = red(RK, RV, RLR, RR),
Tree = black(RLK, RLV, TreeL, TreeR)
else if RR = red(_RRK, _RRV, _RRL, _RRR) then
TreeL = red(K0, V0, L0, RL),
Tree = black(RK, RV, TreeL, RR)
else
Tree = black(K0, V0, L0, NewR)
)
else
Tree = black(K0, V0, L0, NewR)
)
;
Result = (=),
fail
)
).
%---------------------------------------------------------------------------%
update(_K, _V, empty, _T) :-
fail.
update(K, V, red(K0, V0, L, R), Tree) :-
compare(Result, K, K0),
(
Result = (=),
Tree = red(K, V, L, R)
;
Result = (<),
rbtree.update(K, V, L, NewL),
Tree = red(K0, V0, NewL, R)
;
Result = (>),
rbtree.update(K, V, R, NewR),
Tree = red(K0, V0, L, NewR)
).
update(K, V, black(K0, V0, L, R), Tree) :-
compare(Result, K, K0),
(
Result = (=),
Tree = black(K, V, L, R)
;
Result = (<),
rbtree.update(K, V, L, NewL),
Tree = black(K0, V0, NewL, R)
;
Result = (>),
rbtree.update(K, V, R, NewR),
Tree = black(K0, V0, L, NewR)
).
%---------------------------------------------------------------------------%
transform_value(_P, _K, empty, _T) :-
fail.
transform_value(P, K, red(K0, V0, L, R), Tree) :-
compare(Result, K, K0),
(
Result = (=),
P(V0, NewV),
Tree = red(K, NewV, L, R)
;
Result = (<),
rbtree.transform_value(P, K, L, NewL),
Tree = red(K0, V0, NewL, R)
;
Result = (>),
rbtree.transform_value(P, K, R, NewR),
Tree = red(K0, V0, L, NewR)
).
transform_value(P, K, black(K0, V0, L, R), Tree) :-
compare(Result, K, K0),
(
Result = (=),
P(V0, NewV),
Tree = black(K, NewV, L, R)
;
Result = (<),
rbtree.transform_value(P, K, L, NewL),
Tree = black(K0, V0, NewL, R)
;
Result = (>),
rbtree.transform_value(P, K, R, NewR),
Tree = black(K0, V0, L, NewR)
).
%---------------------------------------------------------------------------%
set(!.RBT, K, V) = !:RBT :-
rbtree.set(K, V, !RBT).
set(K, V, empty, black(K, V, empty, empty)).
set(_K, _V, red(_, _, _, _), _Tree) :-
error($pred, "root node cannot be red!").
set(K, V, black(K0, V0, L0, R0), Tree) :-
rbtree.set_2(black(K0, V0, L0, R0), K, V, Tree0),
% Ensure that the root of the tree is black.
(
Tree0 = black(_, _, _, _),
Tree = Tree0
;
Tree0 = red(K1, V1, L1, R1),
Tree = black(K1, V1, L1, R1)
;
Tree0 = empty,
error($pred, "new tree is empty")
).
% set_2:
%
% We traverse down the tree until we find the correct spot to insert, or
% update. Then as we fall back out of the recursions we look for possible
% rotation cases.
%
:- pred set_2(rbtree(K, V), K, V, rbtree(K, V)).
:- mode set_2(di, di, di, uo) is det.
:- mode set_2(in, in, in, out) is det.
% Red node always inserted at the bottom as it will be rotated into the
% correct place as we move back up the tree.
set_2(empty, K, V, red(K, V, empty, empty)).
set_2(red(K0, V0, L0, R0), K, V, Tree) :-
% Work out which side of the rbtree to insert.
compare(Result, K, K0),
(
Result = (=),
Tree = red(K, V, L0, R0)
;
Result = (<),
rbtree.set_2(L0, K, V, NewL),
Tree = red(K0, V0, NewL, R0)
;
Result = (>),
rbtree.set_2(R0, K, V, NewR),
Tree = red(K0, V0, L0, NewR)
).
set_2(black(K0, V0, L0, R0), K, V, Tree) :-
( if
L0 = red(LK, LV, LL, LR),
R0 = red(RK, RV, RL, RR)
then
% On the way down the rbtree we split any 4-nodes we find.
L = black(LK, LV, LL, LR),
R = black(RK, RV, RL, RR),
Tree0 = red(K0, V0, L, R),
rbtree.set_2(Tree0, K, V, Tree)
else
% Work out which side of the rbtree to insert.
compare(Result, K, K0),
(
Result = (=),
Tree = black(K, V, L0, R0)
;
Result = (<),
rbtree.set_2(L0, K, V, NewL),
( if
% Only need to start looking for a rotation case
% if the current node is black(known), and its
% new child red.
NewL = red(LK, LV, LL, LR)
then
% Check to see if a grandchild is red and if so rotate.
( if LL = red(_LLK, _LLV, _LLL, _LLR) then
TreeR = red(K0, V0, LR, R0),
Tree = black(LK, LV, LL, TreeR)
else if LR = red(LRK, LRV, LRL, LRR) then
TreeL = red(LK, LV, LL, LRL),
TreeR = red(K0, V0, LRR, R0),
Tree = black(LRK, LRV, TreeL, TreeR)
else
% NewL2 == NewL, but this hack
% needed for unique modes to work.
NewL2 = red(LK, LV, LL, LR),
Tree = black(K0, V0, NewL2, R0)
)
else
Tree = black(K0, V0, NewL, R0)
)
;
Result = (>),
rbtree.set_2(R0, K, V, NewR),
( if
% Only need to start looking for a rotation case
% if the current node is black(known), and its
% new child red.
NewR = red(RK, RV, RL, RR)
then
% Check to see if a grandchild is red and if so rotate.
( if RL = red(RLK, RLV, RLL, RLR) then
TreeL = red(K0, V0, L0, RLL),
TreeR = red(RK, RV, RLR, RR),
Tree = black(RLK, RLV, TreeL, TreeR)
else if RR = red(_RRK, _RRV, _RRL, _RRR) then
TreeL = red(K0, V0, L0, RL),
Tree = black(RK, RV, TreeL, RR)
else
% NewR2 == NewR, but this hack
% needed for unique modes to work.
NewR2 = red(RK, RV, RL, RR),
Tree = black(K0, V0, L0, NewR2)
)
else
Tree = black(K0, V0, L0, NewR)
)
)
).
%---------------------------------------------------------------------------%
insert_duplicate(!.RBT, K, V) = !:RBT :-
rbtree.insert_duplicate(K, V, !RBT).
insert_duplicate(K, V, empty, black(K, V, empty, empty)).
insert_duplicate(_K, _V, red(_, _, _, _), _Tree) :-
error($pred, "root node cannot be red!").
insert_duplicate(K, V, black(K0, V0, L0, R0), Tree) :-
rbtree.insert_duplicate_2(black(K0, V0, L0, R0), K, V, Tree0),
% Ensure that the root of the tree is black.
( if Tree0 = red(K1, V1, L1, R1) then
Tree = black(K1, V1, L1, R1)
else
Tree = Tree0
).
% insert_duplicate_2:
%
% We traverse down the tree until we find the correct spot to insert.
% Then as we fall back out of the recursions we look for possible
% rotation cases.
%
:- pred insert_duplicate_2(rbtree(K, V), K, V, rbtree(K, V)).
:- mode insert_duplicate_2(in, in, in, out) is det.
% Red node always inserted at the bottom as it will be rotated into the
% correct place as we move back up the tree.
insert_duplicate_2(empty, K, V, red(K, V, empty, empty)).
insert_duplicate_2(red(K0, V0, L0, R0), K, V, Tree) :-
% Work out which side of the rbtree to insert.
compare(Result, K, K0),
(
Result = (<),
rbtree.insert_duplicate_2(L0, K, V, NewL),
Tree = red(K0, V0, NewL, R0)
;
Result = (>),
rbtree.insert_duplicate_2(R0, K, V, NewR),
Tree = red(K0, V0, L0, NewR)
;
Result = (=),
rbtree.insert_duplicate_2(L0, K, V, NewL),
Tree = red(K0, V0, NewL, R0)
).
% Only ever need to look for a possible rotation if we are in a black node.
% The rotation criteria is when there is 2 red nodes in a row.
insert_duplicate_2(black(K0, V0, L0, R0), K, V, Tree) :-
( if
L0 = red(LK, LV, LL, LR),
R0 = red(RK, RV, RL, RR)
then
% On the way down the rbtree we split any 4-nodes we find.
% This converts the current node to a red node, so we call
% the red node version of rbtree.insert_duplicate_2/4.
L = black(LK, LV, LL, LR),
R = black(RK, RV, RL, RR),
Tree0 = red(K0, V0, L, R),
rbtree.insert_duplicate_2(Tree0, K, V, Tree)
else
% Work out which side of the rbtree to insert.
compare(Result, K, K0),
(
Result = (<),
rbtree.insert_duplicate_2(L0, K, V, NewL),
( if
% Only need to start looking for a rotation case
% if the current node is black(known), and its
% new child red.
NewL = red(LK, LV, LL, LR)
then
% Check to see if a grandchild is red and if so rotate.
( if LL = red(_LLK1, _LLV1, _LLL1, _LLR1) then
TreeR = red(K0, V0, LR, R0),
Tree = black(LK, LV, LL, TreeR)
else if LR = red(LRK, LRV, LRL, LRR) then
TreeL = red(LK, LV, LL, LRL),
TreeR = red(K0, V0, LRR, R0),
Tree = black(LRK, LRV, TreeL, TreeR)
else
Tree = black(K0, V0, NewL, R0)
)
else
Tree = black(K0, V0, NewL, R0)
)
;
Result = (>),
rbtree.insert_duplicate_2(R0, K, V, NewR),
( if
% Only need to start looking for a rotation case
% if the current node is black(known), and its
% new child red.
NewR = red(RK, RV, RL, RR)
then
% Check to see if a grandchild is red and if so rotate.
( if RL = red(RLK, RLV, RLL, RLR) then
TreeL = red(K0, V0, L0, RLL),
TreeR = red(RK, RV, RLR, RR),
Tree = black(RLK, RLV, TreeL, TreeR)
else if RR = red(_RRK, _RRV, _RRL, _RRR) then
TreeL = red(K0, V0, L0, RL),
Tree = black(RK, RV, TreeL, RR)
else
Tree = black(K0, V0, L0, NewR)
)
else
Tree = black(K0, V0, L0, NewR)
)
;
Result = (=),
rbtree.insert_duplicate_2(L0, K, V, NewL),
( if
% Only need to start looking for a rotation case if the
% current node is black(known), and its new child red.
NewL = red(LK, LV, LL, LR)
then
% Check to see if a grandchild is red and if so rotate.
( if LL = red(_LLK2, _LLV2, _LLL2, _LLR2) then
TreeR = red(K0, V0, LR, R0),
Tree = black(LK, LV, LL, TreeR)
else if LR = red(LRK, LRV, LRL, LRR) then
TreeL = red(LK, LV, LL, LRL),
TreeR = red(K0, V0, LRR, R0),
Tree = black(LRK, LRV, TreeL, TreeR)
else
Tree = black(K0, V0, NewL, R0)
)
else
Tree = black(K0, V0, NewL, R0)
)
)
).
%---------------------------------------------------------------------------%
member(empty, _K, _V) :- fail.
member(red(K0, V0, Left, Right), K, V) :-
(
K = K0,
V = V0
;
rbtree.member(Left, K, V)
;
rbtree.member(Right, K, V)
).
member(black(K0, V0, Left, Right), K, V) :-
(
K = K0,
V = V0
;
rbtree.member(Left, K, V)
;
rbtree.member(Right, K, V)
).
%---------------------------------------------------------------------------%
search(Tree, K, V) :-
( Tree = red(K0, V0, Left, Right)
; Tree = black(K0, V0, Left, Right)
),
compare(Result, K, K0),
(
Result = (=),
V = V0
;
Result = (<),
rbtree.search(Left, K, V)
;
Result = (>),
rbtree.search(Right, K, V)
).
lookup(RBT, K) = V :-
rbtree.lookup(RBT, K, V).
lookup(T, K, V) :-
( if rbtree.search(T, K, V0) then
V = V0
else
report_lookup_error("rbtree.lookup: key not found", K, V)
).
%---------------------------------------------------------------------------%
lower_bound_search(Tree, SearchK, K, V) :-
( Tree = red(K0, V0, Left, Right)
; Tree = black(K0, V0, Left, Right)
),
compare(Result, SearchK, K0),
(
Result = (=),
K = K0,
V = V0
;
Result = (<),
rbtree.lower_bound_search(Left, SearchK, K, V)
;
Result = (>),
( if rbtree.lower_bound_search(Right, SearchK, Kp, Vp) then
K = Kp,
V = Vp
else
K = K0,
V = V0
)
).
lower_bound_lookup(T, SearchK, K, V) :-
( if rbtree.lower_bound_search(T, SearchK, K0, V0) then
K = K0,
V = V0
else
report_lookup_error("rbtree.lower_bound_lookup: key not found",
SearchK, V)
).
%---------------------------------------------------------------------------%
upper_bound_search(Tree, SearchK, K, V) :-
( Tree = red(K0, V0, Left, Right)
; Tree = black(K0, V0, Left, Right)
),
compare(Result, SearchK, K0),
(
Result = (=),
K = K0,
V = V0
;
Result = (<),
( if rbtree.upper_bound_search(Left, SearchK, Kp, Vp) then
K = Kp,
V = Vp
else
K = K0,
V = V0
)
;
Result = (>),
rbtree.upper_bound_search(Right, SearchK, K, V)
).
upper_bound_lookup(T, SearchK, K, V) :-
( if rbtree.upper_bound_search(T, SearchK, K0, V0) then
K = K0,
V = V0
else
report_lookup_error("rbtree.upper_bound_lookup: key not found",
SearchK, V)
).
%---------------------------------------------------------------------------%
delete(!.RBT, K) = !:RBT :-
rbtree.delete(K, !RBT).
delete(K, !Tree) :-
rbtree.delete_2(!.Tree, K, no, _, !:Tree).
% delete_2(Tree0, Key, MustRemove, MaybeValue, Tree):
%
% Search the tree Tree0, looking for a node with key Key to delete.
% If MustRemove is `yes' and we don't find the key, fail.
% If we find the key, return it in MaybeValue and delete the node.
% Tree is the resulting tree, whether a node was removed or not.
%
% Deletion algorithm:
%
% Search down the tree, looking for the node to delete. O(log N)
% When we find it, there are 4 possible conditions ->
% * Leaf node
% Remove node O(1)
% * Left subtree of node to be deleted exists
% Move maximum key of Left subtree to current node. O(log N)
% * Right subtree of node to be deleted exists
% Move minimum key of Right subtree to current node. O(log N)
% * Both left and right subtrees of node to be deleted exist
% Move maximum key of Left subtree to current node. O(log N)
%
% Algorithm O(log N).
%
:- pred delete_2(rbtree(K, V), K, bool, maybe(V), rbtree(K, V)).
:- mode delete_2(in, in, in, out, out) is semidet.
:- mode delete_2(in, in, in(bound(no)), out, out) is det.
delete_2(empty, _K, no, no, empty).
delete_2(red(K0, V0, L, R), K, MustRemove, MaybeV, Tree) :-
compare(Result, K, K0),
(
Result = (=),
( if rbtree.remove_largest(NewK, NewV, L, NewL) then
Tree = red(NewK, NewV, NewL, R)
else
% L must be empty.
( if rbtree.remove_smallest(NewK, NewV, R, NewR) then
Tree = red(NewK, NewV, empty, NewR)
else
% R must be empty
Tree = empty
)
),
MaybeV = yes(V0)
;
Result = (<),
rbtree.delete_2(L, K, MustRemove, MaybeV, NewL),
Tree = red(K0, V0, NewL, R)
;
Result = (>),
rbtree.delete_2(R, K, MustRemove, MaybeV, NewR),
Tree = red(K0, V0, L, NewR)
).
delete_2(black(K0, V0, L, R), K, MustRemove, MaybeV, Tree) :-
compare(Result, K, K0),
(
Result = (=),
( if rbtree.remove_largest(NewK, NewV, L, NewL) then
Tree = black(NewK, NewV, NewL, R)
else
% L must be empty
( if rbtree.remove_smallest(NewK, NewV, R, NewR) then
Tree = black(NewK, NewV, empty, NewR)
else
% R must be empty
Tree = empty
)
),
MaybeV = yes(V0)
;
Result = (<),
rbtree.delete_2(L, K, MustRemove, MaybeV, NewL),
Tree = black(K0, V0, NewL, R)
;
Result = (>),
rbtree.delete_2(R, K, MustRemove, MaybeV, NewR),
Tree = black(K0, V0, L, NewR)
).
%---------------------------------------------------------------------------%
remove(K, V, !Tree) :-
rbtree.delete_2(!.Tree, K, yes, yes(V), !:Tree).
remove_smallest(_K, _V, empty, _Tree) :-
fail.
remove_smallest(NewK, NewV, red(K0, V0, L, R), Tree) :-
(
L = empty,
NewK = K0,
NewV = V0,
Tree = R
;
( L = red(_, _, _, _)
; L = black(_, _, _, _)
),
rbtree.remove_smallest(NewK, NewV, L, NewL),
Tree = red(K0, V0, NewL, R)
).
remove_smallest(NewK, NewV, black(K0, V0, L, R), Tree) :-
(
L = empty,
NewK = K0,
NewV = V0,
Tree = R
;
( L = red(_, _, _, _)
; L = black(_, _, _, _)
),
rbtree.remove_smallest(NewK, NewV, L, NewL),
Tree = black(K0, V0, NewL, R)
).
remove_largest(_K, _V, empty, _Tree) :-
fail.
remove_largest(NewK, NewV, red(K0, V0, L, R), Tree) :-
(
R = empty,
NewK = K0,
NewV = V0,
Tree = L
;
( R = red(_, _, _, _)
; R = black(_, _, _, _)
),
rbtree.remove_largest(NewK, NewV, R, NewR),
Tree = red(K0, V0, L, NewR)
).
remove_largest(NewK, NewV, black(K0, V0, L, R), Tree) :-
(
R = empty,
NewK = K0,
NewV = V0,
Tree = L
;
( R = red(_, _, _, _)
; R = black(_, _, _, _)
),
rbtree.remove_largest(NewK, NewV, R, NewR),
Tree = black(K0, V0, L, NewR)
).
%---------------------------------------------------------------------------%
keys(RBT) = Ks :-
rbtree.keys(RBT, Ks).
keys(empty, []).
keys(red(K0, _V0, L, R), List) :-
rbtree.keys(L, List0),
rbtree.keys(R, List1),
list.append(List0, [K0 | List1], List).
keys(black(K0, _V0, L, R), List) :-
rbtree.keys(L, List0),
rbtree.keys(R, List1),
list.append(List0, [K0 | List1], List).
%---------------------------------------------------------------------------%
values(RBT) = Vs :-
rbtree.values(RBT, Vs).
values(empty, []).
values(red(_K0, V0, L, R), List) :-
rbtree.values(L, List0),
rbtree.values(R, List1),
list.append(List0, [V0 | List1], List).
values(black(_K0, V0, L, R), List) :-
rbtree.values(L, List0),
rbtree.values(R, List1),
list.append(List0, [V0 | List1], List).
%---------------------------------------------------------------------------%
count(RBT) = N :-
rbtree.count(RBT, N).
count(empty, 0).
count(red(_K, _V, L, R), N) :-
rbtree.count(L, NO),
rbtree.count(R, N1),
N = 1 + NO + N1.
count(black(_K, _V, L, R), N) :-
rbtree.count(L, NO),
rbtree.count(R, N1),
N = 1 + NO + N1.
%---------------------------------------------------------------------------%
assoc_list_to_rbtree(AL) = RBT :-
rbtree.assoc_list_to_rbtree(AL, RBT).
assoc_list_to_rbtree([], empty).
assoc_list_to_rbtree([K - V | T], Tree) :-
rbtree.assoc_list_to_rbtree(T, Tree0),
rbtree.set(K, V, Tree0, Tree).
from_assoc_list(AList) = rbtree.assoc_list_to_rbtree(AList).
%---------------------------------------------------------------------------%
rbtree_to_assoc_list(RBT) = AL :-
rbtree.rbtree_to_assoc_list(RBT, AL).
rbtree_to_assoc_list(empty, []).
rbtree_to_assoc_list(red(K0, V0, Left, Right), L) :-
rbtree.rbtree_to_assoc_list(Left, L0),
rbtree.rbtree_to_assoc_list(Right, L1),
list.append(L0, [K0 - V0|L1], L).
rbtree_to_assoc_list(black(K0, V0, Left, Right), L) :-
rbtree.rbtree_to_assoc_list(Left, L0),
rbtree.rbtree_to_assoc_list(Right, L1),
list.append(L0, [K0 - V0|L1], L).
to_assoc_list(T) = rbtree.rbtree_to_assoc_list(T).
%---------------------------------------------------------------------------%
foldl(F, T, A) = B :-
P = ( pred(W::in, X::in, Y::in, Z::out) is det :- Z = F(W, X, Y) ),
rbtree.foldl(P, T, A, B).
foldl(_Pred, empty, !Acc).
foldl(Pred, red(K, V, Left, Right), !Acc) :-
rbtree.foldl(Pred, Left, !Acc),
Pred(K, V, !Acc),
rbtree.foldl(Pred, Right, !Acc).
foldl(Pred, black(K, V, Left, Right), !Acc) :-
rbtree.foldl(Pred, Left, !Acc),
Pred(K, V, !Acc),
rbtree.foldl(Pred, Right, !Acc).
%---------------------------------------------------------------------------%
foldl2(_, empty, !Acc1, !Acc2).
foldl2(Pred, red(K, V, Left, Right), !Acc1, !Acc2) :-
rbtree.foldl2(Pred, Left, !Acc1, !Acc2),
Pred(K, V, !Acc1, !Acc2),
rbtree.foldl2(Pred, Right, !Acc1, !Acc2).
foldl2(Pred, black(K, V, Left, Right), !Acc1, !Acc2) :-
rbtree.foldl2(Pred, Left, !Acc1, !Acc2),
Pred(K, V, !Acc1, !Acc2),
rbtree.foldl2(Pred, Right, !Acc1, !Acc2).
%---------------------------------------------------------------------------%
foldl3(_, empty, !Acc1, !Acc2, !Acc3).
foldl3(Pred, red(K, V, Left, Right), !Acc1, !Acc2, !Acc3) :-
rbtree.foldl3(Pred, Left, !Acc1, !Acc2, !Acc3),
Pred(K, V, !Acc1, !Acc2, !Acc3),
rbtree.foldl3(Pred, Right, !Acc1, !Acc2, !Acc3).
foldl3(Pred, black(K, V, Left, Right), !Acc1, !Acc2, !Acc3) :-
rbtree.foldl3(Pred, Left, !Acc1, !Acc2, !Acc3),
Pred(K, V, !Acc1, !Acc2, !Acc3),
rbtree.foldl3(Pred, Right, !Acc1, !Acc2, !Acc3).
%---------------------------------------------------------------------------%
foldl_values(_Pred, empty, !Acc).
foldl_values(Pred, red(_K, V, Left, Right), !Acc) :-
rbtree.foldl_values(Pred, Left, !Acc),
Pred(V, !Acc),
rbtree.foldl_values(Pred, Right, !Acc).
foldl_values(Pred, black(_K, V, Left, Right), !Acc) :-
rbtree.foldl_values(Pred, Left, !Acc),
Pred(V, !Acc),
rbtree.foldl_values(Pred, Right, !Acc).
%---------------------------------------------------------------------------%
foldl2_values(_Pred, empty, !Acc1, !Acc2).
foldl2_values(Pred, red(_K, V, Left, Right), !Acc1, !Acc2) :-
rbtree.foldl2_values(Pred, Left, !Acc1, !Acc2),
Pred(V, !Acc1, !Acc2),
rbtree.foldl2_values(Pred, Right, !Acc1, !Acc2).
foldl2_values(Pred, black(_K, V, Left, Right), !Acc1, !Acc2) :-
rbtree.foldl2_values(Pred, Left, !Acc1, !Acc2),
Pred(V, !Acc1, !Acc2),
rbtree.foldl2_values(Pred, Right, !Acc1, !Acc2).
%---------------------------------------------------------------------------%
map_values(F, T1) = T2 :-
P = ( pred(X::in, Y::in, Z::out) is det :- Z = F(X, Y) ),
rbtree.map_values(P, T1, T2).
map_values(_Pred, empty, empty).
map_values(Pred, Tree0, Tree) :-
Tree0 = red(K0, V0, Left0, Right0),
Pred(K0, V0, W0),
rbtree.map_values(Pred, Left0, Left),
rbtree.map_values(Pred, Right0, Right),
Tree = red(K0, W0, Left, Right).
map_values(Pred, Tree0, Tree) :-
Tree0 = black(K0, V0, Left0, Right0),
Pred(K0, V0, W0),
rbtree.map_values(Pred, Left0, Left),
rbtree.map_values(Pred, Right0, Right),
Tree = black(K0, W0, Left, Right).
%---------------------------------------------------------------------------%
:- end_module rbtree.
%---------------------------------------------------------------------------%