% ----------------------------------------------------------------------------
% the XSIM package
% 
%   eXercise Sheets IMproved
% 
% ----------------------------------------------------------------------------
% Clemens Niederberger
% Web:    https://github.com/cgnieder/xsim
% E-Mail: clemens@cnltx.de
% ----------------------------------------------------------------------------
% Copyright 2017--2022 Clemens Niederberger
% 
% This work may be distributed and/or modified under the
% conditions of the LaTeX Project Public License, either version 1.3c
% 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.3c or later is part of all distributions of LaTeX
% version 2008/05/04 or later.
% 
% This work has the LPPL maintenance status `maintained'.
% 
% The Current Maintainer of this work is Clemens Niederberger.
% ----------------------------------------------------------------------------
% If you have any ideas, questions, suggestions or bugs to report, please
% feel free to contact me.
% ----------------------------------------------------------------------------
\RequirePackage {l3keys2e,xsimverb}

\ExplSyntaxOn

\tl_const:Nn \c_xsim_date_tl                 {2022/02/12}
\tl_const:Nn \c_xsim_version_major_number_tl {0}
\tl_const:Nn \c_xsim_version_minor_number_tl {21}
\tl_const:Nn \c_xsim_version_subrelease_tl   {}
\tl_const:Nx \c_xsim_version_number_tl
  {
    \c_xsim_version_major_number_tl .
    \c_xsim_version_minor_number_tl
  }
\tl_const:Nx \c_xsim_version_tl
  {
    \c_xsim_version_number_tl
    \c_xsim_version_subrelease_tl
  }
\tl_const:Nn \c_xsim_info_tl {eXercise ~ Sheets ~ IMproved}

\ProvidesExplPackage
  {xsim}
  {\c_xsim_date_tl}
  {\c_xsim_version_tl}
  {\c_xsim_info_tl}

% --------------------------------------------------------------------------
% options, information

\cs_new_protected:Npn \xsim_bool_provide:N #1
  { \bool_if_exist:NF #1 { \bool_new:N #1 } }
  
\xsim_bool_provide:N \g__xsim_final_bool
\xsim_bool_provide:N \g__xsim_verbose_bool
\xsim_bool_provide:N \g_xsim_clear_aux_bool
\xsim_bool_provide:N \g__xsim_write_to_file_bool
\xsim_bool_provide:N \g_xsim_use_aux_bool
\xsim_bool_provide:N \g__xsim_rerun_bool
\xsim_bool_provide:N \g__xsim_debug_bool
\xsim_bool_provide:N \g__xsim_blank_bool

\keys_define:nn {xsim/package}
  {
    final     .bool_gset:N   = \g__xsim_final_bool ,
    verbose   .bool_gset:N   = \g__xsim_verbose_bool ,
    debug     .bool_gset:N   = \g__xsim_debug_bool ,
    clear-aux .bool_gset:N   = \g_xsim_clear_aux_bool ,
    use-files .bool_gset:N   = \g__xsim_write_to_file_bool ,
    use-files .initial:n     = false ,
    no-files  .choice: ,
    no-files / true  .meta:n = { use-files = false } ,
    no-files / false .meta:n = { use-files = true } ,
    no-files  .default:n     = true ,
    use-aux   .bool_gset:N   = \g_xsim_use_aux_bool ,
    use-aux   .initial:n     = false ,
    blank     .bool_gset:N   = \g__xsim_blank_bool ,
    blank     .initial:n     = false
  }

\ProcessKeysPackageOptions {xsim/package}

\keys_define:nn {xsim/package}
  {
    final     .code:n = \msg_error:nnn {xsim} {load-time-option} {final} ,
    verbose   .code:n = \msg_error:nnn {xsim} {load-time-option} {verbose} ,
    debug     .code:n = \msg_error:nnn {xsim} {load-time-option} {debug} ,
    clear-aux .code:n = \msg_error:nnn {xsim} {load-time-option} {clear-aux} ,
    use-files .code:n = \msg_error:nnn {xsim} {load-time-option} {use-files} ,
    no-files  .code:n = \msg_error:nnn {xsim} {load-time-option} {no-files} ,
    use-aux   .code:n = \msg_error:nnn {xsim} {load-time-option} {use-aux} ,
    blank     .code:n = \msg_error:nnn {xsim} {load-time-option} {blank}
  }

% --------------------------------------------------------------------------

% messages:
\msg_new:nnn {xsim} {load-time-option}
  {
    `#1'~ is~ a~ load-time~ option! \\
    You~ cannot~ set~ it~ with~ \token_to_str:N \xsimsetup ! \\
    You~ need~ to~ use~ \token_to_str:N \usepackage [#1] {xsim} .
  }

\msg_new:nnn {xsim} {rerun}
  {
    Exercise~ properties~ may~ have~ changed.~ Rerun~ to~ get~ them~
    synchronized.
  }

