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