--- loncom/homework/default_homework.lcpm 2002/09/26 20:45:00 1.51 +++ loncom/homework/default_homework.lcpm 2006/09/29 20:55:33 1.116 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # used by lonxml::xmlparse() as input variable $safeinit to Apache::run::run() # -# $Id: default_homework.lcpm,v 1.51 2002/09/26 20:45:00 albertel Exp $ +# $Id: default_homework.lcpm,v 1.116 2006/09/29 20:55:33 albertel Exp $ # # Copyright Michigan State University Board of Trustees # @@ -26,406 +26,578 @@ # http://www.lon-capa.org/ # # -# Guy Albertelli -# -# 05/25/2001 H. K. Ng -# 05/31/2001 H. K. Ng -# 12/21/2001 Matthew -# + #init some globals $hidden::RANDOMINIT=0; $pi=atan2(1,1)*4; $rad2deg=180.0/$pi; $deg2rad=$pi/180.0; $"=' '; +use strict; +{ + my $n = 0; + my $total = 0; + my $num_left = 0; + my @order; + my $type; + + sub init_permutation { + my ($size,$requested_type) = @_; + @order = (0..$size-1); + $n = $size; + $type = $requested_type; + if ($type eq 'ordered') { + $total = $num_left = 1; + } elsif ($type eq 'unordered') { + $total = $num_left = &factorial($size); + } else { + die("Unkown type: $type"); + } + } + + sub get_next_permutation { + if ($num_left == $total) { + $num_left--; + return @order; + } + + # Find largest index j with a[j] < a[j+1] + + my $j = scalar(@order) - 2; + while ($order[$j] > $order[$j+1]) { + $j--; + } + + # Find index k such that a[k] is smallest integer + # greater than a[j] to the right of a[j] + + my $k = scalar(@order) - 1; + while ($order[$j] > $order[$k]) { + $k--; + } + + # Interchange a[j] and a[k] + + @order[($k,$j)] = @order[($j,$k)]; + + # Put tail end of permutation after jth position in increasing order + + my $r = scalar(@order) - 1; + my $s = $j + 1; + + while ($r > $s) { + @order[($s,$r)]=@order[($r,$s)]; + $r--; + $s++; + } + + $num_left--; + return(@order); + } + + sub get_permutations_left { + return $num_left; + } +} + +sub check_commas { + my ($response)=@_; + #print("$response "); + my @numbers=split(',',$response); + #print(" numbers ".join('-',@numbers)." "); + if (scalar(@numbers) > 1) { + #print(" numbers[0] ".$numbers[0]." "); + if (length($numbers[0]) > 3 || length($numbers[0]) == 0) { return -1; } + shift(@numbers); + #print(" numbers ".scalar(@numbers)." "); + while (scalar(@numbers) > 1) { + #print(" numbers ".join('-',@numbers)." "); + if (length($numbers[0]) != 3) { return -2; } + shift(@numbers); + } + my ($number)=split('\.',$numbers[0]); + #print(" number ".$number." "); + #print(" numbers[0] ".$numbers[0]." "); + if (length($number) != 3) { return -3; } + } else { + my ($number)=split('\.',$numbers[0]); + if (length($number) > 3) { return -4; } + } + return 1; +} sub caparesponse_check { - #not properly used yet: calc - #not to be used: $ans_fmt - my ($answer,$type,$tol,$sig,$ans_fmt,$unit,$calc,$samples) = - eval $_[1]. - ';return ($answer,$type,$tol,$sig,$ans_fmt,$unit,$calc,$samples);'; - - my $tol_type=''; # gets it's value from whether tol has a % or not done - my $sig_lbound=''; #done - my $sig_ubound=''; #done - my ($response,$expr)=@_; - - - #type's definitons come from capaParser.h - my $message=''; - #remove leading and trailing whitespace - if ($response=~ /^\s|\s$/) { - $response=~ s:^\s+|\s+$::g; - $message .="Removed ws now :$response:
"; - } else { - $message .="no ws in :$response:
"; - } - - if ($type eq '' ) { - $message .= "Didn't find a type :$type:$expr: defaulting
"; - if ( $answer eq ($answer *1.0)) { $type = 2; - } else { $type = 3; } - } else { - if ($type eq 'cs') { $type = 4; - } elsif ($type eq 'ci') { $type = 3; - } elsif ($type eq 'mc') { $type = 5; - } elsif ($type eq 'fml') { $type = 8; - } elsif ($type eq 'subj') { $type = 7; - } elsif ($type eq 'float') { $type = 2; - } elsif ($type eq 'int') { $type = 1; - } else { return "ERROR: Unknown type of answer: $type" } - } - - my $points; - my $id_list; - #formula type setup the sample points - if ($type eq '8') { - ($id_list,$points)=split(/@/,$samples); - $message.="Found :$points: points
"; - } - if ($tol eq '') { - $tol=0.0; - $tol_type=1; #TOL_ABSOLUTE - } else { - if ($tol =~ /%$/) { - chop $tol; - $tol_type=2; #TOL_PERCENTAGE - } else { - $tol_type=1; #TOL_ABSOLUTE - } - } - - if ($sig eq '') { - $sig_lbound = 0; #SIG_LB_DEFAULT - $sig_ubound =15; #SIG_UB_DEFAULT - } else { - ($sig_lbound,$sig_ubound) = split /,/,$sig; - if (!$sig_lbound) { - $sig_lbound = 0; #SIG_LB_DEFAULT - $sig_ubound =15; #SIG_UB_DEFAULT - } - if (!$sig_ubound) { $sig_ubound=$sig_lbound; } - } - my $result = &caparesponse_capa_check_answer($response,$answer,$type, - $tol_type,$tol, - $sig_lbound,$sig_ubound, - $ans_fmt,$unit,$calc,$id_list, - $points,$external::randomseed); - - if ($result == '1') { $result='EXACT_ANS'; } - elsif ($result == '2') { $result='APPROX_ANS'; } - elsif ($result == '3') { $result='SIG_FAIL'; } - elsif ($result == '4') { $result='UNIT_FAIL'; } - elsif ($result == '5') { $result='NO_UNIT'; } - elsif ($result == '6') { $result='UNIT_OK'; } - elsif ($result == '7') { $result='INCORRECT'; } - elsif ($result == '8') { $result='UNIT_NOTNEEDED'; } - elsif ($result == '9') { $result='ANS_CNT_NOT_MATCH'; } - elsif ($result =='10') { $result='SUB_RECORDED'; } - elsif ($result =='11') { $result='BAD_FORMULA'; } - elsif ($result =='12') { $result='WANTED_NUMERIC'; } - else {$result = "ERROR: Unknown Result:$result:$@:";} - - return "$result:
Error $error:
Answer $answer:
Response $response:
type-$type|$tol|$tol_type|$sig:$sig_lbound:$sig_ubound|$unit|
$message$expr"; -} - -sub get_array_args { - my ($expr,$arg)=@_; - # do these first, because who knows what varname the instructor might have used - # but it probably isn't $CAPARESPONSE_CHECK_LIST_answer - my $CAPARESPONSE_CHECK_LIST_answer = eval $expr.';return $'.$arg; #' - my $GET_ARRAY_ARGS_result; - my @GET_ARRAY_ARGS_list; - if ($CAPARESPONSE_CHECK_LIST_answer =~ /^\s*[\$\@]/) { - (@GET_ARRAY_ARGS_list) = eval $CAPARESPONSE_CHECK_LIST_answer; - } - $GET_ARRAY_ARGS_result.="error:$@:
"; - # if the eval fails just use what is in the answer exactly - if (!defined(@GET_ARRAY_ARGS_list) || !defined($GET_ARRAY_ARGS_list[0])) { - $GET_ARRAY_ARGS_result.="list zero is undefined
"; - $GET_ARRAY_ARGS_list[0]=$CAPARESPONSE_CHECK_LIST_answer; - } - return $GET_ARRAY_ARGS_result,@GET_ARRAY_ARGS_list; + my ($answer,$response)=@_; + #not properly used yet: calc + #not to be used: $ans_fmt + my $type=$LONCAPA::CAPAresponse_args{'type'}; + my $tol=$LONCAPA::CAPAresponse_args{'tol'}; + my $sig=$LONCAPA::CAPAresponse_args{'sig'}; + my $ans_fmt=$LONCAPA::CAPAresponse_args{'format'}; + my $unit=$LONCAPA::CAPAresponse_args{'unit'}; + my $calc=$LONCAPA::CAPAresponse_args{'calc'}; + my $samples=$LONCAPA::CAPAresponse_args{'samples'}; + + my $tol_type=''; # gets it's value from whether tol has a % or not done + my $sig_lbound=''; #done + my $sig_ubound=''; #done + + + #type's definitons come from capaParser.h + + #remove leading and trailing whitespace + if (!defined($response)) { + $response=''; + } + if ($response=~ /^\s|\s$/) { + $response=~ s:^\s+|\s+$::g; + &LONCAPA_INTENAL_DEBUG("Removed ws now :$response:"); + } + + &LONCAPA_INTERNAL_DEBUG(" type is $type "); + if ($type eq 'cs' || $type eq 'ci') { + #for string answers make surec all places spaces occur, there is + #really only 1 space, in both the answer and the response + $answer=~s/ +/ /g; + $response=~s/ +/ /g; + } elsif ($type eq 'mc') { + $answer=~s/[\s,]//g; + $response=~s/[\s,]//g; + } + if ($type eq 'float' && $unit=~/\$/) { + if ($response!~/^\$/) { return "NO_UNIT: Missing \$ "; } + $response=~s/\$//g; + } + if ($type eq 'float' && $unit=~/\,/ && (&check_commas($response)<0)) { + return "COMMA_FAIL:"; + } + $ans_fmt=~s/\W//g; + $unit=~s/[\$,]//g; + if ($type eq 'float') { $response=~s/,//g; } + + if (length($response) > 500) { return "TOO_LONG: Answer too long"; } + + if ($type eq '' ) { + &LONCAPA_INTERNAL_DEBUG("Didn't find a type :$type: defaulting"); + if ( $answer eq ($answer *1.0)) { $type = 2; + } else { $type = 3; } + } else { + if ($type eq 'cs') { $type = 4; } + elsif ($type eq 'ci') { $type = 3 } + elsif ($type eq 'mc') { $type = 5; } + elsif ($type eq 'fml') { $type = 8; } + elsif ($type eq 'math') { $type = 9; } + elsif ($type eq 'subj') { $type = 7; } + elsif ($type eq 'float') { $type = 2; } + elsif ($type eq 'int') { $type = 1; } + else { return "ERROR: Unknown type of answer: $type" } + } + + my $points; + my $id_list; + #formula type setup the sample points + if ($type eq '8') { + ($id_list,$points)=split(/@/,$samples); + &LONCAPA_INTERNAL_DEBUG("Found :$id_list:$points: points in $samples"); + } + if ($tol eq '') { + $tol=0.0; + $tol_type=1; #TOL_ABSOLUTE + } else { + if ($tol =~ /%$/) { + chop $tol; + $tol_type=2; #TOL_PERCENTAGE + } else { + $tol_type=1; #TOL_ABSOLUTE + } + } + + ($sig_ubound,$sig_lbound)=&LONCAPA_INTERNAL_get_sigrange($sig); + + my $reterror=""; + my $result; + if ($type eq '9') { + $result = &maxima_check(&maxima_cas_formula_fix($response),&maxima_cas_formula_fix($answer),\$reterror); + } else { + if ($type eq '8') { # fml type + $response = &capa_formula_fix($response); + $answer = &capa_formula_fix($answer); + } + $result = &caparesponse_capa_check_answer($response,$answer,$type, + $tol_type,$tol, + $sig_lbound,$sig_ubound, + $ans_fmt,$unit,$calc,$id_list, + $points,$external::randomseed, + \$reterror); + } + if ($result == '1') { $result='EXACT_ANS'; } + elsif ($result == '2') { $result='APPROX_ANS'; } + elsif ($result == '3') { $result='SIG_FAIL'; } + elsif ($result == '4') { $result='UNIT_FAIL'; } + elsif ($result == '5') { $result='NO_UNIT'; } + elsif ($result == '6') { $result='UNIT_OK'; } + elsif ($result == '7') { $result='INCORRECT'; } + elsif ($result == '8') { $result='UNIT_NOTNEEDED'; } + elsif ($result == '9') { $result='ANS_CNT_NOT_MATCH'; } + elsif ($result =='10') { $result='SUB_RECORDED'; } + elsif ($result =='11') { $result='BAD_FORMULA'; } + elsif ($result =='12' && !$response) { $result='MISSING_ANSWER'; } + elsif ($result =='12') { $result='WANTED_NUMERIC'; } + elsif ($result =='13') { $result='UNIT_INVALID_INSTRUCTOR'; } + elsif ($result =='141') { $result='UNIT_INVALID_STUDENT'; } + elsif ($result =='142') { $result='UNIT_INVALID_STUDENT'; } + elsif ($result =='143') { $result='UNIT_INVALID_STUDENT'; } + elsif ($result =='15') { $result='UNIT_IRRECONCIBLE'; } + else {$result = "ERROR: Unknown Result:$result:$@:";} + + &LONCAPA_INTERNAL_DEBUG("RetError $reterror: Answer $answer: Response $response: type-$type|$tol|$tol_type|$sig:$sig_lbound:$sig_ubound|$unit|",$reterror); + return ($result,$reterror) +} + +sub maxima_cas_formula_fix { + my ($expression)=@_; + return &implicit_multiplication($expression); +} + +sub capa_formula_fix { + my ($expression)=@_; + return &implicit_multiplication($expression); +} + +sub implicit_multiplication { + my ($expression)=@_; +# Escape scientific notation, so 3e8 does not become 3*e*8 +# 3e8 -> 3&8; 3e-8 -> 3&-8; 3E+8 -> e&+8 + $expression=~s/(\d+)e([\+\-]*\d+)/$1\&\($2\)/gsi; +# 3x10^8 -> 3&8; 3*10^-8 -> 3&-8 + $expression=~s/(\d+)(?:x|\*)10(?:\^|\*\*)([\+\-]*\d+)/$1\&\($2\)/gsi; +# Fill in multiplication signs +# a b -> a*b;3 b -> 3*b;3 4 -> 3*4 + $expression=~s/(\w)\s+(\w)/$1\*$2/gs; +# )( -> )*(; ) ( -> )*( + $expression=~s/\)\s*\(/\)\*\(/gs; +# 3a -> 3*a; 3( -> 3*(; 3 ( -> 3*(; 3A -> 3*A + $expression=~s/(\d)\s*([a-zA-Z\(])/$1\*$2/gs; +# a ( -> a*( + $expression=~s/(\w)\s+\(/$1\*\(/gs; +# a3 -> a*3; + $expression=~s/([a-zA-Z])(\d)/$1\*$2/gs; +# )a -> )*a; )3 -> )*3; ) 3 -> )*3 + $expression=~s/\)\s*(\w)/\)\*$1/gs; +# 3&8 -> 3e8; 3&-4 -> 3e-4 + $expression=~s/(\d+)\&\(([\+\-]*\d+)\)/$1e$2/gs; + return $expression; } sub caparesponse_check_list { - my ($response,$expr)=@_; - my $result; - my ($result,@list) = &get_array_args($expr,'answer'); - my $aresult=''; - my $current_answer; - my $answers=join(':',@list); - $result.="Got response :$answers:
"; - my @responselist; - my $type =eval $expr.';return $answer;'; - if ($type ne '' && $#list > 0) { - (@responselist)=split /,/,$response; - } else { - (@responselist)=($response); - } - my $unit=''; - $result.="Initial final response :$responselist['-1']:
"; - if ($type eq '') { - #for numerical problems split off the unit - if ( $responselist['-1']=~ /(.*[^\s])\s+([^\s]+)/ ) { - $responselist['-1']=$1; - $unit=$2; - } - } - $result.="Final final response :$responselist['-1']:
"; - $result.=":$#list: answers
"; - $unit=~s/\s//; - my $i=0; - my $awards=''; - for ($i=0; $i<@list;$i++) { - $result.="trying answer :$list[$i]:
"; - if ($unit eq '') { - $aresult=&caparesponse_check($responselist[$i], - $expr.';my $answer=\''.$list[$i].'\';'); - } else { - $aresult=&caparesponse_check($responselist[$i]." $unit", - $expr.';my $answer=\''.$list[$i].'\';'); - } - my ($temp)=split /:/, $aresult; - $awards.="$temp,"; - $result.=$aresult; - } - chop $awards; - return "$awards:
$result"; + my $response=$LONCAPA::CAPAresponse_args{'response'}; + my $result="Got response :".join(':',@LONCAPA::CAPAresponse_answer).":\n"; + &LONCAPA_INTERNAL_DEBUG("args ".join(':',%LONCAPA::CAPAresponse_args)); + my @responselist; + my $type = $LONCAPA::CAPAresponse_args{'type'}; + &LONCAPA_INTERNAL_DEBUG("Got type :$type:\n"); + my $num_answers = scalar(@{$LONCAPA::CAPAresponse_answer->{'answers'}}); + if ($type ne '' + && $num_answers > 1) { + (@responselist)=split(/,/,$response); + if (@responselist < $num_answers) { + return 'MISSING_ANSWER'; + } + if (@responselist > $num_answers) { + return 'EXTRA_ANSWER'; + } + } else { + (@responselist)=($response); + } + &LONCAPA_INTERNAL_DEBUG("Initial final response :$responselist['-1']:"); + my $unit; + if ($type eq '' || $type eq 'float') { + #for numerical problems split off the unit + if ( $responselist['-1']=~ /(.*[^\s])\s+([^\s]+)/ ) { + $responselist['-1']=$1; + $unit=$2; + } + } + &LONCAPA_INTERNAL_DEBUG("Final final response :$responselist['-1']:$unit:"); + $unit=~s/\s//; + + #&reset_caparesponse_memoization(); + my ($final_award,$final_msg); + &init_permutation(scalar(@responselist), + $LONCAPA::CAPAresponse_answer->{'type'}); + while( &get_permutations_left() ) { + my @responses_ordered = @responselist[&get_next_permutation()]; + my (@awards, @msgs, $i); + foreach my $thisanswer (@{ $LONCAPA::CAPAresponse_answer->{'answers'} }) { + my ($msg,$aresult); + if (defined($thisanswer)) { + my $response = $responses_ordered[$i]; + if ($unit eq '') { + $response .= " $unit"; + } + ($aresult,$msg)=&caparesponse_check($thisanswer,$response); + } else { + $aresult='ERROR'; + $msg='answer was undefined'; + } + #&LONCAPA_INTERNAL_DEBUG("after if $aresult -- $msg"); + my ($temp)=split(/:/, $aresult); + push(@awards,$temp); + push(@msgs,$msg); + $i++; + } + my ($possible_award,$possible_msg) = + &LONCAPA_INTERNAL_FINALIZEAWARDS(\@awards,\@msgs); + if ($final_award) { + ($final_award,$final_msg) = + &LONCAPA_INTERNAL_FINALIZEAWARDS([$final_award,$possible_award], + [$final_msg,$possible_msg], + undef,1); + } else { + ($final_award,$final_msg) = ($possible_award,$possible_msg); + } + } + #&reset_caparesponse_memoization(); + return ($final_award,$final_msg); } sub tex { - if ( $external::target eq "tex" ) { - return @_[0]; - } else { - return @_[1]; - } + if ( $external::target eq "tex" ) { + return $_[0]; + } else { + return $_[1]; + } } sub var_in_tex { - if ( $external::target eq "tex" ) { - return @_[0]; - } else { - return ""; - } + if ( $external::target eq "tex" ) { + return $_[0]; + } else { + return ""; + } } sub web { - if ( $external::target eq "tex" ) { - return @_[1]; - } else { - if ( $external::target eq "web" || $external::target eq "answer") { - return @_[2]; + if ( $external::target eq "tex" ) { + return $_[1]; } else { - return @_[0]; + if ( $external::target eq "web" || $external::target eq "answer") { + return $_[2]; + } else { + return $_[0]; + } } - } } sub html { - if ( $external::target eq "web" ) { - return shift; - } -} - -sub problem { - return '1'; + if ( $external::target eq "web" ) { + return shift; + } } sub hinton { - return 0; + return 0; } sub random { - my ($start,$end,$step)=@_; - if ( ! $hidden::RANDOMINIT ) { - &random_set_seed_from_phrase($external::randomseed); - $hidden::RANDOMINIT=1; - } - if (!defined($step)) { $step=1; } - my $num=1+int(($end-$start)/$step); - my $result=$start + int(&math_random_uniform() * $num)*$step; - return $result; + my ($start,$end,$step)=@_; + if ( ! $hidden::RANDOMINIT ) { + if ($external::randomseed == 0) { $external::randomseed=1; } + if ($external::randomseed =~/,/) { + my ($num1,$num2)=split(/,/,$external::randomseed); + &random_set_seed(1,abs($num1)); + } elsif ($external::randomseed =~/:/) { + my ($num1,$num2)=split(/:/,$external::randomseed); + &random_set_seed(abs($num1),abs($num2)); + } else { + &random_set_seed(1,int(abs($external::randomseed))); + } + &math_random_uniform(); + $hidden::RANDOMINIT=1; + } + if (!defined($step)) { $step=1; } + my $num=1+int(($end-$start)/$step); + my $result=$start + int(&math_random_uniform() * $num)*$step; + return $result; } sub random_normal { - my ($item_cnt,$seed,$av,$std_dev) = @_; - my @oldseed=&random_get_seed(); - my @retArray; - &random_set_seed_from_phrase($seed); - @retArray=&math_random_normal($item_cnt,$av,$std_dev); - &random_set_seed(@oldseed); - return @retArray; + my ($item_cnt,$seed,$av,$std_dev) = @_; + my @oldseed=&random_get_seed(); + my @retArray; + &random_set_seed_from_phrase($seed); + @retArray=&math_random_normal($item_cnt,$av,$std_dev); + &random_set_seed(@oldseed); + return @retArray; } sub random_beta { - my ($item_cnt,$seed,$aa,$bb) = @_; - my @oldseed=&random_get_seed(); - my @retArray; - &random_set_seed_from_phrase($seed); - @retArray=&math_random_beta($item_cnt,$aa,$bb); - &random_set_seed(@oldseed); - return @retArray; + my ($item_cnt,$seed,$aa,$bb) = @_; + my @oldseed=&random_get_seed(); + my @retArray; + &random_set_seed_from_phrase($seed); + @retArray=&math_random_beta($item_cnt,$aa,$bb); + &random_set_seed(@oldseed); + return @retArray; } sub random_gamma { - my ($item_cnt,$seed,$a,$r) = @_; - my @oldseed=&random_get_seed(); - my @retArray; - &random_set_seed_from_phrase($seed); - @retArray=&math_random_gamma($item_cnt,$a,$r); - &random_set_seed(@oldseed); - return @retArray; + my ($item_cnt,$seed,$a,$r) = @_; + my @oldseed=&random_get_seed(); + my @retArray; + &random_set_seed_from_phrase($seed); + @retArray=&math_random_gamma($item_cnt,$a,$r); + &random_set_seed(@oldseed); + return @retArray; } sub random_exponential { - my ($item_cnt,$seed,$av) = @_; - my @oldseed=&random_get_seed(); - my @retArray; - &random_set_seed_from_phrase($seed); - @retArray=&math_random_exponential($item_cnt,$av); - &random_set_seed(@oldseed); - return @retArray; + my ($item_cnt,$seed,$av) = @_; + my @oldseed=&random_get_seed(); + my @retArray; + &random_set_seed_from_phrase($seed); + @retArray=&math_random_exponential($item_cnt,$av); + &random_set_seed(@oldseed); + return @retArray; } sub random_poisson { - my ($item_cnt,$seed,$mu) = @_; - my @oldseed=&random_get_seed(); - my @retArray; - &random_set_seed_from_phrase($seed); - @retArray=&math_random_poisson($item_cnt,$mu); - &random_set_seed(@oldseed); - return @retArray; + my ($item_cnt,$seed,$mu) = @_; + my @oldseed=&random_get_seed(); + my @retArray; + &random_set_seed_from_phrase($seed); + @retArray=&math_random_poisson($item_cnt,$mu); + &random_set_seed(@oldseed); + return @retArray; } sub random_chi { - my ($item_cnt,$seed,$df) = @_; - my @oldseed=&random_get_seed(); - my @retArray; - &random_set_seed_from_phrase($seed); - @retArray=&math_random_chi_square($item_cnt,$df); - &random_set_seed(@oldseed); - return @retArray; + my ($item_cnt,$seed,$df) = @_; + my @oldseed=&random_get_seed(); + my @retArray; + &random_set_seed_from_phrase($seed); + @retArray=&math_random_chi_square($item_cnt,$df); + &random_set_seed(@oldseed); + return @retArray; } sub random_noncentral_chi { - my ($item_cnt,$seed,$df,$nonc) = @_; - my @oldseed=&random_get_seed(); - my @retArray; - &random_set_seed_from_phrase($seed); - @retArray=&math_random_noncentral_chi_square($item_cnt,$df,$nonc); - &random_set_seed(@oldseed); - return @retArray; + my ($item_cnt,$seed,$df,$nonc) = @_; + my @oldseed=&random_get_seed(); + my @retArray; + &random_set_seed_from_phrase($seed); + @retArray=&math_random_noncentral_chi_square($item_cnt,$df,$nonc); + &random_set_seed(@oldseed); + return @retArray; } sub random_f { - my ($item_cnt,$seed,$dfn,$dfd) = @_; - my @oldseed=&random_get_seed(); - my @retArray; - &random_set_seed_from_phrase($seed); - @retArray=&math_random_f($item_cnt,$dfn,$dfd); - &random_set_seed(@oldseed); - return @retArray; + my ($item_cnt,$seed,$dfn,$dfd) = @_; + my @oldseed=&random_get_seed(); + my @retArray; + &random_set_seed_from_phrase($seed); + @retArray=&math_random_f($item_cnt,$dfn,$dfd); + &random_set_seed(@oldseed); + return @retArray; } sub random_noncentral_f { - my ($item_cnt,$seed,$dfn,$dfd,$nonc) = @_; - my @oldseed=&random_get_seed(); - my @retArray; - &random_set_seed_from_phrase($seed); - @retArray=&math_random_noncentral_f($item_cnt,$dfn,$dfd,$nonc); - &random_set_seed(@oldseed); - return @retArray; + my ($item_cnt,$seed,$dfn,$dfd,$nonc) = @_; + my @oldseed=&random_get_seed(); + my @retArray; + &random_set_seed_from_phrase($seed); + @retArray=&math_random_noncentral_f($item_cnt,$dfn,$dfd,$nonc); + &random_set_seed(@oldseed); + return @retArray; } sub random_multivariate_normal { - my ($item_cnt,$seed,$mean,$covar) = @_; - my @oldseed=&random_get_seed(); - &random_set_seed_from_phrase($seed); - @retArray=&math_random_multivariate_normal($item_cnt,@$mean,@$covar); - &random_set_seed(@oldseed); - return @retArray; + my ($item_cnt,$seed,$mean,$covar) = @_; + my @oldseed=&random_get_seed(); + &random_set_seed_from_phrase($seed); + my @retArray=&math_random_multivariate_normal($item_cnt,@$mean,@$covar); + &random_set_seed(@oldseed); + return @retArray; } sub random_multinomial { - my ($item_cnt,$seed,@p) = @_; - my @oldseed=&random_get_seed(); - my @retArray; - &random_set_seed_from_phrase($seed); - @retArray=&math_random_multinomial($item_cnt,@p); - &random_set_seed(@oldseed); - return @retArray; + my ($item_cnt,$seed,@p) = @_; + my @oldseed=&random_get_seed(); + my @retArray; + &random_set_seed_from_phrase($seed); + my @retArray=&math_random_multinomial($item_cnt,@p); + &random_set_seed(@oldseed); + return @retArray; } sub random_permutation { - my ($seed,@inArray) = @_; - my @oldseed=&random_get_seed(); - my @retArray; - &random_set_seed_from_phrase($seed); - @retArray=&math_random_permutation(@inArray); - &random_set_seed(@oldseed); - return @retArray; + my ($seed,@inArray) = @_; + my @oldseed=&random_get_seed(); + my @retArray; + &random_set_seed_from_phrase($seed); + @retArray=&math_random_permutation(@inArray); + &random_set_seed(@oldseed); + return @retArray; } sub random_uniform { - my ($item_cnt,$seed,$low,$high) = @_; - my @oldseed=&random_get_seed(); - my @retArray; - &random_set_seed_from_phrase($seed); - @retArray=&math_random_uniform($item_cnt,$low,$high); - &random_set_seed(@oldseed); - return @retArray; + my ($item_cnt,$seed,$low,$high) = @_; + my @oldseed=&random_get_seed(); + my @retArray; + &random_set_seed_from_phrase($seed); + @retArray=&math_random_uniform($item_cnt,$low,$high); + &random_set_seed(@oldseed); + return @retArray; } sub random_uniform_integer { - my ($item_cnt,$seed,$low,$high) = @_; - my @oldseed=&random_get_seed(); - my @retArray; - &random_set_seed_from_phrase($seed); - @retArray=&math_random_uniform_integer($item_cnt,$low,$high); - &random_set_seed(@oldseed); - return @retArray; + my ($item_cnt,$seed,$low,$high) = @_; + my @oldseed=&random_get_seed(); + my @retArray; + &random_set_seed_from_phrase($seed); + @retArray=&math_random_uniform_integer($item_cnt,$low,$high); + &random_set_seed(@oldseed); + return @retArray; } sub random_binomial { - my ($item_cnt,$seed,$nt,$p) = @_; - my @oldseed=&random_get_seed(); - my @retArray; - &random_set_seed_from_phrase($seed); - @retArray=&math_random_binomial($item_cnt,$nt,$p); - &random_set_seed(@oldseed); - return @retArray; + my ($item_cnt,$seed,$nt,$p) = @_; + my @oldseed=&random_get_seed(); + my @retArray; + &random_set_seed_from_phrase($seed); + @retArray=&math_random_binomial($item_cnt,$nt,$p); + &random_set_seed(@oldseed); + return @retArray; } sub random_negative_binomial { - my ($item_cnt,$seed,$ne,$p) = @_; - my @oldseed=&random_get_seed(); - my @retArray; - &random_set_seed_from_phrase($seed); - @retArray=&math_random_negative_binomial($item_cnt,$ne,$p); - &random_set_seed(@oldseed); - return @retArray; -} - -sub abs { abs(shift) } -sub sin { sin(shift) } -sub cos { cos(shift) } -sub exp { exp(shift) } -sub int { int(shift) } -sub log { log(shift) } -sub atan2 { atan2($_[0],$_[1]) } -sub sqrt { sqrt(shift) } + my ($item_cnt,$seed,$ne,$p) = @_; + my @oldseed=&random_get_seed(); + my @retArray; + &random_set_seed_from_phrase($seed); + @retArray=&math_random_negative_binomial($item_cnt,$ne,$p); + &random_set_seed(@oldseed); + return @retArray; +} -sub tan { sin($_[0]) / cos($_[0]) } +sub abs { CORE::abs(shift) } +sub sin { CORE::sin(shift) } +sub cos { CORE::cos(shift) } +sub exp { CORE::exp(shift) } +sub int { CORE::int(shift) } +sub log { CORE::log(shift) } +sub atan2 { CORE::atan2($_[0],$_[1]) } +sub sqrt { CORE::sqrt(shift) } + +sub tan { CORE::sin($_[0]) / CORE::cos($_[0]) } #sub atan { atan2($_[0], 1); } #sub acos { atan2(sqrt(1 - $_[0] * $_[0]), $_[0] ); } #sub asin { atan2($_[0], sqrt(1- $_[0] * $_[0]) ); } -sub log10 { log($_[0])/log(10); } +sub log10 { CORE::log($_[0])/CORE::log(10); } sub factorial { - my $input = int(shift); + my $input = CORE::int(shift); return "Error - unable to take factorial of an negative number ($input)" if $input < 0; return "Error - factorial result is greater than system limit ($input)" if $input > 170; return 1 if $input == 0; @@ -468,67 +640,188 @@ sub sub_string { } sub pow {return $_[0] ** $_[1]; } -sub ceil {return (($_[0]-int($_[0]))== 0.0) ? $_[0] : (($_[0] > 0) ? (int($_[0])+ 1) : int($_[0])); } -sub floor {return (($_[0]-int($_[0]))== 0.0) ? $_[0] : (($_[0] > 0) ? int($_[0]) : (int($_[0])-1)); } +sub ceil {return (($_[0]-CORE::int($_[0]))== 0.0) ? $_[0] : (($_[0] > 0) ? (CORE::int($_[0])+ 1) : CORE::int($_[0])); } +sub floor {return (($_[0]-CORE::int($_[0]))== 0.0) ? $_[0] : (($_[0] > 0) ? CORE::int($_[0]) : (CORE::int($_[0])-1)); } #sub floor {return int($_[0]); } sub format { - my ($value,$fmt)=@_; - my $dollarmode; - if ($fmt =~ /^\$(.*)/) { $fmt=$1; $dollarmode=1; } - my $result=sprintf('%.'.$fmt,$value); - $result=~s/(E[+-]*)0/$1/; - if ($dollarmode) {$result=&dollarmode($result);} - return $result; + my ($value,$fmt)=@_; + my ($dollarmode,$commamode,$alwaysperiod,$options); + if ($fmt =~ /^([^\d]*)(.*)/) { $options=$1; $fmt=$2; } + #if ($options =~ /\$/) { $dollamode=1; } + #if ($options =~ /,/) { $commamode=1; } + if ($options =~ /\./) { $alwaysperiod=1; } + my $result; + if ($fmt=~/s$/i) { + $result=&format_significant_figures($value,$fmt); + } else { + $fmt=~s/e/E/g; + $result=sprintf('%.'.$fmt,$value); + if ($alwaysperiod && $fmt eq '0f') { $result .='.'; } + $result=~s/(E[+-]*)0/$1/; + } + #if ($dollarmode) {$result=&dollarformat($result);} + #if ($commamode) {$result=&commaformat($result);} + return $result; +} + +sub chemparse { + my ($reaction) = @_; + my @tokens = split(/(\s\+|\->|<=>|<\-|\.)/,$reaction); + my $formula = ''; + foreach my $token (@tokens) { + if ($token eq '->' ) { + $formula .= '\ensuremath{\rightarrow} '; + next; + } + if ($token eq '<-' ) { + $formula .= '\ensuremath{\leftarrow} '; + next; + } + if ($token eq '<=>') { + if ($external::target eq 'web' && + &EXT('request.browser.unicode')) { + $formula .= '⇌ '; + } else { + $formula .= &web('<=> ','\ensuremath{\rightleftharpoons} ', + '<=> '); + } + next; + } + if ($token eq '.') { + $formula =~ s/(\ \;| )$//; + $formula .= '·'; + next; + } + $token =~ /^\s*([\d|\/]*(?:&frac\d\d)?)(.*)/; + $formula .= $1 if ($1 ne '1'); # stoichiometric coefficient + + my $molecule = $2; + # subscripts + $molecule =~ s|(?<=[a-zA-Z\)\]\s])(\d+)|$1|g; + # superscripts + $molecule =~ s|\^(\d*[+\-]*)|$1|g; + # strip whitespace + $molecule =~ s/\s*//g; + # forced space + $molecule =~ s/_/ /g; + $molecule =~ s/-/−/g; + $formula .= $molecule.' '; + } + # get rid of trailing space + $formula =~ s/(\ \;| )$//; + return &xmlparse($formula); } sub prettyprint { - my ($value,$fmt)=@_; - my $result; - my $dollarmode; - if ($fmt =~ /^\$(.*)/) { $fmt=$1; $dollarmode=1; } - if ($fmt) { $value=sprintf('%.'.$fmt,$value); } - if ($value =~ /([0-9\.\-\+]+)E([0-9\-\+]+)/ ) { - my $frac=$1; - if ($dollarmode) { $frac=&dollarformat($frac); } - my $exponent=$2; - $exponent=~s/^\+0*//; - $exponent=~s/^-0*/-/; - if ($exponent) { - if ($external::target eq 'web') { - $result=$frac.'×10'.$exponent.''; - } elsif ($external::target eq 'tex') { - $result='\ensuremath{'.$frac.'\times 10^{'.$exponent.'}}'; - } else { + my ($value,$fmt,$target)=@_; + my $result; + if (!$target) { $target = $external::target; } + if ($fmt =~ /chem/i) { return(&chemparse($value)); } + my ($dollarmode,$commamode,$alwaysperiod,$options); + if ($fmt =~ /^([^\d]*)(.*)/) { $options=$1; $fmt=$2; } + if ($options =~ /\$/) { $dollarmode=1; } + if ($options =~ /,/) { $commamode=1; } + if ($options =~ /\./) { $alwaysperiod=1; } + if ($fmt=~/s$/i) { + $value=&format_significant_figures($value,$fmt); + } elsif ($fmt) { + $value=sprintf('%.'.$fmt,$value); + } + if ($alwaysperiod && $fmt eq '0f') { + if ($target eq 'tex') { + $value .='\\ensuremath{.}'; + } else { + $value .='.'; + } + } + if ($value =~ /([0-9\.\-\+]+)E([0-9\-\+]+)/i ) { + my $frac=$1; + if ($dollarmode) { $frac=&dollarformat($frac); } + if ($commamode) { $frac=&commaformat($frac); } + my $exponent=$2; + $exponent=~s/^\+0*//; + $exponent=~s/^-0*/-/; + $exponent=~s/^-0*/-/; + if ($exponent eq '-') { undef($exponent); } + if ($exponent) { + if ($target eq 'web') { + $result=$frac.'×10'.$exponent.''; + } elsif ($target eq 'tex') { + $result='\ensuremath{'.$frac.'\times 10^{'.$exponent.'}}'; + } else { + $result=$value; + } + } else { + $result=$frac; + } + } else { $result=$value; - } + if ($dollarmode) { $result=&dollarformat($result,$target); } + elsif ($commamode) { $result=&commaformat($result,$target); } + } + return $result; +} + +sub commaformat { + my ($number,$target) = @_; + if ($number =~ /\./) { + while ($number =~ /([^0-9]*)([0-9]+)([^\.,][^\.,][^\.,])([,0-9]*\.[0-9]*)$/) { + $number = $1.$2.','.$3.$4; + } } else { - $result=$frac; + while ($number =~ /^([^0-9]*)([0-9]+)([^,][^,][^,])([,0-9]*)$/) { + $number = $1.$2.','.$3.$4; + } } - } else { - $result=$value; - if ($dollarmode) { $result=&dollarformat($result); } - } - return $result; + return $number; } sub dollarformat { - my ($number) = @_; - if ($number =~ /\./) { - while ($number =~ /([^\.,]+)([^\.,][^\.,][^\.,])([,0-9]*\.[0-9]*$)/) { - $number = $1.','.$2.$3; - } - } else { - while ($number =~ /([^,]+)([^,][^,][^,])([,0-9]*)$/) { - $number = $1.','.$2.$3; - } - } - if ($external::target eq 'tex') { - $number='\$'.$number; #' stupid emacs - } else { - $number='$'.$number; #' stupid emacs - } - return $number; + my ($number,$target) = @_; + if (!$target) { $target = $external::target; } + $number=&commaformat($number,$target); + if ($target eq 'tex') { + $number='\$'.$number; #' stupid emacs + } else { + $number='$'.$number; #' stupid emacs + } + return $number; +} + +# format of form ns or nS where n is an integer +sub format_significant_figures { + my ($number,$format) = @_; + return '0' if ($number == 0); + # extract number of significant figures needed + my ($sig) = ($format =~ /(\d+)s/i); + # arbitrary choice - suggestions ?? or throw error message? + $sig = 3 if ($sig eq ''); + # save the minus sign + my $sign = ($number < 0) ? '-' : ''; + $number = abs($number); + # needed to correct for a number greater than 1 (or + my $power = ($number < 1) ? 0 : 1; + # could round up. Take the integer part of log10. + my $x10 = int(log($number)/log(10)); + # find number with values left of decimal pt = # of sign figs. + my $xsig = $number*10**($sig-$x10-$power); + # get just digits left of decimal pt - also rounds off correctly + my $xint = sprintf('%.0f',$xsig); + # save any trailing zero's + my ($zeros) = ($xint =~ /(0+)$/); + # return number to original magnitude + my $numSig = $xint*10**($x10-$sig+$power); + # insert trailing zero's if have decimal point + $numSig =~ s/^(\d+)\.(\d+)(\e?(.*)?)$/$1\.$2$zeros$3/; + # put a decimal pt for number ending with 0 and length = # of sig fig + $numSig.='.' if (length($numSig) == $sig && $numSig =~ /0$/); + if (length($numSig) < $sig) { + $numSig.='.'.substr($zeros,0,($sig-length($numSig))); + } + # return number with sign + return $sign.$numSig; + } sub map { @@ -565,7 +858,7 @@ sub map { } } } - &random_set_seed(@seed); + &random_set_seed(@oldseed); return ''; } @@ -624,117 +917,151 @@ sub capa_id { return } sub problem { return } sub name{ - my $fullname = &EXT('environment.lastname').', '.&EXT('environment.firstname').' '.&EXT('environment.middlename'); - $fullname = "" if $fullname eq ", "; - $fullname =~ s/\%2d/-/g; - return $fullname; + my $fullname = &EXT('environment.lastname').', '.&EXT('environment.firstname').' '.&EXT('environment.middlename'); + $fullname = "" if $fullname eq ", "; + $fullname =~ s/\%2d/-/g; + return $fullname; } sub student_number { - my $id = &EXT('environment.id'); - $id = '' if $id eq ""; - return $id; + my $id = &EXT('environment.id'); + $id = '' if $id eq ""; + return $id; } sub class { - my $course = &EXT('course.description'); - $course = '' if $course eq ""; - return $course; + my $course = &EXT('course.description'); + $course = '' if $course eq ""; + return $course; +} + +sub firstname { + my $firstname = &EXT('environment.firstname'); + $firstname = '' if $firstname eq ""; + return $firstname; +} + +sub lastname { + my $lastname = &EXT('environment.lastname'); + $lastname = '' if $lastname eq ""; + return $lastname; } sub sec { - my $sec = &EXT('request.course.sec'); - $sec = '' if $sec eq ""; - return $sec; + my $sec = &EXT('request.course.sec'); + $sec = '' if $sec eq ""; + return $sec; } sub open_date { - my @dc = split(/\s+/,localtime(&EXT('resource.0.opendate'))); - return '' if ($dc[0] eq "Wed" and $dc[2] == 31 and $dc[4] == 1969); - my @hm = split(/:/,$dc[3]); - my $ampm = " am"; - if ($hm[0] > 12) { - $hm[0]-=12; - $ampm = " pm"; - } - return $dc[0].', '.$dc[1].' '.$dc[2].', '.$dc[4].' at '.$hm[0].':'.$hm[1].$ampm; + my @dc = split(/\s+/,localtime(&EXT('resource.0.opendate'))); + return '' if ($dc[0] eq "Wed" and $dc[2] == 31 and $dc[4] == 1969); + my @hm = split(/:/,$dc[3]); + my $ampm = " am"; + if ($hm[0] > 12) { + $hm[0]-=12; + $ampm = " pm"; + } + return $dc[0].', '.$dc[1].' '.$dc[2].', '.$dc[4].' at '.$hm[0].':'.$hm[1].$ampm; } sub due_date { - my @dc = split(/\s+/,localtime(&EXT('resource.0.duedate'))); - return '' if ($dc[0] eq "Wed" and $dc[2] == 31 and $dc[4] == 1969); - my @hm = split(/:/,$dc[3]); - my $ampm = " am"; - if ($hm[0] > 12) { - $hm[0]-=12; - $ampm = " pm"; - } - return $dc[0].', '.$dc[1].' '.$dc[2].', '.$dc[4].' at '.$hm[0].':'.$hm[1].$ampm; -# return $dc[0].', '.$dc[1].' '.$dc[2].', '.$dc[4].' at '.$dc[3]; + my @dc = split(/\s+/,localtime(&EXT('resource.0.duedate'))); + return '' if ($dc[0] eq "Wed" and $dc[2] == 31 and $dc[4] == 1969); + my @hm = split(/:/,$dc[3]); + my $ampm = " am"; + if ($hm[0] > 12) { + $hm[0]-=12; + $ampm = " pm"; + } + return $dc[0].', '.$dc[1].' '.$dc[2].', '.$dc[4].' at '.$hm[0].':'.$hm[1].$ampm; } sub answer_date { - my @dc = split(/\s+/,localtime(&EXT('resource.0.answerdate'))); - return '' if ($dc[0] eq "Wed" and $dc[2] == 31 and $dc[4] == 1969); - my @hm = split(/:/,$dc[3]); - my $ampm = " am"; - if ($hm[0] > 12) { - $hm[0]-=12; - $ampm = " pm"; - } - return $dc[0].', '.$dc[1].' '.$dc[2].', '.$dc[4].' at '.$hm[0].':'.$hm[1].$ampm; -# return $dc[0].', '.$dc[1].' '.$dc[2].', '.$dc[4].' at '.$dc[3]; + my @dc = split(/\s+/,localtime(&EXT('resource.0.answerdate'))); + return '' if ($dc[0] eq "Wed" and $dc[2] == 31 and $dc[4] == 1969); + my @hm = split(/:/,$dc[3]); + my $ampm = " am"; + if ($hm[0] > 12) { + $hm[0]-=12; + $ampm = " pm"; + } + return $dc[0].', '.$dc[1].' '.$dc[2].', '.$dc[4].' at '.$hm[0].':'.$hm[1].$ampm; } sub array_moments { - my @input=@_; - my (@output,$N); - $N=scalar (@input); - $output[0]=$N; - if ($N <= 1) { - $output[1]=$input[0]; - $output[1]="Input array not defined" if ($N == 0); - $output[2]="variance undefined for N<=1"; - $output[3]="skewness undefined for N<=1"; - $output[4]="kurtosis undefined for N<=1"; + my @input=@_; + my (@output,$N); + $N=scalar (@input); + $output[0]=$N; + if ($N <= 1) { + $output[1]=$input[0]; + $output[1]="Input array not defined" if ($N == 0); + $output[2]="variance undefined for N<=1"; + $output[3]="skewness undefined for N<=1"; + $output[4]="kurtosis undefined for N<=1"; + return @output; + } + my $sum=0; + foreach my $line (@input) { + $sum+=$line; + } + $output[1] = $sum/$N; + my ($x,$sdev,$var,$skew,$kurt) = 0; + foreach my $line (@input) { + $x=$line-$output[1]; + $var+=$x**2; + $skew+=$x**3; + $kurt+=$x**4; + } + $output[2]=$var/($N-1); + $sdev=CORE::sqrt($output[2]); + if ($sdev == 0) { + $output[3]="inf-variance=0"; + $output[4]="inf-variance=0"; + return @output; + } + $output[3]=$skew/($sdev**3*$N); + $output[4]=$kurt/($sdev**4*$N)-3; return @output; - } - my $sum=0; - foreach my $line (@input) { - $sum+=$line; - } - $output[1] = $sum/$N; - my ($x,$sdev,$var,$skew,$kurt) = 0; - foreach my $line (@input) { - $x=$line-$output[1]; - $var+=$x**2; - $skew+=$x**3; - $kurt+=$x**4; - } - $output[2]=$var/($N-1); - $sdev=sqrt($output[2]); - if ($sdev == 0) { - $output[3]="inf-variance=0"; - $output[4]="inf-variance=0"; - return @output; - } - $output[3]=$skew/($sdev**3*$N); - $output[4]=$kurt/($sdev**4*$N)-3; - return @output; } sub choose { - my $num = $_[0]; - return $_[$num]; + my $num = $_[0]; + return $_[$num]; } +#&sum1(1,$x,sub { &sum1($_[0],2*$_[0], sub { fact($_[0])**2 })}); +#sub sum1 { +# my ($start,$end,$sub)=@_; +# my $sum=0; +# for (my $i=$start;$i<=$end;$i++) { +# $sum+=&$sub($i); +# } +# return $sum +#} + +#&sum2('a',1,$x,'&sum2(\'b\',$a,2*$a, \'&factorial($b)**2\')'); +#sub sum2 { +# my ($varname,$start,$end,$line)=@_; +# my $sum=0; +# for (my $i=$start;$i<=$end;$i++) { +# my $func=sub { +# eval("\$".$varname."=$i"); +# eval($line); +# }; +# $sum+=&$func($i); +# } +# return $sum +#} + # expiremental idea sub proper_path { - my ($path)=@_; - if ( $external::target eq "tex" ) { - return '/home/httpd/html'.$path; - } else { - return $path; - } + my ($path)=@_; + if ( $external::target eq "tex" ) { + return '/home/httpd/html'.$path; + } else { + return $path; + } }