summaryrefslogtreecommitdiffstats
path: root/misc
diff options
context:
space:
mode:
Diffstat (limited to 'misc')
-rwxr-xr-xmisc/randomize-test.pl183
1 files changed, 183 insertions, 0 deletions
diff --git a/misc/randomize-test.pl b/misc/randomize-test.pl
new file mode 100755
index 0000000..42ff144
--- /dev/null
+++ b/misc/randomize-test.pl
@@ -0,0 +1,183 @@
+#! /usr/local/bin/perl -w
+
+use strict;
+use warnings;
+
+use Digest::SHA qw(sha256);
+
+use Getopt::Std;
+
+my %opts;
+
+getopts("c:N:n:", \%opts);
+
+my @preamble = (); # Preamble lines
+my @postamble = (); # Postamble lines
+my @questions = (); # Array of question hashrefs
+# Each question hash has keys:
+# {question}: arrayref with question lines
+# {answers}: arrayref of arrayrefs with answer lines, correct answer first
+# {varid}: idnex in qvars array
+my @qvars = (); # Array of question variants
+# Each entry is an arrayref of indexes in the questions array
+
+
+my $commonseed = $opts{N} // "";
+my $seed = $opts{n} // "";
+
+### READ INPUT FILE
+
+if ( 1 ) { ## Keep following variables local
+
+my $in_preamble = 1;
+my $in_postamble = 0;
+my $in_qvar = 0;
+my $curqn;
+my $listref = \@preamble;
+my $varid;
+LINELOOP:
+while ($_ = <>) {
+ if ( $_ =~ m/^\\def\\seedval\{.*\}$/ ) {
+ $_ = "\\def\\seedval\{$seed\}\n";
+ }
+ if ( $_ =~ m/\\begin\{qcm\}/ ) {
+ die "wrong placement" unless $in_preamble;
+ die "bad format" unless $_ eq "\\begin\{qcm\}\n";
+ $in_preamble = 0;
+ $listref = undef;
+ next LINELOOP;
+ } elsif ( $_ =~ m/\\end\{qcm\}/ ) {
+ die "wrong placement" if $in_preamble;
+ die "bad format" unless $_ eq "\\end\{qcm\}\n";
+ $in_postamble = 0;
+ $listref = \@postamble;
+ next LINELOOP;
+ } elsif ( $_ =~ m/\\begin\{qvar\}/ ) {
+ die "wrong placement" if $in_preamble || $in_postamble || $in_qvar || defined($curqn);
+ die "bad format" unless $_ eq "\\begin\{qvar\}\n";
+ $in_qvar = 1;
+ push @qvars, [];
+ next LINELOOP;
+ } elsif ( $_ =~ m/\\end\{qvar\}/ ) {
+ die "wrong placement" if $in_preamble || $in_postamble || (!$in_qvar) || defined($curqn);
+ die "bad format" unless $_ eq "\\end\{qvar\}\n";
+ $in_qvar = 0;
+ next LINELOOP;
+ } elsif ( $_ =~ m/\\begin\{question\}/ ) {
+ die "wrong placement" if $in_preamble || $in_postamble || defined($curqn);
+ die "bad format" unless $_ eq "\\begin\{question\}\n";
+ my %qn = ();
+ push @qvars, [] unless $in_qvar;
+ $qn{varid} = $#qvars;
+ $qn{question} = [];
+ $qn{answers} = [];
+ push @questions, \%qn;
+ push @{$qvars[$#qvars]}, $#questions;
+ $listref = $qn{question};
+ $curqn = \%qn;
+ next LINELOOP;
+ } elsif ( $_ =~ m/\\end\{question\}/ ) {
+ die "wrong placement" if $in_preamble || $in_postamble || !defined($curqn);
+ die "bad format" unless $_ eq "\\end\{question\}\n";
+ $listref = undef;
+ $curqn = undef;
+ next LINELOOP;
+ } elsif ( $_ =~ m/\\(right)?answer/
+ && $_ !~ /\\newcommand/ && $_ !~ /\\let\\rightanswer/ ) {
+ die "wrong placement" if $in_preamble || $in_postamble || !defined($curqn);
+ die "bad format" unless $_ eq "\\answer\n" || $_ eq "\\rightanswer\n";
+ die "this is impossible" unless ref($curqn) eq "HASH" && defined $curqn->{answers};
+ die "right answer should come first" unless ($_ eq "\\rightanswer\n") == (scalar(@{$curqn->{answers}}) == 0);
+ my @ans = ();
+ push @{$curqn->{answers}}, \@ans;
+ $listref = \@ans;
+ ## no next LINELOOP here: include \answer in answer itself!
+ }
+ die "this is impossible" if $in_preamble && $in_postamble;
+ die "this is impossible" if $in_preamble && ($listref ne \@preamble);
+ die "this is impossible" if $in_postamble && ($listref ne \@postamble);
+ push @{$listref}, $_ if defined($listref);
+}
+
+}
+
+
+### RANDOMIZE
+
+my $nbqn = $opts{c} // int((scalar(@qvars)+1)/2);
+
+my @questab = ();
+my @quesanstab = ();
+
+if ( 1 ) { ## Keep following variables local
+
+my @hashlist;
+
+for ( my $u=0 ; $u<scalar(@qvars) ; $u++ ) {
+ push @hashlist, sha256("${commonseed}\n${seed}\nQV\n${u}\n");
+}
+my @qvartab = sort { $hashlist[$a] cmp $hashlist[$b] } (0..(scalar(@qvars)-1));
+
+for ( my $k=0 ; $k<scalar(@qvartab) && $k<$nbqn ; $k++ ) {
+ my $u = $qvartab[$k];
+ @hashlist = ();
+ for ( my $kv=0 ; $kv<scalar(@{$qvars[$u]}) ; $kv++ ) {
+ my $i = $qvars[$u]->[$kv];
+ push @hashlist, sha256("${commonseed}\n${seed}\nQ\n${i}\n");
+ }
+ my $kv = (sort { $hashlist[$a] cmp $hashlist[$b] } (0..(scalar(@{$qvars[$u]})-1)))[0];
+ my $i = $qvars[$u]->[$kv];
+ die "this is impossible" unless $questions[$i]->{varid} == $u;
+ push @questab, $i;
+}
+
+for ( my $i=0 ; $i<scalar(@questions) ; $i++ ) {
+ @hashlist = ();
+ my $r = $questions[$i]->{answers};
+ for ( my $j=0 ; $j<scalar(@$r) ; $j++ ) {
+ push @hashlist, sha256("${commonseed}\n${seed}\nQ\n${i}\nA\n${j}\n");
+ }
+ my @anstab;
+ @anstab = sort { $hashlist[$a] cmp $hashlist[$b] } (0..(scalar(@$r)-1));
+ push @quesanstab, \@anstab;
+}
+
+}
+
+
+### WRITE OUTPUT FILE
+
+my @correct;
+
+foreach my $l ( @preamble ) {
+ print $l;
+}
+
+print "\\begin\{qcm\}\n\n";
+
+for ( my $k=0 ; $k<scalar(@questab) ; $k++ ) {
+ my $i = $questab[$k];
+ my $qn = $questions[$i];
+ print "\\begin\{question\}\n";
+ foreach my $l ( @{$qn->{question}} ) {
+ print $l;
+ }
+ for ( my $kj=0 ; $kj<scalar(@{$quesanstab[$i]}) ; $kj++ ) {
+ my $j = $quesanstab[$i]->[$kj];
+ my $a = $qn->{answers}->[$j];
+ foreach my $l ( @{$a} ) {
+ print $l;
+ }
+ push @correct, sprintf("%d%s", $k+1, chr(ord("A")+$kj)) if $j==0;
+ }
+ print "\\end\{question\}\n\n";
+}
+
+print "\\end\{qcm\}\n\n";
+
+printf "\%\% === %s ===\n", join(" ", @correct);
+printf "\\ifcorrige\\bigskip\\noindent\\textbf{Corrigé.} %s\\fi\n", join(" ", @correct);
+
+foreach my $l ( @postamble ) {
+ print $l;
+}