///////////////////////////////////////////////////////////////////////////////
//                             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        := UINTEGER_BIG; // Aucun Auto-Cast pour l'instant...(peut etre
                              // INTEGER_BIG ...)
                               
  - comment     := "Integer without bits limit! (Just for fun!)";

  //- default     := 0;

Section Inherit

  - parent_unsigned:UNSIGNED_INTEGER := UNSIGNED_INTEGER;

Section UINTEGER_BIG
  
  + storage:FAST_ARRAY[UINTEGER];
  
  - last_modulo:UINTEGER;
  
  - make init:UINTEGER :SELF <-
  (     
    storage:=FAST_ARRAY[UINTEGER].create 1; 
    storage.put init to 0;
    Self 
  );

  - count:INTEGER <- storage.count;

  - lower:INTEGER <- storage.lower;

  - upper:INTEGER <- storage.upper;

  - item idx:INTEGER :UINTEGER <- storage.item idx;

  - copy other:UINTEGER_BIG <- storage.copy (other.storage);

  - force elt:UINTEGER to idx:INTEGER <- storage.force elt to idx;
  
  - put elt:UINTEGER to idx:INTEGER <- storage.put elt to idx;
  
  - add_last elt:UINTEGER <- storage.add_last elt;
  
  - resize new_size:INTEGER <-
  (
    ? { new_size > 0};    
    storage.resize new_size;
  );
  
Section INTEGER

  - create init:UINTEGER :UINTEGER_BIG <- UINTEGER_BIG.clone.make init;
  
