% updatemarks-nums.sty 
% Copyright 2024-2025 Wenjian Chern.
%
% 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 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 2005/12/01 or later.
%
% This work has the LPPL maintenance status `maintained'.
% 
% The Current Maintainer of this work is Wenjian Chern.
%
% This work consists of the files updatemarks-nums.sty
%
\@ifundefined{ProvidesExplPackage}{\RequirePackage{expl3}}
\ProvidesExplPackage{updatemarks-nums}{2024/02/19}{v0.1}{parse number range}

\tl_new:N \l__parserange_tmpa_tl
\tl_new:N \l__parserange_tmpb_tl
\seq_new:N \l__parserange_tmp_seq
\int_new:N \l__parserange_begin_int
\int_new:N \l__parserange_end_int
\int_new:N \l__parserange_max_int
\int_new:N \l__parserange_min_int
\bool_new:N \l__parserange_invalid_bool
\quark_new:N \q__parserange
\cs_new_protected:Npn \parserange_check:
  {
    \cs_set_eq:NN \__parserange_single:n \__parserange_single_check:n
    \cs_set_eq:NN \__parserange_range: \__parserange_range_check:
  }
\cs_new_protected:Npn \parserange_nocheck:
  {
    \cs_set_eq:NN \__parserange_single:n \__parserange_single_nocheck:n
    \cs_set_eq:NN \__parserange_range: \__parserange_range_nocheck:
  }
\cs_new_protected:Npn \parserange:nnnN #1#2#3#4
  {
    \seq_set_eq:NN #4 \c_empty_seq
    \int_set:Nn \l__parserange_min_int { \__parserange_to_int:n {#1} }
    \int_set:Nn \l__parserange_max_int { \__parserange_to_int:n {#2} }
    \clist_map_inline:nn {#3}
      {
        \__parserange_aux:n {##1}
        \bool_if:NF \l__parserange_invalid_bool
          { \seq_concat:NNN #4 #4 \l__parserange_tmp_seq }
      }
  }
\cs_generate_variant:Nn \parserange:nnnN { nnvN, nneN }
\cs_new_protected:Npn \parserange:nnN
  { \parserange:nnnN { 1 } }
\cs_generate_variant:Nn \parserange:nnN { nvN, neN }
\cs_new_protected:Npn \__parserange_aux:n #1
  {
    \bool_set_false:N \l__parserange_invalid_bool
    \seq_clear:N \l__parserange_tmp_seq
    \__parserange_action:nnn 
      {#1}
      { \__parserange_single:n {#1} }
      {
        \tl_if_empty:NTF \l__parserange_tmpa_tl
          { \int_set_eq:NN \l__parserange_begin_int \l__parserange_min_int }
          { 
            \int_set:Nn \l__parserange_begin_int 
              { \exp_args:No \__parserange_to_int:n \l__parserange_tmpa_tl } 
          }
        \tl_if_empty:NTF \l__parserange_tmpb_tl
          { \int_set_eq:NN \l__parserange_end_int \l__parserange_max_int }
          { 
            \int_set:Nn \l__parserange_end_int 
              { \exp_args:No \__parserange_to_int:n \l__parserange_tmpb_tl } 
          }
        \__parserange_range:
      }
  }
\cs_new_protected:Npn \parserange_use_delimiter:n #1
  {
    \tl_if_empty:nTF {#1}
      {
        \cs_set:Npn \__parserange_action:nnn ##1
          { \__parserange_aux:w ##1 \prg_do_nothing: \q_nil \q__parserange }
        \cs_set:Npn \__parserange_aux:w ##1##2 ##3 \q__parserange
          {
            \tl_set:Nx \l__parserange_tmpa_tl { \tl_trim_spaces:n {##1} }
            \tl_set:Nx \l__parserange_tmpa_tl { \tl_trim_spaces:n {##2} }
            \quark_if_nil:nTF {##3}
          }
      }
      {
        \cs_set:Npn \__parserange_action:nnn ##1
          { \__parserange_aux:w ##1 #1 #1 \q__parserange }
        \cs_set:Npn \__parserange_aux:w ##1 #1 ##2 #1 ##3 \q__parserange
          {
            \tl_set:Nx \l__parserange_tmpa_tl { \tl_trim_spaces:n {##1} }
            \tl_set:Nx \l__parserange_tmpb_tl { \tl_trim_spaces:n {##2} }
            \tl_if_blank:nTF {##3}
          }
      }
  }
\cs_new_protected:Npn \parserange_set_to_int:n 
  { \cs_set:Npn \__parserange_to_int:n ##1 }
\parserange_set_to_int:n {#1}
\cs_new_protected:Npn \parserange_use_default_delimiter: 
  {
    \cs_set:Npn \__parserange_action:nnn ##1
      { \__parserange_aux:w ##1 -> -> \q__parserange }
    \cs_set:Npn \__parserange_aux:w ##1 -> ##2 -> ##3 \q__parserange
      {
        \tl_set:Nx \l__parserange_tmpa_tl { \tl_trim_spaces:n {##1} }
        \tl_set:Nx \l__parserange_tmpb_tl { \tl_trim_spaces:n {##2} }
        \tl_if_blank:nTF {##3} { \use_i:nn } { \use_ii:nn }
      }
  }
\parserange_use_default_delimiter: % \parserange_use_delimiter:n {->}
\cs_new:Npn \__parserange_single_check:n #1
  {
    \bool_lazy_or:nnTF
      { \int_compare_p:nNn {#1} > \l__parserange_max_int }
      { \int_compare_p:nNn {#1} < \l__parserange_min_int }
      { \bool_set_true:N \l__parserange_invalid_bool }
      { 
        \seq_put_right:Nx \l__parserange_tmp_seq 
          { \int_eval:n { \__parserange_to_int:n {#1} } } 
      }
  }
\cs_new:Npn \__parserange_single_nocheck:n #1
  {
    \seq_put_right:Nx \l__parserange_tmp_seq 
      { \int_eval:n { \__parserange_to_int:n {#1} } } 
  }
\cs_new_eq:NN \__parserange_single:n \__parserange_single_check:n
\cs_new:Npn \__parserange_range_check:
  {
    \int_compare:nNnT \l__parserange_begin_int < \l__parserange_min_int
      { \int_set_eq:NN \l__parserange_begin_int \l__parserange_min_int }
    \int_compare:nNnT \l__parserange_end_int > \l__parserange_max_int
      { \int_set_eq:NN \l__parserange_end_int \l__parserange_max_int }
    \bool_lazy_or:nnTF
      { \int_compare_p:nNn \l__parserange_begin_int > \l__parserange_max_int }
      { \int_compare_p:nNn \l__parserange_begin_int > \l__parserange_end_int }
      { \bool_set_true:N \l__parserange_invalid_bool }
      {
        \int_step_inline:nnn
          { \l__parserange_begin_int } { \l__parserange_end_int }
          { \seq_put_right:Nn \l__parserange_tmp_seq {##1} }
      }
  }
\cs_new:Npn \__parserange_range_nocheck:
  {
    \int_compare:nNnTF \l__parserange_begin_int > \l__parserange_end_int
      { \bool_set_true:N \l__parserange_invalid_bool }
      {
        \int_step_inline:nnn
          { \l__parserange_begin_int } { \l__parserange_end_int }
          { \seq_put_right:Nn \l__parserange_tmp_seq {##1} }
      }
  }
\cs_new_eq:NN \__parserange_range: \__parserange_range_check:
