(********************************************************************)
(*                                                                  *)
(*  integer.s7i   Integer support library                           *)
(*  Copyright (C) 1989 - 2016, 2018 - 2021, 2024  Thomas Mertes     *)
(*                                                                  *)
(*  This file is part of the Seed7 Runtime Library.                 *)
(*                                                                  *)
(*  The Seed7 Runtime Library is free software; you can             *)
(*  redistribute it and/or modify it under the terms of the GNU     *)
(*  Lesser General Public License as published by the Free Software *)
(*  Foundation; either version 2.1 of the License, or (at your      *)
(*  option) any later version.                                      *)
(*                                                                  *)
(*  The Seed7 Runtime Library is distributed in the hope that it    *)
(*  will be useful, but WITHOUT ANY WARRANTY; without even the      *)
(*  implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR *)
(*  PURPOSE.  See the GNU Lesser General Public License for more    *)
(*  details.                                                        *)
(*                                                                  *)
(*  You should have received a copy of the GNU Lesser General       *)
(*  Public License along with this program; if not, write to the    *)
(*  Free Software Foundation, Inc., 51 Franklin Street,             *)
(*  Fifth Floor, Boston, MA  02110-1301, USA.                       *)
(*                                                                  *)
(********************************************************************)


$ system "integer" is integer;

const proc: (ref integer: dest) ::= (ref integer: source)           is action "INT_CREATE";

const proc: (inout integer: dest) := (in integer: source)           is action "INT_CPY";


(**
 *  Default value of ''integer'' (0).
 *)
const integer: (attr integer) . value is 0;


(**
 *  Minimum value of ''integer'' (-9223372036854775808).
 *)
const integer: (attr integer) . first is forward;


(**
 *  Maximum value of ''integer'' (9223372036854775807).
 *)
const integer: (attr integer) . last  is  9223372036854775807;


(**
 *  Plus sign for integer numbers.
 *  @return its operand unchanged.
 *)
const func integer: + (in integer: number)                          is action "INT_PLUS";


(**
 *  Minus sign, negate an integer number.
 *  @return the negated value of the number.
 *  @exception OVERFLOW_ERROR If an integer overflow occurs.
 *)
const func integer: - (in integer: number)                          is action "INT_NEGATE";


(**
 *  Compute the factorial of a number.
 *  @return the factorial of the number.
 *  @exception NUMERIC_ERROR The number is negative or the result
 *             does not fit into an integer.
 *)
const func integer: ! (in integer: number)                          is action "INT_FACT";


(**
 *  Add two integer numbers.
 *  @return the sum of the two numbers.
 *  @exception OVERFLOW_ERROR If an integer overflow occurs.
 *)
const func integer: (in integer: summand1) + (in integer: summand2)    is action "INT_ADD";


(**
 *  Compute the subtraction of two integer numbers.
 *  @return the difference of the two numbers.
 *  @exception OVERFLOW_ERROR If an integer overflow occurs.
 *)
const func integer: (in integer: minuend) - (in integer: subtrahend)   is action "INT_SBTR";


const integer: (attr integer) . first is -9223372036854775807 - 1;


(**
 *  Multiply two integer numbers.
 *  @return the product of the two numbers.
 *  @exception OVERFLOW_ERROR If an integer overflow occurs.
 *)
const func integer: (in integer: factor1) * (in integer: factor2)      is action "INT_MULT";


(**
 *  Integer division truncated towards zero.
 *  The remainder of this division is computed with ''rem''.
 *  For the operations ''div'' and ''rem'' holds for all A:
 *   (A div B) * B + A rem B = A           when B <> 0
 *   -A div B = -(A div B)                 when B <> 0
 *   -A rem B = -(A rem B)                 when B <> 0
 *   A rem B >= 0 and A rem B < abs(B)     when B <> 0 and A >= 0
 *   A rem B <= 0 and A rem B > -abs(B)    when B <> 0 and A <= 0
 *  @return the quotient of the integer division.
 *  @exception NUMERIC_ERROR If a division by zero occurs.
 *  @exception OVERFLOW_ERROR If an integer overflow occurs.
 *)
const func integer: (in integer: dividend) div (in integer: divisor)   is action "INT_DIV";


(**
 *  Compute the remainder of the integer division ''div''.
 *  The remainder has the same sign as the dividend.
 *   A rem B
 *  is equivalent to
 *   A - (A div B) * B
 *  @return the remainder of the integer division.
 *  @exception NUMERIC_ERROR If a division by zero occurs.
 *  @exception OVERFLOW_ERROR If an integer overflow occurs.
 *)
const func integer: (in integer: dividend) rem (in integer: divisor)   is action "INT_REM";


(**
 *  Integer division truncated towards negative infinity.
 *  The modulo (remainder) of this division is computed with ''mod''.
 *  Therefore this division is called modulo division (''mdiv'').
 *  For the operations ''mdiv'' and ''mod'' holds for all A:
 *   (A mdiv B) * B + A mod B = A          when B <> 0
 *   -A mdiv B = A mdiv -B                 when B <> 0
 *   -A mod -B = -(A mod B)                when B <> 0
 *   A mod B >= 0 and A mod B < B          when B > 0
 *   A mod B <= 0 and A mod B > B          when B < 0
 *  @return the quotient of the integer division.
 *  @exception NUMERIC_ERROR If a division by zero occurs.
 *  @exception OVERFLOW_ERROR If an integer overflow occurs.
 *)
const func integer: (in integer: dividend) mdiv (in integer: divisor)  is action "INT_MDIV";


(**
 *  Compute the modulo (remainder) of the integer division ''mdiv''.
 *  The modulo has the same sign as the divisor.
 *   A mod B
 *  is equivalent to
 *   A - (A mdiv B) * B
 *  @return the modulo of the integer division.
 *  @exception NUMERIC_ERROR If a division by zero occurs.
 *  @exception OVERFLOW_ERROR If an integer overflow occurs.
 *)
const func integer: (in integer: dividend) mod (in integer: divisor)   is action "INT_MOD";


(**
 *  Compute the exponentiation of an integer base with an integer exponent.
 *   A ** 0  returns 1           for every A, even for A = 0
 *   1 ** B  returns 1           for B >= 0
 *   A ** B  returns -(-A) ** B  for A <= 0 and B >= 0 and odd(B)
 *   A ** B  returns (-A) ** B   for A <= 0 and B >= 0 and not odd(B)
 *  @return the result of the exponentiation.
 *  @exception NUMERIC_ERROR If the exponent is negative.
 *  @exception OVERFLOW_ERROR If an integer overflow occurs.
 *)
const func integer: (in integer: base) ** (in integer: exponent)       is action "INT_POW";


(**
 *  Shift an integer number left by lshift bits.
 *  A << B is equivalent to A * 2 ** B
 *  @return the left shifted number.
 *  @exception OVERFLOW_ERROR If the shift amount is
 *             negative or greater equal 64 or
 *             if an integer overflow occurs.
 *)
const func integer: (in integer: number) << (in integer: lshift)    is action "INT_LSHIFT";


(**
 *  Shift an integer number right by rshift bits.
 *  A >> B is equivalent to A mdiv 2 ** B
 *  @return the right shifted number.
 *  @exception OVERFLOW_ERROR If the shift amount is
 *             negative or greater equal 64.
 *)
const func integer: (in integer: number) >> (in integer: rshift)    is action "INT_RSHIFT";


(**
 *  Increment an integer variable by a delta.
 *  @exception OVERFLOW_ERROR If an integer overflow occurs.
 *)
const proc: (inout integer: number) +:= (in integer: delta)         is action "INT_ADD_ASSIGN";


(**
 *  Decrement an integer variable by a delta.
 *  @exception OVERFLOW_ERROR If an integer overflow occurs.
 *)
const proc: (inout integer: number) -:= (in integer: delta)         is action "INT_SBTR_ASSIGN";


(**
 *  Multiply an integer number by a factor and assign the result back to number.
 *  @exception OVERFLOW_ERROR If an integer overflow occurs.
 *)
const proc: (inout integer: number) *:= (in integer: factor)        is action "INT_MULT_ASSIGN";


