As described in UTF-8 and in Wikipedia, UTF-8 is a popular encoding of (multi-byte) Unicode code-points into eight-bit octets.

The goal of this task is to write a encoder that takes a unicode code-point (an integer representing a unicode character) and returns a sequence of 1-4 bytes representing that character in the UTF-8 encoding.

Then you have to write the corresponding decoder that takes a sequence of 1-4 UTF-8 encoded bytes and return the corresponding unicode character.

Demonstrate the functionality of your encoder and decoder on the following five characters:

Character   Name                                  Unicode    UTF-8 encoding (hex)
---------------------------------------------------------------------------------
A           LATIN CAPITAL LETTER A                U+0041     41
ö           LATIN SMALL LETTER O WITH DIAERESIS   U+00F6     C3 B6
Ж           CYRILLIC CAPITAL LETTER ZHE           U+0416     D0 96
€           EURO SIGN                             U+20AC     E2 82 AC
𝄞           MUSICAL SYMBOL G CLEF                 U+1D11E    F0 9D 84 9E

Provided below is a reference implementation in Common Lisp.

(*
 
  UTF-8 encoding and decoding in ATS2.
 
  This is adapted from library code I wrote long ago and actually
  handles the original 1-to-6-byte encoding, as well. Valid Unicode
  requires only 1 to 4 bytes.
 
  What is remarkable about the following rather lengthy code is its
  use of proofs of what is and what is not valid UTF-8. Much of what
  follows is proofs rather than executable code. It seemed simpler to
  "mostly copy" my old code, than to try to pare that code down to a
  minimum that still demonstrated some of what makes ATS different
  from all but a few (if any) other languages.
 
*)
 
#define ATS_EXTERN_PREFIX "utf8_encoding_"
 
#include "share/atspre_define.hats"
#include "share/atspre_staload.hats"
 
(* A variant of ‘andalso’, with dependent types. *)
fn {}
andalso1 {b1, b2 : bool} (b1 : bool b1, b2 : bool b2) :<>
    [b3 : bool | b3 == (b1 && b2)] bool b3 =
  if b1 then
    b2
  else
    false
 
infixl (&&) &&&
macdef &&& = andalso1
 
