%% The LaTeX package didec - version 1.0.0 (2024/02/28)
%% didec.sty: numbers with two decimal places
%% !TeX encoding=UTF-8
%%
%% -------------------------------------------------------------------------------------------
%% Copyright (c) 2024-2024 by Prof. Dr. Dr. Thomas F. Sturm <thomas dot sturm at unibw dot de>
%% -------------------------------------------------------------------------------------------
%%
%% This work may be distributed and/or modified under the
%% conditions of the LaTeX Project Public License, either version 1.3
%% of this license or (at your option) any later version.
%% The latest version of this license is in
%%   http://www.latex-project.org/lppl.txt
%% and version 1.3 or later is part of all distributions of LaTeX
%% version 2005/12/01 or later.
%%
%% This work has the LPPL maintenance status `author-maintained'.
%%
%% This work consists of all files listed in README.md
%%
\NeedsTeXFormat{LaTeX2e}[2023-11-01]
\ProvidesExplPackage{didec}{2024/02/28}{1.0.0}
  {Fixed-point arithmetic with two decimal places}

%------- package options -------------------

\DeclareKeys
  {
    int .code:n =
      {
        \prg_set_conditional:Nnn \didec_if_kernel_int: { p, T, TF } { \prg_return_true: }
        \prg_set_conditional:Nnn \didec_if_kernel_fp:  { p, T, TF } { \prg_return_false: }
      },
    fp  .code:n =
      {
        \prg_set_conditional:Nnn \didec_if_kernel_int: { p, T, TF } { \prg_return_false: }
        \prg_set_conditional:Nnn \didec_if_kernel_fp:  { p, T, TF } { \prg_return_true: }
      },
  }

\ProcessKeyOptions

\cs_if_exist:NF \didec_if_kernel_int_p:
  {
    \SetKeys{ int }
  }

%------- integer kernel (int) -------------------

\if_predicate:w \didec_if_kernel_int_p:

\str_const:Nn \c_didec_kernel_str { int }

% <int>
% test on existence of didec variable #1
% adapted from \cs_if_exist:c
\prg_new_conditional:Npnn \didec_if_exist:n #1 { p, T, F, TF }
  {
    \if_cs_exist:w g_didec_var_#1_int \cs_end:
      \exp_after:wN \use_i:nn
    \else:
      \exp_after:wN \use_ii:nn
    \fi:
    {
      \exp_after:wN \if_meaning:w \cs:w g_didec_var_#1_int \cs_end: \scan_stop:
        \prg_return_false:
      \else:
        \prg_return_true:
      \fi:
    }
    \prg_return_false:
  }

% <int>
% #1 integer value
\prg_new_conditional:Npnn \__didec_if_negative:n #1 { TF }
  {
    \if_int_compare:w #1 < \c_zero_int
      \prg_return_true:
    \else:
      \prg_return_false:
    \fi:
  }

% <int>
% #1 didec variable
\prg_new_conditional:Npnn \didec_if_negative:n #1 { p, T, F, TF }
  {
    \if_int_compare:w \didec_to_int:n { #1 } < \c_zero_int
      \prg_return_true:
    \else:
      \prg_return_false:
    \fi:
  }

% <int>
% #1 didec variable
\prg_new_conditional:Npnn \didec_if_positive:n #1 { p, T, F, TF }
  {
    \if_int_compare:w \didec_to_int:n { #1 } > \c_zero_int
      \prg_return_true:
    \else:
      \prg_return_false:
    \fi:
  }

% <int>
% #1 didec variable
\prg_new_conditional:Npnn \didec_if_zero:n #1 { p, T, F, TF }
  {
    \if_int_compare:w \didec_to_int:n { #1 } = \c_zero_int
      \prg_return_true:
    \else:
      \prg_return_false:
    \fi:
  }

% <int>
% #1 didec variable
% #2 relation
% #3 didec variable
\prg_new_conditional:Npnn \didec_compare:nNn #1#2#3 { p, T, F, TF }
  {
    \if_int_compare:w \__didec_eval:n { #1 } #2 \__didec_eval:n { #3 }
      \prg_return_true:
    \else:
      \prg_return_false:
    \fi:
  }

% <int>
% #1 didec variable
\cs_new:Npn \didec_to_int:n #1
  {
    \int_use:c { g_didec_var_#1_int }
  }

% <int>
% #1 didec variable
\cs_new_protected:Npn \didec_new:n #1
  {
    \didec_if_exist:nTF { #1 }
      {
        \msg_error:nne { didec }{ already-defined }{ #1 }
      }
      {
        \int_new:c { g_didec_var_#1_int }
      }
  }

% <int>
% Sets didec variable #1 to the current value of didec variable #2
\cs_new_protected:Npn \didec_gset_eq:nn #1#2
  {
    \int_gset_eq:cc { g_didec_var_#1_int }{ g_didec_var_#2_int }
  }

% <int>
% set didec variable #1 to integer value #2
\cs_new_protected:Npn \__didec_gset_int:nn #1#2
  {
    \int_gset:cn { g_didec_var_#1_int }{ #2 }
  }

% <int>
% add integer #2 to didec variable #1
\cs_new_protected:Npn \__didec_gadd_int:nn #1#2
  {
    \int_gadd:cn { g_didec_var_#1_int }{ #2 }
  }

\fi:

%------- floating point kernel (fp) -------------------

\if_predicate:w \didec_if_kernel_fp_p:

\str_const:Nn \c_didec_kernel_str { fp }

% <fp>
% test on existence of didec variable #1
% adapted from \cs_if_exist:c
\prg_new_conditional:Npnn \didec_if_exist:n #1 { p, T, F, TF }
  {
    \if_cs_exist:w g_didec_var_#1_fp \cs_end:
      \exp_after:wN \use_i:nn
    \else:
      \exp_after:wN \use_ii:nn
    \fi:
    {
      \exp_after:wN \if_meaning:w \cs:w g_didec_var_#1_fp \cs_end: \scan_stop:
        \prg_return_false:
      \else:
        \prg_return_true:
      \fi:
    }
    \prg_return_false:
  }

% <fp>
% #1 large integer value
\prg_new_conditional:Npnn \__didec_if_negative:n #1 { TF }
  {
    \exp_last_unbraced:Ne \token_if_eq_charcode:NNTF { \tl_head:n { #1 } }{ - }
      {
        \prg_return_true:
      }
      {
        \prg_return_false:
      }
  }

% <fp>
% #1 didec variable
\prg_new_conditional:Npnn \didec_if_negative:n #1 { p, T, F, TF }
  {
    \exp_last_unbraced:Ne \token_if_eq_charcode:NNTF { \exp_args:Ne \tl_head:n { \didec_to_int:n { #1 } } }{ - }
      {
        \prg_return_true:
      }
      {
        \prg_return_false:
      }
  }

% <fp>
% #1 didec variable
\prg_new_conditional:Npnn \didec_if_positive:n #1 { p, T, F, TF }
  {
    \exp_args:Ne \__didec_if_positive_token:n { \exp_args:Ne \tl_head:n { \didec_to_int:n { #1 } } }
  }

% <fp>
% #1 didec variable
\cs_new:Npn \__didec_if_positive_token:n #1
  {
    \token_if_eq_charcode:NNTF { #1 }{ - }
      {
        \prg_return_false:
      }
      {
        \token_if_eq_charcode:NNTF { #1 }{ 0 }
          {
            \prg_return_false:
          }
          {
            \prg_return_true:
          }
      }
  }

% <fp>
% #1 didec variable
\prg_new_conditional:Npnn \didec_if_zero:n #1 { p, T, F, TF }
  {
    \exp_last_unbraced:Ne \token_if_eq_charcode:NNTF { \exp_args:Ne \tl_head:n { \didec_to_int:n { #1 } } }{ 0 }
      {
        \prg_return_true:
      }
      {
        \prg_return_false:
      }
  }

% <fp>
% #1 didec variable
% #2 relation
% #3 didec variable
\prg_new_conditional:Npnn \didec_compare:nNn #1#2#3 { p, T, F, TF }
  {
    \fp_compare:nNnTF { \__didec_eval:n { #1 } } #2 { \__didec_eval:n { #3 } }
      {
        \prg_return_true:
      }
      {
        \prg_return_false:
      }
  }

% <fp>
% #1 didec variable
\cs_new:Npn \didec_to_int:n #1
  {
    \fp_use:c { g_didec_var_#1_fp }
  }

% <fp>
% #1 didec variable
\cs_new_protected:Npn \didec_new:n #1
  {
    \didec_if_exist:nTF { #1 }
      {
        \msg_error:nne { didec }{ already-defined }{ #1 }
      }
      {
        \fp_new:c { g_didec_var_#1_fp }
      }
  }

% <fp>
% Sets didec variable #1 to the current value of didec variable #2
\cs_new_protected:Npn \didec_gset_eq:nn #1#2
  {
    \fp_gset_eq:cc { g_didec_var_#1_fp }{ g_didec_var_#2_fp }
  }

% <fp>
% set didec variable #1 to integer value #2
\cs_new_protected:Npn \__didec_gset_int:nn #1#2
  {
    \fp_gset:cn { g_didec_var_#1_fp }{ #2 }
  }

% <fp>
% add integer #2 to didec variable #1
\cs_new_protected:Npn \__didec_gadd_int:nn #1#2
  {
    \fp_gadd:cn { g_didec_var_#1_fp }{ #2 }
  }


\fi:


%------- didec variables -------------------


\cs_new:Npn \__didec_if_exist_else_error:nT #1#2
  {
    \didec_if_exist:nTF { #1 }
      {
        #2
      }
      {
        \msg_error:nne { didec }{ no-variable }{ #1 }
      }
  }


% set didec variable #1 to didec expression #2
% #1 didec variable
% #2 didec expression
\cs_new_protected:Npn \didec_gset:nn #1#2
  {
    \__didec_gset_int:nn { #1 }{ \__didec_eval:n { #2 } }
  }


% set didec variable #1 to didec expression #2
% #1 didec variable
% #2 didec expression
\cs_new_protected:Npn \didec_gset_negative:nn #1#2
  {
    \__didec_gset_int:nn { #1 }{ - \__didec_eval:n { #2 } }
  }


% set didec variable #1 to floating-point expression #2
% #1 didec variable
% #2 floating-point expression
\cs_new_protected:Npn \didec_gset_fp:nn #1#2
  {
    \__didec_gset_int:nn { #1 }{ \fp_to_int:n { (#2)*100 } }
  }


% add didec expression #2 to didec variable #1
% #1 didec variable
% #2 didec expression
\cs_new_protected:Npn \didec_gadd:nn #1#2
  {
    \__didec_gadd_int:nn { #1 }{ \__didec_eval:n { #2 } }
  }


% add sum of didec expression #2 and #3 to didec variable #1
% #1 didec variable
% #2 didec expression
% #3 didec expression
\cs_new_protected:Npn \didec_gadd_to:nnn #1#2#3
  {
    \__didec_gset_int:nn { #1 }{ \__didec_eval:n { #2 } + \__didec_eval:n { #3 } }
  }


% subtract didec expression #2 from didec variable #1
% #1 didec variable
% #2 didec expression
\cs_new_protected:Npn \didec_gsub:nn #1#2
  {
    \__didec_gadd_int:nn { #1 }{ - \__didec_eval:n { #2 } }
  }


% set didec variable #1 to the difference of didec expression #2 and #3
% #1 didec variable
% #2 didec expression
% #3 didec expression
\cs_new_protected:Npn \didec_gsub_to:nnn #1#2#3
  {
    \__didec_gset_int:nn { #1 }{ \__didec_eval:n { #2 } - \__didec_eval:n { #3 } }
  }


% multiply didec variable #1 with floating-point expression #2
% #1 didec variable
% #2 floating-point expression
\cs_new_protected:Npn \didec_gmul_fp:nn #1#2
  {
    \__didec_gset_int:nn { #1 }{ \fp_to_int:n { \didec_to_int:n { #1 } * (#2) } }
  }


% set didec variable #1 to the product of didec variable #2 with floating-point expression #3
% #1 didec variable
% #2 didec variable
% #3 floating-point expression
\cs_new_protected:Npn \didec_gmul_fp_to:nnn #1#2#3
  {
    \__didec_gset_int:nn { #1 }{ \fp_to_int:n { \didec_to_int:n { #2 } * (#3) } }
  }


% divide didec variable #1 by floating-point expression #2
% #1 didec variable
% #2 floating-point expression
\cs_new_protected:Npn \didec_gdiv_fp:nn #1#2
  {
    \__didec_gset_int:nn { #1 }{ \fp_to_int:n { \didec_to_int:n { #1 } / (#2) } }
  }


% set didec variable #1 to the quotient of didec variable #2 with floating-point expression #3
% #1 didec variable
% #2 didec variable
% #3 floating-point expression
\cs_new_protected:Npn \didec_gdiv_fp_to:nnn #1#2#3
  {
    \__didec_gset_int:nn { #1 }{ \fp_to_int:n { \didec_to_int:n { #2 } / (#3) } }
  }


%------- formatting didec variables -------------------


% fp value of didec variable #1
% #1 didec variable
\cs_new:Npn \didec_to_fp:n #1
  {
    \exp_args:Nf \__didec_format:nn { \didec_to_int:n {#1} }{ . }
  }


% fp value of didec variable #1
% #1 didec variable
\cs_new:Npn \didec_to_fc:n #1
  {
    \exp_args:Nf \__didec_format:nn { \didec_to_int:n {#1} }{ , }
  }


% #1 (large) integer value (from didec variable)
% #2 decimal separator
\cs_new:Npn \__didec_format:nn #1#2
  {
    \__didec_if_negative:nTF { #1 }
      { - \exp_args:No \__didec_format_pos:nn { \use_none:n #1 }{ #2 } }
      { \__didec_format_pos:nn { #1 }{ #2 } }
  }

\cs_new:Npn \__didec_format_pos:nn #1#2
  {
    \exp_args:Ne \__didec_format_pos_i:w { \tl_count:n {#1} }{ #2 } #1 \q_nil \q_nil \q_nil \q_nil \q_nil \q_nil \q_stop
  }

\cs_new:Npn \__didec_format_pos_i:w #1#2#3#4#5#6#7#8#9 \q_stop
  {
    \if_case:w #1 \exp_stop_f:
    \or:            0 #2 0 #3   % 1
    \or:            0 #2 #3#4   % 2
    \or:           #3 #2 #4#5   % 3
    \or:         #3#4 #2 #5#6   % 4
    \or:       #3#4#5 #2 #6#7   % 5
    \or:     #3#4#5#6 #2 #7#8   % 6
    \or:   #3#4#5#6#7 #2 #8 \__didec_format_pos_ii:w { 0 }{ #2 } #9 \q_stop  % 7
    \or: #3#4#5#6#7#8 #2    \__didec_format_pos_ii:w { 1 }{ #2 } #9 \q_stop  % 8
    \or: #3#4#5#6#7#8       \__didec_format_pos_ii:w { 2 }{ #2 } #9 \q_stop  % 9
    \or: #3#4#5#6#7#8       \__didec_format_pos_ii:w { 3 }{ #2 } #9 \q_stop  % 10
    \or: #3#4#5#6#7#8       \__didec_format_pos_ii:w { 4 }{ #2 } #9 \q_stop  % 11
    \or: #3#4#5#6#7#8       \__didec_format_pos_ii:w { 5 }{ #2 } #9 \q_stop  % 12
    \or: #3#4#5#6#7#8       \__didec_format_pos_ii:w { 6 }{ #2 } #9 \q_stop  % 13
    \or: #3#4#5#6#7#8       \__didec_format_pos_ii:w { 7 }{ #2 } #9 \q_stop  % 14
    \or: #3#4#5#6#7#8       \__didec_format_pos_ii:w { 8 }{ #2 } #9 \q_stop  % 15
    \or: #3#4#5#6#7#8       \__didec_format_pos_ii:w { 9 }{ #2 } #9 \q_stop  % 16
    \else 0 #2 00 % error
    \fi:
  }

\cs_new:Npn \__didec_format_pos_ii:w #1#2#3#4#5#6#7#8#9 \q_stop
  {
    \if_case:w #1 \exp_stop_f:
                           #3   % 0 = 7 = 13
    \or:                 #3#4   % 1 = 8 = 14
    \or:           #3 #2 #4#5   % 2 = 9 = 15
    \or:         #3#4 #2 #5#6   % 3 = 10 = 16
    \or:       #3#4#5 #2 #6#7   % 4 = 11
    \or:     #3#4#5#6 #2 #7#8   % 5 = 12
    \or:   #3#4#5#6#7 #2 #8 \__didec_format_pos_ii:w { 0 }{ #2 } #9 \q_stop % 6 = 13
    \or: #3#4#5#6#7#8 #2    \__didec_format_pos_ii:w { 1 }{ #2 } #9 \q_stop % 7 = 14
    \or: #3#4#5#6#7#8       \__didec_format_pos_ii:w { 2 }{ #2 } #9 \q_stop % 8 = 15
    \or: #3#4#5#6#7#8       \__didec_format_pos_ii:w { 3 }{ #2 } #9 \q_stop % 9 = 16
    \fi:
  }


% #1 (large) integer value (from didec variable)
% #2 positive extra
% #3 negative extra
\cs_new:Npn \__didec_format_group:nnn #1#2#3
  {
    \__didec_if_negative:nTF { #1 }
      {
        #3
        \l__didec_currency_negative_prefix_tl
        \exp_args:No \__didec_format_group_pos:nnn
          { \use_none:n #1 }
          { \l__didec_decimal_separator_tl }
          { \l__didec_grouping_separator_tl }
        \l__didec_currency_negative_postfix_tl
      }
      {
        #2
        \l__didec_currency_prefix_tl
        \__didec_format_group_pos:nnn
          { #1 }
          { \l__didec_decimal_separator_tl }
          { \l__didec_grouping_separator_tl }
        \l__didec_currency_postfix_tl
      }
  }

\cs_new:Npn \__didec_format_group_pos:nnn #1#2#3
  {
    \exp_args:Ne \__didec_format_group_pos_i:w { \tl_count:n {#1} }{ #2 }{ #3 } #1 \q_nil \q_nil \q_nil \q_nil \q_nil \q_stop
  }

\cs_new:Npn \__didec_format_group_pos_i:w #1#2#3#4#5#6#7#8#9 \q_stop
  {
    \if_case:w #1 \exp_stop_f:
    \or:      0 #2 0 #4   % 1
    \or:      0 #2 #4#5   % 2
    \or:     #4 #2 #5#6   % 3
    \or:   #4#5 #2 #6#7   % 4
    \or: #4#5#6 #2 #7#8   % 5
    \or:     #4 #3 #5#6#7 #2 #8 \__didec_format_group_pos_ii:w { 0 }{ #2 }{ #3 } #9 \q_stop  % 6
    \or:   #4#5 #3 #6#7#8 #2    \__didec_format_group_pos_ii:w { 1 }{ #2 }{ #3 } #9 \q_stop  % 7
    \or: #4#5#6 #3 #7#8         \__didec_format_group_pos_ii:w { 2 }{ #2 }{ #3 } #9 \q_stop  % 8
    \or:     #4 #3 #5#6#7 #3 #8 \__didec_format_group_pos_ii:w { 3 }{ #2 }{ #3 } #9 \q_stop  % 9
    \or:   #4#5 #3 #6#7#8 #3    \__didec_format_group_pos_ii:w { 4 }{ #2 }{ #3 } #9 \q_stop  % 10
    \or: #4#5#6 #3 #7#8         \__didec_format_group_pos_ii:w { 5 }{ #2 }{ #3 } #9 \q_stop  % 11
    \or:     #4 #3 #5#6#7 #3 #8 \__didec_format_group_pos_ii:w { 6 }{ #2 }{ #3 } #9 \q_stop  % 12
    \or:   #4#5 #3 #6#7#8 #3    \__didec_format_group_pos_ii:w { 7 }{ #2 }{ #3 } #9 \q_stop  % 13
    \or: #4#5#6 #3 #7#8         \__didec_format_group_pos_ii:w { 8 }{ #2 }{ #3 } #9 \q_stop  % 14
    \or:     #4 #3 #5#6#7 #3 #8 \__didec_format_group_pos_ii:w { 9 }{ #2 }{ #3 } #9 \q_stop  % 15
    \or:   #4#5 #3 #6#7#8 #3    \__didec_format_group_pos_ii:w { 10 }{ #2 }{ #3 } #9 \q_stop % 16
    \else     1 #2 00 % error
    \fi:
  }

\cs_new:Npn \__didec_format_group_pos_ii:w #1#2#3#4#5#6#7#8#9 \q_stop
  {
    \if_case:w #1 \exp_stop_f:
                     #4    % 0 = 6 = 11
    \or:           #4#5    % 1 = 7 = 12
    \or:     #4 #2 #5#6    % 2 = 8 = 13
    \or:   #4#5 #2 #6#7    % 3 = 9 = 14
    \or: #4#5#6 #2 #7#8    % 4 = 10 = 15
    \or:     #4 #3 #5#6#7 #2 #8 \__didec_format_group_pos_ii:w { 0 }{ #2 }{ #3 } #9 \q_stop % 5 = 11 = 16
    \or:   #4#5 #3 #6#7#8 #2    \__didec_format_group_pos_ii:w { 1 }{ #2 }{ #3 } #9 \q_stop % 6 = 12
    \or: #4#5#6 #3 #7#8         \__didec_format_group_pos_ii:w { 2 }{ #2 }{ #3 } #9 \q_stop % 7 = 13
    \or:     #4 #3 #5#6#7 #3 #8 \__didec_format_group_pos_ii:w { 3 }{ #2 }{ #3 } #9 \q_stop % 8 = 14
    \or:   #4#5 #3 #6#7#8 #3    \__didec_format_group_pos_ii:w { 4 }{ #2 }{ #3 } #9 \q_stop % 9 = 15
    \or: #4#5#6 #3 #7#8         \__didec_format_group_pos_ii:w { 5 }{ #2 }{ #3 } #9 \q_stop % 10 = 16
    \fi:
  }


% #1 didec variable
% #2 stream
\cs_new_protected:Npn \didec_write:nn #1#2
  {
    \iow_now:Ne #2
      {
        \c_backslash_str didecset
          { #1 }
          { \didec_to_fp:n { #1 } }
          \c_percent_str
      }
  }


% #1 didec variable
% #2 stream
\cs_new_protected:Npn \didec_write_check:nn #1#2
  {
    \__didec_if_exist_else_error:nT { #1 }
      {
        \didec_write:nn { #1 }{ #2 }
      }
  }



%------- number scanner  -------------------

% Evaluates #1 and leaves the result in the input stream as an integer denotation
% #1 can be a single didec variable or a number with floating-comma or floating-point
\cs_new:Npn \__didec_eval:n #1
  {
    \exp_args:Ne \__didec_eval_expanded:n { \exp_args:Ne \tl_trim_spaces:n { #1 } }
  }


\cs_new:Npn \__didec_eval_expanded:n #1
  {
    \didec_if_exist:nTF { #1 }
      {
        \didec_to_int:n { #1 }
      }
      {
        \__didec_eval_p_a:w #1.\q_nil \q_stop
      }
  }


\cs_new:Npn \__didec_eval_p_a:w #1.#2#3 \q_stop
  {
    \quark_if_nil:NTF #2
      {
        \__didec_eval_c_a:w #1,\q_nil\q_stop
      }
      {
        #1\__didec_eval_p_b:w #2#3
      }
  }

\cs_new:Npn \__didec_eval_p_b:w #1. \q_nil
  {
    \__didec_eval_c:w #1 00 \q_stop
  }

\cs_new:Npn \__didec_eval_c_a:w #1,#2#3 \q_stop
  {
    \quark_if_nil:NTF #2
      {
        #100
      }
      {
        #1\__didec_eval_c_b:w #2#3
      }
  }

\cs_new:Npn \__didec_eval_c_b:w #1, \q_nil
  {
    \__didec_eval_c:w #1 00 \q_stop
  }

\cs_new:Npn \__didec_eval_c:w #1#2#3 \q_stop
  {
    #1#2
  }


%------- didec variables (extended) -------------------

% #1 didec variable
\cs_new_protected:Npn \didec_show:n #1
  {
    \__didec_if_exist_else_error:nT { #1 }
      {
        \didec_if_kernel_int:T
          {
            \int_show:c { g_didec_var_#1_int }
          }
        \didec_if_kernel_fp:T
          {
            \fp_show:c { g_didec_var_#1_fp }
          }
      }
  }

% #1 didec variable
\cs_new:Npn \didec_to_int_check:n #1
  {
    \__didec_if_exist_else_error:nT { #1 }
      {
        \didec_to_int:n { #1 }
      }
  }

% #1 didec variable
\cs_new:Npn \didec_to_fp_check:n #1
  {
    \__didec_if_exist_else_error:nT { #1 }
      {
        \didec_to_fp:n { #1 }
      }
  }

% #1 didec variable
\cs_new:Npn \didec_to_fc_check:n #1
  {
    \__didec_if_exist_else_error:nT { #1 }
      {
        \didec_to_fc:n { #1 }
      }
  }


% set didec variable #1 to integer value #2
\cs_new_protected:Npn \__didec_gset_int_check:nn #1#2
  {
    \__didec_if_exist_else_error:nT { #1 }
      {
        \__didec_gset_int:nn { #1 }{ #2 }
      }
  }

% Sets didec variable #1 to the current value of didec variable #2
\cs_new_protected:Npn \didec_gset_eq_check:nn #1#2
  {
    \__didec_if_exist_else_error:nT { #1 }
      {
        \__didec_if_exist_else_error:nT { #2 }
          {
            \didec_gset_eq:nn { #1 }{ #2 }
          }
      }
  }

% set didec variable #1 to didec expression #2
% #1 didec variable
% #2 didec expression
\cs_new_protected:Npn \didec_gset_check:nn #1#2
  {
    \__didec_if_exist_else_error:nT { #1 }
      {
        \didec_gset:nn { #1 }{ #2 }
      }
  }

% set didec variable #1 to didec expression #2
% #1 didec variable
% #2 didec expression
\cs_new_protected:Npn \didec_gset_negative_check:nn #1#2
  {
    \__didec_if_exist_else_error:nT { #1 }
      {
        \didec_gset_negative:nn { #1 }{ #2 }
      }
  }

% set didec variable #1 to floating-point expression #2
% #1 didec variable
% #2 floating-point expression
\cs_new_protected:Npn \didec_gset_fp_check:nn #1#2
  {
    \__didec_if_exist_else_error:nT { #1 }
      {
        \didec_gset_fp:nn { #1 }{ #2 }
      }
  }

% add didec expression #2 to didec variable #1
% #1 didec variable
% #2 didec expression
\cs_new_protected:Npn \didec_gadd_check:nn #1#2
  {
    \__didec_if_exist_else_error:nT { #1 }
      {
        \didec_gadd:nn { #1 }{ #2 }
      }
  }

% add sum of didec expression #2 and #3 to didec variable #1
% #1 didec variable
% #2 didec expression
% #3 didec expression
\cs_new_protected:Npn \didec_gadd_to_check:nnn #1#2#3
  {
    \__didec_if_exist_else_error:nT { #1 }
      {
        \didec_gadd_to:nnn { #1 }{ #2 }{ #3 }
      }
  }

% subtract didec expression #2 from didec variable #1
% #1 didec variable
% #2 didec expression
\cs_new_protected:Npn \didec_gsub_check:nn #1#2
  {
    \__didec_if_exist_else_error:nT { #1 }
      {
        \didec_gsub:nn { #1 }{ #2 }
      }
  }

% set didec variable #1 to the difference of didec expression #2 and #3
% #1 didec variable
% #2 didec expression
% #3 didec expression
\cs_new_protected:Npn \didec_gsub_to_check:nnn #1#2#3
  {
    \__didec_if_exist_else_error:nT { #1 }
      {
        \didec_gsub_to:nnn { #1 }{ #2 }{ #3 }
      }
  }

% multiply didec variable #1 with floating-point expression #2
% #1 didec variable
% #2 floating-point expression
\cs_new_protected:Npn \didec_gmul_fp_check:nn #1#2
  {
    \__didec_if_exist_else_error:nT { #1 }
      {
        \didec_gmul_fp:nn { #1 }{ #2 }
      }
  }

% set didec variable #1 to the product of didec variable #2 with floating-point expression #3
% #1 didec variable
% #2 didec variable
% #3 floating-point expression
\cs_new_protected:Npn \didec_gmul_fp_to_check:nnn #1#2#3
  {
    \__didec_if_exist_else_error:nT { #1 }
      {
        \didec_gmul_fp_to:nnn { #1 }{ #2 }{ #3 }
      }
  }

% divide didec variable #1 by floating-point expression #2
% #1 didec variable
% #2 floating-point expression
\cs_new_protected:Npn \didec_gdiv_fp_check:nn #1#2
  {
    \__didec_if_exist_else_error:nT { #1 }
      {
        \didec_gdiv_fp:nn { #1 }{ #2 }
      }
  }

% set didec variable #1 to the quotient of didec variable #2 with floating-point expression #3
% #1 didec variable
% #2 didec variable
% #3 floating-point expression
\cs_new_protected:Npn \didec_gdiv_fp_to_check:nnn #1#2#3
  {
    \__didec_if_exist_else_error:nT { #1 }
      {
        \didec_gdiv_fp_to:nnn { #1 }{ #2 }{ #3 }
      }
  }


%------- formatting didec variables (extended) -------------------

\tl_new:N \l__didec_decimal_separator_tl
\tl_new:N \l__didec_grouping_separator_tl
\tl_new:N \l__didec_currency_prefix_tl
\tl_new:N \l__didec_currency_postfix_tl
\tl_new:N \l__didec_currency_negative_prefix_tl
\tl_new:N \l__didec_currency_negative_postfix_tl

\keys_define:nn { didec }
  {
    decimal-separator  .tl_set:N = \l__didec_decimal_separator_tl,
    grouping-separator .tl_set:N = \l__didec_grouping_separator_tl,
    currency           .code:n =
      {
        \tl_set:No \l__didec_currency_prefix_tl           { \use_i:nn #1  }
        \tl_set:No \l__didec_currency_postfix_tl          { \use_ii:nn #1 }
        \tl_set:No \l__didec_currency_negative_prefix_tl  { \use_i:nn #1 - }
        \tl_set:No \l__didec_currency_negative_postfix_tl { \use_ii:nn #1 }
      },
    currency-negative  .code:n =
      {
        \tl_set:No \l__didec_currency_negative_prefix_tl  { \use_i:nn #1 }
        \tl_set:No \l__didec_currency_negative_postfix_tl { \use_ii:nn #1 }
      },
    german             .meta:n =
      {
        decimal-separator  = {,},
        grouping-separator = {.},
        currency           = {}{},
      },
    english            .meta:n =
      {
        decimal-separator  = {.},
        grouping-separator = {,},
        currency           = {}{},
      },
    french            .meta:n =
      {
        decimal-separator  = {,},
        grouping-separator = {\;},
        currency           = {}{},
      },
    float              .meta:n =
      {
        decimal-separator  = {.},
        grouping-separator = ,
        currency           = {}{},
      },
    color-positive     .code:n =
      {
        \color_set:nn { didec-positive }{ #1 }
      },
    color-negative     .code:n =
      {
        \color_set:nn { didec-negative }{ #1 }
      },
  }

\color_set:nn { didec-gray }{ black!85!white }

\color_set:nnn { didec-green }{ rgb }{ 0,    0.35, 0    }
\color_set:nnn { didec-red   }{ rgb }{ 0.35, 0,    0    }
\color_set:nnn { didec-blue  }{ rgb }{ 0,    0,    0.35 }


\keys_set:nn { didec }
  {
    german,
    color-positive     = didec-green,
    color-negative     = didec-red,
  }

% #1 didec variable
\cs_new:Npn \didec_use:n #1
  {
    \exp_args:Nf \__didec_format_group:nnn { \didec_to_int:n {#1} }{}{}
  }

% #1 didec variable
\cs_new:Npn \didec_use_check:n #1
  {
    \__didec_if_exist_else_error:nT { #1 }
      {
        \didec_use:n { #1 }
      }
  }

% #1 didec variable
\cs_new:Npn \didec_color_use:n #1
  {
    \color_group_begin:
    \exp_args:Nf \__didec_format_group:nnn { \didec_to_int:n {#1} }
      { \color_select:n { didec-positive } }
      { \color_select:n { didec-negative } }
    \color_group_end:
  }

% #1 didec variable
\cs_new:Npn \didec_color_use_check:n #1
  {
    \__didec_if_exist_else_error:nT { #1 }
      {
        \didec_color_use:n { #1 }
      }
  }

% #1 didec variable
\cs_new:Npn \didec_color_inverse_use:n #1
  {
    \color_group_begin:
    \exp_args:Nf \__didec_format_group:nnn { \didec_to_int:n {#1} }
      { \color_select:n { didec-negative } }
      { \color_select:n { didec-positive } }
    \color_group_end:
  }

% #1 didec variable
\cs_new:Npn \didec_color_inverse_use_check:n #1
  {
    \__didec_if_exist_else_error:nT { #1 }
      {
        \didec_color_inverse_use:n { #1 }
      }
  }


%------- messages -------------------

\msg_new:nnnn { didec }{ already-defined }
  { Didec~variable~'#1'~already~defined. }
  { You~tried~to~create~a~new~didec~variable~'#1',~but~it~was~already~defined~elsewhere. }


\msg_new:nnnn { didec }{ no-variable }
  { Unknown~didec~variable~'#1'. }
  { The~didec~variable~'#1'~you~used~is~unknown. }



%------- didec user functions -------------------

% #1 didec variable
\NewExpandableDocumentCommand \didectoint { m }
  {
    \exp_args:Ne \didec_to_int_check:n{ \tl_trim_spaces:n { #1 } }
  }

% #1 didec variable
\NewExpandableDocumentCommand \didectofp { m }
  {
    \exp_args:Ne \didec_to_fp_check:n{ \tl_trim_spaces:n { #1 } }
  }

% #1 didec variable
\NewExpandableDocumentCommand \didectofc { m }
  {
    \exp_args:Ne \didec_to_fc_check:n{ \tl_trim_spaces:n { #1 } }
  }

% #1 didec variable
\NewDocumentCommand \didecnew { m }
  {
    \exp_args:Ne \didec_new:n{ \tl_trim_spaces:n { #1 } }
  }
\NewCommandCopy \didecnewvar \didecnew

% Sets didec variable #1 to the current value of didec variable #2
\NewDocumentCommand \didecsetequal { m m }
  {
    \exp_args:Nee \didec_gset_eq_check:nn { \tl_trim_spaces:n { #1 } }{ \tl_trim_spaces:n { #2 } }
  }

% set didec variable #1 to didec expression #2
% #1 didec variable
% #2 didec expression
\NewDocumentCommand \didecset { m m }
  {
    \exp_args:Ne \didec_gset_check:nn { \tl_trim_spaces:n { #1 } }{ #2 }
  }

% set didec variable #1 to didec expression #2
% #1 didec variable
% #2 didec expression
\NewDocumentCommand \didecsetnegative { m m }
  {
    \exp_args:Ne \didec_gset_negative_check:nn { \tl_trim_spaces:n { #1 } }{ #2 }
  }

% set didec variable #1 to floating-point expression #2
% #1 didec variable
% #2 floating-point expression
\NewDocumentCommand \didecsetfp { m m }
  {
    \exp_args:Ne \didec_gset_fp_check:nn { \tl_trim_spaces:n { #1 } }{ #2 }
  }


% #1 didec variable
% #2 didec expression / variable
% #3 didec expression
\NewDocumentCommand \didecadd { o m m }
  {
    \IfNoValueTF {#1}
      {
        \exp_args:Ne \didec_gadd_check:nn { \tl_trim_spaces:n { #2 } }{ #3 }
      }
      {
        \exp_args:Ne \didec_gadd_to_check:nnn { \tl_trim_spaces:n { #1 } }{ #2 }{ #3 }
      }
  }


% #1 didec variable
% #2 didec expression / variable
% #3 didec expression
\NewDocumentCommand \didecsub { o m m }
  {
    \IfNoValueTF {#1}
      {
        \exp_args:Ne \didec_gsub_check:nn { \tl_trim_spaces:n { #2 } }{ #3 }
      }
      {
        \exp_args:Ne \didec_gsub_to_check:nnn { \tl_trim_spaces:n { #1 } }{ #2 }{ #3 }
      }
  }

% #1 didec variable
% #2 didec variable
% #3 floating-point expression
\NewDocumentCommand \didecmulfp { o m m }
  {
    \IfNoValueTF {#1}
      {
        \exp_args:Ne \didec_gmul_fp_check:nn { \tl_trim_spaces:n { #2 } }{ #3 }
      }
      {
        \exp_args:Nee \didec_gmul_fp_to_check:nnn { \tl_trim_spaces:n { #1 } }{ \tl_trim_spaces:n { #2 } }{ #3 }
      }
  }

% #1 didec variable
% #2 didec variable
% #3 floating-point expression
\NewDocumentCommand \didecdivfp { o m m }
  {
    \IfNoValueTF {#1}
      {
        \exp_args:Ne \didec_gdiv_fp_check:nn { \tl_trim_spaces:n { #2 } }{ #3 }
      }
      {
        \exp_args:Nee \didec_gdiv_fp_to_check:nnn { \tl_trim_spaces:n { #1 } }{ \tl_trim_spaces:n { #2 } }{ #3 }
      }
  }


% Deprecated
\NewDocumentCommand \didecmul { >{\TrimSpaces} O{#2} >{\TrimSpaces} m >{\TrimSpaces} m }
  {
    \__didec_gset_int_check:nn { #1 }{ \fp_to_int:n { \__didec_eval:n { #2 } / 100 * \__didec_eval:n { #3 } } }
  }


% Deprecated
\NewDocumentCommand \didecdiv { >{\TrimSpaces} O{#2} >{\TrimSpaces} m >{\TrimSpaces} m }
  {
    \__didec_gset_int_check:nn { #1 }{ \fp_to_int:n { \__didec_eval:n { #2 } / \__didec_eval:n { #3 } * 100 } }
  }


% Deprecated
\NewDocumentCommand \didecpercent { >{\TrimSpaces} O{#2} >{\TrimSpaces} m >{\TrimSpaces} m }
  {
    \__didec_gset_int_check:nn { #1 }{ \fp_to_int:n { \__didec_eval:n { #2 } / \__didec_eval:n { #3 } * 10000 } }
  }


\cs_new:Npn \__didec_sum:w #1#2+#3#4 \q_stop
  {
    \exp_args:Nne \didec_gadd:nn { #1 }{ #2 }
    \quark_if_nil:NF #3
      {
        \__didec_sum:w {#1} #3#4 \q_stop
      }
  }

\didec_new:n {__@temp@__}

% #1 didec variable
% #2 sum of didec expressions
\NewDocumentCommand \didecsetsum { m m }
  {
    \__didec_if_exist_else_error:nT { #1 }
      {
        \__didec_gset_int:nn {__@temp@__}{ 0 }
        \__didec_sum:w {__@temp@__} #2 + \q_nil \q_stop
        \didec_gset_eq:nn { #1 }{__@temp@__}
      }
  }


% Deprecated
% #1 type (german,english,float)
% #2 didec expression
\NewDocumentCommand \didecprint { O{german} m }
  {
    \didecset{__@temp@__}{ #2 }
    \didecuse [ #1 ]{__@temp@__}
  }


\tl_new:N \g__didec_temp_tl

% Deprecated
% #1 type (german,english,float)
% #2 didec expression
% #3 target macro without backslash
\NewDocumentCommand \didecstore { O{german} m >{\TrimSpaces}m }
  {
    \didecset{__@temp@__}{ #2 }
    \group_begin:
    \keys_set:nn { didec }{ #1 }
    \tl_gset:Ne \g__didec_temp_tl { \didec_use:n { __@temp@__ } }
    \group_end:
    \cs_set_eq:cN { #3 } \g__didec_temp_tl
  }

% #1 key list
% #2 didec variable
\NewDocumentCommand \didecuse { o m }
  {
    \IfNoValueTF {#1}
      {
        \exp_args:Ne \didec_use_check:n { \tl_trim_spaces:n { #2 } }
      }
      {
        \group_begin:
        \keys_set:nn { didec }{ #1 }
        \exp_args:Ne \didec_use_check:n { \tl_trim_spaces:n { #2 } }
        \group_end:
      }
  }

% #1 key list
% #2 didec variable
\NewDocumentCommand \dideccoluse { o m }
  {
    \IfNoValueTF {#1}
      {
        \exp_args:Ne \didec_color_use_check:n { \tl_trim_spaces:n { #2 } }
      }
      {
        \group_begin:
        \keys_set:nn { didec }{ #1 }
        \exp_args:Ne \didec_color_use_check:n { \tl_trim_spaces:n { #2 } }
        \group_end:
      }
  }

% #1 key list
% #2 didec variable
\NewDocumentCommand \dideccolinvuse { o m }
  {
    \IfNoValueTF {#1}
      {
        \exp_args:Ne \didec_color_inverse_use_check:n { \tl_trim_spaces:n { #2 } }
      }
      {
        \group_begin:
        \keys_set:nn { didec }{ #1 }
        \exp_args:Ne \didec_color_inverse_use_check:n { \tl_trim_spaces:n { #2 } }
        \group_end:
      }
  }

% #1 key list
% #2 didec expression
\NewDocumentCommand \didecformat { o m }
  {
    \didec_gset:nn { __@temp@__ }{ #2 }
    \IfNoValueTF {#1}
      {
        \didec_use:n { __@temp@__ }
      }
      {
        \group_begin:
        \keys_set:nn { didec }{ #1 }
        \didec_use:n { __@temp@__ }
        \group_end:
      }
  }

% #1 key list
% #2 didec expression
\NewDocumentCommand \dideccolformat { o m }
  {
    \didec_gset:nn { __@temp@__ }{ #2 }
    \IfNoValueTF {#1}
      {
        \didec_color_use:n { __@temp@__ }
      }
      {
        \group_begin:
        \keys_set:nn { didec }{ #1 }
        \didec_color_use:n { __@temp@__ }
        \group_end:
      }
  }

% #1 key list
% #2 didec expression
\NewDocumentCommand \dideccolinvformat { o m }
  {
    \didec_gset:nn { __@temp@__ }{ #2 }
    \IfNoValueTF {#1}
      {
        \didec_color_inverse_use:n { __@temp@__ }
      }
      {
        \group_begin:
        \keys_set:nn { didec }{ #1 }
        \didec_color_inverse_use:n { __@temp@__ }
        \group_end:
      }
  }

% #1 key list
\NewDocumentCommand \didecsetup { m }
  {
    \keys_set:nn { didec }{ #1 }
  }

% #1 didec variable
% #2 stream
\NewDocumentCommand \didecwrite { m m }
  {
    \exp_args:Ne \didec_write_check:nn { \tl_trim_spaces:n { #1 } }{ #2 }
  }


%------- didec conditionals -------------------

% #1 didec variable
\NewDocumentCommand \didecifnegative { m }
  {
    \exp_args:Ne \didec_if_negative:nTF { \tl_trim_spaces:n { #1 } }
  }
\NewCommandCopy \didecifnegativ \didecifnegative % Deprecated

% #1 didec variable
\NewDocumentCommand \didecifpositive { m }
  {
    \exp_args:Ne \didec_if_positive:nTF { \tl_trim_spaces:n { #1 } }
  }
\NewCommandCopy \didecifpositiv \didecifpositive % Deprecated

% #1 didec variable
\NewDocumentCommand \didecifzero { m }
  {
    \exp_args:Ne \didec_if_zero:nTF { \tl_trim_spaces:n { #1 } }
  }
\NewCommandCopy \didecifnull \didecifzero % Deprecated

% #1 didec expression
% #2 didec expression
\NewDocumentCommand \dideciflowerthan { m m }
  {
    \didec_compare:nNnTF { #1 } < { #2 }
  }

% #1 didec expression
% #2 didec expression
\NewDocumentCommand \didecifequal { m m }
  {
    \didec_compare:nNnTF { #1 } = { #2 }
  }

% #1 didec expression
% #2 didec expression
\NewDocumentCommand \didecifgreaterthan { m m }
  {
    \didec_compare:nNnTF { #1 } > { #2 }
  }
