% \iffalse meta-comment
%
% File: zref-check.dtx
%
% This file is part of the LaTeX package "zref-check".
%
% Copyright (C) 2021-2023  Gustavo Barros
%
% It may be distributed and/or modified under the conditions of the
% LaTeX Project Public License (LPPL), either version 1.3c of this
% license or (at your option) any later version.  The latest version
% of this license is in the file:
%
%    https://www.latex-project.org/lppl.txt
%
% and version 1.3 or later is part of all distributions of LaTeX
% version 2005/12/01 or later.
%
%
% This work is "maintained" (as per LPPL maintenance status) by
% Gustavo Barros.
%
% This work consists of the files zref-check.dtx,
%                                 zref-check.ins,
%                                 zref-check-doc.tex,
%                                 zref-check-code.tex,
%                   and the files generated from them.
%
% The released version of this package is available from CTAN.
%
% -----------------------------------------------------------------------
%
% The development version of the package can be found at
%
%    https://github.com/gusbrs/zref-check
%
% for those people who are interested.
%
% -----------------------------------------------------------------------
%
% \fi
%
% \iffalse
%<*driver>
\documentclass{l3doc}
% Have \GetFileInfo pick up date and version data
\usepackage{zref-check}
\NewDocumentCommand\opt{m}{\texttt{#1}}
\MakeShortVerb{\|}
\begin{document}
  \DocInput{zref-check.dtx}
\end{document}
%</driver>
% \fi
%
%
%
% \section{Initial setup}
%
% Start the \pkg{DocStrip} guards.
%    \begin{macrocode}
%<*package>
%    \end{macrocode}
%
% Identify the internal prefix (\LaTeX3 \pkg{DocStrip} convention).
%    \begin{macrocode}
%<@@=zrefcheck>
%    \end{macrocode}
%
%
% For the \texttt{chapter} and \texttt{section} checks, \pkg{zref-check} uses
% the new hook system in \pkg{ltcmdhooks}, which was released with the
% 2021/06/01 \LaTeX{} kernel.
%    \begin{macrocode}
\def\zrefcheck@required@kernel{2021-06-01}
\NeedsTeXFormat{LaTeX2e}[\zrefcheck@required@kernel]
\providecommand\IfFormatAtLeastTF{\@ifl@t@r\fmtversion}
\IfFormatAtLeastTF{\zrefcheck@required@kernel}
  {}
  {%
    \PackageError{zref-check}{LaTeX kernel too old}
      {%
        'zref-check' requires a LaTeX kernel \zrefcheck@required@kernel\space or newer.%
      }%
  }%
%    \end{macrocode}
%
% Identify the package.
%    \begin{macrocode}
\ProvidesExplPackage {zref-check} {2023-08-13} {0.3.4}
  {Flexible cross-references with contextual checks based on zref}
%    \end{macrocode}
%
% \section{Dependencies}
%
%    \begin{macrocode}
\RequirePackage { zref-user }
\RequirePackage { zref-abspage }
\RequirePackage { ifdraft }
%    \end{macrocode}
%
%
% \section{\pkg{zref} setup}
%
% Provide absolute counters for section and chapter, and respective \pkg{zref}
% properties, so that we can make checks about relation of chapters/sections
% regardless of internal counters, since we don't get those for the unnumbered
% (starred) ones.  Thanks Ulrike Fischer for suggestions at TeX.SX about the
% proper place to make the hooks for this purpose.
%    \begin{macrocode}
\newcounter { zc@abschap }
\newcounter { zc@abssec } [ zc@abschap ]
%    \end{macrocode}
%
%
% If the documentclass does not define \cs{chapter} the only thing that
% happens is that the chapter counter is never incremented, and the section
% one never reset.
%    \begin{macrocode}
\AddToHook { cmd / chapter / before }
  { \stepcounter { zc@abschap } }
\zref@newprop { zc@abschap } [0] { \int_use:N \c@zc@abschap }
\zref@addprop \ZREF@mainlist { zc@abschap }
%    \end{macrocode}
%
%    \begin{macrocode}
\AddToHook { cmd / section / before }
  { \stepcounter { zc@abssec } }
\zref@newprop { zc@abssec } [0] { \int_use:N \c@zc@abssec }
\zref@addprop \ZREF@mainlist { zc@abssec }
%    \end{macrocode}
%
%
% These are the lists of properties to be used by \pkg{zref-check}, that is,
% the list of properties the references and targets store.  This is the
% minimum set required, more properties may be added according to options.
% For user facing labels, we must use the \texttt{main} property list, so that
% \pkg{zref-clever} can also retrieve the properties it needs to refer to
% them.
%    \begin{macrocode}
\zref@newlist { zrefcheck-check }
\zref@addprops { zrefcheck-check }
  {
    page , % for messages
    abspage ,
    zc@abschap ,
    zc@abssec
  }
\zref@newlist { zrefcheck-end }
\zref@addprops { zrefcheck-end }
  {
    abspage ,
    zc@abschap ,
    zc@abssec
  }
%    \end{macrocode}
% For \pkg{zref-vario} we only need page information, since we only perform
% \opt{above} and \opt{below} checks there.
%    \begin{macrocode}
\zref@newlist { zrefcheck-zrefvario }
\zref@addprops { zrefcheck-zrefvario }
  {
    page , % for messages
    abspage ,
  }
%    \end{macrocode}
%
%
% \section{Plumbing}
%
% \subsection{Messages}
%
% \begin{macro}{\@@_message:nnnn, \@@_message:nnnx}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_message:nnnn #1#2#3#4
  {
    \use:c { msg_ \l_@@_msglevel_tl :nnnnn }
      { zref-check } {#1} {#2} {#3} {#4}
  }
\cs_generate_variant:Nn \@@_message:nnnn { nnnx }
%    \end{macrocode}
% \end{macro}
%
%    \begin{macrocode}
\msg_new:nnn { zref-check } { check-failed }
  {
    Check~failed~\msg_line_context:.~
    Failed~check~'#1'~for~label~'#2'~on~page~#3.
  }
\msg_new:nnn { zref-check } { double-check }
  {
    Same~page~check~\msg_line_context:.~
    Double-check~'#1'~for~label~'#2'~on~page~#3.
  }
\msg_new:nnn { zref-check } { empty-label }
  {
    Check~failed~\msg_line_context:.~
    Failed~check~'#1'~for~empty~label.
  }
\msg_new:nnn { zref-check } { no-checks }
  { No~checks~for~'\iow_char:N\\zcheck'~\msg_line_context:. }
%    \end{macrocode}
%
%    \begin{macrocode}
\msg_new:nnn { zref-check } { check-missing }
  { Check~'#1'~not~defined~\msg_line_context:. }
\msg_new:nnn { zref-check } { property-undefined }
  { Property~'#1'~not~defined~\msg_line_context:. }
\msg_new:nnn { zref-check } { property-not-in-label }
  { Label~'#1'~has~no~property~'#2'~\msg_line_context:. }
\msg_new:nnn { zref-check } { property-not-integer }
  { Property~'#1'~for~label~'#2'~not~an~integer~\msg_line_context:. }
%    \end{macrocode}
%
%    \begin{macrocode}
\msg_new:nnn { zref-check } { hyperref-preamble-only }
  {
    Option~'hyperref'~only~available~in~the~preamble. \iow_newline:
    Use~the~starred~version~of~'\iow_char:N\\zcheck'~instead.
  }
\msg_new:nnn { zref-check } { missing-hyperref }
  { Missing~'hyperref'~package. \iow_newline: Setting~'hyperref=false'. }
\msg_new:nnn { zref-check } { ignore-ok-document-only }
  {
    Option~'#1'~only~available~in~the~document. \iow_newline:
    Use~option~'msglevel'~instead.
  }
\msg_new:nnn { zref-check } { option-preamble-only }
  { Option~'#1'~is~preamble~only~\msg_line_context:. }
\msg_new:nnn { zref-check } { closerange-not-positive-integer }
  {
    Option~'closerange'~not~a~positive~integer~\msg_line_context:.~
    Using~default~value.
  }
\msg_new:nnn { zref-check } { labelcmd-undefined }
  {
    Control~sequence~named~'#1'~used~in~option~'labelcmd'~is~not~defined.~
    Using~default~value.
  }
\msg_new:nnn { zref-check } { option-deprecated-with-alternative }
  {
    Option~'#1'~has~been~deprecated~\msg_line_context:.\iow_newline:
    Use~'#2'~instead.
  }
\msg_new:nnn { zref-check } { option-deprecated }
  { Option~'#1'~has~been~deprecated~\msg_line_context:. }
\msg_new:nnn { zref-check } { load-time-options }
  {
    'zref-check'~does~not~accept~load-time~options.~
    To~configure~package~options,~use~'\iow_char:N\\zrefchecksetup'.
  }
%    \end{macrocode}
%
%
% \subsection{Integer testing}
%
% \begin{macro}{\@@_is_integer:n, \@@_int_to_roman:w}
%   From \url{https://tex.stackexchange.com/a/244405} (thanks Enrico Gregorio,
%   aka `egreg'), also see \url{https://tex.stackexchange.com/a/19769}.
%   Following the \texttt{l3styleguide}, I made a copy of
%   \cs{__int_to_roman:w}, since it is an internal function from the
%   \texttt{int} module, but we still get a warning from \texttt{l3build doc},
%   complaining about it.  And we're using \cs{tl_if_empty:oTF} instead of
%   \cs{tl_if_blank:oTF} as in egreg's answer, since \cs{romannumeral} is
%   defined so that ``the expansion is empty if the number is zero or
%   negative'', not ``blank''.  A couple of comments about this technique: the
%   underlying \cs{romannumeral} ignores space tokens and explicit signs
%   (\texttt{+} and \texttt{-}) in the expansion and hence it can only be used
%   to test positive integers; also the technique cannot distinguish whether
%   it received an empty argument or if ``the expansion was empty'' as a
%   result of receiving number as argument, so this must also be controlled
%   for since, in our use case, this may happen.
%    \begin{macrocode}
\cs_new_eq:NN \@@_int_to_roman:w \__int_to_roman:w
\prg_new_conditional:Npnn \@@_is_integer:n #1 { p, T , F , TF }
  {
    \tl_if_empty:oTF {#1}
      { \prg_return_false: }
      {
        \tl_if_empty:oTF { \@@_int_to_roman:w -0#1 }
          { \prg_return_true:  }
          { \prg_return_false: }
      }
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_is_integer_rgx:n}
%   A possible alternative to \cs{@@_is_integer:n} is to use a straightforward
%   regexp match (see \url{https://tex.stackexchange.com/a/427559}).  It does
%   not suffer from the mentioned caveats from the \cs{__int_to_roman:w}
%   technique, however, while \cs{@@_is_integer:n} is expandable,
%   \cs{@@_is_integer_rgx:n} is not.  Also, \cs{@@_is_integer_rgx:n} is
%   probably slower.
%    \begin{macrocode}
\prg_new_protected_conditional:Npnn \@@_is_integer_rgx:n #1 { TF }
  {
    \regex_match:nnTF { \A\d+\Z } {#1}
      { \prg_return_true:  }
      { \prg_return_false: }
  }
%    \end{macrocode}
% \end{macro}
%
%
%
% \subsection{Options}
%
%
% \subsubsection*{\opt{hyperref} option}
%
% \begin{variable}{\l_@@_use_hyperref_bool, \l_@@_warn_hyperref_bool}
%    \begin{macrocode}
\bool_new:N \l_@@_use_hyperref_bool
\bool_new:N \l_@@_warn_hyperref_bool
\keys_define:nn { zref-check }
  {
    hyperref .choice: ,
    hyperref / auto .code:n =
      {
        \bool_set_true:N \l_@@_use_hyperref_bool
        \bool_set_false:N \l_@@_warn_hyperref_bool
      } ,
    hyperref / true .code:n =
      {
        \bool_set_true:N \l_@@_use_hyperref_bool
        \bool_set_true:N \l_@@_warn_hyperref_bool
      } ,
    hyperref / false .code:n =
      {
        \bool_set_false:N \l_@@_use_hyperref_bool
        \bool_set_false:N \l_@@_warn_hyperref_bool
      } ,
    hyperref .initial:n = auto ,
    hyperref .default:n = auto
  }
%    \end{macrocode}
% \end{variable}
%
%    \begin{macrocode}
\AddToHook { begindocument }
  {
    \@ifpackageloaded { hyperref }
      {
        \bool_if:NT \l_@@_use_hyperref_bool
          { \RequirePackage { zref-hyperref } }
      }
      {
        \bool_if:NT \l_@@_warn_hyperref_bool
          { \msg_warning:nn { zref-check } { missing-hyperref } }
        \bool_set_false:N \l_@@_use_hyperref_bool
      }
    \keys_define:nn { zref-check }
      {
        hyperref .code:n =
          { \msg_warning:nn { zref-check } { hyperref-preamble-only } }
      }
  }
%    \end{macrocode}
%
%
% \subsubsection*{\opt{msglevel} option}
%
% \begin{variable}{\l_@@_msglevel_tl}
%    \begin{macrocode}
\tl_new:N \l_@@_msglevel_tl
\keys_define:nn { zref-check }
  {
    msglevel .choice: ,
    msglevel / warn .code:n =
      { \tl_set:Nn \l_@@_msglevel_tl { warning } } ,
    msglevel / info .code:n =
      { \tl_set:Nn \l_@@_msglevel_tl { info } } ,
    msglevel / none .code:n =
      { \tl_set:Nn \l_@@_msglevel_tl { none } } ,
    msglevel / infoifdraft .code:n =
      {
        \ifdraft
          { \tl_set:Nn \l_@@_msglevel_tl { info } }
          { \tl_set:Nn \l_@@_msglevel_tl { warning } }
      } ,
    msglevel / warniffinal .code:n =
      {
        \ifoptionfinal
          { \tl_set:Nn \l_@@_msglevel_tl { warning } }
          { \tl_set:Nn \l_@@_msglevel_tl { info } }
      } ,
    msglevel .value_required:n = true ,
    msglevel .initial:n = warn ,
%    \end{macrocode}
% \opt{ignore} and \opt{ok} are convenience aliases for \opt{msglevel=none},
% but only for use in the document body.
%    \begin{macrocode}
    ignore .code:n =
      { \msg_warning:nnn { zref-check } { ignore-ok-document-only } { ignore } } ,
    ignore .value_forbidden:n = true ,
    ok .code:n =
      { \msg_warning:nnn { zref-check } { ignore-ok-document-only } { ok } } ,
    ok .value_forbidden:n = true ,
  }
%    \end{macrocode}
% \end{variable}
%
%    \begin{macrocode}
\AddToHook { begindocument }
  {
    \keys_define:nn { zref-check }
      {
        ignore .meta:n = { msglevel = none } ,
        ok .meta:n = { msglevel = none } ,
      }
  }
%    \end{macrocode}
%
%
% \subsubsection*{\opt{onpage} option}
%
% \begin{variable}{\l_@@_msgonpage_bool}
%    \begin{macrocode}
\bool_new:N \l_@@_msgonpage_bool
\keys_define:nn { zref-check }
  {
    onpage .choice: ,
    onpage / labelseq .code:n =
      {
        \bool_set_false:N \l_@@_msgonpage_bool
      } ,
    onpage / msg .code:n =
      {
        \bool_set_true:N \l_@@_msgonpage_bool
      } ,
    onpage / labelseqifdraft .code:n =
      {
        \ifdraft
          { \bool_set_false:N \l_@@_msgonpage_bool }
          { \bool_set_true:N \l_@@_msgonpage_bool }
      } ,
    onpage / msgiffinal .code:n =
      {
        \ifoptionfinal
          { \bool_set_true:N \l_@@_msgonpage_bool }
          { \bool_set_false:N \l_@@_msgonpage_bool }
      } ,
    onpage .value_required:n = true ,
    onpage .initial:n = labelseq
  }
%    \end{macrocode}
% \end{variable}
%
%
% \subsubsection*{\opt{closerange} option}
%
% \begin{variable}{\l_@@_close_range_int}
%    \begin{macrocode}
\int_new:N \l_@@_close_range_int
\keys_define:nn { zref-check }
  {
    closerange .code:n =
      {
        \@@_is_integer_rgx:nTF {#1}
          { \int_set:Nn \l_@@_close_range_int { \int_eval:n {#1} } }
          {
            \msg_warning:nn { zref-check } { closerange-not-positive-integer }
            \int_set:Nn \l_@@_close_range_int { 5 }
          }
      } ,
    closerange .value_required:n = true ,
    closerange .initial:n = 5
  }
%    \end{macrocode}
% \end{variable}
%
%
% \subsubsection*{Package options}
%
% \pkg{zref-check} does not accept load-time options.  Despite the tradition
% of so doing, Joseph Wright has a point in recommending otherwise at
% \url{https://chat.stackexchange.com/transcript/message/60360822#60360822}:
% separating ``loading the package'' from ``configuring the package'' grants
% less trouble with ``option clashes'' and with expansion of options at
% load-time.
%    \begin{macrocode}
\bool_lazy_and:nnT
  { \tl_if_exist_p:c { opt@ zref-check.sty } }
  { ! \tl_if_empty_p:c { opt@ zref-check.sty } }
  { \msg_warning:nn { zref-check } { load-time-options } }
%    \end{macrocode}
%
%
% \begin{macro}[int]{\zrefchecksetup}
%   Provide \cs{zrefchecksetup}.
%    \begin{macrocode}
\NewDocumentCommand \zrefchecksetup { m }
  { \keys_set:nn { zref-check } {#1} }
%    \end{macrocode}
% \end{macro}
%
%
%
% \subsection{Position on page}
%
% Method for determining relative position within the page: the sequence in
% which the labels get shipped out, inferred from the sequence in which the
% labels occur in the \file{.aux} file.
%
% Some relevant info about the sequence of things:
% \url{https://tex.stackexchange.com/a/120978} and \texttt{texdoc lthooks},
% section ``Hooks provided by |\begin{document}|''.
%
%
% One first attempt at this was to use \cs{zref@newlabel}, which is the macro
% in which \pkg{zref} stores the label information in the aux file.  When the
% \file{.aux} file is read at the beginning of the compilation, this macro is
% expanded for each of the labels.  So, by redefining this macro we can feed a
% variable (a L3 sequence), and then do what it usually does, which is to
% define each label with the internal macro \cs{@newl@bel}, when the
% \file{.aux} file is read.
%
% Patching this macro for this is not possible.  First, \cs{zref@newlabel} is
% one of those ``commands that look ahead'' mentioned in \pkg{ltcmdhooks}
% documentation.  Indeed, \cs{@newl@bel} receives 3 arguments, and
% \cs{zref@newlabel} just passes the first, the following two will be scanned
% ahead.  Second, the \pkg{ltcmdhooks} hooks are not actually available when
% the \file{.aux} file is read, they come only after |\begin{document}|.
% Hence, redefinition would be the only alternative.  My attempts at this
% ended up registered at \url{https://tex.stackexchange.com/a/604744}.  But
% the best result in these lines was:
%
% \begin{verbatim}
% \ZREF@Robust\edef\zref@newlabel#1{
%   \noexpand\seq_gput_right:Nn \noexpand\g__zrefcheck_auxfile_lblseq_seq {#1}
%   \noexpand\@newl@bel{\ZREF@RefPrefix}{#1}
% }
% \end{verbatim}
%
%
% However, better than the above is to just read it from the \file{.aux} file
% directly, which relieves us from hacking into any internals.  That's what
% David Carlisle's answer at \url{https://tex.stackexchange.com/a/147705}
% does.  This answer has actually been converted into the package
% \pkg{listlbls} by Norbert Melzer, but it is made to work with regular
% labels, not with \pkg{zref}'s.  And it also does not really expose the
% information in a retrievable way (as far as I can tell).  So, the below is
% adapted from Carlisle's answer's technique (a poor man's version of it...).
%
% There is some subtlety here as to whether this approach makes it safe for us
% to read the labels at this point without \cs{zref@wrapper@babel}.  The
% common wisdom is that babel's shorthands are only active after
% |\begin{document}| (e.g., \url{https://tex.stackexchange.com/a/98897}).
% Alas, it is more complicated than that.  Babel's documentation says (in
% section 9.5 Shorthands): ``To prevent problems with the loading of other
% packages after babel we reset the catcode of the character to the original
% one at the end of the package and of each language file (except with
% KeepShorthandsActive).  It is re-activate[d] again at |\begin{document}|.
% We also need to make sure that the shorthands are active during the
% processing of the \file{.aux} file.  Otherwise some citations may give
% unexpected results in the printout when a shorthand was used in the optional
% argument of |\bibitem| for example.''  This is done with
% |\if@filesw \immediate\write\@mainaux{...}|.  In other words, the
% catcode change is written in the \file{.aux} file itself!  Indeed, if you
% inspect the file, you'll find them there.  Besides, there is still the
% ominous ``except with KeepShorthandsActive''.
%
% However, the \emph{method} we're using here is not quite the same as the
% usual run of the \file{.aux} file, because we're actively discarding the
% lines for which the first token is not equal to \cs{zref@newlabel}.  I have
% tested the famous sensitive case for this: \pkg{babel} \opt{french} and
% labels with colons.  And things worked as expected.  Well, \emph{if}
% \opt{KeepShorthandsActive} is enabled \emph{with \opt{french}} and we load
% the package \emph{after babel} things do break, but not quite because of the
% colons in the labels.  Even \pkg{siunitx} breaks in the same
% conditions\dots{}
%
% For reference: About what are valid characters for use in labels:
% \url{https://tex.stackexchange.com/a/18312}.  About some problems with
% active colons: \url{https://tex.stackexchange.com/a/89470}.  About the
% difference between L3 strings and token lists, see
% \url{https://tex.stackexchange.com/a/446381}, in particular Joseph Wright's
% comment: ``Strings are for data that will never be typeset, for example file
% names, identifiers, etc.: if the material may be used in typesetting, it
% should be a token list.''  See also moewe's (CW) answer in the same lines.
% Which suggests using L3 strings for the reference labels might be a good
% catch all approach, and possibly more robust.  David Carlisle's comment
% about \pkg{inputenc} and how the strings work is a caveat (see
% \url{https://tex.stackexchange.com/q/446123#comment1516961_446381}, thanks
% David Carlisle).  Still\dots{} let's stick to tradition as long as it works,
% \pkg{zref} already does a great job in this regard anyway.
%
%
% \begin{variable}{\g_@@_auxfile_lblseq_prop}
%    \begin{macrocode}
\prop_new:N \g_@@_auxfile_lblseq_prop
%    \end{macrocode}
% \end{variable}
%
%    \begin{macrocode}
\tl_gset:Nn \g_tmpa_tl { \c_sys_jobname_str .aux }
\file_if_exist:nT { \g_tmpa_tl }
  {
%    \end{macrocode}
% Retrieve the information from the \file{.aux} file, and store it in a
% property list, so that the sequence can be retrieved in key-value fashion.
%    \begin{macrocode}
    \ior_open:Nn \g_tmpa_ior { \g_tmpa_tl }
    \group_begin:
    \int_zero:N \l_tmpa_int
    \tl_clear:N \l_tmpa_tl
    \tl_clear:N \l_tmpb_tl
    \bool_set_false:N \l_tmpa_bool
    \ior_map_variable:NNn \g_tmpa_ior \l_tmpa_tl
      {
        \tl_map_variable:NNn \l_tmpa_tl \l_tmpb_tl
          {
            \tl_if_eq:NnTF \l_tmpb_tl { \zref@newlabel }
              {
%    \end{macrocode}
% Found a \cs{zref@label}, signal it.
%    \begin{macrocode}
                \bool_set_true:N \l_tmpa_bool
              }
              {
                \bool_if:NTF \l_tmpa_bool
                  {
                    \bool_set_false:N \l_tmpa_bool
                    \int_incr:N \l_tmpa_int
                    \prop_gput:Nxx \g_@@_auxfile_lblseq_prop
                      { \l_tmpb_tl } { \int_use:N \l_tmpa_int }
                  }
                  {
%    \end{macrocode}
% If there is not a match of the first token with \cs{zref@newlabel}, break
% the loop and discard the rest of the line, to ensure no babel calls to
% \cs{catcode} in the \file{.aux} file get expanded.  This also breaks the
% loop and discards the rest of the \cs{zref@newlabel} lines after we got the
% label we wanted, since we reset \cs{l_tmpa_bool} in the \texttt{T} branch.
%    \begin{macrocode}
                    \tl_map_break:
                  }
              }
          }
      }
    \group_end:
    \ior_close:N \g_tmpa_ior
  }
%    \end{macrocode}
%
%
%
% The alternate method I had considered (more than that...) for this was using
% yx coordinates supplied by \pkg{zref}'s \pkg{savepos} module.  However, this
% approach brought in a number of complexities, including the need to patch
% either \cs{zref@label} or \cs{ZREF@label}.  In addition, the technique was
% at the bottom fundamentally flawed.  Ulrike Fischer was very much right when
% she said that ``structure and position are two different beasts''
% (\url{https://github.com/ho-tex/zref/issues/12#issuecomment-880022576}).  It
% is true that the checks based on it behaved decently, in normal
% circumstances, and except for outrageous label placement by the user, it
% would return the expected results.  We don't really need exact coordinates
% to decide ``above/below''.  Besides, it would do an exact job for the
% dedicated target macros of this package.  It is also true that the ``page''
% for \cs{pageref} is stored with the value of where the \cs{label} is placed,
% wherever that may be.  However, I could not conceive a situation where the
% \texttt{yx} criterion would perform clearly better than the
% \texttt{labelseq} one.  And, if that's the case, and considering the
% complications it brings, this check was a slippery slope.  All in all, I've
% decided to drop it.
%
% There's an interesting answer by David Carlisle at
% \url{https://tex.stackexchange.com/a/419189} to decide whether to typeset
% ``above'' or ``below'' using a method which essentially boils down to
% ``position in the \file{.aux} file''.
%
%
%
% \subsection{Counter}
%
% We need a dedicated counter for the labels generated by the checks and
% targets.  The value of the counter is not relevant, we just need it to be
% able to set proper anchors with \cs{refstepcounter}.  And, since I couldn't
% find a \cs{refstepcounter} equivalent in L3, we use a standard 2e counter
% here.  I'm also using the technique to ensure the counter is never reset
% that is used by \file{zref-abspage.sty} and \cs{zref@require@unique}.
% Indeed, the requirements are the same, we need numbers ensured to be
% \emph{unique} in the counter.
%
%    \begin{macrocode}
\begingroup
  \let \@addtoreset \ltx@gobbletwo
  \newcounter { zrefcheck }
\endgroup
\setcounter { zrefcheck } { 0 }
%    \end{macrocode}
%
%
%
% \subsection{Label formats}
%
% \begin{macro}{\@@_check_lblfmt:n}
%   \begin{syntax}
%     \cs{@@_check_lblfmt:n} \Arg{check id int}
%   \end{syntax}
%    \begin{macrocode}
\cs_new:Npn \@@_check_lblfmt:n #1 { zrefcheck@ \int_use:N #1 }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}{\@@_end_lblfmt:n}
%   \begin{syntax}
%     \cs{@@_end_lblfmt:n} \Arg{label}
%   \end{syntax}
%    \begin{macrocode}
\cs_new:Npn \@@_end_lblfmt:n #1 { #1 @zrefcheck }
%    \end{macrocode}
% \end{macro}
%
%
%
% \subsection{Property values}
%
%
% \begin{macro}[int]{\zrefcheck_get_astl:nnn}
%   A convenience function to retrieve property values from labels.  Uses
%   \cs{g_@@_auxfile_lblseq_prop} for \texttt{lblseq}, and calls
%   \cs{zref@extractdefault} for everything else.
%
%   We cannot use the ``return value'' of \cs{@@_get_astl:nnn} or
%   \cs{@@_get_asint:nnn} directly, because we need to use the retrieved
%   property values as arguments in the checks, however we use here a number
%   of non-expandable operations.  Hence, we receive a local \texttt{tl/int}
%   variable as third argument and set that, so that it is available (and
%   expandable) at the place of use, and also make these functions `protected'
%   (see egreg's \url{https://tex.stackexchange.com/a/572903}: ``a function
%   that performs assignments should be \texttt{protected}'').  For this
%   reason, we do not group here, because we are passing a local variable
%   around, but it is expected this function will be called within a group.
%
%   We're returning \cs{c_empty_tl} in case of failure to find the intended
%   property value (explicitly in \cs{zref@extractdefault}, but that is also
%   what \cs{tl_clear:N} does).
%
%   \begin{syntax}
%     \cs{zrefcheck_get_astl:nnn} \Arg{label} \Arg{prop} \Arg{tl var}
%   \end{syntax}
%    \begin{macrocode}
\cs_new_protected:Npn \zrefcheck_get_astl:nnn #1#2#3
  {
    \tl_clear:N #3
    \tl_if_eq:nnTF {#2} { lblseq }
      {
        \prop_get:NnNF \g_@@_auxfile_lblseq_prop {#1} #3
          {
            \msg_warning:nnnn { zref-check }
              { property-not-in-label } {#1} {#2}
          }
      }
      {
%    \end{macrocode}
% There are three things we need to check to ensure the information we are
% trying to retrieve here exists: the existence of \Arg{label}, the existence
% of \Arg{prop}, and whether the particular label being queried actually
% contains the property.  If that's all in place, the value is passed to the
% checks, and it's their responsibility to verify the consistency of this
% value.
%
% The existence of the label is an user facing issue, and a warning for this
% is placed in \cs{@@_zcheck:nnnnn} (and done with \cs{zref@refused}).  We do
% check here though for definition with \cs{zref@ifrefundefined} and silently
% do nothing if it is undefined, to reduce irrelevant warnings in a fresh
% compilation round.  The other two are more ``internal'' problems, either
% some problem with the checks, or with the configuration of \pkg{zref} for
% their consumption.
%    \begin{macrocode}
        \zref@ifrefundefined {#1}
          {}
          {
            \zref@ifpropundefined {#2}
              { \msg_warning:nnnn { zref-check } { property-undefined } {#2} }
              {
                \zref@ifrefcontainsprop {#1} {#2}
                  {
                    \tl_set:Nx #3
                      { \zref@extractdefault {#1} {#2} { \c_empty_tl } }
                  }
                  {
                    \msg_warning:nnnn
                      { zref-check } { property-not-in-label } {#1} {#2}
                  }
              }
          }
      }
  }
%    \end{macrocode}
% \end{macro}
%
%
% \begin{variable}{\l_@@_integer_bool}
%   \cs{zrefcheck_get_asint:nnn} is a very convenient wrapper around the more
%   general \cs{zrefcheck_get_astl:nnn}, since almost always we'll be wanting
%   to compare numbers in the checks.  However, it is quite hard for it to
%   ensure an integer is \emph{always} returned in the case of errors.  And
%   those do occur, even in a well structured document (e.g., in a first round
%   of compilation).  To complicate things, the L3 integer predicates are
%   \emph{very} sensitive to receiving any other kind of data, and they
%   \emph{scream}.  To handle this \cs{zrefcheck_get_asint:nnn} uses
%   \cs{l_@@_integer_bool} to signal if an integer could not be returned.  To
%   use this function always set \cs{l_@@_integer_bool} to true first, then
%   call it as much as you need.  If any of these calls got is returning
%   anything which is not an integer, \cs{l_@@_integer_bool} will have been
%   set to false, and you should check that this hasn't happened before
%   actually comparing the integers (\cs{bool_lazy_and:nnTF} is your friend).
%    \begin{macrocode}
\bool_new:N \l_@@_integer_bool
%    \end{macrocode}
% \end{variable}
%
% \begin{variable}{\l_@@_propval_tl}
%    \begin{macrocode}
\tl_new:N \l_@@_propval_tl
%    \end{macrocode}
% \end{variable}
%
% \begin{macro}[int]{\zrefcheck_get_asint:nnn}
%   \begin{syntax}
%     \cs{zrefcheck_get_asint:nnn} \Arg{label} \Arg{prop} \Arg{int var}
%   \end{syntax}
%    \begin{macrocode}
\cs_new_protected:Npn \zrefcheck_get_asint:nnn #1#2#3
  {
    \zrefcheck_get_astl:nnn {#1} {#2} { \l_@@_propval_tl }
    \@@_is_integer:nTF { \l_@@_propval_tl }
      {
%    \end{macrocode}
% Make it an integer data type.
%    \begin{macrocode}
        \int_set:Nn #3 { \int_eval:n { \l_@@_propval_tl } }
      }
      {
        \bool_set_false:N \l_@@_integer_bool
        \zref@ifrefundefined {#1}
%    \end{macrocode}
% Keep silent if ref is undefined to reduce irrelevant warnings in a fresh
% compilation round.  Again, this is also not the point to check for undefined
% references, that's a task for \cs{@@_zcheck:nnnnn}.
%    \begin{macrocode}
          { }
          {
            \msg_warning:nnnn { zref-check }
              { property-not-integer } {#2} {#1}
          }
      }
  }
%    \end{macrocode}
% \end{macro}
%
%
%
%
% \section{User interface}
%
%
% \subsection{\cs{zcheck}}
%
% \begin{macro}[int]{\zcheck}
%   The \marg{text} argument of \cs{zcheck} should not be long, since
%   \cs{hyperlink} cannot receive a long argument.  Besides, there is no
%   reason for it to be.  Note, also, that hyperlinks crossing page boundaries
%   have some known issues: \url{https://tex.stackexchange.com/a/182769},
%   \url{https://tex.stackexchange.com/a/54607},
%   \url{https://tex.stackexchange.com/a/179907}.
%
%   \begin{syntax}
%     \cs{zcheck}\meta{*}\oarg{checks/options}\marg{labels}\marg{text}
%   \end{syntax}
%
%    \begin{macrocode}
\NewDocumentCommand \zcheck { s O { } m m }
  { \zref@wrapper@babel \@@_zcheck:nnnn {#3} {#1} {#2} {#4} }
%    \end{macrocode}
% \end{macro}
%
%
% \begin{variable}
%   {
%     \l_@@_zcheck_labels_seq ,
%     \g_@@_id_int ,
%     \l_@@_checkbeg_tl ,
%     \l_@@_link_label_tl ,
%     \l_@@_link_anchor_tl ,
%     \l_@@_link_star_bool
%   }
%    \begin{macrocode}
\seq_new:N \l_@@_zcheck_labels_seq
\int_new:N \g_@@_id_int
\tl_new:N \l_@@_checkbeg_tl
\tl_new:N \l_@@_link_label_tl
\tl_new:N \l_@@_link_anchor_tl
\bool_new:N \l_@@_link_star_bool
%    \end{macrocode}
% \end{variable}
%
% \begin{macro}{\@@_zcheck:nnnn}
%   An intermediate internal function, which does the actual heavy lifting,
%   and places \Arg{labels} as first argument, so that it can be protected by
%   \cs{zref@wrapper@babel} in \cs{zcheck}.  This is the same procedure as the
%   one used in the definition of \cs{zref} in \file{zref-user.sty} for
%   protection of \pkg{babel} active characters.
%
%   \begin{syntax}
%     \cs{@@_zcheck:nnnn} \Arg{labels} \Arg{*} \Arg{checks/options} \Arg{text}
%   \end{syntax}
%
%    \begin{macrocode}
\cs_new_protected:Npn \@@_zcheck:nnnn #1#2#3#4
  {
    \group_begin:
%    \end{macrocode}
% Process local options and checks.  We use \cs{seq_set_split:Nnn} to set
% \cs{l_@@_zcheck_labels_seq} -- instead of \cs{seq_set_from_clist:Nn} -- to
% support empty labels.
%    \begin{macrocode}
    \keys_set:nn { zref-check / zcheck } {#3}
    \seq_set_split:Nnn \l_@@_zcheck_labels_seq { , } {#1}
%    \end{macrocode}
% Names of the labels for this zcheck call.
%    \begin{macrocode}
    \int_gincr:N \g_@@_id_int
    \tl_set:Nx \l_@@_checkbeg_tl
      { \@@_check_lblfmt:n { \g_@@_id_int } }
%    \end{macrocode}
% Set checkbeg label.
%    \begin{macrocode}
    \zref@labelbylist { \l_@@_checkbeg_tl } { zrefcheck-check }
%    \end{macrocode}
% Typeset \marg{text}, with hyperlink when appropriate.  Even though the first
% argument can receive a list of labels, there is no meaningful way to set
% links to multiple targets.  Hence, only the first one is considered for
% hyperlinking.
%    \begin{macrocode}
    \seq_get:NN \l_@@_zcheck_labels_seq \l_@@_link_label_tl
    \bool_set:Nn \l_@@_link_star_bool {#2}
    \zref@ifrefundefined { \l_@@_link_label_tl }
%    \end{macrocode}
% If the reference is undefined, just typeset.
%    \begin{macrocode}
      {#4}
      {
        \bool_if:nTF
          {
            \l_@@_use_hyperref_bool &&
            ! \l_@@_link_star_bool
          }
          {
            \exp_args:Nx \zrefcheck_get_astl:nnn
              { \l_@@_link_label_tl }
              { anchor } { \l_@@_link_anchor_tl }
            \hyperlink { \l_@@_link_anchor_tl } {#4}
          }
          {#4}
      }
%    \end{macrocode}
% Set checkend label.
%    \begin{macrocode}
    \bool_if:NT \l_@@_zcheck_end_label_bool
      {
        \zref@labelbylist
          { \@@_end_lblfmt:n { \l_@@_checkbeg_tl } }
          { zrefcheck-end }
      }
%    \end{macrocode}
% Check if \meta{labels} are defined.
%    \begin{macrocode}
    \seq_map_inline:Nn \l_@@_zcheck_labels_seq
      { \tl_if_empty:nF {##1} { \zref@refused {##1} } }
%    \end{macrocode}
% Run the checks.
%    \begin{macrocode}
    \@@_run_checks:nnx { \l_@@_zcheck_checks_seq }
      { \l_@@_zcheck_labels_seq } { \l_@@_checkbeg_tl }
    \group_end:
  }
%    \end{macrocode}
% \end{macro}
%
%
% \subsection{Targets}
%
% \begin{macro}[int]{\zctarget}
%   \begin{syntax}
%     \cs{zctarget}\marg{label}\marg{text}
%   \end{syntax}
%    \begin{macrocode}
\NewDocumentCommand \zctarget { m +m }
  {
%    \end{macrocode}
% Group contents of \cs{zctarget} to avoid leaking the effects of
% \cs{refstepcounter} over \cs{@currentlabel}.  The same care is not needed
% for \texttt{zcregion}, since the environment is already grouped.
%    \begin{macrocode}
    \group_begin:
    \refstepcounter { zrefcheck }
    \zref@wrapper@babel \zref@label {#1}
    #2
    \tl_if_empty:nF {#2}
      {
        \zref@wrapper@babel
          \zref@labelbylist { \@@_end_lblfmt:n {#1} } { zrefcheck-end }
      }
    \group_end:
  }
%    \end{macrocode}
% \end{macro}
%
% \begin{macro}[int]{zcregion}
%   \begin{syntax}
%     |\begin{zcregion}|\marg{label}
%       |  ...|
%     |\end{zcregion}|
%   \end{syntax}
%    \begin{macrocode}
\NewDocumentEnvironment {zcregion} { m }
  {
    \refstepcounter { zrefcheck }
    \zref@wrapper@babel \zref@label {#1}
  }
  {
    \zref@wrapper@babel
      \zref@labelbylist { \@@_end_lblfmt:n {#1} } { zrefcheck-end }
  }
%    \end{macrocode}
% \end{macro}
%
%
%
% \section{Checks}
%
% What is needed define a \pkg{zref-check} check?
%
% First, a conditional function defined with:
%
% \cs{prg_new_protected_conditional:Npnn} \cs{@@_check_\meta{check}:nn} |#1#2 { F }|
%
% \noindent where \meta{check} is the name of the check, the first argument is
% the \Arg{label} and the second the \Arg{reference}.  The existence of the
% check is verified by the existence of the function with this name-scheme
% (and signatures).  As usual, this function must return either
% \cs{prg_return_true:} or \cs{prg_return_false:}.  Of course, you can define
% other variants if you need them internally, it is just that what the package
% does expect and verifies is the existence of the \texttt{:nnF} variant.
%
% Note that the naming convention of the checks adopts the perspective of the
% \meta{reference}.  That is, the ``before'' check should return true if the
% \meta{label} occurs before the ``reference''.
%
% The check conditionals are expected to retrieve \pkg{zref}'s label
% information with \cs{zrefcheck_get_astl:nnn} or
% \cs{zrefcheck_get_asint:nnn}.  Also, technically speaking, the
% \meta{reference} argument is also a label, actually a pair of them, as set
% by \cs{zcheck}.  For the ``labels'', any \pkg{zref} property in \pkg{zref}'s
% main list is available, the ``references'' store the properties in the
% \texttt{zrefcheck} list.  Besides those, there is also the \texttt{lblseq}
% (fake) property (for either ``labels'' or ``references''), stored in
% \cs{g_@@_auxfile_lblseq_prop}.
%
% Second, the required properties of labels and references must be duly
% registered for \pkg{zref}.  This can be done with \cs{zref@newprop},
% \cs{zref@addprop} and friends, as usual.
%
% Third, the check must be registered as a key which gets setup in
% \cs{zcheck} by the \texttt{ zref-check / zcheck } key set.
%
% Fourth, if the check requires only a single label to work, it should be
% registered in \cs{c_@@_single_label_checks_seq}.
%
%
% \subsection{Single label checks}
%
%
% Some checks do not require an ``end label'' in \cs{zcheck}, notably the
% sectioning ones, which don't rely on page boundaries.  Hence, in case
% \cs{zcheck} only calls checks in this set, we can spare the setting of the
% end label.
%
% \begin{variable}{\c_@@_single_label_checks_seq}
%    \begin{macrocode}
\seq_const_from_clist:Nn \c_@@_single_label_checks_seq
  {
    thischap ,
    prevchap ,
    nextchap ,
    chapsbefore ,
    chapsafter ,
    thissec ,
    prevsec ,
    nextsec ,
    secsbefore ,
    secsafter ,
    manual ,
  }
%    \end{macrocode}
% \end{variable}
%
%
%
% \subsection{Setup}
%
% \begin{variable}{\l_@@_zcheck_checks_seq,\l_@@_end_label_required_bool}
%    \begin{macrocode}
\seq_new:N \l_@@_zcheck_checks_seq
\bool_new:N \l_@@_zcheck_end_label_bool
%    \end{macrocode}
% \end{variable}
%
%
% First, we inherit all the main options into the keys of \texttt{zref-check /
% zcheck}.
%    \begin{macrocode}
\keys_define:nn { } { zref-check / zcheck .inherit:n = zref-check }
%    \end{macrocode}
%
% Then we add the checks to it.
%    \begin{macrocode}
\clist_map_inline:nn
  {
    thispage ,
    prevpage ,
    nextpage ,
    facing ,
    otherpage ,
    pagegap ,
    above ,
    below ,
    pagesbefore ,
    ppbefore ,
    pagesafter ,
    ppafter ,
    before ,
    after ,
    thischap ,
    prevchap ,
    nextchap ,
    chapsbefore ,
    chapsafter ,
    thissec ,
    prevsec ,
    nextsec ,
    secsbefore ,
    secsafter ,
    close ,
    far ,
    manual ,
  }
  {
    \keys_define:nn { zref-check / zcheck }
      {
        #1 .code:n =
          {
            \seq_put_right:Nn \l_@@_zcheck_checks_seq {#1}
            \seq_if_in:NnF \c_@@_single_label_checks_seq {#1}
              { \bool_set_true:N \l_@@_zcheck_end_label_bool }
          } ,
        #1 .value_forbidden:n = true ,
     }
  }
%    \end{macrocode}
%
%
%
% \subsection{Running}
%
% \begin{macro}{\@@_run_checks:nnn}
%   \begin{syntax}
%     \cs{@@_run_checks:nnn} \Arg{checks} \Arg{labels} \Arg{reference}
%   \end{syntax}
%   \meta{checks} are expected to be received as a sequence variable.
%    \begin{macrocode}
\cs_new_protected:Npn \@@_run_checks:nnn #1#2#3
  {
    \group_begin:
    \seq_map_inline:Nn #2
      {
        \seq_if_empty:NTF #1
          { \@@_message:nnnn { no-checks } { } { } { } }
          {
            \seq_map_inline:Nn #1
              { \@@_do_check:nnn {####1} {##1} {#3} }
          }
      }
    \group_end:
  }
\cs_generate_variant:Nn \@@_run_checks:nnn { nnx }
%    \end{macrocode}
% \end{macro}
%
%
% \begin{variable}
%   {
%     \l_@@_passedcheck_bool ,
%     \l_@@_onpage_bool ,
%     \l_@@_empty_label_bool ,
%     \c_@@_onpage_checks_seq
%   }
%    \begin{macrocode}
\bool_new:N \l_@@_passedcheck_bool
\bool_new:N \l_@@_onpage_bool
\bool_new:N \l_@@_empty_label_bool
\seq_const_from_clist:Nn \c_@@_onpage_checks_seq
  { above , below , before , after }
%    \end{macrocode}
% \end{variable}
%
%
% Variant not provided by \pkg{expl3}.
%    \begin{macrocode}
\cs_generate_variant:Nn \exp_args:Nnno { Nnoo }
%    \end{macrocode}
%
% \begin{macro}{\@@_do_check:nnn}
%   \begin{syntax}
%     \cs{@@_do_check:nnn} \Arg{check} \Arg{label beg} \Arg{reference beg}
%   \end{syntax}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_do_check:nnn #1#2#3
  {
    \group_begin:
    \bool_set_true:N \l_@@_passedcheck_bool
    \bool_set_false:N \l_@@_onpage_bool
    \bool_set_false:N \l_@@_empty_label_bool
    \cs_if_exist:cTF { @@_check_ #1 :nnF }
      {
%    \end{macrocode}
% \meta{label beg} may be defined or not, it is arbitrary user input.  Whether
% this is the case is checked in \cs{@@_zcheck:nnnnn}, and due warning already
% ensues.  So there's no need to do it again here.  The only exception is the
% case of empty labels, for which we want to issue a failed check warning.
%    \begin{macrocode}
        \zref@ifrefundefined {#2}
          {
            \tl_if_empty:nT {#2}
              {
                \bool_set_false:N \l_@@_passedcheck_bool
                \bool_set_true:N \l_@@_empty_label_bool
              }
          }
          {
            % ``label beg'' vs ``reference beg''.
            \use:c { @@_check_ #1 :nnF }
              {#2} {#3}
              { \bool_set_false:N \l_@@_passedcheck_bool }
            % ``reference end'' \emph{may} exist or not depending on the
            % checks.
            \zref@ifrefundefined { \@@_end_lblfmt:n {#3} }
              {
                % ``label end'' \emph{may} have been created by the
                % target commands.
                \zref@ifrefundefined { \@@_end_lblfmt:n {#2} }
                  {}
                  {
                    % ``label end'' vs ``reference beg''.
                    \exp_args:Nno \use:c { @@_check_ #1 :nnF }
                      { \@@_end_lblfmt:n {#2} } {#3}
                      { \bool_set_false:N \l_@@_passedcheck_bool }
                  }
              }
              {
                % ``label beg'' vs ``reference end''.
                \exp_args:Nnno \use:c { @@_check_ #1 :nnF }
                  {#2} { \@@_end_lblfmt:n {#3} }
                  { \bool_set_false:N \l_@@_passedcheck_bool }
                % ``label end'' \emph{may} have been created by the
                % target commands.
                \zref@ifrefundefined { \@@_end_lblfmt:n {#2} }
                  {}
                  {
                    % ``label end'' vs ``reference beg''.
                    \exp_args:Nno \use:c { @@_check_ #1 :nnF }
                      { \@@_end_lblfmt:n {#2} } {#3}
                      { \bool_set_false:N \l_@@_passedcheck_bool }
                    % ``label end'' vs ``reference end''.
                    \exp_args:Nnoo \use:c { @@_check_ #1 :nnF }
                      { \@@_end_lblfmt:n {#2} }
                      { \@@_end_lblfmt:n {#3} }
                      { \bool_set_false:N \l_@@_passedcheck_bool }
                  }
              }
%    \end{macrocode}
% Handle option \opt{onpage=msg}.  This is only granted for tests which
% perform ``within this page'' checks (\opt{above}, \opt{below}, \opt{before},
% \opt{after}) \emph{and} if any of the two by two checks uses a ``within this
% page'' comparison.  If both conditions are met, signal.
%    \begin{macrocode}
            \seq_if_in:NnT \c_@@_onpage_checks_seq {#1}
              {
                \@@_check_thispage:nnT
                  {#2} {#3}
                  { \bool_set_true:N \l_@@_onpage_bool }
                \zref@ifrefundefined { \@@_end_lblfmt:n {#3} }
                  {
                    \zref@ifrefundefined { \@@_end_lblfmt:n {#2} }
                      {}
                      {
                        \@@_check_thispage:nnT
                          { \@@_end_lblfmt:n {#2} } {#3}
                          { \bool_set_true:N \l_@@_onpage_bool }
                      }
                  }
                  {
                    \@@_check_thispage:nnT
                      {#2} { \@@_end_lblfmt:n {#3} }
                      { \bool_set_true:N \l_@@_onpage_bool }
                    \zref@ifrefundefined { \@@_end_lblfmt:n {#2} }
                      {}
                      {
                        \@@_check_thispage:nnT
                          { \@@_end_lblfmt:n {#2} } {#3}
                          { \bool_set_true:N \l_@@_onpage_bool }
                        \@@_check_thispage:nnT
                          { \@@_end_lblfmt:n {#2} }
                          { \@@_end_lblfmt:n {#3} }
                          { \bool_set_true:N \l_@@_onpage_bool }
                      }
                  }
              }
          }
      }
      { \msg_warning:nnn { zref-check } { check-missing } {#1} }
    \bool_if:NTF \l_@@_passedcheck_bool
      {
        \bool_if:nT
          {
            \l_@@_msgonpage_bool &&
            \l_@@_onpage_bool
          }
          {
            \@@_message:nnnx { double-check } {#1} {#2}
              { \zref@extractdefault {#3} {page} {'unknown'} }
          }
      }
      {
        \bool_if:NTF \l_@@_empty_label_bool
          { \@@_message:nnnn { empty-label } {#1} { } { } }
          {
            \@@_message:nnnx { check-failed } {#1} {#2}
              { \zref@extractdefault {#3} {page} {'unknown'} }
          }
      }
    \group_end:
  }
\cs_generate_variant:Nn \@@_do_check:nnn { nnV }
%    \end{macrocode}
% \end{macro}
%
%
% \subsection{Conditionals}
%
% \begin{variable}
%   {
%     \l_@@_lbl_int ,
%     \l_@@_ref_int ,
%     \l_@@_lbl_b_int ,
%     \l_@@_ref_b_int
%   }
%   More readable scratch variables for the tests.
%    \begin{macrocode}
\int_new:N \l_@@_lbl_int
\int_new:N \l_@@_ref_int
\int_new:N \l_@@_lbl_b_int
\int_new:N \l_@@_ref_b_int
%    \end{macrocode}
% \end{variable}
%
%
% \subsubsection{This page}
%
% \begin{macro}{\@@_check_thispage:nn, \@@_check_otherpage:nn}
%    \begin{macrocode}
\prg_new_protected_conditional:Npnn \@@_check_thispage:nn #1#2 { T , F , TF }
  {
    \group_begin:
    \bool_set_true:N \l_@@_integer_bool
    \zrefcheck_get_asint:nnn {#1} { abspage } { \l_@@_lbl_int }
    \zrefcheck_get_asint:nnn {#2} { abspage } { \l_@@_ref_int }
    \bool_lazy_and:nnTF
      { \l_@@_integer_bool }
      {
        \int_compare_p:nNn
          { \l_@@_lbl_int } = { \l_@@_ref_int } &&
%    \end{macrocode}
% `0' is the default value of \texttt{abspage}, but this value should not
% happen normally for this property, since even the first page, after it gets
% shipped out, will receive value `1'.  So, if we do find `0' here, better
% signal something is wrong.  This comment extends to all page number checks.
%    \begin{macrocode}
        ! \int_compare_p:nNn { \l_@@_lbl_int } = { 0 } &&
        ! \int_compare_p:nNn { \l_@@_ref_int } = { 0 }
      }
      { \group_insert_after:N \prg_return_true:  }
      { \group_insert_after:N \prg_return_false: }
    \group_end:
  }
\prg_new_protected_conditional:Npnn \@@_check_otherpage:nn #1#2 { T , F , TF }
  {
    \@@_check_thispage:nnTF {#1} {#2}
      { \prg_return_false: }
      { \prg_return_true:  }
  }
%    \end{macrocode}
% \end{macro}
%
%
% \subsubsection{On page}
%
% \begin{macro}{\@@_check_above:nn, \@@_check_below:nn}
%    \begin{macrocode}
\prg_new_protected_conditional:Npnn \@@_check_above:nn #1#2 { F , TF }
  {
    \group_begin:
    \@@_check_thispage:nnTF {#1} {#2}
      {
        \bool_set_true:N \l_@@_integer_bool
        \zrefcheck_get_asint:nnn {#1} { lblseq } { \l_@@_lbl_int }
        \zrefcheck_get_asint:nnn {#2} { lblseq } { \l_@@_ref_int }
        \bool_lazy_and:nnTF
          { \l_@@_integer_bool }
          {
            \int_compare_p:nNn
              { \l_@@_lbl_int } < { \l_@@_ref_int } &&
            ! \int_compare_p:nNn { \l_@@_lbl_int } = { 0 } &&
            ! \int_compare_p:nNn { \l_@@_ref_int } = { 0 }
          }
          { \group_insert_after:N \prg_return_true:  }
          { \group_insert_after:N \prg_return_false: }
      }
      { \group_insert_after:N \prg_return_false: }
    \group_end:
  }
\prg_new_protected_conditional:Npnn \@@_check_below:nn #1#2 { F , TF }
  {
    \@@_check_thispage:nnTF {#1} {#2}
      {
        \@@_check_above:nnTF {#1} {#2}
          { \prg_return_false: }
          { \prg_return_true:  }
      }
      { \prg_return_false: }
  }
%    \end{macrocode}
% \end{macro}
%
%
% \subsubsection{Before / After}
%
% \begin{macro}{\@@_check_before:nn, \@@_check_after:nn}
%    \begin{macrocode}
\prg_new_protected_conditional:Npnn \@@_check_before:nn #1#2 { F }
  {
    \@@_check_pagesbefore:nnTF {#1} {#2}
      { \prg_return_true: }
      {
        \@@_check_above:nnTF {#1} {#2}
          { \prg_return_true:  }
          { \prg_return_false: }
      }
  }
\prg_new_protected_conditional:Npnn \@@_check_after:nn #1#2 { F }
  {
    \@@_check_pagesafter:nnTF {#1} {#2}
      { \prg_return_true: }
      {
        \@@_check_below:nnTF {#1} {#2}
          { \prg_return_true:  }
          { \prg_return_false: }
      }
  }
%    \end{macrocode}
% \end{macro}
%
%
% \subsubsection{Pages}
%
% \begin{macro}
%   {
%     \@@_check_nextpage:nn ,
%     \@@_check_prevpage:nn ,
%     \@@_check_pagesbefore:nn ,
%     \@@_check_ppbefore:nn ,
%     \@@_check_pagesafter:nn ,
%     \@@_check_ppafter:nn ,
%     \@@_check_pagegap:nn ,
%     \@@_check_facing:nn
%   }
%    \begin{macrocode}
\prg_new_protected_conditional:Npnn \@@_check_nextpage:nn #1#2 { F }
  {
    \group_begin:
    \bool_set_true:N \l_@@_integer_bool
    \zrefcheck_get_asint:nnn {#1} { abspage } { \l_@@_lbl_int }
    \zrefcheck_get_asint:nnn {#2} { abspage } { \l_@@_ref_int }
    \bool_lazy_and:nnTF
      { \l_@@_integer_bool }
      {
        \int_compare_p:nNn
          { \l_@@_lbl_int } = { \l_@@_ref_int + 1 } &&
        ! \int_compare_p:nNn { \l_@@_lbl_int } = { 0 } &&
        ! \int_compare_p:nNn { \l_@@_ref_int } = { 0 }
      }
      { \group_insert_after:N \prg_return_true:  }
      { \group_insert_after:N \prg_return_false: }
    \group_end:
  }
\prg_new_protected_conditional:Npnn \@@_check_prevpage:nn #1#2 { F }
  {
    \group_begin:
    \bool_set_true:N \l_@@_integer_bool
    \zrefcheck_get_asint:nnn {#1} { abspage } { \l_@@_lbl_int }
    \zrefcheck_get_asint:nnn {#2} { abspage } { \l_@@_ref_int }
    \bool_lazy_and:nnTF
      { \l_@@_integer_bool }
      {
        \int_compare_p:nNn
          { \l_@@_lbl_int } = { \l_@@_ref_int - 1 } &&
        ! \int_compare_p:nNn { \l_@@_lbl_int } = { 0 } &&
        ! \int_compare_p:nNn { \l_@@_ref_int } = { 0 }
      }
      { \group_insert_after:N \prg_return_true:  }
      { \group_insert_after:N \prg_return_false: }
    \group_end:
  }
\prg_new_protected_conditional:Npnn \@@_check_pagesbefore:nn #1#2 { F , TF }
  {
    \group_begin:
    \bool_set_true:N \l_@@_integer_bool
    \zrefcheck_get_asint:nnn {#1} { abspage } { \l_@@_lbl_int }
    \zrefcheck_get_asint:nnn {#2} { abspage } { \l_@@_ref_int }
    \bool_lazy_and:nnTF
      { \l_@@_integer_bool }
      {
        \int_compare_p:nNn
          { \l_@@_lbl_int } < { \l_@@_ref_int } &&
        ! \int_compare_p:nNn { \l_@@_lbl_int } = { 0 } &&
        ! \int_compare_p:nNn { \l_@@_ref_int } = { 0 }
      }
      { \group_insert_after:N \prg_return_true:  }
      { \group_insert_after:N \prg_return_false: }
    \group_end:
  }
\cs_new_eq:NN \@@_check_ppbefore:nnF \@@_check_pagesbefore:nnF
\prg_new_protected_conditional:Npnn \@@_check_pagesafter:nn #1#2 { F , TF }
  {
    \group_begin:
    \bool_set_true:N \l_@@_integer_bool
    \zrefcheck_get_asint:nnn {#1} { abspage } { \l_@@_lbl_int }
    \zrefcheck_get_asint:nnn {#2} { abspage } { \l_@@_ref_int }
    \bool_lazy_and:nnTF
      { \l_@@_integer_bool }
      {
        \int_compare_p:nNn
          { \l_@@_lbl_int } > { \l_@@_ref_int } &&
        ! \int_compare_p:nNn { \l_@@_lbl_int } = { 0 } &&
        ! \int_compare_p:nNn { \l_@@_ref_int } = { 0 }
      }
      { \group_insert_after:N \prg_return_true:  }
      { \group_insert_after:N \prg_return_false: }
    \group_end:
  }
\cs_new_eq:NN \@@_check_ppafter:nnF \@@_check_pagesafter:nnF
\prg_new_protected_conditional:Npnn \@@_check_pagegap:nn #1#2 { F }
  {
    \group_begin:
    \bool_set_true:N \l_@@_integer_bool
    \zrefcheck_get_asint:nnn {#1} { abspage } { \l_@@_lbl_int }
    \zrefcheck_get_asint:nnn {#2} { abspage } { \l_@@_ref_int }
    \bool_lazy_and:nnTF
      { \l_@@_integer_bool }
      {
        \int_compare_p:nNn
          { \int_abs:n { \l_@@_lbl_int - \l_@@_ref_int } } > { 1 } &&
        ! \int_compare_p:nNn { \l_@@_lbl_int } = { 0 } &&
        ! \int_compare_p:nNn { \l_@@_ref_int } = { 0 }
      }
      { \group_insert_after:N \prg_return_true:  }
      { \group_insert_after:N \prg_return_false: }
    \group_end:
  }
\prg_new_protected_conditional:Npnn \@@_check_facing:nn #1#2 { F }
  {
    \group_begin:
    \bool_set_true:N \l_@@_integer_bool
    \zrefcheck_get_asint:nnn {#1} { abspage } { \l_@@_lbl_int }
    \zrefcheck_get_asint:nnn {#2} { abspage } { \l_@@_ref_int }
    \bool_lazy_and:nnTF
      { \l_@@_integer_bool }
      {
%    \end{macrocode}
% There exists no ``facing'' page if the document is not twoside.
%    \begin{macrocode}
        \legacy_if_p:n { @twoside } &&
%    \end{macrocode}
% Now we test ``facing''.
%    \begin{macrocode}
        (
          (
            \int_if_odd_p:n { \l_@@_ref_int } &&
            \int_compare_p:nNn
              { \l_@@_lbl_int } = { \l_@@_ref_int - 1 }
          ) ||
          (
            \int_if_even_p:n { \l_@@_ref_int } &&
            \int_compare_p:nNn
              { \l_@@_lbl_int } = { \l_@@_ref_int + 1 }
          )
        ) &&
        ! \int_compare_p:nNn { \l_@@_lbl_int } = { 0 } &&
        ! \int_compare_p:nNn { \l_@@_ref_int } = { 0 }
      }
      { \group_insert_after:N \prg_return_true:  }
      { \group_insert_after:N \prg_return_false: }
    \group_end:
  }
%    \end{macrocode}
% \end{macro}
%
%
% \subsubsection{Close / Far}
%
% \begin{macro}{\@@_check_close:nn, \@@_check_far:nn}
%    \begin{macrocode}
\prg_new_protected_conditional:Npnn \@@_check_close:nn #1#2 { F , TF }
  {
    \group_begin:
    \bool_set_true:N \l_@@_integer_bool
    \zrefcheck_get_asint:nnn {#1} { abspage } { \l_@@_lbl_int }
    \zrefcheck_get_asint:nnn {#2} { abspage } { \l_@@_ref_int }
    \bool_lazy_and:nnTF
      { \l_@@_integer_bool }
      {
        \int_compare_p:nNn
          { \int_abs:n { \l_@@_lbl_int - \l_@@_ref_int } }
          <
          { \l_@@_close_range_int + 1 } &&
        ! \int_compare_p:nNn { \l_@@_lbl_int } = { 0 } &&
        ! \int_compare_p:nNn { \l_@@_ref_int } = { 0 }
      }
      { \group_insert_after:N \prg_return_true:  }
      { \group_insert_after:N \prg_return_false: }
    \group_end:
  }
\prg_new_protected_conditional:Npnn \@@_check_far:nn #1#2 { F }
  {
    \@@_check_close:nnTF {#1} {#2}
      { \prg_return_false: }
      { \prg_return_true:  }
  }
%    \end{macrocode}
% \end{macro}
%
%
% \subsubsection{Chapter}
%
% \begin{macro}
%   {
%     \@@_check_thischap:nn ,
%     \@@_check_nextchap:nn ,
%     \@@_check_prevchap:nn ,
%     \@@_check_chapsafter:nn ,
%     \@@_check_chapsbefore:nn
%   }
%    \begin{macrocode}
\prg_new_protected_conditional:Npnn \@@_check_thischap:nn #1#2 { F }
  {
    \group_begin:
    \bool_set_true:N \l_@@_integer_bool
    \zrefcheck_get_asint:nnn {#1} { zc@abschap } { \l_@@_lbl_int }
    \zrefcheck_get_asint:nnn {#2} { zc@abschap } { \l_@@_ref_int }
    \bool_lazy_and:nnTF
      { \l_@@_integer_bool }
      {
        \int_compare_p:nNn
          { \l_@@_lbl_int } = { \l_@@_ref_int } &&
%    \end{macrocode}
% `0' is the default value of \texttt{zc@abschap} property, and means here no
% \cs{chapter} has yet been issued, therefore it cannot be ``this chapter'',
% nor ``the next chapter'', nor ``the previous chapter'', it is just ``no
% chapter''.  Note, however, that a statement about a ``future'' chapter does
% not require the ``current'' one to exist.  This comment extends to all
% chapter checks.
%    \begin{macrocode}
        ! \int_compare_p:nNn { \l_@@_lbl_int } = { 0 } &&
        ! \int_compare_p:nNn { \l_@@_ref_int } = { 0 }
      }
      { \group_insert_after:N \prg_return_true:  }
      { \group_insert_after:N \prg_return_false: }
    \group_end:
  }
\prg_new_protected_conditional:Npnn \@@_check_nextchap:nn #1#2 { F }
  {
    \group_begin:
    \bool_set_true:N \l_@@_integer_bool
    \zrefcheck_get_asint:nnn {#1} { zc@abschap } { \l_@@_lbl_int }
    \zrefcheck_get_asint:nnn {#2} { zc@abschap } { \l_@@_ref_int }
    \bool_lazy_and:nnTF
      { \l_@@_integer_bool }
      {
        \int_compare_p:nNn
          { \l_@@_lbl_int } = { \l_@@_ref_int + 1 } &&
        ! \int_compare_p:nNn { \l_@@_lbl_int } = { 0 }
      }
      { \group_insert_after:N \prg_return_true:  }
      { \group_insert_after:N \prg_return_false: }
    \group_end:
  }
\prg_new_protected_conditional:Npnn \@@_check_prevchap:nn #1#2 { F }
  {
    \group_begin:
    \bool_set_true:N \l_@@_integer_bool
    \zrefcheck_get_asint:nnn {#1} { zc@abschap } { \l_@@_lbl_int }
    \zrefcheck_get_asint:nnn {#2} { zc@abschap } { \l_@@_ref_int }
    \bool_lazy_and:nnTF
      { \l_@@_integer_bool }
      {
        \int_compare_p:nNn
          { \l_@@_lbl_int } = { \l_@@_ref_int - 1 } &&
        ! \int_compare_p:nNn { \l_@@_lbl_int } = { 0 } &&
        ! \int_compare_p:nNn { \l_@@_ref_int } = { 0 }
      }
      { \group_insert_after:N \prg_return_true:  }
      { \group_insert_after:N \prg_return_false: }
    \group_end:
  }
\prg_new_protected_conditional:Npnn \@@_check_chapsafter:nn #1#2 { F }
  {
    \group_begin:
    \bool_set_true:N \l_@@_integer_bool
    \zrefcheck_get_asint:nnn {#1} { zc@abschap } { \l_@@_lbl_int }
    \zrefcheck_get_asint:nnn {#2} { zc@abschap } { \l_@@_ref_int }
    \bool_lazy_and:nnTF
      { \l_@@_integer_bool }
      {
        \int_compare_p:nNn
          { \l_@@_lbl_int } > { \l_@@_ref_int } &&
        ! \int_compare_p:nNn { \l_@@_lbl_int } = { 0 }
      }
      { \group_insert_after:N \prg_return_true:  }
      { \group_insert_after:N \prg_return_false: }
    \group_end:
  }
\prg_new_protected_conditional:Npnn \@@_check_chapsbefore:nn #1#2 { F }
  {
    \group_begin:
    \bool_set_true:N \l_@@_integer_bool
    \zrefcheck_get_asint:nnn {#1} { zc@abschap } { \l_@@_lbl_int }
    \zrefcheck_get_asint:nnn {#2} { zc@abschap } { \l_@@_ref_int }
    \bool_lazy_and:nnTF
      { \l_@@_integer_bool }
      {
        \int_compare_p:nNn
          { \l_@@_lbl_int } < { \l_@@_ref_int } &&
        ! \int_compare_p:nNn { \l_@@_lbl_int } = { 0 } &&
        ! \int_compare_p:nNn { \l_@@_ref_int } = { 0 }
      }
      { \group_insert_after:N \prg_return_true:  }
      { \group_insert_after:N \prg_return_false: }
    \group_end:
  }
%    \end{macrocode}
% \end{macro}
%
%
% \subsubsection{Section}
%
% \begin{macro}
%   {
%     \@@_check_thissec:nn ,
%     \@@_check_nextsec:nn ,
%     \@@_check_prevsec:nn ,
%     \@@_check_secsafter:nn ,
%     \@@_check_secsbefore:nn
%   }
%    \begin{macrocode}
\prg_new_protected_conditional:Npnn \@@_check_thissec:nn #1#2 { F }
  {
    \group_begin:
    \bool_set_true:N \l_@@_integer_bool
    \zrefcheck_get_asint:nnn {#1} { zc@abssec  } { \l_@@_lbl_int }
    \zrefcheck_get_asint:nnn {#2} { zc@abssec  } { \l_@@_ref_int }
    \zrefcheck_get_asint:nnn {#1} { zc@abschap } { \l_@@_lbl_b_int }
    \zrefcheck_get_asint:nnn {#2} { zc@abschap } { \l_@@_ref_b_int }
    \bool_lazy_and:nnTF
      { \l_@@_integer_bool }
      {
        \int_compare_p:nNn
          { \l_@@_lbl_b_int } = { \l_@@_ref_b_int } &&
        \int_compare_p:nNn
          { \l_@@_lbl_int } = { \l_@@_ref_int } &&
%    \end{macrocode}
% `0' is the default value of \texttt{zc@abssec} property, and means here no
% \cs{section} has yet been issued since its counter has been reset, which
% occurs at the beginning of the document and at every chapter.  Hence, as is
% the case for chapters, `0' is just ``not a section''.  The same observation
% about the need of the ``current'' section to exist to be able to refer to a
% ``future'' one also holds.  This comment extends to all section checks.
%    \begin{macrocode}
        ! \int_compare_p:nNn { \l_@@_lbl_int } = { 0 } &&
        ! \int_compare_p:nNn { \l_@@_ref_int } = { 0 }
      }
      { \group_insert_after:N \prg_return_true:  }
      { \group_insert_after:N \prg_return_false: }
    \group_end:
  }
\prg_new_protected_conditional:Npnn \@@_check_nextsec:nn #1#2 { F }
  {
    \group_begin:
    \bool_set_true:N \l_@@_integer_bool
    \zrefcheck_get_asint:nnn {#1} { zc@abssec  } { \l_@@_lbl_int }
    \zrefcheck_get_asint:nnn {#2} { zc@abssec  } { \l_@@_ref_int }
    \zrefcheck_get_asint:nnn {#1} { zc@abschap } { \l_@@_lbl_b_int }
    \zrefcheck_get_asint:nnn {#2} { zc@abschap } { \l_@@_ref_b_int }
    \bool_lazy_and:nnTF
      { \l_@@_integer_bool }
      {
        \int_compare_p:nNn
          { \l_@@_lbl_b_int } = { \l_@@_ref_b_int } &&
        \int_compare_p:nNn
          { \l_@@_lbl_int } = { \l_@@_ref_int + 1 } &&
        ! \int_compare_p:nNn { \l_@@_lbl_int } = { 0 }
      }
      { \group_insert_after:N \prg_return_true:  }
      { \group_insert_after:N \prg_return_false: }
    \group_end:
  }
\prg_new_protected_conditional:Npnn \@@_check_prevsec:nn #1#2 { F }
  {
    \group_begin:
    \bool_set_true:N \l_@@_integer_bool
    \zrefcheck_get_asint:nnn {#1} { zc@abssec  } { \l_@@_lbl_int }
    \zrefcheck_get_asint:nnn {#2} { zc@abssec  } { \l_@@_ref_int }
    \zrefcheck_get_asint:nnn {#1} { zc@abschap } { \l_@@_lbl_b_int }
    \zrefcheck_get_asint:nnn {#2} { zc@abschap } { \l_@@_ref_b_int }
    \bool_lazy_and:nnTF
      { \l_@@_integer_bool }
      {
        \int_compare_p:nNn
          { \l_@@_lbl_b_int } = { \l_@@_ref_b_int } &&
        \int_compare_p:nNn
          { \l_@@_lbl_int } = { \l_@@_ref_int - 1 } &&
        ! \int_compare_p:nNn { \l_@@_lbl_int } = { 0 } &&
        ! \int_compare_p:nNn { \l_@@_ref_int } = { 0 }
      }
      { \group_insert_after:N \prg_return_true:  }
      { \group_insert_after:N \prg_return_false: }
    \group_end:
  }
\prg_new_protected_conditional:Npnn \@@_check_secsafter:nn #1#2 { F }
  {
    \group_begin:
    \bool_set_true:N \l_@@_integer_bool
    \zrefcheck_get_asint:nnn {#1} { zc@abssec  } { \l_@@_lbl_int }
    \zrefcheck_get_asint:nnn {#2} { zc@abssec  } { \l_@@_ref_int }
    \zrefcheck_get_asint:nnn {#1} { zc@abschap } { \l_@@_lbl_b_int }
    \zrefcheck_get_asint:nnn {#2} { zc@abschap } { \l_@@_ref_b_int }
    \bool_lazy_and:nnTF
      { \l_@@_integer_bool }
      {
        \int_compare_p:nNn
          { \l_@@_lbl_b_int } = { \l_@@_ref_b_int } &&
        \int_compare_p:nNn
          { \l_@@_lbl_int } > { \l_@@_ref_int } &&
        ! \int_compare_p:nNn { \l_@@_lbl_int } = { 0 }
      }
      { \group_insert_after:N \prg_return_true:  }
      { \group_insert_after:N \prg_return_false: }
    \group_end:
  }
\prg_new_protected_conditional:Npnn \@@_check_secsbefore:nn #1#2 { F }
  {
    \group_begin:
    \bool_set_true:N \l_@@_integer_bool
    \zrefcheck_get_asint:nnn {#1} { zc@abssec  } { \l_@@_lbl_int }
    \zrefcheck_get_asint:nnn {#2} { zc@abssec  } { \l_@@_ref_int }
    \zrefcheck_get_asint:nnn {#1} { zc@abschap } { \l_@@_lbl_b_int }
    \zrefcheck_get_asint:nnn {#2} { zc@abschap } { \l_@@_ref_b_int }
    \bool_lazy_and:nnTF
      { \l_@@_integer_bool }
      {
        \int_compare_p:nNn
          { \l_@@_lbl_b_int } = { \l_@@_ref_b_int } &&
        \int_compare_p:nNn
          { \l_@@_lbl_int } < { \l_@@_ref_int } &&
        ! \int_compare_p:nNn { \l_@@_lbl_int } = { 0 } &&
        ! \int_compare_p:nNn { \l_@@_ref_int } = { 0 }
      }
      { \group_insert_after:N \prg_return_true:  }
      { \group_insert_after:N \prg_return_false: }
    \group_end:
  }
%    \end{macrocode}
% \end{macro}
%
%
%
% \subsubsection{Manual}
%
% \begin{macro}{ \@@_check_manual:nn }
%    \begin{macrocode}
\prg_new_protected_conditional:Npnn \@@_check_manual:nn #1#2 { F }
  { \prg_return_false: }
%    \end{macrocode}
% \end{macro}
%
%
%
% \section{\pkg{zref-clever} integration}
%
%
% There are four tasks \pkg{zref-clever} needs to do, in order to offer
% integration with \pkg{zref-check} from the options of \cs{zcref}: i) set the
% ``beg label''; ii) set the checks options; iii) run the checks; iv)
% (possibly) set the ``end label''.  Since `ii)' can be done directly by
% running |\keys_set:nn { zref-check / zcheck }| on the options received, we
% provide convenience functions for the other three tasks.
%
%
% \begin{macro}
%   {
%     \zrefcheck_zcref_beg_label: ,
%     \zrefcheck_zcref_end_label_maybe: ,
%     \zrefcheck_zcref_run_checks_on_labels:n
%   }
%    \begin{macrocode}
\cs_new_protected:Npn \zrefcheck_zcref_beg_label:
  {
    \int_gincr:N \g_@@_id_int
    \tl_set:Nx \l_@@_checkbeg_tl
      { \@@_check_lblfmt:n { \g_@@_id_int } }
    \zref@labelbylist { \l_@@_checkbeg_tl } { zrefcheck-check }
  }
\cs_new_protected:Npn \zrefcheck_zcref_end_label_maybe:
  {
    \bool_if:NT \l_@@_zcheck_end_label_bool
      {
        \zref@labelbylist
          { \@@_end_lblfmt:n { \l_@@_checkbeg_tl } }
          { zrefcheck-end }
      }
  }
\cs_new_protected:Npn \zrefcheck_zcref_run_checks_on_labels:n #1
  {
    \@@_run_checks:nnx
      { \l_@@_zcheck_checks_seq } {#1} { \l_@@_checkbeg_tl }
  }
%    \end{macrocode}
% \end{macro}
%
%
%
% \section{\pkg{zref-vario} integration}
%
%
% \begin{macro}
%   {
%     \zrefcheck_zrefvario_label: ,
%     \zrefcheck_zrefvario_run_check_on_label:n
%   }
%    \begin{macrocode}
\cs_new_protected:Npn \zrefcheck_zrefvario_label:
  {
    \int_gincr:N \g_@@_id_int
    \tl_set:Nx \l_@@_checkbeg_tl
      { \@@_check_lblfmt:n { \g_@@_id_int } }
    \zref@labelbylist { \l_@@_checkbeg_tl } { zrefcheck-zrefvario }
  }
\cs_new_protected:Npn \zrefcheck_zrefvario_run_check_on_label:nn #1#2
  { \@@_do_check:nnV {#1} {#2} \l_@@_checkbeg_tl }
\cs_generate_variant:Nn \zrefcheck_zrefvario_run_check_on_label:nn { Vn }
%    \end{macrocode}
% \end{macro}
%
%
%    \begin{macrocode}
%</package>
%    \end{macrocode}
%
% \PrintIndex
%
