File 3903-array-add-shift-and-slice-mapfold-prepend-append-and.patch of Package erlang
From 0cd5311ca37f82e11f1687136693ec2385cc27b4 Mon Sep 17 00:00:00 2001
From: Richard Carlsson <carlsson.richard@gmail.com>
Date: Sun, 18 Jan 2026 22:58:32 +0100
Subject: [PATCH 3/5] array: add shift and slice, mapfold, prepend, append and
concat
Add useful functions
Also it is unnecessary using ints for unexpanded trees, so remove them.
then internal nodes can be a literal node, should be slightly faster.
---
.../test/indent2_SUITE_data/results/arr | 2 +-
lib/stdlib/src/array.erl | 1604 +++++++++++------
lib/stdlib/test/array_SUITE.erl | 111 +-
3 files changed, 1154 insertions(+), 563 deletions(-)
diff --git a/lib/dialyzer/test/indent2_SUITE_data/results/arr b/lib/dialyzer/test/indent2_SUITE_data/results/arr
index c1ce887a3e..02a66e0b9b 100644
--- a/lib/dialyzer/test/indent2_SUITE_data/results/arr
+++ b/lib/dialyzer/test/indent2_SUITE_data/results/arr
@@ -11,5 +11,5 @@ arr.erl:29:2: Type specification arr:test5
array:array(_)
arr.erl:37:2: Type specification arr:test6
(array:array(integer()), non_neg_integer(), integer()) ->
- array:array(any()) is not equal to the success typing: arr:test6
+ array:array(any()) is a subtype of the success typing: arr:test6
(array:array(_), non_neg_integer(), _) -> array:array(_)
diff --git a/lib/stdlib/src/array.erl b/lib/stdlib/src/array.erl
index 055a75711d..39b626b2e8 100644
--- a/lib/stdlib/src/array.erl
+++ b/lib/stdlib/src/array.erl
@@ -101,16 +101,23 @@ beyond the last set entry:
-export([new/0, new/1, new/2, is_array/1, set/3, get/2, size/1,
sparse_size/1, default/1, reset/2, to_list/1, sparse_to_list/1,
from_list/1, from_list/2, to_orddict/1, sparse_to_orddict/1,
- from/2, from/3,
+ concat/2, concat/1, from/2, from/3,
from_orddict/1, from_orddict/2, map/2, sparse_map/2, foldl/3,
- foldr/3, sparse_foldl/3, sparse_foldr/3, fix/1, relax/1, is_fix/1,
- resize/1, resize/2]).
+ foldl/5, foldr/3, foldr/5, sparse_foldl/3, sparse_foldl/5,
+ sparse_foldr/3, sparse_foldr/5, mapfoldl/3, mapfoldl/5,
+ mapfoldr/3, mapfoldr/5, sparse_mapfoldl/3, sparse_mapfoldl/5,
+ sparse_mapfoldr/3, sparse_mapfoldr/5,
+ fix/1, relax/1, is_fix/1,
+ resize/1, resize/2, shift/2, slice/3, prepend/2, append/2]).
-export_type([array/0, array/1]).
-moduledoc(#{ authors => [~"Richard Carlsson <carlsson.richard@gmail.com>",
~"Dan Gudmundsson <dgud@erix.ericsson.se>"] }).
+%% -define(PROPER_NO_IMPORTS, 1).
+%% -include_lib("proper/include/proper.hrl").
+
%% Developers:
%%
%% The key to speed is to minimize the number of tests, on
@@ -126,8 +133,7 @@ beyond the last set entry:
%%
%% A tree is either a leaf, with LEAFSIZE elements (the "base"), an
%% internal node with LEAFSIZE elements, or an unexpanded tree,
-%% represented by a single integer: the number of elements that may be
-%% stored in the tree when it is expanded.
+%% represented by EMPTY.
%%
%% Note that to update an entry in a tree of height h = log[b] n, the
%% total number of written words is (b+1)+(h-1)*(b+2), since tuples use
@@ -147,11 +153,14 @@ beyond the last set entry:
-define(LEAFSIZE, (1 bsl ?SHIFT)). % the "base" (assumed to be > 1)
-define(MASK, (?LEAFSIZE-1)). % LEAFSIZE - 1, for bitwise rem operation
-define(MASK(X), ((1 bsl (X))-1)).
--define(SIZE(M), (1 bsl (M))).
+-define(SIZE(S), (1 bsl (S))).
-define(NODESIZE, ?LEAFSIZE). % must not be LEAFSIZE-1; keep same as leaf
--define(NEW_NODE(S), erlang:make_tuple((?NODESIZE),(S))). % when E = S
+-define(NEW_NODE(S), %% Hardcoded to get a literal
+ {?EMPTY, ?EMPTY, ?EMPTY, ?EMPTY, ?EMPTY, ?EMPTY, ?EMPTY, ?EMPTY,
+ ?EMPTY, ?EMPTY, ?EMPTY, ?EMPTY,?EMPTY, ?EMPTY, ?EMPTY, ?EMPTY}).
+%% -define(NEW_NODE(S), erlang:make_tuple(?NODESIZE,(?EMPTY))). %% S not actually used
-define(NEW_LEAF(D), erlang:make_tuple(?LEAFSIZE,(D))).
-
+-define(EMPTY, []). % placeholder for empty subtree (keep as immediate)
-define(NEW_CACHE(D), ?NEW_LEAF(D)).
-define(reduce(X), ((X) - ?SHIFT)).
@@ -163,29 +172,27 @@ beyond the last set entry:
-type element_tuple(T) ::
leaf_tuple(T)
- | {element_tuple(T), element_tuple(T), element_tuple(T),
- element_tuple(T), element_tuple(T), element_tuple(T),
- element_tuple(T), element_tuple(T), element_tuple(T),
- element_tuple(T), element_tuple(T), element_tuple(T),
- element_tuple(T), element_tuple(T), element_tuple(T),
- element_tuple(T), non_neg_integer()}.
-
--type elements(T) :: non_neg_integer()
- | element_tuple(T)
- | nil(). % kill reference, for GC
-
--type cache() :: leaf_tuple(_).
-
--record(array, {size :: non_neg_integer(), %% number of defined entries
- fix :: boolean(), %% not automatically growing
- default, %% the default value (usually 'undefined')
- cache :: cache(), %% cached leaf tuple
+ | ?EMPTY
+ | {element_tuple(T), element_tuple(T), element_tuple(T), element_tuple(T),
+ element_tuple(T), element_tuple(T), element_tuple(T), element_tuple(T),
+ element_tuple(T), element_tuple(T), element_tuple(T), element_tuple(T),
+ element_tuple(T), element_tuple(T), element_tuple(T), element_tuple(T)}.
+
+-type elements(T) :: ?EMPTY | element_tuple(T).
+
+-type cache() :: leaf_tuple(dynamic()).
+
+-record(array, {size :: non_neg_integer(), %% number of defined entries
+ zero :: non_neg_integer(), %% offset of zero point
+ fix :: boolean(), %% not automatically growing
+ default :: dynamic(), %% the default value (usually 'undefined')
+ cache :: cache(), %% cached leaf tuple
cache_index :: non_neg_integer(),
- elements :: elements(_), %% the tuple tree
- bits :: integer() %% in bits
+ elements :: elements(dynamic()), %% the tuple tree
+ bits :: integer() %% in bits
}).
--type array() :: array(term()).
+-type array() :: array(dynamic()).
-doc """
A functional, extendible array. The representation is not documented and is
@@ -202,7 +209,7 @@ for equality.
-type array_indx() :: non_neg_integer().
-type array_opt() :: {'fixed', boolean()} | 'fixed'
- | {'default', Type :: term()}
+ | {'default', Type :: dynamic()}
| {'size', N :: non_neg_integer()}
| (N :: non_neg_integer()).
-type array_opts() :: array_opt() | [array_opt()].
@@ -214,8 +221,6 @@ for equality.
-doc """
Creates a new, extendible array with initial size zero.
-
-See also `new/1`, `new/2`.
""".
-spec new() -> array().
@@ -290,8 +295,6 @@ array:new(100, {default,0})
```
creates a fixed-size array of size 100, whose default value is `0`.
-
-See also `new/1`.
""".
-spec new(Size :: non_neg_integer(), Options :: array_opts()) -> array().
@@ -324,18 +327,17 @@ new_1(_Options, _Size, _Fixed, _Default) ->
erlang:error(badarg).
new(Size, Fixed, Default) ->
- E = find_max(Size - 1, ?SHIFT),
+ S = find_bits(Size - 1, ?SHIFT),
C = ?NEW_CACHE(Default),
- #array{size = Size, fix = Fixed, cache = C, cache_index = 0,
- default = Default, elements = E, bits = ?reduce(E)}.
-
--spec find_max(integer(), non_neg_integer()) -> non_neg_integer().
+ #array{size = Size, zero = 0, fix = Fixed, cache = C, cache_index = 0,
+ default = Default, elements = ?EMPTY, bits = S}.
-find_max(I, M) when I >= ?SIZE(M) ->
- find_max(I, ?extend(M));
-find_max(_I, M) ->
- M.
+-spec find_bits(integer(), non_neg_integer()) -> non_neg_integer().
+find_bits(I, S) when I < ?SIZE(?extend(S)) ->
+ S;
+find_bits(I, S) ->
+ find_bits(I, ?extend(S)).
-doc """
Returns `true` if `X` is an array, otherwise `false`.
@@ -356,8 +358,6 @@ is_array(_) ->
Gets the number of entries in the array. Entries are numbered from `0` to
`size(Array)-1`. Hence, this is also the index of the first entry that is
guaranteed to not have been previously set.
-
-See also `set/3`, `sparse_size/1`.
""".
-spec size(Array :: array()) -> non_neg_integer().
@@ -380,29 +380,16 @@ default(_) -> erlang:error(badarg).
Fixes the array size. This prevents it from growing automatically upon
insertion.
-See also `set/3` and `relax/1`.
+Note that operations such as `append/2` which explicitly increase the array
+size may still be used on a fixed size array.
+
+See also `relax/1`, `set/3`.
""".
-spec fix(Array :: array(Type)) -> array(Type).
fix(#array{}=A) ->
A#array{fix = true}.
-%% similar to set_1()
-set_leaf(_I, 0, _E, C) ->
- C;
-set_leaf(I, S, E, C) when is_integer(E) ->
- set_leaf_1(I, S, C);
-set_leaf(I, S, E, C) when S > 0 ->
- IDiv = (I bsr S) band ?MASK,
- I1 = IDiv+1,
- setelement(I1, E, set_leaf(I, ?reduce(S), element(I1, E), C)).
-
-set_leaf_1(I, S, C) when S > 0 ->
- IDiv = (I bsr S) band ?MASK,
- setelement(IDiv+1, ?NEW_NODE(S), set_leaf_1(I, ?reduce(S), C));
-set_leaf_1(_I, _S, C) ->
- C.
-
-doc """
Checks if the array has fixed size. Returns `true` if the array is fixed,
otherwise `false`.
@@ -426,33 +413,29 @@ relax(#array{size = N}=A) when is_integer(N), N >= 0 ->
A#array{fix = false}.
-%% similar to get_1
-get_leaf(_I, _, E, D) when is_integer(E) ->
- ?NEW_CACHE(D);
-get_leaf(_I, 0, E, _D) ->
- E;
-get_leaf(I, S, E, D) ->
- IDiv = (I bsr S) band ?MASK,
- get_leaf(I, ?reduce(S), element(IDiv + 1, E), D).
-
-doc """
Change the array size.
If `Size` is not a non-negative integer, the call fails with reason `badarg`. If
the specified array has fixed size, also the resulting array has fixed size.
+
+Note: As of OTP 29, resizing ensures that entries outside the new range are
+pruned so that garbage collection can recover the memory.
+
+See also `shift/2`.
""".
-spec resize(Size :: non_neg_integer(), Array :: array(Type)) ->
array(Type).
-resize(Size, #array{size = N, fix = Fix, cache = C, cache_index = CI, elements = E, default = D, bits = S}=A)
+resize(Size, #array{size = N, zero = Z, cache = C, cache_index = CI, elements = E, default = D, bits = S}=A)
when is_integer(Size), Size >= 0, is_integer(N), N >= 0,
- is_boolean(Fix), is_integer(CI), is_integer(S) ->
+ is_integer(CI), is_integer(S) ->
if Size > N ->
- {E1, S1} = grow(Size-1, E, S),
+ {E1, S1} = grow(Z + Size-1, E, S),
A#array{size = Size, elements = E1, bits = S1};
Size < N ->
E1 = set_leaf(CI, S, E, C),
- {E2, S1} = shrink(Size-1, S, E1, D),
+ {E2, S1} = shrink(Z + Size-1, S, E1, D),
CI1 = 0,
C1 = get_leaf(CI1, S1, E2, D),
A#array{size = Size, elements = E2, cache = C1, cache_index = CI1, bits = S1};
@@ -464,16 +447,16 @@ resize(_Size, _) ->
%% like grow(), but only used when explicitly resizing down
shrink(I, _S, _E, _D) when I < 0 ->
- S = find_max(I, ?SHIFT),
- {S, ?reduce(S)};
+ S = find_bits(I, ?SHIFT),
+ {?EMPTY, S};
shrink(I, S, E, D) ->
shrink_1(I, S, E, D).
%% I is the largest index, 0 or more (empty arrays handled above)
%% This first discards any unnecessary tuples from the top
-shrink_1(I, _S, E, _D) when is_integer(E) ->
- S = find_max(I, ?SHIFT),
- {S, ?reduce(S)};
+shrink_1(I, _S, ?EMPTY, _D) ->
+ S = find_bits(I, ?SHIFT),
+ {?EMPTY, S};
shrink_1(I, 0, E, D) ->
{prune(E, I, D), 0};
shrink_1(I, S, E, D) when I < ?SIZE(S) ->
@@ -483,14 +466,14 @@ shrink_1(I, S, E, D) ->
%% Here we have at least one top tuple that should be kept
%% and we must not discard any intermediate levels
-shrink_2(_I, S, E, _D) when is_integer(E) ->
- {E, S};
+shrink_2(_I, S, ?EMPTY, _D) ->
+ {?EMPTY, S};
shrink_2(I, 0, E, D) ->
{prune(E, I, D), 0};
shrink_2(I, S, E, D) ->
IDiv = I bsr S,
IRem = I band ?MASK(S),
- E1 = prune(E, IDiv, S),
+ E1 = prune(E, IDiv, ?EMPTY),
I1 = IDiv + 1,
{E2,_} = shrink_2(IRem, ?reduce(S), element(I1, E1), D),
{setelement(I1, E1, E2), S}.
@@ -519,8 +502,10 @@ See also `resize/2`, `sparse_size/1`.
-spec resize(Array :: array(Type)) -> array(Type).
resize(Array) ->
+ %% eqwalizer:ignore ambiguous_union
resize(sparse_size(Array), Array).
+
-doc """
Sets entry `I` of the array to `Value`.
@@ -534,10 +519,11 @@ See also `get/2`, `reset/2`.
""".
-spec set(I :: array_indx(), Value :: Type, Array :: array(Type)) -> array(Type).
-set(I, Value, #array{size = N, fix = Fix, cache = C, cache_index = CI,
+set(I0, Value, #array{size = N, zero = Z, fix = Fix, cache = C, cache_index = CI,
default = D, elements = E, bits = S}=A)
- when is_integer(I), I >= 0, is_integer(N), is_integer(CI), is_integer(S) ->
- if I < N ->
+ when is_integer(I0), I0 >= 0, is_integer(N), is_integer(CI), is_integer(S), is_integer(Z) ->
+ I = I0 + Z,
+ if I0 < N ->
if I >= CI, I < CI + ?LEAFSIZE ->
A#array{cache = setelement(1 + I - CI, C, Value)};
true ->
@@ -545,25 +531,25 @@ set(I, Value, #array{size = N, fix = Fix, cache = C, cache_index = CI,
CI1 = I - R,
E1 = set_leaf(CI, S, E, C),
C1 = get_leaf(CI1, S, E1, D),
- C2 = setelement(1 + I - CI1, C1, Value),
+ C2 = setelement(1 + R, C1, Value),
A#array{elements = E1, cache = C2, cache_index = CI1}
end;
Fix ->
erlang:error(badarg);
true ->
- M = ?SIZE(?extend(S)),
- if I < M ->
+ N1 = I0 + 1,
+ if I < ?SIZE(?extend(S)) ->
R = I band ?MASK,
CI1 = I - R,
if CI1 =/= CI ->
E1 = set_leaf(CI, S, E, C),
C1 = get_leaf(CI1, S, E1, D),
C2 = setelement(1 + R, C1, Value),
- A#array{size = I+1, elements = E1,
+ A#array{size = N1, elements = E1,
cache = C2, cache_index = CI1};
true ->
C1 = setelement(1 + R, C, Value),
- A#array{size = I+1, cache = C1, cache_index = CI1}
+ A#array{size = N1, cache = C1, cache_index = CI1}
end;
true ->
R = I band ?MASK,
@@ -572,7 +558,7 @@ set(I, Value, #array{size = N, fix = Fix, cache = C, cache_index = CI,
E2 = set_leaf(CI, S1, E1, C),
C1 = get_leaf(CI1, S1, E2, D),
C2 = setelement(1 + R, C1, Value),
- A#array{size = I+1, elements = E2,
+ A#array{size = N1, elements = E2,
cache = C2, cache_index = CI1, bits = S1}
end
end;
@@ -581,23 +567,175 @@ set(_I, _V, _A) ->
%% Enlarging the array upwards to accommodate an index `I'
-grow(I, E, M) when is_integer(I), is_integer(E) ->
- S = find_max(I, M),
- {S, ?reduce(S)};
+grow(I, ?EMPTY, S) when is_integer(I) ->
+ S1 = find_bits(I, S),
+ {?EMPTY, S1};
grow(I, E, 0) ->
grow_1(I, E, 0);
-grow(I, E, M) ->
- grow_1(I, E, M).
+grow(I, E, S) ->
+ grow_1(I, E, S).
-grow_1(I, E, M0) ->
- M = ?extend(M0),
- case I >= ?SIZE(M) of
+grow_1(I, E, S) ->
+ S1 = ?extend(S),
+ if I < ?SIZE(S1) ->
+ {E, S};
true ->
- grow_1(I, setelement(1, ?NEW_NODE(M), E), M);
- false ->
- {E, M0}
+ grow_1(I, setelement(1, ?NEW_NODE(S), E), S1)
end.
+%% similar to get_1
+get_leaf(_I, _, ?EMPTY, D) ->
+ ?NEW_CACHE(D);
+get_leaf(_I, 0, E, _D) ->
+ E;
+get_leaf(I, S, E, D) ->
+ IDiv = (I bsr S) band ?MASK,
+ get_leaf(I, ?reduce(S), element(IDiv + 1, E), D).
+
+%% similar to set_1()
+set_leaf(_I, 0, _E, C) ->
+ C;
+set_leaf(I, S, ?EMPTY, C) ->
+ set_leaf_1(I, S, C);
+set_leaf(I, S, E, C) when S > 0 ->
+ IDiv = (I bsr S) band ?MASK,
+ I1 = IDiv+1,
+ setelement(I1, E, set_leaf(I, ?reduce(S), element(I1, E), C)).
+
+set_leaf_1(I, S, C) when S > 0 ->
+ IDiv = (I bsr S) band ?MASK,
+ setelement(IDiv+1, ?NEW_NODE(S), set_leaf_1(I, ?reduce(S), C));
+set_leaf_1(_I, _S, C) ->
+ C.
+
+
+-doc """
+Shift the array a number of steps to the left, or to the right if the
+number is negative.
+
+Shifting left drops elements from the left side, reducing the array
+size, and shifting right adds space on the left, increasing the array
+size.
+
+Note: For efficiency, this does not prune the representation, which means
+that a subsequent shift or similar operation can bring back the values that
+were shifted out. Use `resize/2` or `resize/1` if you want to ensure that
+values outside the range get pruned.
+""".
+-spec shift(Steps :: integer(), Array :: array(Type)) -> array(Type).
+shift(0, A=#array{}) ->
+ A;
+shift(Steps, #array{size = N, zero = Z}=A)
+ when is_integer(Steps), is_integer(N), Steps =< N, is_integer(Z) ->
+ Z1 = Z + Steps,
+ N1 = N - Steps,
+ if Z1 >= 0 ->
+ A#array{size = N1, zero = Z1};
+ true ->
+ #array{cache_index = CI, elements = E, bits = S} = A,
+ {E1, S1, Z2} = grow_left(Z1, E, S),
+ CI1 = CI + (Z2-Z1),
+ A#array{size = N1, zero = Z2, cache_index = CI1, elements = E1, bits = S1}
+ end;
+shift(_Steps, _A) ->
+ erlang:error(badarg).
+
+%% Enlarging the array to the left until the zero point is no longer negative.
+
+grow_left(Z, ?EMPTY, S) ->
+ grow_left_2(Z, S);
+grow_left(Z, E, S) ->
+ grow_left_1(Z, E, S).
+
+grow_left_1(Z, E, S) when Z >= 0 ->
+ {E, S, Z};
+grow_left_1(Z, E, S) ->
+ S1 = ?extend(S),
+ I = ?NODESIZE div 2,
+ grow_left_1(Z + I*?SIZE(S1), setelement(I+1, ?NEW_NODE(S1), E), S1).
+
+grow_left_2(Z, S) when Z >= 0 ->
+ {?EMPTY, S, Z};
+grow_left_2(Z, S) ->
+ S1 = ?extend(S),
+ I = ?NODESIZE div 2,
+ grow_left_2(Z + I*?SIZE(S1), S1).
+
+
+-doc """
+Extract a slice of the array.
+
+This drops elements before `I` as with `shift/2`, and takes the following
+`Length` elements starting from `I`.
+
+If `N` is less than or equal to zero, the resulting array is empty. To extract
+a slice from `Start` to `End` inclusive, use `slice(Start, End-Start+1,
+Array)`.
+
+Note: For efficiency, this does not prune the representation, which means
+that a subsequent shift or similar operation can bring back the values that
+were shifted out. Use `resize/2` or `resize/1` if you want to ensure that
+values outside the range get pruned.
+""".
+-spec slice(I :: array_indx(), Length :: non_neg_integer(), Array :: array(Type)) -> array(Type).
+slice(I, Length, #array{size = N}=A)
+ when is_integer(I), I >= 0, is_integer(N), N >= 0, I + Length =< N ->
+ %% eqwalizer:ignore ambiguous_union
+ A1 = shift(I, A),
+ A1#array{size = Length};
+slice(_I, _N, _A) ->
+ erlang:error(badarg).
+
+
+-doc """
+Append a single value to the right side of the array.
+
+See also `prepend/2`, `concat/2`.
+""".
+-spec append(Value :: any(), Array :: array(Type)) -> array(Type).
+append(Value, #array{size = N, zero = Z, cache = C, cache_index = CI,
+ default = D, elements = E, bits = S}=A)
+ when is_integer(N), is_integer(CI), is_integer(S), is_integer(Z) ->
+ I = N + Z,
+ N1 = N + 1,
+ %% for speed, this is an inlined copy of the growing case from set/3
+ %% since append always allows growing
+ if
+ I < CI + ?LEAFSIZE ->
+ A#array{size = N1, cache = setelement(1 + I - CI, C, Value)};
+ I < ?SIZE(?extend(S)) ->
+ R = I band ?MASK,
+ CI1 = I - R,
+ E1 = set_leaf(CI, S, E, C),
+ C1 = get_leaf(CI1, S, E1, D),
+ C2 = setelement(1 + R, C1, Value),
+ A#array{size = N1, elements = E1,
+ cache = C2, cache_index = CI1};
+ true ->
+ R = I band ?MASK,
+ CI1 = I - R,
+ {E1,S1} = grow(I, E, S),
+ E2 = set_leaf(CI, S1, E1, C),
+ C1 = get_leaf(CI1, S1, E2, D),
+ C2 = setelement(1 + R, C1, Value),
+ A#array{size = N1, elements = E2,
+ cache = C2, cache_index = CI1, bits = S1}
+ end;
+append(_V, _A) ->
+ erlang:error(badarg).
+
+
+-doc """
+Prepend a single value to the left side of the array.
+
+See also `append/2`, `concat/2`.
+""".
+-spec prepend(Value :: Type, Array :: array(Type)) -> array(Type).
+prepend(Value, #array{}=A) ->
+ %% eqwalizer:ignore ambiguous_union
+ set(0, Value, shift(-1, A)).
+
+
-doc """
Gets the value of entry `I`.
@@ -611,9 +749,11 @@ See also `set/3`.
""".
-spec get(I :: array_indx(), Array :: array(Type)) -> Value :: Type.
-get(I, #array{size = N, fix = Fix, cache = C, cache_index = CI, elements = E, default = D, bits = S})
- when is_integer(I), I >= 0, is_integer(N), is_integer(CI), is_integer(S) ->
- if I < N ->
+get(I0, #array{size = N, zero = Z, fix = Fix, cache = C, cache_index = CI,
+ elements = E, default = D, bits = S})
+ when is_integer(I0), I0 >= 0, is_integer(N), is_integer(CI), is_integer(S), is_integer(Z) ->
+ if I0 < N ->
+ I = I0 + Z,
if I >= CI, I < CI + ?LEAFSIZE ->
element(1 + I - CI, C);
true ->
@@ -627,7 +767,7 @@ get(I, #array{size = N, fix = Fix, cache = C, cache_index = CI, elements = E, de
get(_I, _A) ->
erlang:error(badarg).
-get_1(_I, _S, E, D) when is_integer(E) ->
+get_1(_I, _S, ?EMPTY, D) ->
D;
get_1(I, 0, E, _D) ->
element((I band ?MASK)+1, E);
@@ -652,9 +792,11 @@ See also `new/2`, `set/3`.
""".
-spec reset(I :: array_indx(), Array :: array(Type)) -> array(Type).
-reset(I, #array{size = N, fix = Fix, cache = C, cache_index = CI, default = D, elements = E, bits = S}=A)
- when is_integer(I), I >= 0, is_integer(N), is_boolean(Fix), is_integer(CI) ->
- if I < N ->
+reset(I0, #array{size = N, zero = Z, fix = Fix, cache = C, cache_index = CI,
+ default = D, elements = E, bits = S}=A)
+ when is_integer(I0), I0 >= 0, is_integer(N), is_integer(CI), is_integer(S), is_integer(Z) ->
+ if I0 < N ->
+ I = I0 + Z,
if I >= CI, I < CI + ?LEAFSIZE ->
A#array{cache = setelement(1 + I - CI, C, D)};
true ->
@@ -670,7 +812,7 @@ reset(I, #array{size = N, fix = Fix, cache = C, cache_index = CI, default = D, e
reset(_I, _A) ->
erlang:error(badarg).
-reset_1(_I, _, E, _D) when is_integer(E) ->
+reset_1(_I, _, ?EMPTY, _D) ->
throw(default);
reset_1(I, 0, E, D) ->
Indx = (I band ?MASK)+1,
@@ -683,56 +825,64 @@ reset_1(I, S, E, D) ->
I1 = IDiv + 1,
setelement(I1, E, reset_1(I, ?reduce(S), element(I1, E), D)).
+
-doc """
-Converts the array to a list.
+Concatenates two arrays.
-See also `from_list/2`, `sparse_to_list/1`.
+Note: the result will always be an extendible array. Use `fix/1` on the
+result if you want to prevent accesses outside the size range.
+
+See also `concat/1`, `append/2`, `prepend/2`.
""".
--spec to_list(Array :: array(Type)) -> list(Value :: Type).
+-spec concat(Left :: array(Type), Right :: array(Type)) -> array(Type).
-to_list(#array{size = 0}) ->
- [];
-to_list(#array{size = N, cache = C, cache_index = CI, elements = E, default = D, bits = M})
- when is_integer(N), is_integer(CI), is_integer(M) ->
- E1 = set_leaf(CI, M, E, C),
- to_list_1(E1, M, D, N - 1);
-to_list(_) ->
+concat(#array{size = LeftN, fix = Fix, default = DefA}=Left,
+ #array{size = RightN, default = DefB}=Right) ->
+ if RightN > LeftN, DefA =:= DefB ->
+ %% eqwalizer:ignore ambiguous_union
+ foldr(fun (_I, V, Acc) -> prepend(V, Acc) end, Right#array{fix = Fix}, Left);
+ true ->
+ %% eqwalizer:ignore ambiguous_union
+ foldl(fun (_I, V, Acc) -> append(V, Acc) end, Left, Right)
+ end;
+concat(_, _) ->
erlang:error(badarg).
-%% this part handles the rightmost subtrees
+-doc """
+Concatenates a nonempty list of arrays.
-to_list_1(E, _S, D, I) when is_integer(E) ->
- push(I+1, D, []);
-to_list_1(E, 0, _D, I) ->
- push_tuple(I+1, E, []);
-to_list_1(E, S, D, I) ->
- N = I bsr S,
- IRem = I band ?MASK(S),
- to_list_3(N, S, D, to_list_1(element(N+1, E), ?reduce(S), D, IRem), E).
+See also `concat/2`.
+""".
+-spec concat(Arrays :: [array(Type)]) -> array(Type).
-%% this part handles full trees only
+concat([A0|As]) ->
+ %% eqwalizer:ignore ambiguous_union
+ lists:foldl(fun (A, Acc) -> concat(Acc, A) end, A0, As);
+concat(_) ->
+ erlang:error(badarg).
-to_list_2(E, _S, D, L) when is_integer(E) ->
- push(?SIZE(E), D, L);
-to_list_2(E, 0, _D, L) ->
- push_tuple(?LEAFSIZE, E, L);
-to_list_2(E, S, D, L) ->
- to_list_3(?NODESIZE, S, D, L, E).
-to_list_3(0, _S, _D, L, _E) ->
- L;
-to_list_3(N, S, D, L, E) ->
- to_list_3(N-1, S, D, to_list_2(element(N, E), ?reduce(S), D, L), E).
+-doc """
+Converts the array to a list.
-push(0, _E, L) ->
- L;
-push(N, E, L) ->
- push(N - 1, E, [E | L]).
+See also `from_list/2`, `sparse_to_list/1`.
+""".
+-spec to_list(Array :: array(Type)) -> list(Value :: Type).
+
+to_list(Array) ->
+ %% eqwalizer:ignore ambiguous_union
+ foldr(fun (_I, V, A) -> [V|A] end, [], Array).
+
+%% -spec prop_to_list() -> term().
+%% prop_to_list() ->
+%% ?FORALL(Low, proper_types:non_neg_integer(),
+%% ?FORALL(Delta, proper_types:non_neg_integer(),
+%% begin
+%% L = lists:seq(0,Low+Delta),
+%% L =:= to_list(array:from_list(L))
+%% end)
+%% ).
-push_tuple(0, _T, L) ->
- L;
-push_tuple(N, T, L) ->
- push_tuple(N - 1, T, [element(N, T) | L]).
-doc """
Converts the array to a list, skipping default-valued entries.
@@ -741,49 +891,10 @@ See also `to_list/1`.
""".
-spec sparse_to_list(Array :: array(Type)) -> list(Value :: Type).
-sparse_to_list(#array{size = 0}) ->
- [];
-sparse_to_list(#array{size = N, cache = C, cache_index = CI, elements = E, default = D, bits = M})
- when is_integer(N), is_integer(CI), is_integer(M) ->
- E1 = set_leaf(CI, M, E, C),
- sparse_to_list_1(E1, M, D, N - 1);
-sparse_to_list(_) ->
- erlang:error(badarg).
-
-%% see to_list/1 for details
+sparse_to_list(Array) ->
+ %% eqwalizer:ignore ambiguous_union
+ sparse_foldr(fun (_I, V, A) -> [V|A] end, [], Array).
-sparse_to_list_1(E, _S, _D, _I) when is_integer(E) ->
- [];
-sparse_to_list_1(E, 0, D, I) ->
- sparse_push_tuple(I+1, D, E, []);
-sparse_to_list_1(E, S, D, I) ->
- N = I bsr S,
- IRem = I band ?MASK(S),
- sparse_to_list_3(N, S, D,
- sparse_to_list_1(element(N+1, E), ?reduce(S), D, IRem),
- E).
-
-sparse_to_list_2(E, _S, _D, L) when is_integer(E) ->
- L;
-sparse_to_list_2(E, 0, D, L) ->
- sparse_push_tuple(?LEAFSIZE, D, E, L);
-sparse_to_list_2(E, S, D, L) ->
- sparse_to_list_3(?NODESIZE, S, D, L, E).
-
-sparse_to_list_3(0, _S, _D, L, _E) ->
- L;
-sparse_to_list_3(N, S, D, L, E) ->
- sparse_to_list_3(N-1, S, D, sparse_to_list_2(element(N, E), ?reduce(S), D, L), E).
-
-sparse_push_tuple(0, _D, _T, L) ->
- L;
-sparse_push_tuple(N, D, T, L) ->
- case element(N, T) of
- D -> sparse_push_tuple(N - 1, D, T, L);
- E -> sparse_push_tuple(N - 1, D, T, [E | L])
- end.
-
-%% @equiv from_list(List, undefined)
-doc "Equivalent to [`from_list(List, undefined)`](`from_list/2`).".
-spec from_list(List :: list(Value :: Type)) -> array(Type).
@@ -797,6 +908,9 @@ uninitialized entries of the array.
If `List` is not a proper list, the call fails with reason `badarg`.
+Note: Use `fix/1` on the resulting array if you want to prevent accesses
+outside the size range.
+
See also `new/2`, `to_list/1`.
""".
-spec from_list(List :: list(Value :: Type), Default :: term()) -> array(Type).
@@ -804,12 +918,12 @@ See also `new/2`, `to_list/1`.
from_list([], Default) ->
new({default,Default});
from_list(List, Default) when is_list(List) ->
- {E, N, M0} = from_list_1(?LEAFSIZE, List, Default, 0, [], []),
+ {E, N, S0} = from_list_1(?LEAFSIZE, List, Default, 0, [], []),
CI = 0,
- M = ?reduce(M0),
- C = get_leaf(CI, M, E, Default),
- #array{size = N, fix = false, cache = C, cache_index = CI,
- default = Default, elements = E, bits = M};
+ S = ?reduce(S0),
+ C = get_leaf(CI, S, E, Default),
+ #array{size = N, zero = 0, fix = false, cache = C, cache_index = CI,
+ default = Default, elements = E, bits = S};
from_list(_, _) ->
erlang:error(badarg).
@@ -845,7 +959,7 @@ from_list_1(I, Xs, D, N, As, Es) ->
%% Building the internal nodes (note that the input is reversed).
from_list_2_0(N, Es, S) ->
- from_list_2(?NODESIZE, pad(((N-1) bsr S) + 1, ?NODESIZE, S, Es),
+ from_list_2(?NODESIZE, pad(((N-1) bsr S) + 1, ?NODESIZE, ?EMPTY, Es),
S, N, [], []).
from_list_2(0, Xs, S, N, As, Es) ->
@@ -869,7 +983,12 @@ from_list_2(I, [X | Xs], S, N, As, Es) ->
%% left-padding a list Es with elements P to the nearest multiple of K
%% elements from N (adding 0 to K-1 elements).
pad(N, K, P, Es) ->
- push((K - (N rem K)) rem K, P, Es).
+ push_n((K - (N rem K)) rem K, P, Es).
+
+push_n(0, _E, L) ->
+ L;
+push_n(N, E, L) ->
+ push_n(N - 1, E, [E | L]).
-doc "Equivalent to [`from(Fun, State, undefined)`](`from/3`).".
@@ -889,6 +1008,9 @@ until `done` is returned, otherwise the call fails with reason `badarg`.
`Default` is used as the value for uninitialized entries of the array.
+Note: Use `fix/1` on the resulting array if you want to prevent accesses
+outside the size range.
+
## Examples
```erlang
@@ -918,18 +1040,17 @@ See also `new/2`, `from_list/1`, `foldl/3`.
-spec from(Function, State :: term(), Default :: term()) -> array(Type) when
Function :: fun((State0 :: term()) -> {Type, State1 :: term()} | done).
-from(Fun, S0, Default) when is_function(Fun, 1) ->
- VS = Fun(S0),
- {E, N, M0} = from_fun_1(?LEAFSIZE, Default, Fun, VS, 0, [], []),
+from(Fun, St0, Default) when is_function(Fun, 1) ->
+ VS = Fun(St0),
+ {E, N, S0} = from_fun_1(?LEAFSIZE, Default, Fun, VS, 0, [], []),
CI = 0,
- M = ?reduce(M0),
- C = get_leaf(CI, M, E, Default),
- #array{size = N, fix = false, cache = C, cache_index = CI,
- default = Default, elements = E, bits = M};
+ S = ?reduce(S0),
+ C = get_leaf(CI, S, E, Default),
+ #array{size = N, zero = 0, fix = false, cache = C, cache_index = CI,
+ default = Default, elements = E, bits = S};
from(_, _, _) ->
error(badarg).
-
from_fun_1(0, D, Fun, VS, N, As, Es) ->
E = list_to_tuple(lists:reverse(As)),
case VS of
@@ -958,58 +1079,9 @@ See also `from_orddict/2`, `sparse_to_orddict/1`.
""".
-spec to_orddict(Array :: array(Type)) -> indx_pairs(Value :: Type).
-to_orddict(#array{size = 0}) ->
- [];
-to_orddict(#array{size = N, cache = C, cache_index = CI, elements = E, default = D, bits = M})
- when is_integer(N), is_integer(CI), is_integer(M) ->
- E1 = set_leaf(CI, M, E, C),
- I = N - 1,
- to_orddict_1(E1, I, D, I, M);
-to_orddict(_) ->
- erlang:error(badarg).
-
-%% see to_list/1 for comparison
-
-to_orddict_1(E, R, D, I, _S) when is_integer(E) ->
- push_pairs(I+1, R, D, []);
-to_orddict_1(E, R, _D, I, 0) ->
- push_tuple_pairs(I+1, R, E, []);
-to_orddict_1(E, R, D, I, S) ->
- N = I bsr S,
- I1 = I band ?MASK(S),
- to_orddict_3(N, R - I1 - 1, D,
- to_orddict_1(element(N+1, E), R, D, I1, ?reduce(S)),
- E, S).
-
-to_orddict_2(E, R, D, L, _S) when is_integer(E) ->
- push_pairs(?SIZE(E), R, D, L);
-to_orddict_2(E, R, _D, L, 0) ->
- push_tuple_pairs(?LEAFSIZE, R, E, L);
-to_orddict_2(E, R, D, L, S) ->
- to_orddict_3(?NODESIZE, R, D, L, E, S).
-
-to_orddict_3(0, _R, _D, L, _E, _S) -> %% when is_integer(R) ->
- L;
-to_orddict_3(N, R, D, L, E, S) ->
- to_orddict_3(N-1, R - ?SIZE(S), D,
- to_orddict_2(element(N, E), R, D, L, ?reduce(S)),
- E, S).
-
--spec push_pairs(non_neg_integer(), array_indx(), term(), indx_pairs(Type)) ->
- indx_pairs(Type).
-
-push_pairs(0, _I, _E, L) ->
- L;
-push_pairs(N, I, E, L) ->
- push_pairs(N-1, I-1, E, [{I, E} | L]).
-
--spec push_tuple_pairs(non_neg_integer(), array_indx(), term(), indx_pairs(Type)) ->
- indx_pairs(Type).
-
-push_tuple_pairs(0, _I, _T, L) ->
- L;
-push_tuple_pairs(N, I, T, L) ->
- push_tuple_pairs(N-1, I-1, T, [{I, element(N, T)} | L]).
+to_orddict(Array) ->
+ %% eqwalizer:ignore ambiguous_union
+ foldr(fun (I, V, A) -> [{I,V}|A] end, [], Array).
-doc """
@@ -1020,52 +1092,10 @@ See also `to_orddict/1`.
""".
-spec sparse_to_orddict(Array :: array(Type)) -> indx_pairs(Value :: Type).
-sparse_to_orddict(#array{size = 0}) ->
- [];
-sparse_to_orddict(#array{size = N, cache = C, cache_index = CI, elements = E, default = D, bits = M})
- when is_integer(N), is_integer(CI), is_integer(M) ->
- E1 = set_leaf(CI, M, E, C),
- I = N - 1,
- sparse_to_orddict_1(E1, I, D, I, M);
-sparse_to_orddict(_) ->
- erlang:error(badarg).
-
-%% see to_orddict/1 for details
-sparse_to_orddict_1(E, _R, _D, _I,_S) when is_integer(E) ->
- [];
-sparse_to_orddict_1(E, R, D, I, 0) ->
- sparse_push_tuple_pairs(I+1, R, D, E, []);
-sparse_to_orddict_1(E, R, D, I, S) ->
- N = I bsr S,
- I1 = I band ?MASK(S),
- sparse_to_orddict_3(N, R - I1 - 1, D,
- sparse_to_orddict_1(element(N+1, E), R, D, I1, ?reduce(S)),
- E, S).
-
-sparse_to_orddict_2(E, _R, _D, L, _S) when is_integer(E) ->
- L;
-sparse_to_orddict_2(E, R, D, L, 0) ->
- sparse_push_tuple_pairs(?LEAFSIZE, R, D, E, L);
-sparse_to_orddict_2(E, R, D, L, S) ->
- sparse_to_orddict_3(?NODESIZE, R, D, L, E, S).
-
-sparse_to_orddict_3(0, _R, _D, L, _E, _S) -> % when is_integer(R) ->
- L;
-sparse_to_orddict_3(N, R, D, L, E, S) ->
- sparse_to_orddict_3(N-1, R - ?SIZE(S), D,
- sparse_to_orddict_2(element(N, E), R, D, L,?reduce(S)),
- E, S).
-
--spec sparse_push_tuple_pairs(non_neg_integer(), array_indx(),
- _, _, indx_pairs(Type)) -> indx_pairs(Type).
+sparse_to_orddict(Array) ->
+ %% eqwalizer:ignore ambiguous_union
+ sparse_foldr(fun (I, V, A) -> [{I,V}|A] end, [], Array).
-sparse_push_tuple_pairs(0, _I, _D, _T, L) ->
- L;
-sparse_push_tuple_pairs(N, I, D, T, L) ->
- case element(N, T) of
- D -> sparse_push_tuple_pairs(N-1, I-1, D, T, L);
- E -> sparse_push_tuple_pairs(N-1, I-1, D, T, [{I, E} | L])
- end.
-doc "Equivalent to [`from_orddict(Orddict, undefined)`](`from_orddict/2`).".
-spec from_orddict(Orddict :: indx_pairs(Value :: Type)) -> array(Type).
@@ -1080,20 +1110,23 @@ array. `Default` is used as the value for uninitialized entries of the array.
If `Orddict` is not a proper, ordered list of pairs whose first elements are
non-negative integers, the call fails with reason `badarg`.
+Note: Use `fix/1` on the resulting array if you want to prevent accesses
+outside the size range.
+
See also `new/2`, `to_orddict/1`.
""".
--spec from_orddict(Orddict :: indx_pairs(Value :: Type), Default :: Type) ->
+-spec from_orddict(Orddict :: indx_pairs(Value :: Type), Default :: dynamic()) ->
array(Type).
from_orddict([], Default) ->
new({default,Default});
from_orddict(List, Default) when is_list(List) ->
- {E, N, M0} = from_orddict_0(List, 0, ?LEAFSIZE, Default, []),
+ {E, N, S0} = from_orddict_0(List, 0, ?LEAFSIZE, Default, []),
CI = 0,
- M = ?reduce(M0),
- C = get_leaf(CI, M, E, Default),
- #array{size = N, fix = false, cache = C, cache_index = CI,
- default = Default, elements = E, bits = M};
+ S = ?reduce(S0),
+ C = get_leaf(CI, S, E, Default),
+ #array{size = N, zero = 0, fix = false, cache = C, cache_index = CI,
+ default = Default, elements = E, bits = S};
from_orddict(_, _) ->
erlang:error(badarg).
@@ -1178,17 +1211,17 @@ collect_leafs(I, [X | Xs], S, N, As0, Es0)
Step0 = (X bsr S),
if
Step0 < I ->
- As = push(Step0, S, As0),
+ As = push_n(Step0, ?EMPTY, As0),
collect_leafs(I-Step0, Xs, S, N, As, Es0);
I =:= ?NODESIZE ->
Step = Step0 rem ?NODESIZE,
- As = push(Step, S, As0),
+ As = push_n(Step, ?EMPTY, As0),
collect_leafs(I-Step, Xs, S, N, As, [X|Es0]);
I =:= Step0 ->
- As = push(I, S, As0),
+ As = push_n(I, ?EMPTY, As0),
collect_leafs(0, Xs, S, N, As, Es0);
true ->
- As = push(I, S, As0),
+ As = push_n(I, ?EMPTY, As0),
Step = Step0 - I,
collect_leafs(0, [Step bsl S|Xs], S, N, As, Es0)
end;
@@ -1205,68 +1238,18 @@ order from the lowest index to the highest.
If `Function` is not a function, the call fails with reason `badarg`.
-See also `foldl/3`, `foldr/3`, `sparse_map/2`.
+See also `mapfoldl/3`, `sparse_map/2`.
""".
--spec map(Function, Array :: array(Type1)) -> array(Type2) when
+-spec map(Function, Array :: array(Type1)) -> array(Type1 | Type2) when
Function :: fun((Index :: array_indx(), Type1) -> Type2).
-map(Function, Array=#array{size = N, cache = C, cache_index = CI, elements = E, default = D, bits = M})
- when is_function(Function, 2), is_integer(N), is_integer(CI), is_integer(M) ->
- if N > 0 ->
- E1 = set_leaf(CI, M, E, C),
- A = Array#array{elements = []}, % kill reference, for GC
- E2 = map_1(N-1, E1, M, 0, Function, D),
- CI1 = 0,
- C1 = get_leaf(CI1, M, E2, D),
- A#array{elements = E2, cache = C1, cache_index = CI1};
- true ->
- Array
- end;
+map(Function, Array) when is_function(Function, 2) ->
+ %% eqwalizer:ignore ambiguous_union
+ {Array1, _} = mapfoldl(fun (I, V, _) -> {Function(I, V), []} end, [], Array),
+ Array1;
map(_, _) ->
erlang:error(badarg).
-%% It might be simpler to traverse the array right-to-left, as done e.g.
-%% in the to_orddict/1 function, but it is better to guarantee
-%% left-to-right application over the elements - that is more likely to
-%% be a generally useful property.
-
-map_1(N, E, S, Ix, F, D) when is_integer(E) ->
- map_1(N, unfold(E, D), S, Ix, F, D);
-map_1(N, E, 0, Ix, F, D) ->
- list_to_tuple(lists:reverse(map_3(1, E, Ix, F, D, N+1, [])));
-map_1(N, E, S, Ix, F, D) ->
- List = map_2(1, E, S, Ix, F, D, [],
- (N bsr S) + 1, N band ?MASK(S)),
- list_to_tuple(lists:reverse([S | List])).
-
-map_2(I, E, S, Ix, F, D, L, I, R) ->
- map_2_1(I+1, E, [map_1(R, element(I, E), ?reduce(S), Ix, F, D) | L]);
-map_2(I, E, S, Ix, F, D, L, N, R) ->
- Sz = ?SIZE(S),
- map_2(I+1, E, S, Ix + Sz, F, D,
- [map_1(Sz-1, element(I, E), ?reduce(S), Ix, F, D) | L],
- N, R).
-
-map_2_1(I, E, L) when I =< ?NODESIZE ->
- map_2_1(I+1, E, [element(I, E) | L]);
-map_2_1(_I, _E, L) ->
- L.
-
--spec map_3(pos_integer(), _, array_indx(),
- fun((array_indx(),_) -> _), _, non_neg_integer(), [X]) -> [X].
-
-map_3(I, E, Ix, F, D, N, L) when I =< N ->
- map_3(I+1, E, Ix+1, F, D, N, [F(Ix, element(I, E)) | L]);
-map_3(I, E, Ix, F, D, N, L) when I =< ?LEAFSIZE ->
- map_3(I+1, E, Ix+1, F, D, N, [D | L]);
-map_3(_I, _E, _Ix, _F, _D, _N, L) ->
- L.
-
-
-unfold(S, _D) when ?SIZE(S) > ?LEAFSIZE ->
- ?NEW_NODE(?reduce(S));
-unfold(_S, D) ->
- ?NEW_LEAF(D).
-doc """
Maps the specified function onto each array element, skipping default-valued
@@ -1276,59 +1259,16 @@ If `Function` is not a function, the call fails with reason `badarg`.
See also `map/2`.
""".
--spec sparse_map(Function, Array :: array(Type1)) -> array(Type2) when
+-spec sparse_map(Function, Array :: array(Type1)) -> array(Type1 | Type2) when
Function :: fun((Index :: array_indx(), Type1) -> Type2).
-sparse_map(Function, Array=#array{size = N, cache = C, cache_index = CI, elements = E, default = D, bits = M})
- when is_function(Function, 2), is_integer(N), is_integer(CI), is_integer(M) ->
- if N > 0 ->
- E1 = set_leaf(CI, M, E, C),
- A = Array#array{elements = []}, % kill reference, for GC
- E2 = sparse_map_1(N-1, E1, M, 0, Function, D),
- CI1 = 0,
- C1 = get_leaf(CI1, M, E2, D),
- A#array{elements = E2, cache = C1, cache_index = CI1};
- true ->
- Array
- end;
+sparse_map(Function, Array) when is_function(Function, 2) ->
+ %% eqwalizer:ignore ambiguous_union
+ {Array1, _} = sparse_mapfoldl(fun (I, V, _) -> {Function(I, V), []} end, [], Array),
+ Array1;
sparse_map(_, _) ->
erlang:error(badarg).
-%% see map/2 for details
-%% TODO: we can probably optimize away the use of div/rem here
-
-sparse_map_1(_N, E, _S, _Ix, _F, _D) when is_integer(E) ->
- E;
-sparse_map_1(_N, E, 0, Ix, F, D) ->
- list_to_tuple(lists:reverse(sparse_map_3(1, E, Ix, F, D, [])));
-sparse_map_1(N, E, S, Ix, F, D) ->
- List = sparse_map_2(1, E, S, Ix, F, D, [], (N bsr S) + 1, N band ?MASK(S)),
- list_to_tuple(lists:reverse([S | List])).
-
-sparse_map_2(I, E, S, Ix, F, D, L, N, R) when N =:= I ->
- sparse_map_2_1(I+1, E,
- [sparse_map_1(R, element(I, E), ?reduce(S), Ix, F, D) | L]);
-sparse_map_2(I, E, S, Ix, F, D, L, N, R) ->
- Sz = ?SIZE(S),
- sparse_map_2(I+1, E, S, Ix + Sz, F, D,
- [sparse_map_1(Sz-1, element(I, E), ?reduce(S), Ix, F, D) | L],
- N, R).
-
-sparse_map_2_1(I, E, L) when I =< ?NODESIZE ->
- sparse_map_2_1(I+1, E, [element(I, E) | L]);
-sparse_map_2_1(_I, _E, L) ->
- L.
-
--spec sparse_map_3(pos_integer(), _, array_indx(),
- fun((array_indx(),_) -> _), _, [X]) -> [X].
-
-sparse_map_3(I, T, Ix, F, D, L) when I =< ?LEAFSIZE ->
- case element(I, T) of
- D -> sparse_map_3(I+1, T, Ix+1, F, D, [D | L]);
- E -> sparse_map_3(I+1, T, Ix+1, F, D, [F(Ix, E) | L])
- end;
-sparse_map_3(_I, _E, _Ix, _F, _D, L) ->
- L.
-doc """
Folds the array elements using the specified function and initial accumulator
@@ -1336,44 +1276,125 @@ value. The elements are visited in order from the lowest index to the highest.
If `Function` is not a function, the call fails with reason `badarg`.
-See also `foldr/3`, `map/2`, `sparse_foldl/3`.
+See also `foldl/5`, `foldr/3`, `sparse_foldl/3`.
+""".
+-spec foldl(Function, InitialAcc :: A, Array :: array(Type)) -> A when
+ Function :: fun((Index :: array_indx(), Value :: Type, Acc :: A) -> A).
+
+foldl(Function, Acc, #array{size = N}=Array) when is_integer(N) ->
+ %% eqwalizer:ignore ambiguous_union
+ foldl(0, N-1, Function, Acc, Array);
+foldl(_, _, _) ->
+ erlang:error(badarg).
+
+-doc """
+Folds the array elements from `Low` to `High` using the specified function and
+initial accumulator value. The elements are visited in order from the lowest
+index to the highest.
+
+If `Function` is not a function, the call fails with reason `badarg`.
+
+See also `foldl/3`, `sparse_foldl/5`.
""".
--spec foldl(Function, InitialAcc :: A, Array :: array(Type)) -> B when
- Function :: fun((Index :: array_indx(), Value :: Type, Acc :: A) -> B).
-
-foldl(Function, A, #array{size = N, cache = C, cache_index = CI, elements = E, default = D, bits = M})
- when is_function(Function, 3), is_integer(N), is_integer(CI), is_integer(M) ->
- if N > 0 ->
- E1 = set_leaf(CI, M, E, C),
- foldl_1(N-1, E1, M, A, 0, Function, D);
+-spec foldl(Low, High, Function, InitialAcc :: A, Array) -> A when
+ Low :: array_indx(),
+ High :: array_indx(),
+ Function :: fun((Index :: array_indx(), Value :: Type, Acc :: A) -> A),
+ Array :: array(Type).
+
+foldl(Low, High, Function, Acc, #array{size = N, zero = Z, cache = C, cache_index = CI, elements = E, default = D, bits = S})
+ when is_integer(Low), Low >= 0, is_integer(High), is_function(Function, 3), is_integer(N), High < N, is_integer(Z), is_integer(CI), is_integer(S) ->
+ if Low =< High ->
+ E1 = set_leaf(CI, S, E, C),
+ foldl_1(Low + Z, High + Z, Low, S, E1, D, Function, Acc);
true ->
- A
+ Acc
end;
-foldl(_, _, _) ->
+foldl(_, _, _, _, _) ->
erlang:error(badarg).
-foldl_1(N, E, S, A, Ix, F, D) when is_integer(E) ->
- foldl_1(N, unfold(E, D), S, A, Ix, F, D);
-foldl_1(N, E, 0, A, Ix, F, _D) ->
- foldl_3(1, E, A, Ix, F, N+1);
-foldl_1(N, E, S, A, Ix, F, D) ->
- foldl_2(1, E, S, A, Ix, F, D, (N bsr S) + 1, N band ?MASK(S)).
-
-foldl_2(I, E, S, A, Ix, F, D, I, R) ->
- foldl_1(R, element(I, E), ?reduce(S), A, Ix, F, D);
-foldl_2(I, E, S, A, Ix, F, D, N, R) ->
- Sz = ?SIZE(S),
- Acc = foldl_1(Sz-1, element(I, E), ?reduce(S), A, Ix, F, D),
- foldl_2(I+1, E, S, Acc, Ix + Sz, F, D, N, R).
-
--spec foldl_3(pos_integer(), _, A, array_indx(),
- fun((array_indx(), _, A) -> B), integer()) -> B.
-
-foldl_3(I, E, A, Ix, F, N) when I =< N ->
- foldl_3(I+1, E, F(Ix, element(I, E), A), Ix+1, F, N);
-foldl_3(_I, _E, A, _Ix, _F, _N) ->
+%% -spec prop_foldl1() -> term().
+%% prop_foldl1() ->
+%% Fun = fun(I,_X,A)-> [I|A] end,
+%% ?FORALL(Low, proper_types:non_neg_integer(),
+%% ?FORALL(Delta, proper_types:non_neg_integer(),
+%% begin
+%% Arr = array:from_list(lists:seq(0,Low+Delta)),
+%% #array{elements = E, default = D, bits = S} = Arr,
+%% lists:reverse(foldl_1(Low, Low+Delta, Low, S, E, D, Fun, [])) =:= lists:seq(Low, Low+Delta)
+%% end)
+%% ).
+
+foldl_1(Low, High, Ix, S, ?EMPTY, D, F, A) ->
+ foldl_4(Low, High, Ix, S, D, F, A);
+foldl_1(Low, High, Ix, 0, E, _D, F, A) ->
+ foldl_3(Low, High, Ix, E, F, A);
+foldl_1(Low, High, Ix, S, E, D, F, A) ->
+ LDiv = Low bsr S,
+ HDiv = High bsr S,
+ LRem = Low band ?MASK(S),
+ HRem = High band ?MASK(S),
+ if LDiv =:= HDiv ->
+ foldl_1(LRem, HRem, Ix, ?reduce(S), element(LDiv+1, E), D, F, A);
+ true ->
+ A1 = foldl_1(LRem, ?SIZE(S)-1, Ix, ?reduce(S), element(LDiv+1, E), D, F, A),
+ foldl_2(LDiv+1, HDiv, Ix + ?SIZE(S) - LRem, S, E, D, F, A1, HRem)
+ end.
+
+foldl_2(Low, High, Ix, S, E, D, F, A, HRem) when Low < High ->
+ A1 = foldl_1(0, ?SIZE(S)-1, Ix, ?reduce(S), element(Low+1, E), D, F, A),
+ foldl_2(Low+1, High, Ix + ?SIZE(S), S, E, D, F, A1, HRem);
+foldl_2(Low, _High, Ix, S, E, D, F, A, HRem) ->
+ foldl_1(0, HRem, Ix, ?reduce(S), element(Low+1, E), D, F, A).
+
+
+-spec foldl_3(array_indx(), array_indx(), array_indx(), tuple(),
+ fun((array_indx(), _, A) -> A), A) -> A.
+
+foldl_3(Low, High, Ix, E, F, A) when Low =< High ->
+ foldl_3(Low+1, High, Ix+1, E, F, F(Ix, element(Low+1, E), A));
+foldl_3(_Low, _High, _Ix, _E, _F, A) ->
A.
+%% unexpanded tree
+foldl_4(Low, High, Ix, 0, D, F, A) ->
+ foldl_6(Low, High, Ix, D, F, A);
+foldl_4(Low, High, Ix, S, D, F, A) ->
+ LDiv = Low bsr S,
+ HDiv = High bsr S,
+ LRem = Low band ?MASK(S),
+ HRem = High band ?MASK(S),
+ if LDiv =:= HDiv ->
+ foldl_4(LRem, HRem, Ix, ?reduce(S), D, F, A);
+ true ->
+ A1 = foldl_4(LRem, ?SIZE(S)-1, Ix, ?reduce(S), D, F, A),
+ foldl_5(LDiv+1, HDiv, Ix + ?SIZE(S) - LRem, S, D, F, A1, HRem)
+ end.
+
+foldl_5(Low, High, Ix, S, D, F, A, HRem) when Low < High ->
+ A1 = foldl_4(0, ?SIZE(S)-1, Ix, ?reduce(S), D, F, A),
+ foldl_5(Low+1, High, Ix + ?SIZE(S), S, D, F, A1, HRem);
+foldl_5(_Low, _High, Ix, S, D, F, A, HRem) ->
+ foldl_4(0, HRem, Ix, ?reduce(S), D, F, A).
+
+foldl_6(Low, High, Ix, D, F, A) when Low =< High ->
+ foldl_6(Low+1, High, Ix+1, D, F, F(Ix, D, A));
+foldl_6(_Low, _High, _Ix, _D, _F, A) ->
+ A.
+
+%% -spec prop_foldl4() -> term().
+%% prop_foldl4() ->
+%% Fun = fun(I,_X,A)-> [I|A] end,
+%% ?FORALL(Low, proper_types:non_neg_integer(),
+%% ?FORALL(Delta, proper_types:non_neg_integer(),
+%% begin
+%% Arr = array:new(Low+Delta+1),
+%% #array{elements = E, default = D, bits = S} = Arr,
+%% S = E-4,
+%% lists:reverse(foldl_4(Low, Low+Delta, Low, S, D, Fun, [])) =:= lists:seq(Low, Low+Delta)
+%% end)
+%% ).
+
-doc """
Folds the array elements using the specified function and initial accumulator
@@ -1382,48 +1403,40 @@ the lowest index to the highest.
If `Function` is not a function, the call fails with reason `badarg`.
-See also `foldl/3`, `sparse_foldr/3`.
+See also `sparse_foldl/5`, `foldl/3`.
""".
--spec sparse_foldl(Function, InitialAcc :: A, Array :: array(Type)) -> B when
- Function :: fun((Index :: array_indx(), Value :: Type, Acc :: A) -> B).
-
-sparse_foldl(Function, A, #array{size = N, cache = C, cache_index = CI,
- elements = E, default = D, bits = M})
- when is_function(Function, 3), is_integer(N), is_integer(CI), is_integer(M) ->
- if N > 0 ->
- E1 = set_leaf(CI, M, E, C),
- sparse_foldl_1(N-1, E1, M, A, 0, Function, D);
- true ->
- A
- end;
+-spec sparse_foldl(Function, InitialAcc :: A, Array :: array(Type)) -> A when
+ Function :: fun((Index :: array_indx(), Value :: Type, Acc :: A) -> A).
+
+sparse_foldl(Function, Acc, #array{size = N}=Array) when is_integer(N) ->
+ %% eqwalizer:ignore ambiguous_union
+ sparse_foldl(0, N-1, Function, Acc, Array);
sparse_foldl(_, _, _) ->
erlang:error(badarg).
-%% see foldl/3 for details
-%% TODO: this can be optimized
-sparse_foldl_1(_N, E, _S, A, _Ix, _F, _D) when is_integer(E) ->
- A;
-sparse_foldl_1(N, E, 0, A, Ix, F, D) ->
- sparse_foldl_3(1, E, A, Ix, F, D, N+1);
-sparse_foldl_1(N, E, S, A, Ix, F, D) ->
- sparse_foldl_2(1, E, S, A, Ix, F, D, (N bsr S) + 1, N band ?MASK(S)).
-
-sparse_foldl_2(I, E, S, A, Ix, F, D, I, R) ->
- sparse_foldl_1(R, element(I, E), ?reduce(S), A, Ix, F, D);
-sparse_foldl_2(I, E, S, A, Ix, F, D, N, R) ->
- Sz = ?SIZE(S),
- sparse_foldl_2(I+1, E, S,
- sparse_foldl_1(Sz-1, element(I, E), ?reduce(S), A, Ix, F, D),
- Ix + Sz, F, D, N, R).
-
-sparse_foldl_3(I, T, A, Ix, F, D, N) when I =< N ->
- case element(I, T) of
- D -> sparse_foldl_3(I+1, T, A, Ix+1, F, D, N);
- E -> sparse_foldl_3(I+1, T, F(Ix, E, A), Ix+1, F, D, N)
- end;
-sparse_foldl_3(_I, _T, A, _Ix, _F, _D, _N) ->
- A.
+-doc """
+Folds the array elements from `Low` to `High` using the specified
+function and initial accumulator value, skipping default-valued entries.
+The elements are visited in order from the lowest index to the highest.
+
+If `Function` is not a function, the call fails with reason `badarg`.
+
+See also `sparse_foldl/3`, `sparse_foldl/5`.
+""".
+-spec sparse_foldl(Low :: array_indx(), High :: array_indx(), Function,
+ InitialAcc :: A, Array :: array(Type)) -> A when
+ Function :: fun((Index :: array_indx(), Value :: Type, Acc :: A) -> A).
+
+sparse_foldl(Low, High, Function, InitialAcc, #array{default = D}=Array)
+ when is_function(Function, 3) ->
+ Skip = fun (_I, V, A) when V =:= D -> A;
+ (I, V, A) -> Function(I, V, A)
+ end,
+ %% eqwalizer:ignore ambiguous_union
+ foldl(Low, High, Skip, InitialAcc, Array);
+sparse_foldl(_, _, _, _, _) ->
+ erlang:error(badarg).
-doc """
@@ -1433,46 +1446,122 @@ the lowest.
If `Function` is not a function, the call fails with reason `badarg`.
-See also `foldl/3`, `map/2`.
+See also `foldr/5`, `foldl/3`, `sparse_foldr/3`.
+""".
+-spec foldr(Function, InitialAcc :: A, Array :: array(Type)) -> A when
+ Function :: fun((Index :: array_indx(), Value :: Type, Acc :: A) -> A).
+
+foldr(Function, Acc, #array{size = N}=Array) when is_integer(N) ->
+ %% eqwalizer:ignore ambiguous_union
+ foldr(0, N-1, Function, Acc, Array);
+foldr(_, _, _) ->
+ erlang:error(badarg).
+
+
+-doc """
+Folds the array elements from `High` to `Low` using the specified function and
+initial accumulator value. The elements are visited in order from the highest
+index to the lowest.
+
+If `Function` is not a function, the call fails with reason `badarg`.
+
+See also `foldr/3`, `sparse_foldr/5`.
""".
--spec foldr(Function, InitialAcc :: A, Array :: array(Type)) -> B when
- Function :: fun((Index :: array_indx(), Value :: Type, Acc :: A) -> B).
-
-foldr(Function, A, #array{size = N, cache = C, cache_index = CI, elements = E, default = D, bits = M})
- when is_function(Function, 3), is_integer(N), is_integer(CI), is_integer(M) ->
- if N > 0 ->
- I = N - 1,
- E1 = set_leaf(CI, M, E, C),
- foldr_1(I, E1, M, I, A, Function, D);
+-spec foldr(Low, High, Function, InitialAcc :: A, Array :: array(Type)) -> A when
+ Low :: array_indx(),
+ High :: array_indx(),
+ Function :: fun((Index :: array_indx(), Value :: Type, Acc :: A) -> A).
+
+foldr(Low, High, Function, Acc, #array{size = N, zero = Z, cache = C,
+ cache_index = CI, elements = E, default = D, bits = S})
+ when is_integer(Low), Low >= 0, is_integer(High), is_function(Function, 3),
+ is_integer(N), High < N, is_integer(Z), is_integer(CI), is_integer(S) ->
+ if Low =< High ->
+ E1 = set_leaf(CI, S, E, C),
+ foldr_1(Low + Z, High + Z, High, S, E1, D, Function, Acc);
true ->
- A
+ Acc
end;
-foldr(_, _, _) ->
+foldr(_, _, _, _, _) ->
erlang:error(badarg).
-%% this is based on to_orddict/1
-foldr_1(I, E, S, Ix, A, F, D) when is_integer(E) ->
- foldr_1(I, unfold(E, D), S, Ix, A, F, D);
-foldr_1(I, E, 0, Ix, A, F, _D) ->
- I1 = I+1,
- foldr_3(I1, E, Ix-I1, A, F);
-foldr_1(I, E, S, Ix, A, F, D) ->
- foldr_2((I bsr S) + 1, E, S, Ix, A, F, D, I band ?MASK(S), ?SIZE(S)-1).
+%% -spec prop_foldr1() -> term().
+%% prop_foldr1() ->
+%% Fun = fun(I,_X,A)-> [I|A] end,
+%% ?FORALL(Low, proper_types:non_neg_integer(),
+%% ?FORALL(Delta, proper_types:non_neg_integer(),
+%% begin
+%% Arr = array:from_list(lists:seq(0,Low+Delta)),
+%% #array{elements = E, default = D, bits = S} = Arr,
+%% foldr_1(Low, Low+Delta, Low+Delta, S, E, D, Fun, []) =:= lists:seq(Low, Low+Delta)
+%% end)
+%% ).
+
+foldr_1(Low, High, Ix, S, ?EMPTY, D, F, A) ->
+ foldr_4(Low, High, Ix, S, D, F, A);
+foldr_1(Low, High, Ix, 0, E, _D, F, A) ->
+ foldr_3(Low, High, Ix, E, F, A);
+foldr_1(Low, High, Ix, S, E, D, F, A) ->
+ LDiv = Low bsr S,
+ HDiv = High bsr S,
+ LRem = Low band ?MASK(S),
+ HRem = High band ?MASK(S),
+ if LDiv =:= HDiv ->
+ foldr_1(LRem, HRem, Ix, ?reduce(S), element(HDiv+1, E), D, F, A);
+ true ->
+ A1 = foldr_1(0, HRem, Ix, ?reduce(S), element(HDiv+1, E), D, F, A),
+ foldr_2(LDiv, HDiv-1, Ix - HRem - 1, S, E, D, F, A1, LRem)
+ end.
-foldr_2(0, _E, _S, _Ix, A, _F, _D, _R, _R0) ->
- A;
-foldr_2(I, E, S, Ix, A, F, D, R, R0) ->
- foldr_2(I-1, E, S, Ix - R - 1,
- foldr_1(R, element(I, E), ?reduce(S), Ix, A, F, D),
- F, D, R0, R0).
+foldr_2(Low, High, Ix, S, E, D, F, A, LRem) when Low < High ->
+ A1 = foldr_1(0, ?SIZE(S)-1, Ix, ?reduce(S), element(High+1, E), D, F, A),
+ foldr_2(Low, High-1, Ix - ?SIZE(S), S, E, D, F, A1, LRem);
+foldr_2(_Low, High, Ix, S, E, D, F, A, LRem) ->
+ foldr_1(LRem, ?SIZE(S)-1, Ix, ?reduce(S), element(High+1, E), D, F, A).
+
+foldr_3(Low, High, Ix, E, F, A) when Low =< High ->
+ foldr_3(Low, High-1, Ix-1, E, F, F(Ix, element(High+1, E), A));
+foldr_3(_Low, _High, _Ix, _D, _F, A) ->
+ A.
--spec foldr_3(array_indx(), term(), integer(), A,
- fun((array_indx(), _, A) -> B)) -> B.
+%% unexpanded tree
+foldr_4(Low, High, Ix, 0, D, F, A) ->
+ foldr_6(Low, High, Ix, D, F, A);
+foldr_4(Low, High, Ix, S, D, F, A) ->
+ LDiv = Low bsr S,
+ HDiv = High bsr S,
+ LRem = Low band ?MASK(S),
+ HRem = High band ?MASK(S),
+ if LDiv =:= HDiv ->
+ foldr_4(LRem, HRem, Ix, ?reduce(S), D, F, A);
+ true ->
+ A1 = foldr_4(0, HRem, Ix, ?reduce(S), D, F, A),
+ foldr_5(LDiv, HDiv-1, Ix - HRem - 1, S, D, F, A1, LRem)
+ end.
-foldr_3(0, _E, _Ix, A, _F) ->
- A;
-foldr_3(I, E, Ix, A, F) ->
- foldr_3(I-1, E, Ix, F(Ix+I, element(I, E), A), F).
+foldr_5(Low, High, Ix, S, D, F, A, LRem) when Low < High ->
+ A1 = foldr_4(0, ?SIZE(S)-1, Ix, ?reduce(S), D, F, A),
+ foldr_5(Low, High-1, Ix - ?SIZE(S), S, D, F, A1, LRem);
+foldr_5(_Low, _High, Ix, S, D, F, A, LRem) ->
+ foldr_4(LRem, ?SIZE(S)-1, Ix, ?reduce(S), D, F, A).
+
+foldr_6(Low, High, Ix, D, F, A) when Low =< High ->
+ foldr_6(Low, High-1, Ix-1, D, F, F(Ix, D, A));
+foldr_6(_Low, _High, _Ix, _D, _F, A) ->
+ A.
+
+%% -spec prop_foldr4() -> term().
+%% prop_foldr4() ->
+%% Fun = fun(I,_X,A)-> [I|A] end,
+%% ?FORALL(Low, proper_types:non_neg_integer(),
+%% ?FORALL(Delta, proper_types:non_neg_integer(),
+%% begin
+%% Arr = array:new(Low+Delta+1),
+%% #array{elements = E, default = D, bits = S} = Arr,
+%% S = E-4,
+%% foldr_4(Low, Low+Delta, Low+Delta, S, D, Fun, []) =:= lists:seq(Low, Low+Delta)
+%% end)
+%% ).
-doc """
@@ -1482,51 +1571,40 @@ order from the highest index to the lowest.
If `Function` is not a function, the call fails with reason `badarg`.
-See also `foldr/3`, `sparse_foldl/3`.
+See also `sparse_foldr/5`, `foldr/3`, `sparse_foldl/3`.
""".
--spec sparse_foldr(Function, InitialAcc :: A, Array :: array(Type)) -> B when
- Function :: fun((Index :: array_indx(), Value :: Type, Acc :: A) -> B).
-
-sparse_foldr(Function, A, #array{size = N, cache = C, cache_index = CI, elements = E, default = D, bits = M})
- when is_function(Function, 3), is_integer(N), is_integer(CI), is_integer(M) ->
- if N > 0 ->
- I = N - 1,
- E1 = set_leaf(CI, M, E, C),
- sparse_foldr_1(I, E1, M, I, A, Function, D);
- true ->
- A
- end;
+-spec sparse_foldr(Function, InitialAcc :: A, Array :: array(Type)) -> A when
+ Function :: fun((Index :: array_indx(), Value :: Type, Acc :: A) -> A).
+
+sparse_foldr(Function, Acc, #array{size = N}=Array) when is_integer(N) ->
+ %% eqwalizer:ignore ambiguous_union
+ sparse_foldr(0, N-1, Function, Acc, Array);
sparse_foldr(_, _, _) ->
erlang:error(badarg).
-%% see foldr/3 for details
-%% TODO: this can be optimized
-
-sparse_foldr_1(_I, E, _S, _Ix, A, _F, _D) when is_integer(E) ->
- A;
-sparse_foldr_1(I, E, 0, Ix, A, F, D) ->
- I1 = I+1,
- sparse_foldr_3(I1, E, Ix-I1, A, F, D);
-sparse_foldr_1(I, E, S, Ix, A, F, D) ->
- sparse_foldr_2((I bsr S) + 1, E, S, Ix, A, F, D, I band ?MASK(S), ?SIZE(S)-1).
-sparse_foldr_2(0, _E, _S, _Ix, A, _F, _D, _R, _R0) ->
- A;
-sparse_foldr_2(I, E, S, Ix, A, F, D, R, R0) ->
- sparse_foldr_2(I-1, E, S, Ix - R - 1,
- sparse_foldr_1(R, element(I, E), ?reduce(S), Ix, A, F, D),
- F, D, R0, R0).
+-doc """
+Folds the array elements from `High` to `Low` using the specified
+function and initial accumulator value, skipping default-valued entries.
+The elements are visited in order from the highest index to the lowest.
--spec sparse_foldr_3(array_indx(), _, array_indx(), A,
- fun((array_indx(), _, A) -> B), _) -> B.
+If `Function` is not a function, the call fails with reason `badarg`.
-sparse_foldr_3(0, _T, _Ix, A, _F, _D) ->
- A;
-sparse_foldr_3(I, T, Ix, A, F, D) ->
- case element(I, T) of
- D -> sparse_foldr_3(I-1, T, Ix, A, F, D);
- E -> sparse_foldr_3(I-1, T, Ix, F(Ix+I, E, A), F, D)
- end.
+See also `sparse_foldr/3`, `sparse_foldl/5`.
+""".
+-spec sparse_foldr(Low :: array_indx(), High :: array_indx(), Function,
+ InitialAcc :: A, Array :: array(Type)) -> A when
+ Function :: fun((Index :: array_indx(), Value :: Type, Acc :: A) -> A).
+
+sparse_foldr(Low, High, Function, InitialAcc, #array{default = D}=Array)
+ when is_function(Function, 3) ->
+ Skip = fun (_I, V, A) when V =:= D -> A;
+ (I, V, A) -> Function(I, V, A)
+ end,
+ %% eqwalizer:ignore ambiguous_union
+ foldr(Low, High, Skip, InitialAcc, Array);
+sparse_foldr(_, _, _, _, _) ->
+ erlang:error(badarg).
-doc """
@@ -1546,3 +1624,421 @@ sparse_size(A) ->
{value, I} when is_integer(I) ->
I + 1
end.
+
+
+-doc """
+Combined map and fold over the array elements using the specified
+function and initial accumulator value. The elements are visited in
+order from the lowest index to the highest.
+
+If `Function` is not a function, the call fails with reason `badarg`.
+
+See also `mapfoldl/5`, `foldl/3`, `map/2`, `sparse_mapfoldl/3`.
+""".
+-spec mapfoldl(Function, InitialAcc :: A, Array :: array(Type)) -> {array(Type), A} when
+ Function :: fun((Index :: array_indx(), Value :: Type, Acc :: A) -> {Type, A}).
+
+mapfoldl(Function, Acc, #array{size = N}=Array) when is_integer(N) ->
+ mapfoldl(0, N-1, Function, Acc, Array);
+mapfoldl(_, _, _) ->
+ erlang:error(badarg).
+
+-doc """
+Combined map and fold over the array elements from `Low` to `High` using
+the specified function and initial accumulator value. The elements are
+visited in order from the lowest index to the highest.
+
+If `Function` is not a function, the call fails with reason `badarg`.
+
+See also `mapfoldl/3`, `sparse_mapfoldl/5`.
+""".
+-spec mapfoldl(Low, High, Function, InitialAcc :: A, Array :: array(Type)) -> {array(Type), A} when
+ Low :: array_indx(),
+ High :: array_indx(),
+ Function :: fun((Index :: array_indx(), Value :: Type, Acc :: A) -> {Type, A}).
+
+mapfoldl(Low, High, Function, Acc, #array{size = N, zero = Z, cache = C, cache_index = CI,
+ elements = E, default = D, bits = S}=Array)
+ when is_integer(Low), Low >= 0, is_integer(High), is_function(Function, 3),
+ is_integer(N), High < N, is_integer(Z), is_integer(CI), is_integer(S) ->
+ if Low =< High ->
+ E0 = set_leaf(CI, S, E, C),
+ {E1, Acc1} = mapfoldl_1(Low + Z, High + Z, Low, S, E0, D, Function, Acc),
+ C1 = get_leaf(CI, S, E1, D),
+ {Array#array{elements = E1, cache = C1}, Acc1};
+ true ->
+ {Array, Acc}
+ end;
+mapfoldl(_, _, _, _, _) ->
+ erlang:error(badarg).
+
+mapfoldl_1(Low, High, Ix, S, ?EMPTY, D, F, A) ->
+ mapfoldl_1(Low, High, Ix, S, unfold(S, D), D, F, A);
+mapfoldl_1(Low, High, Ix, 0, E, _D, F, A) ->
+ mapfoldl_3(Low, High, Ix, tuple_to_list(E), F, A, [], 0);
+mapfoldl_1(Low, High, Ix, S, E, D, F, A) ->
+ LDiv = Low bsr S,
+ HDiv = High bsr S,
+ LRem = Low band ?MASK(S),
+ HRem = High band ?MASK(S),
+ if LDiv =:= HDiv ->
+ {E1, A1} = mapfoldl_1(LRem, HRem, Ix, ?reduce(S), element(LDiv+1, E), D, F, A),
+ {setelement(LDiv+1, E, E1), A1};
+ true ->
+ Es = tuple_to_list(E),
+ {Es1, A1} = mapfoldl_2(LDiv, HDiv, Ix, S, Es, D, F, A, HRem, [], LRem, 0),
+ {list_to_tuple(Es1), A1}
+ end.
+
+mapfoldl_2(Low, High, Ix, S, [E|Es], D, F, A, HRem, Es1, LRem, I) when I < Low ->
+ mapfoldl_2(Low, High, Ix, S, Es, D, F, A, HRem, [E|Es1], LRem, I + 1);
+mapfoldl_2(Low, High, Ix, S, [E|Es], D, F, A, HRem, Es1, LRem, _I) ->
+ {E1, A1} = mapfoldl_1(LRem, ?SIZE(S)-1, Ix, ?reduce(S), E, D, F, A),
+ mapfoldl_2_1(Low+1, High, Ix + ?SIZE(S) - LRem, S, Es, D, F, A1, HRem, [E1|Es1]).
+
+mapfoldl_2_1(Low, High, Ix, S, [E|Es], D, F, A, HRem, Es1) when Low < High ->
+ {E1, A1} = mapfoldl_1(0, ?SIZE(S)-1, Ix, ?reduce(S), E, D, F, A),
+ mapfoldl_2_1(Low+1, High, Ix + ?SIZE(S), S, Es, D, F, A1, HRem, [E1|Es1]);
+mapfoldl_2_1(_Low, _High, Ix, S, [E|Es], D, F, A, HRem, Es1) ->
+ {E1, A1} = mapfoldl_1(0, HRem, Ix, ?reduce(S), E, D, F, A),
+ {lists:reverse(lists:reverse(Es, [E1|Es1])), A1}.
+
+mapfoldl_3(Low, High, Ix, [E|Es], F, A, Es1, I) when I < Low ->
+ mapfoldl_3(Low, High, Ix, Es, F, A, [E|Es1], I + 1);
+mapfoldl_3(Low, High, Ix, Es, F, A, Es1, _I) ->
+ mapfoldl_3_1(Low, High, Ix, Es, F, A, Es1).
+
+mapfoldl_3_1(Low, High, Ix, [E|Es], F, A, Es1) when Low =< High ->
+ {E1, A1} = F(Ix, E, A),
+ mapfoldl_3_1(Low+1, High, Ix+1, Es, F, A1, [E1|Es1]);
+mapfoldl_3_1(_Low, _High, _Ix, Es, _F, A, Es1) ->
+ {list_to_tuple(lists:reverse(lists:reverse(Es, Es1))), A}.
+
+unfold(S, _D) when S > 0 ->
+ ?NEW_NODE(S);
+unfold(_S, D) ->
+ ?NEW_LEAF(D).
+
+%% -spec prop_mapfoldl1() -> term().
+%% prop_mapfoldl1() ->
+%% Fun = fun(I,X,A)-> {X+10000, [I|A]} end,
+%% ?FORALL(From, proper_types:non_neg_integer(),
+%% ?FORALL(Length, proper_types:non_neg_integer(),
+%% ?FORALL(Tail, proper_types:non_neg_integer(),
+%% begin
+%% Max = From+Length-1+Tail,
+%% Arr = array:from_list(lists:seq(0,Max)),
+%% {Arr1, L} = mapfoldl(From, From+Length-1, Fun, [], Arr),
+%% lists:reverse(L) =:= lists:seq(From, From+Length-1) andalso to_list(Arr1) =:= lists:seq(0, From-1) ++ lists:seq(10000+From, 10000+From+Length-1) ++ lists:seq(From+Length, Max)
+%% end))
+%% ).
+
+%% -spec prop_mapfoldl4() -> term().
+%% prop_mapfoldl4() ->
+%% Fun = fun(I,_,A)-> {I+10000, [I|A]} end,
+%% ?FORALL(From, proper_types:non_neg_integer(),
+%% ?FORALL(Length, proper_types:non_neg_integer(),
+%% ?FORALL(Tail, proper_types:non_neg_integer(),
+%% begin
+%% Max = From+Length-1+Tail,
+%% Arr = array:new(Max+1),
+%% {Arr1, L} = mapfoldl(From, From+Length-1, Fun, [], Arr),
+%% lists:reverse(L) =:= lists:seq(From, From+Length-1) andalso to_list(Arr1) =:= lists:duplicate(From, undefined) ++ lists:seq(10000+From, 10000+From+Length-1) ++ lists:duplicate(Tail, undefined)
+%% end))
+%% ).
+
+
+-doc """
+Like `mapfoldl/3` but skips default-valued entries.
+
+See also `sparse_mapfoldl/5`, `sparse_mapfoldr/3`.
+""".
+-spec sparse_mapfoldl(Function, InitialAcc :: A, Array) -> {ArrayRes, A} when
+ Array :: array(Type1),
+ Function :: fun((Index :: array_indx(), Value :: Type1, Acc :: A) -> {Type2, A}),
+ ArrayRes :: array(Type1 | Type2).
+
+sparse_mapfoldl(Function, Acc, #array{size = N}=Array) when is_integer(N) ->
+ %% eqwalizer:ignore ambiguous_union
+ sparse_mapfoldl(0, N-1, Function, Acc, Array);
+sparse_mapfoldl(_, _, _) ->
+ erlang:error(badarg).
+
+-doc """
+Like `mapfoldl/5` but skips default-valued entries.
+
+See also `sparse_mapfoldl/3`, `sparse_mapfoldr/5`.
+""".
+-spec sparse_mapfoldl(Low, High, Function, InitialAcc :: A, Array) -> {ArrayRes, A} when
+ Low :: array_indx(),
+ High :: array_indx(),
+ Function :: fun((Index :: array_indx(), Value :: Type1, Acc :: A) -> {Type2, A}),
+ Array :: array(Type1),
+ ArrayRes :: array(Type1 | Type2).
+
+sparse_mapfoldl(Low, High, Function, Acc, #array{size = N, zero = Z, cache = C, cache_index = CI,
+ elements = E, default = D, bits = S}=Array)
+ when is_integer(Low), Low >= 0, is_integer(High), is_function(Function, 3), is_integer(N),
+ High < N, is_integer(Z), is_integer(CI), is_integer(S) ->
+ if Low =< High ->
+ E0 = set_leaf(CI, S, E, C),
+ {E1, Acc1} = sparse_mapfoldl_1(Low + Z, High + Z, Low, S, E0, D, Function, Acc),
+ C1 = get_leaf(CI, S, E1, D),
+ {Array#array{elements = E1, cache = C1}, Acc1};
+ true ->
+ {Array, Acc}
+ end;
+sparse_mapfoldl(_, _, _, _, _) ->
+ erlang:error(badarg).
+
+sparse_mapfoldl_1(_Low, _High, _Ix, _S, ?EMPTY, _D, _F, A) ->
+ {?EMPTY, A};
+sparse_mapfoldl_1(Low, High, Ix, 0, E, D, F, A) ->
+ sparse_mapfoldl_3(Low, High, Ix, tuple_to_list(E), D, F, A, [], 0);
+sparse_mapfoldl_1(Low, High, Ix, S, E, D, F, A) ->
+ LDiv = Low bsr S,
+ HDiv = High bsr S,
+ LRem = Low band ?MASK(S),
+ HRem = High band ?MASK(S),
+ if LDiv =:= HDiv ->
+ {E1, A1} = sparse_mapfoldl_1(LRem, HRem, Ix, ?reduce(S), element(LDiv+1, E), D, F, A),
+ {setelement(LDiv+1, E, E1), A1};
+ true ->
+ Es = tuple_to_list(E),
+ {Es1, A1} = sparse_mapfoldl_2(LDiv, HDiv, Ix, S, Es, D, F, A, HRem, [], LRem, 0),
+ {list_to_tuple(Es1), A1}
+ end.
+
+sparse_mapfoldl_2(Low, High, Ix, S, [E|Es], D, F, A, HRem, Es1, LRem, I) when I < Low ->
+ sparse_mapfoldl_2(Low, High, Ix, S, Es, D, F, A, HRem, [E|Es1], LRem, I + 1);
+sparse_mapfoldl_2(Low, High, Ix, S, [E|Es], D, F, A, HRem, Es1, LRem, _I) ->
+ {E1, A1} = sparse_mapfoldl_1(LRem, ?SIZE(S)-1, Ix, ?reduce(S), E, D, F, A),
+ sparse_mapfoldl_2_1(Low+1, High, Ix + ?SIZE(S) - LRem, S, Es, D, F, A1, HRem, [E1|Es1]).
+
+sparse_mapfoldl_2_1(Low, High, Ix, S, [E|Es], D, F, A, HRem, Es1) when Low < High ->
+ {E1, A1} = sparse_mapfoldl_1(0, ?SIZE(S)-1, Ix, ?reduce(S), E, D, F, A),
+ sparse_mapfoldl_2_1(Low+1, High, Ix + ?SIZE(S), S, Es, D, F, A1, HRem, [E1|Es1]);
+sparse_mapfoldl_2_1(_Low, _High, Ix, S, [E|Es], D, F, A, HRem, Es1) ->
+ {E1, A1} = sparse_mapfoldl_1(0, HRem, Ix, ?reduce(S), E, D, F, A),
+ {lists:reverse(lists:reverse(Es, [E1|Es1])), A1}.
+
+sparse_mapfoldl_3(Low, High, Ix, [E|Es], D, F, A, Es1, I) when I < Low ->
+ sparse_mapfoldl_3(Low, High, Ix, Es, D, F, A, [E|Es1], I + 1);
+sparse_mapfoldl_3(Low, High, Ix, Es, D, F, A, Es1, _I) ->
+ sparse_mapfoldl_3_1(Low, High, Ix, Es, D, F, A, Es1).
+
+sparse_mapfoldl_3_1(Low, High, Ix, [E|Es], D, F, A, Es1) when Low =< High ->
+ if E =:= D ->
+ sparse_mapfoldl_3_1(Low+1, High, Ix+1, Es, D, F, A, [E|Es1]);
+ true ->
+ {E1, A1} = F(Ix, E, A),
+ sparse_mapfoldl_3_1(Low+1, High, Ix+1, Es, D, F, A1, [E1|Es1])
+ end;
+sparse_mapfoldl_3_1(_Low, _High, _Ix, Es, _D, _F, A, Es1) ->
+ {list_to_tuple(lists:reverse(lists:reverse(Es, Es1))), A}.
+
+
+-doc """
+Combined map and fold over the array elements using the specified
+function and initial accumulator value. The elements are visited in
+order from the highest index to the lowest.
+
+If `Function` is not a function, the call fails with reason `badarg`.
+
+See also `mapfoldr/5`, `foldr/3`, `map/2`, `sparse_mapfoldr/3`.
+""".
+-spec mapfoldr(Function, InitialAcc :: A, Array :: array(Type)) -> {array(Type), A} when
+ Function :: fun((Index :: array_indx(), Value :: Type, Acc :: A) -> {Type, A}).
+
+mapfoldr(Function, Acc, #array{size = N}=Array) when is_integer(N) ->
+ mapfoldr(0, N-1, Function, Acc, Array);
+mapfoldr(_, _, _) ->
+ erlang:error(badarg).
+
+-doc """
+Combined map and fold over the array elements from `Low` to `High` using
+the specified function and initial accumulator value. The elements are
+visited in order from the highest index to the lowest.
+
+If `Function` is not a function, the call fails with reason `badarg`.
+
+See also `mapfoldr/3`, `mapfoldl/5`, `sparse_mapfoldr/5`.
+""".
+-spec mapfoldr(Low, High, Function, InitialAcc :: A, Array :: array(Type)) -> {array(Type), A} when
+ Low :: array_indx(),
+ High :: array_indx(),
+ Function :: fun((Index :: array_indx(), Value :: Type, Acc :: A) -> {Type, A}).
+
+mapfoldr(Low, High, Function, Acc, #array{size = N, zero = Z, cache = C, cache_index = CI,
+ elements = E, default = D, bits = S}=Array)
+ when is_integer(Low), Low >= 0, is_integer(High), is_function(Function, 3), is_integer(N),
+ High < N, is_integer(Z), is_integer(CI), is_integer(S) ->
+ if Low =< High ->
+ E0 = set_leaf(CI, S, E, C),
+ {E1, Acc1} = mapfoldr_1(Low + Z, High + Z, High, S, E0, D, Function, Acc),
+ C1 = get_leaf(CI, S, E1, D),
+ {Array#array{elements = E1, cache = C1}, Acc1};
+ true ->
+ {Array, Acc}
+ end;
+mapfoldr(_, _, _, _, _) ->
+ erlang:error(badarg).
+
+mapfoldr_1(Low, High, Ix, S, ?EMPTY, D, F, A) ->
+ mapfoldr_1(Low, High, Ix, S, unfold(S, D), D, F, A);
+mapfoldr_1(Low, High, Ix, 0, E, _D, F, A) ->
+ mapfoldr_3(Low, High, Ix, lists:reverse(tuple_to_list(E)), F, A, [], ?LEAFSIZE-1);
+mapfoldr_1(Low, High, Ix, S, E, D, F, A) ->
+ LDiv = Low bsr S,
+ HDiv = High bsr S,
+ LRem = Low band ?MASK(S),
+ HRem = High band ?MASK(S),
+ if LDiv =:= HDiv ->
+ {E1, A1} = mapfoldr_1(LRem, HRem, Ix, ?reduce(S), element(HDiv+1, E), D, F, A),
+ {setelement(HDiv+1, E, E1), A1};
+ true ->
+ Es = lists:reverse(tuple_to_list(E)),
+ {Es1, A1} = mapfoldr_2(LDiv, HDiv, Ix, S, Es, D, F, A, LRem, [], HRem, ?NODESIZE-1),
+ {list_to_tuple(Es1), A1}
+ end.
+
+mapfoldr_2(Low, High, Ix, S, [E|Es], D, F, A, LRem, Es1, HRem, I) when High < I ->
+ mapfoldr_2(Low, High, Ix, S, Es, D, F, A, LRem, [E|Es1], HRem, I - 1);
+mapfoldr_2(Low, High, Ix, S, [E|Es], D, F, A, LRem, Es1, HRem, _I) ->
+ {E1, A1} = mapfoldr_1(0, HRem, Ix, ?reduce(S), E, D, F, A),
+ mapfoldr_2_1(Low, High-1, Ix - HRem - 1, S, Es, D, F, A1, LRem, [E1|Es1]).
+
+mapfoldr_2_1(Low, High, Ix, S, [E|Es], D, F, A, LRem, Es1) when Low < High ->
+ {E1, A1} = mapfoldr_1(0, ?SIZE(S)-1, Ix, ?reduce(S), E, D, F, A),
+ mapfoldr_2_1(Low, High-1, Ix - ?SIZE(S), S, Es, D, F, A1, LRem, [E1|Es1]);
+mapfoldr_2_1(_Low, _High, Ix, S, [E|Es], D, F, A, LRem, Es1) ->
+ {E1, A1} = mapfoldr_1(LRem, ?SIZE(S)-1, Ix, ?reduce(S), E, D, F, A),
+ {lists:reverse(Es, [E1|Es1]), A1}.
+
+mapfoldr_3(Low, High, Ix, [E|Es], F, A, Es1, I) when High < I ->
+ mapfoldr_3(Low, High, Ix, Es, F, A, [E|Es1], I - 1);
+mapfoldr_3(Low, High, Ix, Es, F, A, Es1, _I) ->
+ mapfoldr_3_1(Low, High, Ix, Es, F, A, Es1).
+
+mapfoldr_3_1(Low, High, Ix, [E|Es], F, A, Es1) when Low =< High ->
+ {E1, A1} = F(Ix, E, A),
+ mapfoldr_3_1(Low, High-1, Ix-1, Es, F, A1, [E1|Es1]);
+mapfoldr_3_1(_Low, _High, _Ix, Es, _F, A, Es1) ->
+ {list_to_tuple(lists:reverse(Es, Es1)), A}.
+
+%% -spec prop_mapfoldr1() -> term().
+%% prop_mapfoldr1() ->
+%% Fun = fun(I,X,A)-> {X+10000, [I|A]} end,
+%% ?FORALL(From, proper_types:non_neg_integer(),
+%% ?FORALL(Length, proper_types:non_neg_integer(),
+%% ?FORALL(Tail, proper_types:non_neg_integer(),
+%% begin
+%% Max = From+Length-1+Tail,
+%% Arr = array:from_list(lists:seq(0,Max)),
+%% {Arr1, L} = mapfoldr(From, From+Length-1, Fun, [], Arr),
+%% L =:= lists:seq(From, From+Length-1) andalso to_list(Arr1) =:= lists:seq(0, From-1) ++ lists:seq(10000+From, 10000+From+Length-1) ++ lists:seq(From+Length, Max)
+%% end))
+%% ).
+
+%% -spec prop_mapfoldr4() -> term().
+%% prop_mapfoldr4() ->
+%% Fun = fun(I,_,A)-> {I+10000, [I|A]} end,
+%% ?FORALL(From, proper_types:non_neg_integer(),
+%% ?FORALL(Length, proper_types:non_neg_integer(),
+%% ?FORALL(Tail, proper_types:non_neg_integer(),
+%% begin
+%% Max = From+Length-1+Tail,
+%% Arr = array:new(Max+1),
+%% {Arr1, L} = mapfoldr(From, From+Length-1, Fun, [], Arr),
+%% L =:= lists:seq(From, From+Length-1) andalso to_list(Arr1) =:= lists:duplicate(From, undefined) ++ lists:seq(10000+From, 10000+From+Length-1) ++ lists:duplicate(Tail, undefined)
+%% end))
+%% ).
+
+
+-doc """
+Like `mapfoldr/3` but skips default-valued entries.
+
+See also `sparse_mapfoldr/5`, `sparse_mapfoldl/3`
+""".
+-spec sparse_mapfoldr(Function, InitialAcc :: A, Array) -> {ArrayRes, A} when
+ Array :: array(Type1),
+ Function :: fun((Index :: array_indx(), Value :: Type1, Acc :: A) -> {Type2, A}),
+ ArrayRes :: array(Type1 | Type2).
+
+sparse_mapfoldr(Function, Acc, #array{size = N}=Array) when is_integer(N) ->
+ sparse_mapfoldr(0, N-1, Function, Acc, Array);
+sparse_mapfoldr(_, _, _) ->
+ erlang:error(badarg).
+
+-doc """
+Like `mapfoldr/5` but skips default-valued entries.
+
+See also `sparse_mapfoldr/3`, `sparse_mapfoldl/5`
+""".
+-spec sparse_mapfoldr(Low, High, Function, InitialAcc :: A, Array) -> {ArrayRes, A} when
+ Low :: array_indx(),
+ High :: array_indx(),
+ Function :: fun((Index :: array_indx(), Value :: Type1, Acc :: A) -> {Type2, A}),
+ Array :: array(Type1),
+ ArrayRes :: array(Type1 | Type2).
+
+sparse_mapfoldr(Low, High, Function, Acc, #array{size = N, zero = Z, cache = C, cache_index = CI,
+ elements = E, default = D, bits = S}=Array)
+ when is_integer(Low), Low >= 0, is_integer(High), is_function(Function, 3), is_integer(N),
+ High < N, is_integer(Z), is_integer(CI), is_integer(S) ->
+ if Low =< High ->
+ E0 = set_leaf(CI, S, E, C),
+ {E1, Acc1} = sparse_mapfoldr_1(Low + Z, High + Z, High, S, E0, D, Function, Acc),
+ C1 = get_leaf(CI, S, E1, D),
+ {Array#array{elements = E1, cache = C1}, Acc1};
+ true ->
+ {Array, Acc}
+ end;
+sparse_mapfoldr(_, _, _, _, _) ->
+ erlang:error(badarg).
+
+sparse_mapfoldr_1(_Low, _High, _Ix, _S, ?EMPTY, _D, _F, A) ->
+ {?EMPTY, A};
+sparse_mapfoldr_1(Low, High, Ix, 0, E, D, F, A) ->
+ sparse_mapfoldr_3(Low, High, Ix, lists:reverse(tuple_to_list(E)), D, F, A, [], ?LEAFSIZE-1);
+sparse_mapfoldr_1(Low, High, Ix, S, E, D, F, A) ->
+ LDiv = Low bsr S,
+ HDiv = High bsr S,
+ LRem = Low band ?MASK(S),
+ HRem = High band ?MASK(S),
+ if LDiv =:= HDiv ->
+ {E1, A1} = sparse_mapfoldr_1(LRem, HRem, Ix, ?reduce(S), element(HDiv+1, E), D, F, A),
+ {setelement(HDiv+1, E, E1), A1};
+ true ->
+ Es = lists:reverse(tuple_to_list(E)),
+ {Es1, A1} = sparse_mapfoldr_2(LDiv, HDiv, Ix, S, Es, D, F, A, LRem, [], HRem, ?NODESIZE-1),
+ {list_to_tuple(Es1), A1}
+ end.
+
+sparse_mapfoldr_2(Low, High, Ix, S, [E|Es], D, F, A, LRem, Es1, HRem, I) when High < I ->
+ sparse_mapfoldr_2(Low, High, Ix, S, Es, D, F, A, LRem, [E|Es1], HRem, I - 1);
+sparse_mapfoldr_2(Low, High, Ix, S, [E|Es], D, F, A, LRem, Es1, HRem, _I) ->
+ {E1, A1} = sparse_mapfoldr_1(0, HRem, Ix, ?reduce(S), E, D, F, A),
+ sparse_mapfoldr_2_1(Low, High-1, Ix - HRem - 1, S, Es, D, F, A1, LRem, [E1|Es1]).
+
+sparse_mapfoldr_2_1(Low, High, Ix, S, [E|Es], D, F, A, LRem, Es1) when Low < High ->
+ {E1, A1} = sparse_mapfoldr_1(0, ?SIZE(S)-1, Ix, ?reduce(S), E, D, F, A),
+ sparse_mapfoldr_2_1(Low, High-1, Ix - ?SIZE(S), S, Es, D, F, A1, LRem, [E1|Es1]);
+sparse_mapfoldr_2_1(_Low, _High, Ix, S, [E|Es], D, F, A, LRem, Es1) ->
+ {E1, A1} = sparse_mapfoldr_1(LRem, ?SIZE(S)-1, Ix, ?reduce(S), E, D, F, A),
+ {lists:reverse(Es, [E1|Es1]), A1}.
+
+sparse_mapfoldr_3(Low, High, Ix, [E|Es], D, F, A, Es1, I) when High < I ->
+ sparse_mapfoldr_3(Low, High, Ix, Es, D, F, A, [E|Es1], I - 1);
+sparse_mapfoldr_3(Low, High, Ix, Es, D, F, A, Es1, _I) ->
+ sparse_mapfoldr_3_1(Low, High, Ix, Es, D, F, A, Es1).
+
+sparse_mapfoldr_3_1(Low, High, Ix, [E|Es], D, F, A, Es1) when Low =< High ->
+ if E =:= D ->
+ sparse_mapfoldr_3_1(Low, High-1, Ix-1, Es, D, F, A, [E|Es1]);
+ true ->
+ {E1, A1} = F(Ix, E, A),
+ sparse_mapfoldr_3_1(Low, High-1, Ix-1, Es, D, F, A1, [E1|Es1])
+ end;
+sparse_mapfoldr_3_1(_Low, _High, _Ix, Es, _D, _F, A, Es1) ->
+ {list_to_tuple(lists:reverse(Es, Es1)), A}.
diff --git a/lib/stdlib/test/array_SUITE.erl b/lib/stdlib/test/array_SUITE.erl
index ab1fee088e..35f86cc21b 100644
--- a/lib/stdlib/test/array_SUITE.erl
+++ b/lib/stdlib/test/array_SUITE.erl
@@ -40,6 +40,11 @@
sparse_to_list_test/1,
from_list_test/1,
from_test/1,
+ shift_test/1,
+ slice_test/1,
+ prepend_test/1,
+ append_test/1,
+ concat_test/1,
to_orddict_test/1,
sparse_to_orddict_test/1,
from_orddict_test/1,
@@ -57,9 +62,9 @@
-export([t/0,t/1]).
-import(array,
- [new/0, new/1, new/2, is_array/1, set/3, get/2, %size/1,
- sparse_size/1, default/1, reset/2, to_list/1, sparse_to_list/1,
- from/2, from/3,
+ [new/0, new/1, new/2, is_array/1, set/3, get/2, sparse_size/1,
+ default/1, reset/2, to_list/1, sparse_to_list/1, shift/2, slice/3,
+ prepend/2, append/2, concat/2, concat/1, from/2, from/3,
from_list/1, from_list/2, to_orddict/1, sparse_to_orddict/1,
from_orddict/1, from_orddict/2, map/2, sparse_map/2, foldl/3,
foldr/3, sparse_foldl/3, sparse_foldr/3, fix/1, relax/1, is_fix/1,
@@ -75,8 +80,9 @@ suite() ->
all() ->
[new_test, fix_test, relax_test, resize_test,
set_get_test, to_list_test, sparse_to_list_test,
- from_test,
- from_list_test, to_orddict_test, sparse_to_orddict_test,
+ from_list_test, from_test,
+ shift_test, slice_test, prepend_test, append_test,
+ concat_test, to_orddict_test, sparse_to_orddict_test,
from_orddict_test, map_test, sparse_map_test,
foldl_test, sparse_foldl_test, foldr_test, sparse_foldr_test,
import_export, doctests].
@@ -103,16 +109,17 @@ init_per_testcase(_Case, Config) ->
end_per_testcase(_Case, _Config) ->
ok.
--define(LEAFSIZE,8).
+-define(LEAFSIZE,16).
-define(NODESIZE,?LEAFSIZE).
-record(array, {size, %% number of defined entries
+ zero, %% offset of zero point
fix, %% not automatically growing
default, %% the default value (usually 'undefined')
cache, %% cached leaf tuple
cache_index, %% low index of cache
elements, %% the tuple tree
- max
+ bits
}).
-define(test(Expr), begin Expr end).
@@ -252,6 +259,7 @@ relax_test(_Config) ->
resize_test(_Config) ->
?assert(resize(0, new()) =:= new()),
+ ?assert(array:to_list(resize(1, new())) == [undefined]), %% Bug found by prop tests
?assert(resize(99, new(99)) =:= new(99)),
?assert(resize(99, relax(new(99))) =:= relax(new(99))),
?assert(is_fix(resize(100, new(10)))),
@@ -459,7 +467,94 @@ from_test(_Config) ->
?assertError(badarg, from(no_fun, foo))
].
+shift_test(_Config) ->
+ [
+ ?assertEqual(5, array:size(shift(0, fix(array:new(5))))),
+ ?assertEqual(4, array:size(shift(1, fix(array:new(5))))),
+ ?assertEqual(0, array:size(shift(5, fix(array:new(5))))),
+
+ ?assertEqual(1, array:get(0, shift(0, fix(from_list(lists:seq(1,5)))))),
+ ?assertEqual(2, array:get(0, shift(1, fix(from_list(lists:seq(1,5)))))),
+ ?assertEqual(4, array:get(0, shift(3, fix(from_list(lists:seq(1,5)))))),
+ ?assertEqual(0, array:size(shift(5, from_list(lists:seq(1,5))))),
+ ?assertError(badarg, shift(6, from_list(lists:seq(1,5)))),
+ ?assertError(badarg, shift(0, no_array)),
+
+ ?assertEqual([1,2,3,4,5], to_list(shift(0, from_list(lists:seq(1,5))))),
+ ?assertEqual([2,3,4,5], to_list(shift(1, from_list(lists:seq(1,5))))),
+ ?assertEqual([4,5], to_list(shift(3, from_list(lists:seq(1,5))))),
+ ?assertEqual([], to_list(shift(5, from_list(lists:seq(1,5))))),
+
+ ?assertEqual(lists:seq(?LEAFSIZE+2,?LEAFSIZE*?NODESIZE),
+ to_list(shift(?LEAFSIZE+1, from_list(lists:seq(1,?LEAFSIZE*?NODESIZE))))),
+ ?assertEqual(lists:seq(3,3*?NODESIZE+1),
+ to_list(shift(-3, shift(5, from_list(lists:seq(1,3*?NODESIZE+1)))))),
+ ?assertEqual(lists:seq(13,3*?NODESIZE+1),
+ to_list(shift(-5, shift(?LEAFSIZE+1, from_list(lists:seq(1,3*?NODESIZE+1)))))),
+
+ ?assertEqual(6, array:size(shift(-1, fix(array:new(5))))),
+ ?assertEqual(5+?LEAFSIZE, array:size(shift(-?LEAFSIZE, fix(array:new(5))))),
+ ?assertEqual(3*?LEAFSIZE+7, array:size(shift(-7, fix(array:new(3*?LEAFSIZE))))),
+ ?assertEqual([x] ++ lists:duplicate(2+?LEAFSIZE+1,undefined), array:to_list(array:set(0,x,shift(-3, fix(array:new(?LEAFSIZE+1)))))),
+
+ ?assertEqual(undefined, array:get(0, shift(-1, fix(from_list(lists:seq(1,5)))))),
+ ?assertEqual(1, array:get(1, shift(-1, fix(from_list(lists:seq(1,5)))))),
+ ?assertEqual(3, array:get(4, shift(-2, fix(from_list(lists:seq(1,5)))))),
+ ?assertEqual(1, array:get(?LEAFSIZE, shift(-?LEAFSIZE, fix(from_list(lists:seq(1,5)))))),
+
+ ?assertEqual([undefined,1,2,3,4,5], to_list(shift(-1, from_list(lists:seq(1,5))))),
+ ?assertEqual([undefined,undefined,undefined,1,2,3,4,5], to_list(shift(-3, from_list(lists:seq(1,5))))),
+ ?assertEqual(lists:duplicate(?LEAFSIZE+1,undefined) ++ lists:seq(1,5), to_list(shift(-(?LEAFSIZE+1), from_list(lists:seq(1,5)))))
+ ].
+
+slice_test(_Config) ->
+ [
+ ?assertEqual(lists:seq(1,?LEAFSIZE), to_list(slice(0, ?LEAFSIZE, fix(from_list(lists:seq(1,?LEAFSIZE)))))),
+ ?assertEqual(lists:seq(2,?LEAFSIZE), to_list(slice(1, ?LEAFSIZE-1, fix(from_list(lists:seq(1,?LEAFSIZE)))))),
+ ?assertEqual(lists:seq(3,?LEAFSIZE), to_list(slice(2, ?LEAFSIZE-2, fix(from_list(lists:seq(1,?LEAFSIZE)))))),
+ ?assertEqual(lists:seq(1,?LEAFSIZE-1), to_list(slice(0, ?LEAFSIZE-1, fix(from_list(lists:seq(1,?LEAFSIZE)))))),
+ ?assertEqual(lists:seq(1,?LEAFSIZE-2), to_list(slice(0, ?LEAFSIZE-2, fix(from_list(lists:seq(1,?LEAFSIZE)))))),
+ ?assertEqual(lists:seq(4,?LEAFSIZE-3), to_list(slice(3, ?LEAFSIZE-6, fix(from_list(lists:seq(1,?LEAFSIZE)))))),
+ ?assertEqual([4], to_list(slice(3, 1, fix(from_list(lists:seq(1,?LEAFSIZE)))))),
+ ?assertEqual([], to_list(slice(3, 0, fix(from_list(lists:seq(1,?LEAFSIZE)))))),
+ ?assertEqual([], to_list(slice(0, -1, fix(from_list(lists:seq(1,?LEAFSIZE)))))),
+ ?assertError(badarg, to_list(slice(-1, 3, fix(from_list(lists:seq(1,?LEAFSIZE))))))
+ ].
+
+prepend_test(_Config) ->
+ [
+ ?assertEqual(6, array:size(prepend(0, fix(from_list(lists:seq(1,5)))))),
+ ?assertEqual(0, array:get(0, prepend(0, fix(from_list(lists:seq(1,5)))))),
+ ?assertEqual(1, array:get(1, prepend(0, from_list(lists:seq(1,5))))),
+ ?assertEqual([0,1,2,3,4,5], to_list(prepend(0, from_list(lists:seq(1,5))))),
+ ?assertEqual(lists:seq(0,?LEAFSIZE+1), to_list(prepend(0, from_list(lists:seq(1,?LEAFSIZE+1)))))
+ ].
+
+append_test(_Config) ->
+ [
+ ?assertEqual(6, array:size(append(6, fix(from_list(lists:seq(1,5)))))),
+ ?assertEqual(6, array:get(5, append(6, fix(from_list(lists:seq(1,5)))))),
+ ?assertEqual(5, array:get(4, append(6, from_list(lists:seq(1,5))))),
+ ?assertEqual([1,2,3,4,5,6], to_list(append(6, from_list(lists:seq(1,5))))),
+ ?assertEqual(lists:seq(1,?LEAFSIZE+1), to_list(append(?LEAFSIZE+1, from_list(lists:seq(1,?LEAFSIZE)))))
+ ].
+concat_test(_Config) ->
+ ?assertEqual([1,2], to_list(concat(from_list([1]), from_list([2])))),
+ ?assertEqual([1,2,3,4,5,6], to_list(concat(from_list([1,2,3]), from_list([4,5,6])))),
+ ?assertEqual([2,3,4,5,6], to_list(concat(from_list([2,3]), from_list([4,5,6])))),
+ ?assertEqual([1,2,3], to_list(concat(from_list([1,2,3]), from_list([])))),
+ ?assertEqual([1,2,3], to_list(concat(from_list([]), from_list([1,2,3])))),
+ ?assertEqual([], to_list(concat(from_list([]), from_list([])))),
+ ?assertEqual([], to_list(concat(new(), new()))),
+ ?assertError(badarg, concat(from_list([1,2,3]),no_array)),
+ ?assertError(badarg, concat(no_array,from_list([1,2,3]))),
+ ?assertNot(is_fix(concat(from_list([1,2,3]), from_list([4,5,6])))),
+
+ ?assertEqual([2,3,4,5,6], to_list(concat([from_list([2,3]), from_list([4,5,6])]))),
+ ?assertEqual([1,2,3,4,5,6], to_list(concat([from_list([1]), from_list([2,3]), new(), from_list([4,5,6]), new()]))),
+ ?assertError(badarg, concat(no_list)),
+ ?assertError(badarg, concat([])).
to_orddict_test(_Config) ->
N0 = ?LEAFSIZE,
@@ -608,7 +703,7 @@ map_test(_Config) ->
=:= lists:seq(0,10)),
?assert(to_list(map(Plus(11), from_list(lists:seq(0,99999))))
=:= lists:seq(11,100010)),
- ?assert([{0,0},{N0*2+1,N0*2+1+1},{N0*100+1,N0*100+1+2}] =:=
+ ?assertEqual([{0,0},{N0*2+1,N0*2+1+1},{N0*100+1,N0*100+1+2}],
sparse_to_orddict((map(Default,
set(N0*100+1,2,
set(N0*2+1,1,
--
2.51.0