///////////////////////////////////////////////////////////////////////////////
//                             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 CHAR_UNICODE -> UINTEGER_16;


  - copyright   := "2003-2005 Jérome Boutet, 2003-2007 Benoit Sonntag";
  
  - comment := "Static Unicode Character library .";

  - type    := `unsigned short`;
  - default := '\0';
  
Section Insert
  
  - parent_character_ref:CHARACTER_REF := CHARACTER_REF;
  
Section Public
    
  - in_range low:CHAR_UNICODE to up:CHAR_UNICODE :BOOLEAN <- ((Self >= low) && {Self<= up});
  
  //
  // General :
  //
  
  - object_size:INTEGER := 2;

  - pointer_size:INTEGER := 1;
  
  - maximum:INTEGER := 0FFFFh;
  
  - minimum:INTEGER := 0;
  
  //  - '==' other:CHAR_UNICODE :BOOLEAN <- (code == other.code); BSBS=> JBJB A revoir
/* BSBS: JBJB a revoir ??
  - code:UINTEGER_16 <-
  // Unicode code of Current.
  ( + result:UINTEGER_16;
    result:=to_uinteger_16;
    result
  );
*/  
  - to_uinteger_16:UINTEGER_16 <- UINTEGER_16.force_conversion Self;
  // Auto-cast
  
  //  - print <- to_uinteger_16.print; BSBS-> JBJB a revoir !!
  
  - to_integer:INTEGER <-
  // Sign-extended conversion.
  ( + result:INTEGER;
    result:=code.to_integer;
    ? {result.in_range (INTEGER_8.minimum) to (INTEGER_8.maximum)};
    result
  );
  
  - code:INTEGER_8 <-
  // ASCII code of Current.
  // No Sign-extended conversion.
  ( + result:INTEGER_8;
    result:=to_integer_8;
    //? {result.in_range minimum to maximum};
    result
  );
  
  //
  // Print.
  //
  
  - print <- IO.put_character Self;
  
  //
  // Switch case :
  //
  
  - when value:CHAR_UNICODE then block:BLOCK :CHAR_UNICODE <-
  (
    (Self=value).if block;
    Self
  );
  
  - when first_value:CHAR_UNICODE to last_value:CHAR_UNICODE then block:BLOCK :CHAR_UNICODE <-
  ( ? {first_value<=last_value};
    
    ((Self>=first_value) && {Self<=last_value}).if block;
    Self
  );
  
  //
  // Binary operator :
  //
  
  - '+' other:CHAR_UNICODE :CHAR_UNICODE <- (code+other.code).to_character;
  
  - '-' other:CHAR_UNICODE :CHAR_UNICODE <- (code-other.code).to_character;
  
  - '!==' other:CHAR_UNICODE :BOOLEAN <- (code !== other.code);
  // Comparison using `code'.
  
  - '==' other:CHAR_UNICODE :BOOLEAN <- (code == other.code);
  // Comparison using `code'.
  
  - '<' other:CHAR_UNICODE :BOOLEAN <- ( code < other.code );
  // Comparison using `code'.
  
  - '<=' other:CHAR_UNICODE :BOOLEAN <- ( code <= other.code );
  // Comparison using `code'.
  
  - '>' other:CHAR_UNICODE :BOOLEAN <- ( code > other.code );
  // Comparison using `code'.
  
  - '>=' other:CHAR_UNICODE :BOOLEAN <- ( code >= other.code );
  // Comparison using `code'.
  
  - decimal_value:INTEGER <-
  // Gives the value of a decimal digit.
  ( + result:INTEGER;
    ? {is_digit};
    result := to_integer - 48;
    ? {result.in_range 0 to 9};
    result
  );
  
  - binary_value:INTEGER <-
  // Gives the value of a binary digit.
  ( + result:INTEGER;
    ? {is_binary_digit};
    result := code - 48;
    ? {result.in_range 0 to 1};
    result
  );
  
  - octal_value:INTEGER <-
  // Gives the value of an octal digit.
  ( + result:INTEGER;
    ? {is_octal_digit};
    result := code - 48;
    ? {result.in_range 0 to 7};
    result
  );
  
  - hexadecimal_value:INTEGER <-
  // Gives the value of an hexadecimal digit.
  ( + result:INTEGER;
    ? {is_hexadecimal_digit};
    (code < 'A'.code).if {
      result := code - 48;
    }.elseif {code<'a'.code} then {
      result := code - 55;
    } else {
      result := code - 87;
    };
    ? {result.in_range 0 to 15};
    result
  );
  
  - same_as other:CHAR_UNICODE :BOOLEAN <-
  // Case insensitive comparison.
  // No difference between upper/lower case letters.
  ( + result:BOOLEAN;
    (Self = other).if {
      result:=TRUE;
    } else {
      code
      .when 65 to 90 then {
	result:=(code = (other.code - 32));
      }
      .when 97 to 122 then {
	result:=(code = (other.code + 32));
      };
      ? {result ->> {(to_lower = other) | (to_upper = other)}};
    };
    result
  );
  
  - to_upper:CHAR_UNICODE <-
  // Conversion to the corresponding upper case.
  ( + result:CHAR_UNICODE;
    ((code < 97) || {code > 122}).if {
      result := Self;
    } else {
      result := (code - 32).to_character;
    };
    result
  );
  
  - to_lower:CHAR_UNICODE <-
  // Conversion to the corresponding lower case.
  ( + result:CHAR_UNICODE;
    ((code < 65) || {code > 90}).if {
      result := Self;
    } else {
      result := (code + 32).to_character;
    };
    result
  );
  
  - is_letter:BOOLEAN <-
  // Is it a letter ('a' .. 'z' or 'A' .. 'Z') ?
  ( + result:BOOLEAN;
    (Self >= 'a').if {
      result := (Self <= 'z');
    }.elseif {Self >= 'A'} then {
      result := (Self <= 'Z');
    };
    ? {result = (in_range 'A' to 'Z' | in_range 'a' to 'z')};
    result
  );
  
  - is_digit:BOOLEAN <-
  // Belongs to '0'..'9'.
  ( + result:BOOLEAN;
    (Self >= '0').if {
      result := (Self <= '9');
    };
    ? {result = in_range '0' to '9'};
    result
  );
  
  - is_binary_digit:BOOLEAN <-
  // Belongs to '0'..'1'.
  ( + result:BOOLEAN;
    result:= (Self = '0') || {Self = '1'};
    ? {result = in_range '0' to '1'};
    result
  );
  
  - is_octal_digit:BOOLEAN <-
  // Belongs to '0'..'7'.
  ( + result:BOOLEAN;
    (Self >= '0').if {
      result := (Self <= '7');
    };
    ? {result = in_range '0' to '7'};
    result
  );
  
  - is_hexadecimal_digit:BOOLEAN <-
  // Is it one character of "0123456789abcdefABCDEF" ?
  ( + result:BOOLEAN;
    (is_digit).if {
      result := TRUE;
    }.elseif {Self >= 'a'} then {
      result := (Self <= 'f');
    }.elseif {Self >= 'A'} then {
      result := (Self <= 'F');
    };
    ? {result = ("0123456789abcdefABCDEF".has Self)};
    result
  );
  
  - is_lower:BOOLEAN <-
  // Is it some lowercase letter ('a'..'z')?
  ( + result:BOOLEAN;
    (Self >= 'a').if {
      result:=(Self <= 'z');
    };
    result
  );
  
  - is_upper:BOOLEAN <-
  // Is it some uppercase letter ('A'..'Z')?
  ( + result:BOOLEAN;
    (Self >= 'A').if {
      result:=(Self <= 'Z');
    };
    result
  );
  
  - is_separator:BOOLEAN <-
  // True when character is a separator.
  (
    (Self= ' ') || {Self = '\t'} || {Self='\n'} ||
    {Self='\r'} || {Self = '\0'} || {Self='\f'} || {Self='\v'}
  );
  
  - is_letter_or_digit:BOOLEAN <-
  // Is it a letter (see `is_letter') or a digit (see `is_digit') ?
  ( + result:BOOLEAN;
    result := (is_letter || {is_digit});
    ? {result = (is_letter | is_digit)};
    result
  );
  
  - is_ascii:BOOLEAN := TRUE;
  // Is character a 8-bit ASCII character?
  
  - is_bit:BOOLEAN <- ((Self='0') || {Self='1'});
  // True for `0' and `1'.
  
  - next:CHAR_UNICODE <-
  // Give the next character (the following `code');
  ( ? {code<255};
    (code + 1).to_character
  );
  
  - previous:CHAR_UNICODE <-
  // Give the previous character (the `code' before);
  ( ? {code > 0};
    (code - 1).to_character
  );
  
  //
  // Conversions:
  //
  
  - to_hexadecimal:STRING <-
  // Create a new STRING giving the `code' in hexadecimal.
  // For example :
  //    (255).to_character.to_hexadecimal gives "FF".
  // Note: see `to_hexadecimal_in' to save memory.
  ( + result:STRING;
    string:=STRING.make 2;
    to_hexadecimal_in result;
    ? {result.count = 2};
    result
  );
  
  - to_hexadecimal_in str:STRING <-
  // Append the equivalent of `to_hexadecimal' at the end of
  // `str'. Thus you can save memory because no other
  // STRING is allocate for the job.
  ( + c, old_count:INTEGER;
    
    old_count:=str.count;
    c := code >> 4;
    (c<10).if {
      str.extend (('0'.code + c).to_character);
    } else {
      str.extend (('A'.code - 10 + c).to_character);
    };
    c := code & 00001111b;
    (c<10).if {
      str.extend (('0'.code + c).to_character);
    } else {
      str.extend (('A'.code - 10 + c).to_character);
    };
    ? {str.count = (2 + old_count)};
  );
  
  //
  // Miscellaneous:
  //
  
  - is_alpha:BOOLEAN <-
  // See `is_letter' (yes this is just a call to `is_letter').
  // Isn't `is_letter' better English ;-)
  ( + result:BOOLEAN;
    result := is_letter;
    ? {result = is_letter};
    result
  );
  
  //
  // Hashing :
  //
  
  - hash_code: INTEGER <- code;
