% This is file `diffcoeffx.sty'.
%
% This program is free software; you can redistribute it and/or modify it 
% under the terms of the GNU General Public License as published by the 
% Free Software  Foundation; either version 2 of the License, or (at your
% option) any later version.
% 
% Andrew Parsloe aparsloe@clear.net.nz
%
\RequirePackage{expl3}
\RequirePackage{xparse}
\ProvidesExplPackage{diffcoeffx} {2015/06/27} {1.0} 
  {Write differential coefficients easily.}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\cs_generate_variant:Nn \tl_if_eq:nnF { no }
\cs_generate_variant:Nn \tl_if_eq:nnT { no }
\cs_generate_variant:Nn \tl_if_in:NnTF { NV }
\cs_generate_variant:Nn \prop_pop:NnNT { NV }

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% variables
\tl_new:N \g_diffco_d_ldelim_tl
\tl_new:N \g_diffco_d_rdelim_tl
\tl_new:N \g_diffco_p_ldelim_tl
\tl_new:N \g_diffco_p_rdelim_tl

\tl_new:N \l__diffco_entiand_tl 
\tl_new:N \l__diffco_phantom_tl
\tl_new:N \g_diffco_dop_tl
\tl_new:N \l__diffco_denom_tl
\tl_new:N \l__diffco_coeff_tl

\clist_new:N \l__diffco_order_clist
\tl_new:N \l__diffco_order_spec_tl
\tl_new:N \l__diffco_nudge_tl

\bool_new:N \g__diffco_erent_shape_bool
\bool_new:N \l__diffco_no_parens_bool
\bool_new:N \l__diffco_override_bool
\bool_new:N \l__diffco_ordinary_bool
\bool_new:N \l__diffco_fp_bool
\bool_new:N \l__diffco_vars_noted_bool

\tl_new:N \l__diffco_orders_in_tl
\tl_new:N \l__diffco_order_tl

\tl_new:N \l__diffco_curr_tok_tl
\tl_new:N \l__diffco_curr_term_tl

\int_new:N \l__diffco_curr_tok_int
\int_new:N \l__diffco_curr_state_int
\int_new:N \l__diffco_prev_state_int

\prop_new:N \l__diffco_vars_prop
\tl_new:N \l__diffco_vars_tl
\clist_new:N \l__diffco_vars_clist

\tl_new:N \l__diffco_curr_var_tl
\tl_new:N \l__diffco_curr_qvar_tl
\tl_new:N \l__diffco_nos_tl
\tl_new:N \l__diffco_nos_aux_tl

\tl_new:N \l__diffco_alg_tl
\tl_new:N  \l__diffco_alg_aux_tl
\tl_new:N \l__diffco_alg_expr_tl

\tl_const:Nn \c__diffco_digits_tl { 1234567890 }

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\keys_define:nn { diffcoeffx }
  {
  roman .bool_gset:N  = \g_diffco_roman_bool, 
  roman .default:n = { false }, 
  d-delims .code:n = { 
                      \tl_gset:Nx \g_diffco_d_ldelim_tl { \left \use_i:nn #1 }
                      \tl_gset:Nx \g_diffco_d_rdelim_tl 
                          { \right \use_ii:nn #1 }
                      },
  d-delims .default:n = { .| },
  p-delims .code:n = { 
                      \tl_gset:Nx \g_diffco_p_ldelim_tl { \left \use_i:nn #1 }
                      \tl_gset:Nx \g_diffco_p_rdelim_tl 
                          { \right \use_ii:nn #1 }
                      },
  p-delims .default:n = { () },
  d-nudge .code:n = { \tl_gset:Nn \g_diffco_d_nudge_tl { \mskip #1 mu } },
  d-nudge .default:n = { 0 },
  p-nudge .code:n = { \tl_gset:Nn \g_diffco_p_nudge_tl { \mskip #1 mu } },
  p-nudge .default:n = { -6 },
  sep .code:n = { \tl_gset:Nn \g_diffco_sep_tl { \mskip #1 mu } },
  sep .default:n = { 2 },
  d-sep .code:n = { \tl_gset:Nn \g_diffco_d_sep_tl { \mskip #1 mu } },
  d-sep .default:n = { 1 },
  p-sep .code:n = { \tl_gset:Nn \g_diffco_p_sep_tl { \mskip #1 mu } },
  p-sep .default:n = { 1 }
  }  

\DeclareDocumentCommand \diffset { o }
  {
  \IfValueTF { #1 }
    { 
    \clist_if_empty:nTF { #1 }
      { 
      \keys_set:nn { diffcoeffx } { roman,d-delims,p-delims,
      d-nudge,p-nudge,sep,d-sep,p-sep }
      }
      { 
      \keys_set:nn { diffcoeffx } { #1 } 
      }
    }
    { 
    \keys_set:nn { diffcoeffx } { roman,d-delims,p-delims,
    d-nudge,p-nudge,sep,d-sep,p-sep }
    }
  }
   
% now set the defaults    
\diffset  

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Derivatives
% Ordinary differential coefficient. #1 (star) is the append boolean;
% #2 is the order; #3 the differentiand; #4 slash / boolean;
% #5 the variable of differentiation; #6 the point of evaluation. 
 
\NewDocumentCommand \diff{ s o m t/ m g } 
  {
  \group_begin:
  % roman or italic?
  \bool_if:NF \g__diffco_erent_shape_bool
    { \__diffco_shape:N d }
  
  \IfValueTF { #2 } 
    { 
    \tl_set:Nn \l__diffco_order_tl { \g_diffco_d_sep_tl ^{ #2 } }
    \int_compare:nNnTF { \tl_count:n { #5 } } > { \c_one }
      { \tl_set:Nn \l__diffco_denom_tl { (#5)^{ #2 } } }
      { \tl_set:Nn \l__diffco_denom_tl { #5^{ #2 } } }
    }
    { 
    \tl_set:Nn \l__diffco_order_tl { }
    \tl_if_in:nnTF { #5 } { ^ }
      { \tl_set:Nn \l__diffco_denom_tl { (#5) } }
      { \tl_set:Nn \l__diffco_denom_tl { #5 } } 
    }
  
  % differentiand appended (star) or numerator (no star);
  % slash or fraction?
  \IfBooleanTF { #1 }
    { % differentiand follows diff coeff 
    \__diffco_phantom:n { #3 }
    \tl_set:Nn \l__diffco_coeff_tl
      {
      \IfBooleanTF { #4 }
        { % slash (therefore no phantom)
        (\g_diffco_dop_tl \l__diffco_order_tl 
        /
        \g_diffco_dop_tl \l__diffco_denom_tl )
        \l__diffco_entiand_tl
        }
        { % frac
        \frac { \g_diffco_dop_tl \l__diffco_order_tl \l__diffco_phantom_tl }
            { \g_diffco_dop_tl \l__diffco_denom_tl } \l__diffco_entiand_tl
        }
      }
    }
    { % differentiand in numerator
    \tl_set:Nn \l__diffco_coeff_tl
      {
      \IfBooleanTF { #4 }
        { % slash
        \g_diffco_dop_tl \l__diffco_order_tl #3
        /
        \g_diffco_dop_tl \l__diffco_denom_tl 
        }
        { % frac
        \frac { \g_diffco_dop_tl \l__diffco_order_tl #3  }
          { \g_diffco_dop_tl \l__diffco_denom_tl }
        }
      }
    }
    
  % point of eval?
  \IfNoValueTF { #6 }
    { % no
    \l__diffco_coeff_tl 
    }
    { % yes
    \bool_set_true:N \l__diffco_ordinary_bool
    \__diffco_delims:nnnnN #6;;;;\q_stop \l__diffco_ordinary_bool
    }
  \group_end:  
  }    
  
\NewDocumentCommand \Diff { o m m g }
  {
  \group_begin:
  \__diffco_shape:N D 
  \bool_gset_true:N \g__diffco_erent_shape_bool
  \diff[#1]{#2}{#3}{#4}
  \bool_gset_false:N \g__diffco_erent_shape_bool
  \group_end:
  }
  
\NewDocumentCommand \diffdelta { o m m g } 
  {
  \group_begin:
  \__diffco_shape:N \delta
  \bool_gset_true:N \g__diffco_erent_shape_bool
  \diff[#1]{#2}{#3}{#4}
  \bool_gset_false:N \g__diffco_erent_shape_bool
  \group_end:
  }
  
\NewDocumentCommand \Diffdelta { o m m g }
  {
  \group_begin:
  \__diffco_shape:N \Delta
  \bool_gset_true:N \g__diffco_erent_shape_bool
  \diff[#1]{#2}{#3}{#4}
  \bool_gset_false:N \g__diffco_erent_shape_bool
  \group_end:
  }
    
\cs_new_nopar:Npn \__diffco_shape:N #1    
  {
  \bool_if:NTF \g_diffco_roman_bool
    { \tl_gset:Nn \g_diffco_dop_tl { \mathrm{ #1} } }
    { \tl_gset:Nn \g_diffco_dop_tl { #1 } }
  } 
%%%%%
\cs_new:Npn \__diffco_phantom:n #1
  {
  \bool_if:nTF
      {
      \tl_if_head_eq_meaning_p:nN { #1 } \hfill ||
      \tl_if_head_eq_meaning_p:nN { #1 } \hfil
      }
      {
      \l__diffco_phantom_aux:nn #1\q_stop
      }
      {
      \bool_if:nTF
          {
          \tl_if_head_eq_meaning_p:nN { #1 } \hspace ||
          \tl_if_head_eq_meaning_p:nN { #1 } \hphantom
          }
        {
        \l__diffco_phantom_aux:nnn #1\q_stop
        }
        {
        \tl_set:Nn \l__diffco_entiand_tl { #1 } 
        }
      }
  }

\cs_new:Npn \l__diffco_phantom_aux:nn #1#2\q_stop
  {
  \tl_set:Nn \l__diffco_phantom_tl { #1 }
  \tl_set:Nn \l__diffco_entiand_tl { #2 }
  }
  
\cs_new:Npn \l__diffco_phantom_aux:nnn #1#2#3\q_stop
  {
  \tl_set:Nn \l__diffco_phantom_tl { #1 { #2  } }
  \tl_set:Nn \l__diffco_entiand_tl { #3 }
  }
%%%%%%%%%%%%%%%%%%%%
% Partial differential coefficient. #1 (star) = append differentiand 
% to diff. coeff.(displace from numerator); #2 = comma list of the
%  orders of diff. in each var.; #3 = order override for the 
% numerator; #4 = differentiand; #5 = clist of vars of diff. in
% order; #6 = list of vars held constant
\NewDocumentCommand \diffp{ s o o t/ m m g } 
  {
  \group_begin:
  
  % diff. order list 
  \tl_clear:N \l__diffco_order_clist
  \IfValueT { #2 }
    { 
    \clist_set:Nn \l__diffco_order_clist { #2 }
    \tl_set:Nx \l__diffco_order_spec_tl 
        { [\clist_use:Nn \l__diffco_order_clist { , }]}
    }
  
  % order list override  
  \IfValueT { #3 }
    { 
    \bool_set_true:N \l__diffco_override_bool
    \tl_set:Nn \l__diffco_order_tl { #3 } 
    }
    
  \__diffco_orders:Nn \l__diffco_order_clist { #6 }

  % position of differentiand
  \IfBooleanTF { #1 }
    { % appended
    \__diffco_phantom:n { #5 }
    \tl_set:Nn \l__diffco_coeff_tl
        {
        \frac { \partial
          \__diffco_omit_order_if_i:N \l__diffco_order_tl
          \l__diffco_phantom_tl } { \l__diffco_denom_tl }
        \l__diffco_entiand_tl
        }
    }
    { % in numerator
    \tl_set:Nn \l__diffco_coeff_tl
        {
        \frac { \partial
           \__diffco_omit_order_if_i:N \l__diffco_order_tl
           #5 } { \l__diffco_denom_tl }  
        }
    }
  
  \IfNoValueTF { #7 }
    { 
    \l__diffco_coeff_tl 
    }
    { % hold some vars const./ pt of eval.
    \bool_set_false:N \l__diffco_ordinary_bool
    \__diffco_delims:nnnnN #7;;;;\q_stop \l__diffco_ordinary_bool
    }
  
  \group_end:  
  } % end of \diffp 
  

\NewDocumentCommand \jacob { m m }
  { % need extra braces in denom for clist manipulations
  \diffp{ (#1) }{ { {{(#2)}} } }
  }
 
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%  
% #1 subscript; #2 superscript; #3 delims; #4 nudge override
% #5 detritus; #6 boolean (true = ordinary deriv.; false = partial deriv.)
\cs_new:Npn \__diffco_delims:nnnnN #1;#2;#3;#4;#5\q_stop#6
  {
  \tl_set:Nn \l__diffco_sub_tl { #1 }
  \tl_set:Nn \l__diffco_super_tl { #2 }
  \tl_if_empty:nTF { #3 }
    { % default delims 
    \bool_if:NTF #6
      { % ordinary
      \tl_set_eq:NN \l__diffco_ldelim_tl \g_diffco_d_ldelim_tl
      \tl_set_eq:NN \l__diffco_rdelim_tl \g_diffco_d_rdelim_tl
      \tl_if_empty:nTF { #4 }
        { \tl_set_eq:NN \l__diffco_nudge_tl \g_diffco_d_nudge_tl }
        { \tl_set:Nn \l__diffco_nudge_tl { \mskip #4 mu } }
      }
      { % partial
      \tl_set_eq:NN \l__diffco_ldelim_tl \g_diffco_p_ldelim_tl
      \tl_set_eq:NN \l__diffco_rdelim_tl \g_diffco_p_rdelim_tl
      \tl_if_empty:nTF { #4 }
        { \tl_set_eq:NN \l__diffco_nudge_tl \g_diffco_p_nudge_tl }
        { \tl_set:Nn \l__diffco_nudge_tl { \mskip #4 mu } }
      }
    }
    { % delims specified
    \tl_set:Nn \l__diffco_ldelim_tl { \left \use_i:nn #3 }
    \tl_set:Nn \l__diffco_rdelim_tl { \right \use_ii:nn #3 }
    \tl_if_empty:nTF { #4 }
      { 
      \tl_set:NV \l_tmpa_tl { \use_ii:nn #3 } 
      \__diffco_get_nudge:N \l_tmpa_tl
      }
      { 
      \tl_set:Nx \l__diffco_nudge_tl { \mskip #4 mu } 
      }
    }
  \l__diffco_ldelim_tl \l__diffco_coeff_tl \l__diffco_rdelim_tl
  \tl_if_empty:NF \l__diffco_sub_tl
    { \c_math_subscript_token { \l__diffco_nudge_tl \l__diffco_sub_tl } }
  \tl_if_empty:NF \l__diffco_super_tl
    { \c_math_superscript_token { \l__diffco_nudge_tl \l__diffco_super_tl } }
  }

\cs_new:Npn \__diffco_get_nudge:N #1
  { 
  \exp_args:NV\tl_case:NnF #1
    {
    ) { \tl_set:Nn \l__diffco_nudge_tl { \mskip -6mu } }
    > { \tl_set:Nn \l__diffco_nudge_tl { \mskip -6mu } }
    \} { \tl_set:Nn \l__diffco_nudge_tl { \mskip -4mu } }
    \rangle { \tl_set:Nn \l__diffco_nudge_tl { \mskip -6mu } }
    }
    { \tl_set:Nn \l__diffco_nudge_tl { \mskip 0mu } }
  }

% #1 is the clist of diff. orders
% #2 is the clist of variables
\cs_new:Npn \__diffco_orders:Nn #1#2
  {
  \int_zero:N \l_tmpa_int
  \tl_clear:N \l_tmpb_tl
  \clist_clear:N \l_tmpa_clist
  \clist_clear:N \l_tmpb_clist
  \clist_clear:N \l__diffco_vars_clist

  \clist_set:Nn \l__diffco_vars_clist { #2 }  
  \clist_map_inline:Nn \l__diffco_vars_clist
    {
    \bool_set_false:N \l__diffco_no_parens_bool
    
    % get order of each variable
    \clist_pop:NNF #1 \l_tmpa_tl 
      { \tl_set:Nn \l_tmpa_tl { 1 } }

    \tl_if_eq:noT { 1 } \l_tmpa_tl
      { \bool_set_true:N \l__diffco_no_parens_bool }

    \clist_put_right:NV \l_tmpa_clist \l_tmpa_tl 
    
    % create the denominator
    \tl_clear:N \l_tmpb_tl
    \bool_if:nTF
        {
        \int_compare_p:nNn { \tl_count:n { ##1 } } = { \c_one } 
        ||
        \l__diffco_no_parens_bool
        }
      { \tl_put_right:Nn \l_tmpb_tl { \partial ##1 } }
      { \tl_put_right:Nn \l_tmpb_tl { \partial (##1) } }

    \tl_if_eq:noF { 1 } \l_tmpa_tl
      { 
      \tl_put_right:Nn \l_tmpb_tl { ^ } 
      \tl_put_right:Nx \l_tmpb_tl { { \l_tmpa_tl } }
      }

    \clist_put_right:No \l_tmpb_clist \l_tmpb_tl
    } % end of inline map

  % numerator order 
  \bool_if:NF \l__diffco_override_bool
    {
    \tl_set:Nx \l__diffco_orders_in_tl { \clist_use:Nn \l_tmpa_clist {+} }
    \diff_simplify_orders:NN \l__diffco_orders_in_tl \l__diffco_order_tl
    }

  % denominator
  \tl_set:Nx \l__diffco_denom_tl { \clist_use:Nn
        \l_tmpb_clist { \g_diffco_sep_tl } }
  }

% don't display order 1 superscript
\cs_new:Npn \__diffco_omit_order_if_i:N #1
  {
  \tl_if_eq:noF { 1 } #1
    { ^{ \g_diffco_p_sep_tl \l__diffco_order_tl } }
  }
%%%%%%%%%%%%%%%%%%%%%%
% #1 is expr. in; #2 is expr. out
\cs_new:Npn \diff_simplify_orders:NN #1 #2
  {
  \tl_clear:N \l__diffco_nos_tl
 
  \__diffco_digest_expr:NNNN #1 \l__diffco_nos_tl 
      \l__diffco_alg_tl \l__diffco_vars_prop

  \__diffco_eval_nos:N \l__diffco_nos_tl
  
  \tl_if_empty:NF \l__diffco_alg_tl
    { \__diffco_eval_vars:NN \l__diffco_alg_tl \l__diffco_vars_prop }
  
  \__diffco_manicure_result:NNN \l__diffco_nos_tl \l__diffco_alg_tl #2
  }

% #1 is the expression; #2 (tl) receives the numerical part;
% #3 (tl) receives the algebraic part; #4 (prop) receives the vars
\cs_new:Npn \__diffco_digest_expr:NNNN #1#2#3#4
  {  
  \tl_put_right:Nn #1 { + }
  \tl_set:Nn \l__diffco_curr_term_tl { + }
  \int_set:Nn \l__diffco_curr_state_int { \c_zero }
  \int_set:Nn \l__diffco_prev_state_int { \c_zero }
  \int_set:Nn \l__diffco_curr_tok_int { \c_zero }
  \tl_clear:N #3
  \prop_clear:N #4
    
  \tl_map_inline:Nn #1
    { 
    \tl_set:Nn \l__diffco_curr_tok_tl { ##1 }
    \__diffco_get_curr_index:NN ##1 \l__diffco_curr_tok_int
    \__diffco_compare_states:NNNNN \l__diffco_curr_state_int 
        \l__diffco_curr_tok_int #2#3#4
    }
  \tl_if_empty:NT #2 { \tl_set:Nn #2 { 0 } }
  }

% #1 contains current token (tl); #2 receives current token index (int)
\cs_new:Npn \__diffco_get_curr_index:NN #1#2
  {
  \tl_if_in:NnTF \c__diffco_digits_tl { #1 }
    { % digit
    \int_set:Nn #2 { \c_one }
    }
    {
    \tl_case:NnF #1
      {
      + { \int_set_eq:NN #2 \c_zero }
      - { \int_set_eq:NN #2 \c_zero }
      ^ { \int_set_eq:NN #2 \c_three }
      * { \int_set_eq:NN #2 \c_five }
      \times 
        { 
        \int_set_eq:NN #2 \c_five
        \tl_set:Nn \l__diffco_curr_tok_tl { * }
        }
      ( { \int_set_eq:NN #2 \c_six }
      ) { \int_set_eq:NN #2 \c_seven }
      }  
      {
      \token_if_math_subscript:NTF #1 
        { \int_set_eq:NN #2 { \c_four } }
        { 
        \int_set_eq:NN #2 \c_two }  % var
      }
    }
  }
 
% #1 (int) is curr. state; #2 (int) is curr token index; 
% #3 (tl) receives the numerical part; 
% #4 (tl) receives the algebraic part; 
% #5 (prop) receives the vars
\cs_new:Npn \__diffco_compare_states:NNNNN #1#2#3#4#5
  { 
  \int_case:nn { #1 }
    { 
      { \c_zero } % sgn
        { \__diffco_sgn_transitions:NNNN #1#2#3#4 }
      { \c_one } % num
        { \__diffco_num_transitions:NNNN #1#2#3#4 }
      { \c_two } % alg
        { \__diffco_alg_transitions:NNNNN #1#2#3#4#5 }
      { \c_three } % exp
        { \__diffco_exp_transitions:NN #1#2 }
      { \c_four } % sub (_)
        { \__diffco_sub_transitions:NN #1#2 }
      { \c_five } % mul (*,X)
        { \__diffco_mul_transitions:NNNN #1#2#3#4 }
    }
  }
  
% transitions from the signed state
% #1 = 0, current state; #2 current token index
% #3 store num. part; #4 store alg. part
\cs_new:Npn \__diffco_sgn_transitions:NNNN #1#2#3#4
  {
  \int_case:nnF { #2 }
    {
      { \c_zero } % tok = s
        {
        \tl_if_eq:NNTF \l__diffco_curr_term_tl \l__diffco_curr_tok_tl
          { \tl_set:Nn \l__diffco_curr_term_tl { + } }
          { \tl_set:Nn \l__diffco_curr_term_tl { - } }
        \int_set_eq:NN \l__diffco_prev_state_int #1
        % \int_set:Nn #1 { \c_zero }
        }
      { \c_one } % tok = d
        { \__diffco_term_qvar_append:NN #1 #2 }
      { \c_two } % tok = v
        {
        \tl_put_right:Nn \l__diffco_curr_term_tl { 1 }
        \__diffco_term_var_append:NN #1 #2
        }
      { \c_six } % tok = (
        {
        \tl_put_right:Nn \l__diffco_curr_term_tl { 1*( }
        \tl_put_right:NV #3 \l__diffco_curr_term_tl
        \tl_put_right:NV #4 \l__diffco_curr_term_tl
        \tl_set:Nn \l__diffco_curr_term_tl { + }
        \int_set_eq:NN \l__diffco_prev_state_int #1
        \int_set:Nn #1 { \c_zero }
        }
      { \c_seven } % tok = )
        {
        \tl_put_right:Nn #3 { ) }
        \tl_put_right:Nn #4 { ) }
        \tl_set:Nn \l__diffco_curr_term_tl { + }
        \int_set_eq:NN \l__diffco_prev_state_int #1
        \int_set:Nn #1 { \c_zero }
        }
    }
    {
    \msg_error:nnxxx { diffcoeffx } { order_spec_construct } 
      { \l__diffco_order_spec_tl } { \l__diffco_curr_tok_tl } { sign }
    }
  }
  
% transitions from the numeric state
% #1 = 1, current state; #2 current token index
% #3 store num. part; #4 store alg. part
\cs_new:Npn \__diffco_num_transitions:NNNN #1#2#3#4
  {
  \int_case:nnF { #2 }
    {
      { \c_zero } % tok = s
        {
        \tl_put_right:NV #3 \l__diffco_curr_term_tl 
        \tl_clear:N \l__diffco_curr_qvar_tl
        \tl_clear:N \l__diffco_curr_term_tl
        \__diffco_term_append:NN #1#2
        }
      { \c_one } % tok = d
        { \__diffco_term_qvar_append:NN #1 #2 }
      { \c_two } % tok = v
        { 
        \tl_clear:N \l__diffco_curr_qvar_tl
        \__diffco_term_var_append:NN #1#2 
        }
      { \c_three } % tok = ^
        { \__diffco_term_qvar_append:NN #1#2 }  
      { \c_five } % tok = *
        { 
        \tl_clear:N \l__diffco_curr_qvar_tl
        \__diffco_term_append:NN #1#2
        }
      { \c_six } % tok = (
        {
        \tl_put_right:Nn \l__diffco_curr_term_tl { *( }
        \tl_put_right:NV #3 \l__diffco_curr_term_tl
        \tl_put_right:NV #4 \l__diffco_curr_term_tl
        \tl_set:Nn \l__diffco_curr_term_tl { + }
        \int_set_eq:NN \l__diffco_prev_state_int #1
        \int_set:Nn #1 { \c_zero }
        } 
      { \c_seven } % tok = )
        {
        \tl_put_right:Nn \l__diffco_curr_term_tl { ) }
        \tl_put_right:NV #3 \l__diffco_curr_term_tl
        \tl_put_right:Nn #4 { +0) }
        \tl_set:Nn \l__diffco_curr_term_tl { + }
        \int_set_eq:NN \l__diffco_prev_state_int #1
        \int_set:Nn #1 { \c_zero }
        }
    }
    {
    \msg_error:nnxxx { diffcoeffx } { order_spec_construct } 
      { \l__diffco_order_spec_tl } { \l__diffco_curr_tok_tl } { number }
    }
  }

% transitions from the algebraic state
% #1 = 2, current state; #2 current token index
% #3 store num. part; #4 store alg. part; #5 var store
\cs_new:Npn \__diffco_alg_transitions:NNNNN #1#2#3#4#5
  { 
  \int_case:nnF { #2 }
    {
      { \c_zero } % tok = s
        { 
        \bool_if:NF \l__diffco_vars_noted_bool
          { \__diffco_store_var:NN #5 \l__diffco_curr_var_tl }
        \tl_clear:N \l__diffco_curr_var_tl
        \tl_put_right:NV #4 \l__diffco_curr_term_tl
        \tl_clear:N \l__diffco_curr_term_tl
        \__diffco_term_append:NN #1 #2
        }
      { \c_one } % tok = d
        {
        \int_compare:nNnTF { \l__diffco_prev_state_int } = { \c_three }
          { \__diffco_term_var_append:NN #1 \c_two }
          { 
          \msg_error:nnxxx { diffcoeffx } { order_spec_construct } 
            { \l__diffco_order_spec_tl } { \l__diffco_curr_tok_tl } 
            { variable }
          }
        }
      { \c_two } % tok = v
        { \__diffco_term_var_append:NN #1 #2 }

      { \c_three } % tok = ^
        { \__diffco_term_var_append:NN #1#2 }  
      { \c_four } % tok = _
        { \__diffco_term_var_append:NN #1#2 }
      { \c_seven } % tok = )
        {
        \bool_if:NF \l__diffco_vars_noted_bool
          { \__diffco_store_var:NN #5 \l__diffco_curr_var_tl }
        \tl_clear:N \l__diffco_curr_var_tl 
        \tl_put_right:Nn \l__diffco_curr_term_tl { ) }
        \tl_put_right:Nn #3 { +0) }
        \tl_put_right:NV #4 \l__diffco_curr_term_tl
        \tl_set:Nn \l__diffco_curr_term_tl { + }
        \int_set_eq:NN \l__diffco_prev_state_int #1
        \int_set:Nn #1 { \c_zero }
        }              
    }
    {
    \msg_error:nnxxx { diffcoeffx } { order_spec_construct } 
      { \l__diffco_order_spec_tl } { \l__diffco_curr_tok_tl } { variable }
    }
  }

% transitions from the exponent state
% #1 = 3, current state; #2 current token index
\cs_new:Npn \__diffco_exp_transitions:NN #1#2
  {
  \int_case:nn { \l__diffco_prev_state_int }
    { 
      { \c_one } % prev = num
        { 
        \int_case:nnF { #2 }
          {
            { \c_one } % tok = d
              {
              \tl_clear:N \l__diffco_curr_qvar_tl
              \tl_put_right:Nn \l__diffco_curr_tok_tl { * }
              \__diffco_term_append:NN #1 \c_five
              \bool_set_true:N \l__diffco_fp_bool
              }
            { \c_two } % tok = v
              {
              \__diffco_term_qvar_append:NN #1 \c_two
              \tl_set_eq:NN \l__diffco_curr_var_tl \l__diffco_curr_qvar_tl
              \tl_clear:N \l__diffco_curr_qvar_tl
              }
          }
          {
          \msg_error:nnxxx { diffcoeffx } { order_spec_construct } 
            { \l__diffco_order_spec_tl } { \l__diffco_curr_tok_tl } { ^ }
          }
        }
      { \c_two } % prev = alg
        {
        \int_compare:nNnTF { #2 } < { \c_three }
          { \__diffco_term_var_append:NN #1 \c_two }
          {
          \msg_error:nnxxx { diffcoeffx } { order_spec_construct } 
            { \l__diffco_order_spec_tl } { \l__diffco_curr_tok_tl } { ^ }
          }
        }
    }
  }

% transitions from the subscript state
% #1 = 4, current state; #2 current token index
\cs_new:Npn \__diffco_sub_transitions:NN #1#2  
  {
  \bool_if:nTF 
      {(
      \int_compare_p:nNn { \l__diffco_prev_state_int } = { \c_two } &&
      \int_compare_p:nNn { #2 } < { \c_three }
      )}
    { 
    \__diffco_term_var_append:NN #1 \c_two 
    }
    {
    \msg_error:nnxxx { diffcoeffx } { order_spec_construct } 
      { \l__diffco_order_spec_tl } { \l__diffco_curr_tok_tl } { _ }
    }
  }
  
% transitions from the multiplicative state
% #1 = 5, current state; #2 current token index
% #3 store num. part; #4 store alg. part
\cs_new:Npn \__diffco_mul_transitions:NNNN #1#2#3#4
  { 
  \int_case:nnF { #2 }
    {
      { \c_zero } % tok = s
        {
        \int_compare:nNnTF {\l__diffco_prev_state_int } = { \c_three }
          {
          \tl_put_right:Nn \l__diffco_curr_term_tl { 1 }
          \tl_put_right:NV #3 \l__diffco_curr_term_tl
          \tl_clear:N \l__diffco_curr_term_tl
          \__diffco_term_append:NN #1 #2 
          }
          {
          \msg_error:nnxxx { diffcoeffx } { order_spec_construct } 
            { \l__diffco_order_spec_tl } { \l__diffco_curr_tok_tl } 
            { multiplicative~token }
          }
        }
      { \c_one } % tok = d
        { \__diffco_term_qvar_append:NN #1 #2 }
      { \c_two } % tok = v
        { 
        \tl_put_right:Nn \l__diffco_curr_term_tl {1 }
        \__diffco_term_var_append:NN #1#2
        }
      { \c_five } % tok = *
        { \prg_do_nothing: }  
      { \c_six } % tok = (
        {
        \tl_put_right:Nn \l__diffco_curr_term_tl { ( }
        \tl_put_right:NV #3 \l__diffco_curr_term_tl
        \tl_put_right:NV #4 \l__diffco_curr_term_tl
        \tl_set:Nn \l__diffco_curr_term_tl { + }
        \int_set_eq:NN \l__diffco_prev_state_int #1
        \int_set:Nn #1 { \c_zero }
        }
    }
    {
    \msg_error:nnxxx { diffcoeffx } { order_spec_construct } 
      { \l__diffco_order_spec_tl } { \l__diffco_curr_tok_tl } 
      { multiplicative~token }
    }
  }

%%%%%%%%%%%%%%%%%%%
% term/var/qvar appending routines  
% #1 current state; #2 current token index
\cs_new:Npn \__diffco_term_var_append:NN #1#2
  {
  \tl_put_right:NV \l__diffco_curr_term_tl \l__diffco_curr_tok_tl
  \tl_put_right:NV \l__diffco_curr_var_tl \l__diffco_curr_tok_tl
  \int_set_eq:NN \l__diffco_prev_state_int #1
  \int_set_eq:NN #1 #2
  }
  
% #1 current state; #2 current token index
\cs_new:Npn \__diffco_term_qvar_append:NN #1#2
  {
  \tl_put_right:NV \l__diffco_curr_term_tl \l__diffco_curr_tok_tl
  \tl_put_right:NV \l__diffco_curr_qvar_tl \l__diffco_curr_tok_tl
  \int_set_eq:NN \l__diffco_prev_state_int #1
  \int_set_eq:NN #1 #2
  }  

% #1 current state; #2 current token index
\cs_new:Npn \__diffco_term_append:NN #1#2
  {
  \tl_put_right:NV \l__diffco_curr_term_tl \l__diffco_curr_tok_tl
  \int_set_eq:NN \l__diffco_prev_state_int #1
  \int_set_eq:NN #1 #2
  }  
  
% #1 is prop list; #2 is tl containing var
\cs_new:Npn  \__diffco_store_var:NN #1 #2
  {
  \int_set:Nn \l_tmpa_int { \exp_args:NV \tl_count_tokens:n #2 }
  \prop_get:NVNTF #1 \l_tmpa_int \l_tmpa_tl
    {
    \tl_put_right:Nn \l_tmpa_tl { , }
    \tl_put_right:NV \l_tmpa_tl #2
    \prop_put:NVV #1 \l_tmpa_int \l_tmpa_tl
    }
    {
    \prop_put:NVV #1 \l_tmpa_int #2
    }
  }
  
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% evals numerical expr. in #1, which receives the
% evaluated expr.
\cs_new:Npn \__diffco_eval_nos:N #1 
  {
  \bool_if:NTF \l__diffco_fp_bool
    { % exponent; use l3fp
    \fp_set:Nn \l_tmpa_fp { #1 }
    \int_set:Nn \l_tmpa_int { \fp_use:N \l_tmpa_fp }
    \bool_set_false:N \l__diffco_fp_bool
    }
    { % normal mode; use l3int
    \int_set:Nn \l_tmpa_int { #1 }
    }
  \tl_set:Nx  #1 { \int_use:N \l_tmpa_int } 

  \int_compare:nNnT { \l_tmpa_int } > { \c_zero }
    { \tl_put_left:Nn #1 { + } }
  }

% #1 (tl) has algebraic part & receives eval. expression
% #2 (prop) contains the variables
% The idea is to remove each var but leave coeffs, split into
% num & alg parts, eval. the num part for the net coeff of var
\cs_new:Npn \__diffco_eval_vars:NN #1#2
  { 
  \bool_set_true:N \l__diffco_vars_noted_bool
  \prop_set_eq:NN \l_tmpa_prop #2
  \int_set_eq:NN \l_tmpa_int \c_one
  \bool_until_do:nn { \prop_if_empty_p:N #2 }
    {
    \prop_pop:NVNT #2 \l_tmpa_int \l_tmpa_tl
      {
      \tl_put_left:Nn \l_tmpa_tl { , }
      \tl_put_left:NV \l__diffco_vars_tl  \l_tmpa_tl
      }
    \int_incr:N \l_tmpa_int
    }
    
  \clist_set:NV \l__diffco_vars_clist \l__diffco_vars_tl
  \clist_remove_duplicates:N \l__diffco_vars_clist
  \bool_if:NF \l__diffco_override_bool
    {
    \clist_map_inline:Nn \l__diffco_vars_clist
      {
      \tl_replace_all:Nnn #1 { ##1 } { }
      \__diffco_digest_expr:NNNN #1 \l__diffco_nos_aux_tl 
          \l__diffco_alg_aux_tl \l_tmpa_prop
      \__diffco_eval_nos:N \l__diffco_nos_aux_tl
       
      \__diffco_cumulate_expr:NnN \l__diffco_nos_aux_tl { ##1 }
          \l__diffco_alg_expr_tl
       
      \tl_set_eq:NN #1 \l__diffco_alg_aux_tl
      \tl_clear:N \l__diffco_nos_aux_tl
      \tl_clear:N \l__diffco_alg_aux_tl
      }
    }
  \tl_set_eq:NN #1 \l__diffco_alg_expr_tl
  }

% #1 is num coeff; #2 is var; #3 is cumulating expr. (All tl.)
\cs_new:Npn \__diffco_cumulate_expr:NnN #1#2#3
  { 
  \int_case:nnF { #1 }
    {
      { 0 } { \prg_do_nothing: }
      { +1 } { \tl_put_right:Nn #3 { + #2 } }
      { -1 } { \tl_put_right:Nn #3 { - #2 } }
    }
    {
    \tl_put_right:NV #3 { #1 } 
    \exp_args:NNx \tl_if_in:NnT \c__diffco_digits_tl { \tl_head:n { #2 } }
      { \tl_put_right:Nn #3 { \times} }
    \tl_put_right:Nn #3 { #2 }
    }
  }

% alg. part precedes num. part unless alg. starts 
% with - and nums > 0. #1 nums, #2 alg, #3 output 
\cs_new:Npn \__diffco_manicure_result:NNN #1#2#3
  { % alg < 0 ?
  \exp_args:NV \tl_if_head_eq_charcode:nNTF #2 -
    {
    \int_compare:nNnTF { #1 } > { \c_zero }
      {  % nums > 0
      \tl_concat:NNN #3 #1 #2
      }
      {  % nums =< 0
      \seq_set_split:NnV \l_tmpa_seq { + } #2    
      \seq_pop:NN \l_tmpa_seq \l_tmpa_tl
      \tl_set:Nx \l_tmpb_tl { \seq_use:Nn \l_tmpa_seq { + } }
      \int_compare:nNnF { #1 } = { \c_zero }
        { \tl_concat:NNN \l_tmpa_tl \l_tmpa_tl #1 }
      \tl_concat:NNN #3 \l_tmpb_tl \l_tmpa_tl
      }
    }
    { % alg >= 0
    \int_compare:nNnTF { #1 } = { \c_zero }
      { \tl_set_eq:NN #3 #2 }
      { \tl_concat:NNN #3 #2 #1 }
    }

  % if everything cancels, output 0
  \tl_if_empty:NTF #3 { \tl_set:Nn #3 { 0 } }
    {
    % trim initial + sign
    \exp_args:NV \tl_if_head_eq_charcode:nNT #3 +
      { \tl_set:Nx #3 { \tl_tail:N #3 } }
    }
  }

%%%%%%%%%%%%%%%%%%%%
% messages
\cs_gset:Npn \msg_error_text:n #1
  { #1.sty.~Is~this~construction~intended }
\cs_gset:Npn \msg_info_text:n #1  
  { If~intended~then~use~the~order-override~option~to~specify~the~overall~
      order~of~differentiation. }
  
\msg_new:nnnn { diffcoeffx } { order_spec_construct } 
  { \msg_error_text:n { diffcoeffx }:~#3~followed~by~#2~in~the~order~
      specification~#1~\msg_line_context:? } 
  { \msg_info_text:n { diffcoeffx } }