% ----------------------------------------------------------------------------
% the XSIM package - grades module
% 
%   eXercise Sheets IMproved
% 
% ----------------------------------------------------------------------------
% Clemens Niederberger
% Web:    https://github.com/cgnieder/xsim
% E-Mail: contact@mychemistry.eu
% ----------------------------------------------------------------------------
% Copyright 2017--2018 Clemens Niederberger
% 
% This work may be distributed and/or modified under the
% conditions of the LaTeX Project Public License, either version 1.3
% of this license or (at your option) any later version.
% The latest version of this license is in
%   http://www.latex-project.org/lppl.txt
% and version 1.3 or later is part of all distributions of LaTeX
% version 2005/12/01 or later.
% 
% This work has the LPPL maintenance status `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.
% ----------------------------------------------------------------------------
\XSIMmodule{grades}{distribute goal sums to grades}

\xsim_load_modules:n {goals}

\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_exists: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  = {,}
  }

% ----------------------------------------------------------------------------
\file_input_stop: