File 5301-add-higher-lower-search-functions-in-gb_trees-and-gb.patch of Package erlang
From 1595acf0810451f3fdff4080bc77d71d59cafc2b Mon Sep 17 00:00:00 2001
From: Zeyu Zhang <zeyu@fb.com>
Date: Wed, 11 Oct 2023 14:27:04 -0700
Subject: [PATCH] add higher/lower search functions in gb_trees and gb_sets
---
lib/stdlib/doc/src/gb_sets.xml | 63 ++++++-
lib/stdlib/doc/src/gb_trees.xml | 66 +++++++-
lib/stdlib/src/gb_sets.erl | 141 +++++++++++++---
lib/stdlib/src/gb_trees.erl | 146 ++++++++++++----
.../test/gb_sets_property_test_SUITE.erl | 8 +
.../test/property_test/gb_sets_prop.erl | 156 ++++++++++++++++--
6 files changed, 500 insertions(+), 80 deletions(-)
diff --git a/lib/stdlib/doc/src/gb_sets.xml b/lib/stdlib/doc/src/gb_sets.xml
index 41443f7b04..7a725e33ac 100644
--- a/lib/stdlib/doc/src/gb_sets.xml
+++ b/lib/stdlib/doc/src/gb_sets.xml
@@ -303,10 +303,22 @@
<desc>
<p>Returns an iterator that can be used for traversing the entries of
<c><anno>Set</anno></c>; see
- <seemfa marker="#next/1"><c>next/1</c></seemfa>. The implementation
- of this is very efficient; traversing the whole set using
- <c>next/1</c> is only slightly slower than getting the list of all
- elements using <seemfa marker="#to_list/1"><c>to_list/1</c></seemfa>
+ <seemfa marker="#next/1"><c>next/1</c></seemfa>.</p>
+ <p>Equivalent to <c>iterator(<anno>Set</anno>, ordered)</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="iterator" arity="2" since=""/>
+ <fsummary>Return an iterator for a set.</fsummary>
+ <desc>
+ <p>Returns an iterator that can be used for traversing the entries of
+ <c><anno>Set</anno></c> in either <c>ordered</c> or <c>reversed</c>
+ direction; see <seemfa marker="#next/1"><c>next/1</c></seemfa>.
+ The implementation of this is very efficient; traversing the whole
+ set using <c>next/1</c> is only slightly slower than getting the list
+ of all elements using
+ <seemfa marker="#to_list/1"><c>to_list/1</c></seemfa>
and traversing that.
The main advantage of the iterator approach is that it does
not require the complete list of all elements to be built in
@@ -324,8 +336,36 @@
<seemfa marker="#next/1"><c>next/1</c></seemfa>.
The difference as compared to the iterator returned by
<seemfa marker="#iterator/1"><c>iterator/1</c></seemfa>
- is that the first element greater than
- or equal to <c><anno>Element</anno></c> is returned.</p>
+ is that the iterator starts with the first element greater than
+ or equal to <c><anno>Element</anno></c>.</p>
+ <p>Equivalent to <c>iterator_from(<anno>Element</anno>,
+ <anno>Set</anno>, ordered)</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="iterator_from" arity="3" since="OTP 18.0"/>
+ <fsummary>Return an iterator for a set starting from a specified element.
+ </fsummary>
+ <desc>
+ <p>Returns an iterator that can be used for traversing the
+ entries of <c><anno>Set</anno></c>; see
+ <seemfa marker="#next/1"><c>next/1</c></seemfa>.
+ The difference as compared to the iterator returned by
+ <seemfa marker="#iterator/2"><c>iterator/2</c></seemfa>
+ is that the iterator starts with the first element next to
+ or equal to <c><anno>Element</anno></c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="larger" arity="2" since=""/>
+ <fsummary>Return larger element.</fsummary>
+ <desc>
+ <p>Returns <c>{found, <anno>Element2</anno>}</c>, where
+ <c><anno>Element2</anno></c> is the least element strictly greater
+ than <c><anno>Element1</anno></c>.</p>
+ <p>Returns <c>none</c> if no such element exists.</p>
</desc>
</func>
@@ -385,6 +425,17 @@
</desc>
</func>
+ <func>
+ <name name="smaller" arity="2" since=""/>
+ <fsummary>Return smaller element.</fsummary>
+ <desc>
+ <p>Returns <c>{found, <anno>Element2</anno>}</c>, where
+ <c><anno>Element2</anno></c> is the greatest element strictly less
+ than <c><anno>Element1</anno></c>.</p>
+ <p>Returns <c>none</c> if no such element exists.</p>
+ </desc>
+ </func>
+
<func>
<name name="smallest" arity="1" since=""/>
<fsummary>Return smallest element.</fsummary>
diff --git a/lib/stdlib/doc/src/gb_trees.xml b/lib/stdlib/doc/src/gb_trees.xml
index c8944347a5..2e708f69c7 100644
--- a/lib/stdlib/doc/src/gb_trees.xml
+++ b/lib/stdlib/doc/src/gb_trees.xml
@@ -202,10 +202,21 @@
<desc>
<p>Returns an iterator that can be used for traversing the
entries of <c><anno>Tree</anno></c>; see
- <seemfa marker="#next/1"><c>next/1</c></seemfa>. The implementation
- of this is very efficient; traversing the whole tree using
- <c>next/1</c> is only slightly slower than getting the list
- of all elements using
+ <seemfa marker="#next/1"><c>next/1</c></seemfa>.</p>
+ <p>Equivalent to <c>iterator(<anno>Tree</anno>, ordered)</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="iterator" arity="2" since=""/>
+ <fsummary>Return an iterator for a tree.</fsummary>
+ <desc>
+ <p>Returns an iterator that can be used for traversing the entries of
+ <c><anno>Tree</anno></c> in either <c>ordered</c> or <c>reversed</c>
+ direction; see <seemfa marker="#next/1"><c>next/1</c></seemfa>.
+ The implementation of this is very efficient; traversing the whole
+ tree using <c>next/1</c> is only slightly slower than getting the
+ list of all elements using
<seemfa marker="#to_list/1"><c>to_list/1</c></seemfa>
and traversing that.
The main advantage of the iterator approach is that it does
@@ -224,8 +235,25 @@
<seemfa marker="#next/1"><c>next/1</c></seemfa>.
The difference as compared to the iterator returned by
<seemfa marker="#iterator/1"><c>iterator/1</c></seemfa>
- is that the first key greater than
- or equal to <c><anno>Key</anno></c> is returned.</p>
+ is that the iterator starts with the first key greater than
+ or equal to <c><anno>Key</anno></c>.</p>
+ <p>Equivalent to <c>iterator_from(<anno>Key</anno>, <anno>Tree</anno>,
+ ordered)</c>.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="iterator_from" arity="3" since=""/>
+ <fsummary>Return an iterator for a tree starting from a specified key.
+ </fsummary>
+ <desc>
+ <p>Returns an iterator that can be used for traversing the entries of
+ <c><anno>Tree</anno></c> in either <c>ordered</c> or <c>reversed</c>
+ direction; see <seemfa marker="#next/1"><c>next/1</c></seemfa>.
+ The difference as compared to the iterator returned by
+ <seemfa marker="#iterator/2"><c>iterator/2</c></seemfa>
+ is that the iterator starts with the first key next to
+ or equal to <c><anno>Key</anno></c>.</p>
</desc>
</func>
@@ -237,6 +265,18 @@
</desc>
</func>
+ <func>
+ <name name="larger" arity="2" since=""/>
+ <fsummary>Return larger key and value.</fsummary>
+ <desc>
+ <p>Returns <c>{<anno>Key2</anno>, <anno>Value</anno>}</c>, where
+ <c><anno>Key2</anno></c> is the least key strictly greater than
+ <c><anno>Key1</anno></c>, <c><anno>Value</anno></c> is the value
+ associated with this key.</p>
+ <p>Returns <c>none</c> if no such pair exists.</p>
+ </desc>
+ </func>
+
<func>
<name name="largest" arity="1" since=""/>
<fsummary>Return largest key and value.</fsummary>
@@ -277,7 +317,7 @@
<desc>
<p>Returns <c>{<anno>Key</anno>, <anno>Value</anno>,
<anno>Iter2</anno>}</c>, where <c><anno>Key</anno></c> is the
- smallest key referred to by iterator <c><anno>Iter1</anno></c>, and
+ next key referred to by iterator <c><anno>Iter1</anno></c>, and
<c><anno>Iter2</anno></c> is the new iterator to be used for
traversing the remaining nodes, or the atom <c>none</c> if no
nodes remain.</p>
@@ -292,6 +332,18 @@
</desc>
</func>
+ <func>
+ <name name="smaller" arity="2" since=""/>
+ <fsummary>Return smaller key and value.</fsummary>
+ <desc>
+ <p>Returns <c>{<anno>Key2</anno>, <anno>Value</anno>}</c>, where
+ <c><anno>Key2</anno></c> is the greatest key strictly less than
+ <c><anno>Key1</anno></c>, <c><anno>Value</anno></c> is the value
+ associated with this key.</p>
+ <p>Returns <c>none</c> if no such pair exists.</p>
+ </desc>
+ </func>
+
<func>
<name name="smallest" arity="1" since=""/>
<fsummary>Return smallest key and value.</fsummary>
diff --git a/lib/stdlib/src/gb_sets.erl b/lib/stdlib/src/gb_sets.erl
index f94996a669..c0d886ba83 100644
--- a/lib/stdlib/src/gb_sets.erl
+++ b/lib/stdlib/src/gb_sets.erl
@@ -123,17 +123,30 @@
%% set S, and S1 is the set S with element X deleted. Assumes that the
%% set S is nonempty.
%%
+%% - smaller(X, S): returns {`found', X1}, where X1 is the greatest element
+%% strictly less than X, or `none' if no such element exists.
+%%
+%% - larger(X, S): returns {`found', X1}, where X1 is the least element
+%% strictly greater than K, or `none' if no such element exists.
+%%
%% - iterator(S): returns an iterator that can be used for traversing
-%% the entries of set S; see `next'. The implementation of this is
-%% very efficient; traversing the whole set using `next' is only
-%% slightly slower than getting the list of all elements using
-%% `to_list' and traversing that. The main advantage of the iterator
+%% the entries of set S; see `next'. Equivalent to iterator(T, ordered).
+%%
+%% - iterator(S, Order): returns an iterator that can be used for traversing
+%% the entries of set S in either ordered or reversed direction; see `next'.
+%% The implementation of this is very efficient; traversing the whole set
+%% using `next' is only slightly slower than getting the list of all elements
+%% using `to_list' and traversing that. The main advantage of the iterator
%% approach is that it does not require the complete list of all
%% elements to be built in memory at one time.
%%
%% - iterator_from(X, S): returns an iterator that can be used for
%% traversing the elements of set S greater than or equal to X;
-%% see `next'.
+%% see `next'. Equivalent to iterator_from(X, S, ordered).
+%%
+%% - iterator_from(X, S, Order): returns an iterator that can be used for
+%% traversing the elements of set S in either ordered or reversed direction,
+%% starting from the element equal to or closest to X; see `next'.
%%
%% - next(T): returns {X, T1} where X is the smallest element referred
%% to by the iterator T, and T1 is the new iterator to be used for
@@ -156,9 +169,9 @@
union/1, intersection/2, intersection/1, is_equal/2,
is_disjoint/2, difference/2, is_subset/2, to_list/1,
from_list/1, from_ordset/1, smallest/1, largest/1,
- take_smallest/1, take_largest/1, iterator/1,
- iterator_from/2, next/1, filter/2, fold/3, map/2,
- filtermap/2, is_set/1]).
+ take_smallest/1, take_largest/1, smaller/2, larger/2,
+ iterator/1, iterator/2, iterator_from/2, iterator_from/3,
+ next/1, filter/2, fold/3, map/2, filtermap/2, is_set/1]).
%% `sets' compatibility aliases:
@@ -201,7 +214,7 @@
-type gb_set_node(Element) :: 'nil' | {Element, _, _}.
-opaque set(Element) :: {non_neg_integer(), gb_set_node(Element)}.
-type set() :: set(_).
--opaque iter(Element) :: [gb_set_node(Element)].
+-opaque iter(Element) :: {ordered | reversed, [gb_set_node(Element)]}.
-type iter() :: iter(_).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -495,6 +508,44 @@ largest_1({Key, _Smaller, nil}) ->
largest_1({_Key, _Smaller, Larger}) ->
largest_1(Larger).
+-spec smaller(Element1, Set) -> none | {found, Element2} when
+ Element1 :: Element,
+ Element2 :: Element,
+ Set :: set(Element).
+smaller(Key, {_, T}) ->
+ smaller_1(Key, T).
+
+smaller_1(_Key, nil) ->
+ none;
+smaller_1(Key, {Key1, _Smaller, Larger}) when Key > Key1 ->
+ case smaller_1(Key, Larger) of
+ none ->
+ {found, Key1};
+ Found ->
+ Found
+ end;
+smaller_1(Key, {_Key, Smaller, _Larger}) ->
+ smaller_1(Key, Smaller).
+
+-spec larger(Element1, Set) -> none | {found, Element2} when
+ Element1 :: Element,
+ Element2 :: Element,
+ Set :: set(Element).
+larger(Key, {_, T}) ->
+ larger_1(Key, T).
+
+larger_1(_Key, nil) ->
+ none;
+larger_1(Key, {Key1, Smaller, _Larger}) when Key < Key1 ->
+ case larger_1(Key, Smaller) of
+ none ->
+ {found, Key1};
+ Found ->
+ Found
+ end;
+larger_1(Key, {_Key, _Smaller, Larger}) ->
+ larger_1(Key, Larger).
+
-spec to_list(Set) -> List when
Set :: set(Element),
List :: [Element].
@@ -512,42 +563,80 @@ to_list(nil, L) -> L.
Set :: set(Element),
Iter :: iter(Element).
-iterator({_, T}) ->
- iterator(T, []).
+iterator(Set) ->
+ iterator(Set, ordered).
+
+-spec iterator(Set, Order) -> Iter when
+ Set :: set(Element),
+ Iter :: iter(Element),
+ Order :: ordered | reversed.
+
+iterator({_, T}, ordered) ->
+ {ordered, iterator_1(T, [])};
+iterator({_, T}, reversed) ->
+ {reversed, iterator_r(T, [])}.
%% The iterator structure is really just a list corresponding to the
%% call stack of an in-order traversal. This is quite fast.
-iterator({_, nil, _} = T, As) ->
+iterator_1({_, nil, _} = T, As) ->
[T | As];
-iterator({_, L, _} = T, As) ->
- iterator(L, [T | As]);
-iterator(nil, As) ->
+iterator_1({_, L, _} = T, As) ->
+ iterator_1(L, [T | As]);
+iterator_1(nil, As) ->
+ As.
+
+iterator_r({_, _, nil} = T, As) ->
+ [T | As];
+iterator_r({_, _, R} = T, As) ->
+ iterator_r(R, [T | As]);
+iterator_r(nil, As) ->
As.
-spec iterator_from(Element, Set) -> Iter when
Set :: set(Element),
Iter :: iter(Element).
-iterator_from(S, {_, T}) ->
- iterator_from(S, T, []).
+iterator_from(Element, Set) ->
+ iterator_from(Element, Set, ordered).
+
+-spec iterator_from(Element, Set, Order) -> Iter when
+ Set :: set(Element),
+ Iter :: iter(Element),
+ Order :: ordered | reversed.
+
+iterator_from(S, {_, T}, ordered) ->
+ {ordered, iterator_from_1(S, T, [])};
+iterator_from(S, {_, T}, reversed) ->
+ {reversed, iterator_from_r(S, T, [])}.
+
+iterator_from_1(S, {K, _, T}, As) when K < S ->
+ iterator_from_1(S, T, As);
+iterator_from_1(_, {_, nil, _} = T, As) ->
+ [T | As];
+iterator_from_1(S, {_, L, _} = T, As) ->
+ iterator_from_1(S, L, [T | As]);
+iterator_from_1(_, nil, As) ->
+ As.
-iterator_from(S, {K, _, T}, As) when K < S ->
- iterator_from(S, T, As);
-iterator_from(_, {_, nil, _} = T, As) ->
+iterator_from_r(S, {K, T, _}, As) when K > S ->
+ iterator_from_r(S, T, As);
+iterator_from_r(_, {_, _, nil} = T, As) ->
[T | As];
-iterator_from(S, {_, L, _} = T, As) ->
- iterator_from(S, L, [T | As]);
-iterator_from(_, nil, As) ->
+iterator_from_r(S, {_, _, R} = T, As) ->
+ iterator_from_r(S, R, [T | As]);
+iterator_from_r(_, nil, As) ->
As.
-spec next(Iter1) -> {Element, Iter2} | 'none' when
Iter1 :: iter(Element),
Iter2 :: iter(Element).
-next([{X, _, T} | As]) ->
- {X, iterator(T, As)};
-next([]) ->
+next({ordered, [{X, _, T} | As]}) ->
+ {X, {ordered, iterator_1(T, As)}};
+next({reversed, [{X, T, _} | As]}) ->
+ {X, {reversed, iterator_r(T, As)}};
+next({_, []}) ->
none.
diff --git a/lib/stdlib/src/gb_trees.erl b/lib/stdlib/src/gb_trees.erl
index 54a5ab6690..6e489d86ff 100644
--- a/lib/stdlib/src/gb_trees.erl
+++ b/lib/stdlib/src/gb_trees.erl
@@ -95,19 +95,32 @@
%% in tree T, V is the value associated with X in T, and T1 is the
%% tree T with key X deleted. Assumes that the tree T is nonempty.
%%
+%% - smaller(K, T): returns {Key, Value} pair, where Key is the
+%% greatest key strictly less than K, or `none' if no such key exists.
+%%
+%% - larger(K, T): returns {Key, Value} pair, where Key is the
+%% least key strictly greater than K, or `none' if no such key exists.
+%%
%% - iterator(T): returns an iterator that can be used for traversing
-%% the entries of tree T; see `next'. The implementation of this is
-%% very efficient; traversing the whole tree using `next' is only
-%% slightly slower than getting the list of all elements using
-%% `to_list' and traversing that. The main advantage of the iterator
+%% the entries of tree T; see `next'. Equivalent to iterator(T, ordered).
+%%
+%% - iterator(T, Order): returns an iterator that can be used for traversing
+%% the entries of tree T in either ordered or reversed direction; see `next'.
+%% The implementation of this is very efficient; traversing the whole tree
+%% using `next' is only slightly slower than getting the list of all elements
+%% using `to_list' and traversing that. The main advantage of the iterator
%% approach is that it does not require the complete list of all
%% elements to be built in memory at one time.
%%
%% - iterator_from(K, T): returns an iterator that can be used for
%% traversing the entries of tree T with key greater than or
-%% equal to K; see `next'.
+%% equal to K; see `next'. Equivalent to iterator_from(K, T, ordered).
%%
-%% - next(S): returns {X, V, S1} where X is the smallest key referred to
+%% - iterator_from(K, T, Order): returns an iterator that can be used for
+%% traversing the entries of tree T in either ordered or reversed direction,
+%% starting from the key equal to or closest to K; see `next'.
+%%
+%% - next(S): returns {X, V, S1} where X is the next key referred to
%% by the iterator S, and S1 is the new iterator to be used for
%% traversing the remaining entries, or the atom `none' if no entries
%% remain.
@@ -122,8 +135,9 @@
update/3, enter/3, delete/2, delete_any/2, balance/1,
is_defined/2, keys/1, values/1, to_list/1, from_orddict/1,
smallest/1, largest/1, take/2, take_any/2,
- take_smallest/1, take_largest/1,
- iterator/1, iterator_from/2, next/1, map/2]).
+ take_smallest/1, take_largest/1, smaller/2, larger/2,
+ iterator/1, iterator/2, iterator_from/2, iterator_from/3,
+ next/1, map/2]).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -164,7 +178,7 @@
| {K, V, gb_tree_node(K, V), gb_tree_node(K, V)}.
-opaque tree(Key, Value) :: {non_neg_integer(), gb_tree_node(Key, Value)}.
-type tree() :: tree(_, _).
--opaque iter(Key, Value) :: [gb_tree_node(Key, Value)].
+-opaque iter(Key, Value) :: {ordered | reversed, [gb_tree_node(Key, Value)]}.
-type iter() :: iter(_, _).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -511,6 +525,46 @@ largest_1({_Key, _Value, _Smaller, Larger}) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+-spec smaller(Key1, Tree) -> none | {Key2, Value} when
+ Key1 :: Key,
+ Key2 :: Key,
+ Tree :: tree(Key, Value).
+smaller(Key, {_, TreeNode}) ->
+ smaller_1(Key, TreeNode).
+
+smaller_1(_Key, nil) ->
+ none;
+smaller_1(Key, {Key1, Value, _Smaller, Larger}) when Key > Key1 ->
+ case smaller_1(Key, Larger) of
+ none ->
+ {Key1, Value};
+ Entry ->
+ Entry
+ end;
+smaller_1(Key, {_Key, _Value, Smaller, _Larger}) ->
+ smaller_1(Key, Smaller).
+
+-spec larger(Key1, Tree) -> none | {Key2, Value} when
+ Key1 :: Key,
+ Key2 :: Key,
+ Tree :: tree(Key, Value).
+larger(Key, {_, TreeNode}) ->
+ larger_1(Key, TreeNode).
+
+larger_1(_Key, nil) ->
+ none;
+larger_1(Key, {Key1, Value, Smaller, _Larger}) when Key < Key1 ->
+ case larger_1(Key, Smaller) of
+ none ->
+ {Key1, Value};
+ Entry ->
+ Entry
+ end;
+larger_1(Key, {_Key, _Value, _Smaller, Larger}) ->
+ larger_1(Key, Larger).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
-spec to_list(Tree) -> [{Key, Value}] when
Tree :: tree(Key, Value).
@@ -553,20 +607,34 @@ values(nil, L) -> L.
Tree :: tree(Key, Value),
Iter :: iter(Key, Value).
-iterator({_, T}) ->
- iterator_1(T).
+iterator(Tree) ->
+ iterator(Tree, ordered).
-iterator_1(T) ->
- iterator(T, []).
+-spec iterator(Tree, Order) -> Iter when
+ Tree :: tree(Key, Value),
+ Iter :: iter(Key, Value),
+ Order :: ordered | reversed.
+
+iterator({_, T}, ordered) ->
+ {ordered, iterator_1(T, [])};
+iterator({_, T}, reversed) ->
+ {reversed, iterator_r(T, [])}.
%% The iterator structure is really just a list corresponding to
%% the call stack of an in-order traversal. This is quite fast.
-iterator({_, _, nil, _} = T, As) ->
+iterator_1({_, _, nil, _} = T, As) ->
[T | As];
-iterator({_, _, L, _} = T, As) ->
- iterator(L, [T | As]);
-iterator(nil, As) ->
+iterator_1({_, _, L, _} = T, As) ->
+ iterator_1(L, [T | As]);
+iterator_1(nil, As) ->
+ As.
+
+iterator_r({_, _, _, nil} = T, As) ->
+ [T | As];
+iterator_r({_, _, _, R} = T, As) ->
+ iterator_r(R, [T | As]);
+iterator_r(nil, As) ->
As.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -575,19 +643,35 @@ iterator(nil, As) ->
Tree :: tree(Key, Value),
Iter :: iter(Key, Value).
-iterator_from(S, {_, T}) ->
- iterator_1_from(S, T).
+iterator_from(Key, Tree) ->
+ iterator_from(Key, Tree, ordered).
-iterator_1_from(S, T) ->
- iterator_from(S, T, []).
+-spec iterator_from(Key, Tree, Order) -> Iter when
+ Tree :: tree(Key, Value),
+ Iter :: iter(Key, Value),
+ Order :: ordered | reversed.
+
+iterator_from(S, {_, T}, ordered) ->
+ {ordered, iterator_from_1(S, T, [])};
+iterator_from(S, {_, T}, reversed) ->
+ {reversed, iterator_from_r(S, T, [])}.
+
+iterator_from_1(S, {K, _, _, T}, As) when K < S ->
+ iterator_from_1(S, T, As);
+iterator_from_1(_, {_, _, nil, _} = T, As) ->
+ [T | As];
+iterator_from_1(S, {_, _, L, _} = T, As) ->
+ iterator_from_1(S, L, [T | As]);
+iterator_from_1(_, nil, As) ->
+ As.
-iterator_from(S, {K, _, _, T}, As) when K < S ->
- iterator_from(S, T, As);
-iterator_from(_, {_, _, nil, _} = T, As) ->
+iterator_from_r(S, {K, _, T, _}, As) when K > S ->
+ iterator_from_r(S, T, As);
+iterator_from_r(_, {_, _, _, nil} = T, As) ->
[T | As];
-iterator_from(S, {_, _, L, _} = T, As) ->
- iterator_from(S, L, [T | As]);
-iterator_from(_, nil, As) ->
+iterator_from_r(S, {_, _, _, R} = T, As) ->
+ iterator_from_r(S, R, [T | As]);
+iterator_from_r(_, nil, As) ->
As.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -596,9 +680,11 @@ iterator_from(_, nil, As) ->
Iter1 :: iter(Key, Value),
Iter2 :: iter(Key, Value).
-next([{X, V, _, T} | As]) ->
- {X, V, iterator(T, As)};
-next([]) ->
+next({ordered, [{X, V, _, T} | As]}) ->
+ {X, V, {ordered, iterator_1(T, As)}};
+next({reversed, [{X, V, T, _} | As]}) ->
+ {X, V, {reversed, iterator_r(T, As)}};
+next({_, []}) ->
none.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/stdlib/test/gb_sets_property_test_SUITE.erl b/lib/stdlib/test/gb_sets_property_test_SUITE.erl
index 4ba8809506..d8137efd5f 100644
--- a/lib/stdlib/test/gb_sets_property_test_SUITE.erl
+++ b/lib/stdlib/test/gb_sets_property_test_SUITE.erl
@@ -33,8 +33,10 @@ all() -> [
insert_case,
is_member_case,
iterator_case, iterator_from_case,
+ larger_case,
largest_case,
singleton_case,
+ smaller_case,
smallest_case,
take_largest_case,
take_smallest_case
@@ -76,12 +78,18 @@ iterator_case(Config) ->
iterator_from_case(Config) ->
do_proptest(prop_iterator_from, Config).
+larger_case(Config) ->
+ do_proptest(prop_larger, Config).
+
largest_case(Config) ->
do_proptest(prop_largest, Config).
singleton_case(Config) ->
do_proptest(prop_singleton, Config).
+smaller_case(Config) ->
+ do_proptest(prop_smaller, Config).
+
smallest_case(Config) ->
do_proptest(prop_smallest, Config).
diff --git a/lib/stdlib/test/property_test/gb_sets_prop.erl b/lib/stdlib/test/property_test/gb_sets_prop.erl
index a4097904d9..7854357367 100644
--- a/lib/stdlib/test/property_test/gb_sets_prop.erl
+++ b/lib/stdlib/test/property_test/gb_sets_prop.erl
@@ -59,6 +59,31 @@ prop_balance() ->
gb_sets:is_equal(S, gb_sets:balance(S))
).
+%% --- ceiling/2 -------------------------------------------------------
+prop_ceiling() ->
+ ?FORALL(
+ {S, O, L},
+ ?LET(
+ {L1, L2},
+ {ct_proper_ext:safe_list(),
+ non_empty(ct_proper_ext:safe_list())},
+ {gb_sets:from_list(L1), lists:usort(L1), L1 ++ L2}
+ ),
+ lists:all(
+ fun(E) ->
+ gb_sets:ceiling(E, S) =:= do_ceiling(E, O)
+ end,
+ L
+ )
+ ).
+
+do_ceiling(_, []) ->
+ none;
+do_ceiling(E, [X | _]) when X >= E ->
+ {found, X};
+do_ceiling(E, [X | R]) when X < E ->
+ do_ceiling(E, R).
+
%% --- delete/2 -------------------------------------------------------
prop_delete() ->
?FORALL(
@@ -118,6 +143,31 @@ prop_difference() ->
gb_sets:difference(S1, S2) =:= gb_sets:subtract(S1, S2)
).
+%% --- floor/2 -------------------------------------------------------
+prop_floor() ->
+ ?FORALL(
+ {S, O, L},
+ ?LET(
+ {L1, L2},
+ {ct_proper_ext:safe_list(),
+ non_empty(ct_proper_ext:safe_list())},
+ {gb_sets:from_list(L1), lists:reverse(lists:usort(L1)), L1 ++ L2}
+ ),
+ lists:all(
+ fun(E) ->
+ gb_sets:floor(E, S) =:= do_floor(E, O)
+ end,
+ L
+ )
+ ).
+
+do_floor(_, []) ->
+ none;
+do_floor(E, [X | _]) when X =< E ->
+ {found, X};
+do_floor(E, [X | R]) when X > E ->
+ do_floor(E, R).
+
%% --- from_ordset/1 --------------------------------------------------
prop_from_ordset() ->
?FORALL(
@@ -182,17 +232,29 @@ prop_iterator() ->
{gb_sets:from_list(L1), L1}
end
),
- do_iterate(gb_sets:iterator(S), L)
+ do_iterate(gb_sets:iterator(S), L, ordered)
+ ),
+ ?FORALL(
+ {S, L},
+ ?LET(
+ L,
+ ct_proper_ext:safe_list(),
+ begin
+ L1 = lists:usort(L),
+ {gb_sets:from_list(L1), lists:reverse(L1)}
+ end
+ ),
+ do_iterate(gb_sets:iterator(S, reversed), L, reversed)
).
-do_iterate(none, L) ->
+do_iterate(none, L, _) ->
L =:= [];
-do_iterate(I, []) ->
+do_iterate(I, [], _) ->
none =:= gb_sets:next(I);
-do_iterate(I0, L0) ->
+do_iterate(I0, L0, Order) ->
{E, I1} = gb_sets:next(I0),
lists:member(E, L0) andalso
- do_iterate_from(E, I1, lists:delete(E, L0)).
+ do_iterate_from(E, I1, lists:delete(E, L0), Order).
%% --- iterator_from/2 ------------------------------------------------
%%
@@ -213,18 +275,65 @@ prop_iterator_from() ->
{gb_sets:from_list(L1), L2, F}
end
),
- do_iterate_from(From, gb_sets:iterator_from(From, S), L)
+ do_iterate_from(From, gb_sets:iterator_from(From, S), L, ordered)
+ ),
+ ?FORALL(
+ {S, L, From},
+ ?LET(
+ {L, E},
+ {ct_proper_ext:safe_list(), ct_proper_ext:safe_any()},
+ begin
+ L1 = lists:usort(L),
+ L2 = lists:dropwhile(fun(X) -> X > E end, lists:reverse(L1)),
+ F = case L2 of
+ [] -> E;
+ _ -> oneof([E, hd(L2)])
+ end,
+ {gb_sets:from_list(L1), L2, F}
+ end
+ ),
+ do_iterate_from(From, gb_sets:iterator_from(From, S, reversed), L, reversed)
).
-do_iterate_from(_Min, none, L) ->
+do_iterate_from(_From, none, L, _) ->
L =:= [];
-do_iterate_from(_Min, I, []) ->
+do_iterate_from(_From, I, [], _) ->
none =:= gb_sets:next(I);
-do_iterate_from(Min, I0, L0) ->
+do_iterate_from(From, I0, L0, ordered) ->
{E, I1} = gb_sets:next(I0),
lists:member(E, L0) andalso
- Min =< E andalso
- do_iterate_from(E, I1, lists:delete(E, L0)).
+ From =< E andalso
+ do_iterate_from(E, I1, lists:delete(E, L0), ordered);
+do_iterate_from(From, I0, L0, reversed) ->
+ {E, I1} = gb_sets:next(I0),
+ lists:member(E, L0) andalso
+ From >= E andalso
+ do_iterate_from(E, I1, lists:delete(E, L0), reversed).
+
+%% --- larger/2 -------------------------------------------------------
+prop_larger() ->
+ ?FORALL(
+ {S, O, L},
+ ?LET(
+ {L1, L2},
+ {ct_proper_ext:safe_list(),
+ non_empty(ct_proper_ext:safe_list())},
+ {gb_sets:from_list(L1), lists:usort(L1), L1 ++ L2}
+ ),
+ lists:all(
+ fun(E) ->
+ gb_sets:larger(E, S) =:= do_larger(E, O)
+ end,
+ L
+ )
+ ).
+
+do_larger(_, []) ->
+ none;
+do_larger(E, [X | _]) when X > E ->
+ {found, X};
+do_larger(E, [X | R]) when X =< E ->
+ do_larger(E, R).
%% --- largest/1 ------------------------------------------------------
prop_largest() ->
@@ -249,6 +358,31 @@ prop_singleton() ->
[E] =:= gb_sets:to_list(gb_sets:singleton(E))
).
+%% --- smaller/2 -------------------------------------------------------
+prop_smaller() ->
+ ?FORALL(
+ {S, O, L},
+ ?LET(
+ {L1, L2},
+ {ct_proper_ext:safe_list(),
+ non_empty(ct_proper_ext:safe_list())},
+ {gb_sets:from_list(L1), lists:reverse(lists:usort(L1)), L1 ++ L2}
+ ),
+ lists:all(
+ fun(E) ->
+ gb_sets:smaller(E, S) =:= do_smaller(E, O)
+ end,
+ L
+ )
+ ).
+
+do_smaller(_, []) ->
+ none;
+do_smaller(E, [X | _]) when X < E ->
+ {found, X};
+do_smaller(E, [X | R]) when X >= E ->
+ do_smaller(E, R).
+
%% --- smallest/1 -----------------------------------------------------
prop_smallest() ->
?FORALL(
--
2.35.3