[system] / trunk / pg / macros / PGbasicmacros.pl Repository:
ViewVC logotype

View of /trunk/pg/macros/PGbasicmacros.pl

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2292 - (download) (as text) (annotate)
Fri Jun 11 16:40:44 2004 UTC (15 years, 7 months ago) by dpvc
File size: 68000 byte(s)
Fixed a bug in jsMath mode where it would not display < (when it looked like
part of a tag).

Also, removed unwanted spaced around input box.

    1 
    2 
    3 ####################################################################
    4 # Copyright @ 1995-1998 University of Rochester
    5 # All Rights Reserved
    6 ####################################################################
    7 
    8 =head1 NAME
    9 
   10     PGbasicmacros.pl --- located in the courseScripts directory
   11 
   12 =head1 SYNPOSIS
   13 
   14 
   15 
   16 =head1 DESCRIPTION
   17 
   18 
   19 
   20 =cut
   21 
   22 # this is equivalent to use strict, but can be used within the Safe compartment.
   23 BEGIN{
   24   be_strict;
   25 }
   26 
   27 
   28 my $displayMode;
   29 
   30 my ($PAR,
   31   $BR,
   32   $LQ,
   33   $RQ,
   34   $BM,
   35   $EM,
   36   $BDM,
   37   $EDM,
   38   $LTS,
   39   $GTS,
   40   $LTE,
   41   $GTE,
   42   $BEGIN_ONE_COLUMN,
   43   $END_ONE_COLUMN,
   44   $SOL,
   45   $SOLUTION,
   46   $HINT,
   47   $US,
   48   $SPACE,
   49   $BBOLD,
   50   $EBOLD,
   51   $BITALIC,
   52   $EITALIC,
   53   $BCENTER,
   54   $ECENTER,
   55   $HR,
   56   $LBRACE,
   57   $RBRACE,
   58   $LB,
   59   $RB,
   60   $DOLLAR,
   61   $PERCENT,
   62   $CARET,
   63   $PI,
   64   $E,
   65   @ALPHABET,
   66   $envir,
   67   $PG_random_generator,
   68   $inputs_ref,
   69   $rh_sticky_answers,
   70   $r_ans_rule_count,
   71   );
   72 
   73 sub _PGbasicmacros_init {
   74 
   75     # The big problem is that at compile time in the cached Safe compartment
   76     # main:: has one definition, probably Safe::Root1::
   77     # At runtime main has another definition Safe::Rootx:: where x is > 1
   78 
   79     # It is important to
   80     # initialize the my variable version of $displayMode from the "runtime" version
   81     # of main::displayMode
   82 
   83     $displayMode         =    main::PG_restricted_eval(q!$main::displayMode!);
   84 
   85 # This is initializes the remaining variables in the runtime main:: compartment.
   86 
   87 main::PG_restricted_eval( <<'EndOfFile');
   88     $displayMode            = $displayMode;
   89 
   90   $main::PAR        = PAR();
   91   $main::BR       = BR();
   92   $main::LQ       = LQ();
   93   $main::RQ       = RQ();
   94   $main::BM       = BM();
   95   $main::EM       = EM();
   96   $main::BDM        = BDM();
   97   $main::EDM        = EDM();
   98   $main::LTS        = LTS();
   99   $main::GTS        = GTS();
  100   $main::LTE        = LTE();
  101   $main::GTE        = GTE();
  102   $main::BEGIN_ONE_COLUMN = BEGIN_ONE_COLUMN();
  103   $main::END_ONE_COLUMN = END_ONE_COLUMN();
  104   $main::SOL        = SOLUTION_HEADING();
  105   $main::SOLUTION     = SOLUTION_HEADING();
  106   $main::HINT       = HINT_HEADING();
  107   $main::US       = US();
  108   $main::SPACE      = SPACE();
  109   $main::BBOLD      = BBOLD();
  110   $main::EBOLD      = EBOLD();
  111   $main::BITALIC      = BITALIC();
  112   $main::EITALIC          = EITALIC();
  113   $main::BCENTER          = BCENTER();
  114   $main::ECENTER          = ECENTER();
  115   $main::HR       = HR();
  116   $main::LBRACE     = LBRACE();
  117   $main::RBRACE     = RBRACE();
  118   $main::LB       = LB();
  119   $main::RB       = RB();
  120   $main::DOLLAR     = DOLLAR();
  121   $main::PERCENT      = PERCENT();
  122   $main::CARET      = CARET();
  123   $main::PI       = PI();
  124   $main::E        = E();
  125   @main::ALPHABET     = ('A'..'ZZ');
  126   %main::STICKY_ANSWERS   = ();
  127 
  128 
  129 EndOfFile
  130 
  131 # Next we transfer the correct definitions in the main:: compartment to the local my variables
  132 # This can't be done inside the eval above because my variables seem to be invisible inside the eval
  133 
  134 
  135     $PAR         = PAR();
  136   $BR            = BR();
  137   $LQ            = LQ();
  138   $RQ            = RQ();
  139   $BM            = BM();
  140   $EM            = EM();
  141   $BDM         = BDM();
  142   $EDM         = EDM();
  143   $LTS         = LTS();
  144   $GTS         = GTS();
  145   $LTE         = LTE();
  146   $GTE         = GTE();
  147   $BEGIN_ONE_COLUMN  = BEGIN_ONE_COLUMN();
  148   $END_ONE_COLUMN      = END_ONE_COLUMN();
  149   $SOL         = SOLUTION_HEADING();
  150   $SOLUTION      = SOLUTION_HEADING();
  151   $HINT        = HINT_HEADING();
  152   $US            = US();
  153   $SPACE           = SPACE();
  154   $BBOLD           = BBOLD();
  155   $EBOLD           = EBOLD();
  156   $BITALIC       = BITALIC();
  157   $EITALIC             = EITALIC();
  158   $BCENTER             = BCENTER();
  159   $ECENTER             = ECENTER();
  160   $HR            = HR();
  161   $LBRACE          = LBRACE();
  162   $RBRACE          = RBRACE();
  163   $LB            = LB();
  164   $RB            = RB();
  165   $DOLLAR          = DOLLAR();
  166   $PERCENT       = PERCENT();
  167   $CARET           = CARET();
  168   $PI            = PI();
  169   $E             = E();
  170   @ALPHABET      = ('A'..'ZZ');
  171 
  172    $envir               = PG_restricted_eval(q!\%main::envir!);
  173    $PG_random_generator = PG_restricted_eval(q!$main::PG_random_generator!);
  174    $inputs_ref          = $envir{inputs_ref};
  175    $rh_sticky_answers   = PG_restricted_eval(q!\%main::STICKY_ANSWERS!);
  176    $r_ans_rule_count     = PG_restricted_eval(q!\$ans_rule_count!);
  177 }
  178 
  179 =head2  Answer blank macros:
  180 
  181 These produce answer blanks of various sizes or pop up lists or radio answer buttons.
  182 The names for the answer blanks are
  183 generated implicitly.
  184 
  185   ans_rule( width )
  186   tex_ans_rule( width )
  187   ans_radio_buttons(value1=>label1, value2,label2 => value3,label3=>...)
  188   pop_up_list(@list)  # list consists of (value => label,  PR => "Product rule",...)
  189 
  190 To indicate the checked position of radio buttons put a '%' in front of the value: C<ans_radio_buttons(1, 'Yes','%2','No')>
  191 will have 'No' checked.  C<tex_ans_rule> works inside math equations in C<HTML_tth> mode.  It does not work in C<Latex2HTML> mode
  192 since this mode produces gif pictures.
  193 
  194 
  195 The following method is defined in F<PG.pl> for entering the answer evaluators corresponding
  196 to answer rules with automatically generated names.  The answer evaluators are matched with the
  197 answer rules in the order in which they appear on the page.
  198 
  199   ANS(ans_evaluator1, ans_evaluator2,...);
  200 
  201 These are more primitive macros which produce answer blanks for specialized cases when complete
  202 control over the matching of answers blanks and answer evaluators is desired.
  203 The names of the answer blanks must be generated manually, and it is best if they do NOT begin
  204 with the default answer prefix (currently AnSwEr).
  205 
  206   labeled_ans_rule(name, width)  # an alias for NAMED_ANS_RULE where width defaults to 20 if omitted.
  207 
  208   NAMED_ANS_RULE(name, width)
  209   NAMED_ANS_BOX(name, rows, cols)
  210   NAMED_ANS_RADIO(name, value,label,)
  211   NAMED_ANS_RADIO_EXTENSION(name, value,label)
  212   NAMED_ANS_RADIO_BUTTONS(name,value1,label1,value2,label2,...)
  213   check_box('-name' =>answer5,'-value' =>'statement3','-label' =>'I loved this course!'   )
  214   NAMED_POP_UP_LIST($name, @list) # list consists of (value => tag,  PR => "Product rule",...)
  215 
  216 (Name is the name of the variable, value is the value given to the variable when this option is selected,
  217 and label is the text printed next to the button or check box.    Check box variables can have multiple values.)
  218 
  219 NAMED_ANS_RADIO_BUTTONS creates a sequence of NAMED_ANS_RADIO and NAMED_ANS_RADIO_EXTENSION  items which
  220 are  output either as an array or, in scalar context, as the array glued together with spaces.  It is
  221 usually easier to use this than to manually construct the radio buttons by hand.  However, sometimes
  222  extra flexibility is desiredin which case:
  223 
  224 When entering radio buttons using the "NAMED" format, you should use NAMED_ANS_RADIO button for the first button
  225 and then use NAMED_ANS_RADIO_EXTENSION for the remaining buttons.  NAMED_ANS_RADIO requires a matching answer evalutor,
  226 while NAMED_ANS_RADIO_EXTENSION does not. The name used for NAMED_ANS_RADIO_EXTENSION should match the name
  227 used for NAMED_ANS_RADIO (and the associated answer evaluator).
  228 
  229 
  230 The following method is defined in  F<PG.pl> for entering the answer evaluators corresponding
  231 to answer rules with automatically generated names.  The answer evaluators are matched with the
  232 answer rules in the order in which they appear on the page.
  233 
  234       NAMED_ANS(name1 => ans_evaluator1, name2 => ans_evaluator2,...);
  235 
  236 These auxiliary macros are defined in PG.pl
  237 
  238 
  239   NEW_ANS_NAME( number );   # produces a new answer blank name from a number by adding a prefix (AnSwEr)
  240                             # and registers this name as an implicitly labeled answer
  241                             # Its use is paired with each answer evaluator being entered using ANS()
  242 
  243     ANS_NUM_TO_NAME(number);  # adds the prefix (AnSwEr) to the number, but does nothing else.
  244 
  245   RECORD_ANS_NAME( name );  # records the order in which the answer blank  is rendered
  246                             # This is called by all of the constructs above, but must
  247                             # be called explicitly if an input blank is constructed explictly
  248                             # using HTML code.
  249 
  250 These are legacy macros:
  251 
  252   ANS_RULE( number, width );                  # equivalent to NAMED_ANS_RULE( NEW_ANS_NAME(number), width)
  253   ANS_BOX( question_number,height, width );       # equivalent to NAMED_ANS_BOX( NEW_ANS_NAME(number), height, width)
  254   ANS_RADIO( question_number, value,tag );        # equivalent to NAMED_ANS_RADIO( NEW_ANS_NAME(number), value,tag)
  255   ANS_RADIO_OPTION( question_number, value,tag );   # equivalent to NAMED_ANS_RADIO_EXTENSION( ANS_NUM_TO_NAME(number), value,tag)
  256 
  257 
  258 =cut
  259 
  260 
  261 
  262 sub labeled_ans_rule {   # syntactic sugar for NAMED_ANS_RULE
  263   my($name,$col) = @_;
  264   $col = 20 unless defined($col);
  265   NAMED_ANS_RULE($name,$col);
  266 }
  267 
  268 sub NAMED_ANS_RULE {
  269   my($name,$col) = @_;
  270   my $len = 0.07*$col;
  271   my $answer_value = '';
  272   $answer_value = ${$inputs_ref}{$name} if    defined(${$inputs_ref}{$name});
  273     if ($answer_value =~ /\0/ ) {
  274       my @answers = split("\0", $answer_value);
  275       $answer_value = shift(@answers);  # use up the first answer
  276       $rh_sticky_answers->{$name}=\@answers;
  277       # store the rest -- beacuse this stores to a main:; variable
  278       # it must be evaluated at run time
  279       $answer_value= '' unless defined($answer_value);
  280   } elsif (ref($answer_value) eq 'ARRAY') {
  281     my @answers = @{ $answer_value};
  282       $answer_value = shift(@answers);  # use up the first answer
  283       $rh_sticky_answers->{$name}=\@answers;
  284       # store the rest -- beacuse this stores to a main:; variable
  285       # it must be evaluated at run time
  286       $answer_value= '' unless defined($answer_value);
  287   }
  288 
  289   $answer_value =~ tr/\\$@`//d;   ## make sure student answers can not be interpolated by e.g. EV3
  290   $name = RECORD_ANS_NAME($name);
  291 
  292   # incorporated Davide Cervone's changes
  293   # removed newlines from around <INPUT> tags
  294   # made TeX rule be based on specified width rather than varying size.
  295   my $tcol = $col/2 > 3 ? $col/2 : 3;  ## get max
  296   $tcol = $tcol < 40 ? $tcol : 40;     ## get min
  297 
  298   MODES(
  299     TeX => "\\mbox{\\parbox[t]{${tcol}ex}{\\hrulefill}}",
  300     Latex2HTML => qq!\\begin{rawhtml}<INPUT TYPE=TEXT SIZE=$col NAME=\"$name\" VALUE = \"\">\\end{rawhtml}!,
  301     HTML => qq!<INPUT TYPE=TEXT SIZE=$col NAME="$name" VALUE="$answer_value">!.
  302                         qq!<INPUT TYPE=HIDDEN  NAME="previous_$name" VALUE="$answer_value">!
  303   );
  304 }
  305 
  306 sub NAMED_ANS_RULE_OPTION {   # deprecated
  307   &NAMED_ANS_RULE_EXTENSION;
  308 }
  309 
  310 sub NAMED_ANS_RULE_EXTENSION {
  311   my($name,$col) = @_;
  312   my $len = 0.07*$col;
  313   my $answer_value = '';
  314   $answer_value = ${$inputs_ref}{$name} if defined(${$inputs_ref}{$name});
  315   if ( defined( $rh_sticky_answers->{$name} ) ) {
  316     $answer_value = shift( @{ $rh_sticky_answers->{$name} });
  317     $answer_value = '' unless defined($answer_value);
  318   }
  319   $answer_value =~ tr/\\$@`//d;   ## make sure student answers can not be interpolated by e.g. EV3
  320   MODES(
  321     TeX => '\\hrulefill\\quad ',
  322     Latex2HTML => qq!\\begin{rawhtml}\n<INPUT TYPE=TEXT SIZE=$col NAME=\"$name\" VALUE = \"\">\n\\end{rawhtml}\n!,
  323     HTML => qq!<INPUT TYPE=TEXT SIZE=$col NAME = "$name" VALUE = "$answer_value">\n
  324                <INPUT TYPE=HIDDEN  NAME="previous_$name" VALUE = "$answer_value">
  325               !
  326   );
  327 }
  328 
  329 sub ANS_RULE {  #deprecated
  330   my($number,$col) = @_;
  331   my $name = NEW_ANS_NAME($number);
  332     NAMED_ANS_RULE($name,$col);
  333 }
  334 
  335 
  336 sub  NAMED_ANS_BOX {
  337   my($name,$row,$col) = @_;
  338   $row = 10 unless defined($row);
  339   $col = 80 unless defined($col);
  340   $name = RECORD_ANS_NAME($name);
  341   my $len = 0.07*$col;
  342   my $height = .07*$row;
  343   my $answer_value = '';
  344   $answer_value = $inputs_ref->{$name} if defined( $inputs_ref->{$name} );
  345   $answer_value =~ tr/\\$@`//d;   ## make sure student answers can not be interpolated by e.g. EV3
  346   my $out = M3(
  347        qq!\\vskip $height in \\hrulefill\\quad !,
  348        qq!\\begin{rawhtml}<TEXTAREA NAME="$name" ROWS="$row" COLS="$col"
  349                WRAP="VIRTUAL">$answer_value</TEXTAREA>\\end{rawhtml}!,
  350          qq!<TEXTAREA NAME="$name" ROWS="$row" COLS="$col"
  351                WRAP="VIRTUAL">$answer_value</TEXTAREA>
  352              <INPUT TYPE=HIDDEN  NAME="previous_$name" VALUE = "$answer_value">
  353            !
  354          );
  355   $out;
  356 }
  357 
  358 sub  ANS_BOX { #deprecated
  359   my($number,$row,$col) = @_;
  360   my $name = NEW_ANS_NAME($number);
  361     NAMED_ANS_BOX($name,$row,$col);
  362 }
  363 
  364 sub NAMED_ANS_RADIO {
  365   my $name = shift;
  366   my $value = shift;
  367     my $tag =shift;
  368     $name = RECORD_ANS_NAME($name);
  369     my $checked = '';
  370     if ($value =~/^\%/) {
  371       $value =~ s/^\%//;
  372       $checked = 'CHECKED'
  373     }
  374   if (defined($inputs_ref->{$name}) ) {
  375     if ($inputs_ref->{$name} eq $value) {
  376       $checked = 'CHECKED'
  377     } else {
  378       $checked = '';
  379     }
  380 
  381     }
  382 
  383   MODES(
  384     TeX => qq!\\item{$tag}\n!,
  385     Latex2HTML => qq!\\begin{rawhtml}\n<INPUT TYPE=RADIO NAME="$name" VALUE="$value" $checked>\\end{rawhtml}$tag!,
  386     HTML => qq!<INPUT TYPE=RADIO NAME="$name" VALUE="$value" $checked>$tag!
  387   );
  388 
  389 }
  390 
  391 sub NAMED_ANS_RADIO_OPTION { #deprecated
  392   &NAMED_ANS_RADIO_EXTENSION;
  393 }
  394 
  395 sub NAMED_ANS_RADIO_EXTENSION {
  396   my $name = shift;
  397   my $value = shift;
  398   my $tag =shift;
  399 
  400 
  401     my $checked = '';
  402     if ($value =~/^\%/) {
  403       $value =~ s/^\%//;
  404       $checked = 'CHECKED'
  405     }
  406   if (defined($inputs_ref->{$name}) ) {
  407     if ($inputs_ref->{$name} eq $value) {
  408       $checked = 'CHECKED'
  409     } else {
  410       $checked = '';
  411     }
  412 
  413     }
  414 
  415   MODES(
  416     TeX => qq!\\item{$tag}\n!,
  417     Latex2HTML => qq!\\begin{rawhtml}\n<INPUT TYPE=RADIO NAME="$name" VALUE="$value" $checked>\\end{rawhtml}$tag!,
  418     HTML => qq!<INPUT TYPE=RADIO NAME="$name" VALUE="$value" $checked>$tag!
  419   );
  420 
  421 }
  422 
  423 sub NAMED_ANS_RADIO_BUTTONS {
  424     my $name  =shift;
  425     my $value = shift;
  426     my $tag = shift;
  427 
  428 
  429   my @out = ();
  430   push(@out, NAMED_ANS_RADIO($name, $value,$tag));
  431   my @buttons = @_;
  432   while (@buttons) {
  433     $value = shift @buttons;  $tag = shift @buttons;
  434     push(@out, NAMED_ANS_RADIO_OPTION($name, $value,$tag));
  435   }
  436   (wantarray) ? @out : join(" ",@out);
  437 }
  438 sub ANS_RADIO {
  439   my $number = shift;
  440   my $value = shift;
  441   my $tag =shift;
  442     my $name = NEW_ANS_NAME($number);
  443   NAMED_ANS_RADIO($name,$value,$tag);
  444 }
  445 
  446 sub ANS_RADIO_OPTION {
  447   my $number = shift;
  448   my $value = shift;
  449   my $tag =shift;
  450 
  451 
  452     my $name = ANS_NUM_TO_NAME($number);
  453   NAMED_ANS_RADIO_OPTION($name,$value,$tag);
  454 }
  455 sub ANS_RADIO_BUTTONS {
  456     my $number  =shift;
  457     my $value = shift;
  458     my $tag = shift;
  459 
  460 
  461   my @out = ();
  462   push(@out, ANS_RADIO($number, $value,$tag));
  463   my @buttons = @_;
  464   while (@buttons) {
  465       $value = shift @buttons; $tag = shift @buttons;
  466     push(@out, ANS_RADIO_OPTION($number, $value,$tag));
  467   }
  468   (wantarray) ? @out : join(" ",@out);
  469 }
  470 ##############################################
  471 #   contained_in( $elem, $array_reference or null separated string);
  472 #   determine whether element is equal
  473 #   ( in the sense of eq,  not ==, ) to an element in the array.
  474 ##############################################
  475 sub contained_in {
  476   my $element = shift;
  477   my @input_list    = @_;
  478   my @output_list = ();
  479   # Expand the list -- convert references to  arrays to arrays
  480   # Convert null separated strings to arrays
  481   foreach my $item   (@input_list ) {
  482     if ($item =~ /\0/) {
  483       push @output_list,   split('\0', $item);
  484      } elsif (ref($item) =~/ARRAY/) {
  485       push @output_list, @{$item};
  486      } else {
  487       push @output_list, $item;
  488      }
  489   }
  490 
  491   my @match_list = grep {$element eq $_ } @output_list;
  492   if ( @match_list ) {
  493     return 1;
  494   } else {
  495     return 0;
  496   }
  497 }
  498 
  499 ##########################
  500 # If multiple boxes are checked then the $inputs_ref->{name }will be a null separated string
  501 # or a reference to an array.
  502 ##########################
  503 
  504 sub NAMED_ANS_CHECKBOX {
  505   my $name = shift;
  506   my $value = shift;
  507     my $tag =shift;
  508     $name = RECORD_ANS_NAME($name);
  509 
  510     my $checked = '';
  511     if ($value =~/^\%/) {
  512       $value =~ s/^\%//;
  513       $checked = 'CHECKED'
  514     }
  515 
  516   if (defined($inputs_ref->{$name}) ) {
  517     if ( contained_in($value, $inputs_ref->{$name} ) ) {
  518       $checked = 'CHECKED'
  519     }
  520     else {
  521       $checked = '';
  522     }
  523 
  524     }
  525 
  526   MODES(
  527     TeX => qq!\\item{$tag}\n!,
  528     Latex2HTML => qq!\\begin{rawhtml}\n<INPUT TYPE=CHECKBOX NAME="$name" VALUE="$value" $checked>\\end{rawhtml}$tag!,
  529     HTML => qq!<INPUT TYPE=CHECKBOX NAME="$name" VALUE="$value" $checked>$tag!
  530   );
  531 
  532 }
  533 
  534 sub NAMED_ANS_CHECKBOX_OPTION {
  535   my $name = shift;
  536   my $value = shift;
  537   my $tag =shift;
  538 
  539     my $checked = '';
  540     if ($value =~/^\%/) {
  541       $value =~ s/^\%//;
  542       $checked = 'CHECKED'
  543     }
  544 
  545   if (defined($inputs_ref->{$name}) ) {
  546     if ( contained_in($value, $inputs_ref->{$name}) ) {
  547       $checked = 'CHECKED'
  548     }
  549     else {
  550       $checked = '';
  551     }
  552 
  553     }
  554 
  555   MODES(
  556     TeX => qq!\\item{$tag}\n!,
  557     Latex2HTML => qq!\\begin{rawhtml}\n<INPUT TYPE=CHECKBOX NAME="$name" VALUE="$value" $checked>\\end{rawhtml}$tag!,
  558     HTML => qq!<INPUT TYPE=CHECKBOX NAME="$name" VALUE="$value" $checked>$tag!
  559   );
  560 
  561 }
  562 
  563 sub NAMED_ANS_CHECKBOX_BUTTONS {
  564     my $name  =shift;
  565     my $value = shift;
  566     my $tag = shift;
  567 
  568   my @out = ();
  569   push(@out, NAMED_ANS_CHECKBOX($name, $value,$tag));
  570 
  571   my @buttons = @_;
  572   while (@buttons) {
  573     $value = shift @buttons;  $tag = shift @buttons;
  574     push(@out, NAMED_ANS_CHECKBOX_OPTION($name, $value,$tag));
  575   }
  576 
  577   (wantarray) ? @out : join(" ",@out);
  578 }
  579 
  580 sub ANS_CHECKBOX {
  581   my $number = shift;
  582   my $value = shift;
  583   my $tag =shift;
  584     my $name = NEW_ANS_NAME($number);
  585 
  586   NAMED_ANS_CHECKBOX($name,$value,$tag);
  587 }
  588 
  589 sub ANS_CHECKBOX_OPTION {
  590   my $number = shift;
  591   my $value = shift;
  592   my $tag =shift;
  593     my $name = ANS_NUM_TO_NAME($number);
  594 
  595   NAMED_ANS_CHECKBOX_OPTION($name,$value,$tag);
  596 }
  597 
  598 
  599 
  600 sub ANS_CHECKBOX_BUTTONS {
  601     my $number  =shift;
  602     my $value = shift;
  603     my $tag = shift;
  604 
  605   my @out = ();
  606   push(@out, ANS_CHECKBOX($number, $value, $tag));
  607 
  608   my @buttons = @_;
  609   while (@buttons) {
  610     $value = shift @buttons;  $tag = shift @buttons;
  611     push(@out, ANS_CHECKBOX_OPTION($number, $value,$tag));
  612   }
  613 
  614   (wantarray) ? @out : join(" ",@out);
  615 }
  616 
  617 sub ans_rule {
  618   my $len = shift;     # gives the optional length of the answer blank
  619   $len    = 20 unless $len ;
  620   my $name = NEW_ANS_NAME(inc_ans_rule_count());
  621   NAMED_ANS_RULE($name ,$len);
  622 }
  623 sub ans_rule_extension {
  624   my $len = shift;
  625     $len    = 20 unless $len ;
  626   my $name = NEW_ANS_NAME($$r_ans_rule_count);  # don't update the answer name
  627   NAMED_ANS_RULE($name ,$len);
  628 }
  629 sub ans_radio_buttons {
  630   my $name  = NEW_ANS_NAME(inc_ans_rule_count());
  631   my @radio_buttons = NAMED_ANS_RADIO_BUTTONS($name, @_);
  632 
  633   if ($displayMode eq 'TeX') {
  634     $radio_buttons[0] = "\n\\begin{itemize}\n" . $radio_buttons[0];
  635     $radio_buttons[$#radio_buttons] .= "\n\\end{itemize}\n";
  636   }
  637 
  638   (wantarray) ? @radio_buttons: join(" ", @radio_buttons);
  639 }
  640 
  641 #added 6/14/2000 by David Etlinger
  642 sub ans_checkbox {
  643   my $name = NEW_ANS_NAME( inc_ans_rule_count() );
  644   my @checkboxes = NAMED_ANS_CHECKBOX_BUTTONS( $name, @_ );
  645 
  646   if ($displayMode eq 'TeX') {
  647     $checkboxes[0] = "\n\\begin{itemize}\n" . $checkboxes[0];
  648     $checkboxes[$#checkboxes] .= "\n\\end{itemize}\n";
  649   }
  650 
  651   (wantarray) ? @checkboxes: join(" ", @checkboxes);
  652 }
  653 
  654 
  655 ## define a version of ans_rule which will work inside TeX math mode or display math mode -- at least for tth mode.
  656 ## This is great for displayed fractions.
  657 ## This will not work with latex2HTML mode since it creates gif equations.
  658 
  659 sub tex_ans_rule {
  660   my $len = shift;
  661   $len    = 20 unless $len ;
  662     my $name = NEW_ANS_NAME(inc_ans_rule_count());
  663     my $answer_rule = NAMED_ANS_RULE($name ,$len);  # we don't want to create three answer rules in different modes.
  664     my $out = MODES(
  665                      'TeX' => $answer_rule,
  666                      'Latex2HTML' => '\\fbox{Answer boxes cannot be placed inside typeset equations}',
  667                      'HTML_tth' => '\\begin{rawhtml} '. $answer_rule.'\\end{rawhtml}',
  668                      'HTML_dpng' => '\\fbox{Answer boxes cannot be placed inside typeset equations}',
  669                      'HTML'     => $answer_rule
  670                    );
  671 
  672     $out;
  673 }
  674 sub tex_ans_rule_extension {
  675   my $len = shift;
  676   $len    = 20 unless $len ;
  677     my $name = NEW_ANS_NAME($$r_ans_rule_count);
  678     my $answer_rule = NAMED_ANS_RULE($name ,$len);  # we don't want to create three answer rules in different modes.
  679     my $out = MODES(
  680                      'TeX' => $answer_rule,
  681                      'Latex2HTML' => '\fbox{Answer boxes cannot be placed inside typeset equations}',
  682                      'HTML_tth' => '\\begin{rawhtml} '. $answer_rule.'\\end{rawhtml}',
  683                      'HTML_dpng' => '\fbox{Answer boxes cannot be placed inside typeset equations}',
  684                      'HTML'     => $answer_rule
  685                    );
  686 
  687     $out;
  688 }
  689 # still needs some cleanup.
  690 sub NAMED_TEX_ANS_RULE {
  691     my $name = shift;
  692   my $len = shift;
  693   $len    = 20 unless $len ;
  694     my $answer_rule = NAMED_ANS_RULE($name ,$len);  # we don't want to create three answer rules in different modes.
  695     my $out = MODES(
  696                      'TeX' => $answer_rule,
  697                      'Latex2HTML' => '\\fbox{Answer boxes cannot be placed inside typeset equations}',
  698                      'HTML_tth' => '\\begin{rawhtml} '. $answer_rule.'\\end{rawhtml}',
  699                      'HTML_dpng' => '\\fbox{Answer boxes cannot be placed inside typeset equations}',
  700                      'HTML'     => $answer_rule
  701                    );
  702 
  703     $out;
  704 }
  705 sub NAMED_TEX_ANS_RULE_EXTENSION {
  706   my $name = shift;
  707   my $len = shift;
  708   $len    = 20 unless $len ;
  709     my $answer_rule = NAMED_ANS_RULE_EXTENSION($name ,$len);  # we don't want to create three answer rules in different modes.
  710     my $out = MODES(
  711                      'TeX' => $answer_rule,
  712                      'Latex2HTML' => '\fbox{Answer boxes cannot be placed inside typeset equations}',
  713                      'HTML_tth' => '\\begin{rawhtml} '. $answer_rule.'\\end{rawhtml}',
  714                      'HTML_dpng' => '\fbox{Answer boxes cannot be placed inside typeset equations}',
  715                      'HTML'     => $answer_rule
  716                    );
  717 
  718     $out;
  719 }
  720 sub ans_box {
  721   my $row = shift;
  722   my $col =shift;
  723   $row = 5 unless $row;
  724   $col = 80 unless $col;
  725   my $name = NEW_ANS_NAME(inc_ans_rule_count());
  726   NAMED_ANS_BOX($name ,$row,$col);
  727 }
  728 
  729 #this is legacy code; use ans_checkbox instead
  730 sub checkbox {
  731   my %options = @_;
  732   qq!<INPUT TYPE="checkbox" NAME="$options{'-name'}" VALUE="$options{'-value'}">$options{'-label'}!
  733 }
  734 
  735 
  736 sub NAMED_POP_UP_LIST {
  737     my $name = shift;
  738   my @list = @_;
  739   $name = RECORD_ANS_NAME($name);   # record answer name
  740     my $answer_value = '';
  741   $answer_value = ${$inputs_ref}{$name} if defined(${$inputs_ref}{$name});
  742   my $out = "";
  743   if ($displayMode eq 'HTML' or $displayMode eq 'HTML_tth' or
  744             $displayMode eq 'HTML_dpng' or $displayMode eq 'HTML_img' or $displayMode eq 'HTML_jsMath' or $displayMode eq 'HTML_asciimath') {
  745     $out = qq!<SELECT NAME = "$name" SIZE=1> \n!;
  746     my $i;
  747     foreach ($i=0; $i< @list; $i=$i+2) {
  748       my $select_flag = ($list[$i] eq $answer_value) ? "SELECTED" : "";
  749       $out .= qq!<OPTION $select_flag VALUE ="$list[$i]" > $list[$i+1]  </OPTION>\n!;
  750     };
  751     $out .= " </SELECT>\n";
  752   } elsif ( $displayMode eq "Latex2HTML") {
  753     $out = qq! \\begin{rawhtml}<SELECT NAME = "$name" SIZE=1> \\end{rawhtml} \n !;
  754     my $i;
  755     foreach ($i=0; $i< @list; $i=$i+2) {
  756       my $select_flag = ($list[$i] eq $answer_value) ? "SELECTED" : "";
  757       $out .= qq!\\begin{rawhtml}<OPTION $select_flag VALUE ="$list[$i]" > $list[$i+1]  </OPTION>\\end{rawhtml}\n!;
  758     };
  759     $out .= " \\begin{rawhtml}</SELECT>\\end{rawhtml}\n";
  760   } elsif ( $displayMode eq "TeX") {
  761       $out .= "\\fbox{?}";
  762   }
  763 
  764 }
  765 
  766 sub pop_up_list {
  767   my @list = @_;
  768   my $name = NEW_ANS_NAME(inc_ans_rule_count());  # get new answer name
  769   NAMED_POP_UP_LIST($name, @list);
  770 }
  771 
  772 
  773 
  774 =head5  answer_matrix
  775 
  776     Usage   \[ \{   answer_matrix(rows,columns,width_of_ans_rule, @options) \} \]
  777 
  778     Creates an array of answer blanks and passes it to display_matrix which returns
  779     text which represents the matrix in TeX format used in math display mode. Answers
  780     are then passed back to whatever answer evaluators you write at the end of the problem.
  781     (note, if you have an m x n matrix, you will need mn answer evaluators, and they will be
  782     returned to the evaluaters starting in the top left hand corner and proceed to the left
  783     and then at the end moving down one row, just as you would read them.)
  784 
  785     The options are passed on to display_matrix.
  786 
  787 
  788 =cut
  789 
  790 
  791 sub answer_matrix{
  792   my $m = shift;
  793   my $n = shift;
  794   my $width = shift;
  795   my @options = @_;
  796   my @array=();
  797   for( my $i = 0; $i < $m; $i+=1)
  798   {
  799     my @row_array = ();
  800 
  801     for( my $i = 0; $i < $n; $i+=1)
  802     {
  803       push @row_array,  ans_rule($width);
  804     }
  805     my $r_row_array = \@row_array;
  806     push @array,  $r_row_array;
  807   }
  808   # display_matrix hasn't been loaded into the cache safe compartment
  809   # so we need to refer to the subroutine in this way to make
  810   # sure that main is defined correctly.
  811   my $ra_local_display_matrix=PG_restricted_eval(q!\&main::display_matrix!);
  812   &$ra_local_display_matrix( \@array, @options );
  813 
  814 }
  815 
  816 sub NAMED_ANS_ARRAY_EXTENSION{
  817 
  818   my $name = shift;
  819   my $col = shift;
  820   $col = 20 unless $col;
  821   my $answer_value = '';
  822 
  823   $answer_value = ${$inputs_ref}{$name} if    defined(${$inputs_ref}{$name});
  824   if ($answer_value =~ /\0/ ) {
  825     my @answers = split("\0", $answer_value);
  826     $answer_value = shift(@answers);
  827     $answer_value= '' unless defined($answer_value);
  828   } elsif (ref($answer_value) eq 'ARRAY') {
  829     my @answers = @{ $answer_value};
  830       $answer_value = shift(@answers);
  831         $answer_value= '' unless defined($answer_value);
  832   }
  833 
  834   $answer_value =~ tr/\\$@`//d;   ## make sure student answers can not be interpolated by e.g. EV3
  835   MODES(
  836     TeX => "\\mbox{\\parbox[t]{10pt}{\\hrulefill}}\\hrulefill\\quad ",
  837     Latex2HTML => qq!\\begin{rawhtml}\n<INPUT TYPE=TEXT SIZE=$col NAME=\"$name\" VALUE = \"\">\n\\end{rawhtml}\n!,
  838     HTML => "<INPUT TYPE=TEXT SIZE=$col NAME=\"$name\" VALUE = \"$answer_value\">\n"
  839   );
  840 }
  841 
  842 sub ans_array{
  843   my $m = shift;
  844   my $n = shift;
  845   my $col = shift;
  846   $col = 20 unless $col;
  847   my $num = inc_ans_rule_count() ;
  848   my $name = NEW_ANS_ARRAY_NAME($num,0,0);
  849   my @options = @_;
  850   my @array=();
  851   my $string;
  852   my $answer_value = "";
  853 
  854   $array[0][0] =   NAMED_ANS_RULE($name,$col);
  855 
  856   for( my $i = 1; $i < $n; $i+=1)
  857   {
  858     $name = NEW_ANS_ARRAY_NAME_EXTENSION($num,0,$i);
  859     $array[0][$i] =   NAMED_ANS_ARRAY_EXTENSION($name,$col);
  860 
  861   }
  862 
  863   for( my $j = 1; $j < $m; $j+=1 ){
  864 
  865     for( my $i = 0; $i < $n; $i+=1)
  866     {
  867       $name = NEW_ANS_ARRAY_NAME_EXTENSION($num,$j,$i);
  868       $array[$j][$i] =  NAMED_ANS_ARRAY_EXTENSION($name,$col);
  869 
  870     }
  871 
  872   }
  873   my $ra_local_display_matrix=PG_restricted_eval(q!\&main::display_matrix!);
  874   &$ra_local_display_matrix( \@array, @options );
  875 
  876 }
  877 
  878 sub ans_array_extension{
  879   my $m = shift;
  880   my $n = shift;
  881   my $col = shift;
  882   $col = 20 unless $col;
  883   my $num = PG_restricted_eval(q!$main::ans_rule_count!);
  884   my @options = @_;
  885   my $name;
  886   my @array=();
  887   my $string;
  888   my $answer_value = "";
  889 
  890   for( my $j = 0; $j < $m; $j+=1 ){
  891 
  892     for( my $i = 0; $i < $n; $i+=1)
  893     {
  894       $name = NEW_ANS_ARRAY_NAME_EXTENSION($num,$j,$i);
  895       $array[$j][$i] =  NAMED_ANS_ARRAY_EXTENSION($name,$col);
  896 
  897     }
  898 
  899   }
  900   my $ra_local_display_matrix=PG_restricted_eval(q!\&main::display_matrix!);
  901   &$ra_local_display_matrix( \@array, @options );
  902 
  903 }
  904 
  905 
  906 # end answer blank macros
  907 
  908 =head2 Hints and solutions macros
  909 
  910   solution('text','text2',...);
  911   SOLUTION('text','text2',...);   # equivalent to TEXT(solution(...));
  912 
  913   hint('text', 'text2', ...);
  914   HINT('text', 'text2',...);      # equivalent to TEXT("$BR$HINT" . hint(@_) . "$BR") if hint(@_);
  915 
  916 Solution prints its concatenated input when the check box named 'ShowSol' is set and
  917 the time is after the answer date.  The check box 'ShowSol' is visible only after the
  918 answer date or when the problem is viewed by a professor.
  919 
  920 $main::envir{'displaySolutionsQ'} is set to 1 when a solution is to be displayed.
  921 
  922 Hints are shown only after the number of attempts is greater than $:showHint
  923 ($main::showHint defaults to 1) and the check box named 'ShowHint' is set. The check box
  924 'ShowHint' is visible only after the number of attempts is greater than $main::showHint.
  925 
  926 $main::envir{'displayHintsQ'} is set to 1 when a hint is to be displayed.
  927 
  928 
  929 =cut
  930 
  931 
  932 
  933 #   solution prints its input when $displaySolutionsQ is set.
  934 #   use as TEXT(solution("blah, blah");
  935 #   \$solutionExists
  936 #   is passed to processProblem which displays a "show Solution" button
  937 #   when a solution is available for viewing
  938 
  939 
  940 sub solution {
  941   my @in = @_;
  942   my $out = '';
  943   PG_restricted_eval(q!$main::solutionExists =1!);
  944   if (PG_restricted_eval(q!$main::envir{'displaySolutionsQ'}!)) {$out = join(' ',@in);}
  945     $out;
  946 }
  947 
  948 
  949 sub SOLUTION {
  950   TEXT( solution(@_)) ;
  951 }
  952 
  953 
  954 
  955 sub hint {
  956     my @in = @_;
  957   my $out = '';
  958 
  959   PG_restricted_eval(q!$main::hintExists =1;
  960                          $main::numOfAttempts = 0 unless defined($main::numOfAttempts);
  961     !);
  962 
  963   if ($displayMode eq 'TeX')   {
  964     $out = '';  # do nothing since hints are not available for download
  965   } elsif (($envir->{'displayHintsQ'}) and
  966           PG_restricted_eval(q!($main::numOfAttempts >= $main::showHint)!))
  967 
  968    ## the second test above prevents a hint being shown if a doctored form is submitted
  969 
  970   {$out = join(' ',@in);}    # show hint
  971 
  972   $out ;
  973 }
  974 
  975 
  976 sub HINT {
  977     TEXT("$BR" . hint(@_) . "$BR") if hint(@_);
  978 }
  979 
  980 
  981 
  982 # End hints and solutions macros
  983 #################################
  984 
  985 # Produces a random number between $begin and $end with increment 1.
  986 # You do not have to worry about integer or floating point types.
  987 
  988 =head2 Pseudo-random number generator
  989 
  990   Usage:
  991   random(0,5,.1)      # produces a random number between 0 and 5 in increments of .1
  992   non_zero_random(0,5,.1) # gives a non-zero random number
  993 
  994   list_random(2,3,5,6,7,8,10) # produces random value from the list
  995   list_random(2,3, (5..8),10) # does the same thing
  996 
  997   SRAND(seed)     # resets the main random generator -- use very cautiously
  998 
  999 
 1000 SRAND(time) will create a different problem everytime it is called.  This makes it difficult
 1001 to check the answers :-).
 1002 
 1003 SRAND($envir->{'inputs_ref'}->{'key'} ) will create a different problem for each login session.
 1004 This is probably what is desired.
 1005 
 1006 =cut
 1007 
 1008 
 1009 sub random  {
 1010   my ($begin, $end, $incr) = @_;
 1011   $PG_random_generator->random($begin,$end,$incr);
 1012 }
 1013 
 1014 
 1015 sub non_zero_random { ##gives a non-zero random number
 1016   my (@arguments)=@_;
 1017   my $a=0;
 1018   my $i=100; #safety counter
 1019   while ($a==0 && ( 0 < $i-- ) ) {
 1020     $a=random(@arguments);
 1021   }
 1022   $a;
 1023 }
 1024 
 1025 sub list_random {
 1026         my(@li) = @_;
 1027         return $li[random(1,scalar(@li))-1];
 1028 }
 1029 
 1030 sub SRAND { # resets the main random generator -- use cautiously
 1031     my $seed = shift;
 1032   $PG_random_generator -> srand($seed);
 1033 }
 1034 
 1035 # display macros
 1036 
 1037 =head2 Display Macros
 1038 
 1039 These macros produce different output depending on the display mode being used to show
 1040 the problem on the screen, or whether the problem is being converted to TeX to produce
 1041 a hard copy output.
 1042 
 1043   MODES   ( TeX =>        "Output this in TeX mode",
 1044             HTML =>       "output this in HTML mode",
 1045             HTML_tth =>   "output this in HTML_tth mode",
 1046             HTML_dpng =>   "output this in HTML_dpng mode",
 1047             Latex2HTML => "output this in Latex2HTML mode",
 1048            )
 1049 
 1050   TEX     (tex_version, html_version) #obsolete
 1051 
 1052   M3      (tex_version, latex2html_version, html_version) #obsolete
 1053 
 1054 
 1055 
 1056 =cut
 1057 
 1058 
 1059 sub TEX {
 1060   my ($tex, $html ) = @_;
 1061   MODES(TeX => $tex, HTML => $html, HTML_tth => $html, HTML_dpng => $html);
 1062 }
 1063 
 1064 
 1065 sub M3 {
 1066   my($tex,$l2h,$html) = @_;
 1067   MODES(TeX => $tex, Latex2HTML => $l2h, HTML => $html, HTML_tth => $html, HTML_dpng => $html);
 1068 }
 1069 
 1070 # This replaces M3.  You can add new modes at will to this one.
 1071 
 1072 sub MODES {
 1073   my %options = @_;
 1074 
 1075   return $options{$displayMode} if defined $options{$displayMode};
 1076 
 1077   if (exists $envir{displayModeFailover}->{$displayMode}) {
 1078     my @backup_modes = @{$envir{displayModeFailover}->{$displayMode}};
 1079     foreach my $mode (@backup_modes) {
 1080       return $options{$mode} if defined $options{$mode};
 1081     }
 1082     die "ERROR in defining MODES: neither display mode $displayMode nor",
 1083       " any fallback modes (", join(", ", @backup_modes), ") defined.\n";
 1084   }
 1085 
 1086   die "ERROR in defining MODES: current display mode '$displayMode' not found. available modes: ",
 1087     join (", ", keys %{$envir{displayModeFailover}}), "\n";
 1088 
 1089   return $options{$displayMode}
 1090              if defined( $options{$displayMode} );
 1091 
 1092 =for comment
 1093 
 1094   # default searches.
 1095   if ($displayMode eq "Latex2HTML") {
 1096     return $options{TeX}
 1097              if defined( $options{TeX} );
 1098       return $options{HTML}
 1099              if defined( $options{HTML} );
 1100       die " ERROR in using MODES: 'HTML' and 'TeX' options not defined for 'Latex2HTML'";
 1101   }
 1102 
 1103   if ($displayMode eq "HTML_tth") {
 1104     return $options{HTML}
 1105              if defined( $options{HTML} );
 1106       die " ERROR in using MODES: 'HTML' option not defined for HTML_tth";
 1107 
 1108   }
 1109 
 1110   if ($displayMode eq "HTML_img") {
 1111     return $options{HTML_dpng} if defined $options{HTML_dpng};
 1112     return $options{HTML_tth} if defined $options{HTML_tth};
 1113     return $options{HTML}     if defined $options{HTML};
 1114     die " ERROR in using MODES: 'HTML' option not defined for HTML_img";
 1115   }
 1116 
 1117   if ($displayMode eq "HTML_dpng") {
 1118     return $options{HTML_tth}
 1119              if defined( $options{HTML_tth} );
 1120     return $options{HTML}
 1121              if defined( $options{HTML} );
 1122       die " ERROR in using MODES: 'HTML' option not defined for HTML_dpng";
 1123 
 1124   }
 1125 
 1126   if ($displayMode eq "HTML_jsMath") {
 1127     return $options{HTML_img}  if defined $options{HTML_img};
 1128     return $options{HTML_dpng} if defined $options{HTML_dpng};
 1129     return $options{HTML_tth}  if defined $options{HTML_tth};
 1130     return $options{HTML}      if defined $options{HTML};
 1131     die " ERROR in using MODES: 'HTML' option not defined for HTML_jsMath";
 1132   }
 1133 
 1134   if ($displayMode eq "HTML_asciimath") {
 1135 #   return $options{HTML_img}  if defined $options{HTML_img};
 1136 #   return $options{HTML_dpng} if defined $options{HTML_dpng};
 1137     return $options{HTML_tth}  if defined $options{HTML_tth};
 1138     return $options{HTML}      if defined $options{HTML};
 1139     die " ERROR in using MODES: 'HTML' option not defined for HTML_asciimath";
 1140   }
 1141 
 1142   # trap undefined errors
 1143   die "ERROR in defining MODES:  Can't find |$displayMode| among
 1144            available options:" . join(" ", keys(%options) )
 1145            . " file " . __FILE__ ." line " . __LINE__."\n\n";
 1146 
 1147 =cut
 1148 
 1149 }
 1150 
 1151 
 1152 # end display macros
 1153 
 1154 
 1155 =head2  Display constants
 1156 
 1157   @ALPHABET       ALPHABET()      capital letter alphabet -- ALPHABET[0] = 'A'
 1158   $PAR        PAR()       paragraph character (\par or <p>)
 1159   $BR             BR()        line break character
 1160   $LQ         LQ()        left double quote
 1161   $RQ         RQ()        right double quote
 1162   $BM         BM()        begin math
 1163   $EM         EM()        end math
 1164   $BDM        BDM()       begin display math
 1165   $EDM        EDM()       end display math
 1166   $LTS        LTS()       strictly less than
 1167   $GTS        GTS()       strictly greater than
 1168   $LTE        LTE()       less than or equal
 1169   $GTE        GTE()       greater than or equal
 1170   $BEGIN_ONE_COLUMN BEGIN_ONE_COLUMN()  begin one-column mode
 1171   $END_ONE_COLUMN   END_ONE_COLUMN()  end one-column mode
 1172   $SOL        SOLUTION_HEADING()  solution headline
 1173   $SOLUTION     SOLUTION_HEADING()  solution headline
 1174   $HINT       HINT_HEADING()    hint headline
 1175   $US         US()        underscore character
 1176   $SPACE        SPACE()       space character (tex and latex only)
 1177   $BBOLD        BBOLD()       begin bold typeface
 1178   $EBOLD        EBOLD()       end bold typeface
 1179   $BITALIC        BITALIC()       begin italic typeface
 1180   $EITALIC        EITALIC()       end italic typeface
 1181   $BCENTER        BCENTER()       begin centered environment
 1182   $ECENTER        ECENTER()       end centered environment
 1183   $HR         HR()        horizontal rule
 1184   $LBRACE       LBRACE()      left brace
 1185   $LB         LB ()       left brace
 1186   $RBRACE       RBRACE()      right brace
 1187   $RB         RB ()       right brace
 1188   $DOLLAR       DOLLAR()      a dollar sign
 1189   $PERCENT      PERCENT()     a percent sign
 1190   $CARET        CARET()       a caret sign
 1191   $PI         PI()        the number pi
 1192   $E          E()         the number e
 1193 
 1194 =cut
 1195 
 1196 
 1197 
 1198 
 1199 
 1200 # A utility variable.  Notice that "B"=$ALPHABET[1] and
 1201 # "ABCD"=@ALPHABET[0..3].
 1202 
 1203 sub ALPHABET  {
 1204   ('A'..'ZZ')[@_];
 1205 }
 1206 
 1207 ###############################################################
 1208 # Some constants which are different in tex and in HTML
 1209 # The order of arguments is TeX, Latex2HTML, HTML
 1210 # Adopted Davide Cervone's improvements to PAR, LTS, GTS, LTE, GTE, LBRACE, RBRACE, LB, RB. 7-14-03 AKP
 1211 sub PAR { MODES( TeX => '\\par ', Latex2HTML => '\\begin{rawhtml}<P>\\end{rawhtml}', HTML => '<P>'); };
 1212 sub BR { MODES( TeX => '\\par\\noindent ', Latex2HTML => '\\begin{rawhtml}<BR>\\end{rawhtml}', HTML => '<BR>'); };
 1213 # Alternate definition of BR which is slightly more flexible and gives more white space in printed output
 1214 # which looks better but kills more trees.
 1215 #sub BR { MODES( TeX => '\\\\', Latex2HTML => '\\begin{rawhtml}<BR>\\end{rawhtml}', HTML => '<BR>'); };
 1216 sub LQ { MODES( TeX => "``", Latex2HTML =>   '"',  HTML =>  '&quot;' ); };
 1217 sub RQ { MODES( TeX => "''", Latex2HTML =>   '"',   HTML =>  '&quot;' ); };
 1218 sub BM { MODES(TeX => '\\(', Latex2HTML => '\\(', HTML =>  ''); };  # begin math mode
 1219 sub EM { MODES(TeX => '\\)', Latex2HTML => '\\)', HTML => ''); };  # end math mode
 1220 sub BDM { MODES(TeX => '\\[', Latex2HTML =>   '\\[', HTML =>   '<P ALIGN=CENTER>'); };  #begin displayMath mode
 1221 sub EDM { MODES(TeX => '\\]',  Latex2HTML =>  '\\]', HTML => '</P>'); };              #end displayMath mode
 1222 sub LTS { MODES(TeX => '<', Latex2HTML => '\\lt ', HTML => '&lt;', HTML_tth => '<' ); };
 1223 sub GTS { MODES(TeX => '>', Latex2HTML => '\\gt ', HTML => '&gt;', HTML_tth => '>' ); };
 1224 sub LTE { MODES(TeX => '\\le ', Latex2HTML => '\\le ', HTML => '<U>&lt;</U>', HTML_tth => '\\le ' ); };
 1225 sub GTE { MODES(TeX => '\\ge ', Latex2HTML => '\\ge ', HTML => '<U>&gt;</U>', HTML_tth => '\\ge ' ); };
 1226 sub BEGIN_ONE_COLUMN { MODES(TeX => " \\end{multicols}\n",  Latex2HTML => " ", HTML =>   " "); };
 1227 sub END_ONE_COLUMN { MODES(TeX =>
 1228               " \\begin{multicols}{2}\n\\columnwidth=\\linewidth\n",
 1229                             Latex2HTML => ' ', HTML => ' ');
 1230 
 1231 };
 1232 sub SOLUTION_HEADING { MODES( TeX => '\\par {\\bf Solution:}',
 1233                  Latex2HTML => '\\par {\\bf Solution:}',
 1234                HTML =>  '<P><B>Solution:</B>');
 1235               };
 1236 sub HINT_HEADING { MODES( TeX => "\\par {\\bf Hint:}", Latex2HTML => "\\par {\\bf Hint:}", HTML => "<P><B>Hint:</B>"); };
 1237 sub US { MODES(TeX => '\\_', Latex2HTML => '\\_', HTML => '_');};  # underscore, e.g. file${US}name
 1238 sub SPACE { MODES(TeX => '\\ ',  Latex2HTML => '\\ ', HTML => '&nbsp;');};  # force a space in latex, doesn't force extra space in html
 1239 sub BBOLD { MODES(TeX => '{\\bf ',  Latex2HTML => '{\\bf ', HTML => '<B>'); };
 1240 sub EBOLD { MODES( TeX => '}', Latex2HTML =>  '}',HTML =>  '</B>'); };
 1241 sub BITALIC { MODES(TeX => '{\\it ',  Latex2HTML => '{\\it ', HTML => '<I>'); };
 1242 sub EITALIC { MODES(TeX => '} ',  Latex2HTML => '} ', HTML => '</I>'); };
 1243 sub BCENTER { MODES(TeX => '\\begin{center} ',  Latex2HTML => ' \\begin{rawhtml} <div align="center"> \\end{rawhtml} ', HTML => '<div align="center">'); };
 1244 sub ECENTER { MODES(TeX => '\\end{center} ',  Latex2HTML => ' \\begin{rawhtml} </div> \\end{rawhtml} ', HTML => '</div>'); };
 1245 sub HR { MODES(TeX => '\\par\\hrulefill\\par ', Latex2HTML => '\\begin{rawhtml} <HR> \\end{rawhtml}', HTML =>  '<HR>'); };
 1246 sub LBRACE { MODES( TeX => '\{', Latex2HTML =>   '\\lbrace',  HTML =>  '{' , HTML_tth=> '\\lbrace' ); };
 1247 sub RBRACE { MODES( TeX => '\}', Latex2HTML =>   '\\rbrace',  HTML =>  '}' , HTML_tth=> '\\rbrace',); };
 1248 sub LB { MODES( TeX => '\{', Latex2HTML =>   '\\lbrace',  HTML =>  '{' , HTML_tth=> '\\lbrace' ); };
 1249 sub RB { MODES( TeX => '\}', Latex2HTML =>   '\\rbrace',  HTML =>  '}' , HTML_tth=> '\\rbrace',); };
 1250 sub DOLLAR { MODES( TeX => '\\$', Latex2HTML => '\\$', HTML => '$' ); };
 1251 sub PERCENT { MODES( TeX => '\\%', Latex2HTML => '\\%', HTML => '%' ); };
 1252 sub CARET { MODES( TeX => '\\verb+^+', Latex2HTML => '\\verb+^+', HTML => '^' ); };
 1253 sub PI {4*atan2(1,1);};
 1254 sub E {exp(1);};
 1255 
 1256 ###############################################################
 1257 ## Evaluation macros
 1258 
 1259 
 1260 =head2 TEXT macros
 1261 
 1262   Usage:
 1263     TEXT(@text);
 1264 
 1265 This is the simplest way to print text from a problem.  The strings in the array C<@text> are concatenated
 1266 with spaces between them and printed out in the text of the problem.  The text is not processed in any other way.
 1267 C<TEXT> is defined in PG.pl.
 1268 
 1269   Usage:
 1270     BEGIN_TEXT
 1271       text.....
 1272     END_TEXT
 1273 
 1274 This is the most common way to enter text into the problem.  All of the text between BEGIN_TEXT and END_TEXT
 1275 is processed by the C<EV3> macro described below and then printed using the C<TEXT> command.  The two key words
 1276 must appear on lines by themselves.  The preprocessing that makes this construction work is done in F<PGtranslator.pm>.
 1277 See C<EV3> below for details on the processing.
 1278 
 1279 
 1280 =cut
 1281 
 1282 =head2 Evaluation macros
 1283 
 1284 =head3 EV3
 1285 
 1286         TEXT(EV3("This is a formulat \( \int_0^5 x^2 \, dx \) ");
 1287         TEXT(EV3(@text));
 1288 
 1289     TEXT(EV3(<<'END_TEXT'));
 1290       text stuff...
 1291     END_TEXT
 1292 
 1293 
 1294 The BEGIN_TEXT/END_TEXT construction is translated into the construction above by PGtranslator.pm.  END_TEXT must appear
 1295 on a line by itself and be left justified.  (The << construction is known as a "here document" in UNIX and in PERL.)
 1296 
 1297 The single quotes around END_TEXT mean that no automatic interpolation of variables takes place in the text.
 1298 Using EV3 with strings which have been evaluated by double quotes may lead to unexpected results.
 1299 
 1300 
 1301 The evaluation macro E3 first evaluates perl code inside the braces:  C<\{  code \}>.
 1302 Any perl statment can be put inside the braces.  The
 1303 result of the evaluation (i.e. the last statement evaluated) replaces the C<\{ code \}> construction.
 1304 
 1305 Next interpolation of all variables (e.g. C<$var or @array> ) is performed.
 1306 
 1307 Then mathematical formulas in TeX are evaluated within the
 1308 C<\(  tex math mode \)> and
 1309 C<\[ tex display math mode \] >
 1310 constructions, in that order:
 1311 
 1312 =head3 FEQ
 1313 
 1314   FEQ($string);   # processes and outputs the string
 1315 
 1316 
 1317 The mathematical formulas are run through the macro C<FEQ> (Format EQuations) which performs
 1318 several substitutions (see below).
 1319 In C<HTML_tth> mode the resulting code is processed by tth to obtain an HTML version
 1320 of the formula. (In the future processing by WebEQ may be added here as another option.)
 1321 The Latex2HTML mode does nothing
 1322 at this stage; it creates the entire problem before running it through
 1323 TeX and creating the GIF images of the equations.
 1324 
 1325 The resulting string is output (and usually fed into TEXT to be printed in the problem).
 1326 
 1327   Usage:
 1328 
 1329     $string2 = FEQ($string1);
 1330 
 1331 This is a filter which is used to format equations by C<EV2> and C<EV3>, but can also be used on its own.  It is best
 1332 understood with an example.
 1333 
 1334     $string1 = "${a}x^2 + ${b}x + {$c:%.1f}"; $a = 3;, $b = -2; $c = -7.345;
 1335 
 1336 when interpolated becomes:
 1337 
 1338     $string1 = '3x^2 + -2x + {-7.345:%0.1f}
 1339 
 1340 FEQ first changes the number of decimal places displayed, so that the last term becomes -7.3 Then it removes the
 1341 extraneous plus and minus signs, so that the final result is what you want:
 1342 
 1343     $string2 = '3x^2 - 2x -7.3';
 1344 
 1345 (The %0.1f construction
 1346 is the same formatting convention used by Perl and nearly identical to the one used by the C printf statement. Some common
 1347 usage:  %0.3f 3 decimal places, fixed notation; %0.3e 3 significant figures exponential notation; %0.3g uses either fixed
 1348 or exponential notation depending on the size of the number.)
 1349 
 1350 Two additional legacy formatting constructions are also supported:
 1351 
 1352 C<?{$c:%0.3f} > will give a number with 3 decimal places and a negative
 1353 sign if the number is negative, no sign if the number is positive.
 1354 
 1355 C<!{$c:%0.3f}> determines the sign and prints it
 1356 whether the number is positive or negative.
 1357 
 1358 =head3 EV2
 1359 
 1360     TEXT(EV2(@text));
 1361 
 1362     TEXT(EV2(<<END_OF_TEXT));
 1363       text stuff...
 1364     END_OF_TEXT
 1365 
 1366 This is a precursor to EV3.  In this case the constants are interpolated first, before the evaluation of the \{ ...code...\}
 1367 construct. This can lead to unexpected results.  For example C<\{ join(" ", @text) \}> with C<@text = ("Hello","World");> becomes,
 1368 after interpolation, C<\{ join(" ",Hello World) \}> which then causes an error when evaluated because Hello is a bare word.
 1369 C<EV2> can still be useful if you allow for this, and in particular it works on double quoted strings, which lead to
 1370 unexpected results with C<EV3>. Using single quoted strings with C<EV2> may lead to unexpected results.
 1371 
 1372 The unexpected results have to do with the number of times backslashed constructions have to be escaped. It is quite messy.  For
 1373 more details get a good Perl book and then read the code. :-)
 1374 
 1375 
 1376 
 1377 
 1378 =cut
 1379 
 1380 
 1381 sub ev_substring {
 1382     my $string      = shift;
 1383   my $start_delim = shift;
 1384   my $end_delim   = shift;
 1385   my $actionRef   = shift;
 1386   my ($eval_out,$PG_eval_errors,$PG_full_error_report)=();
 1387     my $out = "";
 1388                 #
 1389                 #  DPVC -- 2001/12/07
 1390                 #     original "while ($string)" fails to process the string "0" correctly
 1391                 #
 1392     while ($string ne "") {
 1393                 #
 1394                 #  end DPVC
 1395                 #
 1396         if ($string =~ /\Q$start_delim\E/s) {
 1397        #print "$start_delim $end_delim evaluating_substring=$string<BR>";
 1398         $string =~ s/^(.*?)\Q$start_delim\E//s;  # get string up to next \{ ---treats string as a single line, ignoring returns
 1399         $out .= $1;
 1400        #print "$start_delim $end_delim substring_out=$out<BR>";
 1401         $string =~ s/^(.*?)\Q$end_delim\E//s;  # get perl code up to \} ---treats string as a single line,  ignoring returns
 1402            #print "$start_delim $end_delim evaluate_string=$1<BR>";
 1403         ($eval_out,$PG_eval_errors,$PG_full_error_report) = &$actionRef($1);
 1404         $eval_out = "$start_delim $eval_out $end_delim" if $PG_full_error_report;
 1405         $out = $out . $eval_out;
 1406        #print "$start_delim $end_delim new substring_out=$out<BR><p><BR>";
 1407         $out .="$PAR ERROR $0 in ev_substring, PGbasicmacros.pl:$PAR <PRE>  $@ </PRE>$PAR" if $@;
 1408         }
 1409       else {
 1410         $out .= $string;  # flush the last part of the string
 1411         last;
 1412         }
 1413 
 1414       }
 1415   $out;
 1416 }
 1417 sub  safe_ev {
 1418     my ($out,$PG_eval_errors,$PG_full_error_report) = &old_safe_ev;   # process input by old_safe_ev first
 1419     $out =~s/\\/\\\\/g;   # protect any new backslashes introduced.
 1420   ($out,$PG_eval_errors,$PG_full_error_report)
 1421 }
 1422 
 1423 sub  old_safe_ev {
 1424     my $in = shift;
 1425     my   ($out,$PG_eval_errors,$PG_full_error_report) = PG_restricted_eval("$in;");
 1426     # the addition of the ; seems to provide better error reporting
 1427     if ($PG_eval_errors) {
 1428       my @errorLines = split("\n",$PG_eval_errors);
 1429     #$out = "<PRE>$PAR % ERROR in $0:old_safe_ev, PGbasicmacros.pl: $PAR % There is an error occuring inside evaluation brackets \\{ ...code... \\} $BR % somewhere in an EV2 or EV3 or BEGIN_TEXT block. $BR % Code evaluated:$BR $in $BR % $BR % $errorLines[0]\n % $errorLines[1]$BR % $BR % $BR </PRE> ";
 1430     warn " ERROR in old_safe_ev, PGbasicmacros.pl: <PRE>
 1431      ## There is an error occuring inside evaluation brackets \\{ ...code... \\}
 1432      ## somewhere in an EV2 or EV3 or BEGIN_TEXT block.
 1433      ## Code evaluated:
 1434      ## $in
 1435      ##" .join("\n     ", @errorLines). "
 1436      ##</PRE>$BR
 1437      ";
 1438      $out ="$PAR $BBOLD  $in $EBOLD $PAR";
 1439 
 1440 
 1441   }
 1442 
 1443   ($out,$PG_eval_errors,$PG_full_error_report);
 1444 }
 1445 
 1446 sub FEQ   {    # Format EQuations
 1447   my $in = shift;
 1448    # formatting numbers -- the ?{} and !{} constructions
 1449   $in =~s/\?\s*\{([.\-\$\w\d]+):?([%.\da-z]*)\}/${ \( &sspf($1,$2) )}/g;
 1450   $in =~s/\!\s*\{([.\-\$\w\d]+):?([%.\da-z]*)\}/${ \( &spf($1,$2) )}/g;
 1451 
 1452   # more formatting numbers -- {number:format} constructions
 1453   $in =~ s/\{(\s*[\+\-\d\.]+[eE]*[\+\-]*\d*):(\%\d*.\d*\w)}/${ \( &spf($1,$2) )}/g;
 1454   $in =~ s/\+\s*\-/ - /g;
 1455   $in =~ s/\-\s*\+/ - /g;
 1456   $in =~ s/\+\s*\+/ + /g;
 1457   $in =~ s/\-\s*\-/ + /g;
 1458   $in;
 1459 }
 1460 
 1461 #sub math_ev3 {
 1462 # my $in = shift; #print "in=$in<BR>";
 1463 # my ($out,$PG_eval_errors,$PG_full_error_report);
 1464 # $in = FEQ($in);
 1465 # $in =~ s/%/\\%/g;   #  % causes trouble in TeX and HTML_tth it usually (always?) indicates an error, not comment
 1466 # return("$BM $in $EM") unless ($displayMode eq 'HTML_tth');
 1467 # $in = "\\(" . $in . "\\)";
 1468 # $out = tth($in);
 1469 # ($out,$PG_eval_errors,$PG_full_error_report);
 1470 #
 1471 #}
 1472 #
 1473 #sub display_math_ev3 {
 1474 # my $in = shift; #print "in=$in<BR>";
 1475 # my ($out,$PG_eval_errors,$PG_full_error_report);
 1476 # $in = FEQ($in);
 1477 # $in =~ s/%/\\%/g;
 1478 # return("$main::BDM $in $main::EDM") unless $displayMode eq 'HTML_tth' ;
 1479 # $in = "\\[" . $in . "\\]";
 1480 # $out =tth($in);
 1481 # ($out,$PG_eval_errors,$PG_full_error_report);
 1482 #}
 1483 
 1484 sub math_ev3 {
 1485   my $in = shift;
 1486   return general_math_ev3($in, "inline");
 1487 }
 1488 
 1489 sub display_math_ev3 {
 1490   my $in = shift;
 1491   return general_math_ev3($in, "display");
 1492 }
 1493 
 1494 sub general_math_ev3 {
 1495   my $in = shift;
 1496   my $mode = shift || "inline";
 1497 
 1498   $in = FEQ($in); # Format EQuations
 1499   $in =~ s/%/\\%/g; # avoid % becoming TeX comments
 1500 
 1501   ## remove leading and trailing spaces so that HTML mode will
 1502   ## not include unwanted spaces as per Davide Cervone.
 1503   $in =~ s/^\s+//;
 1504   $in =~ s/\s+$//;
 1505   ## If it ends with a backslash, there should be another space
 1506   ## at the end
 1507   if($in =~ /\\$/) { $in .= ' ';}
 1508 
 1509   # some modes want the delimiters, some don't
 1510   my $in_delim = $mode eq "inline"
 1511     ? "\\($in\\)"
 1512     : "\\[$in\\]";
 1513 
 1514   my $out;
 1515   if($displayMode eq "HTML_tth") {
 1516     $out = tth($in_delim);
 1517     ## remove leading and trailing spaces as per Davide Cervone.
 1518     $in =~ s/^\s+//;
 1519     $in =~ s/\s+$//;
 1520   } elsif ($displayMode eq "HTML_dpng") {
 1521     # for jj's version of ImageGenerator
 1522     $out = $envir->{'imagegen'}->add($in_delim);
 1523     # for my version of ImageGenerator
 1524     #$out = $envir->{'imagegen'}->add($in, $mode);
 1525   } elsif ($displayMode eq "HTML_img") {
 1526     $out = math2img($in, $mode);
 1527   } elsif ($displayMode eq "HTML_jsMath") {
 1528     $in =~ s/</&lt;/g; $in =~ s/>/&gt;/g;
 1529     $out = '<SPAN CLASS="math">'.$in.'</SPAN>' if $mode eq "inline";
 1530     $out = '<DIV CLASS="math">'.$in.'</DIV>' if $mode eq "display";
 1531   } elsif ($displayMode eq "HTML_asciimath") {
 1532     $out = "`$in`" if $mode eq "inline";
 1533     $out = '<DIV ALIGN="CENTER">`'.$in.'`</DIV>' if $mode eq "display";
 1534   } else {
 1535     $out = "\\($in\\)" if $mode eq "inline";
 1536     $out = "\\[$in\\]" if $mode eq "display";
 1537   }
 1538   return $out;
 1539 }
 1540 
 1541 sub EV2 {
 1542   my $string = join(" ",@_);
 1543   # evaluate code inside of \{  \}  (no nesting allowed)
 1544     $string = ev_substring($string,"\\{","\\}",\&old_safe_ev);
 1545     $string = ev_substring($string,"\\<","\\>",\&old_safe_ev);
 1546   $string = ev_substring($string,"\\(","\\)",\&math_ev3);
 1547   $string = ev_substring($string,"\\[","\\]",\&display_math_ev3);
 1548   # macros for displaying math
 1549   $string =~ s/\\\(/$BM/g;
 1550   $string =~ s/\\\)/$EM/g;
 1551   $string =~ s/\\\[/$BDM/g;
 1552   $string =~ s/\\\]/$EDM/g;
 1553   $string;
 1554 }
 1555 
 1556 sub EV3{
 1557   my $string = join(" ",@_);
 1558   # evaluate code inside of \{  \}  (no nesting allowed)
 1559     $string = ev_substring($string,"\\\\{","\\\\}",\&safe_ev);  # handles \{ \} in single quoted strings of PG files
 1560   # interpolate variables
 1561   my ($evaluated_string,$PG_eval_errors,$PG_full_errors) = PG_restricted_eval("<<END_OF_EVALUATION_STRING\n$string\nEND_OF_EVALUATION_STRING\n");
 1562   if ($PG_eval_errors) {
 1563       my @errorLines = split("\n",$PG_eval_errors);
 1564       $string =~ s/</&lt;/g; $string =~ s/>/&gt;/g;
 1565     $evaluated_string = "<PRE>$PAR % ERROR in $0:EV3, PGbasicmacros.pl: $PAR % There is an error occuring in the following code:$BR $string $BR % $BR % $errorLines[0]\n % $errorLines[1]$BR % $BR % $BR </PRE> ";
 1566     $@="";
 1567   }
 1568   $string = $evaluated_string;
 1569   $string = ev_substring($string,"\\(","\\)",\&math_ev3);
 1570     $string = ev_substring($string,"\\[","\\]",\&display_math_ev3);
 1571   $string;
 1572 }
 1573 
 1574 sub EV4{
 1575     if ($displayMode eq "HTML_dpng") {
 1576         my $string = join(" ",@_);
 1577         my ($evaluated_string,$PG_eval_errors,$PG_full_errors) = PG_restricted_eval("<<END_OF_EVALUATION_STRING\n$string\nEND_OF_EVALUATION_STRING\n");
 1578         if ($PG_eval_errors) {
 1579             my @errorLines = split("\n",$PG_eval_errors);
 1580             $string =~ s/</&lt;/g; $string =~ s/>/&gt;/g;
 1581             $evaluated_string = "<PRE>$PAR % ERROR in $0:EV3, PGbasicmacros.pl:".
 1582       "$PAR % There is an error occuring in the following code:$BR ".
 1583       "$string $BR % $BR % $errorLines[0]\n % $errorLines[1]$BR ".
 1584       "% $BR % $BR </PRE> ";
 1585         }
 1586         $string = $evaluated_string;
 1587         $string = $envir{'imagegen'}->add($string);
 1588         $string;
 1589     } else {
 1590       EV3(@_);
 1591     }
 1592 }
 1593 
 1594 
 1595 =head2 Formatting macros
 1596 
 1597   beginproblem()  # generates text listing number and the point value of
 1598                   # the problem. It will also print the file name containing
 1599                   # the problem for users listed in the PRINT_FILE_NAMES_FOR PG_environment
 1600                   # variable.
 1601   OL(@array)      # formats the array as an Ordered List ( <OL> </OL> ) enumerated by letters.
 1602 
 1603   htmlLink($url, $text)
 1604                   # Places a reference to the URL with the specified text in the problem.
 1605                   # A common usage is \{ htmlLink(alias('prob1_help.html') \}, 'for help')
 1606                   # where alias finds the full address of the prob1_help.html file in the same directory
 1607                   # as the problem file
 1608   appletLink($url, $parameters)
 1609                   # For example
 1610                   # appletLink(q!  archive="http: //webwork.math.rochester.edu/gage/xFunctions/xFunctions.zip"
 1611                                   code="xFunctionsLauncher.class"  width=100 height=14!,
 1612                   " parameter text goes here")
 1613                   # will link to xFunctions.
 1614 
 1615   low level:
 1616 
 1617   spf($number, $format)   # prints the number with the given format
 1618   sspf($number, $format)  # prints the number with the given format, always including a sign.
 1619   nicestring($coefficients, $terms) # print a linear combinations of terms using coefficients
 1620   nicestring($coefficients) # uses the coefficients to make a polynomial
 1621       # For example
 1622       # nicestring([1,-2, 0]) produces 'x^2-2x'
 1623       # nicestring([2,0,-1],['', 't', 't^2']) produces '2-t^2'
 1624   protect_underbar($string) # protects the underbar (class_name) in strings which may have to pass through TeX.
 1625 
 1626 =cut
 1627 
 1628 sub beginproblem {
 1629   my $out = "";
 1630   my $problemValue = $envir->{problemValue};
 1631   my $fileName     = $envir->{fileName};
 1632   my $probNum      = $envir->{probNum};
 1633     my $TeXFileName = protect_underbar($envir->{fileName});
 1634     my $l2hFileName = protect_underbar($envir->{fileName});
 1635   my %inlist;
 1636   my $points ='pts';
 1637 
 1638   $points = 'pt' if $problemValue == 1;
 1639   ##    Prepare header for the problem
 1640   grep($inlist{$_}++,@{ $envir->{'PRINT_FILE_NAMES_FOR'} });
 1641   if ( defined($inlist{$envir->{studentLogin}}) and ($inlist{$envir->{studentLogin}} > 0) ) {
 1642     $out = &M3("\n\n\\medskip\\hrule\\smallskip\\par{\\bf ${probNum}.{\\footnotesize ($problemValue $points) $TeXFileName}}\\newline ",
 1643     " \\begin{rawhtml} ($problemValue $points) <B>$l2hFileName</B><BR>\\end{rawhtml}",
 1644      "($problemValue $points) <B>$fileName</B><BR>"
 1645        ) if ($problemValue ne "");
 1646   } else {
 1647     $out = &M3("\n\n\\smallskip\\hrule\\smallskip\\par{\\bf ${probNum}.}($problemValue $points) ",
 1648     "($problemValue $points) ",
 1649      "($problemValue $points) "
 1650        ) if ($problemValue ne "");
 1651   }
 1652   $out;
 1653 
 1654 }
 1655 
 1656 sub nicestring {
 1657     my($thingy) = shift;
 1658     my(@coefs) = @{$thingy};
 1659     my $n = scalar(@coefs);
 1660     $thingy = shift;
 1661     my(@others);
 1662     if(defined($thingy)) {
 1663   @others = @{$thingy};
 1664     } else {
 1665   my($j);
 1666   for $j (1..($n-2)) {
 1667       $others[$j-1] = "x^".($n-$j);
 1668   }
 1669   if($n>=2) { $others[$n-2] = "x";}
 1670   $others[$n-1] = "";
 1671     }
 1672     my($j, $k)=(0,0);
 1673     while(($k<$n) && ($coefs[$k]==0)) {$k++;}
 1674     if($k==$n) {return("0");}
 1675     my $ans;
 1676     if($coefs[$k]==1) {$ans = ($others[$k]) ? "$others[$k]" : "1";}
 1677     elsif($coefs[$k]== -1) {$ans =  ($others[$k]) ? "- $others[$k]" : "-1"}
 1678     else { $ans = "$coefs[$k] $others[$k]";}
 1679     $k++;
 1680     for $j ($k..($n-1)) {
 1681   if($coefs[$j] != 0) {
 1682       if($coefs[$j] == 1) {
 1683     $ans .= ($others[$j]) ? "+ $others[$j]" : "+ 1";
 1684       } elsif($coefs[$j] == -1) {
 1685     $ans .= ($others[$j]) ? "- $others[$j]" : "-1";
 1686       } else {
 1687     $ans .= "+ $coefs[$j] $others[$j]";
 1688       }
 1689   }
 1690     }
 1691     return($ans);
 1692 }
 1693 
 1694 # kludge to clean up path names
 1695             ## allow underscore character in set and section names and also allows line breaks at /
 1696 sub protect_underbar {
 1697     my $in = shift;
 1698     if ($displayMode eq 'TeX')  {
 1699 
 1700         $in =~ s|_|\\\_|g;
 1701         $in =~ s|/|\\\-/|g;  # allows an optional hyphenation of the path (in tex)
 1702     }
 1703     $in;
 1704 }
 1705 
 1706 
 1707 # An example of a macro which prints out a list (with letters)
 1708 sub OL {
 1709   my(@array) = @_;
 1710   my $i = 0;
 1711   my  $out=   &M3(
 1712           "\\begin{enumerate}\n",
 1713           " \\begin{rawhtml} <OL TYPE=\"A\" VALUE=\"1\"> \\end{rawhtml} ",
 1714           "<OL TYPE=\"A\" VALUE=\"1\">\n"
 1715           ) ;
 1716   my $elem;
 1717   foreach $elem (@array) {
 1718                 $out .= MODES(
 1719                         TeX=>   "\\item[$ALPHABET[$i].] $elem\n",
 1720                         Latex2HTML=>    " \\begin{rawhtml} <LI> \\end{rawhtml} $elem  ",
 1721                         HTML=>  "<LI> $elem\n",
 1722                         HTML_dpng=>     "<LI> $elem <br /> <br /> \n"
 1723                                         );
 1724     $i++;
 1725   }
 1726   $out .= &M3(
 1727         "\\end{enumerate}\n",
 1728         " \\begin{rawhtml} </OL>\n \\end{rawhtml} ",
 1729         "</OL>\n"
 1730         ) ;
 1731 }
 1732 
 1733 sub htmlLink {
 1734   my $url = shift;
 1735   my $text = shift;
 1736   my $options = shift;
 1737   $options = "" unless defined($options);
 1738   return "$BBOLD\[ broken link:  $text \] $EBOLD" unless defined($url);
 1739   M3( "{\\bf \\underline{$text}  }",
 1740       "\\begin{rawhtml} <A HREF=\"$url\" $options> $text </A>\\end{rawhtml}",
 1741       "<A HREF=\"$url\" $options> $text </A>"
 1742       );
 1743 }
 1744 
 1745 
 1746 sub helpLink {
 1747   my $type1 = shift;
 1748   return "" if(not defined($envir{'localHelpURL'}));
 1749   my $type = lc($type1);
 1750   my %typeHash = (
 1751     'interval notation' => 'IntervalNotation.html',
 1752     'units' => 'Units.html',
 1753     );
 1754 
 1755   my $infoRef = $typeHash{$type};
 1756   return htmlLink( $envir{'localHelpURL'}.$infoRef, $type1,
 1757 'target="ww_help" onclick="window.open(this.href,this.target,\'width=550,height=350,scrollbars=yes,resizable=on\'); return false;"');
 1758 }
 1759 
 1760 sub appletLink {
 1761   my $url = shift;
 1762   my $options = shift;
 1763   $options = "" unless defined($options);
 1764   M3( "{\\bf \\underline{APPLET}  }",
 1765       "\\begin{rawhtml} <APPLET $url> $options </APPLET>\\end{rawhtml}",
 1766       "<APPLET $url> $options </APPLET>"
 1767       );
 1768 }
 1769 sub spf {
 1770   my($number,$format) = @_;  # attention, the order of format and number are reversed
 1771   $format = "%4.3g" unless $format;   # default value for format
 1772   sprintf($format, $number);
 1773   }
 1774 sub sspf {
 1775   my($number,$format) = @_;  # attention, the order of format and number are reversed
 1776   $format = "%4.3g" unless $format;   # default value for format
 1777   my $sign = $number>=0 ? " + " : " - ";
 1778   $number = $number>=0 ? $number : -$number;
 1779   $sign .sprintf($format, $number);
 1780   }
 1781 
 1782 =head2  Sorting and other list macros
 1783 
 1784 
 1785 
 1786   Usage:
 1787   lex_sort(@list);   # outputs list in lexigraphic (alphabetical) order
 1788   num_sort(@list);   # outputs list in numerical order
 1789   uniq( @list);      # outputs a list with no duplicates.  Order is unspecified.
 1790 
 1791   PGsort( \&sort_subroutine, @list);
 1792   # &sort_subroutine defines order. It's output must be 1 or 0 (true or false)
 1793 
 1794 =cut
 1795 
 1796 #  uniq gives unique elements of a list:
 1797  sub uniq {
 1798    my (@in) =@_;
 1799    my %temp = ();
 1800    while (@in) {
 1801           $temp{shift(@in)}++;
 1802       }
 1803    my @out =  keys %temp;  # sort is causing trouble with Safe.??
 1804    @out;
 1805 }
 1806 
 1807 sub lex_sort {
 1808   PGsort sub {$_[0] lt $_[1]}, @_;
 1809 }
 1810 sub num_sort {
 1811   PGsort sub {$_[0] < $_[1]}, @_;
 1812 }
 1813 
 1814 
 1815 =head2 Macros for handling tables
 1816 
 1817   Usage:
 1818   begintable( number_of_columns_in_table)
 1819   row(@dataelements)
 1820   endtable()
 1821 
 1822 Example of useage:
 1823 
 1824   BEGIN_TEXT
 1825     This problem tests calculating new functions from old ones:$BR
 1826     From the table below calculate the quantities asked for:$BR
 1827     \{begintable(scalar(@firstrow)+1)\}
 1828     \{row(" \(x\) ",@firstrow)\}
 1829     \{row(" \(f(x)\) ", @secondrow)\}
 1830     \{row(" \(g(x)\) ", @thirdrow)\}
 1831     \{row(" \(f'(x)\) ", @fourthrow)\}
 1832     \{row(" \(g'(x)\) ", @fifthrow)\}
 1833     \{endtable()\}
 1834 
 1835    (The arrays contain numbers which are placed in the table.)
 1836 
 1837   END_TEXT
 1838 
 1839 =cut
 1840 
 1841 sub begintable {
 1842   my ($number)=shift;   #number of columns in table
 1843   my %options = @_;
 1844   warn "begintable(cols) requires a number indicating the number of columns" unless defined($number);
 1845   my $out = "";
 1846   if ($displayMode eq 'TeX') {
 1847     $out .= "\n\\par\\smallskip\\begin{center}\\begin{tabular}{"  .  "|c" x $number .  "|} \\hline\n";
 1848     }
 1849   elsif ($displayMode eq 'Latex2HTML') {
 1850     $out .= "\n\\begin{rawhtml} <TABLE , BORDER=1>\n\\end{rawhtml}";
 1851     }
 1852   elsif ($displayMode eq 'HTML' || $displayMode eq 'HTML_tth' || $displayMode eq 'HTML_dpng' || $displayMode eq 'HTML_img' || $displayMode eq 'HTML_jsMath' || $displayMode eq 'HTML_asciimath') {
 1853     $out .= "<TABLE BORDER=1>\n"
 1854   }
 1855   else {
 1856     $out = "Error: PGbasicmacros: begintable: Unknown displayMode: $displayMode.\n";
 1857     }
 1858   $out;
 1859   }
 1860 
 1861 sub endtable {
 1862   my $out = "";
 1863   if ($displayMode eq 'TeX') {
 1864     $out .= "\n\\end {tabular}\\end{center}\\par\\smallskip\n";
 1865     }
 1866   elsif ($displayMode eq 'Latex2HTML') {
 1867     $out .= "\n\\begin{rawhtml} </TABLE >\n\\end{rawhtml}";
 1868     }
 1869   elsif ($displayMode eq 'HTML' || $displayMode eq 'HTML_tth' || $displayMode eq 'HTML_dpng' ||$displayMode eq 'HTML_img' || $displayMode eq 'HTML_jsMath' || $displayMode eq 'HTML_asciimath') {
 1870     $out .= "</TABLE>\n";
 1871     }
 1872   else {
 1873     $out = "Error: PGbasicmacros: endtable: Unknown displayMode: $displayMode.\n";
 1874     }
 1875   $out;
 1876   }
 1877 
 1878 
 1879 sub row {
 1880   my @elements = @_;
 1881   my $out = "";
 1882   if ($displayMode eq 'TeX') {
 1883     while (@elements) {
 1884       $out .= shift(@elements) . " &";
 1885       }
 1886      chop($out); # remove last &
 1887      $out .= "\\\\ \\hline \n";
 1888      # carriage returns must be added manually for tex
 1889     }
 1890   elsif ($displayMode eq 'Latex2HTML') {
 1891     $out .= "\n\\begin{rawhtml}\n<TR>\n\\end{rawhtml}\n";
 1892     while (@elements) {
 1893       $out .= " \n\\begin{rawhtml}\n<TD> \n\\end{rawhtml}\n" . shift(@elements) . " \n\\begin{rawhtml}\n</TD> \n\\end{rawhtml}\n";
 1894       }
 1895     $out .= " \n\\begin{rawhtml}\n</TR> \n\\end{rawhtml}\n";
 1896   }
 1897   elsif ($displayMode eq 'HTML' || $displayMode eq 'HTML_tth' || $displayMode eq 'HTML_dpng'||$displayMode eq 'HTML_img' || $displayMode eq 'HTML_jsMath' || $displayMode eq 'HTML_asciimath') {
 1898     $out .= "<TR>\n";
 1899     while (@elements) {
 1900       $out .= "<TD>" . shift(@elements) . "</TD>";
 1901       }
 1902     $out .= "\n</TR>\n";
 1903   }
 1904   else {
 1905     $out = "Error: PGbasicmacros: row: Unknown displayMode: $displayMode.\n";
 1906     }
 1907   $out;
 1908 }
 1909 
 1910 =head2 Macros for displaying static images
 1911 
 1912   Usage:
 1913   $string = image($image, width => 100, height => 100, tex_size => 800)
 1914   $string = image([$image1, $image2], width => 100, height => 100, tex_size => 800)
 1915   $string = caption($string);
 1916   $string = imageRow([$image1, $image2 ], [$caption1, $caption2]);
 1917            # produces a complete table with rows of pictures.
 1918 
 1919 
 1920 =cut
 1921 
 1922 #   More advanced macros
 1923 sub image {
 1924   my $image_ref  = shift;
 1925   my @opt = @_;
 1926   unless (scalar(@opt) % 2 == 0 ) {
 1927     warn "ERROR in image macro.  A list of macros must be inclosed in square brackets.";
 1928   }
 1929   my %in_options = @opt;
 1930   my %known_options = (
 1931     width    => 100,
 1932     height   => 100,
 1933     tex_size => 800,
 1934   );
 1935   # handle options
 1936   my %out_options = %known_options;
 1937   foreach my $opt_name (keys %in_options) {
 1938     if ( exists( $known_options{$opt_name} ) ) {
 1939       $out_options{$opt_name} = $in_options{$opt_name} if exists( $in_options{$opt_name} ) ;
 1940     } else {
 1941       die "Option $opt_name not defined for image. " .
 1942           "Default options are:<BR> ", display_options2(%known_options);
 1943     }
 1944   }
 1945   my $width       = $out_options{width};
 1946   my $height      = $out_options{height};
 1947   my $tex_size    = $out_options{tex_size};
 1948   my $width_ratio = $tex_size*(.001);
 1949   my @image_list  = ();
 1950 
 1951   if (ref($image_ref) =~ /ARRAY/ ) {
 1952     @image_list = @{$image_ref};
 1953   } else {
 1954     push(@image_list,$image_ref);
 1955   }
 1956 
 1957   my @output_list = ();
 1958     while(@image_list) {
 1959     my $imageURL = alias(shift @image_list);
 1960     my $out="";
 1961 
 1962     if ($displayMode eq 'TeX') {
 1963       my $imagePath = $imageURL; # in TeX mode, alias gives us a path, not a URL
 1964       if (defined $envir->{texDisposition} and $envir->{texDisposition} eq "pdf") {
 1965         # We're going to create PDF files with our TeX (using pdflatex), so
 1966         # alias should have given us the path to a PNG image. What we need
 1967         # to do is find out the dimmensions of this image, since pdflatex
 1968         # is too dumb to live.
 1969 
 1970         #my ($height, $width) = getImageDimmensions($imagePath);
 1971         ##warn "&image: $imagePath $height $width\n";
 1972         #unless ($height and $width) {
 1973         # warn "Couldn't get the dimmensions of image $imagePath.\n"
 1974         #}
 1975         #$out = "\\includegraphics[bb=0 0 $height $width,width=$width_ratio\\linewidth]{$imagePath}\n";
 1976         $out = "\\includegraphics[width=$width_ratio\\linewidth]{$imagePath}\n";
 1977       } else {
 1978         # Since we're not creating PDF files, alias should have given us the
 1979         # path to an EPS file. latex can get its dimmensions no problem!
 1980 
 1981         $out = "\\includegraphics[width=$width_ratio\\linewidth]{$imagePath}\n";
 1982       }
 1983     } elsif ($displayMode eq 'Latex2HTML') {
 1984       my $wid = ($envir->{onTheFlyImageSize} || 0)+ 30;
 1985       $out = qq!\\begin{rawhtml}\n<A HREF= "$imageURL" TARGET="_blank" onclick="window.open(this.href,this.target, 'width=$wid,height=$wid,scrollbars=yes,resizable=on'); return false;"><IMG SRC="$imageURL"  WIDTH="$width" HEIGHT="$height"></A>\n
 1986       \\end{rawhtml}\n !
 1987     } elsif ($displayMode eq 'HTML' || $displayMode eq 'HTML_tth' || $displayMode eq 'HTML_dpng' || $displayMode eq 'HTML_img' || $displayMode eq 'HTML_jsMath' || $displayMode eq 'HTML_asciimath') {
 1988       my $wid = ($envir->{onTheFlyImageSize} || 0) +30;
 1989       $out = qq!<A HREF= "$imageURL" TARGET="_blank" onclick="window.open(this.href,this.target, 'width=$wid,height=$wid,scrollbars=yes,resizable=on'); return false;"><IMG SRC="$imageURL"  WIDTH="$width" HEIGHT="$height"></A>
 1990       !
 1991     } else {
 1992       $out = "Error: PGbasicmacros: image: Unknown displayMode: $displayMode.\n";
 1993     }
 1994     push(@output_list, $out);
 1995   }
 1996   return wantarray ? @output_list : $output_list[0];
 1997 }
 1998 
 1999 # This is legacy code.
 2000 sub images {
 2001   my @in = @_;
 2002   my @outlist = ();
 2003   while (@in) {
 2004      push(@outlist,&image( shift(@in) ) );
 2005    }
 2006   @outlist;
 2007 }
 2008 
 2009 
 2010 sub caption {
 2011   my ($out) = @_;
 2012   $out = " $out \n" if $displayMode eq 'TeX';
 2013   $out = " $out  " if $displayMode eq 'HTML';
 2014   $out = " $out  " if $displayMode eq 'HTML_tth';
 2015   $out = " $out  " if $displayMode eq 'HTML_dpng';
 2016   $out = " $out  " if $displayMode eq 'HTML_img';
 2017   $out = " $out  " if $displayMode eq 'HTML_jsMath';
 2018   $out = " $out  " if $displayMode eq 'HTML_asciimath';
 2019   $out = " $out  " if $displayMode eq 'Latex2HTML';
 2020     $out;
 2021 }
 2022 
 2023 sub captions {
 2024   my @in = @_;
 2025   my @outlist = ();
 2026   while (@in) {
 2027      push(@outlist,&caption( shift(@in) ) );
 2028   }
 2029   @outlist;
 2030 }
 2031 
 2032 sub imageRow {
 2033 
 2034   my $pImages = shift;
 2035   my $pCaptions=shift;
 2036   my $out = "";
 2037   my @images = @$pImages;
 2038   my @captions = @$pCaptions;
 2039   my $number = @images;
 2040   # standard options
 2041   my %options = ( 'tex_size' => 200,  # width for fitting 4 across
 2042                   'height' => 100,
 2043                   'width' => 100,
 2044                   @_            # overwrite any default options
 2045                 );
 2046 
 2047   if ($displayMode eq 'TeX') {
 2048     $out .= "\n\\par\\smallskip\\begin{center}\\begin{tabular}{"  .  "|c" x $number .  "|} \\hline\n";
 2049     while (@images) {
 2050       $out .= &image( shift(@images),%options ) . '&';
 2051     }
 2052     chop($out);
 2053     $out .= "\\\\ \\hline \n";
 2054     while (@captions) {
 2055       $out .= &caption( shift(@captions) ) . '&';
 2056     }
 2057     chop($out);
 2058     $out .= "\\\\ \\hline \n\\end {tabular}\\end{center}\\par\\smallskip\n";
 2059   } elsif ($displayMode eq 'Latex2HTML'){
 2060 
 2061     $out .= "\n\\begin{rawhtml} <TABLE  BORDER=1><TR>\n\\end{rawhtml}\n";
 2062     while (@images) {
 2063       $out .= "\n\\begin{rawhtml} <TD>\n\\end{rawhtml}\n" . &image( shift(@images),%options )
 2064               . "\n\\begin{rawhtml} </TD>\n\\end{rawhtml}\n" ;
 2065     }
 2066 
 2067     $out .= "\n\\begin{rawhtml}</TR><TR>\\end{rawhtml}\n";
 2068     while (@captions) {
 2069       $out .= "\n\\begin{rawhtml} <TH>\n\\end{rawhtml}\n".&caption( shift(@captions) )
 2070               . "\n\\begin{rawhtml} </TH>\n\\end{rawhtml}\n" ;
 2071     }
 2072 
 2073     $out .= "\n\\begin{rawhtml} </TR> </TABLE >\n\\end{rawhtml}";
 2074   } elsif ($displayMode eq 'HTML' || $displayMode eq 'HTML_tth' || $displayMode eq 'HTML_dpng'|| $displayMode eq 'HTML_img' || $displayMode eq 'HTML_jsMath' || $displayMode eq 'HTML_asciimath'){
 2075     $out .= "<P>\n <TABLE BORDER=2 CELLPADDING=3 CELLSPACING=2 ><TR ALIGN=CENTER    VALIGN=MIDDLE>\n";
 2076     while (@images) {
 2077       $out .= " \n<TD>". &image( shift(@images),%options ) ."</TD>";
 2078     }
 2079     $out .= "</TR>\n<TR>";
 2080     while (@captions) {
 2081       $out .= " <TH>". &caption( shift(@captions) ) ."</TH>";
 2082     }
 2083     $out .= "\n</TR></TABLE></P>\n"
 2084   }
 2085   else {
 2086     $out = "Error: PGbasicmacros: imageRow: Unknown languageMode: $displayMode.\n";
 2087     warn $out;
 2088   }
 2089   $out;
 2090 }
 2091 
 2092 
 2093 ###########
 2094 # Auxiliary macros
 2095 
 2096 sub display_options2{
 2097   my %options = @_;
 2098   my $out_string = "";
 2099   foreach my $key (keys %options) {
 2100     $out_string .= " $key => $options{$key},<BR>";
 2101   }
 2102   $out_string;
 2103 }
 2104 
 2105 
 2106 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9