1: %%
    2: %% %CopyrightBegin%
    3: %%
    4: %% Copyright Ericsson AB 2000-2011. All Rights Reserved.
    5: %%
    6: %% The contents of this file are subject to the Erlang Public License,
    7: %% Version 1.1, (the "License"); you may not use this file except in
    8: %% compliance with the License. You should have received a copy of the
    9: %% Erlang Public License along with this software. If not, it can be
   10: %% retrieved online at http://www.erlang.org/.
   11: %%
   12: %% Software distributed under the License is distributed on an "AS IS"
   13: %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
   14: %% the License for the specific language governing rights and limitations
   15: %% under the License.
   16: %%
   17: %% %CopyrightEnd%
   18: %%
   19: 
   20: -module(select_SUITE).
   21: -author('pan@erix.ericsson.se').
   22: 
   23: -export([test/0]).
   24: 
   25: %%
   26: %% Define to run outside of test server
   27: %%
   28: %%-define(STANDALONE,1).
   29:  
   30: %%
   31: %% Define for debug output
   32: %%
   33: %%-define(debug,1).
   34:  
   35: -ifdef(STANDALONE).
   36: -define(config(A,B),config(A,B)).
   37: -export([config/2]).
   38: -define(fmt(A,B),io:format(A,B)).
   39: -else.
   40: -include_lib("test_server/include/test_server.hrl").
   41: -define(fmt(A,B),test_server:format(A,B)).
   42: -endif.
   43:  
   44: -ifdef(debug).
   45: -ifdef(STANDALONE).
   46: -define(line, erlang:display({?MODULE,?LINE}), ).
   47: -endif.
   48: -define(dbgformat(A,B),io:format(A,B)).
   49: -else.
   50: -ifdef(STANDALONE).
   51: -define(line, noop, ).
   52: -endif.
   53: -define(dbgformat(A,B),noop).
   54: -endif.
   55:  
   56: -ifdef(STANDALONE).
   57: config(priv_dir,_) ->
   58:     ".".
   59: -else.
   60: %% When run in test server.
   61: -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, 
   62: 	 init_per_group/2,end_per_group/2,select_test/1,
   63: 	 init_per_testcase/2, end_per_testcase/2, 
   64: 	 return_values/1]).
   65: 
   66: init_per_testcase(_Case, Config) when is_list(Config) ->
   67:     ?line Dog=test_server:timetrap(test_server:seconds(1200)),
   68:     [{watchdog, Dog}|Config].
   69: 
   70: end_per_testcase(_Case, Config) ->
   71:     Dog=?config(watchdog, Config),
   72:     test_server:timetrap_cancel(Dog),
   73:     ok.
   74: 
   75: suite() -> [{ct_hooks,[ts_install_cth]}].
   76: 
   77: all() -> 
   78:     [return_values, select_test].
   79: 
   80: groups() -> 
   81:     [].
   82: 
   83: init_per_suite(Config) ->
   84:     Config.
   85: 
   86: end_per_suite(_Config) ->
   87:     ok.
   88: 
   89: init_per_group(_GroupName, Config) ->
   90:     Config.
   91: 
   92: end_per_group(_GroupName, Config) ->
   93:     Config.
   94: 
   95: 
   96: select_test(suite) ->
   97:     [];
   98: select_test(doc) ->
   99:     ["Tests select in numerous ways"];
  100: select_test(Config) when is_list(Config) ->
  101:     do_test(Config).
  102: 
  103: return_values(suite) ->
  104:     [];
  105: return_values(doc) ->
  106:     ["Tests return values in specific situations for select/3 and select/1"];
  107: return_values(Config) when is_list(Config) ->
  108:     do_return_values().
  109: 
  110: -endif.
  111: 
  112: 
  113: table_factor({dets,_}) ->
  114:     1;
  115: table_factor({ets,_}) ->
  116:     100.
  117: 
  118: gen_dets_filename(Config,N) ->
  119:     filename:join(?config(priv_dir,Config),
  120: 		  "testdets_" ++ integer_to_list(N) ++ ".dets").
  121: 
  122: create_tables(Config) ->
  123:     Hash = ets:new(xxx, []), 
  124:     Tree = ets:new(yyy, [ordered_set]),
  125:     Bag = ets:new(yyy, [bag]),
  126:     DBag = ets:new(yyy, [duplicate_bag]),
  127:     F1 = gen_dets_filename(Config,1),
  128:     (catch file:delete(F1)),
  129:     {ok,DetsPlain} = dets:open_file(testdets_1,
  130: 			       [{file, F1}]),
  131:     F3 = gen_dets_filename(Config,3),
  132:     (catch file:delete(F3)),
  133:     {ok,DetsBag} = dets:open_file(testdets_3,
  134: 				     [{file, F3},{type, bag}]),
  135:     F4 = gen_dets_filename(Config,4),
  136:     (catch file:delete(F4)),
  137:     {ok,DetsDBag} = dets:open_file(testdets_4,
  138: 				     [{file, F4},{type, duplicate_bag}]),
  139:     [{ets,Hash}, {ets,Tree}, {ets,Bag}, {ets,DBag}, 
  140:      {dets, DetsPlain}, {dets, DetsBag}, {dets, DetsDBag}].
  141: 
  142: 
  143: gen_key(N,list) ->
  144:     [N,N+1,N+2];
  145: gen_key(N,tuple) ->
  146:     {N,N+1,N+2};
  147: gen_key(N,complex) ->
  148:     {[N,N+1],N+2}.
  149: 
  150: gen_fun(N) ->
  151:     fun() ->
  152: 	    N
  153:     end.
  154: 
  155: gen_bin(N) ->
  156:     list_to_binary(integer_to_list(N)).
  157: 
  158: gen_object(N,Type) ->
  159:     L = integer_to_list(N),
  160:     A = list_to_atom("a" ++ L),
  161:     {gen_key(N,Type), L, A, gen_fun(N), gen_bin(N)}.
  162: gen_object1(N,Type) ->
  163:     L = integer_to_list(N),
  164:     A = list_to_atom("a" ++ L),
  165:     {gen_key(N,Type), A, L, gen_fun(N), gen_bin(N)}.
  166: 
  167: fill_table(_,0,_) ->
  168:     ok;
  169: fill_table({Mod,Tab},N,Type) ->
  170:     Obj1 = gen_object1(N,Type),
  171:     Obj = gen_object(N,Type),
  172:     Mod:insert(Tab, Obj1),
  173:     case Mod:info(Tab,type) of
  174: 	bag ->
  175: 	    Mod:insert(Tab, Obj);
  176: 	duplicate_bag ->
  177: 	    Mod:insert(Tab, Obj),
  178: 	    Mod:insert(Tab, Obj1);
  179: 	_ ->
  180: 	    ok
  181:     end,
  182:     fill_table({Mod,Tab},N-1,Type).
  183: 
  184: table_size(Tab) ->
  185:     15 *table_factor(Tab).
  186: 
  187: build_tables(Config,Type) ->
  188:     L = create_tables(Config),
  189:     ?dbgformat("Tables: ~p~n",[L]),
  190:     lists:foreach(fun(TD) ->
  191: 		      fill_table(TD,table_size(TD),Type)
  192: 		  end,
  193: 		  L),
  194:     L.
  195: 
  196: destroy_tables([]) ->
  197:     ok;
  198: destroy_tables([{ets,Tab}|T]) ->
  199:     ets:delete(Tab),
  200:     destroy_tables(T);
  201: destroy_tables([{dets,Tab}|T]) ->
  202:     dets:close(Tab),
  203:     destroy_tables(T).
  204:     
  205: 
  206: init_random(Config) ->
  207:     WriteDir = ReadDir = ?config(priv_dir,Config),
  208:     (catch file:make_dir(WriteDir)),
  209:     Seed = case file:consult(filename:join([ReadDir, 
  210: 					    "preset_random_seed2.txt"])) of
  211: 	       {ok,[X]} ->
  212: 		   X;
  213: 	       _ ->
  214: 		   {A,B,C} = erlang:now(),
  215: 		   random:seed(A,B,C),
  216: 		   get(random_seed)
  217: 	   end,
  218:     put(random_seed,Seed),
  219:     {ok, F} = file:open(filename:join([WriteDir, "last_random_seed2.txt"]), 
  220: 			[write]),
  221:     io:format(F,"~p. ~n",[Seed]),
  222:     file:close(F),
  223:     ok.
  224: 
  225: create_random_key(N,Type) ->
  226:     gen_key(random:uniform(N),Type).
  227: 
  228: create_pb_key(N,list) ->
  229:     X = random:uniform(N),
  230:     case random:uniform(4) of
  231: 	3 -> {[X, X+1, '_'], fun([Z,Z1,P1]) ->  
  232: 				      [Z,Z1,P1] =:= [X,X+1,P1] end};
  233: 	2 -> {[X, '_', '_'], fun([Z,P1,P2]) ->  [Z,P1,P2] =:= [X,P1,P2] end};
  234: 	1 -> {[X, X+1, '$1'], fun([Z,Z1,P1]) ->  
  235: 				      [Z,Z1,P1] =:= [X,X+1,P1] end};
  236: 	_ -> {[X, '$1', '$2'], fun([Z,P1,P2]) ->  [Z,P1,P2] =:= [X,P1,P2] end}
  237:     end;
  238: create_pb_key(N, tuple) ->
  239:     X = random:uniform(N),
  240:     case random:uniform(2) of
  241: 	1 -> {{X, X+1, '$1'},fun({Z,Z1,P1}) ->  {Z,Z1,P1} =:= {X,X+1,P1} end};
  242: 	_ -> {{X, '$1', '$2'},fun({Z,P1,P2}) ->  {Z,P1,P2} =:= {X,P1,P2} end}
  243:     end;
  244: create_pb_key(N, complex) ->
  245:     X = random:uniform(N),
  246:     case random:uniform(2) of
  247: 	1 -> {{[X, X+1], '$1'}, fun({[Z,Z1],P1}) ->  
  248: 					{[Z,Z1],P1} =:= {[X,X+1],P1} end};
  249: 	_ -> {{[X, '$1'], '$2'},fun({[Z,P1],P2}) -> 
  250: 					{[Z,P1],P2} =:= {[X,P1],P2} end}
  251:     end.
  252: table_foldl(_Fun, Acc,{_Mod,_Tab},'$end_of_table') ->
  253:     Acc;
  254: table_foldl(Fun, Acc,{Mod,Tab},Key) ->
  255:     Objs = Mod:lookup(Tab,Key),
  256:     Acc2 = lists:foldl(Fun,Acc,Objs),
  257:     ?dbgformat("Objs: ~p, Acc2: ~p~n",[Objs,Acc2]),
  258:     table_foldl(Fun, Acc2, {Mod,Tab}, Mod:next(Tab,Key)).
  259: table_foldl(Fun, Acc,{Mod,Tab}) ->
  260:     table_foldl(Fun, Acc,{Mod,Tab},Mod:first(Tab)).
  261: 
  262: chunked_select(Mod,Tab,MS,0) ->
  263:     Mod:select(Tab,MS);
  264: chunked_select(Mod,Tab,MS,Chunk) when Chunk > 0->
  265:     do_chunk_select(Mod, Mod:select(Tab,MS,Chunk),[]);
  266: chunked_select(Mod,Tab,MS,Chunk) when Chunk < 0->
  267:     case Mod of
  268: 	ets ->
  269: 	    do_chunk_select_reverse(Mod, 
  270: 				    Mod:select_reverse(Tab,MS,-Chunk),[]);
  271: 	_ ->
  272: 	    chunked_select(Mod,Tab,MS,-Chunk)
  273:     end.
  274: 
  275: 
  276: do_chunk_select_reverse(_Mod, '$end_of_table',Acc) ->
  277:     %% OK, all this reversing is only needed for ordered_set, but
  278:     %% this is only testcases, right?
  279:     erlang:display(did_chunked_select_reverse),
  280:     Acc; 
  281: do_chunk_select_reverse(Mod, {L,C},Acc) ->
  282:     NewAcc = lists:reverse(L)++Acc,
  283:     do_chunk_select(Mod, Mod:select(C), NewAcc).
  284: 
  285: do_chunk_select(_Mod, '$end_of_table',Acc) ->
  286:     %% OK, all this reversing is only needed for ordered_set, but
  287:     %% this is only testcases, right?
  288:     lists:reverse(Acc); 
  289: do_chunk_select(Mod, {L,C},Acc) ->
  290:     NewAcc = lists:reverse(L)++Acc,
  291:     do_chunk_select(Mod, Mod:select(C), NewAcc).
  292: 
  293: cmp_ms_to_fun({Mod,Tab}, MS, Fun1, Fun2) ->
  294:     cmp_ms_to_fun({Mod,Tab}, MS, Fun1, Fun2, 0).
  295: 
  296: cmp_ms_to_fun({Mod,Tab}, MS, Fun1, Fun2, ChunkSize) ->
  297:     MSRes = lists:sort(chunked_select(Mod,Tab,MS,ChunkSize)),
  298:     FunRes0 = table_foldl(Fun1,[],{Mod,Tab}),
  299:     FunRes = case Fun2 of
  300: 		 F when is_function(F) ->
  301: 		     FunRes1 = table_foldl(F,[],{Mod,Tab}),
  302: 		     lists:merge(FunRes0,FunRes1);
  303: 		 [] ->
  304: 		     lists:sort(FunRes0)
  305: 	     end,
  306:     case MSRes =:= FunRes of
  307: 	true ->
  308: 	    true;
  309: 	false ->
  310: 	    ?fmt("Match_spec result differs from fun result:~n",[]),
  311: 	    ?fmt("Parameters: ~p,~p,~p,~p~n", 
  312: 		      [{Mod,Tab}, MS, Fun1, Fun2]),
  313: 	    ?fmt("Match_spec Result: ~p~n", [MSRes]),
  314: 	    ?fmt("Fun Result: ~p~n", [FunRes]),
  315: 	    Info = (catch Mod:info(Tab)),
  316: 	    ?fmt("Table info:~p~n", [Info]),
  317: 	    {'EXIT', {hej, ST}} = (catch erlang:error(hej)),
  318: 	    ?fmt("Stack backtrace: ~p~n", [ST]),
  319: 	    erlang:error(badmatch)
  320:     end.
  321: 
  322: do_n(0,_) -> ok;
  323: do_n(N,Fun) ->
  324:     Fun(),
  325:     do_n(N-1,Fun).
  326: 
  327: %%
  328: %% We want some misses too, so pretend the tables are slightly
  329: %% larger than they really are.
  330: %%
  331: num_els(Tab) ->
  332:     16 * table_factor(Tab).
  333: 
  334: 
  335: test() ->
  336:     do_return_values(),
  337:     do_test([]).
  338: 
  339: do_test(Config) ->
  340:     init_random(Config),
  341:     whitebox(),
  342:     lists:foreach(fun(Type) ->
  343: 			  Tabs = build_tables(Config,Type),
  344: 			  basic_key(Tabs,Type),
  345: 			  ?fmt("basic_key done for type ~w~n",[Type]),
  346: 			  basic_pb_key(Tabs,Type),
  347: 			  ?fmt("basic_pb_key done for type ~w~n",[Type]),
  348: 			  double_pb_key(Tabs,Type),
  349: 			  ?fmt("double_pb_key done for type ~w~n",[Type]),
  350: 			  multi_key(Tabs,Type),
  351: 			  ?fmt("multi_key done for type ~w~n",[Type]),
  352: 			  multi_mixed_key(Tabs,Type),
  353: 			  ?fmt("multi_mixed_key done for type ~w~n",
  354: 				    [Type]),
  355: 			  destroy_tables(Tabs)
  356: 		  end,
  357: 		  [tuple, list, complex]),
  358:     ok.
  359:     
  360: basic_key(Tabs,Type) ->
  361:     Fun = fun() ->
  362: 		  lists:map(fun(Tab) ->
  363: 				    ?line Key = 
  364: 					create_random_key(num_els(Tab),Type),
  365: 				    ?line  MS = 
  366: 					[{{Key,'_','_','_','_'},[],['$_']}],
  367: 				    MF = fun({Key0,A,B,F,Bi},Acc) ->
  368: 						 case Key =:= Key0 of
  369: 						     true ->
  370: 							 [{Key0,A,B,F,Bi} | 
  371: 							  Acc];
  372: 						     _ ->
  373: 							 Acc
  374: 						 end
  375: 					 end,
  376: 				    ?line cmp_ms_to_fun(Tab,MS,MF,[])
  377: 			    end,
  378: 			    Tabs)
  379: 	  end,
  380:     ?line do_n(50,Fun),
  381:     ok.
  382: 		  
  383: basic_pb_key(Tabs,Type) ->
  384:     InnerFun = fun(Tab) ->
  385: 		       ?line {Key,KeyFun}  = 
  386: 			   create_pb_key(num_els(Tab),Type),
  387: 		       ?line MS = [{{Key,'_','_','_','_'},[],['$_']}],
  388: 		       MF = fun({Key0,A,B,F,Bi},Acc) ->
  389: 				    case KeyFun(Key0) of
  390: 					true ->
  391: 					    [{Key0,A,B,F,Bi} | 
  392: 					     Acc];
  393: 					_ ->
  394: 					    Acc
  395: 				    end
  396: 			    end,
  397: 		       ?line cmp_ms_to_fun(Tab,MS,MF,[])
  398: 	       end,
  399:     ?line {Etses, Detses} = split_by_type(Tabs),
  400:     
  401:     ?line FunEts = fun() ->
  402: 			   ?line lists:foreach(InnerFun,
  403: 					       Etses)
  404: 		   end,
  405:     ?line FunDets = fun() ->
  406: 			    ?line lists:foreach(InnerFun,
  407: 						Detses)
  408: 		    end,
  409:     ?line do_n(table_factor(hd(Etses)) div 2,FunEts),
  410:     ?line do_n(10,FunDets),
  411:     ok.
  412: 		  
  413: double_pb_key(Tabs,Type) ->
  414:     InnerFun = fun(Tab) ->
  415: 		       ?line {KeyA,KeyFunA}  = 
  416: 			   create_pb_key(num_els(Tab),Type),
  417: 		       ?line {KeyB,KeyFunB}  = 
  418: 			   create_pb_key(num_els(Tab),Type),
  419: 		       MS = [{{KeyA,'_','_','_','_'},[],['$_']},
  420: 			     {{KeyB,'_','_','_','_'},[],['$_']}],
  421: 		       ?dbgformat("Tab: ~p, MS: ~p~n",
  422: 				  [Tab,MS]),
  423: 		       MF = fun({Key0,A,B,F,Bi},Acc) ->
  424: 				    case KeyFunA(Key0) of
  425: 					true ->
  426: 					    ?dbgformat
  427: 					       ("FunMatched:"
  428: 						" ~p~n",
  429: 						[{Key0,A,
  430: 						  B,F,Bi}]),
  431: 					    [{Key0,A,B,F,Bi} | 
  432: 					     Acc];
  433: 					_ ->
  434: 					    case KeyFunB(Key0) of
  435: 						true ->
  436: 						    ?dbgformat
  437: 						       ("Fun"
  438: 							"Matched:"
  439: 							" ~p~n",
  440: 							[{Key0,A,
  441: 							  B,F,
  442: 							  Bi}]),
  443: 						    [{Key0,A,B,
  444: 						      F,Bi} |
  445: 						     Acc];
  446: 						_ ->
  447: 						    Acc
  448: 					    end
  449: 				    end
  450: 			    end,
  451: 		       ?line cmp_ms_to_fun(Tab,MS,MF,[])
  452: 	       end,
  453:     ?line {Etses, Detses} = split_by_type(Tabs),
  454:     
  455:     ?line FunEts = fun() ->
  456: 			   ?line lists:foreach(InnerFun,
  457: 					       Etses)
  458: 		   end,
  459:     ?line FunDets = fun() ->
  460: 			    ?line lists:foreach(InnerFun,
  461: 						Detses)
  462: 		    end,
  463:     ?line do_n(table_factor(hd(Etses)) div 2,FunEts),
  464:     ?line do_n(10,FunDets),
  465:     ok.
  466: 		  
  467: 	    
  468: multi_key(Tabs,Type) ->
  469:     Fun = fun() ->
  470: 		  lists:map(fun(Tab) ->
  471: 				    ?line KeyA  = 
  472: 					create_random_key(num_els(Tab),Type),
  473: 				    ?line KeyB  = 
  474: 					create_random_key(num_els(Tab),Type),
  475: 				    ?line KeyC  = 
  476: 					create_random_key(num_els(Tab),Type),
  477: 				    ?line KeyD  = 
  478: 					create_random_key(num_els(Tab),Type),
  479: 				    ?line KeyE  = 
  480: 					create_random_key(num_els(Tab),Type),
  481: 				    ?line KeyF  = 
  482: 					create_random_key(num_els(Tab),Type),
  483: 				    ?line KeyG  = 
  484: 					create_random_key(num_els(Tab),Type),
  485: 				    ?line KeyH  = 
  486: 					create_random_key(num_els(Tab),Type),
  487: 				    ?line KeyI  = 
  488: 					create_random_key(num_els(Tab),Type),
  489: 				    ?line KeyJ  = 
  490: 					create_random_key(num_els(Tab),Type),
  491: 				    ?line KeyK  = 
  492: 					create_random_key(num_els(Tab),Type),
  493: 				    ?line KeyL  = 
  494: 					create_random_key(num_els(Tab),Type),
  495: 				    
  496: 				    MS = [{{KeyA,'$1','_','$2','_'},[],
  497: 					   [{{'$1','$2'}}]},
  498: 					  {{KeyB,'$1','_','$2','_'},[],
  499: 					   [{{'$1','$2'}}]},
  500: 					  {{KeyC,'$1','_','$2','_'},[],
  501: 					   [{{'$1','$2'}}]},
  502: 					  {{KeyD,'$1','_','$2','_'},[],
  503: 					   [{{'$1','$2'}}]},
  504: 					  {{KeyE,'$1','_','$2','_'},[],
  505: 					   [{{'$1','$2'}}]},
  506: 					  {{KeyF,'$1','_','$2','_'},[],
  507: 					   [{{'$1','$2'}}]},
  508: 					  {{KeyG,'$1','_','$2','_'},[],
  509: 					   [{{'$1','$2'}}]},
  510: 					  {{KeyH,'$1','_','$2','_'},[],
  511: 					   [{{'$1','$2'}}]},
  512: 					  {{KeyI,'$1','_','$2','_'},[],
  513: 					   [{{'$1','$2'}}]},
  514: 					  {{KeyJ,'$1','_','$2','_'},[],
  515: 					   [{{'$1','$2'}}]},
  516: 					  {{KeyK,'$1','_','$2','_'},[],
  517: 					   [{{'$1','$2'}}]},
  518: 					  {{KeyL,'$1','_','$2','_'},[],
  519: 					   [{{'$1','$2'}}]}
  520: 					 ],
  521: 				    ?dbgformat("Tab: ~p, MS: ~p~n",
  522: 					      [Tab,MS]),
  523: 				    MF = fun({Key0,A,_B,F,_Bi},Acc) ->
  524: 						 case Key0 of
  525: 						     KeyA ->
  526: 							 [ {A,F} | 
  527: 							   Acc];
  528: 						     KeyB ->
  529: 							 [ {A,F} | 
  530: 							   Acc];
  531: 						     KeyC ->
  532: 							 [ {A,F} | 
  533: 							   Acc];
  534: 						     KeyD ->
  535: 							 [ {A,F} | 
  536: 							   Acc];
  537: 						     KeyE ->
  538: 							 [ {A,F} | 
  539: 							   Acc];
  540: 						     KeyF ->
  541: 							 [ {A,F} | 
  542: 							   Acc];
  543: 						     KeyG ->
  544: 							 [ {A,F} | 
  545: 							   Acc];
  546: 						     KeyH ->
  547: 							 [ {A,F} | 
  548: 							   Acc];
  549: 						     KeyI ->
  550: 							 [ {A,F} | 
  551: 							   Acc];
  552: 						     KeyJ ->
  553: 							 [ {A,F} | 
  554: 							   Acc];
  555: 						     KeyK ->
  556: 							 [ {A,F} | 
  557: 							   Acc];
  558: 						     KeyL ->
  559: 							 [ {A,F} | 
  560: 							   Acc];
  561: 						     _ ->
  562: 							 Acc
  563: 						 end
  564: 					 end,
  565: 				    ?line cmp_ms_to_fun(Tab,MS,MF,[])
  566: 			    end,
  567: 			    Tabs)
  568: 	  end,
  569:     ?line do_n(33,Fun),
  570:     ok.
  571: 		  
  572: multi_mixed_key(Tabs,Type) ->
  573:     InnerFun = fun(Tab) ->
  574: 		       ?line KeyA  = 
  575: 			   create_random_key(num_els(Tab),Type),
  576: 		       ?line KeyB  = 
  577: 			   create_random_key(num_els(Tab),Type),
  578: 		       ?line KeyC  = 
  579: 			   create_random_key(num_els(Tab),Type),
  580: 		       ?line KeyD  = 
  581: 			   create_random_key(num_els(Tab),Type),
  582: 		       ?line {KeyE, FunE}  = 
  583: 			   create_pb_key(num_els(Tab),Type),
  584: 		       ?line KeyF  = 
  585: 			   create_random_key(num_els(Tab),Type),
  586: 		       ?line {KeyG, FunG}  = 
  587: 			   create_pb_key(num_els(Tab),Type),
  588: 		       ?line KeyH  = 
  589: 			   create_random_key(num_els(Tab),Type),
  590: 		       ?line KeyI  = 
  591: 			   create_random_key(num_els(Tab),Type),
  592: 		       ?line {KeyJ, FunJ}  = 
  593: 			   create_pb_key(num_els(Tab),Type),
  594: 		       ?line KeyK  = 
  595: 			   create_random_key(num_els(Tab),Type),
  596: 		       ?line KeyL  = 
  597: 			   create_random_key(num_els(Tab),Type),
  598: 		       
  599: 		       MS = [{{KeyA,'$1','_','$2','_'},[],
  600: 			      [{{'$1','$2'}}]},
  601: 			     {{KeyB,'$1','_','$2','_'},[],
  602: 			      [{{'$1','$2'}}]},
  603: 			     {{KeyC,'$1','_','$2','_'},[],
  604: 			      [{{'$1','$2'}}]},
  605: 			     {{KeyD,'$1','_','$2','_'},[],
  606: 			      [{{'$1','$2'}}]},
  607: 			     {{KeyE,'$100','_','$200','_'},[],
  608: 			      [{{'$100','$200'}}]},
  609: 			     {{KeyF,'$1','_','$2','_'},[],
  610: 			      [{{'$1','$2'}}]},
  611: 			     {{KeyG,'$100','_','$200','_'},[],
  612: 			      [{{'$100','$200'}}]},
  613: 			     {{KeyH,'$1','_','$2','_'},[],
  614: 			      [{{'$1','$2'}}]},
  615: 			     {{KeyI,'$1','_','$2','_'},[],
  616: 			      [{{'$1','$2'}}]},
  617: 			     {{KeyJ,'$100','_','$200','_'},[],
  618: 			      [{{'$100','$200'}}]},
  619: 			     {{KeyK,'$1','_','$2','_'},[],
  620: 			      [{{'$1','$2'}}]},
  621: 			     {{KeyL,'$1','_','$2','_'},[],
  622: 			      [{{'$1','$2'}}]}
  623: 			    ],
  624: 		       ?dbgformat("Tab: ~p, MS: ~p~n",
  625: 				  [Tab,MS]),
  626: 		       MF = fun({Key0,A,_B,F,_Bi},Acc) ->
  627: 				    case Key0 of
  628: 					KeyA ->
  629: 					    [ {A,F} | 
  630: 					      Acc];
  631: 					KeyB ->
  632: 					    [ {A,F} | 
  633: 					      Acc];
  634: 					KeyC ->
  635: 					    [ {A,F} | 
  636: 					      Acc];
  637: 					KeyD ->
  638: 					    [ {A,F} | 
  639: 					      Acc];
  640: 					KeyF ->
  641: 					    [ {A,F} | 
  642: 					      Acc];
  643: 					KeyH ->
  644: 					    [ {A,F} | 
  645: 					      Acc];
  646: 					KeyI ->
  647: 					    [ {A,F} | 
  648: 					      Acc];
  649: 					KeyK ->
  650: 					    [ {A,F} | 
  651: 					      Acc];
  652: 					KeyL ->
  653: 					    [ {A,F} | 
  654: 					      Acc];
  655: 					Else ->
  656: 					    case FunE(Else) or 
  657: 						FunG(Else) or
  658: 						FunJ(Else) of
  659: 						true ->
  660: 						    [ {A,F} | 
  661: 						      Acc]; 
  662: 						_ ->
  663: 						    Acc
  664: 					    end
  665: 				    end
  666: 			    end,
  667: 		       ?line cmp_ms_to_fun(Tab,MS,MF,[]),
  668: 		       ?line case Tab of
  669: 				 {ets,_} ->
  670: 				     ?line cmp_ms_to_fun(Tab,MS,MF,[],1),  
  671: 				     ?line cmp_ms_to_fun(Tab,MS,MF,[],10),  
  672: 				     ?line cmp_ms_to_fun(Tab,MS,MF,[],1000000),  
  673: 				     ?line cmp_ms_to_fun(Tab,MS,MF,[],-1),  
  674: 				     ?line cmp_ms_to_fun(Tab,MS,MF,[],-10),  
  675: 				     ?line cmp_ms_to_fun(Tab,MS,MF,[],-1000000);  
  676: 				 _ ->
  677: 				     ok
  678: 			     end
  679: 	       end,
  680:     ?line {Etses, Detses} = split_by_type(Tabs),
  681:     
  682:     ?line FunEts = fun() ->
  683: 			   ?line lists:foreach(InnerFun,
  684: 					       Etses)
  685: 		   end,
  686:     ?line FunDets = fun() ->
  687: 			    ?line lists:foreach(InnerFun,
  688: 						Detses)
  689: 		    end,
  690:     ?line do_n(table_factor(hd(Etses)) div 2,FunEts),
  691:     ?line do_n(table_factor(hd(Detses)) div 2,FunDets),
  692:     ok.
  693: 		  
  694: 	    
  695: split_by_type(List) ->    
  696:     split_by_type(List,[],[]).
  697: split_by_type([],AccEts,AccDets) ->
  698:     {AccEts,AccDets};
  699: split_by_type([{dets,Tab}|T],AccEts,AccDets) ->
  700:     split_by_type(T,AccEts,[{dets,Tab}|AccDets]);
  701: split_by_type([{ets,Tab}|T],AccEts,AccDets) ->
  702:     split_by_type(T,[{ets,Tab}|AccEts],AccDets).
  703: 
  704: whitebox() ->
  705:     ?line ets:new(xxx,[named_table, ordered_set]),
  706:     ?line ets:new(yyy,[named_table]),
  707:     ?line E = fun(0,_)->ok;
  708: 		 (N,F) -> 
  709: 		      ?line ets:insert(xxx,{N,N rem 10}), 
  710: 		      ?line ets:insert(yyy,{N,N rem 10}), 
  711: 		      F(N-1,F) 
  712: 	      end,
  713:     ?line E(10000,E),                                                          
  714: 
  715:     ?line G = fun(F,C,A) -> 
  716: 		      ?line case ets:select(C) of 
  717: 				{L,C2} -> 
  718: 				    ?line F(F,C2,A+length(L)); 
  719: 				'$end_of_table' -> 
  720: 				    ?line A 
  721: 			    end 
  722: 	      end,
  723:     ?line H=fun({L,C}) -> 
  724: 		    ?line G(G,C,length(L)) 
  725: 	    end,
  726:     
  727:     ?line 1 = H(ets:select(xxx,[{{'$1','$2'},[{'<','$1',2}],['$_']}],7)),
  728:     ?line 10000 = H(ets:select(xxx,[{{'$1','$2'},[],['$_']}],1)),
  729:     ?line 1 = H(ets:select(yyy,[{{'$1','$2'},[{'<','$1',2}],['$_']}],7)),
  730:     ?line 10000 = H(ets:select(yyy,[{{'$1','$2'},[],['$_']}],1)),
  731: 
  732:     ?line {[{5,5}],_} = ets:select(xxx,[{{5,'$2'},[],['$_']}],1),
  733:     ?line {[{5,5}],_} = ets:select(yyy,[{{5,'$2'},[],['$_']}],1),
  734: 
  735:     ?line I = fun(_,0) -> 
  736: 		      ok;
  737: 		 (I,N) -> 
  738: 		      ?line 10000 =  
  739: 			  H(ets:select(xxx,[{{'$1','$2'},[],['$_']}],N)), 
  740: 		      I(I,N-1) 
  741: 	      end,
  742:     ?line I(I,2000),
  743:     ?line J = fun(F,C,A) -> 
  744: 		      ?line case ets:select(C) of 
  745: 				{L,C2} -> 
  746: 				    ?line F(F,C2,lists:reverse(L)++A); 
  747: 				'$end_of_table' -> 
  748: 				    ?line lists:reverse(A) 
  749: 			    end 
  750: 	      end,
  751:     ?line K = fun({L,C}) -> 
  752: 		      ?line J(J,C,lists:reverse(L)) 
  753: 	      end,
  754:     ?line M = fun(_, _, 0) -> 
  755: 		      ok;
  756: 		 (F, What, N) -> 
  757: 		      ?line What =  
  758: 			  K(ets:select(xxx,[{{'$1','$2'},[],['$_']}],N)), 
  759: 		      F(F, What, N-1) 
  760: 	      end,
  761:     ?line N = fun(HM) -> 
  762: 		      ?line What = ets:select(xxx,[{{'$1','$2'},[],['$_']}]), 
  763: 		      ?line What = lists:sort(What), 
  764: 		      M(M, What, HM) 
  765: 	      end,
  766:     ?line N(2000),
  767:     ?line ets:delete(xxx),
  768:     ?line ets:delete(yyy).
  769: 
  770: 
  771: do_return_values() ->
  772:     ?line T = ets:new(xxx,[ordered_set]),
  773:     ?line U = ets:new(xxx,[]),
  774:     ?line '$end_of_table' = ets:select(T,[{'_',[],['$_']}],1),
  775:     ?line '$end_of_table' = ets:select(U,[{'_',[],['$_']}],1),
  776:     ?line ets:insert(T,{ett,1}),
  777:     ?line ets:insert(U,{ett,1}),
  778:     ?line {[{ett,1}],C1} = ets:select(T,[{'_',[],['$_']}],1),
  779:     ?line '$end_of_table' = ets:select(C1),
  780:     ?line {[{ett,1}],C2} = ets:select(U,[{'_',[],['$_']}],1),
  781:     ?line '$end_of_table' = ets:select(C2),
  782:     ?line {[{ett,1}],C3} = ets:select(T,[{'_',[],['$_']}],2),
  783:     ?line '$end_of_table' = ets:select(C3),
  784:     ?line {[{ett,1}],C4} = ets:select(U,[{'_',[],['$_']}],2),
  785:     ?line '$end_of_table' = ets:select(C4),
  786:     ?line E = fun(0,_)->ok;
  787: 		 (N,F) -> 
  788: 		      ?line ets:insert(T,{N,N rem 10}), 
  789: 		      ?line ets:insert(U,{N,N rem 10}), 
  790: 		      F(N-1,F) 
  791: 	      end,
  792:     ?line E(10000,E),                                                          
  793:     ?line '$end_of_table' = ets:select(T,[{{hej, hopp},[],['$_']}],1),
  794:     ?line '$end_of_table' = ets:select(U,[{{hej,hopp},[],['$_']}],1),
  795:     ?line {[{ett,1}],CC1} = ets:select(T,[{{'$1','_'},[{is_atom, '$1'}],
  796: 					  ['$_']}],1),
  797:     ?line '$end_of_table' = ets:select(CC1),
  798:     ?line {[{ett,1}],CC2} = ets:select(U,[{{'$1','_'},[{is_atom, '$1'}],
  799: 					  ['$_']}],1),
  800:     ?line '$end_of_table' = ets:select(CC2),
  801:     ?line {[{ett,1}],CC3} = ets:select(T,[{{'$1','_'},[{is_atom, '$1'}],
  802: 					  ['$_']}],2),
  803:     ?line '$end_of_table' = ets:select(CC3),
  804:     ?line {[{ett,1}],CC4} = ets:select(U,[{{'$1','_'},[{is_atom, '$1'}],
  805: 					  ['$_']}],2),
  806:     ?line '$end_of_table' = ets:select(CC4),
  807:     ?line ets:delete(T),
  808:     ?line ets:delete(U),
  809:     ?line V = ets:new(xxx,[{keypos, 4}]),
  810:     ?line X = ets:new(xxx,[ordered_set, {keypos, 4}]),
  811:     ?line ets:insert(V,{1,1,1,ett}),
  812:     ?line ets:insert(X,{1,1,1,ett}),
  813:     ?line '$end_of_table' = ets:select(V,[{{1,1,1},[],['$_']}],1),
  814:     ?line '$end_of_table' = ets:select(X,[{{1,1,1},[],['$_']}],1),
  815:     ?line ets:delete(V),
  816:     ?line ets:delete(X),
  817:     ok.
  818:     
  819:     
  820: 
  821: 
  822: