
(********************************************************************)
(*                                                                  *)
(*  float.s7i     Floating point support library                    *)
(*  Copyright (C) 1993, 1994, 2005  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.                       *)
(*                                                                  *)
(********************************************************************)


include "enable_io.s7i";


(**
 *  Single precision floating point numbers.
 *)
const type: float is          subtype object;


$ system "float" is float;

const proc: destroy (ref float param)                            is noop;
const proc: (ref float param) ::= (ref float param)              is action "FLT_CREATE";
IN_PARAM_IS_VALUE(float);

const float: (attr float) . value is 0.0;


const proc: (inout float param) := (ref float param)             is action "FLT_CPY";


(**
 *  Plus sign for float numbers.
 *  @return its operand unchanged.
 *)
const func float: + (ref float: number)                          is action "FLT_PLUS";


(**
 *  Minus sign, negate a float ''number''.
 *  @return the negated value of the number.
 *)
const func float: - (ref float: number)                          is action "FLT_MINUS";


(**
 *  Add two float numbers.
 *  @return the sum of the two numbers.
 *)
const func float: (ref float: summand1) + (ref float: summand2)  is action "FLT_ADD";


(**
 *  Compute the subtraction of two float numbers.
 *  @return the difference of the two numbers.
 *)
const func float: (ref float: minuend) - (ref float: subtrahend) is action "FLT_SBTR";


(**
 *  Multiply two float numbers.
 *  @return the product of the two numbers.
 *)
const func float: (ref float: factor1) * (ref float: factor2)    is action "FLT_MULT";


(**
 *  Compute the division of two float numbers.
 *  @return the quotient of the division.
 *)
const func float: (ref float: dividend) / (ref float: divisor)   is action "FLT_DIV";


(**
 *  Compute the exponentiation of a float ''base'' by an integer ''exponent''.
 *  @return the result of the exponentation.
 *)
const func float: (ref float: base) ** (ref integer: exponent)   is action "FLT_IPOW";


(**
 *  Compute the exponentiation of a float ''base'' by an float ''exponent''.
 *  @return the result of the exponentation.
 *)
const func float: (ref float: base) ** (ref float: exponent)     is action "FLT_POW";


(**
 *  Increment a float ''number'' by a ''delta''.
 *)
const proc: (inout float: number) +:= (ref float: delta)         is action "FLT_GROW";


(**
 *  Decrement a float ''number'' by a ''delta''.
 *)
const proc: (inout float: number) -:= (ref float: delta)         is action "FLT_SHRINK";


(**
 *  Multiply a float number by a factor and assign the result back to number.
 *)
const proc: (inout float: number) *:= (ref float: factor)        is action "FLT_MULT_ASSIGN";


(**
 *  Divide a float ''number'' by a ''divisor'' and assign the result back to ''number''.
 *)
const proc: (inout float: number) /:= (ref float: divisor)       is action "FLT_DIV_ASSIGN";


(**
 *  Check if two float numbers are equal.
 *  According to IEEE 754 a NaN is not equal to any float value.
 *  Therefore ''NaN = any_value'' and ''any_value = NaN''
 *  always return FALSE. Even ''NaN = NaN'' returns FALSE.
 *  @return TRUE if both numbers are equal, FALSE otherwise.
 *)
const func boolean: (ref float: number1) = (ref float: number2)  is action "FLT_EQ";


(**
 *  Check if two float numbers are not equal.
 *  According to IEEE 754 a NaN is not equal to any float value.
 *  Therefore ''NaN <> any_value'' and ''any_value <> NaN''
 *  always return TRUE. Even ''NaN <> NaN'' returns TRUE.
 *  @return FALSE if both numbers are equal, TRUE otherwise.
 *)
const func boolean: (ref float: number1) <> (ref float: number2) is action "FLT_NE";


(**
 *  Check if ''number1'' is less than ''number2''.
 *  According to IEEE 754 a NaN is neither less than,
 *  equal to, nor greater than any value, including itself.
 *  When ''number1'' or ''number2'' is NaN, the result
 *  is FALSE;
 *  @return TRUE if ''number1'' is less than ''number2'',
 *          FALSE otherwise.
 *)
const func boolean: (ref float: number1) < (ref float: number2)  is action "FLT_LT";


(**
 *  Check if ''number1'' is greater than ''number2''.
 *  According to IEEE 754 a NaN is neither less than,
 *  equal to, nor greater than any value, including itself.
 *  When ''number1'' or ''number2'' is NaN, the result
 *  is FALSE;
 *  @return TRUE if ''number1'' is greater than ''number2'',
 *          FALSE otherwise.
 *)
