% This is file `diffcoeff.sty'.
%
% This work may be distributed and/or modified under the conditions
% of the LaTeX Project Public License, either version 1.3c
% (2008-05-04) of this license or any later version; see
% http://www.latex-project.org/lppl.txt
% 
% Andrew Parsloe ajparsloe@gmail.com
%
\RequirePackage{expl3}
\RequirePackage{xparse,l3keys2e,xtemplate}
\ProvidesExplPackage {diffcoeff} {2019/03/10} {3.1} 
  {Write differential coefficients consistently and easily.}
%
\keys_define:nn { diffcoeff }
  { 
    ISO    .bool_gset:N = \g__diffcoeff_ISO_bool,
    def-file .tl_gset:N = \g__diffcoeff_def_tl  ,
    def-file .initial:n = diffcoeff             ,
    def-file .default:n = diffcoeff
  }
\ProcessKeysPackageOptions { diffcoeff }
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\cs_generate_variant:Nn \tl_if_eq:nnTF { nV }

\tl_new:N \l__diffcoeff_oporder_tl
\tl_new:N \l__diffcoeff_differentiand_tl
\tl_new:N \l__diffcoeff_type_tl
\tl_new:N \l__diffcoeff_tot_order_tl
\tl_new:N \l__diffcoeff_curr_num_tl
\tl_new:N \l__diffcoeff_curr_var_tl
\tl_new:N \l__diffcoeff_paren_tl
\tl_new:N \l__diffcoeff_trailing_tl

\tl_const:Nn \c__diffcoeff_digits_tl { 1234567890 }

\seq_new:N \l__diffcoeff_orders_seq
\seq_new:N \l__diffcoeff_vars_seq
\seq_new:N \l__diffcoeff_denom_seq
\seq_new:N \l__diffcoeff_paren_seq

\prop_new:N \l__diffcoeff_vars_prop

\bool_new:N \l__diffcoeff_op_left_bool
\bool_new:N \l__diffcoeff_single_var_bool
\bool_new:N \l__diffcoeff_opwrap_bool
\bool_new:N \l__diffcoeff_integ_bool

\int_new:N \l__diffcoeff_vars_int
\int_new:N \l__diffcoeff_format_int
\int_new:N \l__diffcoeff_curr_tok_int
\int_new:N \l__diffcoeff_curr_state_int
\int_new:N \l__diffcoeff_nos_int
\int_new:N \l__diffcoeff_parenvar_int

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\DeclareObjectType { derivative } { 5 }

% defaults: ordinary deriv. values
\DeclareTemplateInterface { derivative } { DERIV } { 5 }
  {
    op-symbol        : tokenlist = d       ,
    op-symbol-alt    : tokenlist = \KeyValue { op-symbol },
    op-order-sep     : muskip    = 1 mu    ,
    /-op-order-sep   : muskip    = 1 mu    ,
    *-op-left        : boolean   = false   ,
    *-italic-nudge   : muskip    = 3 mu    ,
    */-op-wrap       : boolean   = true    ,
    long-var-wrap    : choice { dv, d(v), (dv) } 
                                 = d(v)    ,
    denom-term-sep   : muskip    = 2 mu    ,
    /-denom-term-sep : muskip    = 1 mu    ,
    term-sep-adjust  : muskip    = -1 mu   ,
    left-delim       : tokenlist = \left . ,
    right-delim      : tokenlist = \right |,
    elbowroom        : muskip    = 0 mu    ,
    subscr-nudge     : muskip    = 0 mu    ,
    /-left-delim     : tokenlist = (       ,
    /-right-delim    : tokenlist = )       ,
    /-elbowroom      : muskip    = 0 mu    ,
    /-subscr-nudge   : muskip    = 0 mu
  }
% #1 order spec(seqvar); #2 order override(tlvar)
% #3 differentiand(tlvar); #4 denominator(seqvar)
% #5 subscript(tlvar)
\DeclareTemplateCode { derivative } { DERIV } { 5 }
  {
    op-symbol        = \l__diffcoeff_op_tl          ,
    op-symbol-alt    = \l__diffcoeff_op_alt_tl      ,
    op-order-sep     = \l__diffcoeff_oporder_muskip ,
    /-op-order-sep   = \l_tmpa_muskip               ,
    *-op-left        = \l__diffcoeff_op_left_bool   ,
    *-italic-nudge   = \l__diffcoeff_opnudge_muskip ,
    */-op-wrap       = \l__diffcoeff_opwrap_bool    ,
    long-var-wrap    = {
                dv   = \cs_set:Npn \__diffcoeff_wrap_longvars:nn #1#2
                          { \l__diffcoeff_op_alt_tl {#2}^{#1} },
                d(v) = \cs_set:Npn \__diffcoeff_wrap_longvars:nn #1#2
                          { \l__diffcoeff_op_alt_tl {(#2)}^{#1} },
                (dv) = \cs_set:Npn \__diffcoeff_wrap_longvars:nn #1#2
                          { (\l__diffcoeff_op_alt_tl {#2)}^{#1} },
             unknown = \cs_set:Npn \__diffcoeff_wrap_longvars:nn #1#2
                          { \l__diffcoeff_op_alt_tl {(#2)}^{#1} }
                       }                            ,
    denom-term-sep   = \l__diffcoeff_varsep_muskip  ,
    /-denom-term-sep = \l_tmpb_muskip               ,
    term-sep-adjust  = \l__diffcoeff_sep_adj_muskip ,
    left-delim       = \l__diffcoeff_ldelim_tl      ,
    right-delim      = \l__diffcoeff_rdelim_tl      ,
    elbowroom        = \l__diffcoeff_elbowrm_muskip ,
    subscr-nudge     = \l__diffcoeff_subnudge_muskip,
    /-left-delim     = \l_tmpa_tl                   ,
    /-right-delim    = \l_tmpb_tl                   ,
    /-elbowroom      = \l_tmpc_muskip               ,
    /-subscr-nudge   = \l_tmpd_muskip
  }
  { 
    \AssignTemplateKeys
    
    \bool_if:NF\l__diffcoeff_integ_bool
      {
        \int_compare:nNnT { \l__diffcoeff_format_int } > { 1 }
          { \__diffcoeff_slash_vals: }
        \__diffcoeff_build:NNNNN #1#2#3#4#5
      }
  }
%%%%%%%%%%
\cs_new:Npn \__diffcoeff_slash_vals:
  {
    \muskip_set:Nn \l__diffcoeff_oporder_muskip  \l_tmpa_muskip
    \muskip_set:Nn \l__diffcoeff_varsep_muskip   \l_tmpb_muskip
    \muskip_set:Nn \l__diffcoeff_elbowrm_muskip  \l_tmpc_muskip
    \muskip_set:Nn \l__diffcoeff_subnudge_muskip \l_tmpd_muskip
    \tl_set:NV \l__diffcoeff_ldelim_tl \l_tmpa_tl
    \tl_set:NV \l__diffcoeff_rdelim_tl \l_tmpb_tl
  }
\cs_new:Npn \__diffcoeff_build:NNNNN #1#2#3#4#5
  { 
    \bool_if:NF \l__diffcoeff_opwrap_bool 
      { \int_set:Nn \l__diffcoeff_format_int { 4 } }
    \exp_args:NV\tl_if_novalue:nF #5 
      { \l__diffcoeff_ldelim_tl \mkern \l__diffcoeff_elbowrm_muskip }
    \bool_if:NTF \l__diffcoeff_single_var_bool
      { 
        \tl_set:Nx \l_tmpa_tl { \seq_use:Nn #4 { , } }
        \__diffcoeff_single:NNN #2 #3 \l_tmpa_tl 
      }
      { 
        \int_zero:N \l_tmpa_int
        \seq_mapthread_function:NNN #1 #4 \__diffcoeff_map_orders:nn
        \__diffcoeff_mixed:NNN #2 #3 \l__diffcoeff_denom_seq 
      }
    \exp_args:NV\tl_if_novalue:nF #5
      { 
        \mkern \l__diffcoeff_elbowrm_muskip \l__diffcoeff_rdelim_tl
        \exp_args:NV\tl_if_empty:nF #5
          {
            \c_math_subscript_token
            { \mskip \l__diffcoeff_subnudge_muskip #5 }
          }
      }
  }
%%%%%%%%%%%%%%%%%%%%
% (ptl) form denom from #1 orders seq & #2 vars seq
\cs_new_protected:Npn \__diffcoeff_map_orders:nn #1#2
  {
    \tl_if_eq:nnTF { #1 } { 1 }
      { \seq_put_right:Nn \l__diffcoeff_denom_seq { \l__diffcoeff_op_alt_tl #2 } }
      {
        \int_compare:nNnTF { \tl_count:n { #2 } } = { 1 }
          { 
            \seq_put_right:Nn \l__diffcoeff_denom_seq 
                { \l__diffcoeff_op_alt_tl #2^{ #1 } } 
          }
          { 
            \seq_put_right:Nn \l__diffcoeff_denom_seq 
                { \__diffcoeff_wrap_longvars:nn { #1\vphantom{)} } { #2 } }
          }
      }
  }
% #1 order(tlvar) ; #2 diff'iand(tlvar); #3 denom(tlvar)
\cs_new:Npn \__diffcoeff_single:NNN #1#2#3
  { 
    \__diffcoeff_numer:N { #1 }
    \__diffcoeff_form_deriv:NNn 
        \l__diffcoeff_oporder_tl
        \l__diffcoeff_differentiand_tl
        { \__diffcoeff_denom_single:NN #1 #3 }
  }
% #1 total order; #2 differentiand; #3 denom seq
\cs_new_protected:Npn \__diffcoeff_mixed:NNN #1#2#3
  {
    \__diffcoeff_numer:N #1
    \__diffcoeff_form_deriv:NNn 
        \l__diffcoeff_oporder_tl
        \l__diffcoeff_differentiand_tl
        { \__diffcoeff_denom_sep:N #3 }
  }
\cs_new:Npn \__diffcoeff_denom_sep:N #1
  {
    \int_zero:N \l_tmpa_int
    \seq_map_inline:Nn #1
      {
        \int_incr:N \l_tmpa_int ##1
        \int_compare:nNnT { \l_tmpa_int } < { \l__diffcoeff_vars_int }
          {
            \seq_pop:NN \l__diffcoeff_orders_seq \l_tmpa_tl
            \str_if_eq:VnTF \l_tmpa_tl { 1 }
              { \mkern \l__diffcoeff_varsep_muskip }
              { 
                \mkern \muskip_eval:n { \l__diffcoeff_varsep_muskip + 
                    \l__diffcoeff_sep_adj_muskip }
              }
          }
      }
  }
% #1(tl) total order
\cs_new_protected:Npn \__diffcoeff_numer:N #1
  { 
    \bool_if:NTF \l__diffcoeff_op_left_bool
      { 
        \tl_set:Nn \l__diffcoeff_oporder_tl 
            { \mskip \l__diffcoeff_opnudge_muskip } 
      }
      { \tl_clear:N \l__diffcoeff_oporder_tl }
    \tl_put_right:No \l__diffcoeff_oporder_tl \l__diffcoeff_op_tl
    \exp_args:NnV \tl_if_eq:nnF { 1 } #1
      { 
        \tl_put_right:Nn \l__diffcoeff_oporder_tl 
            { \mskip \l__diffcoeff_oporder_muskip  ^ { #1 } }
      }
    \bool_if:NT \l__diffcoeff_op_left_bool
      { \tl_put_right:Nn \l__diffcoeff_oporder_tl { \hfill } }
  }
% #1 order; #2 var
\cs_new_protected:Npn \__diffcoeff_denom_single:NN #1#2
  { 
    \tl_if_eq:nVTF { 1 } #1 
      { \l__diffcoeff_op_alt_tl #2 }
      {
        \int_compare:nNnTF { \tl_count:N #2 } = { 1 }
          { \l__diffcoeff_op_alt_tl #2^{ #1 } }
          { \__diffcoeff_wrap_longvars:nn { #1\vphantom) } { #2 } }
      }
  }
% #1 op+order; #2 diff'iand; #3 denom
% 0 frac; 1 frac append; 2 slash ; 3 slash append
\cs_new:Npn \__diffcoeff_form_deriv:NNn #1#2#3
  { 
    \int_case:nn { \l__diffcoeff_format_int }
      {
        { 0 } { \frac { #1 #2 } { #3 } }
        { 1 } { \frac { #1 } { #3 } { #2 } }
        { 2 } { #1 #2 / #3 }
        { 3 } { ( #1 / #3 ) { #2 } }
        { 4 } { #1 / #3 { #2 } }
      }
  }
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% differential
% #1 variant; #2 = space in mu before d (0--9)
% note \group_end placement for \vec{x} etc
\NewDocumentCommand \dl { d.. m }
  {
    \group_begin:
    \bool_set_true:N \l__diffcoeff_integ_bool
    \bool_set_true:N \l__diffcoeff_single_var_bool
    \seq_set_from_clist:Nn \l__diffcoeff_orders_seq { 1 }
    \tl_set:Nn \l__diffcoeff_tot_order_tl { 1 }
    \IfValueT { #1 } { \tl_set:Nn \l__diffcoeff_type_tl { .#1 } }
    \UseInstance { derivative } { ord\l__diffcoeff_type_tl } 
        \l__diffcoeff_orders_seq \l__diffcoeff_tot_order_tl
        \c_empty_tl \c_empty_seq \c_empty_tl
    \tl_if_in:NnTF \c__diffcoeff_digits_tl { #2 }
      { \mkern #2 mu \l__diffcoeff_op_tl \group_end: }
      { \l__diffcoeff_op_tl \group_end: #2 }
  }
\NewDocumentCommand \negmu {} { \mkern -1 mu }
\NewDocumentCommand \nilmu {} { \mkern 0 mu }
\NewDocumentCommand \onemu {} { \mkern 1 mu }
\NewDocumentCommand \twomu {} { \mkern 2 mu }
% derivative
% #1(tl) = name of variant; #2(*)= append diff'iand boolean
% #3(clist) = orders of diff. in each var.; #4(tl) = order override
% #5(tl) = diff'iand; #6( / ) = slash boolean
% #7(clist) = vars of diff.; #8(tl)  = pt of eval./vars held const
% #9(tl) = as #8 (for backwards compat)
\NewDocumentCommand \diff{ d.. s O{1} o m t/ m !O{#9} g } 
  {
    \group_begin:
    \tl_set:Nn \l__diffcoeff_differentiand_tl { #5 }
    \tl_set:Nn \l__diffcoeff_trailing_tl { #8 }
    \seq_set_from_clist:Nn \l__diffcoeff_vars_seq { #7 }
    \seq_set_from_clist:Nn \l__diffcoeff_orders_seq { #3 }
    \int_set:Nn \l__diffcoeff_vars_int 
        { \seq_count:N \l__diffcoeff_vars_seq }
    \int_set:Nn \l_tmpb_int { \seq_count:N \l__diffcoeff_orders_seq }
    \int_compare:nNnTF { \l__diffcoeff_vars_int } = { 1 }
      { \bool_set_true:N \l__diffcoeff_single_var_bool }
      {
        \int_compare:nNnTF { \l__diffcoeff_vars_int } < { \l_tmpb_int }
          {
            \msg_error:nnxxxx { diffcoeff } { numbers-conflict } 
            { \int_use:N \l__diffcoeff_vars_int } { \int_use:N \l_tmpb_int }
            { \seq_use:Nn \l__diffcoeff_orders_seq { , } }
            { \seq_use:Nn \l__diffcoeff_vars_seq { , } }
          }
          { % pad orders seq if nec.
            \int_step_inline:nnnn 
                { 1 + \l_tmpb_int } { 1 } { \l__diffcoeff_vars_int }
                { \seq_put_right:Nn \l__diffcoeff_orders_seq { 1 } }
          }
      }
    % override/calc total order
    \IfValueTF { #4 }
      { \tl_set:Nn \l__diffcoeff_tot_order_tl { #4 } }
      { 
        \bool_if:NTF \l__diffcoeff_single_var_bool
          { \tl_set:Nn \l__diffcoeff_tot_order_tl { #3 } }
          { 
            \__diffcoeff_calc_tot_order:NN \l__diffcoeff_orders_seq
                \l__diffcoeff_tot_order_tl 
          }
      }
    \IfValueT { #1 } { \tl_set:Nn \l__diffcoeff_type_tl { .#1 } }
    \int_zero:N \l__diffcoeff_format_int
    % append? slash?
    \IfBooleanT #2 { \int_incr:N \l__diffcoeff_format_int }
    \IfBooleanT #6 { \int_add:Nn \l__diffcoeff_format_int { 2 } }

    \UseInstance { derivative } { ord\l__diffcoeff_type_tl } 
        \l__diffcoeff_orders_seq
        \l__diffcoeff_tot_order_tl
        \l__diffcoeff_differentiand_tl
        \l__diffcoeff_vars_seq
        \l__diffcoeff_trailing_tl
    \group_end:  
  } % end of \diff 
%%%%%%%%%%%%%%%%%%%
% #2(seq) expr in; #1(tlv) expr out
\cs_new:Npn \__diffcoeff_calc_tot_order:NN #1 #2
  {
    \tl_clear:N \l__diffcoeff_nos_tl
    \exp_args:Nx\__diffcoeff_digest_expr:n { \seq_use:Nn #1 { + } }
    \prop_if_empty:NTF \l__diffcoeff_vars_prop
      { \tl_set:NV #2 \l__diffcoeff_nos_tl }
      { \__diffcoeff_evaluate:NN \l__diffcoeff_vars_prop #2 }
  }
\cs_new:Npn \__diffcoeff_digest_expr:n #1
  {
    \tl_set:Nn \l__diffcoeff_curr_num_tl { + }
    \tl_set:Nn \l__diffcoeff_paren_tl { +1 }
    \tl_set:Nn \l__diffcoeff_nos_tl { 0 }
    \int_zero:N \l__diffcoeff_curr_state_int
    \int_zero:N \l__diffcoeff_curr_tok_int
    \tl_map_inline:nn { #1+ }
      { 
        \__diffcoeff_get_curr_ndx:nN { ##1 } \l__diffcoeff_curr_tok_int
        \__diffcoeff_transitions:nNN { ##1 } 
            \l__diffcoeff_curr_state_int \l__diffcoeff_curr_tok_int 
      }
    \int_set:Nn \l__diffcoeff_nos_int { \l__diffcoeff_nos_tl }
    \tl_set:Nx \l__diffcoeff_nos_tl { \int_use:N \l__diffcoeff_nos_int }
    \int_compare:nNnT { \l__diffcoeff_nos_int } = { 0 }
      { \tl_clear:N \l__diffcoeff_nos_tl }
  }
% #1 curr tok (tl); #2 <== curr tok ndx (int)
\cs_new_protected:Npn \__diffcoeff_get_curr_ndx:nN #1#2
  { 
    \tl_if_in:NnTF \c__diffcoeff_digits_tl { #1 }
      { \int_set:Nn #2 { 1 } } % digit
      { 
        \str_case:nnF { #1 }
          {
            { + } { \int_set:Nn #2 { 0 } }
            { - } { \int_set:Nn #2 { 0 } }
            { ( } { \int_set:Nn #2 { 3 } }
            { ) } { \int_set:Nn #2 { 4 } }
          }
          { \int_set:Nn #2 { 2 } }  % var
      }
  }
% #1(tl) curr tok; #2(int) curr state; #3(int) curr tok ndx
\cs_new:Npn \__diffcoeff_transitions:nNN #1#2#3
  { 
    \int_case:nn { #2 }
      { 
        { 0 } % sgn + -
          { \__diffcoeff_sgn_transitions:nNN { #1 }#2#3 }
        { 1 } % num 
          { \__diffcoeff_num_transitions:nNN { #1 }#2#3 }
        { 2 } % alg
          { \__diffcoeff_alg_transitions:nNN { #1 }#2#3 }
        { 4 } % )
          { \__diffcoeff_rpar_transitions:nNN { #1 }#2#3 }
      }
  }
% transitions from the signed state
% #1(tl) curr tok; #2(int) 0, curr state; #3 curr tok ndx
\cs_new_protected:Npn \__diffcoeff_sgn_transitions:nNN #1#2#3
  {
    \int_case:nnT { #3 }
      {
        { 0 } % tok = s
          {
            \tl_if_eq:nVTF { #1 } \l__diffcoeff_curr_num_tl
              { \tl_set:Nn \l__diffcoeff_curr_num_tl { + } }
              { \tl_set:Nn \l__diffcoeff_curr_num_tl { - } }
          }
        { 1 } % tok = d
          { \tl_put_right:Nn \l__diffcoeff_curr_num_tl { #1 } }
        { 2 } % tok = v
          {
            \tl_put_right:Nn \l__diffcoeff_curr_num_tl { 1 }
            \tl_set:Nn \l__diffcoeff_curr_var_tl { #1 }
          }
        { 3 } % tok = (
          {
            \seq_push:NV \l__diffcoeff_paren_seq \l__diffcoeff_paren_tl
            \tl_put_left:NV \l__diffcoeff_paren_tl \l__diffcoeff_curr_num_tl
            \tl_set:Nn \l__diffcoeff_curr_num_tl { + }
            \int_set:Nn #3 { 0 }
          }
      }
      { \int_set_eq:NN #2 #3 }
  }
% transitions from the numeric state
% #1 = curr. tok.; #2 = 0, curr. state; #3 curr. tok. index
\cs_new_protected:Npn \__diffcoeff_num_transitions:nNN #1#2#3
  {
    \int_case:nnT { #3 }
      {
        { 0 } % tok = s
          {
            \tl_put_right:NV\l__diffcoeff_nos_tl 
              { \l__diffcoeff_paren_tl * \l__diffcoeff_curr_num_tl }
            \tl_set:Nn \l__diffcoeff_curr_num_tl { #1 } 
          }
        { 1 } % tok = d
          { \tl_put_right:Nn \l__diffcoeff_curr_num_tl { #1 } }
        { 2 } % tok = v
          {
            \tl_if_in:nnTF { ^ \times * / \div } { #1 }
              {
                \msg_error:nnxxx { diffcoeff } { order-specification } 
                { \seq_use:Nn \l__diffcoeff_orders_seq { , } } 
                { #1 } { number }
              }
              { \tl_set:Nn \l__diffcoeff_curr_var_tl { #1 } }
          }
        { 3 } % tok = (
          { 
            \seq_push:NV \l__diffcoeff_paren_seq \l__diffcoeff_paren_tl
            \tl_put_left:Nn \l__diffcoeff_paren_tl { * }
            \tl_put_left:NV \l__diffcoeff_paren_tl \l__diffcoeff_curr_num_tl
             \tl_set:Nn \l__diffcoeff_curr_num_tl { + }
            \int_set:Nn #3 { 0 } 
          }
        { 4 } % tok = )
          {
            \tl_put_right:NV \l__diffcoeff_nos_tl 
                { \l__diffcoeff_paren_tl * \l__diffcoeff_curr_num_tl }
          }
      }
      { 
        \int_set_eq:NN #2 #3 }
  }
% transitions from the algebraic state
% #1 = curr. tok.; #2 = 2, curr. state; #3 curr. tok. index
\cs_new:Npn \__diffcoeff_alg_transitions:nNN #1#2#3
  { 
    \int_case:nnT { #3 }
      {
        { 0 } % tok = s
          { 
            \int_compare:nNnTF { \l__diffcoeff_parenvar_int } = { 0 }
              {
                \__diffcoeff_store_var:NNN \l__diffcoeff_curr_var_tl
                    \l__diffcoeff_paren_tl \l__diffcoeff_curr_num_tl
                \tl_clear:N \l__diffcoeff_curr_var_tl
                \tl_set:Nn \l__diffcoeff_curr_num_tl { #1 }
              }
              {
                \tl_put_right:Nn \l__diffcoeff_curr_var_tl { #1 }
                \int_set:Nn #3 { 2 }
              }
          }
        { 1 } % tok = d
          { 
            \tl_put_right:Nn \l__diffcoeff_curr_var_tl { #1 } 
            \int_set:Nn #3 { 2 }
          }
        { 2 } % tok = v
          { \tl_put_right:Nn \l__diffcoeff_curr_var_tl { #1 } }
        { 3 } % tok = (
          {
            \tl_put_right:Nn \l__diffcoeff_curr_var_tl { #1 }
            \int_set:Nn #3 { 2 }
            \int_incr:N \l__diffcoeff_parenvar_int
          }
        { 4 } % tok = )
          {
            \int_compare:nNnTF { \l__diffcoeff_parenvar_int } = { 0 }
              {
                \__diffcoeff_store_var:NNN \l__diffcoeff_curr_var_tl
                    \l__diffcoeff_paren_tl \l__diffcoeff_curr_num_tl
                \tl_clear:N \l__diffcoeff_curr_var_tl
              }
              {
                \tl_put_right:Nn \l__diffcoeff_curr_var_tl { #1 }
                \int_set:Nn #3 { 2 }
                \int_decr:N \l__diffcoeff_parenvar_int
              }
          }
      }
      { \int_set_eq:NN #2 #3 }
  }
% transitions from the ) state
% #1 = curr. tok.; #2 = 4, curr. state; #3 curr. tok. index
\cs_new:Npn \__diffcoeff_rpar_transitions:nNN #1#2#3
  {
    \int_compare:nNnTF { \int_mod:nn { #3 } { 4} } = { 0 }
      { 
        \tl_set:Nn \l__diffcoeff_curr_num_tl { #1 }
        \seq_pop:NN \l__diffcoeff_paren_seq \l__diffcoeff_paren_tl
        \int_set_eq:NN #2 #3
      }
      {
        \msg_error:nnxxx { diffcoeff } { order-specification } 
            { \seq_use:Nn \l__diffcoeff_orders_seq { , } } { #1 } { ) }
      }
  }
% #1 is var. (tlvar); #2 is num. (tlvar); #3 num. coeff. (tlvar)
\cs_new:Npn \__diffcoeff_store_var:NNN #1#2#3
  {
    \prop_get:NVNF \l__diffcoeff_vars_prop #1 \l_tmpa_tl
      { \tl_clear:N \l_tmpa_tl }
    \tl_put_right:NV \l_tmpa_tl { #2 * #3 }
    \prop_put:NVV \l__diffcoeff_vars_prop #1 \l_tmpa_tl
  }
% #1 (propv) key=var, val=coeff; #2 <= total order
\cs_new_protected:Npn \__diffcoeff_evaluate:NN #1#2
  {
    \prop_map_inline:Nn #1 { \seq_put_left:Nn \l_tmpa_seq { ##1 } }
    \seq_sort:Nn \l_tmpa_seq
      {
        \int_compare:nNnTF { \tl_count:n { ##1 } } < { \tl_count:n { ##2 } }
          { \sort_return_same: } { \sort_return_swapped: }
      }
    \seq_map_inline:Nn \l_tmpa_seq
      {
        \prop_pop:NnN #1 { ##1 } \l_tmpb_tl
        \seq_put_right:Nx \l_tmpb_seq { \int_eval:n \l_tmpb_tl }
       }
    \tl_clear:N \l_tmpa_tl
    \seq_mapthread_function:NNN \l_tmpa_seq \l_tmpb_seq 
        \__diffcoeff_tot_order:nn
    \exp_args:NV \tl_if_head_eq_charcode:nNTF \l_tmpa_tl +
      { 
        \tl_set:Nx \l_tmpb_tl { \tl_tail:N \l_tmpa_tl }
        \int_compare:nNnT { \l__diffcoeff_nos_int } > { 0 }
          { \tl_put_left:Nn \l__diffcoeff_nos_tl { + } }
         \tl_concat:NNN #2 \l_tmpb_tl \l__diffcoeff_nos_tl
      }
      { 
        \int_compare:nNnTF { \l__diffcoeff_nos_int } > { 0 }
          { \tl_concat:NNN #2 \l__diffcoeff_nos_tl \l_tmpa_tl }
          { \tl_concat:NNN #2 \l_tmpa_tl \l__diffcoeff_nos_tl }
      }
    \tl_set_rescan:Nno #2 { } #2
  }
\cs_new:Npn \__diffcoeff_tot_order:nn #1#2
  { 
    \int_compare:nNnTF { #2 } > { 0 }
      { 
        \int_compare:nNnTF { #2 } = { 1 }
          { \tl_put_left:Nn \l_tmpa_tl { +#1 } }
          { \tl_put_left:Nn \l_tmpa_tl { +#2#1 } }
      }
      {
        \int_compare:nNnT { #2 } < { 0 }
          { 
            \int_compare:nNnTF { #2 } = { -1 }
              { \tl_put_right:Nn \l_tmpa_tl { -#1 } }
              { \tl_put_right:Nn \l_tmpa_tl { #2#1 } }
          }
      }
  }
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% create new instances or edit existing ones
% #1 = name; #2 = key-value list
\NewDocumentCommand \diffdef { > { \TrimSpaces } m m }
  { 
    \tl_if_empty:nTF { #1 }
      { 
        \EditTemplateDefaults { derivative } 
            { DERIV } { #2 }
        \EditInstance { derivative } { ord } { #2 }
      }
      {
        \IfInstanceExistTF { derivative } { ord.#1 }
          { \EditInstance { derivative } { ord.#1 } { #2 } } 
          { 
            \DeclareInstance { derivative } { ord.#1 } 
                { DERIV } { #2 } 
          }
      }
  }
% relic from version 1
\NewDocumentCommand \diffset { o } 
  { \msg_warning:nn { diffcoeff } { obsolete } }

% ordinary & D
\DeclareInstance { derivative } { ord } { DERIV } { }
\bool_if:NTF \g__diffcoeff_ISO_bool
  {
    \diffdef {}
      {
        op-symbol    = \mathrm{d},    
        op-order-sep = 0 mu      ,
        left-delim   = \left (   ,
        right-delim  = \right )  ,
        subscr-nudge = -6 mu
      }
    \diffdef { D } { op-symbol = \mathrm{D} }
  }
  { \diffdef { D } { op-symbol = D } }

% partial
\diffdef { p }
  {
    op-symbol    = \partial ,
    left-delim   = \left (  ,
    right-delim  = \right ) ,
    subscr-nudge = -6 mu
  }
\NewDocumentCommand \diffp { } { \diff.p. }

% delta
\diffdef { delta }
  { 
    op-symbol    = \delta ,
    op-order-sep = 0 mu
  }
\diffdef { Delta }
  { 
    op-symbol    = \Delta ,
    op-order-sep = 0 mu
  }
% for compatibility with version 1
\NewDocumentCommand \Diff  { } { \diff.D.     }
\NewDocumentCommand \diffd { } { \diff.delta. }
\NewDocumentCommand \Diffd { } { \diff.Delta. }

% user-defined
\file_if_exist:nT { \g__diffcoeff_def_tl.def }
  { \file_input:n { \g__diffcoeff_def_tl.def } }
%%%%%%%%%%%%%%%%%%%%
% Jacobian
\NewDocumentCommand \jacob { m t/ m }
  {
    \group_begin:
    \IfBooleanTF #2
      { \partial(#1) / \partial(#3) }
      { \frac{ \partial(#1) } { \partial(#3) } }
    \group_end:
  }
%%%%%%%%%%%%%%%%%%%%
% messages
\msg_new:nnn { diffcoeff } { order-specification } 
  {
    #3~followed~by~#2~in~the~order~specification~[#1]~\msg_line_context:.~
    Diffcoeff~cannot~calculate~the~overall~order~of~differentiation~in~
    this~case.~Use~the~order-override~option~to~enter~the~overall~order.
  }
\msg_new:nnn { diffcoeff } { obsolete } 
  {
    Obsolete~command:~\diffset is~superseded~by~the~\diffdef\ 
    command.~\msg_see_documentation_text:n { diffcoeff }
  }
\msg_new:nnn { diffcoeff } { numbers-conflict } 
  { 
    #2~orders~of~differentiation~specified~for~#1~variables;~
    orders~[#3]\ (\msg_line_context:)~for~variables~#4.
  }
% end of file diffcoeff.sty