(**
 *  Shift a number left by lshift bits and assign the result back to number.
 *  @exception OVERFLOW_ERROR If the shift amount is
 *             negative or greater equal 64 or
 *             if an integer overflow occurs.
 *)
const proc: (inout integer: number) <<:= (in integer: lshift)       is action "INT_LSHIFT_ASSIGN";


(**
 *  Shift a number right by rshift bits and assign the result back to number.
 *  @exception OVERFLOW_ERROR If the shift amount is
 *             negative or greater equal 64.
 *)
const proc: (inout integer: number) >>:= (in integer: rshift)       is action "INT_RSHIFT_ASSIGN";


(**
 *  Binomial coefficient
 *   n ! k  returns  0                            for k < 0,
 *   n ! 0  returns  1,
 *   n ! 1  returns  n,
 *   n ! k  returns  0                            for n >= 0 and k > n,
 *   n ! k  returns  !n div (!k * !(n - k))       for k >= 0 and k <= n,
 *   n ! k  returns  (-1) ** k * (n + k - 1 ! k)  for n < 0 and k >= 0
 *  @return the binomial coefficient n over k
 *  @exception OVERFLOW_ERROR If the result would be less than
 *             integer.first or greater than integer.last.
 *)
const func integer: (in integer: n) ! (in integer: k)               is action "INT_BINOM";


(**
 *  Check if two integer numbers are equal.
 *  @return TRUE if the two numbers are equal,
 *          FALSE otherwise.
 *)
const func boolean: (in integer: number1) = (in integer: number2)   is action "INT_EQ";


(**
 *  Check if two integer numbers are not equal.
 *  @return FALSE if the two numbers are equal,
 *          TRUE otherwise.
 *)
const func boolean: (in integer: number1) <> (in integer: number2)  is action "INT_NE";


(**
 *  Check if number1 is less than number2.
 *  @return TRUE if number1 is less than number2,
 *          FALSE otherwise.
 *)
const func boolean: (in integer: number1) < (in integer: number2)   is action "INT_LT";


(**
 *  Check if number1 is greater than number2.
 *  @return TRUE if number1 is greater than number2,
 *          FALSE otherwise.
 *)
const func boolean: (in integer: number1) > (in integer: number2)   is action "INT_GT";


(**
 *  Check if number1 is less than or equal to number2.
 *  @return TRUE if number1 is less than or equal to number2,
 *          FALSE otherwise.
 *)
const func boolean: (in integer: number1) <= (in integer: number2)  is action "INT_LE";


(**
 *  Check if number1 is greater than or equal to number2.
 *  @return TRUE if number1 is greater than or equal to number2,
 *          FALSE otherwise.
 *)
const func boolean: (in integer: number1) >= (in integer: number2)  is action "INT_GE";


(**
 *  Compare two integer numbers.
 *  @return -1, 0 or 1 if the first argument is considered to be
 *          respectively less than, equal to, or greater than the
 *          second.
 *)
const func integer: compare (in integer: number1, in integer: number2) is action "INT_CMP";


(**
 *  Compute the hash value of an integer number.
 *  @return the hash value.
 *)
const func integer: hashCode (in integer: number)                is action "INT_HASHCODE";


(**
 *  Successor of an integer number.
 *  @return number + 1
 *  @exception OVERFLOW_ERROR If an integer overflow occurs.
 *)
const func integer: succ (in integer: number)                    is action "INT_SUCC";


(**
 *  Predecessor of an integer number.
 *  @return number - 1
 *  @exception OVERFLOW_ERROR If an integer overflow occurs.
 *)
const func integer: pred (in integer: number)                    is action "INT_PRED";


(**
 *  Compute the absolute value of an integer number.
 *  @return the absolute value.
 *  @exception OVERFLOW_ERROR If an integer overflow occurs.
 *)
const func integer: abs (in integer: number)                     is action "INT_ABS";


(**
 *  Compute the integer square root of an integer radicand.
 *  @return the integer square root.
 *  @exception NUMERIC_ERROR If the radicand is negative.
 *)
const func integer: sqrt (in integer: radicand)                  is action "INT_SQRT";