\msg_new:nnn {xsim} {verbose} { #1 ~( \msg_line_context: ) }

% --------------------------------------------------------------------------

\cs_new_protected:Npn \xsim_do_rerun:
  { \xsim_if_rerun:T { \msg_warning:nn {xsim} {rerun} } }

% --------------------------------------------------------------------------
% MODULE mechanism
\msg_new:nnn {xsim} {module-missing}
  {
    You've~ requested~ the~ xsim~ module~ `#1'~ but~ it~ appears~ to~
    be~ missing~ on~ your~ system.~ Maybe~ you've~ misspelled~ the~ name?~
    Loading~ the~ module~ will~ abort~ \msg_line_context:
  }

\msg_new:nnn {xsim} {loading-module}
  { Loading~ module~ `#1'~ ... }

\msg_new:nnn {xsim} {wrong-module-name}
  { The~ module~ file~ name~ is~ `#2'~ but~ it~ provides~ module~ `#1'. }

\msg_new:nnn {xsim} {forbidden-module}
  {
    You've~ requested~ the~ xsim~ module~ `modules'~ \msg_line_context: .~
    This~ module~ is~ a~ pseudo~ module~ which~ may~ not~ be~ loaded~ through~
    the~ module~ loading~ mechansim.
  }

% ----------------------------------------------------------------------------

\cs_generate_variant:Nn \str_if_eq:nnF {xx}

\tl_const:Nn \c__xsim_module_extension_tl {code.tex}
\tl_const:Nn \c__xsim_module_prefix_tl    {xsim}

\cs_new_protected:Npn \xsim_load_module:n #1
  {
    \tl_set:Nx \l_tmpa_tl { \tl_trim_spaces:n {#1} }
    \str_if_eq:VnTF \l_tmpa_tl {modules}
      { \msg_error:nn {xsim} {forbidden-module} }
      {  \__xsim_load_module:V \l_tmpa_tl }
  }

\cs_new_protected:Npn \__xsim_load_module:n #1
  {
    \xsim_if_module_loaded:nF {#1}
      {
        \xsim_if_module_exist:nTF {#1}
          {
            \__xsim_module_hook_use:nn {#1} {before}
            \msg_info:nnn {xsim} {loading-module} {#1}
            \@onefilewithoptions
              {\c__xsim_module_prefix_tl.#1}[][]
              \c__xsim_module_extension_tl
            \__xsim_module_hook_use:nn {#1} {after}
          }
          { \msg_error:nnn {xsim} {module-missing} {#1} }
      }
  }
\cs_generate_variant:Nn \__xsim_load_module:n {V}

\tl_new:N \g__xsim_modules_loaded_tl

\prg_new_conditional:Npnn \xsim_if_module_loaded:n #1 {p,T,F,TF}
  {
    \tl_if_in:NnTF \g__xsim_modules_loaded_tl {#1}
      { \prg_return_true: }
      { \prg_return_false: }
  }

\prg_new_conditional:Npnn \xsim_if_module_exist:n #1 {p,T,F,TF}
  {
    \file_if_exist:nTF
      {\c__xsim_module_prefix_tl.#1.\c__xsim_module_extension_tl}
      { \prg_return_true: }
      { \prg_return_false: }
  }

% --------------------------------------------------------------------------
% module hooks:
% #1: module
% #2: code
\cs_new_protected:Npn \xsim_module_after:nn #1#2
  {
    \xsim_if_module_loaded:nTF {#1}
      {#2}
      { \__xsim_add_to_module_hook:nnn {#1} {after} {#2} }
  }

% #1: module
% #2: code
\cs_new_protected:Npn \xsim_module_before:nn #1#2
  {
    % TODO: error in T case?
    \xsim_if_module_loaded:nF {#1}
      { \__xsim_add_to_module_hook:nnn {#1} {before} {#2} }
  }

% #1: module
% #2: hook name
% #3: code
\cs_new_protected:Npn \__xsim_add_to_module_hook:nnn #1#2#3
  {
    \xsim_if_module_loaded:nF {#1}
      {
        \tl_if_exist:cF {g__xsim_module_hook_#1_#2_tl}
          { \tl_new:c {g__xsim_module_hook_#1_#2_tl} }
        \tl_gput_right:cn {g__xsim_module_hook_#1_#2_tl} {#3}
      }
  }

% #1: module
% #2: hook name
\cs_new_protected:Npn \__xsim_module_hook_use:nn #1#2
  {
    \tl_if_exist:cT {g__xsim_module_hook_#1_#2_tl}
      {
        \tl_use:c {g__xsim_module_hook_#1_#2_tl}
        \tl_gclear:c {g__xsim_module_hook_#1_#2_tl}
      }
  }
\cs_generate_variant:Nn \__xsim_module_hook_use:nn {V}

% --------------------------------------------------------------------------

\tl_new:N \l__xsim_module_date_tl
\tl_new:N \l__xsim_module_version_tl

\tl_if_exist:NF \c_xsim_date_tl
  { \tl_set:Nx \l__xsim_module_date_tl {\c_xsimverb_date_tl} }
\tl_if_exist:NF \c_xsim_version_tl
  { \tl_set:Nx \l__xsim_module_version_tl {\c_xsimverb_version_tl} }

\hook_gput_code:nnn {package/xsim/after} {xsim}
  {
    \tl_set:NV \l__xsim_module_date_tl    \c_xsim_date_tl
    \tl_set:NV \l__xsim_module_version_tl \c_xsim_version_tl
  }

\cs_new_protected:Npn \__xsim_module:nn #1#2
  {
    \str_if_eq:xxF
      {\c__xsim_module_prefix_tl.#1.\c__xsim_module_extension_tl}
      {\@currname.\@currext}
      {
        \msg_error:nnnxx {xsim} {wrong-module-name}
          {#1}
          {\@currname.\@currext}
      }
    \ProvidesFile
      {\c__xsim_module_prefix_tl.#1.\c__xsim_module_extension_tl}
      [
        \l__xsim_module_date_tl
        \c_space_tl
        v \l__xsim_module_version_tl
        \c_space_tl
        xsim~ module~ `#1'~ (#2)
      ]
  }
\cs_generate_variant:Nn \msg_error:nnnn {nnnx}

% --------------------------------------------------------------------------
% define a xsim module:
% #1: if star: expl3 mode
% #2: name
% #3: description
\NewDocumentCommand \xsimstyle   {smO{}}
  {
    \__xsim_module:nn {style.#2} {#3}
    \IfBooleanT {#1} { \ExplSyntaxOn }
  }

% \cs_new_protected:Npn \xsim_style_options:nn #1#2
%   { \keys_define:nn {xsim/#1} {#2} }

\cs_new_protected:Npn \xsim_load_style:n #1
  { \clist_map_inline:nn {#1} { \xsim_load_module:n {style.##1} } }

\keys_define:nn {xsim}
  { load-style .code:n = \xsim_load_style:n {#1} }

\NewDocumentCommand \loadxsimstyle {m}
  { \xsim_load_style:n {#1} }

% --------------------------------------------------------------------------
% define internal xsim modules:
\tl_new:N \g_xsim_module_tl
\tl_new:N \g__xsim_module_stop_tl

\cs_new_protected:Npn \xsim_stop_module:n #1
  { \tl_gput_right:Nn \g__xsim_module_stop_tl {{#1}} }

% #1: name
% #2: description
\cs_set_protected:Npn \XSIMmodule #1#2
  {
    \msg_log:nnnn {xsim} {loading-module} {#1} {#2}
    \tl_gput_right:Nn \g__xsim_modules_loaded_tl {{#1}}
    \tl_gset:Nn \g_xsim_module_tl {#1}
    \__xsim_module_hook_use:nn {#1} {before}
    \tl_map_inline:Nn \g__xsim_module_stop_tl
      {
        \str_if_eq:nnT {##1} {#1}
          { \tl_map_break:n { \__xsim_gobble_module:w } }
      }
  }

\cs_set_protected:Npn \XSIMmoduleend
  { \__xsim_module_hook_use:Vn \g_xsim_module_tl {after} }

\cs_new_protected:Npn \__xsim_gobble_module:w #1 \XSIMmoduleend {}

% --------------------------------------------------------------------------
\XSIMmodule{base}{basic functionality of the package}

% \tl_new:N \g_xsim_end_document_hook_tl
% \tl_new:N \g_xsim_begin_document_hook_tl

% \hook_gput_code:nnn {begindocument} {xsim}
%   { \tl_use:N \g_xsim_begin_document_hook_tl }
% \hook_gput_code:nnn {enddocument} {xsim}
%   { \tl_use:N \g_xsim_end_document_hook_tl }

% \cs_new_protected:Npn \xsim_at_begin_document:n #1
%   { \tl_gput_right:Nn \g_xsim_begin_document_hook_tl {#1} }

% \cs_new_protected:Npn \xsim_at_end_document:n #1
%   { \tl_gput_right:Nn \g_xsim_end_document_hook_tl {#1} }

\cs_new_protected:Npn \xsim_at_begin_document:n #1
  { \hook_gput_code:nnn {begindocument} {xsim} {#1} }

\cs_new_protected:Npn \xsim_before_begin_document:n #1
  { \hook_gput_code:nnn {begindocument/before} {xsim} {#1} }

\cs_new_protected:Npn \xsim_at_end_document:n #1
  { \hook_gput_code:nnn {enddocument} {xsim} {#1} }

\cs_new_protected:Npn \xsim_before_end_document:n #1
  { \hook_gput_code:nnn {enddocument/before} {xsim} {#1} }
  
% ----------------------------------------------------------------------------

% variants of kernel functions:
\cs_generate_variant:Nn \seq_set_split:Nnn  {Nnx,NV}
\cs_generate_variant:Nn \seq_gset_split:Nnn {c,cVx,cnx}
\cs_generate_variant:Nn \seq_use:Nn         {cV}
\cs_generate_variant:Nn \seq_count:N        {c}
\cs_generate_variant:Nn \seq_if_empty:NT    {c}
\cs_generate_variant:Nn \seq_if_in:NnT      {cV}
\cs_generate_variant:Nn \seq_gremove_all:Nn {cV}
\cs_generate_variant:Nn \seq_gput_right:Nn  {cV}
\cs_generate_variant:Nn \seq_set_from_clist:Nn {c,Nx}
\cs_generate_variant:Nn \prop_put:Nnn       {Nnx,cxx}
\cs_generate_variant:Nn \prop_gput:Nnn      {Nx,Nnx,cxx}
\cs_generate_variant:Nn \prop_item:Nn       {c}
\cs_generate_variant:Nn \prop_if_in:NnTF    {Nx}
\cs_generate_variant:Nn \use:nn             {nx}
\cs_generate_variant:Nn \file_input:n       {V,x}
\cs_generate_variant:Nn \file_if_exist:nT   {V,x}
\cs_generate_variant:Nn \file_if_exist:nTF  {V}
\cs_generate_variant:Nn \file_get_mdfive_hash:nN {V}
\cs_generate_variant:Nn \msg_error:nnnnn    {nnnxx}
\cs_generate_variant:Nn \msg_warning:nnn    {nnV}
\cs_generate_variant:Nn \keys_define:nn     {nx}
\cs_generate_variant:Nn \keys_set:nn        {xn,nx}
\cs_generate_variant:Nn \text_titlecase:n   {e}
\cs_generate_variant:Nn \tl_if_eq:nnTF      {ee,ff,V}
\cs_generate_variant:Nn \tl_if_eq:nnT       {x}
\cs_generate_variant:Nn \tl_if_blank:nTF    {f,e}
\cs_generate_variant:Nn \tl_set_rescan:Nnn  {Nnx}
\cs_generate_variant:Nn \iow_now:Nn         {NV}
\cs_generate_variant:Nn \iow_open:Nn        {NV}
\cs_generate_variant:Nn \str_if_eq:nnT      {x}
\cs_generate_variant:Nn \str_if_eq:nnF      {xx}
\cs_generate_variant:Nn \str_if_eq_p:nn     {e}
\cs_generate_variant:Nn \prg_new_protected_conditional:Npnn {c}
\cs_generate_variant:Nn \clist_map_inline:nn {V}

% ----------------------------------------------------------------------------
% temporary variables:
\tl_new:N    \l__xsim_tmpa_tl
\tl_new:N    \l__xsim_tmpb_tl
\tl_new:N    \l__xsim_tmpc_tl
\tl_new:N    \l__xsim_tmpd_tl

\str_new:N   \l__xsim_tmpa_str
\str_new:N   \l__xsim_tmpb_str
\str_new:N   \l__xsim_tmpc_str

\bool_new:N  \l__xsim_tmpa_bool
\bool_new:N  \l__xsim_tmpb_bool
\bool_new:N  \l__xsim_tmpc_bool

\dim_new:N   \l__xsim_tmpa_dim
\dim_new:N   \l__xsim_tmpb_dim
\dim_new:N   \l__xsim_tmpc_dim

\seq_new:N   \l__xsim_tmpa_seq
\seq_new:N   \l__xsim_tmpb_seq
\seq_new:N   \l__xsim_tmpc_seq

\int_new:N   \l__xsim_tmpa_int
\int_new:N   \l__xsim_tmpb_int
\int_new:N   \l__xsim_tmpc_int

\fp_new:N    \l__xsim_tmpa_fp
\fp_new:N    \l__xsim_tmpb_fp
\fp_new:N    \l__xsim_tmpc_fp

\box_new:N   \l__xsim_tmpa_box
\box_new:N   \l__xsim_tmpb_box
\box_new:N   \l__xsim_tmpc_box

\clist_new:N \l__xsim_tmpa_clist
\clist_new:N \l__xsim_tmpb_clist
\clist_new:N \l__xsim_tmpc_clist

% ----------------------------------------------------------------------------

\prg_new_conditional:Npnn \xsim_if_komascript: {T,F,TF}
  {
    \cs_if_exist:cTF {KOMAClassName}
      { \prg_return_true: }
      { \prg_return_false: }
  }

% ----------------------------------------------------------------------------

\cs_if_exist:NF \xsim_if_final:T
  {
    \prg_new_conditional:Npnn \xsim_if_final: {p,T,F,TF}
      {
        \bool_if:NTF \g__xsim_final_bool
          { \prg_return_true: }
          { \prg_return_false: }
      }

    \prg_new_conditional:Npnn \xsim_if_verbose: {p,T,F,TF}
      {
        \bool_if:NTF \g__xsim_verbose_bool
          { \prg_return_true: }
          { \prg_return_false: }
      }
  }

\prg_new_conditional:Npnn \xsim_if_debug: {p,T,F,TF}
  {
    \bool_if:NTF \g__xsim_debug_bool
      { \prg_return_true: }
      { \prg_return_false: }
  }

\prg_new_conditional:Npnn \xsim_if_package_blank: {p,T,F,TF}
  {
    \bool_if:NTF \g__xsim_blank_bool
      { \prg_return_true: }
      { \prg_return_false: }
  }

\prg_new_conditional:Npnn \xsim_if_rerun: {p,T,F,TF}
  {
    \bool_if:NTF \g__xsim_rerun_bool
      { \prg_return_true: }
      { \prg_return_false: }
  }

\prg_new_conditional:Npnn \xsim_if_write_to_files: {p,T,F,TF}
  {
    \bool_if:NTF \g__xsim_write_to_file_bool
      { \prg_return_true: }
      { \prg_return_false: }
  }

\prg_new_conditional:Npnn \xsim_if_clear_aux: {p,T,F,TF}
  {
    \xsim_if_final:TF
      { \prg_return_false: }
      {
        \bool_if:NTF \g_xsim_clear_aux_bool
          { \prg_return_true: }
          { \prg_return_false: }
      }
  }

\cs_if_exist:NF \xsim_verbose:n
  {
    \cs_new_protected:Npn \xsim_verbose:n #1
      { \xsim_if_verbose:T { \msg_info:nnn {xsim} {verbose} {#1} } }
    \cs_generate_variant:Nn \xsim_verbose:n {x}
  }

\cs_new_protected:Npn \xsim_rerun:
  { \bool_gset_true:N \g__xsim_rerun_bool }

\prg_new_conditional:Npnn \xsim_if_chapter: {p,T,F,TF}
  {
    \bool_lazy_and:nnTF
      { \cs_if_exist_p:N \chapter }
      { \cs_if_exist_p:N \c@chapter }
      { \prg_return_true: }
      { \prg_return_false: }
  }

% ----------------------------------------------------------------------------

\msg_new:nnn {xsim} {attribute-not-set}
  { The~ attribute~ `#1'~ is~ not~ set~ \msg_line_context: }

% #1: attribute
% #2: item
\cs_new_protected:Npn \xsim_attribute_new:nn #1#2
  { \cs_new:cpn {____xsim_attribute_ \tl_to_str:n {#1::#2} :} {} }
  
% #1: attribute
% #2: item
% #3: value
\cs_new_protected:Npn \xsim_attribute_set:nnn #1#2#3
  {
    \cs_gset:cpx
      {____xsim_attribute_ \tl_to_str:n {#1::#2} :}
      { \exp_not:n { \exp_not:n {#3} } }
  }
\cs_generate_variant:Nn \xsim_attribute_set:nnn {nnx}

% #1: attribute
% #2: item
\cs_new_protected:Npn \xsim_attribute_unset:nn #1#2
  { \cs_undefine:c {____xsim_attribute_ \tl_to_str:n {#1::#2} :} }
  
% #1: attribute
% #2: item
\cs_new:Npn \xsim_attribute_get:nn #1#2
  {
    \cs_if_exist:cTF {____xsim_attribute_ \tl_to_str:n {#1::#2} :}
      { \use:c {____xsim_attribute_ \tl_to_str:n {#1::#2} :} }
      {
        \xsim_if_debug:T
          { \msg_expandable_error:nnn {xsim} {attribute-not-set} {#1::#2} }
      }
  }
  
% #1: attribute
% #2: item
\prg_new_conditional:Npnn \xsim_attribute_if_set:nn #1#2 {p,T,F,TF}
  {
    \cs_if_exist:cTF {____xsim_attribute_ \tl_to_str:n {#1::#2} :}
      { \prg_return_true: }
      { \prg_return_false: }
  }

% #1: attribute
% #2: item
\prg_new_conditional:Npnn \__xsim_if_attribute_eq:nnn #1#2#3 {p,T,F,TF}
  {
    \tl_if_eq:enTF
      { \use:c {____xsim_attribute_ \tl_to_str:n {#1::#2} :} }
      {#3}
      { \prg_return_true: }
      { \prg_return_false: }
  }

% #1: attribute
% #2: item
\cs_new_protected:Npn \xsim_attribute_show:nn #1#2
  {
    \iow_term:n {}
    \iow_term:x
      {
        \c_space_tl \c_space_tl
        \tl_to_str:n {#1::#2}
        \c_space_tl \c_space_tl == \c_space_tl \c_space_tl
        {\use:c {____xsim_attribute_ \tl_to_str:n {#1::#2} :}}
      }
    \iow_term:n {}
  }

% ----------------------------------------------------------------------------

\cs_new_protected:Npn \xsim_setup:n #1
  { \keys_set:nn {xsim} {#1} }

\XSIMmoduleend
\XSIMmodule{auxfile}{writing stuff to an auxiliary file}

\prg_new_conditional:Npnn \xsim_if_use_aux: {p,T,F,TF}
  {
    \bool_if:NTF \g_xsim_use_aux_bool
      { \prg_return_true: }
      { \prg_return_false: }
  }

\cs_new_protected:Npn \xsim_write_to_aux:n #1
  {
    \legacy_if:nT {@filesw}
      {
        \xsim_if_use_aux:TF
          { \iow_now:Nn \@auxout {#1} }
          { \iow_now:Nn \l__xsim_aux_file_iow {#1} }
      }
  }
\cs_generate_variant:Nn \xsim_write_to_aux:n {x}

% --------------------------------------------------------------------------

\cs_new_protected:Npn \XSIM #1 { \use:c {XSIMaux#1} }

\cs_new_protected:Npn \xsim_new_aux_property:cpn #1
  { \cs_new_protected:cpn {XSIMaux#1} }

\cs_new_protected:Npn \xsim_add_property_to_aux:nn #1#2
  {
    \xsim_verbose:n { Writing~ command~ \XSIM {#1}~ to~ aux~ file }
    \xsim_write_to_aux:x { \token_to_str:N \XSIM {#1} #2 }
  }

\xsim_at_begin_document:n
  {
    \iow_now:Nx \@auxout
      { \token_to_str:N \providecommand \token_to_str:N \XSIM [1] {} }
  }

% ----------------------------------------------------------------------------

\xsim_if_use_aux:F
  {
    \iow_new:N \l__xsim_aux_file_iow
    \tl_new:N \l__xsim_mdfive_hash_tl
    \tl_const:Nx \c_xsim_auxfile_tl { \c_sys_jobname_str .xsim }
    \hook_gput_code:nnn {begindocument} {xsim}
      {
        \file_get_mdfive_hash:VN
          \c_xsim_auxfile_tl
          \l__xsim_mdfive_hash_tl
        \iow_open:NV \l__xsim_aux_file_iow \c_xsim_auxfile_tl
      }
  }

\cs_new_protected:Npn \xsim_close_aux:
  {
    \xsim_if_use_aux:F
      {
        \iow_close:N \l__xsim_aux_file_iow
        \file_get_mdfive_hash:VN
          \c_xsim_auxfile_tl
          \l__xsim_tmpa_tl
        \tl_if_eq:NNF \l__xsim_mdfive_hash_tl \l__xsim_tmpa_tl { \xsim_rerun: }
        \legacy_if:nT {@filesw}
          { \iow_now:Nx \@auxout { \token_to_str:N \XSIM {readaux} } }
      }
  }

\xsim_new_aux_property:cpn {readaux}
  {
    \file_if_exist:VT \c_xsim_auxfile_tl
      { \file_input:V \c_xsim_auxfile_tl }
  }

% ----------------------------------------------------------------------------
% list for recording values that need to be written to the aux file and
% updated at begin document

\str_new:N \l__xsim_split_aux_lists_str
\keys_define:nn {xsim}
  {
    split-aux-lists .code:n    = \str_set:Nn \l__xsim_split_aux_lists_str {#1} ,
    split-aux-lists .initial:n = ||
  }

% define list:
% #1: name
\cs_new_protected:Npn \xsim_new_list:n #1
  {
    \xsim_verbose:n { Defining~ new~ auxfile~ list~ `#1'. }
    \seq_new:c {g__xsim_list_#1_seq}
    \seq_new:c {g__xsim_list_recorded_#1_seq}
    \xsim_new_aux_property:cpn {#1} ##1
      {
        \tl_if_blank:nF {##1}
          {
            \seq_gset_split:cVx {g__xsim_list_#1_seq}
              \l__xsim_split_aux_lists_str
              { \tl_to_str:n {##1} }
            \seq_map_inline:cn {g__xsim_list_#1_seq}
              { \xsim_attribute_set:nnn {#1} {####1} {} }
          }
      }
    \xsim_at_begin_document:n { \xsim_set_list:n {#1} }
  }

% #1: name
\cs_new_protected:Npn \xsim_set_list:n #1
  {
    \seq_gclear:c {g__xsim_list_recorded_#1_seq}
    \seq_set_eq:Nc \l__xsim_tmpa_seq {g__xsim_list_#1_seq}
    \seq_map_inline:Nn \l__xsim_tmpa_seq
      {
        \str_set:Nn \l__xsim_tmpa_str {##1}
        \seq_gput_right:cV {g__xsim_list_recorded_#1_seq} \l__xsim_tmpa_str
      }
    \seq_gremove_duplicates:c {g__xsim_list_recorded_#1_seq}
  }

% add to or remove from list:
% #1: name
% #2: entry
\cs_new_protected:Npn \xsim_add_to_list:nn #1#2
  {
    \xsim_verbose:n { Adding~ `#2'~ to~ list~ `#1'. }
    \str_set:Nn \l__xsim_tmpa_str {#2}
    \xsim_attribute_set:nnn {#1} {#2} {}
    \seq_if_in:cVT {g__xsim_list_#1_seq} \l__xsim_tmpa_str
      { \seq_gremove_all:cV {g__xsim_list_#1_seq} \l__xsim_tmpa_str }
    \seq_gput_right:cV {g__xsim_list_#1_seq} \l__xsim_tmpa_str
  }
\cs_generate_variant:Nn \xsim_add_to_list:nn {nx,nV}

% #1: name
% #2: entry
\cs_new_protected:Npn \xsim_remove_from_list:nn #1#2
  {
    \xsim_verbose:n { Removing~ `#2'~ to~ list~ `#1'. }
    \str_set:Nn \l__xsim_tmpa_str {#2}
    \xsim_attribute_if_set:nnT {#1} {#2}
      { \xsim_attribute_unset:nn {#1} {#2} }
    \seq_if_in:cVT {g__xsim_list_#1_seq} \l__xsim_tmpa_str
      { \seq_gremove_all:cV {g__xsim_list_#1_seq} \l__xsim_tmpa_str }
    \seq_if_in:cVT {g__xsim_list_recorded#1_seq} \l__xsim_tmpa_str
      { \seq_gremove_all:cV {g__xsim_list_recorded_#1_seq} \l__xsim_tmpa_str }
  }
\cs_generate_variant:Nn \xsim_remove_from_list:nn {nx}

% check if in list:
% #1: name
% #2: entry
\prg_new_conditional:Npnn \xsim_if_in_list:nn #1#2 {p,T,F,TF}
  {
    \xsim_attribute_if_set:nnTF {#1} {#2}
      { \prg_return_true: }
      { \prg_return_false: }
  }
\prg_generate_conditional_variant:Nnn \xsim_if_in_list:nn {nx} {T,TF}

\bool_new:N \l__xsim_empty_lists_bool
\cs_new_protected:Npn \xsim_empty_lists:
  { \bool_set_true:N \l__xsim_empty_lists_bool }

% update list (should be used inside \xsim_at_end_document:n):
% #1: name
\cs_new_protected:Npn \xsim_update_list:n #1
  {
    \xsim_verbose:n { Updating~ list~ `#1'. }
    \seq_gremove_duplicates:c {g__xsim_list_#1_seq}
    \seq_gremove_duplicates:c {g__xsim_list_recorded_#1_seq}
    \str_set:Nx \l__xsim_tmpa_str
      { \seq_use:cn {g__xsim_list_#1_seq} {} }
    \str_set:Nx \l__xsim_tmpb_str
      { \seq_use:cn {g__xsim_list_recorded_#1_seq} {} }
    \str_if_eq:NNF \l__xsim_tmpa_str \l__xsim_tmpb_str
      { \xsim_rerun: }
    \bool_if:NF \l__xsim_empty_lists_bool
      {
        \xsim_add_property_to_aux:nn {#1}
          { { \seq_use:cV {g__xsim_list_#1_seq} \l__xsim_split_aux_lists_str } }
      }
  }

% loop over list:
% #1: name
% #2: code
\cs_new_protected:Npn \xsim_foreach_list_entry:nn #1#2
  { \seq_map_inline:cn {g__xsim_list_recorded_#1_seq} {#2} }

\cs_new_protected:Npn \xsim_foreach_new_list_entry:nn #1#2
  { \seq_map_inline:cn {g__xsim_list_#1_seq} {#2} }

% length of a list:
% #1: name
\cs_new:Npn \xsim_list_count:n #1
  { \seq_count:c {g__xsim_list_recorded_#1_seq} }

% item of a list:
% #1: name
% #2: integer
\cs_new:Npn \xsim_list_item:nn #1#2
  { \seq_item:cn {g__xsim_list_recorded_#1_seq} {#2} }

\XSIMmoduleend
\XSIMmodule{properties}{properties of exercises}

\msg_new:nnn {xsim} {unknown-property}
  {
    You~ tried~ to~ set~ the~ property~ `#1'~ \msg_line_context: . \\
    This~ property~ does~ not~ exist.~ Check~ for~ a~ typo~ or~ \\
    define~ the~ property~ yourself.
  }

\msg_new:nnn {xsim} {property-unique}
  {
    You~ tried~ to~ set~ the~ property~ `#1'~ \msg_line_context: . \\
    This~ property~ has~ to~ have~ a~ unique~ value~ and~ thus~ \\
    cannot~ be~ set~ to~ `#2'~ since~ this~ value~ is~ already~ taken.
  }

\msg_new:nnn {xsim} {no-boolean-property}
  {
    You~ tried~ to~ check~ the~ boolean~ value~ of~ property~ `#1'~
    \msg_line_context: . \\
    However,~ property~ `#1'~ is~ not~ a~ boolean~ property!
  }

\msg_new:nnn {xsim} {unbalanced-property-list}
  {
    There~ is~ a~ mis-match~ in~ the~ property~ list: \\
    `#1' \\
    \msg_line_context:
  }

% ----------------------------------------------------------------------------
\seq_new:N \l__xsim_properties_seq
\seq_new:N \l__xsim_boolean_properties_seq
\seq_new:N \l__xsim_unique_properties_seq
\seq_new:N \l__xsim_noupdate_properties_seq
\seq_new:N \l__xsim_given_properties_seq

% #1: type
% #2: id
% #3: property
\cs_new_protected:Npn \__xsim_define_property:nnn #1#2#3
  { \xsim_attribute_new:nn {#1!#2} {#3} }

% check if property is set:
% #1: type
% #2: id
% #3: property
\prg_new_conditional:Npnn \xsim_if_property_set:nnn #1#2#3 {p,T,F,TF}
  {
    \xsim_attribute_if_set:nnTF {#1!#2} {#3}
      { \prg_return_true: }
      { \prg_return_false: }
  }
\prg_generate_conditional_variant:Nnn \xsim_if_property_set:nnn
  {ne,nne,ee,nV}
  {p,T,F,TF}

% new property:
% #1: boolean if unique
% #2: boolean if boolean
% #3: boolean if noupdate
% #4: property name
\cs_new_protected:Npn \xsim_declare_property:nnnn #1#2#3#4
  {
    \xsim_if_property_exist:nF {#4}
      {
        \seq_put_right:Nn \l__xsim_properties_seq {#4}
        \bool_if:nTF {#3}
          {
            \seq_if_in:NnF \l__xsim_noupdate_properties_seq {#4}
              { \seq_put_right:Nn \l__xsim_noupdate_properties_seq {#4} }
          }
          { \xsim_new_list:n {#4} }
        \bool_if:nTF {#2}
          {
            \xsim_verbose:n
              { Declaring~ new~ boolean~ exercise~ property~ `#4'. }
            \seq_if_in:NnF \l__xsim_boolean_properties_seq {#4}
              { \seq_put_right:Nn \l__xsim_boolean_properties_seq {#4} }
          }
          {
            \bool_if:nTF {#1}
              {
                \xsim_verbose:n
                  { Declaring~ new~ unique~ exercise~ property~ `#4'. }
                \seq_if_in:NnF \l__xsim_unique_properties_seq {#4}
                  { \seq_put_right:Nn \l__xsim_unique_properties_seq {#4} }
                \seq_new:c {l__xsim_unique_property_#4_values_seq}
              }
              { \xsim_verbose:n { Declaring~ new~ exercise~ property~ `#4'. } }
          }
        \bool_if:nF {#3}
          {
            \xsim_at_end_document:n { \xsim_update_list:n {#4} }
            \xsim_at_begin_document:n { \__xsim_set_property_from_list:n {#4} }
          }
      }
  }

% ----------------------------------------------------------------------------
% check if property exists:
\prg_new_conditional:Npnn \xsim_if_property_exist:n #1 {p,T,F,TF}
  {
    \seq_if_in:NnTF \l__xsim_properties_seq {#1}
      { \prg_return_true: }
      { \prg_return_false: }
  }
\cs_generate_variant:Nn \xsim_if_property_exist:nTF {x}

\prg_new_conditional:Npnn \xsim_if_property_noupdate:n #1 {p,T,F,TF}
  {
    \seq_if_in:NnTF \l__xsim_noupdate_properties_seq {#1}
      { \prg_return_true: }
      { \prg_return_false: }
  }
\cs_generate_variant:Nn \xsim_if_property_noupdate:nF {V}

\prg_new_conditional:Npnn \xsim_if_property_boolean:n #1 {p,T,F,TF}
  {
    \seq_if_in:NnTF \l__xsim_boolean_properties_seq {#1}
      { \prg_return_true: }
      { \prg_return_false: }
  }

\prg_new_conditional:Npnn \xsim_if_property_unique:n #1 {p,T,F,TF}
  {
    \seq_if_in:NnTF \l__xsim_unique_properties_seq {#1}
      { \prg_return_true: }
      { \prg_return_false: }
  }

% true if property #1 has been set in the /last/ call of
% \xsim_set_properties:nnn
\prg_new_conditional:Npnn \xsim_if_property_given:n #1 {p,T,F,TF}
  {
    \seq_if_in:NnTF \l__xsim_given_properties_seq {#1}
      { \prg_return_true: }
      { \prg_return_false: }
  }

% #1: property
% #2: value
\prg_new_conditional:Npnn \xsim_if_property_unique_value:nn #1#2 {p,T,F,TF}
  {
    \xsim_if_property_unique:nTF {#1}
      {
        \seq_if_in:cnTF {l__xsim_unique_property_#1_values_seq} {#2}
          { \prg_return_true: }
          { \prg_return_false: }
      }
      { \prg_return_false: }
  }

% #1: code
\cs_new_protected:Npn \xsim_foreach_property:n #1
  { \seq_map_inline:Nn \l__xsim_properties_seq {#1} }

% ----------------------------------------------------------------------------
% #1: type
% #2: id
% #3: property
% #4: value
\cs_new_protected:Npn \xsim_set_property:nnnn #1#2#3#4
  {
    \xsim_if_property_unique:nTF {#3}
      {
        \xsim_if_property_unique_value:nnTF {#3} {#4}
          { \msg_warning:nnnn {xsim} {property-unique} {#3} {#4} }
          { \__xsim_set_unique_property:nnnn {#1} {#2} {#3} {#4} }
      }
      {
        \xsim_if_property_boolean:nTF {#3}
          { \__xsim_set_boolean_property:nnnn {#1} {#2} {#3} {#4} }
          { \__xsim_set_property:nnnn {#1} {#2} {#3} {#4} }
      }
  }
\cs_generate_variant:Nn \xsim_set_property:nnnn {nnee,nnnV,nV,nVne,nnne,ee,eene,nene}

% #1: type
% #2: id
% #3: property
% #4: value
\cs_new_protected:Npn \__xsim_set_boolean_property:nnnn #1#2#3#4
  {
    \str_case_e:nnTF { \str_lowercase:n {#4} }
      {
        {} {}
        {true} {}
      }
      { \__xsim_set_property:nnnn {#1} {#2} {#3} {true} }
      { \__xsim_set_property:nnnn {#1} {#2} {#3} {false} }
  }

% #1: type
% #2: id
% #3: property
% #4: value
\cs_new_protected:Npn \__xsim_set_unique_property:nnnn #1#2#3#4
  {
    \__xsim_set_property:nnnn {#1} {#2} {#3} {#4}
    \__xsim_set_property_reverse:nnnn {#1} {#2} {#3} {#4}
  }

% #1: type
% #2: id
% #3: property
\cs_new_protected:Npn \__xsim_unset_property:nnn #1#2#3
  {
    \xsim_verbose:n
      { Un-setting~ property~ `#3'~ of~ exercise~ type~ `#1'~ id~ `#2'. }
    \xsim_if_property_noupdate:nF {#3}
      { \__xsim_remove_property_from_list:nnn {#1} {#2} {#3} }
    \xsim_attribute_unset:nn {#1!#2} {#3}
  }

% #1: type
% #2: id
% #3: property
\cs_new_protected:Npn \xsim_unset_property:nnn #1#2#3
  {
    \xsim_if_property_exist:nTF {#3}
      { \__xsim_unset_property:nnn {#1} {#2} {#3} }
      { \msg_error:nnn {xsim} {unknown-property} {#3} }  
  }
\cs_generate_variant:Nn \xsim_unset_property:nnn {nx,nnx,nV}

% #1: type
% #2: id
% #3: property
% #4: value
\cs_new_protected:Npn \__xsim_set_property:nnnn #1#2#3#4
  {
    \xsim_verbose:n
      {
        Setting~ property~ `#3'~ of~ exercise~ type~ `#1'~ id~ `#2'~ to~
        value~ `#4'.
      }
    \xsim_if_property_noupdate:nF {#3}
      {
        \__xsim_remove_property_from_list:nnn {#1} {#2} {#3}
        \xsim_add_to_list:nn {#3} {#1-#2=={#4}}
      }
    \xsim_attribute_set:nnn {#1!#2} {#3} {#4}
  }
\cs_generate_variant:Nn \__xsim_set_property:nnnn {VVnV}

% #1: type
% #2: id
% #3: property
% #4: value
\cs_new_protected:Npn \__xsim_set_property_reverse:nnnn #1#2#3#4
  {
    \xsim_attribute_set:nnn {type:#3} {#4} {#1}
    \xsim_attribute_set:nnn {id:#3} {#4} {#2}
  }

\tl_set:Nx \l__xsim_tmpa_tl { \cs_to_str:N \{ }
\tl_set:Nx \l__xsim_tmpb_tl { \cs_to_str:N \} }
\use:x
  {
    \cs_new_protected:Npn
    \exp_not:N \__xsim_extract_property_list_entry:NNNwww
    ##1##2##3##4-##5== \l__xsim_tmpa_tl ##6 \l__xsim_tmpb_tl
    \exp_not:N \q_stop
  }
  {
    \str_set:Nn #1 {#4}
    \str_set:Nn #2 {#5}
    \str_set:Nn #3 {#6}
  }

% #1: type
% #2: id
% #3: property
\cs_new_protected:Npn \__xsim_remove_property_from_list:nnn #1#2#3
  {
    % make sure we change the entry if it has been recorded previously:
    \xsim_foreach_list_entry:nn {#3}
      {
        \__xsim_extract_property_list_entry:NNNwww
          \l__xsim_tmpa_str \l__xsim_tmpb_str \l__xsim_tmpc_str ##1 \q_stop
        \str_if_eq:xnT { \l__xsim_tmpa_str - \l__xsim_tmpb_str } {#1-#2}
          {
            \xsim_remove_from_list:nx {#3}
              { #1-#2 == {\l__xsim_tmpc_str} }
          }
      }
    % make sure we change the entry if it has been set during the current run:
    \xsim_foreach_new_list_entry:nn {#3}
      {
        \__xsim_extract_property_list_entry:NNNwww
          \l__xsim_tmpa_str \l__xsim_tmpb_str \l__xsim_tmpc_str ##1 \q_stop
        \str_if_eq:xnT { \l__xsim_tmpa_str - \l__xsim_tmpb_str } {#1-#2}
          {
            \xsim_remove_from_list:nx {#3}
              { #1-#2 == {\l__xsim_tmpc_str} }
          }
      }
  }

\seq_new:N \g_xsim_used_order_seq
\seq_new:N \g_xsim_printed_order_seq

\cs_new_protected:Npn \__xsim_set_property_from_list:n #1
  {
    \xsim_foreach_list_entry:nn {#1}
      {
        \__xsim_extract_property_list_entry:NNNwww
          \l__xsim_tmpa_str \l__xsim_tmpb_str \l__xsim_tmpc_str ##1 \q_stop
        \tl_set_rescan:Nnx \l__xsim_tmpa_tl {} { \l__xsim_tmpa_str }
        \tl_set_rescan:Nnx \l__xsim_tmpb_tl {} { \l__xsim_tmpb_str }
        \tl_set_rescan:Nnx \l__xsim_tmpc_tl {} { \l__xsim_tmpc_str }
        \str_if_eq:nnT {#1} {used}
          {
            \str_if_eq:VnT \l__xsim_tmpc_str {true}
              {
                \seq_gput_right:Nx \g_xsim_used_order_seq
                  { \l__xsim_tmpa_tl - \l__xsim_tmpb_tl }
              }
          }
        \str_if_eq:nnT {#1} {printed}
          {
            \str_if_eq:VnT \l__xsim_tmpc_str {true}
              {
                \seq_gput_right:Nx \g_xsim_printed_order_seq
                  { \l__xsim_tmpa_tl - \l__xsim_tmpb_tl }
              }
          }
        \__xsim_set_property:VVnV
          \l__xsim_tmpa_str
          \l__xsim_tmpb_str
          {#1}
          \l__xsim_tmpc_tl
      }
  }

% set properties:
% #1: type
% #2: id
% #3: csv list of properties
\cs_new_protected:Npn \xsim_set_properties:nnn #1#2#3
  {
    \cs_set_protected:Npn \__xsim_tmp:n ##1
      { \xsim_set_property:nnnn {#1} {#2} {##1} {} }
    \cs_set_protected:Npn \__xsim_tmp:nn ##1##2
      { \xsim_set_property:nnnn {#1} {#2} {##1} {##2} }
    \keyval_parse:NNn
      \__xsim_tmp:n
      \__xsim_tmp:nn
      {#3}
    \xsim_property_set_aliases:nn {#1} {#2}
  }
\cs_generate_variant:Nn \xsim_set_properties:nnn {nne,nee,nV,nVe}

% ----------------------------------------------------------------------------

\seq_new:N \g__xsim_property_aliases_seq

% #1: property
\prg_new_conditional:Npnn \xsim_if_property_alias:n #1 {p,T,F,TF}
  {
    \seq_if_in:NnTF \g__xsim_property_aliases_seq {#1}
      { \prg_return_true: }
      { \prg_return_false: }
  }

% #1: property
\cs_new:Npn \xsim_property_alias:n #1
  { \xsim_attribute_get:nn {#1} {alias} }

% #1: property
% #2: alias of
\cs_new_protected:Npn \xsim_make_property_alias:nn #1#2
  {
    \xsim_if_property_alias:nF {#1}
      {
        \xsim_verbose:n
          { Making~ property~ `#1'~ an~ alias~ of~ property~ `#2'. }
        \xsim_attribute_set:nnn {#1} {alias} {#2}
        \seq_gput_right:Nn \g__xsim_property_aliases_seq {#1}
      }
  }

% #1: type
% #2: id
% #3: property
\cs_new_protected:Npn \xsim_property_set_alias:nnn #1#2#3
  {
    \xsim_if_property_set:nneT {#1} {#2}
      { \xsim_property_alias:n {#3} }
      {
        \xsim_attribute_set:nnx {alias:#1!#2} {#3}
          {
            \xsim_get_property:nnf {#1} {#2}
              { \xsim_property_alias:n {#3} }
          }
        \xsim_if_property_set:nnnF {#1} {#2} {#3}
          { 
            \xsim_set_property:nnne {#1} {#2} {#3}
              {
                \xsim_get_property:nnf {#1} {#2}
                  { \xsim_property_alias:n {#3} }
              }
          }
      }
  }
\cs_generate_variant:Nn \xsim_property_set_alias:nnn {nV}

% #1: type
% #2: id
\cs_new_protected:Npn \xsim_property_set_aliases:nn #1#2
  {
    \seq_map_inline:Nn \g__xsim_property_aliases_seq
      { \xsim_property_set_alias:nnn {#1} {#2} {##1} }
  }

% ----------------------------------------------------------------------------
% retrieve properties:
% #1: type
% #2: id
% #3: property
\cs_new:Npn \xsim_get_property:nnn #1#2#3
  { \xsim_attribute_get:nn {#1!#2} {#3} }
\cs_generate_variant:Nn \xsim_get_property:nnn {nx,ne,xx,oo,no,nnf,nf,oof,nV}

% #1: type
% #2: id
% #3: property
\cs_new:Npn \xsim_show_property:nnn #1#2#3
  { \xsim_attribute_show:nn {#1!#2} {#3} }

% #1: property
% #2: value
\cs_new:Npn \xsim_get_id_for_property:nn #1#2
  { \xsim_attribute_get:nn {id:#1} {#2} }

% #1: ID
% #2: variable
\cs_new_protected:Npn \xsim_get_type_for_id:nN #1#2
  {
    \xsim_foreach_list_entry:nn {idtypes}
      {
        \__xsim_extract_property_list_entry:NNNwww
          \l__xsim_tmpa_str \l__xsim_tmpb_str \l__xsim_tmpc_str -##1 \q_stop
          \str_if_eq:VnT \l__xsim_tmpb_str {#1}
            {
              \str_set_eq:NN #2 \l__xsim_tmpc_str
              \seq_map_break:
            }
      }
  }

% #1: property
% #2: value
\cs_new:Npn \xsim_get_type_for_property:nn #1#2
  { \xsim_attribute_get:nn {type:#1} {#2} }
\cs_generate_variant:Nn \xsim_get_type_for_property:nn {nV}

% #1: id or ID
\cs_new:Npn \xsim_normalize_id:n #1
  {
    \tl_if_blank:eTF
      { \xsim_get_id_for_property:nn {ID} {#1} }
      {#1}
      { \xsim_get_id_for_property:nn {ID} {#1} }
  }
\cs_generate_variant:Nn \xsim_normalize_id:n {e}

\tl_new:N \l_xsim_property_value_tl
\tl_new:N \PropertyValue

\cs_new:Npn \__xsim_tmp:n #1 {#1}
\cs_generate_variant:Nn \__xsim_tmp:n {V}

% #1: type
% #2: id
% #3: property
\cs_new_protected:Npn \xsim_get_property_if_set:nnnTF #1#2#3#4#5
  {
    \xsim_if_property_exist:nTF {#3}
      {
        \xsim_if_property_set:nnnTF {#1} {#2} {#3}
          {    
            \tl_set:Nx \l_xsim_property_value_tl
              { \xsim_get_property:nnn {#1} {#2} {#3} }
            \tl_set_eq:NN \PropertyValue \l_xsim_property_value_tl
            \cs_set:Npn \__xsim_tmp:n ##1 {#4}
            \__xsim_tmp:V \l_xsim_property_value_tl
          }
          {#5}
      }
      { \msg_error:nnn {xsim} {unknown-property} {#3} }
  }
\cs_generate_variant:Nn \xsim_get_property_if_set:nnnTF {oo}

\cs_new_protected:Npn \xsim_get_property_if_set:nnnT #1#2#3#4
  { \xsim_get_property_if_set:nnnTF {#1} {#2} {#3} {#4} {} }

\cs_new_protected:Npn \xsim_get_property_if_set:nnnF #1#2#3#4
  { \xsim_get_property_if_set:nnnTF {#1} {#2} {#3} {} {#4} }

% #1: type
% #2: id
% #3: property
\prg_new_conditional:Npnn \xsim_if_boolean_property:nnn #1#2#3 {T,F,TF}
  {
    \xsim_if_property_exist:nTF {#3}
      {
        \xsim_if_property_boolean:nTF {#3}
          {
            \xsim_if_property_set:nnnTF {#1} {#2} {#3}
              {
                \bool_if:cTF
                  {c_ \xsim_get_property:nnn {#1} {#2} {#3} _bool}
                  { \prg_return_true: }
                  { \prg_return_false: }
              }
              { \prg_return_false: }
          }
          { \msg_error:nnn {xsim} {no-boolean-property} {#3} }
      }
      { \msg_error:nnn {xsim} {unknown-property} {#3} }
  }
\prg_generate_conditional_variant:Nnn \xsim_if_boolean_property:nnn {oo,nV} {T,F,TF}

\cs_new_protected:Npn \xsim_save_property:nnnN #1#2#3#4
  { \tl_set:Nx #4 { \xsim_get_property:nnn {#1} {#2} {#3} } }
\cs_generate_variant:Nn \xsim_save_property:nnnN {nx,xx}

\cs_new_protected:Npn \xsim_gsave_property:nnnN #1#2#3#4
  {
    \xsim_save_property:nnnN {#1} {#2} {#3} #4
    \tl_gset:NV #4 #4
  }
\cs_generate_variant:Nn \xsim_gsave_property:nnnN {nx,xx,nV}

\XSIMmoduleend
\XSIMmodule{environments}{generic code for exercise and solution environments}

\xsim_if_write_to_files:F
  {
    \xsim_declare_property:nnnn
      { \c_false_bool }
      { \c_false_bool }
      { \c_false_bool }
      {solution-body}
    \xsim_declare_property:nnnn
      { \c_false_bool }
      { \c_false_bool }
      { \c_false_bool }
      {exercise-body}
  }

\tl_new:N   \l__xsim_file_name_signature_tl
\tl_new:N   \l_xsim_file_path_tl
\tl_new:N   \l_xsim_file_path_and_name_tl
\tl_new:N   \l_xsim_file_name_tl
\tl_new:N   \l__xsim_file_extension_tl
\bool_new:N \l__xsim_options_given_bool
\bool_new:N \l__xsim_insert_mode_bool

\tl_new:N  \ExerciseText
\tl_set:Nn \ExerciseText { \msg_error:nn {xsim} {only-in-solution} }

% ----------------------------------------------------------------------------

\msg_new:nnn {xsim} {only-in-solution}
  {
    You~ can't~ use~ \token_to_str:N \ExerciseText \c_space_tl inside~ an~
    exercise.~ It~ is~ only~ allowed~ inside~ solution~ environments!
  }

% ----------------------------------------------------------------------------

\keys_define:nn {xsim}
  {
    path           .tl_set:N  = \l_xsim_file_path_tl ,
    file-extension .tl_set:N  = \l__xsim_file_extension_tl ,
    file-extension .initial:n = tex
  }

% ----------------------------------------------------------------------------

\tl_const:Nn \c__xsim_comment_line_tl { \c__xsim_percent_char_tl }
\tl_const:Nn \c__xsim_comment_line_fill_tl { \prg_replicate:nn {72} {-} }
\tl_const:Nn \c__xsim_comment_line_indent_tl { \c_space_tl \c_space_tl }

\cs_new:Npn \__xsim_comment_line_and_feed:n #1
  { \__xsim_comment_line:n {#1} ^^J }

\cs_new:Npn \__xsim_comment_line:n #1
  {
    \c__xsim_comment_line_tl
    \tl_if_blank:nF {#1} { \c_space_tl }
    #1
  }

\cs_new:Npn \__xsim_two_digits:n #1
  { \int_compare:nT { #1 < 10 } {0} \int_eval:n {#1} }
  
% #1: type
% #2: id
% #3: exercise|solution
\cs_new_protected:Npn \xsim_set_file_signature:nnn #1#2#3
  { \tl_set:Nn \l__xsim_file_name_signature_tl {#1-#2-#3} }
\cs_generate_variant:Nn \xsim_set_file_signature:nnn {nx,nV}

\tl_set:Nn \l_xsim_file_name_tl
  { \c_sys_jobname_str - \l__xsim_file_name_signature_tl -body . \l__xsim_file_extension_tl }

\tl_set:Nn \l_xsim_file_path_and_name_tl
  {
    \tl_if_blank:VF \l_xsim_file_path_tl
      { \l_xsim_file_path_tl / }
    \l_xsim_file_name_tl
  }

% #1: type
% #2: id
% #3: exercise|solution
% #4: file name
% #5: path
\cs_new_protected:Npn \__xsim_file_info:nnnnn #1#2#3#4#5
  {
    \tl_set:Nx \l_xsim_file_begin_tl
      {
        \__xsim_comment_line_and_feed:n { \c__xsim_comment_line_fill_tl }
        \__xsim_comment_line_and_feed:n {file~ `#4'}
        \tl_if_blank:nF {#5}
          {
            \__xsim_comment_line_and_feed:n
              { \c__xsim_comment_line_indent_tl in~ folder~ `#5/'}
          }
        \__xsim_comment_line_and_feed:n {}
        \__xsim_comment_line_and_feed:n
          {
            \c__xsim_comment_line_indent_tl
            \c__xsim_comment_line_indent_tl
            #3~ of~ type~ `#1'~ with~ id~ `#2'
          }
        \__xsim_comment_line_and_feed:n {}
        \__xsim_comment_line_and_feed:n
          {generated~ by~ the~ `\@currenvir'~ environment~ of~ the}
        \__xsim_comment_line_and_feed:n
          {
            \c__xsim_comment_line_indent_tl
            `xsim'~ package~ v \c_xsim_version_tl
            \c_space_tl (\c_xsim_date_tl)
          }
        \__xsim_comment_line_and_feed:n
          {
            from~ source~ `\c_sys_jobname_str'~ on~
            \int_use:N \c_sys_year_int /
            \__xsim_two_digits:n { \c_sys_month_int } /
            \__xsim_two_digits:n { \c_sys_day_int } ~
            \msg_line_context:
          }
        \__xsim_comment_line:n { \c__xsim_comment_line_fill_tl }
      }
  }
\cs_generate_variant:Nn \__xsim_file_info:nnnnn {nnnVV}

% ----------------------------------------------------------------------------
% #1: type
% #2: id
% #3: exercise|solution
% #4: potential environment body
\cs_new_protected:Npn \xsim_save_environment_body:nnnn #1#2#3#4
  {
    \xsim_if_write_to_files:TF
      { \__xsim_write_environment_body:nnn {#1} {#2} {#3} }
      { \__xsim_save_environment_body:nnnn {#1} {#2} {#3} {#4} }
  }

\cs_new_protected:Npn \xsim_save_environment_body_end:
  {
    \xsim_if_write_to_files:TF
      { \__xsim_write_environment_body_end: }
      { \__xsim_save_environment_body_end: }
  }

% writing to files:
% #1: type
% #2: id
% #3: exercise|solution
\cs_new_protected:Npn \__xsim_write_environment_body:nnn #1#2#3
  {
    \xsim_verbose:n
      { Starting~ to~ write~ #3~ environment~ type~ `#1'~ id~ `#2' }
    \xsim_set_file_signature:nnn {#1} {#2} {#3}
    \__xsim_file_info:nnnVV {#1} {#2} {#3}
      \l_xsim_file_name_tl
      \l_xsim_file_path_tl
    \int_zero:N \l_xsim_line_gobble_int
    \xsim_file_write_start:nV
      { \l__xsim_options_given_bool }
      \l_xsim_file_path_and_name_tl
  }

\cs_new_protected:Npn \__xsim_write_environment_body_end:
  { \xsim_file_write_stop: }

% saving to property lists:
% #1: type
% #2: id
% #3: exercise|solution
% #4: environment body
\cs_new_protected:Npn \__xsim_save_environment_body:nnnn #1#2#3#4
  {
    \xsim_verbose:n { Saving~ #3~ environment~ type~ `#1'~ id~ `#2' }
    \xsim_set_property:nnnn {#1} {#2} {#3-body} {#4}
    \xsim_if_exchange:nnnT {#1} {#2} {#3}
      {
        \tl_if_eq:nnT {#3} {exercise}
          { \xsim_set_property:nnnn {#1} {#2} {solution-body} {#4} }
      }
  }

\cs_new:Npn \__xsim_save_environment_body_end: { }

% ----------------------------------------------------------------------------
% #1: type
% #2: exercise|solution
% #3: hook
\cs_new_protected:Npn \xsim_new_hook:nnn #1#2#3
  {
    \xsim_verbose:n { Defining~ new~ #1~ (#2)~ hook~ `#3' }
    \tl_new:c {l__xsim_#1_#2_#3_hook_tl}
    \keys_define:nx {xsim}
      {
        \xsim_get_parameter:nn {#1} {#2-env}/#3-hook .tl_set:N =
          \exp_not:c {l__xsim_#1_#2_#3_hook_tl}
      }
  }

% #1: type
% #2: exercise|solution
% #3: hook
\cs_new:Npn \xsim_use_hook:nnn #1#2#3
  { \exp_not:v {l__xsim_#1_#2_#3_hook_tl} }

% #1: type
% #2: exercise|solution
% #3: hook
% #4: code
\cs_new_protected:Npn \xsim_set_hook:nnnn #1#2#3#4
  {
    \xsim_verbose:n { Setting~ #1~ (#2)~ hook~ `#3' }
    \tl_set:cn {l__xsim_#1_#2_#3_hook_tl} {#4}
  }

% #1: type
% #2: exercise|solution
% #3: hook
% #4: code
\cs_new_protected:Npn \xsim_addto_hook:nnnn #1#2#3#4
  {
    \xsim_verbose:n { Adding~ to~ #1~ (#2)~ hook~ `#3' }
    \tl_put_right:cn {l__xsim_#1_#2_#3_hook_tl} {#4}
  }

% ----------------------------------------------------------------------------

% #1: type
% #2: id
% #3: exercise|solution
% #4: potential enironment body
\cs_new_protected:Npn \xsim_start_environment:nnnn #1#2#3#4
  { \xsim_save_environment_body:nnnn {#1} {#2} {#3} {#4} }
\cs_generate_variant:Nn \xsim_start_environment:nnnn {nV}

% #1: type
% #2: id
% #3: exercise|solution
\cs_new_protected:Npn \xsim_stop_environment:nnn #1#2#3
  {
    \xsim_save_environment_body_end:
    \xsim_if_print:nnnTF {#1} {#2} {#3}
      { \xsim_typeset_environment:nnn {#1} {#2} {#3} }
      { \xsim_use_hook:nnn {#1} {#3} {print-false} }
  }
\cs_generate_variant:Nn \xsim_stop_environment:nnn {nV}

% #1: type
% #2: id
% #3: exercise|solution
\cs_new_protected:Npn \xsim_typeset_environment:nnn #1#2#3
  {
    \tl_set:Nn \ExerciseType {#1}
    \tl_set:Nn \ExerciseID {#2}
    \xsim_verbose:n
      { Typesetting~ #3~ environment~ type~ `#1'~ id~ `#2' }
    \xsim_use_hook:nnn {#1} {#3} {pre}
    \xsim_use_template:nx
      {begin}
      { \xsim_get_parameter:nn {#1} {#3-template} }
    \xsim_use_hook:nnn {#1} {#3} {begin}
    \xsim_input:nnn {#1} {#2} {#3}
    \xsim_use_hook:nnn {#1} {#3} {end}
    \xsim_use_template:nx
      {end}
      { \xsim_get_parameter:nn {#1} {#3-template} }
    \xsim_use_hook:nnn {#1} {#3} {post}
  }

% #1: type
% #2: id
% #3: exercise|solution
\prg_new_conditional:Npnn \xsim_if_exchange:nnn #1#2#3 {T,F,TF}
  {
    \str_if_eq:nnTF {#3} {solution}
      {
        \xsim_if_boolean_property:nnnTF {#1} {#2} {solution}
          { \prg_return_true: }
          { \prg_return_false: }
      }
      { \prg_return_false: }
  }

% #1: type
% #2: id
% #3: exercise|solution
\prg_new_protected_conditional:Npnn \xsim_if_exist:nnn #1#2#3 {T,F,TF}
  {
    \xsim_if_write_to_files:TF
      {
        \xsim_if_exchange:nnnTF {#1} {#2} {#3}
          { \xsim_set_file_signature:nnn {#1} {#2} {exercise} }
          { \xsim_set_file_signature:nnn {#1} {#2} {#3} }
        \file_if_exist:VTF \l_xsim_file_path_and_name_tl
          { \prg_return_true: }
          { \prg_return_false: }
      }
      {
        \xsim_if_property_exist:nTF {#3-body}
          {
            \xsim_if_property_set:nnnTF {#1} {#2} {#3-body}
              { \prg_return_true: }
              { \prg_return_false: }
          }
          { \prg_return_false: }
      }
  }

% #1: type
% #2: id
% #3: options
% #4: exercise|solution
\cs_new_protected:Npn \xsim_insert:nnnn #1#2#3#4
  {
    \xsim_if_exist:nnnT {#1} {#2} {#4}
      {
        \xsim_verbose:n
          { Inserting~ #4~ type~ `#1'~ id~ `#2'. }
        \group_begin:
          \xsim_insert_mode:
          \tl_gset:Nx \g_xsim_exercise_id_tl {#2}
          \tl_gset:Nn \g_xsim_exercise_type_tl {#1}
          \use:c {xsim_start_#4:nn} {#1} {#3}
          \xsim_typeset_environment:nnn {#1} {#2} {#4}
          \use:c {xsim_stop_#4:n} {#1} {}
        \group_end:
      }
  }
\cs_generate_variant:Nn \xsim_insert:nnnn {nV,VV,nnnV,xx,ne}

% #1: type
% #2: id
% #3: exercise|solution
\cs_new_protected:Npn \xsim_input:nnn #1#2#3
  {
    \xsim_if_exchange:nnnTF {#1} {#2} {#3}
      { \xsim_set_file_signature:nnn {#1} {#2} {exercise} }
      { \xsim_set_file_signature:nnn {#1} {#2} {#3} }
    \xsim_if_write_to_files:TF
      {
        \file_if_exist:VTF \l_xsim_file_path_and_name_tl
          { \file_input:V \l_xsim_file_path_and_name_tl }
          {
            \xsim_verbose:n
              { File~ `\l_xsim_file_path_and_name_tl'~ doesn't~ exist. }
          }
      }
      {
        \xsim_if_exchange:nnnTF {#1} {#2} {#3}
          { \xsim_get_property:nnn {#1} {#2} {exercise-body} }
          { \xsim_get_property:nnn {#1} {#2} {#3-body} }
        }
  }
\cs_generate_variant:Nn \xsim_input:nnn {oo,nV}

\prg_new_conditional:Npnn \xsim_if_insert_mode: {T,F,TF}
  {
    \bool_if:NTF \l__xsim_insert_mode_bool
      { \prg_return_true: }
      { \prg_return_false: }
  }

\cs_new_protected:Npn \xsim_insert_mode:
  { \bool_set_true:N \l__xsim_insert_mode_bool }

% ----------------------------------------------------------------------------

% #1: type
% #2: exercise|solution
\cs_new_protected:Npn \xsim_new_environment:nn #1#2
  {
    \xsim_verbose:n { Defining~ #2~ environment~ type~ `#1'. }
    \xsim_new_hook:nnn {#1} {#2} {pre}
    \xsim_new_hook:nnn {#1} {#2} {begin}
    \xsim_new_hook:nnn {#1} {#2} {end}
    \xsim_new_hook:nnn {#1} {#2} {post}
    \xsim_new_hook:nnn {#1} {#2} {print-false}
    \xsim_if_write_to_files:TF
      {
        \NewDocumentEnvironment
          { \xsim_get_parameter:nn {#1} {#2-env} } {!o}
          {
            \IfNoValueTF {##1}
              {
                \bool_set_false:N \l__xsim_options_given_bool
                \use:c {xsim_start_#2:nn} {#1} {}
              }
              {
                \bool_set_true:N \l__xsim_options_given_bool
                \use:c {xsim_start_#2:nn} {#1} {##1}
              }
              \xsim_start_environment:nVnn {#1} \g_xsim_exercise_id_tl {#2} { }
          }
          {
            \xsim_stop_environment:nVn {#1} \g_xsim_exercise_id_tl {#2}
            \use:c {xsim_stop_#2:n} {#1}
          }
      }
      {
        \NewDocumentEnvironment
          { \xsim_get_parameter:nn {#1} {#2-env} } {o+b}
          {
            \IfNoValueTF {##1}
              {
                \bool_set_false:N \l__xsim_options_given_bool
                \use:c {xsim_start_#2:nn} {#1} {}
              }
              {
                \bool_set_true:N \l__xsim_options_given_bool
                \use:c {xsim_start_#2:nn} {#1} {##1}
              }
            \xsim_start_environment:nVnn {#1} \g_xsim_exercise_id_tl {#2} {##2}
          }
          {
            \xsim_stop_environment:nVn {#1} \g_xsim_exercise_id_tl {#2}
            \use:c {xsim_stop_#2:n} {#1}
          }
      }
  }

\XSIMmoduleend
\XSIMmodule{templates}{templates for typesetting exercises}

\msg_new:nnn {xsim} {unknown-template}
  {
    You~ are~ trying~ to~ load~ the~ template~ `#2'~ (template~ type~ `#1')~
    \msg_line_context: .~ This~ template~ does~ not~ seem~ to~ be~ defined.~
    I~ am~ using~ the~ template~ `#3'~ (template~ type~ `#1')~ instead.
  }

\msg_new:nnn {xsim} {template-type-exists}
  { The~ template~ type~ `#1'~ already~ exists~ \msg_line_context: }

% ----------------------------------------------------------------------------
\seq_new:N \l__xsim_template_types_seq

% #1: template type
\cs_new_protected:Npn \xsim_new_template_type:n #1
  {
    \seq_if_in:NnTF \l__xsim_template_types_seq {#1}
      { \msg_error:nnn {xsim} {template-type-exists} {#1} }
      {
        \xsim_verbose:n { Declaring~ new~ template~ type~ `#1'. }
        \seq_put_right:Nn \l__xsim_template_types_seq {#1}
      }
  }

% #1: template type
% #2: template name
% #3: code
\cs_new_protected:Npn \__xsim_add_template:nnn #1#2#3
  {
    \xsim_verbose:n { Defining~ new~ template~ `#2'~ of~ type~ `#1'. }
    \tl_new:c {l__xsim_template_#1_#2_setup_tl}
    \xsim_attribute_set:nnn {template::#1} {#2} {#3}
  }

\prg_new_conditional:Npnn \xsim_if_template_exist:nn #1#2 {T,F,TF}
  {
    \xsim_attribute_if_set:nnTF {template::#1} {#2}
      { \prg_return_true: }
      { \prg_return_false: }
  }

% #1: template type
% #2: template name
\cs_new_protected:Npn \__xsim_get_template:nn #1#2
  { \xsim_attribute_get:nn {template::#1} {#2} }

% ----------------------------------------------------------------------------

% environment templates (types `begin' and `end'):
\xsim_new_template_type:n {begin}
\xsim_new_template_type:n {end}

% #1: template name
% #2: begin code
% #3: end code
\cs_new_protected:Npn \xsim_declare_environment_template:nnn #1#2#3
  {
    \__xsim_add_template:nnn {begin} {#1}
      {
        \__xsim_start_template:nn {begin} {#1}
        \__xsim_start_template:nn {end} {#1}
        #2
      }
    \__xsim_add_template:nnn {end} {#1}
      {
        #3
        \__xsim_stop_template:nn {end} {#1}
        \__xsim_stop_template:nn {begin} {#1}
      }
  }

% ----------------------------------------------------------------------------

% heading templates for the solution list (type `heading'):
\xsim_new_template_type:n {heading}

% #1: template name
% #2: code
\cs_new_protected:Npn \xsim_declare_heading_template:nn #1#2
  {
    \__xsim_add_template:nnn {heading} {#1}
      {
        \__xsim_start_template:nn {heading} {#1}
        #2
        \__xsim_stop_template:nn {heading} {#1}
      }
  }

% ----------------------------------------------------------------------------

% grading table templates (type `table'):
\xsim_new_template_type:n {table}

% #1: template name
% #2: code
\cs_new_protected:Npn \xsim_declare_table_template:nn #1#2
  {
    \__xsim_add_template:nnn {table} {#1}
      {
        \__xsim_start_template:nn {table} {#1}
        #2
        \__xsim_stop_template:nn {table} {#1}
      }
  }

% ----------------------------------------------------------------------------

% using templates:
% setup up the next usage of `template name':
% #1: template type
% #2: template name
% #3: setup code
\cs_new_protected:Npn \xsim_setup_template:nnn #1#2#3
  { \tl_set:cn {l__xsim_template_#1_#2_setup_tl} {#3} }
\cs_generate_variant:Nn \xsim_setup_template:nnn {nnV,nVn}

% #1: template type
% #2: template name
\cs_new_protected:Npn \__xsim_setup_template:nn #1#2
  { \tl_use:c {l__xsim_template_#1_#2_setup_tl} }

% #1: template type
% #2: template name
\cs_new_protected:Npn \__xsim_clear_template_setup:nn #1#2
  { \tl_clear:c {l__xsim_template_#1_#2_setup_tl} }

% #1: template type
% #2: template name
\cs_new_protected:Npn \__xsim_start_template:nn #1#2
  { \group_begin: \__xsim_setup_template:nn {#1} {#2} }

% #1: template type
% #2: template name
\cs_new_protected:Npn \__xsim_stop_template:nn #1#2
  { \group_end: \__xsim_clear_template_setup:nn {#1} {#2} }
  
% #1: template type
% #2: template name
\cs_new_protected:Npn \xsim_use_template:nn #1#2
  {
    \xsim_if_template_exist:nnTF {#1} {#2}
      { \__xsim_get_template:nn {#1} {#2} }
      {
        \msg_warning:nnnnn {xsim} {unknown-template} {#1} {#2} {default}
        \__xsim_get_template:nn {#1} {default}
      }
  }
\cs_generate_variant:Nn \xsim_use_template:nn {nx,nV}

\XSIMmoduleend
\XSIMmodule{tags}{tagging of exercises}

\seq_new:N \l__xsim_tag_types_seq
\bool_new:N \l____xsim_tagged_bool

\msg_new:nnn {xsim} {tag-exists}
  { The~ tag~ type~ `#1'~ already~ exists.~ I~ am~ doing~ nothing. }

\msg_new:nnn {xsim} {tag-unknown}
  { The~ tag~ type~ `#1'~ is~ not~ defined.~ Check~ for~ a~ typo. }

% #1: name
\cs_new_protected:Npn \xsim_new_tag_type:n #1
  {
    \xsim_verbose:n { Declaring~ new~ tag~ type~ `#1'. }
    \seq_put_right:Nn \l__xsim_tag_types_seq {#1}
    \seq_remove_duplicates:N \l__xsim_tag_types_seq
    \seq_new:c  {l__xsim_chosen_tags_#1_seq}
    \bool_new:c {l__xsim_ignore_untagged_#1_bool}
    \bool_new:c {l____xsim_tagged_#1_bool}
    \bool_new:c {l__xsim_use_unmatched_#1_bool}
    \keys_define:nn {xsim}
      {
        #1/ignore-untagged .bool_set:c = {l__xsim_ignore_untagged_#1_bool} ,
        #1/ignore-untagged .initial:n  = true ,
        #1 .code:n = \seq_set_from_clist:cn {l__xsim_chosen_tags_#1_seq} {##1} ,
        #1/use-unmatched .bool_set:c = {l__xsim_use_unmatched_#1_bool} ,
        #1/use-unmatched .initial:n = false
      }
    \prg_new_protected_conditional:cpnn {xsim_if_#1_value:n} ##1 {T,F,TF}
      {
        \seq_if_in:cnTF {l__xsim_chosen_tags_#1_seq} {##1}
          { \prg_return_true: }
          { \prg_return_false: }
      }
    \xsim_declare_property:nnnn
      { \c_false_bool }
      { \c_false_bool }
      { \c_false_bool }
      {#1}
    \xsim_declare_printsolutions_condition:nn {#1}
      { \__xsim_printsolutions_if_tagged:nnnnTF {#1}  }
    \keys_define:nn {xsim/print-solutions}
      {
        #1         .choice: ,
        #1/false   .code:n    =
          \__xsim_printsolutions_condition:nn {#1} {false} ,
        #1/true    .code:n    =
          \__xsim_printsolutions_condition:nn {#1} {true} ,
        #1/unknown .code:n    =
          \keys_set:nn {xsim} { #1 = ##1 }
          \__xsim_printsolutions_condition:nn {#1} {true} ,
        #1         .initial:n = false
      }
  }

% #1: tag type
\prg_new_protected_conditional:Npnn \xsim_if_tag_type_exist:n #1 {T,F,TF}
  {
    \seq_if_in:NnTF \l__xsim_tag_types_seq {#1}
      { \prg_return_true: }
      { \prg_return_false: }
  }
\prg_generate_conditional_variant:Nnn \xsim_if_tag_type_exist:n {V} {TF}

% #1: tag type
% #2: exercise type
% #3: id
% #4: print|use
\prg_new_protected_conditional:Npnn
  \__xsim_printsolutions_if_tagged:nnnn #1#2#3#4 {T,F,TF}
  {
    \xsim_if_in_list:nnTF {#4} {#2-#3=={true}}
      {
        \__xsim_printsolutions_if_condition:nTF {#1}
          {
            \__xsim_printsolutions_condition_true:
            \xsim_if_tagged:nnnTF {#2} {#3} {#1}
              { \prg_return_true: }
              { \prg_return_false: }
          }
          { \prg_return_false: }
      }
      { \prg_return_false: }
  }

% #1: exercise type
% #2: id
% #3: tag type
\prg_new_protected_conditional:Npnn \xsim_if_tagged:nnn #1#2#3 {T,F,TF}
  {
    \bool_set_false:c {l____xsim_tagged_#3_bool}
    \seq_if_empty:cTF {l__xsim_chosen_tags_#3_seq}
      { \bool_set_true:c {l____xsim_tagged_#3_bool} }
      {
        \seq_set_from_clist:Nx
          \l__xsim_tmpa_seq
          { \xsim_get_property:nnn {#1} {#2} {#3} }
        \seq_if_empty:NTF \l__xsim_tmpa_seq
          {
            \bool_set_eq:cc
              {l____xsim_tagged_#3_bool}
              {l__xsim_ignore_untagged_#3_bool}
          }
          {
            \seq_map_inline:Nn \l__xsim_tmpa_seq
              {
                \seq_if_in:cnT {l__xsim_chosen_tags_#3_seq} {##1}
                  {
                    \bool_set_true:c {l____xsim_tagged_#3_bool}
                    \seq_map_break:
                  }
              }
          }
      }
    \bool_if:cTF {l____xsim_tagged_#3_bool}
      { \prg_return_true: }
      { \prg_return_false: }
  }

% #1: exercise type
% #2: id
% #3: tag type
% #4: tag values
\prg_new_protected_conditional:Npnn \xsim_has_tags:nnnn #1#2#3#4 {T,F,TF}
  {
    \bool_set_false:c {l____xsim_tagged_#3_bool}
    \seq_set_from_clist:Nn \l__xsim_tmpa_seq {#4}
    \seq_if_empty:NTF \l__xsim_tmpa_seq
      {% no tag values given:
        \bool_set_true:c {l____xsim_tagged_#3_bool}
      }
      {
        \seq_set_from_clist:Nx
          \l__xsim_tmpb_seq
          { \xsim_get_property:nnn {#1} {#2} {#3} }
        \seq_if_empty:NTF \l__xsim_tmpb_seq
          {% exercise has no tag values given
            % \bool_set_eq:cc
            %   {l____xsim_tagged_#3_bool}
            %   {l__xsim_ignore_untagged_#3_bool}
            \bool_set_false:c {l____xsim_tagged_#3_bool}
          }
          {
            \seq_map_inline:Nn \l__xsim_tmpb_seq
              {
                \seq_if_in:NnT \l__xsim_tmpa_seq {##1}
                  {
                    \bool_set_true:c {l____xsim_tagged_#3_bool}
                    \seq_map_break:
                  }
              }
          }
      }
    \bool_if:cTF {l____xsim_tagged_#3_bool}
      { \prg_return_true: }
      { \prg_return_false: }
  }
\prg_generate_conditional_variant:Nnn \xsim_has_tags:nnnn {nnVV} {T,F}

% #1: exercise type
% #2: id
\prg_new_protected_conditional:Npnn \xsim_if_tagged:nn #1#2 {T,F,TF}
  {
    \bool_set_true:N \l____xsim_tagged_bool
    \seq_map_inline:Nn \l__xsim_tag_types_seq
      {
        \xsim_if_tagged:nnnF {#1} {#2} {##1}
          {
            \bool_set_false:N \l____xsim_tagged_bool
            \seq_map_break:
          }
      }
    \bool_if:NTF \l____xsim_tagged_bool
      { \prg_return_true: }
      { \prg_return_false: }
  }

% #1: exercise type
% #2: id
% #3: tag type
\prg_new_protected_conditional:Npnn \xsim_if_tags_match:nnn #1#2#3 {T,F,TF}
  {
    \bool_set_false:c {l____xsim_tagged_#3_bool}
    \seq_if_empty:cTF {l__xsim_chosen_tags_#3_seq}
      { \bool_set_true:c {l____xsim_tagged_#3_bool} }
      {
        \seq_set_from_clist:Nx
          \l__xsim_tmpa_seq
          { \xsim_get_property:nnn {#1} {#2} {#3} }
        \seq_if_empty:NTF \l__xsim_tmpa_seq
          {
            \bool_set_eq:cc
              {l____xsim_tagged_#3_bool}
              {l__xsim_ignore_untagged_#3_bool}
          }
          {
            \seq_map_inline:Nn \l__xsim_tmpa_seq
              {
                \seq_if_in:cnT {l__xsim_chosen_tags_#3_seq} {##1}
                  {
                    \bool_set_true:c {l____xsim_tagged_#3_bool}
                    \seq_map_break:
                  }
              }
          }
      }
    \bool_if:cTF {l____xsim_tagged_#3_bool}
      { \prg_return_true: }
      { \prg_return_false: }
  }

\prg_new_protected_conditional:Npnn \xsim_if_tagged_use:nn #1#2 {T,F,TF}
  {
    \bool_set_true:N \l____xsim_tagged_bool
    \seq_map_inline:Nn \l__xsim_tag_types_seq
      {
        \xsim_if_tags_match:nnnF {#1} {#2} {##1}
          {
            
            \bool_if:cF {l__xsim_use_unmatched_##1_bool}
              {
                \bool_set_false:N \l____xsim_tagged_bool
                \seq_map_break:
              }
          }
      }
    \bool_if:NTF \l____xsim_tagged_bool
      { \prg_return_true: }
      { \prg_return_false: }
  }

% #1: exercise type
% #2: id
% #3: tag type
% #4: code
\cs_new_protected:Npn \xsim_foreach_exercise_tag:nnnn #1#2#3#4
  {
    \seq_set_from_clist:Nx \l__xsim_tmpa_seq
      { \xsim_get_property:nnn {#1} {#2} {#3} }
    \seq_map_inline:Nn \l__xsim_tmpa_seq {#4}
  }
\cs_generate_variant:Nn \xsim_foreach_exercise_tag:nnnn {oo}

% #1: exercise type
% #2: id
% #3: tag type
% #4: sep
\cs_new_protected:Npn \xsim_exercise_tags_use:nnnn #1#2#3#4
  {
    \seq_set_from_clist:Nx \l__xsim_tmpa_seq
      { \xsim_get_property:nnn {#1} {#2} {#3} }
    \seq_use:Nn \l__xsim_tmpa_seq {#4}
  }
\cs_generate_variant:Nn \xsim_exercise_tags_use:nnnn {oo}

% #1: exercise type
% #2: id
% #3: tag type
% #4: sep between two
% #5: sep between more than two
% #6: sep between last two
\cs_new_protected:Npn \xsim_exercise_tags_use:nnnnnn #1#2#3#4#5#6
  {
    \seq_set_from_clist:Nx \l__xsim_tmpa_seq
      { \xsim_get_property:nnn {#1} {#2} {#3} }
    \seq_use:Nnnn \l__xsim_tmpa_seq {#4} {#5} {#6}
  }
\cs_generate_variant:Nn \xsim_exercise_tags_use:nnnn   {oo}
\cs_generate_variant:Nn \xsim_exercise_tags_use:nnnnnn {oo}

\XSIMmoduleend
\XSIMmodule{goals}{counting of points and bonus points}

\msg_new:nnn {xsim} {goal-unknown}
  { The~ goal~ `#1'~ has~ never~ been~ declared~ \msg_line_context: }

\seq_new:N \l__xsim_goals_seq
\tl_new:N  \l_xism_grading_table_template_tl
\tl_new:N  \l_xism_grading_table_exercise_type_tl

% #1: name
\cs_new_protected:Npn \xsim_declare_exercise_goal:n #1
  {
    \xsim_if_goal_exist:nF {#1}
      {
        \xsim_verbose:n { Declaring~ new~ goal~ `#1'. }
        \xsim_declare_property:nnnn
          { \c_false_bool } { \c_false_bool } { \c_false_bool } {#1}
        \xsim_declare_property:nnnn
          { \c_false_bool } { \c_false_bool } { \c_false_bool } {user#1}
        \xsim_declare_property:nnnn
          { \c_false_bool } { \c_false_bool } { \c_false_bool } {body#1}
        \seq_put_right:Nn \l__xsim_goals_seq {#1}
        \fp_gzero_new:c {g__xsim_goal_#1_fp}
        \fp_new:c {l__xsim_temporary_#1_fp}
        \bool_new:c {l__xsim_temporary_#1_bool}
        \bool_new:c {l__xsim_goal_#1_given_bool}
        \fp_gzero_new:c {l__xsim_current_goal_#1_fp}
        \fp_if_exist:cF {g__xsim_total_goal_#1_fp}
          { \fp_new:c {g__xsim_total_goal_#1_fp} }
        \fp_if_exist:cF {g__xsim_total_goal_recorded_#1_fp}
          { \fp_new:c {g__xsim_total_goal_recorded_#1_fp} }
        \xsim_before_begin_document:n
          {
            \xsim_foreach_exercise_type:n
              { \xsim_goal_init:nn {##1} {#1} }
          }
      }
  }

% #1: type
% #2: goal
\cs_new_protected:Npn \xsim_goal_init:nn #1#2
  {
    \fp_gzero_new:c {g__xsim_#1_goal_#2_fp}
    \fp_if_exist:cF {g__xsim_total_#1_goal_#2_fp}
      { \fp_new:c {g__xsim_total_#1_goal_#2_fp} }
  }

\prg_new_conditional:Npnn \xsim_if_goal_exist:n #1 {p,T,F,TF}
  {
    \seq_if_in:NnTF \l__xsim_goals_seq {#1}
      { \prg_return_true: }
      { \prg_return_false: }
  }

\prg_new_conditional:Npnn \xsim_if_goal_given:n #1 {p,T,F,TF}
  {
    \bool_if:cTF {l__xsim_goal_#1_given_bool}
      { \prg_return_true: }
      { \prg_return_false: }
  }

\cs_new_protected:Npn \xsim_goal_given:n #1
  { \bool_set_true:c {l__xsim_goal_#1_given_bool} }

% #1: exercise type
% #2: id
% #3: goal
\cs_new_protected:Npn \xsim_update_goal:nnn #1#2#3
  {
    \xsim_if_goal_exist:nT {#3}
      {
        \fp_gadd:cn {g__xsim_goal_#3_fp}
          { \xsim_get_property:nnn {#1} {#2} {#3} + 0 }
        \fp_gadd:cn {g__xsim_#1_goal_#3_fp}
          { \xsim_get_property:nnn {#1} {#2} {#3} + 0 }
      }
  }

% #1: exercise type
% #2: id
\cs_new_protected:Npn \xsim_update_goals:nn #1#2
  {
    \xsim_foreach_goal:n
      { \xsim_update_goal:nnn {#1} {#2} {##1} }
  }
\cs_generate_variant:Nn \xsim_update_goals:nn {nV}

\cs_new_protected:Npn \xsim_foreach_goal:n #1
  { \seq_map_inline:Nn \l__xsim_goals_seq {#1} }


% #1: exercise type
% #2: id
% #3: goal
% #4: value
\cs_new_protected:Npn \xsim_addto_goal:nnnn #1#2#3#4
  {
    \xsim_if_goal_exist:nT {#3}
      {
        \xsim_verbose:n { Adding~ `#4'~ to~ `#3'~ goal~ of~ #1~ #2 }
        \fp_set:Nn \l__xsim_tmpa_fp
          { \xsim_get_property:nnn {#1} {#2} {body#3} + 0 + #4 }
        \xsim_set_property:nnne {#1} {#2} {body#3}
          { \fp_to_decimal:N \l__xsim_tmpa_fp }
      }
  }
\cs_generate_variant:Nn \xsim_addto_goal:nnnn {xx,nVnx}

% #1: exercise type
% #2: id
% #3: goal
% #4: relation and value
\prg_new_conditional:Npnn \xsim_if_goal_value:nnnn #1#2#3#4 {p,T,F,TF}
  {
    \fp_compare:nTF
      { (0 + \xsim_get_property:nnn {#1} {#2} {#3}) #4 }
      { \prg_return_true: }
      { \prg_return_false: }
  }
\cs_generate_variant:Nn \xsim_if_goal_value:nnnnTF {xx}

% ----------------------------------------------------------------------------
% a function which can be used to modify how the numbers of a goal are
% printed:
\cs_new_protected:Npn \__xsim_print_goal:n #1 {#1}
\cs_generate_variant:Nn \__xsim_print_goal:n {x}

% #1: value
% #2: value == 1
% #3: value != 1
\cs_new_protected:Npn \xsim_print_goal:nnn #1#2#3
  {
    \__xsim_print_goal:x { \fp_to_decimal:n {#1} }
    \fp_compare:nTF { (#1) = 1 } {#2} {#3}
  }

% #1: exercise type
% #2: goal
% #3: sum == 1
% #4: sum != 1
\cs_new_protected:Npn \xsim_print_goal_sum:nnnn #1#2#3#4
  { \xsim_print_goal:nnn { \use:c {g__xsim_total_#1_goal_#2_fp} } {#3} {#4} }

% #1: goal
% #2: sum == 1
% #3: sum != 1
\cs_new_protected:Npn \xsim_print_total_goal_sum:nnn #1#2#3
  { \xsim_print_goal:nnn { \use:c {g__xsim_total_goal_#1_fp} } {#2} {#3} }

% ----------------------------------------------------------------------------
% the same as the last to functions but for a list of goals:

% #1: fp variable to be set
% #2: list of goals (separated with +)
% #3: function to be applied to goal names
\cs_new_protected:Npn \__xsim_exercise_goals_sum_aux:Nnn #1#2#3
  {
    \seq_set_split:Nnn \l__xsim_tmpa_seq {+} {#2}
    \seq_set_map:NNn \l__xsim_tmpb_seq \l__xsim_tmpa_seq {#3}
    \fp_set:Nn #1 { \seq_use:Nn \l__xsim_tmpb_seq {+} }
  }

% #1: fp variable
% #2: exercise type
% #3: list of goals (separated with +)
\cs_new_protected:Npn \__xsim_exercise_goals_sum:Nnn #1#2#3
  {
    \__xsim_exercise_goals_sum_aux:Nnn #1 {#3}
      { \use:c {g__xsim_total_#2_goal_ \tl_trim_spaces:n {##1} _fp} }
  }

% #1: fp variable
% #2: list of goals (separated with +)
\cs_new_protected:Npn \__xsim_exercise_goals_sum:Nn #1#2
  {
    \__xsim_exercise_goals_sum_aux:Nnn #1 {#2}
      { \use:c {g__xsim_total_goal_ \tl_trim_spaces:n {##1} _fp} }
  }

% #1: exercise type
% #2: list of goals (separated with +)
% #3: sum == 1
% #4: sum != 1
\cs_new_protected:Npn \xsim_print_goals_sum:nnnn #1#2#3#4
  {
    \__xsim_exercise_goals_sum:Nnn \l__xsim_tmpa_fp {#1} {#2}
    \xsim_print_goal:nnn { \l__xsim_tmpa_fp } {#3} {#4}
  }

% #1: list of goals (separated with +)
% #2: sum == 1
% #3: sum != 1
\cs_new_protected:Npn \xsim_print_total_goals_sum:nnn #1#2#3
  {
    \__xsim_exercise_goals_sum:Nn \l__xsim_tmpa_fp {#1}
    \xsim_print_goal:nnn { \l__xsim_tmpa_fp } {#2} {#3}
  }

% ----------------------------------------------------------------------------
% #1: exercise type
% #2: goal
% #3: relation and value
\prg_new_conditional:Npnn \xsim_if_goal_sum:nnn #1#2#3 {p,T,F,TF}
  {
    \fp_compare:nTF
      { ( \use:c {g__xsim_total_#1_goal_#2_fp} ) #3 }
      { \prg_return_true: }
      { \prg_return_false: }
  }

% #1: exercise type
% #2: list of goals (separated with +)
% #3: relation and value
\prg_new_protected_conditional:Npnn \xsim_if_goals_sum:nnn #1#2#3 {T,F,TF}
  {
    \__xsim_exercise_goals_sum:Nnn \l__xsim_tmpa_fp {#1} {#2}
    \fp_compare:nTF
      { ( \l__xsim_tmpa_fp ) #3 }
      { \prg_return_true: }
      { \prg_return_false: }
  }

% #1: goal
% #2: relation and value
\prg_new_conditional:Npnn \xsim_if_total_goal_sum:nn #1#2 {p,T,F,TF}
  {
    \fp_compare:nTF
      { ( \use:c {g__xsim_total_goal_#1_fp} ) #2 }
      { \prg_return_true: }
      { \prg_return_false: }
  }

% #1: list of goals (separated with +)
% #2: relation and value
\prg_new_protected_conditional:Npnn \xsim_if_total_goals_sum:nn #1#2 {T,F,TF}
  {
    \__xsim_exercise_goals_sum:Nn \l__xsim_tmpa_fp {#1}
    \fp_compare:nTF
      { ( \l__xsim_tmpa_fp ) #2 }
      { \prg_return_true: }
      { \prg_return_false: }
  }

% ----------------------------------------------------------------------------
% #1: exercise type
% #2: goal
% #3: value
\xsim_new_aux_property:cpn {goal} #1#2#3
  { \fp_gset:cn {g__xsim_total_#1_goal_#2_fp} {#3} }

% #1: goal
% #2: value
\xsim_new_aux_property:cpn {totalgoal} #1#2
  { \fp_gset:cn {g__xsim_total_goal_#1_fp} {#2} }

\xsim_at_begin_document:n
  { \xsim_foreach_goal:n { \xsim_get_total_goal:n {#1} } }

% #1: goal
\cs_new_protected:Npn \xsim_get_total_goal:n #1
  {
    \fp_gset_eq:cc
      {g__xsim_total_goal_recorded_#1_fp}
      {g__xsim_total_goal_#1_fp}
  }

\xsim_at_end_document:n { \xsim_save_goals: }

\cs_new_protected:Npn \xsim_save_goals:
  {
    \xsim_foreach_goal:n
      {
        \xsim_foreach_exercise_type:n
          {
            \xsim_add_property_to_aux:nn {goal}
              { {####1} {##1} { \fp_to_decimal:c {g__xsim_####1_goal_##1_fp} } }
          }
        \tl_set:Nx \l__xsim_tmpa_tl
          { \fp_to_decimal:c {g__xsim_total_goal_##1_fp} }
        \tl_set:Nx \l__xsim_tmpb_tl
          { \fp_to_decimal:c {g__xsim_total_goal_recorded_##1_fp} }
        \tl_if_eq:NNF \l__xsim_tmpa_tl \l__xsim_tmpb_tl { \xsim_rerun: }
        \xsim_add_property_to_aux:nn {totalgoal}
          { {##1} { \fp_to_decimal:c {g__xsim_goal_##1_fp} } }
      }
  }

% ----------------------------------------------------------------------------

\keys_define:nn {xsim}
  {
    goal-print .code:n =
      \cs_set_protected:Npn \__xsim_print_goal:n ##1 {#1} ,
    grading-table/template .tl_set:N  = \l_xism_grading_table_template_tl ,
    grading-table/template .initial:n = default ,
    grading-table/type     .tl_set:N  = \l_xism_grading_table_exercise_type_tl ,
    grading-table/type     .initial:n =
  }

\XSIMmoduleend
\XSIMmodule{exercises}{main exercises definitions}

\seq_new:N  \l__xsim_exercise_types_seq
\seq_new:N  \l__xsim_parameters_seq
\seq_new:N  \l__xsim_fixed_parameters_seq
\seq_new:N  \l__xsim_mandatory_parameters_seq
\int_gzero_new:N  \g_xsim_id_int
\int_gzero_new:N  \g_xsim_max_id_int
\tl_new:N   \l_xsim_current_id_tl
\tl_new:N   \g_xsim_exercise_id_tl
\tl_new:N   \g_xsim_exercise_type_tl
\tl_new:N   \ExerciseID
\tl_new:N   \ExerciseType

% ----------------------------------------------------------------------------

\msg_new:nnn {xsim} {parameter-fixed}
  {
    You~ tried~ to~ set~ parameter~ `#2'~ for~ type~ `#1'~ \msg_line_context:
    . ~ However,~ parameter~ `#2'~ is~ a~ fixed~ parameter~ that~ already~
    has~ been~ set~ for~ type~ `#1'.~ Its~ value~ can't~ be~ changed!
  }

\msg_new:nnn {xsim} {type-exists}
  {
    You~ try~ to~ define~ exercise~ type~ `#1'~ \msg_line_context: .~
    However,~ the~ exercise~ type~ `#1'~ already~ exists.
  }

\msg_new:nnn {xsim} {mandatory-parameter}
  {
    You~ forget~ to~ set~ the~ mandatory~ parameter~ `#1'~ when~ defining~
    exercise~ type~ `#2'.
  }

\msg_new:nnn {xsim} {unknown-parameter}
  {
    You~ tried~ to~ retrieve~ the~ parameter~ `#1'~ \msg_line_context: . \\
    This~ parameter~ does~ not~ exist.~ Check~ for~ a~ typo~ or~ \\
    define~ the~ parameter~ yourself.
  }

\msg_new:nnn {xsim} {parameter-undefined}
  {
    You~ tried~ to~ set~ the~ parameter~ `#2'~ for~ exercise~ type~ `#1'~
    \msg_line_context: . \\
    This~ parameter~ does~ not~ exist.~ Check~ for~ a~ typo~ or~ \\
    define~ the~ parameter~ yourself.
  }

\msg_new:nnn {xsim} {parameter-value}
  {
    A~ parameter~ needs~ to~ be~ set~ to~ a~ (possibly~ empty)~ value! \\
    Not~ setting~ parameter~ `#2'~ for~ exercise~ type~ `#1'~
    \msg_line_context: .
  }

% ----------------------------------------------------------------------------
% #1: type
% #2: parameter
% #3: value
\cs_new_protected:Npn \__xsim_set_parameter_type:nnn #1#2#3
  { \xsim_attribute_set:nnn {#1} {#2} {#3} }

% #1: type
% #2: parameter
\cs_new:Npn \xsim_get_parameter:nn #1#2
  { \xsim_attribute_get:nn {#1} {#2} }
\cs_generate_variant:Nn \xsim_get_parameter:nn {o}

% #1: type
% #2: parameter
\prg_new_conditional:Npnn \xsim_if_parameter_set:nn #1#2 {p,T,F,TF}
  {
    \xsim_attribute_if_set:nnTF {#1} {#2}
      { \prg_return_true: }
      { \prg_return_false: }
  }
\prg_generate_conditional_variant:Nnn \xsim_if_parameter_set:nn {o} {T,F,TF}

% ----------------------------------------------------------------------------

\xsim_new_list:n {types}
\xsim_new_list:n {order}
\xsim_at_end_document:n { \xsim_update_list:n {order} }
\xsim_new_list:n {idtypes}

% #1: true|false (fixed parameter)
% #2: true|false (mandatory parameter)
% #3: parameter name
\cs_new_protected:Npn \xsim_declare_parameter:nnn #1#2#3
  {
    \xsim_verbose:n { Declaring~ parameter~ `#3'. }
    \xsim_if_parameter_exist:nF {#3}
      {
        \seq_put_right:Nn \l__xsim_parameters_seq {#3}
        \bool_if:cT {c_#1_bool}
          { \seq_put_right:Nn \l__xsim_fixed_parameters_seq {#3} }
        \bool_if:cT {c_#2_bool}
          { \seq_put_right:Nn \l__xsim_mandatory_parameters_seq {#3} }
      }
  }
  
\cs_new_protected:Npn \xsim_remove_parameter:n #1
  {
    \xsim_verbose:n { Removing~ parameter~ `#1'. }
    \xsim_if_parameter_exist:nT {#1}
      { \seq_remove_all:Nn \l__xsim_parameters_seq {#1} }
  }

\prg_new_conditional:Npnn \xsim_if_parameter_exist:n #1 {p,T,F,TF}
  {
    \seq_if_in:NnTF \l__xsim_parameters_seq {#1}
      { \prg_return_true: }
      { \prg_return_false: }
  }
\cs_generate_variant:Nn \xsim_if_parameter_exist:nT {x}

\prg_new_conditional:Npnn \xsim_if_parameter_fixed:n #1 {p,T,F,TF}
  {
    \seq_if_in:NnTF \l__xsim_fixed_parameters_seq {#1}
      { \prg_return_true: }
      { \prg_return_false: }
  }

% #1: type
% #2: csv list of parameters
\cs_new_protected:Npn \xsim_set_parameters:nn #1#2
  {
    \cs_set_protected:Npn \__xsim_set_parameter:n ##1
      { \msg_warning:nnnn {xsim} {parameter-value} {#1} {##1} }
    \cs_set_protected:Npn \__xsim_set_parameter:nn ##1##2
      { \xsim_set_parameter:nnn {#1} {##1} {##2} }
    \keyval_parse:NNn
      \__xsim_set_parameter:n
      \__xsim_set_parameter:nn
      {#2}
  }
\cs_generate_variant:Nn \xsim_set_parameters:nn {nx}

% #1: type
\cs_new_protected:Npn \xsim_check_mandatory_parameters:n #1
  {
    \seq_map_inline:Nn \l__xsim_mandatory_parameters_seq
      {
        \xsim_if_parameter_set:nnF {#1} {##1}
          { \msg_error:nnnn {xsim} {mandatory-parameter} {##1} {#1} }
      }
  }

% #1: type
\prg_new_conditional:Npnn \xsim_if_type:n #1 {p,T,F,TF}
  {
    \seq_if_in:NnTF \l__xsim_exercise_types_seq {#1}
      { \prg_return_true: }
      { \prg_return_false: }
  }

% #1: type
% #2: parameter
% #3: value
\cs_new_protected:Npn \xsim_set_parameter:nnn #1#2#3
  {
    \xsim_if_parameter_exist:nTF {#2}
      {
        \xsim_if_parameter_fixed:nTF {#1}
          {
            \xsim_if_parameter_set:nnTF {#1} {#2}
              {
                \msg_warning:nnnn {xsim} {parameter-fixed} {#1} {#2}
                \xsim_verbose:n
                  {
                    Not~ setting~ already~ set~ and~ fixed~ parameter~ `#2'~
                    for~ type~ `#1'.
                  }
              }
              {
                \xsim_verbose:n
                  { Setting~ parameter~ `#2'~ for~ type~ `#1'~ to~ `#3'. }
                \__xsim_set_parameter_type:nnn {#1} {#2} {#3}
              }
          }
          {
            \xsim_verbose:n
              { Setting~ parameter~ `#2'~ for~ type~ `#1'~ to~ `#3'. }
            \__xsim_set_parameter_type:nnn {#1} {#2} {#3}
          }
      }
      { \msg_error:nnnn {xsim} {parameter-undefined} {#1} {#2} }
  }
\cs_generate_variant:Nn \xsim_set_parameter:nnn {nnx,nxx}

\cs_new:Npn \__xsim_get_parameter_tmp:n #1 {}
\cs_generate_variant:Nn \__xsim_get_parameter_tmp:n {V}

% #1: type
% #2: parameter
\cs_new_protected:Npn \xsim_get_parameter_if_set:nnTF #1#2#3#4
  {
    \group_begin:
      \xsim_if_parameter_exist:nTF {#2}
        {
          \xsim_if_parameter_set:nnTF {#1} {#2}
            {    
              \tl_set:Nx \ParameterValue
                { \xsim_get_parameter:nn {#1} {#2} }
              \cs_set:Npn \__xsim_get_parameter_tmp:n ##1 {#3}
              \__xsim_get_parameter_tmp:V \ParameterValue
            }
            {#4}
        }
        { \msg_error:nnn {xsim} {unknown-parameter} {#2} }
    \group_end:
  }
\cs_new_protected:Npn \xsim_get_parameter_if_set:nnT #1#2#3
  { \xsim_get_parameter_if_set:nnTF {#1} {#2} {#3} {} }
\cs_new_protected:Npn \xsim_get_parameter_if_set:nnF #1#2#3
  { \xsim_get_parameter_if_set:nnTF {#1} {#2} {} {#3} }
\cs_generate_variant:Nn \xsim_get_parameter_if_set:nnTF {o}

% ----------------------------------------------------------------------------
% inside the argument `#1' refers to the type
\cs_new_protected:Npn \xsim_foreach_exercise_type:n #1
  { \xsim_foreach_new_list_entry:nn {types} {#1} }

\cs_new_protected:Npn \xsim_foreach_exercise_id:n #1
  { \int_step_inline:nnnn {1} {1} { \g_xsim_max_id_int } {#1} }

\cs_new_protected:Npn \xsim_foreach_exercise_order:n #1
  { \xsim_foreach_new_list_entry:nn {order} {#1} }
  
% #1: macro name
% #2: 0 to 9 times `n' (= number of arguments of internal item)
% #3: code; may contain a suiting \__xsim_loop_item:<nnn>
\cs_new_protected:Npn \xsim_define_loop_macro:Nnn #1#2#3
  {
    \xsim_verbose:n { Defining~ loop~ macro~ #1 }
    \cs_new_protected:Npn #1 ##1##2
      { \cs_set:cn {__xsim_loop_item:#2} {##2} #3 }
  }

\cs_new:Npn \__xsim_loop_item:nnnnnn #1#2#3#4#5#6 {}
\cs_generate_variant:Nn \__xsim_loop_item:nnnnnn {VVxxxx}

% #1: type
% #2: id
% #3: boolean property
\cs_new_protected:Npn \__xsim_deliver_exercise_details:nnn #1#2#3
  {
    \tl_if_blank:nTF {#3}
      { \use:n }
      { \xsim_if_in_list:nnT {#3} {#1-#2=={true}} }
      {
        \tl_set_rescan:Nnx \ExerciseType {} {#1}
        \tl_set_rescan:Nnx \ExerciseID {} {#2}
        \__xsim_loop_item:VVxxxx
          \ExerciseType                                       % #1
          \ExerciseID                                         % #2
          { \xsim_get_property:nnn {#1} {#2} {counter} }      % #3
          { \xsim_get_property:nnn {#1} {#2} {subtitle} }     % #4
          { \xsim_get_property:nnn {#1} {#2} {points} }       % #5
          { \xsim_get_property:nnn {#1} {#2} {bonus-points} } % #6
       }
  }

% #1: boolean property
% #2: loop code
\xsim_define_loop_macro:Nnn \xsim_foreach_exercise_type_id:nn
  {nnnnnn}
  {
    \xsim_foreach_exercise_type:n
      {
        \xsim_foreach_exercise_id:n
          { \__xsim_deliver_exercise_details:nnn {##1} {####1} {#1} }
      }
  }

\xsim_define_loop_macro:Nnn \xsim_foreach_exercise_type_order:nn
  {nnnnnn}
  {
    \xsim_foreach_exercise_type:n
      {
        \xsim_foreach_exercise_order:n
          { \__xsim_deliver_exercise_details:nnn {##1} {####1} {#1} }
      }
  }

\xsim_define_loop_macro:Nnn \xsim_foreach_exercise_id_type:nn
  {nnnnnn}
  {
    \xsim_foreach_exercise_id:n
      {
        \xsim_foreach_exercise_type:n
          { \__xsim_deliver_exercise_details:nnn {####1} {##1} {#1} }
      }
  }

\cs_new:Npn \__xsim_loop_item:nn #1#2 {}
\cs_generate_variant:Nn \__xsim_loop_item:nn {VV}

\xsim_define_loop_macro:Nnn \xsim_foreach_exercise:nn
  {nn}
  {
    \cs_set:Npn \__xsim_tmpa:w ##1-##2 \q_stop {##1}
    \cs_set:Npn \__xsim_tmpb:w ##1-##2 \q_stop {##2}
    \seq_map_inline:cn {g_xsim_#1_order_seq}
      {
        \tl_set:Nx \l__xsim_tmpa_tl { \__xsim_tmpa:w ##1 \q_stop }
        \tl_set:Nx \l__xsim_tmpb_tl { \__xsim_tmpb:w ##1 \q_stop }
        \__xsim_loop_item:VV \l__xsim_tmpa_tl \l__xsim_tmpb_tl
      }
  }

% ----------------------------------------------------------------------------
\cs_new_protected:Npn \xsim_define_exercise_type_variables:n #1
  {
    \seq_put_right:Nn \l__xsim_exercise_types_seq {#1}
    \bool_new:c {l__xsim_#1_solution_print_bool}
    \bool_new:c {l__xsim_#1_exercise_print_bool}
    \bool_new:c {l__xsim_#1_exercise_use_bool}
  }

\cs_new_protected:Npn \xsim_define_counters:n #1
  {
    \xsim_verbose:n { Defining~ counters~ for~ new~ exercise~ type~ `#1' }
    \cs_if_exist:cF { c@ \xsim_get_parameter:nn {#1} {counter} }
      { \newcounter { \xsim_get_parameter:nn {#1} {counter} } }
    \cs_if_exist:cF { c@ \xsim_get_parameter:nn {#1} {solution-counter} }
      { \newcounter { \xsim_get_parameter:nn {#1} {solution-counter} } }
    \cs_if_exist:cF { c@ number of \xsim_get_parameter:nn {#1} {exercise-env} s }
      { \newcounter { number of \xsim_get_parameter:nn {#1} {exercise-env} s } }
    \xsim_if_parameter_set:nnT {#1} {within}
      {
        \counterwithin
          { \xsim_get_parameter:nn {#1} {counter} }
          { \xsim_get_parameter:nn {#1} {within} }
      }
    \xsim_if_parameter_set:nnT {#1} {the-counter}
      {
        \cs_set:cpx { the \xsim_get_parameter:nn {#1} {counter} }
          { \xsim_get_parameter:nn {#1} {the-counter} }
      }
  }

% #1: type
% #2: id
\cs_new_protected:Npn \xsim_step_exercise_counter:nn #1#2
  {
    \refstepcounter { \xsim_get_parameter:nn {#1} {counter} }
    \xsim_set_properties:nne {#1} {#2}
      {
        counter = \use:c { the \xsim_get_parameter:nn {#1} {counter} } ,
        counter-value = \arabic { \xsim_get_parameter:nn {#1} {counter} }
      }
  }
\cs_generate_variant:Nn \xsim_step_exercise_counter:nn {nV}

% #1: type
\cs_new_protected:Npn \xsim_declare_exercise_type:nn #1#2
  {
    \xsim_if_type:nTF {#1}
      { \msg_error:nnn {xsim} {type-exists} {#1} }
      {
        \xsim_verbose:n { Declaring~ new~ exercise~ type~ `#1'. }
        \xsim_define_exercise_type_variables:n {#1}
        \xsim_add_to_list:nn {types} {#1}
        \xsim_set_parameters:nn {#1} {#2}
        \xsim_set_parameter:nnx {#1} {number}
          { number of \xsim_get_parameter:nn {#1} {exercise-env} s }
        \xsim_check_mandatory_parameters:n {#1}
        \xsim_if_parameter_set:nnF {#1} {exercises-name}
          {
            \xsim_set_parameters:nn {#1}
              { exercises-name = \xsim_get_parameter:nn {#1} {exercise-name} s }
          }
        \xsim_if_parameter_set:nnF {#1} {solutions-name}
          {
            \xsim_set_parameters:nn {#1}
              { solutions-name = \xsim_get_parameter:nn {#1} {solution-name} s }
          }
        \xsim_if_parameter_set:nnF {#1} {counter}
          {
            \xsim_set_parameters:nx {#1}
              { counter = \xsim_get_parameter:nn {#1} {exercise-env} }
          }
        \xsim_if_parameter_set:nnF {#1} {solution-counter}
          {
            \xsim_set_parameters:nx {#1}
              { solution-counter = \xsim_get_parameter:nn {#1} {solution-env} }
          }
        \xsim_define_counters:n {#1}
        \xsim_verbose:n { Defining~ options~ for~ new~ exercise~ type~ `#1' }
        \keys_define:nx {xsim}
          {
            \xsim_get_parameter:nn {#1} {solution-env} / print
              .bool_set:N = \exp_not:c {l__xsim_#1_solution_print_bool} ,
            \xsim_get_parameter:nn {#1} {solution-env} / print
              .initial:n  = false ,
            \xsim_get_parameter:nn {#1} {exercise-env} / print
              .bool_set:N = \exp_not:c {l__xsim_#1_exercise_print_bool} ,
            \xsim_get_parameter:nn {#1} {exercise-env} / print
              .initial:n  = true ,
            \xsim_get_parameter:nn {#1} {exercise-env} / use
              .bool_set:N = \exp_not:c {l__xsim_#1_exercise_use_bool} ,
            \xsim_get_parameter:nn {#1} {exercise-env} / use
              .initial:n  = true ,
            \xsim_get_parameter:nn {#1} {exercise-env} / collect .choice: ,
            \xsim_get_parameter:nn {#1} {exercise-env} / collect / true .code:n =
              \keys_set:nn
                { xsim / \xsim_get_parameter:nn {#1} {exercise-env} }
                { use = false , print = false } ,
            \xsim_get_parameter:nn {#1} {exercise-env} / collect / false .code:n =
              \keys_set:nn
                { xsim / \xsim_get_parameter:nn {#1} {exercise-env} }
                { use = true , print = true } ,
            \xsim_get_parameter:nn {#1} {exercise-env} / collect .default:n = true ,
            \xsim_get_parameter:nn {#1} {exercise-env} / collect .initial:n = false ,
            \xsim_get_parameter:nn {#1} {exercise-env} / within .code:n =
              \exp_not:N \counterwithin { \xsim_get_parameter:nn {#1} {counter} } {####1},
            \xsim_get_parameter:nn {#1} {exercise-env} / the-counter .code:n =
              \cs_set:cpn {the \xsim_get_parameter:nn {#1} {counter} } {####1} ,
            \xsim_get_parameter:nn {#1} {exercise-env} / template
              .code:n = \xsim_set_parameter:nnn {#1} {exercise-template} {####1} ,
            \xsim_get_parameter:nn {#1} {solution-env} / template
              .code:n = \xsim_set_parameter:nnn {#1} {solution-template} {####1} ,
            \xsim_get_parameter:nn {#1} {exercise-env} / name
              .code:n = \xsim_set_parameter:nnn {#1} {exercise-name} {####1} ,
            \xsim_get_parameter:nn {#1} {exercise-env} s / name
              .code:n = \xsim_set_parameter:nnn {#1} {exercises-name} {####1} ,
            \xsim_get_parameter:nn {#1} {solution-env} / name
              .code:n = \xsim_set_parameter:nnn {#1} {solution-name} {####1} ,
            \xsim_get_parameter:nn {#1} {solution-env} s / name
              .code:n = \xsim_set_parameter:nnn {#1} {solutions-name} {####1} ,
            \xsim_get_parameter:nn {#1} {exercise-env} / heading
              .code:n = \xsim_set_parameter:nnn {#1} {exercise-heading} {####1} ,
            \xsim_get_parameter:nn {#1} {solution-env} / heading
              .code:n = \xsim_set_parameter:nnn {#1} {solution-heading} {####1}
          }
        \xsim_new_environment:nn {#1} {exercise}
        \xsim_new_environment:nn {#1} {solution}
      }
  }
  
% #1: type
% #2: id
\prg_new_conditional:Npnn \xsim_if_exercise_exist:nn #1#2 {T,F,TF}
  {
    \xsim_if_exist:nnnTF {#1} {#2} {exercise}
      { \prg_return_true: }
      { \prg_return_false: }
  }
\cs_generate_variant:Nn \xsim_if_exercise_exist:nnT {VV}

% ----------------------------------------------------------------------------
% define boolean properties <do> and <done> (<done> is a noupdate property)
% and the conditional:
%   \xsim_if_<do>:nnTF
%
% #1: <do>
% #2: <done>
\cs_new_protected:Npn \__xsim_new_exercise_mode:nn #1#2
  {
    % boolean:
    \xsim_declare_property:nnnn
      { \c_false_bool } { \c_true_bool } { \c_false_bool } {#1}
    % boolean:
    \xsim_declare_property:nnnn
      { \c_false_bool } { \c_true_bool } { \c_false_bool } {#1!}
    % boolean, noupdate:
    \xsim_declare_property:nnnn
      { \c_false_bool } { \c_true_bool } { \c_true_bool } {(#2)}
    % boolean:
    \xsim_declare_property:nnnn
      { \c_false_bool } { \c_true_bool } { \c_false_bool } {#2}
    % ##1: type
    % ##2: id
    % ##3: exercise|solution
    \prg_new_protected_conditional:cpnn {xsim_if_#1:nnn} ##1##2##3 {T,F,TF}
      {
        \use:c {xsim_##3_if_#1:nnTF} {##1} {##2}
          { \prg_return_true: }
          { \prg_return_false: }
      }
  }
\cs_generate_variant:Nn \prg_new_protected_conditional:Npnn {c}
\cs_generate_variant:Nn \prg_generate_conditional_variant:Nnn {c}

\__xsim_new_exercise_mode:nn {use}   {used}
\__xsim_new_exercise_mode:nn {print} {printed}

% #1: type
% #2: options
\cs_new_protected:Npn \xsim_exercise_setup:nn #1#2
  {
    \int_gincr:N \g_xsim_id_int
    \int_compare:nF { \g_xsim_max_id_int > \g_xsim_id_int }
      { \int_gset_eq:NN \g_xsim_max_id_int \g_xsim_id_int }
    \tl_gset:Nx \g_xsim_exercise_id_tl { \int_use:N \g_xsim_id_int }
    \tl_gset:Nn \g_xsim_exercise_type_tl {#1}
    \xsim_add_to_list:nx {idtypes} { \g_xsim_exercise_id_tl == {#1} }
    \xsim_foreach_goal:n
      {
        \xsim_if_property_set:nVnT {#1} \g_xsim_exercise_id_tl {##1}
          {
            \bool_set_true:c {l__xsim_temporary_##1_bool}
            \fp_set:cn {l__xsim_temporary_##1_fp}
              { \xsim_get_property:nVn {#1} \g_xsim_exercise_id_tl {##1} }
            \xsim_unset_property:nVn {#1} \g_xsim_exercise_id_tl {##1}
          }
      }
    \xsim_set_properties:nVe {#1} \g_xsim_exercise_id_tl
      {
        % properties set by the user:
        \exp_not:n {#2} ,
        % properties which need to have certain values:
        id = \g_xsim_exercise_id_tl ,
        printed = false ,
        used = false
      }
    \xsim_foreach_goal:n
      {
        \xsim_if_property_set:nVnT {#1} \g_xsim_exercise_id_tl {##1}
          {
            \xsim_set_property:nVne {#1} \g_xsim_exercise_id_tl {user##1}
              { \xsim_get_property:nVn {#1} \g_xsim_exercise_id_tl {##1} }
          }
        \bool_if:cT {l__xsim_temporary_##1_bool}
          {
            \xsim_set_property:nVne {#1} \g_xsim_exercise_id_tl {##1}
              { \fp_to_decimal:c {l__xsim_temporary_##1_fp} }
          }
      }
  }

\prg_new_conditional:Npnn \xsim_exercise_if_use:nn #1#2 {T,F,TF}
  {
    \xsim_if_boolean_property:nnnTF {#1} {#2} {use!}
      { \prg_return_true: }
      {
        \xsim_if_tagged_use:nnTF {#1} {#2}
          {
            \xsim_if_boolean_property:nnnTF {#1} {#2} {(used)}
              { \prg_return_false: }
              {
                \xsim_if_insert_mode:TF
                  {
                    \xsim_if_property_set:nnnTF {#1} {#2} {use}
                      {
                        \xsim_if_boolean_property:nnnTF {#1} {#2} {use}
                          { \prg_return_true: }
                          { \prg_return_false: }
                      }
                      {
                        \bool_if:cTF {l__xsim_#1_exercise_use_bool}
                          { \prg_return_false: }
                          { \prg_return_true: }
                      }
                  }
                  {
                    \xsim_if_property_set:nnnTF {#1} {#2} {use}
                      {
                        \xsim_if_boolean_property:nnnTF {#1} {#2} {use}
                          { \prg_return_true: }
                          { \prg_return_false: }
                      }
                      {
                        \bool_if:cTF {l__xsim_#1_exercise_use_bool}
                          { \prg_return_true: }
                          { \prg_return_false: }
                      }
                  }
              }
          }
          { \prg_return_false: }
      }
  }
\prg_generate_conditional_variant:Nnn \xsim_exercise_if_use:nn {nV} {T,TF}

% #1: type
% #2: ID
\cs_new_protected:Npn \xsim_exercise_use_setup:nn #1#2
  {
    \stepcounter { \xsim_get_parameter:nn {#1} {number} }
    \xsim_step_exercise_counter:nn {#1} {#2}
    \xsim_set_properties:nne {#1} {#2}
      {
        section-value = \arabic{section} ,
        section = \thesection ,
        page-value = \arabic{page} ,
        sectioning =
          { \cs_if_exist:NTF \thechapter { \arabic {chapter} } {0} }
          { \arabic {section} }
          { \arabic {subsection} }
          { \arabic {subsubsection} }
          { \arabic {paragraph} } ,
        page = \thepage
      }
    \xsim_if_property_exist:nT {chapter}
      {
        \xsim_set_properties:nne {#1} {#2}
          {
            chapter-value = \arabic{chapter} ,
            chapter = \thechapter
          }
      }
  }
\cs_generate_variant:Nn \xsim_exercise_use_setup:nn {nV}

\prg_new_conditional:Npnn \xsim_exercise_if_print:nn #1#2 {T,F,TF}
  {
    \xsim_if_boolean_property:nnnTF {#1} {#2} {print!}
      { \prg_return_true: }
      {
        \xsim_if_tagged:nnTF {#1} {#2}
          {
            \xsim_if_insert_mode:TF
              { \prg_return_true: }
              {
                \bool_if:cTF {l__xsim_#1_exercise_print_bool}
                  {
                    \xsim_if_property_set:nnnTF {#1} {#2} {print}
                      {
                        \xsim_if_boolean_property:nnnTF {#1} {#2} {print}
                          { \prg_return_true: }
                          { \prg_return_false: }
                      }
                      { \prg_return_true: }
                  }
                  {
                    \xsim_if_boolean_property:nnnTF {#1} {#2} {print}
                      { \prg_return_true: }
                      { \prg_return_false: }
                  }
              }
          }
          { \prg_return_false: }
      }
  }
\prg_generate_conditional_variant:Nnn \xsim_exercise_if_print:nn {nV} {T,TF}

% #1: type
% #2: ID
\cs_new_protected:Npn \xsim_exercise_print_setup:nn #1#2
  {
    \xsim_foreach_goal:n
      {
        \xsim_if_property_set:nnnT {#1} {#2} {body##1}
          { \xsim_set_property:nnnn {#1} {#2} {body##1} {0} }
      }
  }
\cs_generate_variant:Nn \xsim_exercise_print_setup:nn {nV}

% #1: type
% #2: options
\cs_new_protected:Npn \xsim_start_exercise:nn #1#2
  {
    \xsim_if_insert_mode:F { \xsim_exercise_setup:nn {#1} {#2} }
    \xsim_exercise_if_use:nVT {#1} \g_xsim_exercise_id_tl
      { \xsim_exercise_use_setup:nV {#1} \g_xsim_exercise_id_tl }
    \xsim_exercise_if_print:nVT {#1} \g_xsim_exercise_id_tl
      { \xsim_exercise_print_setup:nV {#1} \g_xsim_exercise_id_tl }
    \tl_set_eq:NN \ExerciseID \g_xsim_exercise_id_tl
    \tl_set:Nn \ExerciseType {#1}
    \xsim_verbose:x
      { Starting~ exercise~ type~ `#1' with~ id~ ` \g_xsim_exercise_id_tl '. }
    \xsim_if_insert_mode:F { \xsim_collect:nV {#1} \g_xsim_exercise_id_tl }
  }

% #1: type
\cs_new_protected:Npn \xsim_stop_exercise:n #1
  {
    \tl_set_eq:NN \ExerciseID \g_xsim_exercise_id_tl
    \tl_set:Nn \ExerciseType {#1}
    \xsim_if_insert_mode:F
      {
        \xsim_foreach_goal:n
          {
            \xsim_if_property_set:nVnTF {#1} \g_xsim_exercise_id_tl {##1}
              {
                \fp_set:Nn \l__xsim_tmpa_fp
                  { \xsim_get_property:nVn {#1} \g_xsim_exercise_id_tl {body##1} + 0 }
                \fp_set:Nn \l__xsim_tmpb_fp
                  { \xsim_get_property:nVn {#1} \g_xsim_exercise_id_tl {user##1} + 0 }
                \fp_set:Nn \l__xsim_tmpa_fp { \l__xsim_tmpa_fp + \l__xsim_tmpb_fp }
                \xsim_set_property:nVne {#1} \g_xsim_exercise_id_tl {##1}
                  { \fp_to_decimal:N \l__xsim_tmpa_fp }
              }
              {
                \xsim_if_property_set:nVnT {#1} \g_xsim_exercise_id_tl {body##1}
                  { \xsim_set_property:nVnn {#1} \g_xsim_exercise_id_tl {##1} {0} }
              }
          }
      }
    \xsim_exercise_if_print:nVT {#1} \g_xsim_exercise_id_tl
      {
        \xsim_set_property:nVnn {#1} \g_xsim_exercise_id_tl {(printed)} {true}
        \xsim_set_property:nVnn {#1} \g_xsim_exercise_id_tl {printed} {true}
      }
    \xsim_exercise_if_use:nVT {#1} \g_xsim_exercise_id_tl
      {
        \xsim_add_to_list:nV {order} \g_xsim_exercise_id_tl
        \xsim_set_property:nVnn {#1} \g_xsim_exercise_id_tl {(used)} {true}
        \xsim_set_property:nVnn {#1} \g_xsim_exercise_id_tl {used} {true}
        \xsim_update_goals:nV {#1} \g_xsim_exercise_id_tl
      }
  }

% ----------------------------------------------------------------------------

\xsim_new_aux_property:cpn {total-number} #1
  { \int_gset:Nn \g_xsim_max_id_int {#1} }

\hook_gput_code:nnn {begindocument/before} {xsim}
  {
    \xsim_foreach_exercise_type:n
      {
        \tl_new:c { number of #1 s }
        \tl_gset:cn { number of #1 s } {0}
        \xsim_new_aux_property:cpn {#1} ##1
          { \tl_gset:cn { number of #1 s } {##1} }
      }
  }

\xsim_at_begin_document:n
  {
    \xsim_foreach_exercise_type:n
      {
        \xsim_write_to_aux:x
          { \token_to_str:N \providecommand \token_to_str:N \numberof #1 s {} }
      }
  }

\xsim_at_end_document:n
  {
    \xsim_if_clear_aux:T
      {
        \int_compare:nNnF { \g_xsim_max_id_int } = { \g_xsim_id_int }
          { \xsim_empty_lists: }
      }
    \xsim_add_property_to_aux:nn {total-number} { { \int_use:N \g_xsim_id_int } }
    \xsim_foreach_exercise_type:n
      {
        \xsim_add_property_to_aux:nn {#1}
          { { \arabic { \xsim_get_parameter:nn {#1} {number} } } }
      }
    \xsim_update_list:n {types}
    \xsim_update_list:n {idtypes}
  }

\XSIMmoduleend
\XSIMmodule{solutions}{managing solutions}

\bool_new:N \l_xsim_inside_solution_bool
\bool_new:N \l__xsim_include_question_bool
\bool_new:N \l__xsim_printsolutions_headings_bool
\bool_new:N \l__xsim_printsolutions_conditions_bool
\bool_new:N \l____xsim_this_condition_bool

\seq_new:N \l__xsim_printsolutions_conditions_seq

\int_new:N  \l_xsim_printsolutions_section_int
\int_new:N  \l_xsim_printsolutions_chapter_int

\tl_new:N   \l__xsim_printsolutions_headings_template_tl
\tl_new:N   \ExerciseSection
\tl_new:N   \ExerciseChapter
\tl_new:N   \l__xsim_printsolutions_collection_tl

% ----------------------------------------------------------------------------

\prg_new_conditional:Npnn \xsim_if_inside_solution: {p,T,F,TF}
  {
    \bool_if:NTF \l_xsim_inside_solution_bool
      { \prg_return_true: }
      { \prg_return_false: }
  }

\prg_new_conditional:Npnn \xsim_if_solution_print:n #1 {p,T,F,TF}
  {
    \bool_if:cTF {l__xsim_#1_solution_print_bool}
      { \prg_return_true: }
      { \prg_return_false: }
  }

\bool_new:N \l____xsim_print_bool

% #1: type
% #2: id
\prg_new_protected_conditional:Npnn \xsim_solution_if_print:nn #1#2 {T,F,TF}
  {
    \bool_set_true:N \l____xsim_print_bool
    \xsim_if_tagged:nnF {#1} {#2}
      { \bool_set_false:N \l____xsim_print_bool }
    \xsim_if_boolean_property:nnnF {#1} {#2} {(used)}
      { \bool_set_false:N \l____xsim_print_bool }
    \bool_if:cF {l__xsim_#1_solution_print_bool}
      { \bool_set_false:N \l____xsim_print_bool }
    \bool_if:NTF \l____xsim_print_bool
      { \prg_return_true: }
      { \prg_return_false: }
  }
\cs_generate_variant:Nn \xsim_solution_if_print:nnT {nV,oo}

% #1: type
% #2: id
\prg_new_protected_conditional:Npnn \xsim_if_solution_exist:nn #1#2 {T,F,TF}
  {
    \xsim_if_boolean_property:nnnTF {#1} {#2} {solution}
      { \xsim_if_exist:nnnTF {#1} {#2} {exercise} }
      { \xsim_if_exist:nnnTF {#1} {#2} {solution} }
      { \prg_return_true: }
      { \prg_return_false: }
  }
\cs_generate_variant:Nn \xsim_if_solution_exist:nnT  {VV,oo}
\cs_generate_variant:Nn \xsim_if_solution_exist:nnTF {oo}

% #1: type
% #2: options
\cs_new_protected:Npn \xsim_start_solution:nn #1#2
  {
    \keys_set:xn { xsim/\xsim_get_parameter:nn {#1} {solution-env} } {#2}
    \bool_set_true:N \l_xsim_inside_solution_bool
    \refstepcounter { \xsim_get_parameter:nn {#1} {solution-counter} }
  }

\cs_new_protected:Npn \xsim_stop_solution:n #1 {}

% ----------------------------------------------------------------------------

% #1: conditional base function (three n args)
% #2: name
% #3: code (refer to type and id with `#1' and `#2'), #3 refers to the boolean
%     properties use/print
\cs_new_protected:Npn \xsim_new_solutions_condition:Nnn #1#2#3
  {
    \prg_new_protected_conditional:Npnn #1 ##1##2##3 {T,F,TF}
      {
        \xsim_if_in_list:nnTF {##3} {##1-##2=={true}}
          {
            \__xsim_printsolutions_if_condition:nTF {#2}
              { \__xsim_printsolutions_condition_true: #3 }
              { \prg_return_false: }
          }
          { \prg_return_false: }
      }
  }

% per section condition:
\xsim_new_solutions_condition:Nnn \__xsim_if_solutions_per_section:nnn
  {per-section}
  {
    \xsim_if_in_list:nxTF {section-value}
      { #1-#2 == { \int_use:N \l_xsim_printsolutions_section_int } }
      {
        \tl_set:Nx \ExerciseSection
          { \xsim_get_property:nnn {#1} {#2} {section} }
        \prg_return_true:   
      }
      { \prg_return_false: }
  }

% per chapter condition:
\xsim_new_solutions_condition:Nnn \__xsim_if_solutions_per_chapter:nnn
  {per-chapter}
  {
    \xsim_if_in_list:nxTF {chapter-value}
      { #1-#2 == { \int_use:N \l_xsim_printsolutions_chapter_int } }
      {
        \tl_set:Nx \ExerciseChapter
          { \xsim_get_property:nnn {#1} {#2} {chapter} }
        \prg_return_true:   
      }
      { \prg_return_false: }
  }

% per collection condition:
\xsim_new_solutions_condition:Nnn \__xsim_if_solutions_in_collection:nnn
  {per-collection}
  {
    \tl_if_blank:VTF \l__xsim_printsolutions_collection_tl
      { \prg_return_false: }
      {
        \xsim_if_in_collection:VnnTF
          \l__xsim_printsolutions_collection_tl
          {#1}
          {#2}
          { \prg_return_true: }
          { \prg_return_false: }
      }
  }

% ----------------------------------------------------------------------------
% conditions mechanism
\cs_new_protected:Npn \__xsim_printsolutions_condition_false:
  { \bool_set_false:N \l__xsim_printsolutions_conditions_bool }

\cs_new_protected:Npn \__xsim_printsolutions_condition_true:
  { \bool_set_true:N \l__xsim_printsolutions_conditions_bool }

% #1: type
% #2: id
% #3: boolean property
\prg_new_conditional:Npnn
  \__xsim_printsolutions_if_conditions:nnn #1#2#3 {T,F,TF}
  {
    \bool_if:NTF \l__xsim_printsolutions_conditions_bool
      { \prg_return_true: }
      {
        \xsim_if_in_list:nnTF {#3} {#1-#2=={true}}
          { \prg_return_false: }
          { \prg_return_true: }
      }
  }

% #1: name
% #2: condition
\cs_new_protected:Npn \xsim_declare_printsolutions_condition:nn #1#2
  {
    \bool_new:c {l__xsim_printsolutions_#1_condition_bool}
    \seq_put_right:Nn \l__xsim_printsolutions_conditions_seq {#1}
    \xsim_attribute_set:nnn {printsolutions} {#1} {#2}
  }

\prg_new_conditional:Npnn \__xsim_printsolutions_if_condition:n #1 {T,F,TF}
  {
    \bool_if:cTF {l__xsim_printsolutions_#1_condition_bool}
      { \prg_return_true: }
      { \prg_return_false: }
  }

% #1: name
% #2: true|false
\cs_new_protected:Npn \__xsim_printsolutions_condition:nn #1#2
  { \bool_set_eq:cc {l__xsim_printsolutions_#1_condition_bool} {c_#2_bool} }

% false if any condition is false for this solution:
% #1: type
% #2: id
% #3: boolean property
\prg_new_protected_conditional:Npnn
  \__xsim_printsolutions_check_conditions:nnn #1#2#3 {T,F,TF}
  {
    \__xsim_printsolutions_condition_false:
    \bool_set_true:N \l____xsim_this_condition_bool
    \seq_map_inline:Nn \l__xsim_printsolutions_conditions_seq
      {
        \bool_if:cT {l__xsim_printsolutions_##1_condition_bool}
          {
            \xsim_attribute_get:nn {printsolutions} {##1}
              {#1} {#2} {#3}
              { }
              { \bool_set_false:N \l____xsim_this_condition_bool }
          }
      }
    \bool_if:NTF \l____xsim_this_condition_bool
      { \prg_return_true: }
      { \prg_return_false: }
  }


% #1: type
% #2: id
% #3: boolean property
% #4: code
\cs_new_protected:Npn \xsim_if_this_solution_do:nnnn #1#2#3#4
  {
    \xsim_if_solution_exist:nnT {#1} {#2}
      { \__xsim_printsolutions_check_conditions:nnnT {#1} {#2} {#3} {#4} }
  }
\cs_generate_variant:Nn \xsim_if_this_solution_do:nnnn {nV}

% #1: type
% #2: boolean property
% #3: code
\cs_new_protected:Npn \xsim_if_solutions_do:nnn #1#2#3
  {
    \xsim_foreach_exercise:nn {#2}
      {
        \str_if_eq:nnT {#1} {##1}
          {
            \xsim_if_this_solution_do:nnnn
              {#1}
              {##2}
              {#2}
              {#3}
          }
      }
  }
\cs_generate_variant:Nn \str_if_eq:nnT {nV}

% ----------------------------------------------------------------------------
% #1: boolean -- if true only the solutions of printed exercises will be
%     output
% #2: pre
% #3: post
% #4: type
% #5: code (refer to type with #1 and id with #2)
\cs_new_protected:Npn \xsim_print_type_code:nnnnn #1#2#3#4#5
  {
    \group_begin:
      \cs_set_protected:Npn \__xsim_print_entry:nn ##1##2 {#5}
      #2
      \bool_set_false:N \l__xsim_tmpa_bool
      \bool_if:nTF {#1}
        {
          \xsim_if_solutions_do:nnn {#4} {printed}
            { \bool_set_true:N \l__xsim_tmpa_bool }
        }
        {
          \xsim_if_solutions_do:nnn {#4} {used}
            { \bool_set_true:N \l__xsim_tmpa_bool }
        }
      \tl_set:Nn \ExerciseType {#4}
      \bool_if:NT \l__xsim_tmpa_bool
        {
          \bool_if:NT \l__xsim_printsolutions_headings_bool
            {
              \xsim_use_template:nV
                {heading}
                \l__xsim_printsolutions_headings_template_tl
            }
        }
      \bool_if:nTF {#1}
        {
          \xsim_if_solutions_do:nnn {#4} {printed}
            { \__xsim_print_entry:nn {#4} {##2} }
        }
        {
          \xsim_if_solutions_do:nnn {#4} {used}
            { \__xsim_print_entry:nn {#4} {##2} }
        }
      #3
    \group_end:
  }

% #1: boolean -- if true only the solutions of printed exercises will be
%     output
% #2: options
% #3: type
\cs_new_protected:Npn \xsim_print_type_solutions:nnn #1#2#3
  {
    \xsim_print_type_code:nnnnn
      {#1}
      {
        \xsim_verbose:n
          { Printing~ solutions~ for~ exercise~ type~ `#3'. }
        \keys_set:nn {xsim/print-solutions} {#2}
      }
      {}
      {#3}
      { \xsim_insert:nnnn {##1} {##2} {} {solution} }
  }

% #1: boolean -- if true only the solutions of printed exercises will be
%     output
% #2: options
\cs_new_protected:Npn \xsim_print_all_solutions_per_type:nn #1#2
  {
    \xsim_foreach_exercise_type:n
      { \xsim_print_type_solutions:nnn {#1} {#2} {##1} }
  }

% #1: boolean -- if true only the solutions of printed exercises will be
%     output
% #2: options
\cs_new_protected:Npn \xsim_print_all_solutions_per_id:nn #1#2
  {
    \xsim_verbose:n { Printing~ solutions. }
    \group_begin:
      \keys_set:nn {xsim/print-solutions} {#2}
      \bool_set_false:N \l__xsim_tmpa_bool
      \bool_if:nTF {#1}
        {
          \xsim_foreach_exercise_id_type:nn {print}
            {
              \xsim_if_this_solution_do:nnnn {##1} {##2} {print}
                { \xsim_insert:nnnn {##1} {##2} {} {solution} }
            }
        }
        {
          \xsim_foreach_exercise_id_type:nn {used}
            {
              \xsim_if_this_solution_do:nnnn {##1} {##2} {used}
                { \xsim_insert:nnnn {##1} {##2} {} {solution} }
            }
        }
    \group_end:
  }

% ----------------------------------------------------------------------------

\xsim_declare_printsolutions_condition:nn {per-section}
  { \__xsim_if_solutions_per_section:nnnTF }
\xsim_declare_printsolutions_condition:nn {per-chapter}
  { \__xsim_if_solutions_per_chapter:nnnTF }
\xsim_declare_printsolutions_condition:nn {per-collection}
  { \__xsim_if_solutions_in_collection:nnnTF }

% ----------------------------------------------------------------------------

\keys_define:nn {xsim/print-solutions}
  {
    headings .bool_set:N = \l__xsim_printsolutions_headings_bool ,
    headings .initial:n  = true ,
    headings-template .tl_set:N  = \l__xsim_printsolutions_headings_template_tl ,
    headings-template .initial:n = default ,
    include-exercise  .bool_set:N = \l__xsim_include_question_bool ,
    include-exercise  .initial:n = false ,
    section           .choice: ,
    section/true      .code:n    =
      \__xsim_printsolutions_condition:nn  {per-section} {true}
      \int_set:Nn \l_xsim_printsolutions_section_int { \value {section} } ,
    section/false     .code:n    =
      \__xsim_printsolutions_condition:nn  {per-section} {false} ,
    section/unknown   .code:n    =
      \__xsim_printsolutions_condition:nn  {per-section} {true}
      \int_set:Nn \l_xsim_printsolutions_section_int {#1} ,
    section  .default:n  = true ,
    section  .initial:n  = false ,
    chapter           .choice: ,
    chapter/true      .code:n    =
      \__xsim_printsolutions_condition:nn  {per-chapter} {true}
      \int_set:Nn \l_xsim_printsolutions_chapter_int { \value {chapter} } ,
    chapter/false     .code:n    =
      \__xsim_printsolutions_condition:nn  {per-chapter} {false} ,
    chapter/unknown   .code:n    =
      \__xsim_printsolutions_condition:nn  {per-chapter} {true}
      \int_set:Nn \l_xsim_printsolutions_chapter_int {#1} ,
    chapter           .default:n  = true ,
    chapter           .initial:n  = false ,
    collection        .choice: ,
    collection/false  .code:n     =
      \__xsim_printsolutions_condition:nn  {per-collection} {false} ,
    collection/unknown .code:n    =
      \xsim_if_collection_exist:nT {#1}
        {
          \__xsim_printsolutions_condition:nn  {per-collection} {true}
          \tl_set:Nn \l__xsim_printsolutions_collection_tl {#1}
        } ,
    collection         .initial:n = false
  }

\XSIMmoduleend
\XSIMmodule{collections}{collect exercises and print collected exercises}

\seq_new:N \g__xsim_collections_seq
\bool_new:N \l____xsim_active_bool

\msg_new:nnn {xsim} {collection-exists}
  {
    The~ collection~ `#1'~ you're~ trying~ to~ define~ \msg_line_context:
    \c_space_tl already~ exists.
  }

\msg_new:nnn {xsim} {collection-unknown}
  {
    The~ collection~ `#1'~ does~ not~ seem~ to~ exist~ \msg_line_context: .
    If~ this~ is~ not�� a~ typo~ define~ it~ first.
  }

\msg_new:nnn {xsim} {collection-activate}
  {
    The~ collection~ `#1'~ is~ already~ active~ \msg_line_context: .~
    Not~ activating~ it~ now.
  }

\msg_new:nnn {xsim} {collection-deactivate}
  {
    The~ collection~ `#1'~ is~ not~ active~ \msg_line_context: .~
    Not~ deactivating~ it~ now.
  }

\msg_new:nnn {xsim} {print-collection-choice}
  {
    The~ choice~ `#1'~ neither~ exists~ for~ `print-collection/print'~ nor~
    for~ `random/print'.~ I'm~ using~ `exercises'~ instead~ \msg_line_context: .
  }

% ----------------------------------------------------------------------------
\xsim_declare_property:nnnn
  { \c_false_bool }
  { \c_false_bool }
  { \c_false_bool }
  {collections}

% #1: collection name
% #2: conditions: clist of <tag>=<value>
\cs_new_protected:Npn \xsim_new_collection:nn #1#2
  {
    \xsim_if_collection_exist:nTF {#1}
      { \msg_error:nnn {xsim} {collection-exists} {#1} }
      {
        \xsim_verbose:n { Declaring~ new~ collection~ `#1'. }
        \xsim_attribute_new:nn {collection} {#1}
        \xsim_attribute_set:nnn {collection} {#1} { \c_false_bool }
        \xsim_attribute_new:nn {collection:conditions} {#1}
        \xsim_attribute_set:nnn {collection:conditions} {#1} {#2}
        \seq_gput_right:Nn \g__xsim_collections_seq {#1}
        \xsim_new_list:n {collection:#1}
        \bool_new:c {l__xsim_collection_#1_condition_all_bool}
        \keys_define:nn {xsim/collection/#1}
          {
            match-all .bool_set:c = {l__xsim_collection_#1_condition_all_bool} ,
            match-all .initial:n  = true ,
            match-any .bool_set_inverse:c = {l__xsim_collection_#1_condition_all_bool}
          }
        \xsim_before_begin_document:n
          {
            \xsim_foreach_exercise_type:n
              {
                \int_new:c {g_xsim_collection_##1_in_#1_int}
                \newcounter {total##1in#1}
              }
          }
        \xsim_at_end_document:n
          {
            \xsim_update_list:n {collection:#1}
            \xsim_foreach_exercise_type:n
              {
                \xsim_write_to_aux:x
                  {
                    \token_to_str:N \setcounter {total##1in#1}
                      { \int_use:c {g_xsim_collection_##1_in_#1_int} }
                  }
              }
          }
      }
  }

% #1: collection name
\prg_new_conditional:Npnn \xsim_if_collection_exist:n #1 {T,F,TF}
  {
    \seq_if_in:NnTF \g__xsim_collections_seq {#1}
      { \prg_return_true: }
      { \prg_return_false: }
  }

\cs_new_protected:Npn \xsim_foreach_collection:n #1
  { \seq_map_inline:Nn \g__xsim_collections_seq {#1} }

\cs_new:Npn \xsim_collection_map_break: { \seq_map_break: }

% ----------------------------------------------------------------------------
% #1: collection name
\prg_new_conditional:Npnn \xsim_if_collection_active:n #1 {T,F,TF}
  {
    \xsim_attribute_if_set:nnTF {collection} {#1}
      {
        \bool_if:nTF
          { \xsim_attribute_get:nn {collection} {#1} }
          { \prg_return_true: }
          { \prg_return_false: }
      }
      { \prg_return_false: }
  }

% #1: collection name
\cs_new_protected:Npn \xsim_activate_collection:n #1
  {
    \xsim_if_collection_exist:nTF {#1}
      {
        \xsim_if_collection_active:nTF {#1}
          { \msg_warning:nnn {xsim} {collection-activate} {#1} }
          {
            \xsim_verbose:n { Activating~ collection~ `#1'. }
            \xsim_attribute_set:nnn {collection} {#1} { \c_true_bool }
          }
      }
      { \msg_error:nnn {xsim} {collection-unknown} {#1} }
  }

% #1: collection name
\cs_new_protected:Npn \xsim_deactivate_collection:n #1
  {
    \xsim_if_collection_exist:nTF {#1}
      {
        \xsim_if_collection_active:nTF {#1}
          {
            \xsim_verbose:n { Deactivating~ collection~ `#1'. }
            \xsim_attribute_set:nnn {collection} {#1} { \c_false_bool }
          }
          { \msg_warning:nnn {xsim} {collection-deactivate} {#1} }
      }
      { \msg_error:nnn {xsim} {collection-unknown} {#1} }
  }

% #1: csv list of collections
% #2: type
\cs_new_protected:Npn \xsim_collect_type_start:nn #1#2
  {
    \group_begin:
    \keys_set:nn {xsim} { #2/collect = true }
    \clist_map_inline:nn {#1}
      {
        \xsim_if_collection_active:nF {##1}
          { \xsim_activate_collection:n {##1} }
      }
  }

% #1: csv list of collections
\cs_new_protected:Npn \xsim_collect_start:n #1
  {
    \group_begin:
      \xsim_foreach_exercise_type:n
        {
          \keys_set:nx {xsim}
            { \xsim_get_parameter:nn {##1} {exercise-env} / collect = true }
        }
      \clist_map_inline:nn {#1}
        {
          \xsim_if_collection_active:nF {##1}
            { \xsim_activate_collection:n {##1} }
        }
  }

% #1: csv list of collections
\cs_new_protected:Npn \xsim_collect_stop:n #1
  {
      \clist_map_inline:nn {#1} { \xsim_deactivate_collection:n {##1} }
    \group_end:
  }

\keys_define:nn {xsim}
  {
    collect .code:n =
      \xsim_foreach_exercise_type:n
        {
          \keys_set:nx {xsim}
            { \xsim_get_parameter:nn {##1} {exercise-env} / collect = #1 }
        } ,
    collect .default:n = true ,
    collect .initial:n = false
  }

% ----------------------------------------------------------------------------
% #1: collection name
% #2: type
% #3: ID
\prg_new_conditional:Npnn \xsim_if_in_collection:nnn #1#2#3 {T,F,TF}
  {
    \xsim_if_in_list:nnTF {collection:#1} {#2-#3}
      { \prg_return_true: }
      { \prg_return_false: }
  }
\cs_generate_variant:Nn \xsim_if_in_collection:nnnTF {V}
\cs_generate_variant:Nn \xsim_if_in_collection:nnnT  {x,nV}

\cs_new:Npn \xsim_collection_count:n #1
  { \xsim_list_count:n {collection:#1} }

\bool_new:N \l____xsim_collection_condition_bool

% #1: collection name
% #2: type
% #3: ID
\prg_new_protected_conditional:Npnn \xsim_collection_conditions_if:nnn #1#2#3 {T,F,TF}
  {
    \bool_if:cTF {l__xsim_collection_#1_condition_all_bool}
      { \bool_set_true:N \l____xsim_collection_condition_bool }
      { \bool_set_false:N \l____xsim_collection_condition_bool }
    \clist_set:Nx \l__xsim_tmpa_clist
      { \xsim_attribute_get:nn {collection:conditions} {#1} }
    \clist_if_empty:NTF \l__xsim_tmpa_clist
      { \bool_set_true:N \l____xsim_collection_condition_bool }
      {
        \clist_map_inline:Vn
          \l__xsim_tmpa_clist
          {
            \tl_set:Nx \l__xsim_tmpa_tl
              { \__xsim_get_condition_tag:w ##1 \q_stop }
            \tl_set:Nx \l__xsim_tmpb_tl
              { \__xsim_get_condition_values:w ##1 \q_stop }
            \xsim_if_tag_type_exist:VTF \l__xsim_tmpa_tl
              {
                \bool_if:cTF {l__xsim_collection_#1_condition_all_bool}
                  {
                    \xsim_has_tags:nnVVF {#2} {#3} \l__xsim_tmpa_tl \l__xsim_tmpb_tl
                      {
                        \bool_set_false:N \l____xsim_collection_condition_bool
                        \clist_map_break:
                      }
                  }
                  {
                    \xsim_has_tags:nnVVT {#2} {#3} \l__xsim_tmpa_tl \l__xsim_tmpb_tl
                      {
                        \bool_set_true:N \l____xsim_collection_condition_bool
                        \clist_map_break:
                      }
                  }
              }
              { \msg_warning:nnV {xsim} {tag-unknown} \l__xsim_tmpa_tl }
          }
      }
    \bool_if:NTF \l____xsim_collection_condition_bool
      { \prg_return_true: }
      { \prg_return_false: }
  }

\cs_new:Npn \__xsim_get_condition_tag:w #1=#2 \q_stop {#1}
\cs_new:Npn \__xsim_get_condition_values:w #1=#2 \q_stop {#2}

% #1: type
% #2: ID
\cs_new_protected:Npn \xsim_collect:nn #1#2
  {
    \xsim_foreach_collection:n
      {
        \xsim_if_collection_active:nT {##1}
          {
            \xsim_collection_conditions_if:nnnT {##1} {#1} {#2}
              { \xsim_add_to_collection:nnn {##1} {#1} {#2} }
          }
      }
  }
\cs_generate_variant:Nn \xsim_collect:nn {nV}

% #1: collection name
% #2: type
% #3: ID
\cs_new_protected:Npn \xsim_add_to_collection:nnn #1#2#3
  {
    \xsim_verbose:n
      { Adding~ exercise~ type~ `#2'~ ID~ `#3'~ to~ collection~ `#1'. }
    \clist_set:Nx \l__xsim_tmpa_clist
      { \xsim_get_property:nnn {#2} {#3} {collections} }
    \clist_if_in:NnF \l__xsim_tmpa_clist {#1}
      { \clist_put_right:Nn \l__xsim_tmpa_clist {#1} }
    \xsim_set_property:nnnV {#2} {#3} {collections} \l__xsim_tmpa_clist
    \xsim_add_to_list:nn {collection:#1} {#2-#3}
    \int_gincr:c {g_xsim_collection_#2_in_#1_int}
  }

% ----------------------------------------------------------------------------
\bool_new:N \l__xsim_printcollection_headings_bool
\tl_new:N   \l__xsim_printcollection_headings_template_tl
\tl_new:N   \l__xsim_print_collection_choice_tl

\keys_define:nn {xsim/print-collection}
  {
    headings          .bool_set:N = \l__xsim_printcollection_headings_bool ,
    headings          .initial:n  = false ,
    headings-template .tl_set:N   = \l__xsim_printcollection_headings_template_tl ,
    headings-template .initial:n  = collection ,
    print             .tl_set:N   = \l__xsim_print_collection_choice_tl ,
    print             .initial:n  = exercises
  }

% #1: options
% #2: collection name
\cs_new_protected:Npn \xsim_print_collection:nn #1#2
  {
    \group_begin:
      \keys_set:nn {xsim/print-collection} {#1}
      \clist_clear:N \l__xsim_tmpa_clist
      \clist_clear:N \l__xsim_tmpb_clist
      \xsim_foreach_exercise_type_id:nn {}
        {
          \xsim_get_property_if_set:nnnT {##1} {##2} {collections}
            {
              \clist_if_in:NnT \l_xsim_property_value_tl {#2}
                {
                  \str_set:Nn \l__xsim_tmpa_str {##1}
                  \clist_put_right:NV \l__xsim_tmpa_clist \l__xsim_tmpa_str
                  \clist_put_right:Nn \l__xsim_tmpb_clist {##2}
                }
            }
        }
      \xsim_foreach_exercise_type:n
        {
          \bool_if:NT \l__xsim_printcollection_headings_bool
            {
              \clist_if_in:NnT \l__xsim_tmpa_clist {##1}
                {
                  \tl_set:Nn \ExerciseType {##1}
                  \xsim_use_template:nV
                    {heading}
                    \l__xsim_printcollection_headings_template_tl
                }
            }
          \clist_map_inline:Nn \l__xsim_tmpa_clist
            {
              \str_if_eq:nnT {##1} {####1}
                {
                  \clist_pop:NN \l__xsim_tmpa_clist \l__xsim_tmpa_tl
                  \clist_pop:NN \l__xsim_tmpb_clist \l__xsim_tmpb_tl
                  \str_case:Vn \l__xsim_print_collection_choice_tl
                    {
                      {exercises}
                        { \xsim_insert:nVnn {##1} \l__xsim_tmpb_tl {} {exercise} }
                      {solutions}
                        { \xsim_insert:nVnn {##1} \l__xsim_tmpb_tl {} {solution} }
                      {both} {
                        \xsim_insert:nVnn {##1} \l__xsim_tmpb_tl {} {exercise}
                        \xsim_insert:nVnn {##1} \l__xsim_tmpb_tl {} {solution}
                      }
                    }
                }
            }
        }
    \group_end:
  }

% ----------------------------------------------------------------------------
\xsim_new_collection:nn {all~ exercises} {}
\xsim_activate_collection:n {all~ exercises}

\XSIMmoduleend
\XSIMmodule{blanks}{add blanks, cloze}

\bool_new:N \l__xsim_blank_width_bool
\bool_new:N \l__xsim_blank_linespread_bool
\bool_new:N \l__xsim_fill_blank_bool
\tl_new:N   \l__xsim_blank_linespread_tl
\tl_new:N   \l__xsim_blank_scale_tl
\dim_new:N  \l__xsim_blank_dim
\dim_new:N  \l__xsim_blank_line_increment_dim
\dim_new:N  \l__xsim_blank_line_minimum_length_dim
\box_new:N  \l__xsim_blank_box

\cs_new_protected:Npn \xsim_write_cloze_blank:n #1 {#1}
\cs_new_protected:Npn \xsim_write_cloze_filled:n #1 {#1}

\keys_define:nn {xsim/blank}
  {
    blank-style         .code:n     =
      \cs_set_protected:Npn \xsim_write_cloze_blank:n ##1 {#1} ,
    blank-style         .initial:n  = \underline {#1} ,
    filled-style        .code:n     =
      \cs_set_protected:Npn \xsim_write_cloze_filled:n ##1 {#1} ,
    filled-style        .initial:n  = \underline {#1} ,
    style               .meta:n     =
      { blank-style = #1 , filled-style = #1 } ,
    fill                .bool_set:N = \l__xsim_fill_blank_bool ,
    fill                .initial:n  = false ,
    scale               .tl_set:N   = \l__xsim_blank_scale_tl ,
    scale               .initial:n  = 1 ,
    width               .code:n     =
      {
        \bool_set_true:N \l__xsim_blank_width_bool
        \dim_set:Nn \l__xsim_blank_dim {#1}
      } ,
    linespread          .code:n     =
      \bool_set_true:N \l__xsim_blank_linespread_bool
      \tl_set:Nn \l__xsim_blank_linespread_tl {#1} ,
    linespread          .initial:n  = 1 ,
    line-increment      .dim_set:N  = \l__xsim_blank_line_increment_dim ,
    line-increment      .initial:n  = 0.001\linewidth ,
    line-minimum-length .dim_set:N  = \l__xsim_blank_line_minimum_length_dim ,
    line-minimum-length .initial:n  = 2em
  }

\cs_new_protected:Npn \xsim_blank:n #1
  {
    \box_clear:N \l__xsim_blank_box
    \mode_if_math:TF
      { \hbox_set:Nn \l__xsim_blank_box { $ \m@th \mathpalette{}{#1} $ } }
      { \hbox_set:Nn \l__xsim_blank_box {#1} }
    \bool_lazy_or:nnTF
      { \xsim_if_inside_solution_p: }
      { \l__xsim_fill_blank_bool }
      { \xsim_write_cloze_filled:n {#1} }
      {
        \bool_if:NTF \l__xsim_blank_width_bool
          { \__xsim_blank_skip:V \l__xsim_blank_dim }
          { \__xsim_blank_skip:n { \box_wd:N \l__xsim_blank_box } }
      }
  }
  
\cs_new_protected:Npn \__xsim_blank_skip:n #1
  {
    \bool_if:NTF \l__xsim_blank_width_bool
      { \dim_set:Nn \l__xsim_tmpa_dim {#1} }
      {
        \fp_set:Nn \l__xsim_tmpa_fp
          { \dim_to_fp:n {#1} * \l__xsim_blank_scale_tl }
        \dim_set:Nn \l__xsim_tmpa_dim { \fp_to_dim:N \l__xsim_tmpa_fp }
      }
    \dim_compare:nTF
      { \l__xsim_tmpa_dim > \l__xsim_blank_line_minimum_length_dim }
      {
        \mode_if_math:TF
          { \xsim_write_cloze_blank:n { \skip_horizontal:N \l__xsim_tmpa_dim } }
          {
            \dim_do_while:nn { \l__xsim_tmpa_dim > \c_zero_dim }
              {
                % I wonder what the correct l3 way would be -- if there is
                % one, yet:
                % \tex_hfil:D
                \tex_penalty:D \hyphenpenalty
                % \tex_hfilneg:D
                \dim_compare:nTF
                  { \l__xsim_tmpa_dim < \l__xsim_blank_line_increment_dim }
                  { \xsim_write_cloze_blank:n { \skip_horizontal:N \l__xsim_tmpa_dim } }
                  {
                    \xsim_write_cloze_blank:n
                      { \skip_horizontal:N \l__xsim_blank_line_increment_dim }
                  }
                \dim_sub:Nn \l__xsim_tmpa_dim { \l__xsim_blank_line_increment_dim }
              }
          }
      }
      {
        \xsim_write_cloze_blank:n
          { \skip_horizontal:N \l__xsim_blank_line_minimum_length_dim }
      }
  }
\cs_generate_variant:Nn \__xsim_blank_skip:n {V}

\XSIMmoduleend
\XSIMmodule{grades}{distribute goal sums to grades}

\msg_new:nnn {xsim} {grade-unknown}
  { The~ grade~ `#1'~ has~ never~ been~ declared~ \msg_line_context: }

\bool_new:N \l__xsim_grades_half_bool
\fp_new:N   \l__xsim_grade_round_fp
\fp_zero:N  \l__xsim_grade_round_fp
\prop_new:N \l__xsim_relative_grades_prop
\tl_new:N   \l__xsim_grade_split_tl

\cs_new:Npn \__xsim_fp_round_to_half:n #1
  { round( 2*(#1),0)/2 }

\cs_new:Npn \__xsim_grades_round:n #1
  { round ( #1 , \l__xsim_grade_round_fp ) }

% #1: factor
% #2: goal sum
\cs_new:Npn \__xsim_grade_rounded:nn #1#2
  {
    \bool_if:NTF \l__xsim_grades_half_bool
      { \__xsim_fp_round_to_half:n }
      { \__xsim_grades_round:n }
    { (#1) * (#2) }
  }

\cs_new_protected:Npn \xsim_declare_relative_grades:n #1
  {
    \seq_set_split:NVn \l__xsim_tmpa_seq \l__xsim_grade_split_tl {#1}
    \seq_map_inline:Nn \l__xsim_tmpa_seq
      { \__xsim_declare_relative_grade:w ##1 \q_stop }
    \xsim_verbose:n {Declaring~ relative~ grade~ distribution}
  }

\cs_new_protected:Npn \__xsim_declare_relative_grade:w #1 = #2 \q_stop
  {
    \tl_set:Nx \l__xsim_tmpa_tl { \tl_trim_spaces:n {#1} }
    \tl_set:Nx \l__xsim_tmpb_tl { \tl_trim_spaces:n {#2} }
    \prop_put:NVV \l__xsim_relative_grades_prop
      \l__xsim_tmpa_tl
      \l__xsim_tmpb_tl
  }

% #1: grade
% #2: sum of points
% #3: code after grade requirement if == 1
% #4: code after grade requirement if != 1
\cs_new_protected:Npn \xsim_get_grade:nnnn #1#2#3#4
  {
    \prop_get:NnNTF \l__xsim_relative_grades_prop {#1} \l__xsim_tmpa_tl
      {
        \xsim_print_goal:nnn
          {
            min(
              \__xsim_grade_rounded:nn
                { \l__xsim_tmpa_tl }
                {#2} ,
              #2
            )
          }
          {#3}
          {#4}
      }
      { \msg_error:nnn {xsim} {grade-unknown} {#1} }
  }

% #1: grade
% #2: goal
% #3: code after grade requirement if == 1
% #4: code after grade requirement if != 1
\cs_new_protected:Npn \xsim_get_grade_goal:nnnn #1#2#3#4
  {
    \xsim_if_goal_exist:nTF {#2}
      {
        \xsim_get_grade:nnnn
          {#1}
          { \use:c {g__xsim_total_goal_#2_fp} }
          {#3}
          {#4}
      }
      { \msg_error:nnn {xsim} {goal-unknown} {#2} }
  }

% #1: grade
% #2: goal list (separated with +)
% #3: code after grade requirement if == 1
% #4: code after grade requirement if != 1
\cs_new_protected:Npn \xsim_get_grade_requirement:nnnn #1#2#3#4
  {
    \seq_set_split:Nnn \l__xsim_tmpa_seq {+} {#2}
    \fp_zero:N \l__xsim_tmpa_fp
    \seq_map_inline:Nn \l__xsim_tmpa_seq
      {
        \fp_add:Nn \l__xsim_tmpa_fp
          { \use:c {g__xsim_total_goal_\tl_trim_spaces:n {##1} _fp} }
      }
    \xsim_get_grade:nnnn {#1} { \l__xsim_tmpa_fp } {#3} {#4}
  }

% #1: grade
% #2: code after grade requirement if == 1
% #3: code after grade requirement if != 1
\cs_new_protected:Npn \xsim_get_absolute_grade_requirement:nnn #1#2#3
  {
    \fp_zero:N \l__xsim_tmpa_fp
    \xsim_foreach_goal:n
      {
        \fp_add:Nn \l__xsim_tmpa_fp
          { \use:c {g__xsim_total_goal_##1_fp} }
      }
    \xsim_get_grade:nnnn {#1} { \l__xsim_tmpa_fp } {#2} {#3}
  }

\keys_define:nn {xsim/grades}
  {
    round .fp_set:N   = \l__xsim_grade_round_fp ,
    half  .bool_set:N = \l__xsim_grades_half_bool ,
    split .tl_set:N   = \l__xsim_grade_split_tl ,
    split .initial:n  = {,}
  }

\XSIMmoduleend
\XSIMmodule{random}{randomly select exercises from collections}

\msg_new:nnn {xsim} {random-numbers-unavailable}
  {
    You~ are~ compiling~ your~ document~ using ~XeLaTeX.~ Please~ be~ aware~
    that~ random~ selection~ of~ exercises~ is~ unavailable~ in~ XeLaTeX.~ If~
    you~ use~ this~ feature~ you~ can~ safely~ ignore~ this~ message.
  }

\sys_if_engine_xetex:T
  { \msg_warning:nn {xsim} {random-numbers-unavailable} }

\tl_new:N    \l_xsim_random_collection_tl
\tl_new:N    \l__xsim_print_random_choice_tl
\seq_new:N   \g__xsim_random_list_seq
\seq_new:N   \l__xsim_allowed_exercise_ids_seq
\int_new:N   \g__xsim_random_list_id_int
\bool_new:N  \l__xsim_sort_random_bool
\clist_new:N \l_xsim_random_exclude_list_clist

% #1: random list id
% #2: csv list of integers
\xsim_new_aux_property:cpn {random} #1#2
  {
    \xsim_random_list_if_exist:nF {#1}
      { \xsim_new_random_list:n {#1} }
    \xsim_set_random_list:nn {#1} {#2}
  }

\cs_new_protected:Npn \xsim_new_random_list:n #1
  {
    \xsim_verbose:n {Defining~ new~ random~ list~ `#1'}
    \seq_new:c {g__xsim_random_#1 _seq}
  }

\cs_new_protected:Npn \xsim_set_random_list:nn #1#2
  { \seq_gset_from_clist:cn {g__xsim_random_#1_seq} {#2} }

\cs_new_protected:Npn \xsim_set_random_list_from_seq:nN #1#2
  { \seq_gset_eq:cN {g__xsim_random_#1_seq} #2 }

\cs_new:Npn \xsim_use_random_list:nn #1#2
  { \seq_use:cn {g__xsim_random_#1_seq} {#2} }

\cs_new_protected:Npn \xsim_map_random_list:nn #1#2
  { \seq_map_inline:cn {g__xsim_random_#1_seq} {#2} }

\prg_new_conditional:Npnn \xsim_random_list_if_exist:n #1 {p,T,F,TF}
  {
    \seq_if_exist:cTF {g__xsim_random_#1_seq}
      { \prg_return_true: }
      { \prg_return_false: }
  }

\prg_new_conditional:Npnn \xsim_random_list_if_empty:n #1 {p,T,F,TF}
  {
    \seq_if_empty:cTF {g__xsim_random_#1_seq}
      { \prg_return_true: }
      { \prg_return_false: }
  }

% #1: random list id
% #2: sequence variable to set list from
\cs_new_protected:Npn \xsim_save_random_list:nN #1#2
  { % TODO: check if #2 is empty
    \xsim_random_list_if_exist:nF {#1}
      { \xsim_new_random_list:n {#1} }
    \xsim_random_list_if_empty:nT {#1}
      { \xsim_set_random_list_from_seq:nN {#1} #2 }
    \xsim_verbose:n {Saving~ random~ list~ `#1'}
    \xsim_add_property_to_aux:nn {random}
      { {#1} { \xsim_use_random_list:nn {#1} {,} } }
  }
\cs_generate_variant:Nn \xsim_save_random_list:nN {x}

% #1: collection
% #2: number of items
% #3: clist variable of excluded ids
\cs_new_protected:Npn \xsim_print_random_list:nnN #1#2#3
  {
    \xsim_if_collection_exist:nTF {#1}
      {
        \int_gincr:N \g__xsim_random_list_id_int
        \int_compare:nNnTF { \xsim_collection_count:n {#1} } = {0}
          {
            % TODO: message
            \xsim_rerun:
          }
          {
            % generate seq of allowed ids from collection:
            \xsim_foreach_exercise_id:n
              {
                \xsim_get_type_for_id:nN {##1} \l__xsim_tmpa_str
                \tl_set_rescan:Nnx \l__xsim_tmpa_tl {} { \l__xsim_tmpa_str }
                \xsim_if_in_collection:nVnT {#1} \l__xsim_tmpa_tl {##1}
                  {
                    \clist_if_in:NnF #3 {##1}
                      { \seq_put_right:Nn \l__xsim_allowed_exercise_ids_seq {##1} }
                  }
              }
            \int_compare:nNnTF
              {#2}
               >
              { \seq_count:N \l__xsim_allowed_exercise_ids_seq }
              {
                \int_set:Nn \l__xsim_tmpa_int
                  { \seq_count:N \l__xsim_allowed_exercise_ids_seq }
              }
              { \int_set:Nn \l__xsim_tmpa_int {#2} }
            % generate random list:
            \xsim_generate_random_list:nnnN
              { \int_to_alph:n { \g__xsim_random_list_id_int } }
              { \seq_count:N \l__xsim_allowed_exercise_ids_seq }
              { \l__xsim_tmpa_int }
              \g__xsim_random_list_seq
          }
        \xsim_save_random_list:xN
          { \int_to_alph:n { \g__xsim_random_list_id_int } }
          \g__xsim_random_list_seq
        \xsim_verbose:x
          {
            Printing~ exercises~ of~ random~ list~
            `\int_to_alph:n { \g__xsim_random_list_id_int }'~ of~
            collection~ `#1'
          }
        \xsim_print_random_exercises:Nn
          \l__xsim_allowed_exercise_ids_seq
          { \int_to_alph:n { \g__xsim_random_list_id_int } }
      }
      { \msg_error:nnn {xsim} {unknown-collection} {#1} }
  }
\cs_generate_variant:Nn \xsim_print_random_list:nnN {V}

% #1: random list id
% #2: max number to choose from
% #3: number of items
% #4: seq variable
\cs_new_protected:Npn \xsim_generate_random_list:nnnN #1#2#3#4
  {
    \bool_lazy_or:nnT
      { !\xsim_random_list_if_exist_p:n {#1} }
      { \xsim_random_list_if_empty_p:n {#1} }
      {
        \xsim_verbose:n
          {Generating~ random~ list~ `#1'~ with~ #3~ out~ of~ #2~ items}
        \seq_gclear:N #4
        \int_compare:nNnF {#2} = 0
          {
            \int_do_until:nNnn { \seq_count:N #4 } = {#3}
              {
                \tl_set:Nx \l__xsim_tmpa_tl
                  { \fp_eval:n { randint(#2) } }
                \seq_if_in:NVF #4
                  \l__xsim_tmpa_tl
                  { \seq_gput_right:NV #4 \l__xsim_tmpa_tl }
              }
          }
        \bool_if:NT \l__xsim_sort_random_bool
          {
            \seq_gsort:Nn #4
              {
                \int_compare:nNnTF {##1} > {##2}
                  { \sort_return_swapped: }
                  { \sort_return_same: }
              }
          }
      }
  }

% #1: seq variable of ids
% #2: random list id
\cs_new_protected:Npn \xsim_print_random_exercises:Nn #1#2
  {
    \seq_if_empty:NF #1
      {
        \bool_lazy_and:nnT
          { \xsim_random_list_if_exist_p:n {#2} }
          { !\xsim_random_list_if_empty_p:n {#2} }
          {
            \xsim_verbose:x
              {
                Printing~
                \str_case:Vn \l__xsim_print_random_choice_tl
                  {
                    {exercises} {exercises~}
                    {solutions} {solutions~}
                    {both} { exercises~ and~ solutions~ }
                  }
                of~ random~ list~ `#2'.
              }
            \xsim_map_random_list:nn {#2}
              {
                \tl_set:Nx \l__xsim_tmpa_tl
                  { \seq_item:Nn #1 {##1} }
                \tl_set:Nx \l__xsim_tmpb_tl
                  { \xsim_get_type_for_property:nV {id} \l__xsim_tmpa_tl }
                \bool_set_false:N \l__xsim_tmpa_bool
                \bool_set_false:N \l__xsim_tmpb_bool
                \str_case:VnF \l__xsim_print_random_choice_tl
                  {
                    {exercises} { \bool_set_true:N \l__xsim_tmpa_bool }
                    {solutions} { \bool_set_true:N \l__xsim_tmpb_bool }
                    {both} {
                      \bool_set_true:N \l__xsim_tmpa_bool
                      \bool_set_true:N \l__xsim_tmpb_bool
                    }
                  }
                  {
                    \msg_warning:nnV {xsim}
                      {print-collection-choice}
                      \l__xsim_print_random_choice_tl
                  }
                \bool_if:NT \l__xsim_tmpa_bool
                  {
                    \xsim_if_exercise_exist:VVT
                      \l__xsim_tmpb_tl
                      \l__xsim_tmpa_tl
                      {
                        \xsim_insert:VVnn
                          \l__xsim_tmpb_tl
                          \l__xsim_tmpa_tl
                          {}
                          {exercise}
                      }
                  }
                \bool_if:NT \l__xsim_tmpb_bool
                  {
                    \xsim_if_solution_exist:VVT
                      \l__xsim_tmpb_tl
                      \l__xsim_tmpa_tl
                      {
                        \xsim_insert:VVnn
                          \l__xsim_tmpb_tl
                          \l__xsim_tmpa_tl
                          {}
                          {solution}
                      }
                  }
              }
          }
      }
  }

\keys_define:nn {xsim/random}
  {
    sort       .bool_set:N = \l__xsim_sort_random_bool ,
    sort       .initial:n  = true ,
    collection .tl_set:N   = \l_xsim_random_collection_tl ,
    collection .initial:n  = all~exercises ,
    exclude    .code:n     = \__xsim_exclude_ids:n {#1} ,
    print      .tl_set:N   = \l__xsim_print_random_choice_tl ,
    print      .initial:n  = exercises
  }

\cs_new_protected:Npn \__xsim_exclude_ids:n #1
  {
    \seq_clear:N \l__xsim_tmpa_seq
    \clist_map_inline:nn {#1}
      {
        \tl_set:Nx \l__xsim_tmpa_tl
          { \xsim_get_id_for_property:nn {ID} {##1} }
        \tl_if_blank:VTF \l__xsim_tmpa_tl
          { \seq_put_right:Nn \l__xsim_tmpa_seq {##1} }
          {
            \seq_put_right:Nx \l__xsim_tmpa_seq
              { \xsim_get_id_for_property:nn {ID} {##1} }
          }
      }
    \seq_remove_duplicates:N \l__xsim_tmpa_seq
    \clist_set_from_seq:NN
      \l_xsim_random_exclude_list_clist
      \l__xsim_tmpa_seq
  }

\XSIMmoduleend
\XSIMmodule{translations}{language settings for XSIM}

\RequirePackage{translations}

\msg_new:nnn {xsim} {language-not-defined}
  {
    You~ chose~ the~ language~ `#1'~ which~ is~ not~ defined~ by~ xsim.~
    `english'~ is~ used~ instead.~ If~ you~ just~ mistyped~ try~ again!~
    Otherwise~ contact~ the~ author~ and~ he'll~ probably~ add~ your~
    language.
  }

\msg_new:nnn {xsim} {empty-translation}
  {
    No~ translation~ provided~ for~ key~ `#1'~ in~ language~ `#2'~
    \msg_line_context: .
  }

% --------------------------------------------------------------------------

\bool_new:N      \l__xsim_language_auto_bool
\bool_set_true:N \l__xsim_language_auto_bool
% this token list will hold the chosen language for xsim; since the
% language is either chosen automatically or by option it is only available at
% begin document
\tl_new:N  \l_xsim_language_tl
\tl_set:Nn \l_xsim_language_tl {english}
\tl_new:N  \l__xsim_current_language_tl

\tl_const:Nn \c__xsim_keyword_prefix_tl {xsim-keyword-}

% ----------------------------------------------------------------------------
  
\prop_new:N \g_xsim_translations_prop

% translate the key #1
\cs_new:Npn \xsim_translate:n #1
  {
    \bool_if:NTF \l__xsim_language_auto_bool
      { \GetTranslation { \c__xsim_keyword_prefix_tl #1 } }
      {
        \GetTranslationFor
          { \l_xsim_language_tl }
          { \c__xsim_keyword_prefix_tl #1 }
      }
  }

\xsim_at_begin_document:n
  {
    \bool_if:NTF \l__xsim_language_auto_bool
      {
        \tl_set:Nx \l_xsim_language_tl
          { \@trnslt@language{\@trnslt@current@language} }
      }
      {
        \tl_set_eq:NN
          \l_xsim_language_tl
          \l__xsim_current_language_tl
      }
  }

% ----------------------------------------------------------------------------
% #1: language
% #2: keyword
% #3: translation
\cs_new_protected:Npn \xsim_declare_translation:nnn #1#2#3
  {
    \xsim_verbose:n
      { Declaring~ `#1'~ tranlation~ of~ `#2':~ `#3'. }
    \declaretranslation
      {#1}
      { \c__xsim_keyword_prefix_tl #2 }
      {#3}
    \prop_gput:Nnn \g_xsim_translations_prop {#2(#1)} {#3}
  }
\cs_generate_variant:Nn \xsim_declare_translation:nnn {V,VnV}

% #1: key
% #2: csv list: { <lang1> = <translation1> , <lang2> = <translation2> }
\cs_new_protected:Npn \xsim_declare_translations:nn #1#2
  {
    \cs_set_protected:Npn \__xsim_declare_translation:n ##1
      { \msg_warning:nnnn {xsim} {empty-translation} {#1} {##1} }
    \cs_set_protected:Npn \__xsim_declare_translation:nn ##1##2
      { \xsim_declare_translation:nnn {##1} {#1} {##2} }
    \keyval_parse:NNn
      \__xsim_declare_translation:n
      \__xsim_declare_translation:nn
      {#2}
  }

\cs_new:Npn \__xsim_parse_translate_list_entry:nnn #1#2#3 {}

\cs_new_protected:Npn \__xsim_parse_translate_list_entry:www #1(#2)\q_mark#3\q_stop
  { \__xsim_parse_translate_list_entry:nnn {#1} {#2} {#3} }

\cs_new_protected:Npn \xsim_for_all_translations_do:n #1
  {
    \cs_set:Npn \__xsim_parse_translate_list_entry:nnn ##1##2##3 {#1}
    \prop_map_inline:Nn \g_xsim_translations_prop
      { \__xsim_parse_translate_list_entry:www ##1 \q_mark ##2 \q_stop }
  }

% ----------------------------------------------------------------------------

\keys_define:nn {xsim}
  {
    language .value_required:n = true ,
    language .code:n =
      \tl_if_eq:nnTF {#1} {auto}
        { \bool_set_true:N \l__xsim_language_auto_bool }
        {
          \bool_set_false:N \l__xsim_language_auto_bool
          \tl_set:Nn \l__xsim_current_language_tl {#1}
        } ,
    language .initial:n = auto
  }

\XSIMmoduleend
\XSIMmodule{interface}{user interface}

\msg_new:nnn {xsim} {remove}
  {
    The~ command~ #2 is~ deprecated.~ As~ of~ version~ #1~ it~ serves~ no~
    purpose~ and~ does~ nothing.
  }

\msg_new:nnn {xsim} {deprecate}
  {
    The~ command~ #2 is~ deprecated.~ As~ of~ version~ #1~ it~ serves~ no~
    purpose~ and~ does~ nothing.~ Use~ #3 instead.
  }

\cs_new_protected:Npn \xsim_remove:nN #1#2
  { \msg_warning:nnnn {xsim} {remove} {#1} {#2} }

\cs_new_protected:Npn \xsim_deprecate:nNN #1#2#3
  { \msg_warning:nnnn {xsim} {remove} {#1} {#2} {#3} }

% ----------------------------------------------------------------------------
\NewDocumentCommand \DeclareExerciseEnvironmentTemplate {m+m+m}
  { \xsim_declare_environment_template:nnn {#1} {#2} {#3} }
\@onlypreamble \DeclareExerciseEnvironmentTemplate

\NewDocumentCommand \DeclareExerciseHeadingTemplate {m+m}
  { \xsim_declare_heading_template:nn {#1} {#2} }
\@onlypreamble \DeclareExerciseHeadingTemplate

\NewDocumentCommand \DeclareExerciseTableTemplate {m+m}
  { \xsim_declare_table_template:nn {#1} {#2} }
\@onlypreamble \DeclareExerciseTableTemplate

\NewDocumentCommand \UseExerciseTemplate {mm}
  { \xsim_use_template:nn {#1} {#2} }

% ----------------------------------------------------------------------------

\NewDocumentCommand \DeclareExerciseType {mm}
  { \xsim_declare_exercise_type:nn {#1} {#2} }
\@onlypreamble \DeclareExerciseType

\NewExpandableDocumentCommand \numberofusedexercises {}
  { \int_use:N \g_xsim_max_id_int }

% ----------------------------------------------------------------------------

\NewDocumentCommand \IfExistSolutionTF {+m+m}
  { \xsim_if_solution_exist:ooTF {\ExerciseType} {\ExerciseID} {#1} {#2} }

\NewDocumentCommand \IfExistSolutionT {+m}
  { \IfExistSolutionTF {#1} {} }

\NewDocumentCommand \IfExistSolutionF {+m}
  { \IfExistSolutionTF {} {#1} }

\NewExpandableDocumentCommand \IfInsideSolutionTF {+m+m}
  { \xsim_if_inside_solution:TF {#1} {#2} }

\NewExpandableDocumentCommand \IfInsideSolutionT {+m}
  { \IfInsideSolutionTF {#1} {} }

\NewExpandableDocumentCommand \IfInsideSolutionF {+m}
  { \IfInsideSolutionTF {} {#1} }

\NewExpandableDocumentCommand \IfSolutionPrintTF {+m+m}
  { \xsim_if_solution_print:nTF {\ExerciseType} {#1} {#2} }

\NewExpandableDocumentCommand \IfSolutionPrintT {+m}
  { \IfSolutionPrintTF {#1} {} }

\NewExpandableDocumentCommand \IfSolutionPrintF {+m}
  { \IfSolutionPrintTF {} {#1} }

% ----------------------------------------------------------------------------

\NewDocumentCommand \DeclareExerciseProperty {t!st-m}
  {
    \IfBooleanTF {#2}
      {
        \IfBooleanTF {#3}
          {
            \xsim_declare_property:nnnn
              { \c_false_bool }
              { \c_true_bool }
              { \c_true_bool }
              {#4}
          }
          {
            \xsim_declare_property:nnnn
              { \c_false_bool }
              { \c_true_bool }
              { \c_false_bool }
              {#4}
          }
      }
      {
        \IfBooleanTF {#1}
          {
            \IfBooleanTF {#3}
              {
                \xsim_declare_property:nnnn
                  { \c_true_bool }
                  { \c_false_bool }
                  { \c_true_bool }
                  {#4}
              }
              {
                \xsim_declare_property:nnnn
                  { \c_true_bool }
                  { \c_false_bool }
                  { \c_false_bool }
                  {#4}
              }
          }
          {
            \IfBooleanTF {#3}
              {
                \xsim_declare_property:nnnn
                  { \c_false_bool }
                  { \c_false_bool }
                  { \c_true_bool }
                  {#4}
              }
              {
                \xsim_declare_property:nnnn
                  { \c_false_bool }
                  { \c_false_bool }
                  { \c_false_bool }
                  {#4}
              }
          }
      }
  }
\@onlypreamble \DeclareExerciseProperty

\NewDocumentCommand \DeclareExercisePropertyAlias {mm}
  { \xsim_make_property_alias:nn {#1} {#2} }
\@onlypreamble \DeclareExercisePropertyAlias

\NewDocumentCommand \SetExerciseProperty {m+m}
  { \xsim_set_property:eenn {\ExerciseType} {\ExerciseID} {#1} {#2} }

\NewDocumentCommand \SetExpandedExerciseProperty {m+m}
  { \xsim_set_property:eene {\ExerciseType} {\ExerciseID} {#1} {#2} }

\NewDocumentCommand \ExerciseSetProperty {mmm+m}
  { \xsim_set_property:nnnn {#1} {#2} {#3} {#4} }

\NewDocumentCommand \ExerciseSetExpandedProperty {mmm+m}
  { \xsim_set_property:nnne {#1} {#2} {#3} {#4} }

\NewExpandableDocumentCommand \IfExercisePropertyExistTF {+m+m+m}
  { \xsim_if_property_exist:nTF {#1} {#2} {#3} }

\NewExpandableDocumentCommand \IfExercisePropertyExistT {+m+m}
  { \IfExercisePropertyExistTF {#1} {#2} {} }

\NewExpandableDocumentCommand \IfExercisePropertyExistF {+m+m}
  { \IfExercisePropertyExistTF {#1} {} {#2} }

\NewExpandableDocumentCommand \IfExercisePropertySetTF {m+m+m}
  { \xsim_if_property_set:eenTF {\ExerciseType} {\ExerciseID} {#1} {#2} {#3} }

\NewExpandableDocumentCommand \IfExercisePropertySetT {m+m}
  { \IfExercisePropertySetTF {#1} {#2} {} }

\NewExpandableDocumentCommand \IfExercisePropertySetF {m+m}
  { \IfExercisePropertySetTF {#1} {} {#2} }

\NewExpandableDocumentCommand \GetExerciseProperty {m}
  { \xsim_get_property:oon {\ExerciseType} {\ExerciseID} {#1} }

\NewExpandableDocumentCommand \GetExerciseAliasProperty {m}
  {
    \xsim_get_property:oof
      {\ExerciseType}
      {\ExerciseID}
      { \xsim_property_alias:n {#1} }
  }

\NewDocumentCommand \GetExercisePropertyTF {m+m+m}
  {
    \xsim_get_property_if_set:oonTF {\ExerciseType} {\ExerciseID} {#1}
      {#2}
      {#3}
  }

\NewDocumentCommand \GetExercisePropertyT {m+m}
  { \GetExercisePropertyTF {#1} {#2} {} }
\NewDocumentCommand \GetExercisePropertyF {m+m+m}
  { \GetExercisePropertyTF {#1} {} {#2} }

\NewExpandableDocumentCommand \GetExerciseIdForProperty {mm}
  { \xsim_get_id_for_property:nn {#1} {#2} }

\NewExpandableDocumentCommand \GetExerciseTypeForProperty {mm}
  { \xsim_get_type_for_property:nn {#1} {#2} }

\NewExpandableDocumentCommand \IfExerciseBooleanPropertyTF {+m+m+m}
  {
    \xsim_if_boolean_property:oonTF {\ExerciseType} {\ExerciseID} {#1}
      {#2}
      {#3}
  }

\NewExpandableDocumentCommand \IfExerciseBooleanPropertyT {+m+m}
  { \IfExerciseBooleanPropertyTF {#1} {#2} {} }
\NewExpandableDocumentCommand \IfExerciseBooleanPropertyF {+m+m}
  { \IfExerciseBooleanPropertyTF {#1} {} {#2} }

\NewDocumentCommand \SaveExerciseProperty {mm}
  { \xsim_save_property:xxnN {\ExerciseType} {\ExerciseID} {#1} #2 }

\NewDocumentCommand \GlobalSaveExerciseProperty {mm}
  { \xsim_gsave_property:xxnN {\ExerciseType} {\ExerciseID} {#1} #2 }

% ----------------------------------------------------------------------------

\NewDocumentCommand \DeclareExerciseParameter {st!m}
   {
     \IfBooleanTF {#1} % fixed
       {
         \IfBooleanTF {#2} % mandatory
           { \xsim_declare_parameter:nnn {true} {true} {#3} }
           { \xsim_declare_parameter:nnn {true} {false} {#3} }
       }
       {
         \IfBooleanTF {#2} % mandatory
           { \xsim_declare_parameter:nnn {false} {true} {#3} }
           { \xsim_declare_parameter:nnn {false} {false} {#3} }
       }
   }

\NewExpandableDocumentCommand \GetExerciseParameter {m}
  { \xsim_get_parameter:on {\ExerciseType} {#1} }

\NewDocumentCommand \SetExerciseParameter {mmm}
  { \xsim_set_parameter:nnn {#1} {#2} {#3} }

\NewDocumentCommand \SetExerciseParameters {mm}
  { \xsim_set_parameters:nn {#1} {#2} }

\NewExpandableDocumentCommand \GetExerciseName {}
  {
    \IfInsideSolutionTF
      { \GetExerciseParameter {solution-name} }
      { \GetExerciseParameter {exercise-name} }
  }

\NewExpandableDocumentCommand \GetExerciseHeadingF {m}
  {
    \IfInsideSolutionTF
      {
        \IfExerciseParameterSetTF {solution-heading}
          { \GetExerciseParameter {solution-heading} }
          {#1}
      }
      {
        \IfExerciseParameterSetTF {exercise-heading}
          { \GetExerciseParameter {exercise-heading} }
          {#1}
      }
  }

\NewDocumentCommand \GetExerciseBody {m}
  { \xsim_input:oon { \ExerciseType } { \ExerciseID } {#1} }
  
\NewExpandableDocumentCommand \IfExerciseParameterSetTF {+m+m+m}
  { \xsim_if_parameter_set:onTF {\ExerciseType} {#1} {#2} {#3} }

\NewExpandableDocumentCommand \IfExerciseParameterSetT {+m+m}
  { \xsim_if_parameter_set:onT {\ExerciseType} {#1} {#2} }

\NewExpandableDocumentCommand \IfExerciseParameterSetF {+m+m}
  { \xsim_if_parameter_set:onF {\ExerciseType} {#1} {#2} }

\NewExpandableDocumentCommand \ExerciseParameterIfSetTF {+m+m+m+m}
  { \xsim_if_parameter_set:nnTF {#1} {#2} {#3} {#4} }

\NewExpandableDocumentCommand \ExerciseParameterIfSetT {+m+m+m}
  { \xsim_if_parameter_set:nnT {#1} {#2} {#3} }

\NewExpandableDocumentCommand \ExerciseParameterIfSetF {+m+m+m}
  { \xsim_if_parameter_set:nnF {#1} {#2} {#3} }

\NewDocumentCommand \GetExerciseParameterTF {m+m+m}
  { \xsim_get_parameter_if_set:onTF {\ExerciseType} {#1} {#2} {#3} }

\NewDocumentCommand \GetExerciseParameterT {m+m}
  { \GetExerciseParameterTF {#1} {#2} {} }

\NewDocumentCommand \GetExerciseParameterF {m+m+m}
  { \GetExerciseParameterTF {#1} {} {#2} }

% ----------------------------------------------------------------------------

\NewDocumentCommand \ExercisePropertyIfSetTF {mmm+m+m}
  { \xsim_if_property_set:nnnTF {#1} {#2} {#3} {#4} {#5} }

\NewDocumentCommand \ExercisePropertyIfSetT {mmm+m}
  { \ExercisePropertyIfSetTF {#1} {#2} {#3} {#4} {} }

\NewDocumentCommand \ExercisePropertyIfSetF {mmm+m}
  { \ExercisePropertyIfSetTF {#1} {#2} {#3} {} {#4} }

\NewExpandableDocumentCommand \ExercisePropertyGet {mmm}
  { \xsim_get_property:nnn {#1} {#2} {#3} }

\NewExpandableDocumentCommand \ExercisePropertyGetAlias {mm}
  { \xsim_get_property:nnf {#1} {#2} { \xsim_property_alias:n {#1} } }

\NewDocumentCommand \ExercisePropertySave {mmmm}
  { \xsim_save_property:nnnN {#1} {#2} {#3} #4 }

\NewDocumentCommand \ExercisePropertyGlobalSave {mmmm}
  { \xsim_gsave_property:nnnN {#1} {#2} {#3} #4 }

\NewExpandableDocumentCommand \ExerciseParameterGet {mm}
  { \xsim_get_parameter:nn {#1} {#2} }

% ----------------------------------------------------------------------------

\NewExpandableDocumentCommand \XSIMtranslate {m}
  { \xsim_translate:n {#1} }

\NewExpandableDocumentCommand \XSIMexpandcode {+m}
  { \use:e {#1} }

\NewExpandableDocumentCommand \XSIMmixedcase {m}
  { \text_titlecase:e {#1} }

\NewDocumentCommand \XSIMputright {mm}
  { \tl_put_right:Nn #1 {#2} }

\NewExpandableDocumentCommand \XSIMifeqTF {+m+m+m+m}
  { \tl_if_eq:eeTF {#1} {#2} {#3} {#4} }

\NewExpandableDocumentCommand \XSIMifeqT {+m+m+m}
  { \XSIMifeqTF {#1} {#2} {#3} {} }

\NewExpandableDocumentCommand \XSIMifeqF {+m+m+m}
  { \XSIMifeqTF {#1} {#2} {} {#3} }

\NewExpandableDocumentCommand \XSIMifblankTF {+m+m+m}
  { \tl_if_blank:eTF {#1} {#2} {#3} }

\NewExpandableDocumentCommand \XSIMifblankT {+m+m}
  { \XSIMifblankTF {#1} {#2} {} }

\NewExpandableDocumentCommand \XSIMifblankF {+m+m}
  { \XSIMifblankTF {#1} {} {#2} }

% ----------------------------------------------------------------------------

\NewDocumentCommand \DeclareExerciseGoal {m}
  { \xsim_declare_exercise_goal:n {#1} }
\@onlypreamble \DeclareExerciseGoal

\NewDocumentCommand \IfExerciseGoalTF {mm+m+m}
  {
    \xsim_if_goal_value:xxnnTF
      {\ExerciseType} {\ExerciseID} {#1}
      {#2}
      {#3} {#4}
  }

\NewDocumentCommand \IfExerciseGoalT {mm+m}
  { \IfExerciseGoalTF {#1} {#2} {#3} {} }

\NewDocumentCommand \IfExerciseGoalF {mm+m}
  { \IfExerciseGoalTF {#1} {#2} {} {#3} }

\NewDocumentCommand \IfExerciseGoalSingularTF {mmm}
  { \IfExerciseGoalTF {#1} {=1} {#2} {#3} }

\NewDocumentCommand \IfExerciseGoalSingularT {mm}
  { \IfExerciseGoalT {#1} {=1} {#2} }

\NewDocumentCommand \IfExerciseGoalSingularF {mm}
  { \IfExerciseGoalF {#1} {=1} {#2} }

\NewDocumentCommand \IfExerciseTypeGoalsSumTF {mmm+m+m}
  { \xsim_if_goals_sum:nnnTF {#1} {#2} {#3} {#4} {#5} }

\NewDocumentCommand \IfExerciseTypeGoalsSumT {mmm+m}
  { \IfExerciseTypeGoalsSumTF {#1} {#2} {#3} {#4} {} }

\NewDocumentCommand \IfExerciseTypeGoalsSumF {mmm+m}
  { \IfExerciseTypeGoalsSumTF {#1} {#2} {#3} {} {#4} }

\NewDocumentCommand \IfExerciseGoalsSumTF {mm+m+m}
  { \xsim_if_total_goals_sum:nnTF {#1} {#2} {#3} {#4} }

\NewDocumentCommand \IfExerciseGoalsSumT {mm+m}
  { \IfExerciseGoalsSumTF {#1} {#2} {#3} {} }

\NewDocumentCommand \IfExerciseGoalsSumF {mm+m}
  { \IfExerciseGoalsSumTF {#1} {#2} {} {#3} }

\NewDocumentCommand \TotalExerciseTypeGoal {mmmm}
  { \xsim_print_goal_sum:nnnn {#1} {#2} {#3} {#4} }

\NewDocumentCommand \TotalExerciseTypeGoals {mmmm}
  { \xsim_print_goals_sum:nnnn {#1} {#2} {#3} {#4} }

\NewDocumentCommand \TotalExerciseGoal {mmm}
  { \xsim_print_total_goal_sum:nnn {#1} {#2} {#3} }

\NewDocumentCommand \TotalExerciseGoals {mmm}
  { \xsim_print_total_goals_sum:nnn {#1} {#2} {#3} }

\NewDocumentCommand \AddtoExerciseTypeGoal {mmmm}
  { \xsim_addto_goal:nnnn {#1} {#2} {#3} {#4} }

\NewDocumentCommand \AddtoExerciseGoal {mm}
  { \xsim_addto_goal:xxnn {\ExerciseType} {\ExerciseID} {#1} {#2} }

\NewDocumentCommand \ExerciseGoalValuePrint {mmm}
  { \xsim_print_goal:nnn {#1} {#2} {#3} }

\NewDocumentCommand \AddtoExerciseTypeGoalPrint {mmmmmm}
  {
    \xsim_addto_goal:nnnn {#1} {#2} {#3} {#4}
    \xsim_print_goal:nnn {#4} {#5} {#6}
  }

\NewDocumentCommand \AddtoExerciseGoalPrint {mmmm}
  {
    \xsim_addto_goal:xxnn {\ExerciseType} {\ExerciseID} {#1} {#2}
    \xsim_print_goal:nnn {#2} {#3} {#4}
  }

% ----------------------------------------------------------------------------
\NewDocumentCommand \DeclareGradeDistribution {m}
  { \xsim_declare_relative_grades:n {#1} }

\NewDocumentCommand \GetGradeRequirementForGoal {mmmm}
  { \xsim_get_grade_goal:nnnn {#1} {#2} {#3} {#4} }

\NewDocumentCommand \GetGradeRequirementForGoals {mmmm}
  { \xsim_get_grade_requirement:nnnn {#1} {#2} {#3} {#4} }

\NewDocumentCommand \GetGradeRequirement {mmm}
  { \xsim_get_absolute_grade_requirement:nnn {#1} {#2} {#3} }

% ----------------------------------------------------------------------------

\NewDocumentCommand \DeclareExerciseTagging {m}
  { \xsim_new_tag_type:n {#1} }
\@onlypreamble \DeclareExerciseTagging

\NewDocumentCommand \ProvideExerciseTagging {m}
  {
    \xsim_if_tag_type_exist:nTF {#1}
      { \msg_warning:nnn {xsim} {tag-exists} {#1} }
      { \xsim_new_tag_type:n {#1} }
  }

\NewDocumentCommand \ForEachExerciseTag {m+m}
  { \xsim_foreach_exercise_tag:oonn {\ExerciseType} {\ExerciseID} {#1} {#2} }

\NewDocumentCommand \ListExerciseTags {mm}
  { \xsim_exercise_tags_use:oonn {\ExerciseType} {\ExerciseID} {#1} {#2} }

\NewDocumentCommand \UseExerciseTags {mmmm}
  {
    \xsim_exercise_tags_use:oonnnn
      {\ExerciseType}
      {\ExerciseID}
      {#1}
      {#2}
      {#3}
      {#4}
  }

\NewDocumentCommand \IfExerciseTagSetTF {m+m+m}
  { \xsim_if_tags_value:nTF {#1} {#2} {#3} }
\NewDocumentCommand \IfExerciseTagSetT {m+m}
  { \xsim_if_tags_value:nT {#1} {#2} }
\NewDocumentCommand \IfExerciseTagSetF {m+m}
  { \xsim_if_tags_value:nF {#1} {#2} }

\NewDocumentCommand \IfExerciseTopicSetTF {m+m+m}
  { \xsim_if_topic_value:nTF {#1} {#2} {#3} }
\NewDocumentCommand \IfExerciseTopicSetT {m+m}
  { \xsim_if_topic_value:nT {#1} {#2} }
\NewDocumentCommand \IfExerciseTopicSetF {m+m}
  { \xsim_if_topic_value:nF {#1} {#2} }

% ----------------------------------------------------------------------------

\NewDocumentCommand \XSIMatbegindocument {+m}
  { \xsim_at_begin_document:n {#1} }

\NewDocumentCommand \XSIMatenddocument {+m}
  { \xsim_at_end_document:n {#1} }

% ----------------------------------------------------------------------------

\NewDocumentCommand \ForEachPrintedExerciseByType {+m}
  { \xsim_foreach_exercise_type_id:nn {printed} {#1} }

\tl_new:N \XSIMtmp

\NewDocumentCommand \ForEachPrintedExerciseByID {+m}
  {
    \tl_set_eq:NN \XSIMtmp \ExerciseType
    \xsim_foreach_exercise_id_type:nn {print} {#1}
    \tl_set_eq:NN \ExerciseType \XSIMtmp
    \tl_clear:N \XSIMtmp
  }

\NewDocumentCommand \ForEachUsedExerciseByType {+m}
  {
    \tl_set_eq:NN \XSIMtmp \ExerciseType
    \xsim_foreach_exercise_type_id:nn {used} {#1}
    \tl_set_eq:NN \ExerciseType \XSIMtmp
    \tl_clear:N \XSIMtmp
  }

\NewDocumentCommand \ForEachUsedExerciseByID {+m}
  {
    \tl_set_eq:NN \XSIMtmp \ExerciseType
    \xsim_foreach_exercise_id_type:nn {used} {#1}
    \tl_set_eq:NN \ExerciseType \XSIMtmp
    \tl_clear:N \XSIMtmp
  }

\NewDocumentCommand \ForEachUsedExerciseByOrder {+m}
  {
    \tl_set_eq:NN \XSIMtmp \ExerciseType
    \xsim_foreach_exercise_type_order:nn {used} {#1}
    \tl_set_eq:NN \ExerciseType \XSIMtmp
    \tl_clear:N \XSIMtmp
  }

% ----------------------------------------------------------------------------

\NewDocumentCommand \DeclareExerciseTranslations {mm}
  { \xsim_declare_translations:nn {#1} {#2} }
\@onlypreamble \DeclareExerciseTranslations
  
\NewDocumentCommand \DeclareExerciseTranslation {mmm}
  { \xsim_declare_translation:nnn {#1} {#2} {#3} }
\@onlypreamble \DeclareExerciseTranslation

\NewDocumentCommand \ForEachExerciseTranslation {+m}
  { \xsim_for_all_translations_do:n {#1} }

% ----------------------------------------------------------------------------

\NewDocumentCommand \DeclareExerciseCollection {om}
  {
    \IfNoValueTF {#1}
      { \xsim_new_collection:nn {#2} {} }
      {
        \xsim_new_collection:nn {#2} {#1}
        \xsim_activate_collection:n {#2}
      }
  }
\@onlypreamble \DeclareExerciseCollection

\NewDocumentCommand \activatecollection {m}
  { \xsim_activate_collection:n {#1} }

\NewDocumentCommand \deactivatecollection {m}
  { \xsim_deactivate_collection:n {#1} }

% \NewDocumentCommand \collectexercisestype {mm}
%   { \xsim_collect_type_start:nn {#1} {#2} }

\NewDocumentCommand \collectexercises {m}
  { \xsim_collect_start:n {#1} }

\NewDocumentCommand \collectexercisesstop {m}
  { \xsim_collect_stop:n {#1} }

\NewDocumentCommand \printcollection {O{}m}
  { \xsim_print_collection:nn {#1} {#2} }

\NewDocumentCommand \printrandomexercises {O{}m}
  {
    \group_begin:
      \keys_set:nn {xsim/random} {#1}
      \xsim_print_random_list:VnN
        \l_xsim_random_collection_tl
        {#2}
        \l_xsim_random_exclude_list_clist
    \group_end:
  }

% ----------------------------------------------------------------------------

\NewDocumentCommand \blank {som}
  {
    \group_begin:
      \IfNoValueF {#2} { \keys_set:nn {xsim/blank} {#2} }
      \mode_if_vertical:TF
        {
          \IfBooleanTF {#1}
            { \xsim_blank:n {#3} }
            {
              \bool_if:NT \l__xsim_blank_linespread_bool
                { \linespread { \l__xsim_blank_linespread_tl } \selectfont }
              \noindent \null \xsim_blank:n {#3} \par
            }
        }
        { \xsim_blank:n {#3} }
    \group_end:
  }

\NewDocumentCommand \examspace { sm }
  {
    \IfBooleanTF {#1}
      { \xsim_examspace:nn { \c_true_bool } {#2} }
      { \xsim_examspace:nn { \c_false_bool } {#2} }
  }

% ----------------------------------------------------------------------------

\NewDocumentCommand \gradingtable {O{}}
  {
    \group_begin:
      \keys_set:nn {xsim/grading-table} {#1}
      \xsim_setup_template:nVn {table} \l_xism_grading_table_template_tl
        {
          \providecommand* \ExerciseTableCode {}
          \providecommand* \ExerciseTableType [1] {}
          \providecommand* \ExerciseType {}
          \tl_set_eq:NN \ExerciseType \l_xism_grading_table_exercise_type_tl
          \renewcommand* \ExerciseTableCode {}
          \renewcommand* \ExerciseTableType [1]
            {
              \XSIMifblankTF { \l_xism_grading_table_exercise_type_tl }
                {##1}
                { \l_xism_grading_table_exercise_type_tl }
            }
        }
      \xsim_use_template:nV {table} \l_xism_grading_table_template_tl
    \group_end:
  }

% ----------------------------------------------------------------------------

\NewDocumentCommand \XSIMprint {mO{}mm}
  { \xsim_insert:nenn {#3} { \xsim_normalize_id:n {#4} } {#2} {#1} }

\NewDocumentCommand \XSIMxprint {mO{}mm}
  { \xsim_insert:xxnn {#3} { \xsim_normalize_id:e {#4} } {#2} {#1} }

\NewDocumentCommand \printexercise {O{}mm}
  {
    \clist_map_inline:nn {#3}
      { \xsim_insert:nenn {#2} { \xsim_normalize_id:n {##1} } {#1} {exercise} }
  }

\NewDocumentCommand \xprintexercise {O{}mm}
  {
    \clist_map_inline:nn {#3}
      { \xsim_insert:xxnn {#2} { \xsim_normalize_id:e {##1} } {#1} {exercise} }
  }

\NewDocumentCommand \printsolution {O{}mm}
  {
    \clist_map_inline:nn {#3}
      { \xsim_insert:nenn {#2} { \xsim_normalize_id:n {##1} } {#1} {solution} }
  }
  
\NewDocumentCommand \xprintsolution {O{}mm}
  {
    \clist_map_inline:nn {#3}
      { \xsim_insert:xxnn {#2} { \xsim_normalize_id:e {##1} } {#1} {solution} }
  }
  
% ----------------------------------------------------------------------------
  
\NewDocumentCommand \printsolutionstype {sO{}m}
  {
    \IfBooleanTF {#1}
      { \xsim_print_type_solutions:nnn { \c_true_bool } {#2} {#3} }
      { \xsim_print_type_solutions:nnn { \c_false_bool } {#2} {#3} }
  }

\NewDocumentCommand \printsolutions {sO{}}
  {
    \IfBooleanTF {#1}
      { \xsim_print_all_solutions_per_type:nn { \c_true_bool } {#2} }
      { \xsim_print_all_solutions_per_type:nn { \c_false_bool } {#2} }
  }

\NewDocumentCommand \printallsolutions {sO{}}
  {
    \IfBooleanTF {#1}
      { \xsim_print_all_solutions_per_id:nn { \c_true_bool } {#2} }
      { \xsim_print_all_solutions_per_id:nn { \c_false_bool } {#2} }
  }

% ----------------------------------------------------------------------------

\NewDocumentCommand \printforexercises {sO{}+mO{}}
  {
    \IfBooleanTF {#1}
      {
        \xsim_foreach_exercise_type:n
          { \xsim_print_type_code:nnnnn { \c_true_bool } {#2} {#4} {##1} {#3} }
      }
      {
        \xsim_foreach_exercise_type:n
          { \xsim_print_type_code:nnnnn { \c_false_bool } {#2} {#4} {##1} {#3} }
      }
  }

% ----------------------------------------------------------------------------

\NewDocumentCommand \xsimsetup {+m}
  { \xsim_setup:n {#1} }

% ----------------------------------------------------------------------------
\NewExpandableDocumentCommand \XSIMifchapterTF {+m+m}
  { \xsim_if_chapter:TF {#1} {#2} }

\NewExpandableDocumentCommand \XSIMifchapterT {+m}
  { \xsim_if_chapter:T {#1} }

\NewExpandableDocumentCommand \XSIMifchapterF {+m}
  { \xsim_if_chapter:F {#1} }

\XSIMmoduleend
\XSIMmodule{definitions}{definition of user commands}

% not exactly a user choice: the `exercises' environments rely on them:
\DeclareExerciseParameter*!{exercise-env}
\DeclareExerciseParameter*!{solution-env}
\DeclareExerciseParameter !{exercise-name}
\DeclareExerciseParameter !{solution-name}
\DeclareExerciseParameter  {exercises-name}
\DeclareExerciseParameter  {solutions-name}
\DeclareExerciseParameter !{exercise-template}
\DeclareExerciseParameter !{solution-template}
\DeclareExerciseParameter* {counter}
\DeclareExerciseParameter* {solution-counter}
\DeclareExerciseParameter* {number}
\DeclareExerciseParameter {within}
\DeclareExerciseParameter {the-counter}

% these are additional - but are used in the `default' environment
% template:
\DeclareExerciseParameter  {exercise-heading}
\DeclareExerciseParameter  {solution-heading}

% ----------------------------------------------------------------------------

% not exactly a user choice, must be present at various places:
\DeclareExerciseProperty!  {id}
\DeclareExerciseProperty!  {ID}
\DeclareExerciseProperty   {counter}
\DeclareExerciseProperty   {counter-value}
\DeclareExerciseProperty * {solution}
\XSIMifchapterT{
  \DeclareExerciseProperty {chapter-value}
  \DeclareExerciseProperty {chapter}
}
\DeclareExerciseProperty  {section-value}
\DeclareExerciseProperty  {section}
\DeclareExerciseProperty  {sectioning}

% those are additional and wouldn't *need* to be present except for
% pre-defined templates which make use of them (the page and page-value
% property actually needs to be defined for xsim to be able to save the
% corresponding values):
\DeclareExerciseProperty  {subtitle}
\DeclareExerciseProperty  {points}
\DeclareExerciseProperty  {bonus-points}
\DeclareExerciseProperty  {page-value}
\DeclareExerciseProperty  {page}

\DeclareExercisePropertyAlias {ID} {id}

% ----------------------------------------------------------------------------

\DeclareExerciseTagging {tags}
\DeclareExerciseTagging {topics}

\xsimsetup{tags/ignore-untagged=false}

% ----------------------------------------------------------------------------

\DeclareExerciseGoal {points}
\DeclareExerciseGoal {bonus-points}

\NewDocumentCommand \printgoal {m}
  { \ExerciseGoalValuePrint {#1} {} {} }

\NewDocumentCommand \points {m}
  {
    \ExerciseGoalValuePrint {#1}
      { \, \XSIMtranslate {point} }
      { \, \XSIMtranslate {points} }
  }

\NewDocumentCommand \addpoints {sm}
  {
    \AddtoExerciseGoal {points} {#2}
    \IfBooleanF {#1} { \points {#2} }
  }

\NewDocumentCommand \printpoints {m}
  {
    \TotalExerciseTypeGoal {#1} {points}
      { \, \XSIMtranslate {point} }
      { \, \XSIMtranslate {points} }
  }

\NewDocumentCommand \printtotalpoints {}
  {
    \TotalExerciseGoal {points}
      { \, \XSIMtranslate {point} }
      { \, \XSIMtranslate {points} }
  }

\NewDocumentCommand \addbonus {sm}
  {
    \AddtoExerciseGoal {bonus-points} {#2}
    \IfBooleanF {#1} { \points {#2} }
  }

\NewDocumentCommand \printbonus {m}
  {
    \TotalExerciseTypeGoal {#1} {bonus-points}
      { \, \XSIMtranslate {point} }
      { \, \XSIMtranslate {points} }
  }

\NewDocumentCommand \printtotalbonus {}
  {
    \TotalExerciseGoal {bonus-points}
      { \, \XSIMtranslate {point} }
      { \, \XSIMtranslate {points} }
  }

% ----------------------------------------------------------------------------
\NewDocumentCommand \pointsforgrade {m}
  {
    \GetGradeRequirementForGoal {#1} {points}
      { \, \XSIMtranslate {point} }
      { \, \XSIMtranslate {points} }
  }

\NewDocumentCommand \goalsforgrade {mm}
  {
    \GetGradeRequirementForGoals {#2} {#1}
      { \, \XSIMtranslate {point} }
      { \, \XSIMtranslate {points} }
  }

\NewDocumentCommand \totalgoalforgrade {m}
  {
    \GetGradeRequirement {#1}
      { \, \XSIMtranslate {point} }
      { \, \XSIMtranslate {points} }
  }

% ----------------------------------------------------------------------------

\xsim_if_package_blank:F
  {
    \DeclareExerciseType {exercise} {
      exercise-env      = exercise ,
      solution-env      = solution ,
      exercise-name     = \XSIMtranslate {exercise} ,
      exercises-name    = \XSIMtranslate {exercises} ,
      solution-name     = \XSIMtranslate {solution} ,
      solutions-name    = \XSIMtranslate {solutions} ,
      exercise-template = default ,
      solution-template = default ,
      exercise-heading  = \subsection* ,
      solution-heading  = \subsection*
    }
  }

% ----------------------------------------------------------------------------

\DeclareExerciseEnvironmentTemplate {default}
  {
    \GetExerciseHeadingF { \subsection* }
    {
      \XSIMmixedcase { \GetExerciseName } \nobreakspace
      \GetExerciseProperty {counter}
      \IfInsideSolutionF
        {
          \IfExercisePropertySetT {subtitle}
            { ~ { \normalfont \itshape \GetExerciseProperty {subtitle} } }
        }
    }
    \GetExercisePropertyT {points}
      {
        \marginpar
          {
            \IfInsideSolutionF { \rule {1.2cm} {1pt} \slash }
            \printgoal {\PropertyValue}
            \GetExercisePropertyT {bonus-points}
              { \nobreakspace ( + \printgoal {\PropertyValue} ) }
            \nobreakspace\XSIMtranslate {point-abbr}
          }
      }
  }
  { \par }

% ----------------------------------------------------------------------------

\DeclareExerciseHeadingTemplate {none} {}

\DeclareExerciseHeadingTemplate {default}
  { \section* { \XSIMtranslate {default-heading} } }

\DeclareExerciseHeadingTemplate {collection}
  { \section* { \XSIMtranslate {collection-heading} } }

\DeclareExerciseHeadingTemplate {per-section}
  { \section* { \XSIMtranslate {per-section-heading} } }
  
\DeclareExerciseHeadingTemplate {per-chapter}
  { \section* { \XSIMtranslate {per-chapter-heading} } }

% ----------------------------------------------------------------------------

\RequirePackage{array,booktabs}

\DeclareExerciseTableTemplate {default*}
  {
    \XSIMputright \ExerciseTableCode
      {
        \toprule
        \XSIMifblankF { \ExerciseType }
          { \XSIMmixedcase { \GetExerciseParameter {exercise-name} } }
        &
      }
    \ForEachUsedExerciseByOrder
      {
        \XSIMifeqT {#1} { \ExerciseTableType {#1} }
          {
            \XSIMifblankT { \ExerciseTableType {} }
              {
                \XSIMputright \ExerciseTableCode
                  {
                    \XSIMmixedcase
                      { \ExerciseParameterGet {#1} {exercise-name} ~ }
                  }
              }
            \XSIMputright \ExerciseTableCode { #3 & }
          }
      }
    \XSIMputright \ExerciseTableCode
      {
        \XSIMtranslate {total} \\
        \midrule \XSIMmixedcase { \XSIMtranslate {points} } &
      }
    \ForEachUsedExerciseByOrder
      {
        \XSIMifeqT {#1} { \ExerciseTableType {#1} }
          {
            \XSIMputright \ExerciseTableCode
              { \XSIMifblankTF {#5} {\printgoal{0}} {\printgoal{#5}} & }
          }
      }
    \XSIMputright \ExerciseTableCode
      {
        \XSIMifblankTF { \ExerciseType }
          { \TotalExerciseGoal {points} {} {} }
          { \TotalExerciseTypeGoal { \ExerciseType } {points} {} {} }
        \\ \midrule
        \XSIMtranslate {reached} &
      }
    \ForEachUsedExerciseByOrder
      {
        \XSIMifeqT {#1} { \ExerciseTableType {#1} }
          { \XSIMputright \ExerciseTableCode {&} }
      }
    \XSIMputright \ExerciseTableCode { \\ \bottomrule }
    \edef \numberofcolumns
      {
        \XSIMifblankTF { \ExerciseType }
          {\numberofusedexercises}
          {\csname numberof \ExerciseType s\endcsname}
      }
    \XSIMifeqF {\numberofcolumns} {0}
      {
        \begin {tabular} {l*{\numberofcolumns}{c}c}
        \ExerciseTableCode
        \end {tabular}
      }
  }

\DeclareExerciseTableTemplate {default}
  {
    \XSIMputright \ExerciseTableCode
      {
        \toprule
        \XSIMifblankF { \ExerciseType }
          { \XSIMmixedcase { \GetExerciseParameter {exercise-name} } }
        &
        \XSIMmixedcase { \XSIMtranslate {points} } &
        \XSIMtranslate {reached} \\
        \midrule
      }
    \ForEachUsedExerciseByOrder
      {
        \XSIMifeqT {#1} { \ExerciseTableType {#1} }
          {
            \XSIMifblankT { \ExerciseTableType {} }
              {
                \XSIMputright \ExerciseTableCode
                  {
                    \XSIMmixedcase
                      { \ExerciseParameterGet {#1} {exercise-name} ~ }
                  }
              }
            \XSIMputright \ExerciseTableCode
              { #3 & \XSIMifblankTF {#5} {\printgoal{0}} {\printgoal{#5}} & \\ }
          }
      }
    \XSIMputright \ExerciseTableCode
      {
        \midrule
        \XSIMtranslate {total} &
        \XSIMifblankTF { \ExerciseType }
          { \TotalExerciseGoal {points} {} {} }
          { \TotalExerciseTypeGoal { \ExerciseType } {points} {} {} } &
        \\ \bottomrule
      }
    \XSIMexpandcode
      {
        \noexpand \begin {tabular} {\XSIMifblankTF{\ExerciseType}{l}{c}cc}
          \noexpand \ExerciseTableCode
        \noexpand \end {tabular}
      }
  }

% ----------------------------------------------------------------------------

\DeclareExerciseTranslations {exercise} {
  Fallback = exercise ,
  English  = exercise ,
  French   = exercice ,
  German   = \"Ubung
}

\DeclareExerciseTranslations {exercises} {
  Fallback = exercises ,
  English  = exercises ,
  French   = exercices ,
  German   = \"Ubungen
}

\DeclareExerciseTranslations {question} {
  Fallback = question ,
  English  = question ,
  French   = question ,
  German   = Aufgabe
}

\DeclareExerciseTranslations {questions} {
  Fallback = questions ,
  English  = questions ,
  French   = questions ,
  German   = Aufgaben
}

\DeclareExerciseTranslations {solution} {
  Fallback = solution ,
  English  = solution ,
  French   = solution ,
  German   = L\"osung
}

\DeclareExerciseTranslations {solutions} {
  Fallback = solutions ,
  English  = solutions ,
  French   = solutions ,
  German   = L\"osungen
}

\DeclareExerciseTranslations {point-abbr} {
  Fallback = p. ,
  English  = p. ,
  French   = p. ,
  German   = P.
}

\DeclareExerciseTranslations {point} {
  Fallback = point ,
  English  = point ,
  French   = point ,
  German   = Punkt
}

\DeclareExerciseTranslations {points} {
  Fallback = points ,
  English  = points ,
  French   = points ,
  German   = Punkte
}

\DeclareExerciseTranslations {reached} {
  Fallback = reached ,
  English  = reached ,
  French   = obtenus ,
  German   = erreicht
}

\DeclareExerciseTranslations {total} {
  Fallback = total ,
  English  = total ,
  French   = total ,
  German   = insgesamt
}

\DeclareExerciseTranslations {default-heading} {
  Fallback = \XSIMmixedcase { \GetExerciseParameter {solutions-name} }~
             to~ the~ \XSIMmixedcase { \GetExerciseParameter {exercises-name} } ,
  English  = \XSIMmixedcase { \GetExerciseParameter {solutions-name} }~
             to~ the~ \XSIMmixedcase { \GetExerciseParameter {exercises-name} } ,
  French   = \XSIMmixedcase { \GetExerciseParameter {solutions-name} ~
             des~ \GetExerciseParameter {exercises-name} } ,
  German   = \XSIMmixedcase { \GetExerciseParameter {solutions-name} }~
             zu~ den~ \XSIMmixedcase { \GetExerciseParameter {exercises-name} }
}

\DeclareExerciseTranslations {collection-heading} {
  Fallback = \XSIMmixedcase { \GetExerciseParameter {exercises-name} } ,
  English  = \XSIMmixedcase { \GetExerciseParameter {exercises-name} } ,
  French   = \XSIMmixedcase { \GetExerciseParameter {exercises-name} } ,
  German   = \XSIMmixedcase { \GetExerciseParameter {exercises-name} }
}

\DeclareExerciseTranslations {per-section-heading} {
  Fallback = \XSIMmixedcase { \GetExerciseParameter {solutions-name} } ~
             to~ the~ \XSIMmixedcase { \GetExerciseParameter {exercises-name} } ~
             of~ Section \nobreakspace \ExerciseSection ,
  English  = \XSIMmixedcase { \GetExerciseParameter {solutions-name} } ~
             to~ the~ \XSIMmixedcase { \GetExerciseParameter {exercises-name} } ~
             of~ Section \nobreakspace \ExerciseSection ,
  French   = \XSIMmixedcase { \GetExerciseParameter {solutions-name} ~
             des~ \GetExerciseParameter {exercises-name} ~ de~ la~
             section \nobreakspace \ExerciseSection } ,
  German   = \XSIMmixedcase { \GetExerciseParameter {solutions-name} } ~
             zu~ den~ \XSIMmixedcase { \GetExerciseParameter {exercises-name} }~
             in~ Abschnitt \nobreakspace \ExerciseSection
}

\DeclareExerciseTranslations {per-chapter-heading} {
  Fallback = \XSIMmixedcase { \GetExerciseParameter {solutions-name} } ~
             to~ the~ \XSIMmixedcase { \GetExerciseParameter {exercises-name} } ~
             of~ Chapter \nobreakspace \ExerciseChapter ,
  English  = \XSIMmixedcase { \GetExerciseParameter {solutions-name} } ~
             to~ the~ \XSIMmixedcase { \GetExerciseParameter {exercises-name} } ~
             of~ Chapter \nobreakspace \ExerciseChapter ,
  French   = \XSIMmixedcase { \GetExerciseParameter {solutions-name} ~
             des~ \GetExerciseParameter {exercises-name} ~
             du~ chapitre \nobreakspace \ExerciseChapter } ,
  German   = \XSIMmixedcase { \GetExerciseParameter {solutions-name} } ~
             zu~ den~ \XSIMmixedcase { \GetExerciseParameter {exercises-name} } ~
             in~ Kapitel \nobreakspace \ExerciseChapter
}

\XSIMmoduleend
% finish package:
\hook_gput_code:nnn {enddocument} {toplevel} { \xsim_close_aux:\xsim_do_rerun: }
%----------------------------------------------------------------------------
\file_input_stop: