------------------------------------------------------------------------------
--                                                                          --
--                         GNAT RUN-TIME COMPONENTS                         --
--                                                                          --
--              A D A . C H A R A C T E R S . H A N D L I N G               --
--                                                                          --
--                                 S p e c                                  --
--                                                                          --
--          Copyright (C) 1992-2025, Free Software Foundation, Inc.         --
--                                                                          --
-- This specification is derived from the Ada Reference Manual for use with --
-- GNAT. The copyright notice above, and the license provisions that follow --
-- apply solely to the  contents of the part following the private keyword. --
--                                                                          --
-- GNAT is free software;  you can  redistribute it  and/or modify it under --
-- terms of the  GNU General Public License as published  by the Free Soft- --
-- ware  Foundation;  either version 3,  or (at your option) any later ver- --
-- sion.  GNAT is distributed in the hope that it will be useful, but WITH- --
-- OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY --
-- or FITNESS FOR A PARTICULAR PURPOSE.                                     --
--                                                                          --
-- As a special exception under Section 7 of GPL version 3, you are granted --
-- additional permissions described in the GCC Runtime Library Exception,   --
-- version 3.1, as published by the Free Software Foundation.               --
--                                                                          --
-- You should have received a copy of the GNU General Public License and    --
-- a copy of the GCC Runtime Library Exception along with this program;     --
-- see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see    --
-- <http://www.gnu.org/licenses/>.                                          --
--                                                                          --
-- GNAT was originally developed  by the GNAT team at  New York University. --
-- Extensive contributions were provided by Ada Core Technologies Inc.      --
--                                                                          --
------------------------------------------------------------------------------

--  Postconditions in this unit are meant for analysis only, not for run-time
--  checking, in order not to slow down the execution of these functions.

pragma Assertion_Policy (Post => Ignore);

with Ada.Characters.Latin_1;

package Ada.Characters.Handling with
  SPARK_Mode,
  Always_Terminates
