///////////////////////////////////////////////////////////////////////////////
//                             Lisaac Library                                //
//                                                                           //
//                   LSIIT - ULP - CNRS - INRIA - FRANCE                     //
//                                                                           //
//   This program is free software: you can redistribute it and/or modify    //
//   it under the terms of the GNU General Public License as published by    //
//   the Free Software Foundation, either version 3 of the License, or       //
//   (at your option) any later version.                                     //
//                                                                           //
//   This program 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 General Public License for more details.                            //
//                                                                           //
//   You should have received a copy of the GNU General Public License       //
//   along with this program.  If not, see <http://www.gnu.org/licenses/>.   //
//                                                                           //
//                     http://isaacproject.u-strasbg.fr/                     //
///////////////////////////////////////////////////////////////////////////////
Section Header
  
  + name   := Expanded REAL;
  
  - export :=
  REAL_32, REAL_64, REAL_80,
  REAL_16_16,  REAL_24_8,  REAL_26_6,
  UREAL_16_16, UREAL_24_8, UREAL_26_6;

  - copyright   := "2003-2005 Jérome Boutet, 2003-2007 Benoit Sonntag, 2007 Xavier Oswald";
  - comment := "Generic real number.";
  
  - type    := `float`;
  - default := 0.0;

Section Insert
  
  - parent_numeric:NUMERIC := NUMERIC;
  
Section Public
  
  - '+'  Left 80  other:SELF :SELF <- `@Self + @other`:SELF;

  - bound_test low:INTEGER_64 to up:UINTEGER_64 :BOOLEAN <-  
  (
    TRUE
  );
  
  - pi   :SELF <- 3.14159265358979323846;

  - atan :SELF <- `atan(@Self)`:SELF;

  - sqrt :SELF <- `sqrt(@Self)`:SELF;

  - log  :SELF <- `log(@Self)`:SELF;

  - sin  :SELF <- `sin(@Self)`:SELF;

  - cos  :SELF <- `cos(@Self)`:SELF;

  - pow exp:SELF :SELF <- `pow(@Self,@exp)`:SELF;

  - '**' Right 120 exp:SELF :SELF <-
  (
    Self.pow exp
  );

  - '^' Right 120 exp:SELF :SELF <- 
  (
    Self.pow exp
  );

  //
  // Convertion format with test.
  //
  
  - floor:INTEGER <-
  // Greatest integral value no greater than Current.
  (     
    to_raw_integer
  );
  
  - ceiling:INTEGER <-
  // Smallest integral value no smaller than Current.
  (     
    (Self + 0.9999).floor
  );
  
  - rounded:INTEGER <-
  // Rounded integral value.
  (
    (Self + 0.5).floor
  );
  
  - truncated_to_integer:INTEGER <- floor;
  // Integer part (largest absolute value no greater than Current).
  
  //
  // Comparaison.
  //
  
  - '~=' other:SELF :BOOLEAN <-
  (
    (Self - other).abs < 0.001
  );
  
  - is_not_a_number:BOOLEAN <- deferred;
  
  - is_infinity:BOOLEAN <- deferred;
  
  //
  // Print.
  //
  
  - append_in buffer:STRING <-
  (
    append_in buffer decimal 4;
  );
  
  - append_in buffer:STRING format n:INTEGER decimal d:INTEGER <-
  (
    append_in buffer format n with ' ' decimal d;
  );
  
  - append_in buffer:STRING format n:INTEGER with c:CHARACTER decimal d:INTEGER <-
  [ 
    -? {n >= 3};
  ]
  ( + old_count:INTEGER;
    
    old_count := buffer.count;
    append_in buffer decimal d;
    buffer.insert c to old_count on (n - (buffer.count - old_count));
  );
  
  - append_in buffer:STRING decimal n:INTEGER <-
  ( + val:SELF;
    + val_10:INTEGER;    
    + char:CHARACTER;
    + i:INTEGER;
    
    get_map Self;
    // Sign.
    (sign).if {
      val := Self;
    } else {
      buffer.add_last '-';
      val := - Self;
    };    
    (is_zero).if {
      // Zero case.      
      buffer.add_last '0';
    }.elseif {is_infinite} then {
      // Infinite case.
      buffer.append "Infinite";
    }.elseif {is_nan} then {
      // Nan case.
      buffer.append "Nan";
    } else {    
      // Value case = 1.mantisse x 2^(exp-127)
      (val > INTEGER.maximum.to_real_32).if {
        scientific_append_in buffer;
      } else {
        val_10 := val.to_integer;	  
        val_10.append_in buffer;		
        val := val - val_10;	  
        (n != 0).if {
          buffer.add_last '.';
          i := n;
          {(val != 0) && {i > 0}}.while_do {	  
            val := val * 10;
            val_10 := val.to_integer;
            char := val_10.decimal_digit;
            buffer.add_last char;
            val := val - val_10;
            i := i - 1;
          };	  	  
          buffer.extend_multiple '0' by i;
        };
      };
    };    
  );
  
  - scientific_append_in buffer:STRING <- 
  ( + val:SELF;
    + val_10:INTEGER;
    + exp_10:INTEGER;
    + char:CHARACTER;
    
    get_map Self;
    // Sign.
    (sign).if {
      val := Self;
    } else {
      buffer.add_last '-';
      val := - Self;
    };    
    (is_zero).if {
      // Zero case.
      buffer.add_last '0';
    }.elseif {is_infinite} then {
      // Infinite case.
      buffer.append "Infinite";
    }.elseif {is_nan} then {
      // Nan case.
      buffer.append "Nan";
    } else {
      // Value case.
      {val > 10}.while_do {
        val    := val / 10;
        exp_10 := exp_10 + 1;
      };
      {val < 0}.while_do {
        val    := val * 10;
        exp_10 := exp_10 - 1;
      };
      val_10 := val.to_integer;
      char := val_10.decimal_digit;
      buffer.add_last char;
      buffer.add_last '.';
      val := val - val_10;
      {val != 0}.while_do {	  
        val := val * 10;
        val_10 := val.to_integer;
        char := val_10.decimal_digit;
        buffer.add_last char;
        val := val - val_10;
      };
      buffer.add_last 'E';
      exp_10.append_in buffer;
    };
  );
  
  - append_in buffer:STRING format_c fmt:ABSTRACT_STRING <-
  ( + nc_buf,nc_fmt:NATIVE_ARRAY[CHARACTER];    
    + old_count,cap:INTEGER;
    
    old_count := buffer.count;
    cap       := buffer.capacity - old_count;
    nc_buf    := buffer.to_external + old_count;
    nc_fmt    := fmt.to_external;
    `snprintf(@nc_buf,@cap,@nc_fmt,@Self)`;
    buffer.restore_after_external;
  );    
  
  - print_decimal s:INTEGER <-
  (
    string_tmp.clear;
    append_in string_tmp decimal s;
    string_tmp.print;
  );
  
  - print_int i:INTEGER decimal d:INTEGER <-
  (
    print_format (i+d+1) decimal d;
  );
  
  - print_format s:INTEGER decimal d:INTEGER <-
  [
    -? {s > d};
  ]
  ( 
    print_format s with ' ' decimal d;
  );

  - print_format s:INTEGER with c:CHARACTER decimal d:INTEGER <-
  [
    -? {s > d};
  ]
  ( 
    string_tmp.clear;
    append_in string_tmp format s with c decimal d;
    string_tmp.print;
  );
  
  - print_format_c fmt:ABSTRACT_STRING <-
  (
    string_tmp.clear;
    append_in string_tmp format_c fmt;
    string_tmp.print;
  );
  
