[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 5175 - (download) (as text) (annotate)
Fri Jul 13 21:35:34 2007 UTC (12 years, 5 months ago) by sh002i
File size: 70881 byte(s)
quick hack to satisfy gedit's dumb syntax hiliting

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

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9