texlive[65299] Master/texmf-dist: l3kernel (17dec22)

commits+karl at tug.org commits+karl at tug.org
Sat Dec 17 21:55:34 CET 2022


Revision: 65299
          http://tug.org/svn/texlive?view=revision&revision=65299
Author:   karl
Date:     2022-12-17 21:55:34 +0100 (Sat, 17 Dec 2022)
Log Message:
-----------
l3kernel (17dec22)

Modified Paths:
--------------
    trunk/Master/texmf-dist/doc/latex/l3kernel/CHANGELOG.md
    trunk/Master/texmf-dist/doc/latex/l3kernel/README.md
    trunk/Master/texmf-dist/doc/latex/l3kernel/expl3.pdf
    trunk/Master/texmf-dist/doc/latex/l3kernel/interface3.pdf
    trunk/Master/texmf-dist/doc/latex/l3kernel/interface3.tex
    trunk/Master/texmf-dist/doc/latex/l3kernel/l3doc.pdf
    trunk/Master/texmf-dist/doc/latex/l3kernel/l3docstrip.pdf
    trunk/Master/texmf-dist/doc/latex/l3kernel/l3news.pdf
    trunk/Master/texmf-dist/doc/latex/l3kernel/l3news01.pdf
    trunk/Master/texmf-dist/doc/latex/l3kernel/l3news02.pdf
    trunk/Master/texmf-dist/doc/latex/l3kernel/l3news03.pdf
    trunk/Master/texmf-dist/doc/latex/l3kernel/l3news04.pdf
    trunk/Master/texmf-dist/doc/latex/l3kernel/l3news05.pdf
    trunk/Master/texmf-dist/doc/latex/l3kernel/l3news06.pdf
    trunk/Master/texmf-dist/doc/latex/l3kernel/l3news07.pdf
    trunk/Master/texmf-dist/doc/latex/l3kernel/l3news08.pdf
    trunk/Master/texmf-dist/doc/latex/l3kernel/l3news09.pdf
    trunk/Master/texmf-dist/doc/latex/l3kernel/l3news10.pdf
    trunk/Master/texmf-dist/doc/latex/l3kernel/l3news11.pdf
    trunk/Master/texmf-dist/doc/latex/l3kernel/l3news12.pdf
    trunk/Master/texmf-dist/doc/latex/l3kernel/l3obsolete.txt
    trunk/Master/texmf-dist/doc/latex/l3kernel/l3prefixes.csv
    trunk/Master/texmf-dist/doc/latex/l3kernel/l3prefixes.pdf
    trunk/Master/texmf-dist/doc/latex/l3kernel/l3styleguide.pdf
    trunk/Master/texmf-dist/doc/latex/l3kernel/l3styleguide.tex
    trunk/Master/texmf-dist/doc/latex/l3kernel/l3syntax-changes.pdf
    trunk/Master/texmf-dist/doc/latex/l3kernel/l3syntax-changes.tex
    trunk/Master/texmf-dist/doc/latex/l3kernel/l3term-glossary.pdf
    trunk/Master/texmf-dist/doc/latex/l3kernel/l3term-glossary.tex
    trunk/Master/texmf-dist/doc/latex/l3kernel/source3.pdf
    trunk/Master/texmf-dist/doc/latex/l3kernel/source3.tex
    trunk/Master/texmf-dist/source/latex/l3kernel/expl3.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3basics.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3bootstrap.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3box.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3candidates.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3cctab.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3clist.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3coffins.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3color.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3debug.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3deprecation.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3doc.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3docstrip.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3expan.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3file.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3flag.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-assign.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-aux.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-basics.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-convert.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-expo.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-extended.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-logic.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-parse.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-random.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-round.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-traps.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-trig.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3fp.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3fparray.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3int.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3intarray.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3kernel-functions.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3keys.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3legacy.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3luatex.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3msg.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3names.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3pdf.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3prg.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3prop.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3quark.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3regex.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3seq.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3skip.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3sort.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3str-convert.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3str.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3sys.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3text-case.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3text-map.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3text-purify.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3text.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3tl-analysis.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3tl.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3token.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3unicode.dtx
    trunk/Master/texmf-dist/tex/latex/l3kernel/expl3-code.tex
    trunk/Master/texmf-dist/tex/latex/l3kernel/expl3-generic.tex
    trunk/Master/texmf-dist/tex/latex/l3kernel/expl3.ltx
    trunk/Master/texmf-dist/tex/latex/l3kernel/expl3.sty
    trunk/Master/texmf-dist/tex/latex/l3kernel/l3doc.cls

Modified: trunk/Master/texmf-dist/doc/latex/l3kernel/CHANGELOG.md
===================================================================
--- trunk/Master/texmf-dist/doc/latex/l3kernel/CHANGELOG.md	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/doc/latex/l3kernel/CHANGELOG.md	2022-12-17 20:55:34 UTC (rev 65299)
@@ -7,6 +7,20 @@
 
 ## [Unreleased]
 
+## [2022-12-17]
+
+### Added
+- `\codepoint_to_nfd:n`
+- `\codepoint_generate:n` and `\codepoint_str_generate:n`
+- `\str_casefold:n`
+
+### Changed
+- Protect `babel` shorthands from expansion
+
+### Fixed
+- Appearance of `\DescribeOption` (issue
+  [\#1111](https://github.com/latex3/latex3/issues/1111))
+
 ## [2022-11-02]
 
 ### Added
@@ -30,6 +44,13 @@
 -  Creation of a message with some engines (issue
   [\#1139](https://github.com/latex3/latex3/issues/1139))
 
+### Deprecated
+- `\char_to_nfd:N`, `\char_to_nfd:n`
+- `\char_foldcase:N`, `\char_lowercase:N`, `\char_uppercase:N`
+   and `str` variants
+- `\char_to_utfviii_bytes:n`
+- `\str_foldcase:n`
+
 ## [2022-09-28]
 
 ### Added
@@ -1231,7 +1252,8 @@
 - Step functions have been added for dim variables,
   e.g. `\dim_step_inline:nnnn`
 
-[Unreleased]: https://github.com/latex3/latex3/compare/2022-11-02...HEAD
+[Unreleased]: https://github.com/latex3/latex3/compare/2022-12-17...HEAD
+[2022-12-17]: https://github.com/latex3/latex3/compare/2022-11-02...2022-12-17
 [2022-11-02]: https://github.com/latex3/latex3/compare/2022-10-26...2022-11-02
 [2022-10-26]: https://github.com/latex3/latex3/compare/2022-09-28...2022-10-26
 [2022-09-28]: https://github.com/latex3/latex3/compare/2022-08-30...2022-09-28

Modified: trunk/Master/texmf-dist/doc/latex/l3kernel/README.md
===================================================================
--- trunk/Master/texmf-dist/doc/latex/l3kernel/README.md	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/doc/latex/l3kernel/README.md	2022-12-17 20:55:34 UTC (rev 65299)
@@ -1,7 +1,7 @@
 LaTeX3 Programming Conventions
 ==============================
 
-Release 2022-11-02
+Release 2022-12-17
 
 Overview
 --------

Modified: trunk/Master/texmf-dist/doc/latex/l3kernel/expl3.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/doc/latex/l3kernel/interface3.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/doc/latex/l3kernel/interface3.tex
===================================================================
--- trunk/Master/texmf-dist/doc/latex/l3kernel/interface3.tex	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/doc/latex/l3kernel/interface3.tex	2022-12-17 20:55:34 UTC (rev 65299)
@@ -54,7 +54,7 @@
          {latex-team at latex-project.org}%
    }%
 }
-\date{Released 2022-11-02}
+\date{Released 2022-12-17}
 
 \pagenumbering{roman}
 \maketitle

Modified: trunk/Master/texmf-dist/doc/latex/l3kernel/l3doc.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/doc/latex/l3kernel/l3docstrip.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/doc/latex/l3kernel/l3news.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/doc/latex/l3kernel/l3news01.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/doc/latex/l3kernel/l3news02.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/doc/latex/l3kernel/l3news03.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/doc/latex/l3kernel/l3news04.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/doc/latex/l3kernel/l3news05.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/doc/latex/l3kernel/l3news06.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/doc/latex/l3kernel/l3news07.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/doc/latex/l3kernel/l3news08.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/doc/latex/l3kernel/l3news09.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/doc/latex/l3kernel/l3news10.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/doc/latex/l3kernel/l3news11.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/doc/latex/l3kernel/l3news12.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/doc/latex/l3kernel/l3obsolete.txt
===================================================================
--- trunk/Master/texmf-dist/doc/latex/l3kernel/l3obsolete.txt	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/doc/latex/l3kernel/l3obsolete.txt	2022-12-17 20:55:34 UTC (rev 65299)
@@ -15,13 +15,21 @@
 Function                            Date deprecated
 ---------------------------------------------------
 \char_fold_case:N                        2020-01-03
+\char_foldcase:N                         2022-10-17
 \char_lower_case:N                       2020-01-03
+\char_lowercase:N                        2022-10-17
 \char_mixed_case:N                       2020-01-03
 \char_upper_case:N                       2020-01-03
+\char_uppercase:N                        2022-10-17
 \char_str_fold_case:N                    2020-01-03
+\char_str_foldcase:N                     2022-10-17
 \char_str_lower_case:N                   2020-01-03
+\char_str_lowercase:N                    2022-10-17
 \char_str_mixed_case:N                   2020-01-03
 \char_str_upper_case:N                   2020-01-03
+\char_str_uppercase:N                    2022-10-17
+\char_to_utfviii_bytes:n                 2022-10-09
+\char_to_nfd:N                           2022-10-09
 \cs_argument_spec:N                      2022-06-24
 \l_keys_key_tl                           2020-02-08
 \l_keys_path_tl                          2020-02-08
@@ -34,6 +42,8 @@
 \str_declare_eight_bit_encoding:nnn      2020-08-20
 \str_fold_case:n                         2020-01-03
 \str_fold_case:V                         2020-01-03
+\str_foldcase:N                          2022-10-17
+\str_foldcase:V                          2022-10-17
 \str_lower_case:f                        2020-01-03
 \str_lower_case:n                        2020-01-03
 \str_upper_case:f                        2020-01-03

Modified: trunk/Master/texmf-dist/doc/latex/l3kernel/l3prefixes.csv
===================================================================
--- trunk/Master/texmf-dist/doc/latex/l3kernel/l3prefixes.csv	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/doc/latex/l3kernel/l3prefixes.csv	2022-12-17 20:55:34 UTC (rev 65299)
@@ -41,6 +41,7 @@
 code,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2021-04-23,2021-04-23,
 codedoc,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,Somewhat experimental: may change
 codehigh,codehigh,Jianrui Lyu,https://github.com/lvjr/codehigh,https://github.com/lvjr/codehigh.git,https://github.com/lvjr/codehigh/issues,2022-04-02,2022-04-02,
+codepoint,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
 coffin,"l3kernel,xcoffins",The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,
 colon,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12,
 color,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27,

Modified: trunk/Master/texmf-dist/doc/latex/l3kernel/l3prefixes.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/doc/latex/l3kernel/l3styleguide.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/doc/latex/l3kernel/l3styleguide.tex
===================================================================
--- trunk/Master/texmf-dist/doc/latex/l3kernel/l3styleguide.tex	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/doc/latex/l3kernel/l3styleguide.tex	2022-12-17 20:55:34 UTC (rev 65299)
@@ -32,7 +32,7 @@
         {latex-team at latex-project.org}%
     }%
 }
-\date{Released 2022-11-02}
+\date{Released 2022-12-17}
 
 \begin{document}
 

Modified: trunk/Master/texmf-dist/doc/latex/l3kernel/l3syntax-changes.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/doc/latex/l3kernel/l3syntax-changes.tex
===================================================================
--- trunk/Master/texmf-dist/doc/latex/l3kernel/l3syntax-changes.tex	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/doc/latex/l3kernel/l3syntax-changes.tex	2022-12-17 20:55:34 UTC (rev 65299)
@@ -32,7 +32,7 @@
         {latex-team at latex-project.org}%
     }%
 }
-\date{Released 2022-11-02}
+\date{Released 2022-12-17}
 
 \newcommand{\TF}{\textit{(TF)}}
 

Modified: trunk/Master/texmf-dist/doc/latex/l3kernel/l3term-glossary.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/doc/latex/l3kernel/l3term-glossary.tex
===================================================================
--- trunk/Master/texmf-dist/doc/latex/l3kernel/l3term-glossary.tex	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/doc/latex/l3kernel/l3term-glossary.tex	2022-12-17 20:55:34 UTC (rev 65299)
@@ -32,7 +32,7 @@
         {latex-team at latex-project.org}%
     }%
 }
-\date{Released 2022-11-02}
+\date{Released 2022-12-17}
 
 \newcommand{\TF}{\textit{(TF)}}
 
@@ -41,7 +41,7 @@
 \maketitle
 
 This file describes aspects of \TeX{} programming that are relevant in a
-\LaTeX3 context.
+\pkg{expl3} context.
 
 \section{Reading a file}
 
@@ -79,7 +79,7 @@
 code (and character code) and cannot be changed.  We call these tokens
 \emph{explicit} character tokens.  Category codes that a character token
 can have are listed below by giving a sample output of the \TeX{}
-primitive \tn{meaning}, together with their \LaTeX3 names and most
+primitive \tn{meaning}, together with their \pkg{expl3} names and most
 common example:
 \begin{itemize}
   \item[1] begin-group character (|group_begin|, often |{|),
@@ -103,11 +103,11 @@
 and we call such tokens \emph{implicit} character tokens.  The meaning
 is otherwise in the following list:
 \begin{itemize}
-  \item a macro, used in \LaTeX3 for most functions and some variables
+  \item a macro, used in \pkg{expl3} for most functions and some variables
     (|tl|, |fp|, |seq|, \ldots{}),
-  \item a primitive such as \tn{def} or \tn{topmark}, used in \LaTeX3
+  \item a primitive such as \tn{def} or \tn{topmark}, used in \pkg{expl3}
     for some functions,
-  \item a register such as \tn{count}|123|, used in \LaTeX3{} for the
+  \item a register such as \tn{count}|123|, used in \pkg{expl3} for the
     implementation of some variables (|int|, |dim|, \ldots{}),
   \item a constant integer such as \tn{char}|"56| or
     \tn{mathchar}|"121|, used when defining a constant using
@@ -116,7 +116,7 @@
   \item undefined.
 \end{itemize}
 Macros can be \tn{protected} or not, \tn{long} or not (the opposite of
-what \LaTeX3 calls |nopar|), and \tn{outer} or not (unused in \LaTeX3).
+what \pkg{expl3} calls |nopar|), and \tn{outer} or not (unused in \pkg{expl3}).
 Their \tn{meaning} takes the form
 \begin{quote}
   \meta{prefix} |macro:|\meta{argument}|->|\meta{replacement}
@@ -146,14 +146,4 @@
 the start of the argument are \emph{not} ignored in this case (and they
 prevent brace-stripping).
 
-\section{Quantities and expressions}
-
-Integer denotations, dimensions, glue (including \texttt{fill} and \texttt{true pt} and the like).
-
-Syntax of integer expressions (including the trap that \verb|-(1+2)| is invalid).
-
-\section{\LaTeX3 terms}
-
-Terms like ``intexpr'' or ``seq var'' used in syntax blocks.
-
 \end{document}

Modified: trunk/Master/texmf-dist/doc/latex/l3kernel/source3.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/doc/latex/l3kernel/source3.tex
===================================================================
--- trunk/Master/texmf-dist/doc/latex/l3kernel/source3.tex	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/doc/latex/l3kernel/source3.tex	2022-12-17 20:55:34 UTC (rev 65299)
@@ -53,7 +53,7 @@
          {latex-team at latex-project.org}%
    }%
 }
-\date{Released 2022-11-02}
+\date{Released 2022-12-17}
 
 \pagenumbering{roman}
 \maketitle

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/expl3.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/expl3.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/expl3.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -24,7 +24,7 @@
 %
 %<*driver|generic|package|2ekernel>
 %</driver|generic|package|2ekernel>
-\def\ExplFileDate{2022-11-02}%
+\def\ExplFileDate{2022-12-17}%
 %<*driver>
 \documentclass[full]{l3doc}
 \usepackage{graphicx}
@@ -51,7 +51,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 %
 % \maketitle
 %
@@ -1182,9 +1182,15 @@
 % Identify the package or add to the format message.
 %    \begin{macrocode}
 %<*2ekernel>
-\everyjob\expandafter{\the\everyjob
-  \message{L3 programming layer <\ExplFileDate>}%
-}
+\ifdefined\LaTeXReleaseInfo
+  \LaTeXReleaseInfo\expandafter{\the\LaTeXReleaseInfo
+    \show at release@info{L3 programming layer <\ExplFileDate>}%
+  }%
+\else
+  \everyjob\expandafter{\the\everyjob
+    \message{L3 programming layer <\ExplFileDate>}%
+  }%
+\fi
 %</2ekernel>
 %<*!2ekernel>
 \ProvidesPackage{expl3}

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3basics.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3basics.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3basics.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3bootstrap.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3bootstrap.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3bootstrap.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3box.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3box.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3box.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3candidates.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3candidates.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3candidates.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -44,7 +44,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 %
 % \maketitle
 %
@@ -347,7 +347,7 @@
 % \begin{function}[EXP, added = 2016-12-06]
 %   {\prop_rand_key_value:N, \prop_rand_key_value:c}
 %   \begin{syntax}
-%     \cs{prop_rand_key_value:N} \meta{prop~var}
+%     \cs{prop_rand_key_value:N} \meta{property list}
 %   \end{syntax}
 %   Selects a pseudo-random key--value pair from the \meta{property list}
 %   and returns \Arg{key} and \Arg{value}.  If the \meta{property list} is
@@ -626,40 +626,6 @@
 %   (\enquote{active}), and character code $32$ (space).
 % \end{variable}
 %
-% \begin{function}[added = 2020-01-09, EXP]{\char_to_utfviii_bytes:n}
-%   \begin{syntax}
-%     \cs{char_to_utfviii_bytes:n} \Arg{codepoint}
-%   \end{syntax}
-%   Converts the (Unicode) \meta{codepoint} to UTF-8 bytes. The expansion
-%   of this function comprises four brace groups, each of which will contain
-%   a hexadecimal value: the appropriate byte. As UTF-8 is a variable-length,
-%   one or more of the groups may be empty: the bytes read in the logical order,
-%   such that a two-byte codepoint will have groups |#1| and |#2| filled
-%   and |#3| and |#4| empty.
-% \end{function}
-%
-% \begin{function}[added = 2020-01-02, rEXP]{\char_to_nfd:N}
-%   \begin{syntax}
-%     \cs{char_to_nfd:N} \meta{char}
-%   \end{syntax}
-%   Converts the \meta{char} to the Unicode Normalization Form Canonical
-%   Decomposition. The category code of the \emph{first} generated character is
-%   the same as the \meta{char}; second and subsequent chars will have the
-%   current category code, as they would if typed in directly. For $8$-bit
-%   engines, no change will take place.
-% \end{function}
-%
-% \begin{function}[added = 2022-08-29, rEXP]{\char_to_nfd:n}
-%   \begin{syntax}
-%     \cs{char_to_nfd:n} \Arg{codepoint}
-%   \end{syntax}
-%   Converts the (Unicode) \meta{codepoint} to the Unicode Normalization
-%   Form Canonical Decomposition. The generated character(s) will have
-%   the current category code as they would if typed in directly. In contrast
-%   to \cs{char_to_nfd:N}, this function \emph{does} decompose codepoints
-%   with $8$-bit engines.
-% \end{function}
-%
 % \begin{function}[added = 2018-09-23]
 %   {
 %     \peek_catcode_collect_inline:Nn,

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3cctab.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3cctab.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3cctab.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3clist.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3clist.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3clist.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -44,7 +44,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3coffins.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3coffins.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3coffins.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3color.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3color.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3color.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 %
 % \maketitle
 %
@@ -2146,7 +2146,7 @@
   {
     \exp_args:Nee \@@_model_new:nnn
       { \tl_to_str:n {#1} }
-      { \str_foldcase:n {#2} } {#3}
+      { \str_casefold:n {#2} } {#3}
   }
 \cs_new_protected:Npn \@@_model_new:nnn #1#2#3
   {

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3debug.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3debug.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3debug.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3deprecation.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3deprecation.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3deprecation.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 %
 % \maketitle
 %
@@ -468,14 +468,24 @@
 \cs_gset:Npn \str_upper_case:n { \str_uppercase:n }
 \__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \str_uppercase:f }
 \cs_gset:Npn \str_upper_case:f { \str_uppercase:f }
-\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \str_foldcase:n }
-\cs_gset:Npn \str_fold_case:n { \str_foldcase:n }
-\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \str_foldcase:V }
-\cs_gset:Npn \str_fold_case:V { \str_foldcase:V }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \str_casefold:n }
+\cs_gset:Npn \str_fold_case:n { \str_casefold:n }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \str_casefold:V }
+\cs_gset:Npn \str_fold_case:V { \str_casefold:V }
 %    \end{macrocode}
 % \end{macro}
 %
 % \begin{macro}[EXP]
+%   {\str_foldcase:n,  \str_foldcase:V}
+%    \begin{macrocode}
+\__kernel_patch_deprecation:nnNNpn { 2020-10-17 } { \str_casefold:n }
+\cs_gset:Npn \str_foldcase:n { \str_casefold:n }
+\__kernel_patch_deprecation:nnNNpn { 2022-10-17 } { \str_casefold:V }
+\cs_gset:Npn \str_foldcase:V { \str_casefold:V }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
 %   {\str_declare_eight_bit_encoding:nnn}
 %   This command was made internal, with one more argument.  There is no
 %   easy way to compute a reasonable value for that extra argument so we
@@ -549,6 +559,22 @@
 %
 % \subsection{Deprecated \pkg{l3token} functions}
 %
+% \begin{macro}[EXP]{\char_to_utfviii_bytes:n}
+%    \begin{macrocode}
+\__kernel_patch_deprecation:nnNNpn { 2022-10-09 } { [ \codepoint_generate:n ] }
+\cs_gset:Npn \char_to_utfviii_bytes:n { \__kernel_codepoint_to_bytes:n }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\char_to_nfd:Nm, \char_to_nfd:n}
+%    \begin{macrocode}
+\__kernel_patch_deprecation:nnNNpn { 2022-10-09 } { \codepoint_to_nfd:n }
+\cs_gset:Npn \char_to_nfd:N #1 { \codepoint_to_nfd:n {`#1} }
+\__kernel_patch_deprecation:nnNNpn { 2022-10-09 } { \codepoint_to_nfd:n }
+\cs_gset:Npn \char_to_nfd:n { \codepoint_to_nfd:n }
+%    \end{macrocode}
+% \end{macro}
+%
 % \begin{macro}[EXP]
 %   {
 %     \char_lower_case:N, \char_upper_case:N,
@@ -557,25 +583,48 @@
 %     \char_str_mixed_case:Nn, \char_str_fold_case:N,
 %   }
 %    \begin{macrocode}
-\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \char_lowercase:N }
-\cs_gset:Npn \char_lower_case:N { \char_lowercase:N }
-\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \char_uppercase:N }
-\cs_gset:Npn \char_upper_case:N { \char_uppercase:N }
-\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \char_titlecase:N }
-\cs_gset:Npn \char_mixed_case:N { \char_titlecase:N }
-\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \char_foldcase:N }
-\cs_gset:Npn \char_fold_case:N { \char_foldcase:N }
-\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \char_str_lowercase:N }
-\cs_gset:Npn \char_str_lower_case:N { \char_str_lowercase:N }
-\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \char_str_uppercase:N }
-\cs_gset:Npn \char_str_upper_case:N { \char_str_uppercase:N }
-\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \char_str_titlecase:N }
-\cs_gset:Npn \char_str_mixed_case:N { \char_str_titlecase:N }
-\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \char_str_foldcase:N }
-\cs_gset:Npn \char_str_fold_case:N { \char_str_foldcase:N }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \text_lowercase:n }
+\cs_gset:Npn \char_lower_case:N { \text_lowercase:n }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \text_uppercase:n }
+\cs_gset:Npn \char_upper_case:N { \text_uppercase:n }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \text_titlecase:n }
+\cs_gset:Npn \char_mixed_case:N { \text_titlecase:n }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \str_casefold:n }
+\cs_gset:Npn \char_fold_case:N { \str_casefold:n }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \str_lowercase:n }
+\cs_gset:Npn \char_str_lower_case:N { \str_lowercase:n }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \str_uppercase:n }
+\cs_gset:Npn \char_str_upper_case:N { \str_uppercase:n }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \str_titlecase:n }
+\cs_gset:Npn \char_str_mixed_case:N { \str_titlecase:n }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \str_casefold:n }
+\cs_gset:Npn \char_str_fold_case:N { \str_casefold:n }
 %    \end{macrocode}
 % \end{macro}
 %
+% \begin{macro}[EXP]
+%   {
+%     \char_lowercase:N, \char_uppercase:N,
+%     \char_foldcase:N,
+%     \char_str_lowercase:N, \char_str_uppercase:N,
+%     \char_str_foldcase:N,
+%   }
+%    \begin{macrocode}
+\__kernel_patch_deprecation:nnNNpn { 2022-10-17 } { \text_lowercase:n }
+\cs_gset:Npn \char_lowercase:N { \text_lowercase:n }
+\__kernel_patch_deprecation:nnNNpn { 2022-10-17 } { \text_uppercase:n }
+\cs_gset:Npn \char_uppercase:N { \text_uppercase:n }
+\__kernel_patch_deprecation:nnNNpn { 2022-10-17 } { \str_casefold:n }
+\cs_gset:Npn \char_foldcase:N { \str_casefold:n }
+\__kernel_patch_deprecation:nnNNpn { 2022-10-17 } { \str_lowercase:n }
+\cs_gset:Npn \char_str_lowercase:N { \str_lowercase:n }
+\__kernel_patch_deprecation:nnNNpn { 2022-10-17 } { \str_uppercase:n }
+\cs_gset:Npn \char_str_uppercase:N { \str_uppercase:n }
+\__kernel_patch_deprecation:nnNNpn { 2022-10-17 } { \str_casefold:n }
+\cs_gset:Npn \char_str_foldcase:N { \str_casefold:n }
+%    \end{macrocode}
+% \end{macro}
+%
 % \begin{macro}[TF]
 %   {
 %     \peek_catcode_ignore_spaces:N, \peek_catcode_remove_ignore_spaces:N,

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3doc.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3doc.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3doc.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -85,7 +85,7 @@
 %    require you to do updates, if the class changes.}}
 %
 % \author{\Team}
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 % \maketitle
 % \tableofcontents
 %
@@ -3410,53 +3410,11 @@
 % \subsubsection{Misc}
 %
 % \begin{macro}{\DescribeOption}
-%   For describing package options.  Due to Joseph Wright.  Name/usage
-%   might change soon.
+%   For describing package options: retained for consistency, but updated for
+%   \pkg{doc}~v3.
 %    \begin{macrocode}
-\newcommand*{\DescribeOption}
-  {
-    \leavevmode
-    \@bsphack
-    \begingroup
-      \MakePrivateLetters
-      \Describe at Option
-  }
+\NewDocElement[idxtype = option, idxgroup = options]{Option}{optionenv}  
 %    \end{macrocode}
-%
-%    \begin{macrocode}
-\newcommand*{\Describe at Option}[1]
-  {
-    \endgroup
-    \marginpar{
-      \raggedleft
-      \PrintDescribeEnv{#1}
-    }
-    \SpecialOptionIndex{#1}
-    \@esphack
-    \ignorespaces
-  }
-%    \end{macrocode}
-%
-%    \begin{macrocode}
-\newcommand*{\SpecialOptionIndex}[1]
-  {
-    \@bsphack
-    \begingroup
-      \@@_target:
-      \index
-        {
-          #1\actualchar{\protect\ttfamily#1}~(option)
-          \encapchar hdclindex{\the\c at HD@hypercount}{usage}
-        }
-      \index
-        {
-          options:\levelchar#1\actualchar{\protect\ttfamily#1}
-          \encapchar hdclindex{\the\c at HD@hypercount}{usage}
-        }
-    \endgroup
-    \@esphack
-  }
-%    \end{macrocode}
 % \end{macro}
 %
 % Here are some definitions for additional markup that helps to

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3docstrip.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3docstrip.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3docstrip.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -63,7 +63,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3expan.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3expan.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3expan.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3file.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3file.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3file.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -44,7 +44,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3flag.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3flag.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3flag.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-assign.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-assign.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-assign.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -40,7 +40,7 @@
 %          {latex-team at latex-project.org}^^A
 %    }^^A
 % }
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 % \maketitle
 %
 % \begin{documentation}

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-aux.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-aux.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-aux.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-basics.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-basics.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-basics.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -40,7 +40,7 @@
 %          {latex-team at latex-project.org}^^A
 %    }^^A
 % }
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-convert.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-convert.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-convert.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-expo.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-expo.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-expo.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -40,7 +40,7 @@
 %          {latex-team at latex-project.org}^^A
 %    }^^A
 % }
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-extended.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-extended.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-extended.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -40,7 +40,7 @@
 %          {latex-team at latex-project.org}^^A
 %    }^^A
 % }
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-logic.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-logic.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-logic.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -40,7 +40,7 @@
 %          {latex-team at latex-project.org}^^A
 %    }^^A
 % }
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-parse.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-parse.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-parse.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -40,7 +40,7 @@
 %          {latex-team at latex-project.org}^^A
 %    }^^A
 % }
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 %
 % \maketitle
 %
@@ -1023,7 +1023,7 @@
     \cs_if_exist_use:cF { @@_parse_word_#2:N }
       {
         \cs_if_exist_use:cF
-          { @@_parse_caseless_ \str_foldcase:n {#2} :N }
+          { @@_parse_caseless_ \str_casefold:n {#2} :N }
           {
             \msg_expandable_error:nnn
               { fp } { unknown-fp-word } {#2}

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-random.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-random.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-random.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -40,7 +40,7 @@
 %          {latex-team at latex-project.org}^^A
 %    }^^A
 % }
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-round.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-round.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-round.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-traps.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-traps.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-traps.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -40,7 +40,7 @@
 %          {latex-team at latex-project.org}^^A
 %    }^^A
 % }
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 % \maketitle
 %
 % \begin{documentation}

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-trig.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-trig.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-trig.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -40,7 +40,7 @@
 %          {latex-team at latex-project.org}^^A
 %    }^^A
 % }
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3fp.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3fp.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3fp.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -49,7 +49,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3fparray.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3fparray.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3fparray.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -44,7 +44,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3int.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3int.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3int.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3intarray.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3intarray.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3intarray.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -44,7 +44,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3kernel-functions.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3kernel-functions.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3kernel-functions.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 %
 % \maketitle
 %
@@ -105,6 +105,18 @@
 %   all of these tests succeed then the \meta{true code} is run.
 % \end{function}
 %
+% \begin{function}[EXP]{\__kernel_codepoint_to_bytes:n}
+%   \begin{syntax}
+%     \cs{__kernel_codepoint_to_bytes:n} \Arg{codepoint}
+%   \end{syntax}
+%   Converts the \meta{codepoint} to UTF-8 bytes. The expansion
+%   of this function comprises four brace groups, each of which will contain
+%   a hexadecimal value: the appropriate byte. As UTF-8 is a variable-length,
+%   one or more of the groups may be empty: the bytes read in the logical order,
+%   such that a two-byte codepoint will have groups |#1| and |#2| filled
+%   and |#3| and |#4| empty.
+% \end{function}
+%
 % \begin{function}{\__kernel_cs_parm_from_arg_count:nnF}
 %   \begin{syntax}
 %     \cs{__kernel_cs_parm_from_arg_count:nnF} \Arg{follow-on} \Arg{args} \Arg{false code}
@@ -477,6 +489,41 @@
 %   \meta{tl~var} and \meta{tokens}.
 % \end{function}
 %
+% \begin{function}[EXP]{\__kernel_codepoint_data:nn}
+%   \begin{syntax}
+%     \cs{__kernel_codepoint_data:nn} \Arg{type} \Arg{codepoint}
+%   \end{syntax}
+%   Expands to the appropriate value for the \meta{type} of data
+%   requested for a \meta{codepoint}. The current list of \meta{types} and
+%   results are
+%   \begin{description}
+%     \item[\texttt{lowercase}] The \emph{single} codepoint specified by
+%       \texttt{UnicodeData.txt} for lowercase mapping of the codepoint:
+%       will be equal to the input \meta{codepoint} if there is no mapping
+%       specified in \texttt{UnicodeData.txt}
+%     \item[\texttt{uppercase}] The \emph{single} codepoint specified by
+%       \texttt{UnicodeData.txt} for uppercase mapping of the codepoint:
+%       will be equal to the input \meta{codepoint} if there is no mapping
+%       specified in \texttt{UnicodeData.txt}
+%   \end{description}
+% \end{function}
+%
+% \begin{function}[EXP]{\__kernel_codepoint_case:nn}
+%   \begin{syntax}
+%     \cs{__kernel_codepoint_case:nn} \Arg{mapping} \Arg{codepoint}
+%   \end{syntax}
+%   Expands to a list of three balanced text, of which at least the first
+%   will contain a codepoint. This list of up to three codepoints specifies
+%   the full case mapping for the input \meta{codepoint}. The \meta{mapping}
+%   should be one of
+%   \begin{itemize}
+%     \item \texttt{casefold}
+%     \item \texttt{lowercase}
+%     \item \texttt{titlecase}
+%     \item \texttt{uppercase}
+%   \end{itemize}
+% \end{function}
+%
 % \subsection{Kernel backend functions}
 %
 % These functions are required to pass information to the backend. The nature

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3keys.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3keys.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3keys.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3legacy.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3legacy.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3legacy.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3luatex.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3luatex.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3luatex.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3msg.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3msg.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3msg.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3names.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3names.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3names.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3pdf.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3pdf.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3pdf.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3prg.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3prg.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3prg.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3prop.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3prop.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3prop.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -43,13 +43,13 @@
 %    }^^A
 % }
 %
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 %
 % \maketitle
 %
 % \begin{documentation}
 %
-% \LaTeX3 implements a \enquote{property list} data type, which contain
+% \pkg{expl3} implements a \meta{property list} data type, which contain
 % an unordered list of entries each of which consists of a \meta{key} and
 % an associated \meta{value}. The \meta{key} and \meta{value} may both
 % be any \meta{balanced text}, the \meta{key} is processed using
@@ -117,13 +117,13 @@
 %     \prop_gset_from_keyval:Nn, \prop_gset_from_keyval:cn,
 %   }
 %   \begin{syntax}
-%     \cs{prop_set_from_keyval:Nn} \meta{prop~var}
+%     \cs{prop_set_from_keyval:Nn} \meta{property list}
 %       \{
 %         \meta{key1} |=| \meta{value1} |,|
 %         \meta{key2} |=| \meta{value2} |,| \ldots{}
 %       \}
 %   \end{syntax}
-%   Sets \meta{prop~var} to contain key--value pairs given in the second
+%   Sets \meta{property list} to contain key--value pairs given in the second
 %   argument.  If duplicate keys appear only the last of the values is kept.
 %
 %   Spaces are trimmed around every \meta{key} and every \meta{value},
@@ -142,14 +142,14 @@
 % \begin{function}[added = 2017-11-28, updated = 2021-11-07]
 %   {\prop_const_from_keyval:Nn, \prop_const_from_keyval:cn}
 %   \begin{syntax}
-%     \cs{prop_const_from_keyval:Nn} \meta{prop~var}
+%     \cs{prop_const_from_keyval:Nn} \meta{property list}
 %       \{
 %         \meta{key1} |=| \meta{value1} |,|
 %         \meta{key2} |=| \meta{value2} |,| \ldots{}
 %       \}
 %   \end{syntax}
-%   Creates a new constant \meta{prop~var} or raises an error if the
-%   name is already taken. The \meta{prop~var} is set globally to
+%   Creates a new constant \meta{property list} or raises an error if the
+%   name is already taken. The \meta{property list} is set globally to
 %   contain key--value pairs given in the second argument, processed in
 %   the way described for \cs{prop_set_from_keyval:Nn}.  If duplicate
 %   keys appear only the last of the values is kept.
@@ -209,12 +209,12 @@
 %     \prop_gconcat:NNN, \prop_gconcat:ccc
 %   }
 %   \begin{syntax}
-%     \cs{prop_concat:NNN} \meta{prop~var_1} \meta{prop~var_2} \meta{prop~var_3}
+%     \cs{prop_concat:NNN} \meta{property list_1} \meta{property list_2} \meta{property list3}
 %   \end{syntax}
-%   Combines the key--value pairs of \meta{prop~var_2} and
-%   \meta{prop~var_3}, and saves the result in \meta{prop~var_1}.  If a
-%   key appears in both \meta{prop~var_2} and \meta{prop~var_3} then the
-%   last value, namely the value in \meta{prop~var_3} is kept.
+%   Combines the key--value pairs of \meta{property list_2} and
+%   \meta{property list_3}, and saves the result in \meta{pproperty list_1}.  If a
+%   key appears in both \meta{property list_2} and \meta{property list_3} then the
+%   last value, namely the value in \meta{property list_3} is kept.
 % \end{function}
 %
 % \begin{function}[added = 2021-05-16, updated = 2021-11-07]
@@ -223,22 +223,22 @@
 %     \prop_gput_from_keyval:Nn, \prop_gput_from_keyval:cn,
 %   }
 %   \begin{syntax}
-%     \cs{prop_put_from_keyval:Nn} \meta{prop~var}
+%     \cs{prop_put_from_keyval:Nn} \meta{property list}
 %       \{
 %         \meta{key1} |=| \meta{value1} |,|
 %         \meta{key2} |=| \meta{value2} |,| \ldots{}
 %       \}
 %   \end{syntax}
-%   Updates the \meta{prop~var} by adding entries for each key--value
+%   Updates the \meta{property list} by adding entries for each key--value
 %   pair given in the second argument.  The addition is done through
-%   \cs{prop_put:Nnn}, hence if the \meta{prop~var} already contains
+%   \cs{prop_put:Nnn}, hence if the \meta{property list} already contains
 %   some of the keys, the corresponding values are discarded and
 %   replaced by those given in the key--value list.  If duplicate keys
 %   appear in the key--value list then only the last of the values is kept.
 %
 %   The function is equivalent to storing the key--value pairs in a
-%   temporary property variable using \cs{prop_set_from_keyval:Nn}, then
-%   combining \meta{prop~var} with the temporary variable using
+%   temporary property list using \cs{prop_set_from_keyval:Nn}, then
+%   combining \meta{property list} with the temporary variable using
 %   \cs{prop_concat:NNN}.  In particular, the \meta{keys} and
 %   \meta{values} are space-trimmed and unbraced as described in
 %   \cs{prop_set_from_keyval:Nn}.

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3quark.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3quark.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3quark.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 %
 % \maketitle
 %
@@ -942,15 +942,6 @@
 %<@@=scan>
 %    \end{macrocode}
 %
-% \begin{variable}{\g_@@_marks_tl}
-% \UnitTested
-%   The list of all scan marks currently declared.
-%   No \pkg{l3tl} yet, so define this by hand.
-%    \begin{macrocode}
-\cs_gset:Npn \g_@@_marks_tl { }
-%    \end{macrocode}
-% \end{variable}
-%
 % \begin{macro}{\scan_new:N}
 % \UnitTested
 %   Check whether the variable is already a scan mark,
@@ -972,26 +963,30 @@
 % \end{macro}
 %
 % \begin{variable}{\s_stop}
+% \begin{variable}{\g_@@_marks_tl}
 % \UnitTested
 %   We only declare one scan mark here, more can be defined
 %   by specific modules.
 %   Can't use \cs{scan_new:N} yet because \pkg{l3tl} isn't loaded,
 %   so define \cs{s_stop} by hand and add it to \cs{g_@@_marks_tl}.
-%   We also add \cs{s__quark} (declared earlier) to the pool here.
-%   Since it lives in a different namespace, a little \pkg{l3docstrip}
+%   We also add the scan marks declared earlier to the pool here.
+%   Since they lives in a different namespace, a little \pkg{l3docstrip}
 %   cheating is necessary.
 %    \begin{macrocode}
 \cs_new_eq:NN \s_stop \scan_stop:
-\cs_gset_nopar:Npx \g_@@_marks_tl
+\cs_gset_nopar:Npn \g_@@_marks_tl
   {
-    \exp_not:o \g_@@_marks_tl
     \s_stop
 %<@@=quark>
     \s_@@
+%<@@=cs>
+    \s_@@_mark
+    \s_@@_stop
 %<@@=scan>
   }
 %    \end{macrocode}
 % \end{variable}
+% \end{variable}
 %
 % \begin{macro}[EXP]{\use_none_delimit_by_s_stop:w}
 % \UnitTested

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3regex.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3regex.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3regex.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3seq.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3seq.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3seq.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3skip.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3skip.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3skip.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -44,7 +44,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3sort.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3sort.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3sort.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3str-convert.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3str-convert.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3str-convert.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -44,7 +44,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 %
 % \maketitle
 %
@@ -2635,7 +2635,7 @@
     \cs_new:Npn \@@_convert_pdfname_bytes:n #1
       {
         \exp_args:Ne \@@_convert_pdfname_bytes_aux:n
-          { \char_to_utfviii_bytes:n {`#1} }
+          { \__kernel_codepoint_to_bytes:n {`#1} }
       }
     \cs_new:Npn \@@_convert_pdfname_bytes_aux:n #1
       { \@@_convert_pdfname_bytes_aux:nnnn #1 }

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3str.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3str.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3str.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 %
 % \maketitle
 %
@@ -744,7 +744,7 @@
 %
 %   These functions should \emph{not} be used for
 %   \begin{itemize}
-%     \item Caseless comparisons: use \cs{str_foldcase:n} for this
+%     \item Caseless comparisons: use \cs{str_casefold:n} for this
 %       situation (case folding is distinct from lower casing).
 %     \item Case changing text for typesetting: see the
 %       \cs[index=text_lowercase:n]{text_lowercase:n(n)},
@@ -755,10 +755,10 @@
 %   \end{itemize}
 % \end{function}
 %
-% \begin{function}[EXP, added = 2019-11-26]
-%   {\str_foldcase:n, \str_foldcase:V}
+% \begin{function}[EXP, added = 2022-10-16]
+%   {\str_casefold:n, \str_casefold:V}
 %   \begin{syntax}
-%     \cs{str_foldcase:n} \Arg{tokens}
+%     \cs{str_casefold:n} \Arg{tokens}
 %   \end{syntax}
 %   Converts the input \meta{tokens} to their string representation, as
 %   described for \cs{tl_to_str:n}, and then folds the case of the resulting
@@ -766,7 +766,7 @@
 %   left in the input stream.
 %
 %   String folding is a process used for material such as identifiers rather
-%   than for \enquote{text}. The folding provided by \cs{str_foldcase:n}
+%   than for \enquote{text}. The folding provided by \cs{str_casefold:n}
 %   follows the mappings provided by the \href{http://www.unicode.org}^^A
 %   {Unicode Consortium}, who
 %   \href{http://www.unicode.org/faq/casemap_charprop.html#2}{state}:
@@ -779,7 +779,7 @@
 %     should be used solely for internal processing and generally should not be
 %     stored or displayed to the end user.
 %   \end{quote}
-%   The folding approach implemented by \cs{str_foldcase:n} follows the
+%   The folding approach implemented by \cs{str_casefold:n} follows the
 %   \enquote{full} scheme defined by the Unicode Consortium
 %   (\emph{e.g.}~\SS folds to \texttt{SS}). As case-folding is
 %   a language-insensitive process, there is no special treatment of
@@ -843,6 +843,16 @@
 %   code and so should only be used for short-term storage.
 % \end{variable}
 %
+% \section{Deprecated functions}
+%
+% \begin{function}[EXP, added = 2019-11-26]
+%   {\str_foldcase:n, \str_foldcase:V}
+%   \begin{syntax}
+%     \cs{str_foldcase:n} \Arg{tokens}
+%   \end{syntax}
+%   A previous name for the functionally-identical \cs{str_casefold:n}.
+% \end{function}
+%
 % \end{documentation}
 %
 % \begin{implementation}
@@ -1896,7 +1906,7 @@
 %
 % \begin{macro}[EXP]
 %   {
-%     \str_foldcase:n, \str_foldcase:V,
+%     \str_casefold:n, \str_casefold:V,
 %     \str_lowercase:n, \str_lowercase:f,
 %     \str_uppercase:n, \str_uppercase:f
 %   }
@@ -1907,17 +1917,13 @@
 % \begin{macro}[EXP]{\@@_change_case_end:nw}
 % \begin{macro}[EXP]{\@@_change_case_loop:nw}
 % \begin{macro}[EXP]{\@@_change_case_space:n}
-% \begin{macro}[EXP]{\@@_change_case_char:nN}
-% \begin{macro}[EXP]{\@@_change_case_char_UTFviii:nNN}
-% \begin{macro}[EXP]{\@@_change_case_char_UTFviii:nNNN}
-% \begin{macro}[EXP]{\@@_change_case_char_UTFviii:nNNNN}
-% \begin{macro}[EXP]
-%   {
-%     \@@_change_case_char_UTFviii:nn ,
-%     \@@_change_case_char_UTFviii_lower:nn ,
-%     \@@_change_case_char_UTFviii_upper:nn ,
-%     \@@_change_case_char_UTFviii_fold:nn
-%   }
+% \begin{macro}[EXP]{\@@_change_case_char:nN, \@@_change_case_char_aux:nN}
+% \begin{macro}[EXP]{\@@_change_case_codepoint:nN}
+% \begin{macro}[EXP]{\@@_change_case_codepoint:nNN}
+% \begin{macro}[EXP]{\@@_change_case_codepoint:nNNN}
+% \begin{macro}[EXP]{\@@_change_case_codepoint:nNNNN}
+% \begin{macro}[EXP]{\@@_change_case_char:nnn, \@@_change_case_char_aux:nnn}
+% \begin{macro}[EXP]{\@@_change_case_char:nnnnn}
 %   Case changing for programmatic reasons is done by first detokenizing
 %   input then doing a simple loop that only has to worry about spaces
 %   and everything else. The output is detokenized to allow data sharing
@@ -1924,10 +1930,10 @@
 %   with text-based case changing. Similarly, for $8$-bit engines the
 %   multi-byte information is shared.
 %    \begin{macrocode}
-\cs_new:Npn \str_foldcase:n  #1 { \@@_change_case:nn {#1} { fold } }
-\cs_new:Npn \str_lowercase:n #1 { \@@_change_case:nn {#1} { lower } }
-\cs_new:Npn \str_uppercase:n #1 { \@@_change_case:nn {#1} { upper } }
-\cs_generate_variant:Nn \str_foldcase:n  { V }
+\cs_new:Npn \str_casefold:n  #1 { \@@_change_case:nn {#1} { casefold } }
+\cs_new:Npn \str_lowercase:n #1 { \@@_change_case:nn {#1} { lowercase } }
+\cs_new:Npn \str_uppercase:n #1 { \@@_change_case:nn {#1} { uppercase } }
+\cs_generate_variant:Nn \str_casefold:n  { V }
 \cs_generate_variant:Nn \str_lowercase:n { f }
 \cs_generate_variant:Nn \str_uppercase:n { f }
 \cs_new:Npn \@@_change_case:nn #1
@@ -1962,99 +1968,90 @@
   {
     \@@_if_recursion_tail_stop_do:Nn #2
       { \@@_change_case_end:wn }
-    \@@_change_case_output:fw
-      { \use:c { char_str_ #1 case:N } #2 }
-    \@@_change_case_loop:nw {#1}
+    \@@_change_case_codepoint:nN {#1} #2
   }
 \if_int_compare:w 0
   \cs_if_exist:NT \tex_XeTeXversion:D { 1 }
   \cs_if_exist:NT \tex_luatexversion:D { 1 }
   > 0 \exp_stop_f:
+  \cs_new:Npn \@@_change_case_codepoint:nN #1#2
+    { \@@_change_case_char:fnn { \int_eval:n {`#2} } {#1} {#2} }
 \else:
-  \cs_gset:Npn \@@_change_case_char:nN #1#2
-    {
-      \@@_if_recursion_tail_stop_do:Nn #2
-        { \@@_change_case_end:wn }
-      \int_compare:nNnTF { `#2 } > { "80 }
-        {
-          \int_compare:nNnTF { `#2 } < { "E0 }
-            { \@@_change_case_char_UTFviii:nNN }
-            {
-              \int_compare:nNnTF { `#2 } < { "F0 }
-                { \@@_change_case_char_UTFviii:nNNN }
-                { \@@_change_case_char_UTFviii:nNNNN }
-            }
-          {#1} #2
-        }
-        {
-          \@@_change_case_output:fw
-            { \use:c { char_str_ #1 case:N } #2 }
-          \@@_change_case_loop:nw {#1}
-        }
-    }
-  \cs_new:Npn \@@_change_case_char_UTFviii:nNN #1#2#3
-    { \@@_change_case_char_UTFviii:nn {#1} {#2#3} }
-  \cs_new:Npn \@@_change_case_char_UTFviii:nNNN #1#2#3#4
-    { \@@_change_case_char_UTFviii:nn {#1} {#2#3#4} }
-  \cs_new:Npn \@@_change_case_char_UTFviii:nNNNN #1#2#3#4#5
-    { \@@_change_case_char_UTFviii:nn {#1} {#2#3#4#5} }
-%    \end{macrocode}
-%   Skip high chars for the Japanese engines.
-%    \begin{macrocode}
-  \cs_if_exist:NF \tex_pdftexversion:D
-    {
-      \cs_gset:Npn \@@_change_case_char_UTFviii:nNNN #1#2#3#4
-        {
-          \@@_change_case_output:nw {#2#3#4}
-          \@@_change_case_loop:nw {#1}
-        }
-      \cs_gset:Npn \@@_change_case_char_UTFviii:nNNNN #1#2#3#4#5
-        {
-          \@@_change_case_output:nw {#2#3#4#5}
-          \@@_change_case_loop:nw {#1}
-        }
-    }
-  \cs_new:Npn \@@_change_case_char_UTFviii:nn #1#2
-    {
-      \use:c { @@_change_case_char_UTFviii_ #1 :nn } {#1} {#2}
-    }
-  \cs_new:Npn \@@_change_case_char_UTFviii_upper:nn #1#2
-    {
-      \@@_change_case_output:fw
-        {
-          \cs_if_exist:cTF { c__kernel_ #1 case_ #2 _tl }
-            {
-              \__kernel_tl_to_str:w \exp_after:wN \exp_after:wN \exp_after:wN
-                { \cs:w c__kernel_ #1 case_ #2 _tl \cs_end: }
-            }
-            {#2}
-        }
-      \@@_change_case_loop:nw {#1}
-    }
-  \cs_new_eq:NN \@@_change_case_char_UTFviii_lower:nn
-    \@@_change_case_char_UTFviii_upper:nn
-  \cs_new:Npn \@@_change_case_char_UTFviii_fold:nn #1#2
-    {
-      \@@_change_case_output:fw
-        {
-          \cs_if_exist:cTF { c__kernel_ #1 case_ #2 _tl }
-            {
-              \__kernel_tl_to_str:w \exp_after:wN \exp_after:wN \exp_after:wN
-                { \cs:w c__kernel_ #1 case_ #2 _tl \cs_end: }
-            }
-            {
-              \cs_if_exist:cTF { c__kernel_lowercase_ #2 _tl }
-                {
-                  \__kernel_tl_to_str:w
-                    \exp_after:wN \exp_after:wN \exp_after:wN
-                    { \cs:w c__kernel_lowercase_ #2 _tl \cs_end: }
-                }
-                {#2}
-            }
-        }
-      \@@_change_case_loop:nw {#1}
-    }
+    \cs_new:Npn \@@_change_case_codepoint:nN #1#2
+      {
+        \int_compare:nNnTF {`#2} > { "80 }
+          {
+            \int_compare:nNnTF {`#2} < { "E0 }
+              { \@@_change_case_codepoint:nNN }
+              {
+                 \int_compare:nNnTF {`#2} < { "F0 }
+                   { \@@_change_case_codepoint:nNNN }
+                   { \@@_change_case_codepoint:nNNNNN }
+              }
+          }
+          { \@@_change_case_char_aux:nN }
+            {#1} #2
+      }
+    \cs_new:Npn \@@_change_case_char_aux:nN #1#2
+      { \@@_change_case_char:fnn { \int_eval:n {`#2} } {#1} {#2} }
+    \cs_new:Npn \@@_change_case_codepoint:nNN #1#2#3
+      {
+        \@@_change_case_char:fnn
+          { \int_eval:n { (`#2 - "C0) * "40 + `#3 - "80 } }
+          {#1} {#2#3}
+      }
+    \cs_new:Npn \@@_change_case_codepoint:nNNN #1#2#3#4
+      {
+        \@@_change_case_char:fnn
+          {
+            \int_eval:n
+              { (`#2 - "E0) * "1000 + (`#3 - "80) * "40 + `#4 - "80 }
+          }
+          {#1} {#2#3#4}
+      }
+    \cs_new:Npn \@@_change_case_codepoint:nNNNN #1#2#3#4#5
+      {
+        \@@_change_case_char:fnn
+          {
+            \int_eval:n
+              {
+                  (`#2 - "F0) * "40000
+                + (`#3 - "80) * "1000
+                + (`#4 - "80) * "40
+                + `#5 - "80
+              }
+          }
+          {#1} {#2#3#4#5}
+      }
 \fi:
+\cs_new:Npn \@@_change_case_char:nnn #1#2#3
+  {
+    \@@_change_case_output:fw
+      {
+        \exp_args:Ne \@@_change_case_char_aux:nnn
+          { \__kernel_codepoint_case:nn {#2} {#1} } {#1} {#3}
+      }
+    \@@_change_case_loop:nw {#2}
+  }
+\cs_generate_variant:Nn \@@_change_case_char:nnn { f }
+\cs_new:Npn \@@_change_case_char_aux:nnn #1#2#3
+  {
+    \use:e { \@@_change_case_char:nnnnn #1 {#2} {#3} }
+  }
+\cs_new:Npn \@@_change_case_char:nnnnn #1#2#3#4#5
+  {
+    \int_compare:nNnTF {#1} = {#4}
+      { \tl_to_str:n {#5} }
+      {
+        \codepoint_str_generate:n {#1}
+        \tl_if_blank:nF {#2}
+          {
+            \codepoint_str_generate:n {#2}
+            \tl_if_blank:nF {#3}
+             { \codepoint_str_generate:n {#3} }
+          }
+      }
+  }
 %    \end{macrocode}
 % \end{macro}
 % \end{macro}
@@ -2069,6 +2066,8 @@
 % \end{macro}
 % \end{macro}
 % \end{macro}
+% \end{macro}
+% \end{macro}
 %
 % \begin{variable}
 %   {

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3sys.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3sys.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3sys.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3text-case.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3text-case.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3text-case.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 %
 % \maketitle
 %
@@ -138,7 +138,8 @@
 %     \@@_change_case_group_titleonly:nnn
 %   }
 % \begin{macro}[EXP]{\@@_change_case_space:nnw}
-% \begin{macro}[EXP]{\@@_change_case_N_type:nnN, \@@_change_case_N_type_aux:nnN}
+% \begin{macro}[EXP]
+%   {\@@_change_case_N_type:nnN, \@@_change_case_N_type_aux:nnN}
 % \begin{macro}[EXP]{\@@_change_case_N_type:nnnN}
 % \begin{macro}[EXP]{\@@_change_case_math_search:nnNNN}
 % \begin{macro}[EXP]{\@@_change_case_math_loop:nnNw}
@@ -170,36 +171,38 @@
 %   }
 % \begin{macro}[EXP]{\@@_change_case_letterlike:nnnnN}
 % \begin{macro}[EXP]
-%   {\@@_change_case_char_lower:nnN, \@@_change_case_char_upper:nnN}
+%   {
+%     \@@_change_case_codepoint_lower:nnn ,
+%     \@@_change_case_codepoint_upper:nnn ,
+%     \@@_change_case_codepoint_title:nnn ,
+%     \@@_change_case_codepoint_titleonly:nnn
+%   }
+% \begin{macro}[EXP]{\@@_change_case_lower_sigma:nnnn}
+% \begin{macro}[EXP]{\@@_change_case_lower_sigma:nnnw}
 % \begin{macro}[EXP]{\@@_change_case_lower_sigma:nnnN}
-% \begin{macro}[EXP]{\@@_change_case_lower_sigma:nnNw}
-% \begin{macro}[EXP]{\@@_change_case_lower_sigma:NnnN}
-% \begin{macro}[EXP]{\@@_change_case_lower_sigma:nnnNN}
-% \begin{macro}[EXP]{\@@_change_case_lower_sigma:nnw}
-% \begin{macro}[EXP]{\@@_change_case_lower_sigma:nnN}
 % \begin{macro}[EXP]
-%   {\@@_change_case_char_title:nnN, \@@_change_case_char_titleonly:nnN}
+%   {
+%     \@@_change_case_codepoint_title:nn     ,
+%     \@@_change_case_codepoint_titleonly:nn
+%   }
+% \begin{macro}[EXP]{\@@_change_case_codepoint_title:nnnn}
 % \begin{macro}[EXP]
-%   {\@@_change_case_char_title:nN, \@@_change_case_char_titleonly:nN}
-% \begin{macro}[EXP]{\@@_change_case_char_title:nnnN}
+%   {\@@_change_case_codepoint:nnnn, \@@_change_case_codepoint_aux:nnnn}
+% \begin{macro}[EXP]{\@@_change_case_codepoint:nn}
 % \begin{macro}[EXP]
 %   {
-%     \@@_change_case_char:nnnN       ,
-%     \@@_change_case_char_auxi:nnnN  ,
-%     \@@_change_case_char_auxii:nnnN
+%     \@@_change_case_codepoint:nnn ,
+%     \@@_change_case_codepoint:fnn ,
+%     \@@_change_case_codepoint_aux:nnn
 %   }
-% \begin{macro}[EXP]{\@@_change_case_char_UTFviii:nnnNN}
-% \begin{macro}[EXP]{\@@_change_case_char_UTFviii:nnnNNN}
-% \begin{macro}[EXP]{\@@_change_case_char_UTFviii:nnnNNNN}
-% \begin{macro}[EXP]{\@@_change_case_char_UTFviii:nnnn}
 % \begin{macro}[EXP]
 %   {
-%   \@@_change_case_char_next_lower:nn     ,
-%   \@@_change_case_char_next_upper:nn     ,
-%   \@@_change_case_char_next_title:nn     ,
-%   \@@_change_case_char_next_titleonly:nn
+%     \@@_change_case_next_lower:nn     ,
+%     \@@_change_case_next_upper:nn     ,
+%     \@@_change_case_next_title:nn     ,
+%     \@@_change_case_next_titleonly:nn ,
+%     \@@_change_case_next_end:nn
 %   }
-% \begin{macro}[EXP]{\@@_change_case_char_next_end:nn}
 %   As for the expansion code, the business end of case changing is the
 %   handling of \texttt{N}-type tokens. First, we expand the input fully
 %   (so the loops here don't need to worry about awkward look-aheads and the
@@ -246,7 +249,7 @@
   { \@@_change_case_BCP:nnnnw {#1} {#2} {#4} {#3} #3 - - \q_@@_stop }
 \cs_new:Npn \@@_change_case_BCP:nnnnw #1#2#3#4#5 - #6 - #7 \q_@@_stop
   {
-    \cs_if_exist:cTF { @@_change_case_ #2 _ #5 -x- #3 :nnnN }
+    \cs_if_exist:cTF { @@_change_case_ #2 _ #5 -x- #3 :nnnn }
       { \@@_change_case_auxii:nnn {#1} {#2} { #5 -x- #3 } }
       { \@@_change_case_auxii:nnn {#1} {#2} {#4} }
   }
@@ -429,9 +432,12 @@
 \cs_new:Npn \@@_change_case_cs_check:nnN #1#2#3
   {
     \token_if_cs:NTF #3
-      { \@@_change_case_exclude:nnN }
-      { \use:c { @@_change_case_char_ #1 :nnN } }
-        {#1} {#2} #3
+      { \@@_change_case_exclude:nnN {#1} {#2} }
+      {
+        \@@_codepoint_process:nN
+          { \use:c { @@_change_case_codepoint_ #1 :nnn } {#1} {#2} }
+      }
+        #3
   }
 %    \end{macrocode}
 %   To deal with a control sequence there is first a need to test if it is
@@ -545,7 +551,7 @@
       {
         \@@_change_case_store:v
           { c_@@_ #1 case_ \token_to_str:N #5 _tl }
-         \use:c { @@_change_case_char_next_ #2 :nn } {#2} {#4}
+         \use:c { @@_change_case_next_ #2 :nn } {#2} {#4}
       }
       {
         \@@_change_case_store:n {#5}
@@ -555,7 +561,7 @@
             \str_if_eq:nnTF {#1} { lower } { upper } { lower }
             case_ \token_to_str:N #5 _tl
           }
-          { \use:c { @@_change_case_char_next_ #2 :nn } {#2} {#4} }
+          { \use:c { @@_change_case_next_ #2 :nn } {#2} {#4} }
           { \@@_change_case_loop:nnw {#3} {#4} }
       }
   }
@@ -563,222 +569,196 @@
 %   For upper- and lowercase changes, once we get to this stage there are only
 %   a couple of questions remaining: is there a language-specific mapping and
 %   is there the special case of a terminal sigma. If not, then we pass to
-%   a simple character mapping.
+%   a simple codepoint mapping.
 %    \begin{macrocode}
-\cs_new:Npn \@@_change_case_char_lower:nnN #1#2#3
+\cs_new:Npn \@@_change_case_codepoint_lower:nnn #1#2#3
   {
-    \cs_if_exist_use:cF { @@_change_case_lower_ #2 :nnnN }
-      { \@@_change_case_lower_sigma:nnnN }
-        {#1} {#1} {#2} #3
+    \cs_if_exist_use:cF { @@_change_case_lower_ #2 :nnnn }
+      { \@@_change_case_lower_sigma:nnnn }
+        {#1} {#1} {#2} {#3}
   }
-\cs_new:Npn \@@_change_case_char_upper:nnN #1#2#3
+\cs_new:Npn \@@_change_case_codepoint_upper:nnn #1#2#3
   {
-    \cs_if_exist_use:cF { @@_change_case_upper_ #2 :nnnN }
-      { \@@_change_case_char:nnnN }
-        {#1} {#1} {#2} #3
+    \cs_if_exist_use:cF { @@_change_case_upper_ #2 :nnnn }
+      { \@@_change_case_codepoint:nnnn }
+        {#1} {#1} {#2} {#3}
   }
 %    \end{macrocode}
 %   If the current character is an uppercase sigma, the a check is made on the
 %   next item in the input.  If it is \texttt{N}-type and not a control sequence
-%   then there is a look-ahead phase: the logic here is simply based on letters.
-%   The one exception is Dutch: see below.
+%   then there is a look-ahead phase: the logic here is simply based on letters
+%   or actives (to cover $8$-bit engines).
 %    \begin{macrocode}
-\bool_lazy_or:nnTF
-  { \sys_if_engine_luatex_p: }
-  { \sys_if_engine_xetex_p: }
+\cs_new:Npn \@@_change_case_lower_sigma:nnnn #1#2#3#4
   {
-    \cs_new:Npn \@@_change_case_lower_sigma:nnnN #1#2#3#4
+    \@@_codepoint_compare:nNnTF {#4} = { "03A3 }
+      { \@@_change_case_lower_sigma:nnnw {#2} }
+      { \@@_change_case_codepoint:nnnn {#1} {#2} }
+        {#3} {#4}
+  }
+\cs_new:Npn \@@_change_case_lower_sigma:nnnw #1#2#3#4 \q_@@_recursion_stop
+  {
+    \tl_if_head_is_N_type:nTF {#4}
+      { \@@_change_case_lower_sigma:nnnN {#3} }
       {
-        \int_compare:nNnTF { `#4 } = { "03A3 }
-          { \@@_change_case_lower_sigma:nnNw {#2} {#3} #4 }
-          { \@@_change_case_char:nnnN {#1} {#2} {#3} #4 }
-      }
-    \cs_new:Npn \@@_change_case_lower_sigma:nnNw #1#2#3#4 \q_@@_recursion_stop
-      {
-        \tl_if_head_is_N_type:nTF {#4}
-          { \@@_change_case_lower_sigma:NnnN #3 }
-          {
-            \@@_change_case_store:e
-              { \char_generate:nn { "03C2 } { \@@_char_catcode:N #3 } }
-            \@@_change_case_loop:nnw
-          }
-            {#1} {#2} #4 \q_@@_recursion_stop
-      }
-    \cs_new:Npn \@@_change_case_lower_sigma:NnnN #1#2#3#4
-      {
         \@@_change_case_store:e
-          {
-            \token_if_letter:NTF #4
-              { \char_generate:nn { "03C3 } { \@@_char_catcode:N #1 } }
-              { \char_generate:nn { "03C2 } { \@@_char_catcode:N #1 } }
-          }
-        \@@_change_case_loop:nnw {#2} {#3} #4
+          { \codepoint_generate:nn { "03C2 } { \@@_char_catcode:N #3 } }
+        \@@_change_case_loop:nnw
       }
+        {#1} {#2} #4 \q_@@_recursion_stop
   }
-%    \end{macrocode}
-%   In the $8$-bit engines, we have to look ahead once we find the first byte of the 
-%   possible hit.
-%    \begin{macrocode}
+\cs_new:Npn \@@_change_case_lower_sigma:nnnN #1#2#3#4
   {
-    \cs_new:Npn \@@_change_case_lower_sigma:nnnN #1#2#3#4
+    \@@_change_case_store:e
       {
-        \int_compare:nNnTF { `#4 } = { "CE }
-          { \@@_change_case_lower_sigma:nnnNN  }
-          { \@@_change_case_char:nnnN }
-            {#1} {#2} {#3} #4
-      }
-    \cs_new:Npn \@@_change_case_lower_sigma:nnnNN #1#2#3#4#5
-      {
-        \int_compare:nNnTF { `#5 } = { "A3 }
-          { \@@_change_case_lower_sigma:nnw {#2} {#3}  }
-          { \@@_change_case_char:nnnN {#1} {#2} {#3} #4#5 }
-      }
-    \cs_new:Npn \@@_change_case_lower_sigma:nnw #1#2#3 \q_@@_recursion_stop
-      {
-        \tl_if_head_is_N_type:nTF {#3}
-          { \@@_change_case_lower_sigma:nnN }
-          {
-            \@@_change_case_store:V \c_@@_final_sigma_tl
-            \@@_change_case_loop:nnw
-          }
-        {#1} {#2} #3 \q_@@_recursion_stop
-      }
-    \cs_new:Npn \@@_change_case_lower_sigma:nnN #1#2#3
-      {
         \bool_lazy_or:nnTF
-          { \token_if_letter_p:N #3 }
+          { \token_if_letter_p:N #4 }
           {
             \bool_lazy_and_p:nn
-              { \token_if_active_p:N #3 }
-              { \int_compare_p:nNn { `#3 } > { "80 } }
+              { \token_if_active_p:N #4 }
+              { \int_compare_p:nNn {`#4} > { "80 } }
           }
-          { \@@_change_case_store:V \c_@@_sigma_tl }
-          { \@@_change_case_store:V \c_@@_final_sigma_tl }
-        \@@_change_case_loop:nnw {#1} {#2} #3
+          { \codepoint_generate:nn { "03C3 } { \@@_char_catcode:N #1 } }
+          { \codepoint_generate:nn { "03C2 } { \@@_char_catcode:N #1 } }
       }
+    \@@_change_case_loop:nnw {#2} {#3} #4
   }
 %    \end{macrocode}
 %   For titlecasing, we need to fully expand the new character to see if it
-%   is a letter (or active) But that means looking ahead in the $8$-bit case, so
-%   we have to grab the required tokens up-front. Life is a lot easier for Unicode
-%   \TeX{}'s, where we just have one token to worry about. The one wrinkle here
-%   is that for look-ahead we'd get into trouble: luckily, only Dutch has that
-%   issue.
+%   is a letter (or active).
 %    \begin{macrocode}
-\cs_new:Npx \@@_change_case_char_title:nnN #1#2#3
+\cs_new:Npn \@@_change_case_codepoint_title:nnn #1#2#3
   {
-    \exp_not:N \bool_if:NTF \l_text_titlecase_check_letter_bool
+    \bool_if:NTF \l_text_titlecase_check_letter_bool
       {
-        \bool_lazy_or:nnTF
-          { \sys_if_engine_luatex_p: }
-          { \sys_if_engine_xetex_p: }
-          { \exp_not:N \token_if_letter:NTF #3 }
+        \tl_if_single:nTF {#3}
           {
-            \exp_not:N \bool_lazy_or:nnTF
-              { \exp_not:N \token_if_letter_p:N #3 }
-              { \exp_not:N \token_if_active_p:N #3 }
+            \bool_lazy_or:nnTF
+              { \token_if_letter_p:N #3 }
+              {
+                \bool_lazy_and_p:nn
+                  { \token_if_active_p:N #3 }
+                  { ! \int_compare_p:nNn {`#3} < { "80 } }
+              }
+              { \use:c { @@_change_case_codepoint_ #1 :nn } }
+              { \@@_change_case_codepoint_title:nnnn { title } {#1} }
           }
-          { \exp_not:N \use:c { @@_change_case_char_ #1 :nN } }
-          { \exp_not:N \@@_change_case_char_title:nnnN { title } {#1} }
+          { \use:c { @@_change_case_codepoint_ #1 :nn } }
       }
-      { \exp_not:N \use:c { @@_change_case_char_ #1 :nN } }
-        {#2} #3
+      { \use:c { @@_change_case_codepoint_ #1 :nn } }
+        {#2} {#3}
   }
-\cs_new_eq:NN \@@_change_case_char_titleonly:nnN
-  \@@_change_case_char_title:nnN
-\cs_new:Npn \@@_change_case_char_title:nN #1#2
-  { \@@_change_case_char_title:nnnN { title } { lower } {#1} #2 }
-\cs_new:Npn \@@_change_case_char_titleonly:nN #1#2
-  { \@@_change_case_char_title:nnnN { title } { end } {#1} #2 }
-\cs_new:Npn \@@_change_case_char_title:nnnN #1#2#3#4
+\cs_new_eq:NN \@@_change_case_codepoint_titleonly:nnn
+  \@@_change_case_codepoint_title:nnn
+\cs_new:Npn \@@_change_case_codepoint_title:nn #1#2
+  { \@@_change_case_codepoint_title:nnnn { title } { lower } {#1} {#2} }
+\cs_new:Npn \@@_change_case_codepoint_titleonly:nn #1#2
+  { \@@_change_case_codepoint_title:nnnn { title } { end } {#1} {#2} }
+\cs_new:Npn \@@_change_case_codepoint_title:nnnn #1#2#3#4
   {
-    \cs_if_exist_use:cF { @@_change_case_title_ #3 :nnnN }
+    \cs_if_exist_use:cF { @@_change_case_title_ #3 :nnnn }
       {
-        \cs_if_exist_use:cF { @@_change_case_upper_ #3 :nnnN }
-          { \@@_change_case_char:nnnN }
+        \cs_if_exist_use:cF { @@_change_case_upper_ #3 :nnnn }
+          { \@@_change_case_codepoint:nnnn }
       }
-        {#1} {#2} {#3} #4
+        {#1} {#2} {#3} {#4}
   }
-%    \end{macrocode}
-%   For Unicode engines we can handle all characters directly. However, for
-%   the $8$-bit engines the aim is to deal with (a subset of) Unicode (UTF-8)
-%   input. They deal with that by making the upper half of the range active,
-%   so we look for that and if found work out how many UTF-8 octets there
-%   are to deal with. Those can then be grabbed to reconstruct the full
-%   Unicode character, which is then used in a lookup. (As will become
-%   obvious below, there is no intention here of covering all of Unicode.)
-%   For (u)p-\TeX{} there are a limited number of tokens we can touch.
-%    \begin{macrocode}
-\cs_new:Npn \@@_change_case_char:nnnN #1#2#3#4
+\cs_new:Npn \@@_change_case_codepoint:nnnn #1#2#3#4
   {
-    \token_if_active:NTF #4
+    \bool_lazy_and:nnTF
+      { \tl_if_single_p:n {#4} }
+      { \token_if_active_p:N #4 }
       { \@@_change_case_store:n {#4} }
       {
         \@@_change_case_store:e
-          { \use:c { char_ #1 case :N } #4 }
+          { \@@_change_case_codepoint:nn {#1} {#4} }
       }
-    \use:c { @@_change_case_char_next_ #2 :nn } {#2} {#3}
+    \use:c { @@_change_case_next_ #2 :nn } {#2} {#3}
   }
-\bool_lazy_or:nnF
-  { \sys_if_engine_luatex_p: }
-  { \sys_if_engine_xetex_p: }
+\cs_new:Npn \@@_change_case_codepoint:nn #1#2
   {
-    \cs_new_eq:NN \@@_change_case_char_auxi:nnnN
-      \@@_change_case_char:nnnN
-    \cs_gset:Npn \@@_change_case_char:nnnN #1#2#3#4
+    \@@_change_case_codepoint:fnn
+      { \int_eval:n { \@@_codepoint_from_chars:Nw #2 } } {#1} {#2}
+  }
+\cs_new:Npn \@@_change_case_codepoint:nnn #1#2#3
+  {
+    \exp_args:Ne \@@_change_case_codepoint_aux:nn
+      { \__kernel_codepoint_case:nn { #2 case } {#1} } {#3}
+  }
+\cs_generate_variant:Nn \@@_change_case_codepoint:nnn { f }
+%    \end{macrocode}
+%   Avoid high chars with p\TeX{}.
+%    \begin{macrocode}
+\sys_if_engine_ptex:T
+  {
+    \cs_new_eq:NN \@@_change_case_codepoint_aux:nnn
+      \@@_change_case_codepoint:nnn
+    \cs_gset:Npn \@@_change_case_codepoint:nnn #1#2#3
       {
-        \int_compare:nNnTF { `#4 } > { "80 }
-          {
-            \int_compare:nNnTF { `#4 } < { "E0 }
-              { \@@_change_case_char_UTFviii:nnnNN }
-              { \@@_change_case_char_auxii:nnnN }
-          }
-          { \@@_change_case_char_auxi:nnnN }
-            {#1} {#2} {#3} #4
-       }
-    \sys_if_engine_pdftex:TF
-      {
-        \cs_new:Npn \@@_change_case_char_auxii:nnnN #1#2#3#4
-          {
-            \int_compare:nNnTF { `#4 } < { "F0 }
-              { \@@_change_case_char_UTFviii:nnnNNN }
-              { \@@_change_case_char_UTFviii:nnnNNNN }
-                {#1} {#2} {#3} #4
-          }
+        \int_compare:nNnTF {#1} = { -1 }
+          { \exp_not:n {#3} }
+          { \@@_change_case_codepoint_aux:nnn {#1} {#2} {#3} }
       }
+  }
+\cs_new:Npn \@@_change_case_codepoint_aux:nn #1#2
+  {
+    \use:e { \@@_change_case_codepoint_aux:nnnn #1 {#2} }
+  }
+\cs_new:Npn \@@_change_case_codepoint_aux:nnnn #1#2#3#4
+  {
+    \@@_codepoint_compare:nNnTF {#4} = {#1}
+      { \exp_not:n {#4} }
       {
-        \cs_new:Npn \@@_change_case_char_auxii:nnnN #1#2#3#4
+        \codepoint_generate:nn {#1}
+          { \@@_change_case_catcode:nn {#4} {#1} }
+        \tl_if_blank:nF {#2}
           {
-            \@@_change_case_store:n {#4}
-            \use:c { @@_change_case_char_next_ #2 :nn } {#2} {#3}
+            \codepoint_generate:nn {#2}
+              { \char_value_catcode:n {#2} }
+            \tl_if_blank:nF {#3}
+             {
+               \codepoint_generate:nn {#3}
+                 { \char_value_catcode:n {#3} }
+             }
           }
       }
-    \cs_new:Npn \@@_change_case_char_UTFviii:nnnNN #1#2#3#4#5
-      { \@@_change_case_char_UTFviii:nnnn {#1} {#2} {#3} {#4#5} }
-    \cs_new:Npn \@@_change_case_char_UTFviii:nnnNNN #1#2#3#4#5#6
-      { \@@_change_case_char_UTFviii:nnnn {#1} {#2} {#3} {#4#5#6} }
-    \cs_new:Npn \@@_change_case_char_UTFviii:nnnNNNN #1#2#3#4#5#6#7
-      { \@@_change_case_char_UTFviii:nnnn {#1} {#2} {#3} {#4#5#6#7} }
-    \cs_new:Npn \@@_change_case_char_UTFviii:nnnn #1#2#3#4
+  }
+%    \end{macrocode}
+%   We need to ensure that only valid catcode-extraction is attempted. That's
+%   fine with Unicode engines but needs a bit of work with 8-bit ones. The
+%   logic is that if the original codepoint was in the ASCII range, we keep
+%   the catcode. Otherwise, if the target is in the ASCII range, we use
+%   the standard catcode. If neither are true, we set as 13 on the grounds that
+%   this will be what is used anyway!
+%    \begin{macrocode}
+\bool_lazy_or:nnTF
+  { \sys_if_engine_luatex_p: }
+  { \sys_if_engine_xetex_p: }
+  {
+    \cs_new:Npn \@@_change_case_catcode:nn #1#2
+      { \@@_char_catcode:N #1 }
+  }
+  {
+    \cs_new:Npn \@@_change_case_catcode:nn #1#2
       {
-        \cs_if_exist:cTF { c__kernel_ #1 case_ \tl_to_str:n {#4} _tl }
+        \@@_codepoint_compare:nNnTF {#1} < { "80 }
+          { \@@_char_catcode:N #1 }
           {
-            \@@_change_case_store:v
-              { c__kernel_ #1 case_ \tl_to_str:n {#4} _tl }
+            \int_compare:nNnTF {#2} < { "80 }
+              { \char_value_catcode:n {#2} }
+              { 13 }
           }
-          { \@@_change_case_store:n {#4} }
-        \use:c { @@_change_case_char_next_ #2 :nn } {#2} {#3}
       }
   }
-\cs_new:Npn \@@_change_case_char_next_lower:nn #1#2
+\cs_new:Npn \@@_change_case_next_lower:nn #1#2
   { \@@_change_case_loop:nnw {#1} {#2} }
-\cs_new_eq:NN \@@_change_case_char_next_upper:nn
-  \@@_change_case_char_next_lower:nn
-\cs_new_eq:NN \@@_change_case_char_next_title:nn
-  \@@_change_case_char_next_lower:nn
-\cs_new_eq:NN \@@_change_case_char_next_titleonly:nn
-  \@@_change_case_char_next_lower:nn
-\cs_new:Npn \@@_change_case_char_next_end:nn #1#2
+\cs_new_eq:NN \@@_change_case_next_upper:nn
+  \@@_change_case_next_lower:nn
+\cs_new_eq:NN \@@_change_case_next_title:nn
+  \@@_change_case_next_lower:nn
+\cs_new_eq:NN \@@_change_case_next_titleonly:nn
+  \@@_change_case_next_lower:nn
+\cs_new:Npn \@@_change_case_next_end:nn #1#2
   { \@@_change_case_break:w }
 %    \end{macrocode}
 % \end{macro}
@@ -822,13 +802,6 @@
 % \end{macro}
 % \end{macro}
 % \end{macro}
-% \end{macro}
-% \end{macro}
-% \end{macro}
-% \end{macro}
-% \end{macro}
-% \end{macro}
-% \end{macro}
 %
 % \begin{macro}{\text_declare_case_equivalent:Nn}
 %  Create equivalents to allow replacement.
@@ -855,76 +828,63 @@
 % \end{macro}
 % \end{macro}
 %
-% \begin{macro}[EXP]{\@@_change_case_upper_de-x-eszett:nnnN, \@@_change_case_upper_de-alt:nnnN}
-% \begin{macro}[EXP]{\@@_change_case_upper_de-x-eszett:nnnNN}
+% \begin{macro}[EXP]
+%   {\@@_change_case_upper_de-x-eszett:nnnn, \@@_change_case_upper_de-alt:nnnn}
 %   A simple alternative version for German.
 %    \begin{macrocode}
-\bool_lazy_or:nnTF
-  { \sys_if_engine_luatex_p: }
-  { \sys_if_engine_xetex_p: }
+\cs_new:cpn { @@_change_case_upper_de-x-eszett:nnnn } #1#2#3#4
   {
-    \cs_new:cpn { @@_change_case_upper_de-x-eszett:nnnN } #1#2#3#4
+    \@@_codepoint_compare:nNnTF {#4} = { "00DF }
       {
-        \int_compare:nNnTF { `#4 } = { "00DF }
-          {
-            \@@_change_case_store:e
-             { \char_generate:nn { "1E9E } { \@@_char_catcode:N #4 } }
-            \use:c { @@_change_case_char_next_ #2 :nn }
-              {#2} {#3}
-          }
-          { \@@_change_case_char:nnnN {#1} {#2} {#3} #4 }
+        \@@_change_case_store:e
+         {
+           \codepoint_generate:nn { "1E9E }
+             { \@@_change_case_catcode:nn {#4} { "1E9E } }
+         }
+        \use:c { @@_change_case_next_ #2 :nn }
+          {#2} {#3}
       }
+      { \@@_change_case_codepoint:nnnn {#1} {#2} {#3} {#4} }
   }
-  {
-    \cs_new:cpx { @@_change_case_upper_de-x-eszett:nnnN } #1#2#3#4
-      {
-        \exp_not:N \int_compare:nNnTF { `#4 } = { "00C3 }
-          {
-            \exp_not:c { @@_change_case_upper_de-x-eszett:nnnNN }
-              {#1} {#2} {#3} #4
-          }
-          { \exp_not:N \@@_change_case_char:nnnN {#1} {#2} {#3} #4 }
-      }
-    \cs_new:cpn { @@_change_case_upper_de-x-eszett:nnnNN } #1#2#3#4#5
-      {
-        \int_compare:nNnTF { `#5 } = { "009F }
-          {
-            \@@_change_case_store:V \c_@@_grosses_Eszett_tl
-            \use:c { @@_change_case_char_next_ #2 :nn } {#2} {#3}
-          }
-          { \@@_change_case_char:nnnN {#1} {#2} {#3} #4#5 }
-      }
-  }
-\cs_new_eq:cc { @@_change_case_upper_de-alt:nnnN }
-  { @@_change_case_upper_de-x-eszett:nnnN }
+\cs_new_eq:cc { @@_change_case_upper_de-alt:nnnn }
+  { @@_change_case_upper_de-x-eszett:nnnn }
 %    \end{macrocode}
 % \end{macro}
-% \end{macro}
 %
 % \begin{macro}[EXP]
-%   {\@@_change_case_upper_el:nnnN, \@@_change_case_upper_el-x-iota:nnnN}
+%   {
+%     \@@_change_case_upper_el:nnnn     ,
+%     \@@_change_case_upper_el_aux:nnnn ,
+%     \@@_change_case_upper_el-x-iota:nnnn
+%   }
 % \begin{macro}[EXP]
-%   {\@@_change_case_upper_el:N, \@@_change_case_upper_el-x-iota:N}
+%   {\@@_change_case_upper_el:n, \@@_change_case_upper_el-x-iota:n}
 % \begin{macro}[EXP]{\@@_change_case_upper_el:nnn}
-% \begin{macro}[EXP]{\@@_change_case_upper_el:nnNw}
-% \begin{macro}[EXP]{\@@_change_case_upper_el:NnnN}
-% \begin{macro}[EXP]{\@@_change_case_upper_el_ypogegrammeni:Nnnnnw}
-% \begin{macro}[EXP]{\@@_change_case_upper_el_ypogegrammeni:NnnnnN}
-% \begin{macro}[EXP]{\@@_change_case_upper_el_dialytika:nnN}
-% \begin{macro}[EXP]{\@@_change_case_upper_el_dialytika:N}
-% \begin{macro}[EXP]{\@@_change_case_upper_el_hiatus:nnNw}
+% \begin{macro}[EXP]{\@@_change_case_upper_el:nnnw}
+% \begin{macro}[EXP]
+%   {\@@_change_case_upper_el:nnnN, \@@_change_case_upper_el_aux:nnnN}
+% \begin{macro}[EXP]{\@@_change_case_upper_el_ypogegrammeni:nnnnnw}
+% \begin{macro}[EXP]{\@@_change_case_upper_el_ypogegrammeni:nnnnnN}
+% \begin{macro}[EXP]{\@@_change_case_upper_el_ypogegrammeni:nnnnnn}
+% \begin{macro}[EXP]{\@@_change_case_upper_el_dialytika:nnn}
+% \begin{macro}[EXP]{\@@_change_case_upper_el_dialytika:n}
+% \begin{macro}[EXP]{\@@_change_case_upper_el_hiatus:nnnw}
 % \begin{macro}[EXP]{\@@_change_case_upper_el_hiatus:nnN}
+% \begin{macro}[EXP]{\@@_change_case_upper_el_hiatus:nnn}
 % \begin{macro}[EXP]{\@@_change_case_upper_el_gobble:nnw}
 % \begin{macro}[EXP]{\@@_change_case_upper_el_gobble:nnN}
-% \begin{macro}[EXP]{\@@_change_case_upper_el:nnnNN}
-% \begin{macro}[EXP]{\@@_change_case_upper_el:nnnNNN}
-% \begin{macro}[EXP]{\@@_change_case_upper_el:nnnn}
+% \begin{macro}[EXP]{\@@_change_case_upper_el_gobble:nnn}
 % \begin{macro}[EXP,TF]{\@@_change_case_if_greek:n}
+% \begin{macro}[EXP]{\@@_change_case_if_greek:n}
 % \begin{macro}[EXP,pTF]{\@@_change_case_if_greek:n}
 % \begin{macro}[EXP,pTF]{\@@_change_case_if_greek_accent:n}
+% \begin{macro}[EXP]{\@@_change_case_if_greek_accent:n}
 % \begin{macro}[EXP,pTF]{\@@_change_case_if_greek_diacritic:n}
+% \begin{macro}[EXP]{\@@_change_case_if_greek_diacritic:n}
 % \begin{macro}[EXP,TF]{\@@_change_case_if_takes_dialytika:n}
+% \begin{macro}[EXP]{\@@_change_case_if_takes_dialytika:n}
 % \begin{macro}[EXP,TF]{\@@_change_case_if_takes_ypogegrammeni:n}
+% \begin{macro}[EXP]{\@@_change_case_if_takes_ypogegrammeni:n}
 %   For Greek uppercasing, we need to know if characters \emph{in the Greek
 %   range} have accents. That means doing a \textsc{nfd} conversion first, then
 %   starting a search. As described by the Unicode \textsc{cldr}, Greek accents
@@ -936,55 +896,58 @@
 %   \emph{ypogegrammeni} is filtered out here as it is not actually in the
 %   Greek range, so gets lost if we leave until later.
 %    \begin{macrocode}
-\bool_lazy_or:nnTF
-  { \sys_if_engine_luatex_p: }
-  { \sys_if_engine_xetex_p: }
+\cs_new:Npn \@@_change_case_upper_el:nnnn #1#2#3#4
   {
-    \cs_new:Npn \@@_change_case_upper_el:nnnN #1#2#3#4
+    \@@_change_case_if_greek:nTF {#4}
       {
-        \@@_change_case_if_greek:nTF { `#4 }
+        \exp_args:Ne \@@_change_case_upper_el:nnn
           {
-            \exp_args:Ne \@@_change_case_upper_el:nnn
-              { \char_to_nfd:N #4 } {#2} {#3}
+            \codepoint_to_nfd:n { \@@_codepoint_from_chars:Nw #4 }
           }
+            {#2} {#3}
+      }
+      {
+        \@@_codepoint_compare:nNnTF {#4} = { "0345 }
           {
-            \int_compare:nNnTF { `#4 } = { "0345 }
+            \@@_change_case_store:e
               {
-                \@@_change_case_store:e
-                  {
-                    \use:c { @@_change_case_upper_ #3 :N } #4
-                  }
-                \@@_change_case_loop:nnw {#2} {#3}
+                \use:c { @@_change_case_upper_ #3 :n } {#4}
               }
-              { \@@_change_case_char:nnnN {#1} {#2} {#3} #4 }
+            \@@_change_case_loop:nnw {#2} {#3}
           }
+          { \@@_change_case_codepoint:nnnn {#1} {#2} {#3} {#4} }
       }
-    \cs_new_eq:cN { @@_change_case_upper_el-x-iota:nnnN }
-      \@@_change_case_upper_el:nnnN
-    \cs_new:Npn \@@_change_case_upper_el:N #1
-      {
-        \char_generate:nn { "0399 }
-          { \char_value_catcode:n { "0399 } }
-      }
-    \cs_new:cpn { @@_change_case_upper_el-x-iota:N } #1
-      { \exp_not:n {#1} }
-    \cs_new:Npn \@@_change_case_upper_el:nnn #1#2#3
-      { \@@_change_case_upper_el:nnNw {#2} {#3} #1 }
+  }
+\cs_new_eq:cN { @@_change_case_upper_el-x-iota:nnnn }
+  \@@_change_case_upper_el:nnnn
+\cs_new:Npn \@@_change_case_upper_el:n #1
+  {
+    \codepoint_generate:nn { "0399 }
+      { \char_value_catcode:n { "0399 } }
+  }
+\cs_new:cpn { @@_change_case_upper_el-x-iota:n } #1
+  { \exp_not:n {#1} }
+\cs_new:Npn \@@_change_case_upper_el:nnn #1#2#3
+  {
+    \@@_codepoint_process:nN
+      { \@@_change_case_upper_el:nnnw {#2} {#3} } #1
+  }
 %    \end{macrocode}
 %   At this stage we have the first NFD codepoint as |#3|. What we need to know
-%   is whether after that we have another character token, either from the NFD or
+%   is whether after that we have another character, either from the NFD or
 %   directly in the input. If not, we store the changed character at this stage.
 %    \begin{macrocode}
-    \cs_new:Npn \@@_change_case_upper_el:nnNw #1#2#3#4 \q_@@_recursion_stop
+\cs_new:Npn \@@_change_case_upper_el:nnnw #1#2#3#4 \q_@@_recursion_stop
+  {
+    \tl_if_head_is_N_type:nTF {#4}
+      { \@@_change_case_upper_el:nnnN {#3} }
       {
-        \tl_if_head_is_N_type:nTF {#4}
-          { \@@_change_case_upper_el:NnnN #3 }
-          {
-            \@@_change_case_store:e { \char_uppercase:N #3 }
-            \@@_change_case_loop:nnw
-          }
-            {#1} {#2} #4 \q_@@_recursion_stop
+        \@@_change_case_store:e
+          { \@@_change_case_codepoint:nn { upper } {#3} }
+        \@@_change_case_loop:nnw
       }
+        {#1} {#2} #4 \q_@@_recursion_stop
+  }
 %    \end{macrocode}
 %   Now, we check the detail of the next codepoint: again we filter out the
 %   not-a-char cases, before checking if it's an dialytika, accent or diacritic.
@@ -993,193 +956,200 @@
 %   to move any ypogegrammeni to after accents (in case the input is not
 %   normalised). The ypogegrammeni itself is handled separately.
 %    \begin{macrocode}
-    \cs_new:Npn \@@_change_case_upper_el:NnnN #1#2#3#4
+\cs_new:Npn \@@_change_case_upper_el:nnnN #1#2#3#4
+  {
+    \token_if_cs:NTF #4
       {
-        \token_if_cs:NTF #4
+        \@@_change_case_store:e
+          { \@@_change_case_codepoint:nn { upper } {#1} }
+        \@@_change_case_loop:nnw {#2} {#3} #4
+      }
+      {
+        \@@_change_case_if_takes_ypogegrammeni:nTF {#1}
           {
-            \@@_change_case_store:e { \char_uppercase:N #1 }
-            \@@_change_case_loop:nnw {#2} {#3} #4
+            \@@_change_case_upper_el_ypogegrammeni:nnnnnw
+              {#1} {#2} {#3} { } { } #4
           }
+          { \@@_change_case_upper_el_aux:nnnN {#1} {#2} {#3} #4 }
+      }
+  }
+\cs_new:Npn \@@_change_case_upper_el_ypogegrammeni:nnnnnw
+  #1#2#3#4#5#6 \q_@@_recursion_stop
+  {
+    \tl_if_head_is_N_type:nTF {#6}
+      {
+        \@@_change_case_upper_el_ypogegrammeni:nnnnnN
+          {#1} {#2} {#3} {#4} {#5}
+      }
+      { \@@_change_case_upper_el_aux:nnnN {#1} {#2} {#3} #4#5 }
+        #6 \q_@@_recursion_stop
+  }
+\cs_new:Npn \@@_change_case_upper_el_ypogegrammeni:nnnnnN #1#2#3#4#5#6
+  {
+    \token_if_cs:NTF #6
+      { \@@_change_case_upper_el_aux:nnnN {#1} {#2} {#3} #4#5 #6 }
+      {
+        \@@_codepoint_process:nN
           {
-            \@@_change_case_if_takes_ypogegrammeni:nTF { `#1 }
-              {
-                \@@_change_case_upper_el_ypogegrammeni:Nnnnnw
-                  #1 {#2} {#3} { } { } #4
-              }
-              { \@@_change_case_upper_el_aux:NnnN #1 {#2} {#3} #4 }
+            \@@_change_case_upper_el_ypogegrammeni:nnnnnn
+              {#1} {#2} {#3} {#4} {#5}
           }
+            #6
       }
-    \cs_new:Npn \@@_change_case_upper_el_ypogegrammeni:Nnnnnw
-      #1#2#3#4#5#6 \q_@@_recursion_stop
+  }
+\cs_new:Npn \@@_change_case_upper_el_ypogegrammeni:nnnnnn #1#2#3#4#5#6
+  {
+    \@@_codepoint_compare:nNnTF {#6} = { "0345 }
       {
-        \tl_if_head_is_N_type:nTF {#6}
+        \@@_change_case_upper_el_ypogegrammeni:nnnnnw
+          {#1} {#2} {#3} {#4} {#6}
+      }
+      {
+        \bool_lazy_or:nnTF
+          { \@@_change_case_if_greek_accent_p:n {#6} }
+          { \@@_change_case_if_greek_diacritic_p:n {#6} }
           {
-            \@@_change_case_upper_el_ypogegrammeni:NnnnnN
-              #1 {#2} {#3} {#4} {#5}
+            \@@_change_case_upper_el_ypogegrammeni:nnnnnw
+              {#1} {#2} {#3} {#4#6} {#5}
           }
-          { \@@_change_case_upper_el_aux:NnnN #1 {#2} {#3} #4#5 }
-            #6 \q_@@_recursion_stop
+          { \@@_change_case_upper_el_aux:nnnN {#1} {#2} {#3} #4#5 #6 }
       }
-    \cs_new:Npn \@@_change_case_upper_el_ypogegrammeni:NnnnnN #1#2#3#4#5#6
+  }
+\cs_new:Npn \@@_change_case_upper_el_aux:nnnN #1#2#3#4
+  {
+    \@@_codepoint_process:nN
+      { \@@_change_case_upper_el_aux:nnnn {#1} {#2} {#3} } #4
+  }
+\cs_new:Npn \@@_change_case_upper_el_aux:nnnn #1#2#3#4
+  {
+    \@@_codepoint_compare:nNnTF {#4} = { "0308 }
+      { \@@_change_case_upper_el_dialytika:nnn {#2} {#3} {#1} }
       {
-        \token_if_cs:NTF #6
-          { \@@_change_case_upper_el_aux:NnnN #1 {#2} {#3} #4#5 #6 }
+         \@@_change_case_if_greek_accent:nTF {#4}
+          { \@@_change_case_upper_el_hiatus:nnnw {#2} {#3} {#1} }
           {
-            \int_compare:nNnTF { `#6 } = { "0345 }
+            \@@_change_case_if_greek_diacritic:nTF {#4}
               {
-                \@@_change_case_upper_el_ypogegrammeni:Nnnnnw
-                  #1 {#2} {#3} {#4} {#6}
+                \@@_change_case_store:e
+                  { \@@_change_case_codepoint:nn { upper } {#1} }
+                \@@_change_case_loop:nnw {#2} {#3}
               }
               {
-                \bool_lazy_or:nnTF
-                  { \@@_change_case_if_greek_accent_p:n { `#6 } }
-                  { \@@_change_case_if_greek_diacritic_p:n { `#6 } }
+                \@@_codepoint_compare:nNnTF {#4} = { "0345 }
+                  { \@@_change_case_store:e { [XXX] } }
                   {
-                    \@@_change_case_upper_el_ypogegrammeni:Nnnnnw
-                      #1 {#2} {#3} {#4#6} {#5}
+                    \@@_change_case_store:e
+                      { \@@_change_case_codepoint:nn { upper } {#1} }
                   }
-                  { \@@_change_case_upper_el_aux:NnnN #1 {#2} {#3} #4#5 #6 }
-            }
-          }
-      }
-    \cs_new:Npn \@@_change_case_upper_el_aux:NnnN #1#2#3#4
-      {
-        \int_compare:nNnTF { `#4 } = { "0308 }
-          { \@@_change_case_upper_el_dialytika:nnN {#2} {#3} #1 }
-          {
-            \@@_change_case_if_greek_accent:nTF { `#4 }
-              { \@@_change_case_upper_el_hiatus:nnNw {#2} {#3} #1 }
-              {
-                \@@_change_case_if_greek_diacritic:nTF { `#4 }
-                  {
-                    \@@_change_case_store:e { \char_uppercase:N #1 }
-                    \@@_change_case_loop:nnw {#2} {#3}
-                  }
-                  {
-                    \int_compare:nNnTF { `#4 } = { "0345 }
-                      { \@@_change_case_store:e { [XXX] } }
-                      { \@@_change_case_store:e { \char_uppercase:N #1 } }
-                    \@@_change_case_loop:nnw {#2} {#3} #4
-                  }
+                \@@_change_case_loop:nnw {#2} {#3} #4
               }
           }
       }
-%    \end{macrocode}
+  }
+%  \end{macrocode}
 %   We handle \emph{dialytika} in parts as it's also needed for the hiatus.
 %   We know only two letters take it, so we can shortcut here on the second
 %   part of the tests.
 %    \begin{macrocode}
-    \cs_new:Npn \@@_change_case_upper_el_dialytika:nnN #1#2#3
+\cs_new:Npn \@@_change_case_upper_el_dialytika:nnn #1#2#3
+  {
+    \@@_change_case_if_takes_dialytika:nTF {#3}
+      { \@@_change_case_upper_el_dialytika:n {#3} }
       {
-        \@@_change_case_if_takes_dialytika:nTF { `#3 }
-          { \@@_change_case_upper_el_dialytika:N #3 }
-          { \@@_change_case_store:e { \char_uppercase:N #3 } }
-        \@@_change_case_upper_el_gobble:nnw {#1} {#2}
+        \@@_change_case_store:e
+          { \@@_change_case_codepoint:nn { upper } {#3} }
       }
-    \cs_new:Npn \@@_change_case_upper_el_dialytika:N #1
+    \@@_change_case_upper_el_gobble:nnw {#1} {#2}
+  }
+\cs_new:Npn \@@_change_case_upper_el_dialytika:n #1
+  {
+    \@@_change_case_store:e
       {
-        \@@_change_case_store:e
+        \bool_lazy_or:nnTF
+          { \@@_codepoint_compare_p:nNn {#1} = { "0399 } }
+          { \@@_codepoint_compare_p:nNn {#1} = { "03B9 } }
           {
-            \bool_lazy_or:nnTF
-              { \int_compare_p:nNn { `#1 } = { "0399 } }
-              { \int_compare_p:nNn { `#1 } = { "03B9 } }
-              { \char_generate:nn { "03AA } { \@@_char_catcode:N #1 } }
-              { \char_generate:nn { "03AB } { \@@_char_catcode:N #1 } }
+            \codepoint_generate:nn { "03AA }
+              { \@@_change_case_catcode:nn {#1} { "03AA } }
           }
+          {
+            \codepoint_generate:nn { "03AB }
+              { \@@_change_case_catcode:nn {#1} { "03AB } }
+          }
       }
+  }
 %    \end{macrocode}
 %   Adding a hiatus needs some of the same ideas, but if there is not one we
 %   skip this code point, hence needing a separate function.
 %    \begin{macrocode}
-    \cs_new:Npn \@@_change_case_upper_el_hiatus:nnNw
-      #1#2#3#4 \q_@@_recursion_stop
+\cs_new:Npn \@@_change_case_upper_el_hiatus:nnnw
+  #1#2#3#4 \q_@@_recursion_stop
+   {
+    \@@_change_case_store:e
+      { \@@_change_case_codepoint:nn { upper } {#3} }
+    \tl_if_head_is_N_type:nTF {#4}
+      { \@@_change_case_upper_el_hiatus:nnN }
+      { \@@_change_case_loop:nnw }
+        {#1} {#2} #4 \q_@@_recursion_stop
+  }
+\cs_new:Npn \@@_change_case_upper_el_hiatus:nnN #1#2#3
+  {
+    \token_if_cs:NTF #3
+      { \@@_change_case_loop:nnw {#1} {#2} #3 }
       {
-        \@@_change_case_store:e { \char_uppercase:N #3 }
-        \tl_if_head_is_N_type:nTF {#4}
-          { \@@_change_case_upper_el_hiatus:nnN }
-          { \@@_change_case_loop:nnw }
-            {#1} {#2} #4 \q_@@_recursion_stop
+        \@@_codepoint_process:nN
+          { \@@_change_case_upper_el_hiatus:nnn {#1} {#2} } #3
       }
-    \cs_new:Npn \@@_change_case_upper_el_hiatus:nnN #1#2#3
+  }
+\cs_new:Npn \@@_change_case_upper_el_hiatus:nnn #1#2#3
+  {
+    \@@_change_case_if_takes_dialytika:nTF {#3}
       {
-        \token_if_cs:NTF #3
-          { \@@_change_case_loop:nnw {#1} {#2} #3 }
-          {
-            \@@_change_case_if_takes_dialytika:nTF { `#3 }
-              {
-                \@@_change_case_upper_el_dialytika:N #3
-                \@@_change_case_upper_el_gobble:nnw {#1} {#2}
-              }
-              { \@@_change_case_loop:nnw {#1} {#2} #3 }
-          }
+        \@@_change_case_upper_el_dialytika:n {#3}
+        \@@_change_case_upper_el_gobble:nnw {#1} {#2}
       }
+      { \@@_change_case_loop:nnw {#1} {#2} #3 }
+  }
 %    \end{macrocode}
 %   For clearing out trailing combining marks after we have dealt with
 %   the first one.
 %    \begin{macrocode}
-    \cs_new:Npn \@@_change_case_upper_el_gobble:nnw
-      #1#2#3 \q_@@_recursion_stop
-      {
-        \tl_if_head_is_N_type:nTF {#3}
-          { \@@_change_case_upper_el_gobble:nnN }
-          { \@@_change_case_loop:nnw }
-            {#1} {#2} #3 \q_@@_recursion_stop
-      }
-    \cs_new:Npn \@@_change_case_upper_el_gobble:nnN #1#2#3
-      {
-        \bool_lazy_or:nnTF
-          { \token_if_cs_p:N #3 }
-          {
-            ! \bool_lazy_or_p:nn
-              { \@@_change_case_if_greek_accent_p:n { `#3 } }
-              { \@@_change_case_if_greek_diacritic_p:n { `#3 } }
-          }
-          { \@@_change_case_loop:nnw {#1} {#2} #3 }
-          { \@@_change_case_upper_el_gobble:nnw {#1} {#2} }
-      }
+\cs_new:Npn \@@_change_case_upper_el_gobble:nnw
+  #1#2#3 \q_@@_recursion_stop
+  {
+    \tl_if_head_is_N_type:nTF {#3}
+      { \@@_change_case_upper_el_gobble:nnN }
+      { \@@_change_case_loop:nnw }
+        {#1} {#2} #3 \q_@@_recursion_stop
   }
-%    \end{macrocode}
-%   For $8$-bit engines we use a simplified approach: covers the common
-%   requirements. This is basically how \pkg{xgreek} works but done by
-%   expansion: use fixed mappings from accented to accent-free letters.
-%   Only the two- and three-byte ranges can be involved.
-%    \begin{macrocode}
+\cs_new:Npn \@@_change_case_upper_el_gobble:nnN #1#2#3
   {
-    \cs_new:Npn \@@_change_case_upper_el:nnnN #1#2#3#4
+    \token_if_cs:NTF #3
+      { \@@_change_case_loop:nnw {#1} {#2} }
       {
-        \int_compare:nNnTF { `#4 } > { "80 }
-          {
-            \int_compare:nNnTF { `#4 } < { "E0 }
-              { \@@_change_case_upper_el:nnnNN }
-              {
-                 \int_compare:nNnTF { `#4 } < { "F0 }
-                  { \@@_change_case_upper_el:nnnNNN }
-                  { \@@_change_case_char:nnnN }
-              }
-          }
-          { \@@_change_case_char:nnnN }
-             {#1} {#2} {#3} #4
+        \@@_codepoint_process:nN
+          { \@@_change_case_upper_el_gobble:nnn {#1} {#2} }
       }
-    \cs_new:Npn \@@_change_case_upper_el:nnnNN #1#2#3#4#5
-      { \@@_change_case_upper_el:nnnn {#1} {#2} {#3} {#4#5} }
-    \cs_new:Npn \@@_change_case_upper_el:nnnNNN #1#2#3#4#5#6
-      { \@@_change_case_upper_el:nnnn {#1} {#2} {#3} {#4#5#6} }
-    \cs_new:Npn \@@_change_case_upper_el:nnnn #1#2#3#4
-      {
-        \cs_if_exist:cTF { c_@@_uppercase_el_ \tl_to_str:n {#4} _tl }
-          {
-            \@@_change_case_store:v
-              { c_@@_uppercase_el_ \tl_to_str:n {#4} _tl }
-            \@@_change_case_loop:nnw {#1} {#3}
-          }
-          { \@@_change_case_char:nnnN {#1} {#2} {#3} #4 }
-      }
+        #3
   }
+\cs_new:Npn \@@_change_case_upper_el_gobble:nnn #1#2#3
+  {
+    \bool_lazy_or:nnTF
+      { \@@_change_case_if_greek_accent_p:n {#3} }
+      { \@@_change_case_if_greek_diacritic_p:n {#3} }
+      { \@@_change_case_upper_el_gobble:nnw {#1} {#2} }
+      { \@@_change_case_loop:nnw {#1} {#2} #3 }
+  }
 %    \end{macrocode}
 %   Luckily the Greek range is limited and clear.
 %    \begin{macrocode}
 \prg_new_conditional:Npnn \@@_change_case_if_greek:n #1 { TF }
   {
+    \exp_args:Nf \@@_change_case_if_greek:n
+      { \int_eval:n { \@@_codepoint_from_chars:Nw #1 } }
+  }
+\cs_new:Npn \@@_change_case_if_greek:n #1
+  {
     \if_int_compare:w #1 < "0370 \exp_stop_f:
       \prg_return_false:
     \else:
@@ -1207,6 +1177,11 @@
 %    \begin{macrocode}
 \prg_new_conditional:Npnn \@@_change_case_if_greek_accent:n #1 { TF , p }
   {
+    \exp_args:Nf \@@_change_case_if_greek_accent:n
+      { \int_eval:n { \@@_codepoint_from_chars:Nw #1 } }
+  }
+\cs_new:Npn \@@_change_case_if_greek_accent:n #1
+  {
     \if_int_compare:w #1 = "0300 \exp_stop_f:
       \prg_return_true:
     \else:
@@ -1236,6 +1211,11 @@
 \prg_new_conditional:Npnn \@@_change_case_if_greek_diacritic:n
   #1 { TF , p }
   {
+    \exp_args:Nf \@@_change_case_if_greek_diacritic:n
+      { \int_eval:n { \@@_codepoint_from_chars:Nw #1 } }
+  }
+\cs_new:Npn \@@_change_case_if_greek_diacritic:n #1
+  {
     \if_int_compare:w #1 = "0304 \exp_stop_f:
       \prg_return_true:
     \else:
@@ -1260,6 +1240,11 @@
   }
 \prg_new_conditional:Npnn \@@_change_case_if_takes_dialytika:n #1 { TF }
   {
+    \exp_args:Nf \@@_change_case_if_takes_dialytika:n
+      { \int_eval:n { \@@_codepoint_from_chars:Nw #1 } }
+  }
+\cs_new:Npn \@@_change_case_if_takes_dialytika:n #1
+  {
     \if_int_compare:w #1 = "0399 \exp_stop_f:
       \prg_return_true:
     \else:
@@ -1280,6 +1265,11 @@
   }
 \prg_new_conditional:Npnn \@@_change_case_if_takes_ypogegrammeni:n #1 { TF }
   {
+    \exp_args:Nf \@@_change_case_if_takes_ypogegrammeni:n
+      { \int_eval:n { \@@_codepoint_from_chars:Nw #1 } }
+  }
+\cs_new:Npn \@@_change_case_if_takes_ypogegrammeni:n #1
+  {
     \if_int_compare:w #1 = "03B1 \exp_stop_f:
       \prg_return_true:
     \else:
@@ -1317,60 +1307,79 @@
 % \end{macro}
 % \end{macro}
 % \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
 % \begin{macro}[EXP]{\@@_change_case_boundary_upper_el:Nnnw}
 % \begin{macro}[EXP]{\@@_change_case_boundary_upper_el:nnN}
-% \begin{macro}[EXP]{\@@_change_case_boundary_upper_el:nnNw}
-% \begin{macro}[EXP]{\@@_change_case_boundary_upper_el:NnnN}
+% \begin{macro}[EXP]{\@@_change_case_boundary_upper_el:nnn}
+% \begin{macro}[EXP]{\@@_change_case_boundary_upper_el:nnnw}
+% \begin{macro}[EXP]{\@@_change_case_boundary_upper_el:nnnN}
 %   There is one special case in Greek that needs to be picked up based on
 %   being an isolated letter. We do that using a test similar to final sigma,
 %   but it has to fire off from the space grabber.
 %    \begin{macrocode}
-\bool_lazy_or:nnT
-  { \sys_if_engine_luatex_p: }
-  { \sys_if_engine_xetex_p: }
+\cs_new:Npn \@@_change_case_boundary_upper_el:Nnnw
+  #1#2#3#4 \q_@@_recursion_stop
   {
-    \cs_new:Npn \@@_change_case_boundary_upper_el:Nnnw
-      #1#2#3#4 \q_@@_recursion_stop
+    \tl_if_head_is_N_type:nTF {#4}
+      { \@@_change_case_boundary_upper_el:nnN }
+      { \@@_change_case_loop:nnw }
+        {#2} {#3} #4 \q_@@_recursion_stop
+  }
+\cs_new:Npn \@@_change_case_boundary_upper_el:nnN #1#2#3
+  {
+    \token_if_cs:NTF #3
+      { \@@_change_case_loop:nnw {#1} {#2} }
       {
-        \tl_if_head_is_N_type:nTF {#4}
-          { \@@_change_case_boundary_upper_el:nnN }
-          { \@@_change_case_loop:nnw }
-            {#2} {#3} #4 \q_@@_recursion_stop
+        \@@_codepoint_process:nN
+          { \@@_change_case_boundary_upper_el:nnn {#1} {#2} }
       }
-    \cs_new:Npn \@@_change_case_boundary_upper_el:nnN #1#2#3
+        #3
+  }
+\cs_new:Npn \@@_change_case_boundary_upper_el:nnn #1#2#3
+  {
+    \bool_lazy_or:nnTF
+      { \@@_codepoint_compare_p:nNn {#3} = { "03AE } }
+      { \@@_codepoint_compare_p:nNn {#3} = { "1F22 } }
+      { \@@_change_case_boundary_upper_el:nnnw {#1} {#2} {#3} }
+      { \@@_change_case_loop:nnw {#1} {#2} #3 }
+  }
+\cs_new:Npn \@@_change_case_boundary_upper_el:nnnw
+  #1#2#3#4 \q_@@_recursion_stop
+  {
+    \tl_if_head_is_N_type:nTF {#4}
+      { \@@_change_case_boundary_upper_el:nnnN {#3} }
       {
-        \bool_lazy_or:nnTF
-          { \token_if_cs_p:N #3 }
+        \@@_change_case_store:e
           {
-            ! \bool_lazy_or_p:nn
-              { \int_compare_p:nNn { `#3 } = { "03AE } }
-              { \int_compare_p:nNn { `#3 } = { "1F22 } }
+            \codepoint_generate:nn { "0389 }
+              { \@@_change_case_catcode:nn {#3} { "0389 } }
           }
-          { \@@_change_case_loop:nnw }
-          { \@@_change_case_boundary_upper_el:nnNw }
-            {#1} {#2} #3
+        \@@_change_case_loop:nnw
       }
-    \cs_new:Npn \@@_change_case_boundary_upper_el:nnNw
-      #1#2#3#4 \q_@@_recursion_stop
-       {
-        \tl_if_head_is_N_type:nTF {#4}
-          { \@@_change_case_boundary_upper_el:NnnN #3 }
-          {
-            \@@_change_case_store:e
-              { \char_generate:nn { "0389 } { \@@_char_catcode:N #3 } }
-            \@@_change_case_loop:nnw
-          }
-            {#1} {#2} #4 \q_@@_recursion_stop
+        {#1} {#2} #4 \q_@@_recursion_stop
+  }
+\cs_new:Npn \@@_change_case_boundary_upper_el:nnnN #1#2#3#4
+  {
+    \MARJ
+    \bool_lazy_or:nnTF
+      { \token_if_letter_p:N #4 }
+      {
+        \bool_lazy_and_p:nn
+          { \token_if_active_p:N #4 }
+          { ! \int_compare_p:nNn {`#4} < { "80 } }
       }
-    \cs_new:Npn \@@_change_case_boundary_upper_el:NnnN #1#2#3#4
+      { \@@_change_case_loop:nnw {#2} {#3} #1#4 }
       {
-        \token_if_letter:NTF #4
-          { \@@_change_case_loop:nnw {#2} {#3} #1#4 }
+        \@@_change_case_store:e
           {
-            \@@_change_case_store:e
-              { \char_generate:nn { "0389 } { \@@_char_catcode:N #1 } }
-            \@@_change_case_loop:nnw {#2} {#3} #4
+            \codepoint_generate:nn { "0389 }
+              { \@@_change_case_catcode:nn {#1} { "0389 } }
           }
+        \@@_change_case_loop:nnw {#2} {#3} #4
       }
   }
 %    \end{macrocode}
@@ -1378,98 +1387,89 @@
 % \end{macro}
 % \end{macro}
 % \end{macro}
-% \begin{macro}[EXP]{\@@_change_case_title_el:nnnN}
+% \end{macro}
+% \begin{macro}[EXP]{\@@_change_case_title_el:nnnn}
 %   Titlecasing retains accents, but to prevent the uppercasing code
 %   from kicking in, there has to be an explicit function here.
 %    \begin{macrocode}
-\bool_lazy_or:nnT
-  { \sys_if_engine_luatex_p: }
-  { \sys_if_engine_xetex_p: }
-  {
-    \cs_new:Npn \@@_change_case_title_el:nnnN #1#2#3#4
-      { \@@_change_case_char:nnnN {#1} {#2} {#3} #4 }
-  }
+\cs_new:Npn \@@_change_case_title_el:nnnn #1#2#3#4
+  { \@@_change_case_codepoint:nnnn {#1} {#2} {#3} {#4} }
 %    \end{macrocode}
 % \end{macro}
 %
 % \begin{macro}[EXP]
 %   {
-%     \@@_change_case_upper_hy:nnnN        ,
-%     \@@_change_case_title_hy:nnNN        ,
-%     \@@_change_case_upper_hy-x-yiwn:nnnN ,
-%     \@@_change_case_title_hy-x-yiwn:nnNN
+%     \@@_change_case_upper_hy:nnnn        ,
+%     \@@_change_case_title_hy:nnnn        ,
+%     \@@_change_case_upper_hy-x-yiwn:nnnn ,
+%     \@@_change_case_title_hy-x-yiwn:nnnn
 %   }
 %     See \url{https://www.unicode.org/L2/L2020/20143-armenian-ech-yiwn.pdf}.
 %    \begin{macrocode}
-\bool_lazy_or:nnT
-  { \sys_if_engine_luatex_p: }
-  { \sys_if_engine_xetex_p: }
+\cs_new:Npn \@@_change_case_upper_hy:nnnn #1#2#3#4
   {
-    \cs_new:cpn { @@_change_case_upper_hy:nnnN } #1#2#3#4
+    \@@_codepoint_compare:nNnTF {#4} = { "0587 }
       {
-        \int_compare:nNnTF { `#4 } = { "0587 }
+        \@@_change_case_store:e
           {
-            \@@_change_case_store:e
-              {
-                \char_generate:nn { "0535 } { \@@_char_catcode:N #4 }
-                \char_generate:nn { "054E } { \@@_char_catcode:N #4 }
-              }
-            \use:c { @@_change_case_char_next_ #2 :nn }
-              {#2} {#3}
+            \codepoint_generate:nn { "0535 }
+              { \@@_change_case_catcode:nn {#4} { "0535 } }
+            \codepoint_generate:nn { "054E }
+              { \@@_change_case_catcode:nn {#4} { "054E } }
           }
-          { \@@_change_case_char:nnnN {#1} {#2} {#3} #4 }
+        \use:c { @@_change_case_next_ #2 :nn }
+          {#2} {#3}
       }
-    \cs_new:cpn { @@_change_case_title_hy:nnnN } #1#2#3#4
+      { \@@_change_case_codepoint:nnnn {#1} {#2} {#3} {#4} }
+  }
+\cs_new:Npn \@@_change_case_title_hy:nnnn #1#2#3#4
+  {
+    \@@_codepoint_compare:nNnTF {#4} = { "0587 }
       {
-        \int_compare:nNnTF { `#4 } = { "0587 }
+        \@@_change_case_store:e
           {
-            \@@_change_case_store:e
-              {
-                \char_generate:nn { "0535 } { \@@_char_catcode:N #4 }
-                \char_generate:nn { "057E } { \@@_char_catcode:N #4 }
-              }
-            \use:c { @@_change_case_char_next_ #2 :nn }
-              {#2} {#3}
+            \codepoint_generate:nn { "0535 }
+              { \@@_change_case_catcode:nn {#4} { "0535 } }
+            \codepoint_generate:nn { "057E }
+              { \@@_change_case_catcode:nn {#4} { "057E } }
           }
-          { \@@_change_case_char:nnnN {#1} {#2} {#3} #4 }
+        \use:c { @@_change_case_next_ #2 :nn }
+          {#2} {#3}
       }
-    \cs_new:cpn { @@_change_case_upper_hy-x-yiwn:nnnN } #1#2#3#4
-      { \@@_change_case_char:nnnN {#1} {#2} {#3} #4 }
-    \cs_new_eq:cc { @@_change_case_title_hy-x-yiwn:nnnN }
-      { @@_change_case_upper_hy-x-yiwn:nnnN }
+      { \@@_change_case_codepoint:nnnn {#1} {#2} {#3} {#4} }
   }
+\cs_new:cpn { @@_change_case_upper_hy-x-yiwn:nnnn } #1#2#3#4
+  { \@@_change_case_codepoint:nnnn {#1} {#2} {#3} {#4} }
+\cs_new_eq:cc { @@_change_case_title_hy-x-yiwn:nnnn }
+  { @@_change_case_upper_hy-x-yiwn:nnnn }
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[EXP]{\@@_change_case_lower_la-x-medieval:nnnN}
-% \begin{macro}[EXP]{\@@_change_case_upper_la-x-medieval:nnnN}
+% \begin{macro}[EXP]{\@@_change_case_lower_la-x-medieval:nnnn}
+% \begin{macro}[EXP]{\@@_change_case_upper_la-x-medieval:nnnn}
 %   Simply swaps of characters.
 %    \begin{macrocode}
-\cs_new:cpn { @@_change_case_lower_la-x-medieval:nnnN } #1#2#3#4
+\cs_new:cpn { @@_change_case_lower_la-x-medieval:nnnn } #1#2#3#4
   {
-    \int_compare:nNnTF { `#4 } = { `V }
+    \@@_codepoint_compare:nNnTF {#4} = { "0056 }
       {
         \@@_change_case_store:e
-          {
-            \char_generate:nn { `u } { \@@_char_catcode:N #4 }
-          }
-        \use:c { @@_change_case_char_next_ #2 :nn }
+          { \char_generate:nn { "0075 } { \@@_char_catcode:N #4 } }
+        \use:c { @@_change_case_next_ #2 :nn }
           {#2} {#3}
       }
-      { \@@_change_case_char:nnnN {#1} {#2} {#3} #4 }
+      { \@@_change_case_codepoint:nnnn {#1} {#2} {#3} {#4} }
   }
-\cs_new:cpn { @@_change_case_upper_la-x-medieval:nnnN } #1#2#3#4
+\cs_new:cpn { @@_change_case_upper_la-x-medieval:nnnn } #1#2#3#4
   {
-    \int_compare:nNnTF { `#4 } = { `u }
+    \@@_codepoint_compare:nNnTF {#4} = { "0075 }
       {
         \@@_change_case_store:e
-          {
-            \char_generate:nn { `V } { \@@_char_catcode:N #4 }
-          }
-        \use:c { @@_change_case_char_next_ #2 :nn }
+          { \char_generate:nn { "0056 } { \@@_char_catcode:N #4 } }
+        \use:c { @@_change_case_next_ #2 :nn }
           {#2} {#3}
       }
-      { \@@_change_case_char:nnnN {#1} {#2} {#3} #4 }
+      { \@@_change_case_codepoint:nnnn {#1} {#2} {#3} {#4} }
   }
 %    \end{macrocode}
 % \end{macro}
@@ -1477,168 +1477,193 @@
 %
 % \begin{macro}[EXP]
 %   {
-%     \@@_change_cases_lower_lt:nnnN      ,
-%     \@@_change_cases_lower_lt_auxi:nnnN ,
-%     \@@_change_cases_lower_lt_auxii:nnnN
+%     \@@_change_cases_lower_lt:nnnn      ,
+%     \@@_change_cases_lower_lt_auxi:nnnn ,
+%     \@@_change_cases_lower_lt_auxii:nnnn
 %   }
 % \begin{macro}[rEXP]{\@@_change_case_lower_lt:nnw}
 % \begin{macro}[rEXP]{\@@_change_case_lower_lt:nnN}
+% \begin{macro}[rEXP]{\@@_change_case_lower_lt:nnn}
 %   For  Lithuanian, the issue to be dealt with is dots over lower case
 %   letters: these should be present if there is another accent. The first step
 %   is a simple match attempt: look for the three uppercase accented letters
 %   which should gain a dot-above char in their lowercase form.
 %    \begin{macrocode}
-\bool_lazy_or:nnT
-  { \sys_if_engine_luatex_p: }
-  { \sys_if_engine_xetex_p: }
+\cs_new:Npn \@@_change_case_lower_lt:nnnn #1#2#3#4
   {
-   \cs_new:Npn \@@_change_case_lower_lt:nnnN #1#2#3#4
-     {
-        \exp_args:Ne \@@_change_case_lower_lt_auxi:nnnN
+    \exp_args:Ne \@@_change_case_lower_lt_auxi:nnnn
+      {
+        \int_case:nn { \@@_codepoint_from_chars:Nw #4 }
           {
-            \int_case:nn { `#4 }
-              {
-                { "00CC } { "0300 }
-                { "00CD } { "0301 }
-                { "0128 } { "0303 }
-              }  
-          }
-            {#2} {#3} #4
+            { "00CC } { "0300 }
+            { "00CD } { "0301 }
+            { "0128 } { "0303 }
+          }  
       }
+        {#2} {#3} {#4}
+  }
 %    \end{macrocode}
 %   If there was a hit, output the result with the dot-above and move on.
 %   Otherwise, look for one of the three letters that can take a combining
 %   accent: I, J and I-ogonek. 
 %    \begin{macrocode}
-    \cs_new:Npn \@@_change_case_lower_lt_auxi:nnnN #1#2#3#4
+\cs_new:Npn \@@_change_case_lower_lt_auxi:nnnn #1#2#3#4
+  {
+    \tl_if_blank:nTF {#1}
       {
-        \tl_if_blank:nTF {#1}
+        \exp_args:Ne \@@_change_case_lower_lt_auxii:nnnn
           {
-            \exp_args:Ne \@@_change_case_lower_lt_auxii:nnnN
+            \int_case:nn { \@@_codepoint_from_chars:Nw #4 }
               {
-                \int_case:nn { `#4 }
-                  {
-                    { "0049 } { "0069 }
-                    { "004A } { "006A }
-                    { "012E } { "012F }
-                  }  
-              }
-              {#2} {#3} #4
+                { "0049 } { "0069 }
+                { "004A } { "006A }
+                { "012E } { "012F }
+              }  
           }
+            {#2} {#3} {#4}
+      }
+      {
+        \@@_change_case_store:e
           {
-            \@@_change_case_store:e
-              {
-                \char_generate:nn { "0069 } { \@@_char_catcode:N #4 }
-                \char_generate:nn { "0307 } { \@@_char_catcode:N #4 }
-                \char_generate:nn {#1} { \@@_char_catcode:N #4 }
-              }
-            \@@_change_case_loop:nnw {#2} {#3}
+            \codepoint_generate:nn { "0069 }
+              { \@@_change_case_catcode:nn {#4} { "0069 } }
+            \codepoint_generate:nn { "0307 }
+              { \@@_change_case_catcode:nn {#4} { "0307 } }
+            \codepoint_generate:nn {#1}
+              { \@@_change_case_catcode:nn {#4} {#1} }
           }
+        \@@_change_case_loop:nnw {#2} {#3}
       }
+  }
 %    \end{macrocode}
 %   Again, branch depending on a hit. If there is one, we output the character
 %   then need to look for a combining accent: as usual, we need to be aware of
 %   the loop situation.
 %    \begin{macrocode}
-    \cs_new:Npn \@@_change_case_lower_lt_auxii:nnnN #1#2#3#4
+\cs_new:Npn \@@_change_case_lower_lt_auxii:nnnn #1#2#3#4
+  {
+    \tl_if_blank:nTF {#1}
+      { \@@_change_case_codepoint:nnnn {#2} {#2} {#3} {#4} }
       {
-        \tl_if_blank:nTF {#1}
-          { \@@_change_case_lower_sigma:nnnN {#2} {#2} {#3} #4 }
+        \@@_change_case_store:e
           {
-            \@@_change_case_store:e
-              { \char_generate:nn {#1} { \@@_char_catcode:N #4 } }
-            \@@_change_case_lower_lt:nnw {#2} {#3}
+            \codepoint_generate:nn {#1}
+              { \@@_change_case_catcode:nn {#4} {#1} }
           }
+        \@@_change_case_lower_lt:nnw {#2} {#3}
       }
-    \cs_new:Npn \@@_change_case_lower_lt:nnw #1#2#3 \q_@@_recursion_stop
+  }
+\cs_new:Npn \@@_change_case_lower_lt:nnw #1#2#3 \q_@@_recursion_stop
+  {
+    \tl_if_head_is_N_type:nTF {#3}
+      { \@@_change_case_lower_lt:nnN }
+      { \@@_change_case_loop:nnw }
+       {#1} {#2} #3 \q_@@_recursion_stop
+  }
+\cs_new:Npn \@@_change_case_lower_lt:nnN #1#2#3
+  {
+    \@@_codepoint_process:nN
+      { \@@_change_case_lower_lt:nnn {#1} {#2} } #3
+  }
+\cs_new:Npn \@@_change_case_lower_lt:nnn #1#2#3
+  {
+    \bool_lazy_and:nnT
       {
-        \tl_if_head_is_N_type:nTF {#3}
-          { \@@_change_case_lower_lt:nnN }
-          { \@@_change_case_loop:nnw }
-           {#1} {#2} #3 \q_@@_recursion_stop
+        \bool_lazy_or_p:nn
+          { ! \tl_if_single_p:n {#3} }
+          { ! \token_if_cs_p:N #3 }
       }
-    \cs_new:Npn \@@_change_case_lower_lt:nnN #1#2#3
       {
-        \bool_lazy_and:nnT
-          { ! \token_if_cs_p:N #3 }
+        \bool_lazy_any_p:n
           {
-            \bool_lazy_any_p:n
-              {
-                { \int_compare_p:nNn { `#3 } = { "0300 } }
-                { \int_compare_p:nNn { `#3 } = { "0301 } }
-                { \int_compare_p:nNn { `#3 } = { "0303 } }
-              }
+            { \@@_codepoint_compare_p:nNn {#3} = { "0300 } }
+            { \@@_codepoint_compare_p:nNn {#3} = { "0301 } }
+            { \@@_codepoint_compare_p:nNn {#3} = { "0303 } }
           }
+      }
+      {
+        \@@_change_case_store:e
           {
-            \@@_change_case_store:e
-              { \char_generate:nn { "0307 } { \@@_char_catcode:N #3 } }
+            \codepoint_generate:nn { "0307 }
+              { \@@_change_case_catcode:nn {#3} { "0307 } }
           }
-        \@@_change_case_loop:nnw {#1} {#2} #3
       }
+    \@@_change_case_loop:nnw {#1} {#2} #3
   }
 %    \end{macrocode}
 % \end{macro}
 % \end{macro}
 % \end{macro}
+% \end{macro}
 % \begin{macro}[EXP]
 %   {
-%     \@@_change_cases_upper_lt:nnnN     ,
-%     \@@_change_cases_upper_lt_aux:nnnN
+%     \@@_change_cases_upper_lt:nnnn     ,
+%     \@@_change_cases_upper_lt_aux:nnnn
 %   }
 % \begin{macro}[rEXP]{\@@_change_case_upper_lt:nnw}
 % \begin{macro}[rEXP]{\@@_change_case_upper_lt:nnN}
+% \begin{macro}[rEXP]{\@@_change_case_upper_lt:nnn}
 %   The uppercasing version: first find i/j/i-ogonek, then look for the
 %   combining char: drop it if present.
 %    \begin{macrocode}
-\bool_lazy_or:nnT
-  { \sys_if_engine_luatex_p: }
-  { \sys_if_engine_xetex_p: }
+\cs_new:Npn \@@_change_case_upper_lt:nnnn #1#2#3#4
+ {
+    \exp_args:Ne \@@_change_case_upper_lt_aux:nnnn
+      {
+        \int_case:nn { \@@_codepoint_from_chars:Nw #4 }
+          {
+            { "0069 } { "0049 }
+            { "006A } { "004A }
+            { "012F } { "012E }
+          }  
+      }
+        {#2} {#3} {#4}
+  }
+\cs_new:Npn \@@_change_case_upper_lt_aux:nnnn #1#2#3#4
   {
-   \cs_new:Npn \@@_change_case_upper_lt:nnnN #1#2#3#4
-     {
-        \exp_args:Ne \@@_change_case_upper_lt_aux:nnnN
+    \tl_if_blank:nTF {#1}
+      { \@@_change_case_codepoint:nnnn { upper } {#2} {#3} {#4} }
+      {
+        \@@_change_case_store:e
           {
-            \int_case:nn { `#4 }
-              {
-                { "0069 } { "0049 }
-                { "006A } { "004A }
-                { "012F } { "012E }
-              }  
+            \codepoint_generate:nn {#1}
+              { \@@_change_case_catcode:nn {#4} {#1} }
           }
-            {#2} {#3} #4
+        \@@_change_case_upper_lt:nnw {#2} {#3}
       }
-   \cs_new:Npn \@@_change_case_upper_lt_aux:nnnN #1#2#3#4
-     {
-       \tl_if_blank:nTF {#1}
-         { \@@_change_case_char:nnnN { upper } {#2} {#3} #4 }
-         {
-           \@@_change_case_store:e
-             { \char_generate:nn {#1} { \@@_char_catcode:N #4 } }
-           \@@_change_case_upper_lt:nnw {#2} {#3}
-         }
-     }
-    \cs_new:Npn \@@_change_case_upper_lt:nnw #1#2#3 \q_@@_recursion_stop
+  }
+\cs_new:Npn \@@_change_case_upper_lt:nnw #1#2#3 \q_@@_recursion_stop
+  {
+    \tl_if_head_is_N_type:nTF {#3}
+      { \@@_change_case_upper_lt:nnN }
+      { \use:c { @@_change_case_next_ #1 :nn } }
+        {#1} {#2} #3 \q_@@_recursion_stop
+  }
+\cs_new:Npn \@@_change_case_upper_lt:nnN #1#2#3
+  {
+    \@@_codepoint_process:nN
+      { \@@_change_case_upper_lt:nnn {#1} {#2} } #3
+  }
+\cs_new:Npn \@@_change_case_upper_lt:nnn #1#2#3
+  {
+    \bool_lazy_and:nnTF
       {
-        \tl_if_head_is_N_type:nTF {#3}
-          { \@@_change_case_upper_lt:nnN }
-          { \use:c { @@_change_case_char_next_ #1 :nn } }
-            {#1} {#2} #3 \q_@@_recursion_stop
-      }
-    \cs_new:Npn \@@_change_case_upper_lt:nnN #1#2#3
-      {
-        \bool_lazy_and:nnTF
+        \bool_lazy_or_p:nn
+          { ! \tl_if_single_p:n {#3} }
           { ! \token_if_cs_p:N #3 }
-          { \int_compare_p:nNn { `#3 } = { "0307 } }
-          { \use:c { @@_change_case_char_next_ #1 :nn } {#1} {#2} }
-          { \use:c { @@_change_case_char_next_ #1 :nn } {#1} {#2} #3 }
       }
+      { \@@_codepoint_compare_p:nNn {#3} = { "0307 } }
+      { \use:c { @@_change_case_next_ #1 :nn } {#1} {#2} }
+      { \use:c { @@_change_case_next_ #1 :nn } {#1} {#2} #3 }
   }
 %    \end{macrocode}
 % \end{macro}
 % \end{macro}
 % \end{macro}
+% \end{macro}
 %
-% \begin{macro}[EXP]{\@@_change_case_title_nl:nnnN}
+% \begin{macro}[EXP]
+%   {\@@_change_case_title_nl:nnnn, \@@_change_case_title_nl_aux:nnnn}
 % \begin{macro}[EXP]{\@@_change_case_title_nl:nnw}
 % \begin{macro}[EXP]{\@@_change_case_title_nl:nnN}
 %   For Dutch, there is a single look-ahead test for \texttt{ij} when
@@ -1645,23 +1670,30 @@
 %   title casing. If the appropriate letters are found, produce \texttt{IJ}
 %   and gobble the \texttt{j}/\texttt{J}.
 %    \begin{macrocode}
-\cs_new:Npn \@@_change_case_title_nl:nnnN #1#2#3#4
+\cs_new:Npn \@@_change_case_title_nl:nnnn #1#2#3#4
   {
+    \tl_if_single:nTF {#4}
+      { \@@_change_case_title_nl_aux:nnnn }
+      { \@@_change_case_codepoint:nnnn }
+        {#1} {#2} {#3} {#4} 
+  }
+\cs_new:Npn \@@_change_case_title_nl_aux:nnnn #1#2#3#4
+  {
     \bool_lazy_or:nnTF
-      { \int_compare_p:nNn { `#4 } = { "0049 } }
-      { \int_compare_p:nNn { `#4 } = { "0069 } }
+      { \int_compare_p:nNn {`#4} = { "0049 } }
+      { \int_compare_p:nNn {`#4} = { "0069 } }
       {
         \@@_change_case_store:e
           { \char_generate:nn { "0049 } { \@@_char_catcode:N #4 } }
         \@@_change_case_title_nl:nnw {#2} {#3}
       }
-      { \@@_change_case_char:nnnN {#1} {#2} {#3} #4 }
+      { \@@_change_case_codepoint:nnnn {#1} {#2} {#3} {#4} }
   }
 \cs_new:Npn \@@_change_case_title_nl:nnw #1#2#3 \q_@@_recursion_stop
   {
     \tl_if_head_is_N_type:nTF {#3}
       { \@@_change_case_title_nl:nnN }
-      { \use:c { @@_change_case_char_next_ #1 :nn } }
+      { \use:c { @@_change_case_next_ #1 :nn } }
         {#1} {#2} #3 \q_@@_recursion_stop
   }
 \cs_new:Npn \@@_change_case_title_nl:nnN #1#2#3
@@ -1670,15 +1702,15 @@
       { ! \token_if_cs_p:N #3 }
       {
         \bool_lazy_or_p:nn
-          { \int_compare_p:nNn { `#3 } = { "004A } }
-          { \int_compare_p:nNn { `#3 } = { "006A } }
+          { \int_compare_p:nNn {`#3} = { "004A } }
+          { \int_compare_p:nNn {`#3} = { "006A } }
       }
       {
         \@@_change_case_store:e
           { \char_generate:nn { "004A } { \@@_char_catcode:N #3 } }
-        \use:c { @@_change_case_char_next_ #1 :nn } {#1} {#2}
+        \use:c { @@_change_case_next_ #1 :nn } {#1} {#2}
       }
-      { \use:c { @@_change_case_char_next_ #1 :nn } {#1} {#2} #3 }
+      { \use:c { @@_change_case_next_ #1 :nn } {#1} {#2} #3 }
   }
 %    \end{macrocode}
 % \end{macro}
@@ -1685,33 +1717,32 @@
 % \end{macro}
 % \end{macro}
 %
-% \begin{macro}[EXP]{\@@_change_case_lower_tr:nnnN}
+% \begin{macro}[EXP]{\@@_change_case_lower_tr:nnnn}
 % \begin{macro}[EXP]{\@@_change_case_lower_tr:nnNw}
 % \begin{macro}[EXP]{\@@_change_case_lower_tr:NnnN}
-% \begin{macro}[EXP]{\@@_change_case_lower_tr:nnnNN}
+% \begin{macro}[EXP]{\@@_change_case_lower_tr:Nnnn}
 %   The Turkic languages need special treatment for dotted-i and dotless-i.
 %   The lower casing rule can be expressed in terms of searching first for
 %   either a dotless-I or a dotted-I. In the latter case the mapping is
 %   easy, but in the former there is a second stage search.
 %    \begin{macrocode}
-\bool_lazy_or:nnTF
-  { \sys_if_engine_luatex_p: }
-  { \sys_if_engine_xetex_p: }
+\cs_new:Npn \@@_change_case_lower_tr:nnnn #1#2#3#4
   {
-    \cs_new:Npn \@@_change_case_lower_tr:nnnN #1#2#3#4
+    \@@_codepoint_compare:nNnTF {#4} = { "0049 }
+      { \@@_change_case_lower_tr:nnNw {#1} {#3} #4 }
       {
-        \int_compare:nNnTF { `#4 } = { "0049 }
-          { \@@_change_case_lower_tr:nnNw {#1} {#3} #4 }
+        \@@_codepoint_compare:nNnTF {#4} = { "0130 }
           {
-            \int_compare:nNnTF { `#4 } = { "0130 }
+            \@@_change_case_store:e
               {
-                \@@_change_case_store:e
-                  { \char_generate:nn { "0069 } { \@@_char_catcode:N #4 } }
-                \@@_change_case_loop:nnw {#1} {#3}
+                \codepoint_generate:nn { "0069 }
+                  { \@@_change_case_catcode:nn {#4} { "0069 } }
               }
-              { \@@_change_case_lower_sigma:nnnN {#1} {#2} {#3} #4 }
+            \@@_change_case_loop:nnw {#1} {#3}
           }
+          { \@@_change_case_codepoint:nnnn {#1} {#2} {#3} {#4} }
       }
+  }
 %    \end{macrocode}
 %   After a dotless-I there may be a dot-above character. If there is then
 %   a dotted-i should be produced, otherwise output a dotless-i. When the
@@ -1718,70 +1749,49 @@
 %   combination is found both the dotless-I and the dot-above char have to
 %   be removed from the input.
 %    \begin{macrocode}
-    \cs_new:Npn \@@_change_case_lower_tr:nnNw #1#2#3#4 \q_@@_recursion_stop
+\cs_new:Npn \@@_change_case_lower_tr:nnNw #1#2#3#4 \q_@@_recursion_stop
+  {
+    \tl_if_head_is_N_type:nTF {#4}
+      { \@@_change_case_lower_tr:NnnN  #3 {#1} {#2} }
       {
-        \tl_if_head_is_N_type:nTF {#4}
-          { \@@_change_case_lower_tr:NnnN #3 }
+        \@@_change_case_store:e
           {
-            \@@_change_case_store:e
-              { \char_generate:nn { "0131 } { \@@_char_catcode:N #3 } }
-            \@@_change_case_loop:nnw
+            \codepoint_generate:nn { "0131 }
+              { \@@_change_case_catcode:nn {#3} { "0131 } }
           }
-            {#1} {#2} #4 \q_@@_recursion_stop
+        \@@_change_case_loop:nnw {#1} {#2}
       }
-    \cs_new:Npn \@@_change_case_lower_tr:NnnN #1#2#3#4
+        #4 \q_@@_recursion_stop
+  }
+\cs_new:Npn \@@_change_case_lower_tr:NnnN #1#2#3#4
+  {
+    \@@_codepoint_process:nN
+      { \@@_change_case_lower_tr:Nnnn #1 {#2} {#3} } #4
+  }
+\cs_new:Npn \@@_change_case_lower_tr:Nnnn #1#2#3#4
+  {
+    \bool_lazy_or:nnTF
       {
-        \bool_lazy_or:nnTF
+        \bool_lazy_and_p:nn
+          { \tl_if_single_p:n {#4} }
           { \token_if_cs_p:N #4 }
-          { ! \int_compare_p:nNn { `#4 } = { "0307 } }
-          {
-            \@@_change_case_store:e 
-              { \char_generate:nn { "0131 } { \@@_char_catcode:N #1 } }
-            \@@_change_case_loop:nnw {#2} {#3} #4
-          }
-          {
-            \@@_change_case_store:e
-              { \char_generate:nn { "0069 } { \@@_char_catcode:N #1 } }
-            \@@_change_case_loop:nnw {#2} {#3}
-          }
       }
-  }
-%    \end{macrocode}
-%   For $8$-bit engines, dot-above is not available so there is a simple
-%   test for an upper-case I. Then we can look for the UTF-8 representation of
-%   an upper case dotted-I without the combining char. If it's not there,
-%   preserve the UTF-8 sequence as-is. With $8$bit engines, we cannot
-%   completely preserve category codes, so we have to make some assumptions:
-%   output a \enquote{normal} \texttt{i} for the dotted case. As the original
-%   character here is catcode-$13$, we have to make a choice about handling
-%   of |i|: generate a \enquote{normal} one.
-%    \begin{macrocode}
-  {
-    \cs_new:Npn \@@_change_case_lower_tr:nnnN #1#2#3#4
+      { ! \@@_codepoint_compare_p:nNn {#4} = { "0307 } }
       {
-        \int_compare:nNnTF { `#4 } = { "0049 }
+        \@@_change_case_store:e 
           {
-            \@@_change_case_store:V \c_@@_dotless_i_tl
-            \@@_change_case_loop:nnw {#1} {#3}
+            \codepoint_generate:nn { "0131 }
+              { \@@_change_case_catcode:nn {#1} { "0131 } }
           }
-          {
-            \int_compare:nNnTF { `#4 } = { "00C4 }
-              { \@@_change_case_lower_tr:nnnNN {#1} {#2} {#3} #4 }
-              { \@@_change_case_char:nnnN {#1} {#2} {#3} #4 }
-          }
+        \@@_change_case_loop:nnw {#2} {#3} #4
       }
-    \cs_new:Npn \@@_change_case_lower_tr:nnnNN #1#2#3#4#5
       {
-        \int_compare:nNnTF { `#5 } = { "00B0 }
+        \@@_change_case_store:e
           {
-            \@@_change_case_store:e
-              {
-                \char_generate:nn { "0069 }
-                  { \char_value_catcode:n { "0069 } }
-              }
-            \@@_change_case_loop:nnw {#1} {#3}
+            \codepoint_generate:nn { "0069 }
+              { \@@_change_case_catcode:nn {#1} { "0069 } }
           }
-          { \@@_change_case_char:nnnN {#1} {#2} {#3} #4#5 }
+        \@@_change_case_loop:nnw {#2} {#3}
       }
   }
 %    \end{macrocode}
@@ -1789,1055 +1799,36 @@
 % \end{macro}
 % \end{macro}
 % \end{macro}
-% \begin{macro}[EXP]{\@@_change_case_upper_tr:nnnN}
+% \begin{macro}[EXP]{\@@_change_case_upper_tr:nnnn}
 %   Uppercasing is easier: just one exception with no context.
 %    \begin{macrocode}
-\cs_new:Npx \@@_change_case_upper_tr:nnnN #1#2#3#4
+\cs_new:Npn \@@_change_case_upper_tr:nnnn #1#2#3#4
   {
-    \exp_not:N \int_compare:nNnTF { `#4 } = { "0069 }
+    \@@_codepoint_compare:nNnTF {#4} = { "0069 }
       {
-        \bool_lazy_or:nnTF
-          { \sys_if_engine_luatex_p: }
-          { \sys_if_engine_xetex_p: }
+        \@@_change_case_store:e
           {
-            \exp_not:N \@@_change_case_store:e
-              {
-                \exp_not:N \char_generate:nn { "0130 }
-                  { \exp_not:N \@@_char_catcode:N #4 }
-              }
+            \codepoint_generate:nn { "0130 }
+              { \@@_change_case_catcode:nn {#4} { "0130 } }
           }
-          {
-            \exp_not:N \@@_change_case_store:V
-            \exp_not:N \c_@@_dotted_I_tl
-          }
-        \exp_not:N \use:c { @@_change_case_char_next_ #2 :nn } {#2} {#3}
+        \use:c { @@_change_case_next_ #2 :nn } {#2} {#3}
       }
-      { \exp_not:N \@@_change_case_char:nnnN {#1} {#2} {#3} #4 }
+      { \@@_change_case_codepoint:nnnn {#1} {#2} {#3} {#4} }
   }
 %    \end{macrocode}
 % \end{macro}
 %
 % \begin{macro}[EXP]
-%   {\@@_change_case_lower_az:nnnN, \@@_change_case_upper_az:nnnN}
+%   {\@@_change_case_lower_az:nnnn, \@@_change_case_upper_az:nnnn}
 %   Straight copies.
 %    \begin{macrocode}
-\cs_new_eq:NN \@@_change_case_lower_az:nnnN
-  \@@_change_case_lower_tr:nnnN
-\cs_new_eq:NN \@@_change_case_upper_az:nnnN
-  \@@_change_case_upper_tr:nnnN
+\cs_new_eq:NN \@@_change_case_lower_az:nnnn
+  \@@_change_case_lower_tr:nnnn
+\cs_new_eq:NN \@@_change_case_upper_az:nnnn
+  \@@_change_case_upper_tr:nnnn
 %    \end{macrocode}
 % \end{macro}
 %
-% \subsection{Case changing data for $8$-bit engines}
-%
-% \begin{variable}
-%   {
-%     \c_@@_dotless_i_tl   ,
-%     \c_@@_dotted_I_tl    ,
-%     \c_@@_i_ogonek_tl    ,
-%     \c_@@_I_ogonek_tl    ,
-%     \c_@@_final_sigma_tl ,
-%     \c_@@_sigma_tl       ,
-%     \c_@@_grosses_Eszett_tl
-%   }
-%  For cases where there is an $8$-bit option in the |T1| font set up,
-%  a variant is provided in both cases. There are also a few extras for
-%  |LGR|.
-%    \begin{macrocode}
-\group_begin:
-  \bool_lazy_or:nnF
-    { \sys_if_engine_luatex_p: }
-    { \sys_if_engine_xetex_p: }
-    {
-      \cs_set_protected:Npn \@@_tmp:w #1#2
-        {
-          \group_begin:
-            \cs_set_protected:Npn \@@_tmp:w ##1##2##3##4
-              {
-                \tl_const:Nx #1
-                  {
-                    \exp_after:wN \exp_after:wN \exp_after:wN
-                      \exp_not:N \char_generate:nn {##1} { 13 }
-                    \exp_after:wN \exp_after:wN \exp_after:wN
-                      \exp_not:N \char_generate:nn {##2} { 13 }
-                    \tl_if_blank:nF {##3}
-                      {
-                        \exp_after:wN \exp_after:wN \exp_after:wN
-                          \exp_not:N \char_generate:nn {##3} { 13 }
-                      }
-                  }
-              }
-            \use:x
-              { \@@_tmp:w \char_to_utfviii_bytes:n { "#2 } }  
-          \group_end:
-        }
-      \@@_tmp:w \c_@@_dotless_i_tl      { 0131 }
-      \@@_tmp:w \c_@@_dotted_I_tl       { 0130 }
-      \@@_tmp:w \c_@@_i_ogonek_tl       { 012F }
-      \@@_tmp:w \c_@@_I_ogonek_tl       { 012E }
-      \@@_tmp:w \c_@@_final_sigma_tl    { 03C2 }
-      \@@_tmp:w \c_@@_sigma_tl          { 03C3 }
-      \@@_tmp:w \c_@@_grosses_Eszett_tl { 1E9E }
-    }
-\group_end:
-%    \end{macrocode}
-% \end{variable}
-%
-% For $8$-bit engines we now need to define the case-change data for
-% the multi-octet mappings. This data is here not in the \pkg{char} module
-% as the multi-byte nature means they are never |N|-type. As this data is
-% needed both for text and string work, it is stored as |kernel|: it remains
-% internal as it is not really suitable for wider exposure.
-%
-% The first set of codepoints are those in |T1|: all of these are two bytes. 
-%    \begin{macrocode}
-\group_begin:
-  \bool_lazy_or:nnF
-    { \sys_if_engine_luatex_p: }
-    { \sys_if_engine_xetex_p: }
-    {
-      \cs_set_protected:Npn \@@_loop:nn #1#2
-        {
-          \quark_if_recursion_tail_stop:n {#1}
-          \use:x
-            {
-              \@@_tmp:w
-                \char_to_utfviii_bytes:n { "#1 }
-                \char_to_utfviii_bytes:n { "#2 }
-            }
-          \@@_loop:nn
-        }
-      \cs_set_protected:Npn \@@_tmp:nnnn #1#2#3#4#5
-        {
-          \tl_const:cx
-            {
-              c__kernel_ #1 case_
-              \char_generate:nn {#2} { 12 }
-              \char_generate:nn {#3} { 12 }
-              _tl
-            }
-            {
-              \exp_after:wN \exp_after:wN \exp_after:wN
-                \exp_not:N \char_generate:nn {#4} { 13 }
-              \exp_after:wN \exp_after:wN \exp_after:wN
-                \exp_not:N \char_generate:nn {#5} { 13 }
-            }
-        }
-      \cs_set_protected:Npn \@@_tmp:w #1#2#3#4#5#6#7#8
-        {
-          \tl_const:cx
-            {
-              c__kernel_lowercase_
-              \char_generate:nn {#1} { 12 }
-              \char_generate:nn {#2} { 12 }
-              _tl
-            }
-            {
-              \exp_after:wN \exp_after:wN \exp_after:wN
-                \exp_not:N \char_generate:nn {#5} { 13 }
-              \exp_after:wN \exp_after:wN \exp_after:wN
-                \exp_not:N \char_generate:nn {#6} { 13 }
-            }
-          \@@_tmp:nnnn { upper } {#5} {#6} {#1} {#2}
-          \@@_tmp:nnnn { title } {#5} {#6} {#1} {#2}
-        }
-      \@@_loop:nn
-        { 00C0 } { 00E0 }
-        { 00C1 } { 00E1 }
-        { 00C2 } { 00E2 }
-        { 00C3 } { 00E3 }
-        { 00C4 } { 00E4 }
-        { 00C5 } { 00E5 }
-        { 00C6 } { 00E6 }
-        { 00C7 } { 00E7 }
-        { 00C8 } { 00E8 }
-        { 00C9 } { 00E9 }
-        { 00CA } { 00EA }
-        { 00CB } { 00EB }
-        { 00CC } { 00EC }
-        { 00CD } { 00ED }
-        { 00CE } { 00EE }
-        { 00CF } { 00EF }
-        { 00D0 } { 00F0 }
-        { 00D1 } { 00F1 }
-        { 00D2 } { 00F2 }
-        { 00D3 } { 00F3 }
-        { 00D4 } { 00F4 }
-        { 00D5 } { 00F5 }
-        { 00D6 } { 00F6 }
-        { 00D8 } { 00F8 }
-        { 00D9 } { 00F9 }
-        { 00DA } { 00FA }
-        { 00DB } { 00FB }
-        { 00DC } { 00FC }
-        { 00DD } { 00FD }
-        { 00DE } { 00FE }
-        { 0100 } { 0101 }
-        { 0102 } { 0103 }
-        { 0104 } { 0105 }
-        { 0106 } { 0107 }
-        { 0108 } { 0109 }
-        { 010A } { 010B }
-        { 010C } { 010D }
-        { 010E } { 010F }
-        { 0110 } { 0111 }
-        { 0112 } { 0113 }
-        { 0114 } { 0115 }
-        { 0116 } { 0117 }
-        { 0118 } { 0119 }
-        { 011A } { 011B }
-        { 011C } { 011D }
-        { 011E } { 011F }
-        { 0120 } { 0121 }
-        { 0122 } { 0123 }
-        { 0124 } { 0125 }
-        { 0128 } { 0129 }
-        { 012A } { 012B }
-        { 012C } { 012D }
-        { 012E } { 012F }
-        { 0132 } { 0133 }
-        { 0134 } { 0135 }
-        { 0136 } { 0137 }
-        { 0139 } { 013A }
-        { 013B } { 013C }
-        { 013E } { 013F }
-        { 0141 } { 0142 }
-        { 0143 } { 0144 }
-        { 0145 } { 0146 }
-        { 0147 } { 0148 }
-        { 014A } { 014B }
-        { 014C } { 014D }
-        { 014E } { 014F }
-        { 0150 } { 0151 }
-        { 0152 } { 0153 }
-        { 0154 } { 0155 }
-        { 0156 } { 0157 }
-        { 0158 } { 0159 }
-        { 015A } { 015B }
-        { 015C } { 015D }
-        { 015E } { 015F }
-        { 0160 } { 0161 }
-        { 0162 } { 0163 }
-        { 0164 } { 0165 }
-        { 0168 } { 0169 }
-        { 016A } { 016B }
-        { 016C } { 016D }
-        { 016E } { 016F }
-        { 0170 } { 0171 }
-        { 0172 } { 0173 }
-        { 0174 } { 0175 }
-        { 0176 } { 0177 }
-        { 0178 } { 00FF }
-        { 0179 } { 017A }
-        { 017B } { 017C }
-        { 017D } { 017E }
-        { 01A0 } { 01A1 }
-        { 01AF } { 01B0 }
-        { 01CD } { 01CE }
-        { 01CF } { 01D0 }
-        { 01D1 } { 01D2 }
-        { 01D3 } { 01D4 }
-        { 01E2 } { 01E3 }
-        { 01E6 } { 01E7 }
-        { 01E8 } { 01E9 }
-        { 01EA } { 01EB }
-        { 01F4 } { 01F5 }
-        { 0218 } { 0219 }
-        { 021A } { 021B }
-%    \end{macrocode}
-% Add |T2| (Cyrillic) : again two bytes.
-%    \begin{macrocode}
-        { 0400 } { 0450 }
-        { 0401 } { 0451 }
-        { 0402 } { 0452 }
-        { 0403 } { 0453 }
-        { 0404 } { 0454 }
-        { 0405 } { 0455 }
-        { 0406 } { 0456 }
-        { 0407 } { 0457 }
-        { 0408 } { 0458 }
-        { 0409 } { 0459 }
-        { 040A } { 045A }
-        { 040B } { 045B }
-        { 040C } { 045C }
-        { 040D } { 045D }
-        { 040E } { 045E }
-        { 040F } { 045F }
-        { 0410 } { 0430 }
-        { 0411 } { 0431 }
-        { 0412 } { 0432 }
-        { 0413 } { 0433 }
-        { 0414 } { 0434 }
-        { 0415 } { 0435 }
-        { 0416 } { 0436 }
-        { 0417 } { 0437 }
-        { 0418 } { 0438 }
-        { 0419 } { 0439 }
-        { 041A } { 043A }
-        { 041B } { 043B }
-        { 041C } { 043C }
-        { 041D } { 043D }
-        { 041E } { 043E }
-        { 041F } { 043F }
-        { 0420 } { 0440 }
-        { 0421 } { 0441 }
-        { 0422 } { 0442 }
-        { 0423 } { 0443 }
-        { 0424 } { 0444 }
-        { 0425 } { 0445 }
-        { 0426 } { 0446 }
-        { 0427 } { 0447 }
-        { 0428 } { 0448 }
-        { 0429 } { 0449 }
-        { 042A } { 044A }
-        { 042B } { 044B }
-        { 042C } { 044C }
-        { 042D } { 044D }
-        { 042E } { 044E }
-        { 042F } { 044F }
-%    \end{macrocode}
-% Greek support: everything in the two-octet range.
-%    \begin{macrocode}
-        { 0370 } { 0371 }
-        { 0372 } { 0373 }
-        { 0376 } { 0377 }
-        { 03FD } { 037B }
-        { 03FE } { 037C }
-        { 03FF } { 037D }
-        { 0386 } { 03AC }
-        { 0388 } { 03AD }
-        { 0389 } { 03AE }
-        { 038A } { 03AF }
-        { 0391 } { 03B1 }
-        { 0392 } { 03B2 }
-        { 0393 } { 03B3 }
-        { 0394 } { 03B4 }
-        { 0395 } { 03B5 }
-        { 0396 } { 03B6 }
-        { 0397 } { 03B7 }
-        { 0398 } { 03B8 }
-        { 0399 } { 03B9 }
-        { 039A } { 03BA }
-        { 039B } { 03BB }
-        { 039C } { 03BC }
-        { 039D } { 03BD }
-        { 039E } { 03BE }
-        { 039F } { 03BF }
-        { 03A0 } { 03C0 }
-        { 03A1 } { 03C1 }
-        { 03A3 } { 03C3 }
-        { 03A4 } { 03C4 }
-        { 03A5 } { 03C5 }
-        { 03A6 } { 03C6 }
-        { 03A7 } { 03C7 }
-        { 03A8 } { 03C8 }
-        { 03A9 } { 03C9 }
-        { 03AA } { 03CA }
-        { 03AB } { 03CB }
-        { 038C } { 03CC }
-        { 038E } { 03CD }
-        { 038F } { 03CE }
-        { 03CF } { 03D7 }
-        { 03D8 } { 03D9 }
-        { 03DA } { 03DB }
-        { 03DC } { 03DD }
-        { 03DE } { 03DF }
-        { 03E0 } { 03E1 }
-        { 03E2 } { 03E3 }
-        { 03E4 } { 03E5 }
-        { 03E6 } { 03E7 }
-        { 03E8 } { 03E9 }
-        { 03EA } { 03EB }
-        { 03EC } { 03ED }
-        { 03EE } { 03EF }
-        { 03F9 } { 03F2 }
-        { 037F } { 03F3 }
-        { 03F7 } { 03F8 }
-        { 03FA } { 03FB }
-        \q_recursion_tail ?
-        \q_recursion_stop
-%    \end{macrocode}
-% Odds and ends for Greek; mainly symbols that are for compatibility,
-% but also things like the terminal sigma. Almost all are uppercase
-% mappings, but there is one that is not!
-%    \begin{macrocode}
-      \cs_set_protected:Npn \@@_tmp:w #1#2#3
-        {
-          \group_begin:
-            \cs_set_protected:Npn \@@_tmp:w ##1##2##3##4##5##6##7##8
-              {
-                \tl_const:cx
-                  {
-                    c__kernel_ #3 case_
-                    \char_generate:nn {##1} { 12 }
-                    \char_generate:nn {##2} { 12 }
-                    _tl
-                  }
-                  {
-                    \exp_after:wN \exp_after:wN \exp_after:wN
-                      \exp_not:N \char_generate:nn {##5} { 13 }
-                    \exp_after:wN \exp_after:wN \exp_after:wN
-                      \exp_not:N \char_generate:nn {##6} { 13 }
-                  }
-              }
-            \use:x
-              {
-                \@@_tmp:w
-                  \char_to_utfviii_bytes:n { "#1 }
-                  \char_to_utfviii_bytes:n { "#2 }
-              }
-          \group_end:
-        }
-      \@@_tmp:w { 0345 } { 0399 } { upper }
-      \@@_tmp:w { 03C2 } { 03A3 } { upper }
-      \@@_tmp:w { 03D0 } { 0392 } { upper }
-      \@@_tmp:w { 03D1 } { 0398 } { upper }
-      \@@_tmp:w { 03D5 } { 03A6 } { upper }
-      \@@_tmp:w { 03D6 } { 03A0 } { upper }
-      \@@_tmp:w { 03F0 } { 039A } { upper }
-      \@@_tmp:w { 03F1 } { 03A1 } { upper }
-      \@@_tmp:w { 03F4 } { 03B8 } { lower }
-      \@@_tmp:w { 03F5 } { 0395 } { upper }
-%    \end{macrocode}
-% Croatian digraph letters: these have a titlecase variant.
-%    \begin{macrocode}
-      \@@_tmp:w { 01C4 } { 01C5 } { title }
-      \@@_tmp:w { 01C4 } { 01C6 } { lower }
-      \@@_tmp:w { 01C5 } { 01C4 } { upper }
-      \@@_tmp:w { 01C5 } { 01C6 } { lower }
-      \@@_tmp:w { 01C6 } { 01C4 } { upper }
-      \@@_tmp:w { 01C6 } { 01C5 } { title }
-      \@@_tmp:w { 01C7 } { 01C8 } { title }
-      \@@_tmp:w { 01C7 } { 01C9 } { lower }
-      \@@_tmp:w { 01C8 } { 01C7 } { upper }
-      \@@_tmp:w { 01C8 } { 01C9 } { lower }
-      \@@_tmp:w { 01C9 } { 01C7 } { upper }
-      \@@_tmp:w { 01C9 } { 01C8 } { title }
-      \@@_tmp:w { 01CA } { 01CB } { title }
-      \@@_tmp:w { 01CA } { 01CC } { lower }
-      \@@_tmp:w { 01CB } { 01CA } { upper }
-      \@@_tmp:w { 01CB } { 01CC } { lower }
-      \@@_tmp:w { 01CC } { 01CA } { upper }
-      \@@_tmp:w { 01CC } { 01CB } { title }
-%    \end{macrocode}
-% Odds and ends that are not simple one-to-one mappings. These
-% are still two-octet code points.
-%    \begin{macrocode}
-      \cs_set_protected:Npn \@@_tmp:w #1#2#3
-        {
-          \group_begin:
-            \cs_set_protected:Npn \@@_tmp:w ##1##2##3##4
-              {
-                \tl_const:cn
-                  {
-                    c__kernel_ #3 case_
-                    \char_generate:nn {##1} { 12 }
-                    \char_generate:nn {##2} { 12 }
-                    _tl
-                  }
-                    {#2}
-              }
-            \use:x
-              { \@@_tmp:w \char_to_utfviii_bytes:n { "#1 } }
-          \group_end:
-        }
-      \@@_tmp:w { 00DF } { SS } { upper }
-      \@@_tmp:w { 00DF } { Ss } { title }
-      \@@_tmp:w { 0131 } { I }  { upper }
-%    \end{macrocode}
-% Coverage for Vietnamese (\texttt{T5})
-%    \begin{macrocode}
-      \cs_set_protected:Npn \@@_tmp:nnnnnn #1#2#3#4#5#6#7
-        {
-          \tl_const:cx
-            {
-              c__kernel_ #1 case_
-              \char_generate:nn {#2} { 12 }
-              \char_generate:nn {#3} { 12 }
-              \char_generate:nn {#4} { 12 }
-              _tl
-            }
-            {
-              \exp_after:wN \exp_after:wN \exp_after:wN
-                \exp_not:N \char_generate:nn {#5} { 13 }
-              \exp_after:wN \exp_after:wN \exp_after:wN
-                \exp_not:N \char_generate:nn {#6} { 13 }
-              \exp_after:wN \exp_after:wN \exp_after:wN
-                \exp_not:N \char_generate:nn {#7} { 13 }
-            }
-        }
-      \cs_set_protected:Npn \@@_tmp:w #1#2#3#4#5#6#7#8
-        {
-          \tl_const:cx
-            {
-              c__kernel_lowercase_
-              \char_generate:nn {#1} { 12 }
-              \char_generate:nn {#2} { 12 }
-              \char_generate:nn {#3} { 12 }
-              _tl
-            }
-            {
-              \exp_after:wN \exp_after:wN \exp_after:wN
-                \exp_not:N \char_generate:nn {#5} { 13 }
-              \exp_after:wN \exp_after:wN \exp_after:wN
-                \exp_not:N \char_generate:nn {#6} { 13 }
-              \exp_after:wN \exp_after:wN \exp_after:wN
-                \exp_not:N \char_generate:nn {#7} { 13 }
-            }
-          \@@_tmp:nnnnnn { upper } {#5} {#6} {#7} {#1} {#2} {#3}
-          \@@_tmp:nnnnnn { title } {#5} {#6} {#7} {#1} {#2} {#3}
-        }
-      \@@_loop:nn
-        { 1EA0 } { 1EA1 }
-        { 1EA2 } { 1EA3 }
-        { 1EA4 } { 1EA5 }
-        { 1EA6 } { 1EA7 }
-        { 1EA8 } { 1EA9 }
-        { 1EAA } { 1EAB }
-        { 1EAC } { 1EAD }
-        { 1EAE } { 1EAF }
-        { 1EB0 } { 1EB1 }
-        { 1EB2 } { 1EB3 }
-        { 1EB4 } { 1EB5 }
-        { 1EB6 } { 1EB7 }
-        { 1EB8 } { 1EB9 }
-        { 1EBA } { 1EBB }
-        { 1EBC } { 1EBD }
-        { 1EBE } { 1EBF }
-        { 1EC0 } { 1EC1 }
-        { 1EC2 } { 1EC3 }
-        { 1EC4 } { 1EC5 }
-        { 1EC6 } { 1EC7 }
-        { 1EC8 } { 1EC9 }
-        { 1ECA } { 1ECB }
-        { 1ECC } { 1ECD }
-        { 1ECE } { 1ECF }
-        { 1ED0 } { 1ED1 }
-        { 1ED2 } { 1ED3 }
-        { 1ED4 } { 1ED5 }
-        { 1ED6 } { 1ED7 }
-        { 1ED8 } { 1ED9 }
-        { 1EDA } { 1EDB }
-        { 1EDC } { 1EDD }
-        { 1EDE } { 1EDF }
-        { 1EE0 } { 1EE1 }
-        { 1EE2 } { 1EE3 }
-        { 1EE4 } { 1EE5 }
-        { 1EE6 } { 1EE7 }
-        { 1EE8 } { 1EE9 }
-        { 1EEA } { 1EEB }
-        { 1EEC } { 1EED }
-        { 1EEE } { 1EEF }
-        { 1EF0 } { 1EF1 }
-        { 1EF2 } { 1EF3 }
-        { 1EF4 } { 1EF5 }
-        { 1EF6 } { 1EF7 }
-        { 1EF8 } { 1EF9 }
-        \q_recursion_tail ?
-        \q_recursion_stop
-%    \end{macrocode}
-% Greek support: the three-octet code points.
-%    \begin{macrocode}
-      \@@_loop:nn
-        { 1F08 } { 1F00 }
-        { 1F09 } { 1F01 }
-        { 1F0A } { 1F02 }
-        { 1F0B } { 1F03 }
-        { 1F0C } { 1F04 }
-        { 1F0D } { 1F05 }
-        { 1F0E } { 1F06 }
-        { 1F0F } { 1F07 }
-        { 1F18 } { 1F10 }
-        { 1F19 } { 1F11 }
-        { 1F1A } { 1F12 }
-        { 1F1B } { 1F13 }
-        { 1F1C } { 1F14 }
-        { 1F1D } { 1F15 }
-        { 1F28 } { 1F20 }
-        { 1F29 } { 1F21 }
-        { 1F2A } { 1F22 }
-        { 1F2B } { 1F23 }
-        { 1F2C } { 1F24 }
-        { 1F2D } { 1F25 }
-        { 1F2E } { 1F26 }
-        { 1F2F } { 1F27 }
-        { 1F38 } { 1F30 }
-        { 1F39 } { 1F31 }
-        { 1F3A } { 1F32 }
-        { 1F3B } { 1F33 }
-        { 1F3C } { 1F34 }
-        { 1F3D } { 1F35 }
-        { 1F3E } { 1F36 }
-        { 1F3F } { 1F37 }
-        { 1F48 } { 1F40 }
-        { 1F49 } { 1F41 }
-        { 1F4A } { 1F42 }
-        { 1F4B } { 1F43 }
-        { 1F4C } { 1F44 }
-        { 1F4D } { 1F45 }
-        { 1F59 } { 1F51 }
-        { 1F5B } { 1F53 }
-        { 1F5D } { 1F55 }
-        { 1F5F } { 1F57 }
-        { 1F68 } { 1F60 }
-        { 1F69 } { 1F61 }
-        { 1F6A } { 1F62 }
-        { 1F6B } { 1F63 }
-        { 1F6C } { 1F64 }
-        { 1F6D } { 1F65 }
-        { 1F6E } { 1F66 }
-        { 1F6F } { 1F67 }
-        { 1FBA } { 1F70 }
-        { 1FBB } { 1F71 }
-        { 1FC8 } { 1F72 }
-        { 1FC9 } { 1F73 }
-        { 1FCA } { 1F74 }
-        { 1FCB } { 1F75 }
-        { 1FDA } { 1F76 }
-        { 1FDB } { 1F77 }
-        { 1FF8 } { 1F78 }
-        { 1FF9 } { 1F79 }
-        { 1FEA } { 1F7A }
-        { 1FEB } { 1F7B }
-        { 1FFA } { 1F7C }
-        { 1FFB } { 1F7D }
-        { 1F88 } { 1F80 }
-        { 1F89 } { 1F81 }
-        { 1F8A } { 1F82 }
-        { 1F8B } { 1F83 }
-        { 1F8C } { 1F84 }
-        { 1F8D } { 1F85 }
-        { 1F8E } { 1F86 }
-        { 1F8F } { 1F87 }
-        { 1F98 } { 1F90 }
-        { 1F99 } { 1F91 }
-        { 1F9A } { 1F92 }
-        { 1F9B } { 1F93 }
-        { 1F9C } { 1F94 }
-        { 1F9D } { 1F95 }
-        { 1F9E } { 1F96 }
-        { 1F9F } { 1F97 }
-        { 1FA8 } { 1FA0 }
-        { 1FA9 } { 1FA1 }
-        { 1FAA } { 1FA2 }
-        { 1FAB } { 1FA3 }
-        { 1FAC } { 1FA4 }
-        { 1FAD } { 1FA5 }
-        { 1FAE } { 1FA6 }
-        { 1FAF } { 1FA7 }
-        { 1FB8 } { 1FB0 }
-        { 1FB9 } { 1FB1 }
-        { 1FBC } { 1FB3 }
-        { 1FCC } { 1FC3 }
-        { 1FD8 } { 1FD0 }
-        { 1FD9 } { 1FD1 }
-        { 1FE8 } { 1FE0 }
-        { 1FE9 } { 1FE1 }
-        { 1FEC } { 1FE5 }
-        { 1FFC } { 1FF3 }
-        \q_recursion_tail ?
-        \q_recursion_stop
-%    \end{macrocode}
-% One three-octet special case for Greek: it also moves to two-octets!
-%    \begin{macrocode}
-      \cs_set_protected:Npn \@@_tmp:w #1#2#3
-        {
-          \group_begin:
-            \cs_set_protected:Npn \@@_tmp:w ##1##2##3##4##5##6##7##8
-              {
-                \tl_const:cx
-                  {
-                    c__kernel_ #3 case_
-                    \char_generate:nn {##1} { 12 }
-                    \char_generate:nn {##2} { 12 }
-                    \char_generate:nn {##3} { 12 }
-                    _tl
-                  }
-                  {
-                    \exp_after:wN \exp_after:wN \exp_after:wN
-                      \exp_not:N \char_generate:nn {##5} { 13 }
-                    \exp_after:wN \exp_after:wN \exp_after:wN
-                      \exp_not:N \char_generate:nn {##6} { 13 }
-                  }
-              }
-            \use:x
-              {
-                \@@_tmp:w
-                  \char_to_utfviii_bytes:n { "#1 }
-                  \char_to_utfviii_bytes:n { "#2 }
-              }
-          \group_end:
-        }
-    \@@_tmp:w { 1FBE } { 0399 } { upper }
-%    \end{macrocode}
-% Greek accent suppression for uppercasing.
-%    \begin{macrocode}
-      \cs_set_protected:Npn \@@_tmp:w #1#2#3#4#5#6#7#8
-        {
-          \tl_const:cx
-            {
-              c_@@_uppercase_el_
-              \char_generate:nn {#1} { 12 }
-              \char_generate:nn {#2} { 12 }
-              _tl
-            }
-            {
-              \exp_after:wN \exp_after:wN \exp_after:wN
-                \exp_not:N \char_generate:nn {#5} { 13 }
-              \exp_after:wN \exp_after:wN \exp_after:wN
-                \exp_not:N \char_generate:nn {#6} { 13 }
-            }
-        }
-      \@@_loop:nn
-        { 0370 } { 0370 }
-        { 0371 } { 0370 }
-        { 0372 } { 0372 }
-        { 0373 } { 0372 }
-        { 0376 } { 0376 }
-        { 0377 } { 0376 }
-        { 03FD } { 03FD }
-        { 037B } { 03FD }
-        { 03FE } { 03FE }
-        { 037C } { 03FE }
-        { 03FF } { 03FF }
-        { 037D } { 03FF }
-        { 0386 } { 0391 }
-        { 0388 } { 0395 }
-        { 0389 } { 0397 }
-        { 038A } { 0399 }
-        { 038C } { 039F }
-        { 038E } { 03A5 }
-        { 038F } { 03A9 }
-        { 0390 } { 03AA }
-        { 0391 } { 0391 }
-        { 0392 } { 0392 }
-        { 0393 } { 0393 }
-        { 0394 } { 0394 }
-        { 0395 } { 0395 }
-        { 0396 } { 0396 }
-        { 0397 } { 0397 }
-        { 0398 } { 0398 }
-        { 0399 } { 0399 }
-        { 039A } { 039A }
-        { 039B } { 039B }
-        { 039C } { 039C }
-        { 039D } { 039D }
-        { 039E } { 039E }
-        { 039F } { 039F }
-        { 03A0 } { 03A0 }
-        { 03A1 } { 03A1 }
-        { 03A3 } { 03A3 }
-        { 03A4 } { 03A4 }
-        { 03A5 } { 03A5 }
-        { 03A6 } { 03A6 }
-        { 03A7 } { 03A7 }
-        { 03A8 } { 03A8 }
-        { 03A9 } { 03A9 }
-        { 03AA } { 03AA }
-        { 03AB } { 03AB }
-        { 03AC } { 0391 }
-        { 03AD } { 0395 }
-        { 03AE } { 0397 }
-        { 03AF } { 0399 }
-        { 03B0 } { 03AB }
-        { 03B1 } { 0391 }
-        { 03B2 } { 0392 }
-        { 03B3 } { 0393 }
-        { 03B4 } { 0394 }
-        { 03B5 } { 0395 }
-        { 03B6 } { 0396 }
-        { 03B7 } { 0397 }
-        { 03B8 } { 0398 }
-        { 03B9 } { 0399 }
-        { 03BA } { 039A }
-        { 03BB } { 039B }
-        { 03BC } { 039C }
-        { 03BD } { 039D }
-        { 03BE } { 039E }
-        { 03BF } { 039F }
-        { 03C0 } { 03A0 }
-        { 03C1 } { 03A1 }
-        { 03C2 } { 03A3 }
-        { 03C3 } { 03A3 }
-        { 03C4 } { 03A4 }
-        { 03C5 } { 03A5 }
-        { 03C6 } { 03A6 }
-        { 03C7 } { 03A7 }
-        { 03C8 } { 03A8 }
-        { 03C9 } { 03A9 }
-        { 03CA } { 03AA }
-        { 03CB } { 03AB }
-        { 03CC } { 039F }
-        { 03CD } { 03A5 }
-        { 03CE } { 03A9 }
-        { 03D0 } { 0392 }
-        { 03D1 } { 0398 }
-        { 03D2 } { 03A5 }
-        { 03D3 } { 03A5 }
-        { 03D4 } { 03AB }
-        { 03D5 } { 03A6 }
-        { 03D6 } { 03A0 }
-        { 03DA } { 03DA }
-        { 03DB } { 03DA }
-        { 03DC } { 03DC }
-        { 03DD } { 03DC }
-        { 03DE } { 03DE }
-        { 03DF } { 03DE }
-        { 03E0 } { 03E0 }
-        { 03E1 } { 03E0 }
-        { 03F0 } { 039A }
-        { 03F1 } { 03A1 }
-        { 03F2 } { 03F9 }
-        { 03F9 } { 03F9 }
-        \q_recursion_tail ?
-        \q_recursion_stop
-      \cs_set_protected:Npn \@@_tmp:w #1#2#3#4#5#6#7#8
-        {
-          \tl_const:cx
-            {
-              c_@@_uppercase_el_
-              \char_generate:nn {#1} { 12 }
-              \char_generate:nn {#2} { 12 }
-              \char_generate:nn {#3} { 12 }
-              _tl
-            }
-            {
-              \exp_after:wN \exp_after:wN \exp_after:wN
-                \exp_not:N \char_generate:nn {#5} { 13 }
-              \exp_after:wN \exp_after:wN \exp_after:wN
-                \exp_not:N \char_generate:nn {#6} { 13 }
-              \tl_if_blank:nF {#7}
-                {
-                  \exp_after:wN \exp_after:wN \exp_after:wN
-                    \exp_not:N \char_generate:nn {#7} { 13 }
-                }
-            }
-        }
-      \@@_loop:nn
-        { 1F00 } { 0391 }
-        { 1F01 } { 0391 }
-        { 1F02 } { 0391 }
-        { 1F03 } { 0391 }
-        { 1F04 } { 0391 }
-        { 1F05 } { 0391 }
-        { 1F06 } { 0391 }
-        { 1F07 } { 0391 }
-        { 1F08 } { 0391 }
-        { 1F09 } { 0391 }
-        { 1F0A } { 0391 }
-        { 1F0B } { 0391 }
-        { 1F0C } { 0391 }
-        { 1F0D } { 0391 }
-        { 1F0E } { 0391 }
-        { 1F0F } { 0391 }
-        { 1F10 } { 0395 }
-        { 1F11 } { 0395 }
-        { 1F12 } { 0395 }
-        { 1F13 } { 0395 }
-        { 1F14 } { 0395 }
-        { 1F15 } { 0395 }
-        { 1F18 } { 0395 }
-        { 1F19 } { 0395 }
-        { 1F1A } { 0395 }
-        { 1F1B } { 0395 }
-        { 1F1C } { 0395 }
-        { 1F1D } { 0395 }
-        { 1F20 } { 0397 }
-        { 1F21 } { 0397 }
-        { 1F22 } { 0397 }
-        { 1F23 } { 0397 }
-        { 1F24 } { 0397 }
-        { 1F25 } { 0397 }
-        { 1F26 } { 0397 }
-        { 1F27 } { 0397 }
-        { 1F28 } { 0397 }
-        { 1F29 } { 0397 }
-        { 1F2A } { 0397 }
-        { 1F2B } { 0397 }
-        { 1F2C } { 0397 }
-        { 1F2D } { 0397 }
-        { 1F2E } { 0397 }
-        { 1F2F } { 0397 }
-        { 1F30 } { 0399 }
-        { 1F31 } { 0399 }
-        { 1F32 } { 0399 }
-        { 1F33 } { 0399 }
-        { 1F34 } { 0399 }
-        { 1F35 } { 0399 }
-        { 1F36 } { 0399 }
-        { 1F37 } { 0399 }
-        { 1F38 } { 0399 }
-        { 1F39 } { 0399 }
-        { 1F3A } { 0399 }
-        { 1F3B } { 0399 }
-        { 1F3C } { 0399 }
-        { 1F3D } { 0399 }
-        { 1F3E } { 0399 }
-        { 1F3F } { 0399 }
-        { 1F40 } { 039F }
-        { 1F41 } { 039F }
-        { 1F42 } { 039F }
-        { 1F43 } { 039F }
-        { 1F44 } { 039F }
-        { 1F45 } { 039F }
-        { 1F48 } { 039F }
-        { 1F49 } { 039F }
-        { 1F4A } { 039F }
-        { 1F4B } { 039F }
-        { 1F4C } { 039F }
-        { 1F4D } { 039F }
-        { 1F50 } { 03A5 }
-        { 1F51 } { 03A5 }
-        { 1F52 } { 03A5 }
-        { 1F53 } { 03A5 }
-        { 1F54 } { 03A5 }
-        { 1F55 } { 03A5 }
-        { 1F56 } { 03A5 }
-        { 1F57 } { 03A5 }
-        { 1F59 } { 03A5 }
-        { 1F5B } { 03A5 }
-        { 1F5D } { 03A5 }
-        { 1F5F } { 03A5 }
-        { 1F60 } { 03A9 }
-        { 1F61 } { 03A9 }
-        { 1F62 } { 03A9 }
-        { 1F63 } { 03A9 }
-        { 1F64 } { 03A9 }
-        { 1F65 } { 03A9 }
-        { 1F66 } { 03A9 }
-        { 1F67 } { 03A9 }
-        { 1F68 } { 03A9 }
-        { 1F69 } { 03A9 }
-        { 1F6A } { 03A9 }
-        { 1F6B } { 03A9 }
-        { 1F6C } { 03A9 }
-        { 1F6D } { 03A9 }
-        { 1F6E } { 03A9 }
-        { 1F6F } { 03A9 }
-        { 1F70 } { 0391 }
-        { 1F71 } { 0391 }
-        { 1F72 } { 0395 }
-        { 1F73 } { 0395 }
-        { 1F74 } { 0397 }
-        { 1F75 } { 0397 }
-        { 1F76 } { 0399 }
-        { 1F77 } { 0399 }
-        { 1F78 } { 039F }
-        { 1F79 } { 039F }
-        { 1F7A } { 03A5 }
-        { 1F7B } { 03A5 }
-        { 1F7C } { 03A9 }
-        { 1F7D } { 03A9 }
-        { 1F80 } { 1FBC }
-        { 1F81 } { 1FBC }
-        { 1F82 } { 1FBC }
-        { 1F83 } { 1FBC }
-        { 1F84 } { 1FBC }
-        { 1F85 } { 1FBC }
-        { 1F86 } { 1FBC }
-        { 1F87 } { 1FBC }
-        { 1F88 } { 1FBC }
-        { 1F89 } { 1FBC }
-        { 1F8A } { 1FBC }
-        { 1F8B } { 1FBC }
-        { 1F8C } { 1FBC }
-        { 1F8D } { 1FBC }
-        { 1F8E } { 1FBC }
-        { 1F8F } { 1FBC }
-        { 1F90 } { 1FCC }
-        { 1F91 } { 1FCC }
-        { 1F92 } { 1FCC }
-        { 1F93 } { 1FCC }
-        { 1F94 } { 1FCC }
-        { 1F95 } { 1FCC }
-        { 1F96 } { 1FCC }
-        { 1F97 } { 1FCC }
-        { 1F98 } { 1FCC }
-        { 1F99 } { 1FCC }
-        { 1F9A } { 1FCC }
-        { 1F9B } { 1FCC }
-        { 1F9C } { 1FCC }
-        { 1F9D } { 1FCC }
-        { 1F9E } { 1FCC }
-        { 1F9F } { 1FCC }
-        { 1FA0 } { 1FFC }
-        { 1FA1 } { 1FFC }
-        { 1FA2 } { 1FFC }
-        { 1FA3 } { 1FFC }
-        { 1FA4 } { 1FFC }
-        { 1FA5 } { 1FFC }
-        { 1FA6 } { 1FFC }
-        { 1FA7 } { 1FFC }
-        { 1FA8 } { 1FFC }
-        { 1FA9 } { 1FFC }
-        { 1FAA } { 1FFC }
-        { 1FAB } { 1FFC }
-        { 1FAC } { 1FFC }
-        { 1FAD } { 1FFC }
-        { 1FAE } { 1FFC }
-        { 1FAF } { 1FFC }
-        { 1FB0 } { 1FB8 }
-        { 1FB1 } { 1FB9 }
-        { 1FB2 } { 1FBC }
-        { 1FB3 } { 1FBC }
-        { 1FB4 } { 1FBC }
-        { 1FB6 } { 0391 }
-        { 1FB7 } { 1FBC }
-        { 1FB8 } { 1FB8 }
-        { 1FB9 } { 1FB9 }
-        { 1FBA } { 0391 }
-        { 1FBB } { 0391 }
-        { 1FBC } { 1FBC }
-        { 1FBD } { 1FBD }
-        { 1FC2 } { 1FCC }
-        { 1FC3 } { 1FCC }
-        { 1FC4 } { 1FCC }
-        { 1FC6 } { 0397 }
-        { 1FC7 } { 1FCC }
-        { 1FC8 } { 0395 }
-        { 1FC9 } { 0395 }
-        { 1FCA } { 0397 }
-        { 1FCB } { 0397 }
-        { 1FCC } { 1FCC }
-        { 1FD0 } { 1FD8 }
-        { 1FD1 } { 1FD9 }
-        { 1FD2 } { 03AA }
-        { 1FD3 } { 03AA }
-        { 1FD6 } { 0399 }
-        { 1FD7 } { 03AA }
-        { 1FD8 } { 1FD8 }
-        { 1FD9 } { 1FD9 }
-        { 1FDA } { 0399 }
-        { 1FDB } { 0399 }
-        { 1FE0 } { 1FE8 }
-        { 1FE1 } { 1FE9 }
-        { 1FE2 } { 03AB }
-        { 1FE3 } { 03AB }
-        { 1FE4 } { 03A1 }
-        { 1FE5 } { 03A1 }
-        { 1FE6 } { 03A5 }
-        { 1FE7 } { 03AB }
-        { 1FE8 } { 1FE8 }
-        { 1FE9 } { 1FE9 }
-        { 1FEA } { 03A5 }
-        { 1FEB } { 03A5 }
-        { 1FEC } { 1FEC }
-        { 1FF2 } { 1FFC }
-        { 1FF3 } { 1FFC }
-        { 1FF4 } { 1FFC }
-        { 1FF6 } { 03A9 }
-        { 1FF7 } { 1FFC }
-        { 1FF8 } { 039F }
-        { 1FF9 } { 039F }
-        { 1FFA } { 03A9 }
-        { 1FFB } { 03A9 }
-        { 1FFC } { 1FFC }
-        \q_recursion_tail ?
-        \q_recursion_stop
-  }
-\group_end:
-%    \end{macrocode}
-%
 % The (fixed) look-up mappings for letter-like control sequences.
 %    \begin{macrocode}
 \group_begin:

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3text-map.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3text-map.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3text-map.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3text-purify.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3text-purify.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3text-purify.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 %
 % \maketitle
 %
@@ -486,7 +486,7 @@
         \text_declare_purify_equivalent:Nx #1
           {
             \exp_args:Ne \@@_tmp:n
-              { \char_to_utfviii_bytes:n { "#2 } }
+              { \__kernel_codepoint_to_bytes:n { "#2 } }
           }
         \@@_loop:Nn
       }
@@ -574,7 +574,7 @@
       \cs_set:Npn \@@_tmp:n #1
         {
           \exp_args:Ne \@@_tmp_aux:n
-            { \char_to_utfviii_bytes:n { "#1 } }
+            { \__kernel_codepoint_to_bytes:n { "#1 } }
         }
       \cs_set:Npn \@@_tmp_aux:n #1 { \@@_tmp:nnnn #1 }
       \cs_set:Npn \@@_tmp:nnnn #1#2#3#4

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3text.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3text.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3text.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 %
 % \maketitle
 %
@@ -89,7 +89,7 @@
 %
 % \section{Case changing}
 %
-% \begin{function}[EXP, added = 2019-11-20, updated = 2020-02-24]
+% \begin{function}[EXP, added = 2019-11-20, updated = 2022-10-13]
 %   {
 %     \text_lowercase:n,  \text_uppercase:n,  \text_titlecase:n,
 %       \text_titlecase_first:n,
@@ -103,8 +103,9 @@
 %   Takes user input \meta{text} first applies \cs{text_expand}, then
 %   transforms the case of character tokens as specified by the
 %   function name. The category code of letters are not changed by this
-%   process (at least where they can be represented by the engine as a single
-%   token: $8$-bit engines may require active characters).
+%   process when Unicode engines are used; in $8$-bit engines, case changed
+%   charters in the ASCII range will have the current prevailing category code,
+%   while those outside of it will be represented by active characters.
 % \end{function}
 %
 %   Upper- and lowercase have the obvious meanings. Titlecasing may be regarded
@@ -118,7 +119,7 @@
 %   Importantly, notice that these functions are intended for working with
 %   user \emph{text for typesetting}. For case changing programmatic data see
 %   the \pkg{l3str} module and discussion there of \cs{str_lowercase:n},
-%   \cs{str_uppercase:n} and \cs{str_foldcase:n}.
+%   \cs{str_uppercase:n} and \cs{str_casefold:n}.
 %
 %   Case changing does not take place within math mode material so for example
 %   \begin{verbatim}
@@ -129,22 +130,15 @@
 %     SOME TEXT $y = mx + c$ WITH {BRACES}
 %   \end{verbatim}
 %
-%   The first maandatory argument of commands listed in
+%   The first mandatory argument of commands listed in
 %   \cs{l_text_case_exclude_arg_tl}
 %   is excluded from case changing; the latter are entirely non-textual
 %   content (such as labels).
 %
-%   As is generally true for \pkg{expl3}, these functions are designed to
-%   work with Unicode input only. As such, UTF-8 input is assumed for
-%   \emph{all} engines. When used with \XeTeX{} or \LuaTeX{} a full range of
-%   Unicode transformations are enabled. Specifically, the standard mappings
+%   The standard mappings
 %   here follow those defined by the \href{http://www.unicode.org}^^A
 %   {Unicode Consortium} in \texttt{UnicodeData.txt} and
-%   \texttt{SpecialCasing.txt}. In the case of $8$-bit engines, mappings
-%   are provided for characters which can be represented in output typeset
-%   using the |T1|, |T2| and |LGR| font encodings.
-%   Thus for example \texttt{ä} can be
-%   case-changed using \pdfTeX{}.  For \pTeX{} only the ASCII range is
+%   \texttt{SpecialCasing.txt}. For \pTeX{}, only the ASCII range is
 %   covered as the engine treats input outside of this range as east Asian.
 %
 %   Language-sensitive conversions are enabled using the \meta{language}
@@ -173,9 +167,7 @@
 %       leaves accents in place. A variant \texttt{el-x-iota} is available
 %       which retains the \textit{ypogegrammeni} (subscript muted iota)
 %       when uppercasing: the standard version converts these to a following
-%       capital iota. For $8$-bit engines a simplified approach is taken
-%       which will remove accents when uppercasing but without full
-%       context analysis.
+%       capital iota.
 %     \item Lithuanian (\texttt{lt}).
 %       The lowercase letters i and j should retain a dot above when the
 %       accents grave, acute or tilde are present. This is implemented for
@@ -677,10 +669,26 @@
       }
     \cs_new:Npn \@@_codepoint_process:nNN #1#2#3
       { #1 {#2#3} }
-    \cs_new:Npn \@@_codepoint_process:nNNN #1#2#3#4
-      { #1 {#2#3#4} }
-    \cs_new:Npn \@@_codepoint_process:nNNNN #1#2#3#4#5
-      { #1 {#2#3#4#5} }
+    \sys_if_engine_ptex:TF
+      {
+        \cs_gset:Npn \@@_codepoint_process:nN #1#2
+          {
+            \int_compare:nNnTF { `#2 } > { "80 }
+              {
+                \int_compare:nNnTF { `#2 } < { "E0 }
+                  { \@@_codepoint_process:nNN }
+                  { \use:n }
+              }
+          { \use:n }
+            {#1} #2
+        }
+      }
+      {
+        \cs_new:Npn \@@_codepoint_process:nNNN #1#2#3#4
+          { #1 {#2#3#4} }
+        \cs_new:Npn \@@_codepoint_process:nNNNN #1#2#3#4#5
+          { #1 {#2#3#4#5} }
+      }
   }
 %    \end{macrocode}
 % \end{macro}
@@ -689,10 +697,11 @@
 % \end{macro}
 %
 % \begin{macro}[EXP, pTF]{\@@_codepoint_compare:nNn}
-% \begin{macro}[EXP]{\@@_codepoint_compare:N, \@@_codepoint_compare_aux:N}
-% \begin{macro}[EXP]{\@@_codepoint_compare:NN}
-% \begin{macro}[EXP]{\@@_codepoint_compare:NNN}
-% \begin{macro}[EXP]{\@@_codepoint_compare:NNNN}
+% \begin{macro}[EXP]{\@@_codepoint_from_chars:Nw}
+% \begin{macro}[EXP]{\@@_codepoint_from_chars:N}
+% \begin{macro}[EXP]{\@@_codepoint_from_chars:NN}
+% \begin{macro}[EXP]{\@@_codepoint_from_chars:NNN}
+% \begin{macro}[EXP]{\@@_codepoint_from_chars:NNNN}
 %   Allows comparison for all engines using a first \enquote{character} followed
 %   by a codepoint.
 %    \begin{macrocode}
@@ -703,52 +712,76 @@
     \prg_new_conditional:Npnn
       \@@_codepoint_compare:nNn #1#2#3 { TF , p }
       {
-        \int_compare:nNnTF { `#1 } #2 {#3}
+        \int_compare:nNnTF {`#1} #2 {#3}
           \prg_return_true: \prg_return_false:
       }
+    \cs_new:Npn \@@_codepoint_from_chars:Nw #1 {`#1}
   }
   {
     \prg_new_conditional:Npnn
       \@@_codepoint_compare:nNn #1#2#3 { TF , p }
       {
-        \int_compare:nNnTF { \@@_codepoint_compare:N #1 }
+        \int_compare:nNnTF { \@@_codepoint_from_chars:Nw #1 }
             #2 {#3}
           \prg_return_true: \prg_return_false:
       }
-    \cs_new:Npn \@@_codepoint_compare:N #1
+    \cs_new:Npn \@@_codepoint_from_chars:Nw #1
       {
         \if_int_compare:w `#1 > "80 \exp_stop_f:
           \if_int_compare:w `#1 < "E0 \exp_stop_f:
             \exp_after:wN \exp_after:wN \exp_after:wN
-              \@@_codepoint_compare:NN
+              \@@_codepoint_from_chars:NN
           \else:
             \if_int_compare:w `#1 < "F0 \exp_stop_f:
               \exp_after:wN \exp_after:wN \exp_after:wN
               \exp_after:wN \exp_after:wN \exp_after:wN
-              \exp_after:wN \@@_codepoint_compare:NNN
+              \exp_after:wN \@@_codepoint_from_chars:NNN
             \else:
               \exp_after:wN \exp_after:wN \exp_after:wN
               \exp_after:wN \exp_after:wN \exp_after:wN
-              \exp_after:wN \@@_codepoint_compare:NNNN
+              \exp_after:wN \@@_codepoint_from_chars:NNNN
             \fi:
           \fi:
         \else:
-          \exp_after:wN \@@_codepoint_compare_aux:N
+          \exp_after:wN \@@_codepoint_from_chars:N
         \fi:
           #1
       }
-    \cs_new:Npn \@@_codepoint_compare_aux:N #1 { `#1 }
-    \cs_new:Npn \@@_codepoint_compare:NN #1#2
+    \cs_new:Npn \@@_codepoint_from_chars:N #1 { `#1 }
+    \cs_new:Npn \@@_codepoint_from_chars:NN #1#2
       { (`#1 - "C0) * "40 + `#2 - "80 }
-    \cs_new:Npn \@@_codepoint_compare:NNN #1#2#3
-      { (`#1 - "E0) * "1000 + (`#2 - "80) * "40 + `#3 - "80 }
-    \cs_new:Npn \@@_codepoint_compare:NNNN #1#2#3#4
+    %    \end{macrocode}
+    %   Avoid high chars with p\TeX{}.
+    %    \begin{macrocode}
+    \sys_if_engine_ptex:TF
       {
-          (`#1 - "F0) * "40000 
-        + (`#2 - "80) * "1000
-        + (`#3 - "80) * "40
-        + `#4 - "80
+        \cs_gset:Npn \@@_codepoint_from_chars:Nw #1
+          {
+            \if_int_compare:w `#1 > "80 \exp_stop_f:
+              \if_int_compare:w `#1 < "E0 \exp_stop_f:
+                \exp_after:wN \exp_after:wN \exp_after:wN
+                  \@@_codepoint_from_chars:NN
+              \else:
+                \exp_after:wN \exp_after:wN \exp_after:wN
+                  \@@_codepoint_from_chars:N
+              \fi:
+            \else:
+              \exp_after:wN \@@_codepoint_from_chars:N
+            \fi:
+              #1
+          }
       }
+      {
+        \cs_new:Npn \@@_codepoint_from_chars:NNN #1#2#3
+          { (`#1 - "E0) * "1000 + (`#2 - "80) * "40 + `#3 - "80 }
+        \cs_new:Npn \@@_codepoint_from_chars:NNNN #1#2#3#4
+          {
+              (`#1 - "F0) * "40000 
+            + (`#2 - "80) * "1000
+            + (`#3 - "80) * "40
+            + `#4 - "80
+          }
+      }
   }
 %    \end{macrocode}
 % \end{macro}
@@ -756,6 +789,7 @@
 % \end{macro}
 % \end{macro}
 % \end{macro}
+% \end{macro}
 %
 % \subsection{Configuration variables}
 %
@@ -793,6 +827,7 @@
   {
     \exp_not:n { \begin \cite \end \label \ref }
     \exp_not:c { cite ~ }
+    \exp_not:n { \babelshorthand }
   }
 %    \end{macrocode}
 % \end{variable}
@@ -1144,7 +1179,8 @@
 %   from handling of legacy input encodings: they need to end up in a
 %   representation we can deal with in further processing. The tests for
 %   explicit parts of the \LaTeXe{} UTF-8 mechanism cover the case of
-%   bookmarks, where definitions change and are no longer protected.
+%   bookmarks, where definitions change and are no longer protected. The
+%   same is true for \pkg{babel} shorthands.
 %    \begin{macrocode}
 \cs_new:Npn \@@_expand_explicit:N #1
   {
@@ -1161,6 +1197,7 @@
                 { \tl_if_head_eq_meaning_p:oN {#1} \UTFviii at two@octets }
                 { \tl_if_head_eq_meaning_p:oN {#1} \UTFviii at three@octets }
                 { \tl_if_head_eq_meaning_p:oN {#1} \UTFviii at four@octets }
+                { \tl_if_head_eq_meaning_p:oN {#1} \active at prefix }
               }
           }
           { \exp_after:wN \@@_expand_loop:w #1 }

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3tl-analysis.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3tl-analysis.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3tl-analysis.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -44,7 +44,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3tl.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3tl.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3tl.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 %
 % \maketitle
 %
@@ -450,7 +450,7 @@
 %   \end{syntax}
 %   Tests if the first \meta{token} in the \meta{token list}
 %   is an explicit space character
-%   (explicit token with character code~$12$ and category code~$10$).
+%   (explicit token with character code~$32$ and category code~$10$).
 %   In particular, the test is \texttt{false} if the \meta{token list}
 %   starts with an implicit token such as \cs{c_space_token}, or if it
 %   is empty.

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3token.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3token.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3token.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 %
 % \maketitle
 %
@@ -155,26 +155,6 @@
 %   \end{texnote}
 % \end{function}
 %
-% \begin{function}[added = 2020-01-09, EXP]
-%   {
-%     \char_lowercase:N, \char_uppercase:N,
-%     \char_titlecase:N, \char_foldcase:N,
-%     \char_str_lowercase:N, \char_str_uppercase:N,
-%     \char_str_titlecase:N, \char_str_foldcase:N
-%   }
-%   \begin{syntax}
-%     \cs{char_lowercase:N} \meta{char}
-%   \end{syntax}
-%   Converts the \meta{char} to the equivalent case-changed character
-%   as detailed by the function name (see \cs{str_foldcase:n}
-%   and \cs{text_titlecase:n} for details of these terms). The case mapping
-%   is carried out with no context-dependence (\emph{cf.}~\cs{text_uppercase:n},
-%   \emph{etc.}) The \texttt{str} versions always generate \enquote{other}
-%   (category code $12$) characters, whilst the standard versions generate
-%   characters with the category code of the \meta{char} (i.e.~only
-%   the character code changes).
-% \end{function}
-%
 % \begin{variable}[added = 2011-09-05]{\c_catcode_other_space_tl}
 %   Token list containing one character with category code $12$,
 %   (\enquote{other}), and character code $32$ (space).
@@ -1222,6 +1202,28 @@
 % characters at the start of the argument are \emph{not} ignored in this
 % case (and they prevent brace-stripping).
 %
+% \section{Deprecated functions}
+%
+% \begin{function}[added = 2020-01-09, EXP]
+%   {
+%     \char_lowercase:N, \char_uppercase:N,
+%     \char_titlecase:N, \char_foldcase:N,
+%     \char_str_lowercase:N, \char_str_uppercase:N,
+%     \char_str_titlecase:N, \char_str_foldcase:N
+%   }
+%   \begin{syntax}
+%     \cs{char_lowercase:N} \meta{char}
+%   \end{syntax}
+%   Converts the \meta{char} to the equivalent case-changed character
+%   as detailed by the function name (see \cs{str_foldcase:n}
+%   and \cs{text_titlecase:n} for details of these terms). The case mapping
+%   is carried out with no context-dependence (\emph{cf.}~\cs{text_uppercase:n},
+%   \emph{etc.}) The \texttt{str} versions always generate \enquote{other}
+%   (category code $12$) characters, whilst the standard versions generate
+%   characters with the category code of the \meta{char} (i.e.~only
+%   the character code changes).
+% \end{function}
+%
 % \end{documentation}
 %
 % \begin{implementation}
@@ -1690,232 +1692,14 @@
 % \end{macro}
 % \end{macro}
 %
-% \begin{macro}[EXP]{\char_to_utfviii_bytes:n}
-% \begin{macro}[EXP]{\@@_to_utfviii_bytes_auxi:n}
-% \begin{macro}[EXP]{\@@_to_utfviii_bytes_auxii:Nnn}
-% \begin{macro}[EXP]{\@@_to_utfviii_bytes_auxiii:n}
 % \begin{macro}[EXP]
 %   {
-%     \@@_to_utfviii_bytes_outputi:nw   ,
-%     \@@_to_utfviii_bytes_outputii:nw  ,
-%     \@@_to_utfviii_bytes_outputiii:nw ,
-%     \@@_to_utfviii_bytes_outputiv:nw
-%   }
-% \begin{macro}[EXP]
-%   {\@@_to_utfviii_bytes_output:nnn, \@@_to_utfviii_bytes_output:fnn}
-% \begin{macro}[EXP]{\@@_to_utfviii_bytes_end:}
-%   This code converts a codepoint into the correct UTF-8 representation.
-%   In terms of the algorithm itself, see
-%   \url{https://en.wikipedia.org/wiki/UTF-8} for the octet pattern.
-%    \begin{macrocode}
-\cs_new:Npn \char_to_utfviii_bytes:n #1
-  {
-    \exp_args:Nf \@@_to_utfviii_bytes_auxi:n
-      { \int_eval:n {#1} }
-  }
-\cs_new:Npn \@@_to_utfviii_bytes_auxi:n #1
-  {
-    \if_int_compare:w #1 > "80 \exp_stop_f:
-      \if_int_compare:w #1 < "800 \exp_stop_f:
-        \@@_to_utfviii_bytes_outputi:nw
-          { \@@_to_utfviii_bytes_auxii:Nnn C {#1} { 64 } }
-        \@@_to_utfviii_bytes_outputii:nw
-          { \@@_to_utfviii_bytes_auxiii:n {#1} }
-      \else:
-        \if_int_compare:w #1 < "10000 \exp_stop_f:
-          \@@_to_utfviii_bytes_outputi:nw
-            { \@@_to_utfviii_bytes_auxii:Nnn E {#1} { 64 * 64 } }
-          \@@_to_utfviii_bytes_outputii:nw
-            {
-              \@@_to_utfviii_bytes_auxiii:n
-                { \int_div_truncate:nn {#1} { 64 } }
-            }
-          \@@_to_utfviii_bytes_outputiii:nw
-            { \@@_to_utfviii_bytes_auxiii:n {#1} }
-        \else:
-          \@@_to_utfviii_bytes_outputi:nw
-            {
-              \@@_to_utfviii_bytes_auxii:Nnn F
-                 {#1} { 64 * 64 * 64 }
-            }
-          \@@_to_utfviii_bytes_outputii:nw
-            {
-              \@@_to_utfviii_bytes_auxiii:n
-                { \int_div_truncate:nn {#1} { 64 * 64 } }
-            }
-          \@@_to_utfviii_bytes_outputiii:nw
-            {
-              \@@_to_utfviii_bytes_auxiii:n
-                { \int_div_truncate:nn {#1} { 64 } }
-            }
-          \@@_to_utfviii_bytes_outputiv:nw
-            { \@@_to_utfviii_bytes_auxiii:n {#1} }
-        \fi:
-      \fi:
-    \else:
-      \@@_to_utfviii_bytes_outputi:nw {#1}
-    \fi:
-    \@@_to_utfviii_bytes_end: { } { } { } { }
-  }
-\cs_new:Npn \@@_to_utfviii_bytes_auxii:Nnn #1#2#3
-  {  "#10 + \int_div_truncate:nn {#2} {#3} }
-\cs_new:Npn \@@_to_utfviii_bytes_auxiii:n #1
-  { \int_mod:nn {#1} { 64 } + 128 }
-\cs_new:Npn \@@_to_utfviii_bytes_outputi:nw
-  #1 #2 \@@_to_utfviii_bytes_end: #3
-  { \@@_to_utfviii_bytes_output:fnn { \int_eval:n {#1} } { } {#2} }
-\cs_new:Npn \@@_to_utfviii_bytes_outputii:nw
-  #1 #2 \@@_to_utfviii_bytes_end: #3#4
-  { \@@_to_utfviii_bytes_output:fnn { \int_eval:n {#1} } { {#3} } {#2} }
-\cs_new:Npn \@@_to_utfviii_bytes_outputiii:nw
-  #1 #2 \@@_to_utfviii_bytes_end: #3#4#5
-  {
-    \@@_to_utfviii_bytes_output:fnn
-      { \int_eval:n {#1} } { {#3} {#4} } {#2}
-  }
-\cs_new:Npn \@@_to_utfviii_bytes_outputiv:nw
-  #1 #2 \@@_to_utfviii_bytes_end: #3#4#5#6
-  {
-    \@@_to_utfviii_bytes_output:fnn
-      { \int_eval:n {#1} } { {#3} {#4} {#5} } {#2}
-  }
-\cs_new:Npn \@@_to_utfviii_bytes_output:nnn #1#2#3
-  {
-    #3
-    \@@_to_utfviii_bytes_end: #2 {#1}
-  }
-\cs_generate_variant:Nn \@@_to_utfviii_bytes_output:nnn { f }
-\cs_new:Npn \@@_to_utfviii_bytes_end: { }
-%    \end{macrocode}
-% \end{macro}
-% \end{macro}
-% \end{macro}
-% \end{macro}
-% \end{macro}
-% \end{macro}
-% \end{macro}
-%
-% \begin{macro}[rEXP]{\char_to_nfd:N}
-% \begin{macro}[rEXP]{\@@_to_nfd:n, \@@_to_nfd:e}
-% \begin{macro}[rEXP]{\@@_to_nfd:Nw}
-% \begin{macro}[rEXP]{\char_to_nfd:n}
-% \begin{macro}[rEXP]{\char_to_nfd:w}
-% \begin{macro}[rEXP]{\char_nfd_generate:n}
-% \begin{macro}[rEXP]{\char_nfd_generate:nnnn}
-%   Look up any \textsc{nfd} and recursively produce the result. Having shared
-%   code between Unicode and $8$-bit engines would be ideal, but this would be
-%   awkward as we have completely different treatment of catcodes, numbers
-%   of tokens, etc. The apparent saving becomes more of a headache than it's
-%   worth \dots
-%    \begin{macrocode}
-\bool_lazy_or:nnTF
-  { \sys_if_engine_luatex_p: }
-  { \sys_if_engine_xetex_p: }
-  {
-    \cs_new:Npn \char_to_nfd:N #1
-      {
-        \cs_if_exist:cTF { c_@@_nfd_ \token_to_str:N #1 _ tl }
-          {
-            \exp_after:wN \exp_after:wN \exp_after:wN \@@_to_nfd:Nw
-              \exp_after:wN \exp_after:wN \exp_after:wN #1
-                \cs:w c_@@_nfd_ \token_to_str:N #1 _tl \cs_end:
-                  \s_@@_stop
-          }
-          { \exp_not:n {#1} }
-      }
-    \cs_new_eq:NN \@@_to_nfd:n \char_to_nfd:N
-    \cs_generate_variant:Nn \@@_to_nfd:n { e }
-    \cs_new:Npn \@@_to_nfd:Nw #1#2#3 \s_@@_stop
-      {
-        \@@_to_nfd:e
-          { \char_generate:nn { `#2 } { \@@_change_case_catcode:N #1 } }
-        \tl_if_blank:nF {#3}
-          {
-            \@@_to_nfd:e
-               { \char_generate:nn { `#3 } { \char_value_catcode:n { `#3 } } }
-          }
-      }
-    \cs_new:Npn \char_to_nfd:n #1
-      {
-        \@@_to_nfd:e { \char_generate:nn {#1} { \char_value_catcode:n {#1} } }
-      }
-  }
-  {
-    \cs_new:Npn \char_to_nfd:N #1 { \exp_not:n {#1} }
-    \cs_new:Npn \char_to_nfd:n #1
-      {
-        \int_compare:nNnTF {#1} > { "80 }
-          { \exp_args:Ne \@@_to_nfd:n { \@@_nfd_generate:n {#1} } }
-          { \@@_nfd_generate:n {#1} }
-      }
-    \cs_new:Npn \@@_to_nfd:n #1
-      {
-        \cs_if_exist:cTF { c_@@_nfd_ \tl_to_str:n {#1} _ tl }
-          {
-            \exp_after:wN \exp_after:wN \exp_after:wN \@@_to_nfd:w
-              \cs:w c_@@_nfd_ \tl_to_str:n {#1} _tl \cs_end:
-                \s_@@_stop
-          }
-          { \exp_not:n {#1} }
-      }
-    \cs_new:Npn \@@_to_nfd:w #1#2 \s_@@_stop
-      {
-        \@@_to_nfd:n {#1}
-        \tl_if_blank:nF {#2}
-          { \@@_to_nfd:n {#2} }
-      }
-     \cs_new:Npn \@@_nfd_generate:n #1
-       {
-         \use:e
-           {
-             \exp_not:N \@@_nfd_generate:nnnn
-               \char_to_utfviii_bytes:n {#1}
-           }
-       }
-      \cs_new:Npn \@@_nfd_generate:nnnn #1#2#3#4
-        {
-          \tl_if_blank:nTF {#2}
-            { \char_generate:nn {#1} { \char_value_catcode:n {#1} } }
-            {
-              \exp_after:wN \exp_after:wN \exp_after:wN
-                \exp_not:N \char_generate:nn {#1} { 13 }
-              \exp_after:wN \exp_after:wN \exp_after:wN
-                \exp_not:N \char_generate:nn {#2} { 13 }
-              \tl_if_blank:nF {#3}
-                {
-                  \exp_after:wN \exp_after:wN \exp_after:wN
-                    \exp_not:N \char_generate:nn {#3} { 13 }
-                  \tl_if_blank:nF {#4}
-                    {
-                      \exp_after:wN \exp_after:wN \exp_after:wN
-                        \exp_not:N \char_generate:nn {#4} { 13 }
-                    }
-                }
-            }
-           
-        }
-  }
-%    \end{macrocode}
-% \end{macro}
-% \end{macro}
-% \end{macro}
-% \end{macro}
-% \end{macro}
-% \end{macro}
-% \end{macro}
-%
-% \begin{macro}[EXP]
-%   {
 %     \char_lowercase:N, \char_uppercase:N,
 %     \char_titlecase:N, \char_foldcase:N
 %   }
-% \begin{macro}[EXP]{\@@_change_case:nNN}
 % \begin{macro}[EXP]{\@@_change_case:nN}
-% \begin{macro}[EXP]{\@@_change_case_multi:nN, \@@_change_case_multi:vN}
-% \begin{macro}[EXP]{\@@_change_case_multi:NNNNw}
-% \begin{macro}[EXP]{\@@_change_case:NNN}
-% \begin{macro}[EXP]{\@@_change_case:NNNN}
-% \begin{macro}[EXP]{\@@_change_case:NN}
+% \begin{macro}[EXP]{\@@_change_case:nnnN}
+% \begin{macro}[EXP]{\@@_change_case_auxi:nN, \@@_change_case_auxii:nN}
 % \begin{macro}[EXP]{\@@_change_case_catcode:N}
 % \begin{macro}[EXP]
 %   {
@@ -1922,72 +1706,64 @@
 %     \char_str_lowercase:N, \char_str_uppercase:N,
 %     \char_str_titlecase:N, \char_str_foldcase:N
 %   }
-% \begin{macro}[EXP]{\@@_str_change_case:nNN}
-% \begin{macro}[EXP]{\@@_str_change_case:nN}
+% \begin{macro}[EXP]{\@@_str_change_case:nN, \@@_str_change_case_aux:nN}
+% \begin{macro}[EXP]{\@@_str_change_case:nnnN}
+% \begin{macro}[EXP]{\@@_str_change_case:n}
 %   To ensure that the category codes produced are predictable, every character
-%   is re-generated even if it is otherwise unchanged. This makes life a little
-%   interesting when we might have multiple output characters: we have to
-%   grab each of them and case change them in reverse order to maintain
-%   \texttt{f}-type expandability.
+%   is re-generated even if it is otherwise unchanged.
 %    \begin{macrocode}
-\cs_new:Npn \char_lowercase:N #1
-  { \@@_change_case:nNN { lower } \char_value_lccode:n #1 }
-\cs_new:Npn \char_uppercase:N #1
-  { \@@_change_case:nNN { upper } \char_value_uccode:n #1 }
-\cs_new:Npn \char_titlecase:N #1
+\cs_new:Npn \char_lowercase:N
+  { \@@_change_case:nN { lowercase } }
+\cs_new:Npn \char_uppercase:N
+  { \@@_change_case:nN { uppercase } }
+\cs_new:Npn \char_titlecase:N
+  { \@@_change_case:nN { titlecase } }
+\cs_new:Npn \char_foldcase:N
+  { \@@_change_case:nN { casefold } }
+\cs_new:Npn \@@_change_case:nN #1#2
   {
-    \tl_if_exist:cTF { c_@@_titlecase_ \token_to_str:N #1 _tl }
+    \int_compare:nNnTF {`#2} = { `\  }
+      { ~ }
       {
-        \@@_change_case_multi:vN
-          { c_@@_titlecase_ \token_to_str:N #1 _tl } #1
+        \exp_args:Ne \@@_change_case_auxi:nN
+          { \__kernel_codepoint_case:nn {#1} {`#2} } #2
       }
-      { \char_uppercase:N #1 }
   }
-\cs_new:Npn \char_foldcase:N #1
-  { \@@_change_case:nNN { fold } \char_value_lccode:n #1 }
-\cs_new:Npn \@@_change_case:nNN #1#2#3
+\cs_new:Npn \@@_change_case_auxi:nN #1#2
+  { \use:e { \@@_change_case:nnnN #1 #2 } }
+\cs_new:Npn \@@_change_case:nnnN #1#2#3#4
   {
-    \tl_if_exist:cTF { c_@@_ #1 case_ \token_to_str:N #3 _tl }
+    \int_compare:nNnTF {#1} = {`#4}
+      { \exp_not:n {#4} }
       {
-        \@@_change_case_multi:vN
-          { c_@@_ #1 case_ \token_to_str:N #3 _tl } #3
+        \@@_change_case_auxii:nN {#1} {#4}
+        \tl_if_blank:nF {#2}
+          {
+            \@@_change_case_auxii:nN {#2} {#4}
+            \tl_if_blank:nF {#3}
+              { \@@_change_case_auxii:nN {#3} {#4} }
+          }
       }
-      { \exp_args:Nf \@@_change_case:nN { #2 { `#3 } } #3 }
   }
-\cs_new:Npn \@@_change_case:nN #1#2
+\cs_new:Npn \@@_change_case_auxii:nN #1#2
   {
-    \int_compare:nNnTF {#1} = 0
-      { #2 }
-      { \char_generate:nn {#1} { \@@_change_case_catcode:N #2 } }
+    \char_generate:nn {#1}
+      { \@@_change_case_catcode:N #2 }
   }
-\cs_new:Npn \@@_change_case_multi:nN #1#2
-  { \@@_change_case_multi:NNNNw #2 #1 \q_@@_no_value \q_@@_no_value \s_@@_stop }
-\cs_generate_variant:Nn \@@_change_case_multi:nN { v }
-\cs_new:Npn \@@_change_case_multi:NNNNw #1#2#3#4#5 \s_@@_stop
+\bool_lazy_or:nnF
+  { \sys_if_engine_luatex_p: }
+  { \sys_if_engine_xetex_p: }
   {
-    \@@_quark_if_no_value:NTF #4
+    \cs_gset:Npn \@@_change_case_auxii:nN #1#2
       {
-        \@@_quark_if_no_value:NTF #3
-          { \@@_change_case:NN #1 #2 }
-          { \@@_change_case:NNN #1 #2#3 }
+        \int_compare:nNnTF {#1} < { "80 }
+          {
+            \char_generate:nn {#1}
+              { \@@_change_case_catcode:N #2 }
+          }
+          { \exp_not:n {#2} }
       }
-      { \@@_change_case:NNNN #1 #2#3#4 }
   }
-\cs_new:Npn \@@_change_case:NNN #1#2#3
-  {
-    \exp_args:Nnf \use:nn
-      { \@@_change_case:NN #1 #2 }
-      { \@@_change_case:NN #1 #3 }
-  }
-\cs_new:Npn \@@_change_case:NNNN #1#2#3#4
-  {
-    \exp_args:Nnff \use:nnn
-      { \@@_change_case:NN #1 #2 }
-      { \@@_change_case:NN #1 #3 }
-      { \@@_change_case:NN #1 #4 }
-  }
-\cs_new:Npn \@@_change_case:NN #1#2
-  { \char_generate:nn { `#2 } { \@@_change_case_catcode:N #1 } }
 \cs_new:Npn \@@_change_case_catcode:N #1
   {
     \if_catcode:w \exp_not:N #1 \c_math_toggle_token
@@ -2022,47 +1798,43 @@
   }
 %    \end{macrocode}
 %   Same story for the string version, except category code is easier
-%   to follow. This of course makes this version significantly faster.
+%   to follow. This of course makes this version faster.
 %    \begin{macrocode}
-\cs_new:Npn \char_str_lowercase:N #1
-  { \@@_str_change_case:nNN { lower } \char_value_lccode:n #1 }
-\cs_new:Npn \char_str_uppercase:N #1
-  { \@@_str_change_case:nNN { upper } \char_value_uccode:n #1 }
-\cs_new:Npn \char_str_titlecase:N #1
-  {
-    \tl_if_exist:cTF { c_@@_titlecase_ \token_to_str:N #1 _tl }
-      { \tl_to_str:c { c_@@_titlecase_ \token_to_str:N #1 _tl } }
-      { \char_str_uppercase:N #1 }
-  }
-\cs_new:Npn \char_str_foldcase:N #1
-  { \@@_str_change_case:nNN { fold } \char_value_lccode:n #1 }
-\cs_new:Npn \@@_str_change_case:nNN #1#2#3
-  {
-    \tl_if_exist:cTF { c_@@_ #1 case_ \token_to_str:N #3 _tl }
-      { \tl_to_str:c { c_@@_ #1 case_ \token_to_str:N #3 _tl } }
-      { \exp_args:Nf \@@_str_change_case:nN { #2 { `#3 } } #3 }
-  }
+\cs_new:Npn \char_str_lowercase:N
+  { \@@_str_change_case:nN { lowercase } }
+\cs_new:Npn \char_str_uppercase:N
+  { \@@_str_change_case:nN { uppercase } }
+\cs_new:Npn \char_str_titlecase:N
+  { \@@_str_change_case:nN { titlecase } }
+\cs_new:Npn \char_str_foldcase:N
+  { \@@_str_change_case:nN { casefold } }
 \cs_new:Npn \@@_str_change_case:nN #1#2
   {
-    \int_compare:nNnTF {#1} = 0
-      { \tl_to_str:n {#2} }
-      { \char_generate:nn {#1} { 12 } }
+    \int_compare:nNnTF {`#2} = { `\  }
+      { ~ }
+      {
+        \exp_args:Ne \@@_str_change_case_aux:nN
+          { \__kernel_codepoint_case:nn {#1} {`#2} } #2
+      }
   }
-\bool_lazy_or:nnF
-  { \cs_if_exist_p:N \tex_luatexversion:D }
-  { \cs_if_exist_p:N \tex_XeTeXversion:D }
+\cs_new:Npn \@@_str_change_case_aux:nN #1#2
+  { \use:e { \@@_str_change_case:nnnN #1 #2 } }
+\cs_new:Npn \@@_str_change_case:nnnN #1#2#3#4
   {
-    \cs_set:Npn \@@_str_change_case:nN #1#2
+    \int_compare:nNnTF {#1} = {`#4}
+      { \tl_to_str:n {#4} }
       {
-        \int_compare:nNnTF {#1} = 0
-          { \tl_to_str:n {#2} }
+        \@@_str_change_case:n {#1}
+        \tl_if_blank:nF {#2}
           {
-            \int_compare:nTF { `A <= #1 <= `z }
-              { \char_generate:nn {#1} { 12 } }
-              { \tl_to_str:n {#2} }
+            \@@_str_change_case:n {#2}
+            \tl_if_blank:nF {#3}
+              { \@@_str_change_case:n {#3} }
           }
       }
   }
+\cs_new:Npn \@@_str_change_case:n #1
+  { \char_generate:nn {#1} { 12 } }
 %    \end{macrocode}
 % \end{macro}
 % \end{macro}
@@ -2073,9 +1845,6 @@
 % \end{macro}
 % \end{macro}
 % \end{macro}
-% \end{macro}
-% \end{macro}
-% \end{macro}
 %
 % \begin{macro}{\c_catcode_other_space_tl}
 %   Create a space with category code $12$: an \enquote{other} space.

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3unicode.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3unicode.dtx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3unicode.dtx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2022-11-02}
+% \date{Released 2022-12-17}
 %
 % \maketitle
 %
@@ -50,9 +50,66 @@
 % \begin{documentation}
 %
 % This module provides Unicode-specific functions along with loading data
-% from a range of Unicode Consortium files. At present, it provides no
-% public functions.
+% from a range of Unicode Consortium files. Most of the code here is
+% internal, but there are a small set of public functions. These work with
+% Unicode \meta{codepoints} and are designed to give useable results with
+% both Unicode-aware and $8$-bit engines.
 %
+% \begin{function}[EXP, added = 2022-10-09, updated = 2022-11-09]
+%   {\codepoint_generate:nn}
+%   \begin{syntax}
+%      \cs{codepoint_generate:nn} \Arg{codepoint} \Arg{catcode}
+%   \end{syntax}
+%   Generates one or more character tokens representing the \meta{codepoint}.
+%   With Unicode engines, exactly one character token will be generated, and
+%   this will have the \meta{catcode} specified as the second argument:
+%   \begin{itemize}
+%     \item $1$ (begin group)
+%     \item $2$ (end group)
+%     \item $3$ (math toggle)
+%     \item $4$ (alignment)
+%     \item $6$ (parameter)
+%     \item $7$ (math superscript)
+%     \item $8$ (math subscript)
+%     \item $10$ (space)
+%     \item $11$ (letter)
+%     \item $12$ (other)
+%     \item $13$ (active)
+%   \end{itemize}
+%   For $8$-bit engines, between one and four character tokens will be
+%   produced: these will be the bytes of the UTF-8 representation of the
+%   \meta{codepoint}. For all codepoints outside of the classical ASCII
+%   range, the generated character tokens will be active (category code
+%   $13$); for codepoints in the ASCII range, the given \meta{catcode}
+%   will be used. To allow the result of this function to be used
+%   inside a expansion context, the result is protected by \cs{exp_not:n}.
+% \end{function}
+%
+% \begin{function}[EXP, added = 2022-10-09]
+%   {\codepoint_str_generate:n}
+%   \begin{syntax}
+%      \cs{codepoint_str_generate:n} \Arg{codepoint}
+%   \end{syntax}
+%   Generates one or more character tokens representing the \meta{codepoint}.
+%   With Unicode engines, exactly one character token will be generated.
+%   For $8$-bit engines, between one and four character tokens will be
+%   produced: these will be the bytes of the UTF-8 representation of the
+%   \meta{codepoint}. All of the generated character tokens will be of
+%   category code $12$, except any spaces (codepoint $32$), which will be
+%   category code $10$.
+% \end{function}
+%
+% \begin{function}[added = 2022-10-09, EXP]{\codepoint_to_nfd:n}
+%   \begin{syntax}
+%     \cs{codepoint_to_nfd:n} \Arg{codepoint}
+%   \end{syntax}
+%   Converts the \meta{codepoint} to the Unicode Normalization
+%   Form Canonical Decomposition. The generated character(s) will have
+%   the current category code as they would if typed in directly for Unicode
+%   engines; for $8$-bit engines, active characters are used for all codepoints
+%   outside of the ASCII range.
+% \end{function}
+%
 % \end{documentation}
 %
 % \begin{implementation}
@@ -64,122 +121,585 @@
 %    \end{macrocode}
 %
 %    \begin{macrocode}
-%<@@=char>
+%<@@=codepoint>
 %    \end{macrocode}
 %
+% \subsection{User functions}
+%
+% \begin{macro}[EXP]{\codepoint_str_generate:n}
+% \begin{macro}[EXP]{\@@_str_generate:nnnn}
+% \begin{macro}[EXP]{\codepoint_generate:nn}
+% \begin{macro}[EXP]{\@@_generate:nnnn}
+% \begin{macro}[EXP]{\@@_generate:n}
+%   Conversion of a codepoint to a character (Unicode engines) or to one
+%   or more bytes ($8$-bit engines) is required. For loading the data,
+%   all that is needed is the form which creates strings: these are outside
+%   the group as they will also be used when looking up data in the hash
+%   table storage at point-of-use. Later, we will also need functions that
+%   can generate character tokens for document use: those are defined below,
+%   in the data recovery setup.
+%    \begin{macrocode}
+\bool_lazy_or:nnTF
+  { \sys_if_engine_luatex_p: }
+  { \sys_if_engine_xetex_p: }
+  {
+    \cs_new:Npn \codepoint_str_generate:n #1
+      {
+        \int_compare:nNnTF {#1} = { `\  }
+          { ~ }
+          { \char_generate:nn {#1} { 12 } }
+      }
+   \cs_new:Npn \codepoint_generate:nn #1#2
+      {
+        \int_compare:nNnTF {#1} = { `\  }
+          { ~ }
+          {
+            \__kernel_exp_not:w \exp_after:wN \exp_after:wN \exp_after:wN
+              { \char_generate:nn {#1} {#2} }
+          }
+      }
+  }
+  {
+    \cs_new:Npn \codepoint_str_generate:n #1
+      {
+        \int_compare:nNnTF {#1} = { `\  }
+          { ~ }
+          {
+            \use:e
+              {
+                \exp_not:N \@@_str_generate:nnnn
+                  \__kernel_codepoint_to_bytes:n {#1}
+              }
+          }
+      }
+    \cs_new:Npn \@@_str_generate:nnnn #1#2#3#4
+      {
+        \char_generate:nn {#1} { 12 }
+        \tl_if_blank:nF {#2}
+          {
+            \char_generate:nn {#2} { 12 }
+            \tl_if_blank:nF {#3}
+              {
+                \char_generate:nn {#3} { 12 }
+                \tl_if_blank:nF {#4}
+                  { \char_generate:nn {#4} { 12 } }
+              }
+          }
+      }
+    \cs_new:Npn \codepoint_generate:nn #1#2
+      {
+        \int_compare:nNnTF {#1} = { `\  }
+          { ~ }
+          {
+            \int_compare:nNnTF {#1} < { "80 }
+              {
+                \__kernel_exp_not:w \exp_after:wN \exp_after:wN \exp_after:wN
+                  { \char_generate:nn {#1} {#2} }
+              }
+              {
+                \use:e
+                  {
+                    \exp_not:N \@@_generate:nnnn
+                      \__kernel_codepoint_to_bytes:n {#1}
+                  }
+              }
+          }
+      }
+    \cs_new:Npn \@@_generate:nnnn #1#2#3#4
+      {
+        \__kernel_exp_not:w \exp_after:wN
+          {
+            \tex_expanded:D
+              {
+                \@@_generate:n {#1}
+                \@@_generate:n {#2}
+                \tl_if_blank:nF {#3}
+                  {
+                    \@@_generate:n {#3}
+                    \tl_if_blank:nF {#4}
+                      { \@@_generate:n {#4} }
+                  }
+              }
+          }
+      }
+     \cs_new:Npn \@@_generate:n #1
+       {
+         \__kernel_exp_not:w \exp_after:wN \exp_after:wN \exp_after:wN
+           { \char_generate:nn {#1} { 13 } }
+       }
+  }
+%    \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\__kernel_codepoint_to_bytes:n}
+% \begin{macro}[EXP]{\@@_to_bytes_auxi:n}
+% \begin{macro}[EXP]{\@@_to_bytes_auxii:Nnn}
+% \begin{macro}[EXP]{\@@_to_bytes_auxiii:n}
+% \begin{macro}[EXP]
+%   {
+%     \@@_to_bytes_outputi:nw   ,
+%     \@@_to_bytes_outputii:nw  ,
+%     \@@_to_bytes_outputiii:nw ,
+%     \@@_to_bytes_outputiv:nw
+%   }
+% \begin{macro}[EXP]
+%   {\@@_to_bytes_output:nnn, \@@_to_bytes_output:fnn}
+% \begin{macro}[EXP]{\@@_to_bytes_end:}
+%   This code converts a codepoint into the correct UTF-8 representation.
+%   In terms of the algorithm itself, see
+%   \url{https://en.wikipedia.org/wiki/UTF-8} for the octet pattern.
+%    \begin{macrocode}
+\cs_new:Npn \__kernel_codepoint_to_bytes:n #1
+  {
+    \exp_args:Nf \@@_to_bytes_auxi:n
+      { \int_eval:n {#1} }
+  }
+\cs_new:Npn \@@_to_bytes_auxi:n #1
+  {
+    \if_int_compare:w #1 > "80 \exp_stop_f:
+      \if_int_compare:w #1 < "800 \exp_stop_f:
+        \@@_to_bytes_outputi:nw
+          { \@@_to_bytes_auxii:Nnn C {#1} { 64 } }
+        \@@_to_bytes_outputii:nw
+          { \@@_to_bytes_auxiii:n {#1} }
+      \else:
+        \if_int_compare:w #1 < "10000 \exp_stop_f:
+          \@@_to_bytes_outputi:nw
+            { \@@_to_bytes_auxii:Nnn E {#1} { 64 * 64 } }
+          \@@_to_bytes_outputii:nw
+            {
+              \@@_to_bytes_auxiii:n
+                { \int_div_truncate:nn {#1} { 64 } }
+            }
+          \@@_to_bytes_outputiii:nw
+            { \@@_to_bytes_auxiii:n {#1} }
+        \else:
+          \@@_to_bytes_outputi:nw
+            {
+              \@@_to_bytes_auxii:Nnn F
+                 {#1} { 64 * 64 * 64 }
+            }
+          \@@_to_bytes_outputii:nw
+            {
+              \@@_to_bytes_auxiii:n
+                { \int_div_truncate:nn {#1} { 64 * 64 } }
+            }
+          \@@_to_bytes_outputiii:nw
+            {
+              \@@_to_bytes_auxiii:n
+                { \int_div_truncate:nn {#1} { 64 } }
+            }
+          \@@_to_bytes_outputiv:nw
+            { \@@_to_bytes_auxiii:n {#1} }
+        \fi:
+      \fi:
+    \else:
+      \@@_to_bytes_outputi:nw {#1}
+    \fi:
+    \@@_to_bytes_end: { } { } { } { }
+  }
+\cs_new:Npn \@@_to_bytes_auxii:Nnn #1#2#3
+  {  "#10 + \int_div_truncate:nn {#2} {#3} }
+\cs_new:Npn \@@_to_bytes_auxiii:n #1
+  { \int_mod:nn {#1} { 64 } + 128 }
+\cs_new:Npn \@@_to_bytes_outputi:nw
+  #1 #2 \@@_to_bytes_end: #3
+  { \@@_to_bytes_output:fnn { \int_eval:n {#1} } { } {#2} }
+\cs_new:Npn \@@_to_bytes_outputii:nw
+  #1 #2 \@@_to_bytes_end: #3#4
+  { \@@_to_bytes_output:fnn { \int_eval:n {#1} } { {#3} } {#2} }
+\cs_new:Npn \@@_to_bytes_outputiii:nw
+  #1 #2 \@@_to_bytes_end: #3#4#5
+  {
+    \@@_to_bytes_output:fnn
+      { \int_eval:n {#1} } { {#3} {#4} } {#2}
+  }
+\cs_new:Npn \@@_to_bytes_outputiv:nw
+  #1 #2 \@@_to_bytes_end: #3#4#5#6
+  {
+    \@@_to_bytes_output:fnn
+      { \int_eval:n {#1} } { {#3} {#4} {#5} } {#2}
+  }
+\cs_new:Npn \@@_to_bytes_output:nnn #1#2#3
+  {
+    #3
+    \@@_to_bytes_end: #2 {#1}
+  }
+\cs_generate_variant:Nn \@@_to_bytes_output:nnn { f }
+\cs_new:Npn \@@_to_bytes_end: { }
+%    \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\codepoint_to_nfd:n}
+% \begin{macro}[EXP]{\@@_to_nfd:nn}
+% \begin{macro}[EXP]{\@@_to_nfd:nnn}
+% \begin{macro}[EXP]{\@@_to_nfd:nnnn}
+% \begin{macro}[EXP]{\@@_to_nfd_generate:nn}
+% \begin{macro}[EXP]{\@@_to_nfd_generate:n}
+% \begin{macro}[EXP]{\@@_to_nfd_generate:nnnn}
+%   Converted to NFD is a potentially-recursive process: the key is to
+%   check if we get the input codepoint back again. As far as possible,
+%   we use the same path for all engines.
+%    \begin{macrocode}
+\cs_new:Npn \codepoint_to_nfd:n #1
+  { \@@_to_nfd:nn {#1} { \char_value_catcode:n {#1} } }
+\cs_new:Npn \@@_to_nfd:nn #1#2
+  {
+    \exp_args:Ne \@@_to_nfd:nnn
+      { \@@_nfd:n {#1} } {#1} {#2}
+  }
+\cs_new:Npn \@@_to_nfd:nnn #1#2#3 { \@@_to_nfd:nnnn #1 {#2} {#3} }
+\cs_new:Npn \@@_to_nfd:nnnn #1#2#3#4
+  {
+    \int_compare:nNnTF {#1} = {#3}
+      { \codepoint_generate:nn {#1} {#4} }
+      {
+        \@@_to_nfd:nn {#1} {#4}
+        \tl_if_blank:nF {#2}
+          { \@@_to_nfd:nn {#2} {#4} }
+      }
+  }
+%    \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Data loader}
+%
 % Text operations requires data from the Unicode Consortium. Data read into
 % Unicode engine formats is at best a small part of what we need, so there
 % is a loader here to set  up the appropriate data structures.
 %
+% Where we need data for most or all of the Unicode range, we use the two-stage
+% table approach recommended by the Unicode Consortium and demonstrated in a
+% model implementation in Python in
+% \url{https://www.strchr.com/multi-stage_tables}. This approach uses the
+% \texttt{intarray} (\texttt{fontdimen}-based) data type as it is fast for
+% random access and avoids significant hash table usage. In contrast, where
+% only a small subset of codepoints are required, storage as macros is
+% preferable. There is also some consideration of the effort needed to load
+% data: see for example the grapheme breaking information, which would be 
+% problematic to convert into a two-stage table but which can be used with
+% reasonable performance in a small number of comma lists (at the cost that
+% breaking at higher codepoint Hangul characters will be slightly slow).
+%
+% \begin{variable}{\c_@@_block_size_int}
+%   Choosing the block size for the blocks in the two-stage approach is
+%   non-trivial: depending on the data stored, the optimal size for
+%   memory usage will vary. At the same time, for us there is also the
+%   question of load-time: larger blocks require longer comma lists
+%   as intermediates, so are slower. As this is going to be needed
+%   to use the data, we set it up outside of the group for clarity.
+%    \begin{macrocode}
+\int_const:Nn \c_@@_block_size_int { 64 }
+%    \end{macrocode}
+% \end{variable}
+%
+% Parsing the data files can be the same way for all engines, but where they
+% are stored as character tokens, the construction method depends on whether
+% they are Unicode or $8$-bit internally. Parsing is therefore done by common
+% functions, with some data storage using engine-specific auxiliaries.
+%
 % As only the data needs to remain at the end of this process, everything
 % is set up inside a group. The only thing that is outside is creating a
 % stream: they are global anyway and it is best to force a stream for
-% all engines. For performance reasons, some of the code here is very
-% low-level: the material is read during loading \pkg{expl3} in package
-% mode.
+% all engines.
 %
-% Parsing the data files can be the same way for all engines, but the detail
-% of data storage varies depending on whether they are Unicode or $8$-bit
-% internally.Parsing is therefore done by common functions, with the data
-% storage usign engine-specific auxiliaries.
+% \begin{variable}{\g_@@_data_ior}
+%    \begin{macrocode}
+\ior_new:N \g_@@_data_ior
+%    \end{macrocode}
+% \end{variable}
 %
-% Conversion of a codepoint to a character (Unicode engines) or to one
-% or more bytes ($8$-bit engines) is required. We might need those to be
-% detokenized: if not, for Unicode engines they have the current category
-% code.
+% We need some setup for the two-part table approach. The number of blocks we
+% need will be variable, but the resulting size of the stage one table
+% is predictable. For performance reasons, we therefore create the stage one 
+% tables now so they can be used immediately, and will later rename them as a
+% constant tables. For each two-stage table construction, we need a comma
+% list to hold the partial block and a couple of integers to track where
+% we are up to. To avoid burning registers, the latter are stored in macros
+% and are \enquote{fake} integers. We also avoid any \texttt{new} functions,
+% keeping as much as possible local.
+%
+% As we need both positive and negative values, case data requires one
+% two-stage table for each transformation. In contrasts, general Unicode
+% properties could be stored in one table with appropriate combination rules:
+% that is not done at present but is likely to be added over time. Here, all
+% that is needed is additional entries into the comma-list to create the
+% structures.
+%
+% Notice that in the standard \pkg{expl3} way we are indexes position not
+% offset: that does mean a little work later.
 %    \begin{macrocode}
-\ior_new:N \g_@@_data_ior
 \group_begin:
-  \bool_lazy_or:nnTF
-    { \sys_if_engine_luatex_p: }
-    { \sys_if_engine_xetex_p: }
+  \clist_map_inline:nn
+    { uppercase , lowercase }
     {
-      \cs_set:Npn \@@_generate_other:n #1
-        { \tex_detokenize:D \tex_expandafter:D { \tex_Uchar:D #1 } }
-      \cs_set:Npn \@@_generate:n #1
+      \cs_set_nopar:cpn { l_@@_ #1 _block_clist } { }
+      \cs_set_nopar:cpn { l_@@_ #1 _block_tl } { 1 }
+      \cs_set_nopar:cpn { l_@@_ #1 _pos_tl } { 0 }
+      \intarray_new:cn { g_@@_ #1 _index_intarray }
+        { \int_div_truncate:nn { "110000 } \c_@@_block_size_int }
+    }
+%    \end{macrocode}
+%  We need an integer value when matching the current block to those we have
+%  already seen, and a way to track codepoints for handling ranges. Again,
+%  we avoid using up registers or creating global names.
+%    \begin{macrocode}
+  \cs_set_nopar:Npn \l_@@_next_codepoint_fint_tl { 0 }
+  \cs_set_nopar:Npn \l_@@_matched_block_tl { 0 }
+%    \end{macrocode}
+% Parse the main Unicode data file and pull out the NFD and case changing
+% data. The NFD data is stored on using the hash table approach and can yield
+% a predictable number of code points: one or two. We also need the case data,
+% which will be modified further below. To allow for finding rangs, the
+% description of the codepoint needs to be carried forward.
+%    \begin{macrocode}
+  \cs_set_protected:Npn \@@_data_auxi:w
+    #1 ; #2 ; #3 ; #4 ; #5 ; #6 ; #7 ; #8 ; #9 ;
+    {
+      \tl_if_blank:nF {#6}
         {
-          \tex_unexpanded:D \exp_after:wN
-            { \tex_Ucharcat:D #1 ~ \tex_catcode:D #1 ~ }
-        } 
+          \tl_if_head_eq_charcode:nNF {#6}  < % >
+            { \@@_data_auxii:w #1 ; #6 ~ \q_stop }
+        }
+      \@@_data_auxiii:w #1 ; #2 ;
     }
+  \cs_set_protected:Npn \@@_data_auxii:w #1 ; #2 ~ #3 \q_stop
     {
-      \cs_set:Npn \@@_generate_other:n #1
+      \tl_const:cx
+        { c_@@_nfd_ \codepoint_str_generate:n {"#1} _tl }
         {
-          \tex_detokenize:D \tex_expandafter:D
-            { \tex_expanded:D { \@@_generate:n {#1} } }
+          {"#2}
+          { \tl_if_blank:nF {#3} {"#3} }
         }
-      \cs_set:Npn \@@_generate:n #1
+    }
+%    \end{macrocode}
+% The case data is going to be stored as an offset from the parent character,
+% rather than an absolute value. We therefore deal with that plus the situation
+% where a codepoint has no mapping data in oen shot.
+%    \begin{macrocode}
+  \cs_set_protected:Npn \@@_data_auxiii:w
+    #1 ; #2 ; #3 ; #4 ; #5 ; #6 ; #7 ; #8 ~ \q_stop
+    {
+      \use:e
         {
-          \use:e
-            {
-              \exp_not:N \@@_generate:nnnn
-                \char_to_utfviii_bytes:n {#1}
-            }
+          \@@_data_auxiv:w
+            #1 ; #2 ;
+            \@@_data_offset:nn {#1} {#6} ;
+            \@@_data_offset:nn {#1} {#7} ;
+            #8;
         }
-      \cs_set:Npn \@@_generate:nnnn #1#2#3#4
+    }
+  \cs_set:Npn \@@_data_offset:nn #1#2
+    {
+      \tl_if_blank:nTF {#2}
+        { 0 }
+        { \int_eval:n { "#2 - "#1 } }
+    }
+%    \end{macrocode}
+% To deal with ranges, we track the position of the next codepoint expected.
+% If there is a gap, we deal with that separately: it could be a range or
+% an unused part of the Unicode space. As such, we deal with the current
+% codepoint here whether or not there is range to fill in. Upper- and 
+% lowercase data go into the two-stage table, any titlecase exception is
+% just stored in a macro. The data for the codepoint is added to the current
+% block, and if that is now complete we move on to save the block. The
+% case exceptions are all stored as codepoints, with a fixed number of
+% balanced text as we know that there are never more than three.
+%    \begin{macrocode}
+  \cs_set_protected:Npn \@@_data_auxiv:w #1 ; #2 ; #3 ; #4 ; #5 ;
+    {
+      \int_compare:nNnT {"#1} > \l_@@_next_codepoint_fint_tl
         {
-          \tex_unexpanded:D \exp_after:wN \exp_after:wN \exp_after:wN
-            { \char_generate:nn {#1} { 13 } }
-          \tl_if_blank:nF {#2}
+          \@@_data_auxvi:nnnw {#1} {#3} {#4}
+            #2 Last> \q_stop
+        }
+      \@@_add:nn { uppercase } {#3}
+      \@@_add:nn { lowercase } {#4}
+      \int_compare:nNnF {#3} = { \@@_data_offset:nn {#1} {#5} }
+        {
+          \tl_const:cx
+            { c_@@_titlecase_ \codepoint_str_generate:n {"#1} _tl }
+            { {"#5} { } { } }
+        }
+      \tl_set:Nx \l_@@_next_codepoint_fint_tl
+        { \int_eval:n { "#1 + 1 } }
+    }
+  \cs_set_protected:Npn \@@_add:nn #1#2
+    {
+      \clist_put_right:cn { l_@@_ #1 _block_clist } {#2}
+      \int_compare:nNnT { \clist_count:c { l_@@_ #1 _block_clist } }
+        = \c_@@_block_size_int
+        { \@@_save_blocks:nn {#1} { 1 } }
+    }
+%    \end{macrocode}
+%  Distinguish between a range and a gap, and pass on the appropriate value(s).
+%    \begin{macrocode}
+  \cs_set_protected:Npn \@@_data_auxvi:nnnw #1#2#3#4 Last> #5 \q_stop
+    {
+       \tl_if_blank:nTF {#5}
+         {
+           \@@_range:nnn {#1} { uppercase } { 0 }
+           \@@_range:nnn {#1} { lowercase } { 0 }
+         }
+         {
+           \@@_range:nnn {#1} { uppercase } {#2}
+           \@@_range:nnn {#1} { lowercase } {#3}
+         }      
+    }
+%    \end{macrocode}
+%  Calculated the length of the range and the space remaining in the current
+%  block.
+%    \begin{macrocode}
+  \cs_set_protected:Npn \@@_range:nnn #1
+    {
+      \exp_args:Nf \@@_range_aux:nnn
+        { \int_eval:n { "#1 - \l_@@_next_codepoint_fint_tl } }
+    }
+  \cs_set_protected:Npn \@@_range_aux:nnn #1#2
+    {
+      \exp_args:Nf \@@_range:nnnn
+        {
+          \int_min:nn
+            {#1}
             {
-              \tex_unexpanded:D \exp_after:wN \exp_after:wN \exp_after:wN
-                { \char_generate:nn {#2} { 13 } }
-              \tl_if_blank:nF {#3}
-                {
-                  \tex_unexpanded:D \exp_after:wN \exp_after:wN \exp_after:wN
-                    { \char_generate:nn {#3} { 13 } }
-                  \tl_if_blank:nF {#4}
-                    {
-                      \tex_unexpanded:D
-                        \exp_after:wN \exp_after:wN \exp_after:wN
-                          { \char_generate:nn {#4} { 13 } }
-                    }
-                }
+              \c_@@_block_size_int 
+              - \clist_count:c { l_@@_ #2 _block_clist }
             }
         }
+        {#1} {#2}
     }
 %    \end{macrocode}
-% Parse the main Unicode data file for two things. First, we want the titlecase
-% exceptions: the one-to-one lower- and uppercase mappings it contains are all
-% be covered by the \TeX{} data. Second, we need normalization data: at present,
-% just the canonical \textsc{nfd} mappings. Those all yield either one or two
-% codepoints, so the split is relatively easy.
+%   Here we want to do three things: add to and possibly complete the current
+%   block, add complete blocks quickly, then finish up the range in a final
+%   open block. We need to avoid as far as possible avoid dealing with ever
+%   single codepoint, so the middle step is optimised.
 %    \begin{macrocode}
-  \cs_set_protected:Npn \@@_data_auxi:w
-    #1 ; #2 ; #3 ; #4 ; #5 ; #6 ; #7 ; #8 ; #9 ;
+  \cs_set_protected:Npn \@@_range:nnnn #1#2#3#4
     {
-      \tl_if_blank:nF {#6}
+      \prg_replicate:nn {#1}
+        { \clist_put_right:cn { l_@@_ #3 _block_clist } {#4} }
+    \int_compare:nNnT { \clist_count:c { l_@@_ #3 _block_clist } }
+      = \c_@@_block_size_int
+      { \@@_save_blocks:nn {#3} { 1 } }
+     \int_compare:nNnF
+       { \int_div_truncate:nn { #2 - #1 } \c_@@_block_size_int } = 0
+       {
+         \tl_set:cx { l_@@_ #3 _block_clist }
+           {
+             \exp_args:NNe \use:nn \use_none:n
+               { \prg_replicate:nn { \c_@@_block_size_int } { , #4 } }
+           }
+         \@@_save_blocks:nn {#3}
+           { \int_div_truncate:nn { (#2 - #1) } \c_@@_block_size_int }
+        }
+     \prg_replicate:nn
+       { \int_mod:nn { #2 - #1 } \c_@@_block_size_int }
+       { \clist_put_right:cx { l_@@_ #3 _block_clist } {#4} }
+    }
+%    \end{macrocode}
+%   To allow rapid comparison, each completed block is stored locally as a
+%   comma list: once all of the blocks have been created, they are converted
+%   into an \texttt{intarray} in one step. The aim here is to check the current
+%   block against those we've already used, and either match to an existing
+%   block or save a new block.
+%    \begin{macrocode}
+  \cs_set_protected:Npn \@@_save_blocks:nn #1#2
+    {
+      \tl_set_eq:Nc \l_@@_matched_block_tl { l_@@_ #1 _block_tl }
+      \int_step_inline:nn { \tl_use:c { l_@@_ #1 _block_tl } - 1 }
         {
-          \tl_if_head_eq_charcode:nNF {#6}  < % >
-            { \@@_data_auxii:w #1 ; #6 ~ \q_stop }
+          \tl_if_eq:ccT { l_@@_ #1 _block_clist }
+            { l_@@_ #1 _block_ ##1 _clist }
+            { \tl_set:Nn \l_@@_matched_block_tl {##1} }
         }
-      \@@_data_auxiii:w #1 ;
+      \int_compare:nNnT
+        { \tl_use:c { l_@@_ #1 _block_tl } } = \l_@@_matched_block_tl
+          {
+            \clist_set_eq:cc
+              {
+                l_@@_ #1 _block_
+                \tl_use:c { l_@@_ #1 _block_tl } _clist
+              }
+              { l_@@_ #1 _block_clist }
+            \tl_set:cx { l_@@_ #1 _block_tl }
+              { \int_eval:n { \tl_use:c { l_@@_ #1 _block_tl } + 1 } }
+          }
+        \prg_replicate:nn {#2}
+          {
+            \tl_set:cx { l_@@_ #1 _pos_tl }
+              { \int_eval:n { \tl_use:c { l_@@_ #1 _pos_tl } + 1 } }
+            \exp_args:Nc \__kernel_intarray_gset:Nnn
+              { g_@@_ #1 _index_intarray }
+              { \tl_use:c { l_@@_ #1 _pos_tl } }
+              \l_@@_matched_block_tl
+          }
+      \clist_clear:c { l_@@_ #1 _block_clist }
     }
-  \cs_set_protected:Npn \@@_data_auxii:w #1 ; #2 ~ #3 \q_stop
+%    \end{macrocode}
+% Close out the final block, rename the first stage table, then combine all
+% of the block comma-lists into one large second-stage table with offsets.
+% As we use an index not an offet, there is a little back-and-forward to do.
+%    \begin{macrocode}
+  \cs_set_protected:Npn \@@_finalise_blocks:
     {
-      \tl_const:cx
-        { c_@@_nfd_ \@@_generate_other:n { "#1 } _tl }
+      \clist_map_inline:nn { uppercase , lowercase }
         {
-          { \@@_generate:n { "#2 } }
-          {
-            \tl_if_blank:nF {#3}
-              { \@@_generate:n { "#3 } }
-            }
+          \@@_range:nnn { 110000 } {##1} { 0 }
+          \@@_finalise_blocks:n {##1}
         }
+   }
+  \cs_set_protected:Npn \@@_finalise_blocks:n #1
+    {
+      \cs_gset_eq:cc { c_@@_ #1 _index_intarray } { g_@@_ #1 _index_intarray }
+      \cs_undefine:c { g_@@_ #1 _index_intarray }
+      \intarray_new:cn { g_@@_ #1 _blocks_intarray }
+         { ( \tl_use:c { l_@@_ #1 _block_tl } - 1 ) * \c_@@_block_size_int }
+      \int_step_inline:nn { \tl_use:c { l_@@_ #1 _block_tl } - 1 }
+        {
+          \exp_args:Nv \@@_finalise_blocks:nnn
+            { l_@@_ #1 _block_ ##1 _clist }
+            {##1} {#1}
+        }
+      \cs_gset_eq:cc { c_@@_ #1 _blocks_intarray }
+        { g_@@_ #1 _blocks_intarray }
+      \cs_undefine:c { g_@@_ #1 _blocks_intarray }
     }
-  \cs_set_protected:Npn \@@_data_auxiii:w
-    #1 ; #2 ; #3 ; #4 ; #5 ; #6 ; #7 ~ \q_stop
+  \cs_set_protected:Npn \@@_finalise_blocks:nnn #1#2#3
     {
-      \cs_set_nopar:Npn \l_@@_tmpa_tl {#7}
-      \reverse_if:N \if_meaning:w \l_@@_tmpa_tl \c_empty_tl
-        \cs_set_nopar:Npn \l_@@_tmpb_tl {#5}
-        \reverse_if:N \if_meaning:w \l_@@_tmpa_tl \l_@@_tmpb_tl
-          \tl_const:cx
-            { c_@@_titlecase_ \@@_generate_other:n { "#1 } _tl }
-            { \@@_generate:n { "#7 } }
-        \fi:
-      \fi:
+      \exp_args:Nnf \@@_finalise_blocks:nnnw { 1 }
+        { \int_eval:n { ( #2 - 1 ) * \c_@@_block_size_int } }
+        {#3}
+        #1 , \q_recursion_tail , \q_recursion_stop
     }
+  \cs_set_protected:Npn \@@_finalise_blocks:nnnw #1#2#3#4 ,
+    {
+      \quark_if_recursion_tail_stop:n {#4}
+      \intarray_gset:cnn { g_@@_ #3 _blocks_intarray }
+        { #1 + #2 }
+        {#4}
+      \exp_args:Nf \@@_finalise_blocks:nnnw
+        { \int_eval:n { #1 + 1 } } {#2} {#3}
+    }
+%    \end{macrocode}
+%  With the setup done, read the main data file: it's easiest to do that as
+%  a token list with spaces retained.
+%    \begin{macrocode}
   \ior_open:Nn \g_@@_data_ior { UnicodeData.txt }
   \group_begin:
     \char_set_catcode_space:n { `\  }%
@@ -190,8 +710,48 @@
         \fi:
         \exp_after:wN \@@_data_auxi:w \l_@@_tmpa_tl \q_stop
       }%
+    \@@_finalise_blocks:
   \group_end:
+\group_end:
 %    \end{macrocode}
+%
+% \begin{macro}[EXP]{\__kernel_codepoint_data:nn}
+% \begin{macro}[EXP]{\@@_data:nnn}
+%   Recover data from a two-stage table: entirely generic as this applies to
+%   all tables (as we use the same block size for all of them). Notice that
+%   as we use indices not offsets we have to shuffle out-by-one issues. This
+%   function is needed \emph{before} loading the special casing data, as there
+%   we need to be able to check the standard case mappings.
+%    \begin{macrocode}
+\cs_new:Npn \__kernel_codepoint_data:nn #1#2
+  {
+    \exp_args:Nf \@@_data:nnn
+      {
+        \int_eval:n
+          {
+            \c_@@_block_size_int *
+              (
+                \intarray_item:cn { c_@@_ #1 _index_intarray }
+                  {
+                    \int_div_truncate:nn {#2}
+                      \c_@@_block_size_int
+                    + 1
+                  }
+                  - 1
+              )
+          }
+      }
+      {#2} {#1}
+  }
+\cs_new:Npn \@@_data:nnn #1#2#3
+  {
+    \intarray_item:cn { c_@@_ #3 _blocks_intarray }
+      { #1 + \int_mod:nn {#2} \c_@@_block_size_int + 1 }
+  }
+%    \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
 % The other data files all use C-style comments so we have to worry about
 % |#| tokens (and reading as strings). The set up for case folding is in two
 % parts. For the basic (core) mappings, folding is the same as lower casing in
@@ -199,15 +759,17 @@
 % always store the result, splitting up the two or three code points in the input
 % as required.
 %    \begin{macrocode}
+\group_begin:
   \ior_open:Nn \g_@@_data_ior { CaseFolding.txt }
   \cs_set_protected:Npn \@@_data_auxi:w #1 ;~ #2 ;~ #3 ; #4 \q_stop
     {
       \if:w \tl_head:n { #2 ? } C
         \reverse_if:N \if_int_compare:w
-          \char_value_lccode:n {"#1} = "#3 ~
+          \int_eval:n { \__kernel_codepoint_data:nn { lowercase } {"#1} + "#1 }
+            = "#3 ~
           \tl_const:cx
-            { c_@@_foldcase_ \@@_generate_other:n { "#1 } _tl }
-            { \@@_generate:n { "#3 } }
+            { c_@@_casefold_ \codepoint_str_generate:n {"#1} _tl }
+            { {"#3} { } { } }
         \fi:
       \else:
         \if:w \tl_head:n { #2 ? } F
@@ -215,25 +777,17 @@
         \fi:
       \fi:
     }
-  \bool_lazy_or:nnF
-    { \sys_if_engine_luatex_p: }
-    { \sys_if_engine_xetex_p: }
-    {
-      \cs_set_protected:Npn \@@_data_auxi:w #1 ;~ #2 ;~ #3 ; #4 \q_stop
-        {
-          \if:w \tl_head:n { #2 ? } F
-            \@@_data_auxii:w #1 ~ #3 ~ \q_stop
-          \fi:
-        }
-    }
+%    \end{macrocode}
+% Here, |#4| can have have a trailing space, so we tidy up a bit at the cost of
+% speed for these small number of cases it applies to.
+%    \begin{macrocode}
   \cs_set_protected:Npn \@@_data_auxii:w #1 ~ #2 ~ #3 ~ #4 \q_stop
     {
-      \tl_const:cx { c_@@_foldcase_ \@@_generate_other:n { "#1 } _tl }
+      \tl_const:cx { c_@@_casefold_ \codepoint_str_generate:n {"#1} _tl }
         {
-          \@@_generate:n { "#2 }
-          \@@_generate:n { "#3 }
-          \tl_if_blank:nF {#4}
-            { \@@_generate:n { \int_value:w "#4 } }
+          {"#2}
+          {"#3}
+          { \tl_if_blank:nF {#4} { " \int_to_Hex:n {"#4} } }
         }
     }
   \ior_str_map_inline:Nn \g_@@_data_ior
@@ -245,7 +799,7 @@
   \ior_close:N \g_@@_data_ior
 %    \end{macrocode}
 % For upper- and lowercasing special situations, there is a bit more to
-% do as we also have title casing to consider, plus we need to stop part-way
+% do as we also have titlecasing to consider, plus we need to stop part-way
 % through the file.
 %    \begin{macrocode}
   \ior_open:Nn \g_@@_data_ior { SpecialCasing.txt }
@@ -262,12 +816,11 @@
     {
       \tl_if_empty:nF {#4}
         {
-          \tl_const:cx { c_@@_ #2 case_ \@@_generate_other:n { "#1 } _tl }
+          \tl_const:cx { c_@@_ #2 case_ \codepoint_str_generate:n {"#1} _tl }
             {
-              \@@_generate:n { "#3 }
-              \@@_generate:n { "#4 }
-              \tl_if_blank:nF {#5}
-                { \@@_generate:n { "#5 } }
+              {"#3}
+              {"#4}
+              { \tl_if_blank:nF {#5} {"#5} }
             }
         }
     }
@@ -284,10 +837,65 @@
     }
   \ior_close:N \g_@@_data_ior
 \group_end:
-
 %    \end{macrocode}
 %
+% \begin{macro}[EXP]{\__kernel_codepoint_case:nn}
+% \begin{macro}[EXP]{\@@_case:nnn}
+% \begin{macro}[EXP]
+%   {\@@_uppercase:n, \@@_lowercase:n, \@@_titlecase:n, \@@_casefold:n}
+% \begin{macro}[EXP]{\@_case:nn}
+%   With the core data files loaded, there is now a need to provide access to
+%   this information for other modules. That is done here such that case
+%   folding can also be covered. At this level, all that needs to be returned
+%   is the
 %    \begin{macrocode}
+\cs_new:Npn \__kernel_codepoint_case:nn #1#2
+  {
+    \exp_args:Ne \@@_case:nnn
+      { \codepoint_str_generate:n {#2} } {#1} {#2}
+  }
+\cs_new:Npn \@@_case:nnn #1#2#3
+  {
+    \cs_if_exist:cTF { c_@@_ #2 _ #1 _tl }
+      {
+        \tl_use:c
+          { c_@@_ #2 _ #1 _tl }
+      }
+      { \use:c { @@_ #2 :n } {#3} }
+  }
+\cs_new:Npn \@@_uppercase:n { \@@_case:nn { uppercase } }
+\cs_new:Npn \@@_lowercase:n { \@@_case:nn { lowercase } }
+\cs_new:Npn \@@_titlecase:n { \@@_case:nn { uppercase } }
+\cs_new:Npn \@@_casefold:n  { \@@_case:nn { lowercase } }
+\cs_new:Npn \@@_case:nn #1#2
+  {
+    { \int_eval:n { \__kernel_codepoint_data:nn {#1} {#2} + #2 } }
+    { }
+    { }
+  }
+%    \end{macrocode}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_nfd:n}
+% \begin{macro}[EXP]{\@@_nfd:nn}
+%   A simple interface.
+%    \begin{macrocode}
+\cs_new:Npn \@@_nfd:n #1
+  { \exp_args:Ne \@@_nfd:nn { \codepoint_str_generate:n {#1} } {#1} }
+\cs_new:Npn \@@_nfd:nn #1#2
+  {
+    \tl_if_exist:cTF { c_@@_nfd_ #1 _tl }
+      { \tl_use:c { c_@@_nfd_ #1 _tl } }
+      { {#2} { } }
+  }
+%    \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+%    \begin{macrocode}
 %<@@=text>
 %    \end{macrocode}
 %

Modified: trunk/Master/texmf-dist/tex/latex/l3kernel/expl3-code.tex
===================================================================
--- trunk/Master/texmf-dist/tex/latex/l3kernel/expl3-code.tex	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/tex/latex/l3kernel/expl3-code.tex	2022-12-17 20:55:34 UTC (rev 65299)
@@ -71,7 +71,7 @@
 %% and all files in that bundle must be distributed together.
 %% 
 %% File: expl3.dtx
-\def\ExplFileDate{2022-11-02}%
+\def\ExplFileDate{2022-12-17}%
 \begingroup
   \def\next{\endgroup}%
   \expandafter\ifx\csname PackageError\endcsname\relax
@@ -3646,7 +3646,6 @@
       ##1 #2 ##2 #1 ##3 \s__quark {##2}
   }
 \exp_after:wN \__quark_tmp:w \tl_to_str:n { : _quark_if_ } \s__quark
-\cs_gset:Npn \g__scan_marks_tl { }
 \cs_new_protected:Npn \scan_new:N #1
   {
     \tl_if_in:NnTF \g__scan_marks_tl { #1 }
@@ -3660,11 +3659,12 @@
       }
   }
 \cs_new_eq:NN \s_stop \scan_stop:
-\cs_gset_nopar:Npx \g__scan_marks_tl
+\cs_gset_nopar:Npn \g__scan_marks_tl
   {
-    \exp_not:o \g__scan_marks_tl
     \s_stop
     \s__quark
+    \s__cs_mark
+    \s__cs_stop
   }
 \cs_new:Npn \use_none_delimit_by_s_stop:w #1 \s_stop { }
 %% File: l3tl.dtx
@@ -5422,10 +5422,10 @@
     \tl_to_str:n {#1} \s__str_mark \s__str_mark \s__str_stop
   }
 \cs_new:Npn \__str_tail_auxii:w #1 #2 \s__str_mark #3 \s__str_stop { #2 }
-\cs_new:Npn \str_foldcase:n  #1 { \__str_change_case:nn {#1} { fold } }
-\cs_new:Npn \str_lowercase:n #1 { \__str_change_case:nn {#1} { lower } }
-\cs_new:Npn \str_uppercase:n #1 { \__str_change_case:nn {#1} { upper } }
-\cs_generate_variant:Nn \str_foldcase:n  { V }
+\cs_new:Npn \str_casefold:n  #1 { \__str_change_case:nn {#1} { casefold } }
+\cs_new:Npn \str_lowercase:n #1 { \__str_change_case:nn {#1} { lowercase } }
+\cs_new:Npn \str_uppercase:n #1 { \__str_change_case:nn {#1} { uppercase } }
+\cs_generate_variant:Nn \str_casefold:n  { V }
 \cs_generate_variant:Nn \str_lowercase:n { f }
 \cs_generate_variant:Nn \str_uppercase:n { f }
 \cs_new:Npn \__str_change_case:nn #1
@@ -5460,96 +5460,90 @@
   {
     \__str_if_recursion_tail_stop_do:Nn #2
       { \__str_change_case_end:wn }
-    \__str_change_case_output:fw
-      { \use:c { char_str_ #1 case:N } #2 }
-    \__str_change_case_loop:nw {#1}
+    \__str_change_case_codepoint:nN {#1} #2
   }
 \if_int_compare:w 0
   \cs_if_exist:NT \tex_XeTeXversion:D { 1 }
   \cs_if_exist:NT \tex_luatexversion:D { 1 }
   > 0 \exp_stop_f:
+  \cs_new:Npn \__str_change_case_codepoint:nN #1#2
+    { \__str_change_case_char:fnn { \int_eval:n {`#2} } {#1} {#2} }
 \else:
-  \cs_gset:Npn \__str_change_case_char:nN #1#2
-    {
-      \__str_if_recursion_tail_stop_do:Nn #2
-        { \__str_change_case_end:wn }
-      \int_compare:nNnTF { `#2 } > { "80 }
-        {
-          \int_compare:nNnTF { `#2 } < { "E0 }
-            { \__str_change_case_char_UTFviii:nNN }
-            {
-              \int_compare:nNnTF { `#2 } < { "F0 }
-                { \__str_change_case_char_UTFviii:nNNN }
-                { \__str_change_case_char_UTFviii:nNNNN }
-            }
-          {#1} #2
-        }
-        {
-          \__str_change_case_output:fw
-            { \use:c { char_str_ #1 case:N } #2 }
-          \__str_change_case_loop:nw {#1}
-        }
-    }
-  \cs_new:Npn \__str_change_case_char_UTFviii:nNN #1#2#3
-    { \__str_change_case_char_UTFviii:nn {#1} {#2#3} }
-  \cs_new:Npn \__str_change_case_char_UTFviii:nNNN #1#2#3#4
-    { \__str_change_case_char_UTFviii:nn {#1} {#2#3#4} }
-  \cs_new:Npn \__str_change_case_char_UTFviii:nNNNN #1#2#3#4#5
-    { \__str_change_case_char_UTFviii:nn {#1} {#2#3#4#5} }
-  \cs_if_exist:NF \tex_pdftexversion:D
-    {
-      \cs_gset:Npn \__str_change_case_char_UTFviii:nNNN #1#2#3#4
-        {
-          \__str_change_case_output:nw {#2#3#4}
-          \__str_change_case_loop:nw {#1}
-        }
-      \cs_gset:Npn \__str_change_case_char_UTFviii:nNNNN #1#2#3#4#5
-        {
-          \__str_change_case_output:nw {#2#3#4#5}
-          \__str_change_case_loop:nw {#1}
-        }
-    }
-  \cs_new:Npn \__str_change_case_char_UTFviii:nn #1#2
-    {
-      \use:c { __str_change_case_char_UTFviii_ #1 :nn } {#1} {#2}
-    }
-  \cs_new:Npn \__str_change_case_char_UTFviii_upper:nn #1#2
-    {
-      \__str_change_case_output:fw
-        {
-          \cs_if_exist:cTF { c__kernel_ #1 case_ #2 _tl }
-            {
-              \__kernel_tl_to_str:w \exp_after:wN \exp_after:wN \exp_after:wN
-                { \cs:w c__kernel_ #1 case_ #2 _tl \cs_end: }
-            }
-            {#2}
-        }
-      \__str_change_case_loop:nw {#1}
-    }
-  \cs_new_eq:NN \__str_change_case_char_UTFviii_lower:nn
-    \__str_change_case_char_UTFviii_upper:nn
-  \cs_new:Npn \__str_change_case_char_UTFviii_fold:nn #1#2
-    {
-      \__str_change_case_output:fw
-        {
-          \cs_if_exist:cTF { c__kernel_ #1 case_ #2 _tl }
-            {
-              \__kernel_tl_to_str:w \exp_after:wN \exp_after:wN \exp_after:wN
-                { \cs:w c__kernel_ #1 case_ #2 _tl \cs_end: }
-            }
-            {
-              \cs_if_exist:cTF { c__kernel_lowercase_ #2 _tl }
-                {
-                  \__kernel_tl_to_str:w
-                    \exp_after:wN \exp_after:wN \exp_after:wN
-                    { \cs:w c__kernel_lowercase_ #2 _tl \cs_end: }
-                }
-                {#2}
-            }
-        }
-      \__str_change_case_loop:nw {#1}
-    }
+    \cs_new:Npn \__str_change_case_codepoint:nN #1#2
+      {
+        \int_compare:nNnTF {`#2} > { "80 }
+          {
+            \int_compare:nNnTF {`#2} < { "E0 }
+              { \__str_change_case_codepoint:nNN }
+              {
+                 \int_compare:nNnTF {`#2} < { "F0 }
+                   { \__str_change_case_codepoint:nNNN }
+                   { \__str_change_case_codepoint:nNNNNN }
+              }
+          }
+          { \__str_change_case_char_aux:nN }
+            {#1} #2
+      }
+    \cs_new:Npn \__str_change_case_char_aux:nN #1#2
+      { \__str_change_case_char:fnn { \int_eval:n {`#2} } {#1} {#2} }
+    \cs_new:Npn \__str_change_case_codepoint:nNN #1#2#3
+      {
+        \__str_change_case_char:fnn
+          { \int_eval:n { (`#2 - "C0) * "40 + `#3 - "80 } }
+          {#1} {#2#3}
+      }
+    \cs_new:Npn \__str_change_case_codepoint:nNNN #1#2#3#4
+      {
+        \__str_change_case_char:fnn
+          {
+            \int_eval:n
+              { (`#2 - "E0) * "1000 + (`#3 - "80) * "40 + `#4 - "80 }
+          }
+          {#1} {#2#3#4}
+      }
+    \cs_new:Npn \__str_change_case_codepoint:nNNNN #1#2#3#4#5
+      {
+        \__str_change_case_char:fnn
+          {
+            \int_eval:n
+              {
+                  (`#2 - "F0) * "40000
+                + (`#3 - "80) * "1000
+                + (`#4 - "80) * "40
+                + `#5 - "80
+              }
+          }
+          {#1} {#2#3#4#5}
+      }
 \fi:
+\cs_new:Npn \__str_change_case_char:nnn #1#2#3
+  {
+    \__str_change_case_output:fw
+      {
+        \exp_args:Ne \__str_change_case_char_aux:nnn
+          { \__kernel_codepoint_case:nn {#2} {#1} } {#1} {#3}
+      }
+    \__str_change_case_loop:nw {#2}
+  }
+\cs_generate_variant:Nn \__str_change_case_char:nnn { f }
+\cs_new:Npn \__str_change_case_char_aux:nnn #1#2#3
+  {
+    \use:e { \__str_change_case_char:nnnnn #1 {#2} {#3} }
+  }
+\cs_new:Npn \__str_change_case_char:nnnnn #1#2#3#4#5
+  {
+    \int_compare:nNnTF {#1} = {#4}
+      { \tl_to_str:n {#5} }
+      {
+        \codepoint_str_generate:n {#1}
+        \tl_if_blank:nF {#2}
+          {
+            \codepoint_str_generate:n {#2}
+            \tl_if_blank:nF {#3}
+             { \codepoint_str_generate:n {#3} }
+          }
+      }
+  }
 \str_const:Nx \c_ampersand_str   { \cs_to_str:N \& }
 \str_const:Nx \c_atsign_str      { \cs_to_str:N \@ }
 \str_const:Nx \c_backslash_str   { \cs_to_str:N \\ }
@@ -8820,229 +8814,58 @@
         }
   \fi:
 \group_end:
-\cs_new:Npn \char_to_utfviii_bytes:n #1
+\cs_new:Npn \char_lowercase:N
+  { \__char_change_case:nN { lowercase } }
+\cs_new:Npn \char_uppercase:N
+  { \__char_change_case:nN { uppercase } }
+\cs_new:Npn \char_titlecase:N
+  { \__char_change_case:nN { titlecase } }
+\cs_new:Npn \char_foldcase:N
+  { \__char_change_case:nN { casefold } }
+\cs_new:Npn \__char_change_case:nN #1#2
   {
-    \exp_args:Nf \__char_to_utfviii_bytes_auxi:n
-      { \int_eval:n {#1} }
-  }
-\cs_new:Npn \__char_to_utfviii_bytes_auxi:n #1
-  {
-    \if_int_compare:w #1 > "80 \exp_stop_f:
-      \if_int_compare:w #1 < "800 \exp_stop_f:
-        \__char_to_utfviii_bytes_outputi:nw
-          { \__char_to_utfviii_bytes_auxii:Nnn C {#1} { 64 } }
-        \__char_to_utfviii_bytes_outputii:nw
-          { \__char_to_utfviii_bytes_auxiii:n {#1} }
-      \else:
-        \if_int_compare:w #1 < "10000 \exp_stop_f:
-          \__char_to_utfviii_bytes_outputi:nw
-            { \__char_to_utfviii_bytes_auxii:Nnn E {#1} { 64 * 64 } }
-          \__char_to_utfviii_bytes_outputii:nw
-            {
-              \__char_to_utfviii_bytes_auxiii:n
-                { \int_div_truncate:nn {#1} { 64 } }
-            }
-          \__char_to_utfviii_bytes_outputiii:nw
-            { \__char_to_utfviii_bytes_auxiii:n {#1} }
-        \else:
-          \__char_to_utfviii_bytes_outputi:nw
-            {
-              \__char_to_utfviii_bytes_auxii:Nnn F
-                 {#1} { 64 * 64 * 64 }
-            }
-          \__char_to_utfviii_bytes_outputii:nw
-            {
-              \__char_to_utfviii_bytes_auxiii:n
-                { \int_div_truncate:nn {#1} { 64 * 64 } }
-            }
-          \__char_to_utfviii_bytes_outputiii:nw
-            {
-              \__char_to_utfviii_bytes_auxiii:n
-                { \int_div_truncate:nn {#1} { 64 } }
-            }
-          \__char_to_utfviii_bytes_outputiv:nw
-            { \__char_to_utfviii_bytes_auxiii:n {#1} }
-        \fi:
-      \fi:
-    \else:
-      \__char_to_utfviii_bytes_outputi:nw {#1}
-    \fi:
-    \__char_to_utfviii_bytes_end: { } { } { } { }
-  }
-\cs_new:Npn \__char_to_utfviii_bytes_auxii:Nnn #1#2#3
-  {  "#10 + \int_div_truncate:nn {#2} {#3} }
-\cs_new:Npn \__char_to_utfviii_bytes_auxiii:n #1
-  { \int_mod:nn {#1} { 64 } + 128 }
-\cs_new:Npn \__char_to_utfviii_bytes_outputi:nw
-  #1 #2 \__char_to_utfviii_bytes_end: #3
-  { \__char_to_utfviii_bytes_output:fnn { \int_eval:n {#1} } { } {#2} }
-\cs_new:Npn \__char_to_utfviii_bytes_outputii:nw
-  #1 #2 \__char_to_utfviii_bytes_end: #3#4
-  { \__char_to_utfviii_bytes_output:fnn { \int_eval:n {#1} } { {#3} } {#2} }
-\cs_new:Npn \__char_to_utfviii_bytes_outputiii:nw
-  #1 #2 \__char_to_utfviii_bytes_end: #3#4#5
-  {
-    \__char_to_utfviii_bytes_output:fnn
-      { \int_eval:n {#1} } { {#3} {#4} } {#2}
-  }
-\cs_new:Npn \__char_to_utfviii_bytes_outputiv:nw
-  #1 #2 \__char_to_utfviii_bytes_end: #3#4#5#6
-  {
-    \__char_to_utfviii_bytes_output:fnn
-      { \int_eval:n {#1} } { {#3} {#4} {#5} } {#2}
-  }
-\cs_new:Npn \__char_to_utfviii_bytes_output:nnn #1#2#3
-  {
-    #3
-    \__char_to_utfviii_bytes_end: #2 {#1}
-  }
-\cs_generate_variant:Nn \__char_to_utfviii_bytes_output:nnn { f }
-\cs_new:Npn \__char_to_utfviii_bytes_end: { }
-\bool_lazy_or:nnTF
-  { \sys_if_engine_luatex_p: }
-  { \sys_if_engine_xetex_p: }
-  {
-    \cs_new:Npn \char_to_nfd:N #1
+    \int_compare:nNnTF {`#2} = { `\  }
+      { ~ }
       {
-        \cs_if_exist:cTF { c__char_nfd_ \token_to_str:N #1 _ tl }
-          {
-            \exp_after:wN \exp_after:wN \exp_after:wN \__char_to_nfd:Nw
-              \exp_after:wN \exp_after:wN \exp_after:wN #1
-                \cs:w c__char_nfd_ \token_to_str:N #1 _tl \cs_end:
-                  \s__char_stop
-          }
-          { \exp_not:n {#1} }
+        \exp_args:Ne \__char_change_case_auxi:nN
+          { \__kernel_codepoint_case:nn {#1} {`#2} } #2
       }
-    \cs_new_eq:NN \__char_to_nfd:n \char_to_nfd:N
-    \cs_generate_variant:Nn \__char_to_nfd:n { e }
-    \cs_new:Npn \__char_to_nfd:Nw #1#2#3 \s__char_stop
-      {
-        \__char_to_nfd:e
-          { \char_generate:nn { `#2 } { \__char_change_case_catcode:N #1 } }
-        \tl_if_blank:nF {#3}
-          {
-            \__char_to_nfd:e
-               { \char_generate:nn { `#3 } { \char_value_catcode:n { `#3 } } }
-          }
-      }
-    \cs_new:Npn \char_to_nfd:n #1
-      {
-        \__char_to_nfd:e { \char_generate:nn {#1} { \char_value_catcode:n {#1} } }
-      }
   }
+\cs_new:Npn \__char_change_case_auxi:nN #1#2
+  { \use:e { \__char_change_case:nnnN #1 #2 } }
+\cs_new:Npn \__char_change_case:nnnN #1#2#3#4
   {
-    \cs_new:Npn \char_to_nfd:N #1 { \exp_not:n {#1} }
-    \cs_new:Npn \char_to_nfd:n #1
+    \int_compare:nNnTF {#1} = {`#4}
+      { \exp_not:n {#4} }
       {
-        \int_compare:nNnTF {#1} > { "80 }
-          { \exp_args:Ne \__char_to_nfd:n { \__char_nfd_generate:n {#1} } }
-          { \__char_nfd_generate:n {#1} }
-      }
-    \cs_new:Npn \__char_to_nfd:n #1
-      {
-        \cs_if_exist:cTF { c__char_nfd_ \tl_to_str:n {#1} _ tl }
+        \__char_change_case_auxii:nN {#1} {#4}
+        \tl_if_blank:nF {#2}
           {
-            \exp_after:wN \exp_after:wN \exp_after:wN \__char_to_nfd:w
-              \cs:w c__char_nfd_ \tl_to_str:n {#1} _tl \cs_end:
-                \s__char_stop
+            \__char_change_case_auxii:nN {#2} {#4}
+            \tl_if_blank:nF {#3}
+              { \__char_change_case_auxii:nN {#3} {#4} }
           }
-          { \exp_not:n {#1} }
       }
-    \cs_new:Npn \__char_to_nfd:w #1#2 \s__char_stop
-      {
-        \__char_to_nfd:n {#1}
-        \tl_if_blank:nF {#2}
-          { \__char_to_nfd:n {#2} }
-      }
-     \cs_new:Npn \__char_nfd_generate:n #1
-       {
-         \use:e
-           {
-             \exp_not:N \__char_nfd_generate:nnnn
-               \char_to_utfviii_bytes:n {#1}
-           }
-       }
-      \cs_new:Npn \__char_nfd_generate:nnnn #1#2#3#4
-        {
-          \tl_if_blank:nTF {#2}
-            { \char_generate:nn {#1} { \char_value_catcode:n {#1} } }
-            {
-              \exp_after:wN \exp_after:wN \exp_after:wN
-                \exp_not:N \char_generate:nn {#1} { 13 }
-              \exp_after:wN \exp_after:wN \exp_after:wN
-                \exp_not:N \char_generate:nn {#2} { 13 }
-              \tl_if_blank:nF {#3}
-                {
-                  \exp_after:wN \exp_after:wN \exp_after:wN
-                    \exp_not:N \char_generate:nn {#3} { 13 }
-                  \tl_if_blank:nF {#4}
-                    {
-                      \exp_after:wN \exp_after:wN \exp_after:wN
-                        \exp_not:N \char_generate:nn {#4} { 13 }
-                    }
-                }
-            }
-
-        }
   }
-\cs_new:Npn \char_lowercase:N #1
-  { \__char_change_case:nNN { lower } \char_value_lccode:n #1 }
-\cs_new:Npn \char_uppercase:N #1
-  { \__char_change_case:nNN { upper } \char_value_uccode:n #1 }
-\cs_new:Npn \char_titlecase:N #1
+\cs_new:Npn \__char_change_case_auxii:nN #1#2
   {
-    \tl_if_exist:cTF { c__char_titlecase_ \token_to_str:N #1 _tl }
-      {
-        \__char_change_case_multi:vN
-          { c__char_titlecase_ \token_to_str:N #1 _tl } #1
-      }
-      { \char_uppercase:N #1 }
+    \char_generate:nn {#1}
+      { \__char_change_case_catcode:N #2 }
   }
-\cs_new:Npn \char_foldcase:N #1
-  { \__char_change_case:nNN { fold } \char_value_lccode:n #1 }
-\cs_new:Npn \__char_change_case:nNN #1#2#3
+\bool_lazy_or:nnF
+  { \sys_if_engine_luatex_p: }
+  { \sys_if_engine_xetex_p: }
   {
-    \tl_if_exist:cTF { c__char_ #1 case_ \token_to_str:N #3 _tl }
+    \cs_gset:Npn \__char_change_case_auxii:nN #1#2
       {
-        \__char_change_case_multi:vN
-          { c__char_ #1 case_ \token_to_str:N #3 _tl } #3
+        \int_compare:nNnTF {#1} < { "80 }
+          {
+            \char_generate:nn {#1}
+              { \__char_change_case_catcode:N #2 }
+          }
+          { \exp_not:n {#2} }
       }
-      { \exp_args:Nf \__char_change_case:nN { #2 { `#3 } } #3 }
   }
-\cs_new:Npn \__char_change_case:nN #1#2
-  {
-    \int_compare:nNnTF {#1} = 0
-      { #2 }
-      { \char_generate:nn {#1} { \__char_change_case_catcode:N #2 } }
-  }
-\cs_new:Npn \__char_change_case_multi:nN #1#2
-  { \__char_change_case_multi:NNNNw #2 #1 \q__char_no_value \q__char_no_value \s__char_stop }
-\cs_generate_variant:Nn \__char_change_case_multi:nN { v }
-\cs_new:Npn \__char_change_case_multi:NNNNw #1#2#3#4#5 \s__char_stop
-  {
-    \__char_quark_if_no_value:NTF #4
-      {
-        \__char_quark_if_no_value:NTF #3
-          { \__char_change_case:NN #1 #2 }
-          { \__char_change_case:NNN #1 #2#3 }
-      }
-      { \__char_change_case:NNNN #1 #2#3#4 }
-  }
-\cs_new:Npn \__char_change_case:NNN #1#2#3
-  {
-    \exp_args:Nnf \use:nn
-      { \__char_change_case:NN #1 #2 }
-      { \__char_change_case:NN #1 #3 }
-  }
-\cs_new:Npn \__char_change_case:NNNN #1#2#3#4
-  {
-    \exp_args:Nnff \use:nnn
-      { \__char_change_case:NN #1 #2 }
-      { \__char_change_case:NN #1 #3 }
-      { \__char_change_case:NN #1 #4 }
-  }
-\cs_new:Npn \__char_change_case:NN #1#2
-  { \char_generate:nn { `#2 } { \__char_change_case_catcode:N #1 } }
 \cs_new:Npn \__char_change_case_catcode:N #1
   {
     \if_catcode:w \exp_not:N #1 \c_math_toggle_token
@@ -9075,45 +8898,41 @@
       \fi:
     \fi:
   }
-\cs_new:Npn \char_str_lowercase:N #1
-  { \__char_str_change_case:nNN { lower } \char_value_lccode:n #1 }
-\cs_new:Npn \char_str_uppercase:N #1
-  { \__char_str_change_case:nNN { upper } \char_value_uccode:n #1 }
-\cs_new:Npn \char_str_titlecase:N #1
-  {
-    \tl_if_exist:cTF { c__char_titlecase_ \token_to_str:N #1 _tl }
-      { \tl_to_str:c { c__char_titlecase_ \token_to_str:N #1 _tl } }
-      { \char_str_uppercase:N #1 }
-  }
-\cs_new:Npn \char_str_foldcase:N #1
-  { \__char_str_change_case:nNN { fold } \char_value_lccode:n #1 }
-\cs_new:Npn \__char_str_change_case:nNN #1#2#3
-  {
-    \tl_if_exist:cTF { c__char_ #1 case_ \token_to_str:N #3 _tl }
-      { \tl_to_str:c { c__char_ #1 case_ \token_to_str:N #3 _tl } }
-      { \exp_args:Nf \__char_str_change_case:nN { #2 { `#3 } } #3 }
-  }
+\cs_new:Npn \char_str_lowercase:N
+  { \__char_str_change_case:nN { lowercase } }
+\cs_new:Npn \char_str_uppercase:N
+  { \__char_str_change_case:nN { uppercase } }
+\cs_new:Npn \char_str_titlecase:N
+  { \__char_str_change_case:nN { titlecase } }
+\cs_new:Npn \char_str_foldcase:N
+  { \__char_str_change_case:nN { casefold } }
 \cs_new:Npn \__char_str_change_case:nN #1#2
   {
-    \int_compare:nNnTF {#1} = 0
-      { \tl_to_str:n {#2} }
-      { \char_generate:nn {#1} { 12 } }
+    \int_compare:nNnTF {`#2} = { `\  }
+      { ~ }
+      {
+        \exp_args:Ne \__char_str_change_case_aux:nN
+          { \__kernel_codepoint_case:nn {#1} {`#2} } #2
+      }
   }
-\bool_lazy_or:nnF
-  { \cs_if_exist_p:N \tex_luatexversion:D }
-  { \cs_if_exist_p:N \tex_XeTeXversion:D }
+\cs_new:Npn \__char_str_change_case_aux:nN #1#2
+  { \use:e { \__char_str_change_case:nnnN #1 #2 } }
+\cs_new:Npn \__char_str_change_case:nnnN #1#2#3#4
   {
-    \cs_set:Npn \__char_str_change_case:nN #1#2
+    \int_compare:nNnTF {#1} = {`#4}
+      { \tl_to_str:n {#4} }
       {
-        \int_compare:nNnTF {#1} = 0
-          { \tl_to_str:n {#2} }
+        \__char_str_change_case:n {#1}
+        \tl_if_blank:nF {#2}
           {
-            \int_compare:nTF { `A <= #1 <= `z }
-              { \char_generate:nn {#1} { 12 } }
-              { \tl_to_str:n {#2} }
+            \__char_str_change_case:n {#2}
+            \tl_if_blank:nF {#3}
+              { \__char_str_change_case:n {#3} }
           }
       }
   }
+\cs_new:Npn \__char_str_change_case:n #1
+  { \char_generate:nn {#1} { 12 } }
 \tl_const:Nx \c_catcode_other_space_tl { \char_generate:nn { `\  } { 12 } }
 \scan_new:N \s__token_mark
 \scan_new:N \s__token_stop
@@ -15823,7 +15642,7 @@
     \cs_if_exist_use:cF { __fp_parse_word_#2:N }
       {
         \cs_if_exist_use:cF
-          { __fp_parse_caseless_ \str_foldcase:n {#2} :N }
+          { __fp_parse_caseless_ \str_casefold:n {#2} :N }
           {
             \msg_expandable_error:nnn
               { fp } { unknown-fp-word } {#2}
@@ -23201,7 +23020,7 @@
     \cs_new:Npn \__str_convert_pdfname_bytes:n #1
       {
         \exp_args:Ne \__str_convert_pdfname_bytes_aux:n
-          { \char_to_utfviii_bytes:n {`#1} }
+          { \__kernel_codepoint_to_bytes:n {`#1} }
       }
     \cs_new:Npn \__str_convert_pdfname_bytes_aux:n #1
       { \__str_convert_pdfname_bytes_aux:nnnn #1 }
@@ -29550,7 +29369,7 @@
   {
     \exp_args:Nee \__color_model_new:nnn
       { \tl_to_str:n {#1} }
-      { \str_foldcase:n {#2} } {#3}
+      { \str_casefold:n {#2} } {#3}
   }
 \cs_new_protected:Npn \__color_model_new:nnn #1#2#3
   {
@@ -31831,170 +31650,486 @@
 \prop_gput:Nnn \g_msg_module_name_prop { luatex } { LaTeX }
 \prop_gput:Nnn \g_msg_module_type_prop { luatex } { }
 %% File: l3unicode.dtx
-\ior_new:N \g__char_data_ior
+\bool_lazy_or:nnTF
+  { \sys_if_engine_luatex_p: }
+  { \sys_if_engine_xetex_p: }
+  {
+    \cs_new:Npn \codepoint_str_generate:n #1
+      {
+        \int_compare:nNnTF {#1} = { `\  }
+          { ~ }
+          { \char_generate:nn {#1} { 12 } }
+      }
+   \cs_new:Npn \codepoint_generate:nn #1#2
+      {
+        \int_compare:nNnTF {#1} = { `\  }
+          { ~ }
+          {
+            \__kernel_exp_not:w \exp_after:wN \exp_after:wN \exp_after:wN
+              { \char_generate:nn {#1} {#2} }
+          }
+      }
+  }
+  {
+    \cs_new:Npn \codepoint_str_generate:n #1
+      {
+        \int_compare:nNnTF {#1} = { `\  }
+          { ~ }
+          {
+            \use:e
+              {
+                \exp_not:N \__codepoint_str_generate:nnnn
+                  \__kernel_codepoint_to_bytes:n {#1}
+              }
+          }
+      }
+    \cs_new:Npn \__codepoint_str_generate:nnnn #1#2#3#4
+      {
+        \char_generate:nn {#1} { 12 }
+        \tl_if_blank:nF {#2}
+          {
+            \char_generate:nn {#2} { 12 }
+            \tl_if_blank:nF {#3}
+              {
+                \char_generate:nn {#3} { 12 }
+                \tl_if_blank:nF {#4}
+                  { \char_generate:nn {#4} { 12 } }
+              }
+          }
+      }
+    \cs_new:Npn \codepoint_generate:nn #1#2
+      {
+        \int_compare:nNnTF {#1} = { `\  }
+          { ~ }
+          {
+            \int_compare:nNnTF {#1} < { "80 }
+              {
+                \__kernel_exp_not:w \exp_after:wN \exp_after:wN \exp_after:wN
+                  { \char_generate:nn {#1} {#2} }
+              }
+              {
+                \use:e
+                  {
+                    \exp_not:N \__codepoint_generate:nnnn
+                      \__kernel_codepoint_to_bytes:n {#1}
+                  }
+              }
+          }
+      }
+    \cs_new:Npn \__codepoint_generate:nnnn #1#2#3#4
+      {
+        \__kernel_exp_not:w \exp_after:wN
+          {
+            \tex_expanded:D
+              {
+                \__codepoint_generate:n {#1}
+                \__codepoint_generate:n {#2}
+                \tl_if_blank:nF {#3}
+                  {
+                    \__codepoint_generate:n {#3}
+                    \tl_if_blank:nF {#4}
+                      { \__codepoint_generate:n {#4} }
+                  }
+              }
+          }
+      }
+     \cs_new:Npn \__codepoint_generate:n #1
+       {
+         \__kernel_exp_not:w \exp_after:wN \exp_after:wN \exp_after:wN
+           { \char_generate:nn {#1} { 13 } }
+       }
+  }
+\cs_new:Npn \__kernel_codepoint_to_bytes:n #1
+  {
+    \exp_args:Nf \__codepoint_to_bytes_auxi:n
+      { \int_eval:n {#1} }
+  }
+\cs_new:Npn \__codepoint_to_bytes_auxi:n #1
+  {
+    \if_int_compare:w #1 > "80 \exp_stop_f:
+      \if_int_compare:w #1 < "800 \exp_stop_f:
+        \__codepoint_to_bytes_outputi:nw
+          { \__codepoint_to_bytes_auxii:Nnn C {#1} { 64 } }
+        \__codepoint_to_bytes_outputii:nw
+          { \__codepoint_to_bytes_auxiii:n {#1} }
+      \else:
+        \if_int_compare:w #1 < "10000 \exp_stop_f:
+          \__codepoint_to_bytes_outputi:nw
+            { \__codepoint_to_bytes_auxii:Nnn E {#1} { 64 * 64 } }
+          \__codepoint_to_bytes_outputii:nw
+            {
+              \__codepoint_to_bytes_auxiii:n
+                { \int_div_truncate:nn {#1} { 64 } }
+            }
+          \__codepoint_to_bytes_outputiii:nw
+            { \__codepoint_to_bytes_auxiii:n {#1} }
+        \else:
+          \__codepoint_to_bytes_outputi:nw
+            {
+              \__codepoint_to_bytes_auxii:Nnn F
+                 {#1} { 64 * 64 * 64 }
+            }
+          \__codepoint_to_bytes_outputii:nw
+            {
+              \__codepoint_to_bytes_auxiii:n
+                { \int_div_truncate:nn {#1} { 64 * 64 } }
+            }
+          \__codepoint_to_bytes_outputiii:nw
+            {
+              \__codepoint_to_bytes_auxiii:n
+                { \int_div_truncate:nn {#1} { 64 } }
+            }
+          \__codepoint_to_bytes_outputiv:nw
+            { \__codepoint_to_bytes_auxiii:n {#1} }
+        \fi:
+      \fi:
+    \else:
+      \__codepoint_to_bytes_outputi:nw {#1}
+    \fi:
+    \__codepoint_to_bytes_end: { } { } { } { }
+  }
+\cs_new:Npn \__codepoint_to_bytes_auxii:Nnn #1#2#3
+  {  "#10 + \int_div_truncate:nn {#2} {#3} }
+\cs_new:Npn \__codepoint_to_bytes_auxiii:n #1
+  { \int_mod:nn {#1} { 64 } + 128 }
+\cs_new:Npn \__codepoint_to_bytes_outputi:nw
+  #1 #2 \__codepoint_to_bytes_end: #3
+  { \__codepoint_to_bytes_output:fnn { \int_eval:n {#1} } { } {#2} }
+\cs_new:Npn \__codepoint_to_bytes_outputii:nw
+  #1 #2 \__codepoint_to_bytes_end: #3#4
+  { \__codepoint_to_bytes_output:fnn { \int_eval:n {#1} } { {#3} } {#2} }
+\cs_new:Npn \__codepoint_to_bytes_outputiii:nw
+  #1 #2 \__codepoint_to_bytes_end: #3#4#5
+  {
+    \__codepoint_to_bytes_output:fnn
+      { \int_eval:n {#1} } { {#3} {#4} } {#2}
+  }
+\cs_new:Npn \__codepoint_to_bytes_outputiv:nw
+  #1 #2 \__codepoint_to_bytes_end: #3#4#5#6
+  {
+    \__codepoint_to_bytes_output:fnn
+      { \int_eval:n {#1} } { {#3} {#4} {#5} } {#2}
+  }
+\cs_new:Npn \__codepoint_to_bytes_output:nnn #1#2#3
+  {
+    #3
+    \__codepoint_to_bytes_end: #2 {#1}
+  }
+\cs_generate_variant:Nn \__codepoint_to_bytes_output:nnn { f }
+\cs_new:Npn \__codepoint_to_bytes_end: { }
+\cs_new:Npn \codepoint_to_nfd:n #1
+  { \__codepoint_to_nfd:nn {#1} { \char_value_catcode:n {#1} } }
+\cs_new:Npn \__codepoint_to_nfd:nn #1#2
+  {
+    \exp_args:Ne \__codepoint_to_nfd:nnn
+      { \__codepoint_nfd:n {#1} } {#1} {#2}
+  }
+\cs_new:Npn \__codepoint_to_nfd:nnn #1#2#3 { \__codepoint_to_nfd:nnnn #1 {#2} {#3} }
+\cs_new:Npn \__codepoint_to_nfd:nnnn #1#2#3#4
+  {
+    \int_compare:nNnTF {#1} = {#3}
+      { \codepoint_generate:nn {#1} {#4} }
+      {
+        \__codepoint_to_nfd:nn {#1} {#4}
+        \tl_if_blank:nF {#2}
+          { \__codepoint_to_nfd:nn {#2} {#4} }
+      }
+  }
+\int_const:Nn \c__codepoint_block_size_int { 64 }
+\ior_new:N \g__codepoint_data_ior
 \group_begin:
-  \bool_lazy_or:nnTF
-    { \sys_if_engine_luatex_p: }
-    { \sys_if_engine_xetex_p: }
+  \clist_map_inline:nn
+    { uppercase , lowercase }
     {
-      \cs_set:Npn \__char_generate_other:n #1
-        { \tex_detokenize:D \tex_expandafter:D { \tex_Uchar:D #1 } }
-      \cs_set:Npn \__char_generate:n #1
+      \cs_set_nopar:cpn { l__codepoint_ #1 _block_clist } { }
+      \cs_set_nopar:cpn { l__codepoint_ #1 _block_tl } { 1 }
+      \cs_set_nopar:cpn { l__codepoint_ #1 _pos_tl } { 0 }
+      \intarray_new:cn { g__codepoint_ #1 _index_intarray }
+        { \int_div_truncate:nn { "110000 } \c__codepoint_block_size_int }
+    }
+  \cs_set_nopar:Npn \l__codepoint_next_codepoint_fint_tl { 0 }
+  \cs_set_nopar:Npn \l__codepoint_matched_block_tl { 0 }
+  \cs_set_protected:Npn \__codepoint_data_auxi:w
+    #1 ; #2 ; #3 ; #4 ; #5 ; #6 ; #7 ; #8 ; #9 ;
+    {
+      \tl_if_blank:nF {#6}
         {
-          \tex_unexpanded:D \exp_after:wN
-            { \tex_Ucharcat:D #1 ~ \tex_catcode:D #1 ~ }
+          \tl_if_head_eq_charcode:nNF {#6}  < % >
+            { \__codepoint_data_auxii:w #1 ; #6 ~ \q_stop }
         }
+      \__codepoint_data_auxiii:w #1 ; #2 ;
     }
+  \cs_set_protected:Npn \__codepoint_data_auxii:w #1 ; #2 ~ #3 \q_stop
     {
-      \cs_set:Npn \__char_generate_other:n #1
+      \tl_const:cx
+        { c__codepoint_nfd_ \codepoint_str_generate:n {"#1} _tl }
         {
-          \tex_detokenize:D \tex_expandafter:D
-            { \tex_expanded:D { \__char_generate:n {#1} } }
+          {"#2}
+          { \tl_if_blank:nF {#3} {"#3} }
         }
-      \cs_set:Npn \__char_generate:n #1
+    }
+  \cs_set_protected:Npn \__codepoint_data_auxiii:w
+    #1 ; #2 ; #3 ; #4 ; #5 ; #6 ; #7 ; #8 ~ \q_stop
+    {
+      \use:e
         {
-          \use:e
-            {
-              \exp_not:N \__char_generate:nnnn
-                \char_to_utfviii_bytes:n {#1}
-            }
+          \__codepoint_data_auxiv:w
+            #1 ; #2 ;
+            \__codepoint_data_offset:nn {#1} {#6} ;
+            \__codepoint_data_offset:nn {#1} {#7} ;
+            #8;
         }
-      \cs_set:Npn \__char_generate:nnnn #1#2#3#4
+    }
+  \cs_set:Npn \__codepoint_data_offset:nn #1#2
+    {
+      \tl_if_blank:nTF {#2}
+        { 0 }
+        { \int_eval:n { "#2 - "#1 } }
+    }
+  \cs_set_protected:Npn \__codepoint_data_auxiv:w #1 ; #2 ; #3 ; #4 ; #5 ;
+    {
+      \int_compare:nNnT {"#1} > \l__codepoint_next_codepoint_fint_tl
         {
-          \tex_unexpanded:D \exp_after:wN \exp_after:wN \exp_after:wN
-            { \char_generate:nn {#1} { 13 } }
-          \tl_if_blank:nF {#2}
+          \__codepoint_data_auxvi:nnnw {#1} {#3} {#4}
+            #2 Last> \q_stop
+        }
+      \__codepoint_add:nn { uppercase } {#3}
+      \__codepoint_add:nn { lowercase } {#4}
+      \int_compare:nNnF {#3} = { \__codepoint_data_offset:nn {#1} {#5} }
+        {
+          \tl_const:cx
+            { c__codepoint_titlecase_ \codepoint_str_generate:n {"#1} _tl }
+            { {"#5} { } { } }
+        }
+      \tl_set:Nx \l__codepoint_next_codepoint_fint_tl
+        { \int_eval:n { "#1 + 1 } }
+    }
+  \cs_set_protected:Npn \__codepoint_add:nn #1#2
+    {
+      \clist_put_right:cn { l__codepoint_ #1 _block_clist } {#2}
+      \int_compare:nNnT { \clist_count:c { l__codepoint_ #1 _block_clist } }
+        = \c__codepoint_block_size_int
+        { \__codepoint_save_blocks:nn {#1} { 1 } }
+    }
+  \cs_set_protected:Npn \__codepoint_data_auxvi:nnnw #1#2#3#4 Last> #5 \q_stop
+    {
+       \tl_if_blank:nTF {#5}
+         {
+           \__codepoint_range:nnn {#1} { uppercase } { 0 }
+           \__codepoint_range:nnn {#1} { lowercase } { 0 }
+         }
+         {
+           \__codepoint_range:nnn {#1} { uppercase } {#2}
+           \__codepoint_range:nnn {#1} { lowercase } {#3}
+         }
+    }
+  \cs_set_protected:Npn \__codepoint_range:nnn #1
+    {
+      \exp_args:Nf \__codepoint_range_aux:nnn
+        { \int_eval:n { "#1 - \l__codepoint_next_codepoint_fint_tl } }
+    }
+  \cs_set_protected:Npn \__codepoint_range_aux:nnn #1#2
+    {
+      \exp_args:Nf \__codepoint_range:nnnn
+        {
+          \int_min:nn
+            {#1}
             {
-              \tex_unexpanded:D \exp_after:wN \exp_after:wN \exp_after:wN
-                { \char_generate:nn {#2} { 13 } }
-              \tl_if_blank:nF {#3}
-                {
-                  \tex_unexpanded:D \exp_after:wN \exp_after:wN \exp_after:wN
-                    { \char_generate:nn {#3} { 13 } }
-                  \tl_if_blank:nF {#4}
-                    {
-                      \tex_unexpanded:D
-                        \exp_after:wN \exp_after:wN \exp_after:wN
-                          { \char_generate:nn {#4} { 13 } }
-                    }
-                }
+              \c__codepoint_block_size_int
+              - \clist_count:c { l__codepoint_ #2 _block_clist }
             }
         }
+        {#1} {#2}
     }
-  \cs_set_protected:Npn \__char_data_auxi:w
-    #1 ; #2 ; #3 ; #4 ; #5 ; #6 ; #7 ; #8 ; #9 ;
+  \cs_set_protected:Npn \__codepoint_range:nnnn #1#2#3#4
     {
-      \tl_if_blank:nF {#6}
+      \prg_replicate:nn {#1}
+        { \clist_put_right:cn { l__codepoint_ #3 _block_clist } {#4} }
+    \int_compare:nNnT { \clist_count:c { l__codepoint_ #3 _block_clist } }
+      = \c__codepoint_block_size_int
+      { \__codepoint_save_blocks:nn {#3} { 1 } }
+     \int_compare:nNnF
+       { \int_div_truncate:nn { #2 - #1 } \c__codepoint_block_size_int } = 0
+       {
+         \tl_set:cx { l__codepoint_ #3 _block_clist }
+           {
+             \exp_args:NNe \use:nn \use_none:n
+               { \prg_replicate:nn { \c__codepoint_block_size_int } { , #4 } }
+           }
+         \__codepoint_save_blocks:nn {#3}
+           { \int_div_truncate:nn { (#2 - #1) } \c__codepoint_block_size_int }
+        }
+     \prg_replicate:nn
+       { \int_mod:nn { #2 - #1 } \c__codepoint_block_size_int }
+       { \clist_put_right:cx { l__codepoint_ #3 _block_clist } {#4} }
+    }
+  \cs_set_protected:Npn \__codepoint_save_blocks:nn #1#2
+    {
+      \tl_set_eq:Nc \l__codepoint_matched_block_tl { l__codepoint_ #1 _block_tl }
+      \int_step_inline:nn { \tl_use:c { l__codepoint_ #1 _block_tl } - 1 }
         {
-          \tl_if_head_eq_charcode:nNF {#6}  < % >
-            { \__char_data_auxii:w #1 ; #6 ~ \q_stop }
+          \tl_if_eq:ccT { l__codepoint_ #1 _block_clist }
+            { l__codepoint_ #1 _block_ ##1 _clist }
+            { \tl_set:Nn \l__codepoint_matched_block_tl {##1} }
         }
-      \__char_data_auxiii:w #1 ;
+      \int_compare:nNnT
+        { \tl_use:c { l__codepoint_ #1 _block_tl } } = \l__codepoint_matched_block_tl
+          {
+            \clist_set_eq:cc
+              {
+                l__codepoint_ #1 _block_
+                \tl_use:c { l__codepoint_ #1 _block_tl } _clist
+              }
+              { l__codepoint_ #1 _block_clist }
+            \tl_set:cx { l__codepoint_ #1 _block_tl }
+              { \int_eval:n { \tl_use:c { l__codepoint_ #1 _block_tl } + 1 } }
+          }
+        \prg_replicate:nn {#2}
+          {
+            \tl_set:cx { l__codepoint_ #1 _pos_tl }
+              { \int_eval:n { \tl_use:c { l__codepoint_ #1 _pos_tl } + 1 } }
+            \exp_args:Nc \__kernel_intarray_gset:Nnn
+              { g__codepoint_ #1 _index_intarray }
+              { \tl_use:c { l__codepoint_ #1 _pos_tl } }
+              \l__codepoint_matched_block_tl
+          }
+      \clist_clear:c { l__codepoint_ #1 _block_clist }
     }
-  \cs_set_protected:Npn \__char_data_auxii:w #1 ; #2 ~ #3 \q_stop
+  \cs_set_protected:Npn \__codepoint_finalise_blocks:
     {
-      \tl_const:cx
-        { c__char_nfd_ \__char_generate_other:n { "#1 } _tl }
+      \clist_map_inline:nn { uppercase , lowercase }
         {
-          { \__char_generate:n { "#2 } }
-          {
-            \tl_if_blank:nF {#3}
-              { \__char_generate:n { "#3 } }
-            }
+          \__codepoint_range:nnn { 110000 } {##1} { 0 }
+          \__codepoint_finalise_blocks:n {##1}
         }
+   }
+  \cs_set_protected:Npn \__codepoint_finalise_blocks:n #1
+    {
+      \cs_gset_eq:cc { c__codepoint_ #1 _index_intarray } { g__codepoint_ #1 _index_intarray }
+      \cs_undefine:c { g__codepoint_ #1 _index_intarray }
+      \intarray_new:cn { g__codepoint_ #1 _blocks_intarray }
+         { ( \tl_use:c { l__codepoint_ #1 _block_tl } - 1 ) * \c__codepoint_block_size_int }
+      \int_step_inline:nn { \tl_use:c { l__codepoint_ #1 _block_tl } - 1 }
+        {
+          \exp_args:Nv \__codepoint_finalise_blocks:nnn
+            { l__codepoint_ #1 _block_ ##1 _clist }
+            {##1} {#1}
+        }
+      \cs_gset_eq:cc { c__codepoint_ #1 _blocks_intarray }
+        { g__codepoint_ #1 _blocks_intarray }
+      \cs_undefine:c { g__codepoint_ #1 _blocks_intarray }
     }
-  \cs_set_protected:Npn \__char_data_auxiii:w
-    #1 ; #2 ; #3 ; #4 ; #5 ; #6 ; #7 ~ \q_stop
+  \cs_set_protected:Npn \__codepoint_finalise_blocks:nnn #1#2#3
     {
-      \cs_set_nopar:Npn \l__char_tmpa_tl {#7}
-      \reverse_if:N \if_meaning:w \l__char_tmpa_tl \c_empty_tl
-        \cs_set_nopar:Npn \l__char_tmpb_tl {#5}
-        \reverse_if:N \if_meaning:w \l__char_tmpa_tl \l__char_tmpb_tl
-          \tl_const:cx
-            { c__char_titlecase_ \__char_generate_other:n { "#1 } _tl }
-            { \__char_generate:n { "#7 } }
-        \fi:
-      \fi:
+      \exp_args:Nnf \__codepoint_finalise_blocks:nnnw { 1 }
+        { \int_eval:n { ( #2 - 1 ) * \c__codepoint_block_size_int } }
+        {#3}
+        #1 , \q_recursion_tail , \q_recursion_stop
     }
-  \ior_open:Nn \g__char_data_ior { UnicodeData.txt }
+  \cs_set_protected:Npn \__codepoint_finalise_blocks:nnnw #1#2#3#4 ,
+    {
+      \quark_if_recursion_tail_stop:n {#4}
+      \intarray_gset:cnn { g__codepoint_ #3 _blocks_intarray }
+        { #1 + #2 }
+        {#4}
+      \exp_args:Nf \__codepoint_finalise_blocks:nnnw
+        { \int_eval:n { #1 + 1 } } {#2} {#3}
+    }
+  \ior_open:Nn \g__codepoint_data_ior { UnicodeData.txt }
   \group_begin:
     \char_set_catcode_space:n { `\  }%
-    \ior_map_variable:NNn \g__char_data_ior \l__char_tmpa_tl
+    \ior_map_variable:NNn \g__codepoint_data_ior \l__codepoint_tmpa_tl
       {%
-        \if_meaning:w \l__char_tmpa_tl \c_space_tl
+        \if_meaning:w \l__codepoint_tmpa_tl \c_space_tl
           \exp_after:wN \ior_map_break:
         \fi:
-        \exp_after:wN \__char_data_auxi:w \l__char_tmpa_tl \q_stop
+        \exp_after:wN \__codepoint_data_auxi:w \l__codepoint_tmpa_tl \q_stop
       }%
+    \__codepoint_finalise_blocks:
   \group_end:
-  \ior_open:Nn \g__char_data_ior { CaseFolding.txt }
-  \cs_set_protected:Npn \__char_data_auxi:w #1 ;~ #2 ;~ #3 ; #4 \q_stop
+\group_end:
+\cs_new:Npn \__kernel_codepoint_data:nn #1#2
+  {
+    \exp_args:Nf \__codepoint_data:nnn
+      {
+        \int_eval:n
+          {
+            \c__codepoint_block_size_int *
+              (
+                \intarray_item:cn { c__codepoint_ #1 _index_intarray }
+                  {
+                    \int_div_truncate:nn {#2}
+                      \c__codepoint_block_size_int
+                    + 1
+                  }
+                  - 1
+              )
+          }
+      }
+      {#2} {#1}
+  }
+\cs_new:Npn \__codepoint_data:nnn #1#2#3
+  {
+    \intarray_item:cn { c__codepoint_ #3 _blocks_intarray }
+      { #1 + \int_mod:nn {#2} \c__codepoint_block_size_int + 1 }
+  }
+\group_begin:
+  \ior_open:Nn \g__codepoint_data_ior { CaseFolding.txt }
+  \cs_set_protected:Npn \__codepoint_data_auxi:w #1 ;~ #2 ;~ #3 ; #4 \q_stop
     {
       \if:w \tl_head:n { #2 ? } C
         \reverse_if:N \if_int_compare:w
-          \char_value_lccode:n {"#1} = "#3 ~
+          \int_eval:n { \__kernel_codepoint_data:nn { lowercase } {"#1} + "#1 }
+            = "#3 ~
           \tl_const:cx
-            { c__char_foldcase_ \__char_generate_other:n { "#1 } _tl }
-            { \__char_generate:n { "#3 } }
+            { c__codepoint_casefold_ \codepoint_str_generate:n {"#1} _tl }
+            { {"#3} { } { } }
         \fi:
       \else:
         \if:w \tl_head:n { #2 ? } F
-          \__char_data_auxii:w #1 ~ #3 ~ \q_stop
+          \__codepoint_data_auxii:w #1 ~ #3 ~ \q_stop
         \fi:
       \fi:
     }
-  \bool_lazy_or:nnF
-    { \sys_if_engine_luatex_p: }
-    { \sys_if_engine_xetex_p: }
+  \cs_set_protected:Npn \__codepoint_data_auxii:w #1 ~ #2 ~ #3 ~ #4 \q_stop
     {
-      \cs_set_protected:Npn \__char_data_auxi:w #1 ;~ #2 ;~ #3 ; #4 \q_stop
+      \tl_const:cx { c__codepoint_casefold_ \codepoint_str_generate:n {"#1} _tl }
         {
-          \if:w \tl_head:n { #2 ? } F
-            \__char_data_auxii:w #1 ~ #3 ~ \q_stop
-          \fi:
+          {"#2}
+          {"#3}
+          { \tl_if_blank:nF {#4} { " \int_to_Hex:n {"#4} } }
         }
     }
-  \cs_set_protected:Npn \__char_data_auxii:w #1 ~ #2 ~ #3 ~ #4 \q_stop
+  \ior_str_map_inline:Nn \g__codepoint_data_ior
     {
-      \tl_const:cx { c__char_foldcase_ \__char_generate_other:n { "#1 } _tl }
-        {
-          \__char_generate:n { "#2 }
-          \__char_generate:n { "#3 }
-          \tl_if_blank:nF {#4}
-            { \__char_generate:n { \int_value:w "#4 } }
-        }
-    }
-  \ior_str_map_inline:Nn \g__char_data_ior
-    {
       \reverse_if:N \if:w \c_hash_str \tl_head:w #1 \c_hash_str \q_stop
-        \__char_data_auxi:w #1 \q_stop
+        \__codepoint_data_auxi:w #1 \q_stop
       \fi:
     }
-  \ior_close:N \g__char_data_ior
-  \ior_open:Nn \g__char_data_ior { SpecialCasing.txt }
-  \cs_set_protected:Npn \__char_data_auxi:w
+  \ior_close:N \g__codepoint_data_ior
+  \ior_open:Nn \g__codepoint_data_ior { SpecialCasing.txt }
+  \cs_set_protected:Npn \__codepoint_data_auxi:w
     #1 ;~ #2 ;~ #3 ;~ #4 ; #5 \q_stop
     {
-      \use:n { \__char_data_auxii:w #1 ~ lower ~ #2 ~ } ~ \q_stop
-      \use:n { \__char_data_auxii:w #1 ~ upper ~ #4 ~ } ~ \q_stop
+      \use:n { \__codepoint_data_auxii:w #1 ~ lower ~ #2 ~ } ~ \q_stop
+      \use:n { \__codepoint_data_auxii:w #1 ~ upper ~ #4 ~ } ~ \q_stop
       \str_if_eq:nnF {#3} {#4}
-        { \use:n { \__char_data_auxii:w #1 ~ title ~ #3 ~ } ~ \q_stop }
+        { \use:n { \__codepoint_data_auxii:w #1 ~ title ~ #3 ~ } ~ \q_stop }
     }
-  \cs_set_protected:Npn \__char_data_auxii:w
+  \cs_set_protected:Npn \__codepoint_data_auxii:w
     #1 ~ #2 ~ #3 ~ #4 ~ #5 \q_stop
     {
       \tl_if_empty:nF {#4}
         {
-          \tl_const:cx { c__char_ #2 case_ \__char_generate_other:n { "#1 } _tl }
+          \tl_const:cx { c__codepoint_ #2 case_ \codepoint_str_generate:n {"#1} _tl }
             {
-              \__char_generate:n { "#3 }
-              \__char_generate:n { "#4 }
-              \tl_if_blank:nF {#5}
-                { \__char_generate:n { "#5 } }
+              {"#3}
+              {"#4}
+              { \tl_if_blank:nF {#5} {"#5} }
             }
         }
     }
-  \ior_str_map_inline:Nn \g__char_data_ior
+  \ior_str_map_inline:Nn \g__codepoint_data_ior
     {
       \str_if_eq:eeTF { \tl_head:w #1 \c_hash_str \q_stop } { \c_hash_str }
         {
@@ -32003,11 +32138,42 @@
             { \c_hash_str \c_space_tl Conditional~Mappings }
             { \ior_map_break: }
         }
-        { \__char_data_auxi:w #1 \q_stop }
+        { \__codepoint_data_auxi:w #1 \q_stop }
     }
-  \ior_close:N \g__char_data_ior
+  \ior_close:N \g__codepoint_data_ior
 \group_end:
-
+\cs_new:Npn \__kernel_codepoint_case:nn #1#2
+  {
+    \exp_args:Ne \__codepoint_case:nnn
+      { \codepoint_str_generate:n {#2} } {#1} {#2}
+  }
+\cs_new:Npn \__codepoint_case:nnn #1#2#3
+  {
+    \cs_if_exist:cTF { c__codepoint_ #2 _ #1 _tl }
+      {
+        \tl_use:c
+          { c__codepoint_ #2 _ #1 _tl }
+      }
+      { \use:c { __codepoint_ #2 :n } {#3} }
+  }
+\cs_new:Npn \__codepoint_uppercase:n { \__codepoint_case:nn { uppercase } }
+\cs_new:Npn \__codepoint_lowercase:n { \__codepoint_case:nn { lowercase } }
+\cs_new:Npn \__codepoint_titlecase:n { \__codepoint_case:nn { uppercase } }
+\cs_new:Npn \__codepoint_casefold:n  { \__codepoint_case:nn { lowercase } }
+\cs_new:Npn \__codepoint_case:nn #1#2
+  {
+    { \int_eval:n { \__kernel_codepoint_data:nn {#1} {#2} + #2 } }
+    { }
+    { }
+  }
+\cs_new:Npn \__codepoint_nfd:n #1
+  { \exp_args:Ne \__codepoint_nfd:nn { \codepoint_str_generate:n {#1} } {#1} }
+\cs_new:Npn \__codepoint_nfd:nn #1#2
+  {
+    \tl_if_exist:cTF { c__codepoint_nfd_ #1 _tl }
+      { \tl_use:c { c__codepoint_nfd_ #1 _tl } }
+      { {#2} { } }
+  }
 \ior_new:N \g__text_data_ior
 \group_begin:
   \ior_open:Nn \g__text_data_ior { GraphemeBreakProperty.txt }
@@ -32224,10 +32390,26 @@
       }
     \cs_new:Npn \__text_codepoint_process:nNN #1#2#3
       { #1 {#2#3} }
-    \cs_new:Npn \__text_codepoint_process:nNNN #1#2#3#4
-      { #1 {#2#3#4} }
-    \cs_new:Npn \__text_codepoint_process:nNNNN #1#2#3#4#5
-      { #1 {#2#3#4#5} }
+    \sys_if_engine_ptex:TF
+      {
+        \cs_gset:Npn \__text_codepoint_process:nN #1#2
+          {
+            \int_compare:nNnTF { `#2 } > { "80 }
+              {
+                \int_compare:nNnTF { `#2 } < { "E0 }
+                  { \__text_codepoint_process:nNN }
+                  { \use:n }
+              }
+          { \use:n }
+            {#1} #2
+        }
+      }
+      {
+        \cs_new:Npn \__text_codepoint_process:nNNN #1#2#3#4
+          { #1 {#2#3#4} }
+        \cs_new:Npn \__text_codepoint_process:nNNNN #1#2#3#4#5
+          { #1 {#2#3#4#5} }
+      }
   }
 \bool_lazy_or:nnTF
   { \sys_if_engine_luatex_p: }
@@ -32236,52 +32418,76 @@
     \prg_new_conditional:Npnn
       \__text_codepoint_compare:nNn #1#2#3 { TF , p }
       {
-        \int_compare:nNnTF { `#1 } #2 {#3}
+        \int_compare:nNnTF {`#1} #2 {#3}
           \prg_return_true: \prg_return_false:
       }
+    \cs_new:Npn \__text_codepoint_from_chars:Nw #1 {`#1}
   }
   {
     \prg_new_conditional:Npnn
       \__text_codepoint_compare:nNn #1#2#3 { TF , p }
       {
-        \int_compare:nNnTF { \__text_codepoint_compare:N #1 }
+        \int_compare:nNnTF { \__text_codepoint_from_chars:Nw #1 }
             #2 {#3}
           \prg_return_true: \prg_return_false:
       }
-    \cs_new:Npn \__text_codepoint_compare:N #1
+    \cs_new:Npn \__text_codepoint_from_chars:Nw #1
       {
         \if_int_compare:w `#1 > "80 \exp_stop_f:
           \if_int_compare:w `#1 < "E0 \exp_stop_f:
             \exp_after:wN \exp_after:wN \exp_after:wN
-              \__text_codepoint_compare:NN
+              \__text_codepoint_from_chars:NN
           \else:
             \if_int_compare:w `#1 < "F0 \exp_stop_f:
               \exp_after:wN \exp_after:wN \exp_after:wN
               \exp_after:wN \exp_after:wN \exp_after:wN
-              \exp_after:wN \__text_codepoint_compare:NNN
+              \exp_after:wN \__text_codepoint_from_chars:NNN
             \else:
               \exp_after:wN \exp_after:wN \exp_after:wN
               \exp_after:wN \exp_after:wN \exp_after:wN
-              \exp_after:wN \__text_codepoint_compare:NNNN
+              \exp_after:wN \__text_codepoint_from_chars:NNNN
             \fi:
           \fi:
         \else:
-          \exp_after:wN \__text_codepoint_compare_aux:N
+          \exp_after:wN \__text_codepoint_from_chars:N
         \fi:
           #1
       }
-    \cs_new:Npn \__text_codepoint_compare_aux:N #1 { `#1 }
-    \cs_new:Npn \__text_codepoint_compare:NN #1#2
+    \cs_new:Npn \__text_codepoint_from_chars:N #1 { `#1 }
+    \cs_new:Npn \__text_codepoint_from_chars:NN #1#2
       { (`#1 - "C0) * "40 + `#2 - "80 }
-    \cs_new:Npn \__text_codepoint_compare:NNN #1#2#3
-      { (`#1 - "E0) * "1000 + (`#2 - "80) * "40 + `#3 - "80 }
-    \cs_new:Npn \__text_codepoint_compare:NNNN #1#2#3#4
+    %    \end{macrocode}
+    %   Avoid high chars with p\TeX{}.
+    %    \begin{macrocode}
+    \sys_if_engine_ptex:TF
       {
-          (`#1 - "F0) * "40000
-        + (`#2 - "80) * "1000
-        + (`#3 - "80) * "40
-        + `#4 - "80
+        \cs_gset:Npn \__text_codepoint_from_chars:Nw #1
+          {
+            \if_int_compare:w `#1 > "80 \exp_stop_f:
+              \if_int_compare:w `#1 < "E0 \exp_stop_f:
+                \exp_after:wN \exp_after:wN \exp_after:wN
+                  \__text_codepoint_from_chars:NN
+              \else:
+                \exp_after:wN \exp_after:wN \exp_after:wN
+                  \__text_codepoint_from_chars:N
+              \fi:
+            \else:
+              \exp_after:wN \__text_codepoint_from_chars:N
+            \fi:
+              #1
+          }
       }
+      {
+        \cs_new:Npn \__text_codepoint_from_chars:NNN #1#2#3
+          { (`#1 - "E0) * "1000 + (`#2 - "80) * "40 + `#3 - "80 }
+        \cs_new:Npn \__text_codepoint_from_chars:NNNN #1#2#3#4
+          {
+              (`#1 - "F0) * "40000
+            + (`#2 - "80) * "1000
+            + (`#3 - "80) * "40
+            + `#4 - "80
+          }
+      }
   }
 \tl_new:N \l_text_accents_tl
 \tl_set:Nn \l_text_accents_tl
@@ -32306,6 +32512,7 @@
   {
     \exp_not:n { \begin \cite \end \label \ref }
     \exp_not:c { cite ~ }
+    \exp_not:n { \babelshorthand }
   }
 \tl_new:N \l_text_math_arg_tl
 \tl_set:Nn \l_text_math_arg_tl { \ensuremath }
@@ -32530,6 +32737,7 @@
                 { \tl_if_head_eq_meaning_p:oN {#1} \UTFviii at two@octets }
                 { \tl_if_head_eq_meaning_p:oN {#1} \UTFviii at three@octets }
                 { \tl_if_head_eq_meaning_p:oN {#1} \UTFviii at four@octets }
+                { \tl_if_head_eq_meaning_p:oN {#1} \active at prefix }
               }
           }
           { \exp_after:wN \__text_expand_loop:w #1 }
@@ -32792,7 +33000,7 @@
   { \__text_change_case_BCP:nnnnw {#1} {#2} {#4} {#3} #3 - - \q__text_stop }
 \cs_new:Npn \__text_change_case_BCP:nnnnw #1#2#3#4#5 - #6 - #7 \q__text_stop
   {
-    \cs_if_exist:cTF { __text_change_case_ #2 _ #5 -x- #3 :nnnN }
+    \cs_if_exist:cTF { __text_change_case_ #2 _ #5 -x- #3 :nnnn }
       { \__text_change_case_auxii:nnn {#1} {#2} { #5 -x- #3 } }
       { \__text_change_case_auxii:nnn {#1} {#2} {#4} }
   }
@@ -32946,9 +33154,12 @@
 \cs_new:Npn \__text_change_case_cs_check:nnN #1#2#3
   {
     \token_if_cs:NTF #3
-      { \__text_change_case_exclude:nnN }
-      { \use:c { __text_change_case_char_ #1 :nnN } }
-        {#1} {#2} #3
+      { \__text_change_case_exclude:nnN {#1} {#2} }
+      {
+        \__text_codepoint_process:nN
+          { \use:c { __text_change_case_codepoint_ #1 :nnn } {#1} {#2} }
+      }
+        #3
   }
 \cs_new:Npn \__text_change_case_exclude:nnN #1#2#3
   {
@@ -33043,7 +33254,7 @@
       {
         \__text_change_case_store:v
           { c__text_ #1 case_ \token_to_str:N #5 _tl }
-         \use:c { __text_change_case_char_next_ #2 :nn } {#2} {#4}
+         \use:c { __text_change_case_next_ #2 :nn } {#2} {#4}
       }
       {
         \__text_change_case_store:n {#5}
@@ -33053,196 +33264,177 @@
             \str_if_eq:nnTF {#1} { lower } { upper } { lower }
             case_ \token_to_str:N #5 _tl
           }
-          { \use:c { __text_change_case_char_next_ #2 :nn } {#2} {#4} }
+          { \use:c { __text_change_case_next_ #2 :nn } {#2} {#4} }
           { \__text_change_case_loop:nnw {#3} {#4} }
       }
   }
-\cs_new:Npn \__text_change_case_char_lower:nnN #1#2#3
+\cs_new:Npn \__text_change_case_codepoint_lower:nnn #1#2#3
   {
-    \cs_if_exist_use:cF { __text_change_case_lower_ #2 :nnnN }
-      { \__text_change_case_lower_sigma:nnnN }
-        {#1} {#1} {#2} #3
+    \cs_if_exist_use:cF { __text_change_case_lower_ #2 :nnnn }
+      { \__text_change_case_lower_sigma:nnnn }
+        {#1} {#1} {#2} {#3}
   }
-\cs_new:Npn \__text_change_case_char_upper:nnN #1#2#3
+\cs_new:Npn \__text_change_case_codepoint_upper:nnn #1#2#3
   {
-    \cs_if_exist_use:cF { __text_change_case_upper_ #2 :nnnN }
-      { \__text_change_case_char:nnnN }
-        {#1} {#1} {#2} #3
+    \cs_if_exist_use:cF { __text_change_case_upper_ #2 :nnnn }
+      { \__text_change_case_codepoint:nnnn }
+        {#1} {#1} {#2} {#3}
   }
-\bool_lazy_or:nnTF
-  { \sys_if_engine_luatex_p: }
-  { \sys_if_engine_xetex_p: }
+\cs_new:Npn \__text_change_case_lower_sigma:nnnn #1#2#3#4
   {
-    \cs_new:Npn \__text_change_case_lower_sigma:nnnN #1#2#3#4
+    \__text_codepoint_compare:nNnTF {#4} = { "03A3 }
+      { \__text_change_case_lower_sigma:nnnw {#2} }
+      { \__text_change_case_codepoint:nnnn {#1} {#2} }
+        {#3} {#4}
+  }
+\cs_new:Npn \__text_change_case_lower_sigma:nnnw #1#2#3#4 \q__text_recursion_stop
+  {
+    \tl_if_head_is_N_type:nTF {#4}
+      { \__text_change_case_lower_sigma:nnnN {#3} }
       {
-        \int_compare:nNnTF { `#4 } = { "03A3 }
-          { \__text_change_case_lower_sigma:nnNw {#2} {#3} #4 }
-          { \__text_change_case_char:nnnN {#1} {#2} {#3} #4 }
-      }
-    \cs_new:Npn \__text_change_case_lower_sigma:nnNw #1#2#3#4 \q__text_recursion_stop
-      {
-        \tl_if_head_is_N_type:nTF {#4}
-          { \__text_change_case_lower_sigma:NnnN #3 }
-          {
-            \__text_change_case_store:e
-              { \char_generate:nn { "03C2 } { \__text_char_catcode:N #3 } }
-            \__text_change_case_loop:nnw
-          }
-            {#1} {#2} #4 \q__text_recursion_stop
-      }
-    \cs_new:Npn \__text_change_case_lower_sigma:NnnN #1#2#3#4
-      {
         \__text_change_case_store:e
-          {
-            \token_if_letter:NTF #4
-              { \char_generate:nn { "03C3 } { \__text_char_catcode:N #1 } }
-              { \char_generate:nn { "03C2 } { \__text_char_catcode:N #1 } }
-          }
-        \__text_change_case_loop:nnw {#2} {#3} #4
+          { \codepoint_generate:nn { "03C2 } { \__text_char_catcode:N #3 } }
+        \__text_change_case_loop:nnw
       }
+        {#1} {#2} #4 \q__text_recursion_stop
   }
+\cs_new:Npn \__text_change_case_lower_sigma:nnnN #1#2#3#4
   {
-    \cs_new:Npn \__text_change_case_lower_sigma:nnnN #1#2#3#4
+    \__text_change_case_store:e
       {
-        \int_compare:nNnTF { `#4 } = { "CE }
-          { \__text_change_case_lower_sigma:nnnNN  }
-          { \__text_change_case_char:nnnN }
-            {#1} {#2} {#3} #4
-      }
-    \cs_new:Npn \__text_change_case_lower_sigma:nnnNN #1#2#3#4#5
-      {
-        \int_compare:nNnTF { `#5 } = { "A3 }
-          { \__text_change_case_lower_sigma:nnw {#2} {#3}  }
-          { \__text_change_case_char:nnnN {#1} {#2} {#3} #4#5 }
-      }
-    \cs_new:Npn \__text_change_case_lower_sigma:nnw #1#2#3 \q__text_recursion_stop
-      {
-        \tl_if_head_is_N_type:nTF {#3}
-          { \__text_change_case_lower_sigma:nnN }
-          {
-            \__text_change_case_store:V \c__text_final_sigma_tl
-            \__text_change_case_loop:nnw
-          }
-        {#1} {#2} #3 \q__text_recursion_stop
-      }
-    \cs_new:Npn \__text_change_case_lower_sigma:nnN #1#2#3
-      {
         \bool_lazy_or:nnTF
-          { \token_if_letter_p:N #3 }
+          { \token_if_letter_p:N #4 }
           {
             \bool_lazy_and_p:nn
-              { \token_if_active_p:N #3 }
-              { \int_compare_p:nNn { `#3 } > { "80 } }
+              { \token_if_active_p:N #4 }
+              { \int_compare_p:nNn {`#4} > { "80 } }
           }
-          { \__text_change_case_store:V \c__text_sigma_tl }
-          { \__text_change_case_store:V \c__text_final_sigma_tl }
-        \__text_change_case_loop:nnw {#1} {#2} #3
+          { \codepoint_generate:nn { "03C3 } { \__text_char_catcode:N #1 } }
+          { \codepoint_generate:nn { "03C2 } { \__text_char_catcode:N #1 } }
       }
+    \__text_change_case_loop:nnw {#2} {#3} #4
   }
-\cs_new:Npx \__text_change_case_char_title:nnN #1#2#3
+\cs_new:Npn \__text_change_case_codepoint_title:nnn #1#2#3
   {
-    \exp_not:N \bool_if:NTF \l_text_titlecase_check_letter_bool
+    \bool_if:NTF \l_text_titlecase_check_letter_bool
       {
-        \bool_lazy_or:nnTF
-          { \sys_if_engine_luatex_p: }
-          { \sys_if_engine_xetex_p: }
-          { \exp_not:N \token_if_letter:NTF #3 }
+        \tl_if_single:nTF {#3}
           {
-            \exp_not:N \bool_lazy_or:nnTF
-              { \exp_not:N \token_if_letter_p:N #3 }
-              { \exp_not:N \token_if_active_p:N #3 }
+            \bool_lazy_or:nnTF
+              { \token_if_letter_p:N #3 }
+              {
+                \bool_lazy_and_p:nn
+                  { \token_if_active_p:N #3 }
+                  { ! \int_compare_p:nNn {`#3} < { "80 } }
+              }
+              { \use:c { __text_change_case_codepoint_ #1 :nn } }
+              { \__text_change_case_codepoint_title:nnnn { title } {#1} }
           }
-          { \exp_not:N \use:c { __text_change_case_char_ #1 :nN } }
-          { \exp_not:N \__text_change_case_char_title:nnnN { title } {#1} }
+          { \use:c { __text_change_case_codepoint_ #1 :nn } }
       }
-      { \exp_not:N \use:c { __text_change_case_char_ #1 :nN } }
-        {#2} #3
+      { \use:c { __text_change_case_codepoint_ #1 :nn } }
+        {#2} {#3}
   }
-\cs_new_eq:NN \__text_change_case_char_titleonly:nnN
-  \__text_change_case_char_title:nnN
-\cs_new:Npn \__text_change_case_char_title:nN #1#2
-  { \__text_change_case_char_title:nnnN { title } { lower } {#1} #2 }
-\cs_new:Npn \__text_change_case_char_titleonly:nN #1#2
-  { \__text_change_case_char_title:nnnN { title } { end } {#1} #2 }
-\cs_new:Npn \__text_change_case_char_title:nnnN #1#2#3#4
+\cs_new_eq:NN \__text_change_case_codepoint_titleonly:nnn
+  \__text_change_case_codepoint_title:nnn
+\cs_new:Npn \__text_change_case_codepoint_title:nn #1#2
+  { \__text_change_case_codepoint_title:nnnn { title } { lower } {#1} {#2} }
+\cs_new:Npn \__text_change_case_codepoint_titleonly:nn #1#2
+  { \__text_change_case_codepoint_title:nnnn { title } { end } {#1} {#2} }
+\cs_new:Npn \__text_change_case_codepoint_title:nnnn #1#2#3#4
   {
-    \cs_if_exist_use:cF { __text_change_case_title_ #3 :nnnN }
+    \cs_if_exist_use:cF { __text_change_case_title_ #3 :nnnn }
       {
-        \cs_if_exist_use:cF { __text_change_case_upper_ #3 :nnnN }
-          { \__text_change_case_char:nnnN }
+        \cs_if_exist_use:cF { __text_change_case_upper_ #3 :nnnn }
+          { \__text_change_case_codepoint:nnnn }
       }
-        {#1} {#2} {#3} #4
+        {#1} {#2} {#3} {#4}
   }
-\cs_new:Npn \__text_change_case_char:nnnN #1#2#3#4
+\cs_new:Npn \__text_change_case_codepoint:nnnn #1#2#3#4
   {
-    \token_if_active:NTF #4
+    \bool_lazy_and:nnTF
+      { \tl_if_single_p:n {#4} }
+      { \token_if_active_p:N #4 }
       { \__text_change_case_store:n {#4} }
       {
         \__text_change_case_store:e
-          { \use:c { char_ #1 case :N } #4 }
+          { \__text_change_case_codepoint:nn {#1} {#4} }
       }
-    \use:c { __text_change_case_char_next_ #2 :nn } {#2} {#3}
+    \use:c { __text_change_case_next_ #2 :nn } {#2} {#3}
   }
-\bool_lazy_or:nnF
-  { \sys_if_engine_luatex_p: }
-  { \sys_if_engine_xetex_p: }
+\cs_new:Npn \__text_change_case_codepoint:nn #1#2
   {
-    \cs_new_eq:NN \__text_change_case_char_auxi:nnnN
-      \__text_change_case_char:nnnN
-    \cs_gset:Npn \__text_change_case_char:nnnN #1#2#3#4
+    \__text_change_case_codepoint:fnn
+      { \int_eval:n { \__text_codepoint_from_chars:Nw #2 } } {#1} {#2}
+  }
+\cs_new:Npn \__text_change_case_codepoint:nnn #1#2#3
+  {
+    \exp_args:Ne \__text_change_case_codepoint_aux:nn
+      { \__kernel_codepoint_case:nn { #2 case } {#1} } {#3}
+  }
+\cs_generate_variant:Nn \__text_change_case_codepoint:nnn { f }
+\sys_if_engine_ptex:T
+  {
+    \cs_new_eq:NN \__text_change_case_codepoint_aux:nnn
+      \__text_change_case_codepoint:nnn
+    \cs_gset:Npn \__text_change_case_codepoint:nnn #1#2#3
       {
-        \int_compare:nNnTF { `#4 } > { "80 }
-          {
-            \int_compare:nNnTF { `#4 } < { "E0 }
-              { \__text_change_case_char_UTFviii:nnnNN }
-              { \__text_change_case_char_auxii:nnnN }
-          }
-          { \__text_change_case_char_auxi:nnnN }
-            {#1} {#2} {#3} #4
-       }
-    \sys_if_engine_pdftex:TF
-      {
-        \cs_new:Npn \__text_change_case_char_auxii:nnnN #1#2#3#4
-          {
-            \int_compare:nNnTF { `#4 } < { "F0 }
-              { \__text_change_case_char_UTFviii:nnnNNN }
-              { \__text_change_case_char_UTFviii:nnnNNNN }
-                {#1} {#2} {#3} #4
-          }
+        \int_compare:nNnTF {#1} = { -1 }
+          { \exp_not:n {#3} }
+          { \__text_change_case_codepoint_aux:nnn {#1} {#2} {#3} }
       }
+  }
+\cs_new:Npn \__text_change_case_codepoint_aux:nn #1#2
+  {
+    \use:e { \__text_change_case_codepoint_aux:nnnn #1 {#2} }
+  }
+\cs_new:Npn \__text_change_case_codepoint_aux:nnnn #1#2#3#4
+  {
+    \__text_codepoint_compare:nNnTF {#4} = {#1}
+      { \exp_not:n {#4} }
       {
-        \cs_new:Npn \__text_change_case_char_auxii:nnnN #1#2#3#4
+        \codepoint_generate:nn {#1}
+          { \__text_change_case_catcode:nn {#4} {#1} }
+        \tl_if_blank:nF {#2}
           {
-            \__text_change_case_store:n {#4}
-            \use:c { __text_change_case_char_next_ #2 :nn } {#2} {#3}
+            \codepoint_generate:nn {#2}
+              { \char_value_catcode:n {#2} }
+            \tl_if_blank:nF {#3}
+             {
+               \codepoint_generate:nn {#3}
+                 { \char_value_catcode:n {#3} }
+             }
           }
       }
-    \cs_new:Npn \__text_change_case_char_UTFviii:nnnNN #1#2#3#4#5
-      { \__text_change_case_char_UTFviii:nnnn {#1} {#2} {#3} {#4#5} }
-    \cs_new:Npn \__text_change_case_char_UTFviii:nnnNNN #1#2#3#4#5#6
-      { \__text_change_case_char_UTFviii:nnnn {#1} {#2} {#3} {#4#5#6} }
-    \cs_new:Npn \__text_change_case_char_UTFviii:nnnNNNN #1#2#3#4#5#6#7
-      { \__text_change_case_char_UTFviii:nnnn {#1} {#2} {#3} {#4#5#6#7} }
-    \cs_new:Npn \__text_change_case_char_UTFviii:nnnn #1#2#3#4
+  }
+\bool_lazy_or:nnTF
+  { \sys_if_engine_luatex_p: }
+  { \sys_if_engine_xetex_p: }
+  {
+    \cs_new:Npn \__text_change_case_catcode:nn #1#2
+      { \__text_char_catcode:N #1 }
+  }
+  {
+    \cs_new:Npn \__text_change_case_catcode:nn #1#2
       {
-        \cs_if_exist:cTF { c__kernel_ #1 case_ \tl_to_str:n {#4} _tl }
+        \__text_codepoint_compare:nNnTF {#1} < { "80 }
+          { \__text_char_catcode:N #1 }
           {
-            \__text_change_case_store:v
-              { c__kernel_ #1 case_ \tl_to_str:n {#4} _tl }
+            \int_compare:nNnTF {#2} < { "80 }
+              { \char_value_catcode:n {#2} }
+              { 13 }
           }
-          { \__text_change_case_store:n {#4} }
-        \use:c { __text_change_case_char_next_ #2 :nn } {#2} {#3}
       }
   }
-\cs_new:Npn \__text_change_case_char_next_lower:nn #1#2
+\cs_new:Npn \__text_change_case_next_lower:nn #1#2
   { \__text_change_case_loop:nnw {#1} {#2} }
-\cs_new_eq:NN \__text_change_case_char_next_upper:nn
-  \__text_change_case_char_next_lower:nn
-\cs_new_eq:NN \__text_change_case_char_next_title:nn
-  \__text_change_case_char_next_lower:nn
-\cs_new_eq:NN \__text_change_case_char_next_titleonly:nn
-  \__text_change_case_char_next_lower:nn
-\cs_new:Npn \__text_change_case_char_next_end:nn #1#2
+\cs_new_eq:NN \__text_change_case_next_upper:nn
+  \__text_change_case_next_lower:nn
+\cs_new_eq:NN \__text_change_case_next_title:nn
+  \__text_change_case_next_lower:nn
+\cs_new_eq:NN \__text_change_case_next_titleonly:nn
+  \__text_change_case_next_lower:nn
+\cs_new:Npn \__text_change_case_next_end:nn #1#2
   { \__text_change_case_break:w }
 \cs_new_protected:Npn \text_declare_case_equivalent:Nn #1#2
   {
@@ -33255,253 +33447,247 @@
     #1
   }
 \cs_new:Npn \__text_case_switch_marker: { }
-\bool_lazy_or:nnTF
-  { \sys_if_engine_luatex_p: }
-  { \sys_if_engine_xetex_p: }
+\cs_new:cpn { __text_change_case_upper_de-x-eszett:nnnn } #1#2#3#4
   {
-    \cs_new:cpn { __text_change_case_upper_de-x-eszett:nnnN } #1#2#3#4
+    \__text_codepoint_compare:nNnTF {#4} = { "00DF }
       {
-        \int_compare:nNnTF { `#4 } = { "00DF }
-          {
-            \__text_change_case_store:e
-             { \char_generate:nn { "1E9E } { \__text_char_catcode:N #4 } }
-            \use:c { __text_change_case_char_next_ #2 :nn }
-              {#2} {#3}
-          }
-          { \__text_change_case_char:nnnN {#1} {#2} {#3} #4 }
+        \__text_change_case_store:e
+         {
+           \codepoint_generate:nn { "1E9E }
+             { \__text_change_case_catcode:nn {#4} { "1E9E } }
+         }
+        \use:c { __text_change_case_next_ #2 :nn }
+          {#2} {#3}
       }
+      { \__text_change_case_codepoint:nnnn {#1} {#2} {#3} {#4} }
   }
+\cs_new_eq:cc { __text_change_case_upper_de-alt:nnnn }
+  { __text_change_case_upper_de-x-eszett:nnnn }
+\cs_new:Npn \__text_change_case_upper_el:nnnn #1#2#3#4
   {
-    \cs_new:cpx { __text_change_case_upper_de-x-eszett:nnnN } #1#2#3#4
+    \__text_change_case_if_greek:nTF {#4}
       {
-        \exp_not:N \int_compare:nNnTF { `#4 } = { "00C3 }
+        \exp_args:Ne \__text_change_case_upper_el:nnn
           {
-            \exp_not:c { __text_change_case_upper_de-x-eszett:nnnNN }
-              {#1} {#2} {#3} #4
+            \codepoint_to_nfd:n { \__text_codepoint_from_chars:Nw #4 }
           }
-          { \exp_not:N \__text_change_case_char:nnnN {#1} {#2} {#3} #4 }
+            {#2} {#3}
       }
-    \cs_new:cpn { __text_change_case_upper_de-x-eszett:nnnNN } #1#2#3#4#5
       {
-        \int_compare:nNnTF { `#5 } = { "009F }
+        \__text_codepoint_compare:nNnTF {#4} = { "0345 }
           {
-            \__text_change_case_store:V \c__text_grosses_Eszett_tl
-            \use:c { __text_change_case_char_next_ #2 :nn } {#2} {#3}
+            \__text_change_case_store:e
+              {
+                \use:c { __text_change_case_upper_ #3 :n } {#4}
+              }
+            \__text_change_case_loop:nnw {#2} {#3}
           }
-          { \__text_change_case_char:nnnN {#1} {#2} {#3} #4#5 }
+          { \__text_change_case_codepoint:nnnn {#1} {#2} {#3} {#4} }
       }
   }
-\cs_new_eq:cc { __text_change_case_upper_de-alt:nnnN }
-  { __text_change_case_upper_de-x-eszett:nnnN }
-\bool_lazy_or:nnTF
-  { \sys_if_engine_luatex_p: }
-  { \sys_if_engine_xetex_p: }
+\cs_new_eq:cN { __text_change_case_upper_el-x-iota:nnnn }
+  \__text_change_case_upper_el:nnnn
+\cs_new:Npn \__text_change_case_upper_el:n #1
   {
-    \cs_new:Npn \__text_change_case_upper_el:nnnN #1#2#3#4
+    \codepoint_generate:nn { "0399 }
+      { \char_value_catcode:n { "0399 } }
+  }
+\cs_new:cpn { __text_change_case_upper_el-x-iota:n } #1
+  { \exp_not:n {#1} }
+\cs_new:Npn \__text_change_case_upper_el:nnn #1#2#3
+  {
+    \__text_codepoint_process:nN
+      { \__text_change_case_upper_el:nnnw {#2} {#3} } #1
+  }
+\cs_new:Npn \__text_change_case_upper_el:nnnw #1#2#3#4 \q__text_recursion_stop
+  {
+    \tl_if_head_is_N_type:nTF {#4}
+      { \__text_change_case_upper_el:nnnN {#3} }
       {
-        \__text_change_case_if_greek:nTF { `#4 }
-          {
-            \exp_args:Ne \__text_change_case_upper_el:nnn
-              { \char_to_nfd:N #4 } {#2} {#3}
-          }
-          {
-            \int_compare:nNnTF { `#4 } = { "0345 }
-              {
-                \__text_change_case_store:e
-                  {
-                    \use:c { __text_change_case_upper_ #3 :N } #4
-                  }
-                \__text_change_case_loop:nnw {#2} {#3}
-              }
-              { \__text_change_case_char:nnnN {#1} {#2} {#3} #4 }
-          }
+        \__text_change_case_store:e
+          { \__text_change_case_codepoint:nn { upper } {#3} }
+        \__text_change_case_loop:nnw
       }
-    \cs_new_eq:cN { __text_change_case_upper_el-x-iota:nnnN }
-      \__text_change_case_upper_el:nnnN
-    \cs_new:Npn \__text_change_case_upper_el:N #1
+        {#1} {#2} #4 \q__text_recursion_stop
+  }
+\cs_new:Npn \__text_change_case_upper_el:nnnN #1#2#3#4
+  {
+    \token_if_cs:NTF #4
       {
-        \char_generate:nn { "0399 }
-          { \char_value_catcode:n { "0399 } }
+        \__text_change_case_store:e
+          { \__text_change_case_codepoint:nn { upper } {#1} }
+        \__text_change_case_loop:nnw {#2} {#3} #4
       }
-    \cs_new:cpn { __text_change_case_upper_el-x-iota:N } #1
-      { \exp_not:n {#1} }
-    \cs_new:Npn \__text_change_case_upper_el:nnn #1#2#3
-      { \__text_change_case_upper_el:nnNw {#2} {#3} #1 }
-    \cs_new:Npn \__text_change_case_upper_el:nnNw #1#2#3#4 \q__text_recursion_stop
       {
-        \tl_if_head_is_N_type:nTF {#4}
-          { \__text_change_case_upper_el:NnnN #3 }
+        \__text_change_case_if_takes_ypogegrammeni:nTF {#1}
           {
-            \__text_change_case_store:e { \char_uppercase:N #3 }
-            \__text_change_case_loop:nnw
+            \__text_change_case_upper_el_ypogegrammeni:nnnnnw
+              {#1} {#2} {#3} { } { } #4
           }
-            {#1} {#2} #4 \q__text_recursion_stop
+          { \__text_change_case_upper_el_aux:nnnN {#1} {#2} {#3} #4 }
       }
-    \cs_new:Npn \__text_change_case_upper_el:NnnN #1#2#3#4
+  }
+\cs_new:Npn \__text_change_case_upper_el_ypogegrammeni:nnnnnw
+  #1#2#3#4#5#6 \q__text_recursion_stop
+  {
+    \tl_if_head_is_N_type:nTF {#6}
       {
-        \token_if_cs:NTF #4
+        \__text_change_case_upper_el_ypogegrammeni:nnnnnN
+          {#1} {#2} {#3} {#4} {#5}
+      }
+      { \__text_change_case_upper_el_aux:nnnN {#1} {#2} {#3} #4#5 }
+        #6 \q__text_recursion_stop
+  }
+\cs_new:Npn \__text_change_case_upper_el_ypogegrammeni:nnnnnN #1#2#3#4#5#6
+  {
+    \token_if_cs:NTF #6
+      { \__text_change_case_upper_el_aux:nnnN {#1} {#2} {#3} #4#5 #6 }
+      {
+        \__text_codepoint_process:nN
           {
-            \__text_change_case_store:e { \char_uppercase:N #1 }
-            \__text_change_case_loop:nnw {#2} {#3} #4
+            \__text_change_case_upper_el_ypogegrammeni:nnnnnn
+              {#1} {#2} {#3} {#4} {#5}
           }
-          {
-            \__text_change_case_if_takes_ypogegrammeni:nTF { `#1 }
-              {
-                \__text_change_case_upper_el_ypogegrammeni:Nnnnnw
-                  #1 {#2} {#3} { } { } #4
-              }
-              { \__text_change_case_upper_el_aux:NnnN #1 {#2} {#3} #4 }
-          }
+            #6
       }
-    \cs_new:Npn \__text_change_case_upper_el_ypogegrammeni:Nnnnnw
-      #1#2#3#4#5#6 \q__text_recursion_stop
+  }
+\cs_new:Npn \__text_change_case_upper_el_ypogegrammeni:nnnnnn #1#2#3#4#5#6
+  {
+    \__text_codepoint_compare:nNnTF {#6} = { "0345 }
       {
-        \tl_if_head_is_N_type:nTF {#6}
+        \__text_change_case_upper_el_ypogegrammeni:nnnnnw
+          {#1} {#2} {#3} {#4} {#6}
+      }
+      {
+        \bool_lazy_or:nnTF
+          { \__text_change_case_if_greek_accent_p:n {#6} }
+          { \__text_change_case_if_greek_diacritic_p:n {#6} }
           {
-            \__text_change_case_upper_el_ypogegrammeni:NnnnnN
-              #1 {#2} {#3} {#4} {#5}
+            \__text_change_case_upper_el_ypogegrammeni:nnnnnw
+              {#1} {#2} {#3} {#4#6} {#5}
           }
-          { \__text_change_case_upper_el_aux:NnnN #1 {#2} {#3} #4#5 }
-            #6 \q__text_recursion_stop
+          { \__text_change_case_upper_el_aux:nnnN {#1} {#2} {#3} #4#5 #6 }
       }
-    \cs_new:Npn \__text_change_case_upper_el_ypogegrammeni:NnnnnN #1#2#3#4#5#6
+  }
+\cs_new:Npn \__text_change_case_upper_el_aux:nnnN #1#2#3#4
+  {
+    \__text_codepoint_process:nN
+      { \__text_change_case_upper_el_aux:nnnn {#1} {#2} {#3} } #4
+  }
+\cs_new:Npn \__text_change_case_upper_el_aux:nnnn #1#2#3#4
+  {
+    \__text_codepoint_compare:nNnTF {#4} = { "0308 }
+      { \__text_change_case_upper_el_dialytika:nnn {#2} {#3} {#1} }
       {
-        \token_if_cs:NTF #6
-          { \__text_change_case_upper_el_aux:NnnN #1 {#2} {#3} #4#5 #6 }
+         \__text_change_case_if_greek_accent:nTF {#4}
+          { \__text_change_case_upper_el_hiatus:nnnw {#2} {#3} {#1} }
           {
-            \int_compare:nNnTF { `#6 } = { "0345 }
+            \__text_change_case_if_greek_diacritic:nTF {#4}
               {
-                \__text_change_case_upper_el_ypogegrammeni:Nnnnnw
-                  #1 {#2} {#3} {#4} {#6}
+                \__text_change_case_store:e
+                  { \__text_change_case_codepoint:nn { upper } {#1} }
+                \__text_change_case_loop:nnw {#2} {#3}
               }
               {
-                \bool_lazy_or:nnTF
-                  { \__text_change_case_if_greek_accent_p:n { `#6 } }
-                  { \__text_change_case_if_greek_diacritic_p:n { `#6 } }
+                \__text_codepoint_compare:nNnTF {#4} = { "0345 }
+                  { \__text_change_case_store:e { [XXX] } }
                   {
-                    \__text_change_case_upper_el_ypogegrammeni:Nnnnnw
-                      #1 {#2} {#3} {#4#6} {#5}
+                    \__text_change_case_store:e
+                      { \__text_change_case_codepoint:nn { upper } {#1} }
                   }
-                  { \__text_change_case_upper_el_aux:NnnN #1 {#2} {#3} #4#5 #6 }
-            }
-          }
-      }
-    \cs_new:Npn \__text_change_case_upper_el_aux:NnnN #1#2#3#4
-      {
-        \int_compare:nNnTF { `#4 } = { "0308 }
-          { \__text_change_case_upper_el_dialytika:nnN {#2} {#3} #1 }
-          {
-            \__text_change_case_if_greek_accent:nTF { `#4 }
-              { \__text_change_case_upper_el_hiatus:nnNw {#2} {#3} #1 }
-              {
-                \__text_change_case_if_greek_diacritic:nTF { `#4 }
-                  {
-                    \__text_change_case_store:e { \char_uppercase:N #1 }
-                    \__text_change_case_loop:nnw {#2} {#3}
-                  }
-                  {
-                    \int_compare:nNnTF { `#4 } = { "0345 }
-                      { \__text_change_case_store:e { [XXX] } }
-                      { \__text_change_case_store:e { \char_uppercase:N #1 } }
-                    \__text_change_case_loop:nnw {#2} {#3} #4
-                  }
+                \__text_change_case_loop:nnw {#2} {#3} #4
               }
           }
       }
-    \cs_new:Npn \__text_change_case_upper_el_dialytika:nnN #1#2#3
+  }
+\cs_new:Npn \__text_change_case_upper_el_dialytika:nnn #1#2#3
+  {
+    \__text_change_case_if_takes_dialytika:nTF {#3}
+      { \__text_change_case_upper_el_dialytika:n {#3} }
       {
-        \__text_change_case_if_takes_dialytika:nTF { `#3 }
-          { \__text_change_case_upper_el_dialytika:N #3 }
-          { \__text_change_case_store:e { \char_uppercase:N #3 } }
-        \__text_change_case_upper_el_gobble:nnw {#1} {#2}
+        \__text_change_case_store:e
+          { \__text_change_case_codepoint:nn { upper } {#3} }
       }
-    \cs_new:Npn \__text_change_case_upper_el_dialytika:N #1
+    \__text_change_case_upper_el_gobble:nnw {#1} {#2}
+  }
+\cs_new:Npn \__text_change_case_upper_el_dialytika:n #1
+  {
+    \__text_change_case_store:e
       {
-        \__text_change_case_store:e
+        \bool_lazy_or:nnTF
+          { \__text_codepoint_compare_p:nNn {#1} = { "0399 } }
+          { \__text_codepoint_compare_p:nNn {#1} = { "03B9 } }
           {
-            \bool_lazy_or:nnTF
-              { \int_compare_p:nNn { `#1 } = { "0399 } }
-              { \int_compare_p:nNn { `#1 } = { "03B9 } }
-              { \char_generate:nn { "03AA } { \__text_char_catcode:N #1 } }
-              { \char_generate:nn { "03AB } { \__text_char_catcode:N #1 } }
+            \codepoint_generate:nn { "03AA }
+              { \__text_change_case_catcode:nn {#1} { "03AA } }
           }
-      }
-    \cs_new:Npn \__text_change_case_upper_el_hiatus:nnNw
-      #1#2#3#4 \q__text_recursion_stop
-      {
-        \__text_change_case_store:e { \char_uppercase:N #3 }
-        \tl_if_head_is_N_type:nTF {#4}
-          { \__text_change_case_upper_el_hiatus:nnN }
-          { \__text_change_case_loop:nnw }
-            {#1} {#2} #4 \q__text_recursion_stop
-      }
-    \cs_new:Npn \__text_change_case_upper_el_hiatus:nnN #1#2#3
-      {
-        \token_if_cs:NTF #3
-          { \__text_change_case_loop:nnw {#1} {#2} #3 }
           {
-            \__text_change_case_if_takes_dialytika:nTF { `#3 }
-              {
-                \__text_change_case_upper_el_dialytika:N #3
-                \__text_change_case_upper_el_gobble:nnw {#1} {#2}
-              }
-              { \__text_change_case_loop:nnw {#1} {#2} #3 }
+            \codepoint_generate:nn { "03AB }
+              { \__text_change_case_catcode:nn {#1} { "03AB } }
           }
       }
-    \cs_new:Npn \__text_change_case_upper_el_gobble:nnw
-      #1#2#3 \q__text_recursion_stop
+  }
+\cs_new:Npn \__text_change_case_upper_el_hiatus:nnnw
+  #1#2#3#4 \q__text_recursion_stop
+   {
+    \__text_change_case_store:e
+      { \__text_change_case_codepoint:nn { upper } {#3} }
+    \tl_if_head_is_N_type:nTF {#4}
+      { \__text_change_case_upper_el_hiatus:nnN }
+      { \__text_change_case_loop:nnw }
+        {#1} {#2} #4 \q__text_recursion_stop
+  }
+\cs_new:Npn \__text_change_case_upper_el_hiatus:nnN #1#2#3
+  {
+    \token_if_cs:NTF #3
+      { \__text_change_case_loop:nnw {#1} {#2} #3 }
       {
-        \tl_if_head_is_N_type:nTF {#3}
-          { \__text_change_case_upper_el_gobble:nnN }
-          { \__text_change_case_loop:nnw }
-            {#1} {#2} #3 \q__text_recursion_stop
+        \__text_codepoint_process:nN
+          { \__text_change_case_upper_el_hiatus:nnn {#1} {#2} } #3
       }
-    \cs_new:Npn \__text_change_case_upper_el_gobble:nnN #1#2#3
+  }
+\cs_new:Npn \__text_change_case_upper_el_hiatus:nnn #1#2#3
+  {
+    \__text_change_case_if_takes_dialytika:nTF {#3}
       {
-        \bool_lazy_or:nnTF
-          { \token_if_cs_p:N #3 }
-          {
-            ! \bool_lazy_or_p:nn
-              { \__text_change_case_if_greek_accent_p:n { `#3 } }
-              { \__text_change_case_if_greek_diacritic_p:n { `#3 } }
-          }
-          { \__text_change_case_loop:nnw {#1} {#2} #3 }
-          { \__text_change_case_upper_el_gobble:nnw {#1} {#2} }
+        \__text_change_case_upper_el_dialytika:n {#3}
+        \__text_change_case_upper_el_gobble:nnw {#1} {#2}
       }
+      { \__text_change_case_loop:nnw {#1} {#2} #3 }
   }
+\cs_new:Npn \__text_change_case_upper_el_gobble:nnw
+  #1#2#3 \q__text_recursion_stop
   {
-    \cs_new:Npn \__text_change_case_upper_el:nnnN #1#2#3#4
+    \tl_if_head_is_N_type:nTF {#3}
+      { \__text_change_case_upper_el_gobble:nnN }
+      { \__text_change_case_loop:nnw }
+        {#1} {#2} #3 \q__text_recursion_stop
+  }
+\cs_new:Npn \__text_change_case_upper_el_gobble:nnN #1#2#3
+  {
+    \token_if_cs:NTF #3
+      { \__text_change_case_loop:nnw {#1} {#2} }
       {
-        \int_compare:nNnTF { `#4 } > { "80 }
-          {
-            \int_compare:nNnTF { `#4 } < { "E0 }
-              { \__text_change_case_upper_el:nnnNN }
-              {
-                 \int_compare:nNnTF { `#4 } < { "F0 }
-                  { \__text_change_case_upper_el:nnnNNN }
-                  { \__text_change_case_char:nnnN }
-              }
-          }
-          { \__text_change_case_char:nnnN }
-             {#1} {#2} {#3} #4
+        \__text_codepoint_process:nN
+          { \__text_change_case_upper_el_gobble:nnn {#1} {#2} }
       }
-    \cs_new:Npn \__text_change_case_upper_el:nnnNN #1#2#3#4#5
-      { \__text_change_case_upper_el:nnnn {#1} {#2} {#3} {#4#5} }
-    \cs_new:Npn \__text_change_case_upper_el:nnnNNN #1#2#3#4#5#6
-      { \__text_change_case_upper_el:nnnn {#1} {#2} {#3} {#4#5#6} }
-    \cs_new:Npn \__text_change_case_upper_el:nnnn #1#2#3#4
-      {
-        \cs_if_exist:cTF { c__text_uppercase_el_ \tl_to_str:n {#4} _tl }
-          {
-            \__text_change_case_store:v
-              { c__text_uppercase_el_ \tl_to_str:n {#4} _tl }
-            \__text_change_case_loop:nnw {#1} {#3}
-          }
-          { \__text_change_case_char:nnnN {#1} {#2} {#3} #4 }
-      }
+        #3
   }
+\cs_new:Npn \__text_change_case_upper_el_gobble:nnn #1#2#3
+  {
+    \bool_lazy_or:nnTF
+      { \__text_change_case_if_greek_accent_p:n {#3} }
+      { \__text_change_case_if_greek_diacritic_p:n {#3} }
+      { \__text_change_case_upper_el_gobble:nnw {#1} {#2} }
+      { \__text_change_case_loop:nnw {#1} {#2} #3 }
+  }
 \prg_new_conditional:Npnn \__text_change_case_if_greek:n #1 { TF }
   {
+    \exp_args:Nf \__text_change_case_if_greek:n
+      { \int_eval:n { \__text_codepoint_from_chars:Nw #1 } }
+  }
+\cs_new:Npn \__text_change_case_if_greek:n #1
+  {
     \if_int_compare:w #1 < "0370 \exp_stop_f:
       \prg_return_false:
     \else:
@@ -33526,6 +33712,11 @@
   }
 \prg_new_conditional:Npnn \__text_change_case_if_greek_accent:n #1 { TF , p }
   {
+    \exp_args:Nf \__text_change_case_if_greek_accent:n
+      { \int_eval:n { \__text_codepoint_from_chars:Nw #1 } }
+  }
+\cs_new:Npn \__text_change_case_if_greek_accent:n #1
+  {
     \if_int_compare:w #1 = "0300 \exp_stop_f:
       \prg_return_true:
     \else:
@@ -33555,6 +33746,11 @@
 \prg_new_conditional:Npnn \__text_change_case_if_greek_diacritic:n
   #1 { TF , p }
   {
+    \exp_args:Nf \__text_change_case_if_greek_diacritic:n
+      { \int_eval:n { \__text_codepoint_from_chars:Nw #1 } }
+  }
+\cs_new:Npn \__text_change_case_if_greek_diacritic:n #1
+  {
     \if_int_compare:w #1 = "0304 \exp_stop_f:
       \prg_return_true:
     \else:
@@ -33579,6 +33775,11 @@
   }
 \prg_new_conditional:Npnn \__text_change_case_if_takes_dialytika:n #1 { TF }
   {
+    \exp_args:Nf \__text_change_case_if_takes_dialytika:n
+      { \int_eval:n { \__text_codepoint_from_chars:Nw #1 } }
+  }
+\cs_new:Npn \__text_change_case_if_takes_dialytika:n #1
+  {
     \if_int_compare:w #1 = "0399 \exp_stop_f:
       \prg_return_true:
     \else:
@@ -33599,6 +33800,11 @@
   }
 \prg_new_conditional:Npnn \__text_change_case_if_takes_ypogegrammeni:n #1 { TF }
   {
+    \exp_args:Nf \__text_change_case_if_takes_ypogegrammeni:n
+      { \int_eval:n { \__text_codepoint_from_chars:Nw #1 } }
+  }
+\cs_new:Npn \__text_change_case_if_takes_ypogegrammeni:n #1
+  {
     \if_int_compare:w #1 = "03B1 \exp_stop_f:
       \prg_return_true:
     \else:
@@ -33613,262 +33819,292 @@
       \fi:
     \fi:
   }
-\bool_lazy_or:nnT
-  { \sys_if_engine_luatex_p: }
-  { \sys_if_engine_xetex_p: }
+\cs_new:Npn \__text_change_case_boundary_upper_el:Nnnw
+  #1#2#3#4 \q__text_recursion_stop
   {
-    \cs_new:Npn \__text_change_case_boundary_upper_el:Nnnw
-      #1#2#3#4 \q__text_recursion_stop
+    \tl_if_head_is_N_type:nTF {#4}
+      { \__text_change_case_boundary_upper_el:nnN }
+      { \__text_change_case_loop:nnw }
+        {#2} {#3} #4 \q__text_recursion_stop
+  }
+\cs_new:Npn \__text_change_case_boundary_upper_el:nnN #1#2#3
+  {
+    \token_if_cs:NTF #3
+      { \__text_change_case_loop:nnw {#1} {#2} }
       {
-        \tl_if_head_is_N_type:nTF {#4}
-          { \__text_change_case_boundary_upper_el:nnN }
-          { \__text_change_case_loop:nnw }
-            {#2} {#3} #4 \q__text_recursion_stop
+        \__text_codepoint_process:nN
+          { \__text_change_case_boundary_upper_el:nnn {#1} {#2} }
       }
-    \cs_new:Npn \__text_change_case_boundary_upper_el:nnN #1#2#3
+        #3
+  }
+\cs_new:Npn \__text_change_case_boundary_upper_el:nnn #1#2#3
+  {
+    \bool_lazy_or:nnTF
+      { \__text_codepoint_compare_p:nNn {#3} = { "03AE } }
+      { \__text_codepoint_compare_p:nNn {#3} = { "1F22 } }
+      { \__text_change_case_boundary_upper_el:nnnw {#1} {#2} {#3} }
+      { \__text_change_case_loop:nnw {#1} {#2} #3 }
+  }
+\cs_new:Npn \__text_change_case_boundary_upper_el:nnnw
+  #1#2#3#4 \q__text_recursion_stop
+  {
+    \tl_if_head_is_N_type:nTF {#4}
+      { \__text_change_case_boundary_upper_el:nnnN {#3} }
       {
-        \bool_lazy_or:nnTF
-          { \token_if_cs_p:N #3 }
+        \__text_change_case_store:e
           {
-            ! \bool_lazy_or_p:nn
-              { \int_compare_p:nNn { `#3 } = { "03AE } }
-              { \int_compare_p:nNn { `#3 } = { "1F22 } }
+            \codepoint_generate:nn { "0389 }
+              { \__text_change_case_catcode:nn {#3} { "0389 } }
           }
-          { \__text_change_case_loop:nnw }
-          { \__text_change_case_boundary_upper_el:nnNw }
-            {#1} {#2} #3
+        \__text_change_case_loop:nnw
       }
-    \cs_new:Npn \__text_change_case_boundary_upper_el:nnNw
-      #1#2#3#4 \q__text_recursion_stop
-       {
-        \tl_if_head_is_N_type:nTF {#4}
-          { \__text_change_case_boundary_upper_el:NnnN #3 }
-          {
-            \__text_change_case_store:e
-              { \char_generate:nn { "0389 } { \__text_char_catcode:N #3 } }
-            \__text_change_case_loop:nnw
-          }
-            {#1} {#2} #4 \q__text_recursion_stop
+        {#1} {#2} #4 \q__text_recursion_stop
+  }
+\cs_new:Npn \__text_change_case_boundary_upper_el:nnnN #1#2#3#4
+  {
+    \MARJ
+    \bool_lazy_or:nnTF
+      { \token_if_letter_p:N #4 }
+      {
+        \bool_lazy_and_p:nn
+          { \token_if_active_p:N #4 }
+          { ! \int_compare_p:nNn {`#4} < { "80 } }
       }
-    \cs_new:Npn \__text_change_case_boundary_upper_el:NnnN #1#2#3#4
+      { \__text_change_case_loop:nnw {#2} {#3} #1#4 }
       {
-        \token_if_letter:NTF #4
-          { \__text_change_case_loop:nnw {#2} {#3} #1#4 }
+        \__text_change_case_store:e
           {
-            \__text_change_case_store:e
-              { \char_generate:nn { "0389 } { \__text_char_catcode:N #1 } }
-            \__text_change_case_loop:nnw {#2} {#3} #4
+            \codepoint_generate:nn { "0389 }
+              { \__text_change_case_catcode:nn {#1} { "0389 } }
           }
+        \__text_change_case_loop:nnw {#2} {#3} #4
       }
   }
-\bool_lazy_or:nnT
-  { \sys_if_engine_luatex_p: }
-  { \sys_if_engine_xetex_p: }
+\cs_new:Npn \__text_change_case_title_el:nnnn #1#2#3#4
+  { \__text_change_case_codepoint:nnnn {#1} {#2} {#3} {#4} }
+\cs_new:Npn \__text_change_case_upper_hy:nnnn #1#2#3#4
   {
-    \cs_new:Npn \__text_change_case_title_el:nnnN #1#2#3#4
-      { \__text_change_case_char:nnnN {#1} {#2} {#3} #4 }
-  }
-\bool_lazy_or:nnT
-  { \sys_if_engine_luatex_p: }
-  { \sys_if_engine_xetex_p: }
-  {
-    \cs_new:cpn { __text_change_case_upper_hy:nnnN } #1#2#3#4
+    \__text_codepoint_compare:nNnTF {#4} = { "0587 }
       {
-        \int_compare:nNnTF { `#4 } = { "0587 }
+        \__text_change_case_store:e
           {
-            \__text_change_case_store:e
-              {
-                \char_generate:nn { "0535 } { \__text_char_catcode:N #4 }
-                \char_generate:nn { "054E } { \__text_char_catcode:N #4 }
-              }
-            \use:c { __text_change_case_char_next_ #2 :nn }
-              {#2} {#3}
+            \codepoint_generate:nn { "0535 }
+              { \__text_change_case_catcode:nn {#4} { "0535 } }
+            \codepoint_generate:nn { "054E }
+              { \__text_change_case_catcode:nn {#4} { "054E } }
           }
-          { \__text_change_case_char:nnnN {#1} {#2} {#3} #4 }
+        \use:c { __text_change_case_next_ #2 :nn }
+          {#2} {#3}
       }
-    \cs_new:cpn { __text_change_case_title_hy:nnnN } #1#2#3#4
+      { \__text_change_case_codepoint:nnnn {#1} {#2} {#3} {#4} }
+  }
+\cs_new:Npn \__text_change_case_title_hy:nnnn #1#2#3#4
+  {
+    \__text_codepoint_compare:nNnTF {#4} = { "0587 }
       {
-        \int_compare:nNnTF { `#4 } = { "0587 }
+        \__text_change_case_store:e
           {
-            \__text_change_case_store:e
-              {
-                \char_generate:nn { "0535 } { \__text_char_catcode:N #4 }
-                \char_generate:nn { "057E } { \__text_char_catcode:N #4 }
-              }
-            \use:c { __text_change_case_char_next_ #2 :nn }
-              {#2} {#3}
+            \codepoint_generate:nn { "0535 }
+              { \__text_change_case_catcode:nn {#4} { "0535 } }
+            \codepoint_generate:nn { "057E }
+              { \__text_change_case_catcode:nn {#4} { "057E } }
           }
-          { \__text_change_case_char:nnnN {#1} {#2} {#3} #4 }
+        \use:c { __text_change_case_next_ #2 :nn }
+          {#2} {#3}
       }
-    \cs_new:cpn { __text_change_case_upper_hy-x-yiwn:nnnN } #1#2#3#4
-      { \__text_change_case_char:nnnN {#1} {#2} {#3} #4 }
-    \cs_new_eq:cc { __text_change_case_title_hy-x-yiwn:nnnN }
-      { __text_change_case_upper_hy-x-yiwn:nnnN }
+      { \__text_change_case_codepoint:nnnn {#1} {#2} {#3} {#4} }
   }
-\cs_new:cpn { __text_change_case_lower_la-x-medieval:nnnN } #1#2#3#4
+\cs_new:cpn { __text_change_case_upper_hy-x-yiwn:nnnn } #1#2#3#4
+  { \__text_change_case_codepoint:nnnn {#1} {#2} {#3} {#4} }
+\cs_new_eq:cc { __text_change_case_title_hy-x-yiwn:nnnn }
+  { __text_change_case_upper_hy-x-yiwn:nnnn }
+\cs_new:cpn { __text_change_case_lower_la-x-medieval:nnnn } #1#2#3#4
   {
-    \int_compare:nNnTF { `#4 } = { `V }
+    \__text_codepoint_compare:nNnTF {#4} = { "0056 }
       {
         \__text_change_case_store:e
-          {
-            \char_generate:nn { `u } { \__text_char_catcode:N #4 }
-          }
-        \use:c { __text_change_case_char_next_ #2 :nn }
+          { \char_generate:nn { "0075 } { \__text_char_catcode:N #4 } }
+        \use:c { __text_change_case_next_ #2 :nn }
           {#2} {#3}
       }
-      { \__text_change_case_char:nnnN {#1} {#2} {#3} #4 }
+      { \__text_change_case_codepoint:nnnn {#1} {#2} {#3} {#4} }
   }
-\cs_new:cpn { __text_change_case_upper_la-x-medieval:nnnN } #1#2#3#4
+\cs_new:cpn { __text_change_case_upper_la-x-medieval:nnnn } #1#2#3#4
   {
-    \int_compare:nNnTF { `#4 } = { `u }
+    \__text_codepoint_compare:nNnTF {#4} = { "0075 }
       {
         \__text_change_case_store:e
-          {
-            \char_generate:nn { `V } { \__text_char_catcode:N #4 }
-          }
-        \use:c { __text_change_case_char_next_ #2 :nn }
+          { \char_generate:nn { "0056 } { \__text_char_catcode:N #4 } }
+        \use:c { __text_change_case_next_ #2 :nn }
           {#2} {#3}
       }
-      { \__text_change_case_char:nnnN {#1} {#2} {#3} #4 }
+      { \__text_change_case_codepoint:nnnn {#1} {#2} {#3} {#4} }
   }
-\bool_lazy_or:nnT
-  { \sys_if_engine_luatex_p: }
-  { \sys_if_engine_xetex_p: }
+\cs_new:Npn \__text_change_case_lower_lt:nnnn #1#2#3#4
   {
-   \cs_new:Npn \__text_change_case_lower_lt:nnnN #1#2#3#4
-     {
-        \exp_args:Ne \__text_change_case_lower_lt_auxi:nnnN
+    \exp_args:Ne \__text_change_case_lower_lt_auxi:nnnn
+      {
+        \int_case:nn { \__text_codepoint_from_chars:Nw #4 }
           {
-            \int_case:nn { `#4 }
-              {
-                { "00CC } { "0300 }
-                { "00CD } { "0301 }
-                { "0128 } { "0303 }
-              }
+            { "00CC } { "0300 }
+            { "00CD } { "0301 }
+            { "0128 } { "0303 }
           }
-            {#2} {#3} #4
       }
-    \cs_new:Npn \__text_change_case_lower_lt_auxi:nnnN #1#2#3#4
+        {#2} {#3} {#4}
+  }
+\cs_new:Npn \__text_change_case_lower_lt_auxi:nnnn #1#2#3#4
+  {
+    \tl_if_blank:nTF {#1}
       {
-        \tl_if_blank:nTF {#1}
+        \exp_args:Ne \__text_change_case_lower_lt_auxii:nnnn
           {
-            \exp_args:Ne \__text_change_case_lower_lt_auxii:nnnN
+            \int_case:nn { \__text_codepoint_from_chars:Nw #4 }
               {
-                \int_case:nn { `#4 }
-                  {
-                    { "0049 } { "0069 }
-                    { "004A } { "006A }
-                    { "012E } { "012F }
-                  }
+                { "0049 } { "0069 }
+                { "004A } { "006A }
+                { "012E } { "012F }
               }
-              {#2} {#3} #4
           }
+            {#2} {#3} {#4}
+      }
+      {
+        \__text_change_case_store:e
           {
-            \__text_change_case_store:e
-              {
-                \char_generate:nn { "0069 } { \__text_char_catcode:N #4 }
-                \char_generate:nn { "0307 } { \__text_char_catcode:N #4 }
-                \char_generate:nn {#1} { \__text_char_catcode:N #4 }
-              }
-            \__text_change_case_loop:nnw {#2} {#3}
+            \codepoint_generate:nn { "0069 }
+              { \__text_change_case_catcode:nn {#4} { "0069 } }
+            \codepoint_generate:nn { "0307 }
+              { \__text_change_case_catcode:nn {#4} { "0307 } }
+            \codepoint_generate:nn {#1}
+              { \__text_change_case_catcode:nn {#4} {#1} }
           }
+        \__text_change_case_loop:nnw {#2} {#3}
       }
-    \cs_new:Npn \__text_change_case_lower_lt_auxii:nnnN #1#2#3#4
+  }
+\cs_new:Npn \__text_change_case_lower_lt_auxii:nnnn #1#2#3#4
+  {
+    \tl_if_blank:nTF {#1}
+      { \__text_change_case_codepoint:nnnn {#2} {#2} {#3} {#4} }
       {
-        \tl_if_blank:nTF {#1}
-          { \__text_change_case_lower_sigma:nnnN {#2} {#2} {#3} #4 }
+        \__text_change_case_store:e
           {
-            \__text_change_case_store:e
-              { \char_generate:nn {#1} { \__text_char_catcode:N #4 } }
-            \__text_change_case_lower_lt:nnw {#2} {#3}
+            \codepoint_generate:nn {#1}
+              { \__text_change_case_catcode:nn {#4} {#1} }
           }
+        \__text_change_case_lower_lt:nnw {#2} {#3}
       }
-    \cs_new:Npn \__text_change_case_lower_lt:nnw #1#2#3 \q__text_recursion_stop
+  }
+\cs_new:Npn \__text_change_case_lower_lt:nnw #1#2#3 \q__text_recursion_stop
+  {
+    \tl_if_head_is_N_type:nTF {#3}
+      { \__text_change_case_lower_lt:nnN }
+      { \__text_change_case_loop:nnw }
+       {#1} {#2} #3 \q__text_recursion_stop
+  }
+\cs_new:Npn \__text_change_case_lower_lt:nnN #1#2#3
+  {
+    \__text_codepoint_process:nN
+      { \__text_change_case_lower_lt:nnn {#1} {#2} } #3
+  }
+\cs_new:Npn \__text_change_case_lower_lt:nnn #1#2#3
+  {
+    \bool_lazy_and:nnT
       {
-        \tl_if_head_is_N_type:nTF {#3}
-          { \__text_change_case_lower_lt:nnN }
-          { \__text_change_case_loop:nnw }
-           {#1} {#2} #3 \q__text_recursion_stop
+        \bool_lazy_or_p:nn
+          { ! \tl_if_single_p:n {#3} }
+          { ! \token_if_cs_p:N #3 }
       }
-    \cs_new:Npn \__text_change_case_lower_lt:nnN #1#2#3
       {
-        \bool_lazy_and:nnT
-          { ! \token_if_cs_p:N #3 }
+        \bool_lazy_any_p:n
           {
-            \bool_lazy_any_p:n
-              {
-                { \int_compare_p:nNn { `#3 } = { "0300 } }
-                { \int_compare_p:nNn { `#3 } = { "0301 } }
-                { \int_compare_p:nNn { `#3 } = { "0303 } }
-              }
+            { \__text_codepoint_compare_p:nNn {#3} = { "0300 } }
+            { \__text_codepoint_compare_p:nNn {#3} = { "0301 } }
+            { \__text_codepoint_compare_p:nNn {#3} = { "0303 } }
           }
+      }
+      {
+        \__text_change_case_store:e
           {
-            \__text_change_case_store:e
-              { \char_generate:nn { "0307 } { \__text_char_catcode:N #3 } }
+            \codepoint_generate:nn { "0307 }
+              { \__text_change_case_catcode:nn {#3} { "0307 } }
           }
-        \__text_change_case_loop:nnw {#1} {#2} #3
       }
+    \__text_change_case_loop:nnw {#1} {#2} #3
   }
-\bool_lazy_or:nnT
-  { \sys_if_engine_luatex_p: }
-  { \sys_if_engine_xetex_p: }
-  {
-   \cs_new:Npn \__text_change_case_upper_lt:nnnN #1#2#3#4
-     {
-        \exp_args:Ne \__text_change_case_upper_lt_aux:nnnN
+\cs_new:Npn \__text_change_case_upper_lt:nnnn #1#2#3#4
+ {
+    \exp_args:Ne \__text_change_case_upper_lt_aux:nnnn
+      {
+        \int_case:nn { \__text_codepoint_from_chars:Nw #4 }
           {
-            \int_case:nn { `#4 }
-              {
-                { "0069 } { "0049 }
-                { "006A } { "004A }
-                { "012F } { "012E }
-              }
+            { "0069 } { "0049 }
+            { "006A } { "004A }
+            { "012F } { "012E }
           }
-            {#2} {#3} #4
       }
-   \cs_new:Npn \__text_change_case_upper_lt_aux:nnnN #1#2#3#4
-     {
-       \tl_if_blank:nTF {#1}
-         { \__text_change_case_char:nnnN { upper } {#2} {#3} #4 }
-         {
-           \__text_change_case_store:e
-             { \char_generate:nn {#1} { \__text_char_catcode:N #4 } }
-           \__text_change_case_upper_lt:nnw {#2} {#3}
-         }
-     }
-    \cs_new:Npn \__text_change_case_upper_lt:nnw #1#2#3 \q__text_recursion_stop
+        {#2} {#3} {#4}
+  }
+\cs_new:Npn \__text_change_case_upper_lt_aux:nnnn #1#2#3#4
+  {
+    \tl_if_blank:nTF {#1}
+      { \__text_change_case_codepoint:nnnn { upper } {#2} {#3} {#4} }
       {
-        \tl_if_head_is_N_type:nTF {#3}
-          { \__text_change_case_upper_lt:nnN }
-          { \use:c { __text_change_case_char_next_ #1 :nn } }
-            {#1} {#2} #3 \q__text_recursion_stop
+        \__text_change_case_store:e
+          {
+            \codepoint_generate:nn {#1}
+              { \__text_change_case_catcode:nn {#4} {#1} }
+          }
+        \__text_change_case_upper_lt:nnw {#2} {#3}
       }
-    \cs_new:Npn \__text_change_case_upper_lt:nnN #1#2#3
+  }
+\cs_new:Npn \__text_change_case_upper_lt:nnw #1#2#3 \q__text_recursion_stop
+  {
+    \tl_if_head_is_N_type:nTF {#3}
+      { \__text_change_case_upper_lt:nnN }
+      { \use:c { __text_change_case_next_ #1 :nn } }
+        {#1} {#2} #3 \q__text_recursion_stop
+  }
+\cs_new:Npn \__text_change_case_upper_lt:nnN #1#2#3
+  {
+    \__text_codepoint_process:nN
+      { \__text_change_case_upper_lt:nnn {#1} {#2} } #3
+  }
+\cs_new:Npn \__text_change_case_upper_lt:nnn #1#2#3
+  {
+    \bool_lazy_and:nnTF
       {
-        \bool_lazy_and:nnTF
+        \bool_lazy_or_p:nn
+          { ! \tl_if_single_p:n {#3} }
           { ! \token_if_cs_p:N #3 }
-          { \int_compare_p:nNn { `#3 } = { "0307 } }
-          { \use:c { __text_change_case_char_next_ #1 :nn } {#1} {#2} }
-          { \use:c { __text_change_case_char_next_ #1 :nn } {#1} {#2} #3 }
       }
+      { \__text_codepoint_compare_p:nNn {#3} = { "0307 } }
+      { \use:c { __text_change_case_next_ #1 :nn } {#1} {#2} }
+      { \use:c { __text_change_case_next_ #1 :nn } {#1} {#2} #3 }
   }
-\cs_new:Npn \__text_change_case_title_nl:nnnN #1#2#3#4
+\cs_new:Npn \__text_change_case_title_nl:nnnn #1#2#3#4
   {
+    \tl_if_single:nTF {#4}
+      { \__text_change_case_title_nl_aux:nnnn }
+      { \__text_change_case_codepoint:nnnn }
+        {#1} {#2} {#3} {#4}
+  }
+\cs_new:Npn \__text_change_case_title_nl_aux:nnnn #1#2#3#4
+  {
     \bool_lazy_or:nnTF
-      { \int_compare_p:nNn { `#4 } = { "0049 } }
-      { \int_compare_p:nNn { `#4 } = { "0069 } }
+      { \int_compare_p:nNn {`#4} = { "0049 } }
+      { \int_compare_p:nNn {`#4} = { "0069 } }
       {
         \__text_change_case_store:e
           { \char_generate:nn { "0049 } { \__text_char_catcode:N #4 } }
         \__text_change_case_title_nl:nnw {#2} {#3}
       }
-      { \__text_change_case_char:nnnN {#1} {#2} {#3} #4 }
+      { \__text_change_case_codepoint:nnnn {#1} {#2} {#3} {#4} }
   }
 \cs_new:Npn \__text_change_case_title_nl:nnw #1#2#3 \q__text_recursion_stop
   {
     \tl_if_head_is_N_type:nTF {#3}
       { \__text_change_case_title_nl:nnN }
-      { \use:c { __text_change_case_char_next_ #1 :nn } }
+      { \use:c { __text_change_case_next_ #1 :nn } }
         {#1} {#2} #3 \q__text_recursion_stop
   }
 \cs_new:Npn \__text_change_case_title_nl:nnN #1#2#3
@@ -33877,1068 +34113,96 @@
       { ! \token_if_cs_p:N #3 }
       {
         \bool_lazy_or_p:nn
-          { \int_compare_p:nNn { `#3 } = { "004A } }
-          { \int_compare_p:nNn { `#3 } = { "006A } }
+          { \int_compare_p:nNn {`#3} = { "004A } }
+          { \int_compare_p:nNn {`#3} = { "006A } }
       }
       {
         \__text_change_case_store:e
           { \char_generate:nn { "004A } { \__text_char_catcode:N #3 } }
-        \use:c { __text_change_case_char_next_ #1 :nn } {#1} {#2}
+        \use:c { __text_change_case_next_ #1 :nn } {#1} {#2}
       }
-      { \use:c { __text_change_case_char_next_ #1 :nn } {#1} {#2} #3 }
+      { \use:c { __text_change_case_next_ #1 :nn } {#1} {#2} #3 }
   }
-\bool_lazy_or:nnTF
-  { \sys_if_engine_luatex_p: }
-  { \sys_if_engine_xetex_p: }
+\cs_new:Npn \__text_change_case_lower_tr:nnnn #1#2#3#4
   {
-    \cs_new:Npn \__text_change_case_lower_tr:nnnN #1#2#3#4
+    \__text_codepoint_compare:nNnTF {#4} = { "0049 }
+      { \__text_change_case_lower_tr:nnNw {#1} {#3} #4 }
       {
-        \int_compare:nNnTF { `#4 } = { "0049 }
-          { \__text_change_case_lower_tr:nnNw {#1} {#3} #4 }
+        \__text_codepoint_compare:nNnTF {#4} = { "0130 }
           {
-            \int_compare:nNnTF { `#4 } = { "0130 }
+            \__text_change_case_store:e
               {
-                \__text_change_case_store:e
-                  { \char_generate:nn { "0069 } { \__text_char_catcode:N #4 } }
-                \__text_change_case_loop:nnw {#1} {#3}
+                \codepoint_generate:nn { "0069 }
+                  { \__text_change_case_catcode:nn {#4} { "0069 } }
               }
-              { \__text_change_case_lower_sigma:nnnN {#1} {#2} {#3} #4 }
+            \__text_change_case_loop:nnw {#1} {#3}
           }
+          { \__text_change_case_codepoint:nnnn {#1} {#2} {#3} {#4} }
       }
-    \cs_new:Npn \__text_change_case_lower_tr:nnNw #1#2#3#4 \q__text_recursion_stop
+  }
+\cs_new:Npn \__text_change_case_lower_tr:nnNw #1#2#3#4 \q__text_recursion_stop
+  {
+    \tl_if_head_is_N_type:nTF {#4}
+      { \__text_change_case_lower_tr:NnnN  #3 {#1} {#2} }
       {
-        \tl_if_head_is_N_type:nTF {#4}
-          { \__text_change_case_lower_tr:NnnN #3 }
+        \__text_change_case_store:e
           {
-            \__text_change_case_store:e
-              { \char_generate:nn { "0131 } { \__text_char_catcode:N #3 } }
-            \__text_change_case_loop:nnw
+            \codepoint_generate:nn { "0131 }
+              { \__text_change_case_catcode:nn {#3} { "0131 } }
           }
-            {#1} {#2} #4 \q__text_recursion_stop
+        \__text_change_case_loop:nnw {#1} {#2}
       }
-    \cs_new:Npn \__text_change_case_lower_tr:NnnN #1#2#3#4
+        #4 \q__text_recursion_stop
+  }
+\cs_new:Npn \__text_change_case_lower_tr:NnnN #1#2#3#4
+  {
+    \__text_codepoint_process:nN
+      { \__text_change_case_lower_tr:Nnnn #1 {#2} {#3} } #4
+  }
+\cs_new:Npn \__text_change_case_lower_tr:Nnnn #1#2#3#4
+  {
+    \bool_lazy_or:nnTF
       {
-        \bool_lazy_or:nnTF
+        \bool_lazy_and_p:nn
+          { \tl_if_single_p:n {#4} }
           { \token_if_cs_p:N #4 }
-          { ! \int_compare_p:nNn { `#4 } = { "0307 } }
-          {
-            \__text_change_case_store:e
-              { \char_generate:nn { "0131 } { \__text_char_catcode:N #1 } }
-            \__text_change_case_loop:nnw {#2} {#3} #4
-          }
-          {
-            \__text_change_case_store:e
-              { \char_generate:nn { "0069 } { \__text_char_catcode:N #1 } }
-            \__text_change_case_loop:nnw {#2} {#3}
-          }
       }
-  }
-  {
-    \cs_new:Npn \__text_change_case_lower_tr:nnnN #1#2#3#4
+      { ! \__text_codepoint_compare_p:nNn {#4} = { "0307 } }
       {
-        \int_compare:nNnTF { `#4 } = { "0049 }
+        \__text_change_case_store:e
           {
-            \__text_change_case_store:V \c__text_dotless_i_tl
-            \__text_change_case_loop:nnw {#1} {#3}
+            \codepoint_generate:nn { "0131 }
+              { \__text_change_case_catcode:nn {#1} { "0131 } }
           }
-          {
-            \int_compare:nNnTF { `#4 } = { "00C4 }
-              { \__text_change_case_lower_tr:nnnNN {#1} {#2} {#3} #4 }
-              { \__text_change_case_char:nnnN {#1} {#2} {#3} #4 }
-          }
+        \__text_change_case_loop:nnw {#2} {#3} #4
       }
-    \cs_new:Npn \__text_change_case_lower_tr:nnnNN #1#2#3#4#5
       {
-        \int_compare:nNnTF { `#5 } = { "00B0 }
+        \__text_change_case_store:e
           {
-            \__text_change_case_store:e
-              {
-                \char_generate:nn { "0069 }
-                  { \char_value_catcode:n { "0069 } }
-              }
-            \__text_change_case_loop:nnw {#1} {#3}
+            \codepoint_generate:nn { "0069 }
+              { \__text_change_case_catcode:nn {#1} { "0069 } }
           }
-          { \__text_change_case_char:nnnN {#1} {#2} {#3} #4#5 }
+        \__text_change_case_loop:nnw {#2} {#3}
       }
   }
-\cs_new:Npx \__text_change_case_upper_tr:nnnN #1#2#3#4
+\cs_new:Npn \__text_change_case_upper_tr:nnnn #1#2#3#4
   {
-    \exp_not:N \int_compare:nNnTF { `#4 } = { "0069 }
+    \__text_codepoint_compare:nNnTF {#4} = { "0069 }
       {
-        \bool_lazy_or:nnTF
-          { \sys_if_engine_luatex_p: }
-          { \sys_if_engine_xetex_p: }
+        \__text_change_case_store:e
           {
-            \exp_not:N \__text_change_case_store:e
-              {
-                \exp_not:N \char_generate:nn { "0130 }
-                  { \exp_not:N \__text_char_catcode:N #4 }
-              }
+            \codepoint_generate:nn { "0130 }
+              { \__text_change_case_catcode:nn {#4} { "0130 } }
           }
-          {
-            \exp_not:N \__text_change_case_store:V
-            \exp_not:N \c__text_dotted_I_tl
-          }
-        \exp_not:N \use:c { __text_change_case_char_next_ #2 :nn } {#2} {#3}
+        \use:c { __text_change_case_next_ #2 :nn } {#2} {#3}
       }
-      { \exp_not:N \__text_change_case_char:nnnN {#1} {#2} {#3} #4 }
+      { \__text_change_case_codepoint:nnnn {#1} {#2} {#3} {#4} }
   }
-\cs_new_eq:NN \__text_change_case_lower_az:nnnN
-  \__text_change_case_lower_tr:nnnN
-\cs_new_eq:NN \__text_change_case_upper_az:nnnN
-  \__text_change_case_upper_tr:nnnN
+\cs_new_eq:NN \__text_change_case_lower_az:nnnn
+  \__text_change_case_lower_tr:nnnn
+\cs_new_eq:NN \__text_change_case_upper_az:nnnn
+  \__text_change_case_upper_tr:nnnn
 \group_begin:
-  \bool_lazy_or:nnF
-    { \sys_if_engine_luatex_p: }
-    { \sys_if_engine_xetex_p: }
-    {
-      \cs_set_protected:Npn \__text_tmp:w #1#2
-        {
-          \group_begin:
-            \cs_set_protected:Npn \__text_tmp:w ##1##2##3##4
-              {
-                \tl_const:Nx #1
-                  {
-                    \exp_after:wN \exp_after:wN \exp_after:wN
-                      \exp_not:N \char_generate:nn {##1} { 13 }
-                    \exp_after:wN \exp_after:wN \exp_after:wN
-                      \exp_not:N \char_generate:nn {##2} { 13 }
-                    \tl_if_blank:nF {##3}
-                      {
-                        \exp_after:wN \exp_after:wN \exp_after:wN
-                          \exp_not:N \char_generate:nn {##3} { 13 }
-                      }
-                  }
-              }
-            \use:x
-              { \__text_tmp:w \char_to_utfviii_bytes:n { "#2 } }
-          \group_end:
-        }
-      \__text_tmp:w \c__text_dotless_i_tl      { 0131 }
-      \__text_tmp:w \c__text_dotted_I_tl       { 0130 }
-      \__text_tmp:w \c__text_i_ogonek_tl       { 012F }
-      \__text_tmp:w \c__text_I_ogonek_tl       { 012E }
-      \__text_tmp:w \c__text_final_sigma_tl    { 03C2 }
-      \__text_tmp:w \c__text_sigma_tl          { 03C3 }
-      \__text_tmp:w \c__text_grosses_Eszett_tl { 1E9E }
-    }
-\group_end:
-\group_begin:
-  \bool_lazy_or:nnF
-    { \sys_if_engine_luatex_p: }
-    { \sys_if_engine_xetex_p: }
-    {
-      \cs_set_protected:Npn \__text_loop:nn #1#2
-        {
-          \quark_if_recursion_tail_stop:n {#1}
-          \use:x
-            {
-              \__text_tmp:w
-                \char_to_utfviii_bytes:n { "#1 }
-                \char_to_utfviii_bytes:n { "#2 }
-            }
-          \__text_loop:nn
-        }
-      \cs_set_protected:Npn \__text_tmp:nnnn #1#2#3#4#5
-        {
-          \tl_const:cx
-            {
-              c__kernel_ #1 case_
-              \char_generate:nn {#2} { 12 }
-              \char_generate:nn {#3} { 12 }
-              _tl
-            }
-            {
-              \exp_after:wN \exp_after:wN \exp_after:wN
-                \exp_not:N \char_generate:nn {#4} { 13 }
-              \exp_after:wN \exp_after:wN \exp_after:wN
-                \exp_not:N \char_generate:nn {#5} { 13 }
-            }
-        }
-      \cs_set_protected:Npn \__text_tmp:w #1#2#3#4#5#6#7#8
-        {
-          \tl_const:cx
-            {
-              c__kernel_lowercase_
-              \char_generate:nn {#1} { 12 }
-              \char_generate:nn {#2} { 12 }
-              _tl
-            }
-            {
-              \exp_after:wN \exp_after:wN \exp_after:wN
-                \exp_not:N \char_generate:nn {#5} { 13 }
-              \exp_after:wN \exp_after:wN \exp_after:wN
-                \exp_not:N \char_generate:nn {#6} { 13 }
-            }
-          \__text_tmp:nnnn { upper } {#5} {#6} {#1} {#2}
-          \__text_tmp:nnnn { title } {#5} {#6} {#1} {#2}
-        }
-      \__text_loop:nn
-        { 00C0 } { 00E0 }
-        { 00C1 } { 00E1 }
-        { 00C2 } { 00E2 }
-        { 00C3 } { 00E3 }
-        { 00C4 } { 00E4 }
-        { 00C5 } { 00E5 }
-        { 00C6 } { 00E6 }
-        { 00C7 } { 00E7 }
-        { 00C8 } { 00E8 }
-        { 00C9 } { 00E9 }
-        { 00CA } { 00EA }
-        { 00CB } { 00EB }
-        { 00CC } { 00EC }
-        { 00CD } { 00ED }
-        { 00CE } { 00EE }
-        { 00CF } { 00EF }
-        { 00D0 } { 00F0 }
-        { 00D1 } { 00F1 }
-        { 00D2 } { 00F2 }
-        { 00D3 } { 00F3 }
-        { 00D4 } { 00F4 }
-        { 00D5 } { 00F5 }
-        { 00D6 } { 00F6 }
-        { 00D8 } { 00F8 }
-        { 00D9 } { 00F9 }
-        { 00DA } { 00FA }
-        { 00DB } { 00FB }
-        { 00DC } { 00FC }
-        { 00DD } { 00FD }
-        { 00DE } { 00FE }
-        { 0100 } { 0101 }
-        { 0102 } { 0103 }
-        { 0104 } { 0105 }
-        { 0106 } { 0107 }
-        { 0108 } { 0109 }
-        { 010A } { 010B }
-        { 010C } { 010D }
-        { 010E } { 010F }
-        { 0110 } { 0111 }
-        { 0112 } { 0113 }
-        { 0114 } { 0115 }
-        { 0116 } { 0117 }
-        { 0118 } { 0119 }
-        { 011A } { 011B }
-        { 011C } { 011D }
-        { 011E } { 011F }
-        { 0120 } { 0121 }
-        { 0122 } { 0123 }
-        { 0124 } { 0125 }
-        { 0128 } { 0129 }
-        { 012A } { 012B }
-        { 012C } { 012D }
-        { 012E } { 012F }
-        { 0132 } { 0133 }
-        { 0134 } { 0135 }
-        { 0136 } { 0137 }
-        { 0139 } { 013A }
-        { 013B } { 013C }
-        { 013E } { 013F }
-        { 0141 } { 0142 }
-        { 0143 } { 0144 }
-        { 0145 } { 0146 }
-        { 0147 } { 0148 }
-        { 014A } { 014B }
-        { 014C } { 014D }
-        { 014E } { 014F }
-        { 0150 } { 0151 }
-        { 0152 } { 0153 }
-        { 0154 } { 0155 }
-        { 0156 } { 0157 }
-        { 0158 } { 0159 }
-        { 015A } { 015B }
-        { 015C } { 015D }
-        { 015E } { 015F }
-        { 0160 } { 0161 }
-        { 0162 } { 0163 }
-        { 0164 } { 0165 }
-        { 0168 } { 0169 }
-        { 016A } { 016B }
-        { 016C } { 016D }
-        { 016E } { 016F }
-        { 0170 } { 0171 }
-        { 0172 } { 0173 }
-        { 0174 } { 0175 }
-        { 0176 } { 0177 }
-        { 0178 } { 00FF }
-        { 0179 } { 017A }
-        { 017B } { 017C }
-        { 017D } { 017E }
-        { 01A0 } { 01A1 }
-        { 01AF } { 01B0 }
-        { 01CD } { 01CE }
-        { 01CF } { 01D0 }
-        { 01D1 } { 01D2 }
-        { 01D3 } { 01D4 }
-        { 01E2 } { 01E3 }
-        { 01E6 } { 01E7 }
-        { 01E8 } { 01E9 }
-        { 01EA } { 01EB }
-        { 01F4 } { 01F5 }
-        { 0218 } { 0219 }
-        { 021A } { 021B }
-        { 0400 } { 0450 }
-        { 0401 } { 0451 }
-        { 0402 } { 0452 }
-        { 0403 } { 0453 }
-        { 0404 } { 0454 }
-        { 0405 } { 0455 }
-        { 0406 } { 0456 }
-        { 0407 } { 0457 }
-        { 0408 } { 0458 }
-        { 0409 } { 0459 }
-        { 040A } { 045A }
-        { 040B } { 045B }
-        { 040C } { 045C }
-        { 040D } { 045D }
-        { 040E } { 045E }
-        { 040F } { 045F }
-        { 0410 } { 0430 }
-        { 0411 } { 0431 }
-        { 0412 } { 0432 }
-        { 0413 } { 0433 }
-        { 0414 } { 0434 }
-        { 0415 } { 0435 }
-        { 0416 } { 0436 }
-        { 0417 } { 0437 }
-        { 0418 } { 0438 }
-        { 0419 } { 0439 }
-        { 041A } { 043A }
-        { 041B } { 043B }
-        { 041C } { 043C }
-        { 041D } { 043D }
-        { 041E } { 043E }
-        { 041F } { 043F }
-        { 0420 } { 0440 }
-        { 0421 } { 0441 }
-        { 0422 } { 0442 }
-        { 0423 } { 0443 }
-        { 0424 } { 0444 }
-        { 0425 } { 0445 }
-        { 0426 } { 0446 }
-        { 0427 } { 0447 }
-        { 0428 } { 0448 }
-        { 0429 } { 0449 }
-        { 042A } { 044A }
-        { 042B } { 044B }
-        { 042C } { 044C }
-        { 042D } { 044D }
-        { 042E } { 044E }
-        { 042F } { 044F }
-        { 0370 } { 0371 }
-        { 0372 } { 0373 }
-        { 0376 } { 0377 }
-        { 03FD } { 037B }
-        { 03FE } { 037C }
-        { 03FF } { 037D }
-        { 0386 } { 03AC }
-        { 0388 } { 03AD }
-        { 0389 } { 03AE }
-        { 038A } { 03AF }
-        { 0391 } { 03B1 }
-        { 0392 } { 03B2 }
-        { 0393 } { 03B3 }
-        { 0394 } { 03B4 }
-        { 0395 } { 03B5 }
-        { 0396 } { 03B6 }
-        { 0397 } { 03B7 }
-        { 0398 } { 03B8 }
-        { 0399 } { 03B9 }
-        { 039A } { 03BA }
-        { 039B } { 03BB }
-        { 039C } { 03BC }
-        { 039D } { 03BD }
-        { 039E } { 03BE }
-        { 039F } { 03BF }
-        { 03A0 } { 03C0 }
-        { 03A1 } { 03C1 }
-        { 03A3 } { 03C3 }
-        { 03A4 } { 03C4 }
-        { 03A5 } { 03C5 }
-        { 03A6 } { 03C6 }
-        { 03A7 } { 03C7 }
-        { 03A8 } { 03C8 }
-        { 03A9 } { 03C9 }
-        { 03AA } { 03CA }
-        { 03AB } { 03CB }
-        { 038C } { 03CC }
-        { 038E } { 03CD }
-        { 038F } { 03CE }
-        { 03CF } { 03D7 }
-        { 03D8 } { 03D9 }
-        { 03DA } { 03DB }
-        { 03DC } { 03DD }
-        { 03DE } { 03DF }
-        { 03E0 } { 03E1 }
-        { 03E2 } { 03E3 }
-        { 03E4 } { 03E5 }
-        { 03E6 } { 03E7 }
-        { 03E8 } { 03E9 }
-        { 03EA } { 03EB }
-        { 03EC } { 03ED }
-        { 03EE } { 03EF }
-        { 03F9 } { 03F2 }
-        { 037F } { 03F3 }
-        { 03F7 } { 03F8 }
-        { 03FA } { 03FB }
-        \q_recursion_tail ?
-        \q_recursion_stop
-      \cs_set_protected:Npn \__text_tmp:w #1#2#3
-        {
-          \group_begin:
-            \cs_set_protected:Npn \__text_tmp:w ##1##2##3##4##5##6##7##8
-              {
-                \tl_const:cx
-                  {
-                    c__kernel_ #3 case_
-                    \char_generate:nn {##1} { 12 }
-                    \char_generate:nn {##2} { 12 }
-                    _tl
-                  }
-                  {
-                    \exp_after:wN \exp_after:wN \exp_after:wN
-                      \exp_not:N \char_generate:nn {##5} { 13 }
-                    \exp_after:wN \exp_after:wN \exp_after:wN
-                      \exp_not:N \char_generate:nn {##6} { 13 }
-                  }
-              }
-            \use:x
-              {
-                \__text_tmp:w
-                  \char_to_utfviii_bytes:n { "#1 }
-                  \char_to_utfviii_bytes:n { "#2 }
-              }
-          \group_end:
-        }
-      \__text_tmp:w { 0345 } { 0399 } { upper }
-      \__text_tmp:w { 03C2 } { 03A3 } { upper }
-      \__text_tmp:w { 03D0 } { 0392 } { upper }
-      \__text_tmp:w { 03D1 } { 0398 } { upper }
-      \__text_tmp:w { 03D5 } { 03A6 } { upper }
-      \__text_tmp:w { 03D6 } { 03A0 } { upper }
-      \__text_tmp:w { 03F0 } { 039A } { upper }
-      \__text_tmp:w { 03F1 } { 03A1 } { upper }
-      \__text_tmp:w { 03F4 } { 03B8 } { lower }
-      \__text_tmp:w { 03F5 } { 0395 } { upper }
-      \__text_tmp:w { 01C4 } { 01C5 } { title }
-      \__text_tmp:w { 01C4 } { 01C6 } { lower }
-      \__text_tmp:w { 01C5 } { 01C4 } { upper }
-      \__text_tmp:w { 01C5 } { 01C6 } { lower }
-      \__text_tmp:w { 01C6 } { 01C4 } { upper }
-      \__text_tmp:w { 01C6 } { 01C5 } { title }
-      \__text_tmp:w { 01C7 } { 01C8 } { title }
-      \__text_tmp:w { 01C7 } { 01C9 } { lower }
-      \__text_tmp:w { 01C8 } { 01C7 } { upper }
-      \__text_tmp:w { 01C8 } { 01C9 } { lower }
-      \__text_tmp:w { 01C9 } { 01C7 } { upper }
-      \__text_tmp:w { 01C9 } { 01C8 } { title }
-      \__text_tmp:w { 01CA } { 01CB } { title }
-      \__text_tmp:w { 01CA } { 01CC } { lower }
-      \__text_tmp:w { 01CB } { 01CA } { upper }
-      \__text_tmp:w { 01CB } { 01CC } { lower }
-      \__text_tmp:w { 01CC } { 01CA } { upper }
-      \__text_tmp:w { 01CC } { 01CB } { title }
-      \cs_set_protected:Npn \__text_tmp:w #1#2#3
-        {
-          \group_begin:
-            \cs_set_protected:Npn \__text_tmp:w ##1##2##3##4
-              {
-                \tl_const:cn
-                  {
-                    c__kernel_ #3 case_
-                    \char_generate:nn {##1} { 12 }
-                    \char_generate:nn {##2} { 12 }
-                    _tl
-                  }
-                    {#2}
-              }
-            \use:x
-              { \__text_tmp:w \char_to_utfviii_bytes:n { "#1 } }
-          \group_end:
-        }
-      \__text_tmp:w { 00DF } { SS } { upper }
-      \__text_tmp:w { 00DF } { Ss } { title }
-      \__text_tmp:w { 0131 } { I }  { upper }
-      \cs_set_protected:Npn \__text_tmp:nnnnnn #1#2#3#4#5#6#7
-        {
-          \tl_const:cx
-            {
-              c__kernel_ #1 case_
-              \char_generate:nn {#2} { 12 }
-              \char_generate:nn {#3} { 12 }
-              \char_generate:nn {#4} { 12 }
-              _tl
-            }
-            {
-              \exp_after:wN \exp_after:wN \exp_after:wN
-                \exp_not:N \char_generate:nn {#5} { 13 }
-              \exp_after:wN \exp_after:wN \exp_after:wN
-                \exp_not:N \char_generate:nn {#6} { 13 }
-              \exp_after:wN \exp_after:wN \exp_after:wN
-                \exp_not:N \char_generate:nn {#7} { 13 }
-            }
-        }
-      \cs_set_protected:Npn \__text_tmp:w #1#2#3#4#5#6#7#8
-        {
-          \tl_const:cx
-            {
-              c__kernel_lowercase_
-              \char_generate:nn {#1} { 12 }
-              \char_generate:nn {#2} { 12 }
-              \char_generate:nn {#3} { 12 }
-              _tl
-            }
-            {
-              \exp_after:wN \exp_after:wN \exp_after:wN
-                \exp_not:N \char_generate:nn {#5} { 13 }
-              \exp_after:wN \exp_after:wN \exp_after:wN
-                \exp_not:N \char_generate:nn {#6} { 13 }
-              \exp_after:wN \exp_after:wN \exp_after:wN
-                \exp_not:N \char_generate:nn {#7} { 13 }
-            }
-          \__text_tmp:nnnnnn { upper } {#5} {#6} {#7} {#1} {#2} {#3}
-          \__text_tmp:nnnnnn { title } {#5} {#6} {#7} {#1} {#2} {#3}
-        }
-      \__text_loop:nn
-        { 1EA0 } { 1EA1 }
-        { 1EA2 } { 1EA3 }
-        { 1EA4 } { 1EA5 }
-        { 1EA6 } { 1EA7 }
-        { 1EA8 } { 1EA9 }
-        { 1EAA } { 1EAB }
-        { 1EAC } { 1EAD }
-        { 1EAE } { 1EAF }
-        { 1EB0 } { 1EB1 }
-        { 1EB2 } { 1EB3 }
-        { 1EB4 } { 1EB5 }
-        { 1EB6 } { 1EB7 }
-        { 1EB8 } { 1EB9 }
-        { 1EBA } { 1EBB }
-        { 1EBC } { 1EBD }
-        { 1EBE } { 1EBF }
-        { 1EC0 } { 1EC1 }
-        { 1EC2 } { 1EC3 }
-        { 1EC4 } { 1EC5 }
-        { 1EC6 } { 1EC7 }
-        { 1EC8 } { 1EC9 }
-        { 1ECA } { 1ECB }
-        { 1ECC } { 1ECD }
-        { 1ECE } { 1ECF }
-        { 1ED0 } { 1ED1 }
-        { 1ED2 } { 1ED3 }
-        { 1ED4 } { 1ED5 }
-        { 1ED6 } { 1ED7 }
-        { 1ED8 } { 1ED9 }
-        { 1EDA } { 1EDB }
-        { 1EDC } { 1EDD }
-        { 1EDE } { 1EDF }
-        { 1EE0 } { 1EE1 }
-        { 1EE2 } { 1EE3 }
-        { 1EE4 } { 1EE5 }
-        { 1EE6 } { 1EE7 }
-        { 1EE8 } { 1EE9 }
-        { 1EEA } { 1EEB }
-        { 1EEC } { 1EED }
-        { 1EEE } { 1EEF }
-        { 1EF0 } { 1EF1 }
-        { 1EF2 } { 1EF3 }
-        { 1EF4 } { 1EF5 }
-        { 1EF6 } { 1EF7 }
-        { 1EF8 } { 1EF9 }
-        \q_recursion_tail ?
-        \q_recursion_stop
-      \__text_loop:nn
-        { 1F08 } { 1F00 }
-        { 1F09 } { 1F01 }
-        { 1F0A } { 1F02 }
-        { 1F0B } { 1F03 }
-        { 1F0C } { 1F04 }
-        { 1F0D } { 1F05 }
-        { 1F0E } { 1F06 }
-        { 1F0F } { 1F07 }
-        { 1F18 } { 1F10 }
-        { 1F19 } { 1F11 }
-        { 1F1A } { 1F12 }
-        { 1F1B } { 1F13 }
-        { 1F1C } { 1F14 }
-        { 1F1D } { 1F15 }
-        { 1F28 } { 1F20 }
-        { 1F29 } { 1F21 }
-        { 1F2A } { 1F22 }
-        { 1F2B } { 1F23 }
-        { 1F2C } { 1F24 }
-        { 1F2D } { 1F25 }
-        { 1F2E } { 1F26 }
-        { 1F2F } { 1F27 }
-        { 1F38 } { 1F30 }
-        { 1F39 } { 1F31 }
-        { 1F3A } { 1F32 }
-        { 1F3B } { 1F33 }
-        { 1F3C } { 1F34 }
-        { 1F3D } { 1F35 }
-        { 1F3E } { 1F36 }
-        { 1F3F } { 1F37 }
-        { 1F48 } { 1F40 }
-        { 1F49 } { 1F41 }
-        { 1F4A } { 1F42 }
-        { 1F4B } { 1F43 }
-        { 1F4C } { 1F44 }
-        { 1F4D } { 1F45 }
-        { 1F59 } { 1F51 }
-        { 1F5B } { 1F53 }
-        { 1F5D } { 1F55 }
-        { 1F5F } { 1F57 }
-        { 1F68 } { 1F60 }
-        { 1F69 } { 1F61 }
-        { 1F6A } { 1F62 }
-        { 1F6B } { 1F63 }
-        { 1F6C } { 1F64 }
-        { 1F6D } { 1F65 }
-        { 1F6E } { 1F66 }
-        { 1F6F } { 1F67 }
-        { 1FBA } { 1F70 }
-        { 1FBB } { 1F71 }
-        { 1FC8 } { 1F72 }
-        { 1FC9 } { 1F73 }
-        { 1FCA } { 1F74 }
-        { 1FCB } { 1F75 }
-        { 1FDA } { 1F76 }
-        { 1FDB } { 1F77 }
-        { 1FF8 } { 1F78 }
-        { 1FF9 } { 1F79 }
-        { 1FEA } { 1F7A }
-        { 1FEB } { 1F7B }
-        { 1FFA } { 1F7C }
-        { 1FFB } { 1F7D }
-        { 1F88 } { 1F80 }
-        { 1F89 } { 1F81 }
-        { 1F8A } { 1F82 }
-        { 1F8B } { 1F83 }
-        { 1F8C } { 1F84 }
-        { 1F8D } { 1F85 }
-        { 1F8E } { 1F86 }
-        { 1F8F } { 1F87 }
-        { 1F98 } { 1F90 }
-        { 1F99 } { 1F91 }
-        { 1F9A } { 1F92 }
-        { 1F9B } { 1F93 }
-        { 1F9C } { 1F94 }
-        { 1F9D } { 1F95 }
-        { 1F9E } { 1F96 }
-        { 1F9F } { 1F97 }
-        { 1FA8 } { 1FA0 }
-        { 1FA9 } { 1FA1 }
-        { 1FAA } { 1FA2 }
-        { 1FAB } { 1FA3 }
-        { 1FAC } { 1FA4 }
-        { 1FAD } { 1FA5 }
-        { 1FAE } { 1FA6 }
-        { 1FAF } { 1FA7 }
-        { 1FB8 } { 1FB0 }
-        { 1FB9 } { 1FB1 }
-        { 1FBC } { 1FB3 }
-        { 1FCC } { 1FC3 }
-        { 1FD8 } { 1FD0 }
-        { 1FD9 } { 1FD1 }
-        { 1FE8 } { 1FE0 }
-        { 1FE9 } { 1FE1 }
-        { 1FEC } { 1FE5 }
-        { 1FFC } { 1FF3 }
-        \q_recursion_tail ?
-        \q_recursion_stop
-      \cs_set_protected:Npn \__text_tmp:w #1#2#3
-        {
-          \group_begin:
-            \cs_set_protected:Npn \__text_tmp:w ##1##2##3##4##5##6##7##8
-              {
-                \tl_const:cx
-                  {
-                    c__kernel_ #3 case_
-                    \char_generate:nn {##1} { 12 }
-                    \char_generate:nn {##2} { 12 }
-                    \char_generate:nn {##3} { 12 }
-                    _tl
-                  }
-                  {
-                    \exp_after:wN \exp_after:wN \exp_after:wN
-                      \exp_not:N \char_generate:nn {##5} { 13 }
-                    \exp_after:wN \exp_after:wN \exp_after:wN
-                      \exp_not:N \char_generate:nn {##6} { 13 }
-                  }
-              }
-            \use:x
-              {
-                \__text_tmp:w
-                  \char_to_utfviii_bytes:n { "#1 }
-                  \char_to_utfviii_bytes:n { "#2 }
-              }
-          \group_end:
-        }
-    \__text_tmp:w { 1FBE } { 0399 } { upper }
-      \cs_set_protected:Npn \__text_tmp:w #1#2#3#4#5#6#7#8
-        {
-          \tl_const:cx
-            {
-              c__text_uppercase_el_
-              \char_generate:nn {#1} { 12 }
-              \char_generate:nn {#2} { 12 }
-              _tl
-            }
-            {
-              \exp_after:wN \exp_after:wN \exp_after:wN
-                \exp_not:N \char_generate:nn {#5} { 13 }
-              \exp_after:wN \exp_after:wN \exp_after:wN
-                \exp_not:N \char_generate:nn {#6} { 13 }
-            }
-        }
-      \__text_loop:nn
-        { 0370 } { 0370 }
-        { 0371 } { 0370 }
-        { 0372 } { 0372 }
-        { 0373 } { 0372 }
-        { 0376 } { 0376 }
-        { 0377 } { 0376 }
-        { 03FD } { 03FD }
-        { 037B } { 03FD }
-        { 03FE } { 03FE }
-        { 037C } { 03FE }
-        { 03FF } { 03FF }
-        { 037D } { 03FF }
-        { 0386 } { 0391 }
-        { 0388 } { 0395 }
-        { 0389 } { 0397 }
-        { 038A } { 0399 }
-        { 038C } { 039F }
-        { 038E } { 03A5 }
-        { 038F } { 03A9 }
-        { 0390 } { 03AA }
-        { 0391 } { 0391 }
-        { 0392 } { 0392 }
-        { 0393 } { 0393 }
-        { 0394 } { 0394 }
-        { 0395 } { 0395 }
-        { 0396 } { 0396 }
-        { 0397 } { 0397 }
-        { 0398 } { 0398 }
-        { 0399 } { 0399 }
-        { 039A } { 039A }
-        { 039B } { 039B }
-        { 039C } { 039C }
-        { 039D } { 039D }
-        { 039E } { 039E }
-        { 039F } { 039F }
-        { 03A0 } { 03A0 }
-        { 03A1 } { 03A1 }
-        { 03A3 } { 03A3 }
-        { 03A4 } { 03A4 }
-        { 03A5 } { 03A5 }
-        { 03A6 } { 03A6 }
-        { 03A7 } { 03A7 }
-        { 03A8 } { 03A8 }
-        { 03A9 } { 03A9 }
-        { 03AA } { 03AA }
-        { 03AB } { 03AB }
-        { 03AC } { 0391 }
-        { 03AD } { 0395 }
-        { 03AE } { 0397 }
-        { 03AF } { 0399 }
-        { 03B0 } { 03AB }
-        { 03B1 } { 0391 }
-        { 03B2 } { 0392 }
-        { 03B3 } { 0393 }
-        { 03B4 } { 0394 }
-        { 03B5 } { 0395 }
-        { 03B6 } { 0396 }
-        { 03B7 } { 0397 }
-        { 03B8 } { 0398 }
-        { 03B9 } { 0399 }
-        { 03BA } { 039A }
-        { 03BB } { 039B }
-        { 03BC } { 039C }
-        { 03BD } { 039D }
-        { 03BE } { 039E }
-        { 03BF } { 039F }
-        { 03C0 } { 03A0 }
-        { 03C1 } { 03A1 }
-        { 03C2 } { 03A3 }
-        { 03C3 } { 03A3 }
-        { 03C4 } { 03A4 }
-        { 03C5 } { 03A5 }
-        { 03C6 } { 03A6 }
-        { 03C7 } { 03A7 }
-        { 03C8 } { 03A8 }
-        { 03C9 } { 03A9 }
-        { 03CA } { 03AA }
-        { 03CB } { 03AB }
-        { 03CC } { 039F }
-        { 03CD } { 03A5 }
-        { 03CE } { 03A9 }
-        { 03D0 } { 0392 }
-        { 03D1 } { 0398 }
-        { 03D2 } { 03A5 }
-        { 03D3 } { 03A5 }
-        { 03D4 } { 03AB }
-        { 03D5 } { 03A6 }
-        { 03D6 } { 03A0 }
-        { 03DA } { 03DA }
-        { 03DB } { 03DA }
-        { 03DC } { 03DC }
-        { 03DD } { 03DC }
-        { 03DE } { 03DE }
-        { 03DF } { 03DE }
-        { 03E0 } { 03E0 }
-        { 03E1 } { 03E0 }
-        { 03F0 } { 039A }
-        { 03F1 } { 03A1 }
-        { 03F2 } { 03F9 }
-        { 03F9 } { 03F9 }
-        \q_recursion_tail ?
-        \q_recursion_stop
-      \cs_set_protected:Npn \__text_tmp:w #1#2#3#4#5#6#7#8
-        {
-          \tl_const:cx
-            {
-              c__text_uppercase_el_
-              \char_generate:nn {#1} { 12 }
-              \char_generate:nn {#2} { 12 }
-              \char_generate:nn {#3} { 12 }
-              _tl
-            }
-            {
-              \exp_after:wN \exp_after:wN \exp_after:wN
-                \exp_not:N \char_generate:nn {#5} { 13 }
-              \exp_after:wN \exp_after:wN \exp_after:wN
-                \exp_not:N \char_generate:nn {#6} { 13 }
-              \tl_if_blank:nF {#7}
-                {
-                  \exp_after:wN \exp_after:wN \exp_after:wN
-                    \exp_not:N \char_generate:nn {#7} { 13 }
-                }
-            }
-        }
-      \__text_loop:nn
-        { 1F00 } { 0391 }
-        { 1F01 } { 0391 }
-        { 1F02 } { 0391 }
-        { 1F03 } { 0391 }
-        { 1F04 } { 0391 }
-        { 1F05 } { 0391 }
-        { 1F06 } { 0391 }
-        { 1F07 } { 0391 }
-        { 1F08 } { 0391 }
-        { 1F09 } { 0391 }
-        { 1F0A } { 0391 }
-        { 1F0B } { 0391 }
-        { 1F0C } { 0391 }
-        { 1F0D } { 0391 }
-        { 1F0E } { 0391 }
-        { 1F0F } { 0391 }
-        { 1F10 } { 0395 }
-        { 1F11 } { 0395 }
-        { 1F12 } { 0395 }
-        { 1F13 } { 0395 }
-        { 1F14 } { 0395 }
-        { 1F15 } { 0395 }
-        { 1F18 } { 0395 }
-        { 1F19 } { 0395 }
-        { 1F1A } { 0395 }
-        { 1F1B } { 0395 }
-        { 1F1C } { 0395 }
-        { 1F1D } { 0395 }
-        { 1F20 } { 0397 }
-        { 1F21 } { 0397 }
-        { 1F22 } { 0397 }
-        { 1F23 } { 0397 }
-        { 1F24 } { 0397 }
-        { 1F25 } { 0397 }
-        { 1F26 } { 0397 }
-        { 1F27 } { 0397 }
-        { 1F28 } { 0397 }
-        { 1F29 } { 0397 }
-        { 1F2A } { 0397 }
-        { 1F2B } { 0397 }
-        { 1F2C } { 0397 }
-        { 1F2D } { 0397 }
-        { 1F2E } { 0397 }
-        { 1F2F } { 0397 }
-        { 1F30 } { 0399 }
-        { 1F31 } { 0399 }
-        { 1F32 } { 0399 }
-        { 1F33 } { 0399 }
-        { 1F34 } { 0399 }
-        { 1F35 } { 0399 }
-        { 1F36 } { 0399 }
-        { 1F37 } { 0399 }
-        { 1F38 } { 0399 }
-        { 1F39 } { 0399 }
-        { 1F3A } { 0399 }
-        { 1F3B } { 0399 }
-        { 1F3C } { 0399 }
-        { 1F3D } { 0399 }
-        { 1F3E } { 0399 }
-        { 1F3F } { 0399 }
-        { 1F40 } { 039F }
-        { 1F41 } { 039F }
-        { 1F42 } { 039F }
-        { 1F43 } { 039F }
-        { 1F44 } { 039F }
-        { 1F45 } { 039F }
-        { 1F48 } { 039F }
-        { 1F49 } { 039F }
-        { 1F4A } { 039F }
-        { 1F4B } { 039F }
-        { 1F4C } { 039F }
-        { 1F4D } { 039F }
-        { 1F50 } { 03A5 }
-        { 1F51 } { 03A5 }
-        { 1F52 } { 03A5 }
-        { 1F53 } { 03A5 }
-        { 1F54 } { 03A5 }
-        { 1F55 } { 03A5 }
-        { 1F56 } { 03A5 }
-        { 1F57 } { 03A5 }
-        { 1F59 } { 03A5 }
-        { 1F5B } { 03A5 }
-        { 1F5D } { 03A5 }
-        { 1F5F } { 03A5 }
-        { 1F60 } { 03A9 }
-        { 1F61 } { 03A9 }
-        { 1F62 } { 03A9 }
-        { 1F63 } { 03A9 }
-        { 1F64 } { 03A9 }
-        { 1F65 } { 03A9 }
-        { 1F66 } { 03A9 }
-        { 1F67 } { 03A9 }
-        { 1F68 } { 03A9 }
-        { 1F69 } { 03A9 }
-        { 1F6A } { 03A9 }
-        { 1F6B } { 03A9 }
-        { 1F6C } { 03A9 }
-        { 1F6D } { 03A9 }
-        { 1F6E } { 03A9 }
-        { 1F6F } { 03A9 }
-        { 1F70 } { 0391 }
-        { 1F71 } { 0391 }
-        { 1F72 } { 0395 }
-        { 1F73 } { 0395 }
-        { 1F74 } { 0397 }
-        { 1F75 } { 0397 }
-        { 1F76 } { 0399 }
-        { 1F77 } { 0399 }
-        { 1F78 } { 039F }
-        { 1F79 } { 039F }
-        { 1F7A } { 03A5 }
-        { 1F7B } { 03A5 }
-        { 1F7C } { 03A9 }
-        { 1F7D } { 03A9 }
-        { 1F80 } { 1FBC }
-        { 1F81 } { 1FBC }
-        { 1F82 } { 1FBC }
-        { 1F83 } { 1FBC }
-        { 1F84 } { 1FBC }
-        { 1F85 } { 1FBC }
-        { 1F86 } { 1FBC }
-        { 1F87 } { 1FBC }
-        { 1F88 } { 1FBC }
-        { 1F89 } { 1FBC }
-        { 1F8A } { 1FBC }
-        { 1F8B } { 1FBC }
-        { 1F8C } { 1FBC }
-        { 1F8D } { 1FBC }
-        { 1F8E } { 1FBC }
-        { 1F8F } { 1FBC }
-        { 1F90 } { 1FCC }
-        { 1F91 } { 1FCC }
-        { 1F92 } { 1FCC }
-        { 1F93 } { 1FCC }
-        { 1F94 } { 1FCC }
-        { 1F95 } { 1FCC }
-        { 1F96 } { 1FCC }
-        { 1F97 } { 1FCC }
-        { 1F98 } { 1FCC }
-        { 1F99 } { 1FCC }
-        { 1F9A } { 1FCC }
-        { 1F9B } { 1FCC }
-        { 1F9C } { 1FCC }
-        { 1F9D } { 1FCC }
-        { 1F9E } { 1FCC }
-        { 1F9F } { 1FCC }
-        { 1FA0 } { 1FFC }
-        { 1FA1 } { 1FFC }
-        { 1FA2 } { 1FFC }
-        { 1FA3 } { 1FFC }
-        { 1FA4 } { 1FFC }
-        { 1FA5 } { 1FFC }
-        { 1FA6 } { 1FFC }
-        { 1FA7 } { 1FFC }
-        { 1FA8 } { 1FFC }
-        { 1FA9 } { 1FFC }
-        { 1FAA } { 1FFC }
-        { 1FAB } { 1FFC }
-        { 1FAC } { 1FFC }
-        { 1FAD } { 1FFC }
-        { 1FAE } { 1FFC }
-        { 1FAF } { 1FFC }
-        { 1FB0 } { 1FB8 }
-        { 1FB1 } { 1FB9 }
-        { 1FB2 } { 1FBC }
-        { 1FB3 } { 1FBC }
-        { 1FB4 } { 1FBC }
-        { 1FB6 } { 0391 }
-        { 1FB7 } { 1FBC }
-        { 1FB8 } { 1FB8 }
-        { 1FB9 } { 1FB9 }
-        { 1FBA } { 0391 }
-        { 1FBB } { 0391 }
-        { 1FBC } { 1FBC }
-        { 1FBD } { 1FBD }
-        { 1FC2 } { 1FCC }
-        { 1FC3 } { 1FCC }
-        { 1FC4 } { 1FCC }
-        { 1FC6 } { 0397 }
-        { 1FC7 } { 1FCC }
-        { 1FC8 } { 0395 }
-        { 1FC9 } { 0395 }
-        { 1FCA } { 0397 }
-        { 1FCB } { 0397 }
-        { 1FCC } { 1FCC }
-        { 1FD0 } { 1FD8 }
-        { 1FD1 } { 1FD9 }
-        { 1FD2 } { 03AA }
-        { 1FD3 } { 03AA }
-        { 1FD6 } { 0399 }
-        { 1FD7 } { 03AA }
-        { 1FD8 } { 1FD8 }
-        { 1FD9 } { 1FD9 }
-        { 1FDA } { 0399 }
-        { 1FDB } { 0399 }
-        { 1FE0 } { 1FE8 }
-        { 1FE1 } { 1FE9 }
-        { 1FE2 } { 03AB }
-        { 1FE3 } { 03AB }
-        { 1FE4 } { 03A1 }
-        { 1FE5 } { 03A1 }
-        { 1FE6 } { 03A5 }
-        { 1FE7 } { 03AB }
-        { 1FE8 } { 1FE8 }
-        { 1FE9 } { 1FE9 }
-        { 1FEA } { 03A5 }
-        { 1FEB } { 03A5 }
-        { 1FEC } { 1FEC }
-        { 1FF2 } { 1FFC }
-        { 1FF3 } { 1FFC }
-        { 1FF4 } { 1FFC }
-        { 1FF6 } { 03A9 }
-        { 1FF7 } { 1FFC }
-        { 1FF8 } { 039F }
-        { 1FF9 } { 039F }
-        { 1FFA } { 03A9 }
-        { 1FFB } { 03A9 }
-        { 1FFC } { 1FFC }
-        \q_recursion_tail ?
-        \q_recursion_stop
-  }
-\group_end:
-\group_begin:
   \cs_set_protected:Npn \__text_change_case_setup:NN #1#2
     {
       \quark_if_recursion_tail_stop:N #1
@@ -35642,7 +34906,7 @@
         \text_declare_purify_equivalent:Nx #1
           {
             \exp_args:Ne \__text_tmp:n
-              { \char_to_utfviii_bytes:n { "#2 } }
+              { \__kernel_codepoint_to_bytes:n { "#2 } }
           }
         \__text_loop:Nn
       }
@@ -35719,7 +34983,7 @@
       \cs_set:Npn \__text_tmp:n #1
         {
           \exp_args:Ne \__text_tmp_aux:n
-            { \char_to_utfviii_bytes:n { "#1 } }
+            { \__kernel_codepoint_to_bytes:n { "#1 } }
         }
       \cs_set:Npn \__text_tmp_aux:n #1 { \__text_tmp:nnnn #1 }
       \cs_set:Npn \__text_tmp:nnnn #1#2#3#4
@@ -36891,10 +36155,14 @@
 \cs_gset:Npn \str_upper_case:n { \str_uppercase:n }
 \__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \str_uppercase:f }
 \cs_gset:Npn \str_upper_case:f { \str_uppercase:f }
-\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \str_foldcase:n }
-\cs_gset:Npn \str_fold_case:n { \str_foldcase:n }
-\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \str_foldcase:V }
-\cs_gset:Npn \str_fold_case:V { \str_foldcase:V }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \str_casefold:n }
+\cs_gset:Npn \str_fold_case:n { \str_casefold:n }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \str_casefold:V }
+\cs_gset:Npn \str_fold_case:V { \str_casefold:V }
+\__kernel_patch_deprecation:nnNNpn { 2020-10-17 } { \str_casefold:n }
+\cs_gset:Npn \str_foldcase:n { \str_casefold:n }
+\__kernel_patch_deprecation:nnNNpn { 2022-10-17 } { \str_casefold:V }
+\cs_gset:Npn \str_foldcase:V { \str_casefold:V }
 \__kernel_patch_deprecation:nnNNpn { 2020-08-20 } { }
 \cs_gset_protected:Npn \str_declare_eight_bit_encoding:nnn #1
   { \__str_declare_eight_bit_encoding:nnnn {#1} { 1114112 } }
@@ -36922,22 +36190,40 @@
 \__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \text_titlecase:nn }
 \cs_gset:Npn \tl_mixed_case:nn #1#2
   { \text_titlecase:nn {#1} {#2} }
-\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \char_lowercase:N }
-\cs_gset:Npn \char_lower_case:N { \char_lowercase:N }
-\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \char_uppercase:N }
-\cs_gset:Npn \char_upper_case:N { \char_uppercase:N }
-\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \char_titlecase:N }
-\cs_gset:Npn \char_mixed_case:N { \char_titlecase:N }
-\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \char_foldcase:N }
-\cs_gset:Npn \char_fold_case:N { \char_foldcase:N }
-\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \char_str_lowercase:N }
-\cs_gset:Npn \char_str_lower_case:N { \char_str_lowercase:N }
-\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \char_str_uppercase:N }
-\cs_gset:Npn \char_str_upper_case:N { \char_str_uppercase:N }
-\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \char_str_titlecase:N }
-\cs_gset:Npn \char_str_mixed_case:N { \char_str_titlecase:N }
-\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \char_str_foldcase:N }
-\cs_gset:Npn \char_str_fold_case:N { \char_str_foldcase:N }
+\__kernel_patch_deprecation:nnNNpn { 2022-10-09 } { [ \codepoint_generate:n ] }
+\cs_gset:Npn \char_to_utfviii_bytes:n { \__kernel_codepoint_to_bytes:n }
+\__kernel_patch_deprecation:nnNNpn { 2022-10-09 } { \codepoint_to_nfd:n }
+\cs_gset:Npn \char_to_nfd:N #1 { \codepoint_to_nfd:n {`#1} }
+\__kernel_patch_deprecation:nnNNpn { 2022-10-09 } { \codepoint_to_nfd:n }
+\cs_gset:Npn \char_to_nfd:n { \codepoint_to_nfd:n }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \text_lowercase:n }
+\cs_gset:Npn \char_lower_case:N { \text_lowercase:n }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \text_uppercase:n }
+\cs_gset:Npn \char_upper_case:N { \text_uppercase:n }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \text_titlecase:n }
+\cs_gset:Npn \char_mixed_case:N { \text_titlecase:n }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \str_casefold:n }
+\cs_gset:Npn \char_fold_case:N { \str_casefold:n }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \str_lowercase:n }
+\cs_gset:Npn \char_str_lower_case:N { \str_lowercase:n }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \str_uppercase:n }
+\cs_gset:Npn \char_str_upper_case:N { \str_uppercase:n }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \str_titlecase:n }
+\cs_gset:Npn \char_str_mixed_case:N { \str_titlecase:n }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \str_casefold:n }
+\cs_gset:Npn \char_str_fold_case:N { \str_casefold:n }
+\__kernel_patch_deprecation:nnNNpn { 2022-10-17 } { \text_lowercase:n }
+\cs_gset:Npn \char_lowercase:N { \text_lowercase:n }
+\__kernel_patch_deprecation:nnNNpn { 2022-10-17 } { \text_uppercase:n }
+\cs_gset:Npn \char_uppercase:N { \text_uppercase:n }
+\__kernel_patch_deprecation:nnNNpn { 2022-10-17 } { \str_casefold:n }
+\cs_gset:Npn \char_foldcase:N { \str_casefold:n }
+\__kernel_patch_deprecation:nnNNpn { 2022-10-17 } { \str_lowercase:n }
+\cs_gset:Npn \char_str_lowercase:N { \str_lowercase:n }
+\__kernel_patch_deprecation:nnNNpn { 2022-10-17 } { \str_uppercase:n }
+\cs_gset:Npn \char_str_uppercase:N { \str_uppercase:n }
+\__kernel_patch_deprecation:nnNNpn { 2022-10-17 } { \str_casefold:n }
+\cs_gset:Npn \char_str_foldcase:N { \str_casefold:n }
 \tl_map_inline:nn
   {
     { catcode } { catcode_remove }

Modified: trunk/Master/texmf-dist/tex/latex/l3kernel/expl3-generic.tex
===================================================================
--- trunk/Master/texmf-dist/tex/latex/l3kernel/expl3-generic.tex	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/tex/latex/l3kernel/expl3-generic.tex	2022-12-17 20:55:34 UTC (rev 65299)
@@ -19,7 +19,7 @@
 %% and all files in that bundle must be distributed together.
 %% 
 %% File: expl3.dtx
-\def\ExplFileDate{2022-11-02}%
+\def\ExplFileDate{2022-12-17}%
 \let\ExplLoaderFileDate\ExplFileDate
 \begingroup
   \catcode`\_=11

Modified: trunk/Master/texmf-dist/tex/latex/l3kernel/expl3.ltx
===================================================================
--- trunk/Master/texmf-dist/tex/latex/l3kernel/expl3.ltx	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/tex/latex/l3kernel/expl3.ltx	2022-12-17 20:55:34 UTC (rev 65299)
@@ -19,7 +19,7 @@
 %% and all files in that bundle must be distributed together.
 %% 
 %% File: expl3.dtx
-\def\ExplFileDate{2022-11-02}%
+\def\ExplFileDate{2022-12-17}%
 \let\ExplLoaderFileDate\ExplFileDate
 \begingroup
   \catcode`\_=11
@@ -28,9 +28,15 @@
     \global\let\c__kernel_expl_date_tl\ExplFileDate
   \fi
 \endgroup
-\everyjob\expandafter{\the\everyjob
-  \message{L3 programming layer <\ExplFileDate>}%
-}
+\ifdefined\LaTeXReleaseInfo
+  \LaTeXReleaseInfo\expandafter{\the\LaTeXReleaseInfo
+    \show at release@info{L3 programming layer <\ExplFileDate>}%
+  }%
+\else
+  \everyjob\expandafter{\the\everyjob
+    \message{L3 programming layer <\ExplFileDate>}%
+  }%
+\fi
 \protected\def\ProvidesExplPackage
   {\@expl at provides@file@@Nnnnnn\ProvidesPackage{Package}}
 \protected\def\ProvidesExplClass

Modified: trunk/Master/texmf-dist/tex/latex/l3kernel/expl3.sty
===================================================================
--- trunk/Master/texmf-dist/tex/latex/l3kernel/expl3.sty	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/tex/latex/l3kernel/expl3.sty	2022-12-17 20:55:34 UTC (rev 65299)
@@ -19,7 +19,7 @@
 %% and all files in that bundle must be distributed together.
 %% 
 %% File: expl3.dtx
-\def\ExplFileDate{2022-11-02}%
+\def\ExplFileDate{2022-12-17}%
 \let\ExplLoaderFileDate\ExplFileDate
 \ProvidesPackage{expl3}
   [%

Modified: trunk/Master/texmf-dist/tex/latex/l3kernel/l3doc.cls
===================================================================
--- trunk/Master/texmf-dist/tex/latex/l3kernel/l3doc.cls	2022-12-17 20:54:42 UTC (rev 65298)
+++ trunk/Master/texmf-dist/tex/latex/l3kernel/l3doc.cls	2022-12-17 20:55:34 UTC (rev 65299)
@@ -1637,43 +1637,7 @@
       }
     \seq_gclear:N \g__codedoc_nested_names_seq
   }
-\newcommand*{\DescribeOption}
-  {
-    \leavevmode
-    \@bsphack
-    \begingroup
-      \MakePrivateLetters
-      \Describe at Option
-  }
-\newcommand*{\Describe at Option}[1]
-  {
-    \endgroup
-    \marginpar{
-      \raggedleft
-      \PrintDescribeEnv{#1}
-    }
-    \SpecialOptionIndex{#1}
-    \@esphack
-    \ignorespaces
-  }
-\newcommand*{\SpecialOptionIndex}[1]
-  {
-    \@bsphack
-    \begingroup
-      \__codedoc_target:
-      \index
-        {
-          #1\actualchar{\protect\ttfamily#1}~(option)
-          \encapchar hdclindex{\the\c at HD@hypercount}{usage}
-        }
-      \index
-        {
-          options:\levelchar#1\actualchar{\protect\ttfamily#1}
-          \encapchar hdclindex{\the\c at HD@hypercount}{usage}
-        }
-    \endgroup
-    \@esphack
-  }
+\NewDocElement[idxtype = option, idxgroup = options]{Option}{optionenv}
 \font \manual = manfnt \scan_stop:
 \cs_gset:Npn \dbend { {\manual\char127} }
 \newenvironment {danger}



More information about the tex-live-commits mailing list.