const func boolean: (ref float: number1) > (ref float: number2)  is action "FLT_GT";


(**
 *  Check if ''number1'' is less than or equal to ''number2''.
 *  According to IEEE 754 a NaN is neither less than,
 *  equal to, nor greater than any value, including itself.
 *  When ''number1'' or ''number2'' is NaN, the result
 *  is FALSE;
 *  @return TRUE if ''number1'' is less than or equal to ''number2'',
 *          FALSE otherwise.
 *)
const func boolean: (ref float: number1) <= (ref float: number2) is action "FLT_LE";


(**
 *  Check if ''number1'' is greater than or equal to ''number2''.
 *  According to IEEE 754 a NaN is neither less than,
 *  equal to, nor greater than any value, including itself.
 *  When ''number1'' or ''number2'' is NaN, the result
 *  is FALSE;
 *  @return TRUE if ''number1'' is greater than or equal to ''number2'',
 *          FALSE otherwise.
 *)
const func boolean: (ref float: number1) >= (ref float: number2) is action "FLT_GE";


const func integer: (attr integer) cast (in float param)         is action "FLT_CAST";
const func float: (attr float) cast (in integer param)           is action "FLT_ICAST";


(**
 *  Positive infinity.
 *  Infinity is the result of 1.0 / 0.0 .
 *)
const float: Infinity is 1.0 / 0.0;


(**
 *  Not-a-Number (NaN) value.
 *  Represents an undefined or unrepresentable value.
 *  NaN is the result of 0.0 / 0.0 .
 *  Checking for NaN is done with the function isNaN.
 *)
const float: NaN is 0.0 / 0.0;


(**
 *  Compare two float numbers.
 *  Because ''compare'' is used to sort float values, a total
 *  order of all float values is needed. Therefore ''compare''
 *  considers ''NaN'' as equal to itself and greater than
 *  ''Infinite''.
 *  @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 float: number1, in float: number2) is action "FLT_CMP";


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


(**
 *  Convert a float number to a [[string]].
 *  The number is converted to a string with decimal representation.
 *  The sign of negative zero (-0.0) is ignored.
 *   str(16.125)    returns "16.125"
 *   str(-0.0)      returns "0.0"
 *   str(Infinity)  returns "Infinity"
 *   str(-Infinity) returns "-Infinity"
 *   str(NaN)       returns "NaN"
 *  @return the string result of the conversion.
 *  @exception MEMORY_ERROR Not enough memory to represent the result.
 *)
const func string: str (ref float: number)                       is action "FLT_STR";


(**
 *  Convert a [[string]] to a float number.
 *  @return the float result of the conversion.
 *  @exception RANGE_ERROR When the string contains not a float literal.
 *)
const func float: (attr float) parse (in string: stri)           is action "FLT_PARSE";


(**
 *  Convert a float to a [[string]] in decimal fixed point notation.
 *  The ''precision'' parameter specifies the number of digits after
 *  the decimal point. When the ''precision'' is zero the decimal
 *  point is omitted. When all digits in the result are 0 a negative
 *  sign is omitted.
 *   0.012345 digits 4   returns "0.0123"
 *   1.2468 digits 2     returns "1.25"
 *   3.1415 digits 0     returns "3"
 *   0.125 digits 2      returns "0.12"
 *   0.375 digits 2      returns "0.38"
 *   Infinity digits 5   returns "Infinity"
 *   -Infinity digits 6  returns "-Infinity"
 *   NaN digits 7        returns "NaN"
 *   -0.004 digits 2     returns "0.00"
 *  @return the string result of the conversion.
 *  @exception MEMORY_ERROR Not enough memory to represent the result.
 *)
const func string: (ref float: number) digits (ref integer: precision) is action "FLT_DGTS";


(**
 *  Convert a float to a [[string]] in scientific notation.
 *  Scientific notation uses a decimal float with optional sign, which
 *  has only one digit before the decimal point. The float is followed
 *  by the letter e and an exponent, which is always signed.
 *  The ''precision'' parameter specifies the number of digits after
 *  the decimal point. When the ''precision'' is zero the decimal
 *  point is omitted. When all digits in the result are 0 a negative
 *  sign is omitted.
 *   0.012345 sci 4   returns "1.2345e-2"
 *   1.2468 sci 2     returns "1.25e+0"
 *   3.1415 sci 0     returns "3e+0"
 *   0.125 sci 1      returns "1.2e-1"
 *   0.375 sci 1      returns "3.8e-1"
 *   Infinity sci 5   returns "Infinity"
 *   -Infinity sci 6  returns "-Infinity"
 *   NaN sci 7        returns "NaN"
 *   -0.004 sci 2     returns "-4.00e-3"
 *  @return the string result of the conversion.
 *  @exception MEMORY_ERROR Not enough memory to represent the result.
 *)