(*###################### C CODE ####################################*)
 
%{^
 
_Static_assert (4 <= sizeof (int),
                "sizeof(int) must equal at least 4");
 
#define utf8_encoding_2_entries(v)   \
  (v), (v)
 
#define utf8_encoding_4_entries(v)   \
  utf8_encoding_2_entries (v),       \
    utf8_encoding_2_entries (v)
 
#define utf8_encoding_8_entries(v)   \
  utf8_encoding_4_entries (v),       \
    utf8_encoding_4_entries (v)
 
#define utf8_encoding_16_entries(v)  \
  utf8_encoding_8_entries (v),       \
    utf8_encoding_8_entries (v)
 
#define utf8_encoding_32_entries(v)  \
  utf8_encoding_16_entries (v),      \
    utf8_encoding_16_entries (v)
 
#define utf8_encoding_64_entries(v)  \
  utf8_encoding_32_entries (v),      \
    utf8_encoding_32_entries (v)
 
#define utf8_encoding_128_entries(v) \
  utf8_encoding_64_entries (v),      \
    utf8_encoding_64_entries (v)
 
static const atstype_int8 utf8_encoding_extended_utf8_lengths__[256] = {
  utf8_encoding_128_entries (1),
  utf8_encoding_64_entries (-1),
  utf8_encoding_32_entries (2),
  utf8_encoding_16_entries (3),
  utf8_encoding_8_entries (4),
  utf8_encoding_4_entries (5),
  utf8_encoding_2_entries (6),
  utf8_encoding_2_entries (-1)
};
 
static const atstype_int8 utf8_encoding_utf8_lengths__[256] = {
  utf8_encoding_128_entries (1),
  utf8_encoding_64_entries (-1),
  utf8_encoding_32_entries (2),
  utf8_encoding_16_entries (3),
  utf8_encoding_8_entries (4),
  utf8_encoding_4_entries (-1),
  utf8_encoding_2_entries (-1),
  utf8_encoding_2_entries (-1)
};
 
#define utf8_encoding_extended_utf8_character_length(c)      \
  (utf8_encoding_extended_utf8_lengths__[(atstype_uint8) (c)])
 
#define utf8_encoding_utf8_character_length(c)               \
  (utf8_encoding_utf8_lengths__[(atstype_uint8) (c)])
 
%}
 
(*###################### INTERFACE #################################*)
 
stadef
is_valid_unicode_code_point (u : int) : bool =
  (0x0 <= u && u < 0xD800) || (0xE000 <= u && u <= 0x10FFFF)
 
extern fun {}
is_valid_unicode_code_point :
  {u : int} int u -<>
    [b : bool | b == is_valid_unicode_code_point u]
    bool b
 
(*------------------------------------------------------------------*)
 
stadef
is_extended_utf8_1byte_first_byte (c0 : int) : bool =
  0x00 <= c0 && c0 <= 0x7F
 
stadef
is_extended_utf8_2byte_first_byte (c0 : int) : bool =
  0xC0 <= c0 && c0 <= 0xDF
 
stadef
is_extended_utf8_3byte_first_byte (c0 : int) : bool =
  0xE0 <= c0 && c0 <= 0xEF
 
stadef
is_extended_utf8_4byte_first_byte (c0 : int) : bool =
  0xF0 <= c0 && c0 <= 0xF7
 
stadef
is_extended_utf8_5byte_first_byte (c0 : int) : bool =
  0xF8 <= c0 && c0 <= 0xFB
 
stadef
is_extended_utf8_6byte_first_byte (c0 : int) : bool =
  0xFC <= c0 && c0 <= 0xFD
 
stadef
extended_utf8_character_length (c0 : int) : int =
  ifint (is_extended_utf8_1byte_first_byte c0, 1,
    ifint (is_extended_utf8_2byte_first_byte c0, 2,
      ifint (is_extended_utf8_3byte_first_byte c0, 3,
        ifint (is_extended_utf8_4byte_first_byte c0, 4,
          ifint (is_extended_utf8_5byte_first_byte c0, 5,
            ifint (is_extended_utf8_6byte_first_byte c0, 6, ~1))))))
 
stadef
extended_utf8_char_length_relation (c0 : int, n : int) : bool =
  (n == 1 && is_extended_utf8_1byte_first_byte c0) ||
  (n == 2 && is_extended_utf8_2byte_first_byte c0) ||
  (n == 3 && is_extended_utf8_3byte_first_byte c0) ||
  (n == 4 && is_extended_utf8_4byte_first_byte c0) ||
  (n == 5 && is_extended_utf8_5byte_first_byte c0) ||
  (n == 6 && is_extended_utf8_6byte_first_byte c0)
 
extern prfun
extended_utf8_char_length_relation_to_length :
  {c0 : int}
  {n : int | extended_utf8_char_length_relation (c0, n) ||
             (n == ~1 && ((c0 < 0x00) ||
                          (0x7F < c0 && c0 < 0xC0) ||
                          (0xFD < c0)))}
  () -<prf> [n == extended_utf8_character_length c0] void
 
extern prfun
extended_utf8_char_length_to_length_relation :
  {c0 : int}
  {n : int | n == extended_utf8_character_length c0}
  () -<prf>
    [extended_utf8_char_length_relation (c0, n) ||
     (n == ~1 && ((c0 < 0x00) || (0x7F < c0 && c0 < 0xC0) || (0xFD < c0)))]
    void
 
// extended_utf8_character_length:
//
// Return value = 1, 2, 3, or 4 indicates that there may be a valid
// UTF-8 character, of the given length. It may also be a `valid'
// overlong sequence. Otherwise there definitely is not a valid
// character of any sort starting with the given byte. Return
// value = 5 or 6 indicates the possible start of an `extended
// UTF-8' character of the given length, including code points up
// to 0xffffffff. Return value = ~1 means the byte is not the
// start of a valid sequence.
extern fun
extended_utf8_character_length :
  {c0 : int | 0x00 <= c0; c0 <= 0xFF} int c0 -<>
    [n : int | n == extended_utf8_character_length c0] int n = "mac#%"
 
stadef
utf8_character_length (c0 : int) : int =
  ifint (is_extended_utf8_1byte_first_byte c0, 1,
    ifint (is_extended_utf8_2byte_first_byte c0, 2,
      ifint (is_extended_utf8_3byte_first_byte c0, 3,
        ifint (is_extended_utf8_4byte_first_byte c0, 4, ~1))))
 
stadef
utf8_char_length_relation (c0 : int, n : int) : bool =
  (n == 1 && is_extended_utf8_1byte_first_byte c0) ||
  (n == 2 && is_extended_utf8_2byte_first_byte c0) ||
  (n == 3 && is_extended_utf8_3byte_first_byte c0) ||
  (n == 4 && is_extended_utf8_4byte_first_byte c0)
 
extern prfun
utf8_char_length_relation_to_length :
  {c0 : int}
  {n : int | utf8_char_length_relation (c0, n) ||
             (n == ~1 && ((c0 < 0x00) ||
                          (0x7F < c0 && c0 < 0xC0) ||
                          (0xF7 < c0)))}
  () -<prf> [n == utf8_character_length c0] void
 
extern prfun
utf8_char_length_to_length_relation :
  {c0 : int}
  {n : int | n == utf8_character_length c0}
  () -<prf>
    [utf8_char_length_relation (c0, n) ||
     (n == ~1 && ((c0 < 0x00) || (0x7F < c0 && c0 < 0xC0) || (0xF7 < c0)))]
    void
 
extern fun
utf8_character_length :
  {c0 : int | 0x00 <= c0; c0 <= 0xFF} int c0 -<>
    [n : int | n == utf8_character_length c0] int n = "mac#%"
 
(*------------------------------------------------------------------*)
 
stadef
is_valid_utf8_continuation_byte (c : int) : bool =
  0x80 <= c && c <= 0xBF
 
stadef
is_invalid_utf8_continuation_byte (c : int) : bool =
  c < 0x80 || 0xBF < c
 
extern fun {}
is_valid_utf8_continuation_byte :
  {c : int} int c -<>
    [b : bool | b == is_valid_utf8_continuation_byte c] bool b
 
(*------------------------------------------------------------------*)
 
stadef
extended_utf8_char_1byte_decoding (c0 : int) : int =
  c0
 
stadef
extended_utf8_char_2byte_decoding (c0 : int, c1 : int) : int =
  64 * (c0 - 0xC0) + (c1 - 0x80)
 
stadef
extended_utf8_char_3byte_decoding (c0 : int, c1 : int, c2 : int) : int =
  64 * 64 * (c0 - 0xE0) + 64 * (c1 - 0x80) + (c2 - 0x80)
 
stadef
extended_utf8_char_4byte_decoding (c0 : int, c1 : int, c2 : int,
                                   c3 : int) : int =
  64 * 64 * 64 * (c0 - 0xF0) + 64 * 64 * (c1 - 0x80) +
    64 * (c2 - 0x80) + (c3 - 0x80)
 
stadef
extended_utf8_char_5byte_decoding (c0 : int, c1 : int, c2 : int,
                                   c3 : int, c4 : int) : int =
  64 * 64 * 64 * 64 * (c0 - 0xF8) + 64 * 64 * 64 * (c1 - 0x80) +
    64 * 64 * (c2 - 0x80) + 64 * (c3 - 0x80) + (c4 - 0x80)
 
stadef
extended_utf8_char_6byte_decoding (c0 : int, c1 : int, c2 : int,
                                   c3 : int, c4 : int, c5 : int) : int =
  64 * 64 * 64 * 64 * 64 * (c0 - 0xFC) + 64 * 64 * 64 * 64 * (c1 - 0x80) +
    64 * 64 * 64 * (c2 - 0x80) + 64 * 64 * (c3 - 0x80) +
       64 * (c4 - 0x80) + (c5 - 0x80)
 
dataprop EXTENDED_UTF8_CHAR (length : int, u : int, c0 : int, c1 : int,
                             c2 : int, c3 : int, c4 : int, c5 : int) =
  | {u, c0 : int |
     0 <= u; u <= 0x7F;
     is_extended_utf8_1byte_first_byte c0;
     u == extended_utf8_char_1byte_decoding (c0)}
    EXTENDED_UTF8_CHAR_1byte (1, u, c0, ~1, ~1, ~1, ~1, ~1)
    //
  | {u, c0, c1 : int |
     0 <= u; u <= 0x7FF;
     is_extended_utf8_2byte_first_byte c0;
     is_valid_utf8_continuation_byte c1;
     u == extended_utf8_char_2byte_decoding (c0, c1)}
    EXTENDED_UTF8_CHAR_2byte (2, u, c0, c1, ~1, ~1, ~1, ~1)
    //
  | {u, c0, c1, c2 : int |
     0 <= u; u <= 0xFFFF;
     is_extended_utf8_3byte_first_byte c0;
     is_valid_utf8_continuation_byte c1;
     is_valid_utf8_continuation_byte c2;
     u == extended_utf8_char_3byte_decoding (c0, c1, c2)}
    EXTENDED_UTF8_CHAR_3byte (3, u, c0, c1, c2, ~1, ~1, ~1)
    //
  | {u, c0, c1, c2, c3 : int |
     0 <= u; u <= 0x1FFFFF;
     is_extended_utf8_4byte_first_byte c0;
     is_valid_utf8_continuation_byte c1;
     is_valid_utf8_continuation_byte c2;
     is_valid_utf8_continuation_byte c3;
     u == extended_utf8_char_4byte_decoding (c0, c1, c2, c3)}
    EXTENDED_UTF8_CHAR_4byte (4, u, c0, c1, c2, c3, ~1, ~1)
    //
  | {u, c0, c1, c2, c3, c4 : int |
     0 <= u; u <= 0x3FFFFFF;
     is_extended_utf8_5byte_first_byte c0;
     is_valid_utf8_continuation_byte c1;
     is_valid_utf8_continuation_byte c2;
     is_valid_utf8_continuation_byte c3;
     is_valid_utf8_continuation_byte c4;
     u == extended_utf8_char_5byte_decoding (c0, c1, c2, c3, c4)}
    EXTENDED_UTF8_CHAR_5byte (5, u, c0, c1, c2, c3, c4, ~1)
    //
  | {u, c0, c1, c2, c3, c4, c5 : int |
     0 <= u; u <= 0x7FFFFFFF;
     is_extended_utf8_6byte_first_byte c0;
     is_valid_utf8_continuation_byte c1;
     is_valid_utf8_continuation_byte c2;
     is_valid_utf8_continuation_byte c3;
     is_valid_utf8_continuation_byte c4;
     is_valid_utf8_continuation_byte c5;
     u == extended_utf8_char_6byte_decoding (c0, c1, c2, c3, c4, c5)}
    EXTENDED_UTF8_CHAR_6byte (6, u, c0, c1, c2, c3, c4, c5)
 
extern prfun
decode_extended_utf8_istot :
  {n : int | 1 <= n; n <= 6}
  {c0, c1, c2, c3, c4, c5 : int |
   extended_utf8_char_length_relation (c0, n);
   n <= 1 || is_valid_utf8_continuation_byte c1;
   n <= 2 || is_valid_utf8_continuation_byte c2;
   n <= 3 || is_valid_utf8_continuation_byte c3;
   n <= 4 || is_valid_utf8_continuation_byte c4;
   n <= 5 || is_valid_utf8_continuation_byte c5;
   1 < n || c1 == ~1;
   2 < n || c2 == ~1;
   3 < n || c3 == ~1;
   4 < n || c4 == ~1;
   5 < n || c5 == ~1}
   () -<prf> [u : int] EXTENDED_UTF8_CHAR (n, u, c0, c1, c2, c3, c4, c5)
 
extern prfun
decode_extended_utf8_isfun :
  {na : int | 1 <= na; na <= 6}
  {ua : int}
  {c0a, c1a, c2a, c3a, c4a, c5a : int}
  {nb : int | nb == na}
  {ub : int}
  {c0b, c1b, c2b, c3b, c4b, c5b : int |
   c0b == c0a;
   c1b == c1a;
   c2b == c2a;
   c3b == c3a;
   c4b == c4a;
   c5b == c5a}
  (EXTENDED_UTF8_CHAR (na, ua, c0a, c1a, c2a, c3a, c4a, c5a),
   EXTENDED_UTF8_CHAR (nb, ub, c0b, c1b, c2b, c3b, c4b, c5b)) -<prf>
    [ua == ub] void
 
extern prfun
lemma_extended_utf8_char_length :
  {n : int} {u : int} {c0, c1, c2, c3, c4, c5 : int}
  EXTENDED_UTF8_CHAR (n, u, c0, c1, c2, c3, c4, c5) -<prf>
    [n == extended_utf8_character_length c0] void
 
extern fun {}
decode_extended_utf8_1byte :
  {c0 : int | 0x00 <= c0; c0 <= 0x7F} int c0 -<>
    [u : int] (EXTENDED_UTF8_CHAR (1, u, c0, ~1, ~1, ~1, ~1, ~1) | int u)
 
extern fun {}
decode_extended_utf8_2byte :
  {c0, c1 : int | 0xC0 <= c0; c0 <= 0xDF;
                  is_valid_utf8_continuation_byte c1}
  (int c0, int c1) -<>
    [u : int] (EXTENDED_UTF8_CHAR (2, u, c0, c1, ~1, ~1, ~1, ~1) | int u)
 
extern fun {}
decode_extended_utf8_3byte :
  {c0, c1, c2 : int | 0xE0 <= c0; c0 <= 0xEF;
                      is_valid_utf8_continuation_byte c1;
                      is_valid_utf8_continuation_byte c2}
  (int c0, int c1, int c2) -<>
    [u : int] (EXTENDED_UTF8_CHAR (3, u, c0, c1, c2, ~1, ~1, ~1) | int u)
 
extern fun {}
decode_extended_utf8_4byte :
  {c0, c1, c2, c3 : int | 0xF0 <= c0; c0 <= 0xF7;
                          is_valid_utf8_continuation_byte c1;
                          is_valid_utf8_continuation_byte c2;
                          is_valid_utf8_continuation_byte c3}
  (int c0, int c1, int c2, int c3) -<>
    [u : int] (EXTENDED_UTF8_CHAR (4, u, c0, c1, c2, c3, ~1, ~1) | int u)
 
extern fun {}
decode_extended_utf8_5byte :
  {c0, c1, c2, c3, c4 : int | 0xF8 <= c0; c0 <= 0xFB;
                              is_valid_utf8_continuation_byte c1;
                              is_valid_utf8_continuation_byte c2;
                              is_valid_utf8_continuation_byte c3;
                              is_valid_utf8_continuation_byte c4}
  (int c0, int c1, int c2, int c3, int c4) -<>
    [u : int] (EXTENDED_UTF8_CHAR (5, u, c0, c1, c2, c3, c4, ~1) | int u)
 
extern fun {}
decode_extended_utf8_6byte :
  {c0, c1, c2, c3, c4, c5 : int | 0xFC <= c0; c0 <= 0xFD;
                                  is_valid_utf8_continuation_byte c1;
                                  is_valid_utf8_continuation_byte c2;
                                  is_valid_utf8_continuation_byte c3;
                                  is_valid_utf8_continuation_byte c4;
                                  is_valid_utf8_continuation_byte c5}
  (int c0, int c1, int c2, int c3, int c4, int c5) -<>
    [u : int]
    (EXTENDED_UTF8_CHAR (6, u, c0, c1, c2, c3, c4, c5) | int u)
 
(*------------------------------------------------------------------*)
 
stadef extended_utf8_shortest_length (u : int) =
  ifint (u < 0, ~1,
    ifint (u <= 0x7F, 1,
      ifint (u <= 0x7FF, 2,
        ifint (u <= 0xFFFF, 3,
          ifint (u <= 0x1FFFFF, 4,
            ifint (u <= 0x3FFFFFF, 5,
              ifint (u <= 0x7FFFFFFF, 6, ~1)))))))
 
dataprop EXTENDED_UTF8_SHORTEST (length : int, u : int, c0 : int, c1 : int,
                                 c2 : int, c3 : int, c4 : int, c5 : int) =
  | {u, c0 : int |
     0 <= u; u <= 0x7F;
     c0 == u}
    EXTENDED_UTF8_SHORTEST_1byte (1, u, c0, ~1, ~1, ~1, ~1, ~1) of
      EXTENDED_UTF8_CHAR (1, u, c0, ~1, ~1, ~1, ~1, ~1)
    //
  | {u, c0, c1 : int |
     0x7F < u; u <= 0x7FF;
     c0 == 0xC0 + (u \ndiv 64);
     c1 == 0x80 + (u \nmod 64)}
    EXTENDED_UTF8_SHORTEST_2byte (2, u, c0, c1, ~1, ~1, ~1, ~1) of
      EXTENDED_UTF8_CHAR (2, u, c0, c1, ~1, ~1, ~1, ~1)
    //
  | {u, c0, c1, c2 : int |
     0x7FF < u; u <= 0xFFFF;
     c0 == 0xE0 + (u \ndiv (64 * 64));
     c1 == 0x80 + ((u \ndiv 64) \nmod 64);
     c2 == 0x80 + (u \nmod 64)}
    EXTENDED_UTF8_SHORTEST_3byte (3, u, c0, c1, c2, ~1, ~1, ~1) of
      EXTENDED_UTF8_CHAR (3, u, c0, c1, c2, ~1, ~1, ~1)
    //
  | {u, c0, c1, c2, c3 : int |
     0xFFFF < u; u <= 0x1FFFFF;
     c0 == 0xF0 + (u \ndiv (64 * 64 * 64));
     c1 == 0x80 + ((u \ndiv (64 * 64)) \nmod 64);
     c2 == 0x80 + ((u \ndiv 64) \nmod 64);
     c3 == 0x80 + (u \nmod 64)}
    EXTENDED_UTF8_SHORTEST_4byte (4, u, c0, c1, c2, c3, ~1, ~1) of
      EXTENDED_UTF8_CHAR (4, u, c0, c1, c2, c3, ~1, ~1)
    //
  | {u, c0, c1, c2, c3, c4 : int |
     0x1FFFFF < u; u <= 0x3FFFFFF;
     c0 == 0xF8 + (u \ndiv (64 * 64 * 64 * 64));
     c1 == 0x80 + ((u \ndiv (64 * 64 * 64)) \nmod 64);
     c2 == 0x80 + ((u \ndiv (64 * 64)) \nmod 64);
     c3 == 0x80 + ((u \ndiv 64) \nmod 64);
     c4 == 0x80 + (u \nmod 64)}
    EXTENDED_UTF8_SHORTEST_5byte (5, u, c0, c1, c2, c3, c4, ~1) of
      EXTENDED_UTF8_CHAR (5, u, c0, c1, c2, c3, c4, ~1)
    //
  | {u, c0, c1, c2, c3, c4, c5 : int |
     0x3FFFFFF < u; u <= 0x7FFFFFFF;
     c0 == 0xFC + (u \ndiv (64 * 64 * 64 * 64 * 64));
     c1 == 0x80 + ((u \ndiv (64 * 64 * 64 * 64)) \nmod 64);
     c2 == 0x80 + ((u \ndiv (64 * 64 * 64)) \nmod 64);
     c3 == 0x80 + ((u \ndiv (64 * 64)) \nmod 64);
     c4 == 0x80 + ((u \ndiv 64) \nmod 64);
     c5 == 0x80 + (u \nmod 64)}
    EXTENDED_UTF8_SHORTEST_6byte (6, u, c0, c1, c2, c3, c4, c5) of
      EXTENDED_UTF8_CHAR (6, u, c0, c1, c2, c3, c4, c5)
 
extern prfun
extended_utf8_shortest_is_char :
  {n : int} {u : int} {c0, c1, c2, c3, c4, c5 : int}
  EXTENDED_UTF8_SHORTEST (n, u, c0, c1, c2, c3, c4, c5) -<prf>
    EXTENDED_UTF8_CHAR (n, u, c0, c1, c2, c3, c4, c5)
 
extern prfun
lemma_extended_utf8_shortest_length :
  {n : int} {u : int} {c0, c1, c2, c3, c4, c5 : int}
  EXTENDED_UTF8_SHORTEST (n, u, c0, c1, c2, c3, c4, c5) -<prf>
    [n == extended_utf8_shortest_length u] void
 
extern fun {}
encode_extended_utf8_character :
  {u : nat | u <= 0x7FFFFFFF}
  int u -<>
    [n : int] [c0, c1, c2, c3, c4, c5 : int]
    @(EXTENDED_UTF8_SHORTEST (n, u, c0, c1, c2, c3, c4, c5) |
      int n, int c0, int c1, int c2, int c3, int c4, int c5)
 
(*------------------------------------------------------------------*)
//
// A valid UTF-8 character is one that encodes a valid Unicode
// code point and is not overlong.
//
 
dataprop UTF8_CHAR_INVALID_CASES (c0 : int, c1 : int, c2 : int, c3 : int) =
  // The cases are not mutually exclusive.
  | {c0, c1, c2, c3 : int | extended_utf8_character_length c0 == ~1}
    // We might not be using this case, presently (has that changed?),
    // but it is included for completeness.
    UTF8_CHAR_INVALID_bad_c0 (c0, c1, c2, c3)
  | {c0, c1, c2, c3 : int | 2 <= extended_utf8_character_length c0;
                            ~(is_valid_utf8_continuation_byte c1)}
    UTF8_CHAR_INVALID_bad_c1 (c0, c1, c2, c3)
  | {c0, c1, c2, c3 : int | 3 <= extended_utf8_character_length c0;
                            ~(is_valid_utf8_continuation_byte c2)}
    UTF8_CHAR_INVALID_bad_c2 (c0, c1, c2, c3)
  | {c0, c1, c2, c3 : int | extended_utf8_character_length c0 == 4;
                            ~(is_valid_utf8_continuation_byte c3)}
    UTF8_CHAR_INVALID_bad_c3 (c0, c1, c2, c3)
  | {c0, c1, c2, c3 : int |
     extended_utf8_character_length c0 == 2;
     extended_utf8_char_2byte_decoding (c0, c1) <= 0x7F}
    UTF8_CHAR_INVALID_invalid_2byte (c0, c1, c2, c3)
  | {c0, c1, c2, c3 : int |
     extended_utf8_character_length c0 == 3
       && (extended_utf8_char_3byte_decoding (c0, c1, c2) <= 0x7FF
             || ~(is_valid_unicode_code_point
                    (extended_utf8_char_3byte_decoding (c0, c1, c2))))}
    UTF8_CHAR_INVALID_invalid_3byte (c0, c1, c2, c3)
  | {c0, c1, c2, c3 : int |
     extended_utf8_character_length c0 == 4
       && (extended_utf8_char_4byte_decoding (c0, c1, c2, c3) <= 0xFFFF
             || ~(is_valid_unicode_code_point
                    (extended_utf8_char_4byte_decoding (c0, c1, c2, c3))))}
    UTF8_CHAR_INVALID_invalid_4byte (c0, c1, c2, c3)
 
dataprop UTF8_CHAR_VALID_BYTES (c0 : int, c1 : int, c2 : int, c3 : int) =
  | {c0 : int | 0 <= c0 && c0 <= 0x7F}
    UTF8_CHAR_VALID_BYTES_1byte (c0, ~1, ~1, ~1)
  | {c0, c1 : int | 0xC2 <= c0 && c0 <= 0xDF;
                    is_valid_utf8_continuation_byte c1}
    UTF8_CHAR_VALID_BYTES_2byte (c0, c1, ~1, ~1)
  | {c0, c1, c2 : int | (0xE1 <= c0 && c0 <= 0xEC)
                          || c0 == 0xEE
                          || c0 == 0xEF
                          || (c0 == 0xE0 && 0xA0 <= c1)
                          || (c0 == 0xED && c1 < 0xA0);
                        is_valid_utf8_continuation_byte c1;
                        is_valid_utf8_continuation_byte c2}
    UTF8_CHAR_VALID_BYTES_3byte (c0, c1, c2, ~1)
  | {c0, c1, c2, c3 : int | (0xF1 <= c0 && c0 <= 0xF3)
                              || (c0 == 0xF0 && 0x90 <= c1)
                              || (c0 == 0xF4 && c1 < 0x90);
                            is_valid_utf8_continuation_byte c1;
                            is_valid_utf8_continuation_byte c2;
                            is_valid_utf8_continuation_byte c3}
    UTF8_CHAR_VALID_BYTES_4byte (c0, c1, c2, c3)
 
dataprop UTF8_CHAR_INVALID_BYTES (c0 : int, c1 : int, c2 : int, c3 : int) =
  | // This should never occur.
    {c0 : int | is_extended_utf8_1byte_first_byte c0}
    UTF8_CHAR_INVALID_BYTES_1byte (c0, ~1, ~1, ~1)
  | {c0, c1 : int | is_extended_utf8_2byte_first_byte c0;
                    c0 == 0xC0 || c0 == 0xC1 || 
                    is_invalid_utf8_continuation_byte c1}
    UTF8_CHAR_INVALID_BYTES_2byte (c0, c1, ~1, ~1)
  | {c0, c1, c2 : int | is_extended_utf8_3byte_first_byte c0;
                        (c0 == 0xE0 && c1 < 0xA0) ||
                        (c0 == 0xED && 0xA0 <= c1) ||
                        is_invalid_utf8_continuation_byte c1 ||
                        is_invalid_utf8_continuation_byte c2}
    UTF8_CHAR_INVALID_BYTES_3byte (c0, c1, c2, ~1)
  | {c0, c1, c2, c3 : int | is_extended_utf8_4byte_first_byte c0;
                            0xF4 < c0 ||
                            (c0 == 0xF0 && c1 < 0x90) ||
                            (c0 == 0xF4 && 0x90 <= c1) ||
                            is_invalid_utf8_continuation_byte c1 ||
                            is_invalid_utf8_continuation_byte c2 ||
                            is_invalid_utf8_continuation_byte c3}
    UTF8_CHAR_INVALID_BYTES_4byte (c0, c1, c2, c3)
  | {c0, c1, c2, c3 : int | ~(is_extended_utf8_1byte_first_byte c0);
                            ~(is_extended_utf8_2byte_first_byte c0);
                            ~(is_extended_utf8_3byte_first_byte c0);
                            ~(is_extended_utf8_4byte_first_byte c0)}
    UTF8_CHAR_INVALID_BYTES_bad_c0 (c0, c1, c2, c3)
 
dataprop UTF8_CHAR_VALIDITY (n : int, u : int, c0 : int, c1 : int,
                             c2 : int, c3 : int, b : bool) =
  | {n : int} {u : int | is_valid_unicode_code_point u}
    {c0, c1, c2, c3 : int}
    UTF8_CHAR_valid (n, u, c0, c1, c2, c3, true) of
      (EXTENDED_UTF8_SHORTEST (n, u, c0, c1, c2, c3, ~1, ~1),
       UTF8_CHAR_VALID_BYTES (c0, c1, c2, c3))
  | {n : int} {u : int} {c0, c1, c2, c3 : int}
    UTF8_CHAR_invalid (n, u, c0, c1, c2, c3, false) of
      (UTF8_CHAR_INVALID_CASES (c0, c1, c2, c3),
       UTF8_CHAR_INVALID_BYTES (c0, c1, c2, c3))
 
propdef UTF8_CHAR_VALID (n : int, u : int, c0 : int, c1 : int,
                         c2 : int, c3 : int) =
  UTF8_CHAR_VALIDITY (n, u, c0, c1, c2, c3, true)
 
propdef UTF8_CHAR_INVALID (c0 : int, c1 : int, c2 : int, c3 : int) =
  [n : int] [u : int] UTF8_CHAR_VALIDITY (n, u, c0, c1, c2, c3, false)
 
extern prfun
utf8_char_valid_implies_shortest :
  {n : int} {u : int} {c0, c1, c2, c3 : int}
  UTF8_CHAR_VALID (n, u, c0, c1, c2, c3) -<prf>
    EXTENDED_UTF8_SHORTEST (n, u, c0, c1, c2, c3, ~1, ~1)
 
extern prfun
lemma_valid_utf8_character_1byte :
  {u, c0 : int |
   is_extended_utf8_1byte_first_byte c0;
   u == extended_utf8_char_1byte_decoding c0;
   0 <= u; u <= 0x7F}
  () -<prf> UTF8_CHAR_VALID (1, u, c0, ~1, ~1, ~1)
 
extern prfun
lemma_valid_utf8_character_2byte :
  {u, c0, c1 : int |
   is_extended_utf8_2byte_first_byte c0;
   is_valid_utf8_continuation_byte c1;
   u == extended_utf8_char_2byte_decoding (c0, c1);
   0x7F < u; u <= 0x7FF}
  () -<prf> UTF8_CHAR_VALID (2, u, c0, c1, ~1, ~1)
 
extern prfun
lemma_valid_utf8_character_3byte :
  {u, c0, c1, c2 : int |
   is_extended_utf8_3byte_first_byte c0;
   is_valid_utf8_continuation_byte c1;
   is_valid_utf8_continuation_byte c2;
   u == extended_utf8_char_3byte_decoding (c0, c1, c2);
   0x7FF < u; u <= 0xFFFF;
   //
   // Exclude the UTF-16 surrogate halves.
   //
   ~(0xD800 <= u && u < 0xE000)}
  () -<prf> UTF8_CHAR_VALID (3, u, c0, c1, c2, ~1)
 
extern prfun
lemma_valid_utf8_character_4byte :
  {u, c0, c1, c2, c3 : int |
   is_extended_utf8_4byte_first_byte c0;
   is_valid_utf8_continuation_byte c1;
   is_valid_utf8_continuation_byte c2;
   is_valid_utf8_continuation_byte c3;
   u == extended_utf8_char_4byte_decoding (c0, c1, c2, c3);
   0xFFFF < u; u <= 0x10FFFF}
  () -<prf> UTF8_CHAR_VALID (4, u, c0, c1, c2, c3)
 
extern prfun
utf8_character_1byte_valid_bytes :
  // This does not really do anything, but is included
  // for completeness.
  {u, c0 : int | is_extended_utf8_1byte_first_byte c0}
  UTF8_CHAR_VALID (1, u, c0, ~1, ~1, ~1) -<prf> void
 
extern prfun
utf8_character_2byte_valid_bytes :
  {u, c0, c1 : int | is_extended_utf8_2byte_first_byte c0}
  UTF8_CHAR_VALID (2, u, c0, c1, ~1, ~1) -<prf>
    [0xC2 <= c0; c0 <= 0xDF;
     is_valid_utf8_continuation_byte c1]
    void
 
extern prfun
utf8_character_3byte_valid_bytes :
  {u, c0, c1, c2 : int | is_extended_utf8_3byte_first_byte c0}
  UTF8_CHAR_VALID (3, u, c0, c1, c2, ~1) -<prf>
    [(0xE1 <= c0 && c0 <= 0xEC)
        || c0 == 0xEE
        || c0 == 0xEF
        || (c0 == 0xE0 && 0xA0 <= c1)
        || (c0 == 0xED && c1 < 0xA0);
     is_valid_utf8_continuation_byte c1;
     is_valid_utf8_continuation_byte c2]
    void
 
extern prfun
utf8_character_4byte_valid_bytes :
  {u, c0, c1, c2, c3 : int | is_extended_utf8_4byte_first_byte c0}
  UTF8_CHAR_VALID (4, u, c0, c1, c2, c3) -<prf>
    [(0xF1 <= c0 && c0 <= 0xF3)
        || (c0 == 0xF0 && 0x90 <= c1)
        || (c0 == 0xF4 && c1 < 0x90);
     is_valid_utf8_continuation_byte c1;
     is_valid_utf8_continuation_byte c2;
     is_valid_utf8_continuation_byte c3]
    void
 
extern prfun
utf8_character_1byte_invalid_bytes :
  // This does not really do anything, but is included
  // for completeness.
  {c0 : int | is_extended_utf8_1byte_first_byte c0}
  UTF8_CHAR_INVALID (c0, ~1, ~1, ~1) -<prf> void
 
extern prfun
utf8_character_2byte_invalid_bytes :
  {c0, c1 : int | is_extended_utf8_2byte_first_byte c0}
  UTF8_CHAR_INVALID (c0, c1, ~1, ~1) -<prf>
    [c0 == 0xC0 || c0 == 0xc1 || is_invalid_utf8_continuation_byte c1]
    void
 
extern prfun
utf8_character_3byte_invalid_bytes :
  {c0, c1, c2 : int | is_extended_utf8_3byte_first_byte c0}
  UTF8_CHAR_INVALID (c0, c1, c2, ~1) -<prf>
    [(c0 == 0xE0 && c1 < 0xA0) ||
     (c0 == 0xED && 0xA0 <= c1) ||
     is_invalid_utf8_continuation_byte c1 ||
     is_invalid_utf8_continuation_byte c2]
    void
 
extern prfun
utf8_character_4byte_invalid_bytes :
  {c0, c1, c2, c3 : int | is_extended_utf8_4byte_first_byte c0}
  UTF8_CHAR_INVALID (c0, c1, c2, c3) -<prf>
    [0xF4 < c0 ||
     (c0 == 0xF0 && c1 < 0x90) ||
     (c0 == 0xF4 && 0x90 <= c1) ||
     is_invalid_utf8_continuation_byte c1 ||
     is_invalid_utf8_continuation_byte c2 ||
     is_invalid_utf8_continuation_byte c3]
    void
 
extern fun {}
is_valid_utf8_character_1byte :
  {c0 : int | is_extended_utf8_1byte_first_byte c0}
  int c0 -<>
    [b : bool | b == true] [u : int]
    (UTF8_CHAR_VALIDITY (1, u, c0, ~1, ~1, ~1, b) | bool b)
 
extern fun {}
is_valid_utf8_character_2byte :
  {c0, c1 : int | is_extended_utf8_2byte_first_byte c0}
  (int c0, int c1) -<>
    [b : bool] [u : int]
    (UTF8_CHAR_VALIDITY (2, u, c0, c1, ~1, ~1, b) | bool b)
 
extern fun {}
is_valid_utf8_character_3byte :
  {c0, c1, c2 : int | is_extended_utf8_3byte_first_byte c0}
  (int c0, int c1, int c2) -<>
    [b : bool] [u : int]
    (UTF8_CHAR_VALIDITY (3, u, c0, c1, c2, ~1, b) | bool b)
 
extern fun {}
is_valid_utf8_character_4byte :
  {c0, c1, c2, c3 : int | is_extended_utf8_4byte_first_byte c0}
  (int c0, int c1, int c2, int c3) -<>
    [b : bool] [u : int]
    (UTF8_CHAR_VALIDITY (4, u, c0, c1, c2, c3, b) | bool b)
 
extern fun {}
decode_utf8_1byte :
  {c0 : int | is_extended_utf8_1byte_first_byte c0}
  int c0 -<> [u : int | 0 <= u; u <= 0x7F] int u
 
extern fun {}
decode_utf8_2byte :
  {c0, c1 : int | 0xC2 <= c0; c0 <= 0xDF;
                  is_valid_utf8_continuation_byte c1}
  (int c0, int c1) -<> [u : int | 0x7F < u; u <= 0x7FF] int u
 
extern fun {}
decode_utf8_3byte :
  {c0, c1, c2 : int | (0xE1 <= c0 && c0 <= 0xEC)
                        || c0 == 0xEE
                        || c0 == 0xEF
                        || (c0 == 0xE0 && 0xA0 <= c1)
                        || (c0 == 0xED && c1 < 0xA0);
                      is_valid_utf8_continuation_byte c1;
                      is_valid_utf8_continuation_byte c2}
  (int c0, int c1, int c2) -<>
    [u : int | 0x7FF < u; u <= 0xFFFF; u < 0xD800 || 0xE000 <= u] int u
 
extern fun {}
decode_utf8_4byte :
  {c0, c1, c2, c3 : int | (0xF1 <= c0 && c0 <= 0xF3)
                            || (c0 == 0xF0 && 0x90 <= c1)
                            || (c0 == 0xF4 && c1 < 0x90);
                          is_valid_utf8_continuation_byte c1;
                          is_valid_utf8_continuation_byte c2;
                          is_valid_utf8_continuation_byte c3}
  (int c0, int c1, int c2, int c3) -<>
    [u : int | 0xFFFF < u; u <= 0x10FFFF] int u
 
extern fun {}
encode_utf8_character :
  {u : int | is_valid_unicode_code_point u}
  int u -<>
    [n : int]
    [c0, c1, c2, c3 : int | extended_utf8_char_length_relation (c0, n)]
    @(UTF8_CHAR_VALID (n, u, c0, c1, c2, c3) |
      int n, int c0, int c1, int c2, int c3)
 
(*------------------------------------------------------------------*)
 
#define utf8_decode_next_error_char ~1
 
(* Returns @(utf8_decode_next_error_char, ..) on error. *)
extern fun
utf8_array_decode_next {utf8len    : int | 0 < utf8len}
                       {n_utf8arr  : int | utf8len <= n_utf8arr}
                       {i          : int | i < utf8len}
                       (utf8len    : size_t utf8len,
                        utf8arr    : &(@[char][n_utf8arr]),
                        i          : size_t i) :<>
    [c      : int | is_valid_unicode_code_point c ||
                    c == utf8_decode_next_error_char]
    [i_next : int | i_next == i + 1 || i_next == i + 2 ||
                    i_next == i + 3 || i_next == i + 4;
                    i_next <= utf8len]
    @(int c, size_t i_next)
 
extern fun
utf8_string_decode_next {utf8len    : int | 0 < utf8len}
                        {n_utf8str  : int | utf8len <= n_utf8str}
                        {i          : int | i < utf8len}
                        (utf8len    : size_t utf8len,
                         utf8str    : string n_utf8str,
                         i          : size_t i) :<>
    [c      : int | is_valid_unicode_code_point c ||
                    c == utf8_decode_next_error_char]
    [i_next : int | i_next == i + 1 || i_next == i + 2 ||
                    i_next == i + 3 || i_next == i + 4;
                    i_next <= utf8len]
    @(int c, size_t i_next)
 
overload utf8_decode_next with utf8_array_decode_next
overload utf8_decode_next with utf8_string_decode_next
 
(*###################### IMPLEMENTATION ############################*)
 
// Integer division by 64 is equivalent to shifting right
// by 6 bits.
 
extern prfun _shift_right_twelve :
  {x, y, z : nat | y == (x \ndiv 64);
                   z == (y \ndiv 64)}
  (int x, int y, int z) -<prf> [z == (x \ndiv (64 * 64))] void
 
primplement _shift_right_twelve (x, y, z) =
  ()
 
extern prfun _shift_right_eighteen :
  {x, y, z, u : nat | y == (x \ndiv 64);
                      z == (y \ndiv 64);
                      u == (z \ndiv 64)}
  (int x, int y, int z, int u) -<prf> [u == (x \ndiv (64 * 64 * 64))] void
 
primplement _shift_right_eighteen (x, y, z, u) = ()
 
 
extern prfun _shift_right_twenty_four :
  {x, y, z, u, v : nat | y == (x \ndiv 64);
                         z == (y \ndiv 64);
                         u == (z \ndiv 64);
                         v == (u \ndiv 64)}
  (int x, int y, int z, int u, int v) -<prf>
    [v == (x \ndiv (64 * 64 * 64 * 64))] void
 
primplement _shift_right_twenty_four (x, y, z, u, v) = ()
 
(*------------------------------------------------------------------*)
 
implement {}
is_valid_unicode_code_point u =
  ((0x0 <= u) * (u < 0xD800)) + ((0xE000 <= u) * (u <= 0x10FFFF))
 
(*------------------------------------------------------------------*)
 
primplement
extended_utf8_char_length_relation_to_length () = ()
 
primplement
extended_utf8_char_length_to_length_relation () = ()
 
primplement
utf8_char_length_relation_to_length () = ()
 
primplement
utf8_char_length_to_length_relation () = ()
 
(*------------------------------------------------------------------*)
 
implement {}
is_valid_utf8_continuation_byte c =
  (0x80 <= c) * (c <= 0xBF)
 
(*------------------------------------------------------------------*)
 
primplement
decode_extended_utf8_istot {n} {c0, c1, c2, c3, c4, c5} () =
  sif n == 1 then
    let
      prfn
      make_pf {u : int | u == extended_utf8_char_1byte_decoding (c0)}
              () :<prf>
          EXTENDED_UTF8_CHAR (n, u, c0, c1, c2, c3, c4, c5) =
        EXTENDED_UTF8_CHAR_1byte ()
      stadef u = extended_utf8_char_1byte_decoding (c0)
    in
      make_pf {u} ()
    end
  else sif n == 2 then
    let
      prfn
      make_pf {u : int | u == extended_utf8_char_2byte_decoding (c0, c1)}
              () :<prf>
          EXTENDED_UTF8_CHAR (n, u, c0, c1, c2, c3, c4, c5) =
        EXTENDED_UTF8_CHAR_2byte ()
      stadef u = extended_utf8_char_2byte_decoding (c0, c1)
    in
      make_pf {u} ()
    end
  else sif n == 3 then
    let
      prfn
      make_pf {u : int | u == extended_utf8_char_3byte_decoding (c0, c1, c2)}
              () :<prf>
          EXTENDED_UTF8_CHAR (n, u, c0, c1, c2, c3, c4, c5) =
        EXTENDED_UTF8_CHAR_3byte ()
      stadef u = extended_utf8_char_3byte_decoding (c0, c1, c2)
    in
      make_pf {u} ()
    end
  else sif n == 4 then
    let
      prfn
      make_pf {u : int |
               u == extended_utf8_char_4byte_decoding (c0, c1, c2, c3)}
              () :<prf>
          EXTENDED_UTF8_CHAR (n, u, c0, c1, c2, c3, c4, c5) =
        EXTENDED_UTF8_CHAR_4byte ()
      stadef u = extended_utf8_char_4byte_decoding (c0, c1, c2, c3)
    in
      make_pf {u} ()
    end
  else sif n == 5 then
    let
      prfn
      make_pf {u : int |
               u == extended_utf8_char_5byte_decoding (c0, c1, c2, c3, c4)}
              () :<prf>
          EXTENDED_UTF8_CHAR (n, u, c0, c1, c2, c3, c4, c5) =
        EXTENDED_UTF8_CHAR_5byte ()
      stadef u = extended_utf8_char_5byte_decoding (c0, c1, c2, c3, c4)
    in
      make_pf {u} ()
    end
  else
    let
      prfn
      make_pf {u : int |
               u == extended_utf8_char_6byte_decoding (c0, c1, c2, c3, c4, c5)}
              () :<prf>
          EXTENDED_UTF8_CHAR (n, u, c0, c1, c2, c3, c4, c5) =
        EXTENDED_UTF8_CHAR_6byte ()
      stadef u = extended_utf8_char_6byte_decoding (c0, c1, c2, c3, c4, c5)
    in
      make_pf {u} ()
    end
 
primplement
decode_extended_utf8_isfun {na} (pf_a, pf_b) =
  sif na == 1 then
    let
      prval EXTENDED_UTF8_CHAR_1byte () = pf_a
      prval EXTENDED_UTF8_CHAR_1byte () = pf_b
    in
    end
  else sif na == 2 then
    let
      prval EXTENDED_UTF8_CHAR_2byte () = pf_a
      prval EXTENDED_UTF8_CHAR_2byte () = pf_b
    in
    end
  else sif na == 3 then
    let
      prval EXTENDED_UTF8_CHAR_3byte () = pf_a
      prval EXTENDED_UTF8_CHAR_3byte () = pf_b
    in
    end
  else sif na == 4 then
    let
      prval EXTENDED_UTF8_CHAR_4byte () = pf_a
      prval EXTENDED_UTF8_CHAR_4byte () = pf_b
    in
    end
  else sif na == 5 then
    let
      prval EXTENDED_UTF8_CHAR_5byte () = pf_a
      prval EXTENDED_UTF8_CHAR_5byte () = pf_b
    in
    end
  else
    let
      prval EXTENDED_UTF8_CHAR_6byte () = pf_a
      prval EXTENDED_UTF8_CHAR_6byte () = pf_b
    in
    end
 
 
primplement
lemma_extended_utf8_char_length pf_char =
  case+ pf_char of
  | EXTENDED_UTF8_CHAR_1byte () => ()
  | EXTENDED_UTF8_CHAR_2byte () => ()
  | EXTENDED_UTF8_CHAR_3byte () => ()
  | EXTENDED_UTF8_CHAR_4byte () => ()
  | EXTENDED_UTF8_CHAR_5byte () => ()
  | EXTENDED_UTF8_CHAR_6byte () => ()
 
implement {}
decode_extended_utf8_1byte c0 =
  (EXTENDED_UTF8_CHAR_1byte () | c0)
 
implement {}
decode_extended_utf8_2byte (c0, c1) =
  let
    val u0 = c0 - 0xC0
    val u1 = c1 - 0x80
  in
    (EXTENDED_UTF8_CHAR_2byte () | 64 * u0 + u1)
  end
 
implement {}
decode_extended_utf8_3byte (c0, c1, c2) =
  let
    val u0 = c0 - 0xE0
    val u1 = c1 - 0x80
    val u2 = c2 - 0x80
  in
    (EXTENDED_UTF8_CHAR_3byte () | 64 * (64 * u0 + u1) + u2)
  end
 
implement {}
decode_extended_utf8_4byte (c0, c1, c2, c3) =
  let
    val u0 = c0 - 0xF0
    val u1 = c1 - 0x80
    val u2 = c2 - 0x80
    val u3 = c3 - 0x80
  in
    (EXTENDED_UTF8_CHAR_4byte () | 64 * (64 * (64 * u0 + u1) + u2) + u3)
  end
 
implement {}
decode_extended_utf8_5byte (c0, c1, c2, c3, c4) =
  let
    val u0 = c0 - 0xF8
    val u1 = c1 - 0x80
    val u2 = c2 - 0x80
    val u3 = c3 - 0x80
    val u4 = c4 - 0x80
  in
    (EXTENDED_UTF8_CHAR_5byte () |
     64 * (64 * (64 * (64 * u0 + u1) + u2) + u3) + u4)
  end
 
implement {}
decode_extended_utf8_6byte (c0, c1, c2, c3, c4, c5) =
  let
    val u0 = c0 - 0xFC
    val u1 = c1 - 0x80
    val u2 = c2 - 0x80
    val u3 = c3 - 0x80
    val u4 = c4 - 0x80
    val u5 = c5 - 0x80
  in
    (EXTENDED_UTF8_CHAR_6byte () |
     64 * (64 * (64 * (64 * (64 * u0 + u1) + u2) + u3) + u4) + u5)
  end
 
(*------------------------------------------------------------------*)
 
primplement
extended_utf8_shortest_is_char pf_shortest =
  case+ pf_shortest of
  | EXTENDED_UTF8_SHORTEST_1byte pf_char => pf_char
  | EXTENDED_UTF8_SHORTEST_2byte pf_char => pf_char
  | EXTENDED_UTF8_SHORTEST_3byte pf_char => pf_char
  | EXTENDED_UTF8_SHORTEST_4byte pf_char => pf_char
  | EXTENDED_UTF8_SHORTEST_5byte pf_char => pf_char
  | EXTENDED_UTF8_SHORTEST_6byte pf_char => pf_char
 
primplement
lemma_extended_utf8_shortest_length pf_shortest =
  case+ pf_shortest of
  | EXTENDED_UTF8_SHORTEST_1byte pf_char =>
    { prval EXTENDED_UTF8_CHAR_1byte () = pf_char }
  | EXTENDED_UTF8_SHORTEST_2byte pf_char =>
    { prval EXTENDED_UTF8_CHAR_2byte () = pf_char }
  | EXTENDED_UTF8_SHORTEST_3byte pf_char =>
    { prval EXTENDED_UTF8_CHAR_3byte () = pf_char }
  | EXTENDED_UTF8_SHORTEST_4byte pf_char =>
    { prval EXTENDED_UTF8_CHAR_4byte () = pf_char }
  | EXTENDED_UTF8_SHORTEST_5byte pf_char =>
    { prval EXTENDED_UTF8_CHAR_5byte () = pf_char }
  | EXTENDED_UTF8_SHORTEST_6byte pf_char =>
    { prval EXTENDED_UTF8_CHAR_6byte () = pf_char }
 
implement {}
encode_extended_utf8_character u =
  if u <= 0x7F then
    let
      val c0 = u
    in
      @(EXTENDED_UTF8_SHORTEST_1byte (EXTENDED_UTF8_CHAR_1byte ()) |
        1, c0, ~1, ~1, ~1, ~1, ~1)
    end
  else if u <= 0x7FF then
    let
      val c1 = 0x80 + (u \nmod 64)
      val u1 = u \ndiv 64
      val c0 = 0xC0 + u1
    in
      @(EXTENDED_UTF8_SHORTEST_2byte (EXTENDED_UTF8_CHAR_2byte ()) |
        2, c0, c1, ~1, ~1, ~1, ~1)
    end
  else if u <= 0xFFFF then
    let
      val c2 = 0x80 + (u \nmod 64)
      val u1 = u \ndiv 64
      val c1 = 0x80 + (u1 \nmod 64)
      val u2 = u1 \ndiv 64
      val c0 = 0xE0 + u2
    in
      @(EXTENDED_UTF8_SHORTEST_3byte (EXTENDED_UTF8_CHAR_3byte ()) |
        3, c0, c1, c2, ~1, ~1, ~1)
    end
  else if u <= 0x1FFFFF then
    let
      val c3 = 0x80 + (u \nmod 64)
      val u1 = u \ndiv 64
      val c2 = 0x80 + (u1 \nmod 64)
      val u2 = u1 \ndiv 64
      val c1 = 0x80 + (u2 \nmod 64)
      val u3 = u2 \ndiv 64
      val c0 = 0xF0 + u3
      prval () = _shift_right_twelve (u, u1, u2)
    in
      @(EXTENDED_UTF8_SHORTEST_4byte (EXTENDED_UTF8_CHAR_4byte ()) |
        4, c0, c1, c2, c3, ~1, ~1)
    end
  else if u <= 0x3FFFFFF then
    let
      val c4 = 0x80 + (u \nmod 64)
      val u1 = u \ndiv 64
      val c3 = 0x80 + (u1 \nmod 64)
      val u2 = u1 \ndiv 64
      val c2 = 0x80 + (u2 \nmod 64)
      val u3 = u2 \ndiv 64
      val c1 = 0x80 + (u3 \nmod 64)
      val u4 = u3 \ndiv 64
      val c0 = 0xF8 + u4
      prval () = _shift_right_twelve (u, u1, u2)
      prval () = _shift_right_eighteen (u, u1, u2, u3)
    in
      @(EXTENDED_UTF8_SHORTEST_5byte (EXTENDED_UTF8_CHAR_5byte ()) |
        5, c0, c1, c2, c3, c4, ~1)
    end
  else
    let
      val c5 = 0x80 + (u \nmod 64)
      val u1 = u \ndiv 64
      val c4 = 0x80 + (u1 \nmod 64)
      val u2 = u1 \ndiv 64
      val c3 = 0x80 + (u2 \nmod 64)
      val u3 = u2 \ndiv 64
      val c2 = 0x80 + (u3 \nmod 64)
      val u4 = u3 \ndiv 64
      val c1 = 0x80 + (u4 \nmod 64)
      val u5 = u4 \ndiv 64
      val c0 = 0xFC + u5
      prval () = _shift_right_twelve (u, u1, u2)
      prval () = _shift_right_eighteen (u, u1, u2, u3)
      prval () = _shift_right_twenty_four (u, u1, u2, u3, u4)
    in
      @(EXTENDED_UTF8_SHORTEST_6byte (EXTENDED_UTF8_CHAR_6byte ()) |
        6, c0, c1, c2, c3, c4, c5)
    end
 
(*------------------------------------------------------------------*)
 
primplement
utf8_char_valid_implies_shortest pf_valid =
  case+ pf_valid of
  | UTF8_CHAR_valid (pf_shortest, _) => pf_shortest
 
primplement
lemma_valid_utf8_character_1byte {u, c0} () =
  let
    prfn
    make_pf {u : int | u == extended_utf8_char_1byte_decoding (c0)}
            () :<prf> EXTENDED_UTF8_CHAR (1, u, c0, ~1, ~1, ~1, ~1, ~1) =
      EXTENDED_UTF8_CHAR_1byte ()
    prval pf_char = make_pf {u} ()
    prval pf_shortest = EXTENDED_UTF8_SHORTEST_1byte pf_char
    prval pf_bytes = UTF8_CHAR_VALID_BYTES_1byte ()
    prval pf_valid = UTF8_CHAR_valid (pf_shortest, pf_bytes)
  in
    pf_valid
  end
 
primplement
lemma_valid_utf8_character_2byte {u, c0, c1} () =
  let
    prfn
    make_pf {u : int | u == extended_utf8_char_2byte_decoding (c0, c1)}
            () :<prf> EXTENDED_UTF8_CHAR (2, u, c0, c1, ~1, ~1, ~1, ~1) =
      EXTENDED_UTF8_CHAR_2byte ()
    prval pf_char = make_pf {u} ()
    prval pf_shortest = EXTENDED_UTF8_SHORTEST_2byte pf_char
    prval pf_bytes = UTF8_CHAR_VALID_BYTES_2byte ()
    prval pf_valid = UTF8_CHAR_valid (pf_shortest, pf_bytes)
  in
    pf_valid
  end
 
primplement
lemma_valid_utf8_character_3byte {u, c0, c1, c2} () =
  let
    prfn
    make_pf {u : int | u == extended_utf8_char_3byte_decoding (c0, c1, c2)}
            () :<prf> EXTENDED_UTF8_CHAR (3, u, c0, c1, c2, ~1, ~1, ~1) =
      EXTENDED_UTF8_CHAR_3byte ()
    prval pf_char = make_pf {u} ()
    prfn lemma_c1 () :<prf> [c1 == 0x80 + ((u \ndiv 64) \nmod 64)] void =
      {
        // Convert to Horner form.
        stadef u1 = 64 * (64 * (c0 - 0xE0) + (c1 - 0x80)) + (c2 - 0x80)
        prval EQINT () = eqint_make {u, u1} ()
      }
    prval () = lemma_c1 ()
    prval pf_shortest = EXTENDED_UTF8_SHORTEST_3byte pf_char
    prval pf_bytes = UTF8_CHAR_VALID_BYTES_3byte ()
    prval pf_valid = UTF8_CHAR_valid (pf_shortest, pf_bytes)
  in
    pf_valid
  end
 
primplement
lemma_valid_utf8_character_4byte {u, c0, c1, c2, c3} () =
  let
    prfn
    make_pf {u : int | u == extended_utf8_char_4byte_decoding (c0, c1, c2, c3)}
            () :<prf> EXTENDED_UTF8_CHAR (4, u, c0, c1, c2, c3, ~1, ~1) =
      EXTENDED_UTF8_CHAR_4byte ()
    prval pf_char = make_pf {u} ()
    prfn lemma_c1 () :<prf> [c1 == 0x80 + ((u \ndiv (64 * 64)) \nmod 64)] void =
      {
        prval EQINT () =
          eqint_make {(64 * 64 * (c0 - 0xE0)) \ndiv (64 * 64), c0 - 0xE0} ()
        prval pfd = divmod_istot {u \ndiv (64 * 64), 64} ()
        prval pfm = divmod_elim pfd
        prval () = mul_elim pfm
      }
    prval () = lemma_c1 ()
    prfn lemma_c2 () :<prf> [c2 == 0x80 + ((u \ndiv 64) \nmod 64)] void =
      {
        // Convert to Horner form.
        stadef u1 = 64 * (64 * (64 * (c0 - 0xF0) + (c1 - 0x80)) + (c2 - 0x80))
                     + (c3 - 0x80)
        prval EQINT () = eqint_make {u, u1} ()
      }
    prval () = lemma_c2 ()
    prval pf_shortest = EXTENDED_UTF8_SHORTEST_4byte pf_char
    prval pf_bytes = UTF8_CHAR_VALID_BYTES_4byte ()
    prval pf_valid = UTF8_CHAR_valid (pf_shortest, pf_bytes)
  in
    pf_valid
  end
 
primplement
utf8_character_1byte_valid_bytes pf_valid = ()
 
primplement
utf8_character_2byte_valid_bytes pf_valid =
  {
    prval UTF8_CHAR_valid (_, pf_bytes) = pf_valid
    prval UTF8_CHAR_VALID_BYTES_2byte () = pf_bytes
  }
 
primplement
utf8_character_3byte_valid_bytes pf_valid =
  {
    prval UTF8_CHAR_valid (_, pf_bytes) = pf_valid
    prval UTF8_CHAR_VALID_BYTES_3byte () = pf_bytes
  }
 
primplement
utf8_character_4byte_valid_bytes pf_valid =
  {
    prval UTF8_CHAR_valid (_, pf_bytes) = pf_valid
    prval UTF8_CHAR_VALID_BYTES_4byte () = pf_bytes
  }
 
primplement
utf8_character_1byte_invalid_bytes pf_invalid =
  {
    prval UTF8_CHAR_invalid (_, pf_bytes) = pf_invalid
    prval UTF8_CHAR_INVALID_BYTES_1byte () = pf_bytes
  }
 
primplement
utf8_character_2byte_invalid_bytes pf_invalid =
  {
    prval UTF8_CHAR_invalid (_, pf_bytes) = pf_invalid
    prval UTF8_CHAR_INVALID_BYTES_2byte () = pf_bytes
  }
 
primplement
utf8_character_3byte_invalid_bytes pf_invalid =
  {
    prval UTF8_CHAR_invalid (_, pf_bytes) = pf_invalid
    prval UTF8_CHAR_INVALID_BYTES_3byte () = pf_bytes
  }
 
primplement
utf8_character_4byte_invalid_bytes pf_invalid =
  {
    prval UTF8_CHAR_invalid (_, pf_bytes) = pf_invalid
    prval UTF8_CHAR_INVALID_BYTES_4byte () = pf_bytes
  }
 
implement {}
is_valid_utf8_character_1byte {c0} c0 =
  let
    stadef u = extended_utf8_char_1byte_decoding c0
  in
    (lemma_valid_utf8_character_1byte {u, c0} () | true)
  end
 
implement {}
is_valid_utf8_character_2byte {c0, c1} (c0, c1) =
  let
    stadef u = extended_utf8_char_2byte_decoding (c0, c1)
  in
    if not (is_valid_utf8_continuation_byte c1) then
      (UTF8_CHAR_invalid (UTF8_CHAR_INVALID_bad_c1 (),
                          UTF8_CHAR_INVALID_BYTES_2byte ()) |
       false)
    else if c0 < 0xC2 then
      // The sequence is overlong.
      (UTF8_CHAR_invalid (UTF8_CHAR_INVALID_invalid_2byte (),
                          UTF8_CHAR_INVALID_BYTES_2byte ()) |
       false)
    else
      (lemma_valid_utf8_character_2byte {u, c0, c1} () | true)
  end
 
implement {}
is_valid_utf8_character_3byte {c0, c1, c2} (c0, c1, c2) =
  let
    stadef u = extended_utf8_char_3byte_decoding (c0, c1, c2)
  in
    if not (is_valid_utf8_continuation_byte c1) then
      (UTF8_CHAR_invalid (UTF8_CHAR_INVALID_bad_c1 (),
                          UTF8_CHAR_INVALID_BYTES_3byte ()) |
       false)
    else if not (is_valid_utf8_continuation_byte c2) then
      (UTF8_CHAR_invalid (UTF8_CHAR_INVALID_bad_c2 (),
                          UTF8_CHAR_INVALID_BYTES_3byte ()) |
       false)
    else if (0xE1 <= c0) * (c0 <= 0xEC) then
      (lemma_valid_utf8_character_3byte {u, c0, c1, c2} () | true)
    else if c0 = 0xEE then
      (lemma_valid_utf8_character_3byte {u, c0, c1, c2} () | true)
    else if c0 = 0xEF then
      (lemma_valid_utf8_character_3byte {u, c0, c1, c2} () | true)
    else if (c0 = 0xE0) * (0xA0 <= c1) then
      (lemma_valid_utf8_character_3byte {u, c0, c1, c2} () | true)
    else if (c0 = 0xED) * (c1 < 0xA0) then
      (lemma_valid_utf8_character_3byte {u, c0, c1, c2} () | true)
    else
      // Either the sequence is overlong or it decodes to
      // an invalid code point.
      (UTF8_CHAR_invalid (UTF8_CHAR_INVALID_invalid_3byte (),
                          UTF8_CHAR_INVALID_BYTES_3byte ()) |
       false)
  end
 
implement {}
is_valid_utf8_character_4byte {c0, c1, c2, c3} (c0, c1, c2, c3) =
  let
    stadef u = extended_utf8_char_4byte_decoding (c0, c1, c2, c3)
  in
    if not (is_valid_utf8_continuation_byte c1) then
      (UTF8_CHAR_invalid (UTF8_CHAR_INVALID_bad_c1 (),
                          UTF8_CHAR_INVALID_BYTES_4byte ()) |
       false)
    else if not (is_valid_utf8_continuation_byte c2) then
      (UTF8_CHAR_invalid (UTF8_CHAR_INVALID_bad_c2 (),
                          UTF8_CHAR_INVALID_BYTES_4byte ()) |
       false)
    else if not (is_valid_utf8_continuation_byte c3) then
      (UTF8_CHAR_invalid (UTF8_CHAR_INVALID_bad_c3 (),
                          UTF8_CHAR_INVALID_BYTES_4byte ()) |
       false)
    else if (0xF1 <= c0) * (c0 <= 0xF3) then
      (lemma_valid_utf8_character_4byte {u, c0, c1, c2, c3} () | true)
    else if (c0 = 0xF0) * (0x90 <= c1) then
      (lemma_valid_utf8_character_4byte {u, c0, c1, c2, c3} () | true)
    else if (c0 = 0xF4) * (c1 < 0x90) then
      (lemma_valid_utf8_character_4byte {u, c0, c1, c2, c3} () | true)
    else
      // Either the sequence is overlong or it decodes to
      // an invalid code point.
      (UTF8_CHAR_invalid (UTF8_CHAR_INVALID_invalid_4byte (),
                          UTF8_CHAR_INVALID_BYTES_4byte ()) |
       false)
  end
 
implement {}
decode_utf8_1byte c0 = c0
 
implement {}
decode_utf8_2byte (c0, c1) =
  let
    val u0 = c0 - 0xC0
    val u1 = c1 - 0x80
  in
    64 * u0 + u1
  end
 
implement {}
decode_utf8_3byte (c0, c1, c2) =
  let
    val u0 = c0 - 0xE0
    val u1 = c1 - 0x80
    val u2 = c2 - 0x80
  in
    64 * (64 * u0 + u1) + u2
  end
 
implement {}
decode_utf8_4byte (c0, c1, c2, c3) =
  let
    val u0 = c0 - 0xF0
    val u1 = c1 - 0x80
    val u2 = c2 - 0x80
    val u3 = c3 - 0x80
  in
    64 * (64 * (64 * u0 + u1) + u2) + u3
  end
 
implement {}
encode_utf8_character {u} u =
  if u <= 0x7F then
    let
      val c0 = u
      stadef c0 = u
    in
      @(lemma_valid_utf8_character_1byte {u, c0} () | 1, c0, ~1, ~1, ~1)
    end
  else if u <= 0x7FF then
    let
      val c1 = 0x80 + (u \nmod 64)
      val u1 = u \ndiv 64
      val c0 = 0xC0 + u1
      stadef c1 = 0x80 + (u \nmod 64)
      stadef u1 = u \ndiv 64
      stadef c0 = 0xC0 + u1
    in
      @(lemma_valid_utf8_character_2byte {u, c0, c1} () | 2, c0, c1, ~1, ~1)
    end
  else if u <= 0xFFFF then
    let
      val c2 = 0x80 + (u \nmod 64)
      val u1 = u \ndiv 64
      val c1 = 0x80 + (u1 \nmod 64)
      val u2 = u1 \ndiv 64
      val c0 = 0xE0 + u2
      stadef c2 = 0x80 + (u \nmod 64)
      stadef u1 = u \ndiv 64
      stadef c1 = 0x80 + (u1 \nmod 64)
      stadef u2 = u1 \ndiv 64
      stadef c0 = 0xE0 + u2
    in
      @(lemma_valid_utf8_character_3byte {u, c0, c1, c2} () | 3, c0, c1, c2, ~1)
    end
  else
    let
      val c3 = 0x80 + (u \nmod 64)
      val u1 = u \ndiv 64
      val c2 = 0x80 + (u1 \nmod 64)
      val u2 = u1 \ndiv 64
      val c1 = 0x80 + (u2 \nmod 64)
      val u3 = u2 \ndiv 64
      val c0 = 0xF0 + u3
      stadef c3 = 0x80 + (u \nmod 64)
      stadef u1 = u \ndiv 64
      stadef c2 = 0x80 + (u1 \nmod 64)
      stadef u2 = u1 \ndiv 64
      stadef c1 = 0x80 + (u2 \nmod 64)
      stadef u3 = u2 \ndiv 64
      stadef c0 = 0xF0 + u3
      prval () = _shift_right_twelve (u, u1, u2)
    in
      @(lemma_valid_utf8_character_4byte {u, c0, c1, c2, c3} () |
        4, c0, c1, c2, c3)
    end
 
(*------------------------------------------------------------------*)
 
implement
utf8_array_decode_next (utf8len, utf8arr, i) =
  let
    macdef getchr (j) = g1ofg0 (char2u2i (utf8arr[,(j)]))
    macdef error_return = @(utf8_decode_next_error_char, i + i2sz 1)
 
    prval _ = lemma_g1uint_param (i)
 
    val c0 = getchr (i)
  in
    if 0x00 <= c0 &&& c0 <= 0xFF then
      let
        val seqlen = utf8_character_length c0
      in
        case+ seqlen of
        | 1 => @(c0, i + i2sz 1)
 
        | 2 =>
          if utf8len < i + i2sz 2 then
            error_return
          else
            let
              val c1 = getchr (i + i2sz 1)
              val (pf | valid) =
                is_valid_utf8_character_2byte (c0, c1)
            in
              if valid then
                let
                  prval _ = utf8_character_2byte_valid_bytes pf
                  val code_point = decode_utf8_2byte (c0, c1)
                in
                  @(code_point, i + i2sz 2)
                end
              else
                error_return
            end
 
        | 3 =>
          if utf8len < i + i2sz 3 then
            error_return
          else
            let
              val c1 = getchr (i + i2sz 1)
              val c2 = getchr (i + i2sz 2)
              val (pf | valid) =
                is_valid_utf8_character_3byte (c0, c1, c2)
            in
              if valid then
                let
                  prval _ = utf8_character_3byte_valid_bytes pf
                  val code_point = decode_utf8_3byte (c0, c1, c2)
                in
                  @(code_point, i + i2sz 3)
                end
              else
                error_return
            end
 
        | 4 =>
          if utf8len < i + i2sz 4 then
            error_return
          else
            let
              val c1 = getchr (i + i2sz 1)
              val c2 = getchr (i + i2sz 2)
              val c3 = getchr (i + i2sz 3)
              val (pf | valid) =
                is_valid_utf8_character_4byte (c0, c1, c2, c3)
            in
              if valid then
                let
                  prval _ = utf8_character_4byte_valid_bytes pf
                  val code_point = decode_utf8_4byte (c0, c1, c2, c3)
                in
                  @(code_point, i + i2sz 4)
                end
              else
                error_return
            end
 
        | _ => error_return
      end
    else
      (* This branch should never be run on a system
         with 8-bit char. *)
      error_return
  end
 
implement
utf8_string_decode_next {..} {n_utf8str} (utf8len, utf8str, i) =
  let
    val [p : addr] p = string2ptr utf8str
    val (pf, consume_pf | p) =
      $UNSAFE.ptr1_vtake{@[char][n_utf8str]} p
    val result = utf8_array_decode_next (utf8len, !p, i)
    prval _ = consume_pf pf
  in
    result
  end
 
(*###################### DEMONSTRATION #############################*)
 
fn
encode_LATIN_CAPITAL_LETTER_A () : void =
  {
    val u = 0x0041
 
    (* Return both a proof of valid encoding and the encoding. *)
    val (pf_valid | n, c0, c1, c2, c3) = encode_utf8_character (u)
 
    (* Verify the encoding. *)
    val _ = assertloc (n = 1)
    val _ = assertloc (c0 = 0x41)
  }
 
fn
decode_LATIN_CAPITAL_LETTER_A () : void =
  {
    val str = "\x41\0"
    val n = length str
    val (c, i) = utf8_decode_next (n, str, i2sz 0)
 
    (* Verify that the decoding is correct. *)
    val _ = assertloc (c = 0x0041)
 
    (* Verify that the next index is 1. *)
    val _ = assertloc (i = i2sz 1)
  }
 
fn
encode_LATIN_SMALL_LETTER_O_WITH_DIAERESIS () : void =
  {
    val u = 0x00F6
 
    (* Return both a proof of valid encoding and the encoding. *)
    val (pf_valid | n, c0, c1, c2, c3) = encode_utf8_character (u)
 
    (* Verify the encoding. *)
    val _ = assertloc (n = 2)
    val _ = assertloc (c0 = 0xC3)
    val _ = assertloc (c1 = 0xB6)
  }
 
fn
decode_LATIN_SMALL_LETTER_O_WITH_DIAERESIS () : void =
  {
    val str = "\xC3\xB6\0"
    val n = length str
    val (c, i) = utf8_decode_next (n, str, i2sz 0)
 
    (* Verify that the decoding is correct. *)
    val _ = assertloc (c = 0x00F6)
 
    (* Verify that the next index is 2. *)
    val _ = assertloc (i = i2sz 2)
  }
 
fn
encode_CYRILLIC_CAPITAL_LETTER_ZHE () : void =
  {
    val u = 0x0416
 
    (* Return both a proof of valid encoding and the encoding. *)
    val (pf_valid | n, c0, c1, c2, c3) = encode_utf8_character (u)
 
    (* Verify the encoding. *)
    val _ = assertloc (n = 2)
    val _ = assertloc (c0 = 0xD0)
    val _ = assertloc (c1 = 0x96)
  }
 
fn
decode_CYRILLIC_CAPITAL_LETTER_ZHE () : void =
  {
    val str = "\xD0\x96\0"
    val n = length str
    val (c, i) = utf8_decode_next (n, str, i2sz 0)
 
    (* Verify that the decoding is correct. *)
    val _ = assertloc (c = 0x0416)
 
    (* Verify that the next index is 2. *)
    val _ = assertloc (i = i2sz 2)
  }
 
fn
encode_EURO_SIGN () : void =
  {
    val u = 0x20AC
 
    (* Return both a proof of valid encoding and the encoding. *)
    val (pf_valid | n, c0, c1, c2, c3) = encode_utf8_character (u)
 
    (* Verify the encoding. *)
    val _ = assertloc (n = 3)
    val _ = assertloc (c0 = 0xE2)
    val _ = assertloc (c1 = 0x82)
    val _ = assertloc (c2 = 0xAC)
  }
 
fn
decode_EURO_SIGN () : void =
  {
    val str = "\xE2\x82\xAC\0"
    val n = length str
    val (c, i) = utf8_decode_next (n, str, i2sz 0)
 
    (* Verify that the decoding is correct. *)
    val _ = assertloc (c = 0x20AC)
 
    (* Verify that the next index is 3. *)
    val _ = assertloc (i = i2sz 3)
  }
 
fn
encode_MUSICAL_SYMBOL_G_CLEF () : void =
  {
    val u = 0x1D11E
 
    (* Return both a proof of valid encoding and the encoding. *)
    val (pf_valid | n, c0, c1, c2, c3) = encode_utf8_character (u)
 
    (* Verify the encoding. *)
    val _ = assertloc (n = 4)
    val _ = assertloc (c0 = 0xF0)
    val _ = assertloc (c1 = 0x9D)
    val _ = assertloc (c2 = 0x84)
    val _ = assertloc (c3 = 0x9E)
  }
 
fn
decode_MUSICAL_SYMBOL_G_CLEF () : void =
  {
    val str = "\xF0\x9D\x84\x9E\0"
    val n = length str
    val (c, i) = utf8_decode_next (n, str, i2sz 0)
 
    (* Verify that the decoding is correct. *)
    val _ = assertloc (c = 0x1D11E)
 
    (* Verify that the next index is 4. *)
    val _ = assertloc (i = i2sz 4)
  }
 
implement
main0 () =
  begin
    encode_LATIN_CAPITAL_LETTER_A ();
    decode_LATIN_CAPITAL_LETTER_A ();
 
    encode_LATIN_SMALL_LETTER_O_WITH_DIAERESIS ();
    decode_LATIN_SMALL_LETTER_O_WITH_DIAERESIS ();
 
    encode_CYRILLIC_CAPITAL_LETTER_ZHE ();
    decode_CYRILLIC_CAPITAL_LETTER_ZHE ();
 
    encode_EURO_SIGN ();
    decode_EURO_SIGN ();
 
    encode_MUSICAL_SYMBOL_G_CLEF ();
    decode_MUSICAL_SYMBOL_G_CLEF ();
 
    println! ("SUCCESS")
  end