

Function("IsMatrix",{aLeft})
[
  If(IsList(aLeft),
    [
      Local(result);
      result:=True;

      /*TODO also check nr elements! */
      ForEach(item,aLeft)
        If(Not(IsList(item)),result:=False);
      result;
    ],
    False
    );
];


Function("IsRational",{aLeft}) Type(aLeft) = "/";

Function("IsRationalNumeric",{aLeft})
    Type(aLeft) = "/" And
    IsNumber(aLeft[1]) And
    IsNumber(aLeft[2]);

IsRationalOrNumber(_x) <-- (IsNumber(x) Or IsRationalNumeric(x));

10 # IsRationalOrInteger(x_IsInteger) <-- True;
10 # IsRationalOrInteger(x_IsInteger / y_IsInteger) <-- True;
20 # IsRationalOrInteger(_x) <-- False;

IsNegativeNumber(x):= IsNumber(x) And x < 0;
IsPositiveNumber(x):= IsNumber(x) And x > 0;

IsNegativeInteger(x):= IsInteger(x) And x < 0;
IsNonNegativeInteger(x):=IsInteger(x) And x >= 0;
IsPositiveInteger(x):= IsInteger(x) And x > 0;

10 # IsZero(x_IsNumber) <-- ( (x+0.5)-0.5  = 0);
10 # IsNotZero(x_IsNumber)       <-- ( (x+0.5)-0.5 != 0);
1000 # IsZero(_n) <-- False;
1000 # IsNotZero(_n) <-- False;

IsNonZeroInteger(x) := (IsInteger(x) And x != 0);

IsEven(n) := IsInteger(n) And ( BitAnd(n,1)  = 0 );
IsOdd(n)  := IsInteger(n) And ( BitAnd(n,1)  = 1 );


10 # IsInfinity(Infinity) <-- True;
10 # IsInfinity(-Infinity) <-- True;
20 # IsInfinity(_x) <-- False;

IsConstant(_n) <-- (VarList(n) = {});

Function ("IsBoolean", {x})
	(x=True) Or (x=False) Or IsFunction(x) And Contains({"=", ">", "<", ">=", "<=", "!=", "And", "Not", "Or"}, Type(x));

0 # IsBoolType(True) <-- True;
0 # IsBoolType(False) <-- True;
1 # IsBoolType(_anythingelse) <-- False;

/* See if a number, when evaluated, would be a positive/negative real value */
IsPositiveReal(_r) <--
[
  r:=N(r);
  (IsNumber(r) And r >= 0);
];
IsNegativeReal(_r) <--
[
  r:=N(r);
  (IsNumber(r) And r <= 0);
];


/* Predicates on matrices */
IsSkewSymmetric(A_IsMatrix) <-- (Transpose(A)=(-1*A));
IsHermitian(A_IsMatrix) <-- (Conjugate(Transpose(A))=A);
IsSymmetric(A_IsMatrix) <-- (Transpose(A)=A);
IsOrthogonal(A_IsMatrix) <-- (Transpose(A)*A=Identity(Length(A)));
IsIdempotent(A_IsMatrix) <-- (A^2 = A);
IsUnitary(A_IsMatrix) <-- (Transpose(Conjugate(A))*A = Identity(Length(A)));

IsVariable(_expr) <-- (IsAtom(expr) And Not(IsNumber(N(expr))));