const func string: (ref float: number) sci (ref integer: precision)    is action "FLT_SCI";


(**
 *  Set the number of exponent digits in a scientific float notation.
 *  When ''sciNumber'' contains a [[string]] in scientific float notation
 *  the exponent is changed to contain at least ''expDigits'' digits.
 *  When ''sciNumber'' contains not a string in scientific float
 *  notation it is returned unchanged. The ''exp'' operator is
 *  intended to be used together with the ''sci'' operator
 *   0.012345 sci 4 exp 2   returns "1.2345e-02"
 *   1.2468e15 sci 2 exp 1  returns "1.25e+15"
 *   3.1415 sci 0 exp 3     returns "3e+000"
 *   0.125 sci 1 exp 2      returns "1.2e-01"
 *   0.375 sci 1 exp 2      returns "3.8e-01"
 *   Infinity sci 5 exp 2   returns "Infinity"
 *   -Infinity sci 6 exp 2  returns "-Infinity"
 *   NaN sci 7 exp 2        returns "NaN"
 *   -0.004 sci 2 exp 2     returns "-4.00e-03"
 *  @return the string result of the conversion.
 *  @exception MEMORY_ERROR Not enough memory to represent the result.
 *)
const func string: (in string: sciNumber) exp (in integer: expDigits) is func
  result
    var string: paddedStri is "";
  local
    var integer: pos is 0;
  begin
    pos := length(sciNumber);
    while pos >= 1 and sciNumber[pos] >= '0' and sciNumber[pos] <= '9' do
      decr(pos);
    end while;
    if pos >= 2 and sciNumber[pred(pos)] = 'e' and
        (sciNumber[pos] = '+' or sciNumber[pos] = '-') and
        expDigits > length(sciNumber) - pos then
      paddedStri := sciNumber[.. pos] & "0" mult expDigits - length(sciNumber) + pos &
          sciNumber[succ(pos) ..];
    else
      paddedStri := sciNumber;
    end if;
  end func;


(**
 *  Convert an integer to a float.
 *  @return the float result of the conversion.
 *)
const func float: flt (ref integer: number)                      is action "FLT_IFLT";


(**
 *  Convert an integer to a float.
 *  @return the float result of the conversion.
 *)
const func float: (attr float) conv (ref integer: number)        is action "FLT_ICONV";


(**
 *  Round towards the nearest integer.
 *  Halfway cases are rounded away from zero.
 *  @return the rounded value.
 *)
const func integer: round (ref float: number)                    is action "FLT_ROUND";


(**
 *  Truncate towards zero.
 *  The fractional part of a number is discarded.
 *  @return the nearest integer not larger in absolute value
 *          than the argument.
 *)
const func integer: trunc (ref float: number)                    is action "FLT_TRUNC";


(**
 *  Determine if a number has a Not-a-Number (NaN) value.
 *  NaN represents an undefined or unrepresentable value.
 *  @return TRUE if the number has a Not-a-Number (NaN) value,
 *          FALSE otherwise.
 *)
const func boolean: isNaN (ref float: number)                    is action "FLT_ISNAN";


(**
 *  Determine if a number is -0.0.
 *  This function is the only possibility to determine if a number
 *  is -0.0. The comparison operators (=, <>, <, >, <=, >=) and
 *  the function ''compare'' treat 0.0 and -0.0 as equal. The
 *  ''digits'' operator and the ''str'' function convert -0.0 to
 *  the [[string]] "0.0".
 *  @return TRUE if the number is -0.0,
 *          FALSE otherwise.
 *)
const func boolean: isNegativeZero (in float: number)            is action "FLT_ISNEGATIVEZERO";


(**
 *  Convert a float number to a [[string]].
 *  The number is converted to a string with decimal representation.
 *  @return the string result of the conversion.
 *  @exception MEMORY_ERROR Not enough memory to represent the result.
 *)
const func string: literal (ref float: number)                   is action "FLT_STR";


(**
 *  Compute pseudo-random number in the range [low, high].
 *  The random values are uniform distributed.
 *  @return the computed pseudo-random number.
 *  @exception RANGE_ERROR The range is empty (low > high holds).
 *)
const func float: rand (ref float: low, ref float: high)         is action "FLT_RAND";


enable_io(float);
