///////////////////////////////////////////////////////////////////////////////
//                             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 INTEGER;
  
  - export := 
  // Integer: 
  UINTEGER_8,  UINTEGER_16, UINTEGER_32, UINTEGER_64,
  INTEGER_8 ,  INTEGER_16 , INTEGER_32 , INTEGER_64 ,
  UINTEGER_BIG,//INTEGER_BIG,
  // Fixed real:
  UREAL_16_16, UREAL_24_8 , UREAL_26_6 , 
  REAL_16_16 , REAL_24_8  , REAL_26_6  , 
  // Float Real:
  REAL, REAL_32, REAL_64, REAL_80,
  // Other:
  POINTER;
 
  - comment := "Generic Integer.";
  
  - type    := `int`;
  - default := 0;

Section Insert
  
  - parent_numeric:NUMERIC := NUMERIC;
  
Section Public
    
  //
  // Range.
  //
  
  - maximum:UINTEGER_64 <- 07FFFFFFFh.to_raw_uinteger_64; 
  
  - minimum:INTEGER_64  <- (- 07FFFFFFFh).to_raw_integer_64; 
  
  //
  // Binary Operator.
  //
  
  - '%'  Left 100 other:SELF    :SELF <- Self - ((Self / other) * other);
  
  - '%#' Left 100 other:INTEGER :SELF <- Self % other;
  
  - '**' Right 120 exp:SELF :SELF <-
  ( + result:SELF;
    
    (exp = 0).if {
      result := 1;
    } else {
      ((exp & 1) = 0).if {
        result := (Self * Self) ** (exp / 2);
      } else {
        result := Self * (Self ** (exp - 1));
      };
    };
    result
  );

  - pow exp:SELF :SELF <-
  (
    Self ** exp
  );

  //
  // Conversion format with test.
  //
  
  - bound_test low:INTEGER_64 to up:UINTEGER_64 :BOOLEAN <-
  (
    TRUE
  );
  
