% \iffalse
% vim: set expandtab:
% vim: set shiftwidth=2:
% vim: set tabstop=2:
% \fi
% \iffalse meta-comment
%
% Copyright (C) 2026 by Lukas Heindl <oss.heindl+latex@protonmail.com>
% ---------------------------------------------------------------------------
% This work 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
% and version 1.3c or later is part of all distributions of LaTeX
% version 2008/05/04 or later.
%
% This work has the LPPL maintenance status `maintained'.
%
% The Current Maintainer of this work is Lukas Heindl.
%
% This work consists of all files listed in manifest.txt.
%
% \fi
%
% \iffalse
%<*driver>
\ProvidesFile{hexdumptikz-selector-matcher.dtx}
%</driver>
%<package>\NeedsTeXFormat{LaTeX2e}[2022-06-01]
%
%<*driver>
\begin{document}
  \DocInput{\jobname.dtx}
  \PrintChanges
  \PrintIndex
\end{document}
%
% \changes{v0.0.0}{2026-05-14}{First draft}
%
%</driver>
% \fi
%
% \iffalse
%<*package>
%<@@=hexdumptikz_selector_matcher>
% \fi
%
% \maketitle
%
% \begin{abstract}
%   Matches parsed selector/style rules against coordinates and/or addresses.
% \end{abstract}
%
% Identify the package and give the over all version information.
%    \begin{macrocode}
\ProvidesExplPackage {hexdumptikz-selector-matcher} {2026-06-20} {1.0.1}
  {Match coordinates and addresses with speficied display rules}
%    \end{macrocode}
%
% Create a shortcut for obtaining the variable name of a variable in a context.
% Only for being able to use \emph{@@} in the \emph{.dtx} file.
%    \begin{macrocode}
\cs_new_eq:NN
  \@@_ctx_var:nn
  \__hexdumptikz_selector_ctx_var:nn
%    \end{macrocode}
%
% \subsection{Variables}
% \begin{var}{\l_@@_range_state_int}
% Functions checking whether an element is inside a given range need to return more than just true/false.
% Thus, such functions use this (local) variable to return the decision.
% It basically is an enum which works as follows:
% \begin{itemize}
%   \item[-1] range has already passed
%   \item[+0] range is currently active
%   \item[+1] range is still ahead / did not start yet
% \end{itemize}
%    \begin{macrocode}
\int_new:N \l_@@_range_state_int
%    \end{macrocode}
% \end{var}

% \begin{var}{\l_@@_rule_id_int}
% Scratch variable for storing the rule-id which is currently processed.
% (avoids clobbering other scratch variables and makes the code more readable)
%    \begin{macrocode}
\int_new:N \l_@@_rule_id_int
%    \end{macrocode}
% \end{var}
% \begin{var}{\l_@@_search_all_bool}
% Decision variable whether all active rules should be searched.
% Allows to specify the matcher should stop after finding the first active rule.
%
% Is used as a scratch variable in the public matcher functions (which then gets passed to the util-functions).
%    \begin{macrocode}
\bool_new:N \l_@@_search_all_bool
%    \end{macrocode}
% \end{var}
% \begin{var}{\l_@@_match_loop_bool}
% Whether to continue searching for a match.
%    \begin{macrocode}
\bool_new:N \l_@@_match_loop_bool
%    \end{macrocode}
% \end{var}
% \begin{var}{\l_@@_active_tmp_seq}
% Scratch variable for temporarily storing the set of active rules.
%    \begin{macrocode}
\seq_new:N \l_@@_active_tmp_seq
%    \end{macrocode}
% \end{var}

% \subsection{Helpers}
% \begin{fn}{\@@_match_mod:nnn}
% Check a modulos-rule.
% Such a rule checks $\mathbf{x} \bmod \mathbf{m} \overset{!}{=} \mathbf{r}$
% \begin{sideeffects}
% \end{sideeffects}
% \begin{args}
%   1 & \ain & item / $\mathbf{x}$ \\
%   2 & \ain & modulos / $\mathbf{m}$ \\
%   3 & \ain & residue / $\mathbf{r}$ \\
% \end{args}
%    \begin{macrocode}
\cs_generate_variant:Nn \int_show:n { e }
\cs_generate_variant:Nn \int_mod:nn { en }
\prg_new_protected_conditional:Npnn
  \@@_match_mod:nnn
  #1 #2 #3
{ TF }
{
  \int_compare:nNnTF { \int_mod:nn { #1 } { #2 } } = { #3 }
  { \prg_return_true: }
  { \prg_return_false: }
}
%    \end{macrocode}
% \end{fn}
%
% \begin{fn}{\@@_match_apply_style:Nn}
% Applies/Appends the style of a specific rule to the current iterator state.
% \begin{sideeffects}
% \end{sideeffects}
% \begin{args}
%   1 & \ain & context-id \\
%   2 & \ain & rule-id \\
% \end{args}
%    \begin{macrocode}
\cs_new_protected:Npn \@@_match_apply_style:Nn #1 #2
{
  \tl_if_empty:cTF
  { \@@_ctx_var:nn { #1 } { style_tl } }
  {
    \tl_gset:ce
    { \@@_ctx_var:nn { #1 } { style_tl } }
    {
      \seq_item:cn
      { \@@_ctx_var:nn { #1 } { style_seq } }
      { #2 }
    }
  }
  {
    \tl_gput_right:ce
    { \@@_ctx_var:nn { #1 } { style_tl } }
    {
      ,
      \seq_item:cn
      { \@@_ctx_var:nn { #1 } { style_seq } }
      { #2 }
    }
  }
}
%    \end{macrocode}
% \end{fn}
%
%
% \subsection{Predicate Checking}
%
% \begin{fn}{\@@_match_rule_predicate:NnnNn}
% Check if the rule matches with respect to the predicate.
% Does not check the range.
% \begin{sideeffects}
%   \sclobber & \sdir & \texttt{l\_tmpa\_int} \\
%   \sclobber & \sdir & \texttt{l\_tmpa\_tl} \\
% \end{sideeffects}
% \begin{args}
%   1 & \ain & context-id \\
%   2 & \ain & current y \\
%   3 & \ain & current x \\
%   4 & \ain & current address (currently unused) \\
%   5 & \ain & rule-id \\
% \end{args}
%    \begin{macrocode}
\prg_new_protected_conditional:Npnn
  \@@_match_rule_predicate:NnnNn
  #1 #2 #3 #4 #5
{ T, TF }
{
  \int_set:Nn
    \l_tmpa_int
    {
      \seq_item:cn
      { \@@_ctx_var:nn { #1 } { pred_idx_seq } }
      { #5 }
    }

  \int_compare:nNnTF { \l_tmpa_int } = { 0 }
  {
%    \end{macrocode}
% no predicate present $\to$ matches
%    \begin{macrocode}
    \prg_return_true:
  }
  {
    \tl_set:Ne
      \l_tmpa_tl
      {
        \seq_item:cV
        { \@@_ctx_var:nn { #1 } { pred_kind_seq } }
        \l_tmpa_int
      }

    \str_case:VnF \l_tmpa_tl
    {
      { mod }
      {
        \tl_set:Ne
          \l_tmpa_tl
          {
            \seq_item:cn
            { \@@_ctx_var:nn { #1 } { pred_axis_seq } } % noqa: S103
            { \l_tmpa_int }
          }
        \int_set:Nn
          \l_tmpb_int
          {
            \seq_item:cn
            { \@@_ctx_var:nn { #1 } { pred_mod_seq } } % noqa: S103
            { \l_tmpa_int }
          }
        \int_set:Nn
          \l_tmpa_int
          {
            \seq_item:cn
            { \@@_ctx_var:nn { #1 } { pred_res_seq } } % noqa: S103
            { \l_tmpa_int }
          }

        \str_case:VnF \l_tmpa_tl
        {
          { x }
          {
            \@@_match_mod:nnnTF
            { #3 }
            { \l_tmpb_int }
            { \l_tmpa_int }
            { \prg_return_true: }
            { \prg_return_false: }
          }
          { y } {
            \@@_match_mod:nnnTF
            { #2 }
            { \l_tmpb_int }
            { \l_tmpa_int }
            { \prg_return_true: }
            { \prg_return_false: }
          }
        }
        {
          \prg_return_false:
        }
      }
    }
    {
      \msg_critical:nnV
      { hexdumptikz-selector }
      { unknown-predicate }
      \l_tmpa_tl
    }
  }
}
%    \end{macrocode}
% \end{fn}
%
%
% \subsection{Coordinate/Address Checking}
%
% \begin{fn}{\@@_coord_lt:nnnn}
% checks whether $a < b$ holds
% \begin{sideeffects}
% \end{sideeffects}
% \begin{args}
%   1 & \ain & $y_a$ \\
%   2 & \ain & $x_a$ \\
%   3 & \ain & $y_b$ \\
%   4 & \ain & $x_b$ \\
% \end{args}
%    \begin{macrocode}
\prg_new_protected_conditional:Npnn
  \@@_coord_lt:nnnn
  #1 #2 #3 #4
{ TF }
{
  \int_compare:nNnTF { #1 } < { #3 }
    { \prg_return_true: }
    {
      \int_compare:nNnTF { #1 } > { #3 }
        { \prg_return_false: }
        {
          \int_compare:nNnTF { #2 } < { #4 }
            { \prg_return_true: }
            { \prg_return_false: }
        }
    }
}
%    \end{macrocode}
% \end{fn}
%
% \begin{fn}{\@@_coord_gt:nnnn}
% checks whether $a > b$ holds
% \begin{sideeffects}
% \end{sideeffects}
% \begin{args}
%   1 & \ain & $y_a$ \\
%   2 & \ain & $x_a$ \\
%   3 & \ain & $y_b$ \\
%   4 & \ain & $x_b$ \\
% \end{args}
%    \begin{macrocode}
\prg_new_protected_conditional:Npnn
  \@@_coord_gt:nnnn
  #1 #2 #3 #4
{ TF }
{
  \int_compare:nNnTF { #1 } > { #3 }
    { \prg_return_true: }
    {
      \int_compare:nNnTF { #1 } < { #3 }
        { \prg_return_false: }
        {
          \int_compare:nNnTF { #2 } > { #4 }
            { \prg_return_true: }
            { \prg_return_false: }
        }
    }
}
%    \end{macrocode}
% \end{fn}
%
% \begin{fn}{\@@_addr_x_lt:nnnn}
% checks whether $a < b$ holds
% \begin{sideeffects}
% \end{sideeffects}
% \begin{args}
%   1 & \ain & $\text{addr}_a$ \\
%   2 & \ain & $x_a$ \\
%   3 & \ain & $\text{addr}_b$ \\
%   4 & \ain & $x_b$ \\
% \end{args}
%    \begin{macrocode}
\prg_new_protected_conditional:Npnn
  \@@_addr_x_lt:nnnn
  #1 #2 #3 #4
{ TF }
{
  \str_compare:eNeTF { #1 } < { #3 }
    { \prg_return_true: }
    {
      \str_compare:eNeTF { #1 } > { #3 }
        { \prg_return_false: }
        {
          \int_compare:nNnTF { #2 } < { #4 }
            { \prg_return_true: }
            { \prg_return_false: }
        }
    }
}
%    \end{macrocode}
% \end{fn}
%
% \begin{fn}{\@@_addr_x_gt:nnnn}
% checks whether $a > b$ holds
% \begin{sideeffects}
% \end{sideeffects}
% \begin{args}
%   1 & \ain & $\text{addr}_a$ \\
%   2 & \ain & $x_a$ \\
%   3 & \ain & $\text{addr}_b$ \\
%   4 & \ain & $x_b$ \\
% \end{args}
%    \begin{macrocode}
\prg_new_protected_conditional:Npnn
  \@@_addr_x_gt:nnnn
  #1 #2 #3 #4
{ TF }
{
  \str_compare:eNeTF { #1 } > { #3 }
    { \prg_return_true: }
    {
      \str_compare:eNeTF { #1 } < { #3 }
        { \prg_return_false: }
        {
          \int_compare:nNnTF { #2 } > { #4 }
            { \prg_return_true: }
            { \prg_return_false: }
        }
    }
}
%    \end{macrocode}
% \end{fn}
%
% \begin{fn}{\@@_match_rule_idx:nnnn}
% Match an index rule against the current item.
% \begin{sideeffects}
%   \sclobber & \sdir & \texttt{l\_tmpa\_int} \\
%   \sclobber & \sdir & \texttt{l\_tmpb\_int} \\
% \end{sideeffects}
% \begin{args}
%   1 & \ain  & context-id \\
%   2 & \ain  & $y$ \\
%   3 & \ain  & $x$ \\
%   4 & \ain  & rule-id \\
%   - & \aout & \texttt{l\_@@\_range\_state\_int}
% \end{args}
%    \begin{macrocode}
\cs_new_protected:Npn
  \@@_match_rule_idx:nnnn
  #1 #2 #3 #4
{
  \int_set:Nn
    \l_tmpa_int
    {
      \seq_item:cn
      { \@@_ctx_var:nn { #1 } { idx_low_y_seq } }
      { #4 }
    }
  \int_set:Nn
    \l_tmpb_int
    {
      \seq_item:cn
      { \@@_ctx_var:nn { #1 } { idx_low_x_seq } }
      { #4 }
    }
  \@@_coord_lt:nnnnTF
  { #2 }
  { #3 }
  { \l_tmpa_int }
  { \l_tmpb_int }
  {
%    \end{macrocode}
% range is still ahead
%    \begin{macrocode}
    \int_set:Nn \l_@@_range_state_int { +1 }
  }
  {
    \int_set:Nn
      \l_tmpa_int
      {
        \seq_item:cn
        { \@@_ctx_var:nn { #1 } { idx_high_y_seq } }
        { #4 }
      }
    \int_set:Nn
      \l_tmpb_int
      {
        \seq_item:cn
        { \@@_ctx_var:nn { #1 } { idx_high_x_seq } }
        { #4 }
      }
    \@@_coord_gt:nnnnTF
    { #2 }
    { #3 }
    { \l_tmpa_int }
    { \l_tmpb_int }
    {
%    \end{macrocode}
% range has already passed
%    \begin{macrocode}
      \int_set:Nn \l_@@_range_state_int { -1 }
    }
    {
      \int_set:Nn \l_@@_range_state_int { 0 }
    }
  }
}
%    \end{macrocode}
% \end{fn}
%
% \begin{fn}{\@@_match_rule_addr:nnnn}
% Match an address rule against the current item.
% \begin{sideeffects}
%   \sclobber & \sdir & \texttt{l\_tmpa\_tl} \\
%   \sclobber & \sdir & \texttt{l\_tmpa\_int} \\
% \end{sideeffects}
% \begin{args}
%   1 & \ain & context-id \\
%   2 & \ain & address \\
%   3 & \ain & $x$ \\
%   4 & \ain & rule-id \\
%   - & \aout & \texttt{l\_@@\_range\_state\_int}
% \end{args}
%    \begin{macrocode}
\cs_new_protected:Npn
  \@@_match_rule_addr:nnnn
  #1 #2 #3 #4
{
  \tl_set:Ne
    \l_tmpa_tl
    {
      \seq_item:cn
      { \@@_ctx_var:nn { #1 } { addr_low_seq } }
      { #4 }
    }
  \int_set:Nn
    \l_tmpa_int
    {
      \seq_item:cn
      { \@@_ctx_var:nn { #1 } { idx_low_x_seq } }
      { #4 }
    }
  \@@_addr_x_lt:nnnnTF
  { #2 }
  { #3 }
  { \l_tmpa_tl }
  { \l_tmpa_int }
  {
%    \end{macrocode}
% range is still ahead
%    \begin{macrocode}
    \int_set:Nn \l_@@_range_state_int { +1 }
  }
  {
    \tl_set:Ne
      \l_tmpa_tl
      {
        \seq_item:cn
        { \@@_ctx_var:nn { #1 } { addr_high_seq } }
        { #4 }
      }
    \int_set:Nn
      \l_tmpa_int
      {
        \seq_item:cn
        { \@@_ctx_var:nn { #1 } { idx_high_x_seq } }
        { #4 }
      }
    \@@_addr_x_gt:nnnnTF
    { #2 }
    { #3 }
    { \l_tmpa_tl }
    { \l_tmpa_int }
    {
%    \end{macrocode}
% range has already passed
%    \begin{macrocode}
      \int_set:Nn \l_@@_range_state_int { -1 }
    }
    {
      \int_set:Nn \l_@@_range_state_int { 0 }
    }
  }
}
%    \end{macrocode}
% \end{fn}
%
% \subsection{Iteration State Updates}
%
% \begin{fn}{\@@_match_rule_state:NnnNn}
% Matches an entire rule against the current position/address and checks if the rule is active, still ahead or already past.
% \begin{sideeffects}
%   \sclobber & \sdir & \texttt{l\_tmpa\_tl} \\
% \end{sideeffects}
% \begin{args}
%   1 & \ain & context-id \\
%   2 & \ain & $y$ \\
%   3 & \ain & $x$ \\
%   4 & \ain & address \\
%   5 & \ain & rule-id \\
%   - & \aout & \texttt{l\_@@\_range\_state\_int}
% \end{args}
%    \begin{macrocode}
\cs_new_protected:Npn
  \@@_match_rule_state:NnnNn
  #1 #2 #3 #4 #5
{
  \tl_set:Ne
    \l_tmpa_tl
    {
      \seq_item:cn
      { \@@_ctx_var:nn { #1 } { kind_seq } }
      { #5 }
    }
  \str_case:VnF \l_tmpa_tl
    {
      { gap }
      {
%    \end{macrocode}
% range already \enquote{passed} (in a sense this range/rule's processing is already done)
%    \begin{macrocode}
        \int_set:Nn \l_@@_range_state_int { -1 }
      }
      { addr }
      {
        \@@_match_rule_addr:nnnn
        { #1 }
        { #4 }
        { #3 }
        { #5 }
      }
      { idx }
      {
        \@@_match_rule_idx:nnnn
        { #1 }
        { #2 }
        { #3 }
        { #5 }
      }
    }
    {
      \msg_critical:nnV
      { hexdumptikz-selector }
      { invalid-rule }
      \l_tmpa_tl
    }
}
%    \end{macrocode}
% \end{fn}
%
% \begin{fn}{\@@_match_add_active_rules:NnnNN}
% Search linearly for rules which should become active and add them to the context's \texttt{active\_rules\_seq}.
% Note the search starts at \texttt{next\_rule\_int} so the rules must be in ascending order.
% \begin{sideeffects}
%   \sclobber & \sdir & \texttt{l\_tmpa\_tl} \\
%   \sclobber & \sdir & \texttt{l\_@@\_match\_loop\_bool} \\
% \end{sideeffects}
% \begin{args}
%   1 & \ain & context-id \\
%   2 & \ain & $y$ \\
%   3 & \ain & $x$ \\
%   4 & \ain & address \\
%   5 & \ain & bool whether all rules should be added or only a single one \\
% \end{args}
%    \begin{macrocode}
\cs_new_protected:Npn
  \@@_match_add_active_rules:NnnNN
  #1 #2 #3 #4 #5
{
%    \end{macrocode}
% add next rules that already started
%    \begin{macrocode}
  \bool_set_true:N \l_@@_match_loop_bool
  \bool_while_do:Nn \l_@@_match_loop_bool
  {
    \int_compare:nNnTF
      {
        \int_use:c
        { \@@_ctx_var:nn { #1 } { next_rule_int } }
      }
      >
      {
        \seq_count:c
        { \@@_ctx_var:nn { #1 } { kind_seq } }
      }
      {
        \bool_set_false:N
        \l_@@_match_loop_bool
      }
      {
        \tl_set:Ne
          \l_tmpa_tl
          {
            \seq_item:cn
              { \@@_ctx_var:nn { #1 } { kind_seq } }
              {
                \int_use:c
                {
                  \@@_ctx_var:nn
                  { #1 }
                  { next_rule_int }
                }
              }
          }
        \str_case:VnF \l_tmpa_tl
          {
            { gap }
              {
%    \end{macrocode}
% gaps are not active rules; just skip them here
%    \begin{macrocode}
                \int_gincr:c
                  {
                    \@@_ctx_var:nn
                    { #1 }
                    { next_rule_int }
                  }
                \int_gincr:c
                  {
                    \@@_ctx_var:nn
                    { #1 }
                    { gap_cnt_int }
                  }
              }
          }
          {
            \int_set:Nn
              \l_@@_rule_id_int
              {
                \int_use:c
                {
                  \@@_ctx_var:nn
                  { #1 }
                  { next_rule_int }
                }
              }

            \@@_match_rule_state:NnnNn
              #1
              { #2 }
              { #3 }
              #4
              { \l_@@_rule_id_int }
            \int_case:nn { \l_@@_range_state_int }
            {
              { -1 }
              {
%    \end{macrocode}
% range already passed $\to$ continue searching
%    \begin{macrocode}
                \int_gincr:c
                  {
                    \@@_ctx_var:nn
                    { #1 }
                    { next_rule_int }
                  }
              }
              { 0 }
              {
%    \end{macrocode}
% range is active $\to$ add and continue searching
%    \begin{macrocode}
                \seq_gput_right:ce
                  {
                    \@@_ctx_var:nn
                    { #1 }
                    { active_rules_seq }
                  }
                  { \int_use:N \l_@@_rule_id_int }
                \bool_if:NTF #5
                {
%    \end{macrocode}
% continue searching
%    \begin{macrocode}
                  \int_gincr:c
                    {
                      \@@_ctx_var:nn
                      { #1 }
                      { next_rule_int }
                    }
                }
                {
%    \end{macrocode}
% stop after first found
%    \begin{macrocode}
                  \int_gincr:c
                    {
                      \@@_ctx_var:nn
                      { #1 }
                      { next_rule_int }
                    }
                  \bool_set_false:N
                    \l_@@_match_loop_bool
                }
              }
              { +1 }
              {
%    \end{macrocode}
% range is still ahead $\to$ stop
%    \begin{macrocode}
                \bool_set_false:N
                  \l_@@_match_loop_bool
              }
            }
          }
      }
  }
}
%    \end{macrocode}
% \end{fn}
%
% \begin{fn}{\@@_match_update_active_rules:NnnN}
% Update the set of currently active rules in the context.
% This means rules which are not valid anymore get removed and newly activated rules get searched for and get added.
% \begin{sideeffects}
%   \sclobber & \sdir & \texttt{l\_@@\_active\_tmp\_seq} \\
% \end{sideeffects}
% \begin{args}
%   1 & \ain & context-id \\
%   2 & \ain & $y$ \\
%   3 & \ain & $x$ \\
%   4 & \ain & address \\
% \end{args}
%    \begin{macrocode}
\cs_new_protected:Npn
  \@@_match_update_active_rules:NnnN
  #1 #2 #3 #4
{
  \seq_clear:N \l_@@_active_tmp_seq
%    \end{macrocode}
%
% remove rules that are no longer active
%
% TOOD: can this be done in a more performant way? (than by copying the sequence over)
%    \begin{macrocode}
  \seq_map_inline:cn
    { \@@_ctx_var:nn { #1 } { active_rules_seq } }
    {
      \@@_match_rule_state:NnnNn
        #1
        { #2 }
        { #3 }
        #4
        { ##1 }
      \int_compare:nNnT
      { \l_@@_range_state_int }
      =
      { 0 }
      {
        \seq_put_right:Nn
          \l_@@_active_tmp_seq
          { ##1 }
      }
    }
  \seq_gset_eq:cN
    { \@@_ctx_var:nn { #1 } { active_rules_seq } }
    \l_@@_active_tmp_seq
%    \end{macrocode}
% search for rules which shall be added and add them
%    \begin{macrocode}
  \bool_set_true:N \l_@@_search_all_bool
  \@@_match_add_active_rules:NnnNN
    #1
    { #2 }
    { #3 }
    #4
    \l_@@_search_all_bool
}
%    \end{macrocode}
% \end{fn}
%
% \begin{fn}{\@@_match_update_active_rules_fastpath:NnnN}
% fast-path when only one element in the active set is allowed
% \begin{sideeffects}
%   \sclobber & \sdir & \texttt{l\_@@\_search\_all\_bool} \\
% \end{sideeffects}
% \begin{args}
%   1 & \ain & context-id \\
%   2 & \ain & $y$ \\
%   3 & \ain & $x$ \\
%   4 & \ain & address \\
% \end{args}
%    \begin{macrocode}
\cs_new_protected:Npn
  \@@_match_update_active_rules_fastpath:NnnN
  #1 #2 #3 #4
{
%    \end{macrocode}
% check whether the current rule shall be removed
%    \begin{macrocode}
  \int_compare:nNnT
  {
    \seq_count:c
    { \@@_ctx_var:nn { #1 } { active_rules_seq } }
  }
  >
  { 0 }
  {
    \int_set:Nn
      \l_@@_rule_id_int
      {
        \seq_item:cn
        {
          \@@_ctx_var:nn
          { #1 }
          { active_rules_seq }
        }
        { 1 }
      }
    \@@_match_rule_state:NnnNn
      #1
      { #2 }
      { #3 }
      #4
      { \l_@@_rule_id_int }
    \int_compare:nNnF
    { \l_@@_range_state_int }
    =
    { 0 }
    {
      \seq_gclear:c
      { \@@_ctx_var:nn { #1 } { active_rules_seq } }
    }
  }
%    \end{macrocode}
% check if has to search for the next rule whose range fits, if so do it and add it
%    \begin{macrocode}
  \bool_set_false:N \l_@@_search_all_bool
  \int_compare:nNnF
  {
    \seq_count:c
    { \@@_ctx_var:nn { #1 } { active_rules_seq } }
  }
  >
  { 0 }
  {
    \@@_match_add_active_rules:NnnNN
    #1
    { #2 }
    { #3 }
    #4
    \l_@@_search_all_bool
  }
}
%    \end{macrocode}
% \end{fn}
%
% \subsection{Public}
%
% \begin{fn}{\hexdumptikz_selector_match_styles:NnnN}
% Match the current position/address against the list of rules (adjusts the context's iterator output state).
% Intended when the selector specifies a style mapping.
% This supports overlapping ranges.
% Still the rules needs to be in ascending order, sorted by the start of the respective range.
% \begin{sideeffects}
% \end{sideeffects}
% \begin{args}
%   1 & \ain & context-id \\
%   2 & \ain & $y$ \\
%   3 & \ain & $x$ \\
%   4 & \ain & address \\
% \end{args}
%    \begin{macrocode}
\cs_new_protected:Npn \hexdumptikz_selector_match_styles:NnnN #1 #2 #3 #4
{
%    \end{macrocode}
% First clean the iterator output
%    \begin{macrocode}
  \hexdumptikz_selector_ctx_clear_iter_out:N #1
%    \end{macrocode}
% Update the set of active rules (remove and add if needed)
%    \begin{macrocode}
  \@@_match_update_active_rules:NnnN
  #1
  { #2 }
  { #3 }
  #4
%    \end{macrocode}
%
% collect the style which shall be applied from the set of active rules after filtering them with the corresponding predicate.
%    \begin{macrocode}
  \seq_map_inline:cn
    { \@@_ctx_var:nn { #1 } { active_rules_seq } }
    {
      \@@_match_rule_predicate:NnnNnT
      #1
      { #2 }
      { #3 }
      #4
      { ##1 }
      {
        \@@_match_apply_style:Nn #1 { ##1 }
        \bool_gset_true:c
        {
          \@@_ctx_var:nn
          { #1 }
          { show_bool }
        }
      }
    }
}
%    \end{macrocode}
% \end{fn}
%
% \begin{fn}{\hexdumptikz_selector_match_rows:NnnN}
% Match the current position/address against the list of rules (adjusts the context's iterator output state).
% Intended when the selector is a raw specification of the selection.
% This does NOT support overlapping ranges.
% The rules needs to be in ascending order, sorted by the start of the respective range.
% \begin{sideeffects}
% \end{sideeffects}
% \begin{args}
%   1 & \ain & context-id \\
%   2 & \ain & $y$ \\
%   3 & \ain & $x$ \\
%   4 & \ain & address \\
% \end{args}
%    \begin{macrocode}
\cs_new_protected:Npn \hexdumptikz_selector_match_rows:NnnN #1 #2 #3 #4
{
%    \end{macrocode}
% First clean the iterator output
%    \begin{macrocode}
  \hexdumptikz_selector_ctx_clear_iter_out:N #1
%    \end{macrocode}
% Update the set of active rules (remove and add if needed)
% Use the fast-path since this needs to only support a single active rule/range.
%    \begin{macrocode}
  \@@_match_update_active_rules_fastpath:NnnN
  #1
  { #2 }
  { #3 }
  #4
%    \end{macrocode}
%
% check if all rules are already passed $\to$ caller might want to stop at this point
%    \begin{macrocode}
  \seq_if_empty:cTF
  { \@@_ctx_var:nn { #1 } { active_rules_seq } }
  {
    \int_compare:nNnT
      {
        \int_use:c
        {
          \@@_ctx_var:nn
          { #1 }
          { next_rule_int }
        }
      }
      >
      {
        \seq_count:c
        { \@@_ctx_var:nn { #1 } { kind_seq } }
      }
      {
        \bool_gset_true:c
        {
          \@@_ctx_var:nn
          { #1 }
          { finished_bool }
        }
      }
  }
  {
%    \end{macrocode}
% no overlapping rules are supported, so we can use a fast-path here and just work with the single active rule
%    \begin{macrocode}
    \int_set:Nn
      \l_@@_rule_id_int
      {
        \seq_item:cn
          {
            \@@_ctx_var:nn
            { #1 }
            { active_rules_seq }
          }
          { 1 }
      }
    \@@_match_rule_predicate:NnnNnTF
    #1
    { #2 }
    { #3 }
    #4
    { \l_@@_rule_id_int }
    {
      \bool_gset_true:c
      {
        \@@_ctx_var:nn
        { #1 }
        { show_bool }
      }
    }
    {
      \bool_gset_false:c
      {
        \@@_ctx_var:nn
        { #1 }
        { show_bool }
      }
    }
  }
}
%    \end{macrocode}
% \iffalse
%</package>
% \fi
%
% \Finale
