texlive[68632] Master/texmf-dist: l3kernel (23oct23)

commits+karl at tug.org commits+karl at tug.org
Tue Oct 24 19:51:37 CEST 2023


Revision: 68632
          https://tug.org/svn/texlive?view=revision&revision=68632
Author:   karl
Date:     2023-10-24 19:51:37 +0200 (Tue, 24 Oct 2023)
Log Message:
-----------
l3kernel (23oct23)

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/doc/latex/l3kernel/source3body.tex
    trunk/Master/texmf-dist/source/latex/l3kernel/expl3.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3.ins
    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

Added Paths:
-----------
    trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-functions.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-symbolic.dtx
    trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-types.dtx

Modified: trunk/Master/texmf-dist/doc/latex/l3kernel/CHANGELOG.md
===================================================================
--- trunk/Master/texmf-dist/doc/latex/l3kernel/CHANGELOG.md	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/doc/latex/l3kernel/CHANGELOG.md	2023-10-24 17:51:37 UTC (rev 68632)
@@ -7,6 +7,27 @@
 
 ## [Unreleased]
 
+## [2023-10-23]
+
+### Added
+- `\text_titlecase_all:n(n)`
+- `\token_to_catcode:N`
+- Support for symbolic variables in fp input:
+  `\fp_new_variable:n`, `\fp_set_variable:nn` and `\fp_clear_variable:n`
+- Support for user-defined functions in fp expressions:
+  `\fp_new_function:n`, `\fp_set_function:nnn` and `\fp_clear_function:n`
+
+### Changed
+- Refine action of `\text_titlecase_first:n(n)` to be focussed strictly on
+  first (relevant) codepoint in the input
+
+### Deprecated
+- `\text_titlecase:n(n)` as ambiguous: replaced by `\text_titlecase_all:n(n)`
+
+### Fixed
+- Support arbitrary BCP-47 locales for case-changing overrides (issue \#1239)
+- Retain braces when ending titlecasing with some input structures
+
 ## [2023-10-10]
 
 ### Added
@@ -119,7 +140,6 @@
 - `\file_input_raw:n`
 - `\int_if_zero:n(TF)`
 - `\str_mdfive_hash:n`
-
 ### Changed
 - Remove `\noexpand` inside math mode in `\text_expand:n`
 - Re-implement `\dim_to_decimal_in_bp:n` and 
@@ -1538,7 +1558,8 @@
 - Step functions have been added for dim variables,
   e.g. `\dim_step_inline:nnnn`
 
-[Unreleased]: https://github.com/latex3/latex3/compare/2023-10-10...HEAD
+[Unreleased]: https://github.com/latex3/latex3/compare/2023-10-23...HEAD
+[2023-10-23]: https://github.com/latex3/latex3/compare/2023-10-10...2023-10-23
 [2023-10-10]: https://github.com/latex3/latex3/compare/2023-08-29...2023-10-10
 [2023-08-29]: https://github.com/latex3/latex3/compare/2023-08-11...2023-08-29
 [2023-08-11]: https://github.com/latex3/latex3/compare/2023-08-03...2023-08-11

Modified: trunk/Master/texmf-dist/doc/latex/l3kernel/README.md
===================================================================
--- trunk/Master/texmf-dist/doc/latex/l3kernel/README.md	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/doc/latex/l3kernel/README.md	2023-10-24 17:51:37 UTC (rev 68632)
@@ -1,7 +1,7 @@
 LaTeX3 Programming Conventions
 ==============================
 
-Release 2023-10-10
+Release 2023-10-23
 
 Overview
 --------
@@ -57,7 +57,7 @@
 2019. Similarly, the full set of these utility primitives has been available in
 XeTeX from the 2019 TeX Live release, and has always been available in LuaTeX
 (some by Lua emulation). The Japanese pTeX and upTeX gained all of the above
-(except `\ifincsname`) for TeX Live 2019 `\ifincsname` for TeX Live 2020.
+(except `\ifincsname`) for TeX Live 2019 and `\ifincsname` for TeX Live 2020.
 
 Starting from release 2023-05-15, the `\expanded` primitive is *required*.
 Its slow emulation has been removed.

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	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/doc/latex/l3kernel/interface3.tex	2023-10-24 17:51:37 UTC (rev 68632)
@@ -64,7 +64,7 @@
          {latex-team at latex-project.org}%
    }%
 }
-\date{Released 2023-10-10}
+\date{Released 2023-10-23}
 
 \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	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/doc/latex/l3kernel/l3obsolete.txt	2023-10-24 17:51:37 UTC (rev 68632)
@@ -56,6 +56,8 @@
 \seq_indexed_map_inline:Nn               2020-06-18
 \seq_indexed_map_function:NN             2020-06-18
 \sys_load_deprecation:                   2021-01-11
+\text_titlecase:n                        2023-07-08
+\text_titlecase:nn                       2023-07-08
 \tl_case:cn                              2023-05-23
 \tl_case:cnF                             2023-05-23
 \tl_case:cnT                             2023-05-23

Modified: trunk/Master/texmf-dist/doc/latex/l3kernel/l3prefixes.csv
===================================================================
--- trunk/Master/texmf-dist/doc/latex/l3kernel/l3prefixes.csv	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/doc/latex/l3kernel/l3prefixes.csv	2023-10-24 17:51:37 UTC (rev 68632)
@@ -22,6 +22,7 @@
 bearwear,bearwear,Ulrike Fischer,https://github.com/u-fischer/bearwear,https://github.com/u-fischer/bearwear,https://github.com/u-fischer/bearwear/issues,2020-04-24,2020-04-24,
 beuron,beuron,Keno Wehr,https://ctan.org/pkg/beuron,,,2021-08-23,2021-08-23,
 bitset,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2020-12-26,2020-12-26,
+block,latex2e,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex2e.git,https://github.com/latex3/latex2e/issues,2023-10-17,2023-10-17,
 bool,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,
 box,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,
 bxjh,BXjaholiday,Takuto Asakura,https://github.com/wtsnjp/BXjaholiday,https://github.com/wtsnjp/BXjaholiday.git,https://github.com/wtsnjp/BXjaholiday/issues,2018-02-02,2019-02-02,
@@ -92,6 +93,7 @@
 fmdug,dashundergaps,Frank Mittelbach,https://www.latex-project.org/,https://github.com/FrankMittelbach/fmitex-dashundergaps.git,https://github.com/FrankMittelbach/fmitex-dashundergaps/issues,2018-06-24,2021-10-11,
 fmuft,unicodefonttable,Frank Mittelbach,https://www.latex-project.org/,https://github.com/FrankMittelbach/fmitex-unicodefonttable.git,https://github.com/FrankMittelbach/fmitex-unicodefonttable/issues,2020-02-17,2021-10-11,
 fmwao,widows-and-orphans,Frank Mittelbach,https://www.latex-project.org/,https://github.com/FrankMittelbach/fmitex-widows-and-orphans.git,https://github.com/FrankMittelbach/fmitex-widows-and-orphans/issues,2018-09-26,2018-09-26,
+fnote,latex2e,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex2e.git,https://github.com/latex3/latex2e/issues,2023-10-17,2023-10-17,
 fnpct,fnpct,Clemens Niederberger,https://github.com/cgnieder/fnpct/,https://github.com/cgnieder/fnpct.git,https://github.com/cgnieder/fnpct/issues,2013-03-16,2020-04-14,
 fontsizes,fontsizes,Julien Rivaud,,,,,2018-06-13,
 fontspec,fontspec,Will Robertson,https://github.com/wspr/fontspec,https://github.com/wspr/fontspec.git,https://github.com/wspr/fontspec/issues,2013-03-16,2013-03-16,
@@ -227,6 +229,8 @@
 siunitx,siunitx,Joseph Wright,https://github.com/josephwright/siunitx,https://github.com/josephwright/siunitx.git,https://github.com/josephwright/siunitx/issues,2012-11-04,2012-11-04,
 skel,skeldoc,Magnus Lie Hetland,https://github.com/mlhetland/skeldoc.sty,https://github.com/mlhetland/skeldoc.sty.git,https://github.com/mlhetland/skeldoc.sty/issues,2021-01-04,2021-01-04,
 skip,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,
+slcd,se2thesis,Stephan Lukasczyk,https://github.com/se2p/se2thesis,https://github.com/se2p/se2thesis,https://github.com/se2p/se2thesis/issues,2023-10-18,2023-10-18,
+socket,latex2e,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex2e.git,https://github.com/latex3/latex2e/issues,2023-10-17,2023-10-17,
 sort,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,2017-02-13,
 space,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,
 starray,starray,Alceu Frigeri,https://github.com/alceu-frigeri/starray,https://github.com/alceu-frigeri/starray,https://github.com/alceu-frigeri/starray/issues,2023-05-15,2023-05-15,

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	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/doc/latex/l3kernel/l3styleguide.tex	2023-10-24 17:51:37 UTC (rev 68632)
@@ -32,7 +32,7 @@
         {latex-team at latex-project.org}%
     }%
 }
-\date{Released 2023-10-10}
+\date{Released 2023-10-23}
 
 \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	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/doc/latex/l3kernel/l3syntax-changes.tex	2023-10-24 17:51:37 UTC (rev 68632)
@@ -32,7 +32,7 @@
         {latex-team at latex-project.org}%
     }%
 }
-\date{Released 2023-10-10}
+\date{Released 2023-10-23}
 
 \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	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/doc/latex/l3kernel/l3term-glossary.tex	2023-10-24 17:51:37 UTC (rev 68632)
@@ -32,7 +32,7 @@
         {latex-team at latex-project.org}%
     }%
 }
-\date{Released 2023-10-10}
+\date{Released 2023-10-23}
 
 \newcommand{\TF}{\textit{(TF)}}
 

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	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/doc/latex/l3kernel/source3.tex	2023-10-24 17:51:37 UTC (rev 68632)
@@ -57,7 +57,7 @@
          {latex-team at latex-project.org}%
    }%
 }
-\date{Released 2023-10-10}
+\date{Released 2023-10-23}
 
 \pagenumbering{roman}
 \maketitle

Modified: trunk/Master/texmf-dist/doc/latex/l3kernel/source3body.tex
===================================================================
--- trunk/Master/texmf-dist/doc/latex/l3kernel/source3body.tex	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/doc/latex/l3kernel/source3body.tex	2023-10-24 17:51:37 UTC (rev 68632)
@@ -517,9 +517,9 @@
 
 \section{\TeX{} concepts not supported by \LaTeX3{}}
 
-The \TeX{} concept of an \enquote{\cs{outer}} macro is \emph{not supported}
+The \TeX{} concept of an \enquote{\tn{outer}} macro is \emph{not supported}
 at all by \LaTeX3{}. As such, the functions provided here may break when
-used on top of \LaTeXe{} if \cs{outer} tokens are used in the arguments.
+used on top of \LaTeXe{} if \tn{outer} tokens are used in the arguments.
 
 \DisableImplementation
 
@@ -582,6 +582,9 @@
     l3fp-trig.dtx ,
     l3fp-convert.dtx ,
     l3fp-random.dtx ,
+    l3fp-types.dtx ,
+    l3fp-symbolic.dtx ,
+    l3fp-functions.dtx
   }
 \ExplSyntaxOff
 

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/expl3.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/expl3.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/expl3.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -24,7 +24,7 @@
 %
 %<*driver|generic|package|2ekernel>
 %</driver|generic|package|2ekernel>
-\def\ExplFileDate{2023-10-10}%
+\def\ExplFileDate{2023-10-23}%
 %<*driver>
 \documentclass[full]{l3doc}
 \usepackage{graphicx}
@@ -51,7 +51,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 %
 % \maketitle
 %
@@ -311,9 +311,8 @@
 % or private.
 %
 % To allow clear separation of these two cases, the following convention
-% is used. In private functions and variables, two |_| characters are
-% added to the start of the module name. On the other hand, public
-% interfaces to not have these two |_| character. Thus
+% is used. To denote a private function or a private variable (of the module),
+% two |_| characters are used in front of the module name, e.g.
 % \begin{verbatim}
 %   \module_foo:nnn
 % \end{verbatim}

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3.ins
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3.ins	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3.ins	2023-10-24 17:51:37 UTC (rev 68632)
@@ -92,6 +92,9 @@
         \from{l3fp-trig.dtx}    {package}
         \from{l3fp-convert.dtx} {package}
         \from{l3fp-random.dtx}  {package}
+        \from{l3fp-types.dtx}   {package}
+        \from{l3fp-symbolic.dtx}{package}
+        \from{l3fp-functions.dtx}{package}
         \from{l3fparray.dtx}    {package}
         \from{l3cctab.dtx}      {package}
         \from{l3sort.dtx}       {package}

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3basics.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3basics.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3basics.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 %
 % \maketitle
 %
@@ -1045,7 +1045,7 @@
 %   \end{texnote}
 % \end{function}
 %
-% \begin{function}[EXP, added = 2018-06-18]{\use:e}
+% \begin{function}[EXP, added = 2018-06-18, updated = 2023-07-05]{\use:e}
 %   \begin{syntax}
 %     \cs{use:e} \Arg{expandable tokens}
 %   \end{syntax}

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3bootstrap.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3bootstrap.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3bootstrap.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3box.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3box.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3box.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3candidates.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3candidates.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3candidates.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -44,7 +44,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3cctab.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3cctab.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3cctab.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3clist.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3clist.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3clist.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -44,7 +44,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3coffins.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3coffins.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3coffins.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3color.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3color.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3color.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 %
 % \maketitle
 %
@@ -330,7 +330,7 @@
 %
 % \subsection{Coloring math mode material}
 %
-% Coloring math mode material using \cs{color_select:nn(n)} has some restrictions
+% Coloring math mode material using \cs[no-index]{color_select:nn(n)} has some restrictions
 % and often leads to spacing issues and/or poor input syntax. Avoiding generating
 % \tn{mathord} atoms whilst coloring only those parts of the input which are
 % required needs careful handling. The functionality here covers this important
@@ -341,7 +341,7 @@
 %     \cs{color_math:nn} \Arg{color expression}\Arg{content}
 %     \cs{color_math:nnn} \Arg{model(s)} \Arg{value(s)} \Arg{content}
 %   \end{syntax}
-%   Works as for \cs{color_select:n(n)} but applies color only to the math mode
+%   Works as for \cs[no-index]{color_select:n(n)} but applies color only to the math mode
 %   \meta{content}. The function does not generate a group and the \meta{content}
 %   therefore retains its math atom states. Sub/superscripts are also properly
 %   handled.

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3debug.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3debug.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3debug.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3deprecation.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3deprecation.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3deprecation.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 %
 % \maketitle
 %
@@ -549,6 +549,21 @@
 %    \end{macrocode}
 % \end{macro}
 %
+% \subsection{Deprecated \pkg{l3text} functions}
+%
+% \begin{macro}[EXP]{\text_titlecase:n}
+% \begin{macro}[EXP]{\text_titlecase:nn}
+%    \begin{macrocode}
+\__kernel_patch_deprecation:nnNNpn { 2023-07-08 } { \text_titlecase_first:n }
+\cs_gset:Npn \text_titlecase:n #1
+  { \text_titlecase_first:n { \text_lowercase:n {#1} } }
+\__kernel_patch_deprecation:nnNNpn { 2023-07-08 } { \text_titlecase_first:nn }
+\cs_gset:Npn \text_titlecase:nn #1#2
+  { \text_titlecase_first:nn {#1} { \text_lowercase:n {#2} } }
+%    \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
 % \subsection{Deprecated \pkg{l3tl} functions}
 %
 %    \begin{macrocode}
@@ -574,12 +589,12 @@
 \__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \text_uppercase:nn }
 \cs_gset:Npn \tl_upper_case:nn #1#2
   { \text_uppercase:nn {#1} {#2} }
-\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \text_titlecase:n }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \text_titlecase_first:n }
 \cs_gset:Npn \tl_mixed_case:n #1
-  { \text_titlecase:n {#1} }
-\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \text_titlecase:nn }
+  { \text_titlecase_first:n {#1} }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \text_titlecase_first:nn }
 \cs_gset:Npn \tl_mixed_case:nn #1#2
-  { \text_titlecase:nn {#1} {#2} }
+  { \text_titlecase_first:nn {#1} {#2} }
 %    \end{macrocode}
 % \end{macro}
 %
@@ -608,7 +623,7 @@
 %    \end{macrocode}
 % \end{macro}
 %
-% \begin{macro}[EXP]{\char_to_nfd:Nm, \char_to_nfd:n}
+% \begin{macro}[EXP]{\char_to_nfd:N, \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} }
@@ -629,8 +644,8 @@
 \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 } { \text_titlecase_first:n }
+\cs_gset:Npn \char_mixed_case:N { \text_titlecase_first: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 }

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3doc.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3doc.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3doc.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -85,7 +85,7 @@
 %    require you to do updates, if the class changes.}}
 %
 % \author{\Team}
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 % \maketitle
 % \tableofcontents
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3docstrip.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3docstrip.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3docstrip.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -2,7 +2,7 @@
 %
 %% File l3dosctrip.dtx
 %
-% Copyright (C) 2012,2014-2023 The LaTeX Project
+% Copyright (C) 2012-2023 The LaTeX Project
 %
 % It may be distributed and/or modified under the conditions of the
 % LaTeX Project Public License (LPPL), either version 1.3c of this
@@ -63,7 +63,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3expan.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3expan.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3expan.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3file.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3file.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3file.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -44,7 +44,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3flag.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3flag.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3flag.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-assign.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-assign.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-assign.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -40,7 +40,7 @@
 %          {latex-team at latex-project.org}^^A
 %    }^^A
 % }
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 % \maketitle
 %
 % \begin{documentation}

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-aux.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-aux.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-aux.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-basics.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-basics.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-basics.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -2,7 +2,7 @@
 %
 %% File: l3fp-basics.dtx
 %
-% Copyright (C) 2011-2014,2016-2023 The LaTeX Project
+% Copyright (C) 2011-2023 The LaTeX Project
 %
 % It may be distributed and/or modified under the conditions of the
 % LaTeX Project Public License (LPPL), either version 1.3c of this
@@ -40,7 +40,7 @@
 %          {latex-team at latex-project.org}^^A
 %    }^^A
 % }
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-convert.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-convert.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-convert.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-expo.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-expo.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-expo.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -40,7 +40,7 @@
 %          {latex-team at latex-project.org}^^A
 %    }^^A
 % }
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-extended.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-extended.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-extended.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -40,7 +40,7 @@
 %          {latex-team at latex-project.org}^^A
 %    }^^A
 % }
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 %
 % \maketitle
 %

Added: trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-functions.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-functions.dtx	                        (rev 0)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-functions.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -0,0 +1,282 @@
+% \iffalse
+%
+%% File l3fp-functions.dtx (C) Copyright 2012-2018,2020,2021,2023 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version.  The latest version
+% of this license is in the file
+%
+%    http://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+%    https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full]{l3doc}
+\usepackage{amsmath}
+\begin{document}
+  \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+%   The \pkg{l3fp-functions} package\\ Floating point functions^^A
+% }
+%
+% \author{^^A
+%  The \LaTeX{} Project\thanks
+%    {^^A
+%      E-mail:
+%        \href{mailto:latex-team at latex-project.org}
+%          {latex-team at latex-project.org}^^A
+%    }^^A
+% }
+%
+% \date{Released 2023-10-23}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3fp-functions} implementation}
+%
+%    \begin{macrocode}
+%<*package>
+%    \end{macrocode}
+%
+%    \begin{macrocode}
+%<@@=fp>
+%    \end{macrocode}
+%
+% \subsection{Declaring functions}
+%
+% \begin{macro}{\fp_new_function:n}
+% \begin{macro}{\@@_new_function:n}
+%    \begin{macrocode}
+\cs_new_protected:Npn \fp_new_function:n #1
+  { \exp_args:No \@@_new_function:n { \tl_to_str:n {#1} } }
+\cs_new_protected:Npn \@@_new_function:n #1
+  {
+    \@@_id_if_invalid:nTF {#1}
+      { \msg_error:nnn { fp } { invalid-identifier } {#1} }
+      {
+        \cs_if_exist:cT { @@_parse_word_#1:N }
+          {
+            \msg_error:nnn
+              { fp } { id-already-defined } {#1}
+            \cs_undefine:c { @@_parse_word_#1:N }
+            \cs_undefine:c { @@_#1_o:w }
+          }
+        \@@_function_set_parsing:Nn \cs_gset_eq:NN {#1}
+      }
+  }
+%    \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}
+%   {\@@_function_set_parsing:Nn, \@@_function_set_parsing_aux:NNn}
+%    \begin{macrocode}
+\cs_new:Npn \@@_function_set_parsing:Nn #1#2
+  {
+    \exp_args:NNc \@@_function_set_parsing_aux:NNn #1
+      { @@_parse_word_#2:N } {#2}
+  }
+\cs_new:Npn \@@_function_set_parsing_aux:NNn #1#2#3
+  {
+    \cs_set:Npe \@@_tmp:w
+      {
+        \exp_not:N \@@_parse_function:NNN
+        \exp_not:N \@@_function_o:w
+        \exp_not:c { @@_#3_o:w }
+      }
+    \cs_if_eq:NNF #2 \@@_tmp:w
+      {
+        \cs_if_exist:NTF #2
+          {
+            \msg_warning:nnnn
+              { fp } { id-used-elsewhere } {#3} { function }
+            #1 #2 \@@_tmp:w
+          }
+          {
+            \cs_new_eq:NN #2 \scan_stop: % to declare the function
+            #1 #2 \@@_tmp:w
+          }
+      }
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_function_o:w}
+%    \begin{macrocode}
+\cs_new:Npn \@@_function_o:w #1#2 @
+  {
+    \cs_if_exist:NTF #1
+      { #1 #2 @ }
+      {
+        \exp_after:wN \s_@@_symbolic
+        \exp_after:wN \@@_symbolic_chk:w
+        \exp_after:wN \@@_function_o:w
+        \exp_after:wN #1
+        \exp_after:wN ,
+        \exp_after:wN {
+          \exp:w \exp_end_continue_f:w
+          \@@_exp_after_array_f:w #2 \s_@@_expr_stop
+          \exp_after:wN
+        }
+        \exp_after:wN ;
+      }
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \subsection{Defining functions by their expression}
+%
+% \begin{variable}{\l_@@_function_arg_int}
+%   Labels the arguments of a function being defined.
+%    \begin{macrocode}
+\int_new:N \l_@@_function_arg_int
+%    \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\fp_set_function:nnn}
+% \begin{macro}{\@@_set_function:Nnnn}
+%   \begin{syntax}
+%     \cs{fp_set_function:nnn} \Arg{identifier}
+%       \Arg{comma-list of variables} \Arg{expression}
+%   \end{syntax}
+%   Defines the \meta{identifier} to stand for a function which expects
+%   some arguments defined by the \meta{comma-list of variables}, and
+%   evaluates to the \meta{expression}.
+%    \begin{macrocode}
+\cs_new_protected:Npn \fp_set_function:nnn #1
+  {
+    \exp_args:NNo \@@_set_function:Nnnn \cs_set_eq:cN
+      { \tl_to_str:n {#1} }
+  }
+\cs_new_protected:Npn \@@_set_function:Nnnn #1#2#3#4
+  {
+    \@@_id_if_invalid:nTF {#2}
+      { \msg_error:nnn { fp } { invalid-identifier } {#2} }
+      {
+        \cs_if_exist:cF { @@_parse_word_#2:N }
+          { \@@_function_set_parsing:Nn \cs_set_eq:NN {#2} }
+        \group_begin:
+          \int_zero:N \l_@@_function_arg_int
+          \exp_args:No \clist_map_inline:nn { \tl_to_str:n {#3} }
+            {
+              \int_incr:N \l_@@_function_arg_int
+              \exp_args:Ne \@@_clear_variable:n
+                { _ \tex_romannumeral:D \l_@@_function_arg_int }
+              \fp_clear_variable:n {##1}
+              \cs_set_nopar:cpe { l_@@_variable_##1_fp }
+                {
+                  \exp_not:N \s__fp_symbolic
+                  \exp_not:N \@@_symbolic_chk:w
+                  \exp_not:N \@@_function_arg_o:w
+                  \int_use:N \l_@@_function_arg_int
+                  ########1 , { } ;
+                }
+            }
+          \cs_set:Npn \@@_function_arg_o:w ##1 @
+            {
+              \exp_after:wN \s_@@_symbolic
+              \exp_after:wN \@@_symbolic_chk:w
+              \exp_after:wN \@@_function_arg_o:w
+              \tex_romannumeral:D
+              \@@_exp_after_symbolic_loop:N ##1
+                { , \tex_romannumeral:D \use_none:nn }
+              \exp_after:wN \c_zero_int
+              \exp_after:wN { \exp_after:wN } \exp_after:wN ;
+            }
+          \fp_set:Nn \l_@@_symbolic_fp {#4}
+          \use:e
+            {
+              \exp_not:n { \cs_gset:Npn \@@_tmp:w ##1 }
+                { \exp_not:o { \l_@@_symbolic_fp } }
+            }
+          \use:e
+            {
+              \exp_not:n { \cs_gset:Npn \@@_tmp:w ##1 @ }
+                {
+                  \exp_not:N \@@_exp_after_symbolic_f:nw
+                  \exp_not:n { { \exp_after:wN \exp_stop_f: } }
+                  \exp_not:o { \@@_tmp:w { . , {##1} } }
+                }
+            }
+        \group_end:
+        #1 { @@_#2_o:w } \@@_tmp:w
+      }
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+%   {
+%     \@@_function_arg_o:w,
+%     \@@_function_arg_few:w,
+%     \@@_function_arg_get:w
+%   }
+%    \begin{macrocode}
+\cs_new:Npn \@@_function_arg_o:w #1. #2
+  {
+    \if_meaning:w @ #2
+      \exp_after:wN \@@_function_arg_few:w
+    \fi:
+    \if_int_compare:w #1 = \c_one_int
+      \exp_after:wN \@@_function_arg_get:w
+    \fi:
+    \@@_use_i_until_s:nw
+      {
+        \exp_after:wN \@@_function_arg_o:w
+        \int_value:w \int_eval:n { #1 - 1 } .
+      }
+      #2
+  }
+\cs_new:Npn \@@_function_arg_few:w #1 @ { \exp_after:wN \c_nan_fp }
+\cs_new:Npn \@@_function_arg_get:w #1#2#3; #4 @
+  {
+    \@@_exp_after_array_f:w #3; \s_@@_expr_stop
+    \exp_after:wN \exp_stop_f:
+  }
+%    \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\fp_clear_function:n}
+% \begin{macro}{\@@_clear_function:n}
+%    \begin{macrocode}
+\cs_new_protected:Npn \fp_clear_function:n #1
+  { \exp_args:No \@@_clear_function:n { \tl_to_str:n {#1} } }
+\cs_new_protected:Npn \@@_clear_function:n #1
+  {
+    \cs_undefine:c { @@_parse_word_ #1 :N }
+    \@@_function_set_parsing:Nn \cs_set_eq:NN {#1}
+  }
+%    \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% ^^A todo: add check for number of args
+%
+%    \begin{macrocode}
+%</package>
+%    \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex


Property changes on: trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-functions.dtx
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-logic.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-logic.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-logic.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -40,7 +40,7 @@
 %          {latex-team at latex-project.org}^^A
 %    }^^A
 % }
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 %
 % \maketitle
 %
@@ -163,7 +163,7 @@
 % \end{macro}
 % \end{macro}
 %
-% \begin{macro}[EXP]{\@@_compare_back_any:ww, \@@_compare_back:ww, \@@_compare_nan:w}
+% \begin{macro}[EXP]{\@@_compare_back:ww, \@@_bcmp:ww, \@@_compare_back_any:ww, \@@_compare_nan:w}
 %   \begin{quote}
 %     \cs{@@_compare_back_any:ww} \meta{y} |;| \meta{x} |;|
 %   \end{quote}
@@ -178,6 +178,17 @@
 %   a different type, the highest type is a larger number.  Finally, if
 %   $y\leq 0$, then $x>y$, unless both are zero.
 %    \begin{macrocode}
+\cs_new:Npn \@@_compare_back:ww #1#2; #3#4;
+  {
+    \cs:w
+      @@
+      \@@_type_from_scan:N #1
+      _bcmp
+      \@@_type_from_scan:N #3
+      :ww
+    \cs_end:
+    #1#2; #3#4;
+  }
 \cs_new:Npn \@@_compare_back_any:ww #1#2; #3
   {
     \@@_if_type_fp:NTwFw
@@ -195,7 +206,7 @@
     }
     #1#2 ; #3
   }
-\cs_new:Npn \@@_compare_back:ww
+\cs_new:Npn \@@_bcmp:ww
     \s_@@ \@@_chk:w #1 #2 #3;
     \s_@@ \@@_chk:w #4 #5 #6;
   {

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-parse.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-parse.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-parse.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -40,7 +40,7 @@
 %          {latex-team at latex-project.org}^^A
 %    }^^A
 % }
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-random.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-random.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-random.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -40,7 +40,7 @@
 %          {latex-team at latex-project.org}^^A
 %    }^^A
 % }
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-round.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-round.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-round.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 %
 % \maketitle
 %

Added: trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-symbolic.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-symbolic.dtx	                        (rev 0)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-symbolic.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -0,0 +1,673 @@
+% \iffalse
+%
+%% File l3fp-symbolic.dtx (C) Copyright 2012-2015,2017,2018,2020,2021,2023 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version.  The latest version
+% of this license is in the file
+%
+%    http://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+%    https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full]{l3doc}
+\usepackage{amsmath}
+\begin{document}
+  \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+%   The \pkg{l3fp-symbolic} package\\ Symbolic expressions^^A
+% }
+%
+% \author{^^A
+%  The \LaTeX{} Project\thanks
+%    {^^A
+%      E-mail:
+%        \href{mailto:latex-team at latex-project.org}
+%          {latex-team at latex-project.org}^^A
+%    }^^A
+% }
+%
+% \date{Released 2023-10-23}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3fp-symbolic} implementation}
+%
+%    \begin{macrocode}
+%<*package>
+%    \end{macrocode}
+%
+%    \begin{macrocode}
+%<@@=fp>
+%    \end{macrocode}
+%
+% \subsection{Misc}
+%
+% \begin{variable}{\l_@@_symbolic_fp}
+%   Scratch floating point.
+%    \begin{macrocode}
+\fp_new:N \l_@@_symbolic_fp
+%    \end{macrocode}
+% \end{variable}
+%
+% \subsection{Building blocks for expressions}
+%
+% Every symbolic expression has the form \cs{s_@@_symbolic}
+% \cs{@@_symbolic_chk:w} \meta{operation} |,| \Arg{operands} \meta{junk}
+% |;| where the \meta{operation} is a list of \texttt{N}-type tokens,
+% the \meta{operands} is an array of floating point objects, and the
+% \meta{junk} is to be discarded.  If the outermost operator (last to be
+% evaluated) is unary, the expression has the form
+% \begin{quote}
+%   \cs{s_@@_symbolic} \cs{@@_symbolic_chk:w} \\
+%   \cs{@@_types_unary:NNw} |\__fp_|\meta{op}|_o:w| \meta{token} |,| \\
+%   |{| \meta{operand} |}| \meta{junk} |;|
+% \end{quote}
+% where the \meta{op} is a unary operation (|set_sign|, |cos|,
+% \ldots{}), and the \meta{token} and \meta{operand} are used as
+% arguments for \cs{@@_\meta{op}_o:w} (or the type-specific analog of
+% this function).  If the outermost operator is binary, the expression
+% has the form
+% \begin{quote}
+%   \cs{s_@@_symbolic} \cs{@@_symbolic_chk:w} \\
+%   \cs{@@_types_binary:Nww} |\__fp_|\meta{op}|_o:ww| |,| \\
+%   |{| \meta{operand_1} \meta{operand_2} |}| \meta{junk} |;|
+% \end{quote}
+% where the \meta{op} is an operation (|+|, |&|, \ldots{}), and
+% |\__fp_|\meta{op}|_o:ww| receives the \meta{operands} as arguments.
+% If the expression consists of a single variable, it is stored as
+% \begin{quote}
+%   \cs{s_@@_symbolic} \cs{@@_symbolic_chk:w} \\
+%   \cs{@@_variable_o:w} \meta{identifier} |,| \\
+%   |{| |}| \meta{junk} |;|
+% \end{quote}
+%
+% Symbolic expressions are stored in a prefix form.  When encountering a
+% symbolic expression in a floating point computation, we attempt to
+% evaluate the operands as much as possible, and if that yields floating
+% point numbers rather than expressions, we apply the operator which
+% follows (if the function is known).
+%
+% For instance, the expression |a + b * sin(c)| is stored as
+% \begin{verbatim}
+% \s__fp_symbolic \__fp_symbolic_chk:w
+%   \__fp_types_binary:Nww \__fp_+_o:ww ,
+%   {
+%     \s__fp_symbolic \__fp_symbolic_chk:w
+%       \__fp_variable_o:w a , { } ;
+%     \s__fp_symbolic \__fp_symbolic_chk:w
+%       \__fp_types_binary:Nww \__fp_*_o:ww ,
+%       {
+%         \s__fp_symbolic \__fp_symbolic_chk:w
+%           \__fp_variable_o:w b , { } ;
+%         \s__fp_symbolic \__fp_symbolic_chk:w
+%           \__fp_types_unary:NNw \__fp_sin_o:w \use_i:nn ,
+%           {
+%             \s__fp_symbolic \__fp_symbolic_chk:w
+%               \__fp_variable_o:w c , { } ;
+%           } ;
+%       } ;
+%   } ;
+% \end{verbatim}
+%
+% \begin{variable}{\s_@@_symbolic}
+%   Scan mark indicating the start of a symbolic expression.
+%    \begin{macrocode}
+\scan_new:N \s_@@_symbolic
+%    \end{macrocode}
+% \end{variable}
+%
+% \begin{macro}{\@@_symbolic_chk:w}
+%   Analog of \cs{@@_chk:w} for symbolic expressions.
+%    \begin{macrocode}
+\cs_new_protected:Npn \@@_symbolic_chk:w #1,#2#3;
+  {
+    \msg_error:nne { fp } { misused-fp }
+      {
+        \@@_to_tl_dispatch:w
+          \s_@@_symbolic \@@_symbolic_chk:w #1,{#2};
+      }
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \subsection{Expanding after a symbolic expression}
+%
+% \begin{macro}[EXP]
+%   {\@@_if_has_symbolic:nTF, \@@_if_has_symbolic_aux:w}
+%   Tests if |#1| contains \cs{s_@@_symbolic} at top-level.  This test
+%   should be precise enough to determine if a given an array contains a
+%   symbolic expression or only consists in floating points.  Used in
+%   \cs{@@_exp_after_symbolic_f:nw}.
+%    \begin{macrocode}
+\cs_new:Npn \@@_if_has_symbolic:nTF #1
+  {
+    \@@_if_has_symbolic_aux:w
+      #1             \s_@@_mark \use_i:nn
+      \s_@@_symbolic \s_@@_mark \use_ii:nn
+    \s_@@_stop
+  }
+\cs_new:Npn \@@_if_has_symbolic_aux:w
+    #1 \s_@@_symbolic #2 \s_@@_mark #3#4 \s_@@_stop { #3 }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_exp_after_symbolic_f:nw}
+% \begin{macro}[EXP]
+%   {\@@_exp_after_symbolic_aux:w, \@@_exp_after_symbolic_loop:N}
+%   This function does two things: trigger an \texttt{f}-expansion of
+%   the argument~|#1| after the following symbolic expression, and
+%   evaluate all pieces of the expression which can be evaluated.
+%    \begin{macrocode}
+\cs_new:Npn \@@_exp_after_symbolic_f:nw
+    #1 \s_@@_symbolic \@@_symbolic_chk:w #2, #3#4;
+  {
+    \exp_after:wN \@@_exp_after_symbolic_aux:w
+    \exp:w
+    \@@_exp_after_symbolic_loop:N #2
+      { , \exp:w \use_none:nn }
+    \exp_after:wN \exp_end: \exp_after:wN
+      {
+        \exp:w \exp_end_continue_f:w
+        \@@_exp_after_array_f:w #3 \s_@@_expr_stop
+        \exp_after:wN
+      }
+    \exp_after:wN ;
+    \exp:w \exp_end_continue_f:w #1
+  }
+\cs_new:Npn \@@_exp_after_symbolic_aux:w #1, #2;
+  {
+    \@@_if_has_symbolic:nTF {#2}
+      { \s_@@_symbolic \@@_symbolic_chk:w #1, {#2} ; }
+      { #1 #2 @ \prg_do_nothing: }
+  }
+\cs_new:Npn \@@_exp_after_symbolic_loop:N #1
+  {
+    \exp_after:wN \exp_end:
+    \exp_after:wN #1
+    \exp:w
+    \@@_exp_after_symbolic_loop:N
+  }
+%    \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Applying infix operators to expressions}
+%
+% \begin{macro}[EXP]{\@@_symbolic_binary_o:Nww}
+%   Used when applying infix operators to expressions.
+%    \begin{macrocode}
+\cs_new:Npn \@@_symbolic_binary_o:Nww #1 #2; #3;
+  {
+    \@@_exp_after_symbolic_f:nw { \exp_after:wN \exp_stop_f: }
+      \s_@@_symbolic \@@_symbolic_chk:w
+      \@@_types_binary:Nww #1 , { #2; #3; } ;
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{@makeother}{^} %^^A Hack!
+% \begin{@makeother}{|} %^^A Hack!
+% \begin{@makeother}{&} %^^A Hack!
+% \begin{macro}[EXP]
+%   {
+%     \@@_symbolic_+_symbolic_o:ww,
+%     \@@_symbolic_+_o:ww,
+%     \@@_+_symbolic_o:ww,
+%     \@@_symbolic_-_symbolic_o:ww,
+%     \@@_symbolic_-_o:ww,
+%     \@@_-_symbolic_o:ww,
+%     \@@_symbolic_*_symbolic_o:ww,
+%     \@@_symbolic_*_o:ww,
+%     \@@_*_symbolic_o:ww,
+%     \@@_symbolic_/_symbolic_o:ww,
+%     \@@_symbolic_/_o:ww,
+%     \@@_/_symbolic_o:ww,
+%     \@@_symbolic_^_symbolic_o:ww,
+%     \@@_symbolic_^_o:ww,
+%     \@@_^_symbolic_o:ww,
+%     \@@_symbolic_|_symbolic_o:ww,
+%     \@@_symbolic_|_o:ww,
+%     \@@_|_symbolic_o:ww,
+%     \@@_symbolic_&_symbolic_o:ww,
+%     \@@_symbolic_&_o:ww,
+%     \@@_&_symbolic_o:ww,
+%   }
+%    \begin{macrocode}
+\cs_set:Npn \@@_tmp:w #1#2
+  {
+    \cs_new_nopar:cpn
+      { @@_symbolic_#2_symbolic_o:ww }
+      { \@@_symbolic_binary_o:Nww #1 }
+    \cs_new_eq:cc
+      { @@_symbolic_#2         _o:ww }
+      { @@_symbolic_#2_symbolic_o:ww }
+    \cs_new_eq:cc
+      { @@         _#2_symbolic_o:ww }
+      { @@_symbolic_#2_symbolic_o:ww }
+  }
+\tl_map_inline:nn { + - * / ^ & | }
+  { \exp_args:Nc \@@_tmp:w { @@_#1_o:ww } {#1} }
+%    \end{macrocode}
+% \end{macro}
+% \end{@makeother}
+% \end{@makeother}
+% \end{@makeother}
+%
+% \subsection{Applying prefix functions to expressions}
+%
+% \begin{macro}[aux, EXP]{\@@_symbolic_unary_o:NNw}
+%   Used when applying infix operators to expressions.
+%    \begin{macrocode}
+\cs_new:Npn \@@_symbolic_unary_o:NNw #1#2#3; @
+  {
+    \@@_exp_after_symbolic_f:nw { \exp_after:wN \exp_stop_f: }
+      \s_@@_symbolic \@@_symbolic_chk:w
+      \@@_types_unary:NNw #1#2 , { #3; } ;
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+%   {
+%     \@@_symbolic_acos_o:w     ,
+%     \@@_symbolic_acsc_o:w     ,
+%     \@@_symbolic_asec_o:w     ,
+%     \@@_symbolic_asin_o:w     ,
+%     \@@_symbolic_cos_o:w      ,
+%     \@@_symbolic_cot_o:w      ,
+%     \@@_symbolic_csc_o:w      ,
+%     \@@_symbolic_exp_o:w      ,
+%     \@@_symbolic_ln_o:w       ,
+%     \@@_symbolic_not_o:w      ,
+%     \@@_symbolic_sec_o:w      ,
+%     \@@_symbolic_set_sign_o:w ,
+%     \@@_symbolic_sin_o:w      ,
+%     \@@_symbolic_tan_o:w      ,
+%   }
+%    \begin{macrocode}
+\tl_map_inline:nn
+  {
+    {acos} {acsc} {asec} {asin} {cos} {cot} {csc} {exp} {ln}
+    {not} {sec} {set_sign} {sin} {sqrt} {tan}
+  }
+  {
+    \cs_new_nopar:cpe { @@_symbolic_#1_o:w }
+      {
+        \exp_not:N \@@_symbolic_unary_o:NNw
+        \exp_not:c { @@_#1_o:w }
+      }
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \subsection{Conversions}
+%
+% \begin{macro}[EXP]
+%   {
+%     \@@_symbolic_to_decimal:w,
+%     \@@_symbolic_to_int:w,
+%     \@@_symbolic_to_scientific:w
+%   }
+% \begin{macro}[EXP]{\@@_symbolic_convert:wnnN}
+%   Symbolic expressions cannot be converted to decimal, integer, or
+%   scientific notation unless they can be reduced to
+%    \begin{macrocode}
+\cs_set_protected:Npn \@@_tmp:w #1#2#3
+  {
+    \cs_new_nopar:cpn { @@_symbolic_to_#1:w }
+      {
+        \exp_after:wN \@@_symbolic_convert:wnnN
+        \exp:w \exp_end_continue_f:w
+        \@@_exp_after_symbolic_f:nw { { #2 } { fp_to_#1 } #3 }
+      }
+  }
+\@@_tmp:w { decimal    } { 0   } \@@_to_decimal_dispatch:w
+\@@_tmp:w { int        } { 0   } \@@_to_int_dispatch:w
+\@@_tmp:w { scientific } { nan } \@@_to_scientific_dispatch:w
+\cs_new:Npn \@@_symbolic_convert:wnnN #1#2; #3#4#5
+  {
+    \str_if_eq:nnTF {#1} { \s_@@_symbolic }
+      { \@@_invalid_operation:nnw {#3} {#4} #1#2; }
+      { #5 #1#2; }
+  }
+%    \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]
+%   {\@@_symbolic_cs_arg_to_fn:NN, \@@_symbolic_op_arg_to_fn:nN}
+%    \begin{macrocode}
+\cs_new:Npn \@@_symbolic_cs_arg_to_fn:NN #1
+  {
+    \exp_args:Nf \@@_symbolic_op_arg_to_fn:nN
+      { \@@_types_cs_to_op:N #1 }
+  }
+\cs_new:Npn \@@_symbolic_op_arg_to_fn:nN #1#2
+  {
+    \str_case:nnF { #1 #2 }
+      {
+        { not ? } { ! }
+        { set_sign 0 } { abs }
+        { set_sign 2 } { - }
+      }
+      {
+        \token_if_eq_meaning:NNTF #2 \use_ii:nn
+          { #1 d } {#1}
+      }
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_symbolic_to_tl:w}
+% \begin{macro}[rEXP]
+%   {
+%     \@@_symbolic_unary_to_tl:NNw,
+%     \@@_symbolic_binary_to_tl:Nww
+%   }
+%   Converting a symbolic expression to a token list is possible.
+%    \begin{macrocode}
+\cs_new:Npn \@@_symbolic_to_tl:w
+    \s_@@_symbolic \@@_symbolic_chk:w #1#2, #3#4;
+  {
+    \str_case:nnTF {#1}
+      {
+        { \@@_types_unary:NNw } { \@@_symbolic_unary_to_tl:NNw }
+        { \@@_types_binary:Nww } { \@@_symbolic_binary_to_tl:Nww }
+        { \@@_function_o:w } { \@@_symbolic_function_to_tl:Nw }
+      }
+      { #2, #3 @ }
+      { \tl_to_str:n {#2} }
+  }
+\cs_new:Npn \@@_symbolic_unary_to_tl:NNw #1#2 , #3 @
+  {
+    \use:e
+      {
+        \@@_symbolic_cs_arg_to_fn:NN #1#2
+        ( \@@_to_tl_dispatch:w #3 )
+      }
+  }
+\cs_new:Npn \@@_symbolic_binary_to_tl:Nww #1, #2; #3; @
+  {
+    \use:e
+      {
+        ( \@@_to_tl_dispatch:w #2; )
+        \@@_types_cs_to_op:N #1
+        ( \@@_to_tl_dispatch:w #3; )
+      }
+  }
+\cs_new:Npn \@@_symbolic_function_to_tl:Nw #1, #2@
+  {
+    \use:e
+      {
+        \@@_types_cs_to_op:N #1
+        ( \@@_array_to_clist:n {#2} )
+      }
+  }
+%    \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Identifiers}
+%
+% Functions defined here are not necessarily tied to symbolic
+% expressions.
+%
+% \begin{macro}[TF]{\@@_id_if_invalid:n}
+% \begin{macro}[EXP]{\@@_id_if_invalid_aux:N}
+%   If |#1| contains a space, it is not a valid identifier.  Otherwise,
+%   loop through letters in |#1|: if it is not a letter, break the loop
+%   and return \texttt{true}.  If the end of the loop is reached
+%   without finding any non-letter, return \texttt{false}.
+%    \begin{macrocode}
+\prg_new_protected_conditional:Npnn
+    \@@_id_if_invalid:n #1 { T , F , TF }
+  {
+    \tl_if_empty:nTF {#1}
+      { \prg_return_true: }
+      {
+        \tl_if_in:onTF { \tl_to_str:n {#1} } { ~ }
+          { \prg_return_true: }
+          {
+            \exp_after:wN \@@_id_if_invalid_aux:N \tl_to_str:n {#1}
+              { ? \prg_break:n \prg_return_false: }
+            \prg_break_point:
+          }
+      }
+  }
+\cs_new:Npn \@@_id_if_invalid_aux:N #1
+  {
+    \use_none:n #1
+    \int_compare:nF { `a <= `#1 <= `z }
+      {
+        \int_compare:nF { `A <= `#1 <= `Z }
+          { \prg_break:n \prg_return_true: }
+      }
+    \@@_id_if_invalid_aux:N
+  }
+%    \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Declaring variables and assigning values}
+%
+% \begin{macro}[EXP]{\@@_variable_o:w}
+%   We do not use \cs{exp_last_unbraced:Nv} to extract the value of
+%   |\l__fp_variable_#1_fp| because in \cs{fp_set_variable:nn} we define
+%   this \texttt{fp} variable to be something which \texttt{f}-expands
+%   to an actual floating point, rather than a genuine floating point.
+%    \begin{macrocode}
+\cs_new:Npn \@@_variable_o:w #1 @ #2
+  {
+    \fp_if_exist:cTF { l_@@_variable_#1_fp }
+      {
+        \exp_last_unbraced:Nf \@@_exp_after_array_f:w
+          { \use:c { l_@@_variable_#1_fp } } \s_@@_expr_stop
+        \exp_after:wN \exp_stop_f: #2
+      }
+      {
+        \token_if_eq_meaning:NNTF #2 \prg_do_nothing:
+          {
+            \s_@@_symbolic \@@_symbolic_chk:w
+              \@@_variable_o:w #1 , { } ;
+          }
+          {
+            \exp_after:wN \s_@@_symbolic
+            \exp_after:wN \@@_symbolic_chk:w
+            \exp_after:wN \@@_variable_o:w
+            \exp:w
+            \@@_exp_after_symbolic_loop:N #1
+              { , \exp:w \use_none:nn }
+            \exp_after:wN \exp_end:
+            \exp_after:wN { \exp_after:wN } \exp_after:wN ;
+            #2
+          }
+      }
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}
+%   {\@@_variable_set_parsing:Nn, \@@_variable_set_parsing_aux:NNn}
+%    \begin{macrocode}
+\cs_new:Npn \@@_variable_set_parsing:Nn #1#2
+  {
+    \cs_set_nopar:Npn \@@_tmp:w
+      {
+        \@@_exp_after_symbolic_f:nw { \@@_parse_infix:NN }
+        \s_@@_symbolic \@@_symbolic_chk:w
+          \@@_variable_o:w #2 , { } ;
+      }
+    \exp_args:NNc \@@_variable_set_parsing_aux:NNn #1
+      { @@_parse_word_#2:N } {#2}
+  }
+\cs_new:Npn \@@_variable_set_parsing_aux:NNn #1#2#3
+  {
+    \cs_if_eq:NNF #2 \@@_tmp:w
+      {
+        \cs_if_exist:NTF #2
+          {
+            \msg_warning:nnnn
+              { fp } { id-used-elsewhere } {#3} { variable }
+            #1 #2 \@@_tmp:w
+          }
+          {
+            \cs_new_eq:NN #2 \scan_stop: % to declare the function
+            #1 #2 \@@_tmp:w
+          }
+      }
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}{\fp_clear_variable:n}
+% \begin{macro}{\@@_clear_variable:n}
+%    \begin{macrocode}
+\cs_new_protected:Npn \fp_clear_variable:n #1
+  {
+    \@@_id_if_invalid:nTF {#1}
+      { \msg_error:nnn { fp } { id-invalid } {#1} }
+      { \exp_args:No \@@_clear_variable:n { \tl_to_str:n {#1} } }
+  }
+\cs_new_protected:Npn \@@_clear_variable:n #1
+  {
+    \cs_undefine:c { l_@@_variable_#1_fp }
+    \@@_variable_set_parsing:Nn \cs_set_eq:NN {#1}
+  }
+%    \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\fp_new_variable:n}
+% \begin{macro}{\@@_new_variable:n}
+%   Check that |#1| is a valid identifier.  If the identifier is already
+%   in use, complain.  Then set |\__fp_parse_word_#1:N| to use
+%   |\__fp_variable_o:w|.
+%    \begin{macrocode}
+\cs_new_protected:Npn \fp_new_variable:n #1
+  {
+    \@@_id_if_invalid:nTF {#1}
+      { \msg_error:nnn { fp } { id-invalid } {#1} }
+      { \exp_args:No \@@_new_variable:n { \tl_to_str:n {#1} } }
+  }
+\cs_new_protected:Npn \@@_new_variable:n #1
+  {
+    \cs_if_exist:cT { @@_parse_word_#1:N }
+      {
+        \msg_error:nnn
+          { fp } { id-already-defined } {#1}
+        \cs_undefine:c { @@_parse_word_#1:N }
+        \cs_undefine:c { l_@@_variable_#1_fp }
+      }
+    \@@_variable_set_parsing:Nn \cs_gset_eq:NN {#1}
+  }
+%    \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}{\fp_set_variable:nn}
+% \begin{macro}{\@@_set_variable:nn}
+%   Refuse invalid identifiers.  If the variable does not exist yet,
+%   define it just as in \cs{fp_new_variable:n} (but without unnecessary
+%   checks).  Then evaluate~|#2|.  If the result contains the
+%   identifier~|#1|, we would later get a loop in cases such as
+%   \begin{quote}
+%     \cs{fp_set_variable:nn} |{A}| |{A}|\\
+%     \cs{fp_show:n} |{A}|
+%   \end{quote}
+%   To detect this, define |\l__fp_variable_#1_fp| to raise an
+%   internal flag and evaluate to \texttt{nan}.  Then re-evaluate
+%   \cs{l_@@_symbolic_fp}, and store the result in~|#1|.  If the flag is
+%   raised, |#1|~was present in \cs{l_@@_symbolic_fp}.  In all cases,
+%   the |#1|-free result ends up in |\l__fp_variable_#1_fp|.
+%    \begin{macrocode}
+\flag_new:n { @@_symbolic }
+\cs_new_protected:Npn \fp_set_variable:nn #1
+  {
+    \@@_id_if_invalid:nTF {#1}
+      { \msg_error:nnn { fp } { id-invalid } {#1} }
+      { \exp_args:No \@@_set_variable:nn { \tl_to_str:n {#1} } }
+  }
+\cs_new_protected:Npn \@@_set_variable:nn #1#2
+  {
+    \@@_variable_set_parsing:Nn \cs_set_eq:NN {#1}
+    \fp_set:Nn \l_@@_symbolic_fp {#2}
+    \cs_set_nopar:cpn { l_@@_variable_#1_fp }
+      { \flag_ensure_raised:n { @@_symbolic } \c_nan_fp }
+    \flag_clear:n { @@_symbolic }
+    \fp_set:cn { l_@@_variable_#1_fp } { \l_@@_symbolic_fp }
+    \flag_if_raised:nT { @@_symbolic }
+      {
+        \msg_error:nneee { fp } { id-loop }
+          { \tl_to_str:n {#1} }
+          { \tl_to_str:n {#2} }
+          { \fp_to_tl:N \l_@@_symbolic_fp }
+      }
+  }
+%    \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \subsection{Messages}
+%
+%    \begin{macrocode}
+\msg_new:nnnn { fp } { id-invalid }
+  { Floating~point~identifier~'#1'~invalid. }
+  {
+    LaTeX~has~been~asked~to~create~a~new~floating~point~identifier~'#1'~
+    but~this~may~only~contain~ASCII~letters.
+  }
+\msg_new:nnnn { fp } { id-already-defined }
+  { Floating~point~identifier~'#1'~already~defined. }
+  {
+    LaTeX~has~been~asked~to~create~a~new~floating~point~identifier~'#1'~
+    but~this~name~has~already~been~used~elsewhere.
+  }
+\msg_new:nnnn { fp } { id-used-elsewhere }
+  { Floating~point~identifier~'#1'~already~used~for~something~else. }
+  {
+    LaTeX~has~been~asked~to~create~a~new~floating~point~identifier~'#1'~
+    but~this~name~is~used,~and~is~not~a~user-defined~#2.
+  }
+\msg_new:nnnn { fp } { id-loop }
+  { Variable~'#1'~used~in~the~definition~of~'#1'. }
+  {
+    LaTeX~has~been~asked~to~set~the~floating~point~identifier~'#1'~
+    to~the~expression~'#2'.~Evaluating~this~expression~yields~'#3',~
+    which~contains~'#1'~itself.
+  }
+%    \end{macrocode}
+%
+% \subsection{Road-map}
+%
+% The following functions are not implemented: |min|, |max|, |?:|,
+% comparisons, |round|, |atan|, |acot|.
+%
+%    \begin{macrocode}
+%</package>
+%    \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex


Property changes on: trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-symbolic.dtx
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-traps.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-traps.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-traps.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -2,7 +2,7 @@
 %
 %% File: l3fp-traps.dtx
 %
-% Copyright (C) 2011-2014,2016-2023 The LaTeX Project
+% Copyright (C) 2011-2023 The LaTeX Project
 %
 % It may be distributed and/or modified under the conditions of the
 % LaTeX Project Public License (LPPL), either version 1.3c of this
@@ -40,7 +40,7 @@
 %          {latex-team at latex-project.org}^^A
 %    }^^A
 % }
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 % \maketitle
 %
 % \begin{documentation}

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-trig.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-trig.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-trig.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -40,7 +40,7 @@
 %          {latex-team at latex-project.org}^^A
 %    }^^A
 % }
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 %
 % \maketitle
 %

Added: trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-types.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-types.dtx	                        (rev 0)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-types.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -0,0 +1,184 @@
+% \iffalse
+%
+%% File l3fp-types.dtx (C) Copyright 2012-2015,2017,2018,2020,2021,2023 The LaTeX Project
+%
+% It may be distributed and/or modified under the conditions of the
+% LaTeX Project Public License (LPPL), either version 1.3c of this
+% license or (at your option) any later version.  The latest version
+% of this license is in the file
+%
+%    http://www.latex-project.org/lppl.txt
+%
+% This file is part of the "l3kernel bundle" (The Work in LPPL)
+% and all files in that bundle must be distributed together.
+%
+% -----------------------------------------------------------------------
+%
+% The development version of the bundle can be found at
+%
+%    https://github.com/latex3/latex3
+%
+% for those people who are interested.
+%
+%<*driver>
+\documentclass[full]{l3doc}
+\usepackage{amsmath}
+\begin{document}
+  \DocInput{\jobname.dtx}
+\end{document}
+%</driver>
+% \fi
+%
+% \title{^^A
+%   The \pkg{l3fp-types} package\\ Floating point types^^A
+% }
+%
+% \author{^^A
+%  The \LaTeX{} Project\thanks
+%    {^^A
+%      E-mail:
+%        \href{mailto:latex-team at latex-project.org}
+%          {latex-team at latex-project.org}^^A
+%    }^^A
+% }
+%
+% \date{Released 2023-10-23}
+%
+% \maketitle
+%
+% \begin{documentation}
+%
+% \end{documentation}
+%
+% \begin{implementation}
+%
+% \section{\pkg{l3fp-types} implementation}
+%
+%    \begin{macrocode}
+%<*package>
+%    \end{macrocode}
+%
+%    \begin{macrocode}
+%<@@=fp>
+%    \end{macrocode}
+%
+% \subsection{Support for types}
+%
+% Despite lack of documentation, the \pkg{l3fp} internals support types.
+% Each additional type must define
+% \begin{itemize}
+%   \item |\s__fp_|\meta{type} and |\__fp_|\meta{type}|_chk:w|;
+%   \item |\__fp_exp_after_|\meta{type}|_f:nw|;
+%   \item |\__fp_|\meta{type}|_to_|\meta{out}|:w| for \meta{out} among
+%     |decimal|, |scientific|, |tl|;
+% \end{itemize}
+% and may define
+% \begin{itemize}
+%   \item |\__fp_|\meta{type}|_to_int:w| and |\__fp_|\meta{type}|_to_dim:w|;
+%   \item |\__fp_|\meta{op}|_|\meta{type}|_o:w| for any of the \meta{op}
+%     that the type implements, among |acos|, |acsc|, |asec|, |asin|,
+%     |cos|, |cot|, |csc|, |exp|, |ln|, |not|, |sec|, |set_sign|, |sin|,
+%     |tan|;
+%   \item |\__fp_|\meta{type_1}|_|\meta{op}|_|\meta{type_2}|_o:ww| for
+%     \meta{op} among |^*/-+&|\verb"|" and for every pair of types;
+%   \item |\__fp_|\meta{type_1}|_bcmp_|\meta{type_2}|:ww| for every
+%     pair of types.
+% \end{itemize}
+% The latter is set up in \pkg{l3fp-logic}.
+%
+% \subsection{Dispatch according to the type}
+%
+% \begin{macro}[EXP]
+%   {\@@_types_cs_to_op:N, \@@_types_cs_to_op_auxi:wwwn}
+%   From |\__fp_|\meta{op}|_o:w| produce \meta{op}, otherwise~|?|.
+%    \begin{macrocode}
+\cs_new:Npe \@@_types_cs_to_op:N #1
+  {
+    \exp_not:N \exp_after:wN \exp_not:N \@@_types_cs_to_op_auxi:wwwn
+      \exp_not:N \token_to_str:N #1 \s_@@_mark
+      \exp_not:N \@@_use_i_delimit_by_s_stop:nw
+      \tl_to_str:n { @@_ _o:w } \s_@@_mark
+        { \exp_not:N \@@_use_i_delimit_by_s_stop:nw ? }
+      \s_@@_stop
+  }
+\use:e
+  {
+    \cs_new:Npn \exp_not:N \@@_types_cs_to_op_auxi:wwwn
+      #1 \tl_to_str:n { @@_ } #2
+      \tl_to_str:n { _o:w } #3 \s_@@_mark #4 { #4 {#2} }
+  }
+%    \end{macrocode}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_types_unary:NNw}
+% \begin{macro}[EXP]
+%   {\@@_types_unary_auxi:nNw, \@@_types_unary_auxii:NnNw}
+%   \begin{syntax}
+%     \cs{@@_types_unary:NNw} |\__fp_|\meta{function}|_o:w|
+%     ~~\meta{token} \meta{operand} |@|
+%   \end{syntax}
+%    \begin{macrocode}
+\cs_new:Npn \@@_types_unary:NNw #1
+  {
+    \exp_args:Nf \@@_types_unary_auxi:nNw
+      { \@@_types_cs_to_op:N #1 }
+  }
+\cs_new:Npn \@@_types_unary_auxi:nNw #1#2#3
+  {
+    \exp_after:wN \@@_types_unary_auxii:NnNw
+    \cs:w @@_#1 \@@_type_from_scan:N #3 _o:w \cs_end:
+    {#1}
+    #2#3
+  }
+\cs_new:Npn \@@_types_unary_auxii:NnNw #1#2#3
+  {
+    \token_if_eq_meaning:NNTF \scan_stop: #1
+      { \@@_invalid_operation_o:nw {#2} }
+      { #1 #3 }
+  }
+%    \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+% \begin{macro}[EXP]{\@@_types_binary:Nww}
+% \begin{macro}[EXP]
+%   {\@@_types_binary_auxi:Nww, \@@_types_binary_auxii:NNww}
+%   \begin{syntax}
+%     \cs{@@_types_binary:Nww} |\__fp_|\meta{binop}|_o:ww|
+%     ~~\meta{operand_1} \meta{operand_2} |@|
+%   \end{syntax}
+%    \begin{macrocode}
+\cs_new:Npn \@@_types_binary:Nww #1
+  {
+    \exp_last_unbraced:Nf \@@_types_binary_auxi:Nww
+      { \@@_types_cs_to_op:N #1 }
+  }
+\cs_new:Npn \@@_types_binary_auxi:Nww #1#2#3; #4#5; @
+  {
+    \exp_after:wN \@@_types_binary_auxii:NNww
+    \cs:w
+      @@
+      \@@_type_from_scan:N #2
+      _#1
+      \@@_type_from_scan:N #4
+      _o:ww
+    \cs_end:
+    #1 #2#3; #4#5;
+  }
+\cs_new:Npn \@@_types_binary_auxii:NNww #1#2
+  {
+    \token_if_eq_meaning:NNTF \scan_stop: #1
+      { \@@_invalid_operation_o:Nww #2 }
+      {#1}
+  }
+%    \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
+%    \begin{macrocode}
+%</package>
+%    \end{macrocode}
+%
+% \end{implementation}
+%
+% \PrintIndex


Property changes on: trunk/Master/texmf-dist/source/latex/l3kernel/l3fp-types.dtx
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3fp.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3fp.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3fp.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -49,7 +49,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 %
 % \maketitle
 %
@@ -658,6 +658,138 @@
 %   the \meta{code} should make use of the \meta{tl~var}.
 % \end{function}
 %
+% \section{Symbolic expressions}
+%
+% Floating point expressions support variables: these can only be set locally,
+% so act like standard \cs[no-index]{l_\dots} variables.
+% \begin{quote}\let\obeyedline=\newline\obeylines^^A
+%   \cs{fp_new_variable:n} |{ A }|
+%   \cs{fp_set:Nn} \cs{l_tmpb_fp} |{ 1 * sin(A) + 3**2 }|
+%   \cs{fp_show:n} |{| \cs{l_tmpb_fp} |}|
+%   \cs{fp_show:N} \cs{l_tmpb_fp}
+%   \cs{fp_set_variable:nn} |{ A }| |{ pi/2 }|
+%   \cs{fp_show:n} |{| \cs{l_tmpb_fp} |}|
+%   \cs{fp_show:N} \cs{l_tmpb_fp}
+%   \cs{fp_set_variable:nn} |{ A }| |{ 0 }|
+%   \cs{fp_show:n} |{| \cs{l_tmpb_fp} |}|
+%   \cs{fp_show:N} \cs{l_tmpb_fp}
+% \end{quote}
+% defines~|A| to be a variable, then defines \cs{l_tmpb_fp} to stand for
+% |1*sin(A)+9| (note that |3**2| is evaluated, but the |1*|~product is
+% not simplified away).  Until \cs{l_tmpb_fp} is changed, \cs{fp_show:N}
+% \cs{l_tmpb_fp} will show |((1*sin(A))+9)| regardless of the value
+% of~|A|.  The next step defines~|A| to be equal to~|pi/2|: then
+% \cs{fp_show:n} |{| \cs{l_tmpb_fp} |}| will evaluate \cs{l_tmpb_fp} and
+% show~|10|.  We then redefine~|A| to be~|0|: since \cs{l_tmpb_fp} still
+% stands for |1*sin(A)+9|, the value shown is then~|9|.  Variables can
+% be set with \cs{fp_set_variable:nn} to arbitrary floating point
+% expressions including other variables.
+%
+% \begin{function}[added = 2023-10-19]{\fp_new_variable:n}
+%   \begin{syntax}
+%     \cs{fp_new_variable:n} \Arg{identifier}
+%   \end{syntax}
+%   Declares the \meta{identifier} as a variable, which allows it to be
+%   used in floating point expressions.  For instance,
+%   \begin{quote}
+%     \cs{fp_new_variable:n} |{ A }| \\
+%     \cs{fp_show:n} |{ A**2 - A + 1 }|
+%   \end{quote}
+%   shows |(((A^2)-A)+1)|.  If the declaration was missing, the parser
+%   would complain about an \enquote{\texttt{Unknown fp word 'A'}}.  The
+%   \meta{identifier} must consist entirely of Latin letters among
+%   |[a-zA-Z]|.
+% \end{function}
+%
+% \begin{function}[added = 2023-10-19]{\fp_set_variable:nn}
+%   \begin{syntax}
+%     \cs{fp_set_variable:nn} \Arg{identifier} \Arg{fp expr}
+%   \end{syntax}
+%   Defines the \meta{identifier} to stand in any further expression for
+%   the result of evaluating the \meta{floating point expression} as
+%   much as possible.  The result may contain other variables, which are
+%   then replaced by their values if they have any.  For instance,
+%   \begin{quote}\let\obeyedline=\newline\obeylines^^A
+%     \cs{fp_new_variable:n} |{ A }|
+%     \cs{fp_new_variable:n} |{ B }|
+%     \cs{fp_new_variable:n} |{ C }|
+%     \cs{fp_set_variable:nn} |{ A } { 3 }|
+%     \cs{fp_set_variable:nn} |{ C } { A ** 2 + B * 1 }|
+%     \cs{fp_show:n} |{ C + 4 }|
+%     \cs{fp_set_variable:nn} |{ A } { 4 }|
+%     \cs{fp_show:n} |{ C + 4 }|
+%   \end{quote}
+%   shows |((9+(B*1))+4)| twice: changing the value of~|A| to~|4| does
+%   not alter~|C| because |A|~was replaced by its value~|3| when
+%   evaluating |A**2+B*1|.
+% \end{function}
+%
+% \begin{function}[added = 2023-10-19]{\fp_clear_variable:n}
+%   \begin{syntax}
+%     \cs{fp_clear_variable:n} \Arg{identifier}
+%   \end{syntax}
+%   Removes any value given by \cs{fp_set_variable:nn} to the variable
+%   with this \meta{identifier}.  For instance,
+%   \begin{quote}\let\obeyedline=\newline\obeylines^^A
+%     \cs{fp_new_variable:n} |{ A }|
+%     \cs{fp_set_variable:nn} |{ A } { 3 }|
+%     \cs{fp_show:n} |{ A ^ 2 }|
+%     \cs{fp_clear_variable:n} |{ A }|
+%     \cs{fp_show:n} |{ A ^ 2 }|
+%   \end{quote}
+%   shows~|9|, then~|(A^2)|.
+% \end{function}
+%
+% \section{User-defined functions}
+%
+% It is possible to define new user functions which can be used inside
+% the argument to \cs{fp_eval:n}, etc. These functions may take one or
+% more named arguments, and should be implemented using expansion methods
+% only.
+%
+% \begin{function}[added = 2023-10-19]{\fp_new_function:n}
+%   \begin{syntax}
+%     \cs{fp_new_function:n} \Arg{identifier}
+%   \end{syntax}
+%   Declares the \meta{identifier} as a function, which allows it to be
+%   used in floating point expressions.  For instance,
+%   \begin{quote}
+%      \cs{fp_new_function:n} |{ foo }| \\
+%      \cs{fp_show:n} |{ foo ( 1 + 2 , foo(3), A ) ** 2 } }|
+%   \end{quote}
+%   shows |(foo(3, foo(3), A))^(2)|.  If the declaration was missing,
+%   the parser would complain about an \enquote{\texttt{Unknown fp word 'foo'}}.
+%   The \meta{identifier} must consist entirely of Latin letters |[a-zA-Z]|.
+% \end{function}
+%
+% \begin{function}[added = 2023-10-19]{\fp_set_function:nnn}
+%   \begin{syntax}
+%     \cs{fp_set_function:nnn} \Arg{identifier} \Arg{vars} \Arg{fp expr}
+%   \end{syntax}
+%   Defines the \meta{identifier} to stand in any further expression for
+%   the result of evaluating the \meta{floating point expression}, with
+%   the \meta{identifier} accepting the \meta{vars} (a comma list).
+%   The result may contain other functions, which are
+%   then replaced by their results if they have any.  For instance,
+%   \begin{quote}
+%     \cs{fp_new_function:n} |{ foo }| \\
+%     \cs{fp_set_function:nnn} |{ npow } { a,b } { a**b }| \\
+%     \cs{fp_show:n} |{ npow(16,0.25) } }|
+%   \end{quote}
+%   shows |2|. The names of the \meta{vars} must
+%   consist entirely of Latin letters |[a-zA-Z]|, but are otherwise not
+%   restricted: in particular, they are independent of any variables
+%   declared by \cs{fp_new_variable:n}.
+% \end{function}
+%
+% \begin{function}[added = 2023-10-19]{\fp_clear_function:n}
+%   \begin{syntax}
+%     \cs{fp_clear_function:n} \Arg{identifier}
+%   \end{syntax}
+%   Removes any definition given by \cs{fp_set_function:nnn} to the function
+%   with this \meta{identifier}.
+% \end{function}
+%
 % \section{Some useful constants, and scratch variables}
 %
 % \begin{variable}[added = 2012-05-08, module = fp]{\c_zero_fp, \c_minus_zero_fp}
@@ -1417,7 +1549,7 @@
 % \begin{function}[EXP, added = 2012-09-26, tested = m3fp-convert003]
 %   {\fp_max:nn, \fp_min:nn}
 %   \begin{syntax}
-%     \cs{fp_max:nn} \Arg{fp expression 1} \Arg{fp expression 2}
+%     \cs{fp_max:nn} \Arg{fp expr_1} \Arg{fp expr_2}
 %   \end{syntax}
 %   Evaluates the \meta{fp exprs} as described for
 %   \cs{fp_eval:n} and leaves the resulting larger (\texttt{max}) or

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3fparray.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3fparray.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3fparray.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -44,7 +44,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3int.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3int.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3int.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 %
 % \maketitle
 %
@@ -94,7 +94,7 @@
 % \int_show:n { \l_my_tl +  \l_my_int * 3 - ( 3 + 4 * 5 ) }
 % \end{verbatim}
 % show the same result $-6$ because \cs[no-index]{l_my_tl} expands to
-% the integer denotation~|5| while the integer variable \cs{l_my_int}
+% the integer denotation~|5| while the integer variable \cs[no-index]{l_my_int}
 % takes the value~$4$.  As the \meta{integer expression} is fully
 % expanded from left to right during evaluation, fully expandable and
 % restricted-expandable functions can both be used, and \cs{exp_not:n}

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3intarray.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3intarray.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3intarray.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -44,7 +44,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3kernel-functions.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3kernel-functions.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3kernel-functions.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3keys.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3keys.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3keys.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 %
 % \maketitle
 %
@@ -385,10 +385,10 @@
 %   \begin{syntax}
 %     \meta{key} .legacy_if_set:n = \meta{switch}
 %   \end{syntax}
-%   Defines \meta{key} to set legacy \cs{if} \meta{switch} to \meta{value}
+%   Defines \meta{key} to set legacy \cs[no-index]{if\meta{switch}} to \meta{value}
 %   (which must be either \enquote{\texttt{true}} or \enquote{\texttt{false}}).
 %   The \meta{switch} is the name of the switch \emph{without the leading
-%   \cs{if}}.
+%   \texttt{if}}.
 %
 %   The \texttt{inverse} versions will set the \meta{switch} to the logical
 %   opposite of the \meta{value}.
@@ -740,16 +740,12 @@
 % \end{function}
 %
 % \begin{variable}[updated = 2020-02-08]
-%   {\l_keys_key_str, \l_keys_path_str, \l_keys_value_tl}
+%   {\l_keys_path_str, \l_keys_key_str, \l_keys_value_tl}
 %   For each key processed, information of the full \emph{path} of the
 %   key, the \emph{name} of the key and the \emph{value} of the key is
 %   available within two string and one token list variables.
 %   These may be used within the code of the key.
 %
-%   The \emph{value} is everything after the \texttt{=}, which may be
-%   empty if no value was given. This is stored in \cs{l_keys_value_tl}, and
-%   is not processed in any way by \cs{keys_set:nn}.
-%
 %   The \emph{path} of the key is a \enquote{full} description of the key,
 %   and is unique for each key. It consists of the module and full key name,
 %   thus for example
@@ -767,6 +763,10 @@
 %   \texttt{/}, and thus is not unique. In the preceding examples, both keys
 %   have name \texttt{key-a} despite having different paths.  This information
 %   is stored in \cs{l_keys_key_str}.
+%
+%   The \emph{value} is everything after the \texttt{=}, which may be
+%   empty if no value was given. This is stored in \cs{l_keys_value_tl}, and
+%   is not processed in any way by \cs{keys_set:nn}.
 % \end{variable}
 %
 % \section{Handling of unknown keys}
@@ -788,10 +788,10 @@
 %   {
 %     \keys_set_known:nn, \keys_set_known:nV,
 %     \keys_set_known:nv, \keys_set_known:ne,
-%     \keys_set_known:no
+%     \keys_set_known:no,
 %     \keys_set_known:nnN, \keys_set_known:nVN,
 %     \keys_set_known:nvN, \keys_set_known:neN,
-%     \keys_set_known:noN
+%     \keys_set_known:noN,
 %     \keys_set_known:nnnN, \keys_set_known:nVnN,
 %     \keys_set_known:nvnN, \keys_set_known:nenN,
 %     \keys_set_known:nonN

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3legacy.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3legacy.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3legacy.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 %
 % \maketitle
 %
@@ -60,7 +60,7 @@
 %     \cs{legacy_if:nTF} \Arg{name} \Arg{true code} \Arg{false code}
 %   \end{syntax}
 %   Tests if the \LaTeXe{}/plain \TeX{} conditional (generated by \tn{newif})
-%   if \texttt{true} or \texttt{false} and branches accordingly. The
+%   is \texttt{true} or \texttt{false} and branches accordingly. The
 %   \meta{name} of the conditional should \emph{omit} the leading \texttt{if}.
 % \end{function}
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3luatex.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3luatex.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3luatex.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3msg.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3msg.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3msg.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3names.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3names.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3names.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3pdf.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3pdf.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3pdf.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3prg.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3prg.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3prg.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 %
 % \maketitle
 %
@@ -581,7 +581,7 @@
 %   \begin{syntax}
 %     \cs{bool_until_do:Nn} \meta{boolean} \Arg{code}
 %   \end{syntax}
-%   This function firsts checks the logical value of the \meta{boolean}.
+%   This function first checks the logical value of the \meta{boolean}.
 %   If it is \texttt{false} the \meta{code} is placed in the input stream
 %   and expanded. After the completion of the \meta{code} the truth
 %   of the \meta{boolean} is re-evaluated. The process then loops
@@ -592,7 +592,7 @@
 %   \begin{syntax}
 %     \cs{bool_while_do:Nn} \meta{boolean} \Arg{code}
 %   \end{syntax}
-%   This function firsts checks the logical value of the \meta{boolean}.
+%   This function first checks the logical value of the \meta{boolean}.
 %   If it is \texttt{true} the \meta{code} is placed in the input stream
 %   and expanded. After the completion of the \meta{code} the truth
 %   of the \meta{boolean} is re-evaluated. The process then loops
@@ -627,7 +627,7 @@
 %   \begin{syntax}
 %     \cs{bool_until_do:nn} \Arg{boolean expression} \Arg{code}
 %   \end{syntax}
-%   This function firsts checks the logical value of the
+%   This function first checks the logical value of the
 %   \meta{boolean expression} (as described for \cs{bool_if:nTF}).
 %   If it is \texttt{false} the \meta{code} is placed in the input stream
 %   and expanded. After the completion of the \meta{code} the truth
@@ -639,7 +639,7 @@
 %   \begin{syntax}
 %     \cs{bool_while_do:nn} \Arg{boolean expression} \Arg{code}
 %   \end{syntax}
-%   This function firsts checks the logical value of the
+%   This function first checks the logical value of the
 %   \meta{boolean expression} (as described for \cs{bool_if:nTF}).
 %   If it is \texttt{true} the \meta{code} is placed in the input stream
 %   and expanded. After the completion of the \meta{code} the truth
@@ -808,7 +808,8 @@
 %     \cs{prg_break:n} \Arg{code} \ldots{} \cs{prg_break_point:}
 %   \end{syntax}
 %   Breaks a recursion which has no \meta{ending code} and which is not
-%   a user-breakable mapping (see for instance \cs{prop_get:Nn}), and
+%   a user-breakable mapping (see for instance
+%   implementation of \cs{int_step_function:nnnN}), and
 %   inserts the \meta{code} in the input stream.
 % \end{function}
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3prop.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3prop.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3prop.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 %
 % \maketitle
 %
@@ -60,7 +60,7 @@
 % Each entry in a property list must have a unique \meta{key}: if an entry is
 % added to a property list which already contains the \meta{key} then the new
 % entry overwrites the existing one. The \meta{keys} are compared on a
-% string basis, using the same method as \cs{str_if_eq:nn}.
+% string basis, using the same method as \cs{str_if_eq:nnTF}.
 %
 % Property lists are intended for storing key-based information for use within
 % code.  This is in contrast to key--value lists, which are a form of

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3quark.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3quark.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3quark.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3regex.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3regex.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3regex.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 %
 % \maketitle
 %
@@ -921,7 +921,7 @@
 %   \item Optimize states with a single \cs[no-index]{__regex_action_free:n}.
 %   \item Optimize the use of \cs[no-index]{__regex_action_success:} by inserting it
 %     in state $2$ directly instead of having an extra transition.
-%   \item Optimize the use of \cs{int_step_...} functions.
+%   \item Optimize the use of \cs[no-index]{int_step_...} functions.
 %   \item Groups don't capture within regexes for csnames; optimize and
 %     document.
 %   \item Better \enquote{show} for anchors, properties, and catcode tests.
@@ -930,7 +930,7 @@
 %     numbers.
 %   \item Instead of checking whether the character is special or
 %     alphanumeric using its character code, check if it is special in
-%     regexes with \cs{cs_if_exist} tests.
+%     regexes with \cs[no-index]{cs_if_exist} tests.
 % \end{itemize}
 %
 % The following features are likely to be implemented at some point

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3seq.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3seq.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3seq.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3skip.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3skip.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3skip.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -44,7 +44,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3sort.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3sort.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3sort.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3str-convert.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3str-convert.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3str-convert.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -44,7 +44,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3str.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3str.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3str.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 %
 % \maketitle
 %
@@ -752,7 +752,7 @@
 %     \item Case changing text for typesetting: see the
 %       \cs[index=text_lowercase:n]{text_lowercase:n(n)},
 %       \cs[index=text_uppercase:n]{text_uppercase:n(n)} and
-%       \cs[index=text_titlecase:n]{text_titlecase:n(n)} functions which
+%       \cs[index=text_titlecase_all:n]{text_titlecase_(all|once):n(n)} functions which
 %       correctly deal with context-dependence and other factors appropriate
 %       to text case changing.
 %   \end{itemize}
@@ -1138,7 +1138,7 @@
 % \end{macro}
 %
 % \begin{macro}[EXP]{\@@_if_eq:nn}
-%   String comparisons rely on the primitive \cs[index=pdfstrcmp]{(pdf)strcmp},
+%   String comparisons rely on the primitive \tn[index=pdfstrcmp]{(pdf)strcmp},
 %   so we define a new name for it.
 %    \begin{macrocode}
 \cs_new_eq:NN \@@_if_eq:nn \tex_strcmp:D

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3sys.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3sys.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3sys.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 %
 % \maketitle
 %
@@ -299,7 +299,7 @@
 %   enabled.  This returns false if unrestricted shell escape is
 %   enabled.  Unrestricted shell escape is not considered a superset
 %   of restricted shell escape in this case.  To find whether any
-%   shell escape is enabled use \cs{sys_if_shell:}.
+%   shell escape is enabled use \cs{sys_if_shell:TF}.
 % \end{function}
 %
 % \begin{function}[added = 2017-05-27]

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3text-case.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3text-case.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3text-case.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 %
 % \maketitle
 %
@@ -77,7 +77,7 @@
 %   {
 %     \text_lowercase:n,
 %     \text_uppercase:n,
-%     \text_titlecase:n,
+%     \text_titlecase_all:n,
 %     \text_titlecase_first:n
 %   }
 % \begin{macro}[EXP]
@@ -84,9 +84,10 @@
 %   {
 %     \text_lowercase:nn,
 %     \text_uppercase:nn,
-%     \text_titlecase:nn,
+%     \text_titlecase_all:nn,
 %     \text_titlecase_first:nn
 %   }
+% \begin{macro}[EXP]{\@@_change_case:nnn}
 %   The user level functions here are all wrappers around the internal
 %   functions for case changing.
 %    \begin{macrocode}
@@ -94,31 +95,34 @@
   { \@@_change_case:nnn { lower } { } {#1} }
 \cs_new:Npn \text_uppercase:n #1
   { \@@_change_case:nnn { upper } { } {#1} }
-\cs_new:Npn \text_titlecase:n #1
+\cs_new:Npn \text_titlecase_all:n #1
   { \@@_change_case:nnn { title } { } {#1} }
 \cs_new:Npn \text_titlecase_first:n #1
-  { \@@_change_case:nnn { titleonly } { } {#1} }
+  { \@@_change_case:nnnn { title } { break } { } {#1} }
 \cs_new:Npn \text_lowercase:nn #1#2
   { \@@_change_case:nnn { lower } {#1} {#2} }
 \cs_new:Npn \text_uppercase:nn #1#2
   { \@@_change_case:nnn { upper } {#1} {#2} }
-\cs_new:Npn \text_titlecase:nn #1#2
+\cs_new:Npn \text_titlecase_all:nn #1#2
   { \@@_change_case:nnn { title } {#1} {#2} }
 \cs_new:Npn \text_titlecase_first:nn #1#2
-  { \@@_change_case:nnn { titleonly } {#1} {#2} }
+  { \@@_change_case:nnnn { title } { break } {#1} {#2} }
+\cs_new:Npn \@@_change_case:nnn #1#2#3
+  { \@@_change_case:nnnn {#1} {#1} {#2} {#3} }
 %    \end{macrocode}
 % \end{macro}
 % \end{macro}
+% \end{macro}
 %
 % \begin{macro}[EXP]
 %   {
-%     \@@_change_case:nnn      ,
-%     \@@_change_case_auxi:nnn ,
-%     \@@_change_case_auxii:nnn
+%     \@@_change_case:nnnn       ,
+%     \@@_change_case_auxi:nnnn  ,
+%     \@@_change_case_auxii:nnnn
 %   }
-% \begin{macro}[EXP]{\@@_change_case_BCP:nnn}
-% \begin{macro}[EXP]{\@@_change_case_BCP:nnw}
-% \begin{macro}[EXP]{\@@_change_case_BCP:nnnnw}
+% \begin{macro}[EXP]{\@@_change_case_BCP:nnnn}
+% \begin{macro}[EXP]{\@@_change_case_BCP:nnnw}
+% \begin{macro}[EXP]{\@@_change_case_BCP:nnnnnw}
 % \begin{macro}[EXP]
 %   {
 %     \@@_change_case_store:n, \@@_change_case_store:o,
@@ -128,74 +132,69 @@
 % \begin{macro}[EXP]{\@@_change_case_store:nw}
 % \begin{macro}[EXP]{\@@_change_case_result:n} 
 % \begin{macro}[EXP]{\@@_change_case_end:w}
-% \begin{macro}[EXP]{\@@_change_case_loop:nnw}
-% \begin{macro}[EXP]{\@@_change_case_break:w}
+% \begin{macro}[EXP]{\@@_change_case_loop:nnnw}
+% \begin{macro}[EXP]{\@@_change_case_break:w, \@@_change_case_break_aux:w}
 % \begin{macro}[EXP]
 %   {
-%     \@@_change_case_group_lower:nnn     ,
-%     \@@_change_case_group_upper:nnn     ,
-%     \@@_change_case_group_title:nnn     ,
-%     \@@_change_case_group_titleonly:nnn
+%     \@@_change_case_group_lower:nnnn ,
+%     \@@_change_case_group_upper:nnnn ,
+%     \@@_change_case_group_title:nnnn
 %   }
-% \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:nnnN}
-% \begin{macro}[EXP]{\@@_change_case_math_search:nnNNN}
-% \begin{macro}[EXP]{\@@_change_case_math_loop:nnNw}
-% \begin{macro}[EXP]{\@@_change_case_math_N_type:nnNN}
-% \begin{macro}[EXP]{\@@_change_case_math_group:nnNn}
-% \begin{macro}[EXP]{\@@_change_case_math_space:nnNw}
-% \begin{macro}[EXP]{\@@_change_case_cs_check:nnN}
-% \begin{macro}[EXP]{\@@_change_case_exclude:nnN}
+%   {\@@_change_case_space:nnnw, \@@_change_case_space_break:nnnw}
+% \begin{macro}[EXP]
+%   {\@@_change_case_N_type:nnnN, \@@_change_case_N_type_aux:nnnN}
+% \begin{macro}[EXP]{\@@_change_case_N_type:nnnnN}
+% \begin{macro}[EXP]{\@@_change_case_math_search:nnnNNN}
+% \begin{macro}[EXP]{\@@_change_case_math_loop:nnnNw}
+% \begin{macro}[EXP]{\@@_change_case_math_N_type:nnnNN}
+% \begin{macro}[EXP]{\@@_change_case_math_group:nnnNn}
+% \begin{macro}[EXP]{\@@_change_case_math_space:nnnNw}
+% \begin{macro}[EXP]{\@@_change_case_cs_check:nnnN}
 % \begin{macro}[EXP]{\@@_change_case_exclude:nnnN}
-% \begin{macro}[EXP]{\@@_change_case_exclude:nnNN}
-% \begin{macro}[EXP]{\@@_change_case_exclude:nnNw}
-% \begin{macro}[EXP]{\@@_change_case_exclude:nnNnn}
-% \begin{macro}[EXP]{\@@_change_case_replace:nnN}
-% \begin{macro}[EXP]{\@@_change_case_replace:nnn, \@@_change_case_replace:vnn}
-% \begin{macro}[EXP]{\@@_change_case_switch:nnN}
+% \begin{macro}[EXP]{\@@_change_case_exclude:nnnnN}
+% \begin{macro}[EXP]{\@@_change_case_exclude:nnnNN}
+% \begin{macro}[EXP]{\@@_change_case_exclude:nnnNw}
+% \begin{macro}[EXP]{\@@_change_case_exclude:nnnNnn}
+% \begin{macro}[EXP]{\@@_change_case_replace:nnnN}
+% \begin{macro}[EXP]{\@@_change_case_replace:nnnn, \@@_change_case_replace:vnnn}
+% \begin{macro}[EXP]{\@@_change_case_switch:nnnN}
 % \begin{macro}[EXP]
 %   {
-%     \@@_change_case_switch_lower:nnNnnnn ,
-%     \@@_change_case_switch_upper:nnNnnnn ,
-%     \@@_change_case_switch_title:nnNnnnn ,
-%     \@@_change_case_switch_titleonly:nnNnnnn
+%     \@@_change_case_switch_lower:nnnNnnnn ,
+%     \@@_change_case_switch_upper:nnnNnnnn ,
+%     \@@_change_case_switch_title:nnnNnnnn
 %   }
+% \begin{macro}[EXP]{\@@_change_case_skip:nnw}
+% \begin{macro}[EXP]{\@@_change_case_skip_N_type:nnN}
+% \begin{macro}[EXP]{\@@_change_case_skip_group:nnn}
+% \begin{macro}[EXP]{\@@_change_case_skip_space:nnw}
 % \begin{macro}[EXP]
 %   {
-%     \@@_change_case_letterlike_lower:nnN ,
-%     \@@_change_case_letterlike_upper:nnN ,
-%     \@@_change_case_letterlike_title:nnN ,
-%     \@@_change_case_letterlike_titleonly:nnN
+%     \@@_change_case_letterlike_lower:nnnN ,
+%     \@@_change_case_letterlike_upper:nnnN ,
+%     \@@_change_case_letterlike_title:nnnN
 %   }
-% \begin{macro}[EXP]{\@@_change_case_letterlike:nnnnN}
+% \begin{macro}[EXP]{\@@_change_case_letterlike:nnnnnN}
 % \begin{macro}[EXP]
 %   {
-%     \@@_change_case_custom_lower:nnn ,
-%     \@@_change_case_custom_title:nnn ,
-%     \@@_change_case_custom_upper:nnn ,
-%     \@@_change_case_custom_titleonly:nnn
+%     \@@_change_case_custom_lower:nnnn ,
+%     \@@_change_case_custom_title:nnnn ,
+%     \@@_change_case_custom_upper:nnnn
 %   }
-% \begin{macro}[EXP]{\@@_change_case_custom:nnn}
+% \begin{macro}[EXP]{\@@_change_case_custom:nnnnn}
 % \begin{macro}[EXP]
 %   {
-%     \@@_change_case_codepoint_lower:nnn ,
-%     \@@_change_case_codepoint_upper:nnn ,
-%     \@@_change_case_codepoint_title:nnn ,
-%     \@@_change_case_codepoint_titleonly:nnn
+%     \@@_change_case_codepoint_lower:nnnn ,
+%     \@@_change_case_codepoint_upper:nnnn ,
+%     \@@_change_case_codepoint_title:nnnn
 %   }
-% \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_codepoint_title:nn     ,
-%     \@@_change_case_codepoint_titleonly:nn
-%   }
-% \begin{macro}[EXP]{\@@_change_case_codepoint_title:nnnn}
-% \begin{macro}[EXP]
-%   {\@@_change_case_codepoint:nnnn, \@@_change_case_codepoint_aux:nnnn}
+% \begin{macro}[EXP]{\@@_change_case_lower_sigma:nnnnn}
+% \begin{macro}[EXP]{\@@_change_case_lower_sigma:nnnnw}
+% \begin{macro}[EXP]{\@@_change_case_lower_sigma:nnnnN}
+% \begin{macro}[EXP]{\@@_change_case_codepoint_title:nnn}
+% \begin{macro}[EXP]{\@@_change_case_codepoint_title:nnnnn}
+% \begin{macro}[EXP]{\@@_change_case_codepoint:nnnnn}
 % \begin{macro}[EXP]{\@@_change_case_codepoint:nn}
 % \begin{macro}[EXP]
 %   {
@@ -203,13 +202,15 @@
 %     \@@_change_case_codepoint:fnn ,
 %     \@@_change_case_codepoint_aux:nnn
 %   }
+% \begin{macro}[EXP]{\@@_change_case_codepoint_aux:nnn}
+% \begin{macro}[EXP]{\@@_change_case_codepoint_aux:nn}
+% \begin{macro}[EXP]{\@@_change_case_catcode:nn}
 % \begin{macro}[EXP]
 %   {
-%     \@@_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
+%     \@@_change_case_next_lower:nnn ,
+%     \@@_change_case_next_upper:nnn ,
+%     \@@_change_case_next_title:nnn ,
+%     \@@_change_case_next_end:nnn
 %   }
 %   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
@@ -231,45 +232,50 @@
 %   wrap the entire result in exactly one \cs{exp_not:n}, or rather in the
 %   kernel version.
 %    \begin{macrocode}
-\cs_new:Npn \@@_change_case:nnn #1#2#3
+\cs_new:Npn \@@_change_case:nnnn #1#2#3#4
   {
      \__kernel_exp_not:w \exp_after:wN
       {
         \exp:w
-        \exp_args:Ne \@@_change_case_auxi:nnn
-          { \text_expand:n {#3} }
-          {#1} {#2}
+        \exp_args:Ne \@@_change_case_auxi:nnnn
+          { \text_expand:n {#4} }
+          {#1} {#2} {#3}
       }
   }
-\cs_new:Npn \@@_change_case_auxi:nnn #1#2#3
-  { \exp_args:No \@@_change_case_BCP:nnn { \tl_to_str:n {#3} } {#1} {#2} }
-\cs_new:Npe \@@_change_case_BCP:nnn #1#2#3
+\cs_new:Npn \@@_change_case_auxi:nnnn #1#2#3#4
   {
-    \exp_not:N \@@_change_case_BCP:nnw
-      {#2} {#3} #1 \tl_to_str:n { -x- -x- } \exp_not:N \q_@@_stop
+    \exp_args:No \@@_change_case_BCP:nnnn
+      { \tl_to_str:n {#4} } {#1} {#2} {#3}
   }
+\cs_new:Npe \@@_change_case_BCP:nnnn #1#2#3#4
+  {
+    \exp_not:N \@@_change_case_BCP:nnnw
+      {#2} {#3} {#4} #1 \tl_to_str:n { -x- -x- } \exp_not:N \q_@@_stop
+  }
 \use:e
   {
-    \cs_new:Npn \exp_not:N \@@_change_case_BCP:nnw
-      #1#2#3 \tl_to_str:n { -x- } #4 \tl_to_str:n { -x- } #5
+    \cs_new:Npn \exp_not:N \@@_change_case_BCP:nnnw
+      #1#2#3#4 \tl_to_str:n { -x- } #5 \tl_to_str:n { -x- } #6
       \exp_not:N \q_@@_stop
   }
-  { \@@_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
+  { \@@_change_case_BCP:nnnnnw {#1} {#2} {#3} {#5} {#4} #4 - \q_@@_stop }
+\cs_new:Npn \@@_change_case_BCP:nnnnnw #1#2#3#4#5#6 - #7 \q_@@_stop
   {
-    \cs_if_exist:cTF { @@_change_case_ #2 _ #5 -x- #3 :nnnn }
-      { \@@_change_case_auxii:nnn {#1} {#2} { #5 -x- #3 } }
+    \bool_lazy_or:nnTF
+      { \cs_if_exist_p:c { @@_change_case_ #2 _ #6 -x- #4 :nnnnn } }
+      { \tl_if_exist_p:c { l_@@_ #2 case_special_ #6 -x- #4 _tl } }
+      { \@@_change_case_auxii:nnnn {#1} {#2} {#3} { #6 -x- #4 } }
       {
-        \cs_if_exist:cTF { @@_change_case_ #2 _ #5 :nnnn }
-          { \@@_change_case_auxii:nnn {#1} {#2} {#5} }
-          { \@@_change_case_auxii:nnn {#1} {#2} {#4} }
+        \cs_if_exist:cTF { @@_change_case_ #2 _ #6 :nnnnn }
+          { \@@_change_case_auxii:nnnn {#1} {#2} {#3} {#6} }
+          { \@@_change_case_auxii:nnnn {#1} {#2} {#3} {#5} }
       }
   }
-\cs_new:Npn \@@_change_case_auxii:nnn #1#2#3
+\cs_new:Npn \@@_change_case_auxii:nnnn #1#2#3#4
   {
     \group_align_safe_begin:
-    \cs_if_exist_use:c { @@_change_case_boundary_ #2 _ #3 :Nnnw }
-    \@@_change_case_loop:nnw {#2} {#3} #1
+    \cs_if_exist_use:c { @@_change_case_boundary_ #2 _ #4 :Nnnnw }
+    \@@_change_case_loop:nnnw {#2} {#3} {#4} #1
       \q_@@_recursion_tail \q_@@_recursion_stop
     \@@_change_case_result:n { }
   }
@@ -290,20 +296,23 @@
 %    \end{macrocode}
 %   The main loop is the standard \texttt{tl action} type.
 %    \begin{macrocode}
-\cs_new:Npn \@@_change_case_loop:nnw #1#2#3 \q_@@_recursion_stop
+\cs_new:Npn \@@_change_case_loop:nnnw #1#2#3#4 \q_@@_recursion_stop
   {
-    \tl_if_head_is_N_type:nTF {#3}
-      { \@@_change_case_N_type:nnN }
+    \tl_if_head_is_N_type:nTF {#4}
+      { \@@_change_case_N_type:nnnN }
       {
-        \tl_if_head_is_group:nTF {#3}
-          { \use:c { @@_change_case_group_ #1 :nnn } }
-          { \@@_change_case_space:nnw }
+        \tl_if_head_is_group:nTF {#4}
+          { \use:c { @@_change_case_group_ #1 :nnnn } }
+          { \@@_change_case_space:nnnw }
       }
-    {#1} {#2} #3 \q_@@_recursion_stop
+    {#1} {#2} {#3} #4 \q_@@_recursion_stop
   }
-\cs_new:Npn \@@_change_case_break:w #1 \q_@@_recursion_tail \q_@@_recursion_stop
+\cs_new:Npn \@@_change_case_break:w
+  { \@@_change_case_break_aux:w \prg_do_nothing: }
+\cs_new:Npn \@@_change_case_break_aux:w
+  #1 \q_@@_recursion_tail \q_@@_recursion_stop
   {
-    \@@_change_case_store:n {#1}
+    \@@_change_case_store:o {#1}
     \@@_change_case_end:w
   }
 %    \end{macrocode}
@@ -315,7 +324,7 @@
 %   having too much testing, we use a two-step process here to allow the
 %   titlecase functions to be separate.
 %    \begin{macrocode}
-\cs_new:Npn \@@_change_case_group_lower:nnn #1#2#3
+\cs_new:Npn \@@_change_case_group_lower:nnnn #1#2#3#4
   {
     \@@_change_case_store:o
       {
@@ -322,14 +331,14 @@
         \exp_after:wN
           {
             \exp:w
-            \@@_change_case_auxii:nnn {#3} {#1} {#2}
+            \@@_change_case_auxii:nnnn {#4} {#1} {#2} {#3}
           }
       }
-    \@@_change_case_loop:nnw {#1} {#2}
+    \@@_change_case_loop:nnnw {#1} {#2} {#3}
   }
-\cs_new_eq:NN \@@_change_case_group_upper:nnn
-  \@@_change_case_group_lower:nnn
-\cs_new:Npn \@@_change_case_group_title:nnn #1#2#3
+\cs_new_eq:NN \@@_change_case_group_upper:nnnn
+  \@@_change_case_group_lower:nnnn
+\cs_new:Npn \@@_change_case_group_title:nnnn #1#2#3#4
   {
     \@@_change_case_store:o
       {
@@ -336,32 +345,26 @@
         \exp_after:wN
           {
             \exp:w
-            \@@_change_case_auxii:nnn {#3} {#1} {#2}
+            \@@_change_case_auxii:nnnn {#4} {#1} {#2} {#3}
           }
       }
-    \@@_change_case_loop:nnw { lower } {#2}
+    \@@_change_case_skip:nnw {#2} {#3}
   }
-\cs_new:Npn \@@_change_case_group_titleonly:nnn #1#2#3
-  {
-    \@@_change_case_store:o
-      {
-        \exp_after:wN
-          {
-            \exp:w
-            \@@_change_case_auxii:nnn {#3} {#1} {#2}
-          }
-      }
-    \@@_change_case_break:w
-  }
 \use:e
   {
-    \cs_new:Npn \exp_not:N \@@_change_case_space:nnw #1#2 \c_space_tl
+    \cs_new:Npn \exp_not:N \@@_change_case_space:nnnw #1#2#3 \c_space_tl
   }
   {
     \@@_change_case_store:n { ~ }
-    \cs_if_exist_use:c { @@_change_case_boundary_ #1 _ #2 :Nnnw }
-    \@@_change_case_loop:nnw {#1} {#2}
+    \cs_if_exist_use:cF { @@_change_case_space_ #2 :nnn }
+      {
+        \cs_if_exist_use:c { @@_change_case_boundary_ #1 _ #3 :Nnnnw }
+        \@@_change_case_loop:nnnw
+      }
+        {#2} {#2} {#3}
   }
+\cs_new:Npn \@@_change_case_space_break:nnn #1#2#3
+  { \@@_change_case_break:w }
 %    \end{macrocode}
 %   The first step of handling \texttt{N}-type tokens is to filter out the
 %   end-of-loop. That has to be done separately from the first real step
@@ -371,70 +374,70 @@
 %   (i.e.~there is no assumption of \enquote{well-behaved} input in terms of
 %   math mode).
 %    \begin{macrocode}
-\cs_new:Npn \@@_change_case_N_type:nnN #1#2#3
+\cs_new:Npn \@@_change_case_N_type:nnnN #1#2#3#4
   {
-    \@@_if_q_recursion_tail_stop_do:Nn #3
+    \@@_if_q_recursion_tail_stop_do:Nn #4
       { \@@_change_case_end:w }
-    \@@_change_case_N_type_aux:nnN {#1} {#2} #3
+    \@@_change_case_N_type_aux:nnnN {#1} {#2} {#3} #4
   }
-\cs_new:Npn \@@_change_case_N_type_aux:nnN #1#2#3
+\cs_new:Npn \@@_change_case_N_type_aux:nnnN #1#2#3#4
   {
-    \exp_args:NV \@@_change_case_N_type:nnnN
-      \l_text_math_delims_tl {#1} {#2} #3
+    \exp_args:NV \@@_change_case_N_type:nnnnN
+      \l_text_math_delims_tl {#1} {#2} {#3} #4
   }
-\cs_new:Npn \@@_change_case_N_type:nnnN #1#2#3#4
+\cs_new:Npn \@@_change_case_N_type:nnnnN #1#2#3#4#5
   {
-    \@@_change_case_math_search:nnNNN {#2} {#3} #4 #1
+    \@@_change_case_math_search:nnnNNN {#2} {#3} {#4} #5 #1
       \q_@@_recursion_tail \q_@@_recursion_tail
       \q_@@_recursion_stop
   }
-\cs_new:Npn \@@_change_case_math_search:nnNNN #1#2#3#4#5
+\cs_new:Npn \@@_change_case_math_search:nnnNNN #1#2#3#4#5#6
   {
-    \@@_if_q_recursion_tail_stop_do:Nn #4
-      { \@@_change_case_cs_check:nnN {#1} {#2} #3 }
-    \token_if_eq_meaning:NNTF #3 #4
+    \@@_if_q_recursion_tail_stop_do:Nn #5
+      { \@@_change_case_cs_check:nnnN {#1} {#2} {#3} #4 }
+    \token_if_eq_meaning:NNTF #4 #5
       {
         \@@_use_i_delimit_by_q_recursion_stop:nw
            {
-             \@@_change_case_store:n {#3}
-             \@@_change_case_math_loop:nnNw {#1} {#2} #5
+             \@@_change_case_store:n {#4}
+             \@@_change_case_math_loop:nnnNw {#1} {#2} {#3} #6
            }
       }
-      { \@@_change_case_math_search:nnNNN {#1} {#2} #3 }
+      { \@@_change_case_math_search:nnnNNN {#1} {#2} {#3} #4 }
   }
-\cs_new:Npn \@@_change_case_math_loop:nnNw #1#2#3#4 \q_@@_recursion_stop
+\cs_new:Npn \@@_change_case_math_loop:nnnNw #1#2#3#4#5 \q_@@_recursion_stop
   {
-    \tl_if_head_is_N_type:nTF {#4}
-      { \@@_change_case_math_N_type:nnNN }
+    \tl_if_head_is_N_type:nTF {#5}
+      { \@@_change_case_math_N_type:nnnNN }
       {
-        \tl_if_head_is_group:nTF {#4}
-          { \@@_change_case_math_group:nnNn }
-          { \@@_change_case_math_space:nnNw }
+        \tl_if_head_is_group:nTF {#5}
+          { \@@_change_case_math_group:nnnNn }
+          { \@@_change_case_math_space:nnnNw }
       }
-    {#1} {#2} #3 #4 \q_@@_recursion_stop
+    {#1} {#2} {#3} #4 #5 \q_@@_recursion_stop
   }
-\cs_new:Npn \@@_change_case_math_N_type:nnNN #1#2#3#4
+\cs_new:Npn \@@_change_case_math_N_type:nnnNN #1#2#3#4#5
   {
-    \@@_if_q_recursion_tail_stop_do:Nn #4
+    \@@_if_q_recursion_tail_stop_do:Nn #5
       { \@@_change_case_end:w }
-    \@@_change_case_store:n {#4}
-    \token_if_eq_meaning:NNTF #4 #3
-      { \@@_change_case_loop:nnw {#1} {#2} }
-      { \@@_change_case_math_loop:nnNw {#1} {#2} #3 }
+    \@@_change_case_store:n {#5}
+    \token_if_eq_meaning:NNTF #5 #4
+      { \@@_change_case_loop:nnnw {#1} {#2} {#3} }
+      { \@@_change_case_math_loop:nnnNw {#1} {#2} {#3} #4 }
   }
-\cs_new:Npn \@@_change_case_math_group:nnNn #1#2#3#4
+\cs_new:Npn \@@_change_case_math_group:nnnNn #1#2#3#4#5
   {
-    \@@_change_case_store:n { {#4} }
-    \@@_change_case_math_loop:nnNw {#1} {#2} #3
+    \@@_change_case_store:n { {#5} }
+    \@@_change_case_math_loop:nnnNw {#1} {#2} {#3} #4
   }
 \use:e
   {
-    \cs_new:Npn \exp_not:N \@@_change_case_math_space:nnNw #1#2#3
+    \cs_new:Npn \exp_not:N \@@_change_case_math_space:nnnNw #1#2#3#4
       \c_space_tl
   }
   {
     \@@_change_case_store:n { ~ }
-    \@@_change_case_math_loop:nnNw {#1} {#2} #3
+    \@@_change_case_math_loop:nnnNw {#1} {#2} {#3} #4
   }
 %    \end{macrocode}
 %   Once potential math-mode cases are filtered out the next stage is to
@@ -441,15 +444,15 @@
 %   test if the token grabbed is a control sequence: the two routes the code
 %   may take are then very different.
 %    \begin{macrocode}
-\cs_new:Npn \@@_change_case_cs_check:nnN #1#2#3
+\cs_new:Npn \@@_change_case_cs_check:nnnN #1#2#3#4
   {
-    \token_if_cs:NTF #3
-      { \@@_change_case_exclude:nnN {#1} {#2} }
+    \token_if_cs:NTF #4
+      { \@@_change_case_exclude:nnnN {#1} {#2} {#3} }
       {
         \@@_codepoint_process:nN
-          { \use:c { @@_change_case_custom_ #1 :nnn } {#1} {#2} }
+          { \use:c { @@_change_case_custom_ #1 :nnnn } {#1} {#2} {#3} }
       }
-        #3
+        #4
   }
 %    \end{macrocode}
 %   To deal with a control sequence there is first a need to test if it is
@@ -457,92 +460,115 @@
 %   done using a loop as for the other special cases. If a hit is found then
 %   the argument is grabbed and passed through as-is.
 %    \begin{macrocode}
-\cs_new:Npn \@@_change_case_exclude:nnN #1#2#3
+\cs_new:Npn \@@_change_case_exclude:nnnN #1#2#3#4
   {
-    \exp_args:Ne \@@_change_case_exclude:nnnN
+    \exp_args:Ne \@@_change_case_exclude:nnnnN
       {
         \exp_not:V \l_text_math_arg_tl
         \exp_not:V \l_text_case_exclude_arg_tl
       }
-      {#1} {#2} #3
+      {#1} {#2} {#3} #4
   }
-\cs_new:Npn \@@_change_case_exclude:nnnN #1#2#3#4
+\cs_new:Npn \@@_change_case_exclude:nnnnN #1#2#3#4#5
   {
-    \@@_change_case_exclude:nnNN {#2} {#3} #4 #1 
+    \@@_change_case_exclude:nnnNN {#2} {#3} {#4} #5 #1 
       \q_@@_recursion_tail \q_@@_recursion_stop
   }
-\cs_new:Npn \@@_change_case_exclude:nnNN #1#2#3#4
+\cs_new:Npn \@@_change_case_exclude:nnnNN #1#2#3#4#5
   {
-    \@@_if_q_recursion_tail_stop_do:Nn #4
-      { \@@_change_case_replace:nnN {#1} {#2} #3 }
-    \str_if_eq:nnTF {#3} {#4}
+    \@@_if_q_recursion_tail_stop_do:Nn #5
+      { \@@_change_case_replace:nnnN {#1} {#2} {#3} #4 }
+    \str_if_eq:nnTF {#4} {#5}
       {
         \@@_use_i_delimit_by_q_recursion_stop:nw
-          { \@@_change_case_exclude:nnNw {#1} {#2} #3 }
+          { \@@_change_case_exclude:nnnNw {#1} {#2} {#3} #4 }
       }
-      { \@@_change_case_exclude:nnNN {#1} {#2} #3 }
+      { \@@_change_case_exclude:nnnNN {#1} {#2} {#3} #4 }
   }
-\cs_new:Npn \@@_change_case_exclude:nnNw #1#2#3#4#
-  { \@@_change_case_exclude:nnNnn {#1} {#2} {#3} {#4} }
-\cs_new:Npn \@@_change_case_exclude:nnNnn #1#2#3#4#5
+\cs_new:Npn \@@_change_case_exclude:nnnNw #1#2#3#4#5#
+  { \@@_change_case_exclude:nnnNnn {#1} {#2} {#3} {#4} {#5} }
+\cs_new:Npn \@@_change_case_exclude:nnnNnn #1#2#3#4#5#6
   {
-    \tl_if_blank:nTF {#4}
-       { \@@_change_case_store:n { #3 {#5} } }
+    \tl_if_blank:nTF {#5}
+       { \@@_change_case_store:n { #4 {#6} } }
        {
         \@@_change_case_store:o
           {
-            \exp_after:wN #3
-              \exp:w \@@_change_case_auxii:nnn {#4} {#1} {#2}
-              {#5}
+            \exp_after:wN #4
+              \exp:w \@@_change_case_auxii:nnnn {#5} {#1} {#2} {#3}
+              {#6}
           }
       }
-    \@@_change_case_loop:nnw {#1} {#2}
+    \@@_change_case_loop:nnnw {#1} {#2} {#3}
   }
 %    \end{macrocode}
 %   Deal with any specialist replacement for case changing.
 %    \begin{macrocode}
-\cs_new:Npn \@@_change_case_replace:nnN #1#2#3
+\cs_new:Npn \@@_change_case_replace:nnnN #1#2#3#4
   {
-    \cs_if_exist:cTF { l_@@_case_ \token_to_str:N #3 _tl }
+    \cs_if_exist:cTF { l_@@_case_ \token_to_str:N #4 _tl }
       {
-        \@@_change_case_replace:vnn
-          { l_@@_case_ \token_to_str:N #3 _tl } {#1} {#2}
+        \@@_change_case_replace:vnnn
+          { l_@@_case_ \token_to_str:N #4 _tl } {#1} {#2} {#3}
       }
-      { \@@_change_case_switch:nnN {#1} {#2} #3 }
+      { \@@_change_case_switch:nnnN {#1} {#2} {#3} #4 }
   }
-\cs_new:Npn \@@_change_case_replace:nnn #1#2#3
-  { \@@_change_case_loop:nnw {#2} {#3} #1 }
-\cs_generate_variant:Nn \@@_change_case_replace:nnn { v }
+\cs_new:Npn \@@_change_case_replace:nnnn #1#2#3#4
+  { \@@_change_case_loop:nnnw {#2} {#3} {#4} #1 }
+\cs_generate_variant:Nn \@@_change_case_replace:nnnn { v }
 %    \end{macrocode}
 %   Allow for manually-controlled case switching.
 %    \begin{macrocode}
-\cs_new:Npn \@@_change_case_switch:nnN #1#2#3
+\cs_new:Npn \@@_change_case_switch:nnnN #1#2#3#4
   {
-    \cs_if_eq:NNTF #3 \text_case_switch:nnnn
-      { \use:c { @@_change_case_switch_ #1 :nnNnnnn  } }
-      { \use:c { @@_change_case_letterlike_ #1 :nnN } }
-        {#1} {#2} #3
+    \cs_if_eq:NNTF #4 \text_case_switch:nnnn
+      { \use:c { @@_change_case_switch_ #1 :nnnNnnnn  } }
+      { \use:c { @@_change_case_letterlike_ #1 :nnnN } }
+        {#1} {#2} {#3} #4
   }
-\cs_new:Npn \@@_change_case_switch_lower:nnNnnnn #1#2#3#4#5#6#7
+\cs_new:Npn \@@_change_case_switch_lower:nnnNnnnn #1#2#3#4#5#6#7#8
   {
+    \@@_change_case_store:n {#7}
+    \@@_change_case_loop:nnnw {#1} {#2} {#3}
+  }
+\cs_new:Npn \@@_change_case_switch_upper:nnnNnnnn #1#2#3#4#5#6#7#8
+  {
     \@@_change_case_store:n {#6}
-    \@@_change_case_loop:nnw {#1} {#2}
+    \@@_change_case_loop:nnnw {#1} {#2} {#3}
   }
-\cs_new:Npn \@@_change_case_switch_upper:nnNnnnn #1#2#3#4#5#6#7
+\cs_new:Npn \@@_change_case_switch_title:nnnNnnnn #1#2#3#4#5#6#7#8
   {
-    \@@_change_case_store:n {#5}
-    \@@_change_case_loop:nnw {#1} {#2}
+    \@@_change_case_store:n {#8}
+    \@@_change_case_skip:nnw {#2} {#3}
   }
-\cs_new:Npn \@@_change_case_switch_title:nnNnnnn #1#2#3#4#5#6#7
+%    \end{macrocode}
+%   Skip over material quickly after titlecase-first-only initials
+%    \begin{macrocode}
+\cs_new:Npn \@@_change_case_skip:nnw #1#2#3 \q_@@_recursion_stop
   {
-    \@@_change_case_store:n {#7}
-    \@@_change_case_loop:nnw {#1} {#2}
+    \tl_if_head_is_N_type:nTF {#3}
+      { \@@_change_case_skip_N_type:nnN }
+      {
+        \tl_if_head_is_group:nTF {#3}
+          { \@@_change_case_skip_group:nnn }
+          { \@@_change_case_skip_space:nnw }
+      }
+        {#1} {#2} #3 \q_@@_recursion_stop
   }
-\cs_new:Npn \@@_change_case_switch_titleonly:nnNnnnn #1#2#3#4#5#6#7
+\cs_new:Npn \@@_change_case_skip_N_type:nnN #1#2#3
   {
-    \@@_change_case_store:n {#7}
-    \@@_change_case_break:w
+    \@@_if_q_recursion_tail_stop_do:Nn #3
+      { \@@_change_case_end:w }
+    \@@_change_case_store:n {#3}
+    \@@_change_case_skip:nnw {#1} {#2}
   }
+\cs_new:Npn \@@_change_case_skip_group:nnn #1#2#3
+  {
+    \@@_change_case_store:n { {#3} }
+    \@@_change_case_skip:nnw {#1} {#2}
+  }
+\cs_new:Npn \@@_change_case_skip_space:nnw #1#2
+  { \@@_change_case_space:nnnw {#1} {#1} {#2} }
 %    \end{macrocode}
 %  Letter-like commands may still be present: they are set up using a simple
 %  lookup approach, so can easily be handled with no loop. If there is no
@@ -550,68 +576,64 @@
 %  are all available only in upper- and lowercase, so titlecasing maps to the
 %  uppercase version.
 %    \begin{macrocode}
-\cs_new:Npn \@@_change_case_letterlike_lower:nnN #1#2#3
-  { \@@_change_case_letterlike:nnnnN {#1} {#1} {#1} {#2} #3 }
-\cs_new_eq:NN \@@_change_case_letterlike_upper:nnN
-  \@@_change_case_letterlike_lower:nnN
-\cs_new:Npn \@@_change_case_letterlike_title:nnN #1#2#3
-  { \@@_change_case_letterlike:nnnnN { upper } { lower } {#1} {#2} #3 }
-\cs_new:Npn \@@_change_case_letterlike_titleonly:nnN #1#2#3
-  { \@@_change_case_letterlike:nnnnN { upper } { end } {#1} {#2} #3 }
-\cs_new:Npn \@@_change_case_letterlike:nnnnN #1#2#3#4#5
+\cs_new:Npn \@@_change_case_letterlike_lower:nnnN #1#2#3#4
+  { \@@_change_case_letterlike:nnnnnN {#1} {#1} {#1} {#2} {#3} #4 }
+\cs_new_eq:NN \@@_change_case_letterlike_upper:nnnN
+  \@@_change_case_letterlike_lower:nnnN
+\cs_new:Npn \@@_change_case_letterlike_title:nnnN #1#2#3#4
+  { \@@_change_case_letterlike:nnnnnN { upper } { end } {#1} {#2} {#3} #4 }
+\cs_new:Npn \@@_change_case_letterlike:nnnnnN #1#2#3#4#5#6
   {
-    \cs_if_exist:cTF { c_@@_ #1 case_ \token_to_str:N #5 _tl }
+    \cs_if_exist:cTF { c_@@_ #1 case_ \token_to_str:N #6 _tl }
       {
         \@@_change_case_store:v
-          { c_@@_ #1 case_ \token_to_str:N #5 _tl }
-         \use:c { @@_change_case_next_ #2 :nn } {#2} {#4}
+          { c_@@_ #1 case_ \token_to_str:N #6 _tl }
+         \use:c { @@_change_case_next_ #2 :nnn } {#2} {#4} {#5}
       }
       {
-        \@@_change_case_store:n {#5}
+        \@@_change_case_store:n {#6}
         \cs_if_exist:cTF
           {
             c_@@_
             \str_if_eq:nnTF {#1} { lower } { upper } { lower }
-            case_ \token_to_str:N #5 _tl
+            case_ \token_to_str:N #6 _tl
           }
-          { \use:c { @@_change_case_next_ #2 :nn } {#2} {#4} }
-          { \@@_change_case_loop:nnw {#3} {#4} }
+          { \use:c { @@_change_case_next_ #2 :nnn } {#2} {#4} {#5} }
+          { \@@_change_case_loop:nnnw {#3} {#4} {#5} }
       }
   }
 %    \end{macrocode}
 %  Check for a customised codepoint result.
 %    \begin{macrocode}
-\cs_new:Npn \@@_change_case_custom_lower:nnn #1#2#3
+\cs_new:Npn \@@_change_case_custom_lower:nnnn #1#2#3#4
   {
-    \@@_change_case_custom:nnnnn {#1} {#2} {#3} {#1}
-      { \use:c { @@_change_case_codepoint_ #1 :nnn } {#1} {#2} {#3} }
+    \@@_change_case_custom:nnnnnn {#1} {#1} {#2} {#3} {#4}
+      { \use:c { @@_change_case_codepoint_ #1 :nnnn } {#1} {#2} {#3} {#4} }
   }
-\cs_new_eq:NN \@@_change_case_custom_upper:nnn
-  \@@_change_case_custom_lower:nnn
-\cs_new:Npn \@@_change_case_custom_title:nnn #1#2#3
+\cs_new_eq:NN \@@_change_case_custom_upper:nnnn
+  \@@_change_case_custom_lower:nnnn
+\cs_new:Npn \@@_change_case_custom_title:nnnn #1#2#3#4
   {
-    \@@_change_case_custom:nnnnn { title } {#2} {#3} {#1}
+    \@@_change_case_custom:nnnnnn { title } {#1} {#2} {#3} {#4}
       {
-        \@@_change_case_custom:nnnnn { upper } {#2} {#3} {#1}
-          { \use:c { @@_change_case_codepoint_ #1 :nnn } {#1} {#2} {#3} }
+        \@@_change_case_custom:nnnnnn { upper } {#1} {#2} {#3} {#4}
+          { \use:c { @@_change_case_codepoint_ #1 :nnnn } {#1} {#2} {#3} {#4} }
       }
   }
-\cs_new_eq:NN \@@_change_case_custom_titleonly:nnn
-  \@@_change_case_custom_title:nnn
-\cs_new:Npn \@@_change_case_custom:nnnnn #1#2#3#4#5
+\cs_new:Npn \@@_change_case_custom:nnnnnn #1#2#3#4#5#6
   {
-    \tl_if_exist:cTF { l_@@_ #1 case _ \tl_to_str:n {#3} _ #2 _tl }
+    \tl_if_exist:cTF { l_@@_ #1 case _ \tl_to_str:n {#5} _ #4 _tl }
       {
-        \@@_change_case_replace:vnn
-          { l_@@_ #1 case _ \tl_to_str:n {#3} _ #2 _tl } {#4} {#2}
+        \@@_change_case_replace:vnnn
+          { l_@@_ #1 case _ \tl_to_str:n {#5} _ #4 _tl } {#2} {#3} {#4}
       }
       {
-        \tl_if_exist:cTF { l_@@_ #1 case _ \tl_to_str:n {#3} _tl }
+        \tl_if_exist:cTF { l_@@_ #1 case _ \tl_to_str:n {#5} _tl }
           {
-            \@@_change_case_replace:vnn
-              { l_@@_ #1 case _ \tl_to_str:n {#3} _tl } {#4} {#2}
+            \@@_change_case_replace:vnnn
+              { l_@@_ #1 case _ \tl_to_str:n {#5} _tl } {#2} {#3} {#4}
           }
-          {#5}
+          {#6}
       }
   }
 %    \end{macrocode}
@@ -620,17 +642,17 @@
 %   is there the special case of a terminal sigma. If not, then we pass to
 %   a simple codepoint mapping.
 %    \begin{macrocode}
-\cs_new:Npn \@@_change_case_codepoint_lower:nnn #1#2#3
+\cs_new:Npn \@@_change_case_codepoint_lower:nnnn #1#2#3#4
   {
-    \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_ #3 :nnnnn }
+      { \@@_change_case_lower_sigma:nnnnn }
+        {#1} {#1} {#2} {#3} {#4}
   }
-\cs_new:Npn \@@_change_case_codepoint_upper:nnn #1#2#3
+\cs_new:Npn \@@_change_case_codepoint_upper:nnnn #1#2#3#4
   {
-    \cs_if_exist_use:cF { @@_change_case_upper_ #2 :nnnn }
-      { \@@_change_case_codepoint:nnnn }
-        {#1} {#1} {#2} {#3}
+    \cs_if_exist_use:cF { @@_change_case_upper_ #3 :nnnnn }
+      { \@@_change_case_codepoint:nnnnn }
+        {#1} {#1} {#2} {#3} {#4}
   }
 %    \end{macrocode}
 %   If the current character is an uppercase sigma, the a check is made on the
@@ -638,91 +660,87 @@
 %   then there is a look-ahead phase: the logic here is simply based on letters
 %   or actives (to cover $8$-bit engines).
 %    \begin{macrocode}
-\cs_new:Npn \@@_change_case_lower_sigma:nnnn #1#2#3#4
+\cs_new:Npn \@@_change_case_lower_sigma:nnnnn #1#2#3#4#5
   {
-    \@@_codepoint_compare:nNnTF {#4} = { "03A3 }
-      { \@@_change_case_lower_sigma:nnnw {#2} }
-      { \@@_change_case_codepoint:nnnn {#1} {#2} }
-        {#3} {#4}
+    \@@_codepoint_compare:nNnTF {#5} = { "03A3 }
+      { \@@_change_case_lower_sigma:nnnnw {#2} }
+      { \@@_change_case_codepoint:nnnnn {#1} {#2} }
+        {#3} {#4} {#5}
   }
-\cs_new:Npn \@@_change_case_lower_sigma:nnnw #1#2#3#4 \q_@@_recursion_stop
+\cs_new:Npn \@@_change_case_lower_sigma:nnnnw #1#2#3#4#5 \q_@@_recursion_stop
   {
-    \tl_if_head_is_N_type:nTF {#4}
-      { \@@_change_case_lower_sigma:nnnN {#3} }
+    \tl_if_head_is_N_type:nTF {#5}
+      { \@@_change_case_lower_sigma:nnnnN {#4} }
       {
         \@@_change_case_store:e
-          { \codepoint_generate:nn { "03C2 } { \@@_char_catcode:N #3 } }
-        \@@_change_case_loop:nnw
+          { \codepoint_generate:nn { "03C2 } { \@@_char_catcode:N #4 } }
+        \@@_change_case_loop:nnnw
       }
-        {#1} {#2} #4 \q_@@_recursion_stop
+        {#1} {#2} {#3} #5 \q_@@_recursion_stop
   }
-\cs_new:Npn \@@_change_case_lower_sigma:nnnN #1#2#3#4
+\cs_new:Npn \@@_change_case_lower_sigma:nnnnN #1#2#3#4#5
   {
     \@@_change_case_store:e
       {
         \bool_lazy_or:nnTF
-          { \token_if_letter_p:N #4 }
+          { \token_if_letter_p:N #5 }
           {
             \bool_lazy_and_p:nn
-              { \token_if_active_p:N #4 }
-              { \int_compare_p:nNn {`#4} > { "80 } }
+              { \token_if_active_p:N #5 }
+              { \int_compare_p:nNn {`#5} > { "80 } }
           }
           { \codepoint_generate:nn { "03C3 } { \@@_char_catcode:N #1 } }
           { \codepoint_generate:nn { "03C2 } { \@@_char_catcode:N #1 } }
       }
-    \@@_change_case_loop:nnw {#2} {#3} #4
+    \@@_change_case_loop:nnnw {#2} {#3} {#4} #5
   }
 %    \end{macrocode}
 %   For titlecasing, we need to fully expand the new character to see if it
 %   is a letter (or active).
 %    \begin{macrocode}
-\cs_new:Npn \@@_change_case_codepoint_title:nnn #1#2#3
+\cs_new:Npn \@@_change_case_codepoint_title:nnnn #1#2#3#4
   {
     \bool_if:NTF \l_text_titlecase_check_letter_bool
       {
-        \tl_if_single:nTF {#3}
+        \tl_if_single:nTF {#4}
           {
             \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 } }
               }
-              { \use:c { @@_change_case_codepoint_ #1 :nn } }
-              { \@@_change_case_codepoint_title:nnnn { title } {#1} }
+              { \@@_change_case_codepoint_title:nnn }
+              { \@@_change_case_codepoint_title:nnnnn { title } {#1} }
           }
-          { \use:c { @@_change_case_codepoint_ #1 :nn } }
+          { \@@_change_case_codepoint_title:nnn }
       }
-      { \use:c { @@_change_case_codepoint_ #1 :nn } }
-        {#2} {#3}
+      { \@@_change_case_codepoint_title:nnn }
+        {#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_new:Npn \@@_change_case_codepoint_title:nnn #1#2#3
+  { \@@_change_case_codepoint_title:nnnnn { title } { end } {#1} {#2} {#3} }
+\cs_new:Npn \@@_change_case_codepoint_title:nnnnn #1#2#3#4#5
   {
-    \cs_if_exist_use:cF { @@_change_case_title_ #3 :nnnn }
+    \cs_if_exist_use:cF { @@_change_case_title_ #4 :nnnnn }
       {
-        \cs_if_exist_use:cF { @@_change_case_upper_ #3 :nnnn }
-          { \@@_change_case_codepoint:nnnn }
+        \cs_if_exist_use:cF { @@_change_case_upper_ #4 :nnnnn }
+          { \@@_change_case_codepoint:nnnnn }
       }
-        {#1} {#2} {#3} {#4}
+        {#1} {#2} {#3} {#4} {#5}
   }
-\cs_new:Npn \@@_change_case_codepoint:nnnn #1#2#3#4
+\cs_new:Npn \@@_change_case_codepoint:nnnnn #1#2#3#4#5
   {
     \bool_lazy_and:nnTF
-      { \tl_if_single_p:n {#4} }
-      { \token_if_active_p:N #4 }
-      { \@@_change_case_store:n {#4} }
+      { \tl_if_single_p:n {#5} }
+      { \token_if_active_p:N #5 }
+      { \@@_change_case_store:n {#5} }
       {
         \@@_change_case_store:e
-          { \@@_change_case_codepoint:nn {#1} {#4} }
+          { \@@_change_case_codepoint:nn {#1} {#5} }
       }
-    \use:c { @@_change_case_next_ #2 :nn } {#2} {#3}
+    \use:c { @@_change_case_next_ #2 :nnn } {#2} {#3} {#4}
   }
 \cs_new:Npn \@@_change_case_codepoint:nn #1#2
   {
@@ -799,16 +817,14 @@
           }
       }
   }
-\cs_new:Npn \@@_change_case_next_lower:nn #1#2
-  { \@@_change_case_loop:nnw {#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 }
+\cs_new:Npn \@@_change_case_next_lower:nnn #1#2#3
+  { \@@_change_case_loop:nnnw {#1} {#2} {#3} }
+\cs_new_eq:NN \@@_change_case_next_upper:nnn
+  \@@_change_case_next_lower:nnn
+\cs_new_eq:NN \@@_change_case_next_title:nnn
+  \@@_change_case_next_lower:nnn
+\cs_new:Npn \@@_change_case_next_end:nnn #1#2#3
+  { \@@_change_case_skip:nnw {#2} {#3} }
 %    \end{macrocode}
 % \end{macro}
 % \end{macro}
@@ -853,6 +869,13 @@
 % \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.
@@ -914,6 +937,7 @@
   {
     \tl_clear_new:c { l_@@_ #2 case _ #1 _ #3 _tl }
     \tl_set:cn { l_@@_ #2 case _ #1 _ #3 _ tl } {#4}
+    \tl_clear_new:c { l_@@_ #2 case_special_ #3 _tl }
   }
 %    \end{macrocode}
 % \end{macro}
@@ -944,46 +968,49 @@
 % \end{macro}
 %
 % \begin{macro}[EXP]
-%   {\@@_change_case_upper_de-x-eszett:nnnn, \@@_change_case_upper_de-alt:nnnn}
+%   {
+%      \@@_change_case_upper_de-x-eszett:nnnnn,
+%      \@@_change_case_upper_de-alt:nnnnn
+%   }
 %   A simple alternative version for German.
 %    \begin{macrocode}
-\cs_new:cpn { @@_change_case_upper_de-x-eszett:nnnn } #1#2#3#4
+\cs_new:cpn { @@_change_case_upper_de-x-eszett:nnnnn } #1#2#3#4#5
   {
-    \@@_codepoint_compare:nNnTF {#4} = { "00DF }
+    \@@_codepoint_compare:nNnTF {#5} = { "00DF }
       {
         \@@_change_case_store:e
          {
            \codepoint_generate:nn { "1E9E }
-             { \@@_change_case_catcode:nn {#4} { "1E9E } }
+             { \@@_change_case_catcode:nn {#5} { "1E9E } }
          }
-        \use:c { @@_change_case_next_ #2 :nn }
-          {#2} {#3}
+        \use:c { @@_change_case_next_ #2 :nnn }
+          {#2} {#3} {#4}
       }
-      { \@@_change_case_codepoint:nnnn {#1} {#2} {#3} {#4} }
+      { \@@_change_case_codepoint:nnnnn {#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:nnnnn }
+  { @@_change_case_upper_de-x-eszett:nnnnn }
 %    \end{macrocode}
 % \end{macro}
 %
 % \begin{macro}[EXP]
 %   {
-%     \@@_change_case_upper_el:nnnn     ,
-%     \@@_change_case_upper_el_aux:nnnn ,
-%     \@@_change_case_upper_el-x-iota:nnnn
+%     \@@_change_case_upper_el:nnnnn        ,
+%     \@@_change_case_upper_el-x-iota:nnnnn ,
+%     \@@_change_case_upper_el_aux:nnnnn
 %   }
-% \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:nnnnw}
 % \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}
+%   {\@@_change_case_upper_el:nnnnN, \@@_change_case_upper_el_aux:nnnnN}
+% \begin{macro}[EXP]{\@@_change_case_upper_el_ypogegrammeni:nnnnnnw}
+% \begin{macro}[EXP]{\@@_change_case_upper_el_ypogegrammeni:nnnnnnN}
+% \begin{macro}[EXP]{\@@_change_case_upper_el_ypogegrammeni:nnnnnnn}
+% \begin{macro}[EXP]{\@@_change_case_upper_el_dialytika:nnnn}
 % \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:nnnN}
-% \begin{macro}[EXP]{\@@_change_case_upper_el_hiatus:nnnn}
+% \begin{macro}[EXP]{\@@_change_case_upper_el_hiatus:nnnnw}
+% \begin{macro}[EXP]{\@@_change_case_upper_el_hiatus:nnnnN}
+% \begin{macro}[EXP]{\@@_change_case_upper_el_hiatus:nnnnn}
 % \begin{macro}[EXP]
 %   {
 %     \@@_change_case_upper_el_ypogegrammeni:n        ,
@@ -990,9 +1017,9 @@
 %     \@@_change_case_upper_el-x-iota_ypogegrammeni:n
 %   }
 % \begin{macro}[EXP]{\@@_change_case_upper_el_stress:nn}
-% \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_gobble:nnn}
+% \begin{macro}[EXP]{\@@_change_case_upper_el_gobble:nnnw}
+% \begin{macro}[EXP]{\@@_change_case_upper_el_gobble:nnnN}
+% \begin{macro}[EXP]{\@@_change_case_upper_el_gobble:nnnn}
 % \begin{macro}[EXP,noTF]
 %   {
 %     \@@_change_case_if_greek:n                   ,
@@ -1017,32 +1044,32 @@
 %   for \pdfTeX{} so is best left unchanged, and the latter has issues concerning
 %   how \texttt{LGR} outputs the input and output (differently!).
 %    \begin{macrocode}
-\cs_new:Npn \@@_change_case_upper_el:nnnn #1#2#3#4
+\cs_new:Npn \@@_change_case_upper_el:nnnnn #1#2#3#4#5
   {
     \bool_lazy_and:nnTF
-      { \@@_change_case_if_greek_p:n {#4} }
+      { \@@_change_case_if_greek_p:n {#5} }
       {
         ! \bool_lazy_or_p:nn
-          { \@@_codepoint_compare_p:nNn {#4} = { "0374 } }
-          { \@@_codepoint_compare_p:nNn {#4} = { "037E } }
+          { \@@_codepoint_compare_p:nNn {#5} = { "0374 } }
+          { \@@_codepoint_compare_p:nNn {#5} = { "037E } }
       }
       {
-        \@@_change_case_if_greek_spacing_diacritic:nTF {#4}
+        \@@_change_case_if_greek_spacing_diacritic:nTF {#5}
           {
-            \@@_change_case_store:n {#4}
-            \@@_change_case_loop:nnw
+            \@@_change_case_store:n {#5}
+            \@@_change_case_loop:nnnw
           }
           {
-            \exp_args:Ne \@@_change_case_upper_el:nnn
+            \exp_args:Ne \@@_change_case_upper_el:nnnn
               {
                 \codepoint_to_nfd:n
-                  { \@@_codepoint_from_chars:Nw #4 }
+                  { \@@_codepoint_from_chars:Nw #5 }
               }
           }
-            {#2} {#3}
+            {#2} {#3} {#4}
       }
       {
-        \@@_codepoint_compare:nNnTF {#4} = { "0345 }
+        \@@_codepoint_compare:nNnTF {#5} = { "0345 }
           {
             \@@_change_case_store:e
               {
@@ -1049,17 +1076,17 @@
                 \codepoint_generate:nn { "0399 }
                   { \char_value_catcode:n { "0399 } }
               }
-            \@@_change_case_loop:nnw {#2} {#3}
+            \@@_change_case_loop:nnnw {#2} {#3} {#4}
           }
-          { \@@_change_case_codepoint:nnnn {#1} {#2} {#3} {#4} }
+          { \@@_change_case_codepoint:nnnnn {#1} {#2} {#3} {#4} {#5} }
       }
   }
-\cs_new_eq:cN { @@_change_case_upper_el-x-iota:nnnn }
-  \@@_change_case_upper_el:nnnn
-\cs_new:Npn \@@_change_case_upper_el:nnn #1#2#3
+\cs_new_eq:cN { @@_change_case_upper_el-x-iota:nnnnn }
+  \@@_change_case_upper_el:nnnnn
+\cs_new:Npn \@@_change_case_upper_el:nnnn #1#2#3#4
   {
     \@@_codepoint_process:nN
-      { \@@_change_case_upper_el:nnnw {#2} {#3} } #1
+      { \@@_change_case_upper_el:nnnnw {#2} {#3} {#4} } #1
   }
 %    \end{macrocode}
 %   At this stage we have the first NFD codepoint as |#3|. What we need to know
@@ -1066,16 +1093,16 @@
 %   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:nnnnw #1#2#3#4#5 \q_@@_recursion_stop
   {
-    \tl_if_head_is_N_type:nTF {#4}
-      { \@@_change_case_upper_el:nnnN {#3} }
+    \tl_if_head_is_N_type:nTF {#5}
+      { \@@_change_case_upper_el:nnnnN {#4} }
       {
         \@@_change_case_store:e
-          { \@@_change_case_codepoint:nn { upper } {#3} }
-        \@@_change_case_loop:nnw
+          { \@@_change_case_codepoint:nn { upper } {#4} }
+        \@@_change_case_loop:nnnw
       }
-        {#1} {#2} #4 \q_@@_recursion_stop
+        {#1} {#2} {#3} #5 \q_@@_recursion_stop
   }
 %    \end{macrocode}
 %   Now, we check the detail of the next codepoint: again we filter out the
@@ -1085,99 +1112,99 @@
 %   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:nnnnN #1#2#3#4#5
   {
-    \token_if_cs:NTF #4
+    \token_if_cs:NTF #5
       {
         \@@_change_case_store:e
           { \@@_change_case_codepoint:nn { upper } {#1} }
-        \@@_change_case_loop:nnw {#2} {#3} #4
+        \@@_change_case_loop:nnnw {#2} {#3} {#4} #5
       }
       {
         \@@_change_case_if_takes_ypogegrammeni:nTF {#1}
           {
-            \@@_change_case_upper_el_ypogegrammeni:nnnnnw
-              {#1} {#2} {#3} { } { } #4
+            \@@_change_case_upper_el_ypogegrammeni:nnnnnnw
+              {#1} {#2} {#3} {#4} { } { } #5
           }
-          { \@@_change_case_upper_el_aux:nnnN {#1} {#2} {#3} #4 }
+          { \@@_change_case_upper_el_aux:nnnnN {#1} {#2} {#3} {#4} #5 }
       }
   }
-\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:nnnnnnw
+  #1#2#3#4#5#6#7 \q_@@_recursion_stop
   {
-    \tl_if_head_is_N_type:nTF {#6}
+    \tl_if_head_is_N_type:nTF {#7}
       {
-        \@@_change_case_upper_el_ypogegrammeni:nnnnnN
-          {#1} {#2} {#3} {#4} {#5}
+        \@@_change_case_upper_el_ypogegrammeni:nnnnnnN
+          {#1} {#2} {#3} {#4} {#5} {#6}
       }
-      { \@@_change_case_upper_el_aux:nnnN {#1} {#2} {#3} #4#5 }
-        #6 \q_@@_recursion_stop
+      { \@@_change_case_upper_el_aux:nnnnN {#1} {#2} {#3} {#4} #5#6 }
+        #7 \q_@@_recursion_stop
   }
-\cs_new:Npn \@@_change_case_upper_el_ypogegrammeni:nnnnnN #1#2#3#4#5#6
+\cs_new:Npn \@@_change_case_upper_el_ypogegrammeni:nnnnnnN #1#2#3#4#5#6#7
   {
-    \token_if_cs:NTF #6
-      { \@@_change_case_upper_el_aux:nnnN {#1} {#2} {#3} #4#5 }
+    \token_if_cs:NTF #7
+      { \@@_change_case_upper_el_aux:nnnnN {#1} {#2} {#3} {#4} #5#6 }
       {
         \@@_codepoint_process:nN
           {
-            \@@_change_case_upper_el_ypogegrammeni:nnnnnn
-              {#1} {#2} {#3} {#4} {#5}
+            \@@_change_case_upper_el_ypogegrammeni:nnnnnnn
+              {#1} {#2} {#3} {#4} {#5} {#6}
           }
       }
-        #6
+        #7
   }
-\cs_new:Npn \@@_change_case_upper_el_ypogegrammeni:nnnnnn #1#2#3#4#5#6
+\cs_new:Npn \@@_change_case_upper_el_ypogegrammeni:nnnnnnn #1#2#3#4#5#6#7
   {
-    \@@_codepoint_compare:nNnTF {#6} = { "0345 }
+    \@@_codepoint_compare:nNnTF {#7} = { "0345 }
       {
-        \@@_change_case_upper_el_ypogegrammeni:nnnnnw
-          {#1} {#2} {#3} {#4} {#6}
+        \@@_change_case_upper_el_ypogegrammeni:nnnnnnw
+          {#1} {#2} {#3} {#4} {#5} {#7}
       }
       {
         \bool_lazy_or:nnTF
-          { \@@_change_case_if_greek_accent_p:n {#6} }
-          { \@@_change_case_if_greek_breathing_p:n {#6} }
+          { \@@_change_case_if_greek_accent_p:n {#7} }
+          { \@@_change_case_if_greek_breathing_p:n {#7} }
           {
-            \@@_change_case_upper_el_ypogegrammeni:nnnnnw
-              {#1} {#2} {#3} {#4#6} {#5}
+            \@@_change_case_upper_el_ypogegrammeni:nnnnnnw
+              {#1} {#2} {#3} {#4} {#5#7} {#6}
           }
-          { \@@_change_case_upper_el_aux:nnnN {#1} {#2} {#3} #4#5 #6 }
+          { \@@_change_case_upper_el_aux:nnnnN {#1} {#2} {#3} {#4} #5#6 #7 }
       }
   }
-\cs_new:Npn \@@_change_case_upper_el_aux:nnnN #1#2#3#4
+\cs_new:Npn \@@_change_case_upper_el_aux:nnnnN #1#2#3#4#5
   {
     \@@_codepoint_process:nN
-      { \@@_change_case_upper_el_aux:nnnn {#1} {#2} {#3} } #4
+      { \@@_change_case_upper_el_aux:nnnnn {#1} {#2} {#3} {#4} } #5
   }
-\cs_new:Npn \@@_change_case_upper_el_aux:nnnn #1#2#3#4
+\cs_new:Npn \@@_change_case_upper_el_aux:nnnnn #1#2#3#4#5
   {
-    \@@_codepoint_compare:nNnTF {#4} = { "0308 }
-      { \@@_change_case_upper_el_dialytika:nnn {#2} {#3} {#1} }
+    \@@_codepoint_compare:nNnTF {#5} = { "0308 }
+      { \@@_change_case_upper_el_dialytika:nnnn {#2} {#3} {#4} {#1} }
       {
-        \@@_change_case_if_greek_accent:nTF {#4}
-          { \@@_change_case_upper_el_hiatus:nnnw {#2} {#3} {#1} }
+        \@@_change_case_if_greek_accent:nTF {#5}
+          { \@@_change_case_upper_el_hiatus:nnnnw {#2} {#3} {#4} {#1} }
           {
-            \@@_change_case_if_greek_breathing:nTF {#4}
-              { \@@_change_case_upper_el:nnn {#1} {#2} {#3} }
+            \@@_change_case_if_greek_breathing:nTF {#5}
+              { \@@_change_case_upper_el:nnnn {#1} {#2} {#3} {#4} }
               {
-                \@@_codepoint_compare:nNnTF {#4} = { "0345 }
+                \@@_codepoint_compare:nNnTF {#5} = { "0345 }
                   {
                     \@@_change_case_store:e
-                      { \use:c { @@_change_case_upper_ #3 _ypogegrammeni:n } {#1} }
-                    \@@_change_case_loop:nnw {#2} {#3}
+                      { \use:c { @@_change_case_upper_ #4 _ypogegrammeni:n } {#1} }
+                    \@@_change_case_loop:nnnw {#2} {#3} {#4}
                   }
                   {
-                    \@@_change_case_if_greek_stress:nTF {#4}
+                    \@@_change_case_if_greek_stress:nTF {#5}
                       {
                         \@@_change_case_store:e
-                          { \@@_change_case_upper_el_stress:nn {#1} {#4} }
-                        \@@_change_case_loop:nnw {#2} {#3}
+                          { \@@_change_case_upper_el_stress:nn {#1} {#5} }
+                        \@@_change_case_loop:nnnw {#2} {#3} {#4}
 
                       }
                       {
                         \@@_change_case_store:e
                           { \@@_change_case_codepoint:nn { upper } {#1} }
-                        \@@_change_case_loop:nnw {#2} {#3} #4
+                        \@@_change_case_loop:nnnw {#2} {#3} {#4} #5
                       }
                   }
               }
@@ -1189,15 +1216,15 @@
 %   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:nnnn #1#2#3#4
   {
-    \@@_change_case_if_takes_dialytika:nTF {#3}
-      { \@@_change_case_upper_el_dialytika:n {#3} }
+    \@@_change_case_if_takes_dialytika:nTF {#4}
+      { \@@_change_case_upper_el_dialytika:n {#4} }
       {
         \@@_change_case_store:e
-          { \@@_change_case_codepoint:nn { upper } {#3} }
+          { \@@_change_case_codepoint:nn { upper } {#4} }
       }
-    \@@_change_case_upper_el_gobble:nnw {#1} {#2}
+    \@@_change_case_upper_el_gobble:nnnw {#1} {#2} {#3}
   }
 \cs_new:Npn \@@_change_case_upper_el_dialytika:n #1
   {
@@ -1220,41 +1247,41 @@
 %   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:nnnnw
+  #1#2#3#4#5 \q_@@_recursion_stop
   {
-    \tl_if_head_is_N_type:nTF {#4}
-      { \@@_change_case_upper_el_hiatus:nnnN {#3} }
+    \tl_if_head_is_N_type:nTF {#5}
+      { \@@_change_case_upper_el_hiatus:nnnnN {#4} }
       {
         \@@_change_case_store:e
-          { \@@_change_case_codepoint:nn { upper } {#3} }
-        \@@_change_case_loop:nnw
+          { \@@_change_case_codepoint:nn { upper } {#4} }
+        \@@_change_case_loop:nnnw
       }
-        {#1} {#2} #4 \q_@@_recursion_stop
+        {#1} {#2} {#3} #5 \q_@@_recursion_stop
   }
-\cs_new:Npn \@@_change_case_upper_el_hiatus:nnnN #1#2#3#4
+\cs_new:Npn \@@_change_case_upper_el_hiatus:nnnnN #1#2#3#4#5
   {
-    \token_if_cs:NTF #4
+    \token_if_cs:NTF #5
       {
         \@@_change_case_store:e
           { \@@_change_case_codepoint:nn { upper } {#1} }
-        \@@_change_case_loop:nnw {#2} {#3} #4
+        \@@_change_case_loop:nnnw {#2} {#3} {#4} #5 
       }
       {
         \@@_codepoint_process:nN
-          { \@@_change_case_upper_el_hiatus:nnnn {#1} {#2} {#3} } #4
+          { \@@_change_case_upper_el_hiatus:nnnnn {#1} {#2} {#3} {#4} } #5
       }
   }
-\cs_new:Npn \@@_change_case_upper_el_hiatus:nnnn #1#2#3#4
+\cs_new:Npn \@@_change_case_upper_el_hiatus:nnnnn #1#2#3#4#5
   {
-    \@@_change_case_if_takes_dialytika:nTF {#4}
+    \@@_change_case_if_takes_dialytika:nTF {#5}
       {
         \@@_change_case_store:e
           { \@@_change_case_codepoint:nn { upper } {#1} }
-        \@@_change_case_upper_el_dialytika:n {#4}
-        \@@_change_case_upper_el_gobble:nnw {#2} {#3}
+        \@@_change_case_upper_el_dialytika:n {#5}
+        \@@_change_case_upper_el_gobble:nnnw {#2} {#3} {#4}
       }
-      { \@@_change_case_upper_el:nnn {#1} {#2} {#3} #4 }
+      { \@@_change_case_upper_el:nnnn {#1} {#2} {#3} {#4} #5 }
   }
 %    \end{macrocode}
 %   Handling the \emph{ypogegrammeni} output depends on the selected approach
@@ -1323,31 +1350,31 @@
 %   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
+\cs_new:Npn \@@_change_case_upper_el_gobble:nnnw
+  #1#2#3#4 \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
+    \tl_if_head_is_N_type:nTF {#4}
+      { \@@_change_case_upper_el_gobble:nnnN }
+      { \@@_change_case_loop:nnnw }
+        {#1} {#2} {#3} #4 \q_@@_recursion_stop
   }
-\cs_new:Npn \@@_change_case_upper_el_gobble:nnN #1#2#3
+\cs_new:Npn \@@_change_case_upper_el_gobble:nnnN #1#2#3#4
   {
-    \token_if_cs:NTF #3
-      { \@@_change_case_loop:nnw {#1} {#2} }
+    \token_if_cs:NTF #4
+      { \@@_change_case_loop:nnnw {#1} {#2} {#3} }
       {
         \@@_codepoint_process:nN
-          { \@@_change_case_upper_el_gobble:nnn {#1} {#2} }
+          { \@@_change_case_upper_el_gobble:nnnn {#1} {#2} {#3} }
       }
-        #3
+        #4
   }
-\cs_new:Npn \@@_change_case_upper_el_gobble:nnn #1#2#3
+\cs_new:Npn \@@_change_case_upper_el_gobble:nnnn #1#2#3#4
   {
     \bool_lazy_or:nnTF
-      { \@@_change_case_if_greek_accent_p:n {#3} }
-      { \@@_change_case_if_greek_breathing_p:n {#3} }
-      { \@@_change_case_upper_el_gobble:nnw {#1} {#2} }
-      { \@@_change_case_loop:nnw {#1} {#2} #3 }
+      { \@@_change_case_if_greek_accent_p:n {#4} }
+      { \@@_change_case_if_greek_breathing_p:n {#4} }
+      { \@@_change_case_upper_el_gobble:nnnw {#1} {#2} {#3} }
+      { \@@_change_case_loop:nnnw {#1} {#2} {#3} #4 }
   }
 %    \end{macrocode}
 %   Luckily the Greek range is limited and clear.
@@ -1597,61 +1624,64 @@
 % \end{macro}
 % \end{macro}
 % \begin{macro}[EXP]
-%   {\@@_change_case_boundary_upper_el:Nnnw, \@@_change_case_boundary_upper_el-x-iota:Nnnw}
-% \begin{macro}[EXP]{\@@_change_case_boundary_upper_el:nnN}
-% \begin{macro}[EXP]{\@@_change_case_boundary_upper_el:nnn}
-% \begin{macro}[EXP]{\@@_change_case_boundary_upper_el:nnnw}
+%   {
+%     \@@_change_case_boundary_upper_el:Nnnnw,
+%     \@@_change_case_boundary_upper_el-x-iota:Nnnnw
+%   }
+% \begin{macro}[EXP]{\@@_change_case_boundary_upper_el:nnnN}
+% \begin{macro}[EXP]{\@@_change_case_boundary_upper_el:nnnn}
+% \begin{macro}[EXP]{\@@_change_case_boundary_upper_el:nnnnw}
 %   There is one things that need special treatment at start start of
 %   words in Greek. For an isolated accent \emph{eta},
 %   which is handled by seeing if we have exactly one of the affected
 %   codepoints followed by a space or brace group.
 %    \begin{macrocode}
-\cs_new:Npn \@@_change_case_boundary_upper_el:Nnnw
-  #1#2#3#4 \q_@@_recursion_stop
+\cs_new:Npn \@@_change_case_boundary_upper_el:Nnnnw
+  #1#2#3#4#5 \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
+    \tl_if_head_is_N_type:nTF {#5}
+      { \@@_change_case_boundary_upper_el:nnnN }
+      { \@@_change_case_loop:nnnw }
+        {#2} {#3} {#4} #5 \q_@@_recursion_stop
   }
-\cs_new_eq:cN { @@_change_case_boundary_upper_el-x-iota:Nnnw }
-  \@@_change_case_boundary_upper_el:Nnnw
-\cs_new:Npn \@@_change_case_boundary_upper_el:nnN #1#2#3
+\cs_new_eq:cN { @@_change_case_boundary_upper_el-x-iota:Nnnnw }
+  \@@_change_case_boundary_upper_el:Nnnnw
+\cs_new:Npn \@@_change_case_boundary_upper_el:nnnN #1#2#3#4
   {
-    \token_if_cs:NTF #3
-      { \@@_change_case_loop:nnw {#1} {#2} }
+    \token_if_cs:NTF #4
+      { \@@_change_case_loop:nnnw {#1} {#2} {#3} }
       {
         \@@_codepoint_process:nN
-          { \@@_change_case_boundary_upper_el:nnn {#1} {#2} }
+          { \@@_change_case_boundary_upper_el:nnnn {#1} {#2} {#3} }
       }
-        #3
+        #4
   }
-\cs_new:Npn \@@_change_case_boundary_upper_el:nnn #1#2#3
+\cs_new:Npn \@@_change_case_boundary_upper_el:nnnn #1#2#3#4
   {
     \bool_lazy_any:nTF
       {
-        { \@@_codepoint_compare_p:nNn {#3} = { "0389 } }
-        { \@@_codepoint_compare_p:nNn {#3} = { "03AE } }
-        { \@@_codepoint_compare_p:nNn {#3} = { "1F22 } }
-        { \@@_codepoint_compare_p:nNn {#3} = { "1F2A } }
+        { \@@_codepoint_compare_p:nNn {#4} = { "0389 } }
+        { \@@_codepoint_compare_p:nNn {#4} = { "03AE } }
+        { \@@_codepoint_compare_p:nNn {#4} = { "1F22 } }
+        { \@@_codepoint_compare_p:nNn {#4} = { "1F2A } }
       }
-      { \@@_change_case_boundary_upper_el:nnnw {#1} {#2} {#3} }
-      { \@@_change_case_breathing:nnn {#1} {#2} {#3} }
+      { \@@_change_case_boundary_upper_el:nnnnw {#1} {#2} {#3} {#4} }
+      { \@@_change_case_breathing:nnnn {#1} {#2} {#3} {#4} }
   }
-\cs_new:Npn \@@_change_case_boundary_upper_el:nnnw
-  #1#2#3#4 \q_@@_recursion_stop
+\cs_new:Npn \@@_change_case_boundary_upper_el:nnnnw
+  #1#2#3#4#5 \q_@@_recursion_stop
   {
-    \tl_if_head_is_N_type:nTF {#4}
-      { \@@_change_case_loop:nnw {#1} {#2} #3 }
+    \tl_if_head_is_N_type:nTF {#5}
+      { \@@_change_case_loop:nnnw {#1} {#2} {#3} #4 }
       {
         \@@_change_case_store:e
           {
             \codepoint_generate:nn { "0389 }
-              { \@@_change_case_catcode:nn {#3} { "0389 } }
+              { \@@_change_case_catcode:nn {#4} { "0389 } }
           }
-        \@@_change_case_loop:nnw {#1} {#2}
+        \@@_change_case_loop:nnnw {#1} {#2} {#3}
       }
-        #4 \q_@@_recursion_stop
+        #5 \q_@@_recursion_stop
   }
 %    \end{macrocode}
 % \end{macro}
@@ -1658,14 +1688,14 @@
 % \end{macro}
 % \end{macro}
 % \end{macro}
-% \begin{macro}[EXP]{\@@_change_case_breathing:nnn}
 % \begin{macro}[EXP]{\@@_change_case_breathing:nnnn}
-% \begin{macro}[EXP]{\@@_change_case_breathing:nnnnw}
+% \begin{macro}[EXP]{\@@_change_case_breathing:nnnnn}
 % \begin{macro}[EXP]{\@@_change_case_breathing:nnnnnw}
-% \begin{macro}[EXP]{\@@_change_case_breathing_aux:nnnnn}
-% \begin{macro}[EXP]{\@@_change_case_breathing_aux:nnnw}
-% \begin{macro}[EXP]{\@@_change_case_breathing_aux:nnN}
-% \begin{macro}[EXP]{\@@_change_case_breathing_dialytika:nnn}
+% \begin{macro}[EXP]{\@@_change_case_breathing:nnnnnnw}
+% \begin{macro}[EXP]{\@@_change_case_breathing_aux:nnnnnn}
+% \begin{macro}[EXP]{\@@_change_case_breathing_aux:nnnnw}
+% \begin{macro}[EXP]{\@@_change_case_breathing_aux:nnnN}
+% \begin{macro}[EXP]{\@@_change_case_breathing_dialytika:nnnn}
 %   In Greek, breathing diacritics are normally dropped when uppercasing:
 %   see the code for the general case. However, for the first character
 %   of a word, if there is a breather \emph{and} the next character takes
@@ -1672,23 +1702,23 @@
 %   a \emph{dialytika}, it needs to be added. We start by checking if
 %   the current codepoint is in the Greek range, then decomposing.
 %    \begin{macrocode}
-\cs_new:Npn \@@_change_case_breathing:nnn #1#2#3
+\cs_new:Npn \@@_change_case_breathing:nnnn #1#2#3#4
   {
-    \@@_change_case_if_greek:nTF {#3}
+    \@@_change_case_if_greek:nTF {#4}
       {
-        \exp_args:Ne \@@_change_case_breathing:nnnn
+        \exp_args:Ne \@@_change_case_breathing:nnnnn
           {
             \codepoint_to_nfd:n
-              { \@@_codepoint_from_chars:Nw #3 }
+              { \@@_codepoint_from_chars:Nw #4 }
           }
-            {#1} {#2} {#3}
+            {#1} {#2} {#3} {#4}
       }
-      { \@@_change_case_loop:nnw {#1} {#2} #3 }
+      { \@@_change_case_loop:nnnw {#1} {#2} {#3} #4 }
   }
-\cs_new:Npn \@@_change_case_breathing:nnnn #1#2#3#4
+\cs_new:Npn \@@_change_case_breathing:nnnnn #1#2#3#4#5
   {
     \@@_codepoint_process:nN
-      { \@@_change_case_breathing:nnnnw {#2} {#3} {#4} }
+      { \@@_change_case_breathing:nnnnnw {#2} {#3} {#4} {#5} }
         #1 \q_mark
   }
 %    \end{macrocode}
@@ -1699,36 +1729,36 @@
 %   and second if the final resulting codepoint is one of the two we
 %   care about.
 %    \begin{macrocode}
-\cs_new:Npn \@@_change_case_breathing:nnnnw #1#2#3#4#5 \q_mark
+\cs_new:Npn \@@_change_case_breathing:nnnnnw #1#2#3#4#5#6 \q_mark
   {
-    \tl_if_blank:nTF {#5}
-      { \@@_change_case_loop:nnw {#1} {#2} #3 }
+    \tl_if_blank:nTF {#6}
+      { \@@_change_case_loop:nnnw {#1} {#2} {#3} #4 }
       {
         \@@_codepoint_process:nN
-          { \@@_change_case_breathing:nnnnnw {#1} {#2} {#3} {#4} }
-            #5 \q_mark
+          { \@@_change_case_breathing:nnnnnnw {#1} {#2} {#3} {#4} {#5} }
+            #6 \q_mark
       }
   }
-\cs_new:Npn \@@_change_case_breathing:nnnnnw #1#2#3#4#5#6 \q_mark
+\cs_new:Npn \@@_change_case_breathing:nnnnnnw #1#2#3#4#5#6#7 \q_mark
   {
-    \tl_if_blank:nTF {#6}
+    \tl_if_blank:nTF {#7}
      {
-       \@@_change_case_breathing_aux:nnnnn
-         {#1} {#2} {#3} {#4} {#5}
+       \@@_change_case_breathing_aux:nnnnnn
+         {#1} {#2} {#3} {#4} {#5} {#6}
      }
      {
        \@@_codepoint_process:nN
-         { \@@_change_case_breathing:nnnnnw {#1} {#2} {#3} {#4} }
-           #6 \q_mark
+         { \@@_change_case_breathing:nnnnnnw {#1} {#2} {#3} {#4} {#5} }
+           #7 \q_mark
      }
   }
-\cs_new:Npn \@@_change_case_breathing_aux:nnnnn #1#2#3#4#5
+\cs_new:Npn \@@_change_case_breathing_aux:nnnnnn #1#2#3#4#5#6
   {
     \bool_lazy_or:nnTF
-      { \@@_codepoint_compare_p:nNn {#5} = { "0313 } }
-      { \@@_codepoint_compare_p:nNn {#5} = { "0314 } }
-      { \@@_change_case_breathing_aux:nnnw {#1} {#2} {#4} }
-      { \@@_change_case_loop:nnw {#1} {#2} #3 }
+      { \@@_codepoint_compare_p:nNn {#6} = { "0313 } }
+      { \@@_codepoint_compare_p:nNn {#6} = { "0314 } }
+      { \@@_change_case_breathing_aux:nnnnw {#1} {#2} {#3} {#5} }
+      { \@@_change_case_loop:nnnw {#1} {#2} {#3} #4 }
   }
 %    \end{macrocode}
 %   Now the lookahead can be fired: check the next codepoint and assess
@@ -1736,29 +1766,29 @@
 %    breathing mark or generate the \emph{dialytika}: the
 %   latter is code shared with the general mechanism.
 %    \begin{macrocode}
-\cs_new:Npn \@@_change_case_breathing_aux:nnnw #1#2#3#4
+\cs_new:Npn \@@_change_case_breathing_aux:nnnnw #1#2#3#4#5
   \q_@@_recursion_stop
   {
     \@@_change_case_store:e
-      { \@@_change_case_codepoint:nn { upper } {#3} }
-    \tl_if_head_is_N_type:nTF {#4}
-      { \@@_change_case_breathing_aux:nnN  }
-      { \@@_change_case_loop:nnw }
-        {#1} {#2} #4 \q_@@_recursion_stop
+      { \@@_change_case_codepoint:nn { upper } {#4} }
+    \tl_if_head_is_N_type:nTF {#5}
+      { \@@_change_case_breathing_aux:nnnN }
+      { \@@_change_case_loop:nnnw }
+        {#1} {#2} {#3} #5 \q_@@_recursion_stop
   }
-\cs_new:Npn \@@_change_case_breathing_aux:nnN #1#2#3
+\cs_new:Npn \@@_change_case_breathing_aux:nnnN #1#2#3#4
   {
     \@@_codepoint_process:nN
-      { \@@_change_case_breathing_dialytika:nnn {#1} {#2} } #3
+      { \@@_change_case_breathing_dialytika:nnnn {#1} {#2} {#3} } #4
   }
-\cs_new:Npn \@@_change_case_breathing_dialytika:nnn #1#2#3
+\cs_new:Npn \@@_change_case_breathing_dialytika:nnnn #1#2#3#4
   {
-     \@@_change_case_if_takes_dialytika:nTF {#3}
+     \@@_change_case_if_takes_dialytika:nTF {#4}
        {
-         \@@_change_case_upper_el_dialytika:n {#3}
-         \@@_change_case_loop:nnw {#1} {#2}
+         \@@_change_case_upper_el_dialytika:n {#4}
+         \@@_change_case_loop:nnnw {#1} {#2} {#3}
        }
-       { \@@_change_case_loop:nnw {#1} {#2} #3 }
+       { \@@_change_case_loop:nnnw {#1} {#2} {#3} #4 }
   }
 %    \end{macrocode}
 % \end{macro}
@@ -1769,88 +1799,88 @@
 % \end{macro}
 % \end{macro}
 % \end{macro}
-% \begin{macro}[EXP]{\@@_change_case_title_el:nnnn}
+% \begin{macro}[EXP]{\@@_change_case_title_el:nnnnn}
 %   Titlecasing retains accents, but to prevent the uppercasing code
 %   from kicking in, there has to be an explicit function here.
 %    \begin{macrocode}
-\cs_new:Npn \@@_change_case_title_el:nnnn #1#2#3#4
-  { \@@_change_case_codepoint:nnnn {#1} {#2} {#3} {#4} }
+\cs_new:Npn \@@_change_case_title_el:nnnnn #1#2#3#4#5
+  { \@@_change_case_codepoint:nnnnn {#1} {#2} {#3} {#4} {#5} }
 %    \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:nnnnn        ,
+%     \@@_change_case_title_hy:nnnnn        ,
+%     \@@_change_case_upper_hy-x-yiwn:nnnnn ,
+%     \@@_change_case_title_hy-x-yiwn:nnnnn
 %   }
 %     See \url{https://www.unicode.org/L2/L2020/20143-armenian-ech-yiwn.pdf}.
 %    \begin{macrocode}
-\cs_new:Npn \@@_change_case_upper_hy:nnnn #1#2#3#4
+\cs_new:Npn \@@_change_case_upper_hy:nnnnn #1#2#3#4#5
   {
-    \@@_codepoint_compare:nNnTF {#4} = { "0587 }
+    \@@_codepoint_compare:nNnTF {#5} = { "0587 }
       {
         \@@_change_case_store:e
           {
             \codepoint_generate:nn { "0535 }
-              { \@@_change_case_catcode:nn {#4} { "0535 } }
+              { \@@_change_case_catcode:nn {#5} { "0535 } }
             \codepoint_generate:nn { "054E }
-              { \@@_change_case_catcode:nn {#4} { "054E } }
+              { \@@_change_case_catcode:nn {#5} { "054E } }
           }
-        \use:c { @@_change_case_next_ #2 :nn }
-          {#2} {#3}
+        \use:c { @@_change_case_next_ #2 :nnn }
+          {#2} {#3} {#4}
       }
-      { \@@_change_case_codepoint:nnnn {#1} {#2} {#3} {#4} }
+      { \@@_change_case_codepoint:nnnnn {#1} {#2} {#3} {#4} {#5} }
   }
-\cs_new:Npn \@@_change_case_title_hy:nnnn #1#2#3#4
+\cs_new:Npn \@@_change_case_title_hy:nnnnn #1#2#3#4#5
   {
-    \@@_codepoint_compare:nNnTF {#4} = { "0587 }
+    \@@_codepoint_compare:nNnTF {#5} = { "0587 }
       {
         \@@_change_case_store:e
           {
             \codepoint_generate:nn { "0535 }
-              { \@@_change_case_catcode:nn {#4} { "0535 } }
+              { \@@_change_case_catcode:nn {#5} { "0535 } }
             \codepoint_generate:nn { "057E }
-              { \@@_change_case_catcode:nn {#4} { "057E } }
+              { \@@_change_case_catcode:nn {#5} { "057E } }
           }
-        \use:c { @@_change_case_next_ #2 :nn }
-          {#2} {#3}
+        \use:c { @@_change_case_next_ #2 :nnn }
+          {#2} {#3} {#4}
       }
-      { \@@_change_case_codepoint:nnnn {#1} {#2} {#3} {#4} }
+      { \@@_change_case_codepoint:nnnnn {#1} {#2} {#3} {#4} {#5} }
   }
-\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 }
+\cs_new:cpn { @@_change_case_upper_hy-x-yiwn:nnnnn } #1#2#3#4#5
+  { \@@_change_case_codepoint:nnnnn {#1} {#2} {#3} {#4} {#5} }
+\cs_new_eq:cc { @@_change_case_title_hy-x-yiwn:nnnnn }
+  { @@_change_case_upper_hy-x-yiwn:nnnnn }
 %    \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:nnnnn}
+% \begin{macro}[EXP]{\@@_change_case_upper_la-x-medieval:nnnnn}
 %   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:nnnnn } #1#2#3#4#5
   {
-    \@@_codepoint_compare:nNnTF {#4} = { "0056 }
+    \@@_codepoint_compare:nNnTF {#5} = { "0056 }
       {
         \@@_change_case_store:e
-          { \char_generate:nn { "0075 } { \@@_char_catcode:N #4 } }
-        \use:c { @@_change_case_next_ #2 :nn }
-          {#2} {#3}
+          { \char_generate:nn { "0075 } { \@@_char_catcode:N #5 } }
+        \use:c { @@_change_case_next_ #2 :nnn }
+          {#2} {#3} {#4}
       }
-      { \@@_change_case_codepoint:nnnn {#1} {#2} {#3} {#4} }
+      { \@@_change_case_codepoint:nnnnn {#1} {#2} {#3} {#4} {#5} }
   }
-\cs_new:cpn { @@_change_case_upper_la-x-medieval:nnnn } #1#2#3#4
+\cs_new:cpn { @@_change_case_upper_la-x-medieval:nnnnn } #1#2#3#4#5
   {
-    \@@_codepoint_compare:nNnTF {#4} = { "0075 }
+    \@@_codepoint_compare:nNnTF {#5} = { "0075 }
       {
         \@@_change_case_store:e
-          { \char_generate:nn { "0056 } { \@@_char_catcode:N #4 } }
-        \use:c { @@_change_case_next_ #2 :nn }
-          {#2} {#3}
+          { \char_generate:nn { "0056 } { \@@_char_catcode:N #5 } }
+        \use:c { @@_change_case_next_ #2 :nnn }
+          {#2} {#3} {#4}
       }
-      { \@@_change_case_codepoint:nnnn {#1} {#2} {#3} {#4} }
+      { \@@_change_case_codepoint:nnnnn {#1} {#2} {#3} {#4} {#5} }
   }
 %    \end{macrocode}
 % \end{macro}
@@ -1858,23 +1888,23 @@
 %
 % \begin{macro}[EXP]
 %   {
-%     \@@_change_cases_lower_lt:nnnn      ,
-%     \@@_change_cases_lower_lt_auxi:nnnn ,
-%     \@@_change_cases_lower_lt_auxii:nnnn
+%     \@@_change_cases_lower_lt:nnnnn      ,
+%     \@@_change_cases_lower_lt_auxi:nnnnn ,
+%     \@@_change_cases_lower_lt_auxii:nnnnn
 %   }
-% \begin{macro}[rEXP]{\@@_change_case_lower_lt:nnw}
-% \begin{macro}[rEXP]{\@@_change_case_lower_lt:nnN}
-% \begin{macro}[rEXP]{\@@_change_case_lower_lt:nnn}
+% \begin{macro}[rEXP]{\@@_change_case_lower_lt:nnnw}
+% \begin{macro}[rEXP]{\@@_change_case_lower_lt:nnnN}
+% \begin{macro}[rEXP]{\@@_change_case_lower_lt:nnnn}
 %   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}
-\cs_new:Npn \@@_change_case_lower_lt:nnnn #1#2#3#4
+\cs_new:Npn \@@_change_case_lower_lt:nnnnn #1#2#3#4#5
   {
-    \exp_args:Ne \@@_change_case_lower_lt_auxi:nnnn
+    \exp_args:Ne \@@_change_case_lower_lt_auxi:nnnnn
       {
-        \int_case:nn { \@@_codepoint_from_chars:Nw #4 }
+        \int_case:nn { \@@_codepoint_from_chars:Nw #5 }
           {
             { "00CC } { "0300 }
             { "00CD } { "0301 }
@@ -1881,7 +1911,7 @@
             { "0128 } { "0303 }
           }  
       }
-        {#2} {#3} {#4}
+        {#2} {#3} {#4} {#5}
   }
 %    \end{macrocode}
 %   If there was a hit, output the result with the dot-above and move on.
@@ -1888,13 +1918,13 @@
 %   Otherwise, look for one of the three letters that can take a combining
 %   accent: I, J nd 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:nnnnn #1#2#3#4#5
   {
     \tl_if_blank:nTF {#1}
       {
-        \exp_args:Ne \@@_change_case_lower_lt_auxii:nnnn
+        \exp_args:Ne \@@_change_case_lower_lt_auxii:nnnnn
           {
-            \int_case:nn { \@@_codepoint_from_chars:Nw #4 }
+            \int_case:nn { \@@_codepoint_from_chars:Nw #5 }
               {
                 { "0049 } { "0069 }
                 { "004A } { "006A }
@@ -1901,19 +1931,19 @@
                 { "012E } { "012F }
               }  
           }
-            {#2} {#3} {#4}
+            {#2} {#3} {#4} {#5}
       }
       {
         \@@_change_case_store:e
           {
             \codepoint_generate:nn { "0069 }
-              { \@@_change_case_catcode:nn {#4} { "0069 } }
+              { \@@_change_case_catcode:nn {#5} { "0069 } }
             \codepoint_generate:nn { "0307 }
-              { \@@_change_case_catcode:nn {#4} { "0307 } }
+              { \@@_change_case_catcode:nn {#5} { "0307 } }
             \codepoint_generate:nn {#1}
-              { \@@_change_case_catcode:nn {#4} {#1} }
+              { \@@_change_case_catcode:nn {#5} {#1} }
           }
-        \@@_change_case_loop:nnw {#2} {#3}
+        \@@_change_case_loop:nnnw {#2} {#3} {#4}
       }
   }
 %    \end{macrocode}
@@ -1921,45 +1951,45 @@
 %   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:nnnnn #1#2#3#4#5
   {
     \tl_if_blank:nTF {#1}
-      { \@@_change_case_codepoint:nnnn {#2} {#2} {#3} {#4} }
+      { \@@_change_case_codepoint:nnnnn {#2} {#2} {#3} {#4} {#5} }
       {
         \@@_change_case_store:e
           {
             \codepoint_generate:nn {#1}
-              { \@@_change_case_catcode:nn {#4} {#1} }
+              { \@@_change_case_catcode:nn {#5} {#1} }
           }
-        \@@_change_case_lower_lt:nnw {#2} {#3}
+        \@@_change_case_lower_lt:nnnw {#2} {#3} {#4}
       }
   }
-\cs_new:Npn \@@_change_case_lower_lt:nnw #1#2#3 \q_@@_recursion_stop
+\cs_new:Npn \@@_change_case_lower_lt:nnnw #1#2#3#4 \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
+    \tl_if_head_is_N_type:nTF {#4}
+      { \@@_change_case_lower_lt:nnnN }
+      { \@@_change_case_loop:nnnw }
+       {#1} {#2} {#3} #4 \q_@@_recursion_stop
   }
-\cs_new:Npn \@@_change_case_lower_lt:nnN #1#2#3
+\cs_new:Npn \@@_change_case_lower_lt:nnnN #1#2#3#4
   {
     \@@_codepoint_process:nN
-      { \@@_change_case_lower_lt:nnn {#1} {#2} } #3
+      { \@@_change_case_lower_lt:nnnn {#1} {#2} {#3} } #4
   }
-\cs_new:Npn \@@_change_case_lower_lt:nnn #1#2#3
+\cs_new:Npn \@@_change_case_lower_lt:nnnn #1#2#3#4
   {
     \bool_lazy_and:nnT
       {
         \bool_lazy_or_p:nn
-          { ! \tl_if_single_p:n {#3} }
-          { ! \token_if_cs_p:N #3 }
+          { ! \tl_if_single_p:n {#4} }
+          { ! \token_if_cs_p:N #4 }
       }
       {
         \bool_lazy_any_p:n
           {
-            { \@@_codepoint_compare_p:nNn {#3} = { "0300 } }
-            { \@@_codepoint_compare_p:nNn {#3} = { "0301 } }
-            { \@@_codepoint_compare_p:nNn {#3} = { "0303 } }
+            { \@@_codepoint_compare_p:nNn {#4} = { "0300 } }
+            { \@@_codepoint_compare_p:nNn {#4} = { "0301 } }
+            { \@@_codepoint_compare_p:nNn {#4} = { "0303 } }
           }
       }
       {
@@ -1966,10 +1996,10 @@
         \@@_change_case_store:e
           {
             \codepoint_generate:nn { "0307 }
-              { \@@_change_case_catcode:nn {#3} { "0307 } }
+              { \@@_change_case_catcode:nn {#4} { "0307 } }
           }
       }
-    \@@_change_case_loop:nnw {#1} {#2} #3
+    \@@_change_case_loop:nnnw {#1} {#2} {#3} #4
   }
 %    \end{macrocode}
 % \end{macro}
@@ -1978,20 +2008,20 @@
 % \end{macro}
 % \begin{macro}[EXP]
 %   {
-%     \@@_change_cases_upper_lt:nnnn     ,
-%     \@@_change_cases_upper_lt_aux:nnnn
+%     \@@_change_cases_upper_lt:nnnnn     ,
+%     \@@_change_cases_upper_lt_aux:nnnnn
 %   }
-% \begin{macro}[rEXP]{\@@_change_case_upper_lt:nnw}
-% \begin{macro}[rEXP]{\@@_change_case_upper_lt:nnN}
-% \begin{macro}[rEXP]{\@@_change_case_upper_lt:nnn}
+% \begin{macro}[rEXP]{\@@_change_case_upper_lt:nnnw}
+% \begin{macro}[rEXP]{\@@_change_case_upper_lt:nnnN}
+% \begin{macro}[rEXP]{\@@_change_case_upper_lt:nnnn}
 %   The uppercasing version: first find i/j/i-ogonek, then look for the
 %   combining char: drop it if present.
 %    \begin{macrocode}
-\cs_new:Npn \@@_change_case_upper_lt:nnnn #1#2#3#4
+\cs_new:Npn \@@_change_case_upper_lt:nnnnn #1#2#3#4#5
  {
-    \exp_args:Ne \@@_change_case_upper_lt_aux:nnnn
+    \exp_args:Ne \@@_change_case_upper_lt_aux:nnnnn
       {
-        \int_case:nn { \@@_codepoint_from_chars:Nw #4 }
+        \int_case:nn { \@@_codepoint_from_chars:Nw #5 }
           {
             { "0069 } { "0049 }
             { "006A } { "004A }
@@ -1998,44 +2028,44 @@
             { "012F } { "012E }
           }  
       }
-        {#2} {#3} {#4}
+        {#2} {#3} {#4} {#5}
   }
-\cs_new:Npn \@@_change_case_upper_lt_aux:nnnn #1#2#3#4
+\cs_new:Npn \@@_change_case_upper_lt_aux:nnnnn #1#2#3#4#5
   {
     \tl_if_blank:nTF {#1}
-      { \@@_change_case_codepoint:nnnn { upper } {#2} {#3} {#4} }
+      { \@@_change_case_codepoint:nnnnn { upper } {#2} {#3} {#4} {#5} }
       {
         \@@_change_case_store:e
           {
             \codepoint_generate:nn {#1}
-              { \@@_change_case_catcode:nn {#4} {#1} }
+              { \@@_change_case_catcode:nn {#5} {#1} }
           }
-        \@@_change_case_upper_lt:nnw {#2} {#3}
+        \@@_change_case_upper_lt:nnnw {#2} {#3} {#4}
       }
   }
-\cs_new:Npn \@@_change_case_upper_lt:nnw #1#2#3 \q_@@_recursion_stop
+\cs_new:Npn \@@_change_case_upper_lt:nnnw #1#2#3#4 \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
+    \tl_if_head_is_N_type:nTF {#4}
+      { \@@_change_case_upper_lt:nnnN }
+      { \use:c { @@_change_case_next_ #1 :nnn } }
+        {#1} {#2} {#3} #4 \q_@@_recursion_stop
   }
-\cs_new:Npn \@@_change_case_upper_lt:nnN #1#2#3
+\cs_new:Npn \@@_change_case_upper_lt:nnnN #1#2#3#4
   {
     \@@_codepoint_process:nN
-      { \@@_change_case_upper_lt:nnn {#1} {#2} } #3
+      { \@@_change_case_upper_lt:nnnn {#1} {#2} {#3} } #4
   }
-\cs_new:Npn \@@_change_case_upper_lt:nnn #1#2#3
+\cs_new:Npn \@@_change_case_upper_lt:nnnn #1#2#3#4
   {
     \bool_lazy_and:nnTF
       {
         \bool_lazy_or_p:nn
-          { ! \tl_if_single_p:n {#3} }
-          { ! \token_if_cs_p:N #3 }
+          { ! \tl_if_single_p:n {#4} }
+          { ! \token_if_cs_p:N #4 }
       }
-      { \@@_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 }
+      { \@@_codepoint_compare_p:nNn {#4} = { "0307 } }
+      { \use:c { @@_change_case_next_ #1 :nnn } {#1} {#2} {#3} }
+      { \use:c { @@_change_case_next_ #1 :nnn } {#1} {#2} {#3} #4 }
   }
 %    \end{macrocode}
 % \end{macro}
@@ -2044,54 +2074,54 @@
 % \end{macro}
 %
 % \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}
+%   {\@@_change_case_title_nl:nnnnn, \@@_change_case_title_nl_aux:nnnnn}
+% \begin{macro}[EXP]{\@@_change_case_title_nl:nnnw}
+% \begin{macro}[EXP]{\@@_change_case_title_nl:nnnN}
 %   For Dutch, there is a single look-ahead test for \texttt{ij} when
 %   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:nnnnn #1#2#3#4#5
   {
-    \tl_if_single:nTF {#4}
-      { \@@_change_case_title_nl_aux:nnnn }
-      { \@@_change_case_codepoint:nnnn }
-        {#1} {#2} {#3} {#4} 
+    \tl_if_single:nTF {#5}
+      { \@@_change_case_title_nl_aux:nnnnn }
+      { \@@_change_case_codepoint:nnnnn }
+        {#1} {#2} {#3} {#4}  {#5}
   }
-\cs_new:Npn \@@_change_case_title_nl_aux:nnnn #1#2#3#4
+\cs_new:Npn \@@_change_case_title_nl_aux:nnnnn #1#2#3#4#5
   {
     \bool_lazy_or:nnTF
-      { \int_compare_p:nNn {`#4} = { "0049 } }
-      { \int_compare_p:nNn {`#4} = { "0069 } }
+      { \int_compare_p:nNn {`#5} = { "0049 } }
+      { \int_compare_p:nNn {`#5} = { "0069 } }
       {
         \@@_change_case_store:e
-          { \char_generate:nn { "0049 } { \@@_char_catcode:N #4 } }
-        \@@_change_case_title_nl:nnw {#2} {#3}
+          { \char_generate:nn { "0049 } { \@@_char_catcode:N #5 } }
+        \@@_change_case_title_nl:nnnw {#2} {#3} {#4}
       }
-      { \@@_change_case_codepoint:nnnn {#1} {#2} {#3} {#4} }
+      { \@@_change_case_codepoint:nnnnn {#1} {#2} {#3} {#4} {#5} }
   }
-\cs_new:Npn \@@_change_case_title_nl:nnw #1#2#3 \q_@@_recursion_stop
+\cs_new:Npn \@@_change_case_title_nl:nnnw #1#2#3#4 \q_@@_recursion_stop
   {
-    \tl_if_head_is_N_type:nTF {#3}
-      { \@@_change_case_title_nl:nnN }
-      { \use:c { @@_change_case_next_ #1 :nn } }
-        {#1} {#2} #3 \q_@@_recursion_stop
+    \tl_if_head_is_N_type:nTF {#4}
+      { \@@_change_case_title_nl:nnnN }
+      { \use:c { @@_change_case_next_ #1 :nnn } }
+        {#1} {#2} {#3} #4 \q_@@_recursion_stop
   }
-\cs_new:Npn \@@_change_case_title_nl:nnN #1#2#3
+\cs_new:Npn \@@_change_case_title_nl:nnnN #1#2#3#4
   {
     \bool_lazy_and:nnTF
-      { ! \token_if_cs_p:N #3 }
+      { ! \token_if_cs_p:N #4 }
       {
         \bool_lazy_or_p:nn
-          { \int_compare_p:nNn {`#3} = { "004A } }
-          { \int_compare_p:nNn {`#3} = { "006A } }
+          { \int_compare_p:nNn {`#4} = { "004A } }
+          { \int_compare_p:nNn {`#4} = { "006A } }
       }
       {
         \@@_change_case_store:e
-          { \char_generate:nn { "004A } { \@@_char_catcode:N #3 } }
-        \use:c { @@_change_case_next_ #1 :nn } {#1} {#2}
+          { \char_generate:nn { "004A } { \@@_char_catcode:N #4 } }
+        \use:c { @@_change_case_next_ #1 :nnn } {#1} {#2} {#3}
       }
-      { \use:c { @@_change_case_next_ #1 :nn } {#1} {#2} #3 }
+      { \use:c { @@_change_case_next_ #1 :nnn } {#1} {#2} {#3} #4 }
   }
 %    \end{macrocode}
 % \end{macro}
@@ -2098,30 +2128,30 @@
 % \end{macro}
 % \end{macro}
 %
-% \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:Nnnn}
+% \begin{macro}[EXP]{\@@_change_case_lower_tr:nnnnn}
+% \begin{macro}[EXP]{\@@_change_case_lower_tr:nnnNw}
+% \begin{macro}[EXP]{\@@_change_case_lower_tr:NnnnN}
+% \begin{macro}[EXP]{\@@_change_case_lower_tr:Nnnnn}
 %   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}
-\cs_new:Npn \@@_change_case_lower_tr:nnnn #1#2#3#4
+\cs_new:Npn \@@_change_case_lower_tr:nnnnn #1#2#3#4#5
   {
-    \@@_codepoint_compare:nNnTF {#4} = { "0049 }
-      { \@@_change_case_lower_tr:nnNw {#1} {#3} #4 }
+    \@@_codepoint_compare:nNnTF {#5} = { "0049 }
+      { \@@_change_case_lower_tr:nnnNw {#1} {#3} {#4} #5 }
       {
-        \@@_codepoint_compare:nNnTF {#4} = { "0130 }
+        \@@_codepoint_compare:nNnTF {#5} = { "0130 }
           {
             \@@_change_case_store:e
               {
                 \codepoint_generate:nn { "0069 }
-                  { \@@_change_case_catcode:nn {#4} { "0069 } }
+                  { \@@_change_case_catcode:nn {#5} { "0069 } }
               }
-            \@@_change_case_loop:nnw {#1} {#3}
+            \@@_change_case_loop:nnnw {#1} {#3} {#4}
           }
-          { \@@_change_case_codepoint:nnnn {#1} {#2} {#3} {#4} }
+          { \@@_change_case_codepoint:nnnnn {#1} {#2} {#3} {#4} {#5} }
       }
   }
 %    \end{macrocode}
@@ -2130,34 +2160,34 @@
 %   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:nnnNw #1#2#3#4#5 \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 {#5}
+      { \@@_change_case_lower_tr:NnnnN  #4 {#1} {#2} {#3} }
       {
         \@@_change_case_store:e
           {
             \codepoint_generate:nn { "0131 }
-              { \@@_change_case_catcode:nn {#3} { "0131 } }
+              { \@@_change_case_catcode:nn {#4} { "0131 } }
           }
-        \@@_change_case_loop:nnw {#1} {#2}
+        \@@_change_case_loop:nnnw {#1} {#2} {#3}
       }
-        #4 \q_@@_recursion_stop
+        #5 \q_@@_recursion_stop
   }
-\cs_new:Npn \@@_change_case_lower_tr:NnnN #1#2#3#4
+\cs_new:Npn \@@_change_case_lower_tr:NnnnN #1#2#3#4#5
   {
     \@@_codepoint_process:nN
-      { \@@_change_case_lower_tr:Nnnn #1 {#2} {#3} } #4
+      { \@@_change_case_lower_tr:Nnnnn #1 {#2} {#3} {#4} } #5
   }
-\cs_new:Npn \@@_change_case_lower_tr:Nnnn #1#2#3#4
+\cs_new:Npn \@@_change_case_lower_tr:Nnnnn #1#2#3#4#5
   {
     \bool_lazy_or:nnTF
       {
         \bool_lazy_and_p:nn
-          { \tl_if_single_p:n {#4} }
-          { \token_if_cs_p:N #4 }
+          { \tl_if_single_p:n {#5} }
+          { \token_if_cs_p:N #5 }
       }
-      { ! \@@_codepoint_compare_p:nNn {#4} = { "0307 } }
+      { ! \@@_codepoint_compare_p:nNn {#5} = { "0307 } }
       {
         \@@_change_case_store:e 
           {
@@ -2164,7 +2194,7 @@
             \codepoint_generate:nn { "0131 }
               { \@@_change_case_catcode:nn {#1} { "0131 } }
           }
-        \@@_change_case_loop:nnw {#2} {#3} #4
+        \@@_change_case_loop:nnnw {#2} {#3} {#4} #5
       }
       {
         \@@_change_case_store:e
@@ -2172,7 +2202,7 @@
             \codepoint_generate:nn { "0069 }
               { \@@_change_case_catcode:nn {#1} { "0069 } }
           }
-        \@@_change_case_loop:nnw {#2} {#3}
+        \@@_change_case_loop:nnnw {#2} {#3} {#4}
       }
   }
 %    \end{macrocode}
@@ -2180,33 +2210,33 @@
 % \end{macro}
 % \end{macro}
 % \end{macro}
-% \begin{macro}[EXP]{\@@_change_case_upper_tr:nnnn}
+% \begin{macro}[EXP]{\@@_change_case_upper_tr:nnnnn}
 %   Uppercasing is easier: just one exception with no context.
 %    \begin{macrocode}
-\cs_new:Npn \@@_change_case_upper_tr:nnnn #1#2#3#4
+\cs_new:Npn \@@_change_case_upper_tr:nnnnn #1#2#3#4#5
   {
-    \@@_codepoint_compare:nNnTF {#4} = { "0069 }
+    \@@_codepoint_compare:nNnTF {#5} = { "0069 }
       {
         \@@_change_case_store:e
           {
             \codepoint_generate:nn { "0130 }
-              { \@@_change_case_catcode:nn {#4} { "0130 } }
+              { \@@_change_case_catcode:nn {#5} { "0130 } }
           }
-        \use:c { @@_change_case_next_ #2 :nn } {#2} {#3}
+        \use:c { @@_change_case_next_ #2 :nnn } {#2} {#3} {#4}
       }
-      { \@@_change_case_codepoint:nnnn {#1} {#2} {#3} {#4} }
+      { \@@_change_case_codepoint:nnnnn {#1} {#2} {#3} {#4} {#5} }
   }
 %    \end{macrocode}
 % \end{macro}
 %
 % \begin{macro}[EXP]
-%   {\@@_change_case_lower_az:nnnn, \@@_change_case_upper_az:nnnn}
+%   {\@@_change_case_lower_az:nnnnn, \@@_change_case_upper_az:nnnnn}
 %   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:nnnnn
+  \@@_change_case_lower_tr:nnnnn
+\cs_new_eq:NN \@@_change_case_upper_az:nnnnn
+  \@@_change_case_upper_tr:nnnnn
 %    \end{macrocode}
 % \end{macro}
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3text-map.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3text-map.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3text-map.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -2,7 +2,7 @@
 %
 %% File: l3text-map.dtx
 %
-% Copyright (C) 2022,2023 The LaTeX Project
+% Copyright (C) 2022-2023 The LaTeX Project
 %
 % It may be distributed and/or modified under the conditions of the
 % LaTeX Project Public License (LPPL), either version 1.3c of this
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3text-purify.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3text-purify.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3text-purify.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3text.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3text.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3text.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 %
 % \maketitle
 %
@@ -94,11 +94,11 @@
 %
 % \section{Case changing}
 %
-% \begin{function}[EXP, added = 2019-11-20, updated = 2022-10-13]
+% \begin{function}[EXP, added = 2019-11-20, updated = 2023-07-08]
 %   {
-%     \text_lowercase:n,  \text_uppercase:n,  \text_titlecase:n,
+%     \text_lowercase:n,  \text_uppercase:n,  \text_titlecase_all:n,
 %       \text_titlecase_first:n,
-%     \text_lowercase:nn, \text_uppercase:nn, \text_titlecase:nn,
+%     \text_lowercase:nn, \text_uppercase:nn, \text_titlecase_all:nn,
 %       \text_titlecase_first:nn
 %   }
 %   \begin{syntax}
@@ -105,7 +105,7 @@
 %     \cs{text_uppercase:n}  \Arg{tokens}
 %     \cs{text_uppercase:nn} \Arg{BCP-47} \Arg{tokens}
 %   \end{syntax}
-%   Takes user input \meta{text} first applies \cs{text_expand}, then
+%   Takes user input \meta{text} first applies \cs{text_expand:n}, then
 %   transforms the case of character tokens as specified by the
 %   function name. The category code of letters are not changed by this
 %   process when Unicode engines are used; in $8$-bit engines, case changed
@@ -115,11 +115,13 @@
 %
 %   Upper- and lowercase have the obvious meanings. Titlecasing may be regarded
 %   informally as converting the first character of the \meta{tokens} to
-%   uppercase and the rest to lowercase. However, the process is more complex
+%   uppercase. However, the process is more complex
 %   than this as there are some situations where a single lowercase character
 %   maps to a special form, for example \texttt{ij} in Dutch which becomes
-%   \texttt{IJ}. The \texttt{titlecase_first} variant does not attempt
-%   any case changing at all after the first letter has been processed.
+%   \texttt{IJ}. There are two functions available for titlecasing: one which
+%   applies the change to each \enquote{word} and a second which only applies
+%   at the start of the input. (Here, \enquote{word} boundaries are spaces:
+%   at present, full Unicode word breaking is not attempted.)
 %
 %   Importantly, notice that these functions are intended for working with
 %   user \emph{text for typesetting}. For case changing programmatic data see
@@ -163,10 +165,7 @@
 %       I-dot and introduced when upper casing i-dotless.
 %     \item German (\texttt{de-x-eszett}).
 %       An alternative mapping for German in which the lowercase
-%       \emph{Eszett} maps to a \emph{gro\ss{}es Eszett}. Since there is
-%       a |T1| slot for the \emph{gro\ss{}es Eszett} in |T1|, this
-%       tailoring \emph{is} available with \pdfTeX{} as well as in the
-%       Unicode \TeX{} engines.
+%       \emph{Eszett} maps to a \emph{gro\ss{}es Eszett}.
 %     \item Greek (\texttt{el}).
 %       Removes accents from Greek letters when uppercasing; titlecasing
 %       leaves accents in place. A variant \texttt{el-x-iota} is available
@@ -185,25 +184,17 @@
 %      The characters |u| and |V| are interchanged on case changing.
 %     \item Dutch (\texttt{nl}).
 %       Capitalisation of \texttt{ij} at the beginning of titlecased
-%       input produces \texttt{IJ} rather than \texttt{Ij}. The output
-%       retains two separate letters, thus this transformation \emph{is}
-%       available using \pdfTeX{}.
+%       input produces \texttt{IJ} rather than \texttt{Ij}.
 %   \end{itemize}
 %
-%  For titlecasing, note that there are two functions available. The
-%  function \cs{text_titlecase:n} applies (broadly) uppercasing to the first
-%  letter of the input, then lowercasing to the remainder. In contrast,
-%  \cs{text_titlecase_first:n} \emph{only} carries out the uppercasing operation,
-%  and leaves the balance of the input unchanged. Determining whether
-%  non-letter characters at the start of text should switch from upper- to
-%  lowercasing is controllable. When \cs{l_text_titlecase_check_letter_bool} is
+%  Determining whether non-letter characters at the start of text should count
+%  as the uppercase element is controllable. When \cs{l_text_titlecase_check_letter_bool} is
 %  \texttt{true}, characters which are not letters (category code~$11$) are
-%  left unchanged and \enquote{skipped}: the first \emph{letter} is uppercased.
+%  \enquote{skipped}: the first \emph{letter} is uppercased.
 %  (With $8$-bit engines, this is extended to active characters which form
 %  part of a multi-byte letter codepoint.) When
 %  \cs{l_text_titlecase_check_letter_bool} is \texttt{false}, the first
-%  character is uppercased, and the rest lowercased, irrespective of the nature
-%  of the character.
+%  character is uppercased, irrespective of the category code of the character.
 %
 % \begin{function}[added = 2022-07-04]
 %   {

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3tl-analysis.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3tl-analysis.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3tl-analysis.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -44,7 +44,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 %
 % \maketitle
 %
@@ -177,9 +177,6 @@
 % by avoiding \cs{exp_not:n} for characters other than active and macro
 % parameters.)
 %
-% ^^A todo: ask LuaTeX list for an \ifx\undefined <active char>
-% ^^A which does not add the <active char> in memory.
-%
 %    \begin{macrocode}
 %<*package>
 %    \end{macrocode}
@@ -443,6 +440,42 @@
 %    \end{macrocode}
 % \end{macro}
 %
+% \begin{macro}{\@@_analysis_disable_char:N}
+%   Similar to \cs{@@_analysis_disable:n}, but it receives a normal
+%   character token, tests if that token is active (by turning it into
+%   a space: the active space has been undefined at this point), and
+%   if so, disables it.  Even if the character is active and set equal
+%   to a primitive conditional, nothing blows up.
+%   Again, in \pTeX{} and \upTeX{} we skip characters beyond $[0,255]$,
+%   which cannot be active anyways.
+%    \begin{macrocode}
+\group_begin:
+  \char_set_catcode_active:N \^^@
+  \cs_new_protected:Npn \@@_analysis_disable_char:N #1
+    {
+      \tex_lccode:D `#1 = 32 \exp_stop_f:
+      \tex_lowercase:D { \if_meaning:w #1 } \tex_undefined:D
+        \tex_let:D #1 \tex_undefined:D
+      \fi:
+    }
+  \bool_lazy_or:nnT
+    { \sys_if_engine_ptex_p: }
+    { \sys_if_engine_uptex_p: }
+    {
+      \cs_gset_protected:Npn \@@_analysis_disable_char:N #1
+        {
+          \if_int_compare:w 256 > `#1 \exp_stop_f:
+            \tex_lccode:D `#1 = 32 \exp_stop_f:
+            \tex_lowercase:D { \if_meaning:w #1 } \tex_undefined:D
+              \tex_let:D #1 \tex_undefined:D
+            \fi:
+          \fi:
+        }
+    }
+\group_end:
+%    \end{macrocode}
+% \end{macro}
+%
 % \subsection{First pass}
 %
 % The goal of this pass is to detect special (non-\texttt{N}-type) tokens,
@@ -737,7 +770,7 @@
       \exp_after:wN \use_ii:nn
     \fi:
       {
-        \@@_analysis_disable:n { `#1 }
+        \@@_analysis_disable_char:N #1
         \int_incr:N \l_@@_analysis_normal_int
       }
       { \@@_analysis_cs_space_count:NN \@@_analysis_a_cs:ww #1 }

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3tl.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3tl.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3tl.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 %
 % \maketitle
 %
@@ -187,7 +187,7 @@
 %     \tl_put_left:co,
 %     \tl_gput_left:Nn, \tl_gput_left:NV, \tl_gput_left:Nv, \tl_gput_left:Ne,
 %     \tl_gput_left:No,
-%     \tl_gput_left:cn, \tl_gput_left:cV, \tl_gput_left:cv, \tl_gput_left:ce
+%     \tl_gput_left:cn, \tl_gput_left:cV, \tl_gput_left:cv, \tl_gput_left:ce,
 %     \tl_gput_left:co
 %   }
 %   \begin{syntax}
@@ -205,7 +205,7 @@
 %     \tl_put_right:co,
 %     \tl_gput_right:Nn, \tl_gput_right:NV, \tl_gput_right:Nv, \tl_gput_right:Ne,
 %     \tl_gput_right:No,
-%     \tl_gput_right:cn, \tl_gput_right:cV, \tl_gput_right:cv, \tl_gput_right:ce
+%     \tl_gput_right:cn, \tl_gput_right:cV, \tl_gput_right:cv, \tl_gput_right:ce,
 %     \tl_gput_right:co
 %   }
 %   \begin{syntax}
@@ -592,8 +592,8 @@
 %   \begin{syntax}
 %     \cs{tl_reverse_items:n} \Arg{token list}
 %   \end{syntax}
-%   Reverses the order of the \meta{items} stored in \meta{tl~var},
-%   so that \Arg{item_1}\Arg{item_2}\Arg{item_3} \ldots \Arg{item_n}
+%   Reverses the order of the \meta{items} in the \meta{token list},
+%   so that \meta{item_1}\meta{item_2}\meta{item_3} \ldots \meta{item_n}
 %   becomes \Arg{item_n} \ldots{} \Arg{item_3}\Arg{item_2}\Arg{item_1}.
 %   This process removes any unprotected space within the
 %   \meta{token list}. Braced token groups are copied without

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3token.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3token.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3token.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 %
 % \maketitle
 %
@@ -481,6 +481,18 @@
 %   \end{texnote}
 % \end{function}
 %
+% \begin{function}[EXP,added = 2023-10-15]{\token_to_catcode:N}
+%   \begin{syntax}
+%     \cs{token_to_catcode:N} \meta{token}
+%   \end{syntax}
+%   Converts the given \meta{token} into a number describing its category code.
+%   If \meta{token} is a control sequence this expands to $16$. This can't
+%   detect the categories $0$ (escape character), $5$ (end of line), $9$
+%   (ignored character), $14$ (comment character), or $15$ (invalid character).
+%   Control sequences or active characters let to a token of one of the
+%   detectable category codes will yield that category.
+% \end{function}
+%
 % \section{Token conditionals}
 %
 % \begin{function}[EXP,pTF]{\token_if_group_begin:N}
@@ -1022,7 +1034,7 @@
 %     For instance \cs{peek_regex:nTF} \verb"{ abc | [a-z] }" |{ } { }|
 %     |abc| will only inspect the first token~|a| even though the first
 %     branch |abc| of the alternative is preferred in functions such as
-%     \cs{peek_regex_remove_once:n}.  This may have an effect on
+%     \cs{peek_regex_remove_once:nTF}.  This may have an effect on
 %     tokenization if the input stream has not yet been tokenized and
 %     category codes are changed.
 %   \end{texnote}
@@ -1221,7 +1233,7 @@
 %   \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
+%   and \cs{text_titlecase_all: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
@@ -1873,6 +1885,66 @@
 % \end{macro}
 % \end{macro}
 %
+% \begin{macro}{\token_to_catcode:N}
+% \begin{macro}{\@@_to_catcode:N}
+%   The macro works by comparing the input token with \cs{if_catcode:w} with all
+%   valid category codes. Since the most common tokens in an average argument
+%   list are of category $11$ or $12$ those are tested first. And since a space
+%   and braces are no ordinary |N|-type arguments, and only control sequences
+%   let to those categories can match them they are tested last.
+%    \begin{macrocode}
+\cs_new:Npn \token_to_catcode:N
+  { \int_value:w \group_align_safe_begin: \@@_to_catcode:N }
+\cs_new:Npn \@@_to_catcode:N #1
+  {
+    \if_catcode:w \exp_not:N #1 \c_catcode_letter_token
+      11
+    \else:
+      \if_catcode:w \exp_not:N #1 \c_catcode_other_token
+        12
+      \else:
+        \if_catcode:w \exp_not:N #1 \c_math_toggle_token
+          3
+        \else:
+          \if_catcode:w \exp_not:N #1 \c_alignment_token
+            4
+          \else:
+            \if_catcode:w \exp_not:N #1 ##
+              6
+            \else:
+              \if_catcode:w \exp_not:N #1 \c_math_superscript_token
+                7
+              \else:
+                \if_catcode:w \exp_not:N #1 \c_math_subscript_token
+                  8
+                \else:
+                  \if_catcode:w \exp_not:N #1 \c_group_begin_token
+                    1
+                  \else:
+                    \if_catcode:w \exp_not:N #1 \c_group_end_token
+                      2
+                    \else:
+                      \if_catcode:w \exp_not:N #1 \c_space_token
+                        10
+                      \else:
+                        \token_if_cs:NTF #1 { 16 } { 13 }
+                      \fi:
+                    \fi:
+                  \fi:
+                \fi:
+              \fi:
+            \fi:
+          \fi:
+        \fi:
+      \fi:
+    \fi:
+    \group_align_safe_end:
+    \exp_stop_f:
+  }
+%    \end{macrocode}
+% \end{macro}
+% \end{macro}
+%
 % \begin{macro}
 %   {
 %     \c_group_begin_token,

Modified: trunk/Master/texmf-dist/source/latex/l3kernel/l3unicode.dtx
===================================================================
--- trunk/Master/texmf-dist/source/latex/l3kernel/l3unicode.dtx	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/source/latex/l3kernel/l3unicode.dtx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -43,7 +43,7 @@
 %    }^^A
 % }
 %
-% \date{Released 2023-10-10}
+% \date{Released 2023-10-23}
 %
 % \maketitle
 %

Modified: trunk/Master/texmf-dist/tex/latex/l3kernel/expl3-code.tex
===================================================================
--- trunk/Master/texmf-dist/tex/latex/l3kernel/expl3-code.tex	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/tex/latex/l3kernel/expl3-code.tex	2023-10-24 17:51:37 UTC (rev 68632)
@@ -38,6 +38,9 @@
 %% l3fp-trig.dtx  (with options: `package')
 %% l3fp-convert.dtx  (with options: `package')
 %% l3fp-random.dtx  (with options: `package')
+%% l3fp-types.dtx  (with options: `package')
+%% l3fp-symbolic.dtx  (with options: `package')
+%% l3fp-functions.dtx  (with options: `package')
 %% l3fparray.dtx  (with options: `package')
 %% l3cctab.dtx  (with options: `package')
 %% l3sort.dtx  (with options: `package')
@@ -71,7 +74,7 @@
 %% and all files in that bundle must be distributed together.
 %% 
 %% File: expl3.dtx
-\def\ExplFileDate{2023-10-10}%
+\def\ExplFileDate{2023-10-23}%
 \begingroup
   \def\next{\endgroup}%
   \expandafter\ifx\csname PackageError\endcsname\relax
@@ -8806,6 +8809,54 @@
 \tl_const:Ne \c_catcode_other_space_tl { \char_generate:nn { `\  } { 12 } }
 \scan_new:N \s__token_mark
 \scan_new:N \s__token_stop
+\cs_new:Npn \token_to_catcode:N
+  { \int_value:w \group_align_safe_begin: \__token_to_catcode:N }
+\cs_new:Npn \__token_to_catcode:N #1
+  {
+    \if_catcode:w \exp_not:N #1 \c_catcode_letter_token
+      11
+    \else:
+      \if_catcode:w \exp_not:N #1 \c_catcode_other_token
+        12
+      \else:
+        \if_catcode:w \exp_not:N #1 \c_math_toggle_token
+          3
+        \else:
+          \if_catcode:w \exp_not:N #1 \c_alignment_token
+            4
+          \else:
+            \if_catcode:w \exp_not:N #1 ##
+              6
+            \else:
+              \if_catcode:w \exp_not:N #1 \c_math_superscript_token
+                7
+              \else:
+                \if_catcode:w \exp_not:N #1 \c_math_subscript_token
+                  8
+                \else:
+                  \if_catcode:w \exp_not:N #1 \c_group_begin_token
+                    1
+                  \else:
+                    \if_catcode:w \exp_not:N #1 \c_group_end_token
+                      2
+                    \else:
+                      \if_catcode:w \exp_not:N #1 \c_space_token
+                        10
+                      \else:
+                        \token_if_cs:NTF #1 { 16 } { 13 }
+                      \fi:
+                    \fi:
+                  \fi:
+                \fi:
+              \fi:
+            \fi:
+          \fi:
+        \fi:
+      \fi:
+    \fi:
+    \group_align_safe_end:
+    \exp_stop_f:
+  }
 \group_begin:
   \__kernel_chk_if_free_cs:N \c_group_begin_token
   \tex_global:D \tex_let:D \c_group_begin_token {
@@ -16843,6 +16894,17 @@
     \exp_after:wN \__fp_compare_back_any:ww
       \exp:w \exp_end_continue_f:w \__fp_parse:n {#2} #1;
   }
+\cs_new:Npn \__fp_compare_back:ww #1#2; #3#4;
+  {
+    \cs:w
+      __fp
+      \__fp_type_from_scan:N #1
+      _bcmp
+      \__fp_type_from_scan:N #3
+      :ww
+    \cs_end:
+    #1#2; #3#4;
+  }
 \cs_new:Npn \__fp_compare_back_any:ww #1#2; #3
   {
     \__fp_if_type_fp:NTwFw
@@ -16860,7 +16922,7 @@
     }
     #1#2 ; #3
   }
-\cs_new:Npn \__fp_compare_back:ww
+\cs_new:Npn \__fp_bcmp:ww
     \s__fp \__fp_chk:w #1 #2 #3;
     \s__fp \__fp_chk:w #4 #5 #6;
   {
@@ -20664,6 +20726,529 @@
       \fi:
     \fi:
   }
+%% File l3fp-types.dtx (C) Copyright 2012-2015,2017,2018,2020,2021,2023 The LaTeX Project
+\cs_new:Npe \__fp_types_cs_to_op:N #1
+  {
+    \exp_not:N \exp_after:wN \exp_not:N \__fp_types_cs_to_op_auxi:wwwn
+      \exp_not:N \token_to_str:N #1 \s__fp_mark
+      \exp_not:N \__fp_use_i_delimit_by_s_stop:nw
+      \tl_to_str:n { __fp_ _o:w } \s__fp_mark
+        { \exp_not:N \__fp_use_i_delimit_by_s_stop:nw ? }
+      \s__fp_stop
+  }
+\use:e
+  {
+    \cs_new:Npn \exp_not:N \__fp_types_cs_to_op_auxi:wwwn
+      #1 \tl_to_str:n { __fp_ } #2
+      \tl_to_str:n { _o:w } #3 \s__fp_mark #4 { #4 {#2} }
+  }
+\cs_new:Npn \__fp_types_unary:NNw #1
+  {
+    \exp_args:Nf \__fp_types_unary_auxi:nNw
+      { \__fp_types_cs_to_op:N #1 }
+  }
+\cs_new:Npn \__fp_types_unary_auxi:nNw #1#2#3
+  {
+    \exp_after:wN \__fp_types_unary_auxii:NnNw
+    \cs:w __fp_#1 \__fp_type_from_scan:N #3 _o:w \cs_end:
+    {#1}
+    #2#3
+  }
+\cs_new:Npn \__fp_types_unary_auxii:NnNw #1#2#3
+  {
+    \token_if_eq_meaning:NNTF \scan_stop: #1
+      { \__fp_invalid_operation_o:nw {#2} }
+      { #1 #3 }
+  }
+\cs_new:Npn \__fp_types_binary:Nww #1
+  {
+    \exp_last_unbraced:Nf \__fp_types_binary_auxi:Nww
+      { \__fp_types_cs_to_op:N #1 }
+  }
+\cs_new:Npn \__fp_types_binary_auxi:Nww #1#2#3; #4#5; @
+  {
+    \exp_after:wN \__fp_types_binary_auxii:NNww
+    \cs:w
+      __fp
+      \__fp_type_from_scan:N #2
+      _#1
+      \__fp_type_from_scan:N #4
+      _o:ww
+    \cs_end:
+    #1 #2#3; #4#5;
+  }
+\cs_new:Npn \__fp_types_binary_auxii:NNww #1#2
+  {
+    \token_if_eq_meaning:NNTF \scan_stop: #1
+      { \__fp_invalid_operation_o:Nww #2 }
+      {#1}
+  }
+%% File l3fp-symbolic.dtx (C) Copyright 2012-2015,2017,2018,2020,2021,2023 The LaTeX Project
+\fp_new:N \l__fp_symbolic_fp
+\scan_new:N \s__fp_symbolic
+\cs_new_protected:Npn \__fp_symbolic_chk:w #1,#2#3;
+  {
+    \msg_error:nne { fp } { misused-fp }
+      {
+        \__fp_to_tl_dispatch:w
+          \s__fp_symbolic \__fp_symbolic_chk:w #1,{#2};
+      }
+  }
+\cs_new:Npn \__fp_if_has_symbolic:nTF #1
+  {
+    \__fp_if_has_symbolic_aux:w
+      #1             \s__fp_mark \use_i:nn
+      \s__fp_symbolic \s__fp_mark \use_ii:nn
+    \s__fp_stop
+  }
+\cs_new:Npn \__fp_if_has_symbolic_aux:w
+    #1 \s__fp_symbolic #2 \s__fp_mark #3#4 \s__fp_stop { #3 }
+\cs_new:Npn \__fp_exp_after_symbolic_f:nw
+    #1 \s__fp_symbolic \__fp_symbolic_chk:w #2, #3#4;
+  {
+    \exp_after:wN \__fp_exp_after_symbolic_aux:w
+    \exp:w
+    \__fp_exp_after_symbolic_loop:N #2
+      { , \exp:w \use_none:nn }
+    \exp_after:wN \exp_end: \exp_after:wN
+      {
+        \exp:w \exp_end_continue_f:w
+        \__fp_exp_after_array_f:w #3 \s__fp_expr_stop
+        \exp_after:wN
+      }
+    \exp_after:wN ;
+    \exp:w \exp_end_continue_f:w #1
+  }
+\cs_new:Npn \__fp_exp_after_symbolic_aux:w #1, #2;
+  {
+    \__fp_if_has_symbolic:nTF {#2}
+      { \s__fp_symbolic \__fp_symbolic_chk:w #1, {#2} ; }
+      { #1 #2 @ \prg_do_nothing: }
+  }
+\cs_new:Npn \__fp_exp_after_symbolic_loop:N #1
+  {
+    \exp_after:wN \exp_end:
+    \exp_after:wN #1
+    \exp:w
+    \__fp_exp_after_symbolic_loop:N
+  }
+\cs_new:Npn \__fp_symbolic_binary_o:Nww #1 #2; #3;
+  {
+    \__fp_exp_after_symbolic_f:nw { \exp_after:wN \exp_stop_f: }
+      \s__fp_symbolic \__fp_symbolic_chk:w
+      \__fp_types_binary:Nww #1 , { #2; #3; } ;
+  }
+\cs_set:Npn \__fp_tmp:w #1#2
+  {
+    \cs_new_nopar:cpn
+      { __fp_symbolic_#2_symbolic_o:ww }
+      { \__fp_symbolic_binary_o:Nww #1 }
+    \cs_new_eq:cc
+      { __fp_symbolic_#2         _o:ww }
+      { __fp_symbolic_#2_symbolic_o:ww }
+    \cs_new_eq:cc
+      { __fp         _#2_symbolic_o:ww }
+      { __fp_symbolic_#2_symbolic_o:ww }
+  }
+\tl_map_inline:nn { + - * / ^ & | }
+  { \exp_args:Nc \__fp_tmp:w { __fp_#1_o:ww } {#1} }
+\cs_new:Npn \__fp_symbolic_unary_o:NNw #1#2#3; @
+  {
+    \__fp_exp_after_symbolic_f:nw { \exp_after:wN \exp_stop_f: }
+      \s__fp_symbolic \__fp_symbolic_chk:w
+      \__fp_types_unary:NNw #1#2 , { #3; } ;
+  }
+\tl_map_inline:nn
+  {
+    {acos} {acsc} {asec} {asin} {cos} {cot} {csc} {exp} {ln}
+    {not} {sec} {set_sign} {sin} {sqrt} {tan}
+  }
+  {
+    \cs_new_nopar:cpe { __fp_symbolic_#1_o:w }
+      {
+        \exp_not:N \__fp_symbolic_unary_o:NNw
+        \exp_not:c { __fp_#1_o:w }
+      }
+  }
+\cs_set_protected:Npn \__fp_tmp:w #1#2#3
+  {
+    \cs_new_nopar:cpn { __fp_symbolic_to_#1:w }
+      {
+        \exp_after:wN \__fp_symbolic_convert:wnnN
+        \exp:w \exp_end_continue_f:w
+        \__fp_exp_after_symbolic_f:nw { { #2 } { fp_to_#1 } #3 }
+      }
+  }
+\__fp_tmp:w { decimal    } { 0   } \__fp_to_decimal_dispatch:w
+\__fp_tmp:w { int        } { 0   } \__fp_to_int_dispatch:w
+\__fp_tmp:w { scientific } { nan } \__fp_to_scientific_dispatch:w
+\cs_new:Npn \__fp_symbolic_convert:wnnN #1#2; #3#4#5
+  {
+    \str_if_eq:nnTF {#1} { \s__fp_symbolic }
+      { \__fp_invalid_operation:nnw {#3} {#4} #1#2; }
+      { #5 #1#2; }
+  }
+\cs_new:Npn \__fp_symbolic_cs_arg_to_fn:NN #1
+  {
+    \exp_args:Nf \__fp_symbolic_op_arg_to_fn:nN
+      { \__fp_types_cs_to_op:N #1 }
+  }
+\cs_new:Npn \__fp_symbolic_op_arg_to_fn:nN #1#2
+  {
+    \str_case:nnF { #1 #2 }
+      {
+        { not ? } { ! }
+        { set_sign 0 } { abs }
+        { set_sign 2 } { - }
+      }
+      {
+        \token_if_eq_meaning:NNTF #2 \use_ii:nn
+          { #1 d } {#1}
+      }
+  }
+\cs_new:Npn \__fp_symbolic_to_tl:w
+    \s__fp_symbolic \__fp_symbolic_chk:w #1#2, #3#4;
+  {
+    \str_case:nnTF {#1}
+      {
+        { \__fp_types_unary:NNw } { \__fp_symbolic_unary_to_tl:NNw }
+        { \__fp_types_binary:Nww } { \__fp_symbolic_binary_to_tl:Nww }
+        { \__fp_function_o:w } { \__fp_symbolic_function_to_tl:Nw }
+      }
+      { #2, #3 @ }
+      { \tl_to_str:n {#2} }
+  }
+\cs_new:Npn \__fp_symbolic_unary_to_tl:NNw #1#2 , #3 @
+  {
+    \use:e
+      {
+        \__fp_symbolic_cs_arg_to_fn:NN #1#2
+        ( \__fp_to_tl_dispatch:w #3 )
+      }
+  }
+\cs_new:Npn \__fp_symbolic_binary_to_tl:Nww #1, #2; #3; @
+  {
+    \use:e
+      {
+        ( \__fp_to_tl_dispatch:w #2; )
+        \__fp_types_cs_to_op:N #1
+        ( \__fp_to_tl_dispatch:w #3; )
+      }
+  }
+\cs_new:Npn \__fp_symbolic_function_to_tl:Nw #1, #2@
+  {
+    \use:e
+      {
+        \__fp_types_cs_to_op:N #1
+        ( \__fp_array_to_clist:n {#2} )
+      }
+  }
+\prg_new_protected_conditional:Npnn
+    \__fp_id_if_invalid:n #1 { T , F , TF }
+  {
+    \tl_if_empty:nTF {#1}
+      { \prg_return_true: }
+      {
+        \tl_if_in:onTF { \tl_to_str:n {#1} } { ~ }
+          { \prg_return_true: }
+          {
+            \exp_after:wN \__fp_id_if_invalid_aux:N \tl_to_str:n {#1}
+              { ? \prg_break:n \prg_return_false: }
+            \prg_break_point:
+          }
+      }
+  }
+\cs_new:Npn \__fp_id_if_invalid_aux:N #1
+  {
+    \use_none:n #1
+    \int_compare:nF { `a <= `#1 <= `z }
+      {
+        \int_compare:nF { `A <= `#1 <= `Z }
+          { \prg_break:n \prg_return_true: }
+      }
+    \__fp_id_if_invalid_aux:N
+  }
+\cs_new:Npn \__fp_variable_o:w #1 @ #2
+  {
+    \fp_if_exist:cTF { l__fp_variable_#1_fp }
+      {
+        \exp_last_unbraced:Nf \__fp_exp_after_array_f:w
+          { \use:c { l__fp_variable_#1_fp } } \s__fp_expr_stop
+        \exp_after:wN \exp_stop_f: #2
+      }
+      {
+        \token_if_eq_meaning:NNTF #2 \prg_do_nothing:
+          {
+            \s__fp_symbolic \__fp_symbolic_chk:w
+              \__fp_variable_o:w #1 , { } ;
+          }
+          {
+            \exp_after:wN \s__fp_symbolic
+            \exp_after:wN \__fp_symbolic_chk:w
+            \exp_after:wN \__fp_variable_o:w
+            \exp:w
+            \__fp_exp_after_symbolic_loop:N #1
+              { , \exp:w \use_none:nn }
+            \exp_after:wN \exp_end:
+            \exp_after:wN { \exp_after:wN } \exp_after:wN ;
+            #2
+          }
+      }
+  }
+\cs_new:Npn \__fp_variable_set_parsing:Nn #1#2
+  {
+    \cs_set_nopar:Npn \__fp_tmp:w
+      {
+        \__fp_exp_after_symbolic_f:nw { \__fp_parse_infix:NN }
+        \s__fp_symbolic \__fp_symbolic_chk:w
+          \__fp_variable_o:w #2 , { } ;
+      }
+    \exp_args:NNc \__fp_variable_set_parsing_aux:NNn #1
+      { __fp_parse_word_#2:N } {#2}
+  }
+\cs_new:Npn \__fp_variable_set_parsing_aux:NNn #1#2#3
+  {
+    \cs_if_eq:NNF #2 \__fp_tmp:w
+      {
+        \cs_if_exist:NTF #2
+          {
+            \msg_warning:nnnn
+              { fp } { id-used-elsewhere } {#3} { variable }
+            #1 #2 \__fp_tmp:w
+          }
+          {
+            \cs_new_eq:NN #2 \scan_stop: % to declare the function
+            #1 #2 \__fp_tmp:w
+          }
+      }
+  }
+\cs_new_protected:Npn \fp_clear_variable:n #1
+  {
+    \__fp_id_if_invalid:nTF {#1}
+      { \msg_error:nnn { fp } { id-invalid } {#1} }
+      { \exp_args:No \__fp_clear_variable:n { \tl_to_str:n {#1} } }
+  }
+\cs_new_protected:Npn \__fp_clear_variable:n #1
+  {
+    \cs_undefine:c { l__fp_variable_#1_fp }
+    \__fp_variable_set_parsing:Nn \cs_set_eq:NN {#1}
+  }
+\cs_new_protected:Npn \fp_new_variable:n #1
+  {
+    \__fp_id_if_invalid:nTF {#1}
+      { \msg_error:nnn { fp } { id-invalid } {#1} }
+      { \exp_args:No \__fp_new_variable:n { \tl_to_str:n {#1} } }
+  }
+\cs_new_protected:Npn \__fp_new_variable:n #1
+  {
+    \cs_if_exist:cT { __fp_parse_word_#1:N }
+      {
+        \msg_error:nnn
+          { fp } { id-already-defined } {#1}
+        \cs_undefine:c { __fp_parse_word_#1:N }
+        \cs_undefine:c { l__fp_variable_#1_fp }
+      }
+    \__fp_variable_set_parsing:Nn \cs_gset_eq:NN {#1}
+  }
+\flag_new:n { __fp_symbolic }
+\cs_new_protected:Npn \fp_set_variable:nn #1
+  {
+    \__fp_id_if_invalid:nTF {#1}
+      { \msg_error:nnn { fp } { id-invalid } {#1} }
+      { \exp_args:No \__fp_set_variable:nn { \tl_to_str:n {#1} } }
+  }
+\cs_new_protected:Npn \__fp_set_variable:nn #1#2
+  {
+    \__fp_variable_set_parsing:Nn \cs_set_eq:NN {#1}
+    \fp_set:Nn \l__fp_symbolic_fp {#2}
+    \cs_set_nopar:cpn { l__fp_variable_#1_fp }
+      { \flag_ensure_raised:n { __fp_symbolic } \c_nan_fp }
+    \flag_clear:n { __fp_symbolic }
+    \fp_set:cn { l__fp_variable_#1_fp } { \l__fp_symbolic_fp }
+    \flag_if_raised:nT { __fp_symbolic }
+      {
+        \msg_error:nneee { fp } { id-loop }
+          { \tl_to_str:n {#1} }
+          { \tl_to_str:n {#2} }
+          { \fp_to_tl:N \l__fp_symbolic_fp }
+      }
+  }
+\msg_new:nnnn { fp } { id-invalid }
+  { Floating~point~identifier~'#1'~invalid. }
+  {
+    LaTeX~has~been~asked~to~create~a~new~floating~point~identifier~'#1'~
+    but~this~may~only~contain~ASCII~letters.
+  }
+\msg_new:nnnn { fp } { id-already-defined }
+  { Floating~point~identifier~'#1'~already~defined. }
+  {
+    LaTeX~has~been~asked~to~create~a~new~floating~point~identifier~'#1'~
+    but~this~name~has~already~been~used~elsewhere.
+  }
+\msg_new:nnnn { fp } { id-used-elsewhere }
+  { Floating~point~identifier~'#1'~already~used~for~something~else. }
+  {
+    LaTeX~has~been~asked~to~create~a~new~floating~point~identifier~'#1'~
+    but~this~name~is~used,~and~is~not~a~user-defined~#2.
+  }
+\msg_new:nnnn { fp } { id-loop }
+  { Variable~'#1'~used~in~the~definition~of~'#1'. }
+  {
+    LaTeX~has~been~asked~to~set~the~floating~point~identifier~'#1'~
+    to~the~expression~'#2'.~Evaluating~this~expression~yields~'#3',~
+    which~contains~'#1'~itself.
+  }
+%% File l3fp-functions.dtx (C) Copyright 2012-2018,2020,2021,2023 The LaTeX Project
+\cs_new_protected:Npn \fp_new_function:n #1
+  { \exp_args:No \__fp_new_function:n { \tl_to_str:n {#1} } }
+\cs_new_protected:Npn \__fp_new_function:n #1
+  {
+    \__fp_id_if_invalid:nTF {#1}
+      { \msg_error:nnn { fp } { invalid-identifier } {#1} }
+      {
+        \cs_if_exist:cT { __fp_parse_word_#1:N }
+          {
+            \msg_error:nnn
+              { fp } { id-already-defined } {#1}
+            \cs_undefine:c { __fp_parse_word_#1:N }
+            \cs_undefine:c { __fp_#1_o:w }
+          }
+        \__fp_function_set_parsing:Nn \cs_gset_eq:NN {#1}
+      }
+  }
+\cs_new:Npn \__fp_function_set_parsing:Nn #1#2
+  {
+    \exp_args:NNc \__fp_function_set_parsing_aux:NNn #1
+      { __fp_parse_word_#2:N } {#2}
+  }
+\cs_new:Npn \__fp_function_set_parsing_aux:NNn #1#2#3
+  {
+    \cs_set:Npe \__fp_tmp:w
+      {
+        \exp_not:N \__fp_parse_function:NNN
+        \exp_not:N \__fp_function_o:w
+        \exp_not:c { __fp_#3_o:w }
+      }
+    \cs_if_eq:NNF #2 \__fp_tmp:w
+      {
+        \cs_if_exist:NTF #2
+          {
+            \msg_warning:nnnn
+              { fp } { id-used-elsewhere } {#3} { function }
+            #1 #2 \__fp_tmp:w
+          }
+          {
+            \cs_new_eq:NN #2 \scan_stop: % to declare the function
+            #1 #2 \__fp_tmp:w
+          }
+      }
+  }
+\cs_new:Npn \__fp_function_o:w #1#2 @
+  {
+    \cs_if_exist:NTF #1
+      { #1 #2 @ }
+      {
+        \exp_after:wN \s__fp_symbolic
+        \exp_after:wN \__fp_symbolic_chk:w
+        \exp_after:wN \__fp_function_o:w
+        \exp_after:wN #1
+        \exp_after:wN ,
+        \exp_after:wN {
+          \exp:w \exp_end_continue_f:w
+          \__fp_exp_after_array_f:w #2 \s__fp_expr_stop
+          \exp_after:wN
+        }
+        \exp_after:wN ;
+      }
+  }
+\int_new:N \l__fp_function_arg_int
+\cs_new_protected:Npn \fp_set_function:nnn #1
+  {
+    \exp_args:NNo \__fp_set_function:Nnnn \cs_set_eq:cN
+      { \tl_to_str:n {#1} }
+  }
+\cs_new_protected:Npn \__fp_set_function:Nnnn #1#2#3#4
+  {
+    \__fp_id_if_invalid:nTF {#2}
+      { \msg_error:nnn { fp } { invalid-identifier } {#2} }
+      {
+        \cs_if_exist:cF { __fp_parse_word_#2:N }
+          { \__fp_function_set_parsing:Nn \cs_set_eq:NN {#2} }
+        \group_begin:
+          \int_zero:N \l__fp_function_arg_int
+          \exp_args:No \clist_map_inline:nn { \tl_to_str:n {#3} }
+            {
+              \int_incr:N \l__fp_function_arg_int
+              \exp_args:Ne \__fp_clear_variable:n
+                { _ \tex_romannumeral:D \l__fp_function_arg_int }
+              \fp_clear_variable:n {##1}
+              \cs_set_nopar:cpe { l__fp_variable_##1_fp }
+                {
+                  \exp_not:N \s__fp_symbolic
+                  \exp_not:N \__fp_symbolic_chk:w
+                  \exp_not:N \__fp_function_arg_o:w
+                  \int_use:N \l__fp_function_arg_int
+                  ########1 , { } ;
+                }
+            }
+          \cs_set:Npn \__fp_function_arg_o:w ##1 @
+            {
+              \exp_after:wN \s__fp_symbolic
+              \exp_after:wN \__fp_symbolic_chk:w
+              \exp_after:wN \__fp_function_arg_o:w
+              \tex_romannumeral:D
+              \__fp_exp_after_symbolic_loop:N ##1
+                { , \tex_romannumeral:D \use_none:nn }
+              \exp_after:wN \c_zero_int
+              \exp_after:wN { \exp_after:wN } \exp_after:wN ;
+            }
+          \fp_set:Nn \l__fp_symbolic_fp {#4}
+          \use:e
+            {
+              \exp_not:n { \cs_gset:Npn \__fp_tmp:w ##1 }
+                { \exp_not:o { \l__fp_symbolic_fp } }
+            }
+          \use:e
+            {
+              \exp_not:n { \cs_gset:Npn \__fp_tmp:w ##1 @ }
+                {
+                  \exp_not:N \__fp_exp_after_symbolic_f:nw
+                  \exp_not:n { { \exp_after:wN \exp_stop_f: } }
+                  \exp_not:o { \__fp_tmp:w { . , {##1} } }
+                }
+            }
+        \group_end:
+        #1 { __fp_#2_o:w } \__fp_tmp:w
+      }
+  }
+\cs_new:Npn \__fp_function_arg_o:w #1. #2
+  {
+    \if_meaning:w @ #2
+      \exp_after:wN \__fp_function_arg_few:w
+    \fi:
+    \if_int_compare:w #1 = \c_one_int
+      \exp_after:wN \__fp_function_arg_get:w
+    \fi:
+    \__fp_use_i_until_s:nw
+      {
+        \exp_after:wN \__fp_function_arg_o:w
+        \int_value:w \int_eval:n { #1 - 1 } .
+      }
+      #2
+  }
+\cs_new:Npn \__fp_function_arg_few:w #1 @ { \exp_after:wN \c_nan_fp }
+\cs_new:Npn \__fp_function_arg_get:w #1#2#3; #4 @
+  {
+    \__fp_exp_after_array_f:w #3; \s__fp_expr_stop
+    \exp_after:wN \exp_stop_f:
+  }
+\cs_new_protected:Npn \fp_clear_function:n #1
+  { \exp_args:No \__fp_clear_function:n { \tl_to_str:n {#1} } }
+\cs_new_protected:Npn \__fp_clear_function:n #1
+  {
+    \cs_undefine:c { __fp_parse_word_ #1 :N }
+    \__fp_function_set_parsing:Nn \cs_set_eq:NN {#1}
+  }
 %% File: l3fparray.dtx
 \int_new:N \g__fp_array_int
 \int_new:N \l__fp_array_loop_int
@@ -23058,6 +23643,30 @@
         }
     }
 \group_end:
+\group_begin:
+  \char_set_catcode_active:N \^^@
+  \cs_new_protected:Npn \__tl_analysis_disable_char:N #1
+    {
+      \tex_lccode:D `#1 = 32 \exp_stop_f:
+      \tex_lowercase:D { \if_meaning:w #1 } \tex_undefined:D
+        \tex_let:D #1 \tex_undefined:D
+      \fi:
+    }
+  \bool_lazy_or:nnT
+    { \sys_if_engine_ptex_p: }
+    { \sys_if_engine_uptex_p: }
+    {
+      \cs_gset_protected:Npn \__tl_analysis_disable_char:N #1
+        {
+          \if_int_compare:w 256 > `#1 \exp_stop_f:
+            \tex_lccode:D `#1 = 32 \exp_stop_f:
+            \tex_lowercase:D { \if_meaning:w #1 } \tex_undefined:D
+              \tex_let:D #1 \tex_undefined:D
+            \fi:
+          \fi:
+        }
+    }
+\group_end:
 \cs_new_protected:Npn \__tl_analysis_a:n #1
   {
     \__tl_analysis_disable:n { 32 }
@@ -23179,7 +23788,7 @@
       \exp_after:wN \use_ii:nn
     \fi:
       {
-        \__tl_analysis_disable:n { `#1 }
+        \__tl_analysis_disable_char:N #1
         \int_incr:N \l__tl_analysis_normal_int
       }
       { \__tl_analysis_cs_space_count:NN \__tl_analysis_a_cs:ww #1 }
@@ -33038,57 +33647,64 @@
   { \__text_change_case:nnn { lower } { } {#1} }
 \cs_new:Npn \text_uppercase:n #1
   { \__text_change_case:nnn { upper } { } {#1} }
-\cs_new:Npn \text_titlecase:n #1
+\cs_new:Npn \text_titlecase_all:n #1
   { \__text_change_case:nnn { title } { } {#1} }
 \cs_new:Npn \text_titlecase_first:n #1
-  { \__text_change_case:nnn { titleonly } { } {#1} }
+  { \__text_change_case:nnnn { title } { break } { } {#1} }
 \cs_new:Npn \text_lowercase:nn #1#2
   { \__text_change_case:nnn { lower } {#1} {#2} }
 \cs_new:Npn \text_uppercase:nn #1#2
   { \__text_change_case:nnn { upper } {#1} {#2} }
-\cs_new:Npn \text_titlecase:nn #1#2
+\cs_new:Npn \text_titlecase_all:nn #1#2
   { \__text_change_case:nnn { title } {#1} {#2} }
 \cs_new:Npn \text_titlecase_first:nn #1#2
-  { \__text_change_case:nnn { titleonly } {#1} {#2} }
+  { \__text_change_case:nnnn { title } { break } {#1} {#2} }
 \cs_new:Npn \__text_change_case:nnn #1#2#3
+  { \__text_change_case:nnnn {#1} {#1} {#2} {#3} }
+\cs_new:Npn \__text_change_case:nnnn #1#2#3#4
   {
      \__kernel_exp_not:w \exp_after:wN
       {
         \exp:w
-        \exp_args:Ne \__text_change_case_auxi:nnn
-          { \text_expand:n {#3} }
-          {#1} {#2}
+        \exp_args:Ne \__text_change_case_auxi:nnnn
+          { \text_expand:n {#4} }
+          {#1} {#2} {#3}
       }
   }
-\cs_new:Npn \__text_change_case_auxi:nnn #1#2#3
-  { \exp_args:No \__text_change_case_BCP:nnn { \tl_to_str:n {#3} } {#1} {#2} }
-\cs_new:Npe \__text_change_case_BCP:nnn #1#2#3
+\cs_new:Npn \__text_change_case_auxi:nnnn #1#2#3#4
   {
-    \exp_not:N \__text_change_case_BCP:nnw
-      {#2} {#3} #1 \tl_to_str:n { -x- -x- } \exp_not:N \q__text_stop
+    \exp_args:No \__text_change_case_BCP:nnnn
+      { \tl_to_str:n {#4} } {#1} {#2} {#3}
   }
+\cs_new:Npe \__text_change_case_BCP:nnnn #1#2#3#4
+  {
+    \exp_not:N \__text_change_case_BCP:nnnw
+      {#2} {#3} {#4} #1 \tl_to_str:n { -x- -x- } \exp_not:N \q__text_stop
+  }
 \use:e
   {
-    \cs_new:Npn \exp_not:N \__text_change_case_BCP:nnw
-      #1#2#3 \tl_to_str:n { -x- } #4 \tl_to_str:n { -x- } #5
+    \cs_new:Npn \exp_not:N \__text_change_case_BCP:nnnw
+      #1#2#3#4 \tl_to_str:n { -x- } #5 \tl_to_str:n { -x- } #6
       \exp_not:N \q__text_stop
   }
-  { \__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
+  { \__text_change_case_BCP:nnnnnw {#1} {#2} {#3} {#5} {#4} #4 - \q__text_stop }
+\cs_new:Npn \__text_change_case_BCP:nnnnnw #1#2#3#4#5#6 - #7 \q__text_stop
   {
-    \cs_if_exist:cTF { __text_change_case_ #2 _ #5 -x- #3 :nnnn }
-      { \__text_change_case_auxii:nnn {#1} {#2} { #5 -x- #3 } }
+    \bool_lazy_or:nnTF
+      { \cs_if_exist_p:c { __text_change_case_ #2 _ #6 -x- #4 :nnnnn } }
+      { \tl_if_exist_p:c { l__text_ #2 case_special_ #6 -x- #4 _tl } }
+      { \__text_change_case_auxii:nnnn {#1} {#2} {#3} { #6 -x- #4 } }
       {
-        \cs_if_exist:cTF { __text_change_case_ #2 _ #5 :nnnn }
-          { \__text_change_case_auxii:nnn {#1} {#2} {#5} }
-          { \__text_change_case_auxii:nnn {#1} {#2} {#4} }
+        \cs_if_exist:cTF { __text_change_case_ #2 _ #6 :nnnnn }
+          { \__text_change_case_auxii:nnnn {#1} {#2} {#3} {#6} }
+          { \__text_change_case_auxii:nnnn {#1} {#2} {#3} {#5} }
       }
   }
-\cs_new:Npn \__text_change_case_auxii:nnn #1#2#3
+\cs_new:Npn \__text_change_case_auxii:nnnn #1#2#3#4
   {
     \group_align_safe_begin:
-    \cs_if_exist_use:c { __text_change_case_boundary_ #2 _ #3 :Nnnw }
-    \__text_change_case_loop:nnw {#2} {#3} #1
+    \cs_if_exist_use:c { __text_change_case_boundary_ #2 _ #4 :Nnnnw }
+    \__text_change_case_loop:nnnw {#2} {#3} {#4} #1
       \q__text_recursion_tail \q__text_recursion_stop
     \__text_change_case_result:n { }
   }
@@ -33103,23 +33719,26 @@
     \exp_end:
     #2
   }
-\cs_new:Npn \__text_change_case_loop:nnw #1#2#3 \q__text_recursion_stop
+\cs_new:Npn \__text_change_case_loop:nnnw #1#2#3#4 \q__text_recursion_stop
   {
-    \tl_if_head_is_N_type:nTF {#3}
-      { \__text_change_case_N_type:nnN }
+    \tl_if_head_is_N_type:nTF {#4}
+      { \__text_change_case_N_type:nnnN }
       {
-        \tl_if_head_is_group:nTF {#3}
-          { \use:c { __text_change_case_group_ #1 :nnn } }
-          { \__text_change_case_space:nnw }
+        \tl_if_head_is_group:nTF {#4}
+          { \use:c { __text_change_case_group_ #1 :nnnn } }
+          { \__text_change_case_space:nnnw }
       }
-    {#1} {#2} #3 \q__text_recursion_stop
+    {#1} {#2} {#3} #4 \q__text_recursion_stop
   }
-\cs_new:Npn \__text_change_case_break:w #1 \q__text_recursion_tail \q__text_recursion_stop
+\cs_new:Npn \__text_change_case_break:w
+  { \__text_change_case_break_aux:w \prg_do_nothing: }
+\cs_new:Npn \__text_change_case_break_aux:w
+  #1 \q__text_recursion_tail \q__text_recursion_stop
   {
-    \__text_change_case_store:n {#1}
+    \__text_change_case_store:o {#1}
     \__text_change_case_end:w
   }
-\cs_new:Npn \__text_change_case_group_lower:nnn #1#2#3
+\cs_new:Npn \__text_change_case_group_lower:nnnn #1#2#3#4
   {
     \__text_change_case_store:o
       {
@@ -33126,14 +33745,14 @@
         \exp_after:wN
           {
             \exp:w
-            \__text_change_case_auxii:nnn {#3} {#1} {#2}
+            \__text_change_case_auxii:nnnn {#4} {#1} {#2} {#3}
           }
       }
-    \__text_change_case_loop:nnw {#1} {#2}
+    \__text_change_case_loop:nnnw {#1} {#2} {#3}
   }
-\cs_new_eq:NN \__text_change_case_group_upper:nnn
-  \__text_change_case_group_lower:nnn
-\cs_new:Npn \__text_change_case_group_title:nnn #1#2#3
+\cs_new_eq:NN \__text_change_case_group_upper:nnnn
+  \__text_change_case_group_lower:nnnn
+\cs_new:Npn \__text_change_case_group_title:nnnn #1#2#3#4
   {
     \__text_change_case_store:o
       {
@@ -33140,341 +33759,347 @@
         \exp_after:wN
           {
             \exp:w
-            \__text_change_case_auxii:nnn {#3} {#1} {#2}
+            \__text_change_case_auxii:nnnn {#4} {#1} {#2} {#3}
           }
       }
-    \__text_change_case_loop:nnw { lower } {#2}
+    \__text_change_case_skip:nnw {#2} {#3}
   }
-\cs_new:Npn \__text_change_case_group_titleonly:nnn #1#2#3
-  {
-    \__text_change_case_store:o
-      {
-        \exp_after:wN
-          {
-            \exp:w
-            \__text_change_case_auxii:nnn {#3} {#1} {#2}
-          }
-      }
-    \__text_change_case_break:w
-  }
 \use:e
   {
-    \cs_new:Npn \exp_not:N \__text_change_case_space:nnw #1#2 \c_space_tl
+    \cs_new:Npn \exp_not:N \__text_change_case_space:nnnw #1#2#3 \c_space_tl
   }
   {
     \__text_change_case_store:n { ~ }
-    \cs_if_exist_use:c { __text_change_case_boundary_ #1 _ #2 :Nnnw }
-    \__text_change_case_loop:nnw {#1} {#2}
+    \cs_if_exist_use:cF { __text_change_case_space_ #2 :nnn }
+      {
+        \cs_if_exist_use:c { __text_change_case_boundary_ #1 _ #3 :Nnnnw }
+        \__text_change_case_loop:nnnw
+      }
+        {#2} {#2} {#3}
   }
-\cs_new:Npn \__text_change_case_N_type:nnN #1#2#3
+\cs_new:Npn \__text_change_case_space_break:nnn #1#2#3
+  { \__text_change_case_break:w }
+\cs_new:Npn \__text_change_case_N_type:nnnN #1#2#3#4
   {
-    \__text_if_q_recursion_tail_stop_do:Nn #3
+    \__text_if_q_recursion_tail_stop_do:Nn #4
       { \__text_change_case_end:w }
-    \__text_change_case_N_type_aux:nnN {#1} {#2} #3
+    \__text_change_case_N_type_aux:nnnN {#1} {#2} {#3} #4
   }
-\cs_new:Npn \__text_change_case_N_type_aux:nnN #1#2#3
+\cs_new:Npn \__text_change_case_N_type_aux:nnnN #1#2#3#4
   {
-    \exp_args:NV \__text_change_case_N_type:nnnN
-      \l_text_math_delims_tl {#1} {#2} #3
+    \exp_args:NV \__text_change_case_N_type:nnnnN
+      \l_text_math_delims_tl {#1} {#2} {#3} #4
   }
-\cs_new:Npn \__text_change_case_N_type:nnnN #1#2#3#4
+\cs_new:Npn \__text_change_case_N_type:nnnnN #1#2#3#4#5
   {
-    \__text_change_case_math_search:nnNNN {#2} {#3} #4 #1
+    \__text_change_case_math_search:nnnNNN {#2} {#3} {#4} #5 #1
       \q__text_recursion_tail \q__text_recursion_tail
       \q__text_recursion_stop
   }
-\cs_new:Npn \__text_change_case_math_search:nnNNN #1#2#3#4#5
+\cs_new:Npn \__text_change_case_math_search:nnnNNN #1#2#3#4#5#6
   {
-    \__text_if_q_recursion_tail_stop_do:Nn #4
-      { \__text_change_case_cs_check:nnN {#1} {#2} #3 }
-    \token_if_eq_meaning:NNTF #3 #4
+    \__text_if_q_recursion_tail_stop_do:Nn #5
+      { \__text_change_case_cs_check:nnnN {#1} {#2} {#3} #4 }
+    \token_if_eq_meaning:NNTF #4 #5
       {
         \__text_use_i_delimit_by_q_recursion_stop:nw
            {
-             \__text_change_case_store:n {#3}
-             \__text_change_case_math_loop:nnNw {#1} {#2} #5
+             \__text_change_case_store:n {#4}
+             \__text_change_case_math_loop:nnnNw {#1} {#2} {#3} #6
            }
       }
-      { \__text_change_case_math_search:nnNNN {#1} {#2} #3 }
+      { \__text_change_case_math_search:nnnNNN {#1} {#2} {#3} #4 }
   }
-\cs_new:Npn \__text_change_case_math_loop:nnNw #1#2#3#4 \q__text_recursion_stop
+\cs_new:Npn \__text_change_case_math_loop:nnnNw #1#2#3#4#5 \q__text_recursion_stop
   {
-    \tl_if_head_is_N_type:nTF {#4}
-      { \__text_change_case_math_N_type:nnNN }
+    \tl_if_head_is_N_type:nTF {#5}
+      { \__text_change_case_math_N_type:nnnNN }
       {
-        \tl_if_head_is_group:nTF {#4}
-          { \__text_change_case_math_group:nnNn }
-          { \__text_change_case_math_space:nnNw }
+        \tl_if_head_is_group:nTF {#5}
+          { \__text_change_case_math_group:nnnNn }
+          { \__text_change_case_math_space:nnnNw }
       }
-    {#1} {#2} #3 #4 \q__text_recursion_stop
+    {#1} {#2} {#3} #4 #5 \q__text_recursion_stop
   }
-\cs_new:Npn \__text_change_case_math_N_type:nnNN #1#2#3#4
+\cs_new:Npn \__text_change_case_math_N_type:nnnNN #1#2#3#4#5
   {
-    \__text_if_q_recursion_tail_stop_do:Nn #4
+    \__text_if_q_recursion_tail_stop_do:Nn #5
       { \__text_change_case_end:w }
-    \__text_change_case_store:n {#4}
-    \token_if_eq_meaning:NNTF #4 #3
-      { \__text_change_case_loop:nnw {#1} {#2} }
-      { \__text_change_case_math_loop:nnNw {#1} {#2} #3 }
+    \__text_change_case_store:n {#5}
+    \token_if_eq_meaning:NNTF #5 #4
+      { \__text_change_case_loop:nnnw {#1} {#2} {#3} }
+      { \__text_change_case_math_loop:nnnNw {#1} {#2} {#3} #4 }
   }
-\cs_new:Npn \__text_change_case_math_group:nnNn #1#2#3#4
+\cs_new:Npn \__text_change_case_math_group:nnnNn #1#2#3#4#5
   {
-    \__text_change_case_store:n { {#4} }
-    \__text_change_case_math_loop:nnNw {#1} {#2} #3
+    \__text_change_case_store:n { {#5} }
+    \__text_change_case_math_loop:nnnNw {#1} {#2} {#3} #4
   }
 \use:e
   {
-    \cs_new:Npn \exp_not:N \__text_change_case_math_space:nnNw #1#2#3
+    \cs_new:Npn \exp_not:N \__text_change_case_math_space:nnnNw #1#2#3#4
       \c_space_tl
   }
   {
     \__text_change_case_store:n { ~ }
-    \__text_change_case_math_loop:nnNw {#1} {#2} #3
+    \__text_change_case_math_loop:nnnNw {#1} {#2} {#3} #4
   }
-\cs_new:Npn \__text_change_case_cs_check:nnN #1#2#3
+\cs_new:Npn \__text_change_case_cs_check:nnnN #1#2#3#4
   {
-    \token_if_cs:NTF #3
-      { \__text_change_case_exclude:nnN {#1} {#2} }
+    \token_if_cs:NTF #4
+      { \__text_change_case_exclude:nnnN {#1} {#2} {#3} }
       {
         \__text_codepoint_process:nN
-          { \use:c { __text_change_case_custom_ #1 :nnn } {#1} {#2} }
+          { \use:c { __text_change_case_custom_ #1 :nnnn } {#1} {#2} {#3} }
       }
-        #3
+        #4
   }
-\cs_new:Npn \__text_change_case_exclude:nnN #1#2#3
+\cs_new:Npn \__text_change_case_exclude:nnnN #1#2#3#4
   {
-    \exp_args:Ne \__text_change_case_exclude:nnnN
+    \exp_args:Ne \__text_change_case_exclude:nnnnN
       {
         \exp_not:V \l_text_math_arg_tl
         \exp_not:V \l_text_case_exclude_arg_tl
       }
-      {#1} {#2} #3
+      {#1} {#2} {#3} #4
   }
-\cs_new:Npn \__text_change_case_exclude:nnnN #1#2#3#4
+\cs_new:Npn \__text_change_case_exclude:nnnnN #1#2#3#4#5
   {
-    \__text_change_case_exclude:nnNN {#2} {#3} #4 #1
+    \__text_change_case_exclude:nnnNN {#2} {#3} {#4} #5 #1
       \q__text_recursion_tail \q__text_recursion_stop
   }
-\cs_new:Npn \__text_change_case_exclude:nnNN #1#2#3#4
+\cs_new:Npn \__text_change_case_exclude:nnnNN #1#2#3#4#5
   {
-    \__text_if_q_recursion_tail_stop_do:Nn #4
-      { \__text_change_case_replace:nnN {#1} {#2} #3 }
-    \str_if_eq:nnTF {#3} {#4}
+    \__text_if_q_recursion_tail_stop_do:Nn #5
+      { \__text_change_case_replace:nnnN {#1} {#2} {#3} #4 }
+    \str_if_eq:nnTF {#4} {#5}
       {
         \__text_use_i_delimit_by_q_recursion_stop:nw
-          { \__text_change_case_exclude:nnNw {#1} {#2} #3 }
+          { \__text_change_case_exclude:nnnNw {#1} {#2} {#3} #4 }
       }
-      { \__text_change_case_exclude:nnNN {#1} {#2} #3 }
+      { \__text_change_case_exclude:nnnNN {#1} {#2} {#3} #4 }
   }
-\cs_new:Npn \__text_change_case_exclude:nnNw #1#2#3#4#
-  { \__text_change_case_exclude:nnNnn {#1} {#2} {#3} {#4} }
-\cs_new:Npn \__text_change_case_exclude:nnNnn #1#2#3#4#5
+\cs_new:Npn \__text_change_case_exclude:nnnNw #1#2#3#4#5#
+  { \__text_change_case_exclude:nnnNnn {#1} {#2} {#3} {#4} {#5} }
+\cs_new:Npn \__text_change_case_exclude:nnnNnn #1#2#3#4#5#6
   {
-    \tl_if_blank:nTF {#4}
-       { \__text_change_case_store:n { #3 {#5} } }
+    \tl_if_blank:nTF {#5}
+       { \__text_change_case_store:n { #4 {#6} } }
        {
         \__text_change_case_store:o
           {
-            \exp_after:wN #3
-              \exp:w \__text_change_case_auxii:nnn {#4} {#1} {#2}
-              {#5}
+            \exp_after:wN #4
+              \exp:w \__text_change_case_auxii:nnnn {#5} {#1} {#2} {#3}
+              {#6}
           }
       }
-    \__text_change_case_loop:nnw {#1} {#2}
+    \__text_change_case_loop:nnnw {#1} {#2} {#3}
   }
-\cs_new:Npn \__text_change_case_replace:nnN #1#2#3
+\cs_new:Npn \__text_change_case_replace:nnnN #1#2#3#4
   {
-    \cs_if_exist:cTF { l__text_case_ \token_to_str:N #3 _tl }
+    \cs_if_exist:cTF { l__text_case_ \token_to_str:N #4 _tl }
       {
-        \__text_change_case_replace:vnn
-          { l__text_case_ \token_to_str:N #3 _tl } {#1} {#2}
+        \__text_change_case_replace:vnnn
+          { l__text_case_ \token_to_str:N #4 _tl } {#1} {#2} {#3}
       }
-      { \__text_change_case_switch:nnN {#1} {#2} #3 }
+      { \__text_change_case_switch:nnnN {#1} {#2} {#3} #4 }
   }
-\cs_new:Npn \__text_change_case_replace:nnn #1#2#3
-  { \__text_change_case_loop:nnw {#2} {#3} #1 }
-\cs_generate_variant:Nn \__text_change_case_replace:nnn { v }
-\cs_new:Npn \__text_change_case_switch:nnN #1#2#3
+\cs_new:Npn \__text_change_case_replace:nnnn #1#2#3#4
+  { \__text_change_case_loop:nnnw {#2} {#3} {#4} #1 }
+\cs_generate_variant:Nn \__text_change_case_replace:nnnn { v }
+\cs_new:Npn \__text_change_case_switch:nnnN #1#2#3#4
   {
-    \cs_if_eq:NNTF #3 \text_case_switch:nnnn
-      { \use:c { __text_change_case_switch_ #1 :nnNnnnn  } }
-      { \use:c { __text_change_case_letterlike_ #1 :nnN } }
-        {#1} {#2} #3
+    \cs_if_eq:NNTF #4 \text_case_switch:nnnn
+      { \use:c { __text_change_case_switch_ #1 :nnnNnnnn  } }
+      { \use:c { __text_change_case_letterlike_ #1 :nnnN } }
+        {#1} {#2} {#3} #4
   }
-\cs_new:Npn \__text_change_case_switch_lower:nnNnnnn #1#2#3#4#5#6#7
+\cs_new:Npn \__text_change_case_switch_lower:nnnNnnnn #1#2#3#4#5#6#7#8
   {
+    \__text_change_case_store:n {#7}
+    \__text_change_case_loop:nnnw {#1} {#2} {#3}
+  }
+\cs_new:Npn \__text_change_case_switch_upper:nnnNnnnn #1#2#3#4#5#6#7#8
+  {
     \__text_change_case_store:n {#6}
-    \__text_change_case_loop:nnw {#1} {#2}
+    \__text_change_case_loop:nnnw {#1} {#2} {#3}
   }
-\cs_new:Npn \__text_change_case_switch_upper:nnNnnnn #1#2#3#4#5#6#7
+\cs_new:Npn \__text_change_case_switch_title:nnnNnnnn #1#2#3#4#5#6#7#8
   {
-    \__text_change_case_store:n {#5}
-    \__text_change_case_loop:nnw {#1} {#2}
+    \__text_change_case_store:n {#8}
+    \__text_change_case_skip:nnw {#2} {#3}
   }
-\cs_new:Npn \__text_change_case_switch_title:nnNnnnn #1#2#3#4#5#6#7
+\cs_new:Npn \__text_change_case_skip:nnw #1#2#3 \q__text_recursion_stop
   {
-    \__text_change_case_store:n {#7}
-    \__text_change_case_loop:nnw {#1} {#2}
+    \tl_if_head_is_N_type:nTF {#3}
+      { \__text_change_case_skip_N_type:nnN }
+      {
+        \tl_if_head_is_group:nTF {#3}
+          { \__text_change_case_skip_group:nnn }
+          { \__text_change_case_skip_space:nnw }
+      }
+        {#1} {#2} #3 \q__text_recursion_stop
   }
-\cs_new:Npn \__text_change_case_switch_titleonly:nnNnnnn #1#2#3#4#5#6#7
+\cs_new:Npn \__text_change_case_skip_N_type:nnN #1#2#3
   {
-    \__text_change_case_store:n {#7}
-    \__text_change_case_break:w
+    \__text_if_q_recursion_tail_stop_do:Nn #3
+      { \__text_change_case_end:w }
+    \__text_change_case_store:n {#3}
+    \__text_change_case_skip:nnw {#1} {#2}
   }
-\cs_new:Npn \__text_change_case_letterlike_lower:nnN #1#2#3
-  { \__text_change_case_letterlike:nnnnN {#1} {#1} {#1} {#2} #3 }
-\cs_new_eq:NN \__text_change_case_letterlike_upper:nnN
-  \__text_change_case_letterlike_lower:nnN
-\cs_new:Npn \__text_change_case_letterlike_title:nnN #1#2#3
-  { \__text_change_case_letterlike:nnnnN { upper } { lower } {#1} {#2} #3 }
-\cs_new:Npn \__text_change_case_letterlike_titleonly:nnN #1#2#3
-  { \__text_change_case_letterlike:nnnnN { upper } { end } {#1} {#2} #3 }
-\cs_new:Npn \__text_change_case_letterlike:nnnnN #1#2#3#4#5
+\cs_new:Npn \__text_change_case_skip_group:nnn #1#2#3
   {
-    \cs_if_exist:cTF { c__text_ #1 case_ \token_to_str:N #5 _tl }
+    \__text_change_case_store:n { {#3} }
+    \__text_change_case_skip:nnw {#1} {#2}
+  }
+\cs_new:Npn \__text_change_case_skip_space:nnw #1#2
+  { \__text_change_case_space:nnnw {#1} {#1} {#2} }
+\cs_new:Npn \__text_change_case_letterlike_lower:nnnN #1#2#3#4
+  { \__text_change_case_letterlike:nnnnnN {#1} {#1} {#1} {#2} {#3} #4 }
+\cs_new_eq:NN \__text_change_case_letterlike_upper:nnnN
+  \__text_change_case_letterlike_lower:nnnN
+\cs_new:Npn \__text_change_case_letterlike_title:nnnN #1#2#3#4
+  { \__text_change_case_letterlike:nnnnnN { upper } { end } {#1} {#2} {#3} #4 }
+\cs_new:Npn \__text_change_case_letterlike:nnnnnN #1#2#3#4#5#6
+  {
+    \cs_if_exist:cTF { c__text_ #1 case_ \token_to_str:N #6 _tl }
       {
         \__text_change_case_store:v
-          { c__text_ #1 case_ \token_to_str:N #5 _tl }
-         \use:c { __text_change_case_next_ #2 :nn } {#2} {#4}
+          { c__text_ #1 case_ \token_to_str:N #6 _tl }
+         \use:c { __text_change_case_next_ #2 :nnn } {#2} {#4} {#5}
       }
       {
-        \__text_change_case_store:n {#5}
+        \__text_change_case_store:n {#6}
         \cs_if_exist:cTF
           {
             c__text_
             \str_if_eq:nnTF {#1} { lower } { upper } { lower }
-            case_ \token_to_str:N #5 _tl
+            case_ \token_to_str:N #6 _tl
           }
-          { \use:c { __text_change_case_next_ #2 :nn } {#2} {#4} }
-          { \__text_change_case_loop:nnw {#3} {#4} }
+          { \use:c { __text_change_case_next_ #2 :nnn } {#2} {#4} {#5} }
+          { \__text_change_case_loop:nnnw {#3} {#4} {#5} }
       }
   }
-\cs_new:Npn \__text_change_case_custom_lower:nnn #1#2#3
+\cs_new:Npn \__text_change_case_custom_lower:nnnn #1#2#3#4
   {
-    \__text_change_case_custom:nnnnn {#1} {#2} {#3} {#1}
-      { \use:c { __text_change_case_codepoint_ #1 :nnn } {#1} {#2} {#3} }
+    \__text_change_case_custom:nnnnnn {#1} {#1} {#2} {#3} {#4}
+      { \use:c { __text_change_case_codepoint_ #1 :nnnn } {#1} {#2} {#3} {#4} }
   }
-\cs_new_eq:NN \__text_change_case_custom_upper:nnn
-  \__text_change_case_custom_lower:nnn
-\cs_new:Npn \__text_change_case_custom_title:nnn #1#2#3
+\cs_new_eq:NN \__text_change_case_custom_upper:nnnn
+  \__text_change_case_custom_lower:nnnn
+\cs_new:Npn \__text_change_case_custom_title:nnnn #1#2#3#4
   {
-    \__text_change_case_custom:nnnnn { title } {#2} {#3} {#1}
+    \__text_change_case_custom:nnnnnn { title } {#1} {#2} {#3} {#4}
       {
-        \__text_change_case_custom:nnnnn { upper } {#2} {#3} {#1}
-          { \use:c { __text_change_case_codepoint_ #1 :nnn } {#1} {#2} {#3} }
+        \__text_change_case_custom:nnnnnn { upper } {#1} {#2} {#3} {#4}
+          { \use:c { __text_change_case_codepoint_ #1 :nnnn } {#1} {#2} {#3} {#4} }
       }
   }
-\cs_new_eq:NN \__text_change_case_custom_titleonly:nnn
-  \__text_change_case_custom_title:nnn
-\cs_new:Npn \__text_change_case_custom:nnnnn #1#2#3#4#5
+\cs_new:Npn \__text_change_case_custom:nnnnnn #1#2#3#4#5#6
   {
-    \tl_if_exist:cTF { l__text_ #1 case _ \tl_to_str:n {#3} _ #2 _tl }
+    \tl_if_exist:cTF { l__text_ #1 case _ \tl_to_str:n {#5} _ #4 _tl }
       {
-        \__text_change_case_replace:vnn
-          { l__text_ #1 case _ \tl_to_str:n {#3} _ #2 _tl } {#4} {#2}
+        \__text_change_case_replace:vnnn
+          { l__text_ #1 case _ \tl_to_str:n {#5} _ #4 _tl } {#2} {#3} {#4}
       }
       {
-        \tl_if_exist:cTF { l__text_ #1 case _ \tl_to_str:n {#3} _tl }
+        \tl_if_exist:cTF { l__text_ #1 case _ \tl_to_str:n {#5} _tl }
           {
-            \__text_change_case_replace:vnn
-              { l__text_ #1 case _ \tl_to_str:n {#3} _tl } {#4} {#2}
+            \__text_change_case_replace:vnnn
+              { l__text_ #1 case _ \tl_to_str:n {#5} _tl } {#2} {#3} {#4}
           }
-          {#5}
+          {#6}
       }
   }
-\cs_new:Npn \__text_change_case_codepoint_lower:nnn #1#2#3
+\cs_new:Npn \__text_change_case_codepoint_lower:nnnn #1#2#3#4
   {
-    \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_ #3 :nnnnn }
+      { \__text_change_case_lower_sigma:nnnnn }
+        {#1} {#1} {#2} {#3} {#4}
   }
-\cs_new:Npn \__text_change_case_codepoint_upper:nnn #1#2#3
+\cs_new:Npn \__text_change_case_codepoint_upper:nnnn #1#2#3#4
   {
-    \cs_if_exist_use:cF { __text_change_case_upper_ #2 :nnnn }
-      { \__text_change_case_codepoint:nnnn }
-        {#1} {#1} {#2} {#3}
+    \cs_if_exist_use:cF { __text_change_case_upper_ #3 :nnnnn }
+      { \__text_change_case_codepoint:nnnnn }
+        {#1} {#1} {#2} {#3} {#4}
   }
-\cs_new:Npn \__text_change_case_lower_sigma:nnnn #1#2#3#4
+\cs_new:Npn \__text_change_case_lower_sigma:nnnnn #1#2#3#4#5
   {
-    \__text_codepoint_compare:nNnTF {#4} = { "03A3 }
-      { \__text_change_case_lower_sigma:nnnw {#2} }
-      { \__text_change_case_codepoint:nnnn {#1} {#2} }
-        {#3} {#4}
+    \__text_codepoint_compare:nNnTF {#5} = { "03A3 }
+      { \__text_change_case_lower_sigma:nnnnw {#2} }
+      { \__text_change_case_codepoint:nnnnn {#1} {#2} }
+        {#3} {#4} {#5}
   }
-\cs_new:Npn \__text_change_case_lower_sigma:nnnw #1#2#3#4 \q__text_recursion_stop
+\cs_new:Npn \__text_change_case_lower_sigma:nnnnw #1#2#3#4#5 \q__text_recursion_stop
   {
-    \tl_if_head_is_N_type:nTF {#4}
-      { \__text_change_case_lower_sigma:nnnN {#3} }
+    \tl_if_head_is_N_type:nTF {#5}
+      { \__text_change_case_lower_sigma:nnnnN {#4} }
       {
         \__text_change_case_store:e
-          { \codepoint_generate:nn { "03C2 } { \__text_char_catcode:N #3 } }
-        \__text_change_case_loop:nnw
+          { \codepoint_generate:nn { "03C2 } { \__text_char_catcode:N #4 } }
+        \__text_change_case_loop:nnnw
       }
-        {#1} {#2} #4 \q__text_recursion_stop
+        {#1} {#2} {#3} #5 \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:nnnnN #1#2#3#4#5
   {
     \__text_change_case_store:e
       {
         \bool_lazy_or:nnTF
-          { \token_if_letter_p:N #4 }
+          { \token_if_letter_p:N #5 }
           {
             \bool_lazy_and_p:nn
-              { \token_if_active_p:N #4 }
-              { \int_compare_p:nNn {`#4} > { "80 } }
+              { \token_if_active_p:N #5 }
+              { \int_compare_p:nNn {`#5} > { "80 } }
           }
           { \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
+    \__text_change_case_loop:nnnw {#2} {#3} {#4} #5
   }
-\cs_new:Npn \__text_change_case_codepoint_title:nnn #1#2#3
+\cs_new:Npn \__text_change_case_codepoint_title:nnnn #1#2#3#4
   {
     \bool_if:NTF \l_text_titlecase_check_letter_bool
       {
-        \tl_if_single:nTF {#3}
+        \tl_if_single:nTF {#4}
           {
             \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 } }
               }
-              { \use:c { __text_change_case_codepoint_ #1 :nn } }
-              { \__text_change_case_codepoint_title:nnnn { title } {#1} }
+              { \__text_change_case_codepoint_title:nnn }
+              { \__text_change_case_codepoint_title:nnnnn { title } {#1} }
           }
-          { \use:c { __text_change_case_codepoint_ #1 :nn } }
+          { \__text_change_case_codepoint_title:nnn }
       }
-      { \use:c { __text_change_case_codepoint_ #1 :nn } }
-        {#2} {#3}
+      { \__text_change_case_codepoint_title:nnn }
+        {#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_new:Npn \__text_change_case_codepoint_title:nnn #1#2#3
+  { \__text_change_case_codepoint_title:nnnnn { title } { end } {#1} {#2} {#3} }
+\cs_new:Npn \__text_change_case_codepoint_title:nnnnn #1#2#3#4#5
   {
-    \cs_if_exist_use:cF { __text_change_case_title_ #3 :nnnn }
+    \cs_if_exist_use:cF { __text_change_case_title_ #4 :nnnnn }
       {
-        \cs_if_exist_use:cF { __text_change_case_upper_ #3 :nnnn }
-          { \__text_change_case_codepoint:nnnn }
+        \cs_if_exist_use:cF { __text_change_case_upper_ #4 :nnnnn }
+          { \__text_change_case_codepoint:nnnnn }
       }
-        {#1} {#2} {#3} {#4}
+        {#1} {#2} {#3} {#4} {#5}
   }
-\cs_new:Npn \__text_change_case_codepoint:nnnn #1#2#3#4
+\cs_new:Npn \__text_change_case_codepoint:nnnnn #1#2#3#4#5
   {
     \bool_lazy_and:nnTF
-      { \tl_if_single_p:n {#4} }
-      { \token_if_active_p:N #4 }
-      { \__text_change_case_store:n {#4} }
+      { \tl_if_single_p:n {#5} }
+      { \token_if_active_p:N #5 }
+      { \__text_change_case_store:n {#5} }
       {
         \__text_change_case_store:e
-          { \__text_change_case_codepoint:nn {#1} {#4} }
+          { \__text_change_case_codepoint:nn {#1} {#5} }
       }
-    \use:c { __text_change_case_next_ #2 :nn } {#2} {#3}
+    \use:c { __text_change_case_next_ #2 :nnn } {#2} {#3} {#4}
   }
 \cs_new:Npn \__text_change_case_codepoint:nn #1#2
   {
@@ -33540,16 +34165,14 @@
           }
       }
   }
-\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_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:Npn \__text_change_case_next_lower:nnn #1#2#3
+  { \__text_change_case_loop:nnnw {#1} {#2} {#3} }
+\cs_new_eq:NN \__text_change_case_next_upper:nnn
+  \__text_change_case_next_lower:nnn
+\cs_new_eq:NN \__text_change_case_next_title:nnn
+  \__text_change_case_next_lower:nnn
+\cs_new:Npn \__text_change_case_next_end:nnn #1#2#3
+  { \__text_change_case_skip:nnw {#2} {#3} }
 \cs_new_protected:Npn \text_declare_case_equivalent:Nn #1#2
   {
     \tl_clear_new:c { l__text_case_ \token_to_str:N #1 _tl }
@@ -33586,6 +34209,7 @@
   {
     \tl_clear_new:c { l__text_ #2 case _ #1 _ #3 _tl }
     \tl_set:cn { l__text_ #2 case _ #1 _ #3 _ tl } {#4}
+    \tl_clear_new:c { l__text_ #2 case_special_ #3 _tl }
   }
 \cs_new:Npn \text_case_switch:nnnn #1#2#3#4
   {
@@ -33595,48 +34219,48 @@
 \cs_new:Npn \__text_case_switch_marker: { }
 \cs_new:Npn \__text_change_case_generate:n #1
   { \codepoint_generate:nn {#1} { \char_value_catcode:n {#1} } }
-\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:nnnnn } #1#2#3#4#5
   {
-    \__text_codepoint_compare:nNnTF {#4} = { "00DF }
+    \__text_codepoint_compare:nNnTF {#5} = { "00DF }
       {
         \__text_change_case_store:e
          {
            \codepoint_generate:nn { "1E9E }
-             { \__text_change_case_catcode:nn {#4} { "1E9E } }
+             { \__text_change_case_catcode:nn {#5} { "1E9E } }
          }
-        \use:c { __text_change_case_next_ #2 :nn }
-          {#2} {#3}
+        \use:c { __text_change_case_next_ #2 :nnn }
+          {#2} {#3} {#4}
       }
-      { \__text_change_case_codepoint:nnnn {#1} {#2} {#3} {#4} }
+      { \__text_change_case_codepoint:nnnnn {#1} {#2} {#3} {#4} {#5} }
   }
-\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_eq:cc { __text_change_case_upper_de-alt:nnnnn }
+  { __text_change_case_upper_de-x-eszett:nnnnn }
+\cs_new:Npn \__text_change_case_upper_el:nnnnn #1#2#3#4#5
   {
     \bool_lazy_and:nnTF
-      { \__text_change_case_if_greek_p:n {#4} }
+      { \__text_change_case_if_greek_p:n {#5} }
       {
         ! \bool_lazy_or_p:nn
-          { \__text_codepoint_compare_p:nNn {#4} = { "0374 } }
-          { \__text_codepoint_compare_p:nNn {#4} = { "037E } }
+          { \__text_codepoint_compare_p:nNn {#5} = { "0374 } }
+          { \__text_codepoint_compare_p:nNn {#5} = { "037E } }
       }
       {
-        \__text_change_case_if_greek_spacing_diacritic:nTF {#4}
+        \__text_change_case_if_greek_spacing_diacritic:nTF {#5}
           {
-            \__text_change_case_store:n {#4}
-            \__text_change_case_loop:nnw
+            \__text_change_case_store:n {#5}
+            \__text_change_case_loop:nnnw
           }
           {
-            \exp_args:Ne \__text_change_case_upper_el:nnn
+            \exp_args:Ne \__text_change_case_upper_el:nnnn
               {
                 \codepoint_to_nfd:n
-                  { \__text_codepoint_from_chars:Nw #4 }
+                  { \__text_codepoint_from_chars:Nw #5 }
               }
           }
-            {#2} {#3}
+            {#2} {#3} {#4}
       }
       {
-        \__text_codepoint_compare:nNnTF {#4} = { "0345 }
+        \__text_codepoint_compare:nNnTF {#5} = { "0345 }
           {
             \__text_change_case_store:e
               {
@@ -33643,122 +34267,122 @@
                 \codepoint_generate:nn { "0399 }
                   { \char_value_catcode:n { "0399 } }
               }
-            \__text_change_case_loop:nnw {#2} {#3}
+            \__text_change_case_loop:nnnw {#2} {#3} {#4}
           }
-          { \__text_change_case_codepoint:nnnn {#1} {#2} {#3} {#4} }
+          { \__text_change_case_codepoint:nnnnn {#1} {#2} {#3} {#4} {#5} }
       }
   }
-\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:nnn #1#2#3
+\cs_new_eq:cN { __text_change_case_upper_el-x-iota:nnnnn }
+  \__text_change_case_upper_el:nnnnn
+\cs_new:Npn \__text_change_case_upper_el:nnnn #1#2#3#4
   {
     \__text_codepoint_process:nN
-      { \__text_change_case_upper_el:nnnw {#2} {#3} } #1
+      { \__text_change_case_upper_el:nnnnw {#2} {#3} {#4} } #1
   }
-\cs_new:Npn \__text_change_case_upper_el:nnnw #1#2#3#4 \q__text_recursion_stop
+\cs_new:Npn \__text_change_case_upper_el:nnnnw #1#2#3#4#5 \q__text_recursion_stop
   {
-    \tl_if_head_is_N_type:nTF {#4}
-      { \__text_change_case_upper_el:nnnN {#3} }
+    \tl_if_head_is_N_type:nTF {#5}
+      { \__text_change_case_upper_el:nnnnN {#4} }
       {
         \__text_change_case_store:e
-          { \__text_change_case_codepoint:nn { upper } {#3} }
-        \__text_change_case_loop:nnw
+          { \__text_change_case_codepoint:nn { upper } {#4} }
+        \__text_change_case_loop:nnnw
       }
-        {#1} {#2} #4 \q__text_recursion_stop
+        {#1} {#2} {#3} #5 \q__text_recursion_stop
   }
-\cs_new:Npn \__text_change_case_upper_el:nnnN #1#2#3#4
+\cs_new:Npn \__text_change_case_upper_el:nnnnN #1#2#3#4#5
   {
-    \token_if_cs:NTF #4
+    \token_if_cs:NTF #5
       {
         \__text_change_case_store:e
           { \__text_change_case_codepoint:nn { upper } {#1} }
-        \__text_change_case_loop:nnw {#2} {#3} #4
+        \__text_change_case_loop:nnnw {#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_ypogegrammeni:nnnnnnw
+              {#1} {#2} {#3} {#4} { } { } #5
           }
-          { \__text_change_case_upper_el_aux:nnnN {#1} {#2} {#3} #4 }
+          { \__text_change_case_upper_el_aux:nnnnN {#1} {#2} {#3} {#4} #5 }
       }
   }
-\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:nnnnnnw
+  #1#2#3#4#5#6#7 \q__text_recursion_stop
   {
-    \tl_if_head_is_N_type:nTF {#6}
+    \tl_if_head_is_N_type:nTF {#7}
       {
-        \__text_change_case_upper_el_ypogegrammeni:nnnnnN
-          {#1} {#2} {#3} {#4} {#5}
+        \__text_change_case_upper_el_ypogegrammeni:nnnnnnN
+          {#1} {#2} {#3} {#4} {#5} {#6}
       }
-      { \__text_change_case_upper_el_aux:nnnN {#1} {#2} {#3} #4#5 }
-        #6 \q__text_recursion_stop
+      { \__text_change_case_upper_el_aux:nnnnN {#1} {#2} {#3} {#4} #5#6 }
+        #7 \q__text_recursion_stop
   }
-\cs_new:Npn \__text_change_case_upper_el_ypogegrammeni:nnnnnN #1#2#3#4#5#6
+\cs_new:Npn \__text_change_case_upper_el_ypogegrammeni:nnnnnnN #1#2#3#4#5#6#7
   {
-    \token_if_cs:NTF #6
-      { \__text_change_case_upper_el_aux:nnnN {#1} {#2} {#3} #4#5 }
+    \token_if_cs:NTF #7
+      { \__text_change_case_upper_el_aux:nnnnN {#1} {#2} {#3} {#4} #5#6 }
       {
         \__text_codepoint_process:nN
           {
-            \__text_change_case_upper_el_ypogegrammeni:nnnnnn
-              {#1} {#2} {#3} {#4} {#5}
+            \__text_change_case_upper_el_ypogegrammeni:nnnnnnn
+              {#1} {#2} {#3} {#4} {#5} {#6}
           }
       }
-        #6
+        #7
   }
-\cs_new:Npn \__text_change_case_upper_el_ypogegrammeni:nnnnnn #1#2#3#4#5#6
+\cs_new:Npn \__text_change_case_upper_el_ypogegrammeni:nnnnnnn #1#2#3#4#5#6#7
   {
-    \__text_codepoint_compare:nNnTF {#6} = { "0345 }
+    \__text_codepoint_compare:nNnTF {#7} = { "0345 }
       {
-        \__text_change_case_upper_el_ypogegrammeni:nnnnnw
-          {#1} {#2} {#3} {#4} {#6}
+        \__text_change_case_upper_el_ypogegrammeni:nnnnnnw
+          {#1} {#2} {#3} {#4} {#5} {#7}
       }
       {
         \bool_lazy_or:nnTF
-          { \__text_change_case_if_greek_accent_p:n {#6} }
-          { \__text_change_case_if_greek_breathing_p:n {#6} }
+          { \__text_change_case_if_greek_accent_p:n {#7} }
+          { \__text_change_case_if_greek_breathing_p:n {#7} }
           {
-            \__text_change_case_upper_el_ypogegrammeni:nnnnnw
-              {#1} {#2} {#3} {#4#6} {#5}
+            \__text_change_case_upper_el_ypogegrammeni:nnnnnnw
+              {#1} {#2} {#3} {#4} {#5#7} {#6}
           }
-          { \__text_change_case_upper_el_aux:nnnN {#1} {#2} {#3} #4#5 #6 }
+          { \__text_change_case_upper_el_aux:nnnnN {#1} {#2} {#3} {#4} #5#6 #7 }
       }
   }
-\cs_new:Npn \__text_change_case_upper_el_aux:nnnN #1#2#3#4
+\cs_new:Npn \__text_change_case_upper_el_aux:nnnnN #1#2#3#4#5
   {
     \__text_codepoint_process:nN
-      { \__text_change_case_upper_el_aux:nnnn {#1} {#2} {#3} } #4
+      { \__text_change_case_upper_el_aux:nnnnn {#1} {#2} {#3} {#4} } #5
   }
-\cs_new:Npn \__text_change_case_upper_el_aux:nnnn #1#2#3#4
+\cs_new:Npn \__text_change_case_upper_el_aux:nnnnn #1#2#3#4#5
   {
-    \__text_codepoint_compare:nNnTF {#4} = { "0308 }
-      { \__text_change_case_upper_el_dialytika:nnn {#2} {#3} {#1} }
+    \__text_codepoint_compare:nNnTF {#5} = { "0308 }
+      { \__text_change_case_upper_el_dialytika:nnnn {#2} {#3} {#4} {#1} }
       {
-        \__text_change_case_if_greek_accent:nTF {#4}
-          { \__text_change_case_upper_el_hiatus:nnnw {#2} {#3} {#1} }
+        \__text_change_case_if_greek_accent:nTF {#5}
+          { \__text_change_case_upper_el_hiatus:nnnnw {#2} {#3} {#4} {#1} }
           {
-            \__text_change_case_if_greek_breathing:nTF {#4}
-              { \__text_change_case_upper_el:nnn {#1} {#2} {#3} }
+            \__text_change_case_if_greek_breathing:nTF {#5}
+              { \__text_change_case_upper_el:nnnn {#1} {#2} {#3} {#4} }
               {
-                \__text_codepoint_compare:nNnTF {#4} = { "0345 }
+                \__text_codepoint_compare:nNnTF {#5} = { "0345 }
                   {
                     \__text_change_case_store:e
-                      { \use:c { __text_change_case_upper_ #3 _ypogegrammeni:n } {#1} }
-                    \__text_change_case_loop:nnw {#2} {#3}
+                      { \use:c { __text_change_case_upper_ #4 _ypogegrammeni:n } {#1} }
+                    \__text_change_case_loop:nnnw {#2} {#3} {#4}
                   }
                   {
-                    \__text_change_case_if_greek_stress:nTF {#4}
+                    \__text_change_case_if_greek_stress:nTF {#5}
                       {
                         \__text_change_case_store:e
-                          { \__text_change_case_upper_el_stress:nn {#1} {#4} }
-                        \__text_change_case_loop:nnw {#2} {#3}
+                          { \__text_change_case_upper_el_stress:nn {#1} {#5} }
+                        \__text_change_case_loop:nnnw {#2} {#3} {#4}
 
                       }
                       {
                         \__text_change_case_store:e
                           { \__text_change_case_codepoint:nn { upper } {#1} }
-                        \__text_change_case_loop:nnw {#2} {#3} #4
+                        \__text_change_case_loop:nnnw {#2} {#3} {#4} #5
                       }
                   }
               }
@@ -33765,15 +34389,15 @@
           }
       }
   }
-\cs_new:Npn \__text_change_case_upper_el_dialytika:nnn #1#2#3
+\cs_new:Npn \__text_change_case_upper_el_dialytika:nnnn #1#2#3#4
   {
-    \__text_change_case_if_takes_dialytika:nTF {#3}
-      { \__text_change_case_upper_el_dialytika:n {#3} }
+    \__text_change_case_if_takes_dialytika:nTF {#4}
+      { \__text_change_case_upper_el_dialytika:n {#4} }
       {
         \__text_change_case_store:e
-          { \__text_change_case_codepoint:nn { upper } {#3} }
+          { \__text_change_case_codepoint:nn { upper } {#4} }
       }
-    \__text_change_case_upper_el_gobble:nnw {#1} {#2}
+    \__text_change_case_upper_el_gobble:nnnw {#1} {#2} {#3}
   }
 \cs_new:Npn \__text_change_case_upper_el_dialytika:n #1
   {
@@ -33792,41 +34416,41 @@
           }
       }
   }
-\cs_new:Npn \__text_change_case_upper_el_hiatus:nnnw
-  #1#2#3#4 \q__text_recursion_stop
+\cs_new:Npn \__text_change_case_upper_el_hiatus:nnnnw
+  #1#2#3#4#5 \q__text_recursion_stop
   {
-    \tl_if_head_is_N_type:nTF {#4}
-      { \__text_change_case_upper_el_hiatus:nnnN {#3} }
+    \tl_if_head_is_N_type:nTF {#5}
+      { \__text_change_case_upper_el_hiatus:nnnnN {#4} }
       {
         \__text_change_case_store:e
-          { \__text_change_case_codepoint:nn { upper } {#3} }
-        \__text_change_case_loop:nnw
+          { \__text_change_case_codepoint:nn { upper } {#4} }
+        \__text_change_case_loop:nnnw
       }
-        {#1} {#2} #4 \q__text_recursion_stop
+        {#1} {#2} {#3} #5 \q__text_recursion_stop
   }
-\cs_new:Npn \__text_change_case_upper_el_hiatus:nnnN #1#2#3#4
+\cs_new:Npn \__text_change_case_upper_el_hiatus:nnnnN #1#2#3#4#5
   {
-    \token_if_cs:NTF #4
+    \token_if_cs:NTF #5
       {
         \__text_change_case_store:e
           { \__text_change_case_codepoint:nn { upper } {#1} }
-        \__text_change_case_loop:nnw {#2} {#3} #4
+        \__text_change_case_loop:nnnw {#2} {#3} {#4} #5
       }
       {
         \__text_codepoint_process:nN
-          { \__text_change_case_upper_el_hiatus:nnnn {#1} {#2} {#3} } #4
+          { \__text_change_case_upper_el_hiatus:nnnnn {#1} {#2} {#3} {#4} } #5
       }
   }
-\cs_new:Npn \__text_change_case_upper_el_hiatus:nnnn #1#2#3#4
+\cs_new:Npn \__text_change_case_upper_el_hiatus:nnnnn #1#2#3#4#5
   {
-    \__text_change_case_if_takes_dialytika:nTF {#4}
+    \__text_change_case_if_takes_dialytika:nTF {#5}
       {
         \__text_change_case_store:e
           { \__text_change_case_codepoint:nn { upper } {#1} }
-        \__text_change_case_upper_el_dialytika:n {#4}
-        \__text_change_case_upper_el_gobble:nnw {#2} {#3}
+        \__text_change_case_upper_el_dialytika:n {#5}
+        \__text_change_case_upper_el_gobble:nnnw {#2} {#3} {#4}
       }
-      { \__text_change_case_upper_el:nnn {#1} {#2} {#3} #4 }
+      { \__text_change_case_upper_el:nnnn {#1} {#2} {#3} {#4} #5 }
   }
 \cs_new:Npn \__text_change_case_upper_el_ypogegrammeni:n #1
   {
@@ -33884,31 +34508,31 @@
           }
       }
   }
-\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_gobble:nnnw
+  #1#2#3#4 \q__text_recursion_stop
   {
-    \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
+    \tl_if_head_is_N_type:nTF {#4}
+      { \__text_change_case_upper_el_gobble:nnnN }
+      { \__text_change_case_loop:nnnw }
+        {#1} {#2} {#3} #4 \q__text_recursion_stop
   }
-\cs_new:Npn \__text_change_case_upper_el_gobble:nnN #1#2#3
+\cs_new:Npn \__text_change_case_upper_el_gobble:nnnN #1#2#3#4
   {
-    \token_if_cs:NTF #3
-      { \__text_change_case_loop:nnw {#1} {#2} }
+    \token_if_cs:NTF #4
+      { \__text_change_case_loop:nnnw {#1} {#2} {#3} }
       {
         \__text_codepoint_process:nN
-          { \__text_change_case_upper_el_gobble:nnn {#1} {#2} }
+          { \__text_change_case_upper_el_gobble:nnnn {#1} {#2} {#3} }
       }
-        #3
+        #4
   }
-\cs_new:Npn \__text_change_case_upper_el_gobble:nnn #1#2#3
+\cs_new:Npn \__text_change_case_upper_el_gobble:nnnn #1#2#3#4
   {
     \bool_lazy_or:nnTF
-      { \__text_change_case_if_greek_accent_p:n {#3} }
-      { \__text_change_case_if_greek_breathing_p:n {#3} }
-      { \__text_change_case_upper_el_gobble:nnw {#1} {#2} }
-      { \__text_change_case_loop:nnw {#1} {#2} #3 }
+      { \__text_change_case_if_greek_accent_p:n {#4} }
+      { \__text_change_case_if_greek_breathing_p:n {#4} }
+      { \__text_change_case_upper_el_gobble:nnnw {#1} {#2} {#3} }
+      { \__text_change_case_loop:nnnw {#1} {#2} {#3} #4 }
   }
 \prg_new_conditional:Npnn \__text_change_case_if_greek:n #1 { p , TF }
   {
@@ -34132,192 +34756,192 @@
       \fi:
     \fi:
   }
-\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:Nnnnw
+  #1#2#3#4#5 \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
+    \tl_if_head_is_N_type:nTF {#5}
+      { \__text_change_case_boundary_upper_el:nnnN }
+      { \__text_change_case_loop:nnnw }
+        {#2} {#3} {#4} #5 \q__text_recursion_stop
   }
-\cs_new_eq:cN { __text_change_case_boundary_upper_el-x-iota:Nnnw }
-  \__text_change_case_boundary_upper_el:Nnnw
-\cs_new:Npn \__text_change_case_boundary_upper_el:nnN #1#2#3
+\cs_new_eq:cN { __text_change_case_boundary_upper_el-x-iota:Nnnnw }
+  \__text_change_case_boundary_upper_el:Nnnnw
+\cs_new:Npn \__text_change_case_boundary_upper_el:nnnN #1#2#3#4
   {
-    \token_if_cs:NTF #3
-      { \__text_change_case_loop:nnw {#1} {#2} }
+    \token_if_cs:NTF #4
+      { \__text_change_case_loop:nnnw {#1} {#2} {#3} }
       {
         \__text_codepoint_process:nN
-          { \__text_change_case_boundary_upper_el:nnn {#1} {#2} }
+          { \__text_change_case_boundary_upper_el:nnnn {#1} {#2} {#3} }
       }
-        #3
+        #4
   }
-\cs_new:Npn \__text_change_case_boundary_upper_el:nnn #1#2#3
+\cs_new:Npn \__text_change_case_boundary_upper_el:nnnn #1#2#3#4
   {
     \bool_lazy_any:nTF
       {
-        { \__text_codepoint_compare_p:nNn {#3} = { "0389 } }
-        { \__text_codepoint_compare_p:nNn {#3} = { "03AE } }
-        { \__text_codepoint_compare_p:nNn {#3} = { "1F22 } }
-        { \__text_codepoint_compare_p:nNn {#3} = { "1F2A } }
+        { \__text_codepoint_compare_p:nNn {#4} = { "0389 } }
+        { \__text_codepoint_compare_p:nNn {#4} = { "03AE } }
+        { \__text_codepoint_compare_p:nNn {#4} = { "1F22 } }
+        { \__text_codepoint_compare_p:nNn {#4} = { "1F2A } }
       }
-      { \__text_change_case_boundary_upper_el:nnnw {#1} {#2} {#3} }
-      { \__text_change_case_breathing:nnn {#1} {#2} {#3} }
+      { \__text_change_case_boundary_upper_el:nnnnw {#1} {#2} {#3} {#4} }
+      { \__text_change_case_breathing:nnnn {#1} {#2} {#3} {#4} }
   }
-\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:nnnnw
+  #1#2#3#4#5 \q__text_recursion_stop
   {
-    \tl_if_head_is_N_type:nTF {#4}
-      { \__text_change_case_loop:nnw {#1} {#2} #3 }
+    \tl_if_head_is_N_type:nTF {#5}
+      { \__text_change_case_loop:nnnw {#1} {#2} {#3} #4 }
       {
         \__text_change_case_store:e
           {
             \codepoint_generate:nn { "0389 }
-              { \__text_change_case_catcode:nn {#3} { "0389 } }
+              { \__text_change_case_catcode:nn {#4} { "0389 } }
           }
-        \__text_change_case_loop:nnw {#1} {#2}
+        \__text_change_case_loop:nnnw {#1} {#2} {#3}
       }
-        #4 \q__text_recursion_stop
+        #5 \q__text_recursion_stop
   }
-\cs_new:Npn \__text_change_case_breathing:nnn #1#2#3
+\cs_new:Npn \__text_change_case_breathing:nnnn #1#2#3#4
   {
-    \__text_change_case_if_greek:nTF {#3}
+    \__text_change_case_if_greek:nTF {#4}
       {
-        \exp_args:Ne \__text_change_case_breathing:nnnn
+        \exp_args:Ne \__text_change_case_breathing:nnnnn
           {
             \codepoint_to_nfd:n
-              { \__text_codepoint_from_chars:Nw #3 }
+              { \__text_codepoint_from_chars:Nw #4 }
           }
-            {#1} {#2} {#3}
+            {#1} {#2} {#3} {#4}
       }
-      { \__text_change_case_loop:nnw {#1} {#2} #3 }
+      { \__text_change_case_loop:nnnw {#1} {#2} {#3} #4 }
   }
-\cs_new:Npn \__text_change_case_breathing:nnnn #1#2#3#4
+\cs_new:Npn \__text_change_case_breathing:nnnnn #1#2#3#4#5
   {
     \__text_codepoint_process:nN
-      { \__text_change_case_breathing:nnnnw {#2} {#3} {#4} }
+      { \__text_change_case_breathing:nnnnnw {#2} {#3} {#4} {#5} }
         #1 \q_mark
   }
-\cs_new:Npn \__text_change_case_breathing:nnnnw #1#2#3#4#5 \q_mark
+\cs_new:Npn \__text_change_case_breathing:nnnnnw #1#2#3#4#5#6 \q_mark
   {
-    \tl_if_blank:nTF {#5}
-      { \__text_change_case_loop:nnw {#1} {#2} #3 }
+    \tl_if_blank:nTF {#6}
+      { \__text_change_case_loop:nnnw {#1} {#2} {#3} #4 }
       {
         \__text_codepoint_process:nN
-          { \__text_change_case_breathing:nnnnnw {#1} {#2} {#3} {#4} }
-            #5 \q_mark
+          { \__text_change_case_breathing:nnnnnnw {#1} {#2} {#3} {#4} {#5} }
+            #6 \q_mark
       }
   }
-\cs_new:Npn \__text_change_case_breathing:nnnnnw #1#2#3#4#5#6 \q_mark
+\cs_new:Npn \__text_change_case_breathing:nnnnnnw #1#2#3#4#5#6#7 \q_mark
   {
-    \tl_if_blank:nTF {#6}
+    \tl_if_blank:nTF {#7}
      {
-       \__text_change_case_breathing_aux:nnnnn
-         {#1} {#2} {#3} {#4} {#5}
+       \__text_change_case_breathing_aux:nnnnnn
+         {#1} {#2} {#3} {#4} {#5} {#6}
      }
      {
        \__text_codepoint_process:nN
-         { \__text_change_case_breathing:nnnnnw {#1} {#2} {#3} {#4} }
-           #6 \q_mark
+         { \__text_change_case_breathing:nnnnnnw {#1} {#2} {#3} {#4} {#5} }
+           #7 \q_mark
      }
   }
-\cs_new:Npn \__text_change_case_breathing_aux:nnnnn #1#2#3#4#5
+\cs_new:Npn \__text_change_case_breathing_aux:nnnnnn #1#2#3#4#5#6
   {
     \bool_lazy_or:nnTF
-      { \__text_codepoint_compare_p:nNn {#5} = { "0313 } }
-      { \__text_codepoint_compare_p:nNn {#5} = { "0314 } }
-      { \__text_change_case_breathing_aux:nnnw {#1} {#2} {#4} }
-      { \__text_change_case_loop:nnw {#1} {#2} #3 }
+      { \__text_codepoint_compare_p:nNn {#6} = { "0313 } }
+      { \__text_codepoint_compare_p:nNn {#6} = { "0314 } }
+      { \__text_change_case_breathing_aux:nnnnw {#1} {#2} {#3} {#5} }
+      { \__text_change_case_loop:nnnw {#1} {#2} {#3} #4 }
   }
-\cs_new:Npn \__text_change_case_breathing_aux:nnnw #1#2#3#4
+\cs_new:Npn \__text_change_case_breathing_aux:nnnnw #1#2#3#4#5
   \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_breathing_aux:nnN  }
-      { \__text_change_case_loop:nnw }
-        {#1} {#2} #4 \q__text_recursion_stop
+      { \__text_change_case_codepoint:nn { upper } {#4} }
+    \tl_if_head_is_N_type:nTF {#5}
+      { \__text_change_case_breathing_aux:nnnN }
+      { \__text_change_case_loop:nnnw }
+        {#1} {#2} {#3} #5 \q__text_recursion_stop
   }
-\cs_new:Npn \__text_change_case_breathing_aux:nnN #1#2#3
+\cs_new:Npn \__text_change_case_breathing_aux:nnnN #1#2#3#4
   {
     \__text_codepoint_process:nN
-      { \__text_change_case_breathing_dialytika:nnn {#1} {#2} } #3
+      { \__text_change_case_breathing_dialytika:nnnn {#1} {#2} {#3} } #4
   }
-\cs_new:Npn \__text_change_case_breathing_dialytika:nnn #1#2#3
+\cs_new:Npn \__text_change_case_breathing_dialytika:nnnn #1#2#3#4
   {
-     \__text_change_case_if_takes_dialytika:nTF {#3}
+     \__text_change_case_if_takes_dialytika:nTF {#4}
        {
-         \__text_change_case_upper_el_dialytika:n {#3}
-         \__text_change_case_loop:nnw {#1} {#2}
+         \__text_change_case_upper_el_dialytika:n {#4}
+         \__text_change_case_loop:nnnw {#1} {#2} {#3}
        }
-       { \__text_change_case_loop:nnw {#1} {#2} #3 }
+       { \__text_change_case_loop:nnnw {#1} {#2} {#3} #4 }
   }
-\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:nnnnn #1#2#3#4#5
+  { \__text_change_case_codepoint:nnnnn {#1} {#2} {#3} {#4} {#5} }
+\cs_new:Npn \__text_change_case_upper_hy:nnnnn #1#2#3#4#5
   {
-    \__text_codepoint_compare:nNnTF {#4} = { "0587 }
+    \__text_codepoint_compare:nNnTF {#5} = { "0587 }
       {
         \__text_change_case_store:e
           {
             \codepoint_generate:nn { "0535 }
-              { \__text_change_case_catcode:nn {#4} { "0535 } }
+              { \__text_change_case_catcode:nn {#5} { "0535 } }
             \codepoint_generate:nn { "054E }
-              { \__text_change_case_catcode:nn {#4} { "054E } }
+              { \__text_change_case_catcode:nn {#5} { "054E } }
           }
-        \use:c { __text_change_case_next_ #2 :nn }
-          {#2} {#3}
+        \use:c { __text_change_case_next_ #2 :nnn }
+          {#2} {#3} {#4}
       }
-      { \__text_change_case_codepoint:nnnn {#1} {#2} {#3} {#4} }
+      { \__text_change_case_codepoint:nnnnn {#1} {#2} {#3} {#4} {#5} }
   }
-\cs_new:Npn \__text_change_case_title_hy:nnnn #1#2#3#4
+\cs_new:Npn \__text_change_case_title_hy:nnnnn #1#2#3#4#5
   {
-    \__text_codepoint_compare:nNnTF {#4} = { "0587 }
+    \__text_codepoint_compare:nNnTF {#5} = { "0587 }
       {
         \__text_change_case_store:e
           {
             \codepoint_generate:nn { "0535 }
-              { \__text_change_case_catcode:nn {#4} { "0535 } }
+              { \__text_change_case_catcode:nn {#5} { "0535 } }
             \codepoint_generate:nn { "057E }
-              { \__text_change_case_catcode:nn {#4} { "057E } }
+              { \__text_change_case_catcode:nn {#5} { "057E } }
           }
-        \use:c { __text_change_case_next_ #2 :nn }
-          {#2} {#3}
+        \use:c { __text_change_case_next_ #2 :nnn }
+          {#2} {#3} {#4}
       }
-      { \__text_change_case_codepoint:nnnn {#1} {#2} {#3} {#4} }
+      { \__text_change_case_codepoint:nnnnn {#1} {#2} {#3} {#4} {#5} }
   }
-\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
+\cs_new:cpn { __text_change_case_upper_hy-x-yiwn:nnnnn } #1#2#3#4#5
+  { \__text_change_case_codepoint:nnnnn {#1} {#2} {#3} {#4} {#5} }
+\cs_new_eq:cc { __text_change_case_title_hy-x-yiwn:nnnnn }
+  { __text_change_case_upper_hy-x-yiwn:nnnnn }
+\cs_new:cpn { __text_change_case_lower_la-x-medieval:nnnnn } #1#2#3#4#5
   {
-    \__text_codepoint_compare:nNnTF {#4} = { "0056 }
+    \__text_codepoint_compare:nNnTF {#5} = { "0056 }
       {
         \__text_change_case_store:e
-          { \char_generate:nn { "0075 } { \__text_char_catcode:N #4 } }
-        \use:c { __text_change_case_next_ #2 :nn }
-          {#2} {#3}
+          { \char_generate:nn { "0075 } { \__text_char_catcode:N #5 } }
+        \use:c { __text_change_case_next_ #2 :nnn }
+          {#2} {#3} {#4}
       }
-      { \__text_change_case_codepoint:nnnn {#1} {#2} {#3} {#4} }
+      { \__text_change_case_codepoint:nnnnn {#1} {#2} {#3} {#4} {#5} }
   }
-\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:nnnnn } #1#2#3#4#5
   {
-    \__text_codepoint_compare:nNnTF {#4} = { "0075 }
+    \__text_codepoint_compare:nNnTF {#5} = { "0075 }
       {
         \__text_change_case_store:e
-          { \char_generate:nn { "0056 } { \__text_char_catcode:N #4 } }
-        \use:c { __text_change_case_next_ #2 :nn }
-          {#2} {#3}
+          { \char_generate:nn { "0056 } { \__text_char_catcode:N #5 } }
+        \use:c { __text_change_case_next_ #2 :nnn }
+          {#2} {#3} {#4}
       }
-      { \__text_change_case_codepoint:nnnn {#1} {#2} {#3} {#4} }
+      { \__text_change_case_codepoint:nnnnn {#1} {#2} {#3} {#4} {#5} }
   }
-\cs_new:Npn \__text_change_case_lower_lt:nnnn #1#2#3#4
+\cs_new:Npn \__text_change_case_lower_lt:nnnnn #1#2#3#4#5
   {
-    \exp_args:Ne \__text_change_case_lower_lt_auxi:nnnn
+    \exp_args:Ne \__text_change_case_lower_lt_auxi:nnnnn
       {
-        \int_case:nn { \__text_codepoint_from_chars:Nw #4 }
+        \int_case:nn { \__text_codepoint_from_chars:Nw #5 }
           {
             { "00CC } { "0300 }
             { "00CD } { "0301 }
@@ -34324,15 +34948,15 @@
             { "0128 } { "0303 }
           }
       }
-        {#2} {#3} {#4}
+        {#2} {#3} {#4} {#5}
   }
-\cs_new:Npn \__text_change_case_lower_lt_auxi:nnnn #1#2#3#4
+\cs_new:Npn \__text_change_case_lower_lt_auxi:nnnnn #1#2#3#4#5
   {
     \tl_if_blank:nTF {#1}
       {
-        \exp_args:Ne \__text_change_case_lower_lt_auxii:nnnn
+        \exp_args:Ne \__text_change_case_lower_lt_auxii:nnnnn
           {
-            \int_case:nn { \__text_codepoint_from_chars:Nw #4 }
+            \int_case:nn { \__text_codepoint_from_chars:Nw #5 }
               {
                 { "0049 } { "0069 }
                 { "004A } { "006A }
@@ -34339,60 +34963,60 @@
                 { "012E } { "012F }
               }
           }
-            {#2} {#3} {#4}
+            {#2} {#3} {#4} {#5}
       }
       {
         \__text_change_case_store:e
           {
             \codepoint_generate:nn { "0069 }
-              { \__text_change_case_catcode:nn {#4} { "0069 } }
+              { \__text_change_case_catcode:nn {#5} { "0069 } }
             \codepoint_generate:nn { "0307 }
-              { \__text_change_case_catcode:nn {#4} { "0307 } }
+              { \__text_change_case_catcode:nn {#5} { "0307 } }
             \codepoint_generate:nn {#1}
-              { \__text_change_case_catcode:nn {#4} {#1} }
+              { \__text_change_case_catcode:nn {#5} {#1} }
           }
-        \__text_change_case_loop:nnw {#2} {#3}
+        \__text_change_case_loop:nnnw {#2} {#3} {#4}
       }
   }
-\cs_new:Npn \__text_change_case_lower_lt_auxii:nnnn #1#2#3#4
+\cs_new:Npn \__text_change_case_lower_lt_auxii:nnnnn #1#2#3#4#5
   {
     \tl_if_blank:nTF {#1}
-      { \__text_change_case_codepoint:nnnn {#2} {#2} {#3} {#4} }
+      { \__text_change_case_codepoint:nnnnn {#2} {#2} {#3} {#4} {#5} }
       {
         \__text_change_case_store:e
           {
             \codepoint_generate:nn {#1}
-              { \__text_change_case_catcode:nn {#4} {#1} }
+              { \__text_change_case_catcode:nn {#5} {#1} }
           }
-        \__text_change_case_lower_lt:nnw {#2} {#3}
+        \__text_change_case_lower_lt:nnnw {#2} {#3} {#4}
       }
   }
-\cs_new:Npn \__text_change_case_lower_lt:nnw #1#2#3 \q__text_recursion_stop
+\cs_new:Npn \__text_change_case_lower_lt:nnnw #1#2#3#4 \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
+    \tl_if_head_is_N_type:nTF {#4}
+      { \__text_change_case_lower_lt:nnnN }
+      { \__text_change_case_loop:nnnw }
+       {#1} {#2} {#3} #4 \q__text_recursion_stop
   }
-\cs_new:Npn \__text_change_case_lower_lt:nnN #1#2#3
+\cs_new:Npn \__text_change_case_lower_lt:nnnN #1#2#3#4
   {
     \__text_codepoint_process:nN
-      { \__text_change_case_lower_lt:nnn {#1} {#2} } #3
+      { \__text_change_case_lower_lt:nnnn {#1} {#2} {#3} } #4
   }
-\cs_new:Npn \__text_change_case_lower_lt:nnn #1#2#3
+\cs_new:Npn \__text_change_case_lower_lt:nnnn #1#2#3#4
   {
     \bool_lazy_and:nnT
       {
         \bool_lazy_or_p:nn
-          { ! \tl_if_single_p:n {#3} }
-          { ! \token_if_cs_p:N #3 }
+          { ! \tl_if_single_p:n {#4} }
+          { ! \token_if_cs_p:N #4 }
       }
       {
         \bool_lazy_any_p:n
           {
-            { \__text_codepoint_compare_p:nNn {#3} = { "0300 } }
-            { \__text_codepoint_compare_p:nNn {#3} = { "0301 } }
-            { \__text_codepoint_compare_p:nNn {#3} = { "0303 } }
+            { \__text_codepoint_compare_p:nNn {#4} = { "0300 } }
+            { \__text_codepoint_compare_p:nNn {#4} = { "0301 } }
+            { \__text_codepoint_compare_p:nNn {#4} = { "0303 } }
           }
       }
       {
@@ -34399,16 +35023,16 @@
         \__text_change_case_store:e
           {
             \codepoint_generate:nn { "0307 }
-              { \__text_change_case_catcode:nn {#3} { "0307 } }
+              { \__text_change_case_catcode:nn {#4} { "0307 } }
           }
       }
-    \__text_change_case_loop:nnw {#1} {#2} #3
+    \__text_change_case_loop:nnnw {#1} {#2} {#3} #4
   }
-\cs_new:Npn \__text_change_case_upper_lt:nnnn #1#2#3#4
+\cs_new:Npn \__text_change_case_upper_lt:nnnnn #1#2#3#4#5
  {
-    \exp_args:Ne \__text_change_case_upper_lt_aux:nnnn
+    \exp_args:Ne \__text_change_case_upper_lt_aux:nnnnn
       {
-        \int_case:nn { \__text_codepoint_from_chars:Nw #4 }
+        \int_case:nn { \__text_codepoint_from_chars:Nw #5 }
           {
             { "0069 } { "0049 }
             { "006A } { "004A }
@@ -34415,132 +35039,132 @@
             { "012F } { "012E }
           }
       }
-        {#2} {#3} {#4}
+        {#2} {#3} {#4} {#5}
   }
-\cs_new:Npn \__text_change_case_upper_lt_aux:nnnn #1#2#3#4
+\cs_new:Npn \__text_change_case_upper_lt_aux:nnnnn #1#2#3#4#5
   {
     \tl_if_blank:nTF {#1}
-      { \__text_change_case_codepoint:nnnn { upper } {#2} {#3} {#4} }
+      { \__text_change_case_codepoint:nnnnn { upper } {#2} {#3} {#4} {#5} }
       {
         \__text_change_case_store:e
           {
             \codepoint_generate:nn {#1}
-              { \__text_change_case_catcode:nn {#4} {#1} }
+              { \__text_change_case_catcode:nn {#5} {#1} }
           }
-        \__text_change_case_upper_lt:nnw {#2} {#3}
+        \__text_change_case_upper_lt:nnnw {#2} {#3} {#4}
       }
   }
-\cs_new:Npn \__text_change_case_upper_lt:nnw #1#2#3 \q__text_recursion_stop
+\cs_new:Npn \__text_change_case_upper_lt:nnnw #1#2#3#4 \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
+    \tl_if_head_is_N_type:nTF {#4}
+      { \__text_change_case_upper_lt:nnnN }
+      { \use:c { __text_change_case_next_ #1 :nnn } }
+        {#1} {#2} {#3} #4 \q__text_recursion_stop
   }
-\cs_new:Npn \__text_change_case_upper_lt:nnN #1#2#3
+\cs_new:Npn \__text_change_case_upper_lt:nnnN #1#2#3#4
   {
     \__text_codepoint_process:nN
-      { \__text_change_case_upper_lt:nnn {#1} {#2} } #3
+      { \__text_change_case_upper_lt:nnnn {#1} {#2} {#3} } #4
   }
-\cs_new:Npn \__text_change_case_upper_lt:nnn #1#2#3
+\cs_new:Npn \__text_change_case_upper_lt:nnnn #1#2#3#4
   {
     \bool_lazy_and:nnTF
       {
         \bool_lazy_or_p:nn
-          { ! \tl_if_single_p:n {#3} }
-          { ! \token_if_cs_p:N #3 }
+          { ! \tl_if_single_p:n {#4} }
+          { ! \token_if_cs_p:N #4 }
       }
-      { \__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 }
+      { \__text_codepoint_compare_p:nNn {#4} = { "0307 } }
+      { \use:c { __text_change_case_next_ #1 :nnn } {#1} {#2} {#3} }
+      { \use:c { __text_change_case_next_ #1 :nnn } {#1} {#2} {#3} #4 }
   }
-\cs_new:Npn \__text_change_case_title_nl:nnnn #1#2#3#4
+\cs_new:Npn \__text_change_case_title_nl:nnnnn #1#2#3#4#5
   {
-    \tl_if_single:nTF {#4}
-      { \__text_change_case_title_nl_aux:nnnn }
-      { \__text_change_case_codepoint:nnnn }
-        {#1} {#2} {#3} {#4}
+    \tl_if_single:nTF {#5}
+      { \__text_change_case_title_nl_aux:nnnnn }
+      { \__text_change_case_codepoint:nnnnn }
+        {#1} {#2} {#3} {#4}  {#5}
   }
-\cs_new:Npn \__text_change_case_title_nl_aux:nnnn #1#2#3#4
+\cs_new:Npn \__text_change_case_title_nl_aux:nnnnn #1#2#3#4#5
   {
     \bool_lazy_or:nnTF
-      { \int_compare_p:nNn {`#4} = { "0049 } }
-      { \int_compare_p:nNn {`#4} = { "0069 } }
+      { \int_compare_p:nNn {`#5} = { "0049 } }
+      { \int_compare_p:nNn {`#5} = { "0069 } }
       {
         \__text_change_case_store:e
-          { \char_generate:nn { "0049 } { \__text_char_catcode:N #4 } }
-        \__text_change_case_title_nl:nnw {#2} {#3}
+          { \char_generate:nn { "0049 } { \__text_char_catcode:N #5 } }
+        \__text_change_case_title_nl:nnnw {#2} {#3} {#4}
       }
-      { \__text_change_case_codepoint:nnnn {#1} {#2} {#3} {#4} }
+      { \__text_change_case_codepoint:nnnnn {#1} {#2} {#3} {#4} {#5} }
   }
-\cs_new:Npn \__text_change_case_title_nl:nnw #1#2#3 \q__text_recursion_stop
+\cs_new:Npn \__text_change_case_title_nl:nnnw #1#2#3#4 \q__text_recursion_stop
   {
-    \tl_if_head_is_N_type:nTF {#3}
-      { \__text_change_case_title_nl:nnN }
-      { \use:c { __text_change_case_next_ #1 :nn } }
-        {#1} {#2} #3 \q__text_recursion_stop
+    \tl_if_head_is_N_type:nTF {#4}
+      { \__text_change_case_title_nl:nnnN }
+      { \use:c { __text_change_case_next_ #1 :nnn } }
+        {#1} {#2} {#3} #4 \q__text_recursion_stop
   }
-\cs_new:Npn \__text_change_case_title_nl:nnN #1#2#3
+\cs_new:Npn \__text_change_case_title_nl:nnnN #1#2#3#4
   {
     \bool_lazy_and:nnTF
-      { ! \token_if_cs_p:N #3 }
+      { ! \token_if_cs_p:N #4 }
       {
         \bool_lazy_or_p:nn
-          { \int_compare_p:nNn {`#3} = { "004A } }
-          { \int_compare_p:nNn {`#3} = { "006A } }
+          { \int_compare_p:nNn {`#4} = { "004A } }
+          { \int_compare_p:nNn {`#4} = { "006A } }
       }
       {
         \__text_change_case_store:e
-          { \char_generate:nn { "004A } { \__text_char_catcode:N #3 } }
-        \use:c { __text_change_case_next_ #1 :nn } {#1} {#2}
+          { \char_generate:nn { "004A } { \__text_char_catcode:N #4 } }
+        \use:c { __text_change_case_next_ #1 :nnn } {#1} {#2} {#3}
       }
-      { \use:c { __text_change_case_next_ #1 :nn } {#1} {#2} #3 }
+      { \use:c { __text_change_case_next_ #1 :nnn } {#1} {#2} {#3} #4 }
   }
-\cs_new:Npn \__text_change_case_lower_tr:nnnn #1#2#3#4
+\cs_new:Npn \__text_change_case_lower_tr:nnnnn #1#2#3#4#5
   {
-    \__text_codepoint_compare:nNnTF {#4} = { "0049 }
-      { \__text_change_case_lower_tr:nnNw {#1} {#3} #4 }
+    \__text_codepoint_compare:nNnTF {#5} = { "0049 }
+      { \__text_change_case_lower_tr:nnnNw {#1} {#3} {#4} #5 }
       {
-        \__text_codepoint_compare:nNnTF {#4} = { "0130 }
+        \__text_codepoint_compare:nNnTF {#5} = { "0130 }
           {
             \__text_change_case_store:e
               {
                 \codepoint_generate:nn { "0069 }
-                  { \__text_change_case_catcode:nn {#4} { "0069 } }
+                  { \__text_change_case_catcode:nn {#5} { "0069 } }
               }
-            \__text_change_case_loop:nnw {#1} {#3}
+            \__text_change_case_loop:nnnw {#1} {#3} {#4}
           }
-          { \__text_change_case_codepoint:nnnn {#1} {#2} {#3} {#4} }
+          { \__text_change_case_codepoint:nnnnn {#1} {#2} {#3} {#4} {#5} }
       }
   }
-\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:nnnNw #1#2#3#4#5 \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 {#5}
+      { \__text_change_case_lower_tr:NnnnN  #4 {#1} {#2} {#3} }
       {
         \__text_change_case_store:e
           {
             \codepoint_generate:nn { "0131 }
-              { \__text_change_case_catcode:nn {#3} { "0131 } }
+              { \__text_change_case_catcode:nn {#4} { "0131 } }
           }
-        \__text_change_case_loop:nnw {#1} {#2}
+        \__text_change_case_loop:nnnw {#1} {#2} {#3}
       }
-        #4 \q__text_recursion_stop
+        #5 \q__text_recursion_stop
   }
-\cs_new:Npn \__text_change_case_lower_tr:NnnN #1#2#3#4
+\cs_new:Npn \__text_change_case_lower_tr:NnnnN #1#2#3#4#5
   {
     \__text_codepoint_process:nN
-      { \__text_change_case_lower_tr:Nnnn #1 {#2} {#3} } #4
+      { \__text_change_case_lower_tr:Nnnnn #1 {#2} {#3} {#4} } #5
   }
-\cs_new:Npn \__text_change_case_lower_tr:Nnnn #1#2#3#4
+\cs_new:Npn \__text_change_case_lower_tr:Nnnnn #1#2#3#4#5
   {
     \bool_lazy_or:nnTF
       {
         \bool_lazy_and_p:nn
-          { \tl_if_single_p:n {#4} }
-          { \token_if_cs_p:N #4 }
+          { \tl_if_single_p:n {#5} }
+          { \token_if_cs_p:N #5 }
       }
-      { ! \__text_codepoint_compare_p:nNn {#4} = { "0307 } }
+      { ! \__text_codepoint_compare_p:nNn {#5} = { "0307 } }
       {
         \__text_change_case_store:e
           {
@@ -34547,7 +35171,7 @@
             \codepoint_generate:nn { "0131 }
               { \__text_change_case_catcode:nn {#1} { "0131 } }
           }
-        \__text_change_case_loop:nnw {#2} {#3} #4
+        \__text_change_case_loop:nnnw {#2} {#3} {#4} #5
       }
       {
         \__text_change_case_store:e
@@ -34555,26 +35179,26 @@
             \codepoint_generate:nn { "0069 }
               { \__text_change_case_catcode:nn {#1} { "0069 } }
           }
-        \__text_change_case_loop:nnw {#2} {#3}
+        \__text_change_case_loop:nnnw {#2} {#3} {#4}
       }
   }
-\cs_new:Npn \__text_change_case_upper_tr:nnnn #1#2#3#4
+\cs_new:Npn \__text_change_case_upper_tr:nnnnn #1#2#3#4#5
   {
-    \__text_codepoint_compare:nNnTF {#4} = { "0069 }
+    \__text_codepoint_compare:nNnTF {#5} = { "0069 }
       {
         \__text_change_case_store:e
           {
             \codepoint_generate:nn { "0130 }
-              { \__text_change_case_catcode:nn {#4} { "0130 } }
+              { \__text_change_case_catcode:nn {#5} { "0130 } }
           }
-        \use:c { __text_change_case_next_ #2 :nn } {#2} {#3}
+        \use:c { __text_change_case_next_ #2 :nnn } {#2} {#3} {#4}
       }
-      { \__text_change_case_codepoint:nnnn {#1} {#2} {#3} {#4} }
+      { \__text_change_case_codepoint:nnnnn {#1} {#2} {#3} {#4} {#5} }
   }
-\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:nnnnn
+  \__text_change_case_lower_tr:nnnnn
+\cs_new_eq:NN \__text_change_case_upper_az:nnnnn
+  \__text_change_case_upper_tr:nnnnn
 \group_begin:
   \cs_set_protected:Npn \__text_change_case_setup:NN #1#2
     {
@@ -36010,6 +36634,12 @@
 \cs_gset:Npn \seq_mapthread_function:NNN { \seq_map_pairwise_function:NNN }
 \__kernel_patch_deprecation:nnNNpn { 2021-01-11 } { (no~longer~required) }
 \cs_gset_protected:Npn \sys_load_deprecation: { }
+\__kernel_patch_deprecation:nnNNpn { 2023-07-08 } { \text_titlecase_first:n }
+\cs_gset:Npn \text_titlecase:n #1
+  { \text_titlecase_first:n { \text_lowercase:n {#1} } }
+\__kernel_patch_deprecation:nnNNpn { 2023-07-08 } { \text_titlecase_first:nn }
+\cs_gset:Npn \text_titlecase:nn #1#2
+  { \text_titlecase_first:nn {#1} { \text_lowercase:n {#2} } }
 \__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \text_lowercase:n }
 \cs_gset:Npn \tl_lower_case:n #1
   { \text_lowercase:n {#1} }
@@ -36022,12 +36652,12 @@
 \__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \text_uppercase:nn }
 \cs_gset:Npn \tl_upper_case:nn #1#2
   { \text_uppercase:nn {#1} {#2} }
-\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \text_titlecase:n }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \text_titlecase_first:n }
 \cs_gset:Npn \tl_mixed_case:n #1
-  { \text_titlecase:n {#1} }
-\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \text_titlecase:nn }
+  { \text_titlecase_first:n {#1} }
+\__kernel_patch_deprecation:nnNNpn { 2020-01-03 } { \text_titlecase_first:nn }
 \cs_gset:Npn \tl_mixed_case:nn #1#2
-  { \text_titlecase:nn {#1} {#2} }
+  { \text_titlecase_first:nn {#1} {#2} }
 \__kernel_patch_deprecation:nnNNpn { 2022-05-23 } { \token_case_meaning:Nn }
 \cs_gset:Npn \tl_case:Nn { \token_case_meaning:Nn }
 \__kernel_patch_deprecation:nnNNpn { 2022-05-23 } { \token_case_meaning:NnT }
@@ -36049,8 +36679,8 @@
 \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 } { \text_titlecase_first:n }
+\cs_gset:Npn \char_mixed_case:N { \text_titlecase_first: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 }

Modified: trunk/Master/texmf-dist/tex/latex/l3kernel/expl3-generic.tex
===================================================================
--- trunk/Master/texmf-dist/tex/latex/l3kernel/expl3-generic.tex	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/tex/latex/l3kernel/expl3-generic.tex	2023-10-24 17:51:37 UTC (rev 68632)
@@ -19,7 +19,7 @@
 %% and all files in that bundle must be distributed together.
 %% 
 %% File: expl3.dtx
-\def\ExplFileDate{2023-10-10}%
+\def\ExplFileDate{2023-10-23}%
 \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	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/tex/latex/l3kernel/expl3.ltx	2023-10-24 17:51:37 UTC (rev 68632)
@@ -19,7 +19,7 @@
 %% and all files in that bundle must be distributed together.
 %% 
 %% File: expl3.dtx
-\def\ExplFileDate{2023-10-10}%
+\def\ExplFileDate{2023-10-23}%
 \let\ExplLoaderFileDate\ExplFileDate
 \begingroup
   \catcode`\_=11

Modified: trunk/Master/texmf-dist/tex/latex/l3kernel/expl3.sty
===================================================================
--- trunk/Master/texmf-dist/tex/latex/l3kernel/expl3.sty	2023-10-24 17:51:10 UTC (rev 68631)
+++ trunk/Master/texmf-dist/tex/latex/l3kernel/expl3.sty	2023-10-24 17:51:37 UTC (rev 68632)
@@ -19,7 +19,7 @@
 %% and all files in that bundle must be distributed together.
 %% 
 %% File: expl3.dtx
-\def\ExplFileDate{2023-10-10}%
+\def\ExplFileDate{2023-10-23}%
 \let\ExplLoaderFileDate\ExplFileDate
 \ProvidesPackage{expl3}
   [%



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