(**
 *  Compute the truncated base 10 logarithm of an integer number.
 *  The definition of ''log10'' is extended by defining log10(0) = -1.
 *   log10(10 ** A)        returns A        for A >= 0
 *   log10(pred(10 ** A))  returns pred(A)  for A >= 0
 *   log10(10)             returns 1
 *   log10(1)              returns 0
 *   log10(0)              returns -1
 *  @return the truncated base 10 logarithm.
 *  @exception NUMERIC_ERROR The number is negative.
 *)
const func integer: log10 (in integer: number)                   is action "INT_LOG10";


(**
 *  Compute the truncated base 2 logarithm of an integer number.
 *  The definition of ''log2'' is extended by defining log2(0) = -1.
 *   log2(2 ** A)        returns A        for A >= 0
 *   log2(pred(2 ** A))  returns pred(A)  for A >= 0
 *   log2(2)             returns 1
 *   log2(1)             returns 0
 *   log2(0)             returns -1
 *  @return the truncated base 2 logarithm.
 *  @exception NUMERIC_ERROR The number is negative.
 *)
const func integer: log2 (in integer: number)                    is action "INT_LOG2";


(**
 *  Determine if an integer number is odd.
 *  @return TRUE if the number is odd,
 *          FALSE otherwise.
 *)
const func boolean: odd (in integer: number)                     is action "INT_ODD";


(**
 *  Convert to integer.
 *  @return the unchanged number.
 *)
const func integer: ord (in integer: number)                     is action "INT_ICONV1";


(**
 *  Convert to integer.
 *  @return the unchanged number.
 *)
const func integer: (attr integer) conv (in integer: number)     is action "INT_ICONV3";


(**
 *  Convert an ''integer'' number to a [[string]].
 *  The number is converted to a string with decimal representation.
 *  For negative numbers a minus sign is prepended.
 *  @param number Number to be converted to a [[string]].
 *  @return the string result of the conversion.
 *  @exception MEMORY_ERROR Not enough memory to represent the result.
 *)
const func string: str (in integer: number)                      is action "INT_STR";


(**
 *  Convert an ''integer'' number to a [[string]].
 *  The number is converted to a string with decimal representation.
 *  For negative numbers a minus sign is prepended.
 *  @param number Number to be converted to a [[string]].
 *  @return the string result of the conversion.
 *  @exception MEMORY_ERROR Not enough memory to represent the result.
 *)
const func string: string (in integer: number)                   is action "INT_STR";


(**
 *  Convert an ''integer'' number to a [[string]].
 *  The number is converted to a string with decimal representation.
 *  For negative numbers a minus sign is prepended.
 *  @param number Number to be converted to a [[string]].
 *  @return the string result of the conversion.
 *  @exception MEMORY_ERROR Not enough memory to represent the result.
 *)
const func string: literal (in integer: number)                  is action "INT_STR";


(**
 *  Convert an ''integer'' number to a [[string]] using a radix.
 *  The conversion uses the numeral system with the given ''base''.
 *  Digit values from 10 upward are encoded with lower case letters.
 *  E.g.: 10 is encoded with a, 11 with b, etc.
 *  For negative numbers a minus sign is prepended.
 *   48879 radix 16   returns "beef"
 *   -48879 radix 16  returns "-beef"
 *  @param number Number to be converted to a [[string]].
 *  @param base Base of the numeral system used for the conversion.
 *  @return the string result of the conversion.
 *  @exception RANGE_ERROR If base < 2 or base > 36 holds.
 *  @exception MEMORY_ERROR Not enough memory to represent the result.
 *)
const func string: (in integer: number) radix (in integer: base) is action "INT_radix";


(**
 *  Convert an ''integer'' number to a [[string]] using a radix.
 *  The conversion uses the numeral system with the given ''base''.
 *  Digit values from 10 upward are encoded with upper case letters.
 *  E.g.: 10 is encoded with A, 11 with B, etc.
 *  For negative numbers a minus sign is prepended.
 *   48879 RADIX 16   returns "BEEF"
 *   -48879 RADIX 16  returns "-BEEF"
 *  @param number Number to be converted to a [[string]].
 *  @param base Base of the numeral system used for the conversion.
 *  @return the string result of the conversion.
 *  @exception RANGE_ERROR If base < 2 or base > 36 holds.
 *  @exception MEMORY_ERROR Not enough memory to represent the result.
 *)
