%%
%% This is file `randques.sty',
%% generated with the docstrip utility.
%%
%% The original source files were:
%%
%% randques.dtx  (with options: `package')
%% 
%%    Copyright (C) 2013 by Qing Lee <sobenlee@gmail.com>
%% --------------------------------------------------------------------------
%%    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 Qing Lee.
%% 
\NeedsTeXFormat{LaTeX2e}
\RequirePackage{expl3}
\GetIdInfo$Id: randques.dtx 182 2013-12-02 03:27:58Z sobenlee@gmail.com $
  {Random question selection.}
\ProvidesExplPackage{\ExplFileName}
  {\ExplFileDate}{0.1}{\ExplFileDescription}
\RequirePackage { xparse }
\RequirePackage { l3keys2e }
%\RequirePackage { l3str }
\RequirePackage { tikz }
\ior_new:N \g__randques_ior
\iow_new:N \g__randques_iow
\int_new:N \l__randques_tmp_int
\int_new:N \l__randques_question_int
\int_new:N \l__randques_count_int
\int_new:N \l__randques_state_int
\int_new:N \g__randques_output_int
\tl_new:N \g__randques_output_tl
\tl_new:N \l__randques_output_tl
\tl_new:N \l__randques_newline_tl
\tl_new:N \l__randques_rand_num_tl
\tl_new:N \l__randques_question_num_tl
\prop_new:N \l__randques_question_prop
\bool_new:N \l__randques_skip_bool
\bool_new:N \l__randques_subquestion_block_bool
\bool_new:N \l__randques_option_block_bool
\bool_new:N \l__randques_all_bool
\bool_new:N \l__randques_sort_bool
\bool_new:N \l__randques_auto_name_bool
\str_new:N \l__randques_before_question_block_str
\str_new:N \l__randques_before_subquestion_block_str
\str_new:N \l__randques_before_option_block_str
\str_new:N \l__randques_before_question_str
\str_new:N \l__randques_before_subquestion_str
\str_new:N \l__randques_before_option_str
\str_new:N \l__randques_after_question_str
\str_new:N \l__randques_after_subquestion_str
\str_new:N \l__randques_after_option_str
\str_new:N \l__randques_after_question_block_str
\str_new:N \l__randques_after_subquestion_block_str
\str_new:N \l__randques_after_option_block_str
\cs_new_protected_nopar:Npn \__randques_rand_num:Nnn
  { \pgfmathrandominteger }
