% \iffalse meta-comment
% 
% ┌─────────────────────────────────────────────────────────────┐
% │                                                             │
% │                  The “intexgral” package                    │
% │           A LaTeX package for typesetting integrals         │
% │                                                             │
% │  Copyright (C) 2025-2026 Valentin Dao                       │
% │                                                             │
% │  This file may be distributed and/or modified under the     │
% │  conditions of the LaTeX Project Public License, either     │
% │  version 1.3c of this license or (at your option) any       │
% │  later version.                                             │
% │                                                             │
% │  The latest version of this license is in:                  │
% │                                                             │
% │    > http://www.latex-project.org/lppl.txt                  │
% │                                                             │
% │  This work has the LPPL maintenance status 'maintained'     │
% │  and the current maintainer is Valentin Dao                 │
% │  (vdao.texdev@gmail.com).                                   │
% │                                                             │
% │  This work consists of the files listed in the              │
% │  'MANIFEST.md' file.                                        │
% │                                                             │
% │  The repository of this work can be found here:             │
% │                                                             │
% │    > https://github.com/TeXackers/intexgral                 │
% │                                                             │
% └─────────────────────────────────────────────────────────────┘
%
%<*driver>
% \fi
% \iffalse
\documentclass[11pt, a4paper]{l3doc}

\usepackage{amsmath}

\usepackage{fontspec}
\usepackage{unicode-math}

\IfFontExistsTF{ChronicleTextG3-Roman-Pro.otf}
  {
    \setmainfont{ChronicleTextG3}[
      Renderer          = Node,
      UprightFont       = *-Roman-Pro,
      ItalicFont        = *-Italic-Pro,
      BoldFont          = *-Bold-Pro,
      BoldItalicFont    = *-BoldIta-Pro,
      ]
  }{}

\IfFontExistsTF{Whitney-Medium.otf}
  {
    \setsansfont{Whitney}[
      Renderer         = Node,
      UprightFont      = *-Medium,
      ItalicFont       = *-MediumItalic,
      BoldFont         = *-Bold,
      BoldItalicFont   = *-BoldItalic,
      UprightFeatures  = 
        {
          StylisticSet = 1,
          StylisticSet = 10,
          StylisticSet = 11,
        },
      BoldFeatures     =
        {
          StylisticSet = 1,
          StylisticSet = 10,
          StylisticSet = 11,
        },
      ItalicFeatures   =
        {
          StylisticSet = 1,
          StylisticSet = 10,
          StylisticSet = 11,
          StylisticSet = 8,
          StylisticSet = 9
        }
      ]
    \newfontface{\smbold}{Whitney-Semibold.otf}
  }{\let\smbold\bfseries}

\IfFontExistsTF{MonaspaceArgon-Medium.otf}
  {
    \setmonofont{MonaspaceArgon}[
      Scale           = MatchAveragecase,
      Renderer        = Node,
      UprightFont     = *-Medium,
      BoldFont        = *-SemiBold,
      ItalicFont      = *-MediumItalic,
      BoldItalicFont  = *-SemiBoldItalic
    ]
  }{}

\IfFontExistsTF{NewCMMath-Regular.otf}
  {\setmathfont{NewCMMath-Regular.otf}}{}

\ExplSyntaxOn
\debug_on:n { all }
\usepackage{intexgral}
\debug_off:n { all }
\ExplSyntaxOff

\usepackage[
  a4paper,
  marginparwidth  = 32mm,
  top             = 3cm,
  bottom          = 2cm,
  left            = 44mm,
  right           = 30mm,
  marginparsep    = 2mm,
  ]{geometry}

\usepackage{fancyhdr}
\fancyhf{}
\fancyfoot[C]{\small\sffamily\itshape---\space\thepage\space\kern0.4mm\/---}
\renewcommand{\headrulewidth}{0pt}
\pagestyle{fancy}

\usepackage{fontawesome5}
\usepackage{microtype}

\usepackage[addtotoc]{abstract}
\usepackage[silence]{codedescribe}

\usepackage[svgnames]{xcolor}

\definecolor{RoyalBlue}{RGB}{0, 35, 102}
\definecolor{RoyalRed}{RGB}{157, 16, 45}
\definecolor{RoyalGreen}{HTML}{48845F}
\definecolor{urlcolour}{HTML}{B42F5E}

\definecolor{packagecolour}{HTML}{B86B00}
\definecolor{keycolour}{HTML}{004766}
\definecolor{macrocolour}{HTML}{047658}
\definecolor{macro3colour}{HTML}{27262E}
\definecolor{optioncolour}{HTML}{630476}

\setcodelabels{
  new       = \textsf{new},
  update    = \textsf{update},
  note      = \textsf{N.B},
  remark    = \textsf{remark},
  and       = \textsf{and},
  or        = \textsf{or},
}

\lstset{gobble=2}

\newcodekey{intexgral}{
  ruleht        = 0,
  texcs         = {
      integral,
      IntegralSetup,
      differentials,
      intexgralsetup,
    },
  texcsstyle    = \bfseries\color{RoyalBlue},
  texcsstyle    = [2]\color{macrocolour},
  emph          =
    {
      true,
      false,
      default,
      nested,
      product,
    },
  keywd         =
    {
      equation,
      limits,
      limits*,
      mode,
      symbol,
      nint,
      llimit,
      ulimit,
      domain,
      domain*,
      boundary,
      variables,
      jacobian,
      diff-vec,
      diff-symb,
      diff-order,
      defaultvar,
      defaultvar*,
      varsep,
      diffsep,
      innersymbsep,
      postsymbsep,
      vectorstyle,
      domainstyle,
      single,
      double,
      triple,
      quadruple,
      contour,
      surface,
      volume
    },
  codeprefix    = {},
  resultprefix  = {}
}

\defobjectfmt{macro}{code}{color=macrocolour}
\defobjectfmt{macro3}{code}{color=macro3colour}
\defobjectfmt{variable3}{code}{color=SlateGrey}
\defobjectfmt{pkg}{pkg}{font=\ttfamily}
\defobjectfmt{option}{option}{color=optioncolour, shape preadj=thin, shape posadj=thin, noshape}
\defgroupfmt{keys}{color=keycolour, noshape}

\usepackage{luacode}

\begin{luacode}
function fontcopyright(fontname)
  local font_path = kpse.find_file(fontname, 'opentype fonts')
  if font_path then
    local font = fontloader.open(font_path)
    local metrics = fontloader.to_table(font)
    local copyright = metrics.copyright:gsub("https?://%S+$", "")
    local copyright = copyright:gsub("Copyright%s%(C%)", "\\copyright\\")
    local copyright = copyright:gsub("&", "\\&")
    tex.print('\\textit{' .. metrics.fullname .. '}\\par')
    if not string.find(copyright, "missing") then
      tex.print(copyright)
    end
  end
end
\end{luacode}

\usepackage{changelog}
\usepackage{enumitem}
\renewenvironment{changelogitemize}
  {\begin{itemize}[label=\raisebox{0.5ex}{$\scriptscriptstyle\blacktriangleright$}]}
  {\end{itemize}}

\usepackage[normalem]{ulem}

\setlength{\parindent}{0pt}

\usepackage[totoc=true, columnsep=25pt]{idxlayout}

\usepackage{polyglossia}
\setmainlanguage{english}

\catcode`\@=11
\usepackage{titlesec}
\titleformat{\part}[display]
  {
    \fontsize{35}{20}\selectfont\filcenter^^A
    \addfontfeatures{LetterSpace=15, Letters=SmallCaps}^^A
  }
  {
    \newpage
    \spaceskip=1.5\fontdimen2\font
    \begingroup^^A
    \Huge\scshape\addfontfeatures{LetterSpace=10}^^A
    \def\partname{part}^^A
    \edef\thepart{\romannumeral\the\c@part}^^A
    \partname\space\thepart^^A
    \endgroup^^A
    \vskip0.2mm^^A
  }
  {0pt}
  {}
  [\vspace*{1cm}]
\catcode`\@=12
\titleformat*{\section}{\sffamily\LARGE\bfseries}
\titleformat*{\subsection}{\sffamily\Large\bfseries}
\titleformat*{\subsubsection}{\smbold\large}

\usepackage[titles]{tocloft}
\let\cftdot\relax

\usepackage{csquotes}

\usepackage{hyperref}
\hypersetup{
  pdfauthor   = {Valentin Dao},
  pdftitle    = {The intexgral package},
  pdfcreator  = {LuaLaTeX with hyperref package},
  colorlinks  = true,
  linkcolor   = RoyalBlue,
  urlcolor    = urlcolour,
}

\usepackage{cleveref}
\crefname{subsection}{sub-section}{sub-sections}
\crefname{subsubsection}{sub-sub-section}{sub-sub-sections}

\catcode`\@=11
\title{The \href{https://ctan.org/pkg/intexgral}{\textsf{\intexgral@module}} package\medbreak{\Large\intexgral@version}}
\author{Valentin Dao\footnote{E-mail: \href{mailto:vdao.texdev@gmail.com}{\ttfamily vdao.texdev@gmail.com}}}
\date{\today}
\catcode`\@=12

\listfiles

\EnableCrossrefs
\EnableDocumentation
\EnableImplementation

\begin{document}
  \DocInput{intexgral-en.dtx}