const func string: (in integer: number) RADIX (in integer: base) is action "INT_RADIX";


(**
 *  Convert ''integer'' to [[string]] and pad it with zeros at the left side.
 *  The number is converted to a string with decimal representation.
 *  For negative numbers a minus sign is prepended.
 *   123 lpad0 5   returns "00123"
 *   -123 lpad0 5  returns "-0123"
 *   123 lpad0 2   returns "123"
 *   -123 lpad0 2  returns "-123"
 *  @param number Number to be converted to a [[string]].
 *  @param length Minimum length of the result.
 *  @return number as decimal [[string]] left padded with zeroes.
 *  @exception MEMORY_ERROR Not enough memory to represent the result.
 *)
const func string: (in integer: number) lpad0 (in integer: length) is action "INT_LPAD0";


(**
 *  Increment an integer variable.
 *  Increments ''number'' by 1.
 *  This is equivalent to:
 *   number := succ(number);
 *  @exception OVERFLOW_ERROR If an integer overflow occurs.
 *)
const proc: incr (inout integer: number)                         is action "INT_INCR";


(**
 *  Decrement an integer variable.
 *  Decrements ''number'' by 1.
 *  This is equivalent to:
 *   number := pred(number);
 *  @exception OVERFLOW_ERROR If an integer overflow occurs.
 *)
const proc: decr (inout integer: number)                         is action "INT_DECR";


(**
 *  Compute pseudo-random number in the range [low, high].
 *  The random values are uniform distributed.
 *  @return a random number such that low <= rand(low, high) and
 *          rand(low, high) <= high holds.
 *  @exception RANGE_ERROR The range is empty (low > high holds).
 *)
const func integer: rand (in integer: low, in integer: high)     is action "INT_RAND";


(**
 *  Number of bits in the minimum two's-complement representation.
 *  The high bits equivalent to the sign bit are not part of the
 *  minimum two's-complement representation.
 *   bitLength(0)   returns 0
 *   bitLength(1)   returns 1
 *   bitLength(4)   returns 3
 *   bitLength(-1)  returns 0
 *   bitLength(-2)  returns 1
 *   bitLength(-4)  returns 2
 *  @return the number of bits.
 *)
const func integer: bitLength (in integer: number)               is action "INT_BIT_LENGTH";


(**
 *  Number of lowest-order zero bits in the two's-complement representation.
 *  This is equal to the index of the lowest-order one bit (indices start with 0).
 *  If there are only zero bits (''number'' is 0) the result is -1.
 *   lowestSetBit(0)   returns -1
 *   lowestSetBit(1)   returns  0
 *   lowestSetBit(4)   returns  2
 *   lowestSetBit(-1)  returns  0
 *   lowestSetBit(-2)  returns  1
 *   lowestSetBit(-4)  returns  2
 *  @return the number of lowest-order zero bits or -1 for lowestSetBit(0).
 *)
const func integer: lowestSetBit (in integer: number)            is action "INT_LOWEST_SET_BIT";


(**
 *  Convert a [[string]] to an ''integer'' number.
 *  The [[string]] must contain an integer literal consisting of an
 *  optional + or - sign, followed by a sequence of digits. Other
 *  characters as well as leading or trailing whitespace characters
 *  are not allowed. The sequence of digits is taken to be decimal.
 *   integer("12345")                returns 12345
 *   integer("-12345")               returns -12345
 *   integer("+12345")               returns 12345
 *   integer(" 12345")               raises RANGE_ERROR
 *   integer("12345 ")               raises RANGE_ERROR
 *   integer("-")                    raises RANGE_ERROR
 *   integer("zero")                 raises RANGE_ERROR
 *   integer("9223372036854775807")  returns 9223372036854775807
 *   integer("9223372036854775808")  raises RANGE_ERROR
 *  @return the ''integer'' result of the conversion.
 *  @exception RANGE_ERROR If the string is empty or it does not contain
 *             an integer literal or if the integer literal is too big
 *             or too small to be represented as ''integer'' value.
 *)
