summaryrefslogtreecommitdiff
path: root/lib/stdlib/test/property_test/lists_prop.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/stdlib/test/property_test/lists_prop.erl')
-rw-r--r--lib/stdlib/test/property_test/lists_prop.erl2146
1 files changed, 2146 insertions, 0 deletions
diff --git a/lib/stdlib/test/property_test/lists_prop.erl b/lib/stdlib/test/property_test/lists_prop.erl
new file mode 100644
index 0000000000..68c087b76d
--- /dev/null
+++ b/lib/stdlib/test/property_test/lists_prop.erl
@@ -0,0 +1,2146 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2021-2023. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(lists_prop).
+
+-compile([export_all, nowarn_export_all]).
+
+-proptest(eqc).
+-proptest([triq, proper]).
+
+-ifndef(EQC).
+-ifndef(PROPER).
+-ifndef(TRIQ).
+-define(EQC, true).
+-endif.
+-endif.
+-endif.
+
+-ifdef(EQC).
+-include_lib("eqc/include/eqc.hrl").
+-define(MOD_eqc,eqc).
+
+-else.
+-ifdef(PROPER).
+-include_lib("proper/include/proper.hrl").
+-define(MOD_eqc,proper).
+
+-else.
+-ifdef(TRIQ).
+-define(MOD_eqc,triq).
+-include_lib("triq/include/triq.hrl").
+
+-endif.
+-endif.
+-endif.
+
+-define(RANDOM_ATOMS, 1000).
+
+%%%%%%%%%%%%%%%%%%
+%%% Properties %%%
+%%%%%%%%%%%%%%%%%%
+
+%% all/2
+prop_all_true() ->
+ ?FORALL(
+ InList,
+ gen_list(),
+ lists:all(fun(_) -> true end, InList)
+ ).
+
+prop_all_false() ->
+ ?FORALL(
+ {InList, Elem},
+ ?LET(
+ {F, R, E},
+ {gen_list(), gen_list(), make_ref()},
+ {F ++ [E|R], E}
+ ),
+ not lists:all(fun(T) -> T =/= Elem end, InList)
+ ).
+
+%% any/2
+prop_any_true() ->
+ ?FORALL(
+ {InList, Elem},
+ ?LET(
+ {F, R, E},
+ {gen_list(), gen_list(), make_ref()},
+ {F ++ [E|R], E}
+ ),
+ lists:any(fun(T) -> T =:= Elem end, InList)
+ ).
+
+prop_any_false() ->
+ ?FORALL(
+ InList,
+ gen_list(),
+ not lists:any(fun(_) -> false end, InList)
+ ).
+
+%% append/1
+prop_append_1() ->
+ ?FORALL(
+ InLists,
+ list(gen_list()),
+ check_appended(InLists, lists:append(InLists))
+ ).
+
+%% append/2
+prop_append_2() ->
+ ?FORALL(
+ {InList1, InList2},
+ {gen_list(), gen_list()},
+ lists:append(InList1, InList2) =:= InList1 ++ InList2
+ ).
+
+%% concat/1
+prop_concat() ->
+ ?FORALL(
+ {InList, ExpString},
+ gen_list_fold(
+ oneof([gen_atom(), number(), string()]),
+ fun
+ (A, Acc) when is_atom(A) -> Acc ++ atom_to_list(A);
+ (I, Acc) when is_integer(I) -> Acc ++ integer_to_list(I);
+ (F, Acc) when is_float(F) -> Acc ++ float_to_list(F);
+ (L, Acc) when is_list(L) -> Acc ++ L
+ end,
+ []
+ ),
+ lists:concat(InList) =:= ExpString
+ ).
+
+%% delete/2
+prop_delete() ->
+ ?FORALL(
+ {InList, DelElem},
+ ?LET(
+ {F, R, E},
+ {gen_list(), gen_list(), gen_any()},
+ {F ++ [E|R], E}
+ ),
+ begin
+ DeletedList = lists:delete(DelElem, InList),
+ length(DeletedList) =:= length(InList) - 1 andalso
+ check_deleted(DelElem, InList, DeletedList)
+ end
+ ).
+
+prop_delete_absent() ->
+ ?FORALL(
+ InList,
+ gen_list(),
+ lists:delete(make_ref(), InList) =:= InList
+ ).
+
+%% droplast/1
+prop_droplast() ->
+ ?FORALL(
+ InList,
+ gen_list(),
+ try
+ lists:droplast(InList) =:= lists:reverse(tl(lists:reverse(InList)))
+ catch
+ error:_ ->
+ InList =:= []
+ end
+ ).
+
+%% dropwhile/2
+prop_dropwhile() ->
+ ?FORALL(
+ {Pred, InList, ExpList},
+ ?LET(
+ Fn,
+ function1(boolean()),
+ ?LET(
+ {L, {_, DL}},
+ gen_list_fold(
+ gen_any(),
+ fun(E, {Drop, Acc}) ->
+ case Drop andalso Fn(E) of
+ true -> {true, Acc};
+ false -> {false, Acc ++ [E]}
+ end
+ end,
+ {true, []}
+ ),
+ {Fn, L, DL}
+ )
+ ),
+ lists:dropwhile(Pred, InList) =:= ExpList
+ ).
+
+%% duplicate/2
+prop_duplicate() ->
+ ?FORALL(
+ {N, Term, ExpList},
+ ?LET(
+ T,
+ gen_any(),
+ ?LET(L, list(T), {length(L), T, L})
+ ),
+ lists:duplicate(N, Term) =:= ExpList
+ ).
+
+%% enumerate/1
+prop_enumerate_1() ->
+ ?FORALL(
+ {InList, ExpList},
+ ?LET(
+ {L, {_, EL}},
+ gen_list_fold(
+ gen_any(),
+ fun(T, {I, Acc}) ->
+ {I + 1, Acc ++ [{I, T}]}
+ end,
+ {1, []}
+ ),
+ {L, EL}
+ ),
+ lists:enumerate(InList) =:= ExpList
+ ).
+
+%% enumerate/2
+prop_enumerate_2() ->
+ ?FORALL(
+ {StartIndex, InList, ExpList},
+ ?LET(
+ N,
+ integer(),
+ ?LET(
+ {L, {_, EL}},
+ gen_list_fold(
+ gen_any(),
+ fun(T, {I, Acc}) ->
+ {I + 1, Acc ++ [{I, T}]}
+ end,
+ {N, []}
+ ),
+ {N, L, EL}
+ )
+ ),
+ lists:enumerate(StartIndex, InList) =:= ExpList
+ ).
+
+%% enumerate/3
+prop_enumerate_3() ->
+ ?FORALL(
+ {StartIndex, Step, InList, ExpList},
+ ?LET(
+ {N, S},
+ {integer(), integer()},
+ ?LET(
+ {L, {_, EL}},
+ gen_list_fold(
+ gen_any(),
+ fun(T, {I, Acc}) ->
+ {I + S, Acc ++ [{I, T}]}
+ end,
+ {N, []}
+ ),
+ {N, S, L, EL}
+ )
+ ),
+ lists:enumerate(StartIndex, Step, InList) =:= ExpList
+ ).
+
+%% filter/2
+prop_filter() ->
+ ?FORALL(
+ {Pred, InList, ExpList},
+ ?LET(
+ P,
+ function1(boolean()),
+ ?LET(
+ {L, F},
+ gen_list_fold(
+ gen_any(),
+ fun(T, Acc) ->
+ case P(T) of
+ true -> Acc ++ [T];
+ false -> Acc
+ end
+ end,
+ []
+ ),
+ {P, L, F}
+ )
+ ),
+ lists:filter(Pred, InList) =:= ExpList
+ ).
+
+%% filtermap/2
+prop_filtermap() ->
+ ?FORALL(
+ {FilterMapFn, InList, ExpList},
+ ?LET(
+ Fn,
+ function1(oneof([true, false, {true, gen_any()}])),
+ ?LET(
+ {L, FM},
+ gen_list_fold(
+ gen_any(),
+ fun(T, Acc) ->
+ case Fn(T) of
+ false -> Acc;
+ true -> Acc ++ [T];
+ {true, T1} -> Acc ++ [T1]
+ end
+ end,
+ []
+ ),
+ {Fn, L, FM}
+ )
+ ),
+ lists:filtermap(FilterMapFn, InList) =:= ExpList
+ ).
+
+%% flatlength/1
+prop_flatlength() ->
+ ?FORALL(
+ {DeepList, Len},
+ gen_list_deepfold(fun(_, _, Cnt) -> Cnt + 1 end, 0),
+ lists:flatlength(DeepList) =:= Len
+ ).
+
+%% flatmap/2
+prop_flatmap() ->
+ ?FORALL(
+ {MapFn, InList, ExpList},
+ ?LET(
+ Fn,
+ function1(gen_list()),
+ ?LET(
+ {L, FlatMapped},
+ gen_list_fold(
+ gen_any(),
+ fun(T, Acc) ->
+ Acc ++ Fn(T)
+ end,
+ []
+ ),
+ {Fn, L, FlatMapped}
+ )
+ ),
+ lists:flatmap(MapFn, InList) =:= ExpList
+ ).
+
+%% flatten/1
+prop_flatten_1() ->
+ ?FORALL(
+ {DeepList, FlatList},
+ gen_list_deepfold(fun(_, E, Acc) -> Acc ++ [E] end, []),
+ lists:flatten(DeepList) =:= FlatList
+ ).
+
+%% flatten/2
+prop_flatten_2() ->
+ ?FORALL(
+ {{DeepList, FlatList}, Tail},
+ {gen_list_deepfold(fun(_, E, Acc) -> Acc ++ [E] end, []), gen_list()},
+ lists:flatten(DeepList, Tail) =:= FlatList ++ Tail
+ ).
+
+%% foldl/3
+prop_foldl() ->
+ ?FORALL(
+ {FoldFn, InList, Acc0, Exp},
+ ?LET(
+ {Fn, Acc0},
+ {function2(gen_any()), gen_any()},
+ ?LET(
+ {L, V},
+ gen_list_fold(gen_any(), Fn, Acc0),
+ {Fn, L, Acc0, V}
+ )
+ ),
+ lists:foldl(FoldFn, Acc0, InList) =:= Exp
+ ).
+
+%% foldr/3
+prop_foldr() ->
+ ?FORALL(
+ {FoldFn, InList, Acc0, Exp},
+ ?LET(
+ {Fn, Acc0},
+ {function2(gen_any()), gen_any()},
+ ?LET(
+ {L, V},
+ gen_list_fold(gen_any(), Fn, Acc0),
+ {Fn, lists:reverse(L), Acc0, V}
+ )
+ ),
+ lists:foldr(FoldFn, Acc0, InList) =:= Exp
+ ).
+
+%% foreach/2
+prop_foreach() ->
+ ?FORALL(
+ InList,
+ gen_list(),
+ begin
+ Tag = make_ref(),
+ lists:foreach(fun(E) -> self() ! {Tag, E} end, InList),
+ [receive {Tag, T} -> T after 100 -> error(timeout) end || _ <- InList] =:= InList
+ end
+ ).
+
+%% join/2
+prop_join() ->
+ ?FORALL(
+ {Sep, InList},
+ {gen_any(), gen_list()},
+ check_joined(Sep, InList, lists:join(Sep, InList))
+ ).
+
+%% keydelete/3
+prop_keydelete() ->
+ ?FORALL(
+ {Key, N, InList},
+ ?LET(
+ {K, N},
+ {gen_any(), range(1, 5)},
+ ?LET(
+ {F, R, E},
+ {gen_list(), gen_list(), gen_keytuple(K, N, N + 3)},
+ {K, N, F ++ [E|R]}
+ )
+ ),
+ begin
+ DeletedL = lists:keydelete(Key, N, InList),
+ length(DeletedL) =:= length(InList) - 1 andalso
+ check_keydeleted(Key, N, InList, DeletedL)
+ end
+ ).
+
+prop_keydelete_absent() ->
+ ?FORALL(
+ {N, InList},
+ {pos_integer(), gen_list()},
+ lists:keydelete(make_ref(), N, InList) =:= InList
+ ).
+
+%% keyfind/3
+prop_keyfind() ->
+ ?FORALL(
+ {Key, N, InList},
+ ?LET(
+ {K, N},
+ {gen_any(), range(1, 5)},
+ ?LET(
+ {F, R, E},
+ {gen_list(), gen_list(), gen_keytuple(K, N, N + 3)},
+ {K, N, F ++ [E|R]}
+ )
+ ),
+ begin
+ Found = lists:keyfind(Key, N, InList),
+ is_tuple(Found) andalso
+ tuple_size(Found) >= N andalso
+ element(N, Found) == Key
+ end
+ ).
+
+prop_keyfind_absent() ->
+ ?FORALL(
+ {N, InList},
+ {pos_integer(), gen_list()},
+ not lists:keyfind(make_ref(), N, InList)
+ ).
+
+%% keymap/3
+prop_keymap() ->
+ ?FORALL(
+ {MapFn, N, InList, ExpList},
+ ?LET(
+ Fn,
+ function([gen_any()], gen_any()),
+ ?LET(
+ N,
+ range(1, 5),
+ ?LET(
+ {L, M},
+ gen_list_fold(
+ gen_tuple(N, N + 3),
+ fun(T, Acc) ->
+ Acc ++ [setelement(N, T, Fn(element(N, T)))]
+ end,
+ []
+ ),
+ {Fn, N, L, M}
+ )
+ )
+ ),
+ lists:keymap(MapFn, N, InList) =:= ExpList
+ ).
+
+%% keymember/3
+prop_keymember() ->
+ ?FORALL(
+ {Key, N, InList},
+ ?LET(
+ {K, N},
+ {gen_any(), range(1, 5)},
+ ?LET(
+ {F, R, E},
+ {gen_list(), gen_list(), gen_keytuple(K, N, N + 3)},
+ {K, N, F ++ [E|R]}
+ )
+ ),
+ lists:keymember(Key, N, InList)
+ ).
+
+prop_keymember_absent() ->
+ ?FORALL(
+ {N, InList},
+ {pos_integer(), gen_list()},
+ not lists:keymember(make_ref(), N, InList)
+ ).
+
+%% keymerge/3
+prop_keymerge() ->
+ ?FORALL(
+ {N, InList1, InList2},
+ ?LET(
+ N,
+ range(1, 5),
+ ?LET(
+ {L1, L2},
+ {list(gen_tuple(N, N+3)), list(gen_tuple(N, N+3))},
+ {N, lists:sort(L1), lists:sort(L2)}
+ )
+ ),
+ check_merged(
+ fun (E1, E2) -> element(N, E1) =< element(N, E2) end,
+ [InList1, InList2],
+ lists:keymerge(N, InList1, InList2)
+ )
+ ).
+
+prop_keymerge_invalid() ->
+ ?FORALL(
+ {N, InList, X, Y},
+ ?LET(
+ N,
+ range(1, 5),
+ ?LET(
+ {L, X, Y},
+ {list(gen_tuple(N, N+3)), non_list(), non_list()},
+ {N, L, X, Y}
+ )
+ ),
+ expect_error(fun lists:keymerge/3, [N, InList, Y]) andalso
+ expect_error(fun lists:keymerge/3, [N, X, InList]) andalso
+ expect_error(fun lists:keymerge/3, [N, X, Y])
+ ).
+
+%% keyreplace/4
+prop_keyreplace() ->
+ ?FORALL(
+ {Key, N, InList, Replacement},
+ ?LET(
+ {K, N},
+ {gen_any(), range(1, 5)},
+ ?LET(
+ {F, R, E0, E1},
+ {gen_list(), gen_list(), gen_keytuple(K, N, N + 3), gen_tuple()},
+ {K, N, F ++ [E0|R], E1}
+ )
+ ),
+ check_keyreplaced(Key, N, Replacement, InList, lists:keyreplace(Key, N, InList, Replacement))
+ ).
+
+prop_keyreplace_absent() ->
+ ?FORALL(
+ {N, InList, Replacement},
+ {pos_integer(), gen_list(), gen_tuple()},
+ lists:keyreplace(make_ref(), N, InList, Replacement) =:= InList
+ ).
+
+%% keysearch/3
+prop_keysearch() ->
+ ?FORALL(
+ {Key, N, InList},
+ ?LET(
+ {K, N},
+ {gen_any(), range(1, 5)},
+ ?LET(
+ {F, R, E},
+ {gen_list(), gen_list(), gen_keytuple(K, N, N + 3)},
+ {K, N, F ++ [E|R]}
+ )
+ ),
+ begin
+ {value, Found} = lists:keysearch(Key, N, InList),
+ is_tuple(Found) andalso
+ tuple_size(Found) >= N andalso
+ element(N, Found) == Key
+ end
+ ).
+
+prop_keysearch_absent() ->
+ ?FORALL(
+ {N, InList},
+ {pos_integer(), gen_list()},
+ not lists:keysearch(make_ref(), N, InList)
+ ).
+
+%% keysort/2
+prop_keysort() ->
+ ?FORALL(
+ {N, InList},
+ ?LET(
+ N,
+ range(1, 5),
+ {N, list(gen_tuple(N, N + 3))}
+ ),
+ begin
+ Sorted = lists:keysort(N, InList),
+ length(Sorted) =:= length(InList) andalso
+ check_sorted(fun(E1, E2) -> element(N, E1) =< element(N, E2) end, InList, Sorted)
+ end
+ ).
+
+%% keystore/4
+prop_keystore() ->
+ ?FORALL(
+ {Key, N, InList, ToStore},
+ ?LET(
+ {K, N},
+ {gen_any(), range(1, 5)},
+ ?LET(
+ {F, R, E0, E1},
+ {gen_list(), gen_list(), gen_keytuple(K, N, N + 3), gen_tuple()},
+ {K, N, F ++ [E0|R], E1}
+ )
+ ),
+ check_keyreplaced(Key, N, ToStore, InList, lists:keystore(Key, N, InList, ToStore))
+ ).
+
+prop_keystore_absent() ->
+ ?FORALL(
+ {N, InList, ToStore},
+ {pos_integer(), gen_list(), gen_tuple()},
+ lists:keystore(make_ref(), N, InList, ToStore) =:= InList ++ [ToStore]
+ ).
+
+%% keytake/3
+prop_keytake() ->
+ ?FORALL(
+ {Key, N, InList, ExpList, ExpElem},
+ ?LET(
+ {K, N},
+ {make_ref(), range(1, 5)},
+ ?LET(
+ {F, R, E},
+ {gen_list(), gen_list(), gen_keytuple(K, N, N + 3)},
+ {K, N, F ++ [E|R], F ++ R, E}
+ )
+ ),
+ lists:keytake(Key, N, InList) =:= {value, ExpElem, ExpList}
+ ).
+
+prop_keytake_absent() ->
+ ?FORALL(
+ {N, InList},
+ {pos_integer(), gen_list()},
+ lists:keytake(make_ref(), N, InList) =:= false
+ ).
+
+%% last/1
+prop_last() ->
+ ?FORALL(
+ InList,
+ gen_list(),
+ try
+ lists:last(InList) =:= hd(lists:reverse(InList))
+ catch
+ error:_ ->
+ InList =:= []
+ end
+ ).
+
+%% map/2
+prop_map() ->
+ ?FORALL(
+ {MapFn, InList, ExpList},
+ ?LET(
+ Fn,
+ function1(gen_any()),
+ ?LET(
+ {L, M},
+ gen_list_fold(
+ gen_any(),
+ fun(T, Acc) ->
+ Acc ++ [Fn(T)]
+ end,
+ []
+ ),
+ {Fn, L, M}
+ )
+ ),
+ lists:map(MapFn, InList) =:= ExpList
+ ).
+
+%% mapfoldl/3
+prop_mapfoldl() ->
+ ?FORALL(
+ {MapFoldFn, InList, Acc0, Exp},
+ ?LET(
+ {MapFn, FoldFn, Acc0},
+ {function1(gen_any()), function2(gen_any()), gen_any()},
+ ?LET(
+ {L, MV},
+ gen_list_fold(
+ gen_any(),
+ fun(T, {AccM, AccF}) ->
+ {AccM ++ [MapFn(T)], FoldFn(T, AccF)}
+ end,
+ {[], Acc0}
+ ),
+ {fun(T, Acc) -> {MapFn(T), FoldFn(T, Acc)} end, L, Acc0, MV}
+ )
+ ),
+ lists:mapfoldl(MapFoldFn, Acc0, InList) =:= Exp
+ ).
+
+%% mapfoldr/3
+prop_mapfoldr() ->
+ ?FORALL(
+ {MapFoldFn, InList, Acc0, Exp},
+ ?LET(
+ {MapFn, FoldFn, Acc0},
+ {function1(gen_any()), function2(gen_any()), gen_any()},
+ ?LET(
+ {L, MV},
+ gen_list_fold(
+ gen_any(),
+ fun(T, {AccM, AccF}) ->
+ {[MapFn(T)|AccM], FoldFn(T, AccF)}
+ end,
+ {[], Acc0}
+ ),
+ {fun(T, Acc) -> {MapFn(T), FoldFn(T, Acc)} end, lists:reverse(L), Acc0, MV}
+ )
+ ),
+ lists:mapfoldr(MapFoldFn, Acc0, InList) =:= Exp
+ ).
+
+%% max/1
+prop_max() ->
+ ?FORALL(
+ {InList, ExpMax},
+ gen_list_fold(gen_any(), fun erlang:max/2),
+ try
+ lists:max(InList) == ExpMax
+ catch
+ error:_ ->
+ InList =:= []
+ end
+ ).
+
+%% member/2
+prop_member() ->
+ ?FORALL(
+ {InList, Member},
+ ?LET(
+ {F, R, E},
+ {gen_list(), gen_list(), gen_any()},
+ {F ++ [E|R], E}
+ ),
+ lists:member(Member, InList)
+ ).
+
+prop_member_absent() ->
+ ?FORALL(
+ InList,
+ gen_list(),
+ not lists:member(make_ref(), InList)
+ ).
+
+%% merge/1
+prop_merge_1() ->
+ ?FORALL(
+ InLists,
+ list(?LET(L, gen_list(), lists:sort(L))),
+ check_merged(fun erlang:'=<'/2, InLists, lists:merge(InLists))
+ ).
+
+prop_merge_1_invalid() ->
+ ?FORALL(
+ InLists,
+ ?LET(
+ {L1, X, L2},
+ {list(oneof([non_list(), gen_list()])), non_list(), list(oneof([non_list(), gen_list()]))},
+ L1 ++ [X|L2]
+ ),
+ expect_error(fun lists:merge/1, [InLists])
+ ).
+
+%% merge/2
+prop_merge_2() ->
+ ?FORALL(
+ {InList1, InList2},
+ ?LET(
+ {L1, L2},
+ {gen_list(), gen_list()},
+ {lists:sort(L1), lists:sort(L2)}
+ ),
+ check_merged(fun erlang:'=<'/2, [InList1, InList2], lists:merge(InList1, InList2))
+ ).
+
+prop_merge_2_invalid() ->
+ ?FORALL(
+ {InList, X, Y},
+ {gen_list(), non_list(), non_list()},
+ expect_error(fun lists:merge/2, [InList, X]) andalso
+ expect_error(fun lists:merge/2, [X, InList]) andalso
+ expect_error(fun lists:merge/2, [X, Y])
+ ).
+
+%% merge/3
+prop_merge_3() ->
+ ?FORALL(
+ {SortFn, InList1, InList2},
+ ?LET(
+ {Fn, L1, L2},
+ {gen_ordering_fun(), gen_list(), gen_list()},
+ {Fn, lists:sort(Fn, L1), lists:sort(Fn, L2)}
+ ),
+ check_merged(SortFn, [InList1, InList2], lists:merge(SortFn, InList1, InList2))
+ ).
+
+prop_merge_3_invalid() ->
+ ?FORALL(
+ {SortFn, InList, X, Y},
+ {gen_ordering_fun(), gen_list(), non_list(), non_list()},
+ expect_error(fun lists:merge/3, [SortFn, InList, Y]) andalso
+ expect_error(fun lists:merge/3, [SortFn, X, InList]) andalso
+ expect_error(fun lists:merge/3, [SortFn, X, Y])
+ ).
+
+%% merge3/3
+prop_merge3() ->
+ ?FORALL(
+ {InList1, InList2, InList3},
+ ?LET(
+ {L1, L2, L3},
+ {gen_list(), gen_list(), gen_list()},
+ {lists:sort(L1), lists:sort(L2), lists:sort(L3)}
+ ),
+ check_merged(fun erlang:'=<'/2, [InList1, InList2, InList3], lists:merge3(InList1, InList2, InList3))
+ ).
+
+prop_merge3_invalid() ->
+ ?FORALL(
+ {InList, X, Y, Z},
+ {gen_list(), non_list(), non_list(), non_list()},
+ expect_error(fun lists:merge/3, [InList, InList, Z]) andalso
+ expect_error(fun lists:merge/3, [InList, Y, InList]) andalso
+ expect_error(fun lists:merge/3, [InList, Y, Z]) andalso
+ expect_error(fun lists:merge/3, [X, InList, Z]) andalso
+ expect_error(fun lists:merge/3, [X, Y, InList]) andalso
+ expect_error(fun lists:merge/3, [X, Y, Z])
+ ).
+
+%% min/1
+prop_min() ->
+ ?FORALL(
+ {InList, ExpMin},
+ gen_list_fold(gen_any(), fun erlang:min/2),
+ try
+ lists:min(InList) == ExpMin
+ catch
+ error:_ ->
+ InList =:= []
+ end
+ ).
+
+%% nth/2
+prop_nth() ->
+ ?FORALL(
+ {InList, N, ExpElem},
+ ?LET(
+ {F, R, E},
+ {gen_list(), gen_list(), gen_any()},
+ {F ++ [E|R], length(F)+1, E}
+ ),
+ lists:nth(N, InList) =:= ExpElem
+ ).
+
+prop_nth_outofrange() ->
+ ?FORALL(
+ {N, InList},
+ ?LET(
+ {L, Offset},
+ {gen_list(), pos_integer()},
+ {length(L) + Offset, L}
+ ),
+ try
+ lists:nth(N, InList)
+ of
+ _ ->
+ false
+ catch
+ error:_ ->
+ true
+ end
+ ).
+
+%% nthtail/2
+prop_nthtail() ->
+ ?FORALL(
+ {InList, N, ExpTail},
+ ?LET(
+ {F, R},
+ {gen_list(), gen_list()},
+ {F ++ R, length(F), R}
+ ),
+ lists:nthtail(N, InList) =:= ExpTail
+ ).
+
+prop_nthtail_outofrange() ->
+ ?FORALL(
+ {N, InList},
+ ?LET(
+ {L, Offset},
+ {gen_list(), pos_integer()},
+ {length(L) + Offset, L}
+ ),
+ try
+ lists:nthtail(N, InList)
+ of
+ _ ->
+ false
+ catch
+ error:_ ->
+ true
+ end
+ ).
+
+%% partition/2
+prop_partition() ->
+ ?FORALL(
+ {Pred, InList},
+ {function1(boolean()), gen_list()},
+ begin
+ {Group1, Group2} = lists:partition(Pred, InList),
+ check_partitioned(Pred, InList, Group1, Group2)
+ end
+ ).
+
+%% prefix/2
+prop_prefix() ->
+ ?FORALL(
+ {InList, Prefix},
+ ?LET(
+ {F, R},
+ {gen_list(), gen_list()},
+ {F ++ R, F}
+ ),
+ lists:prefix(Prefix, InList) andalso
+ not lists:prefix([make_ref()|Prefix], InList) andalso
+ not lists:prefix(Prefix ++ [make_ref()], InList) andalso
+ (not lists:prefix(Prefix, [make_ref()|InList]) orelse Prefix =:= [])
+ ).
+
+%% reverse/1
+prop_reverse_1() ->
+ ?FORALL(
+ InList,
+ gen_list(),
+ check_reversed(InList, lists:reverse(InList)) andalso
+ lists:reverse(lists:reverse(InList)) =:= InList
+ ).
+
+%% reverse/2
+prop_reverse_2() ->
+ ?FORALL(
+ {InList, InTail},
+ {gen_list(), gen_list()},
+ check_reversed(InList, lists:reverse(InList, InTail), InTail)
+ ).
+
+%% search/2
+prop_search() ->
+ ?FORALL(
+ {Pred, InList, ExpElem},
+ ?LET(
+ {F, R, E},
+ {gen_list(), gen_list(), make_ref()},
+ {fun(T) -> T =:= E end, F ++ [E|R], E}
+ ),
+ lists:search(Pred, InList) =:= {value, ExpElem}
+ ).
+
+prop_search_absent() ->
+ ?FORALL(
+ InList,
+ gen_list(),
+ lists:search(fun(_) -> false end, InList) =:= false
+ ).
+
+%% seq/2
+prop_seq2() ->
+ ?FORALL(
+ {From, To},
+ {integer(), integer()},
+ try
+ lists:seq(From, To)
+ of
+ Seq ->
+ To >= From - 1 andalso
+ check_seq(Seq, From, To, 1)
+ catch
+ error:_ ->
+ To < From - 1
+ end
+ ).
+
+%% seq/3
+prop_seq3() ->
+ ?FORALL(
+ {From, To, Step},
+ {integer(), integer(), integer()},
+ try
+ lists:seq(From, To, Step)
+ of
+ Seq when Step > 0 ->
+ To >= From - Step andalso
+ check_seq(Seq, From, To, Step);
+ Seq when Step < 0 ->
+ To =< From - Step andalso
+ check_seq(Seq, From, To, Step);
+ Seq when Step =:= 0 ->
+ From =:= To andalso
+ check_seq(Seq, From, To, Step)
+ catch
+ error:_ when Step > 0 ->
+ To < From - Step;
+ error:_ when Step < 0 ->
+ To > From - Step;
+ error:_ when Step =:= 0 ->
+ From =/= To
+ end
+ ).
+
+%% sort/1
+prop_sort_1() ->
+ ?FORALL(
+ InList,
+ gen_list(),
+ begin
+ Sorted = lists:sort(InList),
+ length(Sorted) =:= length(InList) andalso
+ check_sorted(InList, Sorted)
+ end
+ ).
+
+%% sort/2
+prop_sort_2() ->
+ ?FORALL(
+ {SortFn, InList},
+ {gen_ordering_fun(), gen_list()},
+ begin
+ Sorted = lists:sort(SortFn, InList),
+ length(Sorted) =:= length(InList) andalso
+ check_sorted(SortFn, InList, Sorted)
+ end
+ ).
+
+%% split/2
+prop_split() ->
+ ?FORALL(
+ {N, InList, ExpList1, ExpList2},
+ ?LET(
+ {F, R},
+ {gen_list(), gen_list()},
+ {length(F), F ++ R, F, R}
+ ),
+ lists:split(N, InList) =:= {ExpList1, ExpList2}
+ ).
+
+prop_split_outofrange() ->
+ ?FORALL(
+ {N, InList},
+ ?LET(
+ {L, Offset},
+ {gen_list(), pos_integer()},
+ {length(L) + Offset, L}
+ ),
+ try
+ lists:split(N, InList)
+ of
+ _ ->
+ false
+ catch
+ error:_ ->
+ true
+ end
+ ).
+
+%% splitwith/2
+prop_splitwith() ->
+ ?FORALL(
+ {Pred, InList},
+ {function1(boolean()), gen_list()},
+ begin
+ {Part1, Part2} = lists:splitwith(Pred, InList),
+ check_splitwithed(Pred, InList, Part1, Part2)
+ end
+ ).
+
+%% sublist/2
+prop_sublist_2() ->
+ ?FORALL(
+ {Len, InList, ExpList},
+ ?LET(
+ {F, R},
+ {gen_list(), gen_list()},
+ {length(F), F ++ R, F}
+ ),
+ lists:sublist(InList, Len) =:= ExpList
+ ).
+
+%% sublist/3
+prop_sublist_3() ->
+ ?FORALL(
+ {Start, Len, InList, ExpList},
+ ?LET(
+ {F, M, R},
+ {gen_list(), gen_list(), gen_list()},
+ {length(F)+1, length(M), F ++ M ++ R, M}
+ ),
+ lists:sublist(InList, Start, Len) =:= ExpList
+ ).
+
+%% subtract/2
+prop_subtract() ->
+ ?FORALL(
+ {InList, SubtractList},
+ ?LET(
+ {L, B, S},
+ {gen_list(), gen_list(), gen_list()},
+ {L ++ B, S ++ B}
+ ),
+ lists:subtract(InList, SubtractList) =:= InList -- SubtractList
+ ).
+
+%% suffix/2
+prop_suffix() ->
+ ?FORALL(
+ {InList, Suffix},
+ ?LET(
+ {F, R},
+ {gen_list(), gen_list()},
+ {F ++ R, R}
+ ),
+ lists:suffix(Suffix, InList) andalso
+ not lists:suffix([make_ref()|Suffix], InList) andalso
+ not lists:suffix(Suffix ++ [make_ref()], InList) andalso
+ (not lists:suffix(Suffix, InList ++ [make_ref()]) orelse Suffix =:= [])
+ ).
+
+%% sum/1
+prop_sum() ->
+ ?FORALL(
+ {InList, ExpSum},
+ gen_list_fold(number(), fun erlang:'+'/2, 0),
+ lists:sum(InList) =:= ExpSum
+ ).
+
+%% takewhile/2
+prop_takewhile() ->
+ ?FORALL(
+ {Pred, InList, ExpList},
+ ?LET(
+ Fn,
+ function1(boolean()),
+ ?LET(
+ {L, {_, TL}},
+ gen_list_fold(
+ gen_any(),
+ fun(E, {Take, Acc}) ->
+ case Take andalso Fn(E) of
+ true -> {true, Acc ++ [E]};
+ false -> {false, Acc}
+ end
+ end,
+ {true, []}
+ ),
+ {Fn, L, TL}
+ )
+ ),
+ lists:takewhile(Pred, InList) =:= ExpList
+ ).
+
+%% ukeymerge/3
+prop_ukeymerge() ->
+ ?FORALL(
+ {N, InList1, InList2},
+ ?LET(
+ N,
+ range(1, 5),
+ ?LET(
+ {L1, L2},
+ {list(gen_tuple(N, N+3)), list(gen_tuple(N, N+3))},
+ {N, lists:ukeysort(N, L1), lists:ukeysort(N, L2)}
+ )
+ ),
+ check_umerged(
+ fun(E1, E2) -> element(N, E1) =< element(N, E2) end,
+ [InList1, InList2],
+ lists:ukeymerge(N, InList1, InList2)
+ )
+ ).
+
+prop_ukeymerge_invalid() ->
+ ?FORALL(
+ {N, InList, X, Y},
+ ?LET(
+ N,
+ range(1, 5),
+ ?LET(
+ {L, X, Y},
+ {list(gen_tuple(N, N+3)), non_list(), non_list()},
+ {N, L, X, Y}
+ )
+ ),
+ expect_error(fun lists:ukeymerge/3, [N, InList, Y]) andalso
+ expect_error(fun lists:ukeymerge/3, [N, X, InList]) andalso
+ expect_error(fun lists:ukeymerge/3, [N, X, Y])
+ ).
+
+%% ukeysort/2
+prop_ukeysort() ->
+ ?FORALL(
+ {N, InList},
+ ?LET(
+ N,
+ range(1, 5),
+ {N, list(gen_tuple(N, N + 3))}
+ ),
+ begin
+ Sorted = lists:ukeysort(N, InList),
+ length(Sorted) =< length(InList) andalso
+ check_usorted(fun(E1, E2) -> element(N, E1) =< element(N, E2) end, InList, Sorted)
+ end
+ ).
+
+%% umerge/1
+prop_umerge_1() ->
+ ?FORALL(
+ InLists,
+ list(?LET(L, gen_list(), lists:usort(L))),
+ check_umerged(InLists, lists:umerge(InLists))
+ ).
+
+prop_umerge_1_invalid() ->
+ ?FORALL(
+ InList,
+ ?LET(
+ {L1, X, L2},
+ {list(oneof([non_list(), gen_list()])), non_list(), list(oneof([non_list(), gen_list()]))},
+ L1 ++ [X|L2]
+ ),
+ expect_error(fun lists:umerge/1, [InList])
+ ).
+
+%% umerge/2
+prop_umerge_2() ->
+ ?FORALL(
+ {InList1, InList2},
+ ?LET(
+ {L1, L2},
+ {gen_list(), gen_list()},
+ {lists:usort(L1), lists:usort(L2)}
+ ),
+ check_umerged([InList1, InList2], lists:umerge(InList1, InList2))
+ ).
+
+prop_umerge_2_invalid() ->
+ ?FORALL(
+ {InList, X, Y},
+ {gen_list(), non_list(), non_list()},
+ expect_error(fun lists:umerge/2, [InList, Y]) andalso
+ expect_error(fun lists:umerge/2, [X, InList]) andalso
+ expect_error(fun lists:umerge/2, [X, Y])
+ ).
+
+%% umerge/3
+prop_umerge_3() ->
+ ?FORALL(
+ {SortFn, InList1, InList2},
+ ?LET(
+ {Fn, L1, L2},
+ {gen_ordering_fun(), gen_list(), gen_list()},
+ {Fn, lists:usort(Fn, L1), lists:usort(Fn, L2)}
+ ),
+ check_umerged(SortFn, [InList1, InList2], lists:umerge(SortFn, InList1, InList2))
+ ).
+
+prop_umerge_3_invalid() ->
+ ?FORALL(
+ {SortFn, InList, X, Y},
+ {gen_ordering_fun(), gen_list(), non_list(), non_list()},
+ expect_error(fun lists:umerge/3, [SortFn, InList, Y]) andalso
+ expect_error(fun lists:umerge/3, [SortFn, X, InList]) andalso
+ expect_error(fun lists:umerge/3, [SortFn, X, Y])
+ ).
+
+%% umerge3/3
+prop_umerge3() ->
+ ?FORALL(
+ {InList1, InList2, InList3},
+ ?LET(
+ {L1, L2, L3},
+ {gen_list(), gen_list(), gen_list()},
+ {lists:usort(L1), lists:usort(L2), lists:usort(L3)}
+ ),
+ check_umerged([InList1, InList2, InList3], lists:umerge3(InList1, InList2, InList3))
+ ).
+
+prop_umerge3_invalid() ->
+ ?FORALL(
+ {InList, X, Y, Z},
+ {gen_list(), non_list(), non_list(), non_list()},
+ expect_error(fun lists:umerge3/3, [InList, InList, Z]) andalso
+ expect_error(fun lists:umerge3/3, [InList, Y, InList]) andalso
+ expect_error(fun lists:umerge3/3, [InList, Y, Z]) andalso
+ expect_error(fun lists:umerge3/3, [X, InList, InList]) andalso
+ expect_error(fun lists:umerge3/3, [X, InList, Z]) andalso
+ expect_error(fun lists:umerge3/3, [X, Y, InList]) andalso
+ expect_error(fun lists:umerge3/3, [X, Y, Z])
+ ).
+
+%% uniq/1
+prop_uniq_1() ->
+ ?FORALL(
+ InList,
+ ?LET(
+ {L, M},
+ {gen_list(), gen_list()},
+ ?LET(
+ S,
+ vector(length(L) + 2 * length(M), integer()),
+ [E || {_, E} <- lists:sort(lists:zip(S, L ++ M ++ M))]
+ )
+ ),
+ check_uniqed(InList, lists:uniq(InList))
+ ).
+
+%% uniq/2
+prop_uniq_2() ->
+ ?FORALL(
+ {UniqFn, InList},
+ {function1(oneof([a, b, c])), gen_list()},
+ check_uniqed(UniqFn, InList, lists:uniq(UniqFn, InList))
+ ).
+
+%% unzip/1
+prop_unzip() ->
+ ?FORALL(
+ {InList, {ExpList1, ExpList2}},
+ gen_list_fold(
+ {gen_any(), gen_any()},
+ fun({T1, T2}, {L1, L2}) ->
+ {L1 ++ [T1], L2 ++ [T2]}
+ end,
+ {[], []}
+ ),
+ lists:unzip(InList) =:= {ExpList1, ExpList2}
+ ).
+
+%% unzip3/1
+prop_unzip3() ->
+ ?FORALL(
+ {InList, {ExpList1, ExpList2, ExpList3}},
+ gen_list_fold(
+ {gen_any(), gen_any(), gen_any()},
+ fun({T1, T2, T3}, {L1, L2, L3}) ->
+ {L1 ++ [T1], L2 ++ [T2], L3 ++ [T3]}
+ end,
+ {[], [], []}
+ ),
+ lists:unzip3(InList) =:= {ExpList1, ExpList2, ExpList3}
+ ).
+
+%% usort/1
+prop_usort_1() ->
+ ?FORALL(
+ InList,
+ gen_list(),
+ begin
+ Sorted = lists:usort(InList),
+ length(Sorted) =< length(InList) andalso
+ check_usorted(InList, Sorted)
+ end
+ ).
+
+%% usort/2
+prop_usort_2() ->
+ ?FORALL(
+ {SortFn, InList},
+ {gen_ordering_fun(), gen_list()},
+ begin
+ Sorted = lists:usort(SortFn, InList),
+ length(Sorted) =< length(InList) andalso
+ check_usorted(SortFn, InList, Sorted)
+ end
+ ).
+
+%% zip/2
+prop_zip_2() ->
+ ?FORALL(
+ {ExpList, {InList1, InList2}},
+ gen_list_fold(
+ {gen_any(), gen_any()},
+ fun({T1, T2}, {L1, L2}) ->
+ {L1 ++ [T1], L2 ++ [T2]}
+ end,
+ {[], []}
+ ),
+ lists:zip(InList1, InList2) =:= ExpList
+ ).
+
+%% zip/3
+prop_zip_3() ->
+ ?FORALL(
+ {{ExpList, {InList1, InList2}}, ExtraList},
+ {
+ gen_list_fold(
+ {gen_any(), gen_any()},
+ fun({T1, T2}, {L1, L2}) ->
+ {L1 ++ [T1], L2 ++ [T2]}
+ end,
+ {[], []}
+ ),
+ non_empty(gen_list())
+ },
+ begin
+ Tag = make_ref(),
+
+ Res1 = ExpList =:= lists:zip(InList1, InList2, fail) andalso
+ ExpList =:= lists:zip(InList1, InList2, trim) andalso
+ ExpList =:= lists:zip(InList1, InList2, {pad, {Tag, Tag}}),
+
+ Res2 = try lists:zip(InList1, InList2 ++ ExtraList, fail) of _ -> false catch error:_ -> true end andalso
+ try lists:zip(InList1 ++ ExtraList, InList2, fail) of _ -> false catch error:_ -> true end,
+
+ Res3 = ExpList =:= lists:zip(InList1, InList2 ++ ExtraList, trim) andalso
+ ExpList =:= lists:zip(InList1 ++ ExtraList, InList2, trim),
+
+ Padded1 = lists:zip(InList1, InList2 ++ ExtraList, {pad, {Tag, Tag}}),
+ Padded2 = lists:zip(InList1 ++ ExtraList, InList2, {pad, {Tag, Tag}}),
+ Res4 = Padded1 =:= ExpList ++ [{Tag, X} || X <- ExtraList] andalso
+ Padded2 =:= ExpList ++ [{X, Tag} || X <- ExtraList],
+
+ Res1 andalso Res2 andalso Res3 andalso Res4
+ end
+ ).
+
+%% zip3/3
+prop_zip3_3() ->
+ ?FORALL(
+ {ExpList, {InList1, InList2, InList3}},
+ gen_list_fold(
+ {gen_any(), gen_any(), gen_any()},
+ fun({T1, T2, T3}, {L1, L2, L3}) ->
+ {L1 ++ [T1], L2 ++ [T2], L3 ++ [T3]}
+ end,
+ {[], [], []}
+ ),
+ lists:zip3(InList1, InList2, InList3) =:= ExpList
+ ).
+
+%% zip3/4
+prop_zip3_4() ->
+ ?FORALL(
+ {{ExpList, {InList1, InList2, InList3}}, ExtraList},
+ {
+ gen_list_fold(
+ {gen_any(), gen_any(), gen_any()},
+ fun({T1, T2, T3}, {L1, L2, L3}) ->
+ {L1 ++ [T1], L2 ++ [T2], L3 ++ [T3]}
+ end,
+ {[], [], []}
+ ),
+ non_empty(gen_list())
+ },
+ begin
+ Tag = make_ref(),
+
+ Res1 = ExpList =:= lists:zip3(InList1, InList2, InList3, fail) andalso
+ ExpList =:= lists:zip3(InList1, InList2, InList3, trim) andalso
+ ExpList =:= lists:zip3(InList1, InList2, InList3, {pad, {Tag, Tag, Tag}}),
+
+ Res2 = try lists:zip3(InList1, InList2, InList3 ++ ExtraList, fail) of _ -> false catch error:_ -> true end andalso
+ try lists:zip3(InList1, InList2 ++ ExtraList, InList3, fail) of _ -> false catch error:_ -> true end andalso
+ try lists:zip3(InList1, InList2 ++ ExtraList, InList3 ++ ExtraList, fail) of _ -> false catch error:_ -> true end andalso
+ try lists:zip3(InList1 ++ ExtraList, InList2, InList3, fail) of _ -> false catch error:_ -> true end andalso
+ try lists:zip3(InList1 ++ ExtraList, InList2, InList3 ++ ExtraList, fail) of _ -> false catch error:_ -> true end andalso
+ try lists:zip3(InList1 ++ ExtraList, InList2 ++ ExtraList, InList3, fail) of _ -> false catch error:_ -> true end,
+
+ Res3 = ExpList =:= lists:zip3(InList1, InList2, InList3 ++ ExtraList, trim) andalso
+ ExpList =:= lists:zip3(InList1, InList2 ++ ExtraList, InList3, trim) andalso
+ ExpList =:= lists:zip3(InList1, InList2 ++ ExtraList, InList3 ++ ExtraList, trim) andalso
+ ExpList =:= lists:zip3(InList1 ++ ExtraList, InList2, InList3, trim) andalso
+ ExpList =:= lists:zip3(InList1 ++ ExtraList, InList2, InList3 ++ ExtraList, trim) andalso
+ ExpList =:= lists:zip3(InList1 ++ ExtraList, InList2 ++ ExtraList, InList3, trim),
+
+ Padded1 = lists:zip3(InList1, InList2, InList3 ++ ExtraList, {pad, {Tag, Tag, Tag}}),
+ Padded2 = lists:zip3(InList1, InList2 ++ ExtraList, InList3, {pad, {Tag, Tag, Tag}}),
+ Padded3 = lists:zip3(InList1, InList2 ++ ExtraList, InList3 ++ ExtraList, {pad, {Tag, Tag, Tag}}),
+ Padded4 = lists:zip3(InList1 ++ ExtraList, InList2, InList3, {pad, {Tag, Tag, Tag}}),
+ Padded5 = lists:zip3(InList1 ++ ExtraList, InList2, InList3 ++ ExtraList, {pad, {Tag, Tag, Tag}}),
+ Padded6 = lists:zip3(InList1 ++ ExtraList, InList2 ++ ExtraList, InList3, {pad, {Tag, Tag, Tag}}),
+ Res4 = Padded1 =:= ExpList ++ [{Tag, Tag, X} || X <- ExtraList] andalso
+ Padded2 =:= ExpList ++ [{Tag, X, Tag} || X <- ExtraList] andalso
+ Padded3 =:= ExpList ++ [{Tag, X, X} || X <- ExtraList] andalso
+ Padded4 =:= ExpList ++ [{X, Tag, Tag} || X <- ExtraList] andalso
+ Padded5 =:= ExpList ++ [{X, Tag, X} || X <- ExtraList] andalso
+ Padded6 =:= ExpList ++ [{X, X, Tag} || X <- ExtraList],
+
+ Res1 andalso Res2 andalso Res3 andalso Res4
+ end
+ ).
+
+%% zipwith/3
+prop_zipwith_3() ->
+ ?FORALL(
+ {ZipFn, InList1, InList2, ExpList},
+ ?LET(
+ Fn,
+ function2(gen_any()),
+ ?LET(
+ {_, {L1, L2, Z}},
+ gen_list_fold(
+ {gen_any(), gen_any()},
+ fun({T1, T2}, {L1, L2, Z}) ->
+ {L1 ++ [T1], L2 ++ [T2], Z ++ [Fn(T1, T2)]}
+ end,
+ {[], [], []}
+ ),
+ {Fn, L1, L2, Z}
+ )
+ ),
+ lists:zipwith(ZipFn, InList1, InList2) =:= ExpList
+ ).
+
+%% zipwith/4
+prop_zipwith_4() ->
+ ?FORALL(
+ {ZipFn, InList1, InList2, ExpList, ExtraList},
+ ?LET(
+ {Extra, Fn},
+ {non_empty(gen_list()), function2(gen_any())},
+ ?LET(
+ {_, {L1, L2, Z}},
+ gen_list_fold(
+ {gen_any(), gen_any()},
+ fun({T1, T2}, {L1, L2, Z}) ->
+ {L1 ++ [T1], L2 ++ [T2], Z ++ [Fn(T1, T2)]}
+ end,
+ {[], [], []}
+ ),
+ {Fn, L1, L2, Z, Extra}
+ )
+ ),
+ begin
+ Tag = make_ref(),
+
+ Res1 = ExpList =:= lists:zipwith(ZipFn, InList1, InList2, fail) andalso
+ ExpList =:= lists:zipwith(ZipFn, InList1, InList2, trim) andalso
+ ExpList =:= lists:zipwith(ZipFn, InList1, InList2, {pad, {Tag, Tag}}),
+
+ Res2 = try lists:zipwith(ZipFn, InList1, InList2 ++ ExtraList, fail) of _ -> false catch error:_ -> true end andalso
+ try lists:zipwith(ZipFn, InList1 ++ ExtraList, InList2, fail) of _ -> false catch error:_ -> true end,
+
+ Res3 = ExpList =:= lists:zipwith(ZipFn, InList1, InList2 ++ ExtraList, trim) andalso
+ ExpList =:= lists:zipwith(ZipFn, InList1 ++ ExtraList, InList2, trim),
+
+ Padded1 = lists:zipwith(ZipFn, InList1, InList2 ++ ExtraList, {pad, {Tag, Tag}}),
+ Padded2 = lists:zipwith(ZipFn, InList1 ++ ExtraList, InList2, {pad, {Tag, Tag}}),
+ Res4 = Padded1 =:= ExpList ++ [ZipFn(Tag, X) || X <- ExtraList] andalso
+ Padded2 =:= ExpList ++ [ZipFn(X, Tag) || X <- ExtraList],
+
+ Res1 andalso Res2 andalso Res3 andalso Res4
+ end
+ ).
+
+%% zipwith3/4
+prop_zipwith3_4() ->
+ ?FORALL(
+ {ZipFn, InList1, InList2, InList3, ExpList},
+ ?LET(
+ Fn,
+ function3(gen_any()),
+ ?LET(
+ {_, {L1, L2, L3, Z}},
+ gen_list_fold(
+ {gen_any(), gen_any(), gen_any()},
+ fun({T1, T2, T3}, {L1, L2, L3, Z}) ->
+ {L1 ++ [T1], L2 ++ [T2], L3 ++ [T3], Z ++ [Fn(T1, T2, T3)]}
+ end,
+ {[], [], [], []}
+ ),
+ {Fn, L1, L2, L3, Z}
+ )
+ ),
+ lists:zipwith3(ZipFn, InList1, InList2, InList3) =:= ExpList
+ ).
+
+%% zipwith3/5
+prop_zipwith3_5() ->
+ ?FORALL(
+ {ZipFn, InList1, InList2, InList3, ExpList, ExtraList},
+ ?LET(
+ {Extra, Fn},
+ {non_empty(gen_list()), function3(gen_any())},
+ ?LET(
+ {_, {L1, L2, L3, Z}},
+ gen_list_fold(
+ {gen_any(), gen_any(), gen_any()},
+ fun({T1, T2, T3}, {L1, L2, L3, Z}) ->
+ {L1 ++ [T1], L2 ++ [T2], L3 ++ [T3], Z ++ [Fn(T1, T2, T3)]}
+ end,
+ {[], [], [], []}
+ ),
+ {Fn, L1, L2, L3, Z, Extra}
+ )
+ ),
+ begin
+ Tag = make_ref(),
+
+ Res1 = ExpList =:= lists:zipwith3(ZipFn, InList1, InList2, InList3, fail) andalso
+ ExpList =:= lists:zipwith3(ZipFn, InList1, InList2, InList3, trim) andalso
+ ExpList =:= lists:zipwith3(ZipFn, InList1, InList2, InList3, {pad, {Tag, Tag, Tag}}),
+
+ Res2 = try lists:zipwith3(ZipFn, InList1, InList2, InList3 ++ ExtraList, fail) of _ -> false catch error:_ -> true end andalso
+ try lists:zipwith3(ZipFn, InList1, InList2 ++ ExtraList, InList3, fail) of _ -> false catch error:_ -> true end andalso
+ try lists:zipwith3(ZipFn, InList1, InList2 ++ ExtraList, InList3 ++ ExtraList, fail) of _ -> false catch error:_ -> true end andalso
+ try lists:zipwith3(ZipFn, InList1 ++ ExtraList, InList2, InList3, fail) of _ -> false catch error:_ -> true end andalso
+ try lists:zipwith3(ZipFn, InList1 ++ ExtraList, InList2, InList3 ++ ExtraList, fail) of _ -> false catch error:_ -> true end andalso
+ try lists:zipwith3(ZipFn, InList1 ++ ExtraList, InList2 ++ ExtraList, InList3, fail) of _ -> false catch error:_ -> true end,
+
+ Res3 = ExpList =:= lists:zipwith3(ZipFn, InList1, InList2, InList3 ++ ExtraList, trim) andalso
+ ExpList =:= lists:zipwith3(ZipFn, InList1, InList2 ++ ExtraList, InList3, trim) andalso
+ ExpList =:= lists:zipwith3(ZipFn, InList1, InList2 ++ ExtraList, InList3 ++ ExtraList, trim) andalso
+ ExpList =:= lists:zipwith3(ZipFn, InList1 ++ ExtraList, InList2, InList3, trim) andalso
+ ExpList =:= lists:zipwith3(ZipFn, InList1 ++ ExtraList, InList2, InList3 ++ ExtraList, trim) andalso
+ ExpList =:= lists:zipwith3(ZipFn, InList1 ++ ExtraList, InList2 ++ ExtraList, InList3, trim),
+
+ Padded1 = lists:zipwith3(ZipFn, InList1, InList2, InList3 ++ ExtraList, {pad, {Tag, Tag, Tag}}),
+ Padded2 = lists:zipwith3(ZipFn, InList1, InList2 ++ ExtraList, InList3, {pad, {Tag, Tag, Tag}}),
+ Padded3 = lists:zipwith3(ZipFn, InList1, InList2 ++ ExtraList, InList3 ++ ExtraList, {pad, {Tag, Tag, Tag}}),
+ Padded4 = lists:zipwith3(ZipFn, InList1 ++ ExtraList, InList2, InList3, {pad, {Tag, Tag, Tag}}),
+ Padded5 = lists:zipwith3(ZipFn, InList1 ++ ExtraList, InList2, InList3 ++ ExtraList, {pad, {Tag, Tag, Tag}}),
+ Padded6 = lists:zipwith3(ZipFn, InList1 ++ ExtraList, InList2 ++ ExtraList, InList3, {pad, {Tag, Tag, Tag}}),
+ Res4 = Padded1 =:= ExpList ++ [ZipFn(Tag, Tag, X) || X <- ExtraList] andalso
+ Padded2 =:= ExpList ++ [ZipFn(Tag, X, Tag) || X <- ExtraList] andalso
+ Padded3 =:= ExpList ++ [ZipFn(Tag, X, X) || X <- ExtraList] andalso
+ Padded4 =:= ExpList ++ [ZipFn(X, Tag, Tag) || X <- ExtraList] andalso
+ Padded5 =:= ExpList ++ [ZipFn(X, Tag, X) || X <- ExtraList] andalso
+ Padded6 =:= ExpList ++ [ZipFn(X, X, Tag) || X <- ExtraList],
+
+ Res1 andalso Res2 andalso Res3 andalso Res4
+ end
+ ).
+
+%%%%%%%%%%%%%%%%%%
+%%% Generators %%%
+%%%%%%%%%%%%%%%%%%
+
+non_list() ->
+ ?SUCHTHAT(NonList, gen_any(), not is_list(NonList)).
+
+%% Generator for lists of the given type, folding the given function
+%% over values on the top level as they are generated. The first generated
+%% value serves as the initial accumulator.
+gen_list_fold(Gen, FoldFn) ->
+ ?SIZED(
+ Size,
+ ?LET(
+ T,
+ Gen,
+ if
+ Size =< 1 ->
+ {[], T};
+ true ->
+ gen_list_fold(max(0, Size - 1), Gen, [T], FoldFn, T)
+ end
+ )
+ ).
+
+%% Generator for lists of the given type, folding the given function
+%% over values on the top level as they are generated.
+gen_list_fold(Gen, FoldFn, Acc0) ->
+ ?SIZED(
+ Size,
+ gen_list_fold(max(0, Size - 1), Gen, [], FoldFn, Acc0)
+ ).
+
+gen_list_fold(0, _Gen, L, _FoldFn, Acc) ->
+ {L, Acc};
+gen_list_fold(N, Gen, L, FoldFn, Acc) ->
+ ?LET(
+ E,
+ Gen,
+ gen_list_fold(N - 1, Gen, L ++ [E], FoldFn, FoldFn(E, Acc))
+ ).
+
+%% Generator for key tuples of the given size,
+%% with the given key in the given (ie, last) position.
+gen_keytuple(Key, Size) ->
+ gen_keytuple(Key, Size, Size).
+
+%% Generator for key tuples of the given minimum and maximum
+%% sizes, with the given key in the given minimum position.
+gen_keytuple(Key, MinSize, MaxSize) ->
+ ?LET(
+ Tuple,
+ gen_tuple(MinSize, MaxSize),
+ setelement(MinSize, Tuple, Key)
+ ).
+
+%% Generator for tuples of random size.
+gen_tuple() ->
+ ?LET(
+ N,
+ non_neg_integer(),
+ gen_tuple(N)
+ ).
+
+%% Generator for tuples of the given size.
+gen_tuple(Size) ->
+ ?LET(
+ V,
+ vector(Size, gen_any()),
+ list_to_tuple(V)
+ ).
+
+%% Generator for tuples of the given minimum and
+%% maximum sizes.
+gen_tuple(MinSize, MaxSize) ->
+ ?LET(
+ N,
+ range(MinSize, MaxSize),
+ ?LET(
+ V,
+ vector(N, gen_any()),
+ list_to_tuple(V)
+ )
+ ).
+
+%% Generator for lists of anything.
+gen_list() ->
+ list(gen_any()).
+
+%% Generator for lists of anything, folding the given function
+%% over values on all levels of list-nesting as they are generated.
+gen_list_deepfold(FoldFn, Acc0) ->
+ ?SIZED(
+ Size,
+ ?LET(
+ {_, L, Acc},
+ gen_list_deepfold(max(0, Size - 1), 0, [], FoldFn, Acc0),
+ {L, Acc}
+ )
+ ).
+
+gen_list_deepfold(N, _Level, L, _FoldFn, Acc) when N =< 0 ->
+ {N, lists:reverse(L), Acc};
+gen_list_deepfold(N, Level, L, FoldFn, Acc) ->
+ ?LET(
+ X,
+ frequency([
+ {4, {term, gen_any_simple()}},
+ {1, deeplist},
+ {1, tuple},
+ {2, stop}
+ ]),
+ case X of
+ deeplist ->
+ ?LET(
+ {N1, L1, Acc1},
+ gen_list_deepfold(N, Level + 1, [], FoldFn, Acc),
+ gen_list_deepfold(N1, Level, [L1|L], FoldFn, Acc1)
+ );
+ tuple ->
+ ?LET(
+ {N1, L1, _},
+ gen_list_deepfold(N, Level + 1, [], fun(_, _, _) -> undefined end, undefined),
+ begin
+ E = list_to_tuple(L1),
+ gen_list_deepfold(N1, Level, [E|L], FoldFn, FoldFn(Level, E, Acc))
+ end
+ );
+ stop ->
+ {N, lists:reverse(L), Acc};
+ {term, E} ->
+ gen_list_deepfold(N - 1, Level, [E|L], FoldFn, FoldFn(Level, E, Acc))
+ end
+ ).
+
+%% Generator for simple and composite (lists and tuples) types.
+gen_any() ->
+ frequency(
+ [
+ {4, gen_any_simple()},
+ {1, ?LET({L, _}, gen_list_deepfold(fun(_, _, Acc) -> Acc end, undefined), L)},
+ {1, ?LET({L, _}, gen_list_deepfold(fun(_, _, Acc) -> Acc end, undefined), list_to_tuple(L))}
+ ]
+ ).
+
+%% Generator for simple types:
+%% - atoms
+%% - integers
+%% - floats
+%% - bitstrings
+gen_any_simple() ->
+ oneof([gen_atom(), integer(), float(), bitstring()]).
+
+%% Generator for interesting atoms:
+%% - well-known atoms like `ok', `undefined', `infinity'...
+%% - randomly generated "weird" atoms
+gen_atom() ->
+ oneof(
+ [
+ oneof([ok, error, true, false, undefined, infinity]),
+ oneof(['', '"', '\'', '(', ')', '()', '[', '[', '[]', '{', '}', '{}']),
+ gen_random_atom()
+ ]
+ ).
+
+%% Generator for a limited set of random atoms. The number of
+%% atoms that will be generated is set in `?RANDOM_ATOMS'.
+gen_random_atom() ->
+ ?LAZY(
+ ?LET(
+ N,
+ range(1, ?RANDOM_ATOMS),
+ try
+ persistent_term:get({?MODULE, random_atoms})
+ of
+ Atoms ->
+ maps:get(N, Atoms)
+ catch
+ error:badarg ->
+ ?LET(
+ AtomsList,
+ vector(?RANDOM_ATOMS, ?SIZED(Size, resize(Size * 100, atom()))),
+ begin
+ Fn = fun
+ F(_, [], Acc) ->
+ Acc;
+ F(Index, [A|As], Acc) ->
+ F(Index + 1, As, Acc#{Index => A})
+ end,
+ Atoms = Fn(1, AtomsList, #{}),
+ persistent_term:put({?MODULE, random_atoms}, Atoms),
+ maps:get(N, Atoms)
+ end
+ )
+ end
+ )
+ ).
+
+%% Generator for ordering functions, to be used for sorting and merging.
+%% The generated ordering functions are designed to fulfill the requirements given
+%% at the top of the `lists' documentation, namely to be antisymmetric, transitive,
+%% and total. Further, the chances that two terms compare equal, less or greater
+%% are equal.
+gen_ordering_fun() ->
+ ?LET(
+ F,
+ function1(range(1, 3)),
+ fun(T1, T2) ->
+ F(T1) =< F(T2)
+ end
+ ).
+
+%%%%%%%%%%%%%%%
+%%% Helpers %%%
+%%%%%%%%%%%%%%%
+
+%% --------------------------------------------------------------------
+expect_error(Fn, Args) when is_function(Fn, length(Args))->
+ try
+ erlang:apply(Fn, Args)
+ of
+ _ -> false
+ catch
+ error:_ -> true;
+ _:_ -> false
+ end.
+
+%% --------------------------------------------------------------------
+check_appended([], []) ->
+ true;
+check_appended([[]|Ls], AL) ->
+ check_appended(Ls, AL);
+check_appended([L], AL) ->
+ L =:= AL;
+check_appended([[E1|L]|Ls], [E2|AL]) ->
+ E1 =:= E2 andalso
+ check_appended([L|Ls], AL);
+check_appended(_Ls, _AL) ->
+ false.
+
+%% --------------------------------------------------------------------
+check_deleted(E, [E|L], DL) ->
+ L =:= DL;
+check_deleted(E, [_|L], [_|DL]) ->
+ check_deleted(E, L, DL);
+check_deleted(_E, [], []) ->
+ true;
+check_deleted(_E, _L, _DL) ->
+ false.
+
+%% --------------------------------------------------------------------
+check_joined(Sep, [E|L], [E, Sep|JL]) ->
+ check_joined(Sep, L, JL);
+check_joined(_Sep, [E], [E]) ->
+ true;
+check_joined(_Sep, [], []) ->
+ true;
+check_joined(_Sep, _L, _JL) ->
+ false.
+
+%% --------------------------------------------------------------------
+check_keydeleted(K, N, [E|L], KDL) when element(N, E) == K ->
+ L =:= KDL;
+check_keydeleted(K, N, [_|L], [_|KDL]) ->
+ check_keydeleted(K, N, L, KDL);
+check_keydeleted(_K, _N, _L, _KDL) ->
+ false.
+
+%% --------------------------------------------------------------------
+check_keyreplaced(K, N, R, [E1|L], [E2|KRL]) when element(N, E1) == K ->
+ E2 =:= R andalso L =:= KRL;
+check_keyreplaced(K, N, R, [_|L], [_|KRL]) ->
+ check_keyreplaced(K, N, R, L, KRL);
+check_keyreplaced(_K, _N, _R, _L, _KRL) ->
+ false.
+
+%% --------------------------------------------------------------------
+check_merged(Ls, ML) ->
+ check_merged(fun erlang:'=<'/2, Ls, ML).
+
+check_merged(Fn, [[]|Ls], ML) ->
+ check_merged(Fn, Ls, ML);
+check_merged(_Fn, [], ML) ->
+ ML =:= [];
+check_merged(_Fn, [L], ML) ->
+ ML =:= L;
+check_merged(Fn, Ls, [E|ML]) ->
+ case find_in_heads(Fn, E, Ls) of
+ {true, Ls1} ->
+ check_merged(Fn, Ls1, ML);
+ false ->
+ false
+ end;
+check_merged(_Fn, _Ls, _ML) ->
+ false.
+
+find_in_heads(Fn, E, Ls) ->
+ find_in_heads(Fn, E, Ls, []).
+
+find_in_heads(Fn, E, [[]|Ls], Seen) ->
+ find_in_heads(Fn, E, Ls, Seen);
+find_in_heads(Fn, E, [[E1|LRest]=L|Ls], Seen) ->
+ case Fn(E, E1) andalso Fn(E1, E) of
+ true ->
+ {true, lists:reverse(Seen, [LRest|Ls])};
+ false ->
+ find_in_heads(Fn, E, Ls, [L|Seen])
+ end;
+find_in_heads(_Fn, _E, _Ls, _Seen) ->
+ false.
+
+%% --------------------------------------------------------------------
+check_partitioned(Pred, [E|L], P1, P2) ->
+ case {Pred(E), P1, P2} of
+ {true, [E|Rest], _} ->
+ check_partitioned(Pred, L, Rest, P2);
+ {false, _, [E|Rest]} ->
+ check_partitioned(Pred, L, P1, Rest);
+ _ ->
+ false
+ end;
+check_partitioned(_Pred, [], [], []) ->
+ true;
+check_partitioned(_Pred, _L, _P1, _P2) ->
+ false.
+
+%% --------------------------------------------------------------------
+check_reversed(L1, L2) ->
+ check_reversed(L1, L2, []).
+
+check_reversed(L1, L2, Tail) ->
+ check_reversed1(L1, L2) =:= Tail.
+
+check_reversed1([], L2) ->
+ L2;
+check_reversed1([E|L1], L2) ->
+ case check_reversed1(L1, L2) of
+ [E|L2Rest] -> L2Rest;
+ _ -> false
+ end.
+
+%% --------------------------------------------------------------------
+check_seq([F|Seq], F, T, S) ->
+ check_seq(Seq, F + S, T, S);
+check_seq([], F, T, S) when S >= 0 ->
+ F >= T;
+check_seq([], F, T, S) when S < 0 ->
+ F =< T;
+check_seq(_Seq, _F, _T, _S) ->
+ false.
+
+%% --------------------------------------------------------------------
+check_sorted(L, Sorted) ->
+ check_sorted(fun erlang:'=<'/2, L, Sorted).
+
+check_sorted(SortFun, L, Sorted) ->
+ ExpElems = count_elems(L),
+ check_sorted(SortFun, Sorted, ExpElems, #{}).
+
+check_sorted(_SortFun, [], ExpElems, FoundElems) ->
+ ExpElems =:= FoundElems;
+check_sorted(SortFun, [E], ExpElems, FoundElems) ->
+ maps:is_key(E, ExpElems) andalso
+ check_sorted(SortFun, [], ExpElems, maps:update_with(E, fun(Cnt) -> Cnt + 1 end, 1, FoundElems));
+check_sorted(SortFun, [E1|[E2|_]=L], ExpElems, FoundElems) ->
+ SortFun(E1, E2) andalso
+ maps:is_key(E1, ExpElems) andalso
+ check_sorted(SortFun, L, ExpElems, maps:update_with(E1, fun(Cnt) -> Cnt + 1 end, 1, FoundElems));
+check_sorted(_SortFun, _L, _ExpElems, _FoundElems) ->
+ false.
+
+count_elems(L) ->
+ count_elems(L, #{}).
+
+count_elems([E|Es], Acc) ->
+ count_elems(Es, maps:update_with(E, fun(Cnt) -> Cnt + 1 end, 1, Acc));
+count_elems([], Acc) ->
+ Acc.
+
+%% --------------------------------------------------------------------
+check_splitwithed(Pred, [E|L], [E|P1], P2) ->
+ Pred(E) andalso
+ check_splitwithed(Pred, L, P1, P2);
+check_splitwithed(Pred, [E|_]=L, [], P2) ->
+ not Pred(E) andalso L =:= P2;
+check_splitwithed(_Pred, [], [], []) ->
+ true;
+check_splitwithed(_Pred, _L, _P1, _P2) ->
+ false.
+
+%% --------------------------------------------------------------------
+check_umerged(Ls, ML) ->
+ check_umerged(fun erlang:'=<'/2, Ls, ML).
+
+check_umerged(Fn, [[]|Ls], ML) ->
+ check_umerged(Fn, Ls, ML);
+check_umerged(_Fn, [L], ML) ->
+ ML =:= L;
+check_umerged(_Fn, [], ML) ->
+ ML =:= [];
+check_umerged(Fn, Ls, [E|ML]) ->
+ case find_and_remove_from_heads(Fn, E, Ls) of
+ {true, Ls1} ->
+ check_umerged(Fn, Ls1, ML);
+ false ->
+ false
+ end;
+check_umerged(_Fn, _Ls, _ML) ->
+ false.
+
+find_and_remove_from_heads(Fn, E, Ls) ->
+ find_and_remove_from_heads(false, Fn, E, Ls, []).
+
+find_and_remove_from_heads(Found, Fn, E, [[]|Ls], Seen) ->
+ find_and_remove_from_heads(Found, Fn, E, Ls, Seen);
+find_and_remove_from_heads(false, _Fn, _E, [], _Seen) ->
+ false;
+find_and_remove_from_heads(true, _Fn, _E, [], Seen) ->
+ {true, lists:reverse(Seen)};
+find_and_remove_from_heads(Found, Fn, E, [[E1|LRest]=L|Ls], Seen) ->
+ case Fn(E, E1) andalso Fn(E1, E) of
+ true ->
+ find_and_remove_from_heads(true, Fn, E, Ls, [LRest|Seen]);
+ false ->
+ find_and_remove_from_heads(Found, Fn, E, Ls, [L|Seen])
+ end.
+
+%% --------------------------------------------------------------------
+check_uniqed(L, UL) ->
+ check_uniqed(fun(X) -> X end, L, UL).
+
+check_uniqed(Fn, L, UL) ->
+ check_uniqed1(Fn, L, UL, sets:new([{version, 2}])).
+
+check_uniqed1(Fn, [E|L], [], Seen) ->
+ sets:is_element(Fn(E), Seen) andalso
+ check_uniqed1(Fn, L, [], Seen);
+check_uniqed1(Fn, [E1|L], [E2|URest]=U, Seen) ->
+ X1 = Fn(E1),
+ X2 = Fn(E2),
+ case sets:is_element(X1, Seen) of
+ true ->
+ X1 =/= X2 andalso
+ check_uniqed1(Fn, L, U, Seen);
+ false ->
+ X1 =:= X2 andalso
+ check_uniqed1(Fn, L, URest, sets:add_element(X1, Seen))
+ end;
+check_uniqed1(_Fn, [], [], _Seen) ->
+ true;
+check_uniqed1(_Fn, _L, _UL, _Seen) ->
+ false.
+
+%% --------------------------------------------------------------------
+check_usorted(L, Sorted) ->
+ check_usorted(fun erlang:'=<'/2, L, Sorted).
+
+check_usorted(SortFun, L, Sorted) ->
+ ExpElems = ucount_elems(SortFun, L),
+ check_sorted(SortFun, Sorted, ExpElems, #{}).
+
+ucount_elems(SortFun, L) ->
+ ucount_elems(SortFun, L, #{}).
+
+ucount_elems(SortFun, [E|Es], Acc) ->
+ K = ufind_key(SortFun, E, maps:keys(Acc)),
+ ucount_elems(SortFun, Es, maps:put(K, 1, Acc));
+ucount_elems(_SortFun, [], Acc) ->
+ Acc.
+
+ufind_key(SortFun, E, [K|Keys]) ->
+ case SortFun(E, K) andalso SortFun(K, E) of
+ true ->
+ K;
+ false ->
+ ufind_key(SortFun, E, Keys)
+ end;
+ufind_key(_SortFun, E, []) ->
+ E.