//  - to_integer:INTEGER <- Self;
  
  - to_pointer:POINTER <-
  (
    to_raw_pointer
  );
  
  //
  // BCD Format. 
  //
  
  - to_binary:SELF <-
  // Self is BCD formatted, convert to binary value
  ( + result:SELF;
    + val,mul:SELF;
    
    val := Self;
    mul := 1;
    {val!=0}.while_do {
      result := result + (val & 0Fh) * mul;
      mul    := mul * 10;
      val    := val >> 4;
    };
    
    result
  );
  
  - to_bcd:SELF <-
  // Self is binary formatted, convert to bcd value
  ( + result:SELF;
    + val,mul:SELF;
    
    val := Self;
    {val != 0}.while_do {
      result := result | ((val % 10) << mul);
      mul    := mul + 4;
      val    := val / 10;
    };
    
    result    
  );
  
  //
  // Facility typing.
  //
  
  - kb:SELF <- Self << 10;
  
  - mb:SELF <- Self << 20;
  
  - gb:SELF <- Self << 30;
  
  - tb:SELF <- Self << 40;
  
  //
  // Logic Operator
  //
      
  - '&'  Left 100 other:SELF :SELF <- `6`;
  
  - '|'  Left 80  other:SELF :SELF <- ~(~Self & ~other);
  
  - '^'  Left 80  other:SELF :SELF <- (~Self & other) | (Self & ~other);

  - '>>' Left 100 other:INTEGER :SELF <- `7`;

  - '<<' Left 100 other:INTEGER :SELF <- `8`;
  
  //
  // Unary operator
  //
    
  - '~' :SELF <- -Self - SELF.one; //(-SELF.one) - Self;
  
  //
  // Test. 
  //
  
  - align_power step:SELF :SELF <-
  [ 
    -? {step.is_power_2};
  ]
  ( + a:SELF;
    
    a := step - 1;
    (Self + a) & (~ a)
  );
  
  - is_power_2:BOOLEAN <-
  ( + val:SELF;
    + result:BOOLEAN;
    
    (Self != 0).if {
      val := Self;
      {val.is_even}.while_do {
	val := val >> 1;
      };
      result := val = 1;
    };
    result
  );
    
  //
  // Function :
  //
  
  - sqrt:SELF <-
  ( + r,x:SELF;
    
    x:=(Self + 1) >> 1;
    {x > r}.while_do {
      x:=x-r;
      r:=r+1;
    };
    r
  );
  
  - factorial:SELF <-
  [
    -? {Self >= 0};
  ]
  ( + result,value:SELF;
   
    result := 1;
    value  := Self;
    {value <= 1}.until_do {
      result := result * value;
      value  := value - 1;
    };
    result
  );
  
  - fibonacci:SELF <-
  [
    -? {Self >= 0};
  ]
  ( + result:SELF;
        
    (Self <= 1).if {
      result := 1;
    } else {
      result := (Self-1).fibonacci + (Self-2).fibonacci;
    };
    result
  );
  
  - is_odd:BOOLEAN  <- (Self & 1) = 1;  // Is odd ?
  
  - is_even:BOOLEAN <- ! is_odd; // Is even ?
  
  - gcd other:SELF :SELF <-
  // Great Common Divisor of `self' and `other'.
  [
    -? {Self  >= 0};
    -? {other >= 0};
  ]
  ( + result:SELF;
    
    (other = 0).if {
      result := Self;
    } else {
      result := other.gcd (Self % other);
    };
    
    result
  )
  [
    +? {result == other.gcd self};
  ];
 
  //
  // Random
  //

  - random :SELF <-
  // Random number between 0 to `maximum' in SELF.
  ( - r_seed:UINTEGER_32;
    r_seed := (110351524 * r_seed + 12345) % 214748364;
    CONVERT[UINTEGER_64,SELF].on (maximum & r_seed)
  )
  [
    +? {Result >= 0};
  ];

  - random_upper upper:SELF :SELF <-
  // Random number between 0 to `upper'.
  [ 
    -? {upper > 0}; 
  ]
  (
    random % (upper+1)
  )
  [
    +? {Result.in_range 0 to upper};
  ];

  - random_between lower:SELF and upper:SELF :SELF <-
  // Random number between `lower' to `upper'.
  [
    -? {lower < upper};
  ]
  ( 
    lower + random_upper (upper-lower)
  )
  [
    +? {Result.in_range lower to upper};
  ];
  
  //
  // Looping.
  //
  
  - times action:BLOCK <-
  (
    1.to Self do { i:INTEGER;
      action.value;
    };
  );
  
  //
  // Convertion
  //
    
  - append_in buffer:STRING <- to_integer_32.append_in buffer;
  // Append in the `buffer' the equivalent of `to_string'. No new STRING
  // creation during the process.
 
  - decimal_digit:CHARACTER <-
  // Gives the corresponding CHARACTER for range 0..9.
  [
    -? {in_range 0 to 9};
  ]
  ( 
    (Self.to_integer_8 + '0'.code).to_character
  )
  [
    +? {"0123456789".has Result};
    +? {Result.decimal_value = Self};
  ];
  
  - digit:CHARACTER <- decimal_digit;
  
  - hexadecimal_digit:CHARACTER <-
  // Gives the corresponding CHARACTER for range 0..15.
  [
    -? {in_range 0 to 15};
  ]
  ( + result:CHARACTER;
        
    (Self <= 9).if {
      result := digit;
    } else {
      result := ('A'.code + (Self - 10).to_integer_8).to_character;
    };    
    result
  )
  [
    +? {"0123456789ABCDEF".has Result};
  ];
  
  - to_character:CHARACTER <- to_integer_8.to_character;
  // Return the coresponding ASCII character.
  
  - to_octal:SELF <-
  // Gives coresponding octal value.
  ( + result:SELF;
    
    deferred;
    result
  );
  
  - to_hexadecimal:STRING <-
  // Convert the hexadecimal view of `self' into a new allocated
  // STRING. For example, if `self' is -1 the new STRING is
  // "FFFFFFFF" on a 32 bit machine.
  // Note: see also `to_hexadecimal_in' to save memory.
  ( + result:STRING;
    
    result := STRING.create 8;
    to_hexadecimal_in result;
    result
  );
  
  - to_hexadecimal_format s:INTEGER :STRING <-
  // Convert the hexadecimal view of `self' into a new allocated
  // STRING. For example, if `self' is -1 the new STRING is
  // "FFFFFFFF" on a 32 bit machine.
  // Note: see also `to_hexadecimal_in' to save memory.
  ( + result:STRING;
    
    result := STRING.create 8;
    to_hexadecimal_in result format s;
    result
  ); 
  
  - to_hexadecimal_in buffer:STRING <-
  // Append in `buffer' the equivalent of `to_hexadecimal'. No new STRING
  // creation during the process.
  [ -? {buffer!=NULL}; ]
  ( + val:SELF;
    + i,old_count:INTEGER;
        
    (Self = 0).if {
      buffer.extend '0';
    } else {
      i := buffer.count + 1;
      val := Self;
      {val != 0}.while_do { //JBJB 
	buffer.extend ((val & 15).hexadecimal_digit);
	val := val >> 4;
      };
      old_count := buffer.count;
      {i >= old_count}.until_do {
	buffer.swap i with old_count;
	old_count := old_count - 1;
	i := i + 1;
      };
    };
  );
  
  - to_hexadecimal_in buffer:STRING format s:INTEGER <-
  // Append in `buffer' the equivalent of `to_hexadecimal'. No new STRING
  // creation during the process.
  [ -? {buffer!=NULL}; ]
  ( + val:SELF;
    + i,old_count:INTEGER;
        
    (Self = 0).if {
      buffer.extend '0';
    } else {
      i := buffer.count + 1;
      val := Self;
      {val != 0}.while_do { //JBJB 
	buffer.extend ((val & 15).hexadecimal_digit);
	val := val >> 4;
      };
      old_count := buffer.count;
      {i >= old_count}.until_do {
	buffer.swap i with old_count;
	old_count := old_count - 1;
	i := i + 1;
      };
    };
    buffer.precede_multiple '0' by (s.to_integer - buffer.count);
  ); 

  - to_binary_in buffer:STRING format s:INTEGER <-
  // Append in `buffer' the equivalent of `to_binary_string'. No new STRING
  // creation during the process.
  [ -? {buffer!=NULL}; ]
  ( + val:SELF;
    + i,old_count:INTEGER;
        
    (Self = 0).if {
      buffer.extend '0';
    } else {
      i := buffer.count + 1;
      val := Self;
      {val != 0}.while_do { 
	buffer.extend ('0' +# (val & 1));
	val := val >> 1;
      };
      old_count := buffer.count;
      {i >= old_count}.until_do {
	buffer.swap i with old_count;
	old_count := old_count - 1;
	i := i + 1;
      };
    };
    buffer.precede_multiple '0' by ((s.to_integer) - buffer.count);
  ); 
  
  //
  // Hashing:
  //
  
  - hash_code:INTEGER <- to_integer_32.hash_code; // BSBS:  Il faut revoir => Depending processor
  
  //
  // Print
  //
  
  - print <-
  (
    (Self = 0).if {
      '0'.print;
    }.elseif {Self < 0} then {
      '-'.print;
      (-Self).print_positif;
    } else {
      print_positif;
    };
  );
  
  - print_positif <-
  // Display this number without memory.
  [
    -? {Self >= 0};
  ]
  ( + char:CHARACTER;
    + val:SELF;    
        
    char := (Self % 10).decimal_digit;
    val  := Self / 10;
    (val != 0).if {
      val.print_positif;
    };
    char.print;
  );

  - print_hex <-
  // Display this number without memory.
  ( + char:CHARACTER;
    + val:SELF;
        
    char := (Self & 0Fh).hexadecimal_digit; 
    val  := Self / 16; 
    (val != 0).if {
      val.print_hex;
    };
    char.print;
  );
  
  //
  // Debug manager facility.
  //
  
  - '?' blc:BLOCK <-  blc ?# Self;
  
Section INTEGER  
  
  - to_raw_pointer:POINTER         <- CONVERT[SELF,POINTER].on Self;