Section Public  
  
  - object_size:INTEGER <- `52`; 
 
  //
  // Range.
  //
  
  - maximum:UINTEGER_64 <- 
  (
    crash_with_message "UINTEGER_BIG's maximum is undefined.";
    0
  );
    
  //
  // Conversion format with test.
  //
  
  - to_uinteger_64:UINTEGER_64   <- 
  ( ? {count <= 2}; // Bound limit control.
    storage.first.to_uinteger_64 + (storage.item 1.to_uinteger_64 << 32) 
  );
  
  - to_uinteger:UINTEGER   <- 
  ( ? {count = 1};
    storage.first.to_uinteger
  );
  
  - to_uinteger_16:UINTEGER_16 <- 
  ( ? {in_range (0,(UINTEGER_16.maximum))};
    storage.first.to_uinteger_16
  );
  
  - to_uinteger_8:UINTEGER_8 <- 
  ( ? {in_range (0,(UINTEGER_8.maximum))};
    storage.first.to_uinteger_8
  );
  
  - to_integer_64:INTEGER_64     <- 
  ( ? {in_range ((INTEGER_64.minimum),(INTEGER_64.maximum))};
    storage.first.to_integer_64
  );
  
  - to_integer:INTEGER     <- 
  ( ? {in_range ((INTEGER.minimum),(INTEGER.maximum))};
    storage.first.to_integer
  );
  
  - to_integer_16:INTEGER_16   <- 
  ( ? {in_range ((INTEGER_16.minimum),(INTEGER_16.maximum))};
    storage.first.to_uinteger_16
  );
  
  - to_integer_8:INTEGER_8   <- 
  ( ? {in_range ((INTEGER_8.minimum),(INTEGER_8.maximum))};
    storage.first.to_integer_8
  );

  //- to_ureal_26_6:UREAL_26_6 <- self;

  - to_ulargeint:UINTEGER_BIG <- Self;
  
  - clean <-
  (
    {(storage.count!=1) && {storage.last == 0}}.while_do {
      storage.remove_last;
    };	
  );
  
  //
  // binary operator :
  //
  
  - '-' Left 80  other:SELF :SELF <- 
  (
    + result:UINTEGER_BIG;
    + over,substr,part1,part2:UINTEGER_64;

    (other > Self).if {
      crash;
      result := 0;
    } else {      
      result.resize count;
      (other.count < count).if {
	other.resize count;
      };
      lower.to upper do { idx:INTEGER;
	part1 := item idx;
	part2 := other.item idx.to_uinteger_64 + over;
	substr := part1 - part2;
	((substr >> 32) == 1).if {
	  over := 1;
	};
	result.put ((substr & (UINTEGER.maximum.to_uinteger_64)).to_uinteger) to idx;
      };
    };
    
    result.clean;
    result
  );
  
  
  - '-#' Left 80 other:INTEGER :SELF <-
  (
    + result:UINTEGER_BIG;
    + part1,over,substr:UINTEGER_64;
    + idx:INTEGER;

    (Self <# other).if {
      crash;
      result := 0;
    } else {
      result.resize count;
      part1 := item 0;
      substr := part1 - other;
      ((substr >> 32) == 1).if {
	over := 1;
      };
      result.put ((substr & (UINTEGER.maximum.to_uinteger_64)).to_uinteger) to 0;
      idx := idx + 1;
      {over == 0}.until_do {
	part1 := item idx;
	substr := part1 - over;
	((substr >> 32) == 1).if {
	  over := 1;
	};
	result.put ((substr & (UINTEGER.maximum.to_uinteger_64)).to_uinteger) to idx;
	idx := idx + 1;
      };      
    };
    
    result.clean;
    result
  );
  
  
  - '+'  Left 80  other:SELF :SELF <- 
  (
    + result:UINTEGER_BIG;
    + part1,part2,sum,over:UINTEGER_64;

    (other.count > count).if {
      result := other + Self;
    } else {
      result := 0;
      result.resize count;
      (other.lower).to (other.upper) do { idx:INTEGER;
        part1:=item idx;
        part2:=other.item idx;
        sum:=part1+part2+over;
        result.put ((sum & (UINTEGER.maximum.to_uinteger_64)).to_uinteger) to idx;
        over:=sum >> 32;
      };
      (other.upper+1).to upper do { idx:INTEGER;
        part1:=item idx;
        sum:=part1+over;
        result.put ((sum & (UINTEGER.maximum.to_uinteger_64)).to_uinteger) to idx;
        over:=sum >> 32;
      };
      (over!==0).if {
        result.add_last (over.to_uinteger);
      };
    };    
    result
  );  
  
  
  - '+#' Left 80  other:INTEGER :SELF <-
  (
    + result:UINTEGER_BIG;
    + part1,sum,over:UINTEGER_64;
    + idx:INTEGER;
    result.resize count;
    part1 := item 0;
    sum := part1 + other;
    result.put ((sum & (UINTEGER.maximum)).to_uinteger) to 0;
    idx := idx + 1;
    over:=sum >> 32;
    {(over == 0) || {idx > upper}}.until_do {
      part1 := item idx;
      sum := part1 + over;
      result.put ((sum & (UINTEGER.maximum)).to_uinteger) to idx;
      idx := idx + 1; 
      over:=sum >> 32;
    };
    
    (over!==0).if {
      result.add_last (over.to_uinteger);
    };
    
    result
  );
  
  
  - '*'  Left 100 other:SELF :SELF <- 
  (
    + result:UINTEGER_BIG;
    + part1,part2,product:UINTEGER_64;
    + index_result:INTEGER;
    
    result := 0;
    result.resize (count + other.count);
    
    lower.to upper do { idx:INTEGER;
      + result_current:UINTEGER_BIG;
      
      result_current := 0;
      result_current.resize count;
      part1 := item idx;
      index_result := idx;
      (other.lower).to (other.upper) do { idx_other:INTEGER;
	part2 := other.item idx_other;
	product := part1 * part2;	
	(product < (UINTEGER.maximum.to_uinteger_64)).if {
	  result_current.put (product.to_uinteger) to index_result;
	} else {
	  result_current.put ((product & (UINTEGER.maximum.to_uinteger_64)).to_uinteger) to index_result;
	  index_result := index_result + 1;
	  result_current.force ((product >> 32).to_uinteger) to index_result;
	  index_result := index_result + 1;
	};
      };
      
      result := result + result_current;
      
    };
    result.clean;
    result
  );
  
  - '*#' Left 100 other:INTEGER :SELF <-
  (
    + result:UINTEGER_BIG;
    + part1,product,over:UINTEGER_64;
    
    result := 0;
    result.resize count;
    lower.to upper do { idx:INTEGER;      
      part1 := item idx;      
      product :=  part1 * other + over;      
      (product < (UINTEGER.maximum.to_uinteger_64)).if {
	result.put (product.to_uinteger) to idx;
	over := 0;
      } else {
	result.put ((product & (UINTEGER.maximum.to_uinteger_64)).to_uinteger) to idx;
	over := product >> 32;
      };
    };
    
    (over!==0).if {
      result.add_last (over.to_uinteger);
    };    
    
    result.clean;
    result
  );
  
  - '/'  Left 100 other:SELF :SELF <- 
  (
    not_yet_implemented; Self
  );
  
  
  - '/#' Left 100 other:INTEGER :SELF <-
  (
    + result:UINTEGER_BIG;
    + last_mod,div:UINTEGER_64;
    + idx:INTEGER;
    result := 0;
    result.resize count;
    idx := upper;
    last_mod := item idx;    
    {idx < lower}.until_do {
      div := 0;
      (last_mod < other).if {
	idx := idx - 1;
	(idx >= lower).if {
	  last_mod := (last_mod << 32) + item idx.to_uinteger_64;
	  div := last_mod / (other.to_uinteger_64);
	  last_mod := last_mod % (other.to_uinteger_64);
	};
      } else {
	div := last_mod / (other.to_uinteger_64);
	last_mod := last_mod % (other.to_uinteger_64); 
      };      
      (div !== 0).if {
	result.put ((div & (UINTEGER.maximum.to_uinteger_64)).to_uinteger) to idx;
      };
    };
    last_modulo := last_mod.to_uinteger;
    result.clean;
    result
  );
  
  
  
  - '&'  Left 100 other:SELF :SELF <-
  (
    not_yet_implemented; Self
  );

  //
  // Test binary operator :
  //
    
  - '>'   Right 60 other:SELF :BOOLEAN <- 
  (
    + result:BOOLEAN;
    (count > (other.count)).if {
      result := TRUE;
    }.elseif {count < other.count} then {
      result  := FALSE;
    } else {
      + i:INTEGER;
      + find:BOOLEAN;
      i := lower;

      {find || {i > upper}}.until_do {
	(item i > other.item i).if {
	  result := TRUE;
	  find := TRUE;
	}.elseif {item i < other.item i} then {
	  result := FALSE;
	  find := TRUE;
	};

	i := i + 1; 
      };
    };
    
    result
  );
  
  
  - '>#' Right 60 other:INTEGER :BOOLEAN <-
  (
    + result:BOOLEAN;
    (count > 1).if {
      result := TRUE;
    } else {
      result := item 0 > other;
    };
    result
  );
  
  - '=='  Right 60 other:SELF :BOOLEAN <-
  (
    + result:BOOLEAN;
    + idx:INTEGER;
    (count == other.count).if {
      idx := upper;
      { (idx >= lower) && {item idx == other.item idx} }.while_do {
	idx := idx - 1;
      };
      (idx < lower).if {
	result := TRUE;
      };
    };
    
    result
  );
  
  //
  // prefix : Unary operator
  //
    
  - '~' :SELF <- 
  (
    not_yet_implemented; Self 
  );  
  
  - '-' :SELF <-
  (
    lower.to upper do { idx:INTEGER;
      force (~item idx) to idx;
    };
    
    Self + 1
  );


  - factorial:SELF <-
  ( + result,value:SELF;
    + value2:UINTEGER;
    ? {Self >=# 0};
    
    (count = 1).if {
      result := 1;
      value2 := to_uinteger;
      {value2 <= 1}.until_do {
	result := result *# value2;
	value2  := value2 - 1;
      };
    } else {
      result := 1;
      value  := Self;
      {value <=# 1}.until_do {
	result := result * value;
	value  := value -# 1;
      };
    };
    result
  );
  

  //
  // conversion
  //
  
  - append_in buffer:STRING <- 
  // Append in the `buffer' the equivalent of `to_string'. No new STRING
  // creation during the process.
  ( + value:UINTEGER_BIG; 
    + mod:UINTEGER;
    + counter:INTEGER;
    + old_upper,new_upper:INTEGER;
    
    old_upper:=buffer.upper + 1;
    
    (upper = 0).if { 
      item 0.append_in buffer;
    } else {
      value:=Self;
      {
	value:=value /# 1000000000;
	mod:=last_modulo;	
	{counter==0}.until_do {
	  buffer.extend '0';
	  counter:=counter-1;
	};
	counter := 9;
	{	
	  buffer.extend ((mod % 10).digit);
	  mod := mod / 10;
	  counter := counter-1;
	}.do_while {mod!==0};
      }.do_while {value!==0};
      
      new_upper := buffer.upper;      

      // Swap.
      {old_upper >= new_upper}.until_do {
	buffer.swap old_upper with new_upper;
	new_upper := new_upper - 1;
	old_upper := old_upper + 1;
      };
    };
  );
  