const func integer: integer (in string: stri)                    is action "INT_PARSE1";


(**
 *  Convert a [[string]] to an ''integer'' number.
 *  The [[string]] must contain an integer literal consisting of an
 *  optional + or - sign, followed by a sequence of digits. Other
 *  characters as well as leading or trailing whitespace characters
 *  are not allowed. The sequence of digits is taken to be decimal.
 *   integer parse "12345"                returns 12345
 *   integer parse "-12345"               returns -12345
 *   integer parse "+12345"               returns 12345
 *   integer parse " 12345"               raises RANGE_ERROR
 *   integer parse "12345 "               raises RANGE_ERROR
 *   integer parse "-"                    raises RANGE_ERROR
 *   integer parse "zero"                 raises RANGE_ERROR
 *   integer parse "9223372036854775807"  returns 9223372036854775807
 *   integer parse "9223372036854775808"  raises RANGE_ERROR
 *  @return the ''integer'' result of the conversion.
 *  @exception RANGE_ERROR If the string is empty or it does not contain
 *             an integer literal or if the integer literal is too big
 *             or too small to be represented as ''integer'' value.
 *)
const func integer: (attr integer) parse (in string: stri) is
    return integer(stri);


(**
 *  Convert a numeric [[string]], with a specified radix, to an ''integer''.
 *  The numeric [[string]] must contain the representation of an integer
 *  in the specified radix. It consists of an optional + or - sign,
 *  followed by a sequence of digits in the specified radix. Digit values
 *  from 10 upward can be encoded with upper or lower case letters.
 *  E.g.: 10 can be encoded with A or a, 11 with B or b, etc. Other
 *  characters as well as leading or trailing whitespace characters
 *  are not allowed.
 *   integer("beef", 16)     returns  48879
 *   integer("-177", 8)      returns   -127
 *   integer("10101010", 2)  returns    170
 *   integer("Cafe", 16)     returns  51966
 *  @param stri Numeric string to be converted to an integer.
 *  @param base Radix of the integer in the ''stri'' parameter.
 *  @return the ''integer'' result of the conversion.
 *  @exception RANGE_ERROR If base < 2 or base > 36 holds or
 *             the string is empty or it does not contain an integer
 *             literal with the specified base.
 *)
const func integer: integer (in string: stri, in integer: base)  is forward;


(**
 *  Convert an ''integer'' number to a [[string]] in scientific notation.
 *  Scientific notation uses a decimal significand and a decimal exponent.
 *  The significand has an optional sign and exactly one digit before the
 *  decimal point. The fractional part of the significand is rounded
 *  to the specified number of digits (''precision''). Halfway cases are
 *  rounded away from zero. The fractional part is followed by the
 *  letter e and an exponent, which is always signed. The value zero is
 *  never written with a negative sign.
 *   12345 sci 4     returns "1.2345e+4"
 *   12345 sci 3     returns "1.235e+4"
 *   12345 sci 2     returns "1.23e+4"
 *   3141592 sci 0   returns "3e+6"
 *   27182818 sci 0  returns "3e+7"
 *   2**62 sci 6     returns "4.611686e+18"
 *   -1 sci 3        returns "-1.000e+0"
 *   -0 sci 2        returns "0.00e+0"
 *  @param number Number to be converted to a string.
 *  @param precision Number of digits after the decimal point.
 *         If the ''precision'' is zero the decimal point is omitted.
 *  @return the string result of the conversion.
 *  @exception RANGE_ERROR If the ''precision'' is negative.
 *)
const func string: (in integer: number) sci (in integer: precision)  is forward;


const proc: DECLARE_MIN_MAX (in type: aType) is func
  begin

    (**
     *  Determine the minimum of two values.
     *  @return the minimum of the two values.
     *)
    const func aType: min (in aType: value1, in aType: value2) is
        return value1 < value2 ? value1 : value2;

    (**
     *  Determine the maximum of two values.
     *  @return the maximum of the two values.
     *)
    const func aType: max (in aType: value1, in aType: value2) is
        return value1 > value2 ? value1 : value2;

  end func;


DECLARE_TERNARY(integer);
DECLARE_MIN_MAX(integer);