\cs_new_protected_nopar:Npn \__randques_rand:nnnNN #1#2#3#4#5
  {
    \int_compare:nNnTF {#3} > {#2}
      {
        \msg_warning:nnxxx { randques } { not-enough-questions }
          {#1} { \int_eval:n {#2} } { \int_eval:n {#3} }
        \keys_set:nn { randques }  { all = true }
      }
      {
        \int_zero:N \l__randques_tmp_int
        \seq_clear_new:N #4
        \prop_clear_new:N #5
        \int_do_while:nNnn \l__randques_tmp_int < {#3}
          {
            \__randques_rand_num:Nnn \l__randques_rand_num_tl { 1 } {#2}
            \__randques_rand_aux:oNN { \l__randques_rand_num_tl } #4#5
          }
      }
  }
\msg_new:nnn { randques } { not-enough-questions }
  {
    There~are~only~#2~questions~in~file~`#1'.\\
    But~you~ask~#3~questions,~so~all~of~them~are~selected.
  }
\cs_new_protected_nopar:Npn \__randques_rand_aux:nNN #1#2#3
  {
    \seq_if_in:NnF #2 {#1}
      {
        \seq_put_right:Nn #2 {#1}
        \prop_put:Nnn #3 {#1} { }
        \int_incr:N \l__randques_tmp_int
      }
  }
\cs_generate_variant:Nn \__randques_rand_aux:nNN { o }
\cs_new_protected_nopar:Npn \__randques_update_tester:nNNN #1#2#3#4
  {
    \__randques_def:Npn #2 ##1 #1 ##2 \q_stop
      {
        \if:w \scan_stop: ##1##2 \scan_stop:
          ?
        \else:
          \use_none:n ##1 ?
        \fi:
      }
    \__randques_def_conditional:Npnn #3 ##1 { T , F , TF }
      {
        \if:w \scan_stop: #2 ##1 #1 \q_stop \scan_stop:
          \prg_return_true: \else: \prg_return_false: \fi:
      }
    \__randques_def:Npn #4 #1 ##1 \q_stop {##1}
  }
\cs_generate_variant:Nn \__randques_update_tester:nNNN { x }
\cs_new_eq:NN \__randques_def:Npn \cs_new:Npn
\cs_new_eq:NN \__randques_def_conditional:Npnn \prg_new_conditional:Npnn
\cs_new_protected_nopar:Npn \__randques_count:N #1
  {
    \int_zero:N #1
    \ior_str_map_inline:Nn \g__randques_ior
      {
        \tl_if_blank:nF {##1}
          { \__randques_tag_is_question:nT {##1} { \int_incr:N #1 } }
      }
    \ior_close:N \g__randques_ior
  }
\cs_new_protected_nopar:Npn \__randques_select:n #1
  {
    \int_gincr:N \g__randques_output_int
    \bool_if:NTF \l__randques_auto_name_bool
      {
        \tl_gset:Nx \g__randques_output_tl
          { \c_sys_jobname_str - questions - \int_use:N \g__randques_output_int }
      }
      { \tl_gset_eq:NN \g__randques_output_tl \l__randques_output_tl }
    \bool_if:NTF \l__randques_freeze_bool
      {
        \file_if_exist:nTF { \g__randques_output_tl }
          { \use_none:n }
          { \use:n }
      }
      { \use:n }
      {
        \__randques_count:N \l__randques_count_int
        \int_compare:nNnTF \l__randques_count_int = \c_zero
          { \msg_warning:nnx { randques } { no-questions } {#1} }
          {
            \bool_if:NF \l__randques_all_bool
              {
                \bool_if:NT \l__randques_sort_bool
                  { \int_set_eq:NN \l__randques_selection_int \l__randques_count_int }
                \__randques_rand:nnnNN {#1}
                  { \l__randques_count_int } { \l__randques_selection_int }
                  \l__randques_rand_seq
                  \l__randques_question_prop
                \bool_set_false:N \l__randques_skip_bool
                \tl_clear:N \l__randques_question_num_tl
              }
            \__randques_select_aux:n {#1}
          }
      }
  }
\msg_new:nnn { randques } { no-questions }
  { There~is~no~question~be~found~in~file~#1. }
\cs_new_protected_nopar:Npn \__randques_select_aux:n #1
  {
    \tl_clear:N \l__randques_question_block_str
    \int_zero:N \l__randques_question_int
    \iow_open:Nn \g__randques_iow { \g__randques_output_tl }
    \tl_if_empty:NF \l__randques_before_question_block_str
      {
        \iow_now:Nx \g__randques_iow
          { \l__randques_before_question_block_str \iow_newline: }
      }
    \ior_open:Nn \g__randques_ior {#1}
    \ior_str_map_inline:Nn \g__randques_ior
      {
        \tl_if_blank:nTF {##1}
          { \tl_set_eq:NN \l__randques_newline_tl \iow_newline: }
          { \__randques_select_question:n {##1} }
      }
    \__randques_output:
  }
\cs_new_protected_nopar:Npn \__randques_select_all:n #1
  {
    \__randques_tag_is_question:nTF {#1}
      {
        \__randques_output_question_block:
        \int_set_eq:NN \l__randques_state_int \c_zero
        \tl_set:Nx \l__randques_question_block_str
          {
            \l__randques_before_question_str
            \__randques_remove_question_tag:w #1 \q_stop
            \iow_newline:
          }
        \tl_clear:N \l__randques_newline_tl
      }
      { \__randques_select_subquestion:n {#1} }
  }
\cs_new_protected_nopar:Npn \__randques_select_rand:n #1
  {
    \__randques_tag_is_question:nTF {#1}
      {
        \int_incr:N \l__randques_question_int
        \seq_if_in:NVTF \l__randques_rand_seq \l__randques_question_int
          {
            \__randques_save_question_block:
            \tl_set:Nx \l__randques_question_num_tl
              { \int_use:N \l__randques_question_int }
            \bool_set_false:N \l__randques_skip_bool
            \int_set_eq:NN \l__randques_state_int \c_zero
            \tl_set:Nx \l__randques_question_block_str
              {
                \l__randques_before_question_str
                \__randques_remove_question_tag:w #1 \q_stop
                \iow_newline:
              }
            \tl_clear:N \l__randques_newline_tl
          }
          { \bool_set_true:N \l__randques_skip_bool }
      }
      { \bool_if:NF \l__randques_skip_bool { \__randques_select_subquestion:n {#1} } }
  }
\cs_new_protected_nopar:Npn \__randques_select_sort:n #1
  {
    \__randques_tag_is_question:nTF {#1}
      {
        \int_incr:N \l__randques_question_int
        \__randques_save_question_block:
        \tl_set:Nx \l__randques_question_num_tl
          { \int_use:N \l__randques_question_int }
        \int_set_eq:NN \l__randques_state_int \c_zero
        \tl_set:Nx \l__randques_question_block_str
          {
            \l__randques_before_question_str
            \__randques_remove_question_tag:w #1 \q_stop
            \iow_newline:
          }
        \tl_clear:N \l__randques_newline_tl
      }
      { \__randques_select_subquestion:n {#1} }
  }
\cs_new_protected_nopar:Npn \__randques_select_subquestion:n #1
  {
    \__randques_tag_is_option:nTF {#1}
      {
        \tl_put_right:Nx \l__randques_question_block_str
          {
            \__randques_item_tail_str:
            \l__randques_newline_tl
            \bool_if:NF \l__randques_option_block_bool
              { \l__randques_before_option_block_str  \iow_newline: }
            \l__randques_before_option_str
            \__randques_remove_option_tag:w #1 \q_stop
            \iow_newline:
          }
        \int_set_eq:NN \l__randques_state_int \c_two
        \bool_set_true:N \l__randques_option_block_bool
      }
      {
        \__randques_tag_is_subquestion:nTF {#1}
          {
            \tl_put_right:Nx \l__randques_question_block_str
              {
                \__randques_item_tail_str:
                \bool_if:NT \l__randques_option_block_bool
                  { \l__randques_after_option_block_str  \iow_newline: }
                \l__randques_newline_tl
                \bool_if:NF \l__randques_subquestion_block_bool
                  { \l__randques_before_subquestion_block_str  \iow_newline: }
                \l__randques_before_subquestion_str
                \__randques_remove_subquestion_tag:w #1 \q_stop
                 \iow_newline:
              }
            \bool_set_true:N \l__randques_subquestion_block_bool
            \bool_set_false:N \l__randques_option_block_bool
            \int_set_eq:NN \l__randques_state_int 1
          }
          {
            \tl_put_right:Nx \l__randques_question_block_str
              { \l__randques_newline_tl #1  \iow_newline: }
          }
      }
    \tl_clear:N \l__randques_newline_tl
  }
\cs_new_protected_nopar:Npn \__randques_output_question_block:
  {
    \tl_if_empty:NF \l__randques_question_block_str
      {
        \iow_now:Nx \g__randques_iow
          {
            \l__randques_question_block_str
            \__randques_item_tail_str:
            \bool_if:NT \l__randques_option_block_bool
              { \l__randques_after_option_block_str \iow_newline: }
            \bool_if:NT \l__randques_subquestion_block_bool
              { \l__randques_after_subquestion_block_str \iow_newline: }
          }
        \bool_set_false:N \l__randques_option_block_bool
        \bool_set_false:N \l__randques_subquestion_block_bool
      }
  }
\cs_new_protected_nopar:Npn \__randques_save_question_block:
  {
    \tl_if_empty:NF \l__randques_question_num_tl
      {
        \prop_put:NVx \l__randques_question_prop \l__randques_question_num_tl
          {
            \l__randques_question_block_str
            \__randques_item_tail_str:
            \bool_if:NT \l__randques_option_block_bool
              { \l__randques_after_option_block_str \iow_newline: }
            \bool_if:NT \l__randques_subquestion_block_bool
              { \l__randques_after_subquestion_block_str \iow_newline: }
          }
        \bool_set_false:N \l__randques_option_block_bool
        \bool_set_false:N \l__randques_subquestion_block_bool
      }
  }
\cs_generate_variant:Nn \prop_put:Nnn { NVx }
\cs_new_nopar:Npn \__randques_item_tail_str:
  {
    \if_case:w \l__randques_state_int
      \tl_if_empty:NF \l__randques_after_question_str
        { \l__randques_after_question_str \iow_newline: }
    \or:
      \tl_if_empty:NF \l__randques_after_subquestion_str
        { \l__randques_after_subquestion_str \iow_newline: }
    \or:
      \tl_if_empty:NF \l__randques_after_option_str
        { \l__randques_after_option_str \iow_newline: }
    \fi:
  }
\cs_new_protected_nopar:Npn \__randques_output_all:
  {
    \tl_if_empty:NF \l__randques_question_block_str
      {
        \iow_now:Nx \g__randques_iow
          {
            \l__randques_question_block_str
            \__randques_item_tail_str:
            \bool_if:NT \l__randques_option_block_bool
              { \l__randques_after_option_block_str \iow_newline: }
            \bool_if:NT \l__randques_subquestion_block_bool
              { \l__randques_after_subquestion_block_str \iow_newline: }
            \l__randques_after_question_block_str
          }
      }
    \iow_close:N \g__randques_iow
  }
\cs_new_protected_nopar:Npn \__randques_output_rand:
  {
    \__randques_save_question_block:
    \tl_if_empty:NF \l__randques_question_num_tl
      {
        \prop_map_inline:Nn \l__randques_question_prop
          { \iow_now:Nn \g__randques_iow {##2} }
        \tl_if_empty:NF \l__randques_after_question_block_str
          { \iow_now:Nx \g__randques_iow { \l__randques_after_question_block_str } }
      }
    \iow_close:N \g__randques_iow
  }
\cs_new_protected_nopar:Npn \__randques_input:
  { \file_input:n { \g__randques_output_tl } }
\bool_set_true:N \l__randques_auto_name_bool
\keys_define:nn { randques }
  {
    all            .choice: ,
    all / true     .code:n =
      {
        \bool_set_true:N \l__randques_all_bool
        \cs_set_eq:NN \__randques_select_question:n \__randques_select_all:n
        \cs_set_eq:NN \__randques_output: \__randques_output_all:
      } ,
    all / false    .code:n =
      {
        \bool_set_false:N \l__randques_all_bool
        \bool_set_false:N \l__randques_sort_bool
        \cs_set_eq:NN \__randques_select_question:n \__randques_select_rand:n
        \cs_set_eq:NN \__randques_output: \__randques_output_rand:
      } ,
    all / randsort .code:n =
      {
        \bool_set_false:N \l__randques_all_bool
        \bool_set_true:N  \l__randques_sort_bool
        \cs_set_eq:NN \__randques_select_question:n \__randques_select_sort:n
        \cs_set_eq:NN \__randques_output: \__randques_output_rand:
      } ,
    all      .initial:n = {  false } ,
    all      .default:n = {  true } ,
    freeze  .bool_set:N = \l__randques_freeze_bool ,
    random   .int_set:N = \l__randques_selection_int ,
    random   .initial:n = { 1 } ,
    random   .default:n = { 1 } ,
    output   .code:n    =
      {
        \bool_false:N \l__randques_auto_name_tl
        \tl_set:Nx \l__randques_output_tl {#1}
      } ,
    output   .value_required:n=true ,
    question-tag       .code:n =
      {
        \__randques_update_tester:xNNN { \tl_to_str:n {#1} }
          \__randques_tag_is_question:w
          \__randques_tag_is_question:n
          \__randques_remove_question_tag:w
      } ,
    subquestion-tag   .code:n =
      {
        \__randques_update_tester:xNNN { \tl_to_str:n {#1} }
          \__randques_tag_is_subquestion:w
          \__randques_tag_is_subquestion:n
          \__randques_remove_subquestion_tag:w
      } ,
    option-tag        .code:n =
      {
        \__randques_update_tester:xNNN { \tl_to_str:n {#1} }
          \__randques_tag_is_option:w
          \__randques_tag_is_option:n
          \__randques_remove_option_tag:w
      } ,
    question-tag    .initial:n = { \question } ,
    question-tag    .default:n = { \question } ,
    subquestion-tag .initial:n = { \subquestion } ,
    subquestion-tag .default:n = { \subquestion } ,
    option-tag      .initial:n = { \option } ,
    option-tag      .default:n = { \option } ,
    before-question-block      .code:n =
      { \str_set:Nn \l__randques_before_question_block_str {#1} } ,
    before-subquestion-block   .code:n =
      { \str_set:Nn \l__randques_before_subquestion_block_str {#1} } ,
    before-option-block        .code:n =
      { \str_set:Nn \l__randques_before_option_block_str {#1} } ,
    before-question            .code:n =
      { \str_set:Nn \l__randques_before_question_str {#1} } ,
    before-subquestion         .code:n =
      { \str_set:Nn \l__randques_before_subquestion_str {#1} } ,
    before-option              .code:n =
      { \str_set:Nn \l__randques_before_option_str {#1} } ,
    after-question             .code:n =
      { \str_set:Nn \l__randques_after_question_str {#1} } ,
    after-subquestion          .code:n =
      { \str_set:Nn \l__randques_after_subquestion_str {#1} } ,
    after-option               .code:n =
      { \str_set:Nn \l__randques_after_option_str {#1} } ,
    after-question-block       .code:n =
      { \str_set:Nn \l__randques_after_question_block_str {#1} } ,
    after-subquestion-block    .code:n =
      { \str_set:Nn \l__randques_after_subquestion_block_str {#1} } ,
    after-option-block         .code:n =
      { \str_set:Nn \l__randques_after_option_block_str {#1} } ,
    before-question-block    .initial:n = { \begin { enumerate } } ,
    before-subquestion-block .initial:n = { \begin { enumerate } } ,
    before-option-block      .initial:n = { \begin { enumerate } } ,
    before-question          .initial:n = { \item } ,
    before-subquestion       .initial:n = { \item } ,
    before-option            .initial:n = { \item } ,
    after-question-block     .initial:n = { \end { enumerate } } ,
    after-subquestion-block  .initial:n = { \end { enumerate } } ,
    after-option-block       .initial:n = { \end { enumerate } }
  }
\cs_set_eq:NN \__randques_def:Npn \cs_set:Npn
\cs_set_eq:NN \__randques_def_conditional:Npnn \prg_set_conditional:Npnn
\NewDocumentCommand \RandSelQuestions { o m }
  {
    \ior_open:NnTF \g__randques_ior {#2}
      {
        \group_begin:
        \IfNoValueF {#1} { \keys_set:nn { randques } {#1} }
        \__randques_select:n {#2}
        \ior_close:N \g__randques_ior
        \group_end:
        \__randques_input:
      }
      { \msg_error:nnn { randques } { database-not-found } {#2} }
  }
\msg_new:nnn { randques } { database-not-found }
  {
    Question~database~`#1'~not~found.\\
    randques~will~not~work!
  }
\NewDocumentCommand \RandSelQueSetup { m }
  { \keys_set:nn { randques } {#1} }
\ProcessKeysOptions { randques }
%% 
%%    This package consists of the file  randques.dtx,
%%                 and the derived files randques.pdf,
%%                                       randques.sty, and
%%                                       randques.ins.
%%
%% End of file `randques.sty'.
