File:  [LON-CAPA] / loncom / homework / default_homework.lcpm
Revision 1.143: download - view: text, annotated - select for diffs
Fri Feb 6 10:43:48 2009 UTC (15 years, 3 months ago) by riegler
Branches: MAIN
CVS tags: version_2_8_X, version_2_8_2, version_2_8_1, HEAD, GCI_1
There are problems coded as formula resonse WITHOUT sampling requiring the student to enter a set (such as {4,2,b,z}).
As of version 1.142 sets as student's input would return BAD_FORMULA, because they contain commas.
With this version BAD_FORMULA will only be returned if the response contains a , but not a set.

These set problems are inpossible to code using mathresponse as it splits the response at commas. Hence RESPONSE[1]='{4' for the above set which is not a legel maxima expression.
These set problems could be coded by customresponse in combination mathresponse. customresponse would be used to remove the curly brackets. This way of coding, however, is very ackward as compared to formula response.

    1: # The LearningOnline Network with CAPA 
    2: # used by lonxml::xmlparse() as input variable $safeinit to Apache::run::run()
    3: #
    4: # $Id: default_homework.lcpm,v 1.143 2009/02/06 10:43:48 riegler Exp $
    5: #
    6: # Copyright Michigan State University Board of Trustees
    7: #
    8: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
    9: #
   10: # LON-CAPA is free software; you can redistribute it and/or modify
   11: # it under the terms of the GNU General Public License as published by
   12: # the Free Software Foundation; either version 2 of the License, or
   13: # (at your option) any later version.
   14: #
   15: # LON-CAPA is distributed in the hope that it will be useful,
   16: # but WITHOUT ANY WARRANTY; without even the implied warranty of
   17: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   18: # GNU General Public License for more details.
   19: #
   20: # You should have received a copy of the GNU General Public License
   21: # along with LON-CAPA; if not, write to the Free Software
   22: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   23: #
   24: # /home/httpd/html/adm/gpl.txt
   25: #
   26: # http://www.lon-capa.org/
   27: #
   28: #
   29: 
   30: #init some globals
   31: $hidden::RANDOMINIT=0;
   32: $pi=atan2(1,1)*4;
   33: $rad2deg=180.0/$pi;
   34: $deg2rad=$pi/180.0;
   35: $"=' ';
   36: use strict;
   37: {
   38:     my $n = 0;
   39:     my $total = 0;
   40:     my $num_left = 0;
   41:     my @order;
   42:     my $type;
   43: 
   44:     sub init_permutation {
   45: 	my ($size,$requested_type) = @_;
   46: 	@order = (0..$size-1);
   47: 	$n = $size;
   48: 	$type = $requested_type;
   49: 	if ($type eq 'ordered') {
   50: 	    $total = $num_left = 1;
   51: 	} elsif ($type eq 'unordered') {
   52: 	    $total = $num_left = &factorial($size);
   53: 	} else {
   54: 	    die("Unkown type: $type");
   55: 	}
   56:     }
   57: 
   58:     sub get_next_permutation {
   59: 	if ($num_left == $total) {
   60: 	    $num_left--;
   61: 	    return \@order;
   62: 	}
   63: 
   64: 	# Find largest index j with a[j] < a[j+1]
   65: 
   66: 	my $j = scalar(@order) - 2;
   67: 	while ($order[$j] > $order[$j+1]) {
   68: 	    $j--;
   69: 	}
   70: 
   71: 	# Find index k such that a[k] is smallest integer
   72: 	# greater than a[j] to the right of a[j]
   73: 
   74: 	my $k = scalar(@order) - 1;
   75: 	while ($order[$j] > $order[$k]) {
   76: 	    $k--;
   77: 	}
   78: 
   79: 	# Interchange a[j] and a[k]
   80: 
   81: 	@order[($k,$j)] = @order[($j,$k)];
   82: 
   83: 	# Put tail end of permutation after jth position in increasing order
   84: 
   85: 	my $r = scalar(@order) - 1;
   86: 	my $s = $j + 1;
   87: 
   88: 	while ($r > $s) {
   89: 	    @order[($s,$r)]=@order[($r,$s)];
   90: 	    $r--;
   91: 	    $s++;
   92: 	}
   93: 
   94: 	$num_left--;
   95: 	return(\@order);
   96:     }
   97:     
   98:     sub get_permutations_left {
   99: 	return $num_left;
  100:     }
  101: }
  102: 
  103: sub check_commas {
  104:     my ($response)=@_;
  105:     #print("$response ");
  106:     my @numbers=split(',',$response);
  107:     #print(" numbers ".join('-',@numbers)." ");
  108:     if (scalar(@numbers) > 1) {
  109:         #print(" numbers[0] ".$numbers[0]." "); 
  110: 	if (length($numbers[0]) > 3 || length($numbers[0]) == 0) { return -1; }
  111: 	shift(@numbers);
  112: 	#print(" numbers ".scalar(@numbers)." ");
  113: 	while (scalar(@numbers) > 1) {
  114: 	    #print(" numbers ".join('-',@numbers)." ");
  115: 	    if (length($numbers[0]) != 3) { return -2; }
  116: 	    shift(@numbers);
  117: 	}
  118: 	my ($number)=split('\.',$numbers[0]);
  119: 	#print(" number ".$number." ");
  120: 	#print(" numbers[0] ".$numbers[0]." ");
  121: 	if (length($number) != 3) { return -3; }
  122:     } else {
  123: 	my ($number)=split('\.',$numbers[0]);
  124: 	if (length($number) > 3) { return -4; }
  125:     }
  126:     return 1;
  127: }
  128: 
  129: 
  130: sub caparesponse_check {
  131:     my ($answer,$response)=@_;
  132:     #not properly used yet: calc
  133:     #not to be used: $ans_fmt
  134:     my $type=$LONCAPA::CAPAresponse_args{'type'};
  135:     my $tol=$LONCAPA::CAPAresponse_args{'tol'};
  136:     my $sig=$LONCAPA::CAPAresponse_args{'sig'};
  137:     my $ans_fmt=$LONCAPA::CAPAresponse_args{'format'};
  138:     my $unit=$LONCAPA::CAPAresponse_args{'unit'};
  139:     my $calc=$LONCAPA::CAPAresponse_args{'calc'};
  140:     my $samples=$LONCAPA::CAPAresponse_args{'samples'};
  141:     
  142:     my $tol_type=''; # gets it's value from whether tol has a % or not done
  143:     my $sig_lbound=''; #done
  144:     my $sig_ubound=''; #done
  145: 
  146: 
  147:     #type's definitons come from capaParser.h
  148: 
  149:     #remove leading and trailing whitespace
  150:     if (!defined($response)) {
  151: 	$response='';
  152:     }
  153:     if ($response=~ /^\s|\s$/) {
  154: 	$response=~ s:^\s+|\s+$::g;
  155: 	&LONCAPA_INTERNAL_DEBUG("Removed ws now :$response:");
  156:     }
  157: 
  158:     #&LONCAPA_INTERNAL_DEBUG(" type is $type ");
  159:     if ($type eq 'cs' || $type eq 'ci') {
  160: 	#for string answers make sure all places spaces occur, there is 
  161:         #really only 1 space, in both the answer and the response
  162: 	$answer=~s/ +/ /g;
  163: 	$response=~s/ +/ /g;
  164:     } elsif ($type eq 'mc') {
  165: 	$answer=~s/[\s,]//g;
  166: 	$response=~s/[\s,]//g;
  167:     }
  168:     if ($type eq 'float' && $unit=~/\$/) {
  169: 	if ($response!~/^\$|\$$/)  { return ('NO_UNIT', undef); }
  170: 	$response=~s/\$//g;
  171:     }
  172:     if ($type eq 'float' && $unit=~/\,/ && (&check_commas($response)<0)) {
  173: 	return "COMMA_FAIL:";
  174:     }
  175:     $ans_fmt=~s/\W//g;
  176:     $unit=~s/[\$,]//g;
  177:     if ($type eq 'float') { $response=~s/,//g; }
  178: 
  179:     if (length($response) > 500) { return ('TOO_LONG',undef); }
  180: 
  181:     if ($type eq '' ) {
  182: 	&LONCAPA_INTERNAL_DEBUG("Didn't find a type :$type: defaulting");
  183: 	if ( $answer eq ($answer *1.0)) { $type = 2;
  184: 				      } else { $type = 3; }
  185:     } else {
  186: 	if    ($type eq 'cs')    { $type = 4; }
  187: 	elsif ($type eq 'ci')    { $type = 3 }
  188: 	elsif ($type eq 'mc')    { $type = 5; }
  189: 	elsif ($type eq 'fml')   { $type = 8; }
  190:         elsif ($type eq 'math')  { $type = 9; }
  191: 	elsif ($type eq 'subj')  { $type = 7; }
  192: 	elsif ($type eq 'float') { $type = 2; }
  193: 	elsif ($type eq 'int')   { $type = 1; }
  194: 	else { return ('ERROR', "Unknown type of answer: $type") }
  195:     }
  196: 
  197:     my $points;
  198:     my $id_list;
  199:     #formula type setup the sample points
  200:     if ($type eq '8') {
  201: 	($id_list,$points)=split(/@/,$samples);
  202: 	&LONCAPA_INTERNAL_DEBUG("Found :$id_list:$points: points in $samples");
  203:     }
  204:     if ($tol eq '') {
  205: 	$tol=0.0;
  206: 	$tol_type=1; #TOL_ABSOLUTE
  207:     } else {
  208: 	if ($tol =~ /%$/) {
  209: 	    chop $tol;
  210: 	    $tol_type=2; #TOL_PERCENTAGE
  211: 	} else {
  212: 	    $tol_type=1; #TOL_ABSOLUTE
  213: 	}
  214:     }
  215: 
  216:     ($sig_ubound,$sig_lbound)=&LONCAPA_INTERNAL_get_sigrange($sig);
  217: 
  218:     my $reterror="";
  219:     my $result;
  220:     if (($type eq '9') || ($type eq '8')) {
  221:         if ($response=~/\=/) {
  222:             return ('BAD_FORMULA','Please submit just an expression, not an equation.');
  223:         } elsif ($response =~ /\,/ and $response !~ /^\s*\{.*\}\s*$/) {
  224:             return ('BAD_FORMULA');
  225:         }
  226:     }
  227:     if ($type eq '9') {
  228:       $result = &maxima_check(&maxima_cas_formula_fix($response),&maxima_cas_formula_fix($answer),\$reterror);
  229:     } else {
  230: 	if ($type eq '8') { # fml type
  231: 	    $response = &capa_formula_fix($response);
  232: 	    $answer   = &capa_formula_fix($answer);
  233: 	}
  234:        $result = &caparesponse_capa_check_answer($response,$answer,$type,
  235: 						 $tol_type,$tol,
  236: 						 $sig_lbound,$sig_ubound,
  237: 						 $ans_fmt,$unit,$calc,$id_list,
  238: 						 $points,$external::randomseed,
  239: 						 \$reterror);
  240:     }
  241:     if    ($result == '1') { $result='EXACT_ANS'; } 
  242:     elsif ($result == '2') { $result='APPROX_ANS'; }
  243:     elsif ($result == '3') { $result='SIG_FAIL'; }
  244:     elsif ($result == '4') { $result='UNIT_FAIL'; }
  245:     elsif ($result == '5') { $result='NO_UNIT'; }
  246:     elsif ($result == '6') { $result='UNIT_OK'; }
  247:     elsif ($result == '7') { $result='INCORRECT'; }
  248:     elsif ($result == '8') { $result='UNIT_NOTNEEDED'; }
  249:     elsif ($result == '9') { $result='ANS_CNT_NOT_MATCH'; }
  250:     elsif ($result =='10') { $result='SUB_RECORDED'; }
  251:     elsif ($result =='11') { $result='BAD_FORMULA'; }
  252:     elsif ($result =='12' && !$response) { $result='MISSING_ANSWER'; }
  253:     elsif ($result =='12') { $result='WANTED_NUMERIC'; }
  254:     elsif ($result =='13') { $result='UNIT_INVALID_INSTRUCTOR'; }
  255:     elsif ($result =='141') { $result='UNIT_INVALID_STUDENT'; }
  256:     elsif ($result =='142') { $result='UNIT_INVALID_STUDENT'; }
  257:     elsif ($result =='143') { $result='UNIT_INVALID_STUDENT'; }
  258:     elsif ($result =='15') { $result='UNIT_IRRECONCIBLE'; }
  259:     else  {$result = "ERROR: Unknown Result:$result:$@:";}
  260: 
  261:     &LONCAPA_INTERNAL_DEBUG("RetError $reterror: Answer $answer: Response $response:  type-$type|$tol|$tol_type|$sig:$sig_lbound:$sig_ubound|$unit|");
  262:     &LONCAPA_INTERNAL_DEBUG(" $answer $response $result ");
  263:     return ($result,$reterror);
  264: }
  265: 
  266: 
  267: sub caparesponse_check_list {
  268:     my $responses=$LONCAPA::CAPAresponse_args{'response'};
  269:     &LONCAPA_INTERNAL_DEBUG("args ".join(':',%LONCAPA::CAPAresponse_args));
  270:     my $type = $LONCAPA::CAPAresponse_args{'type'};
  271:     my $answerunit=$LONCAPA::CAPAresponse_args{'unit'};
  272:     &LONCAPA_INTERNAL_DEBUG("Got type :$type: answer unit :$answerunit:\n");
  273:     
  274:     my $num_input_lines =
  275: 	scalar(@{$LONCAPA::CAPAresponse_answer->{'answers'}});
  276:     
  277:     if ($type ne '' ) {
  278: 	if (scalar(@$responses) < $num_input_lines) {
  279: 	    return 'MISSING_ANSWER';
  280: 	}
  281: 	if (scalar(@$responses) > $num_input_lines) {
  282: 	    return 'EXTRA_ANSWER';
  283: 	}
  284: 
  285:     }
  286: 
  287:     foreach my $which (0..($num_input_lines-1)) {
  288: 	my $answer_size = 
  289: 	    scalar(@{$LONCAPA::CAPAresponse_answer->{'answers'}[$which]});
  290: 	if ($type ne '' 
  291: 	    && $answer_size > 1) {
  292: 	    $responses->[$which]=[split(/,/,$responses->[$which])];
  293: 	} else {
  294: 	    $responses->[$which]=[$responses->[$which]];
  295: 	}
  296:     }
  297:     foreach my $which (0..($num_input_lines-1)) {
  298: 	my $answer_size = 
  299: 	    scalar(@{$LONCAPA::CAPAresponse_answer->{'answers'}[$which]});
  300: 	my $response_size = 
  301: 	    scalar(@{$responses->[$which]});
  302: 	if ($answer_size > $response_size) {
  303: 	    return 'MISSING_ANSWER';
  304: 	}
  305: 	if ($answer_size < $response_size) {
  306: 	    return 'EXTRA_ANSWER';
  307: 	}
  308:     }
  309: 
  310:     &LONCAPA_INTERNAL_DEBUG("Initial final response :$responses->[0][-1]:");
  311:     my $unit;
  312:     if ($type eq 'float' || $type eq '') {
  313: 	#for numerical problems split off the unit
  314: #	if ( $responses->[0][-1]=~ /(.*[^\s])\s+([^\s]+)/ ) {
  315:         if ( $responses->[0][-1]=~ /^([\d\.\,\s\$]*(?:(?:[xX\*]10[\^\*]*|[eE]*)[\+\-]*\d*)*(?:^|\S)\d+)([\$\s\w\^\*\/\(\)\+\-]*[^\d\.\s\,][\$\s\w\^\*\/\(\)\+\-]*)$/ ) {
  316: 	    $responses->[0][-1]=$1;
  317: 	    $unit=&capa_formula_fix($2);
  318:             &LONCAPA_INTERNAL_DEBUG("Found unit :$unit:");
  319: 	}
  320:     }
  321:     &LONCAPA_INTERNAL_DEBUG("Final final response :$responses->[0][-1]:$unit:");
  322:     $unit=~s/\s//;
  323:     foreach my $response (@$responses) {
  324:        foreach my $element (@$response) {	
  325:           if (($type eq 'float') || (($type eq '') && ($unit ne ''))) {
  326:               $element =~ s/\s//g;
  327:           }
  328:           my $appendunit=$unit;
  329:           if (($unit=~/\%/) && ($answerunit ne '%'))  {
  330:              $element=$element/100;
  331:              $appendunit=~s/\%//;
  332:           }    
  333:           if (($element==0) && ($unit!~/\w/) && ($answerunit=~/\w/)) {
  334:              $appendunit=$answerunit;
  335:           }
  336:           $element .= " $appendunit";
  337:           &LONCAPA_INTERNAL_DEBUG("Made response element :$element:");
  338:        }
  339:     }
  340:     
  341:     foreach my $thisanswer (@{ $LONCAPA::CAPAresponse_answer->{'answers'} }) {
  342: 	if (!defined($thisanswer)) {
  343: 	    return ('ERROR','answer was undefined');
  344: 	}
  345:     }
  346: 
  347: 
  348: #    &LONCAPA_INTERNAL_DEBUG(&LONCAPA_INTERNAL_Dumper($responses));
  349:     my %memoized;
  350:     if ($LONCAPA::CAPAresponse_answer->{'type'}  eq 'ordered') {
  351: 	for (my $i=0; $i<scalar(@$responses);$i++) {
  352: 	    my $answer   = $LONCAPA::CAPAresponse_answer->{'answers'}[$i];
  353: 	    my $response = $responses->[$i];
  354: 	    my $key = "$answer\0$response";
  355: 	    my (@awards,@msgs);
  356: 	    for (my $j=0; $j<scalar(@$response); $j++) { 
  357: 		my ($award,$msg) = &caparesponse_check($answer->[$j],
  358: 						       $response->[$j]);
  359: 		push(@awards,$award);
  360: 		push(@msgs,  $msg);
  361: 	    }
  362: 	    my ($award,$msg) = 
  363: 		&LONCAPA_INTERNAL_FINALIZEAWARDS(\@awards,\@msgs);
  364: 	    $memoized{$key} = [$award,$msg];
  365: 	}
  366:     } else {
  367: 	#FIXME broken with unorder responses where one is a <value>
  368:         #      and the other is a <vector> (need to delay parse til
  369:         #      inside the loop?)
  370: 	foreach my $response (@$responses) {
  371: 	    my $response_size = scalar(@{$response});
  372: 	    foreach my $answer (@{ $LONCAPA::CAPAresponse_answer->{'answers'} }) {
  373: 		my $key = "$answer\0$response";
  374: 		my $answer_size =  scalar(@{$answer});
  375: 		my ($award,$msg);
  376: 		if ($answer_size > $response_size) {
  377: 		    $award = 'MISSING_ANSWER';
  378: 		} elsif ($answer_size < $response_size) {
  379: 		    $award = 'EXTRA_ANSWER';
  380: 		} else {
  381: 		    my (@awards,@msgs);
  382: 		    for (my $j=0; $j<scalar(@$response); $j++) {
  383: 			my ($award,$msg) = &caparesponse_check($answer->[$j],
  384: 							       $response->[$j]);
  385: 			push(@awards,$award);
  386: 			push(@msgs,  $msg);
  387: 		    }
  388: 		    ($award,$msg) = 
  389: 			&LONCAPA_INTERNAL_FINALIZEAWARDS(\@awards,\@msgs);
  390: 		}
  391: 		$memoized{$key} = [$award,$msg];
  392: 	    }
  393: 	}
  394:     }
  395: 
  396:     my ($final_award,$final_msg);
  397:     &init_permutation(scalar(@$responses),
  398: 		      $LONCAPA::CAPAresponse_answer->{'type'});
  399: 
  400:     # possible FIXMEs
  401:     # - significant time is spent calling non-safe space routine
  402:     #   from safe space
  403:     # - early outs could be possible with classifying awards is to stratas
  404:     #   and stopping as so as hitting the top strata 
  405:     # - some early outs also might be possible with check ing the 
  406:     #   memoized hash of results (is correct even possible? etc.)
  407: 
  408:     my (@final_awards,@final_msg);
  409:     while( &get_permutations_left() ) {
  410: 	my $order = &get_next_permutation();
  411: 	my (@awards, @msgs, $i);
  412: 	foreach my $thisanswer (@{ $LONCAPA::CAPAresponse_answer->{'answers'} }) {
  413: 	    my $key = "$thisanswer\0".$responses->[$order->[$i]];
  414: 	    push(@awards,$memoized{$key}[0]);
  415: 	    push(@msgs,$memoized{$key}[1]);
  416: 	    $i++;
  417: 
  418: 	}
  419: 	&LONCAPA_INTERNAL_DEBUG(" all awards ".join(':',@awards));
  420: 
  421: 	my ($possible_award,$possible_msg) = 
  422: 	    &LONCAPA_INTERNAL_FINALIZEAWARDS(\@awards,\@msgs);
  423: 	&LONCAPA_INTERNAL_DEBUG(" pos awards ".$possible_award);
  424: 	push(@final_awards,$possible_award);
  425: 	push(@final_msg,$possible_msg);
  426:     }
  427: 
  428:     &LONCAPA_INTERNAL_DEBUG(" all final_awards ".join(':',@final_awards));
  429:     my ($final_award,$final_msg) = 
  430: 	&LONCAPA_INTERNAL_FINALIZEAWARDS(\@final_awards,\@final_msg,undef,1);
  431:     return ($final_award,$final_msg);
  432: }
  433: 
  434: sub cas {
  435:     my ($system,$input,$library)=@_;
  436:     my $output;
  437:     if ($system eq 'maxima') {
  438:        $output=&maxima_eval($input,$library);
  439:     } else {
  440:        $output='Error: unrecognized CAS';
  441:     }
  442:     return $output;
  443: }
  444: 
  445: sub tex {
  446:     if ( $external::target eq "tex" ) {
  447: 	return $_[0];
  448:     } else {
  449: 	return $_[1];
  450:     }
  451: }
  452: 
  453: sub var_in_tex {
  454:     if ( $external::target eq "tex" ) {
  455: 	return $_[0];
  456:     } else {
  457: 	return "";
  458:     }
  459: }
  460: 
  461: sub web {
  462:     if ( $external::target eq "tex" ) {
  463: 	return $_[1];
  464:     } else {
  465: 	if ( $external::target eq "web" || $external::target eq "answer") {
  466: 	    return $_[2];
  467: 	} else {
  468: 	    return $_[0];
  469: 	}
  470:     }
  471: }
  472: 
  473: sub html {
  474:     if ( $external::target eq "web" ) {
  475: 	return shift;
  476:     }
  477: }
  478: 
  479: sub hinton {
  480:     return 0;
  481: }
  482: 
  483: sub random {
  484:     my ($start,$end,$step)=@_;
  485:     if ( ! $hidden::RANDOMINIT ) {
  486: 	if ($external::randomseed == 0) { $external::randomseed=1; }
  487: 	if ($external::randomseed =~/,/) {
  488: 	    my ($num1,$num2)=split(/,/,$external::randomseed);
  489: 	    &random_set_seed(1,abs($num1));
  490: 	} elsif ($external::randomseed =~/:/) {
  491: 	    my ($num1,$num2)=split(/:/,$external::randomseed);
  492: 	    &random_set_seed(abs($num1),abs($num2));
  493: 	} else {
  494: 	    &random_set_seed(1,int(abs($external::randomseed)));
  495: 	}
  496: 	&math_random_uniform();
  497: 	$hidden::RANDOMINIT=1;
  498:     }
  499:     if (!defined($step)) { $step=1; }
  500:     my $num=1+int(($end-$start)/$step);
  501:     my $result=$start + int(&math_random_uniform() * $num)*$step;
  502:     return $result;
  503: }
  504: 
  505: sub random_normal {
  506:     my ($item_cnt,$seed,$av,$std_dev) = @_;
  507:     my @oldseed=&random_get_seed();
  508:     my @retArray;
  509:     &random_set_seed_from_phrase($seed);
  510:     @retArray=&math_random_normal($item_cnt,$av,$std_dev);
  511:     &random_set_seed(@oldseed);
  512:     return @retArray;
  513: }
  514: 
  515: sub random_beta {
  516:     my ($item_cnt,$seed,$aa,$bb) = @_;
  517:     my @oldseed=&random_get_seed();
  518:     my @retArray;
  519:     &random_set_seed_from_phrase($seed);
  520:     @retArray=&math_random_beta($item_cnt,$aa,$bb);
  521:     &random_set_seed(@oldseed);
  522:     return @retArray;
  523: }
  524: 
  525: sub random_gamma {
  526:     my ($item_cnt,$seed,$a,$r) = @_;
  527:     my @oldseed=&random_get_seed();
  528:     my @retArray;
  529:     &random_set_seed_from_phrase($seed);
  530:     @retArray=&math_random_gamma($item_cnt,$a,$r);
  531:     &random_set_seed(@oldseed);
  532:     return @retArray;
  533: }
  534: 
  535: sub random_exponential {
  536:     my ($item_cnt,$seed,$av) = @_;
  537:     my @oldseed=&random_get_seed();
  538:     my @retArray;
  539:     &random_set_seed_from_phrase($seed);
  540:     @retArray=&math_random_exponential($item_cnt,$av);
  541:     &random_set_seed(@oldseed);
  542:     return @retArray;
  543: }
  544: 
  545: sub random_poisson {
  546:     my ($item_cnt,$seed,$mu) = @_;
  547:     my @oldseed=&random_get_seed();
  548:     my @retArray;
  549:     &random_set_seed_from_phrase($seed);
  550:     @retArray=&math_random_poisson($item_cnt,$mu);
  551:     &random_set_seed(@oldseed);
  552:     return @retArray;
  553: }
  554: 
  555: sub random_chi {
  556:     my ($item_cnt,$seed,$df) = @_;
  557:     my @oldseed=&random_get_seed();
  558:     my @retArray;
  559:     &random_set_seed_from_phrase($seed);
  560:     @retArray=&math_random_chi_square($item_cnt,$df);
  561:     &random_set_seed(@oldseed);
  562:     return @retArray;
  563: }
  564: 
  565: sub random_noncentral_chi {
  566:     my ($item_cnt,$seed,$df,$nonc) = @_;
  567:     my @oldseed=&random_get_seed();
  568:     my @retArray;
  569:     &random_set_seed_from_phrase($seed);
  570:     @retArray=&math_random_noncentral_chi_square($item_cnt,$df,$nonc);
  571:     &random_set_seed(@oldseed);
  572:     return @retArray;
  573: }
  574: 
  575: sub random_f {
  576:     my ($item_cnt,$seed,$dfn,$dfd) = @_;
  577:     my @oldseed=&random_get_seed();
  578:     my @retArray;
  579:     &random_set_seed_from_phrase($seed);
  580:     @retArray=&math_random_f($item_cnt,$dfn,$dfd);
  581:     &random_set_seed(@oldseed);
  582:     return @retArray;
  583: }
  584: 
  585: sub random_noncentral_f {
  586:     my ($item_cnt,$seed,$dfn,$dfd,$nonc) = @_;
  587:     my @oldseed=&random_get_seed();
  588:     my @retArray;
  589:     &random_set_seed_from_phrase($seed);
  590:     @retArray=&math_random_noncentral_f($item_cnt,$dfn,$dfd,$nonc);
  591:     &random_set_seed(@oldseed);
  592:     return @retArray;
  593: }
  594: 
  595: sub random_multivariate_normal {
  596:     my ($item_cnt,$seed,$mean,$covar) = @_;
  597:     my @oldseed=&random_get_seed();
  598:     &random_set_seed_from_phrase($seed);
  599:     my @retArray=&math_random_multivariate_normal($item_cnt,@$mean,@$covar);
  600:     &random_set_seed(@oldseed);
  601:     return @retArray;
  602: }
  603: 
  604: sub random_multinomial {
  605:     my ($item_cnt,$seed,@p) = @_;
  606:     my @oldseed=&random_get_seed();
  607:     my @retArray;
  608:     &random_set_seed_from_phrase($seed);
  609:     my @retArray=&math_random_multinomial($item_cnt,@p);
  610:     &random_set_seed(@oldseed);
  611:     return @retArray;
  612: }
  613: 
  614: sub random_permutation {
  615:     my ($seed,@inArray) = @_;
  616:     my @oldseed=&random_get_seed();
  617:     my @retArray;
  618:     &random_set_seed_from_phrase($seed);
  619:     @retArray=&math_random_permutation(@inArray);
  620:     &random_set_seed(@oldseed);
  621:     return @retArray;
  622: }
  623: 
  624: sub random_uniform {
  625:     my ($item_cnt,$seed,$low,$high) = @_;
  626:     my @oldseed=&random_get_seed();
  627:     my @retArray;
  628:     &random_set_seed_from_phrase($seed);
  629:     @retArray=&math_random_uniform($item_cnt,$low,$high);
  630:     &random_set_seed(@oldseed);
  631:     return @retArray;
  632: }
  633: 
  634: sub random_uniform_integer {
  635:     my ($item_cnt,$seed,$low,$high) = @_;
  636:     my @oldseed=&random_get_seed();
  637:     my @retArray;
  638:     &random_set_seed_from_phrase($seed);
  639:     @retArray=&math_random_uniform_integer($item_cnt,$low,$high);
  640:     &random_set_seed(@oldseed);
  641:     return @retArray;
  642: }
  643: 
  644: sub random_binomial {
  645:     my ($item_cnt,$seed,$nt,$p) = @_;
  646:     my @oldseed=&random_get_seed();
  647:     my @retArray;
  648:     &random_set_seed_from_phrase($seed);
  649:     @retArray=&math_random_binomial($item_cnt,$nt,$p);
  650:     &random_set_seed(@oldseed);
  651:     return @retArray;
  652: }
  653: 
  654: sub random_negative_binomial {
  655:     my ($item_cnt,$seed,$ne,$p) = @_;
  656:     my @oldseed=&random_get_seed();
  657:     my @retArray;
  658:     &random_set_seed_from_phrase($seed);
  659:     @retArray=&math_random_negative_binomial($item_cnt,$ne,$p);
  660:     &random_set_seed(@oldseed);
  661:     return @retArray;
  662: }
  663: 
  664: sub abs { CORE::abs(shift) }
  665: sub sin { CORE::sin(shift) }
  666: sub cos { CORE::cos(shift) }
  667: sub exp { CORE::exp(shift) }
  668: sub int { CORE::int(shift) }
  669: sub log { CORE::log(shift) }
  670: sub atan2 { CORE::atan2($_[0],$_[1]) }
  671: sub sqrt { CORE::sqrt(shift) }
  672: 
  673: sub tan  { CORE::sin($_[0]) / CORE::cos($_[0]) }
  674: #sub atan { atan2($_[0], 1); }
  675: #sub acos { atan2(sqrt(1 - $_[0] * $_[0]), $_[0] ); }
  676: #sub asin { atan2($_[0], sqrt(1- $_[0] * $_[0]) );  }
  677: 
  678: sub log10 { CORE::log($_[0])/CORE::log(10); }
  679: 
  680: sub factorial {
  681:     my $input = CORE::int(shift);
  682:     return "Error - unable to take factorial of an negative number ($input)" if $input < 0;
  683:     return "Error - factorial result is greater than system limit ($input)" if $input > 170;
  684:     return 1 if $input == 0;
  685:     my $result = 1; 
  686:     for (my $i=2; $i<=$input; $i++) { $result *= $i }
  687:     return $result;
  688: }
  689: 
  690: sub sgn {
  691:     return -1 if $_[0] < 0;
  692:     return 0 if $_[0] == 0;
  693:     return 1 if $_[0] > 0;
  694: }
  695: 
  696: sub min {
  697:     my @sorted = sort { $a <=> $b || $a cmp $b } @_;
  698:     return shift @sorted;
  699: }
  700: 
  701: sub max {
  702:     my @sorted = sort { $a <=> $b || $a cmp $b } @_;
  703:     return pop @sorted;
  704: }
  705: 
  706: sub roundto {
  707:     my ($input,$n) = @_;
  708:     return sprintf('%.'.$n.'f',$input);
  709: }
  710: 
  711: sub to_string {
  712:     my ($input,$n) = @_;
  713:     return sprintf($input) if $n eq "";
  714:     $n = '.'.$n if $n !~ /^\./;
  715:     return sprintf('%'.$n,$input) if $n ne "";
  716: }
  717: 
  718: sub sub_string {
  719:     my ($str,$start,$len) = @_;
  720:     return substr($str,$start-1,$len);
  721: }
  722: 
  723: sub pow   {return $_[0] ** $_[1]; }
  724: sub ceil  {return (($_[0]-CORE::int($_[0]))== 0.0) ? $_[0] : (($_[0] > 0) ? (CORE::int($_[0])+ 1) : CORE::int($_[0])); }
  725: sub floor  {return (($_[0]-CORE::int($_[0]))== 0.0) ? $_[0] : (($_[0] > 0) ? CORE::int($_[0]) : (CORE::int($_[0])-1)); }
  726: #sub floor {return int($_[0]); }
  727: 
  728: sub format {
  729:     my ($value,$fmt)=@_;
  730:     my ($dollarmode,$commamode,$alwaysperiod,$options);
  731:     if ($fmt =~ /^([^\d]*)(.*)/) { $options=$1; $fmt=$2; } 
  732:     #if ($options =~ /\$/) { $dollamode=1; }
  733:     #if ($options =~ /,/)  { $commamode=1; }
  734:     if ($options =~ /\./) { $alwaysperiod=1; }
  735:     my $result;
  736:     if ($fmt=~/s$/i) {
  737: 	$result=&format_significant_figures($value,$fmt);
  738:     } else {
  739: 	$fmt=~s/e/E/g;
  740: 	$result=sprintf('%.'.$fmt,$value);
  741: 	if ($alwaysperiod && $fmt eq '0f') { $result .='.'; }
  742: 	$result=~s/(E[+-]*)0/$1/;
  743:     }
  744:     #if ($dollarmode) {$result=&dollarformat($result);}
  745:     #if ($commamode) {$result=&commaformat($result);}
  746:     return $result;
  747: }
  748: 
  749: sub chemparse {
  750:     my ($reaction) = @_;
  751:     my @tokens = split(/(\s\+|\->|<=>|<\-|\.)/,$reaction);
  752:     my $formula = '';
  753:     foreach my $token (@tokens) {
  754: 	if ($token eq '->' ) {
  755: 	    $formula .= '<m>\ensuremath{\rightarrow}</m> ';
  756: 	    next;
  757: 	}
  758: 	if ($token eq '<-' ) {
  759: 	    $formula .= '<m>\ensuremath{\leftarrow}</m> ';
  760: 	    next;
  761: 	}  
  762: 	if ($token eq '<=>') {
  763: 	    if ($external::target eq 'web' &&
  764: 		&EXT('request.browser.unicode')) {
  765: 		$formula .= '&#8652; ';
  766: 	    } else {
  767: 		$formula .= &web('<=> ','<m>\ensuremath{\rightleftharpoons}</m> ',
  768: 				 '&lt;=&gt; ');
  769: 	    }
  770: 	    next;
  771: 	}
  772: 	if ($token eq '.') {
  773: 	  $formula =~ s/(\&nbsp\;| )$//;
  774: 	  $formula .= '&middot;';
  775: 	  next;
  776: 	}
  777: 	$token =~ /^\s*([\d|\/]*(?:&frac\d\d)?)(.*)/;
  778:         $formula .= $1 if ($1 ne '1');  # stoichiometric coefficient
  779: 	
  780: 	my $molecule = $2;
  781: 	# subscripts
  782: 	$molecule =~ s|(?<=[a-zA-Z\)\]\s])(\d+)|<sub>$1</sub>|g;
  783: 	# superscripts
  784: 	$molecule =~ s|\^(\d*[+\-]*)|<sup>$1</sup>|g;
  785: 	# strip whitespace
  786: 	$molecule =~ s/\s*//g;
  787: 	# forced space
  788: 	$molecule =~ s/_/ /g;
  789: 	$molecule =~ s/-/&minus;/g;
  790: 	$formula .= $molecule.'&nbsp;';
  791:     }
  792:     # get rid of trailing space
  793:     $formula =~ s/(\&nbsp\;| )$//;
  794:     return &xmlparse($formula);
  795: }
  796: 
  797: sub prettyprint {
  798:     my ($value,$fmt,$target)=@_;
  799:     my $result;
  800:     if (!$target) { $target = $external::target; }
  801:     if ($fmt =~ /chem/i) { return(&chemparse($value)); }
  802:     my ($dollarmode,$commamode,$alwaysperiod,$options);
  803:     if ($fmt =~ /^([^\d]*)(.*)/) { $options=$1; $fmt=$2; } 
  804:     if ($options =~ /\$/) { $dollarmode=1; }
  805:     if ($options =~ /,/)  { $commamode=1; }
  806:     if ($options =~ /\./) { $alwaysperiod=1; }
  807:     if ($fmt=~/s$/i) {
  808: 	$value=&format_significant_figures($value,$fmt);
  809:     } elsif ($fmt) {
  810: 	$value=sprintf('%.'.$fmt,$value);
  811:     }
  812:     if ($alwaysperiod && $fmt eq '0f') {
  813: 	if ($target eq 'tex') {
  814: 	    $value .='\\ensuremath{.}';
  815: 	} else {
  816: 	    $value .='.';
  817: 	}
  818:     }
  819:     if ($value =~ /([0-9\.\-\+]+)E([0-9\-\+]+)/i ) {
  820: 	my $frac=$1;
  821: 	if ($dollarmode) { $frac=&dollarformat($frac); }
  822: 	if ($commamode) { $frac=&commaformat($frac); }
  823: 	my $exponent=$2;
  824: 	$exponent=~s/^\+0*//;
  825: 	$exponent=~s/^-0*/-/;
  826: 	$exponent=~s/^-0*/-/;
  827: 	if ($exponent eq '-') { undef($exponent); }
  828: 	if ($exponent) {
  829: 	    if ($target eq 'web') {
  830: 		$result=$frac.'&#215;10<sup>'.$exponent.'</sup>';
  831: 	    } elsif ($target eq 'tex') {
  832: 		$result='\ensuremath{'.$frac.'\times 10^{'.$exponent.'}}';
  833: 	    } else {
  834: 		$result=$value;
  835: 	    }
  836: 	} else {
  837: 	    $result=$frac;
  838: 	}
  839:     } else {
  840: 	$result=$value;
  841: 	if    ($dollarmode) { $result=&dollarformat($result,$target); }
  842: 	elsif ($commamode)  { $result=&commaformat($result,$target); }
  843:     }
  844:     return $result;
  845: }
  846: 
  847: sub commaformat {
  848:     my ($number,$target) = @_;
  849:     if ($number =~ /\./) {
  850: 	while ($number =~ /([^0-9]*)([0-9]+)([^\.,][^\.,][^\.,])([,0-9]*\.[0-9]*)$/) {
  851: 	    $number = $1.$2.','.$3.$4;
  852: 	}
  853:     } else {
  854: 	while ($number =~ /^([^0-9]*)([0-9]+)([^,][^,][^,])([,0-9]*)$/) {
  855: 	    $number = $1.$2.','.$3.$4;
  856: 	}
  857:     }
  858:     return $number;
  859: }
  860: 
  861: sub dollarformat {
  862:     my ($number,$target) = @_;
  863:     if (!$target) { $target = $external::target; }
  864:     $number=&commaformat($number,$target);
  865:     if ($target eq 'tex') {
  866: 	$number='\$'.$number; #' stupid emacs
  867:     } else {
  868: 	$number='$'.$number; #' stupid emacs
  869:     }
  870:     return $number; 
  871: }
  872: 
  873: # format of form ns or nS where n is an integer
  874: sub format_significant_figures {
  875:     my ($number,$format) = @_; 
  876:     return '0' if ($number == 0);
  877:     # extract number of significant figures needed
  878:     my ($sig) = ($format =~ /(\d+)s/i);
  879:     # arbitrary choice - suggestions ?? or throw error message?
  880:     $sig = 3 if ($sig eq '');
  881:     # save the minus sign
  882:     my $sign = ($number < 0) ? '-' : '';
  883:     $number = abs($number);
  884:     # needed to correct for a number greater than 1 (or
  885:     my $power = ($number < 1) ? 0 : 1;
  886:     # could round up. Take the integer part of log10.
  887:     my $x10 = int(log($number)/log(10));
  888:     # find number with values left of decimal pt = # of sign figs.
  889:     my $xsig = $number*10**($sig-$x10-$power);
  890:     # get just digits left of decimal pt - also rounds off correctly
  891:     my $xint  = sprintf('%.0f',$xsig);
  892:     # save any trailing zero's
  893:     my ($zeros) = ($xint =~ /(0+)$/);
  894:     # return number to original magnitude
  895:     my $numSig = $xint*10**($x10-$sig+$power);
  896:     # insert trailing zero's if have decimal point
  897:     $numSig =~ s/^(\d+)\.(\d+)(\e?(.*)?)$/$1\.$2$zeros$3/;
  898:     # put a decimal pt for number ending with 0 and length = # of sig fig
  899:     $numSig.='.' if (length($numSig) == $sig && $numSig =~ /0$/);
  900:     if (length($numSig) < $sig) {
  901: 	$numSig.='.'.substr($zeros,0,($sig-length($numSig)));
  902:     }
  903:     # return number with sign
  904:     return $sign.$numSig;
  905: 
  906: }
  907: 
  908: sub map {
  909:     my ($phrase,$dest,$source)=@_;
  910:     my @oldseed=&random_get_seed();
  911:     my @seed = &random_seed_from_phrase($phrase);
  912:     &random_set_seed(@seed);
  913:     my $destct = scalar(@$dest);
  914:     if (!$source) {
  915: 	my @output;
  916: 	my @idx = &math_random_permuted_index($destct);
  917: 	my $ctr = 0;
  918: 	while ($ctr < $destct) {
  919: 	    $output[$ctr] = $$dest[$idx[$ctr]];
  920: 	    $ctr++;
  921: 	}
  922:         &random_set_seed(@oldseed);
  923: 	return @output;
  924:     } else {
  925: 	my $num = scalar(@$source);
  926: 	my @idx = &math_random_permuted_index($num);
  927: 	my $ctr = 0;
  928: 	my $tot = $num;
  929: 	$tot = $destct if $destct < $num;
  930: 	if (ref($$dest[0])) {
  931: 	    while ($ctr < $tot) {
  932: 		${$$dest[$ctr]} = $$source[$idx[$ctr]];
  933: 	        $ctr++;
  934:             }
  935:         } else {
  936: 	    while ($ctr < $tot) {
  937: 		$$dest[$ctr] = $$source[$idx[$ctr]];
  938: 		$ctr++;
  939: 	    }
  940: 	}
  941:     }
  942:     &random_set_seed(@oldseed);
  943:     return '';
  944: }
  945: 
  946: sub rmap {
  947:     my ($phrase,$dest,$source)=@_;
  948:     my @oldseed=&random_get_seed();
  949:     my @seed = &random_seed_from_phrase($phrase);
  950:     &random_set_seed(@seed);
  951:     my $destct = scalar(@$dest);
  952:     if (!$source) {
  953: 	my @idx = &math_random_permuted_index($destct);
  954: 	my $ctr = 0;
  955: 	my @r_idx;
  956: 	while ($ctr < $destct) {
  957: 	    $r_idx[$idx[$ctr]] = $ctr;
  958: 	    $ctr++;
  959: 	}
  960: 	my @output;
  961: 	$ctr = 0;
  962: 	while ($ctr < $destct) {
  963: 	    $output[$ctr] = $$dest[$r_idx[$ctr]];
  964: 	    $ctr++;
  965: 	}
  966:         &random_set_seed(@oldseed);
  967: 	return @output;
  968:     } else {
  969: 	my $num = scalar(@$source);
  970: 	my @idx = &math_random_permuted_index($num);
  971: 	my $ctr = 0;
  972: 	my $tot = $num;
  973: 	$tot = $destct if $destct < $num;
  974: 	my @r_idx;
  975: 	while ($ctr < $tot) {
  976: 	    $r_idx[$idx[$ctr]] = $ctr;
  977: 	    $ctr++;
  978: 	}
  979: 	$ctr = 0;
  980: 	if (ref($$dest[0])) {
  981: 	    while ($ctr < $tot) {
  982: 		${$$dest[$ctr]} = $$source[$r_idx[$ctr]];
  983: 	        $ctr++;
  984:             }
  985:         } else {
  986: 	    while ($ctr < $tot) {
  987: 		$$dest[$ctr] = $$source[$r_idx[$ctr]];
  988: 		$ctr++;
  989: 	    }
  990: 	}
  991:     }
  992:     &random_set_seed(@oldseed);
  993:     return '';
  994: }
  995: 
  996: sub capa_id { return }
  997: 
  998: sub problem { return }
  999: 
 1000: sub name{
 1001:     my $fullname = &EXT('environment.lastname').', '.&EXT('environment.firstname').' '.&EXT('environment.middlename');
 1002:     $fullname = "" if $fullname eq ",  ";
 1003:     $fullname =~ s/\%2d/-/g;
 1004:     return $fullname;
 1005: }
 1006: 
 1007: sub student_number { 
 1008:     my $id = &EXT('environment.id');
 1009:     $id = '' if $id eq "";
 1010:     return $id;
 1011: }
 1012: 
 1013: sub class {
 1014:     my $course = &EXT('course.description');
 1015:     $course = '' if $course eq "";
 1016:     return $course;
 1017: }
 1018: 
 1019: sub firstname {
 1020:     my $firstname = &EXT('environment.firstname');
 1021:     $firstname = '' if $firstname eq "";
 1022:     return $firstname;
 1023: }
 1024:                                                                                 
 1025: sub lastname {
 1026:     my $lastname = &EXT('environment.lastname');
 1027:     $lastname = '' if $lastname eq "";
 1028:     return $lastname;
 1029: }
 1030: 
 1031: sub sec { 
 1032:     my $sec = &EXT('request.course.sec');
 1033:     $sec = '' if $sec eq "";
 1034:     return $sec;
 1035: }
 1036: 
 1037: sub submission {
 1038:    my ($partid,$responseid,$subnumber)=@_;
 1039:    my $sub='';
 1040:    if ($subnumber) { $sub=$subnumber.':'; }
 1041:    return &EXT('user.resource.'.$sub.'resource.'.$partid.'.'.$responseid.'.submission');
 1042: }
 1043: 
 1044: sub currentpart {
 1045:    return $external::part;
 1046: }
 1047: 
 1048: sub eval_time {
 1049:    my ($timestamp)=@_;
 1050:    unless ($timestamp) { return ''; }
 1051:    return &locallocaltime($timestamp);
 1052: }
 1053: 
 1054: sub open_date { 
 1055:     my ($partid)=@_;
 1056:     unless ($partid) { $partid=0; }
 1057:     return &eval_time(&EXT('resource.'.$partid.'.opendate'));
 1058: }
 1059: 
 1060: sub due_date {
 1061:     my ($partid)=@_;
 1062:     unless ($partid) { $partid=0; } 
 1063:     return &eval_time(&EXT('resource.'.$partid.'.duedate'));
 1064: }
 1065: 
 1066: sub answer_date { 
 1067:     my ($partid)=@_;
 1068:     unless ($partid) { $partid=0; }
 1069:     return &eval_time(&EXT('resource.'.$partid.'.answerdate'));
 1070: }
 1071: 
 1072: sub open_date_epoch {
 1073:     my ($partid)=@_;
 1074:     unless ($partid) { $partid=0; }
 1075:     return &EXT('resource.'.$partid.'.opendate');
 1076: }
 1077: 
 1078: sub due_date_epoch {
 1079:     my ($partid)=@_;
 1080:     unless ($partid) { $partid=0; }
 1081:     return &EXT('resource.'.$partid.'.duedate');
 1082: }
 1083: 
 1084: sub answer_date_epoch {
 1085:     my ($partid)=@_;
 1086:     unless ($partid) { $partid=0; }
 1087:     return &EXT('resource.'.$partid.'.answerdate');
 1088: }
 1089: 
 1090: sub array_moments {
 1091:     my @input=@_;
 1092:     my (@output,$N);
 1093:     $N=scalar (@input);
 1094:     $output[0]=$N;
 1095:     if ($N <= 1) {
 1096: 	$output[1]=$input[0];
 1097: 	$output[1]="Input array not defined" if ($N == 0);
 1098: 	$output[2]="variance undefined for N<=1";
 1099: 	$output[3]="skewness undefined for N<=1";
 1100: 	$output[4]="kurtosis undefined for N<=1";
 1101: 	return @output;
 1102:     }
 1103:     my $sum=0;
 1104:     foreach my $line (@input) {
 1105: 	$sum+=$line;
 1106:     }
 1107:     $output[1] = $sum/$N;
 1108:     my ($x,$sdev,$var,$skew,$kurt) = 0;
 1109:     foreach my $line (@input) {
 1110: 	$x=$line-$output[1];
 1111: 	$var+=$x**2;
 1112: 	$skew+=$x**3;
 1113: 	$kurt+=$x**4;
 1114:     }
 1115:     $output[2]=$var/($N-1);
 1116:     $sdev=CORE::sqrt($output[2]);
 1117:     if ($sdev == 0) {
 1118: 	$output[3]="inf-variance=0";
 1119: 	$output[4]="inf-variance=0";
 1120: 	return @output;
 1121:     }
 1122:     $output[3]=$skew/($sdev**3*$N);
 1123:     $output[4]=$kurt/($sdev**4*$N)-3;
 1124:     return @output;
 1125: }
 1126: 
 1127: sub choose {
 1128:     my $num = $_[0];
 1129:     return $_[$num];
 1130: }
 1131: 
 1132: #&sum1(1,$x,sub { &sum1($_[0],2*$_[0], sub { fact($_[0])**2 })});
 1133: #sub sum1 {
 1134: #    my ($start,$end,$sub)=@_;
 1135: #    my $sum=0;
 1136: #    for (my $i=$start;$i<=$end;$i++) {
 1137: #        $sum+=&$sub($i);
 1138: #    }
 1139: #    return $sum
 1140: #}
 1141: 
 1142: #&sum2('a',1,$x,'&sum2(\'b\',$a,2*$a, \'&factorial($b)**2\')');
 1143: #sub sum2 {
 1144: #    my ($varname,$start,$end,$line)=@_;
 1145: #    my $sum=0;
 1146: #    for (my $i=$start;$i<=$end;$i++) {
 1147: #	my $func=sub {
 1148: #	    eval("\$".$varname."=$i");
 1149: #	    eval($line);
 1150: #	};
 1151: #        $sum+=&$func($i);
 1152: #    }
 1153: #    return $sum
 1154: #}
 1155: 
 1156: # expiremental idea
 1157: sub proper_path {
 1158:     my ($path)=@_;
 1159:     if ( $external::target eq "tex" ) {
 1160: 	return '/home/httpd/html'.$path;
 1161:     } else {
 1162: 	return $path;
 1163:     }
 1164: }
 1165: 

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>