// check that all items in the list are numbers
IsNumericList(_arg'list) <-- IsList(arg'list) And
	("And" @ (MapSingle(Hold({{x},IsNumber(N(x))}), arg'list)));

//////////////////////////////////////////////////
/// Predicates HasExpr*, HasFunc*, ListHasFunc
//////////////////////////////////////////////////

/// HasExpr --- test for an expression containing a subexpression
/// for checking dependence on variables, this may be faster than using VarList or IsFreeOf and this also can be used on non-variables, e.g. strings or numbers or other atoms or even on non-atoms
// an expression contains itself -- check early
10 # HasExpr(_expr, _atom) _ Equals(expr, atom) <-- True;
// an atom contains itself
15 # HasExpr(expr_IsAtom, _atom) <-- Equals(expr, atom);
// a list contains an atom if one element contains it
// we test for lists now because lists are also functions
// first take care of the empty list:
19 # HasExpr({}, _atom) <-- False;
20 # HasExpr(expr_IsList, _atom) <-- HasExpr(Head(expr), atom) Or HasExpr(Tail(expr), atom);
// a function contains an atom if one of its arguments contains it
30 # HasExpr(expr_IsFunction, _atom) <-- HasExpr(Tail(Listify(expr)), atom);

/// Same except only look at function arguments for functions in a given list
HasExprSome(_expr, _atom, _look'list) _ Equals(expr, atom) <-- True;
// an atom contains itself
15 # HasExprSome(expr_IsAtom, _atom, _look'list) <-- Equals(expr, atom);
// a list contains an atom if one element contains it
// we test for lists now because lists are also functions
// first take care of the empty list:
19 # HasExprSome({}, _atom, _look'list) <-- False;
20 # HasExprSome(expr_IsList, _atom, _look'list) <-- HasExprSome(Head(expr), atom, look'list) Or HasExprSome(Tail(expr), atom, look'list);
// a function contains an atom if one of its arguments contains it
// first deal with functions that do not belong to the list: return False since we have already checked it at #15
25 # HasExprSome(expr_IsFunction, _atom, _look'list)_(Not Contains(look'list, Atom(Type(expr)))) <-- False;
// a function contains an atom if one of its arguments contains it
30 # HasExprSome(expr_IsFunction, _atom, _look'list) <-- HasExprSome(Tail(Listify(expr)), atom, look'list);

/// HasFunc --- test for an expression containing a function
/// function name given as string.
10 # HasFunc(_expr, string_IsString) <-- HasFunc(expr, Atom(string));
/// function given as atom.
// atom contains no functions
10 # HasFunc(expr_IsAtom, atom_IsAtom) <-- False;
// a list contains the function List so we test it together with functions
// a function contains itself, or maybe an argument contains it
20 # HasFunc(expr_IsFunction, atom_IsAtom) <-- Equals(Head(Listify(expr)), atom) Or ListHasFunc(Tail(Listify(expr)), atom);

/// function name given as string.
10 # HasFuncSome(_expr, string_IsString, _look'list) <-- HasFuncSome(expr, Atom(string), look'list);
/// function given as atom.
// atom contains no functions
10 # HasFuncSome(expr_IsAtom, atom_IsAtom, _look'list) <-- False;
// a list contains the function List so we test it together with functions
// a function contains itself, or maybe an argument contains it

// first deal with functions that do not belong to the list: return top level function
15 # HasFuncSome(expr_IsFunction, atom_IsAtom, _look'list)_(Not Contains(look'list, Atom(Type(expr)))) <-- Equals(Head(Listify(expr)), atom);
// function belongs to the list - check its arguments
20 # HasFuncSome(expr_IsFunction, atom_IsAtom, _look'list) <-- Equals(Head(Listify(expr)), atom) Or ListHasFuncSome(Tail(Listify(expr)), atom, look'list);

/// ListHasFunc --- test for one of the elements of a list to contain a function
/// this is mainly useful to test whether a list has nested lists, i.e. ListHasFunc({1,2,3}, List)=False and ListHasFunc({1,2,{3}}, List)=True.
// need to exclude the List atom itself, so don't use Listify
19 # ListHasFunc({}, _atom) <-- False;
20 # ListHasFunc(expr_IsList, atom_IsAtom) <-- HasFunc(Head(expr), atom) Or ListHasFunc(Tail(expr), atom);

19 # ListHasFuncSome({}, _atom, _look'list) <-- False;
20 # ListHasFuncSome(expr_IsList, atom_IsAtom, _look'list) <-- HasFuncSome(Head(expr), atom, look'list) Or ListHasFuncSome(Tail(expr), atom, look'list);

/// Analyse arithmetic expressions

HasExprArith(expr, atom) := HasExprSome(expr, atom, {Atom("+"), Atom("-"), *, /});
HasFuncArith(expr, atom) := HasFuncSome(expr, atom, {Atom("+"), Atom("-"), *, /});

