diff --git a/README.md b/README.md index 5ece911..b97581d 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,14 @@ A general balanced tree iterator. ## EXPORTS ## +### copy(Tree1, Tree2) -> Tree3 ### + +Types: + + Tree1 = Tree2 = Tree3 = b_tree() | gb_trees:tree() + +Copies tree Tree1 to an empty tree Tree2. Both trees may be either of type b-tree or binary tree (gb_trees). Returns the new tree Tree3 of the same type as tree Tree2. + ### delete(Key, B-Tree1) -> B-Tree2 ### Types: @@ -78,7 +86,7 @@ Types: Order = pos_integer() B-Tree = b_tree() -Returns a new empty b-tree. The order is defined as the maximum number of children nodes a non-leaf node may hold. +Returns a new empty b-tree. The order Order (min. 4) is defined as the maximum number of children nodes a non-leaf node may hold. ### enter (Key, Value, B-Tree1) -> B-Tree2 ### diff --git a/eep-XXXX.md b/eep-XXXX.md deleted file mode 100644 index 2b9dd9b..0000000 --- a/eep-XXXX.md +++ /dev/null @@ -1,614 +0,0 @@ - Author: Walter Weinmann - Status: Draft - Type: Standards Track - Created: 06-Dec-2016 - Erlang-Version: OTP 20.0 - Post-History: -**** -EEP XXX: B-trees: balanced search trees of order n. ----- - - - -Abstract -======== - -This EEP proposes the creation of a new module named b_trees for the -administration of b-trees. Both the optional persistence and the sort -order should be implemented by pluggable functionality. - - - -Copyright -========= - -This document has been placed in the public domain. - - - -Specification -============= - - -## Data Structure ## - - {MinimumSubtrees, - MaximumKeys, - SizeKeyValues, - SortFunction/2, - State, - Tree} - -`Tree` is composed of nodes of the form - - {KeyNumber, - SubtreeNumber, - [{Key, Value}], - [Tree]} - -and the "empty b-tree" node - - nil - -`State` is a tuple composed of the following parameters: - - {StateTarget, - DeleteFunction/3, - InsertFunction/3, - LookupFunction/3} - -Since the b-trees are always balanced, there is no need for a balance -operation. - - -## DATA TYPES ## - - b_tree() = {pos_integer(), - pos_integer(), - non_neg_integer(), - sort_function(), - state(), - tree()} - -A general balanced tree. - - iterator() = [{key_values(), subtrees()}] - -A general balanced tree iterator. - - -## EXPORTS ## - -### delete(Key, B-Tree1) -> B-Tree2 ### - -Types: - - Key = any() - B-Tree1 = B-Tree2 = b_tree() - -Removes the node with key Key from b-tree B-Tree1 and returns the new -b-tree B-Tree2. Assumes that key Key is present in b-tree B-Tree1, -crashes otherwise. - -### delete_any (Key, B-Tree1) -> B-Tree2 ### - -Types: - - Key = any() - B-Tree1 = B-Tree2 = b_tree() - -Removes the node with key Key from b-tree B-Tree1 if key Key is present -in b-tree B-Tree1, otherwise does nothing. Returns the new b-tree B-Tree2. - -### empty (Order) -> B-Tree ### - -Types: - - Order = pos_integer() - B-Tree = b_tree() - -Returns a new empty b-tree. The order is defined as the maximum number -of children nodes a non-leaf node may hold. - -### enter (Key, Value, B-Tree1) -> B-Tree2 ### - -Types: - - Key = any() - Value = any() - B-Tree1 = B-Tree2 = b_tree() - -Inserts key Key with value Value into b-tree B-Tree1 if key Key is not -present in b-tree B-Tree1, otherwise updates the current value of key Key -to value Value in b-tree B-Tree1. Returns a new b-tree B-Tree2. - -### from_dict (B-Tree1, List) -> B-Tree2 ### - -Types: - - B-Tree1 = B-Tree2 = b_tree() - List = [{Key, Value}] - - -Turns an ordered list List of key value tuples into a b-tree. The given -b-tree B-Tree1 must be empty. The list must not contain duplicate keys. - -### get (Key, B-Tree) -> Value ### - -Types: - - Key = any() - B-Tree = b_tree() - Value = any() - -Retrieves the value stored with key Key in b-tree B-Tree. Assumes that -key Key is present in b-tree B-Tree, crashes otherwise. - -### height (B-Tree) -> integer() >= 0 ### - -Types: - - B-Tree = b_tree() - -Returns the height of b-tree B-Tree as an integer. Assumes that b-tree -B-Tree is non-empty. - -### insert (Key, Value, B-Tree1) -> B-Tree2 ### - -Types: - - Key = any() - Value = any() - B-Tree1 = B-Tree2 = b_tree() - -Inserts key Key with value Value into b-tree B-Tree1 and returns the new -b-tree B-Tree2. Assumes that key Key is **not** present in b-tree B-Tree1, -crashes otherwise. - -### is_defined (Key, B-Tree) -> boolean() ### - -Types: - - Key = any() - B-Tree = b_tree() - -Returns `true` if key Key is present in b-tree B-Tree, otherwise `false`. - -### is_empty (B-Tree) -> boolean() ### - -Types: - - B-Tree = b_tree() - -Returns `true` if b-tree B-Tree is an empty b-tree, otherwise `false`. - -### iterator (B-Tree) -> Iterator ### - -Types: - - B-Tree = b_tree() - Iterator = iterator() - -Returns iterator Iterator that can be used for traversing the entries -of b-tree B-Tree; see `next/1`. The implementation of this iterator is -very efficient; traversing the whole b-tree using `next/1` is only slightly -slower than getting the list of all key-value pairs using `to_list/1` -and traversing that. The main advantage of the iterator approach is that -it does not require the complete list of all key-value pairs to be built -in memory at one time. - -### iterator_from (Key, B-Tree) -> Iterator ### - -Types: - - Key = any(9 - B-Tree = b_tree() - Iterator = iterator() - -Returns iterator Iterator that can be used for traversing the entries -of b-tree B-Tree; see `next/1`. The difference, as compared to the -iterator returned by iterator/1, is that the first key greater than -or equal to key Key is returned. - -### keys (B-Tree) -> [Key] ### - -Types: - - B-Tree = b_tree() - Key = any() - -Returns the keys in b-tree B-Tree as an ordered list. - -### largest (B-Tree) -> {Key, Value} ### - -Types: - - B-Tree = b_tree() - Key = any() - Value = any() - -Returns a tuple {Key, Value}, where Key is the largest key in b-tree -B-Tree, and Value is the value associated with this key. Assumes that -b-tree B-Tree is not empty. - -### lookup (Key, B-Tree) -> none | {value, Value} ### - -Types: - - Key = any() - B-Tree = b_tree() - Value = any() - -Looks up key Key in b-tree B-Tree. Returns {value, Value}, or none if -key Key is not present. - -### map (Function, B-Tree1) -> B-Tree2 ### - -Types: - - Function = fun((Key, Value1) -> Value2) - B-Tree1 = B-Tree2 = b_tree() - Key = any() - Value1 = Value2 = any() - -Maps function Function(Key, Value1) -> Value2 to all key value pairs of -b-tree B-Tree1. Returns the new b-tree B-Tree2 with the same set of -keys as b-tree B-Tree1 and the new set of values. - -### next (Iterator1) -> 'none' | {Key, Value, Iterator2} ### - -Types: - - Iterator1 = Iterator2 = iterator() - Key = any() - Value = any() - - -Returns the tuple {Key, Value, Iterator2}, where Key is the smallest -key referred to by iterator Iterator1, and iterator Iterator2 is the -new iterator to be used for traversing the remaining nodes, or the -atom '**none**' if no nodes remain. - -### set_parameter (B-Tree1, Name, Value) -> B-Tree2 ### - -Types: - - B-Tree1 = B-Tree2 = b_tree() - Name : Value = sort : Function = fun((Key1, Key2) -> equal | - greater | - less) - | state : {StateTarget, - Function = fun(StateTarget, delete, Key) - -> true, - Function = fun(StateTarget, insert, Subtrees) - -> Key, - Function = fun(StateTarget, lookup, Key) - -> Subtrees} - -Sets the parameter Name to value Value in the empty b-tree B-Tree1 and -returns the new b-tree B-Tree2. This function can only be used in -conjunction with an empty b-tree. - -### size_key_values (B-Tree) -> integer() >= 0 ### - -Types: - - B-Tree = b_tree() - -Returns the number of key value pairs in b-tree B-Tree as an integer. -Returns 0 (zero) if b-tree B-Tree is empty. - -### size_nodes (B-Tree) -> {integer() >= 0, integer() >= 0} ### - -Types: - - B-Tree = b_tree() - -Returns the number of total nodes and the number of leaf nodes in b-tree -B-Tree as a tuple of two integers. Returns {0, 0} (zero) if b-tree B-Tree -is empty. - -### smallest (B-Tree) -> {Key, Value} ### - -Types: - - B-Tree = b_tree() - Key = any() - Value = any() - -Returns tuple {Key, Value}, where Key is the smallest key in b-tree -B-Tree, and Value is the value associated with this key. Assumes that -b-tree B-Tree is not empty. - -### sort_ascending (Key1, Key2) -> 'equal' | 'greater' | 'less' ### - -Types: - - Key1 = Key2 = any() - equal = greater = less = atom() - -Returns the atom '**greater**' if Key1 > Key2, the atom '**less**' -if Key1 < Key2 and otherwise the atom '**equal**'. - -### sort_descending (Key1, Key2) -> 'equal' | 'greater' | 'less' ### - -Types: - - Key1 = Key2 = any() - equal = greater = less = atom() - -Returns the atom '**less**' if Key1 > Key2, the atom '**greater**' -if Key1 < Key2 and otherwise the atom '**equal**'. - -### take(Key, B-Tree1) -> B-Tree2 ### - -Types: - - Key = any() - B-Tree1 = B-Tree2 = b_tree() - -Removes the node with key Key from b-tree B-Tree1 and returns the new -b-tree B-Tree2. Assumes that key Key is present in b-tree B-Tree1, -crashes otherwise. - -### delete_any (Key, B-Tree1) -> B-Tree2 ### - -Types: - - Key = any() - B-Tree1 = B-Tree2 = b_tree() - -Removes the node with key Key from b-tree B-Tree1 if key Key is present -in b-tree B-Tree1, otherwise does nothing. Returns the new b-tree B-Tree2. - -### take_largest (B-Tree1) -> {Key, Value, B-Tree2} ### - -Types: - - B-Tree1 = B-Tree2 = b_tree() - Key = any() - Value = any() - -Returns tuple {Key, Value, B-Tree2}, where Key is the largest key in -b-tree B-Tree1, Value is the value associated with this key, and b-tree -B-Tree2 is this b-tree with the corresponding key value pair deleted. -Assumes that b-tree B-Tree1 is not empty. - -### take_smallest (B-Tree1) -> {Key, Value, B-Tree2} ### - -Types: - - B-Tree1 = B-Tree2 = b_tree() - Key = any() - Value = any() - -Returns tuple {Key, Value, B-Tree2}, where Key is the smallest key in -b-tree B-Tree1, Value is the value associated with this key, and b-tree -B-Tree2 is this b-tree with the corresponding key value pair deleted. -Assumes that b-tree B-Tree1 is not empty. - -### to_list (B-Tree) -> [{Key, Value}] ### - -Types: - - B-Tree = b_tree() - Key = any() - Value = any() - -Converts b-tree B-Tree into an ordered list of key value tuples. - -### update (Key, Value, B-Tree1) -> B-Tree2 ### - -Types: - - Key = any() - Value = any() - B-Tree1 = B-Tree2 = b_tree() - - -Updates key Key to value Value in b-tree B-Tree1 and returns the new -b-tree B-Tree2. Assumes that key Key is present in b-tree B-Tree1. - -### values (B-Tree) -> [Value] ### - -Types: - - B-Tree = b_tree() - Value = any() - -Returns the values in b-tree B-Tree as an ordered list, sorted by their -corresponding keys. Duplicates are not removed. - - -## Pluggable Persistence Functionality ## - -### Format: ### - - {StateTarget, DeleteFunction, InsertFunction, LookupFunction} - - StateTarget = any() - - DeleteFunction(StateTarget, delete, Key) -> true - - InsertFunction(StateTarget, insert, Subtrees) -> Key - - LookupFunction(StateTarget, lookup, Key) -> Subtrees - -Examples for state targets are a Dets table or a Mnesia table. The delete -function takes a state target, the atom `delete` and a key as arguments -and returns the atom `true` if successful. The insert function takes a -state target, the atom `insert` and a subtrees data structure as arguments -and returns a key if successful. The lookup function takes a state target, -the atom `lookup` and a key as arguments and returns a subtrees data -structure if successful. - -### Example functions: ### - -The following examples are based on Mnesia. - - persistence_by_mnesia(_, delete, SubtreesKey) - when is_list(SubtreesKey) -> - true; - persistence_by_mnesia(StateTarget, delete, SubtreesKey) -> - F = fun() -> - ok = mnesia:delete({StateTarget, SubtreesKey}), - true - end, - mnesia:activity(transaction, F); - - persistence_by_mnesia(_, insert, []) -> - []; - persistence_by_mnesia(StateTarget, insert, - [{_, _, [{Key, _} | _], _} | _] = Subtrees) -> - SubtreesKey = list_to_binary(Key), - F = fun() -> - ok = mnesia:write(StateTarget, - #subtrees{subtreesKey = SubtreesKey, - subtrees = Subtrees}, write), - SubtreesKey - end, - mnesia:activity(transaction, F); - - persistence_by_mnesia(_, lookup, SubtreesKey) - when is_list(SubtreesKey) -> - SubtreesKey; - persistence_by_mnesia(StateTarget, lookup, SubtreesKey) -> - F = fun() -> - [{subtrees, SubtreesKey, Subtrees}] = mnesia:read(StateTarget, - SubtreesKey), - Subtrees - end, - mnesia:activity(transaction, F). - -### Example usage: ### - -Creating the Mnesia table: - - -record(subtrees, {subtreesKey, subtrees}). - - {atomic, ok} = mnesia:create_table(StateTargetName, [{record_name, - subtrees}]), - -Creating the b-tree: - - BTree1 = b_trees:empty(500), - BTree2 = b_trees:set_parameter(BTree1, state, - {StateTargetName, - fun persistence_by_mnesia/3, - fun persistence_by_mnesia/3, - fun persistence_by_mnesia/3}), - - -## Pluggable Sort Functionality ## - -### Format: ### - - FunctionName(Key1, Key2) -> equal | greater | less - - Key1 = Key2 = any() - -The sort function takes two keys as arguments and returns the atom `less` -if Key1 < Key2, the atom `greater` if Key1 > Key2 and otherwise the -atom `equal`. - -### Example function: ### - - -spec sort_descending(key(), key()) -> sort_result(). - - sort_descending(Key_1, Key_2) -> - if - Key_1 < Key_2 -> greater; - Key_1 > Key_2 -> less; - true -> equal - end. - -### Example usage: ### - - BTree1 = b_trees:empty(500), - BTree2 = b_trees:set_parameter(BTree1, sort, fun sort_descending/2), - - - -Motivation -========== - -B-trees are self-balancing tree data structures that keep data sorted -and allow searches, sequential access, insertions, and deletions in -logarithmic time. B-trees are a generalization of a binary search -trees in that a node can have more than two children. Unlike self-balancing -binary search trees, the b-tree is optimized for systems that read and -write large blocks of data. B-trees are a good example of a data structure -for external memory. - - - -Rationale -========= - -The functional design of the module b_trees is based on the module gb_trees: - - b_trees | gb_trees - ------------------|--------- - n/a | balance/1 - delete/2 | delete/2 - delete_any/2 | delete_any/2 - empty/1 | empty/0 - enter/3 | enter/3 - from_dict/2 | from_orddict/1 - get/2 | get/2 - height/1 | n/a - insert/3 | insert/3 - is_defined/2 | is_defined/2 - is_empty/1 | is_empty/1 - iterator/1 | iterator/1 - iterator_from/2 | iterator_from/2 - keys/1 | keys/1 - largest/1 | largest/1 - lookup/2 | lookup/2 - map/2 | map/2 - next/1 | next/1 - set_parameter/3 | n/a - size_key_values/1| size/1 - size_nodes/1 | n/a - smallest/1 | smallest/1 - sort_ascending/2 | n/a - sort_descending/2| n/a - take/2 | take/2 - take_any/2 | take_any/2 - take_largest/1 | take_largest/1 - take_smallest/1 | take_smallest/1 - to_list/1 | to_list/1 - update/3 | update/3 - values/1 | values/1 - -The functions `delete/2` and `insert/3` are implementations of the algorithms -of Cormen, Thomas; Leiserson, Charles; Rivest, Ronald; Stein, Clifford (2009), -Introduction to Algorithms (Third ed.), MIT Press and McGraw-Hill, pp. 484-504, -ISBN 0-262-03384-4. Chapter 18: B-Trees. - - - -Backwards Compatibility -======================= - -No issues - except module name collisions. - - - -Reference Implementation -======================== - -The reference implementation can be fetched from Github: - - https://github.com/walter-weinmann/b_trees - - - -[EmacsVar]: <> "Local Variables:" -[EmacsVar]: <> "mode: indented-text" -[EmacsVar]: <> "indent-tabs-mode: nil" -[EmacsVar]: <> "sentence-end-double-space: t" -[EmacsVar]: <> "fill-column: 70" -[EmacsVar]: <> "coding: utf-8" -[EmacsVar]: <> "End:" -[VimVar]: <> " vim: set fileencoding=utf-8 expandtab shiftwidth=4 softtabstop=4: " diff --git a/include/b_trees_templates.hrl b/include/b_trees_templates.hrl index 5729cf8..ab33283 100644 --- a/include/b_trees_templates.hrl +++ b/include/b_trees_templates.hrl @@ -3405,3 +3405,41 @@ } } ). +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% GB-trees. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-define(GB_TREE_10, + {10, + { + "k_04", "v_04", + { + "k_02", "v_02", + { + "k_01", "v_01", nil, nil + }, + { + "k_03", "v_03", nil, nil + } + }, + { + "k_06", "v_06", + { + "k_05", "v_05", nil, nil}, + { + "k_07", "v_07", nil, + { + "k_08", "v_08", nil, + { + "k_09", "v_09", nil, + { + "k_10", "v_10", nil, nil + } + } + } + } + } + } + } +). + diff --git a/src/b_trees.erl b/src/b_trees.erl index 78e0bf8..27921b2 100644 --- a/src/b_trees.erl +++ b/src/b_trees.erl @@ -31,6 +31,10 @@ %% ----------------------------------------------------------------------------- %% Operations: %% +%% - copy(T1, T2): copies tree T1 to an empty tree T2. Both trees may be either +%% of type b-tree or binary tree (gb_trees). Returns a new tree of the same +%% type as tree T2. +%% %% - delete(K, B): removes key K from b-tree B; returns a new b-tree B'. Assumes %% that the key is present in the b-tree. %% @@ -139,6 +143,7 @@ -module(b_trees). -export([ + copy/2, delete/2, delete_any/2, empty/1, @@ -220,6 +225,77 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec copy(b_tree() | gb_trees:tree(), b_tree() | gb_trees:tree()) -> b_tree() | gb_trees:tree(). + +% Copy tree to b-tree. +copy(Tree1, {MinimumSubtrees2, _, 0, _, State2, nil} = Tree2) -> + case Tree1 of + {_, _, 0, _, _, nil} -> + Tree2; + {0, nil} -> + Tree2; + {MinimumSubtrees1, _, _, _, State1, _} -> + case MinimumSubtrees1 == MinimumSubtrees2 andalso State1 == State2 of + true -> + Tree1; + _ -> + copy_b_tree_to_b_tree(Tree1, Tree2) + end; + _ -> + copy_binary_tree_to_b_tree(Tree1, Tree2) + end; +% Copy tree to binary tree. +copy(Tree1, {0, nil} = Tree2) -> + case Tree1 of + {_, _, 0, _, _, nil} -> + Tree2; + {_, _, _, _, _, _} -> + copy_b_tree_to_binary_tree(Tree1, Tree2); + _ -> + Tree1 + end. + +% wwe -spec copy_b_tree_to_b_tree(b_tree(), b_tree()) -> b_tree(). + +% Copy b-tree to b-tree. +copy_b_tree_to_b_tree(BTree1, BTree2) -> + copy_b_tree_to_b_tree_iterator(next(iterator(BTree1)), BTree2). + +% wwe -spec copy_b_tree_to_b_tree_iterator({key(), value(), iter()}|none, b_tree()) -> b_tree(). + +copy_b_tree_to_b_tree_iterator(none, BTree) -> + BTree; +copy_b_tree_to_b_tree_iterator({Key, Value, Iterator}, BTree) -> + copy_b_tree_to_b_tree_iterator(next(Iterator), insert(Key, Value, BTree)). + +% wwe -spec copy_b_tree_to_binary_tree(b_tree(), gb_trees:tree(key(), value())) -> gb_trees:tree(key(), value()). + +% Copy b-tree to binary tree. +copy_b_tree_to_binary_tree(BTree, GBTree) -> + copy_b_tree_to_binary_tree_iterator(next(iterator(BTree)), GBTree). + +% wwe -spec copy_b_tree_to_binary_tree_iterator(none | {key(), value(), iter()}, gb_trees:tree(key(), value())) -> gb_trees:tree(key(), value()). + +copy_b_tree_to_binary_tree_iterator(none, GBTree) -> + GBTree; +copy_b_tree_to_binary_tree_iterator({Key, Value, Iterator}, GBTree) -> + copy_b_tree_to_binary_tree_iterator(next(Iterator), gb_trees:insert(Key, Value, GBTree)). + +% wwe -spec copy_binary_tree_to_b_tree(gb_trees:tree(), b_tree()) -> b_tree(). + +% Copy binary tree to b-tree. +copy_binary_tree_to_b_tree(GBTree, BTree) -> + copy_binary_tree_to_b_tree_iterator(gb_trees:next(gb_trees:iterator(GBTree)), BTree). + +% wwe -spec copy_binary_tree_to_b_tree_iterator({key(), value(), gb_trees:iter()}|none, b_tree()) -> b_tree(). + +copy_binary_tree_to_b_tree_iterator(none, BTree) -> + BTree; +copy_binary_tree_to_b_tree_iterator({Key, Value, Iterator}, BTree) -> + copy_binary_tree_to_b_tree_iterator(gb_trees:next(Iterator), insert(Key, Value, BTree)). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % delete. Assumes that key is present. -spec delete(key(), b_tree()) -> b_tree(). diff --git a/src/b_trees.xml b/src/b_trees.xml index 6fb1825..e5899d4 100644 --- a/src/b_trees.xml +++ b/src/b_trees.xml @@ -72,6 +72,18 @@ + + + Copy a tree to an empty tree. + +

