(kill (all), 0);
0;

/* Test simplification of boolean expressions.
 * '(...) expressions are simplified but not evaluated.
 */

['("and" ()), '("or" ())];      /* analogous to * and + with no arguments */
[true, false];

/* These values are supposed to be ignored in quoted expressions '(...).
 */
(a : true, b : false, c : true, 0);
0;

('(a and b), [op (%%), args (%%)]);
["and", '[a, b]];

('(a or b), [op (%%), args (%%)]);
["or", '[a, b]];

('(not a), [op (%%), args (%%)]);
["not", '[a]];

('(a and b or c and d), [op (%%), args (%%)]);
["or", ['(a and b), '(c and d)]];

('((a or b) and (c or d)), [op (%%), args (%%)]);
["and", ['(a or b), '(c or d)]];

'(a and true);
'a;

'(a and false);
false;

'(a or true);
true;

'(a or false);
'a;

'[true and true, true and false, true or true, true or false, false and true, false and false, false or true, false or false];
[true, false, true, true, false, false, true, false];

'((a or true) and (b or true));
true;

'(a and true or b and true);
'(a or b);

'(a or b or c or false);
'(a or b or c);

'(a and b and c and true);
'(a and b and c);

'(not (a and b));
'((not a) or (not b));

'(not a and b);
'((not a) and b);

'(not (a or b));
'((not a) and (not b));

'(not (a or b or c or d or e));
'((not a) and (not b) and (not c) and (not d) and (not e));

'(not (a and b and c and d and e));
'((not a) or (not b) or (not c) or (not d) or (not e));

'(not (a or b and c or d and e));
'((not a) and ((not b) or (not c)) and ((not d) or (not e)));

'(not (a and not b or c and not d and not e));
'(((not a) or b) and ((not c) or d or e));

'(not a or b);
'((not a) or b);

'(not not a);
'a;

'(not not not a);
'(not a);

'(not not not not a);
'a;

/* Flattening nested expressions.
 * Maxima "and" and "or" are not commutative, so order of arguments should be preserved.
 * Flattening is a simplification => should happen in both simplification and evaluation.
 */

kill (all);
done;

('(x and b and ((h and d) and c) and y), [op (%%), args (%%)]);
["and", [x, b, h, d, c, y]];

('(x or b or ((h or d) or c) or y), [op (%%), args (%%)]);
["or", [x, b, h, d, c, y]];

'(x and b and ((h or d) or c) and (y and q and e));
x and b and (h or d or c) and y and q and e;

(x and b and ((h and d) and c) and y, [op (%%), args (%%)]);
["and", [x, b, h, d, c, y]];

(x or b or ((h or d) or c) or y, [op (%%), args (%%)]);
["or", [x, b, h, d, c, y]];

x and b and ((h or d) or c) and (y and q and e);
x and b and (h or d or c) and y and q and e;

/* Side-effects shouldn't happen in simplification (since arguments are not evaluated).
 */

(kill (a, b, c), 0);
0;

('((a : true) and (b : false) and (c : foo ())), [op (%%), args (%%), a, b, c]);
["and", '[a : true, b : false, c : foo ()], 'a, 'b, 'c];

('(not (a : true)), [op (%%), args (%%), a]);
["not", '[a : true], 'a];

/* Miscellaneous tests.
 */
expr: '(a > b and (equal (c, d) or e = 1) and not f <= g or notequal (h, %pi));
'(a > b and (equal (c, d) or e = 1) and f > g or notequal (h, %pi));

'(a > b and b > c and true or a < b and b < c and true or false or not true);
'((a > b and b > c) or (a < b and b < c));

'(not not not not a > b);
'(a > b);

'(not not not not not a > b);
'(a <= b);

'(not not not false and not not not not true);
true;

'(not foo (a));
''(funmake ("not", '[foo (a)]));

'[is (not a > b or false), is (not a > b or true), is (not a > b and false)];
'[is (a <= b), true, false];

'['is (not a > b or false), 'is (not a > b or true), 'is (not a > b and false)];
'['is (a <= b), true, false];

'[maybe (not a > b or false), maybe (not a > b or true), maybe (not a > b and false)];
'[maybe (a <= b), true, false];

'['maybe (not a > b or false), 'maybe (not a > b or true), 'maybe (not a > b and false)];
'['maybe (a <= b), true, false];

/* Test evaluation of boolean expressions.
 * Evaluate the arguments and simplify.
 */

["and" (), "or" ()];      /* analogous to * and + with no arguments */
[true, false];

(a and b, [op (%%), args (%%)]);
["and", '[a, b]];

(a or b, [op (%%), args (%%)]);
["or", '[a, b]];

(not a, [op (%%), args (%%)]);
["not", '[a]];

(a and b or c and d, [op (%%), args (%%)]);
["or", ['(a and b), '(c and d)]];

((a or b) and (c or d), [op (%%), args (%%)]);
["and", ['(a or b), '(c or d)]];

a and true;
'a;

a and false;
false;

a or true;
true;

a or false;
'a;

(a or true) and (b or true);
true;

a and true or b and true;
'(a or b);

a or b or c or false;
'(a or b or c);

a and b and c and true;
'(a and b and c);

not (a and b);
'((not a) or (not b));

not a and b;
'((not a) and b);

not (a or b);
'((not a) and (not b));

not (a or b or c or d or e);
'((not a) and (not b) and (not c) and (not d) and (not e));

not (a and b and c and d and e);
'((not a) or (not b) or (not c) or (not d) or (not e));

not (a or b and c or d and e);
'((not a) and ((not b) or (not c)) and ((not d) or (not e)));

not (a and not b or c and not d and not e);
'(((not a) or b) and ((not c) or d or e));

not a or b;
'((not a) or b);

not not a;
'a;

not not not a;
'(not a);

not not not not a;
'a;

/* Side-effects should happen in evaluation.
 */

((a : true) and (b : true) and (c : foo ()), [%%, a, b, c]);
[foo (), true, true, foo ()];

(not (a : bar ()), [%%, a]);
['(not bar ()), bar ()];

(kill (a, b, c), 0);
0;

/* Miscellaneous tests.
 */
expr: a > b and (equal (c, d) or e = 1) and not f <= g or notequal (h, %pi);
'(a > b and equal (c, d) and f > g or notequal (h, %pi));

''expr, e=1;
'(a > b and equal (c, d) and f > g or notequal (h, %pi));

''expr, e=1, h=%e;
true;

''expr, e=1, h=%pi;
'(a > b and equal (c, d) and f > g);

''expr, e=89, f = g + 1;
'(a > b and equal (c, d) or notequal (h, %pi));

(assume (notequal (c, d), equal (f, 100), g < 0), 0);
0;

''expr, e = 1;
'(notequal (h, %pi));

a > b and b > c and true or a < b and b < c and true or false or not true;
'((a > b and b > c) or (a < b and b < c));

not not not not a > b;
'(a > b);

not not not not not a > b;
'(a <= b);

not not not false and not not not not true;
true;

/* is-evaluation of conditionals.
 * is(foo) => true, false, and unknown or error (depending on prederror flag).
 */

ev (is (a > b or a > c), prederror=false);
unknown;

errcatch (ev (is (a > b or a > c), prederror=true));
[];

[is (not a > b or true), is (not a > b and false), 'is (not a > b or true), 'is (not a > b and false)];
[true, false, true, false];

[maybe (not a > b or false), maybe (not a > b or true), maybe (not a > b and false)];
[unknown, true, false];

['maybe (not a > b or false), 'maybe (not a > b or true), 'maybe (not a > b and false)];
['maybe (a <= b), true, false];

is (not a > b or true);
true;

is (not a > b and false);
false;

ev (maybe (not a > b or false), prederror=false);
unknown;

ev (maybe (not a > b or false), prederror=true);
unknown;

ev (maybe (not a > b), a = 100.0, b = 1000/3);
true;

ev (maybe (not a > b), b = 100.0, a = 1000/3);
false;

(kill (aa, bb, cc, dd),
 implies (a%, b%) := (not a%) or b%,
 equivalent (a%, b%) := implies (a%, b%) and implies (b%, a%),
 translate (implies, equivalent));
[implies, equivalent];

/* output not fully simplified -- these next two tests show bugs */

implies (aa and bb, cc or dd);
not (aa) or not (bb) or cc or dd;

implies (aa and bb, aa);
not (aa) or not (bb) or aa;

equivalent (aa, aa);
(not aa or aa) and (not aa or aa);

/* crime scene example from mailing list 2006/05/06 tnx Mario */

(kill (all),
 "=>" (r, s) := not r or s,
 infix ("=>"),
 implies (ante, conse, listvar) := block ([expr, npos, maxn, combis:[], bits, quot, div],
    npos: length(listvar),
    maxn: 2^npos - 1,
    expr: not ante or conse,
    for i:0 thru maxn do
       (bits: [],
        quot: i,
        for j: 1 thru npos do
           (div: divide (quot, 2),
            quot: div[1],
            bits: cons (listvar[j]=div[2], bits)),
        combis: cons (subst ([1=true, 0=false], bits), combis)),
    apply ("and", makelist (subst (k, expr), k, combis))), 0);
0;

implies ((p or q) and (q => r) and (p => s) and not r, s, [p, q, r, s]);
true;

/* charfun examples following email discussion circa 2007-03-23 */

(kill (x, y, z), expr : charfun (-1 < x and x < 1));
'(charfun (-1 < x and x < 1));

[ev (expr, x = 1/2), ev (expr, x = -3/2)];
[1, 0];

expr : charfun (x > 0 or y > 0);
'(charfun (x > 0 or y > 0));

[ev (expr, x = 1/2), ev (expr, x = -3/2), ev (expr, x = -3/2, y = -3/4)];
[1, '(charfun (y > 0)), 0];

/* ensure that arguments to is and maybe are evaluated exactly once */

(kill (aa, bb, cc),
 aa : bb : cc,
 cc : 1234,
 is (aa = bb));
true;

is (aa = 'cc and bb = 'cc);
true;

is ('aa = 'bb);
false;

maybe (aa = bb);
true;

maybe (aa = 'cc and bb = 'cc);
true;

maybe ('aa = 'bb);
false;

/* SF bug [ 1726148 ] maybe with prederror : true */

(kill (a), ev (maybe (a), prederror = true));
unknown;

ev (maybe (a or false), prederror = true);
unknown;

