% This is file `diffcoeff.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{diffcoeff} {2016/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 \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_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 \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_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 { diffcoeff } { 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 { diffcoeff } { roman,d-delims,p-delims, d-nudge,p-nudge,sep,d-sep,p-sep } } { \keys_set:nn { diffcoeff } { #1 } } } { \keys_set:nn { diffcoeff } { roman,d-delims,p-delims, d-nudge,p-nudge,sep,d-sep,p-sep } } } % now set the defaults \diffset %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Derivatives % Ordinary differential coefficient. #1 is the order; #2 the % differentiand; #3 the variable of differentiation; #4 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 } } } % position of 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 } } } } % pt of eval \IfNoValueTF { #6 } { \l__diffco_coeff_tl } { % trailing arg: pt of eval. \tl_if_empty:nTF { #6 } { \l__diffco_coeff_tl } { \g_diffco_d_ldelim_tl \l__diffco_coeff_tl \g_diffco_d_rdelim_tl \c_math_subscript_token { \g_diffco_d_nudge_tl #6 } } } \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 \diffd { 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 \Diffd { 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_append:nn #1#2 { \g_diffco_d_ldelim_tl \l__diffco_coeff_tl \g_diffco_d_rdelim_tl \c_math_subscript_token { \mskip #1 mu #2 } } \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 boolean % #2 = comma list of the orders of diff. in each var.; % #3 = order override for the numerator; #4 = differentiand; % #5 slash / boolean; #6 = comma list of vars of diff.; % #7 = list of vars held const. \NewDocumentCommand \diffp{ s o o m t/ 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 { #4 } \tl_set:Nn \l__diffco_coeff_tl { \IfBooleanTF { #5 } { % slash (therefore no phantom) ( \partial \__diffco_omit_order_if_i:N \l__diffco_order_tl / \l__diffco_denom_tl ) \l__diffco_entiand_tl } { % frac \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 { \IfBooleanTF { #5 } { \partial \__diffco_omit_order_if_i:N \l__diffco_order_tl #4 / \l__diffco_denom_tl } { \frac { \partial \__diffco_omit_order_if_i:N \l__diffco_order_tl #4 } { \l__diffco_denom_tl } } } } \IfNoValueTF { #7 } { \l__diffco_coeff_tl } { \tl_if_empty:nTF { #7 } { \l__diffco_coeff_tl } { \g_diffco_p_ldelim_tl \l__diffco_coeff_tl \g_diffco_p_rdelim_tl \c_math_subscript_token { \g_diffco_p_nudge_tl #7 } } } \group_end: } % end of \diffp \NewDocumentCommand \jacob { m m } { % need extra braces in denom for clist manipulations \diffp{ (#1) }{ { {{(#2)}} } } } %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % #1 is the clist of diff. orders % #2 is the tl of variables (if commas present treat as clist) \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 \l__diffco_orders_in_tl \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_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 state (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 } ( { \msg_error:nnxx { diffcoeff } { unknown_token } { \l__diffco_order_spec_tl } { parentheses } } ^ { \msg_error:nnxx { diffcoeff } { unknown_token } { \l__diffco_order_spec_tl } { superscript } } } { \token_if_math_subscript:NTF #1 { \msg_error:nnxx { diffcoeff } { unknown_token } { \l__diffco_order_spec_tl } { subscript } } { % var \int_set_eq:NN #2 \c_two } } } } % #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 { \int_case:nn { #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:Nn #1 { \c_zero } } { \c_one } % tok = d { \__diffco_term_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_one } % num { \int_case:nn { #2 } { { \c_zero } % tok = s { \tl_put_right:NV #3 \l__diffco_curr_term_tl \tl_clear:N \l__diffco_curr_term_tl \__diffco_term_append:NN #1#2 } { \c_one } % tok = d { \__diffco_term_append:NN #1 \c_one } { \c_two } % tok = v { \__diffco_term_var_append:NN #1#2 } } } { \c_two } % alg { \int_case:nn { #2 } { { \c_zero } % tok = s { \tl_put_right:NV #4 \l__diffco_curr_term_tl \tl_clear:N \l__diffco_curr_term_tl \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 \__diffco_term_append:NN #1#2 } { \c_one } % tok = d { \msg_error:nnx { diffcoeff } { variable_digit_sequence } { \l__diffco_order_spec_tl } } { \c_two } % tok = v { \__diffco_term_var_append:NN #1 \c_two } } } } } % #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 #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 #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 { \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 \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 } \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 { Unimplemented~in~#1.sty: } \cs_gset:Npn \msg_info_text:n #1 { Use~the~order-override~option~to~specify~the~overall~order~of~ differentiation~(or~use~diffcoeffx.sty). } \msg_new:nnnn { diffcoeff } { unknown_token } { \msg_error_text:n { diffcoeff }~#2~in~the~order~specification~#1~ \msg_line_context:. } { \msg_info_text:n { diffcoeff } } \msg_new:nnnn { diffcoeff } { variable_digit_sequence } { \msg_error_text:n { diffcoeff }~a~digit~follows~a~variable~in~the~order~ specification~#1~\msg_line_context:. } { \msg_info_text:n { diffcoeff } }