--- loncom/homework/default_homework.lcpm 2014/06/16 16:52:50 1.164 +++ loncom/homework/default_homework.lcpm 2019/11/06 16:20:06 1.177 @@ -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.164 2014/06/16 16:52:50 bisitz Exp $ +# $Id: default_homework.lcpm,v 1.177 2019/11/06 16:20:06 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -29,6 +29,7 @@ #init some globals $hidden::RANDOMINIT=0; +$hidden::RANDOMINITTRYPART=''; $pi=atan2(1,1)*4; $rad2deg=180.0/$pi; $deg2rad=$pi/180.0; @@ -309,10 +310,13 @@ sub caparesponse_check_list { #for numerical problems split off the unit my $part1; my $part2; + my $match_algebra = qr{^(.*[^\s])\s+([^\s]+)$}; + # FIXME: with the above regexp, algebra with spaces will not be evaluated correctly + my $match_numerical_units = qr{^([\d\.\,\s\$]*(?:(?:[xX\*]10[\^\*]*|[eE]*)[\+\-]*\d*)*(?:^|\S)\d+)([\$\s\w\^\*\/\(\)\+\-]*[^\d\.\s\,][\$\s\w\^\*\/\(\)\+\-]*)$}; if ($allowalgebra) { - ($part1,$part2)=($responses->[0][-1]=~ /^(.*[^\s])\s+([^\s]+)$/); + ($part1,$part2) = ($responses->[0][-1] =~ /$match_algebra/); } else { - ($part1,$part2)=($responses->[0][-1]=~ /^([\d\.\,\s\$]*(?:(?:[xX\*]10[\^\*]*|[eE]*)[\+\-]*\d*)*(?:^|\S)\d+)([\$\s\w\^\*\/\(\)\+\-]*[^\d\.\s\,][\$\s\w\^\*\/\(\)\+\-]*)$/); + ($part1,$part2) = ($responses->[0][-1] =~ /$match_numerical_units/); } if (defined($part1) && defined($part2)) { $responses->[0][-1]=$part1; @@ -327,6 +331,27 @@ sub caparesponse_check_list { } } } + if (scalar(@$responses) > 0 && defined $answerunit) { + # there are several response values, units should be the same for all + $part2 =~ s/^\s+|\s+$//g; + for (my $i=1; $i[$i][-1]; + my ($part1i, $part2i); + if ($allowalgebra) { + ($part1i, $part2i) = ($element =~ /$match_algebra/); + } else { + ($part1i, $part2i) = ($element =~ /$match_numerical_units/); + } + $part2i =~ s/^\s+|\s+$//g; + if (!defined $part2i) { + return 'NO_UNIT'; + } elsif ($part2i ne $part2) { + return ('UNIT_FAIL', "$part2 $part2i"); + } else { + $responses->[$i][-1] = $part1i; + } + } + } } } $unit=~s/\s//; @@ -631,19 +656,35 @@ sub hinton { sub random { 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))); - } + if (( ! $hidden::RANDOMINIT ) || + (($external::randomizetrypart ne '') && + ($hidden::RANDOMINITTRYPART ne $external::randomizetrypart))) { + if ($external::randomseed == 0) { $external::randomseed=1; } + if ($external::randomseed =~/,/) { + my ($num1,$num2) = map { abs($_); } split(/,/,$external::randomseed); + if ((!$num1) || ($num1 > 2147483398)) { + &random_set_seed_from_phrase($external::randomseed); + } else { + &random_set_seed(1,$num1); + } + } elsif ($external::randomseed =~/:/) { + my ($num1,$num2) = map { abs($_); } split(/:/,$external::randomseed); + if ((!$num1) || (!$num2) || ($num1 > 2147483562) || ($num2 > 2147483398)) { + &random_set_seed_from_phrase($external::randomseed); + } else { + &random_set_seed($num1,$num2); + } + } else { + my $num1 = int(abs($external::randomseed)); + if ((!$num1) || ($num1 > 2147483398)) { + &random_set_seed_from_phrase($external::randomseed); + } else { + &random_set_seed(1,$num1); + } + } &math_random_uniform(); $hidden::RANDOMINIT=1; + $hidden::RANDOMINITTRYPART=$external::randomizetrypart; } if (!defined($step)) { $step=1; } my $num=1+int(($end-$start)/$step); @@ -902,11 +943,19 @@ sub chemparse { my $formula = ''; foreach my $token (@tokens) { if ($token eq '->' ) { - $formula .= '\ensuremath{\rightarrow} '; + if ($external::target eq 'web') { + $formula .= '→ '; + } else { + $formula .= '\ensuremath{\rightarrow} '; + } next; } if ($token eq '<-' ) { - $formula .= '\ensuremath{\leftarrow} '; + if ($external::target eq 'web') { + $formula .= '← '; + } else { + $formula .= '\ensuremath{\leftarrow} '; + } next; } if ($token eq '<=>') { @@ -944,6 +993,18 @@ sub chemparse { return &xmlparse($formula); } +sub conv_eng_format { + my ($ans,$baseunit)=@_; + my ($value,$answer,$unit); + $baseunit =~ s{[^\w/\-\.]}{}g; + eval { + $value = &number_format_pref($ans); + }; + my ($answer,$prefix) = ($value=~ /^(.+)(\w)$/); + my $unit = $prefix.$baseunit; + return($answer,$unit); +} + sub prettyprint { my ($value,$fmt,$target)=@_; my $result; @@ -1033,17 +1094,6 @@ sub format_significant_figures { $number = abs($number); # needed to correct for a number greater than 1 (or my $power = ($number < 1) ? 0 : 1; - if ($power && $number =~ /^\d+$/) { - my $nonzeros = $number; - $nonzeros =~ s/0+$//; - if (length($number) - length($nonzeros) > 1) { - # convert to exponential form - my $n = $sig-1; - my $numSig = sprintf('%.' . $n . 'E', $number); - # return number with sign - return $sign.$numSig; - } - } # 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. @@ -1054,16 +1104,47 @@ sub format_significant_figures { 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))); + if ($numSig =~ /^(\d+)\.(\d+)/) { + # insert trailing zero's if have decimal point + my @digarray = split('',$1.$2); + my $sigcount; + while (@digarray > 0) { + my $item = shift(@digarray); + if ($item) { + $sigcount = 1 + @digarray; + last; + } + } + if (($sigcount) && ($sig >= $sigcount)) { + $zeros = substr($zeros,0,($sig - $sigcount)); + } + $numSig =~ s/^(\d+)\.(\d+)(\e?(.*)?)$/$1\.$2$zeros$3/; + } else { + if ($numSig =~ /^(\d+)e([\+\-]\d+)$/i) { + my $pre_exp = $1; + my $exponent = $2; + $numSig = $pre_exp.'.'.$zeros.'E'.$exponent; + } elsif ($numSig =~ /0$/) { + # add decimal pt for number ending with 0 and length == # of sig figs + if (length($numSig) == $sig) { + $numSig.='.'; + } elsif (length($numSig) > $sig) { + # exponential form for number ending with 0 and length > # of sig figs + my $fmtsig = $sig-1; + if ($fmtsig) { + $numSig = sprintf('%.'.$fmtsig.'E',$numSig); + } + } elsif (length($numSig) < $sig) { + $numSig.='.'.substr($zeros,0,($sig-length($numSig))); + } + } else { + if (length($numSig) < $sig) { + $numSig.='.'.substr($zeros,0,($sig-length($numSig))); + } + } } # return number with sign return $sign.$numSig; - } sub map { @@ -1208,18 +1289,78 @@ sub sec { } sub submission { - my ($partid,$responseid,$subnumber,$encode)=@_; + my ($partid,$responseid,$subnumber,$encode,$cleanupnum,$mapalias)=@_; my $sub=''; if ($subnumber) { $sub=$subnumber.':'; } my $output = - &EXT('user.resource.'.$sub.'resource.'.$partid.'.'.$responseid.'.submission'); - if ($encode) { - $output =~ s/&/&/g; - $output =~ s//>/g; - $output =~ s/"/"/g; - }; - return $output; + &EXT('user.resource.'.$sub.'resource.'.$partid.'.'.$responseid.'.submission',$mapalias); + if (ref($output) eq 'ARRAY') { + my @items = @{$output}; + if ($encode) { + @items = map { &encode_response($_); } @items; + } + if (ref($cleanupnum) eq 'HASH') { + @items = map { &cleanup_numerical_response($cleanupnum,$_); } @items; + } + return \@items; + } else { + if ($encode) { + $output = &encode_response($output); + } + if (ref($cleanupnum) eq 'HASH') { + $output = &cleanup_numerical_response($cleanupnum,$output); + } + return $output; + } +} + +sub encode_response { + my ($value) = @_; + $value =~ s/&/&/g; + $value =~ s//>/g; + $value =~ s/"/"/g; + return $value; +} + +sub cleanup_numerical_response { + my ($cleanupnum,$value) = @_; + if (ref($cleanupnum) eq 'HASH') { + if ($cleanupnum->{exponent}) { + if ($value =~ m{^(.*)[\*xX]\s*10\s*\^\s*(\+|\-)?\s*(\d+)(.*)$}) { + my $pre_exp = $1; + my $sign = $2; + my $exponent = $3; + my $post_exp = $4; + if ($pre_exp !~ /\./) { + $pre_exp .= '.'; + } + if ($sign eq '') { + $sign = '+'; + } + $value = $pre_exp.'E'.$sign.$exponent.$post_exp; + } + } + if ($cleanupnum->{comma}) { + $value =~ s{(\d+),(\d+)}{$1$2}; + } + if ($cleanupnum->{letterforzero}) { + $value =~ s/^\s*o(\.\d+)/0$1/i; + } + if ($cleanupnum->{spaces}) { + $value =~ s{^\s+|\s+$}{}g; + if ($value =~ m{^(.*)\.\s+(\d+)(.*)$}) { + my $pre_pt = $1; + my $decimal = $2; + my $post_dec = $3; + $value = $pre_pt.'.'.$decimal.$post_dec; + } + } + if ($cleanupnum->{format} =~ /^\d+s$/i) { + $value = &format_significant_figures($value,$cleanupnum->{format}); + } + } + return $value; } sub currentpart { @@ -1378,3 +1519,8 @@ sub proper_path { } } +sub input_id { + my ($part_id, $response_id, $textline_id) = @_; + return 'HWVAL_'.$part_id.'_'.$response_id.'_'.$textline_id; +} +