is
   pragma Pure;
   --  In accordance with Ada 2005 AI-362

   ----------------------------------------
   -- Character Classification Functions --
   ----------------------------------------

   --  In the description below for each function that returns a Boolean
   --  result, the effect is described in terms of the conditions under which
   --  the value True is returned. If these conditions are not met, then the
   --  function returns False.
   --
   --  Each of the following classification functions has a formal Character
   --  parameter, Item, and returns a Boolean result.

   function Is_Control               (Item : Character) return Boolean
   with
     Post => Is_Control'Result =
       (Character'Pos (Item) in 0 .. 31 | 127 .. 159);
   --  True if Item is a control character. A control character is a character
   --  whose position is in one of the ranges 0..31 or 127..159.

   function Is_Graphic               (Item : Character) return Boolean
   with
     Post => Is_Graphic'Result =
       (Character'Pos (Item) in 32 .. 126 | 160 .. 255);
   --  True if Item is a graphic character. A graphic character is a character
   --  whose position is in one of the ranges 32..126 or 160..255.

   function Is_Letter                (Item : Character) return Boolean
   with
     Post => Is_Letter'Result =
       (Item in 'A' .. 'Z' | 'a' .. 'z'
         or else Character'Pos (Item) in 192 .. 214 | 216 .. 246 | 248 .. 255);
   --  True if Item is a letter. A letter is a character that is in one of the
   --  ranges 'A'..'Z' or 'a'..'z', or whose position is in one of the ranges
   --  192..214, 216..246, or 248..255.

   function Is_Lower                 (Item : Character) return Boolean
   with
     Post => Is_Lower'Result =
       (Item in 'a' .. 'z'
         or else Character'Pos (Item) in 223 .. 246 | 248 .. 255);
   --  True if Item is a lower-case letter. A lower-case letter is a character
   --  that is in the range 'a'..'z', or whose position is in one of the ranges
   --  223..246 or 248..255.

   function Is_Upper                 (Item : Character) return Boolean
   with
     Post => Is_Upper'Result =
       (Item in 'A' .. 'Z'
         or else Character'Pos (Item) in 192 .. 214 | 216 .. 222);
   --  True if Item is an upper-case letter. An upper-case letter is a
   --  character that is in the range 'A'..'Z' or whose position is in one
   --  of the ranges 192..214 or 216..222.

   function Is_Basic                 (Item : Character) return Boolean
   with
     Post => Is_Basic'Result =
       (Item in 'A' .. 'Z'
          | 'a' .. 'z'
          | Latin_1.UC_AE_Diphthong
          | Latin_1.LC_AE_Diphthong
          | Latin_1.UC_Icelandic_Eth
          | Latin_1.LC_Icelandic_Eth
          | Latin_1.UC_Icelandic_Thorn
          | Latin_1.LC_Icelandic_Thorn
          | Latin_1.LC_German_Sharp_S);
   --  True if Item is a basic letter. A basic letter is a character that
   --  is in one of the ranges 'A'..'Z' and 'a'..'z', or that is one of
   --  the following: UC_AE_Diphthong, LC_AE_Diphthong, UC_Icelandic_Eth,
   --  LC_Icelandic_Eth, UC_Icelandic_Thorn, LC_Icelandic_Thorn, or
   --  LC_German_Sharp_S.

   function Is_Digit                 (Item : Character) return Boolean
   with
     Post => Is_Digit'Result = (Item in '0' .. '9');
   --  True if Item is a decimal digit. A decimal digit is a character in the
   --  range '0'..'9'.

   function Is_Decimal_Digit         (Item : Character) return Boolean
     renames Is_Digit;

   function Is_Hexadecimal_Digit     (Item : Character) return Boolean
   with
     Post => Is_Hexadecimal_Digit'Result =
       (Is_Decimal_Digit (Item) or Item in 'A' .. 'F' | 'a' .. 'f');
   --  True if Item is a hexadecimal digit. A hexadecimal digit is a character
   --  that is either a decimal digit or that is in one of the ranges 'A'..'F'
   --  or 'a'..'f'.

   function Is_Alphanumeric          (Item : Character) return Boolean
   with
     Post => Is_Alphanumeric'Result =
       (Is_Letter (Item) or Is_Decimal_Digit (Item));
   --  True if Item is an alphanumeric character. An alphanumeric character is
   --  a character that is either a letter or a decimal digit.

   function Is_Special               (Item : Character) return Boolean
   with
     Post => Is_Special'Result =
       (Is_Graphic (Item) and not Is_Alphanumeric (Item));
   --  True if Item is a special graphic character. A special graphic character
   --  is a graphic character that is not alphanumeric.

   function Is_Line_Terminator       (Item : Character) return Boolean
   with
     Post => Is_Line_Terminator'Result =
       (Character'Pos (Item) in 10 .. 13 | 133);
   --  True if Item is a character with position 10..13 (Line_Feed,
   --  Line_Tabulation, Form_Feed, Carriage_Return) or 133 (Next_Line).

   function Is_Mark                  (Item : Character) return Boolean
   with
     Post => Is_Mark'Result = False;
   --  Never True (no value of type Character has categories Mark, Non-Spacing
   --  or Mark, Spacing Combining).

   function Is_Other_Format          (Item : Character) return Boolean
   with
     Post => Is_Other_Format'Result = (Character'Pos (Item) = 173);
   --  True if Item is a character with position 173 (Soft_Hyphen).

   function Is_Punctuation_Connector (Item : Character) return Boolean
   with
     Post => Is_Punctuation_Connector'Result =
       (Character'Pos (Item) = 95);
   --  True if Item is a character with position 95 ('_', known as Low_Line or
   --  Underscore).

   function Is_Space                 (Item : Character) return Boolean
   with
     Post => Is_Space'Result = (Character'Pos (Item) in 32 | 160);
   --  True if Item is a character with position 32 (' ') or 160
   --  (No_Break_Space).

   function Is_NFKC                  (Item : Character) return Boolean
   with
     Post => Is_NFKC'Result =
       (Character'Pos (Item) not in
            160 | 168 | 170 | 175 | 178 | 179 | 180
          | 181 | 184 | 185 | 186 | 188 | 189 | 190);
   --  True if Item could be present in a string normalized to Normalization
   --  Form KC (as defined by Clause 21 of ISO/IEC 10646:2017); this includes
   --  all characters except those with positions 160, 168, 170, 175, 178, 179,
   --  180, 181, 184, 185, 186, 188, 189, and 190.

   ---------------------------------------------------
   -- Conversion Functions for Character and String --
   ---------------------------------------------------

   --  Each of the names To_Lower, To_Upper, and To_Basic refers to two
   --  functions: one that converts from Character to Character, and
   --  the other that converts from String to String. The result of each
   --  Character-to-Character function is described below, in terms of
   --  the conversion applied to Item, its formal Character parameter. The
   --  result of each String-to-String conversion is obtained by applying
   --  to each element of the function's String parameter the corresponding
   --  Character-to-Character conversion; the result is the null String if the
   --  value of the formal parameter is the null String. The lower bound of the
   --  result String is 1.

   function To_Lower (Item : Character) return Character
   with
     Post => To_Lower'Result =
       (if Is_Upper (Item) then
          Character'Val (Character'Pos (Item) +
            (if Item in 'A' .. 'Z' then
               Character'Pos ('a') - Character'Pos ('A')
             else
               Character'Pos (Latin_1.LC_A_Grave)
                 - Character'Pos (Latin_1.UC_A_Grave)))
        else
          Item);
   --  Returns the corresponding lower-case value for Item if Is_Upper(Item),
   --  and returns Item otherwise.

   function To_Upper (Item : Character) return Character
   with
     Post => To_Upper'Result =
       (if Is_Lower (Item)
          and then Item not in Latin_1.LC_German_Sharp_S
                             | Latin_1.LC_Y_Diaeresis
        then
          Character'Val (Character'Pos (Item) +
            (if Item in 'A' .. 'Z' then
               Character'Pos ('A') - Character'Pos ('a')
             else
               Character'Pos (Latin_1.UC_A_Grave)
                 - Character'Pos (Latin_1.LC_A_Grave)))
        else
          Item);
   --  Returns the corresponding upper-case value for Item if Is_Lower(Item)
   --  and Item has an upper-case form, and returns Item otherwise. The lower
   --  case letters LC_German_Sharp_S and LC_Y_Diaeresis do not have upper case
   --  forms.

   function To_Basic (Item : Character) return Character
   with
     Post => To_Basic'Result =
       (if not Is_Letter (Item) or else Is_Basic (Item) then
          Item
        else
          (case Item is
             when Latin_1.UC_A_Grave .. Latin_1.UC_A_Ring      => 'A',
             when Latin_1.UC_C_Cedilla                         => 'C',
             when Latin_1.UC_E_Grave .. Latin_1.UC_E_Diaeresis => 'E',
             when Latin_1.UC_I_Grave .. Latin_1.UC_I_Diaeresis => 'I',
             when Latin_1.UC_N_Tilde                           => 'N',
             when Latin_1.UC_O_Grave .. Latin_1.UC_O_Diaeresis => 'O',
             when Latin_1.UC_O_Oblique_Stroke                  => 'O',
             when Latin_1.UC_U_Grave .. Latin_1.UC_U_Diaeresis => 'U',
             when Latin_1.UC_Y_Acute                           => 'Y',
             when Latin_1.LC_A_Grave .. Latin_1.LC_A_Ring      => 'a',
             when Latin_1.LC_C_Cedilla                         => 'c',
             when Latin_1.LC_E_Grave .. Latin_1.LC_E_Diaeresis => 'e',
             when Latin_1.LC_I_Grave .. Latin_1.LC_I_Diaeresis => 'i',
             when Latin_1.LC_N_Tilde                           => 'n',
             when Latin_1.LC_O_Grave .. Latin_1.LC_O_Diaeresis => 'o',
             when Latin_1.LC_O_Oblique_Stroke                  => 'o',
             when Latin_1.LC_U_Grave .. Latin_1.LC_U_Diaeresis => 'u',
             when Latin_1.LC_Y_Acute                           => 'y',
             when Latin_1.LC_Y_Diaeresis                       => 'y',
             when others => raise Program_Error));
   --  Returns the letter corresponding to Item but with no diacritical mark,
   --  if Item is a letter but not a basic letter; returns Item otherwise.

   function To_Lower (Item : String) return String
   with
     Post => To_Lower'Result'First = 1
       and then To_Lower'Result'Length = Item'Length
       and then
         (for all J in To_Lower'Result'Range =>
            To_Lower'Result (J) = To_Lower (Item (Item'First + (J - 1))));

   function To_Upper (Item : String) return String
   with
     Post => To_Upper'Result'First = 1
       and then To_Upper'Result'Length = Item'Length
       and then
         (for all J in To_Upper'Result'Range =>
            To_Upper'Result (J) = To_Upper (Item (Item'First + (J - 1))));

   function To_Basic (Item : String) return String
   with
     Post => To_Basic'Result'First = 1
       and then To_Basic'Result'Length = Item'Length
       and then
         (for all J in To_Basic'Result'Range =>
            To_Basic'Result (J) = To_Basic (Item (Item'First + (J - 1))));

   ----------------------------------------------------------------------
   -- Classifications of and Conversions Between Character and ISO 646 --
   ----------------------------------------------------------------------

   --  The following set of functions test for membership in the ISO 646
   --  character range, or convert between ISO 646 and Character.

   subtype ISO_646 is
     Character range Character'Val (0) .. Character'Val (127);

   function Is_ISO_646 (Item : Character) return Boolean
   with
     Post => Is_ISO_646'Result = (Item in ISO_646);
   --  The function whose formal parameter, Item, is of type Character returns
   --  True if Item is in the subtype ISO_646.

   function Is_ISO_646 (Item : String)    return Boolean
   with
     Post => Is_ISO_646'Result =
       (for all J in Item'Range => Is_ISO_646 (Item (J)));
   --  The function whose formal parameter, Item, is of type String returns
   --  True if Is_ISO_646(Item(I)) is True for each I in Item'Range.

   function To_ISO_646
     (Item       : Character;
      Substitute : ISO_646 := ' ') return ISO_646
   with
     Post => To_ISO_646'Result =
       (if Is_ISO_646 (Item) then Item else Substitute);
   --  The function whose first formal parameter, Item, is of type Character
   --  returns Item if Is_ISO_646(Item), and returns the Substitute ISO_646
   --  character otherwise.

   function To_ISO_646
     (Item       : String;
      Substitute : ISO_646 := ' ') return String
   with
     Post => To_ISO_646'Result'First = 1
       and then To_ISO_646'Result'Length = Item'Length
       and then
         (for all J in To_ISO_646'Result'Range =>
            To_ISO_646'Result (J) =
              To_ISO_646 (Item (Item'First + (J - 1)), Substitute));
   --  The function whose first formal parameter, Item, is of type String
   --  returns the String whose Range is 1..Item'Length and each of whose
   --  elements is given by To_ISO_646 of the corresponding element in Item.

   ------------------------------------------------------
   -- Classifications of Wide_Character and Characters --
   ------------------------------------------------------

   --  Ada 2005 AI 395: these functions are moved to Ada.Characters.Conversions
   --  and are considered obsolete in Ada.Characters.Handling. However we do
   --  not complain about this obsolescence, since in practice it is necessary
   --  to use these routines when creating code that is intended to run in
   --  either Ada 95 or Ada 2005 mode.

   --  We do however have to flag these if the pragma No_Obsolescent_Features
   --  restriction is active (see Restrict.Check_Obsolescent_2005_Entity).

   function Is_Character (Item : Wide_Character) return Boolean
   with
     Post => Is_Character'Result =
       (Wide_Character'Pos (Item) <= Character'Pos (Character'Last));
   --  Returns True if Wide_Character'Pos(Item) <=
   --  Character'Pos(Character'Last).

   function Is_String    (Item : Wide_String)    return Boolean
   with
     Post => Is_String'Result =
       (for all I in Item'Range => Is_Character (Item (I)));
   --  Returns True if Is_Character(Item(I)) is True for each I in Item'Range.

   ------------------------------------------------------
   -- Conversions between Wide_Character and Character --
   ------------------------------------------------------

   --  Ada 2005 AI 395: these functions are moved to Ada.Characters.Conversions
   --  and are considered obsolete in Ada.Characters.Handling. However we do
   --  not complain about this obsolescence, since in practice it is necessary
   --  to use these routines when creating code that is intended to run in
   --  either Ada 95 or Ada 2005 mode.

   --  We do however have to flag these if the pragma No_Obsolescent_Features
   --  restriction is active (see Restrict.Check_Obsolescent_2005_Entity).

   function To_Character
     (Item       : Wide_Character;
      Substitute : Character := ' ') return Character
   with
     Post => To_Character'Result =
       (if Is_Character (Item) then
          Character'Val (Wide_Character'Pos (Item))
        else
          Substitute);
   --  Returns the Character corresponding to Item if Is_Character(Item), and
   --  returns the Substitute Character otherwise.

   function To_String
     (Item       : Wide_String;
      Substitute : Character := ' ') return String
   with
     Post => To_String'Result'First = 1
       and then To_String'Result'Length = Item'Length
       and then
         (for all J in To_String'Result'Range =>
            To_String'Result (J) =
              To_Character (Item (Item'First + (J - 1)), Substitute));
   --  Returns the String whose range is 1..Item'Length and each of whose
   --  elements is given by To_Character of the corresponding element in Item.

   function To_Wide_Character
     (Item : Character) return Wide_Character
   with
     Post => To_Wide_Character'Result =
       Wide_Character'Val (Character'Pos (Item));
   --  Returns the Wide_Character X such that Character'Pos(Item) =
   --  Wide_Character'Pos (X).

   function To_Wide_String
     (Item : String) return Wide_String
   with
     Post => To_Wide_String'Result'First = 1
       and then To_Wide_String'Result'Length = Item'Length
       and then
         (for all J in To_Wide_String'Result'Range =>
            To_Wide_String'Result (J) =
              To_Wide_Character (Item (Item'First + (J - 1))));
   --  Returns the Wide_String whose range is 1..Item'Length and each of whose
   --  elements is given by To_Wide_Character of the corresponding element in
   --  Item.

private
   pragma Inline (Is_Alphanumeric);
   pragma Inline (Is_Basic);
   pragma Inline (Is_Character);
   pragma Inline (Is_Control);
   pragma Inline (Is_Digit);
   pragma Inline (Is_Graphic);
   pragma Inline (Is_Hexadecimal_Digit);
   pragma Inline (Is_ISO_646);
   pragma Inline (Is_Letter);
   pragma Inline (Is_Line_Terminator);
   pragma Inline (Is_Lower);
   pragma Inline (Is_Mark);
   pragma Inline (Is_Other_Format);
   pragma Inline (Is_Punctuation_Connector);
   pragma Inline (Is_Space);
   pragma Inline (Is_Special);
   pragma Inline (Is_Upper);
   pragma Inline (To_Basic);
   pragma Inline (To_Character);
   pragma Inline (To_Lower);
   pragma Inline (To_Upper);
   pragma Inline (To_Wide_Character);

end Ada.Characters.Handling;