\end{document}
%</driver>
% \fi
% 
% \begin{documentation}
%
% \maketitle
%
% \begin{center}
% \rule{12.5cm}{0.6pt}
% \begin{abstract}
%   While integral composition is prevalent in \LaTeX, it often proves to be impractical. When the expression becomes complex, it is then difficult to modify its various elements in an illegible source code. To address this problem, the \tsobj[pkg]{intexgral} package offers a central macro whose only argument is the integrand. Everything else (symbol, limits, variables\dots) can be modified using a \tsobj[meta]{key=value} interface. Unlike the traditional method, where the user chooses the limit order with the active characters \verb|_| and \verb|^|, the package had to establish a convention. Thus, for the two keys that will deal with limits, the assumed input will be \verb|_|\tsobj[meta]{lower limit}\verb|^|\tsobj[meta]{upper limit}. Since this package is written in \tsobj[pkg]{expl3}, it depends on \tsobj[pkg]{l3kernel}.
% \end{abstract}
% \rule{12.5cm}{0.6pt}
% \end{center}
% 
% \newpage
% \tableofcontents
% \newpage
% 
% \subsection*{License}
% 
% \copyright\ 2025 Valentin Dao, published under the \LaTeX\ Project Public License (\textsc{lppl}) 1.3c
% 
% \subsection*{Fonts}
% 
% \directlua{fontcopyright('ChronicleTextG3-Roman-Pro.otf')}
% 
% \vspace*{\baselineskip}
% 
% \begingroup
% \sffamily
% \directlua{fontcopyright('Whitney-Medium.otf')}
% \endgroup
%
% \vspace*{\baselineskip}
% 
% \begingroup
% \ttfamily
% \directlua{fontcopyright('MonaspaceArgon-Medium.otf')}
% \endgroup
% 
% \subsection*{Repository}
% 
% \href{https://github.com/TeXackers/intexgral}{\faGithub\ See GitHub repository}.
% 
% \subsection*{Aknowledgements}
% 
% Many thanks to Plante and Slurpy for accompanying me on this adventure of learning \TeX, your pieces of advice were priceless. I’d also like to thank Anthony, who has always kindly reviewed the new versions and given me ideas for those to come.
% 
% \part[Documentation]{documentation}
% 
% \section{Package options}\label{sec:pkgoptions}
% 
% \begin{codedescribe}[macro, new={v2.0.0}]{\intexgralsetup}
%   \begin{codesyntax}
%     \tsobj[macro]{\intexgralsetup}\tsargs[marg]{package options}
%   \end{codesyntax}
%   These options can be declared in a classic way with \tsobj[macro]{\usepackage} or with \tsobj[macro]{\intexgralsetup} in the preamble.
% \end{codedescribe}
% 
% \begin{codedescribe}[key]{invert-limits}
%   \begin{codesyntax}[key]
%     \tsobj[key]{invert-limits}=\tsobj[option, meta or]{true,\uline{false}}
%   \end{codesyntax}
%   This key inverts the limit order convention. The entirety of the document can only follow one convention.
% \end{codedescribe}
% 
% \begin{codedescribe}[key, update={v3.0.0}]{invert-diff}
%   \begin{codesyntax}[key]
%     \tsobj[key]{invert-diff}=\tsobj[option, meta or]{true,\uline{false}}
%   \end{codesyntax}
%   In physical sciences, it is common to see the differentials placed before the integrand. This key hence swaps their position.
% \end{codedescribe}
% 
% \begin{codedescribe}[key, new={v3.0.0}]{limits-mode}
%   \begin{codesyntax}[key]
%     \tsobj[key]{limits-mode}=\tsobj[option, meta or]{limits,\uline{nolimits}}
%   \end{codesyntax}
%   Applies \tsobj[macro]{\limits} or \tsobj[macro]{\nolimits} to all integrals.
% \end{codedescribe}
% 
% \begin{codedescribe}[key, update={v4.0.0}]{italic, upright}
%   \begin{codesyntax}[key]
%     \tsobj[key]{italic}=\tsobj[option, meta or]{true,\uline{false}}
%     \tsobj[key]{upright}=\tsobj[option, meta or]{\uline{true},false}
%   \end{codesyntax}
%   Applies an upright or italic \enquote{$\mathrm d$} for the differentials.
%   \begin{tsremark}[\textcolor{RoyalGreen}{Remark:}]
%   Since the version v4.0.0, the \tsobj[pkg]{derivative} package is no longer used to manage differentials. The number of options is therefore more limited but should suit most use cases in the context of integrals.
%   \end{tsremark}
% \end{codedescribe}
% 
% \section{Main macro overview}\label{sec:mainmacro}
% 
% \begin{codedescribe}[macro, update={v3.0.0}]{\integral}
%   \begin{codesyntax}
%     \tsmacro{\integral}[list of keys]{integrand}
%   \end{codesyntax}
%   This macro is used to typeset integrals. It must, of course, be used in math mode only. Here is a first example in its simplest form:
% \end{codedescribe}
% 
% \begin{codestore}[main-example]
%   \begin{equation}
%     \integral{x}
%   \end{equation}
% \end{codestore}
% 
% \tsdemo*[intexgral]{main-example}
% 
% Since this macro focuses on the use of keys, the first part of this documentation will present them, grouping them by domain. The second part will introduce a few additional macros that complement the use of certain keys. The rest of this document will outline the other features of the package.
% 
% \section{List of keys}
% 
% \subsection{Integration limits}
% 
% \subsubsection{Defining the limits}
% 
% \begin{codedescribe}[key]{limits, limits*}
%   \begin{codesyntax}[key]
%     \tsobj[key]{limits}=\tsargs[marg]{mixed-list}, \tsargs[arg]{keyword}
%     \tsobj[key]{limits*}=\tsargs[marg]{mixed-list}, \tsargs[arg]{keyword}
%   \end{codesyntax}
%   This key determines the integration limits to be used, following the convention explained in the abstract. In its simplest form, the value of the key takes as its argument a list of elements separated by commas (\emph{comma-separated values} or \tsobj[meta]{csv-list}).
% \end{codedescribe}
% 
% \begin{codestore}[limits]
%   \begin{equation}
%     \integral[limits={1, 10}]{f(x)}
%   \end{equation}
% \end{codestore}
% 
% \tsdemo*[intexgral]{limits}
% 
% The notable advantage of the \tsobj[key]{limits} key is that you can specify the integration limits for several integrals, and the package automatically typesets the corresponding symbols. To separate pairs of limits, we use a semicolon (\emph{semicolon-separated values} or \tsobj[meta]{ssv-list}\footnotetext{We therefore mean by \tsobj[meta]{mixed-list} that \tsobj[key]{limits} can accept a set of \tsobj[meta]{csv-list} in a more general \tsobj[meta]{ssv-list}.}).
% 
% \begin{codestore}[multiple-limits]
%   \begin{equation}
%     \integral[limits={1, 2; 3, 4}, variables={x, y}]{f(x, y)}
%   \end{equation}
% \end{codestore}
% 
% \tsdemo*[intexgral]{multiple-limits}
% 
% It is also possible to specify predefined limits using keywords (see \cref{subsec:limits}). Finally, the starred variant allows the limits of the integral to be expressed as an interval. The direction of the brackets automatically adjusts itself to the presence of $\pm$\verb|\infty|.
% 
% \begin{codestore}[limits*]
%   \begin{equation}
%     \integral[limits*={1, 10}]{f(x)}
%   \end{equation}
% \end{codestore}
% 
% \tsdemo*[intexgral]{limits*}
% 
% \subsection{Display modes}
% 
% \begin{codedescribe}[key, new={v3.0.0}]{mode}
%   \begin{codesyntax}[key]
%     \tsobj[key]{mode}=\tsobj[option, meta or]{\uline{default},nested,product}
%   \end{codesyntax}
%   \emph{Solely} when the \tsobj[key]{limits} key is used, it is possible to choose between three distinct display styles. Each of these is fully compatible with the \tsobj[key]{invert-diff} option.
%   \begin{describelist*}{option}
%     \describe{default}{Typesets all the symbols, then the integrand, then the variables. As the macro operates by default in this mode, it is not necessary to use the key with this value.}
%     \describe{nested}{Typesets a nested integral where the symbol and integrand alternate before all the variables are written.}
%     \describe{product}{Typesets a product of integrals where the symbol, integrand, and variables alternate.}
%   \end{describelist*}
%   Of course, nothing prevents you from using the \tsobj[macro]{\integral} macro several times in a row to produce the same result as in the example below. However, for those who would prefer to obtain the result with a single macro, this method is permitted.
%   \begin{tsremark}[\textcolor{RoyalRed}{Important:}]
%     for the last two modes, the integral will be \emph{cut off} in a certain way. In order to perform this action correctly, the same method must be used as with \tsobj[key]{limits}, i.e. by using a semicolon.
%   \end{tsremark}
% \end{codedescribe}
% 
% \begin{center}
%   \textsc{mode}\kern1ex \tsobj[option]{default}
% \end{center}
% 
% \begin{codestore}[default-mode]
%   \begin{equation}
%     \integral[limits={1, 2; 3, 4; 5, 6}, variables={x, y, z}, mode=default]{xyz}
%   \end{equation}
% \end{codestore}
% 
% \tsdemo*[intexgral]{default-mode}
% 
% \begin{center}
%   \textsc{mode}\kern1ex \tsobj[option]{nested}\nobreak
% \end{center}
% 
% \begin{codestore}[nested-mode]
%   \begin{equation}
%     \integral[limits={1, 2; 3, 4; 5, 6}, variables={z, y, x}, mode=nested]{x;y;z}
%   \end{equation}
% \end{codestore}
% 
% \tsdemo*[intexgral]{nested-mode}
% 
% \begin{center}
%   \textsc{mode}\kern1ex \tsobj[option]{product}\nobreak
% \end{center}
% 
% \begin{codestore}[product-mode]
%   \begin{equation}
%     \integral[limits={1, 2; 3, 4; 5, 6}, variables={x, y, z}, mode=product]{x;y;z}
%   \end{equation}
% \end{codestore}
% 
% \tsdemo*[intexgral]{product-mode}
% 
% To function correctly, it is clear that the keys \tsobj[key]{limits} and \tsobj[key]{variables} (see \ref{subsubsec:variables}), as well as the integrand, must have the same number of elements.
%
% \subsection{Symbol (and more about limits)}
% 
% The \tsobj[key]{limits(*)} keys are very useful when typesetting a definite integral. However, this only covers final expressions where the limits of each variable have been specified. For more general cases — indefinite integrals \nobreak — they are relatively inconvenient. To typeset a double integral over a surface $S$, one would have to write \verb|limits={,;S,}|. Needless to say, this is rather inconvenient for the user and not optimal for the package. Furthermore, the glyph would not be correct. The set of keys below therefore offers an easy way to modify both the symbol and the bounds.
% 
% \subsubsection{Selecting a symbol}
% 
% \begin{codedescribe}[key, update={v3.0.0}]{symbol}
%   \begin{codesyntax}[key]
%     \tsobj[key]{symbol}=\tsobj[meta]{control sequence}
%   \end{codesyntax}
%   This key accepts a macro designating an integral symbol. Any symbol previously defined by a control sequence is acceptable. If you attempt to use one that is not defined, it will be substituted with \tsobj[macro]{\int} and a warning message will be issued.
%   \begin{tsremark}[\textcolor{RoyalGreen}{Remark:}]
%   aside from checking whether the macro exists, no specific checks are performed, and the value of the key is used as-is to typeset the symbol. This implies that the symbol will depend on how it is defined. For example, \tsobj[pkg]{amsmath} and \tsobj[pkg]{unicode-math} do not define \tsobj[macro]{\iint} in the same way. The result will therefore naturally be different: \tsobj[pkg]{amsmath} concatenates two \tsobj[macro]{\int} with some spacing to define \tsobj[macro]{\iint}, whereas \tsobj[pkg]{unicode-math} defines the actual glyph \texttt{U+222C}.
%   \end{tsremark}
% \end{codedescribe}
% 
% \begin{codestore}[symbol]
%   \begin{equation}
%     \integral[symbol=\iint, llimit=S, variables={x, y}]{f(x, y)}
%   \end{equation}
% \end{codestore}
% 
% \tsdemo*[intexgral]{symbol}
% 
% \subsubsection{Generating many integrals}
% 
% \begin{codedescribe}[key]{nint}
%   \begin{codesyntax}[key]
%     \tsobj[key]{nint}=\tsobj[meta]{integer}
%   \end{codesyntax}
%   This key accepts an integer $n$ which allows $n$ integrals to be composed. It is advisable to use this key only if the number of symbols exceeds 4 to favour the glyphs defined by the mathematical font used.
% \end{codedescribe}
% 
% \begin{codestore}[nint]
%   \begin{equation}
%     \integral[nint=5, llimit=\Omega, variables={x_1, x_2, x_3, x_4, x_5}]{f(x_1, x_2, x_3, x_4, x_5)}
%   \end{equation}
% \end{codestore}
% 
% \tsdemo*[intexgral]{nint}
% 
% \subsubsection{Defining boundaries on an ad hoc basis}
% 
% \begin{codedescribe}[key, update={v3.0.0}]{llimit, ulimit}
%   \begin{codesyntax}[key]
%     \tsobj[key]{llimit}=\tsobj[meta]{lower limit}
%     \tsobj[key]{ulimit}=\tsobj[meta]{upper limit}
%   \end{codesyntax}
%   These two keys allow you to specify the lower and upper limits, respectively. They are only suitable if a single symbol is displayed. If you need to specify both limits, the \tsobj[key]{limits} key should be used instead.
% \end{codedescribe}
% 
% \begin{codestore}[ulimit-llimit]
%   \begin{equation}
%     \integral[llimit={x^2 + y^2 \leq 1}, variables={x, y}]{f(x, y)}
%   \end{equation}
% \end{codestore}
% 
% \tsdemo*[intexgral]{ulimit-llimit}
% 
% \subsubsection{Recurring limits motif}
% 
% Limits sometimes follow common patterns that can be generalised with keys, thus avoiding overloading the argument of \tsobj[key]{llimit}.
% 
% \begin{codedescribe}[key, update={v3.0.0}]{domain, domain*}
%   \begin{codesyntax}[key]
%     \tsobj[key]{domain}=\tsargs[marg]{*-list}
%     \tsobj[key]{domain*}=\tsargs[marg]{*-list}
%   \end{codesyntax}
%   These keys accept a list delimited by an asterisk. Each element of the list is then parsed as follows:
%   \begin{itemize}
%     \item The first token of the item is passed \tsobj[macro]{\mathbb} (preceded by \tsobj[macro]{\uppercase} in case the \textsc{shift} key is too far away for your fingers).
%     \item The remaining tokens --- which may be empty --- are placed as superscript (or subscript for the starred variant of the key).
%   \end{itemize}
% \end{codedescribe}
% 
% \begin{codestore}[domain]
%   \begin{equation}
%     \integral[symbol=\iint, domain={r*r}, variables={x, y}]{xy}
%   \end{equation}
% \end{codestore}
% 
% \tsdemo*[intexgral]{domain}
% 
% \begin{codedescribe}[key]{boundary}
%   \begin{codesyntax}[key]
%     \tsobj[key]{boundary}=\tsargs[marg]{lower limit}
%   \end{codesyntax}
%   This key simply places the symbol $\partial\,$ before the lower limit.
% \end{codedescribe}
% 
% \begin{codestore}[boundary]
%   \begin{equation}
%     \integral[symbol=\oint, boundary=S, diff-vec]{G(\vec r)}
%   \end{equation}
% \end{codestore}
% 
% \tsdemo*[intexgral]{boundary}
% 
% \subsection{Differentials}
% 
% \subsubsection{Specifying the integration variables}\label{subsubsec:variables}
% 
% \begin{codedescribe}[key, update={v3.0.0}]{variables}
%   \begin{codesyntax}[key]
%     \tsobj[key]{variables}=\tsargs[marg]{csv-list}, \tsobj[meta]{keyword}, \tsobj[option]{none}
%   \end{codesyntax}
%   This key allows you to define the variables of the integral in the form of a \tsobj[meta]{csv-list}. Like \tsobj[key]{limits}, the key can accept a keyword as an argument. This behaviour is also explained later (see \cref{subsec:variablekey}).
%   \begin{tsremark}[\textcolor{RoyalGreen}{Remark:}]
%     if no variable is specified, i.e. \tsobj[key]{variables} is not called, the package automatically sets \enquote{$\mathrm dx$} (or \enquote{$\mathrm d\vec r$} if \tsobj[key]{diff-vec} is active). Furthermore, if \tsobj[option]{none} is passed as a value, no variable will be displayed.
%   \end{tsremark}
% \end{codedescribe}
% 
% \begin{codestore}[variables]
%   \begin{equation}
%     \integral[variables=t]{t^2}
%   \end{equation}
% \end{codestore}
% 
% \tsdemo*[intexgral]{variables}
%
% \subsubsection{Including the Jacobian}
% 
% \begin{codedescribe}[key]{jacobian}
%   \begin{codesyntax}[key]
%     \tsobj[key]{jacobian}
%   \end{codesyntax}
%   This key enables the display of the Jacobian when defined by \tsobj[macro]{\NewVariableKeyword}.
% \end{codedescribe}
% 
% \begin{codestore}[jacobian]
%   \begin{equation}
%     \integral[limits={0, R; 0, 2\pi; 0, \pi}, variables=spherical, mode=product, jacobian]{}
%   \end{equation}
% \end{codestore}
% 
% \tsdemo*[intexgral]{jacobian}
% 
% \subsubsection{Modifying the differential symbol}
% 
% \begin{codedescribe}[key]{diff-symb}
%   \begin{codesyntax}[key]
%     \tsobj[key]{diff-symb}=\tsargs[meta]{control sequence}
%   \end{codesyntax}
%   This key allows you to modify the symbol or style of the differentials.
% \end{codedescribe}
% 
% \begin{codestore}[diff-symb]
%   \begin{equation}
%     \integral[variables={q(t)}, diff-symb=\mathcal{D}]{e^{+\dfrac{i S[q(t)]}{\hbar}}}
%   \end{equation}
% \end{codestore}
% 
% \tsdemo*[intexgral]{diff-symb}
% 
% \subsubsection{Creating a path integral}
% 
% \begin{codedescribe}[key]{diff-vec}
%   \begin{codesyntax}[key]
%     \tsobj[key]{diff-vec}
%   \end{codesyntax}
%   This key applied a vector to each integration variable along with a centred dot. It is only compatible with the \tsobj[option]{default} mode. Using it with the \tsobj[option]{nested} or \tsobj[option]{product} options will have no effect.
% \end{codedescribe}
% 
% \begin{codestore}[diff-vec]
% \IntegralSetup{vectorstyle=\mathbf}
%   \begin{equation}
%     W = \integral[single=C, variables=s, diff-vec]{\mathbf F}
%   \end{equation}
% \end{codestore}
% 
% \tsdemo*[intexgral]{diff-vec}
% 
% \subsubsection{Adding an exponent}
% 
% \begin{codedescribe}[key]{diff-order}
%   \begin{codesyntax}[key]
%     \tsobj[key]{diff-order}=\tsobj[meta]{csv-list}
%   \end{codesyntax}
%  This key places an exponent on each differential.
% \end{codedescribe}
% 
% \begin{codestore}[diff-order]
%   \begin{equation}
%     S[\phi] = \integral[diff-order=4]{\mathcal L (\phi, \partial_\mu \phi, x^\mu)}
%   \end{equation}
% \end{codestore}
% 
% \tsdemo*[intexgral]{diff-order}
% 
% \section{Additional macros}
% 
% In addition to the wide range of keys defined by the package, a few macros also enhance their uses.
% 
% \subsection{Custom differentials placement}
% 
% \begin{codedescribe}[macro]{\differentials}
%   \begin{codesyntax}[macro]
%     \tsobj[macro]{\differentials}
%   \end{codesyntax}
%   Although the \tsobj[key]{invert-diff} option exists, it is often desirable to be able to place differentials wherever one wishes. For example, they are frequently seen in the numerator of a fraction when the numerator is 1. To meet this need, the package provides the macro \tsobj[macro]{\differentials}, whose functioning varies slightly from one display mode to another:
% \begin{itemize}
%   \item \tsobj[option]{default}: typesets all the differentials at once. It is compatible with the \tsobj[key]{invert-diff} option.
%   \item \tsobj[option]{nested}: typesets all the differentials at once. The macro is \emph{not} defined if the \tsobj[key]{invert-diff} option is active.
%   \item \tsobj[option]{product}: typesets a single differential at a time. It will therefore need to be repeated between each semicolon. It is also compatible with the \tsobj[key]{invert-diff} option.
% \end{itemize}
% \end{codedescribe}
% 
% \begin{codestore}[differentials]
%   \begin{gather}
%     \integral{\frac{\differentials}{x}}\\[1cm]
%     \langle 0 \mid T\{ \phi(x) \phi(y) \} \mid 0 \rangle = \integral[diff-order=4, variables=p]{\frac{\differentials}{(2\pi)^4} \frac{e^{-i p \cdot (x-y)}}{p^2 - m^2 + i \epsilon}}
%   \end{gather}
% \end{codestore}
% 
% \tsdemo*[intexgral]{differentials}
% 
% \subsection{Looking back at the \emph{limits} key}\label{subsec:limitskey}
% 
% \begin{codedescribe}[macro, update={v4.0.0}]{\NewLimitsKeyword, \RenewLimitsKeyword, \ProvideLimitsKeyword, \DeclareLimitsKeyword}
%   \begin{codesyntax}[macro]
%     \tsobj[macro]{\NewLimitsKeyword}\tsargs[marg]{keyword}\tsargs[marg]{limits}
%   \end{codesyntax}
%   It is common to have to specify the same limits in an integral. To make them easier to write, the \tsobj[key]{limits(*)} keys accept, in addition to explicit limits, a keyword designating a pair of them predefined by one of these four macros:
% \end{codedescribe}
%   \begin{describelist*}{macro}
%     \describe{\NewLimitsKeyword}{Creates a new keyword and issues an error if it already exists.}
%     \describe{\RenewLimitsKeyword}{Redefines a keyword and issues an error if it does not already exist.}
%     \describe{\ProvideLimitsKeyword}{Only creates a new keyword if it does not already exist. No message will be issued if this is the case.}
%     \describe{\DeclareLimitsKeyword}{Creates a new keyword regardless, overwriting any previous definition.}
%   When entering a new keyword, you must follow the convention for the order of delimiters. For example, you can write:
% \end{describelist*}
% \begin{codestore}[limitskeywordorder]
%   \usepackage{intexgral}
%   \NewLimitsKeyword{key1}{A, B}
%   \intexgralsetup{invert-limits=true}
%   \NewLimitsKeyword{key2}{B, A}
% \end{codestore}
% 
% \tscode*[intexgral]{limitskeywordorder}
%   \tsobj[option]{key1} and \tsobj[option]{key2} will then produce the same integral. Here is the list of keywords already defined by the package and the limits they contain:
%   \begin{describelist*}{option}
%   \describe{ab}{$a, b$}
%   \describe{unit}{$0, 1$}
%   \describe{real}{$-\infty, +\infty$}
%   \describe{positive}{$0, +\infty$}
%   \describe{negative}{$-\infty, 0$}
%   \describe{circle}{$0, 2\pi$}
%   \describe{sircle}{$0, \pi$}
%   \describe{qcircle}{$0, \pi/2$}
%   \describe{height}{$0, H$}
%   \describe{radius}{$0, R$}
%   \describe{length}{$0, L$}
%   \describe{time}{$0, T$}
%   \end{describelist*}
%   \begin{tsremark}[\textcolor{RoyalGreen}{Remark:}]
%     keywords contain both limits of an integral at the same time. They must therefore be preceded and/or followed by semicolons.
%   \end{tsremark}
%   We could therefore modify the expression of an integral as follows:
% 
% \begin{codestore}[limitskeyword]
%   \begin{equation}
%     \integral[limits={height;circle;radius}, variables={\rho, \theta, z}]{\rho}
%   \end{equation}
% \end{codestore}
% 
% \tsdemo*[intexgral]{limitskeyword}
% 
% It is entirely possible to combine these keywords with the more explicit syntax of \tsobj[key]{limits}.
% 
% \begin{codestore}[limitskeyword*]
%   \begin{equation}
%     \integral[limits={height;0,W;length}, variables={x, y, z}]{\kappa(T)\nabla T \cdot \mathbf{n}}
%   \end{equation}
% \end{codestore}
% 
% \tsdemo*[intexgral]{limitskeyword*}
% 
% \subsection{Looking back at the \emph{variables} key}\label{subsec:variableskey}
% 
% \begin{codedescribe}[macro, update={v4.0.0}]{\NewVariableKeyword, \RenewVariableKeyword, \ProvideVariableKeyword, \DeclareVariableKeyword}
%   \begin{codesyntax}[macro]
%     \tsobj[macro]{\NewVariableKeyword}\tsargs[marg]{keyword}\tsargs[marg]{variables}\tsargs[oarg]{jacobian}
%   \end{codesyntax}
%   Similarly, the same groups of variables often reappear. We can therefore also define keywords containing a set of pre-registered variables. The variants of this macro behave in the same way as for \tsobj[macro]{\NewLimitsKeyword}. The only difference is that here it is possible to define a Jacobian, in the form of a \tsobj[meta]{csv-list} if necessary. Its display is then controlled by the \tsobj[key]{jacobian} key as explained above. Here is the list of variable keywords already defined by the package, with the Jacobian for some of them:
%   \begin{describelist*}{option}
%     \describe{cartesian}{$x, y, z$}
%     \describe{planar}{$x, y$}
%     \describe{polar}{$r, \theta \: (r)$}
%     \describe{cylindrical}{$r, \theta, z \: (r)$}
%     \describe{cylindrical*}{$\rho, \theta, z \: (\rho)$}
%     \describe{spherical}{$r, \theta, \phi \: (r^2, \sin\theta)$}
%   \end{describelist*}
% \end{codedescribe}
% 
% \begin{codestore}[variablekeyword]
%   \begin{equation}
%     \integral[triple=V, variables=spherical]{f(r, \theta, \phi)}
%   \end{equation}
% \end{codestore}
% 
% \tsdemo*[intexgral]{variablekeyword}
% 
% \subsection{Looking back at the \emph{symbol} key}\label{subsec:symbolkey}
% 
% \begin{codedescribe}[macro, new={v3.0.0}]{\NewSymbolKeyword, \RenewSymbolKeyword, \ProvideSymbolKeyword, \DeclareSymbolKeyword}
%   \begin{codesyntax}[macro]
%     \tsobj[macro]{\NewSymbolKeyword}\tsargs[marg]{key}\tsargs[marg]{symbol}
%   \end{codesyntax}
%   In order to typeset indefinite integrals, the package essentially offers two keys: \tsobj[key]{symbol} and \tsobj[key]{llimit}. Although they are simple to use, it is still laborious to write both of them with their values. This is why \tsobj[pkg]{intexgral} provides so-called \emph{shortcut} keys, whose purpose is to combine the selection of the symbol and the limits. Unlike the two previous macros, this involves creating entirely new keys, rather than simply specific values assigned to \tsobj[key]{symbol}. On their own, these keys modify the integral symbol. The value of these keys corresponds to the lower bound. Here is the list of all \emph{symbol-key} defined by the package:
%   \begin{describelist*}{key}
%     \describe{single}{used symbol = \tsobj[macro]{\int}}
%     \describe{double}{used symbol = \tsobj[macro]{\iint}}
%     \describe{triple}{used symbol = \tsobj[macro]{\iiint}}
%     \describe{quadruple}{used symbol = \tsobj[macro]{\iiiint}}
%     \describe{contour}{used symbol = \tsobj[macro]{\oint}}
%     \describe{surface}{used symbol = \tsobj[macro]{\oiint}}
%     \describe{volume}{used symbol = \tsobj[macro]{\oiiint}}
%   \end{describelist*}
%   \begin{tsremark}[\textcolor{RoyalRed}{Important:}]
%   allowing users to create their own keys for the \tsobj[macro]{\integral} macro poses a risk that will be explained in more detail in the next section. For now, just remember that all these macros\footnote{With the exception of \tsobj[macro]{\DeclareSymbolKeyword}, which assumes that the user knows what they are doing.} will issue a warning message if you attempt to create a new \emph{symbol-key} with the same name as a group of defined limits.
%   \end{tsremark}
% \end{codedescribe}
%
% \begin{codestore}[symbolkeyword]
%   \begin{equation}
%     \integral[contour=\mathcal{C}, diff-vec]{f(\vec r)}
%   \end{equation}
% \end{codestore}
% 
% \tsdemo*[intexgral]{symbolkeyword}
% 
% \section{\emph{Special} syntax}
% 
% \subsection{Presentation of the syntax}
% 
% \begin{codedescribe}[macro, new={v3.0.0}]{\integral}
%   \begin{codesyntax}
%     \tsmacro{\integral}[limits:variables(+j):mode]{integrand}
%   \end{codesyntax}
%   With regard to definite integrals, the user will be required to use a maximum of four keys to compose them: \tsobj[key]{limits}, \tsobj[key]{variables}, \tsobj[key]{mode} and \tsobj[key]{jacobian}. However, they can be criticised for the same reason as before; writing these four keys, which can take quite long arguments, runs counter to the main objective of this package. Thus, the user can designate as an optional argument of \tsobj[macro]{\integral} a syntax called \emph{special} that is unique to \tsobj[pkg]{intexgral}. The logic is as follows:
%   \begin{itemize}[label=\textemdash]
%     \item You specify a \emph{valid} argument for the \tsobj[key]{limits} key
%     \item You specify a \emph{valid} argument for the \tsobj[key]{variables} key
%     \item You specify a \emph{valid} argument for the \tsobj[key]{mode} key
%   \end{itemize}
%   And you separate everything with a colon. Let's look at an example to better understand.
% \end{codedescribe}
% 
% \begin{codestore}[special-syntax]
%   \begin{equation}
%     \integral[1, 2; 4, 5:y, x:nested]{x; y}
%   \end{equation}
% \end{codestore}
% 
% \tsdemo*[intexgral]{special-syntax}
% 
% We mean by \emph{valid argument} that all forms of values accepted by the four keys can also be likewise adopted by the special syntax. This hence includes keywords.
% 
% \begin{codestore}[special-syntax*]
%   \begin{equation}
%     \integral[radius;circle;scircle:spherical:product]{r;\theta;\phi}
%   \end{equation}
% \end{codestore}
% 
% \tsdemo*[intexgral]{special-syntax*}
% 
% To include the Jacobian, simply write \texttt{+j} after the argument of \tsobj[key]{variables}. The mode can also be indicated using only an initial.
% 
% \begin{codestore}[special-syntax**]
%   \begin{equation}
%     \integral[radius;circle;scircle:spherical+j:p]{;;}
%   \end{equation}
% \end{codestore}
% 
% \tsdemo*[intexgral]{special-syntax**}
% 
% It is important to reiterate that in the classic configuration of the optional argument, it is of course not mandatory to specify the four keys mentioned. The same applies to this syntax. Since the key \tsobj[key]{mode} is not necessary for \tsobj[option]{default}, this part can be omitted.
% 
% \begin{codestore}[special-syntax***]
%   \begin{equation}
%     \integral[1, 2; 3, 4:x, y]{xy}
%   \end{equation}
% \end{codestore}
% 
% \tsdemo*[intexgral]{special-syntax***}
% 
% You can also take advantage of the automatic placement of \enquote{$\mathrm dx$} with this syntax, ignoring \tsobj[key]{variables}.
% 
% \begin{codestore}[special-syntax****]
%   \begin{equation}
%     \integral[1, 10]{x}
%   \end{equation}
% \end{codestore}
% 
% \tsdemo*[intexgral]{special-syntax****}
% 
% One notable special case of this syntax is that if you want to modify the variable without specifying the bounds, you can do so more quickly as follows:
% 
% \begin{codestore}[special-syntax*****]
%   \begin{equation}
%     \integral[:z]{f(z)}
%   \end{equation}
% \end{codestore}
% 
% \tsdemo*[intexgral]{special-syntax*****}
% 
% Even if it is not recommended.
% 
% \subsection{Functioning of the syntax}
% 
% From the user's perspective, it is relatively simple to choose which syntax to adopt. However, the package must be able to distinguish between the two. Without going too deeply into the details\footnote{The more courageous among you are still invited to read the source code.} of the implementation, it is worth noting that the package attempts to extract the first key from the optional argument\footnote{Or at least, the group of tokens it thinks correspond to a key.} and checks if it exists. This is why, if you create a key with \tsobj[macro]{\NewSymbolKeyword} whose name may probably serve as integration bounds, \tsobj[pkg]{intexrgal} may falsely evaluate the nature of the optional argument. In this kind of situation, the interpretation of the keys will be incorrect and the result will be wrong.
% 
% \section{Additional parameters}\label{sec:setup}
% 
% \begin{codedescribe}[macro, new={v3.0.0}, update={v4.0.0}]{\IntegralSetup}
%   \begin{codesyntax}[macro]
%   \tsmacro{\IntegralSetup}{list of parameters}
%   \end{codesyntax}
%   This macro allows, under the syntax \tsobj[meta]{key=value}, to modify parameters related to certain keys. All assignments made are local and the macro can therefore be placed in a group if needed. Here is an example of the macro with the default assignments of the package:
% \end{codedescribe}
% 
% \begin{codestore}[default-parameters]
%   \IntegralSetup{
%     diff-symb    = \l_@@_default_differential_symbol_tl, % see at the beginning of the implementation
%     defaultvar   = {x},
%     defaultvar*  = {r},
%     varsep       = \thinmuskip,
%     diffsep      = \thinmuskip,
%     innersymbsep = {-5mu},
%     postsymbsep  = {-1mu},
%     vectorstyle  = \vec,
%     domainstyle  = \mathbb,
%     novar    = false
%   }
% \end{codestore}
% 
% \tscode*[intexgral]{default-parameters}
% 
% \begin{codedescribe}[key, new={v4.0.0}]{diff-symb}
%  \begin{codesyntax}[key]
%   \tsobj[key]{diff-symb}=\tsobj[meta]{control sequence}
% \end{codesyntax}
% This key controls the symbol used for differentials. If the option \tsobj[key]{italic} is used, \tsobj[macro]{\mathnormal} is applied (see at the beginning of the implementation). Unlike the homonymous key of \tsobj[macro]{\integral}, this one acts at a more global level.
% \end{codedescribe}
% 
% \begin{codedescribe}[key, new={v3.0.0}]{defaultvar, defaultvar*}
%   \begin{codesyntax}[key]
%     \tsobj[key]{defaultvar}=\tsargs[marg]{variables}
%   \end{codesyntax}
%   This key modifies the variable automatically inserted when \tsobj[key]{variables} is not used. The argument may correspond to a keyword defined by \tsobj[macro]{\NewVariableKeyword}. The starred variant of the key controls the integration parameter to be typeset with \tsobj[key]{diff-vec}.
% \end{codedescribe}
% 
% \begin{codedescribe}[key, new={v4.0.0}]{varsep}
%   \begin{codesyntax}[key]
%     \tsobj[key]{varsep}=\tsobj[meta]{muexpr}
%   \end{codesyntax}
% This key modifies the spacing between the integrand (or the Jacobian if it is displayed) and the variables.
% \end{codedescribe}
% 
% \begin{codedescribe}[key, new={v4.0.0}]{diffsep}
%   \begin{codesyntax}[key]
%     \tsobj[key]{diffsep}=\tsobj[meta]{muexpr}
%   \end{codesyntax}
% This key modifies the spacing between the differentials themselves.
% \end{codedescribe}
% 
% \begin{codedescribe}[key, new={v4.0.0}]{innersymbsep}
%   \begin{codesyntax}[key]
%     \tsobj[key]{innersymbsep}=\tsobj[meta]{muexpr}
%   \end{codesyntax}
%   When the \tsobj[option]{default} mode is in effect, that is to say when the \tsobj[key]{limits} key is used, the package inserts a gap between the symbols to slightly bring them together. This key therefore allows you to adjust this spacing.
% \end{codedescribe}
% 
% \begin{codedescribe}[key, new={v4.0.0}]{postsymbsep}
%   \begin{codesyntax}[key]
%     \tsobj[key]{postsymbsep}=\tsobj[meta]{muexpr}
%   \end{codesyntax}
% This key controls the spacing between the symbol and the element that follows (integrand or differential depending on \tsobj[key]{invert-limits}).
% \end{codedescribe}
% 
% \begin{codedescribe}[key, new={v3.0.0}]{vectorstyle}
%   \begin{codesyntax}[key]
%     \tsobj[key]{vectorstyle}=\tsobj[meta]{control sequence}
%   \end{codesyntax}
%   This key modifies the macro to be applied to variables when \tsobj[key]{diff-vec} is in action. It is therefore possible to alter the style of the vector: bold, underlined, or even another style from a particular package, \tsobj[pkg]{esvect} for example.
% \end{codedescribe}
% 
% \begin{codedescribe}[key, new={v3.0.0}]{domainstyle}
%   \begin{codesyntax}[key]
%     \tsobj[key]{domainstyle}=\tsobj[meta]{control sequence}
%   \end{codesyntax}
%   Similarly, it is possible to change the macro to be applied for the \tsobj[key]{domain(*)} keys.
% \end{codedescribe}
% 
% \begin{codedescribe}[key, new={v3.0.0}, update={v4.0.0}]{novar}
%   \begin{codesyntax}[key]
%     \tsobj[key]{novar}=\tsobj[option, meta or]{true,\uline{false}}
%   \end{codesyntax}
%   When a large part of the document requires not to include the differentials, it is possible to extend the duration of the more punctual action of \tsobj[key]{variables}\texttt{=}\tsobj[option]{none}.
% \end{codedescribe}
% 
% \newpage
% 
% \phantomsection
% \addcontentsline{toc}{section}{Changelog}
% \begin{changelog}[sectioncmd=\section*]
% 
% \begin{version}[v=4.0.0, date=06-06-2026]
% \added
% \item Detailed implementation.
% \item Keys \tsobj[key]{varsep}, \tsobj[key]{diffsep}, \tsobj[key]{postsymbsep} and \tsobj[key]{diff-order}.
% \item Variable keyword \tsobj[option]{cylindrical*}.
% \changed
% \item The \tsobj[pkg]{derivative} is no longer a dependency. The differentials are now managed internally.
% \item Key \tsobj[key]{symbolskip}, renamed in \tsobj[key]{innersymbsep}.
% \item Key \tsobj[key]{hide-diff}, renamed in \tsobj[key]{novar}.
% \removed
% \item Keys \tsobj[key]{diff-star} and \tsobj[key]{diff-options}.
% \end{version}
% 
% \begin{version}[v=3.0.1, date=02-01-2026]
% \fixed
% \item Bug with jacobian in special syntax (\href{https://github.com/ankaa3908/intexgral/issues/3}{issue \#3}).
% \item French and English documentations (\href{https://github.com/ankaa3908/intexgral/issues/4}{issue \#4}, \href{https://github.com/ankaa3908/intexgral/issues/6}{issue \#6} and \href{https://github.com/ankaa3908/intexgral/issues/7}{issue \#7}).
% \changed
% \item Limits keywords \tsobj[option]{positive} and \tsobj[option]{real} now contain a $+$ sign (\href{https://github.com/ankaa3908/intexgral/issues/5}{issue \#5}).
% \end{version}
% 
% \begin{version}[v=3.0.0, date=24-12-2025]
% \added
% \item Special syntax.
% \item Keys \tsobj[key]{domain*} and \tsobj[key]{mode}.
% \item Macros \tsobj[macro]{\IntegralSetup} and \tsobj[macro]{\NewSymbolKeyword}.
% \removed 
% \item Macros \tsobj[macro]{\defaultdiff}, \tsobj[macro]{\defaultdiff}, \tsobj[macro]{\defaultvdiff} et \tsobj[macro]{\vdiffstyle} in favour of \tsobj[macro]{\IntegralSetup}.
% \item Keys controlling both symbol and limit, now managed at a higher level by \tsobj[macro]{\NewSymbolKeyword}.
% \item \tsobj[key]{int-split} key, in favour of \tsobj[key]{mode}.
% \item \tsobj[macro]{\NewInegralSymbol} macro.
% \changed
% \item The names of some keys (\tsobj[key]{variable} in \tsobj[key]{variables}, \tsobj[key]{lower-lim} and \tsobj[key]{upper-lim} in \tsobj[key]{llimit} and \tsobj[key]{ulimit}, \tsobj[key]{int-symb} in \tsobj[key]{symbol}, \tsobj[key]{invert-differintials} and \tsobj[key]{hide-differentials} in \tsobj[key]{invert-diff} and \tsobj[key]{hide-diff}).
% \item \tsobj[key]{jacobian} and \tsobj[key]{diff-star} no longer require a boolean value. Simply writing them will now enable their features.
% \item \tsobj[key]{hide-diff} assigned to a local option rather than a package one.
% \item \tsobj[key]{limits-mode} assigned to a package option rather than a macro key
% \end{version}
% 
% \begin{version}[v=v2.0.1, date=13-09-2025]
% \fixed
% \item  Compatibiliy issue between unicode-math and amssyb depending on the loading order (\href{https://github.com/ankaa3908/intexgral/issues/2}{problem \#2}).
% \end{version}
% 
% \begin{version}[v=2.0.0, date=09-09-2025]
% \added 
% \item Macros \tsobj[macro]{\intexgralsetup}, \tsobj[macro]{\defaultdiff},\tsobj[macro]{\defaultvdiff} and \tsobj[macro]{\vdiffstyle}.
% \changed
% \item Warning messages related to non-existing symbols. They are now only triggered when the integral is typeset.
% \removed
% \item \tsobj[key]{diff-vec-style} key in favour of \tsobj[macro]{\vdiffstyle}.
% \item Warning message about misuse of semi-colon in conjunction with the \tsobj[key]{int-split} key.
% \fixed 
% \item Bug where the integrand was not reset when \tsobj[macro]{\integral} was used successively in the same \TeX\ group (\href{https://tex.stackexchange.com/questions/749990/issue-with-integral-command-from-intexgral-package-in-math-mode}{details here}).
% \item \tsobj[pkg]{expl3} log leftovers that shouldn't have been there.
% \end{version}
% 
% \begin{version}[v=1.1.0, date=29-07-2025]
% \added
% \item Starred variatn for the keys controlling both symbol and limit (keys \tsobj[key]{single}, \tsobj[key]{contour} etc).
% \end{version}
% 
% \shortversion{v=1.0.0, date=26-07-2025, changes=Version initiale.}
% 
% \end{changelog}
% 
% \end{documentation}
% 
% \begin{implementation}
% 
% \part[Implementation]{implementation}
% 
% \CodelineNumbered
% 
% \section{Initialisation}
% 
%    \begin{macrocode}
%<*package>
%<@@=intexgral>
%    \end{macrocode}
%    
% \subsection{Pacakge declaration}
%
%    \begin{macrocode}
\NeedsTeXFormat{LaTeX2e}
\RequirePackage{expl3}[2025-05-14]

\def\intexgral@module{intexgral}
\def\intexgral@version{v4.0.0}
\def\intexgral@date{2026-05-26}
\def\intexgral@description{A LaTeX package for typesetting integrals}

\ProvidesExplPackage
  \intexgral@module
  \intexgral@date
  \intexgral@version
  \intexgral@description
%    \end{macrocode}
%    
% \subsection{Preliminary verifications}
%
% We check that the version of expl3 is recent enough before continuing. The package requires the macro \tsobj[macro]{\regex_if_match:nnTF}, previously named \tsobj[macro]{\regex_match:nnTF} in versions prior to May 2025.
% 
%    \begin{macrocode}
\msg_new:nnn { intexgral } { expl3-too-old }
  {
    Your~expl3~installation~is~too~old,~
    "intexgral"~requires~expl3~dated~2025-05-14~or~later.\iow_newline:
    Package~loading~has~been~aborted.\iow_newline:
    \msg_see_documentation_text:n { intexgral }
  }

\@ifpackagelater{expl3}{2025-05-14}{}
  { \msg_critical:nn { intexgral } { expl3-too-old } }
%    \end{macrocode}
%
% Since \tsobj[pkg]{intexgral} uses \tsobj[macro]{\mathbb} for the keys \tsobj[key]{domain(*)}, we must ensure that the macro exists. The verification is placed in a \emph{hook} executed at the beginning of the document in case \tsobj[pkg]{amsfonts} or any package defining \tsobj[macro]{\mathbb} is loaded after \tsobj[pkg]{intexgral}. This serves particularly to avoid conflicts between packages (\tsobj[pkg]{unicode-math} and \tsobj[pkg]{amssymb} for example).
%
%    \begin{macrocode}
\hook_gput_code:nnn { begindocument/before } { . } 
  { \cs_if_exist:NF \mathbb { \RequirePackage{amsfonts} } }
%    \end{macrocode}
%    
% \section{Package options}
% 
% \subsection{Variable definition}
% 
% \begin{codedescribe}[variable3]{\l_@@_invert_limits_bool}
% Booleen inverting the order of the limits.
%    \begin{macrocode}
\bool_new:N \l_@@_invert_limits_bool
%    \end{macrocode}
% \end{codedescribe}
% 
% \begin{codedescribe}[variable3]{\l_@@_deactivate_variables_bool}
% Determines whether the differentials should be displayed or not.
%    \begin{macrocode}
\bool_new:N \l_@@_deactivate_variables_bool
%    \end{macrocode}
% \end{codedescribe}
% 
% \begin{codedescribe}[variable3]{\l_@@_invert_differential_bool}
% Determines the order of display for the differentials.
%    \begin{macrocode}
\bool_new:N \l_@@_invert_differential_bool
%    \end{macrocode}
% \end{codedescribe}
% 
% \begin{codedescribe}[variable3]{\l_@@_default_differential_symbol_tl}
% Contains the default symbol for differentials.
%    \begin{macrocode}
\tl_new:N \l_@@_default_differential_symbol_tl
%    \end{macrocode}
% \end{codedescribe}
% 
% \begin{codedescribe}[variable3]{\l_@@_limits_style_tl}
% Contains the \TeX\ primitive to use for the limits style.
%    \begin{macrocode}
\tl_new:N \l_@@_limits_style_tl
%    \end{macrocode}
% \end{codedescribe}
% 
% \subsection{Package option declaration}
% We can now define the package options before loading them.
% 
%    \begin{macrocode}
\keys_define:nn { intexgral }
  {
    invert-limits .bool_set:N = \l_@@_invert_limits_bool,
    invert-limits .usage:n    = { preamble },
    invert-limits .initial:n  = { false },

    invert-diff .bool_set:N = \l_@@_invert_differential_bool,
    invert-diff .usage:n    = { preamble },
    invert-diff .initial:n  = { false },

    limits-mode .choice:,
    limits-mode / limits .code:n =
      { \tl_set_eq:NN \l_@@_limits_style_tl \tex_limits:D },
    limits-mode / nolimits .code:n =
      { \tl_set_eq:NN \l_@@_limits_style_tl \tex_nolimits:D },

    limits-mode .usage:n   = { preamble },
    limits-mode .initial:n = { nolimits },

    italic .choice:,
      italic / true .code:n  =
        {
          \tl_set:Nn \l_@@_default_differential_symbol_tl 
            { \mathnormal{d} }
        },
      italic / false .code:n =
        {
          \tl_set:Nn \l_@@_default_differential_symbol_tl
            { \mathrm{d} }
        },
    italic .usage:n   = { preamble },
    italic .initial:n = { false },

    upright .choice:,
      upright / true .code:n  =
        {
          \tl_set:Nn \l_@@_default_differential_symbol_tl
            { \mathrm{d} }
        },
      upright / false .code:n =
        {
          \tl_set:Nn \l_@@_default_differential_symbol_tl
            { \mathnormal{d} }
        },
    upright .usage:n   = { preamble },
    upright .initial:n = { true }
  }

\ProcessKeyOptions[intexgral]
%    \end{macrocode}
%    
% \section{Messages}
% 
% The next part of the implementation defines all the warning and error messages.
% 
% \begin{codedescribe}[variable3]{\g_@@_integral_number_int}
% We start by creating a global counter to number the integrals in the document.
%    \begin{macrocode}
\int_gzero_new:N \g_@@_integral_number_int
%    \end{macrocode}
% \end{codedescribe}
% 
% \begin{codedescribe}[macro3, EXP]{\@@_warning_msg_header:}
% We can now define a macro that acts as a header to warning messages. It uses the counter defined just before.
%    \begin{macrocode}
\cs_new:Nn \@@_warning_msg_header: {
  (
    integral~no.~
    \int_use:N\g_@@_integral_number_int\c_space_tl
    \msg_line_context:
  )
  \iow_newline:
}
%    \end{macrocode}
% \end{codedescribe}
% 
% All the messages are then defined.
%    \begin{macrocode}
\msg_new:nnnn { intexgral } { symb-key-alr-def }
  { Symbol~key~"\tl_trim_spaces:n{#1}"~is~already~defined. }
  { Use~\token_to_str:N \RenewSymbolKeyword\ instead. }

\msg_new:nnnn { intexgral } { symb-key-not-def }
  { Symbol~key~"\tl_trim_spaces:n{#1}"~is~not~defined. }
  { Use~\token_to_str:N \NewSymbolKeyword\ instead. }

\msg_new:nnnn { intexgral } { diff-group-alr-def }  
  { Differential~group~"\tl_trim_spaces:n{#1}"~is~already~defined. }
  { Use~\token_to_str:N \RenewVarKeyword\ instead. }

\msg_new:nnnn { intexgral } { diff-group-not-def }
  { Differential~group~"\tl_trim_spaces:n{#1}"~is~not~defined. }
  { Use~\token_to_str:N \NewVarKeyword\ instead. }

\msg_new:nnnn { intexgral } { lim-group-alr-def }
  { Limits~group~"\tl_trim_spaces:n{#1}"~is~already~defined. }
  { Use~\token_to_str:N \RenewLimitsKeyword\ instead. }

\msg_new:nnnn { intexgral } { lim-group-not-def }
  { Limits~group~"\tl_trim_spaces:n{#1}"~is~not~defined. }
  { Use~\NewLimitsKeyword\ instead. }

\msg_new:nnn { intexgral } { key-exists-for-lim }
  {
    Symbol~key~already~defined~with~
    \token_to_str:N \NewLimitsKeyword\ \@@_warning_msg_header:
    Key~"#1"~already~exists~for~predefined~limits.~
    This~can~disrupt~the~functioning~of~the~special~syntax.
  }

\msg_new:nnn { intexgral } { unknown-symb }
  {
    Unknown~integral~symbol~\@@_warning_msg_header:
    The~symbol~\tl_trim_spaces:n{#1}~has~been~replaced~with~
    \token_to_str:N\int.
  }

\msg_new:nnn { intexgral } { no-jacobian }
  {
    Jacobian~unavailable~\@@_warning_msg_header:
    A~Jacobian~was~requested~for~the~"#1"~keyword,
    ~but~none~was~declared~for~the~latter.
  }

\msg_new:nnn { intexgral } { use-glyph-instead }
  {
    Consider~using~the~dedicated~glyph~\@@_warning_msg_header:
    The~key~"nint"~was~used~with~fewer~than~
    5~regular~integral~signs.
  }
%    \end{macrocode}
%    
% \section{Useful variants}
% 
% \begin{codedescribe}[macro3]{
% \regex_if_match:nVTF,
% \str_if_eq:neTF,
% \str_if_eq:neF,
% \keys_if_exist:nVTF,
% \seq_use:Ne,
% \str_if_eq_p:en,
% \prop_gput:Nne,
% \seq_gset_split:cnn,
% \seq_set_split:Nnf
% }
%    \begin{macrocode}
\cs_generate_variant:Nn \regex_if_match:nnTF { nVTF }
\cs_generate_variant:Nn \str_if_eq:nnTF      { neTF }
\cs_generate_variant:Nn \str_if_eq:nnF       { neF  }
\cs_generate_variant:Nn \keys_if_exist:nnTF  { nVTF }
\cs_generate_variant:Nn \seq_use:Nn          { Ne   }
\cs_generate_variant:Nn \str_if_eq_p:nn      { en   }
\cs_generate_variant:Nn \prop_gput:Nnn       { Nne  }
\cs_generate_variant:Nn \seq_gset_split:Nnn  { cnn  }
\cs_generate_variant:Nn \seq_set_split:Nnn   { Nnf }
%    \end{macrocode}
% \end{codedescribe}
%    
% \section{Internal variables}
% 
% \subsection{Tokens}
% 
% \begin{codedescribe}[variable3]{\l_@@_integrand_tl}
% Token containg the integrand. It is used as-is of the \tsobj[option]{default} mode, whereas for the other modes, it is subsequently separated into a sequence.
%    \begin{macrocode}
\tl_new:N \l_@@_integrand_tl
%    \end{macrocode}
% \end{codedescribe}
% 
% \begin{codedescribe}[variable3]{\l_@@_integral_symbol_tl}
% Token containing the integral symbol. When not modified by the \tsobj[key]{symbol} key, it should still be initialised to \tsobj[macro]{\int}.
%    \begin{macrocode}
\tl_new:N \l_@@_integral_symbol_tl
\tl_set_eq:NN \l_@@_integral_symbol_tl \int
%    \end{macrocode}
% \end{codedescribe}
% 
% \begin{codedescribe}[variable3]{\l_@@_lower_limit_tl, \l_@@_upper_limit_tl}
% Tokens containg the limits
%    \begin{macrocode}
\tl_new:N \l_@@_lower_limit_tl
\tl_new:N \l_@@_upper_limit_tl
%    \end{macrocode}
% \end{codedescribe}
% 
% \begin{codedescribe}[variable3]{\l_@@_differential_symbol_tl}
% Token containing the symbol for differentials, which can be modified with the \tsobj[key]{diff-symb} key. It is initialised to the \tsobj[variable3]{\l_@@_default_differential_symbol_tl} token.
%    \begin{macrocode}
\tl_new:N \l_@@_differential_symbol_tl
%    \end{macrocode}
% \end{codedescribe}
% 
% \begin{codedescribe}[variable3]{\l_@@_default_differential_tl, \l_@@_default_vector_differential_tl}
% Tokens containing the default differentials, used when the key \tsobj[key]{variables} is not provided.
%    \begin{macrocode}
\tl_new:N \l_@@_default_differential_tl
\tl_new:N \l_@@_default_vector_differential_tl
%    \end{macrocode}
% \end{codedescribe}
% 
% \begin{codedescribe}[variable3]{\l_@@_vectorial_differential_style_tl, \l_@@_domain_style_tl}
% Tokens containing the macros to apply to the\tsobj[key]{diff-vec} and \tsobj[key]{domain(*)} keys.
%    \begin{macrocode}
\tl_new:N \l_@@_vectorial_differential_style_tl
\tl_new:N \l_@@_domain_style_tl
%    \end{macrocode}
% \end{codedescribe}
% 
% \begin{codedescribe}[variable3]{\l_@@_domain_char_tl, \l_@@_domain_dimension_tl}
% Token used for storing respectively the character and the dimension of a domain.
%    \begin{macrocode}
\tl_new:N \l_@@_domain_char_tl
\tl_new:N \l_@@_domain_dimension_tl
%    \end{macrocode}
% \end{codedescribe}
% 
% \begin{codedescribe}[variable3]{\c_@@_left_bracket_tl, \c_@@_right_bracket_tl}
% Constant tokens for the \tsobj[key]{limits*} key.
%    \begin{macrocode}
\tl_const:Nn \c_@@_left_bracket_tl { [ }
\tl_const:Nn \c_@@_right_bracket_tl { ] }
%    \end{macrocode}
% \end{codedescribe}
% 
% \begin{codedescribe}[variable3]{\l_@@_key_name_tl}
% Token containing the potential name of a key, used to differentiate the syntax \tsobj[meta]{key=value} from the special syntax.
%    \begin{macrocode}
\tl_new:N \l_@@_key_name_tl
%    \end{macrocode}
% \end{codedescribe}
% 
% \subsection{Booleans}
% 
% \begin{codedescribe}[variable3]{\l_@@_manual_differential_bool}
% Boolean used to determine if the differentials are inserted through \tsobj[macro]{\differentials}.
%    \begin{macrocode}
\bool_new:N \l_@@_manual_differential_bool
%    \end{macrocode}
% \end{codedescribe}
% 
% \begin{codedescribe}[variable3]{\l_@@_custom_variables_bool}
% Boolean used to determine if the variables are defined with \tsobj[key]{variables}.
%    \begin{macrocode}
\bool_new:N \l_@@_custom_variables_bool
%    \end{macrocode}
% \end{codedescribe}
% 
% \begin{codedescribe}[variable3]{\l_@@_separate_integral_bool}
% Boolean used to determine if the integral is \emph{separated} into several parts, that is to say, if the modes \tsobj[option]{nested} or \tsobj[option]{product} are in effect.
%    \begin{macrocode}
\bool_new:N \l_@@_separate_integral_bool
%    \end{macrocode}
% \end{codedescribe}
% 
% \begin{codedescribe}[variable3]{\l_@@_vectorial_differential_bool}
% Boolean used to determine if vectors should be applied to the differentials.
%    \begin{macrocode}
\bool_new:N \l_@@_vectorial_differential_bool
%    \end{macrocode}
% \end{codedescribe}
% 
% \begin{codedescribe}[variable3]{\l_@@_jacobian_bool}
% Boolean used to determine if the Jacobian should be displayed.
%    \begin{macrocode}
\bool_new:N \l_@@_jacobian_bool
%    \end{macrocode}
% \end{codedescribe}
% 
% \subsection{Sequences and clists}
% 
% \begin{codedescribe}[variable3]{\l_@@_domain_seq}
% Sequence containing the integration domains and their dimensions (the main sequences will be explained later).
%    \begin{macrocode}
\seq_new:N \l_@@_domain_seq
%    \end{macrocode}
% \end{codedescribe}
% 
% \begin{codedescribe}[variable3]{\l_@@_differential_order_clist}
% List containing the powers of the differentials (clist and not sequence since \tsobj[macro]{.seq_set:N} does not exist for keys).
%    \begin{macrocode}
\clist_new:N \l_@@_differential_order_clist
%    \end{macrocode}
% \end{codedescribe}
% 
% \subsection{Property lists}
% 
% \begin{codedescribe}[variable3]{\g_@@_limits_keyword_prop, \g_@@_differential_group_keyword_prop}
% A property list is created to store limits and variable keywords.
%    \begin{macrocode}
\prop_new:N \g_@@_limits_keyword_prop
\prop_new:N \g_@@_differential_group_keyword_prop
%    \end{macrocode}
% \end{codedescribe}
% 
% \subsection{Musksip}
% 
% \begin{codedescribe}[variable3]{\l_@@_symbol_inner_sep_muskip, \l_@@_symbol_post_sep_muskip,\l_@@_differential_sep_muskip, \l_@@_variable_sep_muskip}
% Internal muskips used to store the spacings for respectively:
% \begin{enumerate}
%   \item The spacing between the symbols when several are generated by \tsobj[key]{limits(*)}.
%   \item The spacing that follows the symbol.
%   \item The spacing between the differentials when the key \tsobj[key]{variables} is used.
%   \item The spacing between the differentials and the integrand.
% \end{enumerate}
%    \begin{macrocode}
\muskip_new:N \l_@@_symbol_inner_sep_muskip
\muskip_new:N \l_@@_symbol_post_sep_muskip
\muskip_new:N \l_@@_variable_sep_muskip
\muskip_new:N \l_@@_differential_sep_muskip
%    \end{macrocode}
% \end{codedescribe}
% 
% \subsection{String}
% 
% \begin{codedescribe}[variable3]{\l_@@_display_mode_str}
% Internal variable used to store the integral's display mode. The reason for using a \emph{string} stems from the lack of a \tsobj[macro, TF]{\tl_case:nn} macro.
%    \begin{macrocode}
\str_new:N \l_@@_display_mode_str
%    \end{macrocode}
% \end{codedescribe}
% 
% \section{Auxiliary macros and sequences}
% 
% Here we define the two \TeX\ primitives \tsobj[macro]{\mkern} and \tsobj[macro]{\mathchoice} with an \tsobj[pkg]{expl3}-like syntax.
% 
% \begin{codedescribe}[macro3]{\@@_mkern:N}
% \begin{codesyntax}
%   \tsobj[macro3]{\@@_mkern:N} \tsobj[meta]{muskip}
% \end{codesyntax}
% 
% \end{codedescribe}
% 
%    \begin{macrocode}
\cs_new_protected:Npn \@@_mkern:N #1
  { \tex_mkern:D #1 \scan_stop: }
%    \end{macrocode}
%
% \begin{codedescribe}[macro3]{\@@_mathchoice:nnnn}
% \begin{codesyntax}
%   \tsobj[macro3]{\@@_mathchoice:nnnn}\tsargs[marg]{display style}\tsargs[marg]{inline style}\tsargs[marg]{script style}\tsargs[marg]{scriptscript style}
% \end{codesyntax}
% \end{codedescribe}
% 
%    \begin{macrocode}
\cs_new_eq:NN \@@_mathchoice:nnnn \mathchoice
%    \end{macrocode}
% 
% \begin{codedescribe}[macro3]{\@@_invert_limits:w}
% \begin{codesyntax}
%   \tsobj[macro3]{\@@_invert_limits:w} \tsobj[meta]{lower limit},\tsobj[meta]{upper limit}\tsobj[macro3]{\q_stop}
% \end{codesyntax}
% Macro inverting the limits for the \tsobj[option]{invert-limits} option. The action is fully expandable and is more efficient than a \tsobj[macro]{\seq_inverse:N} in the context of this package, knowing that a maximum of two elements are present.
% \end{codedescribe}
%    \begin{macrocode}
\cs_new:Npn \@@_invert_limits:w #1,#2\q_stop{#2,#1}
%    \end{macrocode}
%
% \begin{codedescribe}[macro3]{\@@_general_integral_symbol:}
% We create a macro that contains the general expression for an integral: symbol, limits style, lower limit and upper limit. We just have to assign the tokens to compose a complete symbol, and possibly repeat the macro for integrals defined on several variables.
%    \begin{macrocode}
\cs_new:Npn \@@_general_integral_symbol:
  {
    \l_@@_integral_symbol_tl
    \l_@@_limits_style_tl
    \c_math_subscript_token
      { \l_@@_lower_limit_tl }
    \c_math_superscript_token
      { \l_@@_upper_limit_tl }
  }
%    \end{macrocode}
% \end{codedescribe}
%
% \subsection{Main sequences}
% 
% Given the large number of existing keys and possible combinations, the tokens must be carefully organized and stored in order to assemble the final integral. The approach taken was to create sequences dedicated to each element of an integral:  
% 
% \begin{codedescribe}[variable3]{\l_@@_integral_symbol_seq}
% One for the symbol (or the \emph{symbols}, if the key \tsobj[key]{limits*} is in use).
%    \begin{macrocode}
\seq_new:N \l_@@_integral_symbol_seq
%    \end{macrocode}
% \end{codedescribe}
% 
% \begin{codedescribe}[variable3]{\l_@@_integrand_seq}
% One for the integrand (or \emph{the} integrands, if split into several parts by \tsobj[option]{nested} or \tsobj[option]{product}).
%    \begin{macrocode}
\seq_new:N \l_@@_integrand_seq
%    \end{macrocode}
% \end{codedescribe}
% 
% \begin{codedescribe}[variable3]{\l_@@_jacobian_seq}
% One for the Jacobian.
%    \begin{macrocode}
\seq_new:N \l_@@_jacobian_seq
%    \end{macrocode}
% \end{codedescribe}
% 
% \begin{codedescribe}[variable3]{\l_@@_differential_seq}
% And finally, one for the differentials.
%    \begin{macrocode}
\seq_new:N \l_@@_differential_seq
%    \end{macrocode}
% \end{codedescribe}
% 
% This way, we can construct the final integral by varying how we extract the different elements from these sequences. Here is a more concrete example:
% 
% For an indefinite integral.
% \begin{equation*}
%   \stackrel{\text{S}}{\boxed{\iint_S}}^^A
%   \stackrel{\text{I}}{\boxed{f(x, y)}}^^A
%   \stackrel{\text{D}}{\boxed{\mathrm{d}x\thinspace\mathrm{d}y}}
% \end{equation*}
% 
% For a definite integral.
% \begin{equation*}
%   \stackrel{\text{S}_1}{\boxed{\int_a^b}}^^A
%   \stackrel{\text{S}_2}{\boxed{\int_c^d}}^^A
%   \stackrel{\text{I}_1}{\boxed{f(x)}}^^A
%   \stackrel{\text{I}_2}{\boxed{g(y)}}^^A
%   \stackrel{\text{D}_1}{\boxed{\mathrm{d}x}}^^A
%   \stackrel{\text{D}_2}{\boxed{\mathrm{d}y}}
% \end{equation*}
% 
% \subsection{Supplementary sequences}
% 
% A few additional sequences will be required during the intermediate stages, which will involve preparing the four main sequences listed above.
% 
% \begin{codedescribe}[variable3]{\l_@@_variables_seq}
% Firstly, a sequences containing the variables.
%    \begin{macrocode}
\seq_new:N \l_@@_variables_seq
%    \end{macrocode}
% \end{codedescribe}
%    
% \begin{codedescribe}[variable3]{\l_@@_limits_seq}
% Then, a sequence containing the pairs of integration limits, that is to say the elements of the \tsobj[key]{limits} key separated in two by the semicolon.
%    \begin{macrocode}
\seq_new:N \l_@@_limits_seq
%    \end{macrocode}
% \end{codedescribe}
% 
% \begin{codedescribe}[variable3]{\l_@@_upper_limits_seq, \l_@@_lower_limits_seq}
% Two sequences that contain the upper and lower limits respectively. Thus, if the key \tsobj[key]{limits} contains the elements \texttt{a;b}, \texttt{c;d} and \texttt{e;f}, then the sequence of lower limits will contain the elements \texttt{a}, \texttt{c} and \texttt{e}, while the sequence of upper limits will contain the elements \texttt{b}, \texttt{d} and \texttt{f}.
%    \begin{macrocode}
\seq_new:N \l_@@_upper_limits_seq
\seq_new:N \l_@@_lower_limits_seq
%    \end{macrocode}
% \end{codedescribe}
% 
% \section{Integration limits}\label{sec:implementationbornes}
% 
% \begin{codedescribe}[macro3]{\@@_parse_limits:n}
% \begin{codesyntax}
%   \tsobj[macro3]{\@@_parse_limits:n} \tsargs[marg]{lower limit, upper limit}, \tsobj[meta]{keyword}
% \end{codesyntax}
% This macro checks if its argument corresponds to a keyword defined for the limits, in which case it expands to its content, inverting it if necessary (according to the status of \tsobj[variable3]{\l_@@_invert_limits_bool}). Otherwise, the macro simply expands to its argument.
% \end{codedescribe}
% 
%    \begin{macrocode}
\cs_new:Npn \@@_parse_limits:n #1
  {
    \prop_if_in:NnTF \g_@@_limits_keyword_prop { #1 }
      {
        \bool_if:NTF \l_@@_invert_limits_bool
          {
            \exp_last_unbraced:Ne \@@_invert_limits:w 
              { \prop_item:Nn \g_@@_limits_keyword_prop { #1 } } 
              \q_stop
          }
          { \prop_item:Nn \g_@@_limits_keyword_prop { #1 } }
      }
      { #1 }
  }
%    \end{macrocode}
%
% \begin{codedescribe}[macro3]{\@@_set_limits_regular:}
% Once the argument of the key \tsobj[key]{limits} has been converted into a sequence (action performed within \tsobj[macro]{.code:n} in the key declaration), each pair of limits is assigned to a temporary sequence. This sequence is created so that if a keyword is present, it is replaced by its corresponding limits according to the previous macro. The action is executed within a \tsobj[macro]{\romannumeral}-type expansion to avoid expanding limits containing fragile macros (such as \tsobj[macro]{\mathbb}). The lower and upper limits can then be extracted from this sequence and assigned to their respective sequences.
% \end{codedescribe}
% 
%    \begin{macrocode}
\cs_new_protected:Nn \@@_set_limits_regular:
  {
    \seq_map_inline:Nn \l_@@_limits_seq
      {
        \seq_set_split:Nnf \l_tmpa_seq { , }
          { \@@_parse_limits:n { ##1 } }
        \seq_put_right:Ne \l_@@_lower_limits_seq
          { \seq_item:Nn \l_tmpa_seq { 1 } } 
        \seq_put_right:Ne \l_@@_upper_limits_seq
          { \seq_item:Nn \l_tmpa_seq { 2 } } 
      }
  }
%    \end{macrocode}
%
% \begin{codedescribe}[macro3]{\@@_set_limits_starred:}
% The following macro is similar to the previous one, but it handles limits in the form of intervals when the key \tsobj[key]{limits*} is used.
% \end{codedescribe}
% 
%    \begin{macrocode}
\cs_new_protected:Npn \@@_set_limits_starred:
  {
    \seq_map_indexed_inline:Nn \l_@@_limits_seq
      {
        \seq_set_split:Nnf \l_tmpa_seq { , }
          { \@@_parse_limits:n { ##2 } }
        \bool_if:NTF \l_@@_invert_limits_bool
          {
            \tl_set:Ne \l_@@_lower_limit_tl
              {
                \seq_item:Nn \l_tmpa_seq { 2 }
                \tex_mathpunct:D,
                \seq_item:Nn \l_tmpa_seq { 1 }
              }
          }
          {
            \tl_set:Ne \l_@@_lower_limit_tl
              {
                \seq_item:Nn \l_tmpa_seq { 1 }
                \tex_mathpunct:D,
                \seq_item:Nn \l_tmpa_seq { 2 }
              }
          }
            \bool_if:NTF \l_@@_invert_limits_bool
              { \seq_put_right:Ne \l_@@_upper_limits_seq }
              { \seq_put_right:Ne \l_@@_lower_limits_seq }
          {
            \str_case_e:nnF { \seq_item:Nn \l_tmpa_seq { 1 } }
              {
                { -\infty } { \tex_left:D \c_@@_right_bracket_tl }
              }
              { \tex_left:D \c_@@_left_bracket_tl }
            \tl_use:N \l_@@_lower_limit_tl
            \str_case_e:nnF { \seq_item:Nn \l_tmpa_seq { 2 } }
              {
                { +\infty } { \tex_right:D \c_@@_left_bracket_tl }
              }
              { \tex_right:D \c_@@_right_bracket_tl }
            }
      }
  }
%    \end{macrocode}
% 
% \begin{codedescribe}[macro3]{\@@_set_limits:nn}
% \begin{codesyntax}
%   \tsobj[macro3]{\@@_set_limits:nn}\tsargs[marg]{lower limit}\tsargs[marg]{upper limit}
% \end{codesyntax}
% \end{codedescribe}
% According to the \tsobj[option]{invert-limits} option, this macro assigns the lower and upper limits to their respective tokens. If the option is active, the assignment becomes somewhat counter-intuitive (the lower limit is assigned to \tsobj[variable3]{\l_@@_upper_limit_tl} and vice versa), but will still produce the expected result. The expansion of the limits is prevented to avoid issues with fragile macros as seen previously.
% 
%    \begin{macrocode}
\cs_new_protected:Npn \@@_set_limits:nn #1#2
  {
    \bool_if:NTF \l_@@_invert_limits_bool
      {
        \tl_set:Ne \l_@@_lower_limit_tl { \exp_not:n { #2 } }
        \tl_set:Ne \l_@@_upper_limit_tl { \exp_not:n { #1 } }
      }
      {
        \tl_set:Ne \l_@@_lower_limit_tl { \exp_not:n { #1 } }
        \tl_set:Ne \l_@@_upper_limit_tl { \exp_not:n { #2 } }
      }
  }
%    \end{macrocode}
% 
% \begin{codedescribe}[macro3]{\@@_generate_integral_sequence:}
% Once the sequences of lower and upper limits are filled, we can use the structure of the sequences to our advantage to automatically generate as many symbols as necessary. We proceed with an incremented loop in which we perform two actions:
% \begin{enumerate}
%   \item With the help of \tsobj[macro]{\@@_set_limits:nn}, we assign the integration limits to the \tsobj[variable3]{\l_@@_lower_limit_tl} and \tsobj[variable3]{\l_@@_upper_limit_tl} tokens from the $i$\textsuperscript{th} element of the \tsobj[variable3]{\l_@@_lower_limits_seq} and \tsobj[variable3]{\l_@@_upper_limits_seq} sequences respectively.
%   \item We place to the right (i.e. in order) of the \tsobj[variable3]{\l_@@_integral_symbol_seq} sequence the \tsobj[macro]{\@@_general_integral_symbol:} macro, which contains the general form of an integral. Note that we fully expand its content in order to retrieve the immediate assignment of tokens.
% \end{enumerate}
% \end{codedescribe}
% 
%    \begin{macrocode}
\cs_new_protected:Nn \@@_generate_integral_sequence:
  {
    \int_set:Nn \l_tmpa_int
      {
        \seq_if_empty:NTF \l_@@_lower_limits_seq
          { \seq_count:N \l_@@_upper_limits_seq }
          { \seq_count:N \l_@@_lower_limits_seq }
      }
    \int_step_inline:nn { \l_tmpa_int }
      {
        \@@_set_limits:nn
          { \seq_item:Nn \l_@@_lower_limits_seq { ##1 } }
          { \seq_item:Nn \l_@@_upper_limits_seq { ##1 } }
        \seq_put_right:Ne \l_@@_integral_symbol_seq
          { \@@_general_integral_symbol: }
      }
  }
%    \end{macrocode}
%
% \section{Variables}
%
% \begin{codedescribe}[macro3]{\@@_parse_variables:n}
% \begin{codesyntax}
%   \tsobj[macro3]{\@@_parse_variables:n} \tsargs[marg]{integration variable}, \tsobj[meta]{key}
% \end{codesyntax}
% This macro is applied to each element of the argument of the \tsobj[key]{variables} key to check whether it is a key or an explicit list, in the same way as for the limits. It mainly serves to transfer the elements of \tsobj[variable3]{\l_@@_variables_seq} (containing for example \enquote{$x, y, z$}) to \tsobj[variable3]{\l_@@_differential_seq} (which will contain \enquote{$\mathrm dx, \mathrm dy, \mathrm dz$}); while taking into account the different options that have been applied. In the case where no variable is entered, the macro takes care of placing $x$ or $\vec r$ depending on the value of the boolean \tsobj[variable3]{\l_@@_vectorial_differential_bool}.
% \end{codedescribe}
% 
%    \begin{macrocode}
\cs_new_protected:Nn \@@_parse_variables:
  {
    \bool_if:NTF \l_@@_custom_variables_bool
      {
        \seq_map_indexed_inline:Nn \l_@@_variables_seq
          {
            \seq_put_right:Ne \l_@@_differential_seq
              {
                \exp_not:V \l_@@_differential_symbol_tl
                \clist_if_empty:NF \l_@@_differential_order_clist
                  {
                    \c_math_superscript_token
                      {
                        \clist_item:Nn
                        \l_@@_differential_order_clist
                        { ##1 }
                      }
                  }
                \bool_if:NT \l_@@_vectorial_differential_bool
                  { \exp_not:V \l_@@_vectorial_differential_style_tl }
                  { ##2 }
              }
          }
      }
      {
        \seq_put_right:Ne \l_@@_differential_seq
          {
            \exp_not:V \l_@@_differential_symbol_tl
            \clist_if_empty:NF \l_@@_differential_order_clist
              {
                % TODO: allow passing a sequence for default variables; currently only the first element is naively extracted.
                \c_math_superscript_token
                  {
                    \clist_item:Nn
                    \l_@@_differential_order_clist
                    { 1 }
                  }
              }
            \bool_if:NTF \l_@@_vectorial_differential_bool
              { 
                \exp_not:V \l_@@_vectorial_differential_style_tl
                  {\l_@@_default_vector_differential_tl}
              }
              { \l_@@_default_differential_tl }
          }
      }
  }
%    \end{macrocode}
%
% \section{Special syntax}
% 
% \begin{codedescribe}[macro3, EXP]{\@@_retrieve_key_name:w}
% \begin{codesyntax}
%  \tsobj[macro3]{\@@_retrieve_key_name:w} \tsobj[meta]{key}=\tsobj[meta]{value} \tsobj[macro3]{\q_stop}
% \end{codesyntax}
% In order to handle the special syntax, we start by creating a delimited macro that extracts the key name and its value, and that only returns the former.
% \end{codedescribe}
% 
%    \begin{macrocode}
\cs_new:Npn \@@_retrieve_key_name:w #1=#2\q_stop { #1 }
%    \end{macrocode}
% 
% \begin{codedescribe}[macro3]{\@@_extract_first_key_name:n}
% \begin{codesyntax}
%  \tsobj[macro3]{\@@_extract_first_key_name:n} \tsargs[marg]{\tsobj[macro]{\integral}'s optional argument}
% \end{codesyntax}
% We then associate the optional argument received by \tsobj[macro]{\integral} to a temporary \emph{comma list}, from which we extract the first element; we then check if it contains an \enquote{\texttt{=}} sign. If so, we use the previous macro to retrieve only the key name; otherwise, we simply copy the group of tokens retrieved into \tsobj[variable3]{\l_@@_key_name_tl}.
% \end{codedescribe}
% 
%    \begin{macrocode}
\cs_new_protected:Npn \@@_extract_first_key_name:n #1
  {
    \seq_set_split:Nnn \l_tmpa_seq { : } { #1 }
    \tl_set:Ne \l_tmpa_tl { \seq_item:Nn \l_tmpa_seq { 1 } }
    \tl_if_in:NnTF \l_tmpa_tl { = }
      { 
        \tl_set:Ne \l_@@_key_name_tl 
          { 
            \exp_last_unbraced:NV
            \@@_retrieve_key_name:w \l_tmpa_tl \q_stop 
          } 
      }
      { \tl_set_eq:NN \l_@@_key_name_tl \l_tmpa_tl }
  }
%    \end{macrocode}
%    
% \begin{codedescribe}[macro3]{\@@_parse_integral_keys:n}
% \begin{codesyntax}
%  \tsobj[macro3]{\@@_parse_integral_keys:n} \tsargs[marg]{first key of the optional argument}
% \end{codesyntax}
% Using the \tsobj[pkg]{l3keys} module, we can check if the captured group of tokens corresponds to a defined key, in which case we directly apply \tsobj[macro]{\keys_set:nn}. If the test is negative, we manually assign the keys by considering the optional argument as a sequence to be delimited with a colon. Temporary sequences are used to manipulate the different parts of the argument. We notably check for the presence of the token \texttt{+j} to activate the \tsobj[key]{jacobian} key, and we add the default variable if it is not specified. Finally, we assign values to the \tsobj[key]{limits}, \tsobj[key]{variables} and \tsobj[key]{mode} keys based on the extracted elements.
% \end{codedescribe}
% 
%    \begin{macrocode}
\cs_new_protected:Npn \@@_parse_integral_keys:n #1
  {
    \keys_if_exist:nVTF { integral } \l_@@_key_name_tl
      { \keys_set:nn { integral } { #1 } }
      {  
        \regex_split:nnN { : } { #1 } \l_tmpa_seq
        \str_if_eq:eeTF
          { \seq_item:Nn \l_tmpa_seq { 2 } }
          { +j }
          {
            \exp_args:NNne \seq_set_item:Nnn \l_tmpa_seq { 2 } 
              { \l_@@_default_differential_tl+j }
          }
          {
            \int_compare:nNnT { \seq_count:N \l_tmpa_seq } < { 2 }
              {
                \seq_put_right:NV
                  \l_tmpa_seq
                  \l_@@_default_differential_tl 
              }
          }
        \int_compare:nNnT { \seq_count:N \l_tmpa_seq } < { 3 }
          { \seq_put_right:Nn \l_tmpa_seq { d } }
        \seq_set_split:Nne \l_tmpb_seq
          { + }
          { \seq_item:Nn \l_tmpa_seq { 2 } }
        \keys_set:ne { integral }
          {
            limits    = { \seq_item:Nn \l_tmpa_seq { 1 } },
            variables = { \seq_item:Nn \l_tmpb_seq { 1 } },
            mode      =
              { 
                \str_case:enF { \seq_item:Nn \l_tmpa_seq { 3 } }
                  {
                    { d       } { default }
                    { n       } { nested  }
                    { p       } { product }
                    { default } { default }
                    { nested  } { nested  }
                    { product } { product }
                  }
                { default }
              },
            \bool_if:nT
              {
                \int_compare_p:nNn { \seq_count:N \l_tmpb_seq } > { 1 }
                &&
                \str_if_eq_p:en { \seq_item:Nn \l_tmpb_seq { 2 } } { j }
              }
              { jacobian }
          }
      }
  }
%    \end{macrocode}
% 
% \section{Integral composition}
%
% \begin{codedescribe}[macro3]{\@@_integral_preconfiguration:}
% 
% Before being able to typeset the integral in the document, a number of checks must be performed. Indeed, depending on the combination of keys specified in the optional argument, the sequences may not be filled yet. We therefore perform the following checks:
% \begin{itemize}
%   \item We check if the integrand should be split into several parts by the \tsobj[option]{nested} and \tsobj[option]{product} modes, in which case we populate the integrand sequence by splitting the elements at the semicolon. Otherwise, we simply add the integrand (\tsobj[variable3]{\l_@@_integrand_tl}) to the sequence.
%   \item We verify if the sequence of symbols is empty, indicating that the \tsobj[key]{limits*} key has not been called. Indeed, only the use of this key triggers the execution of all macros presented in section \cref{sec:implementationbornes}. In this case, we simply add to the sequence the general form of the integral, whose symbol and bounds will be optionally modified by the keys \tsobj[key]{symbol}, \tsobj[key]{ulimit} and \tsobj[key]{llimit}.
%   \item We then control the deactivation of differentials in addition to detecting the presence of \tsobj[macro]{\differentials} using a regular expression.
% \end{itemize}
% \end{codedescribe}
% 
%    \begin{macrocode}
\cs_new_protected:Nn \@@_integral_preconfiguration:
  {
    \bool_if:NTF \l_@@_separate_integral_bool
      { 
        \seq_set_split:NnV
          \l_@@_integrand_seq
          { ; } 
          \l_@@_integrand_tl
      }
      {
        \seq_put_right:NV
          \l_@@_integrand_seq
          \l_@@_integrand_tl
      }
      
    \seq_if_empty:NT \l_@@_integral_symbol_seq
      {
        \seq_put_right:Nn \l_@@_integral_symbol_seq
          { \@@_general_integral_symbol: }
      }

    \bool_if:NF \l_@@_deactivate_variables_bool
      { \@@_parse_variables: }

    \regex_if_match:nVTF { \c{differentials} } \l_@@_integrand_tl
      { \bool_set_true:N \l_@@_manual_differential_bool }
      { \bool_set_false:N \l_@@_manual_differential_bool }
  }
%    \end{macrocode}
% 
% We can now define the six macros that will be used to typeset a precise integral; three modes allowed by \tsobj[option]{mode}, each accompanied by a variant for the inverted differentials. It should also be noted that \tsobj[macro]{\differentials} is only created if the boolean \tsobj[variable3]{\l_@@_manual_differential_bool} is true, in order to avoid unnecessary definitions.
% 
% \subsection{Mode \emph{default}}
% 
% \begin{codedescribe}[macro3]{\@@_print_default_integral:}
% For the default mode, we typeset in the following order: integral symbol, integrand, Jacobian (if applicable), and differentials. Since the elements are successive, we apply \tsobj[macro]{\seq_use:Nn} for each of the sequences, inserting a kern when necessary. Note that if \tsobj[variable3]{\l_@@_vectorial_differential_bool} is true, we add a middle dot and remove the previous kern (for spacing reasons related to \tsobj[macro]{\cdot}).
% \end{codedescribe}
% 
%    \begin{macrocode}
\cs_new_protected:Npn \@@_print_default_integral:
  {
    \bool_if:NT \l_@@_manual_differential_bool
      {
        \cs_set_protected:Npn \differentials
          { 
            \seq_use:Nn \l_@@_differential_seq 
              { \@@_mkern:N \l_@@_differential_sep_muskip } 
          }
      }
    \seq_use:Nn \l_@@_integral_symbol_seq 
      { \@@_mkern:N \l_@@_symbol_inner_sep_muskip }
    \@@_mkern:N \l_@@_symbol_post_sep_muskip
    \seq_use:Nn \l_@@_integrand_seq {  }
    \bool_if:NT \l_@@_jacobian_bool
      { \seq_use:Nn \l_@@_jacobian_seq {  } }
    \@@_mkern:N \l_@@_variable_sep_muskip
    \bool_if:NT \l_@@_vectorial_differential_bool
      { \tex_unkern:D \cdot}
    \bool_if:NF \l_@@_manual_differential_bool
      { 
        \seq_use:Nn \l_@@_differential_seq
          { \@@_mkern:N \l_@@_differential_sep_muskip }
      }
  }
%    \end{macrocode}
% 
% \begin{codedescribe}[macro3]{\@@_print_default_integral_inv:}
% For the inverted differentials, nothing is very different, except that \tsobj[variable3]{\l_@@_integrand_seq} and \tsobj[variable3]{\l_@@_differential_seq} are swapped.
% \end{codedescribe}
% 
%    \begin{macrocode}
\cs_new_protected:Npn \@@_print_default_integral_inv:
  {
    \bool_if:NT \l_@@_manual_differential_bool
      {
        \cs_set_protected:Npn \differentials
          { 
            \seq_use:Nn \l_@@_differential_seq 
              { \@@_mkern:N \l_@@_differential_sep_muskip } 
          }
      }
    \seq_use:Nn \l_@@_integral_symbol_seq 
      { \@@_mkern:N \l_@@_symbol_inner_sep_muskip }
    \@@_mkern:N \l_@@_symbol_post_sep_muskip
    \bool_if:NF \l_@@_manual_differential_bool
      { 
        \seq_use:Nn \l_@@_differential_seq 
          { \@@_mkern:N \l_@@_differential_sep_muskip } 
      }
    \@@_mkern:N \l_@@_variable_sep_muskip
    \bool_if:NT \l_@@_vectorial_differential_bool 
      { \tex_unkern:D \cdot}
    \seq_use:Nn \l_@@_integrand_seq {  }
    \bool_if:NT \l_@@_jacobian_bool
      { \seq_use:Nn \l_@@_jacobian_seq {  } }
  }
%    \end{macrocode}
% 
% \subsection{Mode \emph{nested}}
% 
% \begin{codedescribe}[macro3]{\@@_print_nested_integral:}
% In the nested mode, we initiate an incremented loop where we extract the $i$\textsuperscript{th} element of each of the sequences of symbol, integrand and Jacobian. After the iteration, we compose all the differentials again using \tsobj[macro]{\seq_use:Nn}.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_print_nested_integral:
  {
    \bool_if:NT \l_@@_manual_differential_bool
      {
        \cs_set_protected:Npn \differentials
          {
            \seq_use:Nn \l_@@_differential_seq
              { \@@_mkern:N \l_@@_differential_sep_muskip } 
          }
      }
    \int_step_inline:nn { \seq_count:N \l_@@_integral_symbol_seq }
      {
        \seq_item:Nn \l_@@_integral_symbol_seq { ##1 }
        \@@_mkern:N \l_@@_symbol_post_sep_muskip
        \seq_item:Nn \l_@@_integrand_seq { ##1 }
        \bool_if:NT \l_@@_jacobian_bool
          { \seq_item:Nn \l_@@_jacobian_seq { ##1 } }
      }
    \@@_mkern:N \l_@@_variable_sep_muskip
    \bool_if:NF \l_@@_manual_differential_bool
      { 
        \seq_use:Nn \l_@@_differential_seq 
          { \@@_mkern:N \l_@@_differential_sep_muskip } 
      }
  }
%    \end{macrocode}
% \end{codedescribe}
% 
% \begin{codedescribe}[macro3]{\@@_print_nested_integral_inv:}
% Here, it is the sequence of differentials that appears in the loop and the sequence of the integrand that is used at the end, after the iteration.
% \end{codedescribe}
% 
%    \begin{macrocode}
\cs_new_protected:Npn \@@_print_nested_integral_inv:
  {
    \int_step_inline:nn { \seq_count:N \l_@@_integral_symbol_seq }
      {
        \seq_item:Nn \l_@@_integral_symbol_seq { ##1 }
        \@@_mkern:N \l_@@_symbol_post_sep_muskip
        \seq_item:Nn \l_@@_differential_seq { ##1 }
      }
    \@@_mkern:N \l_@@_variable_sep_muskip
    \seq_use:Nn \l_@@_integrand_seq {  }
    \bool_if:NT \l_@@_jacobian_bool
      { \seq_use:Nn \l_@@_jacobian_seq {  } }
  }
%    \end{macrocode}
% 
% \subsection{Mode \emph{product}}
% 
% \begin{codedescribe}[macro3]{\@@_print_product_integral:}
% The product mode is very similar to the nested mode, in the sense that all sequences appear in the loop.
% \end{codedescribe}
% 
%    \begin{macrocode}
\cs_new_protected:Npn \@@_print_product_integral:
  {
    \bool_if:NT \l_@@_manual_differential_bool
      {
        \cs_set_protected:Npn \differentials
          { 
            \seq_pop_left:NN \l_@@_differential_seq \l_tmpa_tl 
            \tl_use:N \l_tmpa_tl
          }
      }
    \int_step_inline:nn { \seq_count:N \l_@@_integral_symbol_seq }
      {
        \seq_item:Nn \l_@@_integral_symbol_seq { ##1 }
        \@@_mkern:N \l_@@_symbol_post_sep_muskip
        \seq_item:Nn \l_@@_integrand_seq { ##1 }
        \bool_if:NT \l_@@_jacobian_bool
          { \seq_item:Nn \l_@@_jacobian_seq { ##1 } }
        \@@_mkern:N \l_@@_variable_sep_muskip
        \bool_if:NF \l_@@_manual_differential_bool
          { \seq_item:Nn \l_@@_differential_seq { ##1 } }
      }
  }
%    \end{macrocode}
% 
% \begin{codedescribe}[macro3]{\@@_print_product_integral_inv:}
% Similarly, with a few adjustments.
% \end{codedescribe}
% 
%    \begin{macrocode}
\cs_new_protected:Npn \@@_print_product_integral_inv:
  {
    \bool_if:NT \l_@@_manual_differential_bool
      {
        \cs_set_protected:Npn \differentials
          { 
            \seq_pop_left:NN \l_@@_differential_seq \l_tmpa_tl 
            \tl_use:N \l_tmpa_tl
          }
      }
    \int_step_inline:nn { \seq_count:N \l_@@_integral_symbol_seq }
      {
        \seq_item:Nn \l_@@_integral_symbol_seq { ##1 }
        
        \bool_if:NF \l_@@_manual_differential_bool
          { 
            \@@_mkern:N \l_@@_symbol_post_sep_muskip
            \seq_item:Nn \l_@@_differential_seq { ##1 }
          }
        \@@_mkern:N \l_@@_variable_sep_muskip
        \seq_item:Nn \l_@@_integrand_seq { ##1 }
        \bool_if:NT \l_@@_jacobian_bool
          { \seq_item:Nn \l_@@_jacobian_seq { ##1 } }
      }
  }
%    \end{macrocode}
% 
% \subsection{Final typesetting}
%
% \begin{codedescribe}[macro3]{\@@_print_integral:}
% The main macro typesetting the integral first increments the global counter allowing each integral to be numbered. Then, it carries out the preparatory actions explained previously. Finally, depending on the placement of the differentials and the requested display mode, one of the six macros is called.  
% \end{codedescribe}
%    \begin{macrocode}
\cs_new_protected:Nn \@@_print_integral:
  {
    \int_gincr:N \g_@@_integral_number_int
    \@@_integral_preconfiguration:
    \bool_if:NTF \l_@@_invert_differential_bool
      {
        \str_case:VnF \l_@@_display_mode_str
          {
            { default } { \@@_print_default_integral_inv: }
            { nested  } { \@@_print_nested_integral_inv:  }
            { product } { \@@_print_product_integral_inv: }
          }
          { \@@_print_default_integral_inv: }
      }
      {
        \str_case:VnF \l_@@_display_mode_str
          {
            { default } { \@@_print_default_integral: }
            { nested  } { \@@_print_nested_integral:  }
            { product } { \@@_print_product_integral: }
          }
          { \@@_print_default_integral: }
      }
  }
%    \end{macrocode}
%
% \section{Key declaration}
% 
% The creation of keys for \tsobj[macro]{\integral} and \tsobj[macro]{\IntegralSetup} is relatively trivial. The few elementary actions for the keys \tsobj[key]{limits} and \tsobj[key]{variables} that were not covered by the macros are performed here.
% 
%    \begin{macrocode}
\keys_define:nn { integral }
  {
    mode .choice:,
      mode / default .code:n =
        {
          \bool_set_false:N \l_@@_separate_integral_bool
          \str_set:Nn \l_@@_display_mode_str { default }
        },
      mode / nested .code:n =
        {
          \bool_set_true:N \l_@@_separate_integral_bool
          \str_set:Nn \l_@@_display_mode_str { nested }
        },
      mode / product .code:n =
        {
          \bool_set_true:N \l_@@_separate_integral_bool
          \str_set:Nn \l_@@_display_mode_str { product }
        },
      mode .default:n = { default },

    limits .code:n =
      {
        \seq_set_split:Nnn \l_@@_limits_seq { ; } { #1 }
        \@@_set_limits_regular:
        \@@_generate_integral_sequence:
      },

    limits* .code:n =
      {
        \seq_set_split:Nnn \l_@@_limits_seq { ; } { #1 }
        \@@_set_limits_starred:
        \@@_generate_integral_sequence:
      },

    llimit .tl_set:N = \l_@@_lower_limit_tl,

    ulimit .tl_set:N = \l_@@_upper_limit_tl,

    symbol .code:n =
      {
        \cs_if_exist:NTF #1
          { \tl_set_eq:NN \l_@@_integral_symbol_tl #1 }
          { 
            \msg_warning:nnn { intexgral } { unknown-symb } { #1 } 
            \tl_set_eq:NN \l_@@_integral_symbol_tl \int
          }
      },

    nint .code:n =
      {
        \int_compare:nNnT { #1 } < { 5 }
          { \msg_warning:nn { intexgral } { use-glyph-instead } }

        \tl_clear:N \l_@@_integral_symbol_tl

        \int_step_inline:nn { #1 }
          {
            \tl_put_right:Nn \l_@@_integral_symbol_tl
              { \int }
            \int_compare:nNnT { ##1 } < { #1 }
              {
                \tl_put_right:Nn \l_@@_integral_symbol_tl
                  {
                    \@@_mathchoice:nnnn
                      { \tex_mkern:D -12mu \scan_stop: }
                      { \tex_mkern:D -8mu \scan_stop: }
                      { \tex_mkern:D -4mu \scan_stop: }
                      { \tex_mkern:D -2mu \scan_stop: }
                  }
              }
          }
      },

    domain .code:n =
      {
        \tl_set:Nn \l_tmpa_tl { #1 }
        \seq_set_split:NnV \l_@@_domain_seq { * } \l_tmpa_tl
        \seq_map_inline:Nn \l_@@_domain_seq
          {
            \tl_if_empty:NF \l_@@_lower_limit_tl
              { \tl_put_right:Nn \l_@@_lower_limit_tl { \times } }
            \tl_set:Ne \l_@@_domain_char_tl
              { \exp_args:Ne \str_uppercase:n { \tl_head:n { ##1 } } }
            \tl_set:Ne \l_@@_domain_dimension_tl
              { \tl_tail:n { ##1 } }
            \tl_put_right:Ne \l_@@_lower_limit_tl
              {
                \exp_not:V \l_@@_domain_style_tl
                  { \l_@@_domain_char_tl }
                \c_math_superscript_token { \l_@@_domain_dimension_tl }
              }
          }
      },

    domain* .code:n =
      {
        \tl_set:Nn \l_tmpa_tl { #1 }
        \seq_set_split:NnV \l_@@_domain_seq { * } \l_tmpa_tl
        \seq_map_inline:Nn \l_@@_domain_seq
          {
            \tl_if_empty:NF \l_@@_lower_limit_tl
              { \tl_put_right:Nn \l_@@_lower_limit_tl { \times } }
            \tl_set:Ne \l_@@_domain_char_tl
              { \exp_args:Ne \str_uppercase:n { \tl_head:n { ##1 } } }
            \tl_set:Ne \l_@@_domain_dimension_tl
              { \tl_tail:n { ##1 } }
            \tl_put_right:Ne \l_@@_lower_limit_tl
              {
                \exp_not:V \l_@@_domain_style_tl
                  { \l_@@_domain_char_tl }
                \c_math_subscript_token { \l_@@_domain_dimension_tl }
              }
          }
      },

    boundary .code:n =
      { \tl_set:Nn \l_@@_lower_limit_tl { \partial #1 } },

    variables .code:n =
      {
        \bool_set_true:N \l_@@_custom_variables_bool
        \str_if_eq:nnTF { #1 } { none }
          { \bool_set_true:N \l_@@_deactivate_variables_bool }
          {
            \prop_get:NnNTF
              \g_@@_differential_group_keyword_prop { #1 } \l_tmpa_tl
                {
                  \seq_set_split:NnV
                  \l_@@_variables_seq
                  { , }
                  \l_tmpa_tl
                  \seq_if_exist:cTF { g_@@_#1_jacobian_seq }
                    {
                      \seq_set_eq:Nc
                        \l_@@_jacobian_seq 
                        { g_@@_#1_jacobian_seq }
                    }
                    { \msg_warning:nnn { intexgral } { no-jacobian } { #1 } }
                }
                { \seq_set_split:Nnn \l_@@_variables_seq { , } { #1 } }
          }
      },

    jacobian .code:n =
      { \bool_set_true:N \l_@@_jacobian_bool },
    diff-vec .code:n =
      { \bool_set_true:N \l_@@_vectorial_differential_bool },
    diff-symb .tl_set:N = \l_@@_differential_symbol_tl,
    diff-order .clist_set:N = \l_@@_differential_order_clist,
  } 

\keys_define:nn { IntegralSetup }
  {
    diff-symb    .tl_set:N     = \l_@@_differential_symbol_tl,
    defaultvar   .tl_set:N     = \l_@@_default_differential_tl,
    defaultvar*  .tl_set:N     = \l_@@_default_vector_differential_tl,
    postsymbsep  .muskip_set:N = \l_@@_symbol_post_sep_muskip,
    varsep       .muskip_set:N = \l_@@_variable_sep_muskip,
    diffsep      .muskip_set:N = \l_@@_differential_sep_muskip,
    vectorstyle  .tl_set:N     = \l_@@_vectorial_differential_style_tl,
    domainstyle  .tl_set:N     = \l_@@_domain_style_tl,
    innersymbsep .tl_set:N     = \l_@@_symbol_inner_sep_muskip,
    novar        .bool_set:N   = \l_@@_deactivate_variables_bool,
  }
%    \end{macrocode}
%    
% \section{User-level macros}
% 
% \subsection{Keywords for limits}
% 
% \begin{codedescribe}[macro3]{\@@_new_limits_group:nn}
% \begin{codesyntax}
%   \tsobj[macro3]{\@@_new_limits_group:nn} \tsargs[marg]{keyword} \tsargs[marg]{limits}
% \end{codesyntax}
% For the functioning of \tsobj[macro]{\NewLimitsKeyword}, we register the keyword and its value in a property list. A particularity that nevertheless needs to be explained is that there is a double inversion of the limits: at the time of the creation of the keyword, and at the time of extracting its content (cf. \tsobj[macro]{\@@_parse_limits:nn}). The reason is that it is difficult to attach to a keyword the status of \emph{inverted limits}. Thus, whatever convention is chosen, the limits are stored \emph{in the same order}. This also allows the keywords defined by the package to be in the correct order, even if the convention is changed after loading it.
% \end{codedescribe}
% 
%    \begin{macrocode}
\cs_new_protected:Npn \@@_new_limits_group:nn #1#2
  {
    \prop_gput:Nne \g_@@_limits_keyword_prop { #1 }
      {
        \bool_if:NTF \l_@@_invert_limits_bool
          { \@@_invert_limits:w #2\q_stop }
          { #2 }
      }
  }
%    \end{macrocode}
% 
% \begin{codedescribe}[macro]{\NewLimitsKeyword}
% \itshape
% This macro is documented in \cref{subsec:limitskey}.
% \end{codedescribe}
%    \begin{macrocode}
\NewDocumentCommand\NewLimitsKeyword{ m m }
  {
    \prop_if_in:NnTF \g_@@_limits_keyword_prop { #1 }
      { \msg_error:nnn { intexgral } { lim-group-alr-def } { #1 } }
      { \@@_new_limits_group:nn { #1 } { #2 } }
  }
%    \end{macrocode}
% 
% \begin{codedescribe}[macro]{\RenewLimitsKeyword}
% \itshape
% This macro is documented in \cref{subsec:limitskey}.
% \end{codedescribe}
%    \begin{macrocode}
\NewDocumentCommand\RenewLimitsKeyword{ m m }
  {
    \prop_pop:NnNTF \g_@@_limits_keyword_prop { #1 } \l_tmpa_tl
      { \@@_new_limits_group:nn { #1 } { #2 } }
      { \msg_error:nnn { intexgral } { lim-group-not-def } { #1 } }
  }
%    \end{macrocode}
% 
% \begin{codedescribe}[macro]{\ProvideLimitsKeyword}
% \itshape
% This macro is documented in \cref{subsec:limitskey}.
% \end{codedescribe}
%    \begin{macrocode}
\NewDocumentCommand\ProvideLimitsKeyword{ m m }
  {
    \prop_if_in:NnF \g_@@_limits_keyword_prop { #1 }
      { \@@_new_limits_group:nn { #1 } { #2 } }
  }
%    \end{macrocode}
% 
% \begin{codedescribe}[macro]{\DeclareLimitsKeyword}
% \itshape
% This macro is documented in \cref{subsec:limitskey}.
% \end{codedescribe}
% 
%    \begin{macrocode}
\NewDocumentCommand\DeclareLimitsKeyword{ m m }
  {
    \prop_remove:Nn \g_@@_limits_keyword_prop { #1 }
    \@@_new_limits_group:nn { #1 } { #2 }
  }
%    \end{macrocode}
%
% \subsection{Keywords for variables}
% 
% \begin{codedescribe}[macro3]{\@@_new_variables_group:nnn}
% \begin{codesyntax}
%   \tsobj[macro3]{\@@_new_variables_group:nnn} \tsargs[marg]{keyword} \tsargs[marg]{variable} \tsargs[oarg]{jacobian}
% \end{codesyntax}
% Just like for the limits, we register the groups of differentials in a property list, with their name, value and eventually their associated jacobian.
% \end{codedescribe}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_new_variables_group:nnn #1#2#3
  {
    \prop_gput:Nnn \g_@@_differential_group_keyword_prop
      { #1 } { #2 }
    \tl_if_blank:nF { #3 }
      {
        \seq_new:c { g_@@_#1_jacobian_seq }
        \seq_gset_split:cnn { g_@@_#1_jacobian_seq } { , } { #3 }
      }
  }
%    \end{macrocode}
%
% \begin{codedescribe}[macro]{\NewVariableKeyword}
% \itshape
% This macro is documented in \cref{subsec:variableskey}.
% \end{codedescribe}
%    \begin{macrocode}
\NewDocumentCommand\NewVariableKeyword{ m m o }
  {
    \prop_if_in:NnTF \g_@@_differential_group_keyword_prop { #1 }
      { \msg_error:nnn { intexgral } { diff-group-alr-def } { #1 } }
      { \@@_new_variables_group:nnn { #1 } { #2 } { #3 } }
  }
%    \end{macrocode}
% 
% \begin{codedescribe}[macro]{\RenewVariableKeyword}
% \itshape
% This macro is documented in \cref{subsec:variableskey}.
% \end{codedescribe}
% 
%    \begin{macrocode}
\NewDocumentCommand\RenewVariableKeyword{ m m o }
  {
    \prop_pop:NnNTF \g_@@_differential_group_keyword_prop { #1 }
      \l_tmpa_tl
      {
        \seq_gclear:c { g_@@_#1_jacobian_seq }
        \@@_new_variables_group:nnn { #1 } { #2 } { #3 }
      }
      { \msg_error:nnn { intexgral } { diff-group-not-def } { #1 } }
  }
%    \end{macrocode}
% 
% \begin{codedescribe}[macro]{\ProvideVariableKeyword}
% \itshape
% This macro is documented in \cref{subsec:variableskey}.
% \end{codedescribe}
% 
%    \begin{macrocode}
\NewDocumentCommand\ProvideVariableKeyword{ m m o }
  {
    \prop_if_in:NnF \g_@@_differential_group_keyword_prop { #1 }
      { \@@_new_variables_group:nnn { #1 } { #2 } { #3 } }
  }
%    \end{macrocode}
% 
% \begin{codedescribe}[macro]{\DeclareVariableKeyword}
% \itshape
% This macro is documented in \cref{subsec:variableskey}.
% \end{codedescribe}
% 
%    \begin{macrocode}
\NewDocumentCommand\DeclareVariableKeyword{ m m o }
  {
    \prop_remove:Nn \g_@@_differential_group_keyword_prop { #1 }
    \seq_gclear:c { g_@@_#1_jacobian_seq }
    \@@_new_variables_group:nnn { #1 } { #2 } { #3 }
  }
%    \end{macrocode}
%
% \subsection{Keywords for symbols}
% 
% This section does not require an intermediate macro, since we modify the keys of the \emph{integral} module directly.
%
% \begin{codedescribe}[macro]{\NewSymbolKeyword}
% \itshape
% This macro is documented in \cref{subsec:symbolkey}.
% \end{codedescribe}
%    \begin{macrocode}
\NewDocumentCommand\NewSymbolKeyword{ m m }
  {
    \prop_if_in:NnT \g_@@_limits_keyword_prop { #1 }
      { \msg_warning:nnn { intexgral } { key-exists-for-lim } { #1 } }
    \keys_if_exist:nnTF { integral } { #1 }
      { \msg_error:nnn { intexgral } { symb-key-alr-def } { #1 } }
      {
        \keys_define:nn { integral }
          {
            #1 .meta:n =
              {
                symbol=#2,
                llimit=##1
              },
          }
      }
  }
%    \end{macrocode}
% 
% \begin{codedescribe}[macro]{\RenewSymbolKeyword}
% \itshape
% This macro is documented in \cref{subsec:symbolkey}.
% \end{codedescribe}
% 
%    \begin{macrocode}
\NewDocumentCommand\RenewSymbolKeyword{ m m }
  {
    \prop_if_in:NnT \g_@@_limits_keyword_prop { #1 }
      { \msg_warning:nnn { intexgral } { key-exists-for-lim } { #1 } }
    \keys_if_exist:nnTF { integral } { #1 }
      {
        \keys_define:nn { integral }
          {
            #1 .undefine:
            #1 .meta:n =
              {
                symbol=#2,
                llimit=##1
              }
          }
      }
      { \msg_error:nnn { intexgral } { symb-key-not-def } }
  }
%    \end{macrocode}
% 
% \begin{codedescribe}[macro]{\ProvideSymbolKeyword}
% \itshape
% This macro is documented in \cref{subsec:symbolkey}.
% \end{codedescribe}
% 
%    \begin{macrocode}
\NewDocumentCommand\ProvideSymbolKeyword{ m m }
  {
    \prop_if_in:NnT \g_@@_limits_keyword_prop { #1 }
      { \msg_warning:nnn { intexgral } { key-exists-for-lim } { #1 } }
    \keys_if_exist:nnF { integral } { #1 }
      {
        \keys_define:nn { integral }
          {
            #1 .meta:n =
              {
                symbol=#2,
                llimit=##1
              },
          }
      }
  }
%    \end{macrocode}
% 
% \begin{codedescribe}[macro]{\DeclareSymbolKeyword}
% \itshape
% This macro is documented in \cref{subsec:symbolkey}.
% \end{codedescribe}
% 
%    \begin{macrocode}
\NewDocumentCommand\DeclareSymbolKeyword{ m m }
  {
    \keys_define:nn { integral }
      {
        #1 .undefine:
        #1 .meta:n =
          {
            symbol=#2,
            llimit=##1
          },
      }
  }
%    \end{macrocode}
% 
% \subsection{Configuration macros}
% 
% \begin{codedescribe}[macro]{\IntegralSetup}
% \itshape
% This macro is documented in \cref{sec:setup}.
% \end{codedescribe}
% 
%    \begin{macrocode}
\NewDocumentCommand\IntegralSetup{ m }
  { \keys_set:nn { IntegralSetup } { #1 } }
%    \end{macrocode}
% 
% \begin{codedescribe}[macro]{\intexgralsetup}
% Just like the previous macro, but without forgetting to restrict its usage to the preamble.\par
% \textit{This macro is documented in \cref{sec:pkgoptions}.}
% \end{codedescribe}
% 
%    \begin{macrocode}
\NewDocumentCommand\intexgralsetup{ m }
  { \keys_set:nn { intexgral } { #1 } }
\@onlypreamble\intexgralsetup
%    \end{macrocode}
% 
% \begin{codedescribe}[macro]{\integral}
% The final macro is only a few lines long. If an optional argument is provided, it performs the necessary checks to determine the nature of that argument (whether it follows special syntax or not) and assigns the keys accordingly. It then assigns the main argument to the corresponding token before calling the central macro that constructs the entire expression. The entire process is executed within a group so that all modifications remain local.\par
% \textit{This macro is documented in \cref{sec:mainmacro}.}
% \end{codedescribe}
% 
%    \begin{macrocode}
\NewDocumentCommand\integral{ O{} m }
  {
    \group_begin:
    \tl_if_empty:nF { #1 }
      {
        \@@_extract_first_key_name:n { #1 }
        \@@_parse_integral_keys:n { #1 }
      }
    \tl_set:Nn \l_@@_integrand_tl { #2 }
    \@@_print_integral:
    \group_end:
  }
%    \end{macrocode}
%    
% \section{Package configuration}
% 
% The last part of the package is dedicated to defining the default keywords for symbols, limits and variables, as well as the default parameters for the typesetting of integrals.
% 
% \subsection{Symbol}
% 
%    \begin{macrocode}
\NewSymbolKeyword{single}     {\int}
\NewSymbolKeyword{double}     {\iint}
\NewSymbolKeyword{triple}     {\iiint}
\NewSymbolKeyword{quadruple}  {\iiiint}
\NewSymbolKeyword{contour}    {\oint}
\NewSymbolKeyword{surface}    {\oiint}
\NewSymbolKeyword{volume}     {\oiiint}
%    \end{macrocode}
%
% \subsection{Limits}
% 
%    \begin{macrocode}
\NewLimitsKeyword{ab}        {a, b}
\NewLimitsKeyword{real}      {-\infty, +\infty}
\NewLimitsKeyword{positive}  {0, +\infty}
\NewLimitsKeyword{negative}  {-\infty, 0}
\NewLimitsKeyword{unit}      {0, 1}
\NewLimitsKeyword{circle}    {0, 2\pi}
\NewLimitsKeyword{scircle}   {0, \pi}
\NewLimitsKeyword{qcircle}   {0, \pi/2}
\NewLimitsKeyword{height}    {0, H}
\NewLimitsKeyword{radius}    {0, R}
\NewLimitsKeyword{length}    {0, L}
\NewLimitsKeyword{time}      {0, T}
%    \end{macrocode}
%
% \subsection{Variables}
% 
%    \begin{macrocode}
\NewVariableKeyword{cartesian}    {x, y, z}
\NewVariableKeyword{planar}       {x, y}
\NewVariableKeyword{polar}        {r, \theta}        [r]
\NewVariableKeyword{cylindrical}  {r, \theta, z}     [r]
\NewVariableKeyword{cylindrical*} {\rho, \theta, z}   [\rho]
\NewVariableKeyword{spherical}    {r, \theta, \phi}  [r^2, \sin\theta]
%    \end{macrocode}
%
% \subsection{Default parameters}
% 
%    \begin{macrocode}
\IntegralSetup{
  diff-symb    = \l_@@_default_differential_symbol_tl,
  defaultvar   = {x},
  defaultvar*  = {r},
  varsep       = \thinmuskip,
  diffsep      = \thinmuskip,
  innersymbsep = {-5mu},
  postsymbsep  = {-1mu},
  vectorstyle  = \vec,
  domainstyle  = \mathbb,
  novar        = false
}
%</package>
%    \end{macrocode}
% \end{implementation}
% 
% \newpage
% 
% \phantomsection
% \PrintIndex