% ---------------------------------------------------------------------------- % the XSIM package - random module % % eXercise Sheets IMproved % % ---------------------------------------------------------------------------- % Clemens Niederberger % Web: https://github.com/cgnieder/xsim % E-Mail: contact@mychemistry.eu % ---------------------------------------------------------------------------- % Copyright 2017--2019 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{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} } \xsim_load_modules:n {collections} \tl_new:N \l_xsim_random_collection_tl \tl_new:N \l_xsim_random_exclude_list_tl \tl_new:N \l__xsim_print_random_choice_tl \tl_const:Nn \c__xsim_print_random_both_tl {both} \tl_const:Nn \c__xsim_print_random_exercise_tl {exercise} \tl_const:Nn \c__xsim_print_random_solution_tl {solution} \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 % #1: random list id % #2: csv list of integers \xsim_new_aux_cs: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 { \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_cs_to_aux:nn {random} { {#1} { \xsim_use_random_list:nn {#1} {,} } } } % #1: collection % #2: number of items % #3: csv list 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} { \xsim_rerun: } { % generate seq of allowed ids from collection: \xsim_foreach_exercise_id:n { \tl_set:Nx \l__xsim_tmpa_tl { \xsim_get_type_for_property:nn {id} {##1} } \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:nN { \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 {VnV} % #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_if:nT { !\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_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_sort: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 { \bool_if:nT { \xsim_random_list_if_exist_p:n {#2} && !\xsim_random_list_if_empty_p:n {#2} } { \xsim_verbose:x { Printing~ \l__xsim_print_random_choice_tl \c_space_tl \str_if_eq:onT { \l__xsim_print_random_choice_tl } {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_if:nT { \tl_if_eq_p:NN \l__xsim_print_random_choice_tl \c__xsim_print_random_both_tl || \tl_if_eq_p:NN \l__xsim_print_random_choice_tl \c__xsim_print_random_exercise_tl } { \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 { \tl_if_eq_p:NN \l__xsim_print_random_choice_tl \c__xsim_print_random_both_tl || \tl_if_eq_p:NN \l__xsim_print_random_choice_tl \c__xsim_print_random_solution_tl } { \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 .choice: , print/exercises .code:n = \tl_set:Nn \l__xsim_print_random_choice_tl {exercise} , print/solutions .code:n = \tl_set:Nn \l__xsim_print_random_choice_tl {solution} , print/both .code:n = \tl_set:Nn \l__xsim_print_random_choice_tl {both} , print/unknown .code:n = \msg_warning:nnn {xsim} {print-collection-choice} {#1} \tl_set:Nn \l__xsim_print_random_choice_tl {exercise} , 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 \tl_set:Nx \l_xsim_random_exclude_list_tl { \seq_use:Nn \l__xsim_tmpa_seq {,} } } % use this in the exclude list: % \xsim_get_type_for_property:nn {id} {} % or a combination of: % \xsim_get_type_for_property:nn {ID} {} % \xsim_get_id_for_property:nn {ID} {} % ---------------------------------------------------------------------------- \file_input_stop: