#! /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 = (); my @postamble = (); my @questions = (); my $nbvarid = 0; ### 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/\\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; $varid = ($nbvarid++); 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; $varid = undef; 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 = (); $varid = ($nbvarid++) unless $in_qvar; $qn{varid} = $varid; $qn{question} = []; $qn{answers} = []; push @questions, \%qn; $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); } } # printf STDERR "nbvarid=%d (total=%d)\n", $nbvarid, scalar(@questions); ### RANDOMIZE my $commonseed = $opts{N} // ""; my $seed = $opts{n} // ""; my $nbqn = $opts{c} // int(($nbvarid+1)/2); my @questab = (); my @quesanstab = (); if ( 1 ) { ## Keep following variables local my @hashlist; for ( my $i=0 ; $i $nbqn; my $i = $prequestab[$k]; my $qn = $questions[$i]; my $varid = $qn->{varid}; if ( defined($varid) ) { if ( defined($varid_punch{$varid}) ) { next PREQUESLOOP; } else { $varid_punch{$varid} = $i; } } push @questab, $i; } for ( my $i=0 ; $i{answers}; for ( my $j=0 ; $j{question}} ) { print $l; } for ( my $kk=0 ; $kk[$kk]; my $a = $qn->{answers}->[$j]; foreach my $l ( @{$a} ) { print $l; } push @correct, sprintf("%d%s", $k+1, chr(ord("A")+$kk)) 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; }