%%% % -*- coding: utf-8 -*-
%%% ----------------------------------------------------------------------------
%%% Tabularray: Typeset tabulars and arrays with LaTeX3
%%% Author    : Jianrui Lyu <tolvjr@163.com>
%%% Repository: https://github.com/lvjr/tabularray
%%% License   : The LaTeX Project Public License 1.3
%%% ----------------------------------------------------------------------------

%%% --------------------------------------------------------
%%  \section{Scratch Variables and Data Structures}
%%% --------------------------------------------------------

\NeedsTeXFormat{LaTeX2e}
\RequirePackage{expl3}
\ProvidesExplPackage{tabularray}{2021-05-13}{2021H}
  {Typeset tabulars and arrays with LaTeX3}

%\RequirePackage{xparse}
\AtBeginDocument{\@ifpackageloaded{xcolor}{\RequirePackage{ninecolors}}{}}

\ExplSyntaxOn

\cs_if_exist:NF \vbox_set_top_to_ht:Nnn
  {
    \cs_new_protected:Npn \vbox_set_top_to_ht:Nnn #1 #2 #3
      {
        \tex_setbox:D #1 \tex_vtop:D to \dim_eval:n { #2 }
          { \color_group_begin: #3 \par \color_group_end: }
      }
 }

\cs_generate_variant:Nn \msg_error:nnnn { nnVn }
\cs_generate_variant:Nn \prop_item:Nn { Ne, NV }
\cs_generate_variant:Nn \prop_put:Nnn { Nxn, Nxx, NxV }
\cs_generate_variant:Nn \regex_replace_all:NnN { NVN }
\cs_generate_variant:Nn \seq_map_indexed_inline:Nn { cn }
\cs_generate_variant:Nn \tl_gput_right:Nn { Nf }
\prg_generate_conditional_variant:Nnn \clist_if_in:Nn { Nx } { TF }
\prg_generate_conditional_variant:Nnn \prop_if_in:Nn { c } { T }
\prg_generate_conditional_variant:Nnn \str_if_eq:nn { xn } { TF }
\prg_generate_conditional_variant:Nnn \tl_if_eq:nn { en } { T, TF }
\prg_generate_conditional_variant:Nnn \tl_if_head_eq_meaning:nN { VN } { T, TF }

\tl_new:N  \l__tblr_a_tl
\tl_new:N  \l__tblr_b_tl
\tl_new:N  \l__tblr_c_tl
\tl_new:N  \l__tblr_d_tl
\tl_new:N  \l__tblr_e_tl
\tl_new:N  \l__tblr_f_tl
\tl_new:N  \l__tblr_h_tl
\tl_new:N  \l__tblr_i_tl  % for row index
\tl_new:N  \l__tblr_j_tl  % for column index
\tl_new:N  \l__tblr_k_tl
\tl_new:N  \l__tblr_n_tl
\tl_new:N  \l__tblr_o_tl
\tl_new:N  \l__tblr_r_tl
\tl_new:N  \l__tblr_s_tl
\tl_new:N  \l__tblr_t_tl
\tl_new:N  \l__tblr_u_tl
\tl_new:N  \l__tblr_v_tl
\tl_new:N  \l__tblr_w_tl
\tl_new:N  \l__tblr_x_tl
\tl_new:N  \l__tblr_y_tl
\int_new:N \l__tblr_a_int
\int_new:N \l__tblr_c_int % for column number
\int_new:N \l__tblr_r_int % for row number
\dim_new:N \l__tblr_d_dim % for depth
\dim_new:N \l__tblr_h_dim % for height
\dim_new:N \l__tblr_o_dim
\dim_new:N \l__tblr_p_dim
\dim_new:N \l__tblr_q_dim
\dim_new:N \l__tblr_r_dim
\dim_new:N \l__tblr_s_dim
\dim_new:N \l__tblr_t_dim
\dim_new:N \l__tblr_v_dim
\dim_new:N \l__tblr_w_dim % for width
\box_new:N \l__tblr_a_box
\box_new:N \l__tblr_b_box
\box_new:N \l__tblr_c_box % for cell box
\box_new:N \l__tblr_d_box

\int_new:N \g_tblr_level_int % store table nesting level

\cs_new_protected:Npn \__tblr_prop_gput:nnn #1 #2 #3
  {
    \prop_gput:cnn
      { g_tblr_#1_ \int_use:N \g_tblr_level_int _prop } { #2 } { #3 }
  }
\cs_generate_variant:Nn \__tblr_prop_gput:nnn { nnx, nnV, nxn, nxx, nxV }

\cs_new:Npn \__tblr_prop_item:nn #1 #2
  {
    \prop_item:cn { g_tblr_#1_ \int_use:N \g_tblr_level_int _prop } { #2 }
  }
\cs_generate_variant:Nn \__tblr_prop_item:nn { ne }

\cs_new_protected:Npn \__tblr_prop_if_in:nnT #1
  {
    \prop_if_in:cnT { g_tblr_#1_ \int_use:N \g_tblr_level_int _prop }
  }
\cs_new_protected:Npn \__tblr_prop_if_in:nnF #1
  {
    \prop_if_in:cnF { g_tblr_#1_ \int_use:N \g_tblr_level_int _prop }
  }
\cs_new_protected:Npn \__tblr_prop_if_in:nnTF #1
  {
    \prop_if_in:cnTF { g_tblr_#1_ \int_use:N \g_tblr_level_int _prop }
  }
\prg_generate_conditional_variant:Nnn \__tblr_prop_if_in:nn { nx } { T, F, TF }

\cs_new_protected:Npn \__tblr_prop_log:n #1
  {
    \prop_log:c { g_tblr_#1_ \int_use:N \g_tblr_level_int _prop }
  }

\cs_new_protected:Npn \__tblr_prop_map_inline:nn #1 #2
  {
    \prop_map_inline:cn { g_tblr_#1_ \int_use:N \g_tblr_level_int _prop } {#2}
  }

\cs_new_protected:Npn \__tblr_prop_gput_if_larger:nnn #1 #2 #3
  {
    \__tblr_gput_if_larger:cnn
      { g_tblr_#1_ \int_use:N \g_tblr_level_int _prop } { #2 } { #3 }
  }
\cs_generate_variant:Nn \__tblr_prop_gput_if_larger:nnn { nnx, nnV, nxn, nxx, nxV }

\cs_new_protected:Npn \__tblr_prop_gadd_dimen_value:nnn #1 #2 #3
  {
    \__tblr_gadd_dimen_value:cnn
      { g_tblr_#1_ \int_use:N \g_tblr_level_int _prop } { #2 } { #3 }
  }
\cs_generate_variant:Nn \__tblr_prop_gadd_dimen_value:nnn { nnx, nnV, nxn, nxx }

%% Put the dimension to the prop list only if it's larger than the old one

\tl_new:N \l__tblr_put_if_larger_tl

\cs_new_protected:Npn \__tblr_put_if_larger:Nnn #1 #2 #3
  {
    \tl_set:Nx \l__tblr_put_if_larger_tl { \prop_item:Nn #1 { #2 } }
    \bool_lazy_or:nnT
      { \tl_if_empty_p:N \l__tblr_put_if_larger_tl }
      { \dim_compare_p:nNn { #3 } > { \l__tblr_put_if_larger_tl } }
      { \prop_put:Nnn #1 { #2 } { #3 }  }
  }
\cs_generate_variant:Nn \__tblr_put_if_larger:Nnn { Nnx, Nxn, Nxx }

\cs_new_protected:Npn \__tblr_gput_if_larger:Nnn #1 #2 #3
  {
    \tl_set:Nx \l__tblr_put_if_larger_tl { \prop_item:Nn #1 { #2 } }
    \bool_lazy_or:nnT
      { \tl_if_empty_p:N \l__tblr_put_if_larger_tl }
      { \dim_compare_p:nNn { #3 } > { \l__tblr_put_if_larger_tl } }
      { \prop_gput:Nnn #1 { #2 } { #3 }  }
  }
\cs_generate_variant:Nn \__tblr_gput_if_larger:Nnn { Nnx, Nxn, Nxx, cnn }

%% Add the dimension to some key value of the prop list
%% #1: the prop list, #2: the key, #3: the dimen to add

\cs_new_protected:Npn \__tblr_add_dimen_value:Nnn #1 #2 #3
  {
    \prop_put:Nnx #1 { #2 } { \dim_eval:n { \prop_item:Nn #1 { #2 } + #3 } }
  }
\cs_generate_variant:Nn \__tblr_add_dimen_value:Nnn { cnn }

\cs_new_protected:Npn \__tblr_gadd_dimen_value:Nnn #1 #2 #3
  {
    \prop_gput:Nnx #1 { #2 } { \dim_eval:n { \prop_item:Nn #1 { #2 } + #3 } }
  }
\cs_generate_variant:Nn \__tblr_gadd_dimen_value:Nnn { cnn }

%% Some counters for row and column numbering
\newcounter{rownum}
\newcounter{colnum}
\newcounter{rowcount}
\newcounter{colcount}

%%% --------------------------------------------------------
%%  \section{Child Selectors}
%%% --------------------------------------------------------

\clist_new:N \g_tblr_used_child_selectors_clist

\tl_new:N \l__tblr_childs_arg_spec_tl

\msg_new:nnn { tabularray } { used-child-selector }
  { Child ~ selector ~ name ~ "#1" ~ has ~ been ~ used! }

\NewDocumentCommand \NewChildSelector { m O{0} o m }
  {
    \__tblr_new_child_selector_aux:xnnn { \tl_trim_spaces:n {#1} } {#2} {#3} {#4}
  }

\cs_new_protected:Npn \__tblr_new_child_selector_aux:nnnn #1 #2 #3 #4
  {
    \clist_if_in:NnTF \g_tblr_used_child_selectors_clist { #1 }
      {
        \msg_error:nnn { tabularray } { used-child-selector } { #1 }
        \clist_log:N \g_tblr_used_child_selectors_clist
      }
      {
        \__tblr_make_xparse_arg_spec:nnN { #2 } { #3 } \l__tblr_childs_arg_spec_tl
        \exp_args:NcV \NewDocumentCommand
          { __tblr_child_selector_ #1 :w } \l__tblr_childs_arg_spec_tl { #4 }
        \clist_gput_right:Nn \g_tblr_used_child_selectors_clist { #1 }
      }
  }
\cs_generate_variant:Nn \__tblr_new_child_selector_aux:nnnn { xnnn }

%% #1: argument number, #2: optional argument default, #3: result tl
\cs_new_protected:Npn \__tblr_make_xparse_arg_spec:nnN #1 #2 #3
  {
    \tl_clear:N #3
    \int_compare:nNnT { #1 } > { 0 }
      {
        \IfValueTF { #2 }
          { \tl_set:Nn #3 { O{#2} } }
          { \tl_set:Nn #3 { m } }
        \tl_put_right:Nx #3 { \prg_replicate:nn { #1 - 1 } { m } }
      }
  }

\clist_new:N \l_tblr_childs_clist
\tl_new:N \l_tblr_childs_total_tl

\NewChildSelector { odd }
  {
    \int_step_inline:nnnn {1} {2} { \l_tblr_childs_total_tl }
      { \clist_put_right:Nn \l_tblr_childs_clist {##1} }
  }

\NewChildSelector { even }
  {
    \int_step_inline:nnnn {2} {2} { \l_tblr_childs_total_tl }
      { \clist_put_right:Nn \l_tblr_childs_clist {##1} }
  }

\regex_const:Nn \c__tblr_split_selector_name_regex { ^ ( [A-Za-z] {2,} ) ( . * ) }
\seq_new:N \l__tblr_childs_split_seq
\seq_new:N \l__tblr_childs_regex_seq
\tl_new:N \l__tblr_childs_end_tl
\tl_new:N \l__tblr_childs_selector_tl

%% #1, child specifications; #2, total number.
%% The result will be put into \l_tblr_childs_clist
\cs_new_protected:Npn \__tblr_get_childs:nn #1 #2
  {
    \clist_clear:N \l_tblr_childs_clist
    \tl_set:Nx \l_tblr_childs_total_tl {#2}
    \regex_extract_once:NnNTF \c__tblr_split_selector_name_regex {#1}
      \l__tblr_childs_regex_seq
      {
        \tl_set:No \l__tblr_childs_selector_tl
          {
            \cs:w
            __tblr_child_selector_ \seq_item:Nn \l__tblr_childs_regex_seq {2} :w
            \cs_end:
          }
        \exp_args:Nx \l__tblr_childs_selector_tl
          { \seq_item:Nn \l__tblr_childs_regex_seq{3} }
      }
      {
        \tl_if_eq:nnTF {#1} {-}
          { \__tblr_get_childs_normal:nn {1-#2} {#2} }
          { \__tblr_get_childs_normal:nn {#1} {#2} }
      }
    %\clist_log:N \l_tblr_childs_clist
  }
\cs_generate_variant:Nn \__tblr_get_childs:nn { nx }

\cs_new_protected:Npn \__tblr_get_childs_normal:nn #1 #2
  {
    \seq_set_split:Nnn \l__tblr_childs_split_seq {,} {#1}
    \seq_map_inline:Nn \l__tblr_childs_split_seq
      { \__tblr_get_childs_normal_aux:w ##1 - s \scan_stop }
  }

\cs_new_protected_nopar:Npn \__tblr_get_childs_normal_aux:w #1 - #2 #3 \scan_stop
  {
    \tl_if_eq:nnTF {#2} {s}
      { \tl_set:Nn \l__tblr_childs_end_tl {#1} }
      { \tl_set:Nn \l__tblr_childs_end_tl {#2} }
    \int_step_inline:nnn {#1} { \l__tblr_childs_end_tl }
      { \clist_put_right:Nn \l_tblr_childs_clist {##1} }
  }

%%% --------------------------------------------------------
%%  \section{New Table Commands}
%%% --------------------------------------------------------

%% We need some commands to modify table/row/column/cell specifications.
%% These commands must be defined with \NewTableCommand command,
%% so that we could extract them, execute them once, then disable them.

\clist_new:N \g__tblr_table_commands_clist

\msg_new:nnn { tabularray } { defined-table-command }
  { Table ~ commnad ~ #1 has ~ been ~ defined! }

\NewDocumentCommand \NewTableCommand { m O{0} o m }
  {
    \clist_if_in:NnTF \g__tblr_table_commands_clist { #1 }
      {
        \msg_error:nnn { tabularray } { defined-table-command } { #1 }
        \clist_log:N \g__tblr_table_commands_clist
      }
      {
        \__tblr_make_xparse_arg_spec:nnN { #2 } { #3 } \l__tblr_a_tl
        \exp_args:NcV \NewDocumentCommand
          { __tblr_table_command_ \cs_to_str:N #1 :w } \l__tblr_a_tl { #4 }
        \exp_args:NcV \NewDocumentCommand
          { __tblr_table_command_ \cs_to_str:N #1 _gobble :w } \l__tblr_a_tl { }
        \IfValueTF { #3 }
          {
            \tl_gset:cn { g__tblr_table_cmd_ \cs_to_str:N #1 _arg_numb_tl } {-#2}
          }
          {
            \tl_gset:cn { g__tblr_table_cmd_ \cs_to_str:N #1 _arg_numb_tl } {#2}
          }
        \clist_gput_right:Nn \g__tblr_table_commands_clist { #1 }
      }
  }

\cs_new_protected:Npn \__tblr_enable_table_commands:
  {
    \clist_map_inline:Nn \g__tblr_table_commands_clist
      { \cs_set_eq:Nc ##1 { __tblr_table_command_ \cs_to_str:N ##1 :w } }
  }

\cs_new_protected:Npn \__tblr_disable_table_commands:
  {
    \clist_map_inline:Nn \g__tblr_table_commands_clist
      { \cs_set_eq:Nc ##1 { __tblr_table_command_ \cs_to_str:N ##1 _gobble:w } }
  }

\cs_new_protected:Npn \__tblr_execute_table_commands:
  {
    \__tblr_prop_map_inline:nn { command }
      {
        \__tblr_set_row_col_from_key_name:w ##1
        ##2
      }
    \LogTblrTracing { cell }
  }

\cs_new_protected:Npn \__tblr_set_row_col_from_key_name:w [#1][#2]
  {
    \int_set:Nn \c@rownum {#1}
    \int_set:Nn \c@colnum {#2}
  }

%%% --------------------------------------------------------
%%  \section{New Dash Styles}
%%% --------------------------------------------------------

%% \NewDashStyle commands

\dim_zero_new:N \rulewidth
\dim_set:Nn \rulewidth {0.4pt}

\prop_gset_from_keyval:Nn \g__tblr_defined_hdash_styles_prop
  { solid = \hrule height \rulewidth }
\prop_gset_from_keyval:Nn \g__tblr_defined_vdash_styles_prop
  { solid = \vrule width \rulewidth }

\NewDocumentCommand \NewDashStyle { m m }
  {
    \seq_set_split:Nnn \l_tmpa_seq { ~ } {#2}
    \tl_set:Nx \l__tblr_a_tl { \seq_item:Nn \l_tmpa_seq {1} }
    \tl_set:Nx \l__tblr_b_tl { \seq_item:Nn \l_tmpa_seq {2} }
    \tl_set:Nx \l__tblr_c_tl { \seq_item:Nn \l_tmpa_seq {3} }
    \tl_set:Nx \l__tblr_d_tl { \seq_item:Nn \l_tmpa_seq {4} }
    \tl_if_eq:NnT \l__tblr_a_tl { on }
      {
        \tl_if_eq:NnT \l__tblr_c_tl { off }
          {
            \__tblr_dash_style_make_boxes:nxx {#1}
              { \dim_eval:n {\l__tblr_b_tl} } { \dim_eval:n {\l__tblr_d_tl} }
          }
      }
  }

\cs_new_protected:Npn \__tblr_dash_style_make_boxes:nnn #1 #2 #3
  {
    \dim_set:Nn \l_tmpa_dim { #2 + #3 }
    \tl_set:Nn \l__tblr_h_tl { \hbox_to_wd:nn }
    \tl_put_right:Nx \l__tblr_h_tl { { \dim_use:N \l_tmpa_dim } }
    \tl_put_right:Nn \l__tblr_h_tl
      {
        { \hss \vbox:n { \hbox_to_wd:nn {#2} {} \hrule height \rulewidth } \hss }
      }
    \prop_gput:NnV \g__tblr_defined_hdash_styles_prop {#1} \l__tblr_h_tl
    %\prop_log:N \g__tblr_defined_hdash_styles_prop
    \tl_set:Nn \l__tblr_v_tl { \vbox_to_ht:nn }
    \tl_put_right:Nx \l__tblr_v_tl { { \dim_use:N \l_tmpa_dim } }
    \tl_put_right:Nn \l__tblr_v_tl
      {
        { \vss \hbox:n { \vbox_to_ht:nn {#2} {} \vrule width \rulewidth } \vss }
      }
    \prop_gput:NnV \g__tblr_defined_vdash_styles_prop {#1} \l__tblr_v_tl
    %\prop_log:N \g__tblr_defined_vdash_styles_prop
  }
\cs_generate_variant:Nn \__tblr_dash_style_make_boxes:nnn { nxx }

\cs_new_protected:Npn \__tblr_get_hline_dash_style:N #1
  {
    \tl_set:Nx \l_tmpa_tl
      { \prop_item:NV \g__tblr_defined_hdash_styles_prop #1 }
    \tl_if_empty:NF \l_tmpa_tl { \tl_set_eq:NN #1 \l_tmpa_tl }
  }

\cs_new_protected:Npn \__tblr_get_vline_dash_style:N #1
  {
    \tl_set:Nx \l_tmpa_tl
      { \prop_item:NV \g__tblr_defined_vdash_styles_prop #1 }
    \tl_if_empty:NF \l_tmpa_tl { \tl_set_eq:NN #1 \l_tmpa_tl }
  }

\NewDashStyle {dashed} {on ~ 2pt ~ off ~ 2pt}
\NewDashStyle {dotted} {on ~ 0.4pt ~ off ~ 1pt}

%%% --------------------------------------------------------
%%  \section{Set Hlines and Vlines}
%%% --------------------------------------------------------

\tl_set:Nn \@tblr@dash { dash }
\tl_set:Nn \@tblr@text { text }

\regex_const:Nn \c__tblr_is_color_key_regex { ^[A-Za-z] }

%% \SetHlines command for setting every hline in the table
\NewTableCommand \SetHlines [3] [+]
  {
    \tblr_set_every_hline:nnn {#1} {#2} {#3}
  }

%% We put all code inside a group to avoid affecting other table commands
\cs_new_protected:Npn \tblr_set_every_hline:nnn #1 #2 #3
  {
    \group_begin:
    \int_step_inline:nn { \int_eval:n { \c@rowcount + 1 } }
      {
        \int_set:Nn \c@rownum {##1}
        \tblr_set_hline:nnn {#1} {#2} {#3}
      }
    \group_end:
  }

%% Check the number of arguments and call \tblr_set_every_hline in different ways
%% This function is called when parsing table specifications
\cs_new_protected:Npn \__tblr_set_every_hline_aux:n #1
  {
    \tl_if_head_is_group:nTF {#1}
      {
        \int_compare:nNnTF { \tl_count:n {#1} } = {3}
          { \tblr_set_every_hline:nnn #1 }
          { \tblr_set_every_hline:nnn {1} #1 }
      }
      { \tblr_set_every_hline:nnn {1} {-} {#1} }
  }

%% Add \SetHline, \hline and \cline commands

\tl_new:N \l__tblr_hline_count_tl % the count of all hlines
\tl_new:N \l__tblr_hline_num_tl   % the index of the hline
\tl_new:N \l__tblr_hline_cols_tl  % the columns of the hline
\tl_new:N \l__tblr_hline_dash_tl  % dash style
\tl_new:N \l__tblr_hline_fg_tl    % dash foreground
\tl_new:N \l__tblr_hline_wd_tl    % dash width

\NewTableCommand \cline [2] [] { \SetHline [=] {#2} {#1} }

\NewTableCommand \hline [1] [] { \SetHline [+] {-} {#1} }

%% #1: the index of the hline (may be + or =)
%% #2: which columns of the hline, separate by commas
%% #3: key=value pairs
\NewTableCommand \SetHline [3] [+]
  {
    \tblr_set_hline:nnn {#1} {#2} {#3}
  }

%% We need to check "text" key first
%% If it does exist and has empty value, then do nothing
\cs_new_protected:Npn \tblr_set_hline:nnn #1 #2 #3
  {
    \group_begin:
    \keys_set_groups:nnn { tblr-hline } { text } {#3}
    \tl_if_eq:NnF \l__tblr_hline_dash_tl { \exp_not:N \@tblr@text }
      {
        \__tblr_set_hline_num:n {#1}
        \tl_clear:N \l__tblr_hline_dash_tl
        \keys_set:nn { tblr-hline } { dash = solid, #3 }
        \__tblr_set_hline_cmd:n {#2}
      }
    \group_end:
  }

\cs_new_protected:Npn \tblr_set_hline:nnnn #1 #2 #3 #4
  {
    \group_begin:
    \__tblr_get_childs:nx {#1} { \int_eval:n { \c@rowcount + 1 } }
    \clist_map_inline:Nn \l_tblr_childs_clist
      {
        \int_set:Nn \c@rownum {##1}
        \tblr_set_hline:nnn {#2} {#3} {#4}
      }
    \group_end:
  }

%% Check the number of arguments and call \tblr_set_hline in different ways
%% Note that #1 always includes an outer pair of braces
%% This function is called when parsing table specifications
\cs_new_protected:Npn \__tblr_set_hline_aux:nn #1 #2
  {
    \tl_if_head_is_group:nTF {#2}
      {
        \int_compare:nNnTF { \tl_count:n {#2} } = {3}
          { \tblr_set_hline:nnnn #1 #2 }
          { \tblr_set_hline:nnnn #1 {1} #2 }
      }
      { \tblr_set_hline:nnnn #1 {1} {-} {#2} }
  }
\cs_generate_variant:Nn \__tblr_set_hline_aux:nn { Vn }

%% #1: the index of hline to set (may be + or =)
\cs_new_protected:Npn \__tblr_set_hline_num:n #1
  {
    \tl_clear:N \l__tblr_hline_num_tl
    \tl_set:Nx \l__tblr_hline_count_tl
      { \__tblr_prop_item:ne { hline } { [\int_use:N \c@rownum] / @hline-count } }
    \tl_if_empty:NTF \l__tblr_hline_count_tl
      {
        \tl_set:Nx \l__tblr_hline_num_tl { 1 }
        \__tblr_prop_gput:nxx { hline }
          { [\int_use:N \c@rownum] / @hline-count } { 1 }
      }
      {
        \tl_if_eq:nnTF {#1} {+}
          { \__tblr_set_hline_num_incr: }
          {
            \tl_if_eq:nnTF {#1} {=}
              { \tl_set_eq:NN \l__tblr_hline_num_tl \l__tblr_hline_count_tl }
              {
                \int_compare:nNnTF {#1} > { \l__tblr_hline_count_tl }
                  { \__tblr_set_hline_num_incr: }
                  { \tl_set:Nn \l__tblr_hline_num_tl {#1} }
              }
          }
      }
  }

\cs_new_protected:Npn \__tblr_set_hline_num_incr:
  {
    \tl_set:Nx \l__tblr_hline_count_tl
      { \int_eval:n { \l__tblr_hline_count_tl + 1 } }
    \__tblr_prop_gput:nxx { hline }
      { [\int_use:N \c@rownum] / @hline-count } { \l__tblr_hline_count_tl }
    \tl_set_eq:NN \l__tblr_hline_num_tl \l__tblr_hline_count_tl
  }

\keys_define:nn { tblr-hline }
  {
    dash .code:n = \tl_set:Nn \l__tblr_hline_dash_tl { \exp_not:N \@tblr@dash #1 },
    text .code:n = \tl_set:Nn \l__tblr_hline_dash_tl { \exp_not:N \@tblr@text #1 },
    text .groups:n = { text },
    wd .code:n = \tl_set:Nn \l__tblr_hline_wd_tl { \dim_eval:n {#1} },
    fg .code:n = \tl_set:Nn \l__tblr_hline_fg_tl {#1},
    baseline .code:n = \__tblr_hline_set_baseline:n {#1},
    unknown .code:n = \__tblr_hline_unknown_key:V \l_keys_key_str,
  }

\cs_new_protected:Npn \__tblr_hline_unknown_key:n #1
  {
    \prop_if_in:NnTF \g__tblr_defined_hdash_styles_prop {#1}
      { \tl_set:Nn \l__tblr_hline_dash_tl { \exp_not:N \@tblr@dash #1 } }
      {
        \regex_match:NnTF \c__tblr_is_color_key_regex {#1}
          { \tl_set:Nn \l__tblr_hline_fg_tl {#1} }
          {
            \tl_set_rescan:Nnn \l__tblr_v_tl {} {#1}
            \tl_set:Nn \l__tblr_hline_wd_tl { \dim_eval:n {\l__tblr_v_tl} }
          }
      }
  }
\cs_generate_variant:Nn \__tblr_hline_unknown_key:n { V }

\cs_new_protected_nopar:Npn \__tblr_set_hline_cmd:n #1
  {
    \__tblr_get_childs:nx {#1} { \int_use:N \c@colcount }
    \clist_map_inline:Nn \l_tblr_childs_clist
      {
        \__tblr_prop_gput:nxx { hline }
          { [\int_use:N \c@rownum][##1](\l__tblr_hline_num_tl) / @dash }
          { \l__tblr_hline_dash_tl }
        \tl_if_empty:NF \l__tblr_hline_wd_tl
          {
            \__tblr_prop_gput:nxx { hline }
              { [\int_use:N \c@rownum][##1](\l__tblr_hline_num_tl) / wd }
              { \l__tblr_hline_wd_tl }
          }
        \tl_if_empty:NF \l__tblr_hline_fg_tl
          {
            \__tblr_prop_gput:nxx { hline }
              { [\int_use:N \c@rownum][##1](\l__tblr_hline_num_tl) / fg }
              { \l__tblr_hline_fg_tl }
          }
      }
  }

\NewTableCommand \firsthline [1] [] { \SetHline [+] {-} { #1, baseline=below } }
\NewTableCommand \lasthline [1] [] { \SetHline [+] {-} { #1, baseline=above } }

\cs_new_protected:Npn \__tblr_hline_set_baseline:n #1
  {
    \tl_if_eq:nnTF {#1} {above}
      {
        \__tblr_prop_gput:nnx { table }
          { baseline } { \int_eval:n { \c@rownum - 1 } }
      }
      {
        \tl_if_eq:nnT {#1} {below}
          {
            \__tblr_prop_gput:nnx { table } { baseline } { \int_use:N \c@rownum }
          }
      }
  }

%% \SetVlines command for setting every vline in the table
\NewTableCommand \SetVlines [3] [+]
  {
    \tblr_set_every_vline:nnn {#1} {#2} {#3}
  }

%% We put all code inside a group to avoid affecting other table commands
\cs_new_protected:Npn \tblr_set_every_vline:nnn #1 #2 #3
  {
    \group_begin:
    \int_step_inline:nn { \int_eval:n { \c@colcount + 1 } }
      {
        \int_set:Nn \c@colnum {##1}
        \tblr_set_vline:nnn {#1} {#2} {#3}
      }
    \group_end:
  }

%% Check the number of arguments and call \tblr_set_every_vline in different ways
%% This function is called when parsing table specifications
\cs_new_protected:Npn \__tblr_set_every_vline_aux:n #1
  {
    \tl_if_head_is_group:nTF {#1}
      {
        \int_compare:nNnTF { \tl_count:n {#1} } = {3}
          { \tblr_set_every_vline:nnn #1 }
          { \tblr_set_every_vline:nnn {1} #1 }
      }
      { \tblr_set_every_vline:nnn {1} {-} {#1} }
  }

%% Add \SetVline, \vline and \rline commands

\tl_new:N \l__tblr_vline_count_tl % the count of all vlines
\tl_new:N \l__tblr_vline_num_tl   % the index of the vline
\tl_new:N \l__tblr_vline_rows_tl  % the rows of the vline
\tl_new:N \l__tblr_vline_dash_tl  % dash style
\tl_new:N \l__tblr_vline_fg_tl    % dash foreground
\tl_new:N \l__tblr_vline_wd_tl    % dash width

\NewTableCommand \rline [2] [] { \SetVline [=] {#2} {#1} }

\NewTableCommand \vline [1] [] { \SetVline [+] {-} {#1} }

%% #1: the index of the vline (may be + or =)
%% #2: which rows of the vline, separate by commas
%% #3: key=value pairs
\NewTableCommand \SetVline [3] [+]
  {
    \tblr_set_vline:nnn {#1} {#2} {#3}
  }

%% We need to check "text" key first
%% If it does exist and has empty value, then do nothing
\cs_new_protected:Npn \tblr_set_vline:nnn #1 #2 #3
  {
    \group_begin:
    \keys_set_groups:nnn { tblr-vline } { text } {#3}
    \tl_if_eq:NnF \l__tblr_vline_dash_tl { \exp_not:N \@tblr@text }
      {
        \__tblr_set_vline_num:n {#1}
        \tl_clear:N \l__tblr_vline_dash_tl
        \keys_set:nn { tblr-vline } { dash = solid, #3 }
        \__tblr_set_vline_cmd:n {#2}
      }
    \group_end:
  }

\cs_new_protected:Npn \tblr_set_vline:nnnn #1 #2 #3 #4
  {
    \group_begin:
    \__tblr_get_childs:nx {#1} { \int_eval:n { \c@colcount + 1} }
    \clist_map_inline:Nn \l_tblr_childs_clist
      {
        \int_set:Nn \c@colnum {##1}
        \tblr_set_vline:nnn {#2} {#3} {#4}
      }
    \group_end:
  }

%% Check the number of arguments and call \tblr_set_vline in different ways
%% Note that #1 always includes an outer pair of braces
%% This function is called when parsing table specifications
\cs_new_protected:Npn \__tblr_set_vline_aux:nn #1 #2
  {
    \tl_if_head_is_group:nTF {#2}
      {
        \int_compare:nNnTF { \tl_count:n {#2} } = {3}
          { \tblr_set_vline:nnnn #1 #2 }
          { \tblr_set_vline:nnnn #1 {1} #2 }
      }
      { \tblr_set_vline:nnnn #1 {1} {-} {#2} }
  }
\cs_generate_variant:Nn \__tblr_set_vline_aux:nn { Vn }

%% #1: the index of vline to set (may be + or =)
\cs_new_protected:Npn \__tblr_set_vline_num:n #1
  {
    \tl_clear:N \l__tblr_vline_num_tl
    \tl_set:Nx \l__tblr_vline_count_tl
      { \__tblr_prop_item:ne { vline } { [\int_use:N \c@colnum] / @vline-count } }
    \tl_if_empty:NTF \l__tblr_vline_count_tl
      {
        \tl_set:Nx \l__tblr_vline_num_tl { 1 }
        \__tblr_prop_gput:nxx { vline }
          { [\int_use:N \c@colnum] / @vline-count } { 1 }
      }
      {
        \tl_if_eq:nnTF {#1} {+}
          { \__tblr_set_vline_num_incr: }
          {
            \tl_if_eq:nnTF {#1} {=}
              { \tl_set_eq:NN \l__tblr_vline_num_tl \l__tblr_vline_count_tl }
              {
                \int_compare:nNnTF {#1} > { \l__tblr_vline_count_tl }
                  { \__tblr_set_vline_num_incr: }
                  { \tl_set:Nn \l__tblr_vline_num_tl {#1} }
              }
          }
      }
  }

\cs_new_protected:Npn \__tblr_set_vline_num_incr:
  {
    \tl_set:Nx \l__tblr_vline_count_tl
      { \int_eval:n { \l__tblr_vline_count_tl + 1 } }
    \__tblr_prop_gput:nxx { vline }
      { [\int_use:N \c@colnum] / @vline-count } { \l__tblr_vline_count_tl }
    \tl_set_eq:NN \l__tblr_vline_num_tl \l__tblr_vline_count_tl
  }

\keys_define:nn { tblr-vline }
  {
    dash .code:n = \tl_set:Nn \l__tblr_vline_dash_tl { \exp_not:N \@tblr@dash #1 },
    text .code:n = \tl_set:Nn \l__tblr_vline_dash_tl { \exp_not:N \@tblr@text #1 },
    text .groups:n = { text },
    wd .code:n = \tl_set:Nn \l__tblr_vline_wd_tl { \dim_eval:n {#1} },
    fg .code:n = \tl_set:Nn \l__tblr_vline_fg_tl {#1},
    unknown .code:n = \__tblr_vline_unknown_key:V \l_keys_key_str,
  }

\cs_new_protected:Npn \__tblr_vline_unknown_key:n #1
  {
    \prop_if_in:NnTF \g__tblr_defined_vdash_styles_prop {#1}
      { \tl_set:Nn \l__tblr_vline_dash_tl { \exp_not:N \@tblr@dash #1 } }
      {
        \regex_match:NnTF \c__tblr_is_color_key_regex {#1}
          { \tl_set:Nn \l__tblr_vline_fg_tl {#1} }
          {
            \tl_set_rescan:Nnn \l__tblr_v_tl {} {#1}
            \tl_set:Nn \l__tblr_vline_wd_tl { \dim_eval:n {\l__tblr_v_tl} }
          }
      }
  }
\cs_generate_variant:Nn \__tblr_vline_unknown_key:n { V }

\cs_new_protected_nopar:Npn \__tblr_set_vline_cmd:n #1
  {
    \__tblr_get_childs:nx {#1} { \int_use:N \c@rowcount }
    \clist_map_inline:Nn \l_tblr_childs_clist
      {
        \__tblr_prop_gput:nxx { vline }
          { [##1][\int_use:N \c@colnum](\l__tblr_vline_num_tl) / @dash }
          { \l__tblr_vline_dash_tl }
        \tl_if_empty:NF \l__tblr_vline_wd_tl
          {
            \__tblr_prop_gput:nxx { vline }
              { [##1][\int_use:N \c@colnum](\l__tblr_vline_num_tl) / wd }
              { \l__tblr_vline_wd_tl }
          }
        \tl_if_empty:NF \l__tblr_vline_fg_tl
          {
            \__tblr_prop_gput:nxx { vline }
              { [##1][\int_use:N \c@colnum](\l__tblr_vline_num_tl) / fg }
              { \l__tblr_vline_fg_tl }
          }
      }
  }

%%% --------------------------------------------------------
%%  \section{Set Cells}
%%% --------------------------------------------------------

%% \SetCells command for setting every cell in the table
\NewTableCommand \SetCells [2] []
  {
    \tblr_set_every_cell:nn {#1} {#2}
  }

%% We put all code inside a group to avoid affecting other table commands
\cs_new_protected:Npn \tblr_set_every_cell:nn #1 #2
  {
    \group_begin:
    \int_step_inline:nn { \c@rowcount }
      {
        \int_set:Nn \c@rownum {##1}
        \int_step_inline:nn { \c@colcount }
          {
            \int_set:Nn \c@colnum {####1}
            \tblr_set_cell:nn {#1} {#2}
          }
      }
    \group_end:
  }

%% Check the number of arguments and call \tblr_set_every_cell in different ways
%% This function is called when parsing table specifications
\cs_new_protected:Npn \__tblr_set_every_cell_aux:n #1
  {
    \tl_if_head_is_group:nTF {#1}
      { \tblr_set_every_cell:nn #1 }
      { \tblr_set_every_cell:nn {} {#1} }
  }

%% \SetCell command for multirow and/or multicolumn cells

\NewTableCommand \SetCell [2] []
  {
    \tblr_set_cell:nn { #1 } { #2 }
  }

\tl_new:N \l__tblr_row_span_num_tl
\tl_new:N \l__tblr_col_span_num_tl

\cs_new_protected:Npn \tblr_set_cell:nn #1 #2
  {
    \tl_set:Nn \l__tblr_row_span_num_tl { 1 }
    \tl_set:Nn \l__tblr_col_span_num_tl { 1 }
    \keys_set:nn { tblr-cell-span } { #1 }
    \keys_set:nn { tblr-cell-spec } { #2 }
    \__tblr_set_span_spec:VV \l__tblr_row_span_num_tl \l__tblr_col_span_num_tl
  }
\cs_generate_variant:Nn \tblr_set_cell:nn { nV }

\cs_new_protected:Npn \tblr_set_cell:nnnn #1 #2 #3 #4
  {
    \group_begin:
    \__tblr_get_childs:nx {#1} { \int_use:N \c@rowcount }
    \clist_set_eq:NN \l_tmpa_clist \l_tblr_childs_clist
    \__tblr_get_childs:nx {#2} { \int_use:N \c@colcount }
    \clist_set_eq:NN \l_tmpb_clist \l_tblr_childs_clist
    \clist_map_inline:Nn \l_tmpa_clist
      {
        \int_set:Nn \c@rownum {##1}
        \clist_map_inline:Nn \l_tmpb_clist
          {
            \int_set:Nn \c@colnum {####1}
            \tblr_set_cell:nn {#3} {#4}
          }
      }
    \group_end:
  }

%% Check the number of arguments and call \tblr_set_cell in different ways
%% Note that #1 is always of the type {<i>}{<j>}
%% This function is called when parsing table specifications
\cs_new_protected:Npn \__tblr_set_cell_aux:nn #1 #2
  {
    \tl_if_head_is_group:nTF {#2}
      { \tblr_set_cell:nnnn #1 #2 }
      { \tblr_set_cell:nnnn #1 {} {#2} }
  }
\cs_generate_variant:Nn \__tblr_set_cell_aux:nn { Vn }

\keys_define:nn { tblr-cell-span }
  {
    r .tl_set:N = \l__tblr_row_span_num_tl,
    c .tl_set:N = \l__tblr_col_span_num_tl,
  }

\keys_define:nn { tblr-cell-spec }
  {
    l .code:n = \__tblr_prop_gput:nxx {cell}
                  {[\int_use:N \c@rownum][\int_use:N \c@colnum] / halign} {l},
    c .code:n = \__tblr_prop_gput:nxx {cell}
                  {[\int_use:N \c@rownum][\int_use:N \c@colnum] / halign} {c},
    r .code:n = \__tblr_prop_gput:nxx {cell}
                  {[\int_use:N \c@rownum][\int_use:N \c@colnum] / halign} {r},
    t .code:n = \__tblr_prop_gput:nxx {cell}
                  {[\int_use:N \c@rownum][\int_use:N \c@colnum] / valign} {t},
    p .code:n = \__tblr_prop_gput:nxx {cell}
                  {[\int_use:N \c@rownum][\int_use:N \c@colnum] / valign} {t},
    m .code:n = \__tblr_prop_gput:nxx {cell}
                  {[\int_use:N \c@rownum][\int_use:N \c@colnum] / valign} {m},
    b .code:n = \__tblr_prop_gput:nxx {cell}
                  {[\int_use:N \c@rownum][\int_use:N \c@colnum] / valign} {b},
    h .code:n = \__tblr_prop_gput:nxx {cell}
                  {[\int_use:N \c@rownum][\int_use:N \c@colnum] / valign} {h},
    f .code:n = \__tblr_prop_gput:nxx {cell}
                  {[\int_use:N \c@rownum][\int_use:N \c@colnum] / valign} {f},
    wd .code:n = \__tblr_prop_gput:nxx {cell}
                  {[\int_use:N \c@rownum][\int_use:N \c@colnum] / width} {#1},
    bg .code:n = \__tblr_prop_gput:nxx {cell}
                  {[\int_use:N \c@rownum][\int_use:N \c@colnum] / background} {#1},
    unknown .code:n = \__tblr_cell_unknown_key:V \l_keys_key_str,
  }

\cs_new_protected:Npn \__tblr_cell_unknown_key:n #1
  {
    \regex_match:NnTF \c__tblr_is_color_key_regex {#1}
      {
        \__tblr_prop_gput:nxx {cell}
          {[\int_use:N \c@rownum][\int_use:N \c@colnum] / background} {#1}
      }
      {
        \tl_set_rescan:Nnn \l__tblr_v_tl {} {#1}
        \__tblr_prop_gput:nxx {cell}
          { [\int_use:N \c@rownum][\int_use:N \c@colnum] / width }
          { \dim_eval:n { \l__tblr_v_tl } }
      }
  }
\cs_generate_variant:Nn \__tblr_cell_unknown_key:n { V }

\cs_new_protected:Npn \__tblr_set_span_spec:nn #1 #2
  {
    \int_compare:nNnT { #1 } > { 1 }
      {
        \__tblr_prop_gput:nnn {table} {rowspan} {true}
        \__tblr_prop_gput:nxn {cell}
          { [\int_use:N \c@rownum][\int_use:N \c@colnum] / rowspan } { #1 }
      }
    \int_compare:nNnT { #2 } > { 1 }
      {
        \__tblr_prop_gput:nnn {table} {colspan} {true}
        \__tblr_prop_gput:nxn {cell}
          { [\int_use:N \c@rownum][\int_use:N \c@colnum] / colspan } { #2 }
      }
    \int_step_variable:nnNn
      { \int_use:N \c@rownum } { \int_eval:n { \c@rownum + #1 - 1 } } \l__tblr_i_tl
      {
        \int_step_variable:nnNn
          { \int_use:N \c@colnum } { \int_eval:n { \c@colnum + #2 - 1 } }
          \l__tblr_j_tl
          {
            \bool_lazy_and:nnF
              { \int_compare_p:nNn { \l__tblr_i_tl } = { \c@rownum } }
              { \int_compare_p:nNn { \l__tblr_j_tl } = { \c@colnum } }
              {
                \__tblr_prop_gput:nxx {cell}
                  { [\l__tblr_i_tl][\l__tblr_j_tl] / omit } {true}
              }
            \int_compare:nNnF { \l__tblr_i_tl } = { \c@rownum }
              {
                \__tblr_prop_gput:nxx {hline}
                  { [\l__tblr_i_tl][\l__tblr_j_tl] / omit } {true}
              }
            \int_compare:nNnF { \l__tblr_j_tl } = { \c@colnum }
              {
                \__tblr_prop_gput:nxx {vline}
                  { [\l__tblr_i_tl][\l__tblr_j_tl] / omit } {true}
              }
          }
      }
  }
\cs_generate_variant:Nn \__tblr_set_span_spec:nn { VV }

%% Legacy \multicolumn and \multirow commands
%% Both of them could be replaced with \SetCell command
%% Note that they don't have cell text as the last arguments

%% If \multicolumn is followed by \multirow,
%% We need to call \tblr_set_cell together
%% in order to omit all hlines inside the span cell.
\tl_new:N \g__tblr_multicolumn_num_tl
\tl_new:N \g__tblr_multicolumn_spec_tl

%% There maybe p{2em} inside #2 of \multicolumn command
\NewTableCommand \multicolumn [2]
  {
    \tl_gclear:N \g__tblr_multicolumn_num_tl
    \tl_gclear:N \g__tblr_multicolumn_spec_tl
    \tl_map_inline:nn {#2}
      {
        \bool_lazy_and:nnF
          { \tl_if_single_token_p:n {##1} }
          { \token_if_eq_charcode_p:NN ##1 | }
          { \tl_put_right:Nn \g__tblr_multicolumn_spec_tl {,##1} }
      }
    \peek_meaning:NTF \multirow
      { \tl_gset:Nn \g__tblr_multicolumn_num_tl {#1} }
      { \tblr_set_cell:nV { c = #1 } \g__tblr_multicolumn_spec_tl }
  }

\NewTableCommand \multirow [3] [m]
  {
    \tl_if_eq:nnTF {#1} {c}
      { \tl_set:Nn \l_tmpa_tl {, m} }
      {
        \tl_if_eq:nnTF {#1} {t}
          { \tl_set:Nn \l_tmpa_tl {, h} }
          { \tl_if_eq:nnTF {#1} {b}
            { \tl_set:Nn \l_tmpa_tl {, f} }
            { \tl_set:Nn \l_tmpa_tl {, #1} }
          }
      }
    \tl_if_eq:nnF {#3} {*}
      { \tl_if_eq:nnF {#3} {=} { \tl_put_right:Nn \l_tmpa_tl {, wd=#3} } }
    \tl_if_empty:NTF \g__tblr_multicolumn_num_tl
      { \tblr_set_cell:nV { r = #2 } \l_tmpa_tl }
      {
        \tl_put_left:NV \l_tmpa_tl \g__tblr_multicolumn_spec_tl
        \exp_args:Nx \tblr_set_cell:nV
          { c = \g__tblr_multicolumn_num_tl, r = #2 } \l_tmpa_tl
        \tl_gclear:N \g__tblr_multicolumn_num_tl
      }
  }

%%% --------------------------------------------------------
%%  \section{Set Columns and Rows}
%%% --------------------------------------------------------

%% \SetColumns command for setting every column in the table
\NewTableCommand \SetColumns [2] []
  {
    \tblr_set_every_column:nn {#1} {#2}
  }

%% We put all code inside a group to avoid affecting other table commands
\cs_new_protected:Npn \tblr_set_every_column:nn #1 #2
  {
    \group_begin:
    \int_step_inline:nn { \c@colcount }
      {
        \int_set:Nn \c@colnum {##1}
        \tblr_set_column:nn {#1} {#2}
      }
    \group_end:
  }

%% Check the number of arguments and call \tblr_set_every_column in different ways
%% This function is called when parsing table specifications
\cs_new_protected:Npn \__tblr_set_every_column_aux:n #1
  {
    \tl_if_head_is_group:nTF {#1}
      { \tblr_set_every_column:nn #1 }
      { \tblr_set_every_column:nn {} {#1} }
  }

%% \SetColumn command for current column or each cells in the column

\NewTableCommand \SetColumn [2] []
  {
    \tblr_set_column:nn {#1} {#2}
  }

\cs_new_protected:Npn \tblr_set_column:nn #1 #2
  {
    \keys_set:nn { tblr-column } {#2}
  }

\cs_new_protected:Npn \tblr_set_column:nnn #1 #2 #3
  {
    \group_begin:
    \__tblr_get_childs:nx {#1} { \int_use:N \c@colcount }
    \clist_map_inline:Nn \l_tblr_childs_clist
      {
        \int_set:Nn \c@colnum {##1}
        \tblr_set_column:nn {#2} {#3}
      }
    \group_end:
  }

%% Check the number of arguments and call \tblr_set_column in different ways
%% Note that #1 always includes an outer pair of braces
%% This function is called when parsing table specifications
\cs_new_protected:Npn \__tblr_set_column_aux:nn #1 #2
  {
    \tl_if_head_is_group:nTF {#2}
      { \tblr_set_column:nnn #1 #2 }
      { \tblr_set_column:nnn #1 {} {#2} }
  }
\cs_generate_variant:Nn \__tblr_set_column_aux:nn { Vn }

\keys_define:nn { tblr-column }
  {
    l .code:n = \__tblr_set_key_for_every_column_cell:nnn
                  { \int_use:N \c@colnum } { halign } {l},
    c .code:n = \__tblr_set_key_for_every_column_cell:nnn
                  { \int_use:N \c@colnum } { halign } {c},
    r .code:n = \__tblr_set_key_for_every_column_cell:nnn
                  { \int_use:N \c@colnum } { halign } {r},
    t .code:n = \__tblr_set_key_for_every_column_cell:nnn
                  { \int_use:N \c@colnum } { valign } {t},
    p .code:n = \__tblr_set_key_for_every_column_cell:nnn
                  { \int_use:N \c@colnum } { valign } {t},
    m .code:n = \__tblr_set_key_for_every_column_cell:nnn
                  { \int_use:N \c@colnum } { valign } {m},
    b .code:n = \__tblr_set_key_for_every_column_cell:nnn
                  { \int_use:N \c@colnum } { valign } {b},
    h .code:n = \__tblr_set_key_for_every_column_cell:nnn
                  { \int_use:N \c@colnum } { valign } {h},
    f .code:n = \__tblr_set_key_for_every_column_cell:nnn
                  { \int_use:N \c@colnum } { valign } {f},
    bg .code:n = \__tblr_set_key_for_every_column_cell:nnn
                  { \int_use:N \c@colnum } { background } {#1},
    wd .code:n = \__tblr_prop_gput:nxx { column }
                   { [\int_use:N \c@colnum] / width } { \dim_eval:n {#1} },
    co .code:n = \__tblr_prop_gput:nxx { column }
                   { [\int_use:N \c@colnum] / coefficient } {#1},
    leftsep .code:n = \__tblr_prop_gput:nxx { column }
                   { [\int_use:N \c@colnum] / leftsep } { \dim_eval:n {#1} },
    rightsep .code:n = \__tblr_prop_gput:nxx { column }
                   { [\int_use:N \c@colnum] / rightsep } { \dim_eval:n {#1} },
    colsep .meta:n = { leftsep = #1, rightsep = #1},
    leftsep+ .code:n = \__tblr_prop_gadd_dimen_value:nxx { column }
                   { [\int_use:N \c@colnum] / leftsep } { \dim_eval:n {#1} },
    rightsep+ .code:n = \__tblr_prop_gadd_dimen_value:nxx { column }
                   { [\int_use:N \c@colnum] / rightsep } { \dim_eval:n {#1} },
    colsep+ .meta:n = { leftsep+ = #1, rightsep+ = #1},
    unknown .code:n = \__tblr_column_unknown_key:V \l_keys_key_str,
  }

%% #1: column number; #2: key; #3: value
\cs_new_protected:Npn \__tblr_set_key_for_every_column_cell:nnn #1 #2 #3
  {
    \int_step_inline:nn { \c@rowcount }
      {
        \__tblr_prop_gput:nxn {cell} { [##1][#1] / #2 } {#3}
      }
  }

\regex_const:Nn \c__tblr_is_number_key_regex { ^[\+\-]? (\d+|\d*\.\d+)$ }

\cs_new_protected:Npn \__tblr_column_unknown_key:n #1
  {
    \regex_match:NnTF \c__tblr_is_number_key_regex {#1}
      {
        \__tblr_prop_gput:nxx { column }
          { [\int_use:N \c@colnum] / coefficient } {#1}
      }
      {
        \regex_match:NnTF \c__tblr_is_color_key_regex {#1}
          {
            \__tblr_set_key_for_every_column_cell:nnn
              { \int_use:N \c@colnum } { background } {#1}
          }
          {
            \tl_set_rescan:Nnn \l__tblr_v_tl {} {#1}
            \__tblr_prop_gput:nxx { column }
              { [\int_use:N \c@colnum] / width } { \dim_eval:n { \l__tblr_v_tl } }
          }
      }
  }
\cs_generate_variant:Nn \__tblr_column_unknown_key:n { V }

%% \SetRows command for setting every row in the table
\NewTableCommand \SetRows [2] []
  {
    \tblr_set_every_row:nn {#1} {#2}
  }

%% We put all code inside a group to avoid affecting other table commands
\cs_new_protected:Npn \tblr_set_every_row:nn #1 #2
  {
    \group_begin:
    \int_step_inline:nn { \c@rowcount }
      {
        \int_set:Nn \c@rownum {##1}
        \tblr_set_row:nn {#1} {#2}
      }
    \group_end:
  }

%% Check the number of arguments and call \tblr_set_every_row in different ways
%% This function is called when parsing table specifications
\cs_new_protected:Npn \__tblr_set_every_row_aux:n #1
  {
    \tl_if_head_is_group:nTF {#1}
      { \tblr_set_every_row:nn #1 }
      { \tblr_set_every_row:nn {} {#1} }
  }

%% \SetRow command for current row or each cells in the row

\NewTableCommand \SetRow [2] []
  {
    \tblr_set_row:nn {#1} {#2}
  }

\cs_new_protected:Npn \tblr_set_row:nn #1 #2
  {
    \keys_set:nn { tblr-row } {#2}
  }

\cs_new_protected:Npn \tblr_set_row:nnn #1 #2 #3
  {
    \group_begin:
    \__tblr_get_childs:nx {#1} { \int_use:N \c@rowcount }
    \clist_map_inline:Nn \l_tblr_childs_clist
      {
        \int_set:Nn \c@rownum {##1}
        \tblr_set_row:nn {#2} {#3}
      }
    \group_end:
  }

%% Check the number of arguments and call \tblr_set_row in different ways
%% Note that #1 always includes an outer pair of braces
%% This function is called when parsing table specifications
\cs_new_protected:Npn \__tblr_set_row_aux:nn #1 #2
  {
    \tl_if_head_is_group:nTF {#2}
      { \tblr_set_row:nnn #1 #2 }
      { \tblr_set_row:nnn #1 {} {#2} }
  }
\cs_generate_variant:Nn \__tblr_set_row_aux:nn { Vn }

\keys_define:nn { tblr-row }
  {
    l .code:n = \__tblr_set_key_for_every_row_cell:nnn
                  { \int_use:N \c@rownum } { halign } {l},
    c .code:n = \__tblr_set_key_for_every_row_cell:nnn
                  { \int_use:N \c@rownum } { halign } {c},
    r .code:n = \__tblr_set_key_for_every_row_cell:nnn
                  { \int_use:N \c@rownum } { halign } {r},
    t .code:n = \__tblr_set_key_for_every_row_cell:nnn
                  { \int_use:N \c@rownum } { valign } {t},
    p .code:n = \__tblr_set_key_for_every_row_cell:nnn
                  { \int_use:N \c@rownum } { valign } {t},
    m .code:n = \__tblr_set_key_for_every_row_cell:nnn
                  { \int_use:N \c@rownum } { valign } {m},
    b .code:n = \__tblr_set_key_for_every_row_cell:nnn
                  { \int_use:N \c@rownum } { valign } {b},
    h .code:n = \__tblr_set_key_for_every_row_cell:nnn
                  { \int_use:N \c@rownum } { valign } {h},
    f .code:n = \__tblr_set_key_for_every_row_cell:nnn
                  { \int_use:N \c@rownum } { valign } {f},
    bg .code:n = \__tblr_set_key_for_every_row_cell:nnn
                  { \int_use:N \c@rownum } { background } {#1},
    ht .code:n = \__tblr_prop_gput:nxx { row }
                   { [\int_use:N \c@rownum] / height } { \dim_eval:n {#1} },
    co .code:n = \__tblr_prop_gput:nxx { row }
                   { [\int_use:N \c@rownum] / coefficient } {#1},
    abovesep .code:n = \__tblr_prop_gput:nxx { row }
                   { [\int_use:N \c@rownum] / abovesep } { \dim_eval:n {#1} },
    belowsep .code:n = \__tblr_prop_gput:nxx { row }
                   { [\int_use:N \c@rownum] / belowsep } { \dim_eval:n {#1} },
    rowsep .meta:n = { abovesep = #1, belowsep = #1},
    abovesep+ .code:n = \__tblr_prop_gadd_dimen_value:nxx { row }
                   { [\int_use:N \c@rownum] / abovesep } { \dim_eval:n {#1} },
    belowsep+ .code:n = \__tblr_prop_gadd_dimen_value:nxx { row }
                   { [\int_use:N \c@rownum] / belowsep } { \dim_eval:n {#1} },
    rowsep+ .meta:n = { abovesep+ = #1, belowsep+ = #1},
    nobreak .code:n = \__tblr_prop_gput:nxx { row }
                   { [\int_eval:n {\c@rownum - 1}] / nobreak } { true },
    unknown .code:n = \__tblr_row_unknown_key:V \l_keys_key_str,
  }

%% #1: row number; #2: key; #3: value
\cs_new_protected:Npn \__tblr_set_key_for_every_row_cell:nnn #1 #2 #3
  {
    \int_step_inline:nn { \c@colcount }
      {
        \__tblr_prop_gput:nxn {cell} { [#1][##1] / #2 } {#3}
      }
  }

\cs_new_protected:Npn \__tblr_row_unknown_key:n #1
  {
    \regex_match:NnTF \c__tblr_is_number_key_regex {#1}
      {
        \__tblr_prop_gput:nxx { row }
          { [\int_use:N \c@rownum] / coefficient } {#1}
      }
      {
        \regex_match:NnTF \c__tblr_is_color_key_regex {#1}
          {
            \__tblr_set_key_for_every_row_cell:nnn
              { \int_use:N \c@rownum } { background } {#1}
          }
          {
          \tl_set_rescan:Nnn \l__tblr_v_tl {} {#1}
            \__tblr_prop_gput:nxx { row }
              { [\int_use:N \c@rownum] / height } { \dim_eval:n { \l__tblr_v_tl } }
          }
      }
  }
\cs_generate_variant:Nn \__tblr_row_unknown_key:n { V }

%%% --------------------------------------------------------
%%  \section{Column Types and Row Types}
%%% --------------------------------------------------------

%% Some primitive column/row types

\str_const:Nn \c_tblr_primitive_colrow_types_str { Q | < > }
\tl_new:N \g__tblr_expanded_colrow_spec_tl

\exp_args:Nc \NewDocumentCommand { tblr_primitive_column_type_ Q } { O{} }
  {
    \keys_set:nn { tblr-column } { #1 }
    \int_incr:N \c@colnum
    \__tblr_execute_colrow_spec_next:N
  }
\exp_args:Nc \NewDocumentCommand { tblr_column_type_ Q } { O{} }
  {
    \tl_gput_right:Nn \g__tblr_expanded_colrow_spec_tl { Q[#1] }
    \__tblr_expand_colrow_spec_next:N
  }

\exp_args:Nc \NewDocumentCommand { tblr_primitive_row_type_ Q } { O{} }
  {
    \keys_set:nn { tblr-row } { #1 }
    \int_incr:N \c@rownum
    \__tblr_execute_colrow_spec_next:N
  }
\exp_args:Nc \NewDocumentCommand { tblr_row_type_ Q } { O{} }
  {
    \tl_gput_right:Nn \g__tblr_expanded_colrow_spec_tl { Q[#1] }
    \__tblr_expand_colrow_spec_next:N
  }

\exp_args:Nc \NewDocumentCommand { tblr_primitive_column_type_ | } { O{} }
  {
    \vline [#1]
    \__tblr_execute_colrow_spec_next:N
  }
\exp_args:Nc \NewDocumentCommand { tblr_column_type_ | } { O{} }
  {
    \tl_gput_right:Nn \g__tblr_expanded_colrow_spec_tl { |[#1] }
    \__tblr_expand_colrow_spec_next:N
  }

\exp_args:Nc \NewDocumentCommand { tblr_primitive_row_type_ | } { O{} }
  {
    \hline [#1]
    \__tblr_execute_colrow_spec_next:N
  }
\exp_args:Nc \NewDocumentCommand { tblr_row_type_ | } { O{} }
  {
    \tl_gput_right:Nn \g__tblr_expanded_colrow_spec_tl { |[#1] }
    \__tblr_expand_colrow_spec_next:N
  }

\exp_args:Nc \NewDocumentCommand { tblr_primitive_column_type_ > } { O{} m }
  {
    \tl_if_blank:nF { #1 }
      {
        \__tblr_prop_gput:nxx
          { column }
          { [\int_use:N \c@colnum] / leftsep}
          { \dim_eval:n { #1 } }
      }
    \tl_if_blank:nF { #2 }
      {
        \int_step_variable:nNn { \c@rowcount } \l__tblr_i_tl
          {
            \tl_set:Nx \l_tmpa_tl
            {
              \__tblr_prop_item:ne {text}
                { [\l__tblr_i_tl][\int_use:N \c@colnum] }
            }
            \tl_put_left:Nn \l_tmpa_tl { #2 }
            \__tblr_prop_gput:nxV {text}
              { [\l__tblr_i_tl][\int_use:N \c@colnum] } \l_tmpa_tl
          }
      }
    \__tblr_execute_colrow_spec_next:N
  }
\exp_args:Nc \NewDocumentCommand { tblr_column_type_ > } { O{} m }
  {
    \tl_gput_right:Nn \g__tblr_expanded_colrow_spec_tl { >[#1]{#2} }
    \__tblr_expand_colrow_spec_next:N
  }

\exp_args:Nc \NewDocumentCommand { tblr_primitive_row_type_ > } { O{} m }
  {
    \tl_if_blank:nF { #1 }
      {
        \__tblr_prop_gput:nxx
          { row }
          { [\int_use:N \c@rownum] / abovesep }
          { \dim_eval:n { #1 } }
      }
    \tl_if_blank:nF { #2 }
      {
        \int_step_variable:nNn { \c@colcount } \l__tblr_j_tl
          {
            \tl_set:Nx \l_tmpa_tl
            {
              \__tblr_prop_item:ne {text}
                { [\int_use:N \c@rownum][\l__tblr_j_tl] }
            }
            \tl_put_left:Nn \l_tmpa_tl { #2 }
            \__tblr_prop_gput:nxV {text}
              { [\int_use:N \c@rownum][\l__tblr_j_tl] } \l_tmpa_tl
          }
      }
    \__tblr_execute_colrow_spec_next:N
  }
\exp_args:Nc \NewDocumentCommand { tblr_row_type_ > } { O{} m }
  {
    \tl_gput_right:Nn \g__tblr_expanded_colrow_spec_tl { >[#1]{#2} }
    \__tblr_expand_colrow_spec_next:N
  }

\exp_args:Nc \NewDocumentCommand { tblr_primitive_column_type_ < } { O{} m }
  {
    \tl_if_blank:nF { #1 }
      {
        \__tblr_prop_gput:nxx
          { column }
          { [\int_eval:n {\c@colnum - 1}] / rightsep }
          { \dim_eval:n { #1 } }
      }
    \tl_if_blank:nF { #2 }
      {
        \int_step_variable:nNn { \c@rowcount } \l__tblr_i_tl
          {
            \tl_set:Nx \l_tmpa_tl
            {
              \__tblr_prop_item:ne {text}
                { [\l__tblr_i_tl][\int_eval:n {\c@colnum - 1}] }
            }
            \tl_put_right:Nn \l_tmpa_tl { #2 }
            \__tblr_prop_gput:nxV {text}
              { [\l__tblr_i_tl][\int_eval:n {\c@colnum - 1}] } \l_tmpa_tl
          }
      }
    \__tblr_execute_colrow_spec_next:N
  }
\exp_args:Nc \NewDocumentCommand { tblr_column_type_ < } { O{} m }
  {
    \tl_gput_right:Nn \g__tblr_expanded_colrow_spec_tl { <[#1]{#2} }
    \__tblr_expand_colrow_spec_next:N
  }

\exp_args:Nc \NewDocumentCommand { tblr_primitive_row_type_ < } { O{} m }
  {
    \tl_if_blank:nF { #1 }
      {
        \__tblr_prop_gput:nxx
          { row }
          { [\int_eval:n {\c@rownum - 1}] / belowsep }
          { \dim_eval:n { #1 } }
      }
    \tl_if_blank:nF { #2 }
      {
        \int_step_variable:nNn { \c@colcount } \l__tblr_j_tl
          {
            \tl_set:Nx \l_tmpa_tl
            {
              \__tblr_prop_item:ne {text}
                { [\int_eval:n {\c@rownum - 1}][\l__tblr_j_tl] }
            }
            \tl_put_right:Nn \l_tmpa_tl { #2 }
            \__tblr_prop_gput:nxV {text}
              { [\int_eval:n {\c@rownum - 1}][\l__tblr_j_tl] } \l_tmpa_tl
          }
      }
    \__tblr_execute_colrow_spec_next:N
  }
\exp_args:Nc \NewDocumentCommand { tblr_row_type_ < } { O{} m }
  {
    \tl_gput_right:Nn \g__tblr_expanded_colrow_spec_tl { <[#1]{#2} }
    \__tblr_expand_colrow_spec_next:N
  }

%% \NewColumnType/\NewRowType command and predefined column/row types

\str_new:N \g_tblr_used_column_types_str
\str_gset_eq:NN \g_tblr_used_column_types_str \c_tblr_primitive_colrow_types_str

\str_new:N \g_tblr_used_row_types_str
\str_gset_eq:NN \g_tblr_used_row_types_str \c_tblr_primitive_colrow_types_str

\bool_new:N \g__tblr_colrow_spec_expand_stop_bool
\tl_new:N \g__tblr_column_or_row_tl

\msg_new:nnn { tabularray } { used-colrow-type }
  { #1 ~ type ~ name ~ #2 ~ has ~ been ~ used! }

\NewDocumentCommand \NewColumnType { m O{0} o m }
  {
    \tl_set:Nn \g__tblr_column_or_row_tl { column }
    \__tblr_new_column_or_row_type:nnnn {#1} {#2} {#3} {#4}
  }

\NewDocumentCommand \NewRowType { m O{0} o m }
  {
    \tl_set:Nn \g__tblr_column_or_row_tl { row }
    \__tblr_new_column_or_row_type:nnnn {#1} {#2} {#3} {#4}
  }

\NewDocumentCommand \NewColumnRowType { m O{0} o m }
  {
    \tl_set:Nn \g__tblr_column_or_row_tl { column }
    \__tblr_new_column_or_row_type:nnnn {#1} {#2} {#3} {#4}
    \tl_set:Nn \g__tblr_column_or_row_tl { row }
    \__tblr_new_column_or_row_type:nnnn {#1} {#2} {#3} {#4}
  }

\cs_new_protected:Npn \__tblr_new_column_or_row_type:nnnn #1 #2 #3 #4
  {
    \str_if_in:cnTF { g_tblr_used_ \g__tblr_column_or_row_tl _types_str } {#1}
      {
        \tl_if_eq:NnTF \g__tblr_column_or_row_tl { row }
          { \msg_warning:nnnn { tabularray } { used-colrow-type } { Row } {#1} }
          { \msg_warning:nnnn { tabularray } { used-colrow-type } { Column } {#1} }
        \str_log:c { g_tblr_used_ \g__tblr_column_or_row_tl _types_str }
      }
      {
        \__tblr_make_xparse_arg_spec:nnN {#2} {#3} \l__tblr_a_tl
        \exp_args:NcV \NewDocumentCommand
          { tblr_ \g__tblr_column_or_row_tl _type_ #1 } \l__tblr_a_tl
          {
            \bool_gset_false:N \g__tblr_colrow_spec_expand_stop_bool
            \tl_gput_right:Nf \g__tblr_expanded_colrow_spec_tl {#4}
            \__tblr_expand_colrow_spec_next:N
          }
        \str_gput_right:cn
          { g_tblr_used_ \g__tblr_column_or_row_tl _types_str } {#1}
      }
  }

\NewColumnRowType { l } { Q[l] }
\NewColumnRowType { c } { Q[c] }
\NewColumnRowType { r } { Q[r] }

\NewColumnType { t } [1] { Q[t,wd=#1] }
\NewColumnType { p } [1] { Q[p,wd=#1] }
\NewColumnType { m } [1] { Q[m,wd=#1] }
\NewColumnType { b } [1] { Q[b,wd=#1] }
\NewColumnType { h } [1] { Q[h,wd=#1] }
\NewColumnType { f } [1] { Q[f,wd=#1] }

\NewRowType { t } [1] { Q[t,ht=#1] }
\NewRowType { p } [1] { Q[p,ht=#1] }
\NewRowType { m } [1] { Q[m,ht=#1] }
\NewRowType { b } [1] { Q[b,ht=#1] }
\NewRowType { h } [1] { Q[h,ht=#1] }
\NewRowType { f } [1] { Q[f,ht=#1] }

\NewColumnRowType { X } [1][] { Q[co=1,#1] }

\NewColumnRowType { ! } [1] { |[text={#1}] }
\NewColumnRowType { @ } [1] { <[0pt]{} |[text={#1}] >[0pt]{} }
\NewColumnRowType { * } [2] { \prg_replicate:nn {#1} {#2} }

\cs_new_protected:Npn \__tblr_parse_colrow_spec:nn #1 #2
  {
    \tl_gset:Nn \g__tblr_column_or_row_tl {#1}
    \tl_gset:Nn \g__tblr_expanded_colrow_spec_tl {#2}
    \__tblr_expand_colrow_spec:N \g__tblr_expanded_colrow_spec_tl
    \__tblr_execute_colrow_spec:N \g__tblr_expanded_colrow_spec_tl
  }

%% Expand defined column/row types

\cs_new_protected:Npn \__tblr_expand_colrow_spec:N #1
  {
    \bool_do_until:Nn \g__tblr_colrow_spec_expand_stop_bool
      {
        \LogTblrTracing { colspec, rowspec }
        \bool_gset_true:N \g__tblr_colrow_spec_expand_stop_bool
        \tl_set_eq:NN \l_tmpa_tl #1
        \tl_gclear:N #1
        \exp_last_unbraced:NV
          \__tblr_expand_colrow_spec_next:N \l_tmpa_tl \scan_stop:
      }
  }

\msg_new:nnn { tabularray } { unexpandable-colrow-type }
  { Unexpandable ~ command ~ #2 inside ~ #1 ~ type! }

\msg_new:nnn { tabularray } { unknown-colrow-type }
  { Unknown ~ #1 ~ type ~ #2! }

\cs_new_protected:Npn \__tblr_expand_colrow_spec_next:N #1
  {
    \token_if_eq_catcode:NNTF #1 \scan_stop:
      {
        \token_if_eq_meaning:NNF #1 \scan_stop:
          {
            \msg_error:nnVn { tabularray } { unexpandable-colrow-type }
              \g__tblr_column_or_row_tl {#1}
          }
      }
      {
        \str_if_in:cnTF { g_tblr_used_ \g__tblr_column_or_row_tl _types_str } {#1}
          { \cs:w tblr_ \g__tblr_column_or_row_tl _type_  #1 \cs_end: }
          {
            \msg_error:nnVn { tabularray } { unknown-colrow-type }
              \g__tblr_column_or_row_tl {#1}
            \str_log:c { g_tblr_used_ \g__tblr_column_or_row_tl _types_str }
          }
      }
  }

%% Execute primitive column/row types

\cs_new_protected:Npn \__tblr_execute_colrow_spec:N #1
  {
    \tl_if_eq:NnTF \g__tblr_column_or_row_tl { row }
      { \int_set:Nn \c@rownum {1} }
      { \int_set:Nn \c@colnum {1} }
    \exp_last_unbraced:NV \__tblr_execute_colrow_spec_next:N #1 \scan_stop:
  }

\cs_new_protected:Npn \__tblr_execute_colrow_spec_next:N #1
  {
    \token_if_eq_meaning:NNF #1 \scan_stop:
      { \cs:w tblr_primitive_ \g__tblr_column_or_row_tl _type_  #1 \cs_end: }
  }

%%% --------------------------------------------------------
%%  \section{Tabularray Environments}
%%% --------------------------------------------------------

\tl_new:N \l__tblr_env_name_tl
\bool_new:N \l__tblr_math_mode_bool

\NewDocumentEnvironment { tblr } { O{c} m +b }
  {
    \tl_set:Nn \l__tblr_env_name_tl { tblr }
    \mode_if_math:TF
      { \bool_set_true:N \l__tblr_math_mode_bool }
      { \bool_set_false:N \l__tblr_math_mode_bool }
    \buildtblr {#1} {#2} {#3}
  } { }

%% Read, split and build the table

\cs_new_protected:Npn \buildtblr #1 #2 #3
  {
    \mode_leave_vertical:
    \int_gincr:N \g_tblr_level_int
    \__tblr_clear_prop_lists:
    \__tblr_enable_table_commands:
    \__tblr_split_table:n { #3 }
    \LogTblrTracing { command }
    \__tblr_initial_table_spec:
    \LogTblrTracing { table }
    \__tblr_parse_table_spec:n { #2 }
    \__tblr_execute_table_commands:
    \__tblr_disable_table_commands:
    \__tblr_calc_cell_and_line_sizes:
    \__tblr_build_whole:n { #1 }
    \int_gdecr:N \g_tblr_level_int
  }

\cs_new_protected:Npn \__tblr_clear_prop_lists:
  {
    \prop_gclear_new:c { g_tblr_text_ \int_use:N \g_tblr_level_int _prop }
    \prop_gclear_new:c { g_tblr_command_ \int_use:N \g_tblr_level_int _prop }
    \prop_gclear_new:c { g_tblr_table_ \int_use:N \g_tblr_level_int _prop }
    \prop_gclear_new:c { g_tblr_row_ \int_use:N \g_tblr_level_int _prop }
    \prop_gclear_new:c { g_tblr_column_ \int_use:N \g_tblr_level_int _prop }
    \prop_gclear_new:c { g_tblr_cell_ \int_use:N \g_tblr_level_int _prop }
    \prop_gclear_new:c { g_tblr_hline_ \int_use:N \g_tblr_level_int _prop }
    \prop_gclear_new:c { g_tblr_vline_ \int_use:N \g_tblr_level_int _prop }
  }

%% Insert and remove braces for nesting environments inside cells
%% These make line split and cell split workable
%% We need to replace N times for N level nestings
\regex_const:Nn \c__tblr_insert_braces_regex
  {
    \c{begin} \cB\{ (\c[^BE].*) \cE\} (.*?) \c{end} \cB\{ (\c[^BE].*) \cE\}
  }
\tl_const:Nn \c__tblr_insert_braces_tl
  {
    \c{begin} \cB\{ \cB\{ \1 \cE\} \2 \c{end} \cE\} \cB\{ \3 \cE\}
  }
\regex_const:Nn \c__tblr_remove_braces_regex
  {
    \c{begin} \cB\{ \cB\{ (.*?) \c{end} \cE\}
  }
\tl_const:Nn \c__tblr_remove_braces_tl
  {
    \c{begin} \cB\{ \1 \c{end}
  }
\cs_new_protected:Npn \__tblr_insert_braces:N #1
  {
    \regex_replace_all:NVN \c__tblr_insert_braces_regex \c__tblr_insert_braces_tl #1
    \regex_replace_all:NVN \c__tblr_insert_braces_regex \c__tblr_insert_braces_tl #1
  }
\cs_new_protected:Npn \__tblr_remove_braces:N #1
  {
    \regex_replace_all:NVN \c__tblr_remove_braces_regex \c__tblr_remove_braces_tl #1
    \regex_replace_all:NVN \c__tblr_remove_braces_regex \c__tblr_remove_braces_tl #1
  }

%% Split table content to cells and store them
%% #1: table content

\seq_new:N \l_tblr_lines_seq

\cs_new_protected:Npn \__tblr_split_table:n #1
  {
    \int_zero:N \c@rowcount
    \int_zero:N \c@colcount
    \__tblr_split_table_to_lines:nN { #1 } \l_tblr_lines_seq
    \__tblr_split_lines_to_cells:N \l_tblr_lines_seq
  }

%% Split table content to a sequence of lines
%% #1: table content, #2: resulting sequence of lines
\cs_new_protected:Npn \__tblr_split_table_to_lines:nN #1 #2
  {
    \tl_set:Nn \l_tmpa_tl { #1 }
    \__tblr_insert_braces:N \l_tmpa_tl
    \seq_set_split:NnV \l_tmpa_seq { \\ } \l_tmpa_tl
    \seq_clear:N #2
    \seq_map_inline:Nn \l_tmpa_seq
      {
        \tl_if_head_eq_meaning:nNTF {##1} *
          {
            \tl_set:Nn \l__tblr_b_tl { \SetRow{nobreak} }
            \tl_set:Nx \l__tblr_c_tl { \tl_tail:n {##1} }
            \tl_trim_spaces:N \l__tblr_c_tl %% Ignore spaces between * and [dimen]
            \tl_log:N \l__tblr_c_tl
            \tl_if_head_eq_meaning:VNT \l__tblr_c_tl [
              {
                \tl_put_right:Nn \l__tblr_b_tl { \RowBefore@AddBelowSep }
              }
            \tl_put_right:NV \l__tblr_b_tl \l__tblr_c_tl
            \seq_put_right:NV #2 \l__tblr_b_tl
          }
          {
            \tl_if_head_eq_meaning:nNTF { ##1 } [
              { \seq_put_right:Nn #2 { \RowBefore@AddBelowSep ##1 } }
              { \seq_put_right:Nn #2 { ##1 } }
          }
      }
    \int_set:Nn \c@rowcount { \seq_count:N #2 }
  }

%% Treat \\[dimen] command
\NewTableCommand \RowBefore@AddBelowSep [1] []
  {
    \IfValueT { #1 }
      {
        \__tblr_prop_gadd_dimen_value:nxx { row }
          { [\int_eval:n {\c@rownum - 1}] / belowsep } { #1 }
      }
  }

%% Split table lines to cells and store them
%% #1: sequence of lines
\cs_new_protected:Npn \__tblr_split_lines_to_cells:N #1
  {
    \seq_map_indexed_function:NN #1 \__tblr_split_one_line:nn
    \LogTblrTracing { text }
  }

%% Split one line into cells and store them
%% #1: row number, #2 the line text
\cs_new_protected:Npn \__tblr_split_one_line:nn #1 #2
  {
    \seq_set_split:Nnn \l_tmpa_seq { & } { #2 }
    \int_set:Nn \c@rownum {#1}
    \int_zero:N \c@colnum
    \seq_map_inline:Nn \l_tmpa_seq
      {
        \tl_set:Nn \l_tmpa_tl { ##1 }
        \__tblr_remove_braces:N \l_tmpa_tl
        \int_incr:N \c@colnum
        \__tblr_extract_table_commands:N \l_tmpa_tl
        \__tblr_prop_gput:nxV {text} { [#1][\int_use:N \c@colnum] } \l_tmpa_tl
        \__tblr_add_multicolumn_empty_cell:
      }
    %% Decrease row count by 1 if the last row has only one empty cell text
    %% We need to do it here since the > or < column type may add text to cells
    \bool_lazy_and:nnTF
      { \int_compare_p:nNn {\c@colnum} = {1} }
      { \tl_if_empty_p:N \l_tmpa_tl }
      { \int_decr:N \c@rowcount }
      {
        \__tblr_prop_gput:nnx
          {row} { [#1] / cell-number } { \int_use:N \c@colnum }
        \int_compare:nT { \c@colnum > \c@colcount }
          {
            \int_set_eq:NN \c@colcount \c@colnum
          }
      }
  }

%% Add empty cells after the \multicolumn span cell
\cs_new_protected:Npn \__tblr_add_multicolumn_empty_cell:
  {
    \int_step_inline:nn { \l__multicolumn_cell_number_int - 1 }
      {
        \int_incr:N \c@colnum
        \__tblr_prop_gput:nxn {text}
          { [\int_use:N \c@rownum][\int_use:N \c@colnum] } { }
      }
  }

%%% --------------------------------------------------------
%%  \section{Extract Table Commands from Cell Text}
%%% --------------------------------------------------------

%% Extract table commands defined with \NewTableCommand from cell text

\clist_gset:Nn \g__tblr_table_commands_unbrace_next_clist {\multirow, \multicolumn}
\bool_new:N \l__tblr_table_command_unbrace_next_bool
\int_new:N \l__multicolumn_cell_number_int
\tl_new:N \l__tblr_saved_table_commands_before_cell_text_tl
\tl_new:N \l__tblr_saved_cell_text_after_table_commands_tl

\cs_new_protected:Npn \__tblr_extract_table_commands:N #1
  {
    \tl_clear:N \l__tblr_saved_table_commands_before_cell_text_tl
    \tl_clear:N \l__tblr_saved_cell_text_after_table_commands_tl
    \int_set:Nn \l__multicolumn_cell_number_int {1}
    \exp_last_unbraced:NV \__tblr_extract_table_commands_next:w #1 \scan_stop:
    \tl_if_empty:NF \l__tblr_saved_table_commands_before_cell_text_tl
      {
        \__tblr_prop_gput:nxV { command }
          {[\int_use:N \c@rownum][\int_use:N \c@colnum]}
          \l__tblr_saved_table_commands_before_cell_text_tl
      }
    \tl_set_eq:NN #1 \l__tblr_saved_cell_text_after_table_commands_tl
  }

%% #1 maybe a single token or multiple tokens given in braces
\cs_new_protected:Npn \__tblr_extract_table_commands_next:w #1
  {
    \clist_if_in:NnTF \g__tblr_table_commands_clist { #1 }
      {
        \clist_if_in:NnTF \g__tblr_table_commands_unbrace_next_clist { #1 }
          { \bool_set_true:N \l__tblr_table_command_unbrace_next_bool }
          { \bool_set_false:N \l__tblr_table_command_unbrace_next_bool }
        \token_if_eq_meaning:NNTF #1 \multicolumn
          { \__tblr_extract_multicolumn_command:Nn #1 }
          { \__tblr_extract_one_table_command:N #1 }
      }
      {
        \tl_if_single_token:nTF {#1}
          {
            \token_if_eq_meaning:NNF #1 \scan_stop:
              { \__tblr_save_real_cell_text:w #1 }
          }
          { \__tblr_save_real_cell_text:w {#1} }
      }
  }

\cs_new_protected:Npn \__tblr_extract_multicolumn_command:Nn #1 #2
  {
    \int_set:Nn \l__multicolumn_cell_number_int {#2}
    \__tblr_extract_one_table_command:N #1 {#2}
  }

\cs_new_protected:Npn \__tblr_extract_one_table_command:N #1
  {
    \int_set:Nn \l__tblr_a_int
      { \cs:w g__tblr_table_cmd_ \cs_to_str:N #1 _arg_numb_tl \cs_end: }
    \tl_put_right:Nn \l__tblr_saved_table_commands_before_cell_text_tl {#1}
    \int_compare:nNnTF {\l__tblr_a_int} < {0}
      {
        \int_set:Nn \l__tblr_a_int { \int_abs:n {\l__tblr_a_int} - 1 }
        \peek_charcode:NTF [
          { \__tblr_extract_table_command_arg_o:w }
          { \__tblr_extract_table_command_arg_next: }
      }
      { \__tblr_extract_table_command_arg_next: }
  }

\cs_new_protected:Npn \__tblr_extract_table_command_arg_o:w [#1]
  {
    \tl_put_right:Nn \l__tblr_saved_table_commands_before_cell_text_tl { [#1] }
    \__tblr_extract_table_command_arg_next:
  }

\cs_new_protected:Npn \__tblr_extract_table_command_arg_m:n #1
  {
    \tl_put_right:Nn \l__tblr_saved_table_commands_before_cell_text_tl { {#1} }
    \__tblr_extract_table_command_arg_next:
  }

\cs_new_protected:Npn \__tblr_extract_table_command_arg_next:
  {
    \int_compare:nNnTF {\l__tblr_a_int} > {0}
      {
        \int_decr:N \l__tblr_a_int
        \__tblr_extract_table_command_arg_m:n
      }
      {
        \bool_if:NTF \l__tblr_table_command_unbrace_next_bool
          { \__tblr_last_unbraced:Nn \__tblr_extract_table_commands_next:w }
          { \__tblr_extract_table_commands_next:w }
      }
  }

\cs_new_protected:Npn \__tblr_last_unbraced:Nn #1 #2 { #1 #2 }

%% The outermost set of braces of cell text #1 will be removed
\cs_new_protected:Npn \__tblr_save_real_cell_text:w #1 \scan_stop:
  {
    \tl_set:Nn \l__tblr_saved_cell_text_after_table_commands_tl {#1}
  }

%%% --------------------------------------------------------
%%  \section{Initial Table Specifications}
%%% --------------------------------------------------------

\prop_gset_from_keyval:Nn \g__tblr_default_tblr_table_prop
  {
    stretch = 1,
  }

\prop_gset_from_keyval:Nn \g__tblr_default_tblr_rows_prop
  {
    abovesep = 2pt,
    belowsep = 2pt,
    @row-height = 0pt,
    @row-head = 0pt,
    @row-foot = 0pt,
    @row-upper = 0pt,
    @row-lower = 0pt,
  }

\prop_gset_from_keyval:Nn \g__tblr_default_tblr_columns_prop
  {
    leftsep = 6pt,
    rightsep = 6pt,
    @col-width = 0pt,
  }

\prop_gset_from_keyval:Nn \g__tblr_default_tblr_cells_prop
  {
    halign = l,
    valign = t,
  }

\prop_gset_from_keyval:Nn \g__tblr_default_tblr_hlines_prop
  {
    rulesep = 2pt,
  }

\prop_gset_from_keyval:Nn \g__tblr_default_tblr_vlines_prop
  {
    rulesep = 2pt,
  }

\cs_new_protected:Npn \__tblr_initial_table_spec:
  {
    \prop_map_inline:cn { g__tblr_default_ \l__tblr_env_name_tl _table_prop }
      {
        \__tblr_prop_gput:nxn { table } { ##1 } {##2}
      }
    \int_step_variable:nNn { \c@rowcount } \l__tblr_i_tl
      {
        \prop_map_inline:cn { g__tblr_default_ \l__tblr_env_name_tl _rows_prop }
          {
            \__tblr_prop_gput:nxn { row } { [\l__tblr_i_tl] / ##1 } {##2}
          }
        \prop_map_inline:cn { g__tblr_default_ \l__tblr_env_name_tl _hlines_prop }
          {
            \__tblr_prop_gput:nxn { hline } { [\l__tblr_i_tl] / ##1 } {##2}
          }
        \int_step_variable:nNn { \c@colcount } \l__tblr_j_tl
          {
            \prop_map_inline:cn
              { g__tblr_default_ \l__tblr_env_name_tl _cells_prop }
              {
                \__tblr_prop_gput:nxn { cell }
                  { [\l__tblr_i_tl][\l__tblr_j_tl] / ##1 } {##2}
              }
          }
      }
    \prop_map_inline:cn { g__tblr_default_ \l__tblr_env_name_tl _hlines_prop }
      {
        \__tblr_prop_gput:nxn { hline }
          { [\int_eval:n { \c@rowcount + 1}] / ##1 } {##2}
      }
    \int_step_variable:nNn { \c@colcount } \l__tblr_j_tl
      {
        \prop_map_inline:cn { g__tblr_default_ \l__tblr_env_name_tl _columns_prop }
          {
            \__tblr_prop_gput:nxn { column } { [\l__tblr_j_tl] / ##1 } {##2}
          }
        \prop_map_inline:cn { g__tblr_default_ \l__tblr_env_name_tl _vlines_prop }
          {
            \__tblr_prop_gput:nxn { vline } { [\l__tblr_j_tl] / ##1 } {##2}
          }
      }
    \prop_map_inline:cn { g__tblr_default_ \l__tblr_env_name_tl _vlines_prop }
      {
        \__tblr_prop_gput:nxn { vline }
          { [\int_eval:n { \c@colcount + 1}] / ##1 } {##2}
      }
    \keys_set:nv { tblr } { l__tblr_default_ \l__tblr_env_name_tl _tl }
  }

\tl_new:N \l__tblr_default_tblr_tl

%% #1: env name; #2: options
\NewDocumentCommand \SetTabularrayDefault { O{tblr} m }
  {
    \tl_put_right:cn { l__tblr_default_ #1 _tl } { , #2 }
  }
\cs_new_eq:NN \SetTblrDefault \SetTabularrayDefault

%%% --------------------------------------------------------
%%  \section{Parse Table Specifications}
%%% --------------------------------------------------------

\clist_new:N \g__tblr_table_known_keys_clist
\clist_gset:Nn \g__tblr_table_known_keys_clist
  {
    colspec, rowspec, width, hspan, stretch,
    column, row, cell, vline, hline, columns, rows, cells, vlines, hlines,
    leftsep, rightsep, colsep, abovesep, belowsep, rowsep,
  }

\keys_define:nn { tblr }
  {
    colspec .code:n = \__tblr_parse_colrow_spec:nn { column } {#1},
    rowspec .code:n = \__tblr_parse_colrow_spec:nn { row } {#1},
    width .code:n = \__tblr_keys_gput:nx { width } { \dim_eval:n {#1} },
    hspan .code:n = \__tblr_keys_gput:nn { hspan } {#1},
    stretch .code:n = \__tblr_keys_gput:nn { stretch } {#1},
    columns .code:n = \__tblr_set_every_column_aux:n {#1},
    rows    .code:n = \__tblr_set_every_row_aux:n {#1},
    cells   .code:n = \__tblr_set_every_cell_aux:n {#1},
    hlines  .code:n = \__tblr_set_every_hline_aux:n {#1},
    vlines  .code:n = \__tblr_set_every_vline_aux:n {#1},
    leftsep .code:n = \tblr_set_every_column:nn { } { leftsep = #1 },
    rightsep .code:n = \tblr_set_every_column:nn { } { rightsep = #1 },
    colsep .meta:n = { leftsep = #1, rightsep = #1 },
    abovesep .code:n = \tblr_set_every_row:nn { } { abovesep = #1 },
    belowsep .code:n = \tblr_set_every_row:nn { } { belowsep = #1 },
    rowsep .meta:n = { abovesep = #1, belowsep = #1 },
    unknown .code:n = \__tblr_table_special_key:Vn \l_keys_key_str {#1},
  }

\regex_const:Nn \c__tblr_split_key_name_regex { ^ ( [a-z] + ) ( . * ) }

\cs_new_protected:Npn \__tblr_table_special_key:nn #1 #2
  {
    \regex_extract_once:NnNT \c__tblr_split_key_name_regex {#1} \l_tmpa_seq
      {
        \tl_set:Nx \l__tblr_a_tl { \seq_item:Nn \l_tmpa_seq {2} }
        \tl_set_rescan:Nnx \l__tblr_b_tl {} { \seq_item:Nn \l_tmpa_seq {3} }
        \cs:w __tblr_set_ \l__tblr_a_tl _aux:Vn \cs_end: \l__tblr_b_tl {#2}
      }
  }
\cs_generate_variant:Nn \__tblr_table_special_key:nn { Vn }

%% If the first key name is known, treat #1 is the table spec;
%% otherwise, treat #1 as colspec.

\regex_const:Nn \c__tblr_first_key_name_regex { ^ \s * ( [A-Za-z\-] + ) }

\cs_new_protected:Npn \__tblr_parse_table_spec:n #1
  {
    \regex_extract_once:NnNTF \c__tblr_first_key_name_regex {#1} \l_tmpa_seq
      {
        \clist_if_in:NxTF \g__tblr_table_known_keys_clist
          { \seq_item:Nn \l_tmpa_seq {2} }
          { \keys_set:nn { tblr } {#1} }
          { \__tblr_parse_colrow_spec:nn { column } {#1} }
      }
      { \__tblr_parse_colrow_spec:nn { column } {#1} }
  }

\cs_new_protected:Npn  \__tblr_keys_gput:nn #1 #2
  {
    \__tblr_prop_gput:nnn { table } {#1} {#2}
  }
\cs_generate_variant:Nn \__tblr_keys_gput:nn { nx }

%%% --------------------------------------------------------
%%  \section{Typeset and Calculate Sizes}
%%% --------------------------------------------------------

%% Calculate the width and height for every cell and border

\cs_new_protected:Npn \__tblr_calc_cell_and_line_sizes:
  {
    \__tblr_make_strut_box:
    \__tblr_calculate_line_sizes:
    \__tblr_calculate_cell_sizes:
    \LogTblrTracing { cell, row, column, hline, vline }
    \__tblr_compute_extendable_column_width:
    \__tblr_adjust_sizes_for_span_cells:
  }

%% make strut box from stretch option of the table

\box_new:N \l__tblr_strut_ht_box
\box_new:N \l__tblr_strut_dp_box

\cs_new_protected:Npn \__tblr_make_strut_box:
  {
    \tl_set:Nx \l__tblr_s_tl { \__tblr_prop_item:ne { table } { stretch } }
    \hbox_set:Nn \l__tblr_strut_ht_box
      { \vrule height \l__tblr_s_tl \box_ht:N \strutbox width ~ 0pt }
    \hbox_set:Nn \l__tblr_strut_dp_box
      { \vrule depth \l__tblr_s_tl \box_dp:N \strutbox width ~ 0pt }
  }

%% Calculate the thickness for every hline and vline
\cs_new_protected:Npn \__tblr_calculate_line_sizes:
  {
    %% We need these two counters in executing hline and vline commands
    \int_zero:N \c@rownum
    \int_zero:N \c@colnum
    \int_step_inline:nn { \c@rowcount + 1 }
      {
        \int_incr:N \c@rownum
        \int_zero:N \c@colnum
        \int_step_inline:nn { \c@colcount + 1 }
          {
            \int_incr:N \c@colnum
            \int_compare:nNnT { ##1 } < { \c@rowcount + 1 }
              {
                \__tblr_measure_and_update_vline_size:nn { ##1 } { ####1 }
              }
            \int_compare:nNnT { ####1 } < { \c@colcount + 1 }
              {
                \__tblr_measure_and_update_hline_size:nn { ##1 } { ####1 }
              }
          }
      }
  }

%% Measure and update thickness of the vline
%% #1: row number, #2 column number
\cs_new_protected:Npn \__tblr_measure_and_update_vline_size:nn #1 #2
  {
    \dim_zero:N \l__tblr_w_dim
    \tl_set:Nx \l__tblr_n_tl
      { \__tblr_prop_item:ne { vline } { [#2] / @vline-count } }
    \tl_if_empty:NF \l__tblr_n_tl
      {
        \tl_set:Nx \l__tblr_s_tl
          { \__tblr_prop_item:ne { vline } { [#2] / rulesep } }
        \int_step_inline:nn { \l__tblr_n_tl }
          {
            \vbox_set_to_ht:Nnn \l__tblr_b_box {1pt}
              {
                \__tblr_get_vline_segment_child:nnnnn
                  {#1} {#2} {##1} {1pt} {1pt}
              }
            \tl_set:Nx \l__tblr_w_tl { \dim_eval:n { \box_wd:N \l__tblr_b_box } }
            \__tblr_prop_gput_if_larger:nxx { vline }
              { [#2](##1) / @vline-width } { \l__tblr_w_tl }
            \dim_add:Nn \l__tblr_w_dim { \l__tblr_w_tl }
            \dim_add:Nn \l__tblr_w_dim { \l__tblr_s_tl }
          }
        \dim_add:Nn \l__tblr_w_dim { - \l__tblr_s_tl }
      }
    \__tblr_prop_gput_if_larger:nxx { vline }
      { [#2]/ @vline-width } { \dim_use:N \l__tblr_w_dim }
  }

%% Get text of a vline segment
%% #1: row number, #2: column number; #3: index number; #4: height; #5: depth
%% We put all code inside a group to avoid conflicts of local variables
\cs_new_protected:Npn \__tblr_get_vline_segment_child:nnnnn #1 #2 #3 #4 #5
  {
    \group_begin:
    \tl_set:Nx \l__tblr_w_tl
      { \__tblr_prop_item:ne { vline } { [#1][#2](#3) / wd } }
    \tl_if_empty:NF \l__tblr_w_tl { \dim_set:Nn \rulewidth { \l__tblr_w_tl } }
    \tl_set:Nx \l__tblr_d_tl
      { \__tblr_prop_item:ne { vline } { [#1][#2](#3) / @dash } }
    \tl_set:Nx \l__tblr_a_tl { \tl_head:N \l__tblr_d_tl }
    \tl_set:Nx \l__tblr_b_tl { \tl_tail:N \l__tblr_d_tl }
    \exp_args:NV \tl_if_eq:NNTF \l__tblr_a_tl \@tblr@dash
      {
        \__tblr_get_vline_dash_style:N \l__tblr_b_tl
        \xleaders \l__tblr_b_tl \vfil
      }
      {
        \hbox_set:Nn \l__tblr_d_box { \l__tblr_b_tl }
        \box_set_ht:Nn \l__tblr_d_box {#4}
        \box_set_dp:Nn \l__tblr_d_box {#5}
        \box_use:N \l__tblr_d_box
      }
    \group_end:
  }
\cs_generate_variant:Nn \__tblr_get_vline_segment_child:nnnnn { nnnxx }

%% Measure and update thickness of the hline
%% #1: row number, #2 column number
\cs_new_protected:Npn \__tblr_measure_and_update_hline_size:nn #1 #2
  {
    \dim_zero:N \l__tblr_h_dim
    \tl_set:Nx \l__tblr_n_tl
      { \__tblr_prop_item:ne { hline } { [#1] / @hline-count } }
    \tl_if_empty:NF \l__tblr_n_tl
      {
        \tl_set:Nx \l__tblr_s_tl
          { \__tblr_prop_item:ne { hline } { [#1] / rulesep } }
        \int_step_inline:nn { \l__tblr_n_tl }
          {
            \hbox_set_to_wd:Nnn \l__tblr_b_box {1pt}
              { \__tblr_get_hline_segment_child:nnn {#1} {#2} {##1} }
            \tl_set:Nx \l__tblr_h_tl
              {
                \dim_eval:n
                  { \box_ht:N \l__tblr_b_box + \box_dp:N \l__tblr_b_box }
              }
            \__tblr_prop_gput_if_larger:nxx { hline }
              { [#1](##1) / @hline-height } { \l__tblr_h_tl }
            \dim_add:Nn \l__tblr_h_dim { \l__tblr_h_tl }
            \dim_add:Nn \l__tblr_h_dim { \l__tblr_s_tl }
          }
        \dim_add:Nn \l__tblr_h_dim { - \l__tblr_s_tl }
      }
    \__tblr_prop_gput_if_larger:nxx { hline }
      { [#1] / @hline-height } { \dim_use:N \l__tblr_h_dim }
  }

%% Get text of a hline segment
%% #1: row number, #2: column number; #3: index number
\cs_new_protected:Npn \__tblr_get_hline_segment_child:nnn #1 #2 #3
  {
    \group_begin:
    \tl_set:Nx \l__tblr_w_tl
      { \__tblr_prop_item:ne { hline } { [#1][#2](#3) / wd } }
    \tl_if_empty:NF \l__tblr_w_tl { \dim_set:Nn \rulewidth { \l__tblr_w_tl } }
    \tl_set:Nx \l__tblr_d_tl
      { \__tblr_prop_item:ne { hline } { [#1][#2](#3) / @dash } }
    \tl_set:Nx \l__tblr_a_tl { \tl_head:N \l__tblr_d_tl }
    \tl_set:Nx \l__tblr_b_tl { \tl_tail:N \l__tblr_d_tl }
    \exp_args:NV \tl_if_eq:NNTF \l__tblr_a_tl \@tblr@dash
      {
        \__tblr_get_hline_dash_style:N \l__tblr_b_tl
        \xleaders \l__tblr_b_tl \hfil
      }
      { \l__tblr_b_tl \hfil }
    \group_end:
  }

%% current cell alignments
\tl_new:N \g__tblr_cell_halign_tl
\tl_new:N \g__tblr_cell_valign_tl
\tl_new:N \g__tblr_cell_middle_tl

\tl_const:Nn \c__tblr_valign_h_tl { h }
\tl_const:Nn \c__tblr_valign_m_tl { m }
\tl_const:Nn \c__tblr_valign_f_tl { f }
\tl_const:Nn \c__tblr_valign_t_tl { t }
\tl_const:Nn \c__tblr_valign_b_tl { b }

\tl_const:Nn \c__tblr_middle_t_tl { t }
\tl_const:Nn \c__tblr_middle_m_tl { m }
\tl_const:Nn \c__tblr_middle_b_tl { b }

%% #1: row number; #2: column number
\cs_new_protected:Npn \__tblr_get_cell_alignments:nn #1 #2
  {
    \group_begin:
    \tl_gset:Nx \g__tblr_cell_halign_tl
      { \__tblr_prop_item:ne { cell } { [#1][#2] / halign } }
    \tl_set:Nx \l__tblr_v_tl
      { \__tblr_prop_item:ne { cell } { [#1][#2] / valign } }
    \tl_case:NnF \l__tblr_v_tl
      {
        \c__tblr_valign_t_tl
          {
            \tl_gset:Nn \g__tblr_cell_valign_tl {m}
            \tl_gset:Nn \g__tblr_cell_middle_tl {t}
          }
        \c__tblr_valign_m_tl
          {
            \tl_gset:Nn \g__tblr_cell_valign_tl {m}
            \tl_gset:Nn \g__tblr_cell_middle_tl {m}
          }
        \c__tblr_valign_b_tl
          {
            \tl_gset:Nn \g__tblr_cell_valign_tl {m}
            \tl_gset:Nn \g__tblr_cell_middle_tl {b}
          }
      }
      {
        \tl_gset_eq:NN \g__tblr_cell_valign_tl \l__tblr_v_tl
        \tl_gclear:N \g__tblr_cell_middle_tl
      }
    \group_end:
  }

%% current cell dimensions
\dim_new:N \g__tblr_cell_wd_dim
\dim_new:N \g__tblr_cell_ht_dim
\dim_new:N \g__tblr_cell_head_dim
\dim_new:N \g__tblr_cell_foot_dim

%% Calculate the width and height for every cell
\cs_new_protected:Npn \__tblr_calculate_cell_sizes:
  {
    %% You can use these two counters in cell text
    \int_zero:N \c@rownum
    \int_zero:N \c@colnum
    \int_step_inline:nn { \c@rowcount }
      {
        \int_incr:N \c@rownum
        \int_zero:N \c@colnum
        \tl_set:Nx \l__tblr_h_tl
           { \__tblr_prop_item:ne { row } { [\int_use:N \c@rownum] / height } }
        \tl_if_empty:NF \l__tblr_h_tl
          {
            \__tblr_prop_gput:nxV { row }
              { [\int_use:N \c@rownum] / @row-height } \l__tblr_h_tl
          }
        \int_step_inline:nn { \c@colcount }
          {
            \int_incr:N \c@colnum
            \__tblr_measure_cell_update_sizes:nnNNNN
              { \int_use:N \c@rownum }
              { \int_use:N \c@colnum }
              \g__tblr_cell_wd_dim
              \g__tblr_cell_ht_dim
              \g__tblr_cell_head_dim
              \g__tblr_cell_foot_dim
          }
      }
  }

%% Measure and update natural dimensions of the row/column/cell
%% #1: row number; #2 column number; #3: width dimension;
%% #4: total height dimension; #5: head dimension; #6: foot dimension
\cs_new_protected:Npn \__tblr_measure_cell_update_sizes:nnNNNN #1 #2 #3 #4 #5 #6
  {
    \__tblr_get_cell_alignments:nn {#1} {#2}
    \hbox_set:Nn \l_tmpa_box { \__tblr_get_cell_text:nn {#1} {#2} }
    \__tblr_update_cell_size:nnNNNN {#1} {#2} #3 #4 #5 #6
    \__tblr_update_row_size:nnNNN {#1} {#2} #4 #5 #6
    \__tblr_update_col_size:nN {#2} #3
  }

%% #1: row number, #2: column number
\cs_new_protected:Npn \__tblr_get_cell_text:nn #1 #2
  {
    \__tblr_prop_if_in:nxTF {cell} { [#1][#2] / omit }
      {
        \dim_gzero:N \g__tblr_cell_wd_dim
        \dim_gzero:N \g__tblr_cell_ht_dim
        \dim_gzero:N \g__tblr_cell_head_dim
        \dim_gzero:N \g__tblr_cell_foot_dim
      }
      { \__tblr_get_cell_text_real:nn { #1 } { #2 } }
  }

%% Get cell text, #1: row number, #2: column number
%% If the width of the cell is not set, split it with \\ and compute the width
%% Therefore we always get a vbox for any cell
\cs_new_protected:Npn \__tblr_get_cell_text_real:nn #1 #2
  {
    \group_begin:
    \tl_set:Nx \l__tblr_c_tl { \__tblr_prop_item:ne {text} {[#1][#2]} }
    \tl_set:Nx \l__tblr_w_tl
      { \__tblr_prop_item:ne { cell } { [#1][#2] / width } }
    \tl_if_empty:NT \l__tblr_w_tl
      {
        \__tblr_prop_if_in:nxF { cell } { [#1][#2] / colspan }
          {
            \tl_set:Nx \l__tblr_w_tl
              { \__tblr_prop_item:ne { column } { [#2] / width } }
          }
      }
    \tl_if_empty:NT \l__tblr_w_tl
      {
        \bool_if:NTF \l__tblr_math_mode_bool
          {
            \hbox_set:Nn \l_tmpa_box { $\l__tblr_c_tl$ }
            \tl_set:Nx \l__tblr_w_tl { \box_wd:N \l_tmpa_box }
          }
          {
            \tl_set_eq:NN \l_tmpb_tl \l__tblr_c_tl
            \__tblr_insert_braces:N \l_tmpb_tl
            \seq_set_split:NnV \l_tmpa_seq { \\ } \l_tmpb_tl
            \tl_set:Nn \l__tblr_w_tl { 0pt }
            \seq_map_variable:NNn \l_tmpa_seq \l_tmpa_tl
              {
                \__tblr_remove_braces:N \l_tmpa_tl
                \hbox_set:Nn \l_tmpa_box { \l_tmpa_tl }
                \tl_set:Nx \l__tblr_w_tl
                  { \dim_max:nn { \l__tblr_w_tl } { \box_wd:N \l_tmpa_box } }
              }
          }
      }
    \__tblr_get_vcell_and_sizes:NN \l__tblr_c_tl \l__tblr_w_tl
    \group_end:
  }

%% #1: cell text; #2: box width
\cs_new_protected:Npn \__tblr_get_vcell_and_sizes:NN #1 #2
  {
    \group_begin:
    \vbox_set_top:Nn \l_tmpa_box { \__tblr_make_vcell_text:nN #1 #2 }
    \vbox_set:Nn \l_tmpb_box { \__tblr_make_vcell_text:nN #1 #2 }
    \dim_gset:Nn \g__tblr_cell_wd_dim { \box_wd:N \l_tmpb_box }
    \dim_gset:Nn \g__tblr_cell_ht_dim
      { \box_ht:N \l_tmpb_box + \box_dp:N \l_tmpb_box }
    \dim_gset:Nn \g__tblr_cell_head_dim { \box_ht:N \l_tmpa_box }
    \dim_gset:Nn \g__tblr_cell_foot_dim { \box_dp:N \l_tmpb_box }
    \tl_case:Nn \g__tblr_cell_valign_tl
      {
        \c__tblr_valign_h_tl
          { \box_use:N \l_tmpa_box }
        \c__tblr_valign_m_tl
          {
            \tl_case:Nn \g__tblr_cell_middle_tl
              {
                \c__tblr_middle_t_tl
                  { \box_use:N \l_tmpa_box }
                \c__tblr_middle_m_tl
                  {
                    \tl_set:Nx \l__tblr_b_tl
                      {
                        \dim_eval:n
                          {
                            ( \g__tblr_cell_ht_dim - \g__tblr_cell_head_dim
                                                   - \g__tblr_cell_foot_dim ) / 2
                          }
                      }
                    \box_set_ht:Nn \l_tmpb_box
                      { \g__tblr_cell_head_dim + \l__tblr_b_tl }
                    \box_set_dp:Nn \l_tmpb_box
                      { \g__tblr_cell_foot_dim + \l__tblr_b_tl }
                    \box_use:N \l_tmpb_box
                  }
                \c__tblr_middle_b_tl
                  { \box_use:N \l_tmpb_box }
              }
          }
        \c__tblr_valign_f_tl
          { \box_use:N \l_tmpb_box }
      }
    \group_end:
  }

\cs_new_eq:NN \__tlbr_halign_l: \raggedright
\cs_new_eq:NN \__tlbr_halign_c: \centering
\cs_new_eq:NN \__tlbr_halign_r: \raggedleft

%% #1: cell text; #2: box width
\cs_new_protected:Npn \__tblr_make_vcell_text:nN #1 #2
  {
    \dim_set:Nn \tex_hsize:D { #2 }
    \@arrayparboxrestore
    \cs:w __tlbr_halign_ \g__tblr_cell_halign_tl : \cs_end:
    \mode_leave_vertical:
    \box_use:N \l__tblr_strut_ht_box
    \bool_if:NTF \l__tblr_math_mode_bool { $#1$ } { #1 }
    \box_use:N \l__tblr_strut_dp_box
  }

%% #1: total height dimension; #2: head dimension; #3: foot dimension;
%% #4: tl for resulting upper size; #5: tl for resulting lower size

\tl_new:N \l__tblr_middle_body_tl

\cs_new_protected:Npn \__tblr_get_middle_cell_upper_lower:NNNNN #1 #2 #3 #4 #5
  {
    \tl_case:Nn \g__tblr_cell_middle_tl
      {
        \c__tblr_middle_t_tl
          {
            \tl_set:Nx #4 { \dim_use:N #2 }
            \tl_set:Nx #5 { \dim_eval:n { #1 - #2 } }
          }
        \c__tblr_middle_m_tl
          {
            \tl_set:Nx \l__tblr_middle_body_tl { \dim_eval:n { #1 - #2 - #3 } }
            \tl_set:Nx #4 { \dim_eval:n { #2 + \l__tblr_middle_body_tl / 2 } }
            \tl_set:Nx #5 { \dim_eval:n { #3 + \l__tblr_middle_body_tl / 2 } }
          }
        \c__tblr_middle_b_tl
          {
            \tl_set:Nx #4 { \dim_eval:n { #1 - #3 } }
            \tl_set:Nx #5 { \dim_use:N #3 }
          }
      }
  }

%% Update natural dimensions of the cell
%% #1: row number; #2 column number; #3: width dimension;
%% #4: total height dimension; #5: head dimension; #6: foot dimension
\cs_new_protected:Npn \__tblr_update_cell_size:nnNNNN #1 #2 #3 #4 #5 #6
  {
    \group_begin:
    \tl_set:Nx \l__tblr_c_tl
      { \__tblr_prop_item:ne {cell} { [#1][#2] / colspan } }
    \tl_if_empty:NF \l__tblr_c_tl
      {
        \__tblr_prop_gput:nxx {cell} { [#1][#2] / @cell-width } { \dim_use:N #3 }
        \dim_gzero:N #3 % don't affect column width
      }
    \tl_set:Nx \l__tblr_r_tl
      { \__tblr_prop_item:ne {cell} { [#1][#2] / rowspan } }
    \tl_if_empty:NF \l__tblr_r_tl
      {
        \tl_case:Nn \g__tblr_cell_valign_tl
          {
            \c__tblr_valign_h_tl
              {
                \tl_set:Nx \l__tblr_u_tl { \dim_use:N #5 }
                \tl_set:Nx \l__tblr_v_tl { \dim_eval:n { #4 - #5 } }
                %% Update the head size of the first span row here
                \__tblr_prop_gput_if_larger:nxx { row }
                  { [#1] / @row-head } { \dim_use:N #5 }
              }
            \c__tblr_valign_f_tl
              {
                \tl_set:Nx \l__tblr_u_tl { \dim_eval:n { #4 - #6 } }
                \tl_set:Nx \l__tblr_v_tl { \dim_use:N #6 }
                %% Update the foot size of the last span row here
                \__tblr_prop_gput_if_larger:nxx { row }
                  { [\int_eval:n { #1 + \l__tblr_r_tl - 1 }] / @row-foot }
                  { \dim_use:N #6 }
              }
            \c__tblr_valign_m_tl
              {
                \__tblr_get_middle_cell_upper_lower:NNNNN
                  #4 #5 #6 \l__tblr_u_tl \l__tblr_v_tl
              }
          }
        \__tblr_prop_gput:nxV {cell} { [#1][#2] / @cell-height } \l__tblr_u_tl
        \__tblr_prop_gput:nxV {cell} { [#1][#2] / @cell-depth } \l__tblr_v_tl
        %% Don't affect row sizes
        \dim_gzero:N #4
        \dim_gzero:N #5
        \dim_gzero:N #6
      }
    \group_end:
  }


%% Update size of the row. #1: row number; #2: column number;
%% #3: total height dimension; #4: head dimension; #5: foot dimension
\cs_new_protected:Npn \__tblr_update_row_size:nnNNN #1 #2 #3 #4 #5
  {
    \group_begin:
    %% Note that \l__tblr_h_tl may be empty
    \tl_set:Nx \l__tblr_h_tl
      { \__tblr_prop_item:ne { row } { [#1] / @row-height } }
    \tl_if_eq:NNTF \g__tblr_cell_valign_tl \c__tblr_valign_m_tl
      {
        \tl_set:Nx \l__tblr_a_tl
          { \__tblr_prop_item:ne { row } { [#1] / @row-upper } }
        \tl_set:Nx \l__tblr_b_tl
          { \__tblr_prop_item:ne { row } { [#1] / @row-lower } }
        \__tblr_get_middle_cell_upper_lower:NNNNN
          #3 #4 #5 \l__tblr_u_tl \l__tblr_v_tl
        \dim_compare:nNnT { \l__tblr_u_tl } > { \l__tblr_a_tl }
          {
            \tl_set_eq:NN \l__tblr_a_tl \l__tblr_u_tl
            \__tblr_prop_gput:nxV { row }
              { [#1] / @row-upper } \l__tblr_a_tl
          }
        \dim_compare:nNnT { \l__tblr_v_tl } > { \l__tblr_b_tl }
          {
            \tl_set_eq:NN \l__tblr_b_tl \l__tblr_v_tl
            \__tblr_prop_gput:nxV { row }
              { [#1] / @row-lower } \l__tblr_b_tl
          }
        \dim_compare:nNnT
          { \l__tblr_a_tl + \l__tblr_b_tl } > { \l__tblr_h_tl + 0pt }
          {
            \__tblr_prop_gput:nxx { row } { [#1] / @row-height }
              { \dim_eval:n { \l__tblr_a_tl + \l__tblr_b_tl } }
          }
      }
      {
        \tl_set:Nx \l__tblr_e_tl
          { \__tblr_prop_item:ne { row } { [#1] / @row-head } }
        \tl_set:Nx \l__tblr_f_tl
          { \__tblr_prop_item:ne { row } { [#1] / @row-foot } }
        \dim_compare:nNnT {#4} > {\l__tblr_e_tl}
          {
            \__tblr_prop_gput:nxx { row } { [#1] / @row-head } { \dim_use:N #4 }
          }
        \dim_compare:nNnT {#5} > {\l__tblr_f_tl}
          {
            \__tblr_prop_gput:nxx { row } { [#1] / @row-foot } { \dim_use:N #5 }
          }
        \tl_set:Nx \l__tblr_x_tl { \dim_max:nn {#4} { \l__tblr_e_tl } }
        \tl_set:Nx \l__tblr_y_tl { \dim_max:nn {#5} { \l__tblr_f_tl } }
        \dim_compare:nNnT
          { #3 - #4 - #5 } > { \l__tblr_h_tl - \l__tblr_x_tl - \l__tblr_y_tl }
          {
            \__tblr_prop_gput:nxx { row } { [#1] / @row-height }
              {
                \dim_eval:n
                  {
                    \l__tblr_x_tl
                    + \dim_use:N #3 - \dim_use:N #4 - \dim_use:N #5
                    + \l__tblr_y_tl
                  }
              }
          }
      }
    \group_end:
  }


%% Update size of the column. #1: column number; #2: width dimension

\cs_new_protected:Npn \__tblr_update_col_size:nN #1 #2
  {
    \tl_set:Nx \l_tmpb_tl
      { \__tblr_prop_item:ne {column} { [#1] / @col-width } }
    \bool_lazy_or:nnT
      { \tl_if_empty_p:N \l_tmpb_tl }
      { \dim_compare_p:nNn { \dim_use:N #2 } > { \l_tmpb_tl } }
      {
        \__tblr_prop_gput:nxx {column} { [#1] / @col-width } { \dim_use:N #2 }
      }
  }

%%% --------------------------------------------------------
%%  \section{Calculate and Adjust Extendable Columns}
%%% --------------------------------------------------------

%% Compute column widths when there are some extendable columns

\dim_new:N \l__column_target_dim
\prop_new:N \l__column_coefficient_prop
\prop_new:N \l__column_natural_width_prop
\prop_new:N \l__column_computed_width_prop

\msg_new:nnn { tabularray } { table-width-too-small }
  { Table ~ width ~ is ~ too ~ small, need ~ #1 ~ more! }

\cs_new_protected:Npn \__tblr_compute_extendable_column_width:
  {
    \__tblr_collect_extendable_column_width:
    \dim_compare:nNnTF { \l__column_target_dim } > { 0pt }
      {
        \prop_if_empty:NF \l__column_coefficient_prop
          { \__tblr_adjust_extendable_column_width: }
      }
      {
        \msg_warning:nnx { tabularray } { table-width-too-small }
          { \dim_abs:n { \l__column_target_dim } }
      }
  }

\cs_new_protected:Npn \__tblr_collect_extendable_column_width:
  {
    \tl_set:Nx \l_tmpa_tl { \__tblr_prop_item:nn {table} {width} }
    \tl_if_empty:NTF \l_tmpa_tl
      { \dim_set_eq:NN \l__column_target_dim \linewidth }
      { \dim_set:Nn \l__column_target_dim { \l_tmpa_tl } }
    \prop_clear:N \l__column_coefficient_prop
    \prop_clear:N \l__column_natural_width_prop
    \prop_clear:N \l__column_computed_width_prop
    \int_step_variable:nNn { \c@colcount } \l__tblr_j_tl
      {
        \tl_set:Nx \l__tblr_a_tl
          { \__tblr_prop_item:ne {column} { [\l__tblr_j_tl] / width } }
        \tl_set:Nx \l__tblr_b_tl
          { \__tblr_prop_item:ne {column} { [\l__tblr_j_tl] / coefficient } }
        \tl_set:Nx \l__tblr_c_tl
          { \__tblr_prop_item:ne {column} { [\l__tblr_j_tl] / @col-width } }
        \tl_if_empty:NTF \l__tblr_a_tl
          {
            \tl_if_empty:NTF \l__tblr_b_tl
              { \dim_sub:Nn \l__column_target_dim { \l__tblr_c_tl } }
              {
                \prop_put:Nxx \l__column_coefficient_prop
                  { \l__tblr_j_tl } { \l__tblr_b_tl }
                \prop_put:Nxn \l__column_computed_width_prop
                  { \l__tblr_j_tl } { 0pt }
                \dim_compare:nNnF { \l__tblr_b_tl pt } > { 0pt }
                  {
                    \prop_put:Nxx \l__column_natural_width_prop
                      { \l__tblr_j_tl } { \l__tblr_c_tl }
                  }
              }
          }
          { \dim_sub:Nn \l__column_target_dim { \l__tblr_a_tl } }
        \tl_set:Nx \l__tblr_a_tl
          { \__tblr_prop_item:ne {vline} { [\l__tblr_j_tl] / @vline-width } }
        \tl_set:Nx \l__tblr_b_tl
          { \__tblr_prop_item:ne {column} { [\l__tblr_j_tl] / leftsep} }
        \tl_set:Nx \l__tblr_c_tl
          { \__tblr_prop_item:ne {column} { [\l__tblr_j_tl] / rightsep } }
        \dim_set:Nn \l__column_target_dim
          { \l__column_target_dim - \l__tblr_a_tl - \l__tblr_b_tl - \l__tblr_c_tl }
      }
    \tl_set:Nx \l__tblr_a_tl
      {
        \__tblr_prop_item:ne {vline}
          { [\int_eval:n {\c@colcount + 1}] / @vline-width }
      }
    \tl_if_empty:NF \l__tblr_a_tl
      { \dim_sub:Nn \l__column_target_dim { \l__tblr_a_tl } }
    \LogTblrTracing { target }
  }

%% If all columns have negative coefficients and small natural widths,
%% \l__column_coefficient_prop will be empty after one or more rounds
\cs_new_protected:Npn \__tblr_adjust_extendable_column_width:
  {
    \bool_while_do:nn
      { \dim_compare_p:nNn { \l__column_target_dim } > { \hfuzz } }
      {
        \prop_if_empty:NTF \l__column_coefficient_prop
          { \__tblr_adjust_extendable_column_width_negative: }
          { \__tblr_adjust_extendable_column_width_once: }
      }
    \prop_map_inline:Nn \l__column_computed_width_prop
      {
        \__tblr_prop_gput:nnx {column} { [##1] / width } { ##2 }
        \__tblr_prop_gput:nnn {column} { [##1] / @col-width } { 0pt }
      }
    \__tblr_calculate_cell_sizes:
  }

%% We use dimen register, since the coefficient may be a decimal number
\cs_new_protected:Npn \__tblr_adjust_extendable_column_width_once:
  {
    \dim_zero:N \l_tmpa_dim
    \prop_map_inline:Nn \l__column_coefficient_prop
      {
        \dim_add:Nn \l_tmpa_dim { \dim_abs:n { ##2 pt } }
      }
    \tl_set:Nx \l__tblr_w_tl
      { \dim_ratio:nn { \l__column_target_dim } { \l_tmpa_dim } }
    \dim_zero:N \l__column_target_dim
    \prop_map_inline:Nn \l__column_coefficient_prop
      {
        \tl_set:Nx \l__tblr_a_tl
          { \dim_eval:n { \dim_abs:n { ##2 pt } * \l__tblr_w_tl } }
        \dim_compare:nNnTF { ##2 pt } > { 0pt }
          {
            \__tblr_add_dimen_value:Nnn
              \l__column_computed_width_prop { ##1 } { \l__tblr_a_tl }
          }
          {
            \tl_set:Nx \l__tblr_b_tl
              { \prop_item:Nn \l__column_natural_width_prop { ##1 } }
            \tl_set:Nx \l__tblr_c_tl
              { \prop_item:Nn \l__column_computed_width_prop { ##1 } }
            \dim_compare:nNnTF { \l__tblr_a_tl + \l__tblr_c_tl } > { \l__tblr_b_tl }
              {
                \prop_put:Nnx \l__column_computed_width_prop
                  { ##1 } { \l__tblr_b_tl }
                \dim_add:Nn \l__column_target_dim
                  { \l__tblr_a_tl + \l__tblr_c_tl - \l__tblr_b_tl }
                \prop_remove:Nn \l__column_coefficient_prop { ##1 }
              }
              {
                \__tblr_add_dimen_value:Nnn
                  \l__column_computed_width_prop { ##1 } { \l__tblr_a_tl }
              }
          }
      }
    \LogTblrTracing { target }
  }

\cs_new_protected:Npn \__tblr_adjust_extendable_column_width_negative:
  {
    \dim_zero:N \l_tmpa_dim
    \prop_map_inline:Nn \l__column_natural_width_prop
      { \dim_add:Nn \l_tmpa_dim { ##2 } }
    \tl_set:Nx \l_tmpa_tl
      { \dim_ratio:nn { \l__column_target_dim } { \l_tmpa_dim } }
    \dim_zero:N \l__column_target_dim
    \prop_map_inline:Nn \l__column_natural_width_prop
      {
        \tl_set:Nx \l_tmpb_tl { \dim_eval:n { ##2 * \l_tmpa_tl } }
        \__tblr_add_dimen_value:Nnn
          \l__column_computed_width_prop { ##1 } { \l_tmpb_tl }
      }
    \LogTblrTracing { target }
  }

%%% --------------------------------------------------------
%%  \section{Calculate and Adjust Multispan Cells}
%%% --------------------------------------------------------

%% Compute and adjust widths when there are some span cells.
%% By default, we will compute column widths from span widths;
%% but if we set table option "hspan = minimal",
%% we will compute span widths from column widths.

\cs_new_protected:Npn \__tblr_adjust_sizes_for_span_cells:
  {
    \__tblr_prop_if_in:nnT {table} {colspan}
      {
        \__tblr_collect_column_widths_skips:
        \str_if_eq:xnTF
          { \__tblr_prop_item:ne {table} {hspan} } {minimal}
          {
            \__tblr_set_span_widths_from_column_widths:
          }
          {
            \__tblr_collect_span_widths:
            \__tblr_set_column_widths_from_span_widths:
          }
        \LogTblrTracing {column}
        \__tblr_calculate_cell_sizes:
      }
    \__tblr_prop_if_in:nnT {table} {rowspan}
      {
        \__tblr_collect_row_heights_skips:
        \__tblr_collect_span_heights:
        \__tblr_set_row_heights_from_span_heights:
        \LogTblrTracing {row}
      }
  }

\prop_new:N \l__tblr_col_item_skip_size_prop
\prop_new:N \l__tblr_col_span_size_prop
\prop_new:N \l__tblr_row_item_skip_size_prop
\prop_new:N \l__tblr_row_span_size_prop

\cs_new_protected:Npn \__tblr_collect_column_widths_skips:
  {
    \prop_clear:N \l__tblr_col_item_skip_size_prop
    \int_step_variable:nNn { \c@colcount } \l__tblr_j_tl
      {
        \int_compare:nNnTF { \l__tblr_j_tl } > { 1 }
          {
            \prop_put:Nxx \l__tblr_col_item_skip_size_prop { skip[\l__tblr_j_tl] }
              {
                \dim_eval:n
                  {
                    \__tblr_prop_item:ne {column}
                      { [\int_eval:n { \l__tblr_j_tl - 1 }] / rightsep }
                    +
                    \__tblr_prop_item:ne {vline}
                      { [\l__tblr_j_tl] / @vline-width }
                    +
                    \__tblr_prop_item:ne {column}
                      { [\l__tblr_j_tl] / leftsep}
                  }
              }
          }
          {
            \prop_put:Nxn \l__tblr_col_item_skip_size_prop { skip[\l__tblr_j_tl] }
              { 0pt }
          }
        \prop_put:Nxx \l__tblr_col_item_skip_size_prop { item[\l__tblr_j_tl] }
          { \__tblr_prop_item:ne {column} { [\l__tblr_j_tl] / @col-width } }
      }
    \__tblr_do_if_tracing:nn { cellspan }
      { \prop_log:N \l__tblr_col_item_skip_size_prop }
  }

\cs_new_protected:Npn \__tblr_collect_row_heights_skips:
  {
    \prop_clear:N \l__tblr_row_item_skip_size_prop
    \int_step_variable:nNn { \c@rowcount } \l__tblr_i_tl
      {
        \int_compare:nNnTF { \l__tblr_i_tl } > { 1 }
          {
            \prop_put:Nxx \l__tblr_row_item_skip_size_prop { skip[\l__tblr_i_tl] }
              {
                \dim_eval:n
                  {
                    \__tblr_prop_item:ne {row}
                      { [\int_eval:n {\l__tblr_i_tl - 1}] / belowsep }
                    +
                    \__tblr_prop_item:ne {hline}
                      { [\l__tblr_i_tl] / @hline-height }
                    +
                    \__tblr_prop_item:ne {row}
                      { [\l__tblr_i_tl] / abovesep }
                  }
              }
          }
          {
            \prop_put:Nxn \l__tblr_row_item_skip_size_prop { skip[\l__tblr_i_tl] }
              { 0pt }
          }
        \__tblr_collect_one_row_height:NN \l__tblr_i_tl \l__tblr_h_tl
        \prop_put:Nxx \l__tblr_row_item_skip_size_prop
          { item[\l__tblr_i_tl] } { \l__tblr_h_tl }
      }
    \__tblr_do_if_tracing:nn { cellspan }
      { \prop_log:N \l__tblr_row_item_skip_size_prop }
  }

%% #1: row number; #2: tl with result
\cs_new_protected:Npn \__tblr_collect_one_row_height:NN #1 #2
  {
    \tl_set:Nx #2 { \__tblr_prop_item:ne { row } { [#1] / @row-height } }
  }

\cs_new_protected:Npn \__tblr_collect_span_widths:
  {
    \prop_clear:N \l__tblr_col_span_size_prop
    \int_step_variable:nNn { \c@colcount } \l__tblr_j_tl
      {
        \int_step_variable:nNn { \c@rowcount } \l__tblr_i_tl
          {
            \tl_set:Nx \l__tblr_a_tl
              {
                \__tblr_prop_item:ne {cell}
                  { [\l__tblr_i_tl][\l__tblr_j_tl] / colspan }
              }
            \tl_if_empty:NF \l__tblr_a_tl
              {
                \__tblr_put_if_larger:Nxx \l__tblr_col_span_size_prop
                  {
                    ( \l__tblr_j_tl -
                      \int_eval:n {\l__tblr_j_tl + \l__tblr_a_tl - 1} )
                  }
                  {
                    \__tblr_prop_item:ne {cell}
                      { [\l__tblr_i_tl][\l__tblr_j_tl] / @cell-width }
                  }
              }
          }
      }
    \__tblr_do_if_tracing:nn { cellspan }
      { \prop_log:N \l__tblr_col_span_size_prop }
  }

\prop_new:N \l__tblr_row_span_to_row_prop

\cs_new_protected:Npn \__tblr_collect_span_heights:
  {
    \prop_clear:N \l__tblr_row_span_to_row_prop
    \prop_clear:N \l__tblr_row_span_size_prop
    \int_step_variable:nNn { \c@rowcount } \l__tblr_i_tl
      {
        \int_step_variable:nNn { \c@colcount } \l__tblr_j_tl
          {
            \tl_set:Nx \l__tblr_a_tl
              {
                \__tblr_prop_item:ne {cell}
                  { [\l__tblr_i_tl][\l__tblr_j_tl] / rowspan }
              }
            \tl_if_empty:NF \l__tblr_a_tl
              {
                \tl_set:Nx \l__tblr_v_tl
                  {
                    \__tblr_prop_item:ne {cell}
                      { [\l__tblr_i_tl][\l__tblr_j_tl] / valign }
                  }
                \tl_if_eq:NnT \l__tblr_v_tl { h }
                  {
                    \tl_set:Nx \l__tblr_h_tl
                      {
                        \__tblr_prop_item:ne {row}
                          { [\l__tblr_i_tl] / @row-head }
                      }
                    \__tblr_prop_gput:nxV {cell}
                      { [\l__tblr_i_tl][\l__tblr_j_tl] / @cell-height }
                      \l__tblr_h_tl
                  }
                \tl_if_eq:NnT \l__tblr_v_tl { f }
                  {
                    \tl_set:Nx \l__tblr_d_tl
                      {
                        \__tblr_prop_item:ne {row}
                          {
                            [\int_eval:n {\l__tblr_i_tl + \l__tblr_a_tl - 1}]
                            / @row-foot
                          }
                      }
                    \__tblr_prop_gput:nxV {cell}
                      { [\l__tblr_i_tl][\l__tblr_j_tl] / @cell-depth }
                      \l__tblr_d_tl
                  }
                \__tblr_put_if_larger:Nxx \l__tblr_row_span_size_prop
                  {
                    ( \l__tblr_i_tl -
                      \int_eval:n {\l__tblr_i_tl + \l__tblr_a_tl - 1} )
                  }
                  {
                    \dim_eval:n
                      {
                        \__tblr_prop_item:ne {cell}
                          { [\l__tblr_i_tl][\l__tblr_j_tl] / @cell-height }
                        +
                        \__tblr_prop_item:ne {cell}
                          { [\l__tblr_i_tl][\l__tblr_j_tl] / @cell-depth }
                      }
                  }
                \prop_put:Nxx \l__tblr_row_span_to_row_prop
                  { [\l__tblr_i_tl][\l__tblr_j_tl] }
                  { \int_eval:n {\l__tblr_i_tl + \l__tblr_a_tl - 1} }
              }
          }
      }
    \__tblr_do_if_tracing:nn { cellspan }
      {
        \prop_log:N \l__tblr_row_span_to_row_prop
        \prop_log:N \l__tblr_row_span_size_prop
      }
  }

%% Compute and set column widths from span widths
\cs_new_protected:Npn \__tblr_set_column_widths_from_span_widths:
  {
    \__tblr_calc_item_sizes_from_span_sizes:xNN
      { \int_use:N \c@colcount }
      \l__tblr_col_item_skip_size_prop
      \l__tblr_col_span_size_prop
    \__tblr_set_all_column_widths:
  }

%% Compute and set row heights from span heights
\cs_new_protected:Npn \__tblr_set_row_heights_from_span_heights:
  {
    \__tblr_calc_item_sizes_from_span_sizes:xNN
      { \int_use:N \c@rowcount }
      \l__tblr_row_item_skip_size_prop
      \l__tblr_row_span_size_prop
    \__tblr_set_all_row_heights:
  }

%% See page 245 in Chapter 22 of TeXbook
%% #1: total number of items
%% #2: prop list with item sizes and skip sizes; #3: prop list with span sizes
\cs_new_protected:Npn \__tblr_calc_item_sizes_from_span_sizes:nNN #1 #2 #3
  {
    \int_step_variable:nNn { #1 } \l__tblr_j_tl
      {
        \dim_set:Nn \l__tblr_w_dim
          {
            \prop_item:Ne #2 { item[\l__tblr_j_tl] }
          }
        \int_step_variable:nNn { \l__tblr_j_tl - 1 } \l__tblr_i_tl
          {
            \tl_set:Nx \l__tblr_a_tl
              { \prop_item:Ne #3 { (\l__tblr_i_tl-\l__tblr_j_tl) } }
            \tl_if_empty:NF \l__tblr_a_tl
              {
                \int_step_variable:nnNn
                  { \l__tblr_i_tl } { \l__tblr_j_tl - 1 } \l__tblr_k_tl
                  {
                    \__tblr_do_if_tracing:nn { cellspan }
                      {
                        \tl_log:x
                          { \l__tblr_j_tl : \l__tblr_i_tl -> \l__tblr_k_tl }
                      }
                    \tl_set:Nx \l_tmpa_tl
                      {
                        \prop_item:Ne #2 { itemskip[\l__tblr_k_tl] }
                      }
                    \tl_set:Nx \l__tblr_a_tl
                      { \dim_eval:n { \l__tblr_a_tl - \l_tmpa_tl } }
                  }
                \dim_compare:nNnT { \l__tblr_a_tl } > { \l__tblr_w_dim }
                  {
                    \dim_set:Nn \l__tblr_w_dim { \l__tblr_a_tl }
                  }
              }
          }
        \prop_put:Nxx #2
          { item[\l__tblr_j_tl] } { \dim_use:N \l__tblr_w_dim }
        \int_compare:nNnT { \l__tblr_j_tl } < { #1 }
          {
            \tl_set:Nx \l_tmpb_tl
              {
                \prop_item:Ne #2
                  { skip[\int_eval:n { \l__tblr_j_tl + 1} ] }
              }
            \dim_add:Nn \l__tblr_w_dim { \l_tmpb_tl }
            \prop_put:Nxx #2
              { itemskip[\l__tblr_j_tl] } { \dim_use:N \l__tblr_w_dim }
          }
      }
    \__tblr_do_if_tracing:nn { cellspan } { \prop_log:N #2 }
  }
\cs_generate_variant:Nn \__tblr_calc_item_sizes_from_span_sizes:nNN { x }

\cs_new_protected:Npn \__tblr_set_all_column_widths:
  {
    \int_step_variable:nNn { \c@colcount } \l__tblr_j_tl
      {
        \__tblr_prop_gput:nxx {column}
          { [\l__tblr_j_tl] / @col-width }
          { \prop_item:Ne \l__tblr_col_item_skip_size_prop { item[\l__tblr_j_tl] } }
      }
  }

\cs_new_protected:Npn \__tblr_set_all_row_heights:
  {
    \int_step_variable:nNn { \c@rowcount } \l__tblr_i_tl
      {
        \tl_set:Nx \l__tblr_h_tl
          {
            \__tblr_prop_item:ne {row} { [\l__tblr_i_tl] / @row-head }
          }
        \tl_set:Nx \l__tblr_d_tl
          {
            \__tblr_prop_item:ne {row} { [\l__tblr_i_tl] / @row-foot }
          }
        \tl_set:Nx \l__tblr_a_tl
          {
            \prop_item:Ne \l__tblr_row_item_skip_size_prop { item[\l__tblr_i_tl] }
          }
        \__tblr_collect_one_row_height:NN \l__tblr_i_tl \l__tblr_t_tl
        \__tblr_prop_gput:nxx {row}
          { [\l__tblr_i_tl] / @row-height } { \l__tblr_a_tl }
      }
  }

\cs_new_protected:Npn \__tblr_get_span_key_row_col:w [#1][#2]
  {
    \tl_set:Nn \l__tblr_i_tl {#1}
    \tl_set:Nn \l__tblr_j_tl {#2}
  }

%% Compute and set span widths from column widths
\cs_new_protected:Npn \__tblr_set_span_widths_from_column_widths:
  {
    \int_step_variable:nNn { \c@colcount } \l__tblr_j_tl
      {
        \int_step_variable:nNn { \c@rowcount } \l__tblr_i_tl
          {
            \tl_set:Nx \l__tblr_a_tl
              {
                \__tblr_prop_item:ne {cell}
                  { [\l__tblr_i_tl][\l__tblr_j_tl] / colspan }
              }
            \tl_if_empty:NF \l__tblr_a_tl
              {
                \__tblr_calc_span_widths:xxN
                  { \l__tblr_j_tl }
                  { \int_eval:n { \l__tblr_j_tl + \l__tblr_a_tl - 1 } }
                  \l__tblr_w_dim
                \__tblr_prop_gput:nxx {cell}
                  { [\l__tblr_i_tl][\l__tblr_j_tl] / width }
                  { \dim_use:N \l__tblr_w_dim }
              }
          }
      }
  }

%% Cell is spanned from col #1 to col #2, #3 is the return dim
\cs_new_protected:Npn \__tblr_calc_span_widths:nnN #1 #2 #3
  {
    \dim_zero:N #3
    \int_step_inline:nnn { #1 } { #2 }
      {
        \tl_set:Nx \l_tmpa_tl
          { \prop_item:Ne \l__tblr_col_item_skip_size_prop { skip[##1] } }
        \tl_set:Nx \l_tmpb_tl
          { \prop_item:Ne \l__tblr_col_item_skip_size_prop { item[##1] } }
        \dim_add:Nn #3 { \dim_eval:n { \l_tmpa_tl + \l_tmpb_tl } }
      }
  }
\cs_generate_variant:Nn \__tblr_calc_span_widths:nnN { xxN }

%%% --------------------------------------------------------
%%  \section{Build the Whole Table}
%%% --------------------------------------------------------

\tl_new:N \__tlbr_vbox_align_tl
\tl_const:Nn \__tlbr_vbox_t_tl {t}
\tl_const:Nn \__tlbr_vbox_m_tl {m}
\tl_const:Nn \__tlbr_vbox_c_tl {c}
\tl_const:Nn \__tlbr_vbox_b_tl {b}

\box_new:N \l__tblr_table_box

\cs_new_protected:Npn \__tblr_build_whole:n #1
  {
    \vbox_set:Nn \l__tblr_table_box
      {
        \int_step_variable:nNn { \c@rowcount } \l__tblr_i_tl
          {
            \hbox:n { \__tblr_build_hline:V \l__tblr_i_tl }
            \hrule height ~ 0pt % remove lineskip between hlines and rows
            \hbox:n { \__tblr_build_row:N \l__tblr_i_tl }
            \hrule height ~ 0pt
          }
        \hbox:n { \__tblr_build_hline:n { \int_eval:n {\c@rowcount + 1} } }
      }
    \__tblr_valign_whole:Nn \l__tblr_table_box #1
  }

\cs_new_protected:Npn \__tblr_valign_whole:Nn #1 #2
  {
    \group_begin:
    \tl_set:Nn \__tlbr_vbox_align_tl {#2}
    \dim_set:Nn \l__tblr_t_dim { \box_ht:N #1 + \box_dp:N #1 }
    \tl_case:NnF \__tlbr_vbox_align_tl
      {
        \__tlbr_vbox_m_tl
          { \__tblr_valign_whole_middle:N #1 }
        \__tlbr_vbox_c_tl
          { \__tblr_valign_whole_middle:N #1 }
        \__tlbr_vbox_t_tl
          { \__tblr_valign_whole_top:N #1 }
        \__tlbr_vbox_b_tl
          { \__tblr_valign_whole_bottom:N #1 }
      }
      { \__tblr_valign_whole_middle:N #1 }
    \group_end:
  }

\cs_new_protected:Npn \__tblr_valign_whole_middle:N #1
  {
    \hbox:n { $ \m@th \tex_vcenter:D { \vbox_unpack_drop:N #1 } $ }
  }

\cs_new_protected:Npn \__tblr_valign_whole_top:N #1
  {
    \tl_set:Nx \l__tblr_a_tl
      { \__tblr_prop_item:ne { hline } { [1] / @hline-height } }
    %% Note that \l__tblr_b_tl may be empty
    \tl_set:Nx \l__tblr_b_tl
      { \__tblr_prop_item:ne { table } { baseline } }
    \bool_lazy_or:nnTF
      { \dim_compare_p:nNn { \l__tblr_a_tl } = { 0pt } }
      { \int_compare_p:nNn { \l__tblr_b_tl + 0 } = { 1 } }
      {
        \dim_set:Nn \l__tblr_h_dim
          {
            \__tblr_prop_item:ne { row } { [1] / abovesep }
            +
            ( \__tblr_prop_item:ne { row } { [1] / @row-height }
              +
              \__tblr_prop_item:ne { row } { [1] / @row-upper }
              -
              \__tblr_prop_item:ne { row } { [1] / @row-lower }
            ) / 2
          }
        \dim_set:Nn \l__tblr_d_dim { \l__tblr_t_dim - \l__tblr_h_dim }
      }
      {
        \dim_set:Nn \l__tblr_h_dim { 0pt }
        \dim_set_eq:NN \l__tblr_d_dim \l__tblr_t_dim
      }
    \box_set_ht:Nn #1 { \l__tblr_h_dim }
    \box_set_dp:Nn #1 { \l__tblr_d_dim }
    \box_use_drop:N #1
  }

\cs_new_protected:Npn \__tblr_valign_whole_bottom:N #1
  {
    \tl_set:Nx \l__tblr_a_tl
      {
        \__tblr_prop_item:ne { hline }
          { [\int_eval:n {\c@rowcount + 1}] / @hline-height }
      }
    %% Note that \l__tblr_b_tl may be empty
    \tl_set:Nx \l__tblr_b_tl
      { \__tblr_prop_item:ne { table } { baseline } }
    \bool_lazy_or:nnTF
      { \dim_compare_p:nNn { \l__tblr_a_tl } = { 0pt } }
      { \int_compare_p:nNn { \l__tblr_b_tl + 0 } = { \c@rowcount } }
      {
        \dim_set:Nn \l__tblr_d_dim
          {
            ( \__tblr_prop_item:ne { row }
                { [\int_use:N \c@rowcount] / @row-height }
              -
              \__tblr_prop_item:ne { row }
                { [\int_use:N \c@rowcount] / @row-upper }
              +
              \__tblr_prop_item:ne { row }
                { [\int_use:N \c@rowcount] / @row-lower }
            ) / 2
            +
            \__tblr_prop_item:ne { row } { [1] / belowsep }
          }
        \dim_set:Nn \l__tblr_h_dim { \l__tblr_t_dim - \l__tblr_d_dim }
      }
      {
        \dim_set:Nn \l__tblr_d_dim { 0pt }
        \dim_set_eq:NN \l__tblr_h_dim \l__tblr_t_dim
      }
    \box_set_ht:Nn #1 { \l__tblr_h_dim }
    \box_set_dp:Nn #1 { \l__tblr_d_dim }
    \box_use_drop:N #1
  }

\dim_new:N \l__tblr_col_o_wd_dim
\dim_new:N \l__tblr_col_b_wd_dim

%% Build hline. #1: row number
\cs_new_protected:Npn \__tblr_build_hline:n #1
  {
    \int_step_inline:nn { \c@colcount }
      { \__tblr_build_hline_segment:nn { #1 } { ##1 } }
  }
\cs_generate_variant:Nn \__tblr_build_hline:n { x, V }

%% #1: row number, #2: column number
\cs_new_protected:Npn \__tblr_build_hline_segment:nn #1 #2
  {
    \tl_set:Nx \l__tblr_n_tl
      { \__tblr_prop_item:ne { hline } { [#1] / @hline-count } }
    \tl_set:Nx \l__tblr_o_tl
      { \__tblr_prop_item:ne { hline } { [#1][#2] / omit } }
    \__tblr_get_col_outer_width_border_width:nNN {#2}
      \l__tblr_col_o_wd_dim \l__tblr_col_b_wd_dim
    \tl_if_empty:NTF \l__tblr_o_tl
      {
        \tl_if_empty:NF \l__tblr_n_tl
          { \__tblr_build_hline_segment_real:nn {#1} {#2} }
      }
      { \__tblr_build_hline_segment_omit:nn {#1} {#2} }
  }

%% #1: row number, #2: column number
\cs_new_protected:Npn \__tblr_build_hline_segment_omit:nn #1 #2
  {
    \skip_horizontal:n { \l__tblr_col_o_wd_dim - \l__tblr_col_b_wd_dim }
  }

%% #1: row number, #2: column number
\cs_new_protected:Npn \__tblr_build_hline_segment_real:nn #1 #2
  {
    \tl_set:Nx \l__tblr_s_tl
      { \__tblr_prop_item:ne { hline } { [#1] / rulesep } }
    \vbox_set:Nn \l__tblr_c_box
      {
        %% add an empty hbox to support vbox width
        \tex_hbox:D to \l__tblr_col_o_wd_dim {}
        \int_step_inline:nn { \l__tblr_n_tl }
          {
            \tl_set:Nx \l__tblr_h_tl
              { \__tblr_prop_item:ne { hline } { [#1](##1) / @hline-height } }
            \hrule height ~ 0pt % remove lineskip
            \hbox_set_to_wd:Nnn \l__tblr_b_box { \l__tblr_col_o_wd_dim }
              {
                \tl_set:Nx \l__tblr_f_tl
                  { \__tblr_prop_item:ne { hline } { [#1][#2](##1) / fg } }
                \tl_if_empty:NF \l__tblr_f_tl { \color{\l__tblr_f_tl} }
                \__tblr_get_hline_segment_child:nnn {#1} {#2} {##1}
              }
            \box_set_ht:Nn \l__tblr_b_box { \l__tblr_h_tl }
            \box_set_dp:Nn \l__tblr_b_box { 0pt }
            \box_use:N \l__tblr_b_box
            \skip_vertical:n { \l__tblr_s_tl }
          }
        \skip_vertical:n { - \l__tblr_s_tl }
      }
    \box_use:N \l__tblr_c_box
    \skip_horizontal:n { - \l__tblr_col_b_wd_dim }
  }

%% Read from table specifications and calculate the widths of row and border
%% column outer width = content width + colsep width + border width
%% #1: the column number, #2: outer width, #3: border width
\cs_new_protected:Npn \__tblr_get_col_outer_width_border_width:nNN #1 #2 #3
  {
    \dim_set:Nn #3
      { \__tblr_prop_item:ne {vline} { [\int_eval:n {#1 + 1}] / @vline-width } }
    \dim_set:Nn #2
      {
        \__tblr_prop_item:ne {vline} { [#1] / @vline-width }
        +
        \__tblr_prop_item:ne {column} { [#1] / leftsep }
        +
        \__tblr_prop_item:ne {column} { [#1] / @col-width }
        +
        \__tblr_prop_item:ne {column} { [#1] / rightsep }
        +
        #3
      }
  }

\dim_new:N \l__tblr_row_ht_dim
\dim_new:N \l__tblr_row_dp_dim
\dim_new:N \l__tblr_row_abovesep_dim
\dim_new:N \l__tblr_row_belowsep_dim

%% Build current row, #1: row number
\cs_new_protected:Npn \__tblr_build_row:N #1
  {
    \__tblr_get_row_inner_height_depth:VNNNN #1
      \l__tblr_row_ht_dim \l__tblr_row_dp_dim
      \l__tblr_row_abovesep_dim \l__tblr_row_belowsep_dim
    \vrule width ~ 0pt ~ height ~ \l__tblr_row_ht_dim ~ depth ~ \l__tblr_row_dp_dim
    \int_step_variable:nNn { \c@colcount } \l__tblr_j_tl
      {
        \__tblr_build_vline_segment:nn {#1} { \l__tblr_j_tl }
        \__tblr_build_cell:NN #1 \l__tblr_j_tl
      }
    \__tblr_build_vline_segment:nn {#1} { \int_eval:n {\c@colcount + 1} }
  }

%% Read from table specifications and calculate inner height/depth of the row
%% inner height = abovesep + above vspace + row upper
%% inner depth = row lower + below vspace + belowsep
%% #1: the row number; #2: resulting inner height; #3: resulting inner depth;
%% #4: restulting abovesep; #5: restulting belowsep.

\dim_new:N \l__row_upper_dim
\dim_new:N \l__row_lower_dim
\dim_new:N \l__row_vpace_dim

\cs_new_protected:Npn \__tblr_get_row_inner_height_depth:nNNNN #1 #2 #3 #4 #5
  {
    \dim_set:Nn #4
      { \__tblr_prop_item:ne { row } { [#1] / abovesep } }
    \dim_set:Nn #5
      { \__tblr_prop_item:ne { row } { [#1] / belowsep } }
    \dim_set:Nn \l__row_upper_dim
      {  \__tblr_prop_item:ne { row } { [#1] / @row-upper } }
    \dim_set:Nn \l__row_lower_dim
      {  \__tblr_prop_item:ne { row } { [#1] / @row-lower } }
    \dim_set:Nn \l__row_vpace_dim
      {
        ( \__tblr_prop_item:ne { row } { [#1] / @row-height }
          - \l__row_upper_dim - \l__row_lower_dim ) / 2
      }
    \dim_set:Nn #2 { #4 + \l__row_vpace_dim + \l__row_upper_dim }
    \dim_set:Nn #3 { \l__row_lower_dim + \l__row_vpace_dim + #5 }
  }
\cs_generate_variant:Nn \__tblr_get_row_inner_height_depth:nNNNN { V }

%% #1: row number, #2: column number
\cs_new_protected:Npn \__tblr_build_vline_segment:nn #1 #2
  {
    \tl_set:Nx \l__tblr_n_tl
      { \__tblr_prop_item:ne { vline } { [#2] / @vline-count } }
    \tl_set:Nx \l__tblr_o_tl
      { \__tblr_prop_item:ne { vline } { [#1][#2] / omit } }
    \tl_if_empty:NTF \l__tblr_o_tl
      {
        \tl_if_empty:NF \l__tblr_n_tl
          { \__tblr_build_vline_segment_real:nn {#1} {#2} }
      }
      { \__tblr_build_vline_segment_omit:nn {#1} {#2} }
  }

%% #1: row number, #2: column number
\cs_new_protected:Npn \__tblr_build_vline_segment_omit:nn #1 #2
  {
    \tl_set:Nx \l__tblr_w_tl
      { \__tblr_prop_item:ne { vline } { [#2] / @vline-width } }
    \skip_horizontal:N \l__tblr_w_tl
  }

%% #1: row number, #2: column number
%% We make every vline segment intersect with first hline below
%% to remove gaps in vlines around multirow cells
\cs_new_protected:Npn \__tblr_build_vline_segment_real:nn #1 #2
  {
    \tl_set:Nx \l__tblr_s_tl
      { \__tblr_prop_item:ne { vline } { [#2] / rulesep } }
    \tl_set:Nx \l__tblr_b_tl
      {
        \__tblr_prop_item:ne { hline }
          { [\int_eval:n{#1 + 1}](1) / @hline-height }
      }
    \tl_if_empty:NT \l__tblr_b_tl { \tl_set:Nn \l__tblr_b_tl { 0pt } }
    \hbox_set:Nn \l__tblr_a_box
      {
        \int_step_inline:nn { \l__tblr_n_tl }
          {
            \tl_set:Nx \l__tblr_w_tl
              { \__tblr_prop_item:ne { vline } { [#2](##1) / @vline-width } }
            \vbox_set_to_ht:Nnn \l__tblr_b_box
              { \dim_eval:n { \l__tblr_row_ht_dim + \l__tblr_row_dp_dim } }
              {
                \tl_set:Nx \l__tblr_f_tl
                  { \__tblr_prop_item:ne { vline } { [#1][#2](##1) / fg } }
                \tl_if_empty:NF \l__tblr_f_tl { \color{\l__tblr_f_tl} }
                \__tblr_get_vline_segment_child:nnnxx {#1} {#2} {##1}
                  { \dim_eval:n { \l__tblr_row_ht_dim } }
                  { \dim_eval:n { \l__tblr_row_dp_dim + \l__tblr_b_tl } }
                \skip_vertical:n {  - \l__tblr_b_tl }
              }
            \box_set_wd:Nn \l__tblr_b_box { \l__tblr_w_tl }
            \box_use:N \l__tblr_b_box
            \skip_horizontal:n { \l__tblr_s_tl }
          }
        \skip_horizontal:n { - \l__tblr_s_tl }
      }
    \vbox_set:Nn \l__tblr_c_box { \box_use:N \l__tblr_a_box }
    \box_set_ht:Nn \l__tblr_c_box { \dim_use:N \l__tblr_row_ht_dim }
    \box_set_dp:Nn \l__tblr_c_box { \dim_use:N \l__tblr_row_dp_dim }
    \box_use:N \l__tblr_c_box
  }

\tl_new:N \l__tblr_cell_rowspan_tl
\tl_new:N \l__tblr_cell_colspan_tl
\dim_new:N \l__tblr_cell_wd_dim
\dim_new:N \l__tblr_cell_ht_dim

\cs_new_protected:Npn \__tblr_build_cell:NN #1 #2
  {
    \int_set:Nn \c@rownum {#1}
    \int_set:Nn \c@colnum {#2}
    \group_begin:
    \tl_set:Nx \l__tblr_w_tl
      { \__tblr_prop_item:ne { column } { [#2] / @col-width } }
    \tl_set:Nx \l__tblr_h_tl
      { \__tblr_prop_item:ne { row } { [#1] / @row-height } }
    \tl_set:Nx \l__tblr_x_tl
      { \__tblr_prop_item:ne { column } { [#2] / leftsep} }
    \tl_set:Nx \l__tblr_y_tl
      { \__tblr_prop_item:ne { column } { [#2] / rightsep } }
    \tl_set:Nx \l__tblr_cell_colspan_tl
      { \__tblr_prop_item:ne { cell } { [#1][#2] / colspan } }
    \tl_if_empty:NTF \l__tblr_cell_colspan_tl
      { \dim_set:Nn \l__tblr_cell_wd_dim { \l__tblr_w_tl } }
      {
        \__tblr_get_span_horizontal_sizes:NNNNN #1 #2
          \l__tblr_o_dim \l__tblr_cell_wd_dim \l__tblr_q_dim
      }
    \tl_set:Nx \l__tblr_cell_rowspan_tl
      { \__tblr_prop_item:ne { cell } { [#1][#2] / rowspan } }
    \tl_if_empty:NTF \l__tblr_cell_rowspan_tl
      { \dim_set:Nn \l__tblr_cell_ht_dim { \l__tblr_h_tl } }
      {
        \__tblr_get_span_vertical_sizes:NNNNN #1 #2
          \l__tblr_r_dim \l__tblr_cell_ht_dim \l__tblr_t_dim
      }
    \__tblr_get_cell_alignments:nn {#1} {#2}
    \__tblr_build_cell_background:NN #1 #2
    \__tblr_build_cell_content:NN #1 #2
    \group_end:
  }

\cs_new_protected:Npn \__tblr_build_cell_content:NN #1 #2
  {
    \hbox_set_to_wd:Nnn \l__tblr_a_box { \l__tblr_cell_wd_dim }
      {
        \tl_if_eq:NnF \g__tblr_cell_halign_tl {l} { \hfil }
        \__tblr_get_cell_text:nn {#1} {#2}
        \tl_if_eq:NnF \g__tblr_cell_halign_tl {r} { \hfil }
      }
    \vbox_set_to_ht:Nnn \l__tblr_b_box { \l__tblr_cell_ht_dim }
      {
        \tl_case:Nn \g__tblr_cell_valign_tl
          {
            \c__tblr_valign_m_tl
              {
                \vfil
                \tl_if_empty:NT \l__tblr_cell_rowspan_tl
                  {
                    \box_set_ht:Nn \l__tblr_a_box
                      { \__tblr_prop_item:ne { row } { [#1] / @row-upper } }
                    \box_set_dp:Nn \l__tblr_a_box
                      { \__tblr_prop_item:ne { row } { [#1] / @row-lower } }
                  }
                \box_use:N \l__tblr_a_box
                \vfil
              }
            \c__tblr_valign_h_tl
              {
                \box_set_ht:Nn \l__tblr_a_box
                  { \__tblr_prop_item:ne { row } { [#1] / @row-head } }
                \box_use:N \l__tblr_a_box
                \vfil
              }
            \c__tblr_valign_f_tl
              {
                \vfil
                \tl_if_empty:NTF \l__tblr_cell_rowspan_tl
                  {
                    \box_set_dp:Nn \l__tblr_a_box
                      { \__tblr_prop_item:ne { row } { [#1] / @row-foot } }
                  }
                  {
                    \box_set_dp:Nn \l__tblr_a_box
                      {
                        \__tblr_prop_item:ne { row }
                          {
                            [\int_eval:n {#1 + \l__tblr_cell_rowspan_tl - 1}]
                            / @row-foot
                          }
                      }
                  }
                \box_use:N \l__tblr_a_box
              }
          }
        \hrule height ~ 0pt %% zero depth
      }
    \vbox_set_to_ht:Nnn \l__tblr_c_box
      { \l__tblr_row_ht_dim - \l__tblr_row_abovesep_dim }
      {
        \box_use:N \l__tblr_b_box
        \vss
      }
    \skip_horizontal:n { \l__tblr_x_tl }
    \box_use:N \l__tblr_c_box
    \skip_horizontal:n { \l__tblr_y_tl - \l__tblr_cell_wd_dim + \l__tblr_w_tl }
  }

\cs_new_protected:Npn \__tblr_build_cell_background:NN #1 #2
  {
    \__tblr_prop_if_in:nxF {cell} { [#1][#2] / omit }
      {
        \group_begin:
        \tl_set:Nx \l__tblr_b_tl
          { \__tblr_prop_item:ne { cell } { [#1][#2] / background } }
        \tl_if_empty:NF \l__tblr_b_tl
          {
            \__tblr_get_cell_background_width:NNN #1 #2 \l_tmpa_dim
            \__tblr_get_cell_background_depth:NNN #1 #2 \l_tmpb_dim
            \__tblr_build_cell_background:nnnn
              { \dim_use:N \l_tmpa_dim }
              { \l__tblr_row_ht_dim }
              { \dim_use:N \l_tmpb_dim }
              { \l__tblr_b_tl }
          }
        \group_end:
      }
  }

%% #1: row number; #2: column number; #3 resulting dimension
\cs_new_protected:Npn \__tblr_get_cell_background_width:NNN #1 #2 #3
  {
    \tl_if_empty:NTF \l__tblr_cell_colspan_tl
      { \dim_set:Nn #3 { \l__tblr_x_tl + \l__tblr_w_tl + \l__tblr_y_tl } }
      {
        \dim_set:Nn #3 { \l__tblr_o_dim + \l__tblr_cell_wd_dim + \l__tblr_q_dim }
      }
  }

%% #1: row number; #2: column number; #3 resulting dimension
\cs_new_protected:Npn \__tblr_get_cell_background_depth:NNN #1 #2 #3
  {
    \tl_if_empty:NTF \l__tblr_cell_rowspan_tl
      { \dim_set_eq:NN #3 \l__tblr_row_dp_dim }
      {
        \dim_set:Nn #3
          {
            \l__tblr_r_dim + \l__tblr_cell_ht_dim
                           + \l__tblr_t_dim - \l__tblr_row_ht_dim
          }
      }
  }

%% #1: width, #2: height, #3: depth, #4: color
\cs_new_protected:Npn \__tblr_build_cell_background:nnnn #1 #2 #3 #4
  {
    \hbox_set:Nn \l__tblr_a_box
      {
        \color {#4}
        \vrule width ~ #1 ~ height ~ #2 ~ depth ~ #3
      }
    \box_set_dp:Nn \l__tblr_a_box { 0pt }
    \box_use:N \l__tblr_a_box
    \skip_horizontal:n { - #1 }
  }

%% #1: row number; #2: column number; #3: dimen register for rowsep above.
%% #4: dimen register for total height; #5: dimen register for rowsep below.
%% We can use \l__tblr_row_item_skip_size_prop which was made before
\cs_new_protected:Npn \__tblr_get_span_vertical_sizes:NNNNN #1 #2 #3 #4 #5
  {
    \dim_set:Nn #3
      { \__tblr_prop_item:ne { row } { [#1] / abovesep } }
    \dim_zero:N #4
    \int_step_inline:nnn { #1 } { #1 + \l__tblr_cell_rowspan_tl - 2 }
      {
        \dim_add:Nn #4
          { \prop_item:Ne \l__tblr_row_item_skip_size_prop { itemskip[##1] } }
      }
    \dim_add:Nn #4
      {
        \prop_item:Ne \l__tblr_row_item_skip_size_prop
          { item[\int_eval:n { #1 + \l__tblr_cell_rowspan_tl - 1 }] }
      }
    \dim_set:Nn #5
      {
        \__tblr_prop_item:ne { row }
          { [\int_eval:n {#1 + \l__tblr_cell_rowspan_tl - 1}] / belowsep }
      }
    %\tl_log:x { cell[#1][#2] ~:~ \dim_use:N #3, \dim_use:N #4, \dim_use:N #5 }
  }

%% #1: row number; #2: column number; #3: dimen register for colsep left.
%% #4: dimen register for total width; #5: dimen register for colsep right.
%% We can use \l__tblr_col_item_skip_size_prop which was made before
%% But when hspan=minimal, there are no itemskip in the prop list.
%% Therefore we need to calculate them from the sizes of items and skips
\cs_new_protected:Npn \__tblr_get_span_horizontal_sizes:NNNNN #1 #2 #3 #4 #5
  {
    \dim_set:Nn #3
      { \__tblr_prop_item:ne { column } { [#2] / leftsep} }
    \dim_zero:N #4
    \int_step_inline:nnn { #2 } { #2 + \l__tblr_cell_colspan_tl - 2 }
      {
        \dim_add:Nn #4
          { \prop_item:Ne \l__tblr_col_item_skip_size_prop { item[##1] } }
        \dim_add:Nn #4
          {
            \prop_item:Ne \l__tblr_col_item_skip_size_prop
              { skip[\int_eval:n { ##1 + 1 }] }
          }
      }
    \dim_add:Nn #4
      {
        \prop_item:Ne \l__tblr_col_item_skip_size_prop
          { item[\int_eval:n { #2 + \l__tblr_cell_colspan_tl - 1 }] }
      }
    \dim_set:Nn #5
      {
        \__tblr_prop_item:ne { column }
          { [\int_eval:n {#2 + \l__tblr_cell_colspan_tl - 1}] / rightsep }
      }
    %\tl_log:x { cell[#1][#2] ~:~ \dim_use:N #3, \dim_use:N #4, \dim_use:N #5 }
  }

%%% --------------------------------------------------------
%%  \section{Tracing Tabularray}
%%% --------------------------------------------------------

\NewDocumentCommand \SetTabularrayTracing { m }
  {
    \keys_set:nn { tblr-set-tracing } {#1}
  }
\cs_new_eq:NN \SetTblrTracing \SetTabularrayTracing

\bool_new:N \g__tblr_tracing_text_bool
\bool_new:N \g__tblr_tracing_command_bool
\bool_new:N \g__tblr_tracing_table_bool
\bool_new:N \g__tblr_tracing_column_bool
\bool_new:N \g__tblr_tracing_row_bool
\bool_new:N \g__tblr_tracing_cell_bool
\bool_new:N \g__tblr_tracing_vline_bool
\bool_new:N \g__tblr_tracing_hline_bool
\bool_new:N \g__tblr_tracing_colspec_bool
\bool_new:N \g__tblr_tracing_rowspec_bool
\bool_new:N \g__tblr_tracing_target_bool
\bool_new:N \g__tblr_tracing_cellspan_bool

\keys_define:nn { tblr-set-tracing }
  {
    +text .code:n = \bool_gset_true:N \g__tblr_tracing_text_bool,
    -text .code:n = \bool_gset_false:N \g__tblr_tracing_text_bool,
    +command .code:n = \bool_gset_true:N \g__tblr_tracing_command_bool,
    -command .code:n = \bool_gset_false:N \g__tblr_tracing_command_bool,
    +table .code:n = \bool_gset_true:N \g__tblr_tracing_table_bool,
    -table .code:n = \bool_gset_false:N \g__tblr_tracing_table_bool,
    +column .code:n = \bool_gset_true:N \g__tblr_tracing_column_bool,
    -column .code:n = \bool_gset_false:N \g__tblr_tracing_column_bool,
    +row .code:n = \bool_gset_true:N \g__tblr_tracing_row_bool,
    -row .code:n = \bool_gset_false:N \g__tblr_tracing_row_bool,
    +cell .code:n = \bool_gset_true:N \g__tblr_tracing_cell_bool,
    -cell .code:n = \bool_gset_false:N \g__tblr_tracing_cell_bool,
    +vline .code:n = \bool_gset_true:N \g__tblr_tracing_vline_bool,
    -vline .code:n = \bool_gset_false:N \g__tblr_tracing_vline_bool,
    +hline .code:n = \bool_gset_true:N \g__tblr_tracing_hline_bool,
    -hline .code:n = \bool_gset_false:N \g__tblr_tracing_hline_bool,
    +colspec .code:n = \bool_gset_true:N \g__tblr_tracing_colspec_bool,
    -colspec .code:n = \bool_gset_false:N \g__tblr_tracing_colspec_bool,
    +rowspec .code:n = \bool_gset_true:N \g__tblr_tracing_rowspec_bool,
    -rowspec .code:n = \bool_gset_false:N \g__tblr_tracing_rowspec_bool,
    +target .code:n = \bool_gset_true:N \g__tblr_tracing_target_bool,
    -target .code:n = \bool_gset_false:N \g__tblr_tracing_target_bool,
    +cellspan .code:n = \bool_gset_true:N \g__tblr_tracing_cellspan_bool,
    -cellspan .code:n = \bool_gset_false:N \g__tblr_tracing_cellspan_bool,
    all .code:n = \__tblr_enable_all_tracings:,
    none .code:n = \__tblr_disable_all_tracings:,
  }

\cs_new_protected_nopar:Npn \__tblr_enable_all_tracings:
  {
    \bool_gset_true:N \g__tblr_tracing_text_bool
    \bool_gset_true:N \g__tblr_tracing_command_bool
    \bool_gset_true:N \g__tblr_tracing_table_bool
    \bool_gset_true:N \g__tblr_tracing_column_bool
    \bool_gset_true:N \g__tblr_tracing_row_bool
    \bool_gset_true:N \g__tblr_tracing_cell_bool
    \bool_gset_true:N \g__tblr_tracing_vline_bool
    \bool_gset_true:N \g__tblr_tracing_hline_bool
    \bool_gset_true:N \g__tblr_tracing_colspec_bool
    \bool_gset_true:N \g__tblr_tracing_rowspec_bool
    \bool_gset_true:N \g__tblr_tracing_target_bool
    \bool_gset_true:N \g__tblr_tracing_cellspan_bool
  }

\cs_new_protected_nopar:Npn \__tblr_disable_all_tracings:
  {
    \bool_gset_false:N \g__tblr_tracing_text_bool
    \bool_gset_false:N \g__tblr_tracing_command_bool
    \bool_gset_false:N \g__tblr_tracing_table_bool
    \bool_gset_false:N \g__tblr_tracing_column_bool
    \bool_gset_false:N \g__tblr_tracing_row_bool
    \bool_gset_false:N \g__tblr_tracing_cell_bool
    \bool_gset_false:N \g__tblr_tracing_vline_bool
    \bool_gset_false:N \g__tblr_tracing_hline_bool
    \bool_gset_false:N \g__tblr_tracing_colspec_bool
    \bool_gset_false:N \g__tblr_tracing_rowspec_bool
    \bool_gset_false:N \g__tblr_tracing_target_bool
    \bool_gset_false:N \g__tblr_tracing_cellspan_bool
  }

\NewDocumentCommand \LogTabularrayTracing { m }
  {
    \keys_set:nn { tblr-log-tracing } {#1}
  }
\cs_new_eq:NN \LogTblrTracing \LogTabularrayTracing

\keys_define:nn { tblr-log-tracing }
  {
    unknown .code:n = \__tblr_log_tracing:N \l_keys_key_str
  }

\cs_new_protected:Npn \__tblr_log_tracing:N #1
  {
    \bool_if:cT { g__tblr_tracing_ #1 _bool }
      { \cs:w __tblr_log_tracing _ #1 : \cs_end: }
  }

\cs_new_protected:Npn \__tblr_log_tracing_text:
  {
    \__tblr_prop_log:n { text }
  }

\cs_new_protected:Npn \__tblr_log_tracing_command:
  {
    \__tblr_prop_log:n { command }
  }

\cs_new_protected:Npn \__tblr_log_tracing_table:
  {
    \__tblr_prop_log:n { table }
  }

\cs_new_protected:Npn \__tblr_log_tracing_column:
  {
    \__tblr_prop_log:n { column }
  }

\cs_new_protected:Npn \__tblr_log_tracing_row:
  {
    \__tblr_prop_log:n { row }
  }

\cs_new_protected:Npn \__tblr_log_tracing_cell:
  {
    \__tblr_prop_log:n { cell }
  }

\cs_new_protected:Npn \__tblr_log_tracing_vline:
  {
    \__tblr_prop_log:n { vline }
  }

\cs_new_protected:Npn \__tblr_log_tracing_hline:
  {
    \__tblr_prop_log:n { hline }
  }

\cs_new_protected:Npn \__tblr_log_tracing_colspec:
  {
    \tl_if_eq:NnT \g__tblr_column_or_row_tl { column }
      { \tl_log:N \g__tblr_expanded_colrow_spec_tl }
  }

\cs_new_protected:Npn \__tblr_log_tracing_rowspec:
  {
    \tl_if_eq:NnT \g__tblr_column_or_row_tl { row }
      { \tl_log:N \g__tblr_expanded_colrow_spec_tl }
  }

\cs_new_protected:Npn \__tblr_log_tracing_target:
  {
    \dim_log:N \l__column_target_dim
    \prop_log:N \l__column_coefficient_prop
    \prop_log:N \l__column_natural_width_prop
    \prop_log:N \l__column_computed_width_prop
  }

\cs_new_protected:Npn \__tblr_log_tracing_cellspan:
  {
    \prop_log:N \l__tblr_col_item_skip_size_prop
    \prop_log:N \l__tblr_col_span_size_prop
    \prop_log:N \l__tblr_row_item_skip_size_prop
    \prop_log:N \l__tblr_row_span_size_prop
    \prop_log:N \l__tblr_row_span_to_row_prop
  }

\cs_new_protected:Npn \__tblr_do_if_tracing:nn #1 #2
  {
    \bool_if:cT { g__tblr_tracing_ #1 _bool } {#2}
  }

\ExplSyntaxOff

