&, && Binary AND between integers. Logical AND over/between booleans and numbers

## Syntax

Element-wise operator:

                intMat = intA & intB
                tfMat = A & B

and()-like short-circuited evaluation:

                tf = U && V
                if (U && V) then ... end
                if (U & V)  then ... end
                while (U && V) then ... end
                while (U & V) then ... end

## Arguments

intA, intB

  Arrays of encoded integers of any inttype.

  `intA` and `intB` must have the same sizes to be processed element-wise. If `intA` or `intB` is a scalar, it is priorly replicated to the size of the other operand before processing.

  If `intA` and `intB` have not the same integer inttype, the operand with the smaller encoding is converted to the wider according to the following ranks: int8 < uint8 < int16 < uint16 < int32 < uint32 < int64 < uint64.

intMat

  Array of encoded integers with the same sizes of `intA` and `intB`, and of the wider inttype of both (see hereabove). For any index `i`, `intMat(i) = bitand(intA(i), intB(i))`

  `intA & []` and `[] & intA` return the boolean array `intA~=0`.
A, B

  Arrays of booleans or of numbers: encoded integers of any inttype, real or complex numbers. `A` and `B` may have distinct types, and may be one or both sparse-encoded. The special case where `A` and `B` are both encoded integers is excluded (see here-above).

  `A` and `B` must have the same sizes to be processed element-wise. If `A` or `B` is a scalar, it is priorly replicated to the size of the other operand before processing.

tfMat

  Boolean array with the same sizes of `A` and `B`. If `A` or `B` is sparse-encoded, so is `tfMat`.

  For any index `i`, `tfMat(i)` is `%T` if neither `A(i)` nor `B(i)` are `%F` or zero. Otherwise, `tfMat(i)` is set to `%F`.

  `A & []` and `[] & A` return `[]`.
U, V

  Arrays of full or sparse-encoded booleans or of numbers of any types and encoding: any inttype of encoded integers, full or sparse-encoded real or complex numbers.

  `U` and `V` may have distinct data types, number of dimensions, and sizes.

tf

  single boolean: Set to `and(U) & and(V)`, without evaluating `and(V)` if `and(U)` is false (this is why the `&&` operator is so-called short-circuited).

  See and() for the definition of the evaluation to false depending on type and encoding.

## Description

&&

  For any boolean or numerical operands `U` and `V`, `G = U && V` is equivalent to `if ~and(U), G = %F, else G = and(V), end`.

  - `U` and `V` may have distinct sizes or numbers of dimensions.
  - The result is always a single boolean.
  - `and(V)` is evaluated only if `and(U)` is true. This mainly avoids yielding errors when further operations with `V` are meaningless or prone to error when `U` is false. See examples.
&

  When the `&` operator is used inside a logical condition tested by a `while` or by an `if` control structure, it is equivalent to the `&&` operator (see above).

  Otherwise, `&` has two different actions:

  1. When both operands are encoded integers, `intA & intB` is processed element-wise and yields an array of integers resulting from the bitwise `AND` between corresponding components.
  2. Otherwise, when operands are arrays of numbers or/and booleans, they are still processed element-wise. Provided that null numbers are equivalent to false booleans, the result is an array of booleans.

Comparing `&` and `&&` with `bitand()` and `and()` :

- `and(A)` is equivalent to `A && %T`, whereas `and(A, side)` has no equivalence with `&&`.
- `intA & intB` accepts negative integers, while `bitand(intA, intB)` does not. `bitand(A,B)` works bitwise with decimal-encoded integers.

The overloading code for `&` and `&&` operators is h.

- `%nan` is `true`.
- A constant polynomial or rational is not considered as a number and is not an acceptable operand for `&` or `&&`.

## Examples

    A = [0 1; 1 0];   // equivalent to [%f %t ; %t %f]
    B = [1 1; 0 0];   // equivalent to [%t %t ; %f %f]
    spA = sparse(A);
    spB = sparse(B);
    spbA = sparse(A<>0);
    spbB = sparse(B<>0);
    iA = int8(A);
    iB = int8(B);
    cA = A + 0*%i;
    cB = B + 0*%i;

    // & as logical operation
    A & B
    A & spB    // Result is sparse encoded
    iA & spB   // Result is sparse encoded
    cA & iB
    A & %nan   // %nan is %t

    // & as bitwise operation
    iA & iB

    Ai8 = int8([ -1, 1; 127, -128]);
    // Integer representation of Ai8:
    // [ 1111 1111, 0000 0001 ;
    //   0111 1111, 1000 0000 ]

    Bi8 = int8([-2, 0; 126, -127]);
    // Integer representation of Bi8:
    // [ 1111 1110, 0000 0000 ;
    //   0111 1110, 1000 0001 ]

    Ai8 & Bi8

    // Integer promotion
    Ai16 = int16(Ai8);
    Bui32 = uint32(Bi8);

    r = Ai16 & Bui32, typeof(r)

    // Shorted & or &&:
    // ----------------
    function res = foo()
        error("foo() shall not be called")
    endfunction

    // & (simple) is always shorted in any if's condition:
    if  %F & foo() then
        // foo() is not called and this is not executed
    end

    A && foo()    // A, iA and cA have at least one element equals to 0.
    iA && foo()   // Therefore, foo() is not called
    cA && foo()

    iA && iB // iA is evaluated as false resulting in the short-circuited operation

Avoiding conditional errors in or out of "if" and "while" conditions:

    A = [ 1 3 -2 ; 4 -1  2]
    c = issquare(A) & det(A)~=0
    // det(A) is evaluated despite issquare(A) is false (so c is false anyway)
    // But A should be square to be able to compute its det(). This leads to the error

    // Now, we use the short-circuited && :
    // det(A) is NO LONGER computed, since issquare(A) is false => anyway c is false
    c = issquare(A) && det(A)~=0      // ==> no error

    // In an "if" (or "while") tested condition, & is equivalent to &&
    if issquare(A) & det(A)~=0      // => no error: here & is meant as &&
       B = inv(A)
    else
       disp("A is not square")
    end

    --> A = [ 1 3 -2 ; 4 -1  2]
     A  =
       1.   3.  -2.
       4.  -1.   2.

    --> c = issquare(A) & det(A)~=0
    det: Wrong type for input argument #1: Square matrix expected.

    --> c = issquare(A) && det(A)~=0
     c  =
      F

    --> if issquare(A) & det(A)~=0        // => no error: here & is meant as &&
      >    B = inv(A)
      > else
      >    disp("A is not square")
      > end

      A is not square

Constant polynomials or rationals can't be processed with & or &&:

    p = 1 + 0*%z
    typeof(p)
    p & 1

    --> p = 1 + 0*%z
     p  =
       1

    --> typeof(p)
     ans  =
     polynomial

    --> p & 1
    Undefined operation for the given operands.
    check or define function %p_h_s for overloading.

## See also

- and
- bitand
- or operator (|)
- not ~
- if
- while
- overloading

## History

6.0 && operator added