aboutsummaryrefslogtreecommitdiffstats
path: root/lib/stdlib/test/select_SUITE.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/stdlib/test/select_SUITE.erl')
-rw-r--r--lib/stdlib/test/select_SUITE.erl804
1 files changed, 804 insertions, 0 deletions
diff --git a/lib/stdlib/test/select_SUITE.erl b/lib/stdlib/test/select_SUITE.erl
new file mode 100644
index 0000000000..54664fbb00
--- /dev/null
+++ b/lib/stdlib/test/select_SUITE.erl
@@ -0,0 +1,804 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2000-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(select_SUITE).
+-author('[email protected]').
+
+-export([test/0]).
+
+%%
+%% Define to run outside of test server
+%%
+%%-define(STANDALONE,1).
+
+%%
+%% Define for debug output
+%%
+%%-define(debug,1).
+
+-ifdef(STANDALONE).
+-define(config(A,B),config(A,B)).
+-export([config/2]).
+-define(fmt(A,B),io:format(A,B)).
+-else.
+-include("test_server.hrl").
+-define(fmt(A,B),test_server:format(A,B)).
+-endif.
+
+-ifdef(debug).
+-ifdef(STANDALONE).
+-define(line, erlang:display({?MODULE,?LINE}), ).
+-endif.
+-define(dbgformat(A,B),io:format(A,B)).
+-else.
+-ifdef(STANDALONE).
+-define(line, noop, ).
+-endif.
+-define(dbgformat(A,B),noop).
+-endif.
+
+-ifdef(STANDALONE).
+config(priv_dir,_) ->
+ ".".
+-else.
+%% When run in test server.
+-export([all/1,select_test/1,init_per_testcase/2, fin_per_testcase/2,
+ return_values/1]).
+
+init_per_testcase(_Case, Config) when is_list(Config) ->
+ ?line Dog=test_server:timetrap(test_server:seconds(1200)),
+ [{watchdog, Dog}|Config].
+
+fin_per_testcase(_Case, Config) ->
+ Dog=?config(watchdog, Config),
+ test_server:timetrap_cancel(Dog),
+ ok.
+
+all(doc) ->
+ ["Test ets:select"];
+all(suite) ->
+ [return_values, select_test].
+
+select_test(suite) ->
+ [];
+select_test(doc) ->
+ ["Tests select in numerous ways"];
+select_test(Config) when list(Config) ->
+ do_test(Config).
+
+return_values(suite) ->
+ [];
+return_values(doc) ->
+ ["Tests return values in specific situations for select/3 and select/1"];
+return_values(Config) when list(Config) ->
+ do_return_values().
+
+-endif.
+
+
+table_factor({dets,_}) ->
+ 1;
+table_factor({ets,_}) ->
+ 100.
+
+gen_dets_filename(Config,N) ->
+ filename:join(?config(priv_dir,Config),
+ "testdets_" ++ integer_to_list(N) ++ ".dets").
+
+create_tables(Config) ->
+ Hash = ets:new(xxx, []),
+ Tree = ets:new(yyy, [ordered_set]),
+ Bag = ets:new(yyy, [bag]),
+ DBag = ets:new(yyy, [duplicate_bag]),
+ F1 = gen_dets_filename(Config,1),
+ (catch file:delete(F1)),
+ {ok,DetsPlain} = dets:open_file(testdets_1,
+ [{file, F1}]),
+ F3 = gen_dets_filename(Config,3),
+ (catch file:delete(F3)),
+ {ok,DetsBag} = dets:open_file(testdets_3,
+ [{file, F3},{type, bag}]),
+ F4 = gen_dets_filename(Config,4),
+ (catch file:delete(F4)),
+ {ok,DetsDBag} = dets:open_file(testdets_4,
+ [{file, F4},{type, duplicate_bag}]),
+ [{ets,Hash}, {ets,Tree}, {ets,Bag}, {ets,DBag},
+ {dets, DetsPlain}, {dets, DetsBag}, {dets, DetsDBag}].
+
+
+gen_key(N,list) ->
+ [N,N+1,N+2];
+gen_key(N,tuple) ->
+ {N,N+1,N+2};
+gen_key(N,complex) ->
+ {[N,N+1],N+2}.
+
+gen_fun(N) ->
+ fun() ->
+ N
+ end.
+
+gen_bin(N) ->
+ list_to_binary(integer_to_list(N)).
+
+gen_object(N,Type) ->
+ L = integer_to_list(N),
+ A = list_to_atom("a" ++ L),
+ {gen_key(N,Type), L, A, gen_fun(N), gen_bin(N)}.
+gen_object1(N,Type) ->
+ L = integer_to_list(N),
+ A = list_to_atom("a" ++ L),
+ {gen_key(N,Type), A, L, gen_fun(N), gen_bin(N)}.
+
+fill_table(_,0,_) ->
+ ok;
+fill_table({Mod,Tab},N,Type) ->
+ Obj1 = gen_object1(N,Type),
+ Obj = gen_object(N,Type),
+ Mod:insert(Tab, Obj1),
+ case Mod:info(Tab,type) of
+ bag ->
+ Mod:insert(Tab, Obj);
+ duplicate_bag ->
+ Mod:insert(Tab, Obj),
+ Mod:insert(Tab, Obj1);
+ _ ->
+ ok
+ end,
+ fill_table({Mod,Tab},N-1,Type).
+
+table_size(Tab) ->
+ 15 *table_factor(Tab).
+
+build_tables(Config,Type) ->
+ L = create_tables(Config),
+ ?dbgformat("Tables: ~p~n",[L]),
+ lists:foreach(fun(TD) ->
+ fill_table(TD,table_size(TD),Type)
+ end,
+ L),
+ L.
+
+destroy_tables([]) ->
+ ok;
+destroy_tables([{ets,Tab}|T]) ->
+ ets:delete(Tab),
+ destroy_tables(T);
+destroy_tables([{dets,Tab}|T]) ->
+ dets:close(Tab),
+ destroy_tables(T).
+
+
+init_random(Config) ->
+ WriteDir = ReadDir = ?config(priv_dir,Config),
+ (catch file:make_dir(WriteDir)),
+ Seed = case file:consult(filename:join([ReadDir,
+ "preset_random_seed2.txt"])) of
+ {ok,[X]} ->
+ X;
+ _ ->
+ {A,B,C} = erlang:now(),
+ random:seed(A,B,C),
+ get(random_seed)
+ end,
+ put(random_seed,Seed),
+ {ok, F} = file:open(filename:join([WriteDir, "last_random_seed2.txt"]),
+ [write]),
+ io:format(F,"~p. ~n",[Seed]),
+ file:close(F),
+ ok.
+
+create_random_key(N,Type) ->
+ gen_key(random:uniform(N),Type).
+
+create_pb_key(N,list) ->
+ X = random:uniform(N),
+ case random:uniform(4) of
+ 3 -> {[X, X+1, '_'], fun([Z,Z1,P1]) ->
+ [Z,Z1,P1] =:= [X,X+1,P1] end};
+ 2 -> {[X, '_', '_'], fun([Z,P1,P2]) -> [Z,P1,P2] =:= [X,P1,P2] end};
+ 1 -> {[X, X+1, '$1'], fun([Z,Z1,P1]) ->
+ [Z,Z1,P1] =:= [X,X+1,P1] end};
+ _ -> {[X, '$1', '$2'], fun([Z,P1,P2]) -> [Z,P1,P2] =:= [X,P1,P2] end}
+ end;
+create_pb_key(N, tuple) ->
+ X = random:uniform(N),
+ case random:uniform(2) of
+ 1 -> {{X, X+1, '$1'},fun({Z,Z1,P1}) -> {Z,Z1,P1} =:= {X,X+1,P1} end};
+ _ -> {{X, '$1', '$2'},fun({Z,P1,P2}) -> {Z,P1,P2} =:= {X,P1,P2} end}
+ end;
+create_pb_key(N, complex) ->
+ X = random:uniform(N),
+ case random:uniform(2) of
+ 1 -> {{[X, X+1], '$1'}, fun({[Z,Z1],P1}) ->
+ {[Z,Z1],P1} =:= {[X,X+1],P1} end};
+ _ -> {{[X, '$1'], '$2'},fun({[Z,P1],P2}) ->
+ {[Z,P1],P2} =:= {[X,P1],P2} end}
+ end.
+table_foldl(_Fun, Acc,{_Mod,_Tab},'$end_of_table') ->
+ Acc;
+table_foldl(Fun, Acc,{Mod,Tab},Key) ->
+ Objs = Mod:lookup(Tab,Key),
+ Acc2 = lists:foldl(Fun,Acc,Objs),
+ ?dbgformat("Objs: ~p, Acc2: ~p~n",[Objs,Acc2]),
+ table_foldl(Fun, Acc2, {Mod,Tab}, Mod:next(Tab,Key)).
+table_foldl(Fun, Acc,{Mod,Tab}) ->
+ table_foldl(Fun, Acc,{Mod,Tab},Mod:first(Tab)).
+
+chunked_select(Mod,Tab,MS,0) ->
+ Mod:select(Tab,MS);
+chunked_select(Mod,Tab,MS,Chunk) when Chunk > 0->
+ do_chunk_select(Mod, Mod:select(Tab,MS,Chunk),[]);
+chunked_select(Mod,Tab,MS,Chunk) when Chunk < 0->
+ case Mod of
+ ets ->
+ do_chunk_select_reverse(Mod,
+ Mod:select_reverse(Tab,MS,-Chunk),[]);
+ _ ->
+ chunked_select(Mod,Tab,MS,-Chunk)
+ end.
+
+
+do_chunk_select_reverse(_Mod, '$end_of_table',Acc) ->
+ %% OK, all this reversing is only needed for ordered_set, but
+ %% this is only testcases, right?
+ erlang:display(did_chunked_select_reverse),
+ Acc;
+do_chunk_select_reverse(Mod, {L,C},Acc) ->
+ NewAcc = lists:reverse(L)++Acc,
+ do_chunk_select(Mod, Mod:select(C), NewAcc).
+
+do_chunk_select(_Mod, '$end_of_table',Acc) ->
+ %% OK, all this reversing is only needed for ordered_set, but
+ %% this is only testcases, right?
+ lists:reverse(Acc);
+do_chunk_select(Mod, {L,C},Acc) ->
+ NewAcc = lists:reverse(L)++Acc,
+ do_chunk_select(Mod, Mod:select(C), NewAcc).
+
+cmp_ms_to_fun({Mod,Tab}, MS, Fun1, Fun2) ->
+ cmp_ms_to_fun({Mod,Tab}, MS, Fun1, Fun2, 0).
+
+cmp_ms_to_fun({Mod,Tab}, MS, Fun1, Fun2, ChunkSize) ->
+ MSRes = lists:sort(chunked_select(Mod,Tab,MS,ChunkSize)),
+ FunRes0 = table_foldl(Fun1,[],{Mod,Tab}),
+ FunRes = case Fun2 of
+ F when function(F) ->
+ FunRes1 = table_foldl(F,[],{Mod,Tab}),
+ lists:merge(FunRes0,FunRes1);
+ [] ->
+ lists:sort(FunRes0)
+ end,
+ case MSRes =:= FunRes of
+ true ->
+ true;
+ false ->
+ ?fmt("Match_spec result differs from fun result:~n",[]),
+ ?fmt("Parameters: ~p,~p,~p,~p~n",
+ [{Mod,Tab}, MS, Fun1, Fun2]),
+ ?fmt("Match_spec Result: ~p~n", [MSRes]),
+ ?fmt("Fun Result: ~p~n", [FunRes]),
+ Info = (catch Mod:info(Tab)),
+ ?fmt("Table info:~p~n", [Info]),
+ {'EXIT', {hej, ST}} = (catch erlang:error(hej)),
+ ?fmt("Stack backtrace: ~p~n", [ST]),
+ erlang:error(badmatch)
+ end.
+
+do_n(0,_) -> ok;
+do_n(N,Fun) ->
+ Fun(),
+ do_n(N-1,Fun).
+
+%%
+%% We want some misses too, so pretend the tables are slightly
+%% larger than they really are.
+%%
+num_els(Tab) ->
+ 16 * table_factor(Tab).
+
+
+test() ->
+ do_return_values(),
+ do_test([]).
+
+do_test(Config) ->
+ init_random(Config),
+ whitebox(),
+ lists:foreach(fun(Type) ->
+ Tabs = build_tables(Config,Type),
+ basic_key(Tabs,Type),
+ ?fmt("basic_key done for type ~w~n",[Type]),
+ basic_pb_key(Tabs,Type),
+ ?fmt("basic_pb_key done for type ~w~n",[Type]),
+ double_pb_key(Tabs,Type),
+ ?fmt("double_pb_key done for type ~w~n",[Type]),
+ multi_key(Tabs,Type),
+ ?fmt("multi_key done for type ~w~n",[Type]),
+ multi_mixed_key(Tabs,Type),
+ ?fmt("multi_mixed_key done for type ~w~n",
+ [Type]),
+ destroy_tables(Tabs)
+ end,
+ [tuple, list, complex]),
+ ok.
+
+basic_key(Tabs,Type) ->
+ Fun = fun() ->
+ lists:map(fun(Tab) ->
+ ?line Key =
+ create_random_key(num_els(Tab),Type),
+ ?line MS =
+ [{{Key,'_','_','_','_'},[],['$_']}],
+ MF = fun({Key0,A,B,F,Bi},Acc) ->
+ case Key =:= Key0 of
+ true ->
+ [{Key0,A,B,F,Bi} |
+ Acc];
+ _ ->
+ Acc
+ end
+ end,
+ ?line cmp_ms_to_fun(Tab,MS,MF,[])
+ end,
+ Tabs)
+ end,
+ ?line do_n(50,Fun),
+ ok.
+
+basic_pb_key(Tabs,Type) ->
+ InnerFun = fun(Tab) ->
+ ?line {Key,KeyFun} =
+ create_pb_key(num_els(Tab),Type),
+ ?line MS = [{{Key,'_','_','_','_'},[],['$_']}],
+ MF = fun({Key0,A,B,F,Bi},Acc) ->
+ case KeyFun(Key0) of
+ true ->
+ [{Key0,A,B,F,Bi} |
+ Acc];
+ _ ->
+ Acc
+ end
+ end,
+ ?line cmp_ms_to_fun(Tab,MS,MF,[])
+ end,
+ ?line {Etses, Detses} = split_by_type(Tabs),
+
+ ?line FunEts = fun() ->
+ ?line lists:foreach(InnerFun,
+ Etses)
+ end,
+ ?line FunDets = fun() ->
+ ?line lists:foreach(InnerFun,
+ Detses)
+ end,
+ ?line do_n(table_factor(hd(Etses)) div 2,FunEts),
+ ?line do_n(10,FunDets),
+ ok.
+
+double_pb_key(Tabs,Type) ->
+ InnerFun = fun(Tab) ->
+ ?line {KeyA,KeyFunA} =
+ create_pb_key(num_els(Tab),Type),
+ ?line {KeyB,KeyFunB} =
+ create_pb_key(num_els(Tab),Type),
+ MS = [{{KeyA,'_','_','_','_'},[],['$_']},
+ {{KeyB,'_','_','_','_'},[],['$_']}],
+ ?dbgformat("Tab: ~p, MS: ~p~n",
+ [Tab,MS]),
+ MF = fun({Key0,A,B,F,Bi},Acc) ->
+ case KeyFunA(Key0) of
+ true ->
+ ?dbgformat
+ ("FunMatched:"
+ " ~p~n",
+ [{Key0,A,
+ B,F,Bi}]),
+ [{Key0,A,B,F,Bi} |
+ Acc];
+ _ ->
+ case KeyFunB(Key0) of
+ true ->
+ ?dbgformat
+ ("Fun"
+ "Matched:"
+ " ~p~n",
+ [{Key0,A,
+ B,F,
+ Bi}]),
+ [{Key0,A,B,
+ F,Bi} |
+ Acc];
+ _ ->
+ Acc
+ end
+ end
+ end,
+ ?line cmp_ms_to_fun(Tab,MS,MF,[])
+ end,
+ ?line {Etses, Detses} = split_by_type(Tabs),
+
+ ?line FunEts = fun() ->
+ ?line lists:foreach(InnerFun,
+ Etses)
+ end,
+ ?line FunDets = fun() ->
+ ?line lists:foreach(InnerFun,
+ Detses)
+ end,
+ ?line do_n(table_factor(hd(Etses)) div 2,FunEts),
+ ?line do_n(10,FunDets),
+ ok.
+
+
+multi_key(Tabs,Type) ->
+ Fun = fun() ->
+ lists:map(fun(Tab) ->
+ ?line KeyA =
+ create_random_key(num_els(Tab),Type),
+ ?line KeyB =
+ create_random_key(num_els(Tab),Type),
+ ?line KeyC =
+ create_random_key(num_els(Tab),Type),
+ ?line KeyD =
+ create_random_key(num_els(Tab),Type),
+ ?line KeyE =
+ create_random_key(num_els(Tab),Type),
+ ?line KeyF =
+ create_random_key(num_els(Tab),Type),
+ ?line KeyG =
+ create_random_key(num_els(Tab),Type),
+ ?line KeyH =
+ create_random_key(num_els(Tab),Type),
+ ?line KeyI =
+ create_random_key(num_els(Tab),Type),
+ ?line KeyJ =
+ create_random_key(num_els(Tab),Type),
+ ?line KeyK =
+ create_random_key(num_els(Tab),Type),
+ ?line KeyL =
+ create_random_key(num_els(Tab),Type),
+
+ MS = [{{KeyA,'$1','_','$2','_'},[],
+ [{{'$1','$2'}}]},
+ {{KeyB,'$1','_','$2','_'},[],
+ [{{'$1','$2'}}]},
+ {{KeyC,'$1','_','$2','_'},[],
+ [{{'$1','$2'}}]},
+ {{KeyD,'$1','_','$2','_'},[],
+ [{{'$1','$2'}}]},
+ {{KeyE,'$1','_','$2','_'},[],
+ [{{'$1','$2'}}]},
+ {{KeyF,'$1','_','$2','_'},[],
+ [{{'$1','$2'}}]},
+ {{KeyG,'$1','_','$2','_'},[],
+ [{{'$1','$2'}}]},
+ {{KeyH,'$1','_','$2','_'},[],
+ [{{'$1','$2'}}]},
+ {{KeyI,'$1','_','$2','_'},[],
+ [{{'$1','$2'}}]},
+ {{KeyJ,'$1','_','$2','_'},[],
+ [{{'$1','$2'}}]},
+ {{KeyK,'$1','_','$2','_'},[],
+ [{{'$1','$2'}}]},
+ {{KeyL,'$1','_','$2','_'},[],
+ [{{'$1','$2'}}]}
+ ],
+ ?dbgformat("Tab: ~p, MS: ~p~n",
+ [Tab,MS]),
+ MF = fun({Key0,A,_B,F,_Bi},Acc) ->
+ case Key0 of
+ KeyA ->
+ [ {A,F} |
+ Acc];
+ KeyB ->
+ [ {A,F} |
+ Acc];
+ KeyC ->
+ [ {A,F} |
+ Acc];
+ KeyD ->
+ [ {A,F} |
+ Acc];
+ KeyE ->
+ [ {A,F} |
+ Acc];
+ KeyF ->
+ [ {A,F} |
+ Acc];
+ KeyG ->
+ [ {A,F} |
+ Acc];
+ KeyH ->
+ [ {A,F} |
+ Acc];
+ KeyI ->
+ [ {A,F} |
+ Acc];
+ KeyJ ->
+ [ {A,F} |
+ Acc];
+ KeyK ->
+ [ {A,F} |
+ Acc];
+ KeyL ->
+ [ {A,F} |
+ Acc];
+ _ ->
+ Acc
+ end
+ end,
+ ?line cmp_ms_to_fun(Tab,MS,MF,[])
+ end,
+ Tabs)
+ end,
+ ?line do_n(33,Fun),
+ ok.
+
+multi_mixed_key(Tabs,Type) ->
+ InnerFun = fun(Tab) ->
+ ?line KeyA =
+ create_random_key(num_els(Tab),Type),
+ ?line KeyB =
+ create_random_key(num_els(Tab),Type),
+ ?line KeyC =
+ create_random_key(num_els(Tab),Type),
+ ?line KeyD =
+ create_random_key(num_els(Tab),Type),
+ ?line {KeyE, FunE} =
+ create_pb_key(num_els(Tab),Type),
+ ?line KeyF =
+ create_random_key(num_els(Tab),Type),
+ ?line {KeyG, FunG} =
+ create_pb_key(num_els(Tab),Type),
+ ?line KeyH =
+ create_random_key(num_els(Tab),Type),
+ ?line KeyI =
+ create_random_key(num_els(Tab),Type),
+ ?line {KeyJ, FunJ} =
+ create_pb_key(num_els(Tab),Type),
+ ?line KeyK =
+ create_random_key(num_els(Tab),Type),
+ ?line KeyL =
+ create_random_key(num_els(Tab),Type),
+
+ MS = [{{KeyA,'$1','_','$2','_'},[],
+ [{{'$1','$2'}}]},
+ {{KeyB,'$1','_','$2','_'},[],
+ [{{'$1','$2'}}]},
+ {{KeyC,'$1','_','$2','_'},[],
+ [{{'$1','$2'}}]},
+ {{KeyD,'$1','_','$2','_'},[],
+ [{{'$1','$2'}}]},
+ {{KeyE,'$100','_','$200','_'},[],
+ [{{'$100','$200'}}]},
+ {{KeyF,'$1','_','$2','_'},[],
+ [{{'$1','$2'}}]},
+ {{KeyG,'$100','_','$200','_'},[],
+ [{{'$100','$200'}}]},
+ {{KeyH,'$1','_','$2','_'},[],
+ [{{'$1','$2'}}]},
+ {{KeyI,'$1','_','$2','_'},[],
+ [{{'$1','$2'}}]},
+ {{KeyJ,'$100','_','$200','_'},[],
+ [{{'$100','$200'}}]},
+ {{KeyK,'$1','_','$2','_'},[],
+ [{{'$1','$2'}}]},
+ {{KeyL,'$1','_','$2','_'},[],
+ [{{'$1','$2'}}]}
+ ],
+ ?dbgformat("Tab: ~p, MS: ~p~n",
+ [Tab,MS]),
+ MF = fun({Key0,A,_B,F,_Bi},Acc) ->
+ case Key0 of
+ KeyA ->
+ [ {A,F} |
+ Acc];
+ KeyB ->
+ [ {A,F} |
+ Acc];
+ KeyC ->
+ [ {A,F} |
+ Acc];
+ KeyD ->
+ [ {A,F} |
+ Acc];
+ KeyF ->
+ [ {A,F} |
+ Acc];
+ KeyH ->
+ [ {A,F} |
+ Acc];
+ KeyI ->
+ [ {A,F} |
+ Acc];
+ KeyK ->
+ [ {A,F} |
+ Acc];
+ KeyL ->
+ [ {A,F} |
+ Acc];
+ Else ->
+ case FunE(Else) or
+ FunG(Else) or
+ FunJ(Else) of
+ true ->
+ [ {A,F} |
+ Acc];
+ _ ->
+ Acc
+ end
+ end
+ end,
+ ?line cmp_ms_to_fun(Tab,MS,MF,[]),
+ ?line case Tab of
+ {ets,_} ->
+ ?line cmp_ms_to_fun(Tab,MS,MF,[],1),
+ ?line cmp_ms_to_fun(Tab,MS,MF,[],10),
+ ?line cmp_ms_to_fun(Tab,MS,MF,[],1000000),
+ ?line cmp_ms_to_fun(Tab,MS,MF,[],-1),
+ ?line cmp_ms_to_fun(Tab,MS,MF,[],-10),
+ ?line cmp_ms_to_fun(Tab,MS,MF,[],-1000000);
+ _ ->
+ ok
+ end
+ end,
+ ?line {Etses, Detses} = split_by_type(Tabs),
+
+ ?line FunEts = fun() ->
+ ?line lists:foreach(InnerFun,
+ Etses)
+ end,
+ ?line FunDets = fun() ->
+ ?line lists:foreach(InnerFun,
+ Detses)
+ end,
+ ?line do_n(table_factor(hd(Etses)) div 2,FunEts),
+ ?line do_n(table_factor(hd(Detses)) div 2,FunDets),
+ ok.
+
+
+split_by_type(List) ->
+ split_by_type(List,[],[]).
+split_by_type([],AccEts,AccDets) ->
+ {AccEts,AccDets};
+split_by_type([{dets,Tab}|T],AccEts,AccDets) ->
+ split_by_type(T,AccEts,[{dets,Tab}|AccDets]);
+split_by_type([{ets,Tab}|T],AccEts,AccDets) ->
+ split_by_type(T,[{ets,Tab}|AccEts],AccDets).
+
+whitebox() ->
+ ?line ets:new(xxx,[named_table, ordered_set]),
+ ?line ets:new(yyy,[named_table]),
+ ?line E = fun(0,_)->ok;
+ (N,F) ->
+ ?line ets:insert(xxx,{N,N rem 10}),
+ ?line ets:insert(yyy,{N,N rem 10}),
+ F(N-1,F)
+ end,
+ ?line E(10000,E),
+
+ ?line G = fun(F,C,A) ->
+ ?line case ets:select(C) of
+ {L,C2} ->
+ ?line F(F,C2,A+length(L));
+ '$end_of_table' ->
+ ?line A
+ end
+ end,
+ ?line H=fun({L,C}) ->
+ ?line G(G,C,length(L))
+ end,
+
+ ?line 1 = H(ets:select(xxx,[{{'$1','$2'},[{'<','$1',2}],['$_']}],7)),
+ ?line 10000 = H(ets:select(xxx,[{{'$1','$2'},[],['$_']}],1)),
+ ?line 1 = H(ets:select(yyy,[{{'$1','$2'},[{'<','$1',2}],['$_']}],7)),
+ ?line 10000 = H(ets:select(yyy,[{{'$1','$2'},[],['$_']}],1)),
+
+ ?line {[{5,5}],_} = ets:select(xxx,[{{5,'$2'},[],['$_']}],1),
+ ?line {[{5,5}],_} = ets:select(yyy,[{{5,'$2'},[],['$_']}],1),
+
+ ?line I = fun(_,0) ->
+ ok;
+ (I,N) ->
+ ?line 10000 =
+ H(ets:select(xxx,[{{'$1','$2'},[],['$_']}],N)),
+ I(I,N-1)
+ end,
+ ?line I(I,2000),
+ ?line J = fun(F,C,A) ->
+ ?line case ets:select(C) of
+ {L,C2} ->
+ ?line F(F,C2,lists:reverse(L)++A);
+ '$end_of_table' ->
+ ?line lists:reverse(A)
+ end
+ end,
+ ?line K = fun({L,C}) ->
+ ?line J(J,C,lists:reverse(L))
+ end,
+ ?line M = fun(_, _, 0) ->
+ ok;
+ (F, What, N) ->
+ ?line What =
+ K(ets:select(xxx,[{{'$1','$2'},[],['$_']}],N)),
+ F(F, What, N-1)
+ end,
+ ?line N = fun(HM) ->
+ ?line What = ets:select(xxx,[{{'$1','$2'},[],['$_']}]),
+ ?line What = lists:sort(What),
+ M(M, What, HM)
+ end,
+ ?line N(2000),
+ ?line ets:delete(xxx),
+ ?line ets:delete(yyy).
+
+
+do_return_values() ->
+ ?line T = ets:new(xxx,[ordered_set]),
+ ?line U = ets:new(xxx,[]),
+ ?line '$end_of_table' = ets:select(T,[{'_',[],['$_']}],1),
+ ?line '$end_of_table' = ets:select(U,[{'_',[],['$_']}],1),
+ ?line ets:insert(T,{ett,1}),
+ ?line ets:insert(U,{ett,1}),
+ ?line {[{ett,1}],C1} = ets:select(T,[{'_',[],['$_']}],1),
+ ?line '$end_of_table' = ets:select(C1),
+ ?line {[{ett,1}],C2} = ets:select(U,[{'_',[],['$_']}],1),
+ ?line '$end_of_table' = ets:select(C2),
+ ?line {[{ett,1}],C3} = ets:select(T,[{'_',[],['$_']}],2),
+ ?line '$end_of_table' = ets:select(C3),
+ ?line {[{ett,1}],C4} = ets:select(U,[{'_',[],['$_']}],2),
+ ?line '$end_of_table' = ets:select(C4),
+ ?line E = fun(0,_)->ok;
+ (N,F) ->
+ ?line ets:insert(T,{N,N rem 10}),
+ ?line ets:insert(U,{N,N rem 10}),
+ F(N-1,F)
+ end,
+ ?line E(10000,E),
+ ?line '$end_of_table' = ets:select(T,[{{hej, hopp},[],['$_']}],1),
+ ?line '$end_of_table' = ets:select(U,[{{hej,hopp},[],['$_']}],1),
+ ?line {[{ett,1}],CC1} = ets:select(T,[{{'$1','_'},[{is_atom, '$1'}],
+ ['$_']}],1),
+ ?line '$end_of_table' = ets:select(CC1),
+ ?line {[{ett,1}],CC2} = ets:select(U,[{{'$1','_'},[{is_atom, '$1'}],
+ ['$_']}],1),
+ ?line '$end_of_table' = ets:select(CC2),
+ ?line {[{ett,1}],CC3} = ets:select(T,[{{'$1','_'},[{is_atom, '$1'}],
+ ['$_']}],2),
+ ?line '$end_of_table' = ets:select(CC3),
+ ?line {[{ett,1}],CC4} = ets:select(U,[{{'$1','_'},[{is_atom, '$1'}],
+ ['$_']}],2),
+ ?line '$end_of_table' = ets:select(CC4),
+ ?line ets:delete(T),
+ ?line ets:delete(U),
+ ?line V = ets:new(xxx,[{keypos, 4}]),
+ ?line X = ets:new(xxx,[ordered_set, {keypos, 4}]),
+ ?line ets:insert(V,{1,1,1,ett}),
+ ?line ets:insert(X,{1,1,1,ett}),
+ ?line '$end_of_table' = ets:select(V,[{{1,1,1},[],['$_']}],1),
+ ?line '$end_of_table' = ets:select(X,[{{1,1,1},[],['$_']}],1),
+ ?line ets:delete(V),
+ ?line ets:delete(X),
+ ok.
+
+
+
+
+