1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2001-2018. 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-module(andor_SUITE).
21
22-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
23	 init_per_group/2,end_per_group/2,
24	 t_case/1,t_and_or/1,t_andalso/1,t_orelse/1,inside/1,overlap/1,
25	 combined/1,in_case/1,slow_compilation/1]).
26
27-include_lib("common_test/include/ct.hrl").
28
29suite() -> [{ct_hooks,[ts_install_cth]}].
30
31all() ->
32    [{group,p}].
33
34groups() ->
35    [{p,[parallel],
36      [t_case,t_and_or,t_andalso,t_orelse,inside,overlap,
37       combined,in_case,slow_compilation]}].
38
39init_per_suite(Config) ->
40    test_lib:recompile(?MODULE),
41    Config.
42
43end_per_suite(_Config) ->
44    ok.
45
46init_per_group(_GroupName, Config) ->
47	Config.
48
49end_per_group(_GroupName, Config) ->
50	Config.
51
52
53t_case(Config) when is_list(Config) ->
54    %% We test boolean cases almost but not quite like cases
55    %% generated by andalso/orelse.
56    less = t_case_a(1, 2),
57    not_less = t_case_a(2, 2),
58    {'EXIT',{{case_clause,false},_}} = (catch t_case_b({x,y,z}, 2)),
59    {'EXIT',{{case_clause,true},_}} = (catch t_case_b(a, a)),
60    eq = t_case_c(a, a),
61    ne = t_case_c(42, []),
62    t = t_case_d(x, x, true),
63    f = t_case_d(x, x, false),
64    f = t_case_d(x, y, true),
65    {'EXIT',{badarg,_}} = (catch t_case_d(x, y, blurf)),
66    true = (catch t_case_e({a,b}, {a,b})),
67    false = (catch t_case_e({a,b}, 42)),
68
69    {true,false} = t_case_f1(true, pos),
70    {false,true} = t_case_f1(true, whatever),
71    {false,true} = t_case_f1(false, pos),
72    {false,true} = t_case_f1(false, whatever),
73    {false,false} = t_case_f1(not_boolean, pos),
74    {false,false} = t_case_f1(not_boolean, whatever),
75
76    false = t_case_f2(true),
77    true = t_case_f2(false),
78    false = t_case_f2(whatever),
79
80    true = t_case_xy(42, 100, 700),
81    true = t_case_xy(42, 100, whatever),
82    false = t_case_xy(42, wrong, 700),
83    false = t_case_xy(42, wrong, whatever),
84
85    true = t_case_xy(0, whatever, 700),
86    true = t_case_xy(0, 100, 700),
87    false = t_case_xy(0, whatever, wrong),
88    false = t_case_xy(0, 100, wrong),
89
90    ok.
91
92t_case_a(A, B) ->
93    case A < B of
94	[_|_] -> ok;
95	true -> less;
96	false -> not_less;
97	{a,b,c} -> ok;
98	_Var -> ok
99    end.
100
101t_case_b(A, B) ->
102    case A =:= B of
103	blurf -> ok
104    end.
105
106t_case_c(A, B) ->
107    case not(A =:= B) of
108	true -> ne;
109	false -> eq
110    end.
111
112t_case_d(A, B, X) ->
113    case (A =:= B) and X of
114	true -> t;
115	false -> f
116    end.
117
118t_case_e(A, B) ->
119    case A =:= B of
120	Bool when is_tuple(A) -> id(Bool)
121    end.
122
123t_case_f1(IsInt, Eval) ->
124    B = case IsInt of
125            true -> Eval =:= pos;
126            false -> false;
127            _ -> IsInt
128        end,
129
130    %% The above is the same as `IsInt andalso Eval =:= pos` in a guard.
131    %% In a real guard, variable `B` will only be used once.
132    {B =:= true, B =:= false}.
133
134t_case_f2(IsInt) ->
135    B = case IsInt of
136            true -> false;
137            false -> true;
138            _ -> IsInt
139        end,
140    B =:= true.
141
142t_case_xy(X, Y, Z) ->
143    Res = t_case_x(X, Y, Z),
144    Res = t_case_y(X, Y, Z).
145
146t_case_x(X, Y, Z) ->
147    case abs(X) =:= 42 of
148	true ->
149	    Y =:= 100;
150	false ->
151	    Z =:= 700
152    end.
153
154t_case_y(X, Y, Z) ->
155    case abs(X) =:= 42 of
156	false ->
157	    Z =:= 700;
158	true ->
159	    Y =:= 100
160    end.
161
162-define(GUARD(E), if E -> true;
163             true -> false
164          end).
165
166t_and_or(Config) when is_list(Config) ->
167    true = true and true,
168    false = true and false,
169    false = false and true,
170    false = false and false,
171
172    true = id(true) and true,
173    false = id(true) and false,
174    false = id(false) and true,
175    false = id(false) and false,
176
177    true = true and id(true),
178    false = true and id(false),
179    false = false and id(true),
180    false = false and id(false),
181
182    true = true or true,
183    true = true or false,
184    true = false or true,
185    false = false or false,
186
187    true = id(true) or true,
188    true = id(true) or false,
189    true = id(false) or true,
190    false = id(false) or false,
191
192    true = true or id(true),
193    true = true or id(false),
194    true = false or id(true),
195    false = false or id(false),
196
197    True = id(true),
198
199    false = ?GUARD(erlang:'and'(bar, True)),
200    false = ?GUARD(erlang:'or'(bar, True)),
201    false = ?GUARD(erlang:'not'(erlang:'and'(bar, True))),
202    false = ?GUARD(erlang:'not'(erlang:'not'(erlang:'and'(bar, True)))),
203
204    true = (fun (X = true) when X or true or X -> true end)(True),
205
206    Tuple = id({a,b}),
207    case Tuple of
208	{_,_} ->
209	    {'EXIT',{badarg,_}} = (catch true and Tuple)
210    end,
211
212    ok.
213
214t_andalso(Config) when is_list(Config) ->
215    Bs = [true,false],
216    Ps = [{X,Y} || X <- Bs, Y <- Bs],
217    lists:foreach(fun (P) -> t_andalso_1(P) end, Ps),
218
219    true = true andalso true,
220    false = true andalso false,
221    false = false andalso true,
222    false = false andalso false,
223
224    true = ?GUARD(true andalso true),
225    false = ?GUARD(true andalso false),
226    false = ?GUARD(false andalso true),
227    false = ?GUARD(false andalso false),
228
229    false = false andalso glurf,
230    false = false andalso exit(exit_now),
231
232    true = not id(false) andalso not id(false),
233    false = not id(false) andalso not id(true),
234    false = not id(true) andalso not id(false),
235    false = not id(true) andalso not id(true),
236
237    {'EXIT',{badarg,_}} = (catch not id(glurf) andalso id(true)),
238    {'EXIT',{badarg,_}} = (catch not id(false) andalso not id(glurf)),
239    false = id(false) andalso not id(glurf),
240    false = false andalso not id(glurf),
241
242    true = begin (X1 = true) andalso X1, X1 end,
243    false = false = begin (X2 = false) andalso X2, X2 end,
244
245    %% Cover conversion to right associativity.
246    true = (is_list(Config) andalso is_list(Bs)) andalso is_list(Ps),
247
248    ok.
249
250t_orelse(Config) when is_list(Config) ->
251    Bs = [true,false],
252    Ps = [{X,Y} || X <- Bs, Y <- Bs],
253    lists:foreach(fun (P) -> t_orelse_1(P) end, Ps),
254
255    true = true orelse true,
256    true = true orelse false,
257    true = false orelse true,
258    false = false orelse false,
259
260    true = ?GUARD(true orelse true),
261    true = ?GUARD(true orelse false),
262    true = ?GUARD(false orelse true),
263    false = ?GUARD(false orelse false),
264
265    true = true orelse glurf,
266    true = true orelse exit(exit_now),
267
268    true = not id(false) orelse not id(false),
269    true = not id(false) orelse not id(true),
270    true = not id(true) orelse not id(false),
271    false = not id(true) orelse not id(true),
272
273    {'EXIT',{badarg,_}} = (catch not id(glurf) orelse id(true)),
274    {'EXIT',{badarg,_}} = (catch not id(true) orelse not id(glurf)),
275    true = id(true) orelse not id(glurf),
276    true = true orelse not id(glurf),
277
278    true = begin (X1 = true) orelse X1, X1 end,
279    false = begin (X2 = false) orelse X2, X2 end,
280
281    %% Cover conversion to right associativity.
282    false = (is_atom(Config) orelse is_atom(Bs)) orelse is_atom(Ps),
283
284    ok.
285
286t_andalso_1({X,Y}) ->
287    io:fwrite("~w andalso ~w: ",[X,Y]),
288    V1 = echo(X) andalso echo(Y),
289    V1 = if
290	     X andalso Y -> true;
291	     true -> false
292	 end,
293    V1 = id(X and Y).
294
295t_orelse_1({X,Y}) ->
296    io:fwrite("~w orelse ~w: ",[X,Y]),
297    V1 = echo(X) orelse echo(Y),
298    V1 = if
299	     X orelse Y -> true;
300	     true -> false
301	 end,
302    V1 = id(X or Y).
303
304inside(Config) when is_list(Config) ->
305    true = inside(-8, 1),
306    false = inside(-53.5, -879798),
307    false = inside(1.0, -879),
308    false = inside(59, -879),
309    false = inside(-11, 1.0),
310    false = inside(100, 0.2),
311    false = inside(100, 1.2),
312    false = inside(-53.5, 4),
313    false = inside(1.0, 5.3),
314    false = inside(59, 879),
315    ok.
316
317inside(Xm, Ym) ->
318    X = -10.0,
319    Y = -2.0,
320    W = 20.0,
321    H = 4.0,
322    Res = inside(Xm, Ym, X, Y, W, H),
323    Res = if
324	      X =< Xm andalso Xm < X+W andalso Y =< Ym andalso Ym < Y+H -> true;
325	      true -> false
326	  end,
327    case not id(Res) of
328	Outside ->
329	    Outside = if
330			  not(X =< Xm andalso Xm < X+W andalso Y =< Ym andalso Ym < Y+H) -> true;
331			  true -> false
332		      end
333    end,
334    {Res,Xm,Ym,X,Y,W,H} = inside_guard(Xm, Ym, X, Y, W, H),
335    io:format("~p =< ~p andalso ~p < ~p andalso ~p =< ~p andalso ~p < ~p ==> ~p",
336	      [X,Xm,Xm,X+W,Y,Ym,Ym,Y+H,Res]),
337    Res.
338
339inside(Xm, Ym, X, Y, W, H) ->
340    X =< Xm andalso Xm < X+W andalso Y =< Ym andalso Ym < Y+H.
341
342inside_guard(Xm, Ym, X, Y, W, H) when X =< Xm andalso Xm < X+W
343				      andalso Y =< Ym andalso Ym < Y+H ->
344    {true,Xm,Ym,X,Y,W,H};
345inside_guard(Xm, Ym, X, Y, W, H) ->
346    {false,Xm,Ym,X,Y,W,H}.
347
348overlap(Config) when is_list(Config) ->
349    true = overlap(7.0, 2.0, 8.0, 0.5),
350    true = overlap(7.0, 2.0, 8.0, 2.5),
351    true = overlap(7.0, 2.0, 5.3, 2),
352    true = overlap(7.0, 2.0, 0.0, 100.0),
353
354    false = overlap(-1, 2, -35, 0.5),
355    false = overlap(-1, 2, 777, 0.5),
356    false = overlap(-1, 2, 2, 10),
357    false = overlap(2, 10, 12, 55.3),
358    ok.
359
360overlap(Pos1, Len1, Pos2, Len2) ->
361    Res = case Pos1 of
362	      Pos1 when (Pos2 =< Pos1 andalso Pos1 < Pos2+Len2)
363			orelse (Pos1 =< Pos2 andalso Pos2 < Pos1+Len1) ->
364		  true;
365	      Pos1 -> false
366	  end,
367    Res = (Pos2 =< Pos1 andalso Pos1 < Pos2+Len2)
368	orelse (Pos1 =< Pos2 andalso Pos2 < Pos1+Len1),
369    Res = case Pos1 of
370	      Pos1 when (Pos2 =< Pos1 andalso Pos1 < Pos2+Len2)
371			orelse (Pos1 =< Pos2 andalso Pos2 < Pos1+Len1) ->
372		  true;
373	      Pos1 -> false
374	  end,
375    id(Res).
376
377
378-define(COMB(A,B,C), (A andalso B orelse C)).
379
380combined(Config) when is_list(Config) ->
381    false = comb(false, false, false),
382    true = comb(false, false, true),
383    false = comb(false, true, false),
384    true = comb(false, true, true),
385
386    false = comb(true, false, false),
387    true = comb(true, true, false),
388    true = comb(true, false, true),
389    true = comb(true, true, true),
390
391    false = comb(false, blurf, false),
392    true = comb(false, blurf, true),
393    true = comb(true, true, blurf),
394
395    false = ?COMB(false, false, false),
396    true = ?COMB(false, false, true),
397    false = ?COMB(false, true, false),
398    true = ?COMB(false, true, true),
399
400    false = ?COMB(true, false, false),
401    true = ?COMB(true, true, false),
402    true = ?COMB(true, false, true),
403    true = ?COMB(true, true, true),
404
405    false = ?COMB(false, blurf, false),
406    true = ?COMB(false, blurf, true),
407    true = ?COMB(true, true, blurf),
408
409    false = simple_comb(false, false),
410    false = simple_comb(false, true),
411    false = simple_comb(true, false),
412    true = simple_comb(true, true),
413
414    ok.
415-undef(COMB).
416
417comb(A, B, C) ->
418    Res = A andalso B orelse C,
419    Res = if
420	      A andalso B orelse C -> true;
421	      true -> false
422	  end,
423    NotRes = if
424		 not(A andalso B orelse C) -> true;
425		 true -> false
426	     end,
427    NotRes = id(not Res),
428    Res = A andalso B orelse C,
429    Res = if
430	      A andalso B orelse C -> true;
431	      true -> false
432	  end,
433    NotRes = id(not Res),
434    Res = if
435	      A andalso B orelse C -> true;
436	      true -> false
437	  end,
438    id(Res).
439
440simple_comb(A, B) ->
441    %% Use Res twice, to ensure that a careless optimization of 'not'
442    %% doesn't leave Res as a free variable.
443    Res = A andalso B,
444    _ = id(not Res),
445    Res.
446
447%% Test that a boolean expression in a case expression is properly
448%% optimized (in particular, that the error behaviour is correct).
449in_case(Config) when is_list(Config) ->
450    edge_rings = in_case_1(1, 1, 1, 1, 1),
451    not_loop = in_case_1(0.5, 1, 1, 1, 1),
452    loop = in_case_1(0.5, 0.9, 1.1, 1, 4),
453    {'EXIT',{badarith,_}} = (catch in_case_1(1, 1, 1, 1, 0)),
454    {'EXIT',{badarith,_}} = (catch in_case_1(1, 1, 1, 1, nan)),
455    {'EXIT',{badarg,_}} = (catch in_case_1(1, 1, 1, blurf, 1)),
456    {'EXIT',{badarith,_}} = (catch in_case_1([nan], 1, 1, 1, 1)),
457    ok.
458
459in_case_1(LenUp, LenDw, LenN, Rotation, Count) ->
460    Res = in_case_1_body(LenUp, LenDw, LenN, Rotation, Count),
461    Res = in_case_1_guard(LenUp, LenDw, LenN, Rotation, Count),
462    Res.
463
464in_case_1_body(LenUp, LenDw, LenN, Rotation, Count) ->
465    case (LenUp/Count > 0.707) and (LenN/Count > 0.707) and
466	(abs(Rotation) > 0.707) of
467	true ->
468	    edge_rings;
469	false ->
470	    case (LenUp >= 1) or (LenDw >= 1) or
471		(LenN =< 1) or (Count < 4) of
472		true ->
473		    not_loop;
474		false ->
475		    loop
476	    end
477    end.
478
479in_case_1_guard(LenUp, LenDw, LenN, Rotation, Count) ->
480    case (LenUp/Count > 0.707) andalso (LenN/Count > 0.707) andalso
481	(abs(Rotation) > 0.707) of
482	true -> edge_rings;
483	false when LenUp >= 1 orelse LenDw >= 1 orelse
484	LenN =< 1 orelse Count < 4 -> not_loop;
485	false -> loop
486    end.
487
488-record(state, {stack = []}).
489
490slow_compilation(_) ->
491    %% The function slow_compilation_1 used to compile very slowly.
492    ok = slow_compilation_1({a}, #state{}).
493
494slow_compilation_1(T1, #state{stack = [T2|_]})
495    when element(1, T2) == a, element(1, T1) == b, element(1, T1) == c ->
496    ok;
497slow_compilation_1(T, _)
498    when element(1, T) == a1; element(1, T) == b1; element(1, T) == c1 ->
499    ok;
500slow_compilation_1(T, _)
501    when element(1, T) == a2; element(1, T) == b2; element(1, T) == c2 ->
502    ok;
503slow_compilation_1(T, _) when element(1, T) == a ->
504    ok;
505slow_compilation_1(T, _)
506    when
507        element(1, T) == a,
508        (element(1, T) == b) and (element(1, T) == c) ->
509    ok;
510slow_compilation_1(_, T) when element(1, T) == a ->
511    ok;
512slow_compilation_1(_, T) when element(1, T) == b ->
513    ok;
514slow_compilation_1(T, _) when element(1, T) == a ->
515    ok.
516
517%% Utilities.
518
519echo(X) ->
520    io:fwrite("eval(~w); ",[X]),
521    X.
522
523id(I) -> I.
524