Copies tree Tree1 to an empty tree + Tree2. Both trees may be either of + b-tree or binary tree (gb_trees). Returns the new tree + Tree3 of the same type as tree + Tree2.

+
+
+ Remove a node from a b-tree. diff --git a/src/b_trees_generator.erl b/src/b_trees_generator.erl index c4099ac..5b762e1 100644 --- a/src/b_trees_generator.erl +++ b/src/b_trees_generator.erl @@ -13,7 +13,7 @@ -define(B_TREE_POS_SORT, 4). -define(B_TREE_POS_STATE, 5). --define(DIRECTORY_DETS, "test/tmp/"). +-define(DIRECTORY_DETS, "/test/tmp/"). -define(VALUE_UPDATE_SUFFIX, "_new"). -export([ @@ -31,8 +31,10 @@ delete_b_tree_from_odd/4, delete_b_tree_list/2, delete_b_tree_till/3, + delete_directory/1, delete_gb_tree_from/2, ets_owner/0, + generate_b_tree_by_key_1/4, generate_b_tree_from_number/3, generate_b_tree_from_number/4, generate_b_tree_from_number_desc/3, @@ -749,6 +751,16 @@ delete_b_tree_1([Key | Tail], BTree) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +delete_directory(Dir) -> + Paths = filelib:wildcard(Dir ++ "/**"), + {Dirs, Files} = lists:partition(fun filelib:is_dir/1, Paths), + ok = lists:foreach(fun file:delete/1, Files), + Sorted = lists:reverse(lists:sort(Dirs)), + ok = lists:foreach(fun file:del_dir/1, Sorted), + file:del_dir(Dir). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + -spec delete_gb_tree_1([non_neg_integer()], gb_trees:tree()) -> gb_trees:tree(). delete_gb_tree_1([], GBTree) -> diff --git a/test/b_trees_test.erl b/test/b_trees_test.erl index e8718e4..ede8809 100644 --- a/test/b_trees_test.erl +++ b/test/b_trees_test.erl @@ -13,8 +13,207 @@ -include_lib("../include/b_trees_templates.hrl"). +-define(DIRECTORY_DETS, "/test/tmp/"). -define(TIMEOUT, 60). +%%-------------------------------------------------------------------- +%% TEST CASES: copy +%%-------------------------------------------------------------------- + +copy_test() -> + + %%---------------------------------------------------------------- + %% Initializing + %%---------------------------------------------------------------- + + ok = filelib:ensure_dir(?DIRECTORY_DETS), + ok = b_trees_generator:delete_directory(?DIRECTORY_DETS), + ok = filelib:ensure_dir(?DIRECTORY_DETS), + {ok, _} = dets:open_file(b_tree_4_dets, [{file, ?DIRECTORY_DETS ++ "b_tree_4_dets"}]), + {ok, _} = dets:open_file(b_tree_4_dets_empty, [{file, ?DIRECTORY_DETS ++ "b_tree_4_dets_empty"}]), + + case ok == mnesia:create_schema([node()]) of + true -> + ok; + _ -> + ok = mnesia:delete_schema([node()]), + mnesia:create_schema([node()]) + end, + ok = mnesia:start(), + + %%---------------------------------------------------------------- + %% Creating templates. + %%---------------------------------------------------------------- + + B_TREE_04_EMPTY = b_trees:empty(4), + B_TREE_04_EMPTY_DETS = b_trees:set_parameter(b_trees:empty(4), state, {b_tree_4_dets_empty, fun b_trees_generator:persistence_by_dets/3, fun b_trees_generator:persistence_by_dets/3, fun b_trees_generator:persistence_by_dets/3}), + B_TREE_04_EMPTY_ETS = b_trees_generator:generate_b_tree_from_number_ets(4, 0, 2, b_tree_4_ets_empty, spawn(fun b_trees_generator:ets_owner/0)), + B_TREE_04_EMPTY_MEMORY = b_trees:empty(4), + B_TREE_04_EMPTY_MNESIA = b_trees_generator:generate_b_tree_from_number_mnesia(4, 0, 2, b_tree_4_mnesia_empty), + + B_TREE_04_64 = b_trees_generator:generate_b_tree_from_number(4, 64, 2), + B_TREE_04_64_DETS_0 = b_trees:set_parameter(b_trees:empty(4), state, {b_tree_4_dets_empty, fun b_trees_generator:persistence_by_dets/3, fun b_trees_generator:persistence_by_dets/3, fun b_trees_generator:persistence_by_dets/3}), + B_TREE_04_64_DETS = b_trees_generator:generate_b_tree_by_key_1(lists:seq(1, 64), [], 2, B_TREE_04_64_DETS_0), + B_TREE_04_64_ETS = b_trees_generator:generate_b_tree_from_number_ets(4, 64, 2, b_tree_4_ets, spawn(fun b_trees_generator:ets_owner/0)), + B_TREE_04_64_MNESIA = b_trees_generator:generate_b_tree_from_number_mnesia(4, 64, 2, b_tree_4_mnesia), + + GB_TREE_EMPTY = gb_trees:empty(), + GB_TREE_64 = b_trees_generator:generate_gb_tree_from_number(64, 2), + + %%---------------------------------------------------------------- + %% binary tree ===> + %%---------------------------------------------------------------- + + % binary tree ===> binary_tree + GB_TREE_64_COPY_1 = b_trees:copy(GB_TREE_64, GB_TREE_EMPTY), + ?assertEqual(GB_TREE_64, GB_TREE_64_COPY_1), + + % binary tree ===> b_tree in memory + B_TREE_04_64_COPY = b_trees:copy(GB_TREE_64, B_TREE_04_EMPTY_MEMORY), + ?assertEqual(B_TREE_04_64, B_TREE_04_64_COPY), + + % binary tree ===> b_tree in dets + B_TREE_04_64_DETS_COPY = b_trees:copy(GB_TREE_64, B_TREE_04_EMPTY_DETS), + b_trees_generator:check_equal(B_TREE_04_64_DETS, B_TREE_04_64_DETS_COPY), + + % binary tree ===> b_tree in ets + B_TREE_04_64_ETS_COPY = b_trees:copy(GB_TREE_64, B_TREE_04_EMPTY_ETS), + b_trees_generator:check_equal(B_TREE_04_64_ETS, B_TREE_04_64_ETS_COPY), + + % binary tree ===> b_tree in mnesia + B_TREE_04_64_MNESIA_COPY = b_trees:copy(GB_TREE_64, B_TREE_04_EMPTY_MNESIA), + b_trees_generator:check_equal(B_TREE_04_64_MNESIA, B_TREE_04_64_MNESIA_COPY), + + %%---------------------------------------------------------------- + %% binary tree empty ===> + %%---------------------------------------------------------------- + + % binary tree ===> b_tree + B_TREE_COPY_1_1 = b_trees:copy(GB_TREE_EMPTY, B_TREE_04_EMPTY_DETS), + ?assertEqual(B_TREE_04_EMPTY_DETS, B_TREE_COPY_1_1), + + % binary tree ===> binary tree + GB_TREE_COPY_1_1 = b_trees:copy(GB_TREE_EMPTY, GB_TREE_EMPTY), + ?assertEqual(GB_TREE_EMPTY, GB_TREE_COPY_1_1), + + %%---------------------------------------------------------------- + %% b_tree empty ===> + %%---------------------------------------------------------------- + + % b_tree ===> b_tree + B_TREE_COPY_1_2 = b_trees:copy(B_TREE_04_EMPTY, B_TREE_04_EMPTY_DETS), + ?assertEqual(B_TREE_04_EMPTY_DETS, B_TREE_COPY_1_2), + + % b_tree ===> binary tree + GB_TREE_COPY_1_2 = b_trees:copy(B_TREE_04_EMPTY, GB_TREE_EMPTY), + ?assertEqual(GB_TREE_EMPTY, GB_TREE_COPY_1_2), + + %%---------------------------------------------------------------- + %% b_tree memory ===> + %%---------------------------------------------------------------- + + % b-tree in memory ===> b-tree in memory + B_TREE_04_64_COPY_2_1 = b_trees:copy(B_TREE_04_64, B_TREE_04_EMPTY), + ?assertEqual(B_TREE_04_64, B_TREE_04_64_COPY_2_1), + + % b-tree in memory ===> b-tree in dets + B_TREE_04_64_COPY_2_2 = b_trees:copy(B_TREE_04_64, B_TREE_04_EMPTY_DETS), + b_trees_generator:check_equal(B_TREE_04_64_DETS, B_TREE_04_64_COPY_2_2), + + % b-tree in memory ===> b-tree in ets + B_TREE_04_64_COPY_2_3 = b_trees:copy(B_TREE_04_64, B_TREE_04_EMPTY_ETS), + b_trees_generator:check_equal(B_TREE_04_64_ETS, B_TREE_04_64_COPY_2_3), + + % b-tree in memory ===> b-tree in mnesia + B_TREE_04_64_COPY_2_4 = b_trees:copy(B_TREE_04_64, B_TREE_04_EMPTY_MNESIA), + b_trees_generator:check_equal(B_TREE_04_64_MNESIA, B_TREE_04_64_COPY_2_4), + + % b-tree in memory ===> binary_tree + GB_TREE_64_COPY_2 = b_trees:copy(B_TREE_04_64, GB_TREE_EMPTY), + ?assertEqual(GB_TREE_64, GB_TREE_64_COPY_2), + + %%---------------------------------------------------------------- + %% b_tree dets ===> + %%---------------------------------------------------------------- + + % b-tree in dets ===> b-tree in memory + B_TREE_04_64_COPY_3_1 = b_trees:copy(B_TREE_04_64_DETS, B_TREE_04_EMPTY), + b_trees_generator:check_equal(B_TREE_04_64, B_TREE_04_64_COPY_3_1), + + % b-tree in dets ===> b-tree in dets + B_TREE_04_64_COPY_3_2 = b_trees:copy(B_TREE_04_64_DETS, B_TREE_04_EMPTY_DETS), + b_trees_generator:check_equal(B_TREE_04_64_DETS, B_TREE_04_64_COPY_3_2), + + % b-tree in dets ===> b-tree in ets + B_TREE_04_64_COPY_3_3 = b_trees:copy(B_TREE_04_64_DETS, B_TREE_04_EMPTY_ETS), + b_trees_generator:check_equal(B_TREE_04_64_ETS, B_TREE_04_64_COPY_3_3), + + % b-tree in dets ===> b-tree in mnesia + B_TREE_04_64_COPY_3_4 = b_trees:copy(B_TREE_04_64_DETS, B_TREE_04_EMPTY_MNESIA), + b_trees_generator:check_equal(B_TREE_04_64_MNESIA, B_TREE_04_64_COPY_3_4), + + % b-tree in dets ===> binary_tree + GB_TREE_64_COPY_3 = b_trees:copy(B_TREE_04_64_DETS, GB_TREE_EMPTY), + ?assertEqual(GB_TREE_64, GB_TREE_64_COPY_3), + + %%---------------------------------------------------------------- + %% b_tree ets ===> + %%---------------------------------------------------------------- + + % b-tree in ets ===> b-tree in memory + B_TREE_04_64_COPY_4_1 = b_trees:copy(B_TREE_04_64_ETS, B_TREE_04_EMPTY), + b_trees_generator:check_equal(B_TREE_04_64, B_TREE_04_64_COPY_4_1), + + % b-tree in ets ===> b-tree in dets + B_TREE_04_64_COPY_4_2 = b_trees:copy(B_TREE_04_64_ETS, B_TREE_04_EMPTY_DETS), + b_trees_generator:check_equal(B_TREE_04_64_ETS, B_TREE_04_64_COPY_4_2), + + % b-tree in ets ===> b-tree in ets + B_TREE_04_64_COPY_4_3 = b_trees:copy(B_TREE_04_64_ETS, B_TREE_04_EMPTY_ETS), + b_trees_generator:check_equal(B_TREE_04_64_ETS, B_TREE_04_64_COPY_4_3), + + % b-tree in ets ===> b-tree in mnesia + B_TREE_04_64_COPY_4_4 = b_trees:copy(B_TREE_04_64_ETS, B_TREE_04_EMPTY_MNESIA), + b_trees_generator:check_equal(B_TREE_04_64_MNESIA, B_TREE_04_64_COPY_4_4), + + % b-tree in ets ===> binary_tree + GB_TREE_64_COPY_4 = b_trees:copy(B_TREE_04_64_ETS, GB_TREE_EMPTY), + ?assertEqual(GB_TREE_64, GB_TREE_64_COPY_4), + + %%---------------------------------------------------------------- + %% b_tree mnesia ===> + %%---------------------------------------------------------------- + + % b-tree in mnesia ===> b-tree in memory + B_TREE_04_64_COPY_5_1 = b_trees:copy(B_TREE_04_64_MNESIA, B_TREE_04_EMPTY), + b_trees_generator:check_equal(B_TREE_04_64, B_TREE_04_64_COPY_5_1), + + % b-tree in mnesia ===> b-tree in dets + B_TREE_04_64_COPY_5_2 = b_trees:copy(B_TREE_04_64_MNESIA, B_TREE_04_EMPTY_DETS), + b_trees_generator:check_equal(B_TREE_04_64_MNESIA, B_TREE_04_64_COPY_5_2), + + % b-tree in mnesia ===> b-tree in ets + B_TREE_04_64_COPY_5_3 = b_trees:copy(B_TREE_04_64_MNESIA, B_TREE_04_EMPTY_ETS), + b_trees_generator:check_equal(B_TREE_04_64_ETS, B_TREE_04_64_COPY_5_3), + + % b-tree in mnesia ===> b-tree in mnesia + B_TREE_04_64_COPY_5_4 = b_trees:copy(B_TREE_04_64_MNESIA, B_TREE_04_EMPTY_MNESIA), + b_trees_generator:check_equal(B_TREE_04_64_MNESIA, B_TREE_04_64_COPY_5_4), + + % b-tree in mnesia ===> binary_tree + GB_TREE_64_COPY_5 = b_trees:copy(B_TREE_04_64_MNESIA, GB_TREE_EMPTY), + ?assertEqual(GB_TREE_64, GB_TREE_64_COPY_5), + + %%---------------------------------------------------------------- + %% Finalizing + %%---------------------------------------------------------------- + + ok = dets:close(b_tree_4_dets), + ok = dets:close(b_tree_4_dets_empty), + + stopped = mnesia:stop(), + mnesia:delete_schema([node()]). + %%-------------------------------------------------------------------- %% TEST CASES: delete_any - persistence by ets %%-------------------------------------------------------------------- @@ -1921,6 +2120,21 @@ smallest_test() -> ok. +%%-------------------------------------------------------------------- +%% TEST CASES: Store b-tree and retrieve it afterwards - ets. +%%-------------------------------------------------------------------- + +store_and_retrieve_ets_test() -> + {_, _, _, _, {StateTarget, _, _, _}, _} = B_TREE_04_64_ETS_ORIGINAL = b_trees_generator:generate_b_tree_from_number_ets(4, 64, 2, b_tree_4_ets, spawn(fun b_trees_generator:ets_owner/0)), + + true = ets:insert(StateTarget, {"B_TREE_04_64_ETS_ORIGINAL", B_TREE_04_64_ETS_ORIGINAL}), + + [{"B_TREE_04_64_ETS_ORIGINAL", B_TREE_04_64_ETS_COPY}] = ets:lookup(StateTarget, "B_TREE_04_64_ETS_ORIGINAL"), + + ?assertEqual(B_TREE_04_64_ETS_ORIGINAL, B_TREE_04_64_ETS_COPY), + + ok. + %%-------------------------------------------------------------------- %% TEST CASES: take_any %%-------------------------------------------------------------------- @@ -2385,4 +2599,3 @@ values_test() -> ?assertEqual(32, length(b_trees:values(b_trees_generator:prepare_template_desc(?B_TREE_06_32_DESC)))), ok. - diff --git a/test/performance_SUITE.erl b/test/performance_SUITE.erl index e5c0e3c..21e791c 100644 --- a/test/performance_SUITE.erl +++ b/test/performance_SUITE.erl @@ -15,7 +15,7 @@ -include_lib("eunit/include/eunit.hrl"). -define(B_TREE_POS_STATE, 5). --define(DIRECTORY_DETS, "test/tmp/"). +-define(DIRECTORY_DETS, "/test/tmp/"). -define(OTP_RELEASE, erlang:system_info(otp_release)). %%% Performance tests @@ -42,7 +42,10 @@ suite() -> ]. init_per_suite(Config) -> - filelib:ensure_dir(?DIRECTORY_DETS), + ok = filelib:ensure_dir(?DIRECTORY_DETS), + ok = b_trees_generator:delete_directory(?DIRECTORY_DETS), + ok = filelib:ensure_dir(?DIRECTORY_DETS), + case ok == mnesia:create_schema([node()]) of true -> ok;