function mathphp($st, $varlist, $skipfactorial = false, $ignorestrings = true) { //translate a math formula to php function notation // a^b --> pow(a,b) // na --> n*a // (...)d --> (...)*d // n! --> factorial(n) // sin^-1 --> asin etc. //parenthesize variables with number endings, ie $c2^3 => ($c2)^3 //not needed since mathphp no longer used on php vars //skipfactorial: legacy: not really used anymore. Originally intended //to handle !something type ifcond. Might need to reexplore $vars = explode('|', $varlist); //security check variables (we might evaling with them later) global $disallowedvar; if (!isset($disallowedvar)) { $disallowedvar = array('$link', '$qidx', '$qnidx', '$seed', '$qdata', '$toevalqtxt', '$la', '$GLOBALS', '$laparts', '$anstype', '$kidx', '$iidx', '$tips', '$options', '$partla', '$partnum', '$score', '$disallowedvar', '$allowedmacros'); } foreach ($vars as $var) { if (in_array('$' . $var, $disallowedvar) || substr($var, 0, 7) == 'GLOBALS') { echo "disallowed variable name"; return "0;"; } } //take care of sin^-1 notation first $st = mathphppre($st); $st = preg_replace('/(\\+|\\-)\\s+(\\+|\\-)/', "\$1\$2", $st); //$exp = str_replace(" ", "", $exp); //caused problems with "x > -3" $st = str_replace("+-", "-", $st); $st = str_replace("-+", "-", $st); $st = str_replace("--", "+", $st); return mathphpinterpretline($st . ' ', $vars, $ignorestrings); }
function scorepart($anstype, $qn, $givenans, $options, $multi) { $defaultreltol = 0.0015; global $mathfuncs; if ($anstype == "number") { if (is_array($options['answer'])) { $answer = $options['answer'][$qn]; } else { $answer = $options['answer']; } if (isset($options['reltolerance'])) { if (is_array($options['reltolerance'])) { $reltolerance = $options['reltolerance'][$qn]; } else { $reltolerance = $options['reltolerance']; } } if (isset($options['abstolerance'])) { if (is_array($options['abstolerance'])) { $abstolerance = $options['abstolerance'][$qn]; } else { $abstolerance = $options['abstolerance']; } } if (isset($options['answerformat'])) { if (is_array($options['answerformat'])) { $answerformat = $options['answerformat'][$qn]; } else { $answerformat = $options['answerformat']; } } if (isset($options['reqsigfigs'])) { if (is_array($options['reqsigfigs'])) { $reqsigfigs = $options['reqsigfigs'][$qn]; } else { $reqsigfigs = $options['reqsigfigs']; } } if (isset($options['requiretimes'])) { if (is_array($options['requiretimes'])) { $requiretimes = $options['requiretimes'][$qn]; } else { $requiretimes = $options['requiretimes']; } } if (isset($options['requiretimeslistpart'])) { if (is_array($options['requiretimeslistpart'])) { $requiretimeslistpart = $options['requiretimeslistpart'][$qn]; } else { $requiretimeslistpart = $options['requiretimeslistpart']; } } if (is_array($options['partialcredit'][$qn]) || $multi > 0 && is_array($options['partialcredit'])) { $partialcredit = $options['partialcredit'][$qn]; } else { $partialcredit = $options['partialcredit']; } $givenans = str_replace('∞', 'oo', $givenans); $GLOBALS['partlastanswer'] = $givenans; if (isset($requiretimes) && checkreqtimes($givenans, $requiretimes) == 0) { return 0; } if (isset($partialcredit)) { if (!is_array($partialcredit)) { $partialcredit = explode(',', $partialcredit); } $altanswers = array(); $altweights = array(); for ($i = 0; $i < count($partialcredit); $i += 2) { $altanswers[] = $partialcredit[$i]; $altweights[] = floatval($partialcredit[$i + 1]); } } if (!isset($reltolerance) && !isset($abstolerance)) { $reltolerance = $defaultreltol; } if (isset($reqsigfigs)) { if ($reqsigfigs[0] == '=') { $exactsigfig = true; $reqsigfigs = substr($reqsigfigs, 1); } else { $exactsigfig = false; } } if ($multi > 0) { $qn = $multi * 1000 + $qn; } if ($answer === '') { if (trim($givenans) === '') { return 1; } else { return 0; } } if ($answer === '0 or ') { if (trim($givenans) === '' || trim($givenans) === '0') { return 1; } else { return 0; } } if ($givenans == null) { return 0; } if ($answerformat == 'exactlist') { $gaarr = explode(',', $givenans); $gaarrcnt = count($gaarr); $anarr = explode(',', $answer); } else { if ($answerformat == 'orderedlist') { $gamasterarr = explode(',', $givenans); $gaarr = $gamasterarr; $anarr = explode(',', $answer); } else { if ($answerformat == 'list') { $tmp = explode(',', $givenans); sort($tmp); $gaarr = array($tmp[0]); for ($i = 1; $i < count($tmp); $i++) { if ($tmp[$i] - $tmp[$i - 1] > 1.0E-12) { $gaarr[] = $tmp[$i]; } } $gaarrcnt = count($gaarr); $tmp = explode(',', $answer); sort($tmp); $anarr = array($tmp[0]); for ($i = 1; $i < count($tmp); $i++) { if ($tmp[$i] - $tmp[$i - 1] > 1.0E-12) { $anarr[] = $tmp[$i]; } } } else { $gaarr = array(str_replace(array('$', ',', ' ', '/', '^'), '', $givenans)); if (strpos($answer, '[') === false && strpos($answer, '(') === false) { $anarr = array(str_replace(',', '', $answer)); } else { $anarr = array($answer); } } } } $extrapennum = count($gaarr) + count($anarr); if ($answerformat == 'orderedlist') { if (count($gamasterarr) != count($anarr)) { return 0; } } if ($answerformat == 'parenneg') { foreach ($gaarr as $k => $v) { if ($v[0] == '(') { $gaarr[$k] = -1 * substr($v, 1, -1); } } } $correct = 0; foreach ($anarr as $i => $answer) { $foundloc = -1; if ($answerformat == 'orderedlist') { $gaarr = array($gamasterarr[$i]); } foreach ($gaarr as $j => $givenans) { $givenans = trim($givenans); if (isset($requiretimeslistpart) && checkreqtimes($givenans, $requiretimeslistpart) == 0) { continue; } $anss = explode(' or ', $answer); foreach ($anss as $anans) { if (!is_numeric($anans)) { if (preg_match('/(\\(|\\[)(-?[\\d\\.]+|-oo)\\,(-?[\\d\\.]+|oo)(\\)|\\])/', $anans, $matches)) { if ($matches[2] == '-oo') { $matches[2] = -1.0E+99; } if ($matches[3] == 'oo') { $matches[3] = 1.0E+99; } if ($matches[1] == "(" && $givenans > $matches[2] || $matches[1] == "[" && $givenans >= $matches[2]) { if ($matches[4] == ")" && $givenans < $matches[3] || $matches[4] == "]" && $givenans <= $matches[3]) { $correct += 1; $foundloc = $j; break 2; } } } else { if ($anans == "DNE" && strtoupper($givenans) == "DNE") { $correct += 1; $foundloc = $j; break 2; } else { if (($anans == "+oo" || $anans == "oo") && ($givenans == "+oo" || $givenans == "oo")) { $correct += 1; $foundloc = $j; break 2; } else { if ($anans == "-oo" && $givenans == "-oo") { $correct += 1; $foundloc = $j; break 2; } else { if (strtoupper($anans) == strtoupper($givenans)) { $correct += 1; $foundloc = $j; break 2; } } } } } } else { //{if (is_numeric($givenans)) { //$givenans = preg_replace('/[^\-\d\.eE]/','',$givenans); //strip out units, dollar signs, whatever $givenans = preg_replace('/^((-|\\+)?\\d*\\.?\\d*E?\\-?\\d*).*$/', '$1', trim($givenans)); //strip out units if (is_numeric($givenans)) { if (isset($reqsigfigs)) { if ($givenans * $anans < 0) { continue; } //move on if opposite signs if ($anans != 0) { $v = -1 * floor(-log10(abs($anans)) - 1.0E-12) - $reqsigfigs; } if (strpos($givenans, 'E') !== false) { //handle computer-style scientific notation preg_match('/^-?[1-9]\\.?(\\d*)E/', $givenans, $matches); $gasigfig = 1 + strlen($matches[1]); if ($exactsigfig) { if ($gasigfig != $reqsigfigs) { continue; } } else { if ($gasigfig < $reqsigfigs) { continue; } } if (abs($anans - $givenans) < pow(10, $v) / 2 + 1.0E-12) { $correct += 1; $foundloc = $j; break 2; } } else { if (!$exactsigfig) { //this line will reject 0.25 if the answer is 0.250 with 3 sigfigs $gadploc = strpos($givenans, '.'); if ($gadploc === false) { $gadploc = strlen($givenans); } if ($anans != 0 && $v < 0 && strlen($givenans) - $gadploc - 1 + $v < 0) { continue; } //not enough decimal places if (abs($anans - $givenans) < pow(10, $v) / 2 + 1.0E-12) { $correct += 1; $foundloc = $j; break 2; } } else { if (ltrim(prettysigfig($anans, $reqsigfigs, ''), '0') === ltrim($givenans, '0')) { $correct += 1; $foundloc = $j; break 2; } } } } else { if (isset($abstolerance)) { if (abs($anans - $givenans) < $abstolerance + ($anans == 0 || abs($anans) > 1 ? 1.0E-12 : abs($anans) * 1.0E-12)) { $correct += 1; $foundloc = $j; break 2; } } else { if ($anans == 0) { if (abs($anans - $givenans) < $reltolerance / 1000 + 1.0E-12) { $correct += 1; $foundloc = $j; break 2; } } else { if (abs($anans - $givenans) / (abs($anans) + (abs($anans) > 1 ? 1.0E-12 : abs($anans) * 1.0E-12)) < $reltolerance + 1.0E-12) { $correct += 1; $foundloc = $j; break 2; } } } } } } } } if ($foundloc > -1) { array_splice($gaarr, $foundloc, 1); //remove from list if (count($gaarr) == 0 && $answerformat != 'orderedlist') { break; //stop if no student answers left } } } if ($answerformat != 'orderedlist') { if ($gaarrcnt <= count($anarr)) { $score = $correct / count($anarr); } else { $score = $correct / count($anarr) - ($gaarrcnt - count($anarr)) / $extrapennum; //take off points for extranous stu answers } } else { $score = $correct / count($anarr); } if ($score < 0) { $score = 0; } if ($score == 0 && isset($partialcredit) && strpos($answerformat, 'list') === false && is_numeric($givenans)) { foreach ($altanswers as $i => $anans) { if (isset($reqsigfigs)) { if ($givenans * $anans < 0) { continue; } //move on if opposite signs if (!$exactsigfig) { if ($anans != 0) { $v = -1 * floor(-log10(abs($anans)) - 1.0E-12) - $reqsigfigs; } //this line will reject 0.25 if the answer is 0.250 with 3 sigfigs if ($anans != 0 && $v < 0 && strlen($givenans) - strpos($givenans, '.') - 1 + $v < 0) { continue; } //not enough decimal places if (abs($anans - $givenans) < pow(10, $v) / 2 + 1.0E-12) { $score = $altweights[$i]; break; } } else { if (ltrim(prettysigfig($anans, $reqsigfigs, ''), '0') === ltrim($givenans, '0')) { $score = $altweights[$i]; break; } } } else { if (isset($abstolerance)) { if (abs($anans - $givenans) < $abstolerance + 1.0E-12) { $score = $altweights[$i]; break; } } else { if (abs($anans - $givenans) / (abs($anans) + 0.0001) < $reltolerance + 1.0E-12) { $score = $altweights[$i]; break; } } } } } return $score; } else { if ($anstype == "choices") { if (is_array($options['questions'][$qn])) { $questions = $options['questions'][$qn]; } else { $questions = $options['questions']; } if (is_array($options['answer'])) { $answer = $options['answer'][$qn]; } else { $answer = $options['answer']; } if (isset($options['noshuffle'])) { if (is_array($options['noshuffle'])) { $noshuffle = $options['noshuffle'][$qn]; } else { $noshuffle = $options['noshuffle']; } } else { $noshuffle = "none"; } if (is_array($options['partialcredit'][$qn]) || $multi > 0 && is_array($options['partialcredit'])) { $partialcredit = $options['partialcredit'][$qn]; } else { $partialcredit = $options['partialcredit']; } if (isset($partialcredit)) { if (!is_array($partialcredit)) { $partialcredit = explode(',', $partialcredit); } $creditweight = array(); for ($i = 0; $i < count($partialcredit); $i += 2) { $creditweight[$partialcredit[$i]] = floatval($partialcredit[$i + 1]); } } if (!is_array($questions)) { echo _('Eeek! $questions is not defined or needs to be an array. Make sure $questions is defined in the Common Control section.'); return false; } if ($multi > 0) { $qn = $multi * 1000 + $qn; } if ($noshuffle == "last") { $randkeys = array_rand(array_slice($questions, 0, count($questions) - 1), count($questions) - 1); shuffle($randkeys); array_push($randkeys, count($questions) - 1); } else { if ($noshuffle == "all") { $randkeys = array_keys($questions); } else { $randkeys = array_rand($questions, count($questions)); shuffle($randkeys); } } if ($givenans === 'NA' || $givenans === null) { $GLOBALS['partlastanswer'] = $givenans; } else { $GLOBALS['partlastanswer'] = $givenans . '$!$' . $randkeys[$givenans]; } if ($givenans == null) { return 0; } if ($givenans == 'NA') { return 0; } $anss = explode(' or ', $answer); foreach ($anss as $k => $v) { $anss[$k] = intval($v); } //if ($randkeys[$givenans] == $answer) {return 1;} else { return 0;} if (in_array($randkeys[$givenans], $anss)) { return 1; } else { if (isset($partialcredit) && isset($creditweight[$randkeys[$givenans]])) { return $creditweight[$randkeys[$givenans]]; } else { return 0; } } } else { if ($anstype == "multans") { if (is_array($options['questions'][$qn])) { $questions = $options['questions'][$qn]; } else { $questions = $options['questions']; } if (isset($options['answers'])) { if (is_array($options['answers'])) { $answers = $options['answers'][$qn]; } else { $answers = $options['answers']; } } else { if (isset($options['answer'])) { if (is_array($options['answer'])) { $answers = $options['answer'][$qn]; } else { $answers = $options['answer']; } } } if (isset($options['noshuffle'])) { if (is_array($options['noshuffle'])) { $noshuffle = $options['noshuffle'][$qn]; } else { $noshuffle = $options['noshuffle']; } } if (isset($options['scoremethod'])) { if (is_array($options['scoremethod'])) { $scoremethod = $options['scoremethod'][$qn]; } else { $scoremethod = $options['scoremethod']; } } if (!is_array($questions)) { echo _('Eeek! $questions is not defined or needs to be an array. Make sure $questions is defined in the Common Control section.'); return false; } if ($multi > 0) { $qn = $multi * 1000 + $qn; } $score = 1.0; if ($noshuffle == "last") { $randqkeys = array_rand(array_slice($questions, 0, count($questions) - 1), count($questions) - 1); shuffle($randqkeys); array_push($randqkeys, count($questions) - 1); } else { if ($noshuffle == "all") { $randqkeys = array_keys($questions); } else { $randqkeys = array_rand($questions, count($questions)); shuffle($randqkeys); } } if (trim($answers) == '') { $akeys = array(); } else { $akeys = explode(",", $answers); } if (isset($scoremethod) && $scoremethod == 'answers') { $deduct = 1.0 / count($akeys); } else { $deduct = 1.0 / count($questions); } $origla = array(); for ($i = 0; $i < count($questions); $i++) { if ($i > 0) { $GLOBALS['partlastanswer'] .= "|"; } else { $GLOBALS['partlastanswer'] = ''; } $GLOBALS['partlastanswer'] .= $_POST["qn{$qn}"][$i]; if (isset($_POST["qn{$qn}"][$i])) { $origla[] = $randqkeys[$i]; } if (isset($_POST["qn{$qn}"][$i]) !== in_array($randqkeys[$i], $akeys)) { $score -= $deduct; } } $GLOBALS['partlastanswer'] .= '$!$' . implode('|', $origla); if (isset($scoremethod)) { if ($scoremethod == 'allornothing' && $score < 1) { $score = 0; } else { if ($scoremethod == 'takeanything') { $score = 1; } } } if ($score < 0) { $score = 0; } return $score; } else { if ($anstype == "matching") { if (is_array($options['questions'][$qn])) { $questions = $options['questions'][$qn]; } else { $questions = $options['questions']; } if (isset($options['answers'])) { if (is_array($options['answers'][$qn])) { $answers = $options['answers'][$qn]; } else { $answers = $options['answers']; } } else { if (isset($options['answer'])) { if (is_array($options['answer'][$qn])) { $answers = $options['answer'][$qn]; } else { $answers = $options['answer']; } } } if (is_array($options['matchlist'])) { $matchlist = $options['matchlist'][$qn]; } else { $matchlist = $options['matchlist']; } if (isset($options['noshuffle'])) { if (is_array($options['noshuffle'])) { $noshuffle = $options['noshuffle'][$qn]; } else { $noshuffle = $options['noshuffle']; } } if (!is_array($questions) || !is_array($answers)) { echo _('Eeek! $questions or $answers is not defined or needs to be an array. Make sure both are defined in the Common Control section.'); return 0; } if ($multi > 0) { $qn = $multi * 1000 + $qn; } $score = 1.0; $deduct = 1.0 / count($questions); if ($noshuffle == "questions" || $noshuffle == 'all') { $randqkeys = array_keys($questions); } else { $randqkeys = array_rand($questions, count($questions)); shuffle($randqkeys); } if ($noshuffle == "answers" || $noshuffle == 'all') { $randakeys = array_keys($answers); } else { $randakeys = array_rand($answers, count($answers)); shuffle($randakeys); } if (isset($matchlist)) { $matchlist = explode(',', $matchlist); } $origla = array(); for ($i = 0; $i < count($questions); $i++) { if ($i > 0) { $GLOBALS['partlastanswer'] .= "|"; } else { $GLOBALS['partlastanswer'] = ''; } $GLOBALS['partlastanswer'] .= $_POST["qn{$qn}-{$i}"]; if ($_POST["qn{$qn}-{$i}"] != "" && $_POST["qn{$qn}-{$i}"] != "-") { $qa = ord($_POST["qn{$qn}-{$i}"]); if ($qa < 97) { //if uppercase answer $qa -= 65; //shift A to 0 } else { //if lower case $qa -= 97; //shift a to 0 } $origla[$randqkeys[$i]] = $randakeys[$qa]; if (isset($matchlist)) { if ($matchlist[$randqkeys[$i]] != $randakeys[$qa]) { $score -= $deduct; } } else { if ($randqkeys[$i] != $randakeys[$qa]) { $score -= $deduct; } } } else { $origla[$randqkeys[$i]] = ''; $score -= $deduct; } } ksort($origla); $GLOBALS['partlastanswer'] .= '$!$' . implode('|', $origla); return $score; } else { if ($anstype == "matrix") { if (is_array($options['answer']) && isset($options['answer'][$qn])) { $answer = $options['answer'][$qn]; } else { $answer = $options['answer']; } if (isset($options['reltolerance'])) { if (is_array($options['reltolerance'])) { $reltolerance = $options['reltolerance'][$qn]; } else { $reltolerance = $options['reltolerance']; } } if (isset($options['abstolerance'])) { if (is_array($options['abstolerance'])) { $abstolerance = $options['abstolerance'][$qn]; } else { $abstolerance = $options['abstolerance']; } } if (!isset($reltolerance) && !isset($abstolerance)) { $reltolerance = $defaultreltol; } if (isset($options['answersize'])) { if (is_array($options['answersize'])) { $answersize = $options['answersize'][$qn]; } else { $answersize = $options['answersize']; } } if ($multi > 0) { $qn = $multi * 1000 + $qn; } $correct = true; $ansr = substr($answer, 2, -2); $ansr = preg_replace('/\\)\\s*\\,\\s*\\(/', ',', $ansr); $answerlist = explode(',', $ansr); foreach ($answerlist as $k => $v) { $v = eval('return (' . mathphp($v, null) . ');'); $answerlist[$k] = preg_replace('/[^\\d\\.,\\-E]/', '', $v); } //$answer = preg_replace_callback('/([^\[\(\)\]\,]+)/',"preg_mathphp_callback",$answer); //$answerlist = explode(",",preg_replace('/[^\d\.,\-]/','',$answer)); if (isset($answersize)) { for ($i = 0; $i < count($answerlist); $i++) { $givenanslist[$i] = $_POST["qn{$qn}-{$i}"]; } $GLOBALS['partlastanswer'] = implode("|", $givenanslist); } else { $givenans = preg_replace('/\\)\\s*,\\s*\\(/', '),(', $givenans); $GLOBALS['partlastanswer'] = $givenans; $givenanslist = explode(",", preg_replace('/[^\\d,\\.\\-]/', '', $givenans)); if (substr_count($answer, '),(') != substr_count($_POST["qn{$qn}"], '),(')) { $correct = false; } } for ($i = 0; $i < count($answerlist); $i++) { if (isset($abstolerance)) { if (abs($answerlist[$i] - $givenanslist[$i]) > $abstolerance - 1.0E-12) { $correct = false; break; } } else { if (abs($answerlist[$i] - $givenanslist[$i]) / (abs($answerlist[$i]) + 0.0001) > $reltolerance - 1.0E-12) { $correct = false; break; } } } if ($correct) { return 1; } else { return 0; } } else { if ($anstype == "calcmatrix") { if (is_array($options['answer'])) { $answer = $options['answer'][$qn]; } else { $answer = $options['answer']; } if (isset($options['reltolerance'])) { if (is_array($options['reltolerance'])) { $reltolerance = $options['reltolerance'][$qn]; } else { $reltolerance = $options['reltolerance']; } } if (isset($options['abstolerance'])) { if (is_array($options['abstolerance'])) { $abstolerance = $options['abstolerance'][$qn]; } else { $abstolerance = $options['abstolerance']; } } if (!isset($reltolerance) && !isset($abstolerance)) { $reltolerance = $defaultreltol; } if (isset($options['answersize'])) { if (is_array($options['answersize'])) { $answersize = $options['answersize'][$qn]; } else { $answersize = $options['answersize']; } } if (isset($options['answerformat'])) { if (is_array($options['answerformat'])) { $answerformat = $options['answerformat'][$qn]; } else { $answerformat = $options['answerformat']; } } if ($multi > 0) { $qn = $multi * 1000 + $qn; } if (!isset($answerformat)) { $answerformat = ''; } $ansformats = explode(',', $answerformat); $correct = true; $ansr = substr($answer, 2, -2); $ansr = preg_replace('/\\)\\s*\\,\\s*\\(/', ',', $ansr); $answerlist = explode(',', $ansr); foreach ($answerlist as $k => $v) { $v = eval('return (' . mathphp($v, null) . ');'); $answerlist[$k] = preg_replace('/[^\\d\\.,\\-E]/', '', $v); } //$answer = preg_replace_callback('/([^\[\(\)\]\,]+)/',"preg_mathphp_callback",$answer); //$answerlist = explode(",",preg_replace('/[^\d\.,\-E]/','',$answer)); if (isset($answersize)) { for ($i = 0; $i < count($answerlist); $i++) { $givenanslist[$i] = $_POST["qn{$qn}-{$i}"]; } $GLOBALS['partlastanswer'] = implode("|", $givenanslist); $GLOBALS['partlastanswer'] .= '$#$' . str_replace(',', '|', str_replace(array('(', ')', '[', ']'), '', $givenans)); for ($i = 0; $i < count($answerlist); $i++) { if (!checkanswerformat($givenanslist[$i], $ansformats)) { return 0; //perhaps should just elim bad answer rather than all? } } } else { $_POST["tc{$qn}"] = preg_replace('/\\)\\s*,\\s*\\(/', '),(', $_POST["tc{$qn}"]); $GLOBALS['partlastanswer'] = $_POST["tc{$qn}"] . '$#$' . $givenans; if (substr_count($answer, '),(') != substr_count($_POST["tc{$qn}"], '),(')) { $correct = false; } $tocheck = str_replace(' ', '', $_POST["tc{$qn}"]); $tocheck = str_replace(array('],[', '),(', '>,<'), ',', $tocheck); $tocheck = substr($tocheck, 2, strlen($tocheck) - 4); $tocheck = explode(',', $tocheck); foreach ($tocheck as $chkme) { if (!checkanswerformat($chkme, $ansformats)) { return 0; //perhaps should just elim bad answer rather than all? } } } $givenanslist = explode(",", preg_replace('/[^\\d\\.,\\-]/', '', $givenans)); for ($i = 0; $i < count($answerlist); $i++) { if (isset($abstolerance)) { if (abs($answerlist[$i] - $givenanslist[$i]) > $abstolerance - 1.0E-12) { $correct = false; break; } } else { if (abs($answerlist[$i] - $givenanslist[$i]) / (abs($answerlist[$i]) + 0.0001) > $reltolerance - 1.0E-12) { $correct = false; break; } } } if ($correct) { return 1; } else { return 0; } } else { if ($anstype == "ntuple" || $anstype == 'calcntuple') { if (is_array($options['answer'])) { $answer = $options['answer'][$qn]; } else { $answer = $options['answer']; } if (isset($options['reltolerance'])) { if (is_array($options['reltolerance'])) { $reltolerance = $options['reltolerance'][$qn]; } else { $reltolerance = $options['reltolerance']; } } if (isset($options['abstolerance'])) { if (is_array($options['abstolerance'])) { $abstolerance = $options['abstolerance'][$qn]; } else { $abstolerance = $options['abstolerance']; } } if (isset($options['answerformat'])) { if (is_array($options['answerformat'])) { $answerformat = $options['answerformat'][$qn]; } else { $answerformat = $options['answerformat']; } } if (isset($options['requiretimes'])) { if (is_array($options['requiretimes'])) { $requiretimes = $options['requiretimes'][$qn]; } else { $requiretimes = $options['requiretimes']; } } if (!isset($reltolerance) && !isset($abstolerance)) { $reltolerance = $defaultreltol; } if ($multi > 0) { $qn = $multi * 1000 + $qn; } if (!isset($answerformat)) { $answerformat = ''; } $givenans = str_replace('∞', 'oo', $givenans); $ansformats = explode(',', $answerformat); $answer = str_replace(' ', '', $answer); if ($anstype == 'ntuple') { $GLOBALS['partlastanswer'] = $givenans; } else { if ($anstype == 'calcntuple') { $GLOBALS['partlastanswer'] = $_POST["tc{$qn}"] . '$#$' . $givenans; //test for correct format, if specified if (checkreqtimes($_POST["tc{$qn}"], $requiretimes) == 0) { return 0; } $tocheck = str_replace(' ', '', $_POST["tc{$qn}"]); $tocheck = str_replace(array('],[', '),(', '>,<'), ',', $tocheck); $tocheck = substr($tocheck, 1, strlen($tocheck) - 2); $tocheck = explode(',', $tocheck); if ($answer != 'DNE' && $answer != 'oo') { foreach ($tocheck as $chkme) { if (!checkanswerformat($chkme, $ansformats)) { return 0; //perhaps should just elim bad answer rather than all? } } } } } if ($givenans == null) { return 0; } $givenans = str_replace(' ', '', $givenans); if ($answer == 'DNE' && strtoupper($givenans) == 'DNE') { return 1; } else { if ($answer == 'oo' && $givenans == 'oo') { return 1; } } preg_match_all('/([\\(\\[\\<\\{])(.*?)([\\)\\]\\>\\}])/', $givenans, $gaarr, PREG_SET_ORDER); //preg_match_all('/([\(\[\<\{])(.*?)([\)\]\>\}])/', $answer, $anarr, PREG_SET_ORDER); //replaced with string-based approach below. Allows eval as needed $anarr = array(); $NCdepth = 0; $lastcut = 0; $answer = makepretty($answer); for ($i = 0; $i < strlen($answer); $i++) { $dec = false; if ($answer[$i] == '(' || $answer[$i] == '[' || $answer[$i] == '<' || $answer[$i] == '{') { if ($NCdepth == 0) { $lastcut = $i; } $NCdepth++; } else { if ($answer[$i] == ')' || $answer[$i] == ']' || $answer[$i] == '>' || $answer[$i] == '}') { $NCdepth--; if ($NCdepth == 0) { $anarr[] = array('', $answer[$lastcut], substr($answer, $lastcut + 1, $i - $lastcut - 1), $answer[$i]); } } } } foreach ($anarr as $k => $v) { $ansparts = explode(',', $v[2]); foreach ($ansparts as $j => $v) { if (!is_numeric($v)) { $ansparts[$j] = eval('return(' . mathphp($v, null) . ');'); } } $anarr[$k][2] = $ansparts; } if (count($gaarr) == 0) { return 0; } $extrapennum = count($gaarr) + count($anarr); $correct = 0; foreach ($anarr as $i => $answer) { $foundloc = -1; foreach ($gaarr as $j => $givenans) { if ($answer[1] != $givenans[1] || $answer[3] != $givenans[3]) { break; } //$ansparts = explode(',',$answer[2]); $ansparts = $answer[2]; $gaparts = explode(',', $givenans[2]); if (count($ansparts) != count($gaparts)) { break; } for ($i = 0; $i < count($ansparts); $i++) { if (is_numeric($ansparts[$i]) && is_numeric($gaparts[$i])) { if (isset($abstolerance)) { if (abs($ansparts[$i] - $gaparts[$i]) >= $abstolerance + 1.0E-12) { break; } } else { if (abs($ansparts[$i] - $gaparts[$i]) / (abs($ansparts[$i]) + 0.0001) >= $reltolerance + 1.0E-12) { break; } } } else { break; } } if ($i == count($ansparts)) { $correct += 1; $foundloc = $j; break; } } if ($foundloc > -1) { array_splice($gaarr, $foundloc, 1); // remove from list if (count($gaarr) == 0) { break; } } } $score = $correct / count($anarr) - count($gaarr) / $extrapennum; if ($score < 0) { $score = 0; } return $score; } else { if ($anstype == "complex" || $anstype == 'calccomplex') { if (is_array($options['answer'])) { $answer = $options['answer'][$qn]; } else { $answer = $options['answer']; } if (isset($options['reltolerance'])) { if (is_array($options['reltolerance'])) { $reltolerance = $options['reltolerance'][$qn]; } else { $reltolerance = $options['reltolerance']; } } if (isset($options['abstolerance'])) { if (is_array($options['abstolerance'])) { $abstolerance = $options['abstolerance'][$qn]; } else { $abstolerance = $options['abstolerance']; } } if (isset($options['answerformat'])) { if (is_array($options['answerformat'])) { $answerformat = $options['answerformat'][$qn]; } else { $answerformat = $options['answerformat']; } } if (isset($options['requiretimes'])) { if (is_array($options['requiretimes'])) { $requiretimes = $options['requiretimes'][$qn]; } else { $requiretimes = $options['requiretimes']; } } if (isset($options['requiretimeslistpart'])) { if (is_array($options['requiretimeslistpart'])) { $requiretimeslistpart = $options['requiretimeslistpart'][$qn]; } else { $requiretimeslistpart = $options['requiretimeslistpart']; } } if (!isset($reltolerance) && !isset($abstolerance)) { $reltolerance = $defaultreltol; } if ($multi > 0) { $qn = $multi * 1000 + $qn; } if (!isset($answerformat)) { $answerformat = ''; } $ansformats = explode(',', $answerformat); if ($anstype == 'complex') { $GLOBALS['partlastanswer'] = $givenans; } else { if ($anstype == 'calccomplex') { $GLOBALS['partlastanswer'] = $_POST["tc{$qn}"]; //test for correct format, if specified if (checkreqtimes($_POST["tc{$qn}"], $requiretimes) == 0) { return 0; } $tocheck = explode(',', $_POST["tc{$qn}"]); foreach ($tocheck as $tchk) { if (in_array('sloppycomplex', $ansformats)) { $tchk = str_replace(array('sin', 'pi'), array('s$n', 'p$'), $tchk); if (substr_count($tchk, 'i') > 1) { return 0; } $tchk = str_replace(array('s$n', 'p$'), array('sin', 'pi'), $tchk); } else { $cpts = parsecomplex($tchk); if (!is_array($cpts)) { return 0; } if ($cpts[1][0] == '+') { $cpts[1] = substr($cpts[1], 1); } //echo $cpts[0].','.$cpts[1].'<br/>'; if ($answer != 'DNE' && (!checkanswerformat($cpts[0], $ansformats) || !checkanswerformat($cpts[1], $ansformats))) { return 0; } if ($answer != 'DNE' && isset($requiretimeslistpart) && checkreqtimes($tchk, $requiretimeslistpart) == 0) { return 0; } } } } } if (!isset($answerformat)) { $answerformat = ''; } $ansformats = explode(',', $answerformat); if ($givenans == null) { return 0; } $answer = str_replace(' ', '', makepretty($answer)); $givenans = str_replace(' ', '', $givenans); if ($answer == 'DNE' && strtoupper($givenans) == 'DNE') { return 1; } $gaarr = explode(',', $givenans); $anarr = explode(',', $answer); if (count($gaarr) == 0) { return 0; } $extrapennum = count($gaarr) + count($anarr); $correct = 0; foreach ($anarr as $i => $answer) { $cparts = parsecomplex($answer); if (!is_array($cparts)) { $ansparts = parsesloppycomplex($answer); } else { $ansparts[0] = eval('return (' . mathphp($cparts[0], null) . ');'); $ansparts[1] = eval('return (' . mathphp($cparts[1], null) . ');'); } $foundloc = -1; foreach ($gaarr as $j => $givenans) { $cparts = parsecomplex($givenans); if (!is_array($cparts)) { return 0; } else { $gaparts[0] = floatval($cparts[0]); $gaparts[1] = floatval($cparts[1]); } if (count($ansparts) != count($gaparts)) { break; } for ($i = 0; $i < count($ansparts); $i++) { if (is_numeric($ansparts[$i]) && is_numeric($gaparts[$i])) { if (isset($abstolerance)) { if (abs($ansparts[$i] - $gaparts[$i]) >= $abstolerance + 1.0E-12) { break; } } else { if (abs($ansparts[$i] - $gaparts[$i]) / (abs($ansparts[$i]) + 0.0001) >= $reltolerance + 1.0E-12) { break; } } } } if ($i == count($ansparts)) { $correct += 1; $foundloc = $j; break; } } if ($foundloc > -1) { array_splice($gaarr, $foundloc, 1); // remove from list if (count($gaarr) == 0) { break; } } } $score = $correct / count($anarr) - count($gaarr) / $extrapennum; if ($score < 0) { $score = 0; } return $score; } else { if ($anstype == "calculated") { if (is_array($options['answer'])) { $answer = $options['answer'][$qn]; } else { $answer = $options['answer']; } if (isset($options['reltolerance'])) { if (is_array($options['reltolerance'])) { $reltolerance = $options['reltolerance'][$qn]; } else { $reltolerance = $options['reltolerance']; } } if (isset($options['abstolerance'])) { if (is_array($options['abstolerance'])) { $abstolerance = $options['abstolerance'][$qn]; } else { $abstolerance = $options['abstolerance']; } } if (isset($options['answerformat'])) { if (is_array($options['answerformat'])) { $answerformat = $options['answerformat'][$qn]; } else { $answerformat = $options['answerformat']; } } if (isset($options['requiretimes'])) { if (is_array($options['requiretimes'])) { $requiretimes = $options['requiretimes'][$qn]; } else { $requiretimes = $options['requiretimes']; } } if (isset($options['requiretimeslistpart'])) { if (is_array($options['requiretimeslistpart'])) { $requiretimeslistpart = $options['requiretimeslistpart'][$qn]; } else { $requiretimeslistpart = $options['requiretimeslistpart']; } } if (!isset($reltolerance) && !isset($abstolerance)) { $reltolerance = $defaultreltol; } if ($multi > 0) { $qn = $multi * 1000 + $qn; } $givenans = str_replace(array('∞', '⁄ '), array('oo', '/'), $givenans); $GLOBALS['partlastanswer'] = $_POST["tc{$qn}"] . '$#$' . $givenans; $_POST["tc{$qn}"] = str_replace(array('∞', '⁄ '), array('oo', '/'), $_POST["tc{$qn}"]); if ($answer === '') { if (trim($_POST["tc{$qn}"]) === '') { return 1; } else { return 0; } } if ($givenans == null) { return 0; } $formatok = "all"; if (checkreqtimes($_POST["tc{$qn}"], $requiretimes) == 0) { //return 0; $formatok = "nowhole"; } $ansformats = explode(',', $answerformat); if (in_array("scinot", $ansformats)) { $answer = str_replace('xx', '*', $answer); } //pre-evaluate all instructor expressions - preg match all intervals. Return array of or options if (in_array('exactlist', $ansformats) || in_array('orderedlist', $ansformats) || in_array('list', $ansformats)) { $anarr = explode(',', $answer); foreach ($anarr as $k => $ananswer) { $aarr = explode(' or ', $ananswer); foreach ($aarr as $j => $anans) { if ($anans == '') { if (isset($GLOBALS['teacherid'])) { echo '<p>', _('Debug info: empty, missing or invalid $answer'), ' </p>'; } return 0; } if (preg_match('/(\\(|\\[)([\\d\\.]+)\\,([\\d\\.]+)(\\)|\\])/', $anans, $matches)) { $aarr[$j] = $matches; } else { if (!is_numeric($anans) && $anans != 'DNE' && $anans != 'oo' && $anans != '+oo' && $anans != '-oo') { $aarr[$j] = eval('return(' . mathphp($anans, null) . ');'); } } } $anarr[$k] = $aarr; } } else { $aarr = explode(' or ', $answer); foreach ($aarr as $j => $anans) { if ($anans == '') { if (isset($GLOBALS['teacherid'])) { echo '<p>', _('Debug info: empty, missing, or invalid $answer'), ' </p>'; } return 0; } if (preg_match('/(\\(|\\[)([\\d\\.]+)\\,([\\d\\.]+)(\\)|\\])/', $anans, $matches)) { $aarr[$j] = $matches; } else { if (!is_numeric($anans) && $anans != 'DNE' && $anans != 'oo' && $anans != '+oo' && $anans != '-oo') { if ((in_array("mixednumber", $ansformats) || in_array("sloppymixednumber", $ansformats) || in_array("mixednumberorimproper", $ansformats)) && preg_match('/^\\s*(\\-?\\s*\\d+)\\s*(_|\\s)\\s*(\\d+)\\s*\\/\\s*(\\d+)\\s*$/', $anans, $mnmatches)) { $aarr[$j] = $mnmatches[1] + ($mnmatches[1] < 0 ? -1 : 1) * ($mnmatches[3] / $mnmatches[4]); } else { $aarr[$j] = eval('return(' . mathphp($anans, null) . ');'); } } } } $answer = $aarr; } if (in_array('exactlist', $ansformats)) { $gaarr = explode(',', $givenans); //$anarr = explode(',',$answer); $orarr = explode(',', $_POST["tc{$qn}"]); } else { if (in_array('orderedlist', $ansformats)) { $gamasterarr = explode(',', $givenans); $gaarr = $gamasterarr; //$anarr = explode(',',$answer); $orarr = explode(',', $_POST["tc{$qn}"]); } else { if (in_array('list', $ansformats)) { $tmp = explode(',', $givenans); $tmpor = explode(',', $_POST["tc{$qn}"]); asort($tmp); $lastval = null; foreach ($tmp as $i => $v) { if ($lastval === null) { $gaarr[] = $tmp[$i]; $orarr[] = $tmpor[$i]; } else { if ($v - $lastval > 1.0E-12) { $gaarr[] = $tmp[$i]; $orarr[] = $tmpor[$i]; } } $lastval = $v; } $tmp = $anarr; sort($tmp); $anarr = array($tmp[0]); for ($i = 1; $i < count($tmp); $i++) { if (!is_numeric($tmp[$i]) || !is_numeric($tmp[$i - 1]) || $tmp[$i] - $tmp[$i - 1] > 1.0E-12) { $anarr[] = $tmp[$i]; } } } else { $gaarr = array(str_replace(',', '', $givenans)); $anarr = array($answer); $orarr = array($_POST["tc{$qn}"]); } } } $extrapennum = count($gaarr) + count($anarr); $gaarrcnt = count($gaarr); if (in_array('orderedlist', $ansformats)) { if (count($gamasterarr) != count($anarr)) { return 0; } } $correct = 0; foreach ($anarr as $i => $anss) { $foundloc = -1; if (in_array('orderedlist', $ansformats)) { $gaarr = array($gamasterarr[$i]); } foreach ($gaarr as $j => $givenans) { $partformatok = true; if (!checkanswerformat($orarr[$j], $ansformats)) { $formatok = "nopart"; $partformatok = false; //continue; } if (isset($requiretimeslistpart) && checkreqtimes($orarr[$j], $requiretimeslistpart) == 0) { $formatok = "nopart"; $partformatok = false; //continue; } //removed - done above already //$anss = explode(' or ',$answer); foreach ($anss as $anans) { if (!is_numeric($anans)) { $givenans = trim($givenans); /* moved to preprocessing if (preg_match('/(\(|\[)([\d\.]+)\,([\d\.]+)(\)|\])/',$anans,$matches)) { if (($matches[1]=="(" && $givenans>$matches[2]) || ($matches[1]=="[" && $givenans>=$matches[2])) { if (($matches[4]==")" && $givenans<$matches[3]) || ($matches[4]=="]" && $givenans<=$matches[3])) { $correct += 1; $foundloc = $j; break 2; } } } */ if (is_array($anans)) { if ($anans[1] == "(" && $givenans > $anans[2] || $anans[1] == "[" && $givenans >= $anans[2]) { if ($anans[4] == ")" && $givenans < $anans[3] || $anans[4] == "]" && $givenans <= $anans[3]) { if ($partformatok) { $correct += 1; } $foundloc = $j; break 2; } } } else { if ($anans == "DNE" && strtoupper($givenans) == "DNE") { if ($partformatok) { $correct += 1; } $foundloc = $j; break 2; } else { if (($anans == "+oo" || $anans == "oo") && ($givenans == "+oo" || $givenans == "oo")) { if ($partformatok) { $correct += 1; } $foundloc = $j; break 2; } else { if ($anans == "-oo" && $givenans == "-oo") { if ($partformatok) { $correct += 1; } $foundloc = $j; break 2; } } } } /* moved to preprocessing else if (is_numeric($givenans)) { //try evaling answer $eanans = eval('return('.mathphp($anans,null).');'); if (isset($abstolerance)) { if (abs($eanans-$givenans) < $abstolerance+1E-12) {$correct += 1; $foundloc = $j; break 2;} } else { if (abs($eanans - $givenans)/(abs($eanans)+.0001) < $reltolerance+1E-12) {$correct += 1; $foundloc = $j; break 2;} } }*/ } else { if (is_numeric($givenans)) { if (isset($abstolerance)) { if (abs($anans - $givenans) < $abstolerance + ($anans == 0 || abs($anans) > 1 ? 1.0E-12 : abs($anans) * 1.0E-12)) { if ($partformatok) { $correct += 1; } $foundloc = $j; break 2; } } else { if ($anans == 0) { if (abs($anans - $givenans) < $reltolerance / 1000 + 1.0E-12) { if ($partformatok) { $correct += 1; } $foundloc = $j; break 2; } } else { if (abs($anans - $givenans) / (abs($anans) + (abs($anans) > 1 ? 1.0E-12 : abs($anans) * 1.0E-12)) < $reltolerance + 1.0E-12) { if ($partformatok) { $correct += 1; } $foundloc = $j; break 2; } } } } } } } if ($foundloc > -1) { array_splice($gaarr, $foundloc, 1); //remove from list array_splice($orarr, $foundloc, 1); if (count($gaarr) == 0 && !in_array('orderedlist', $ansformats)) { break; //stop if no student answers left } } } if (in_array('orderedlist', $ansformats)) { $score = $correct / count($anarr); } else { //$score = $correct/count($anarr) - count($gaarr)/$extrapennum; //take off points for extranous stu answers if ($gaarrcnt <= count($anarr)) { $score = $correct / count($anarr); } else { $score = $correct / count($anarr) - ($gaarrcnt - count($anarr)) / $extrapennum; //take off points for extranous stu answers } } if ($score < 0) { $score = 0; } if ($formatok != "all") { $GLOBALS['partlastanswer'] .= '$f$1'; if ($formatok == 'nowhole') { $score = 0; } } return $score; } else { if ($anstype == "numfunc") { if (is_array($options['answer'])) { $answer = $options['answer'][$qn]; } else { $answer = $options['answer']; } if (isset($options['reltolerance'])) { if (is_array($options['reltolerance'])) { $reltolerance = $options['reltolerance'][$qn]; } else { $reltolerance = $options['reltolerance']; } } if (isset($options['abstolerance'])) { if (is_array($options['abstolerance'])) { $abstolerance = $options['abstolerance'][$qn]; } else { $abstolerance = $options['abstolerance']; } } if (!isset($reltolerance) && !isset($abstolerance)) { $reltolerance = $defaultreltol; } if (is_array($options['variables'])) { $variables = $options['variables'][$qn]; } else { $variables = $options['variables']; } if (isset($options['domain'])) { if (is_array($options['domain'])) { $domain = $options['domain'][$qn]; } else { $domain = $options['domain']; } } if (isset($options['requiretimes'])) { if (is_array($options['requiretimes'])) { $requiretimes = $options['requiretimes'][$qn]; } else { $requiretimes = $options['requiretimes']; } } if (isset($options['answerformat'])) { if (is_array($options['answerformat'])) { $answerformat = $options['answerformat'][$qn]; } else { $answerformat = $options['answerformat']; } } if ($multi > 0) { $qn = $multi * 1000 + $qn; } $GLOBALS['partlastanswer'] = $_POST["tc{$qn}"]; $correct = true; if (!isset($variables)) { $variables = "x"; } $variables = explode(",", $variables); $ofunc = array(); for ($i = 0; $i < count($variables); $i++) { $variables[$i] = trim($variables[$i]); //find f() function variables if (strpos($variables[$i], '(') !== false) { $ofunc[] = substr($variables[$i], 0, strpos($variables[$i], '(')); $variables[$i] = substr($variables[$i], 0, strpos($variables[$i], '(')); } } if (count($ofunc) > 0) { $flist = implode("|", $ofunc); $answer = preg_replace('/(' . $flist . ')\\(/', "\$1*sin(\$1+", $answer); } if (($v = array_search('E', $variables)) !== false) { $variables[$v] = 'varE'; $answer = str_replace('E', 'varE', $answer); } $vlist = implode("|", $variables); if (isset($domain)) { $fromto = explode(",", $domain); } else { $fromto[0] = -10; $fromto[1] = 10; } for ($i = 0; $i < 20; $i++) { for ($j = 0; $j < count($variables); $j++) { if (isset($fromto[2]) && $fromto[2] == "integers") { $tps[$i][$j] = rand($fromto[0], $fromto[1]); } else { if (isset($fromto[2 * $j + 1])) { $tps[$i][$j] = $fromto[2 * $j] + ($fromto[2 * $j + 1] - $fromto[2 * $j]) * rand(0, 499) / 500.0 + 0.001; } else { $tps[$i][$j] = $fromto[0] + ($fromto[1] - $fromto[0]) * rand(0, 499) / 500.0 + 0.001; } } } } if ($answerformat != "equation" && strpos($answer, '=') !== false) { echo 'Your $answer contains an equal sign, but you do not have $answerformat="equation" set. This question probably will not work right.'; } $ansarr = explode(' or ', $answer); foreach ($ansarr as $answer) { $correct = true; $answer = preg_replace('/[^\\w\\*\\/\\+\\=\\-\\(\\)\\[\\]\\{\\}\\,\\.\\^\\$\\!\\s]+/', '', $answer); if ($answerformat == "equation") { if (substr_count($_POST["tc{$qn}"], '=') != 1) { return 0; } $answer = preg_replace('/(.*)=(.*)/', '$1-($2)', $answer); unset($ratios); } else { if ($answerformat == "toconst") { unset($diffs); unset($realanss); } } if ($answer == '') { return 0; } $answer = mathphppre($answer); $answer = makepretty($answer); $answer = mathphp($answer, $vlist); for ($i = 0; $i < count($variables); $i++) { $answer = str_replace("(" . $variables[$i] . ")", '($tp[' . $i . '])', $answer); } $myans = explode(",", $_POST["qn{$qn}-vals"]); $cntnan = 0; $cntzero = 0; $cntbothzero = 0; $stunan = 0; $ysqrtot = 0; $reldifftot = 0; for ($i = 0; $i < 20; $i++) { for ($j = 0; $j < count($variables); $j++) { //causing problems on multipart - breaking messed up rand order /* if (isset($fromto[2]) && $fromto[2]=="integers") { $tp[$j] = rand($fromto[0],$fromto[1]); } else { $tp[$j] = $fromto[0] + ($fromto[1]-$fromto[0])*rand(0,32000)/32000.0; } */ $tp[$j] = $tps[$i][$j]; } $realans = eval("return ({$answer});"); //echo "$answer, real: $realans, my: {$myans[$i]},rel: ". (abs($myans[$i]-$realans)/abs($realans)) ."<br/>"; if (isNaN($realans)) { $cntnan++; continue; } //avoid NaN problems if ($answerformat == "equation") { //if equation, store ratios if (abs($realans) > 1.0E-6 && is_numeric($myans[$i])) { $ratios[] = $myans[$i] / $realans; if (abs($myans[$i]) <= 1.0E-8 && $realans != 0) { $cntzero++; } } else { if (abs($realans) <= 1.0E-6 && is_numeric($myans[$i]) && abs($myans[$i]) <= 1.0E-8) { $cntbothzero++; } } } else { if ($answerformat == "toconst") { $diffs[] = $myans[$i] - $realans; $realanss[] = $realans; $ysqr = $realans * $realans; $ysqrtot += 1 / ($ysqr + 0.0001); $reldifftot += ($myans[$i] - $realans) / ($ysqr + 0.0001); } else { //otherwise, compare points if (isNaN($myans[$i])) { $stunan++; } else { if (isset($abstolerance)) { if (abs($myans[$i] - $realans) > $abstolerance - 1.0E-12) { $correct = false; break; } } else { if (abs($myans[$i] - $realans) / (abs($realans) + 0.0001) > $reltolerance - 1.0E-12) { $correct = false; break; } } } } } } if ($cntnan == 20 && isset($GLOBALS['teacherid'])) { echo "<p>", _('Debug info: function evaled to Not-a-number at all test points. Check $domain'), "</p>"; } if ($stunan > 1) { //if more than 1 student NaN response return 0; } if ($answerformat == "equation") { if ($cntbothzero > 18) { $correct = true; } else { if (count($ratios) > 1) { if (count($ratios) == $cntzero) { $correct = false; return 0; } else { $meanratio = array_sum($ratios) / count($ratios); for ($i = 0; $i < count($ratios); $i++) { if (isset($abstolerance)) { if (abs($ratios[$i] - $meanratio) > $abstolerance - 1.0E-12) { $correct = false; break; } } else { if (abs($ratios[$i] - $meanratio) / (abs($meanratio) + 0.0001) > $reltolerance - 1.0E-12) { $correct = false; break; } } } } } else { $correct = false; } } } else { if ($answerformat == "toconst") { if (isset($abstolerance)) { //if abs, use mean diff - will minimize error in abs diffs $meandiff = array_sum($diffs) / count($diffs); } else { //if relative tol, use meandiff to minimize relative error $meandiff = $reldifftot / $ysqrtot; } if (is_nan($meandiff)) { $correct = false; return 0; } for ($i = 0; $i < count($diffs); $i++) { if (isset($abstolerance)) { if (abs($diffs[$i] - $meandiff) > $abstolerance - 1.0E-12) { $correct = false; break; } } else { //if ((abs($diffs[$i]-$meandiff)/(abs($meandiff)+0.0001) > $reltolerance-1E-12)) {$correct = false; break;} if (abs($diffs[$i] - $meandiff) / (abs($realanss[$i]) + 0.0001) > $reltolerance - 1.0E-12) { $correct = false; break; } } } } } if ($correct == true) { //test for correct format, if specified if (checkreqtimes(str_replace(',', '', $_POST["tc{$qn}"]), $requiretimes) == 0) { $GLOBALS['partlastanswer'] .= '$f$1'; return 0; //$correct = false; } return 1; } } return 0; } else { if ($anstype == "string") { if (is_array($options['answer'])) { $answer = $options['answer'][$qn]; } else { $answer = $options['answer']; } if (is_array($options['strflags'])) { $strflags = $options['strflags'][$qn]; } else { $strflags = $options['strflags']; } if (isset($options['scoremethod'])) { if (is_array($options['scoremethod'])) { $scoremethod = $options['scoremethod'][$qn]; } else { $scoremethod = $options['scoremethod']; } } if (isset($options['answerformat'])) { if (is_array($options['answerformat'])) { $answerformat = $options['answerformat'][$qn]; } else { $answerformat = $options['answerformat']; } } if ($multi > 0) { $qn = $multi * 1000 + $qn; } $GLOBALS['partlastanswer'] = $givenans; if (isset($scoremethod) && $scoremethod == 'takeanything' && trim($givenans) != '') { return 1; } $givenans = stripslashes($givenans); if (!isset($answerformat)) { $answerformat = "normal"; } if ($answerformat == 'list') { $gaarr = explode(',', $givenans); $anarr = explode(',', $answer); } else { $gaarr = array($givenans); $anarr = array($answer); } $strflags = str_replace(' ', '', $strflags); $strflags = explode(",", $strflags); $torem = array(); foreach ($strflags as $flag) { $pc = explode('=', $flag); if ($pc[0] == 'ignore_symbol') { $torem[] = $pc[1]; continue; } if ($pc[1] === 'true' || $pc[1] === '1' || $pc[1] === 1) { $pc[1] = true; } $flags[$pc[0]] = $pc[1]; } if (!isset($flags['compress_whitespace'])) { $flags['compress_whitespace'] = true; } if (!isset($flags['ignore_case'])) { $flags['ignore_case'] = true; } $correct = 0; foreach ($anarr as $i => $answer) { $foundloc = -1; if (count($torem) > 0) { $answer = str_replace($torem, ' ', $answer); } foreach ($gaarr as $j => $givenans) { $givenans = trim($givenans); if (count($torem) > 0) { $givenans = str_replace($torem, ' ', $givenans); } if ($flags['ignore_commas'] === true) { $givenans = str_replace(',', '', $givenans); $answer = str_replace(',', '', $answer); } if ($flags['compress_whitespace'] === true) { $givenans = preg_replace('/\\s+/', ' ', $givenans); $answer = preg_replace('/\\s+/', ' ', $answer); } if ($flags['trim_whitespace'] === true || $flags['compress_whitespace'] === true) { $givenans = trim($givenans); $answer = trim($answer); } if ($flags['remove_whitespace'] === true) { $givenans = trim(preg_replace('/\\s+/', '', $givenans)); } $specialor = false; if ($flags['special_or'] === true) { $specialor = true; } if ($flags['ignore_case'] === true) { $givenans = strtoupper($givenans); $answer = strtoupper($answer); if ($specialor) { $anss = explode(' *OR* ', $answer); } else { $anss = explode(' OR ', $answer); } } else { if ($specialor) { $anss = explode(' *or* ', $answer); } else { $anss = explode(' or ', $answer); } } if ($flags['ignore_order']) { $givenans = explode("\n", chunk_split($givenans, 1, "\n")); sort($givenans, SORT_STRING); $givenans = implode('', $givenans); } foreach ($anss as $anans) { if ($flags['ignore_order'] === true) { $anans = explode("\n", chunk_split($anans, 1, "\n")); sort($anans, SORT_STRING); $anans = implode('', $anans); } if ($flags['trim_whitespace'] === true || $flags['compress_whitespace'] === true) { $anans = trim($anans); } if ($flags['remove_whitespace'] === true) { $anans = trim(preg_replace('/\\s+/', '', $anans)); } if ($flags['partial_credit'] === true && $answerformat != 'list') { $poss = strlen($anans); $dist = levenshtein($anans, $givenans); $score = ($poss - $dist) / $poss; if ($score > $correct) { $correct = $score; } } else { if (isset($flags['allow_diff'])) { if (levenshtein($anans, $givenans) <= 1 * $flags['allow_diff']) { $correct += 1; $foundloc = $j; break 2; } } else { if (isset($flags['in_answer'])) { if (strpos($givenans, $anans) !== false) { $correct += 1; $foundloc = $j; break 2; } } else { if (!strcmp($anans, $givenans)) { $correct += 1; $foundloc = $j; break 2; } } } } } } if ($foundloc > -1) { array_splice($gaarr, $foundloc, 1); //remove from list if (count($gaarr) == 0) { break; //stop if no student answers left } } } $score = $correct / count($anarr); if ($score < 0) { $score = 0; } return $score; //return $correct; } else { if ($anstype == "essay") { require_once "../includes/htmLawed.php"; $htmlawedconfig = array('elements' => '*-script-form'); $givenans = addslashes(htmLawed(stripslashes($givenans), $htmlawedconfig)); $givenans = preg_replace('/&(\\w+;)/', "%\$1", $givenans); $GLOBALS['partlastanswer'] = $givenans; if (isset($options['scoremethod'])) { if (is_array($options['scoremethod'])) { $scoremethod = $options['scoremethod'][$qn]; } else { $scoremethod = $options['scoremethod']; } } if (isset($scoremethod) && $scoremethod == 'takeanything' && trim($givenans) != '') { return 1; } else { if (trim($givenans) == '') { return 0; } else { return -2; } } } else { if ($anstype == 'interval' || $anstype == 'calcinterval') { if (is_array($options['answer'])) { $answer = $options['answer'][$qn]; } else { $answer = $options['answer']; } if (isset($options['reltolerance'])) { if (is_array($options['reltolerance'])) { $reltolerance = $options['reltolerance'][$qn]; } else { $reltolerance = $options['reltolerance']; } } if (isset($options['abstolerance'])) { if (is_array($options['abstolerance'])) { $abstolerance = $options['abstolerance'][$qn]; } else { $abstolerance = $options['abstolerance']; } } if (isset($options['answerformat'])) { if (is_array($options['answerformat'])) { $answerformat = $options['answerformat'][$qn]; } else { $answerformat = $options['answerformat']; } } if (isset($options['requiretimes'])) { if (is_array($options['requiretimes'])) { $requiretimes = $options['requiretimes'][$qn]; } else { $requiretimes = $options['requiretimes']; } } if (isset($options['variables'])) { if (is_array($options['variables'])) { $variables = $options['variables'][$qn]; } else { $variables = $options['variables']; } } if (!isset($variables)) { $variables = 'x'; } if (!isset($reltolerance) && !isset($abstolerance)) { $reltolerance = $defaultreltol; } if ($multi > 0) { $qn = $multi * 1000 + $qn; } $ansformats = explode(',', $answerformat); $givenans = str_replace('∞', 'oo', $givenans); $givenans = str_replace(array('(', ')'), array('(', ')'), $givenans); if ($anstype == 'interval') { $GLOBALS['partlastanswer'] = $givenans; } else { if ($anstype == 'calcinterval') { $GLOBALS['partlastanswer'] = $_POST["tc{$qn}"]; //test for correct format, if specified if (checkreqtimes($_POST["tc{$qn}"], $requiretimes) == 0) { return 0; } if (in_array('inequality', $ansformats)) { $_POST["tc{$qn}"] = str_replace('or', ' or ', $_POST["tc{$qn}"]); preg_match_all('/([a-zA-Z]\\(\\s*[a-zA-Z]\\s*\\)|[a-zA-Z]+)/', $_POST["tc{$qn}"], $matches); foreach ($matches[0] as $var) { $var = str_replace(' ', '', $var); if (in_array($var, $mathfuncs)) { continue; } if ($var != 'or' && $var != 'and' && $var != $variables && $_POST["qn{$qn}"] != "(-oo,oo)") { return 0; } } $orarr = explode(' or ', $_POST["tc{$qn}"]); foreach ($orarr as $opt) { $opt = trim($opt); if ($opt == 'DNE' || $givenans == '(-oo,oo)') { continue; } //DNE or all real numbers $opts = preg_split('/(<=?|>=?)/', $opt); foreach ($opts as $optp) { $optp = trim($optp); if ($optp == $variables) { continue; } if (!checkanswerformat($optp, $ansformats)) { return 0; } } } } else { if (in_array('list', $ansformats)) { $orarr = preg_split('/(?<=[\\)\\]]),(?=[\\(\\[])/', $_POST["tc{$qn}"]); } else { $orarr = explode('U', $_POST["tc{$qn}"]); } foreach ($orarr as $opt) { $opt = trim($opt); if ($opt == 'DNE') { continue; } $opts = explode(',', substr($opt, 1, strlen($opt) - 2)); if (strpos($opts[0], 'oo') === false && !checkanswerformat($opts[0], $ansformats)) { return 0; } if (strpos($opts[1], 'oo') === false && !checkanswerformat($opts[1], $ansformats)) { return 0; } } } } } if ($givenans == null) { return 0; } $correct = 0; $ansar = explode(' or ', $answer); $givenans = str_replace(' ', '', $givenans); foreach ($ansar as $anans) { $answer = str_replace(' ', '', $anans); if ($anans === 'DNE') { if ($givenans === 'DNE') { $correct = 1; break; } else { continue; } } if (in_array('list', $ansformats)) { $aarr = preg_split('/(?<=[\\)\\]]),(?=[\\(\\[])/', $anans); $gaarr = preg_split('/(?<=[\\)\\]]),(?=[\\(\\[])/', $givenans); } else { $aarr = explode('U', $anans); $gaarr = explode('U', $givenans); } if (count($aarr) != count($gaarr)) { continue; } foreach ($aarr as $ansint) { $ansint = trim($ansint); $anssm = substr($ansint, 0, 1); $ansem = substr($ansint, -1); $ansint = substr($ansint, 1, strlen($ansint) - 2); list($anssn, $ansen) = explode(',', $ansint); if (!is_numeric($anssn) && strpos($anssn, 'oo') === false) { $anssn = eval('return(' . mathphp($anssn, null) . ');'); } if (!is_numeric($ansen) && strpos($ansen, 'oo') === false) { $ansen = eval('return(' . mathphp($ansen, null) . ');'); } $foundloc = -1; foreach ($gaarr as $k => $gansint) { $gansint = trim($gansint); $ganssm = substr($gansint, 0, 1); $gansem = substr($gansint, -1); $gansint = substr($gansint, 1, strlen($gansint) - 2); list($ganssn, $gansen) = explode(',', $gansint); if ($anssm != $ganssm || $ansem != $gansem) { continue; } if (strpos($anssn, 'oo') !== false) { $anssn = trim($anssn); if (($anssn == 'oo' || $anssn == '+oo') && ($ganssn == 'oo' || $ganssn == '+oo')) { } else { if ($anssn == '-oo' && $ganssn == '-oo') { } else { continue; } } //if ($anssn===$ganssn) {} else {continue;} } else { if (isset($abstolerance)) { if (abs($anssn - $ganssn) < $abstolerance + 1.0E-12) { } else { continue; } } else { if (abs($anssn - $ganssn) / (abs($anssn) + 0.0001) < $reltolerance + 1.0E-12) { } else { continue; } } } if (strpos($ansen, 'oo') !== false) { $ansen = trim($ansen); if (($ansen == 'oo' || $ansen == '+oo') && ($gansen == 'oo' || $gansen == '+oo')) { } else { if ($ansen == '-oo' && $gansen == '-oo') { } else { continue; } } //if ($ansen===$gansen) {} else {continue;} } else { if (isset($abstolerance)) { if (abs($ansen - $gansen) < $abstolerance + 1.0E-12) { } else { continue; } } else { if (abs($ansen - $gansen) / (abs($ansen) + 0.0001) < $reltolerance + 1.0E-12) { } else { continue; } } } $foundloc = $k; break; } if ($foundloc > -1) { array_splice($gaarr, $foundloc, 1); } else { continue 2; } } if (count($gaarr) > 0) { //extraneous student intervals? continue 2; } $correct = 1; break; } return $correct; } else { if ($anstype == 'draw') { if ($multi > 0) { if (isset($options['grid'][$qn])) { $grid = $options['grid'][$qn]; } if (isset($options['snaptogrid'][$qn])) { $snaptogrid = $options['snaptogrid'][$qn]; } if (isset($options['answers'][$qn])) { $answers = $options['answers'][$qn]; } else { if (isset($options['answer'][$qn])) { $answers = $options['answer'][$qn]; } } if (isset($options['partweights'][$qn])) { $partweights = $options['partweights'][$qn]; } } else { if (isset($options['grid'])) { $grid = $options['grid']; } if (isset($options['snaptogrid'])) { $snaptogrid = $options['snaptogrid']; } if (isset($options['answers'])) { $answers = $options['answers']; } else { if (isset($options['answer'])) { $answers = $options['answer']; } } if (isset($options['partweights'])) { $partweights = $options['partweights']; } } if (isset($options['reltolerance'])) { if (is_array($options['reltolerance'])) { $reltolerance = $options['reltolerance'][$qn]; } else { $reltolerance = $options['reltolerance']; } } if (isset($options['abstolerance'])) { if (is_array($options['abstolerance'])) { $abstolerance = $options['abstolerance'][$qn]; } else { $abstolerance = $options['abstolerance']; } } if (isset($options['answerformat'])) { if (is_array($options['answerformat'])) { $answerformat = $options['answerformat'][$qn]; } else { $answerformat = $options['answerformat']; } } if (!isset($reltolerance)) { if (isset($GLOBALS['CFG']['AMS']['defaultdrawtol'])) { $reltolerance = $GLOBALS['CFG']['AMS']['defaultdrawtol']; } else { $reltolerance = 1; } } if ($multi > 0) { $qn = $multi * 1000 + $qn; } $GLOBALS['partlastanswer'] = $givenans; $imgborder = 5; $step = 5; if (!isset($answerformat)) { $answerformat = array('line', 'dot', 'opendot'); } else { if (!is_array($answerformat)) { $answerformat = explode(',', $answerformat); } } if ($answerformat[0] == 'numberline') { $settings = array(-5, 5, -0.5, 0.5, 1, 0, 300, 50); } else { $settings = array(-5, 5, -5, 5, 1, 1, 300, 300); } if (isset($grid)) { if (!is_array($grid)) { $grid = explode(',', $grid); } for ($i = 0; $i < count($grid); $i++) { if ($grid[$i] != '') { $settings[$i] = evalbasic($grid[$i]); } } } if ($answerformat[0] == 'numberline') { $settings[2] = -0.5; $settings[3] = 0.5; } if ($snaptogrid > 0) { list($newwidth, $newheight) = getsnapwidthheight($settings[0], $settings[1], $settings[2], $settings[3], $settings[6], $settings[7], $snaptogrid); if (($newwidth - $settings[6]) / $settings[6] < 0.1) { $settings[6] = $newwidth; } if (($newheight - $settings[7]) / $settings[7] < 0.1) { $settings[7] = $newheight; } } $pixelsperx = ($settings[6] - 2 * $imgborder) / ($settings[1] - $settings[0]); $pixelspery = ($settings[7] - 2 * $imgborder) / ($settings[3] - $settings[2]); $xtopix = create_function('$x', "return ((\$x - ({$settings[0]}))*({$pixelsperx}) + ({$imgborder}));"); $ytopix = create_function('$y', "return (({$settings[7]}) - (\$y- ({$settings[2]}))*({$pixelspery}) - ({$imgborder}));"); $anslines = array(); $ansdots = array(); $ansodots = array(); $anslineptcnt = array(); $types = array(); $extrastuffpenalty = 0; $linepts = 0; if (is_array($answers) && count($answers) == 0 || !is_array($answers) && $answers == '') { if ($givenans == ';;;;;;;;') { return 1; } else { return 0; } } if (!is_array($answers)) { settype($answers, "array"); } if ($answerformat[0] == "polygon" || $answerformat[0] == 'closedpolygon') { foreach ($answers as $key => $function) { $function = explode(',', $function); $pixx = ($function[0] - $settings[0]) * $pixelsperx + $imgborder; $pixy = $settings[7] - ($function[1] - $settings[2]) * $pixelspery - $imgborder; $ansdots[$key] = array($pixx, $pixy); } $isclosed = false; if (abs($ansdots[0][0] - $ansdots[count($ansdots) - 1][0]) < 0.01 && abs($ansdots[0][1] - $ansdots[count($ansdots) - 1][1]) < 0.01) { $isclosed = true; array_pop($ansdots); } list($lines, $dots, $odots, $tplines, $ineqlines) = explode(';;', $givenans); if ($lines == '') { $line = array(); } else { $lines = explode(';', $lines); $line = $lines[0]; //only use first line $line = explode('),(', substr($line, 1, strlen($line) - 2)); foreach ($line as $j => $pt) { $line[$j] = explode(',', $pt); } if ($isclosed && ($line[0][0] - $line[count($line) - 1][0]) * ($line[0][0] - $line[count($line) - 1][0]) + ($line[0][1] - $line[count($line) - 1][1]) * ($line[0][1] - $line[count($line) - 1][1]) <= 25 * max(1, $reltolerance)) { array_pop($line); } } $matchstu = array(); for ($i = 0; $i < count($ansdots); $i++) { for ($j = 0; $j < count($line); $j++) { if (($ansdots[$i][0] - $line[$j][0]) * ($ansdots[$i][0] - $line[$j][0]) + ($ansdots[$i][1] - $line[$j][1]) * ($ansdots[$i][1] - $line[$j][1]) <= 25 * max(1, $reltolerance)) { $matchstu[$i] = $j; } } } if ($isclosed && isset($matchstu[0])) { $matchstu[count($ansdots)] = $matchstu[0]; } $totaladj = 0; $correctadj = 0; for ($i = 0; $i < count($ansdots) - ($isclosed ? 0 : 1); $i++) { $totaladj++; /*if ($i==count($ansdots)-1) { if (!isset($matchstu[$i]) || !isset($matchstu[0])) { $diff = -1; } else { $diff = abs($matchstu[0]-$matchstu[$i]); } } else { */ if (!isset($matchstu[$i]) || !isset($matchstu[$i + 1])) { $diff = -1; } else { $diff = abs($matchstu[$i] - $matchstu[$i + 1]); } //} if ($diff == 1 || $isclosed && $diff == count($matchstu) - 2 && count($matchstu) != 0) { $correctadj++; } } //echo "Total adjacencies: $totaladj. Correct: $correctadj <br/>"; if ($isclosed && isset($matchstu[0])) { $vals = (count($matchstu) - 1) / max(count($line), count($ansdots)); } else { $vals = count($matchstu) / max(count($line), count($ansdots)); } $adjv = $correctadj / $totaladj; $totscore = ($vals + $adjv) / 2; //echo "Vals score: $vals, adj score: $adjv. </p>"; if (isset($abstolerance)) { if ($totscore < $abstolerance) { return 0; } else { return 1; } } else { return $totscore; } } else { if ($answerformat[0] == "twopoint") { $anscircs = array(); $ansparabs = array(); $ansabs = array(); $anssqrts = array(); $ansexps = array(); $anscoss = array(); $ansvecs = array(); $x0 = $settings[0]; $x1 = 1 / 4 * $settings[1] + 3 / 4 * $settings[0]; $x2 = 1 / 2 * $settings[1] + 1 / 2 * $settings[0]; $x3 = 3 / 4 * $settings[1] + 1 / 4 * $settings[0]; $x4 = $settings[1]; $x0p = $imgborder; $x1p = $xtopix($x1); //($x1 - $settings[0])*$pixelsperx + $imgborder; $x2p = $xtopix($x2); //($x2 - $settings[0])*$pixelsperx + $imgborder; $x3p = $xtopix($x3); //($x3 - $settings[0])*$pixelsperx + $imgborder; $x4p = $xtopix($x4); //($x4 - $settings[0])*$pixelsperx + $imgborder; $ymid = ($settings[2] + $settings[3]) / 2; $ymidp = $ytopix($ymid); //$settings[7] - ($ymid-$settings[2])*$pixelspery - $imgborder; foreach ($answers as $key => $function) { if ($function == '') { continue; } $function = explode(',', $function); //curves: function // function, xmin, xmax //dot: x,y // x,y,"closed" or "open" //form: function, color, xmin, xmax, startmaker, endmarker if (count($function) == 2 || count($function) == 3 && ($function[2] == 'open' || $function[2] == 'closed')) { //is dot $pixx = ($function[0] - $settings[0]) * $pixelsperx + $imgborder; $pixy = $settings[7] - ($function[1] - $settings[2]) * $pixelspery - $imgborder; if (count($function) == 2 || $function[2] == 'closed') { $ansdots[$key] = array($pixx, $pixy); } else { $ansodots[$key] = array($pixx, $pixy); } continue; } else { if ($function[0] == 'vector') { if (count($function) > 4) { // form "vector, x_start, y_start, x_end, y_end" $ansvecs[$key] = array('p', $xtopix($function[1]), $ytopix($function[2]), $xtopix($function[3]), $ytopix($function[4])); } else { if (count($function) > 2) { //form "vector, dx, dy" $ansvecs[$key] = array('d', $function[1] * $pixelsperx, -1 * $function[2] * $pixelspery); } } } else { if ($function[0] == 'circle') { // form "circle,x_center,y_center,radius" $anscircs[$key] = array(($function[1] - $settings[0]) * $pixelsperx + $imgborder, $settings[7] - ($function[2] - $settings[2]) * $pixelspery - $imgborder, $function[3] * $pixelsperx); } else { if (substr($function[0], 0, 2) == 'x=') { $anslines[$key] = array('x', 10000, (substr($function[0], 2) - $settings[0]) * $pixelsperx + $imgborder); } else { if (count($function) == 3) { //line segment or ray $func = makepretty($function[0]); $func = mathphp($func, 'x'); $func = str_replace("(x)", '($x)', $func); $func = create_function('$x', 'return (' . $func . ');'); if ($function[1] == '-oo') { //ray to left $y1p = $ytopix($func(floatval($function[2]) - 1)); $y2p = $ytopix($func(floatval($function[2]))); $ansvecs[$key] = array('r', $xtopix($function[2]), $y2p, $xtopix(floatval($function[2]) - 1), $y1p); } else { if ($function[2] == 'oo') { //ray to right $y1p = $ytopix($func(floatval($function[1]))); $y2p = $ytopix($func(floatval($function[1]) + 1)); $ansvecs[$key] = array('r', $xtopix($function[1]), $y1p, $xtopix(floatval($function[1]) + 1), $y2p); } else { //line seg $y1p = $ytopix($func(floatval($function[1]))); $y2p = $ytopix($func(floatval($function[2]))); $ansvecs[$key] = array('ls', $xtopix($function[1]), $y1p, $xtopix($function[2]), $y2p); } } } else { $func = makepretty($function[0]); $func = mathphp($func, 'x'); $func = str_replace("(x)", '($x)', $func); $func = create_function('$x', 'return (' . $func . ');'); $y1 = $func($x1); $y2 = $func($x2); $y3 = $func($x3); $y1p = $settings[7] - ($y1 - $settings[2]) * $pixelspery - $imgborder; $y2p = $settings[7] - ($y2 - $settings[2]) * $pixelspery - $imgborder; $y3p = $settings[7] - ($y3 - $settings[2]) * $pixelspery - $imgborder; $yop = $imgborder + $settings[3] * $pixelspery; if ($settings[0] < 0 && $settings[1] > 0) { $xop = $xtopix(0); } else { $xop = $x2p; } $settings[7] - ($y1 - $settings[2]) * $pixelspery - $imgborder; if (strpos($function[0], 'abs') !== false) { //is abs $y0 = $func($x0); $y4 = $func($x4); $y0p = $settings[7] - ($y0 - $settings[2]) * $pixelspery - $imgborder; $y4p = $settings[7] - ($y4 - $settings[2]) * $pixelspery - $imgborder; if (abs($y2 - $y1 - ($y1 - $y0)) < 1.0E-9) { //if first 3 points are colinear $slope = ($y2p - $y1p) / ($x2p - $x1p); } else { if (abs($y4 - $y3 - ($y3 - $y2)) < 1.0E-9) { //if last 3 points are colinear $slope = -1 * ($y4p - $y3p) / ($x4p - $x3p); //mult by -1 to get slope on left } } if ($slope == 0) { $anslines[$key] = array('y', $slope, $y2p); } else { $xip = ($slope * ($x4p + $x0p) + $y4p - $y0p) / (2 * $slope); //x value of "vertex" $ansabs[$key] = array($xip, $slope * ($xip - $x0p) + $y0p, $slope); } } else { if (($p = strpos($function[0], 'sqrt(')) !== false) { //is sqrt $nested = 1; for ($i = $p + 5; $i < strlen($function[0]); $i++) { if ($function[0][$i] == '(') { $nested++; } else { if ($function[0][$i] == ')') { $nested--; } } if ($nested == 0) { break; } } if ($nested == 0) { $infunc = makepretty(substr($function[0], $p + 5, $i - $p - 5)); $infunc = mathphp($infunc, 'x'); $infunc = str_replace("(x)", '($x)', $infunc); $infunc = create_function('$x', 'return (' . $infunc . ');'); $y0 = $infunc(0); $y1 = $infunc(1); $xint = -$y0 / ($y1 - $y0); $xintp = ($xint - $settings[0]) * $pixelsperx + $imgborder; $yint = $func($xint); $yintp = $settings[7] - ($yint - $settings[2]) * $pixelspery - $imgborder; $secx = $xint + ($x4 - $x0) / 5 * ($y1 > $y0 ? 1 : -1); //over 1/5 of grid width $secy = $func($secx); $secyp = $settings[7] - ($secy - $settings[2]) * $pixelspery - $imgborder; $anssqrts[$key] = array($xintp, $yintp, $secyp); } } else { if (($p = strpos($function[0], 'cos')) !== false || ($q = strpos($function[0], 'sin')) !== false) { //is sin/cos if ($p === false) { $p = $q; } $nested = 1; for ($i = $p + 4; $i < strlen($function[0]); $i++) { if ($function[0][$i] == '(') { $nested++; } else { if ($function[0][$i] == ')') { $nested--; } } if ($nested == 0) { break; } } if ($nested == 0) { $infunc = makepretty(substr($function[0], $p + 4, $i - $p - 4)); $infunc = mathphp($infunc, 'x'); $infunc = str_replace("(x)", '($x)', $infunc); $infunc = create_function('$x', 'return (' . $infunc . ');'); $y0 = $infunc(0); $y1 = $infunc(1); $period = 2 * M_PI / ($y1 - $y0); //slope of inside function $xint = -$y0 / ($y1 - $y0); if (strpos($function[0], 'sin') !== false) { $xint += $period / 4; } $secx = $xint + $period / 2; $xintp = ($xint - $settings[0]) * $pixelsperx + $imgborder; $secxp = ($secx - $settings[0]) * $pixelsperx + $imgborder; $yint = $func($xint); $yintp = $settings[7] - ($yint - $settings[2]) * $pixelspery - $imgborder; $secy = $func($secx); $secyp = $settings[7] - ($secy - $settings[2]) * $pixelspery - $imgborder; if ($yintp > $secyp) { $anscoss[$key] = array($xintp, $secxp, $yintp, $secyp); } else { $anscoss[$key] = array($secxp, $xintp, $secyp, $yintp); } } } else { if (preg_match('/\\^[^2]/', $function[0])) { //exponential $base = safepow(($yop - $y3p) / ($yop - $y1p), 1 / ($x3p - $x1p)); $str = ($yop - $y3p) / safepow($base, $x3p - $xop); $ansexps[$key] = array($str, $base); } else { if (abs($y3 - $y2 - ($y2 - $y1)) < 1.0E-9) { //colinear $slope = ($y2p - $y1p) / ($x2p - $x1p); if (abs($slope) > 1.4) { //use x value at ymid $anslines[$key] = array('x', $slope, $x1p + ($ymidp - $y1p) / $slope); } else { //use y value at x2 $anslines[$key] = array('y', $slope, $y2p); } } else { //assume parabolic for now $denom = ($x1p - $x2p) * ($x1p - $x3p) * ($x2p - $x3p); $A = ($x3p * ($y2p - $y1p) + $x2p * ($y1p - $y3p) + $x1p * ($y3p - $y2p)) / $denom; $B = ($x3p * $x3p * ($y1p - $y2p) + $x2p * $x2p * ($y3p - $y1p) + $x1p * $x1p * ($y2p - $y3p)) / $denom; $C = ($x2p * $x3p * ($x2p - $x3p) * $y1p + $x3p * $x1p * ($x3p - $x1p) * $y2p + $x1p * $x2p * ($x1p - $x2p) * $y3p) / $denom; $xt = -$B / (2 * $A) + 20; //use vertex and y value at x of vertex + 20 pixels $ansparabs[$key] = array(-$B / (2 * $A), $C - $B * $B / (4 * $A), $A * $xt * $xt + $B * $xt + $C); } } } } } } } } } } } list($lines, $dots, $odots, $tplines, $ineqlines) = explode(';;', $givenans); $lines = array(); $parabs = array(); $circs = array(); $abs = array(); $sqrts = array(); $coss = array(); $exps = array(); $vecs = array(); if ($tplines == '') { $tplines = array(); } else { $tplines = explode('),(', substr($tplines, 1, strlen($tplines) - 2)); foreach ($tplines as $k => $val) { $pts = explode(',', $val); if ($pts[0] == 5) { //line if ($pts[3] == $pts[1]) { $lines[] = array('x', 10000, $pts[1]); } else { $slope = ($pts[4] - $pts[2]) / ($pts[3] - $pts[1]); if (abs($slope) > 100) { $slope = 10000; } if (abs($slope) > 1) { $lines[] = array('x', $slope, $pts[1] + ($ymidp - $pts[2]) / $slope, $pts[2] + ($x2p - $pts[1]) * $slope); } else { $lines[] = array('y', $slope, $pts[2] + ($x2p - $pts[1]) * $slope); } } } else { if ($pts[0] == 5.2) { $vecs[] = array($pts[1], $pts[2], $pts[3], $pts[4], 'r'); } else { if ($pts[0] == 5.3) { $vecs[] = array($pts[1], $pts[2], $pts[3], $pts[4], 'ls'); } else { if ($pts[0] == 5.4) { $vecs[] = array($pts[1], $pts[2], $pts[3], $pts[4], 'v'); } else { if ($pts[0] == 6) { //parab if ($pts[4] == $pts[2]) { $lines[] = array('y', 0, $pts[4]); } else { if ($pts[3] != $pts[1]) { $a = ($pts[4] - $pts[2]) / (($pts[3] - $pts[1]) * ($pts[3] - $pts[1])); $y = $pts[2] + $a * 400; $parabs[] = array($pts[1], $pts[2], $y); } } } else { if ($pts[0] == 6.5) { //sqrt $flip = $pts[3] < $pts[1] ? -1 : 1; $stretch = ($pts[4] - $pts[2]) / sqrt($flip * ($pts[3] - $pts[1])); $secxp = $pts[1] + ($x4p - $x0p) / 5 * $flip; //over 1/5 of grid width $secyp = $stretch * sqrt($flip * ($secxp - $pts[1])) + $pts[2]; $sqrts[] = array($pts[1], $pts[2], $secyp); } else { if ($pts[0] == 7) { //circle $circs[] = array($pts[1], $pts[2], sqrt(($pts[3] - $pts[1]) * ($pts[3] - $pts[1]) + ($pts[4] - $pts[2]) * ($pts[4] - $pts[2]))); } else { if ($pts[0] == 8) { //abs if ($pts[1] == $pts[3]) { if ($pts[4] > $pts[2]) { $slope = -10000000000.0; } else { $slope = 10000000000.0; } } else { $slope = ($pts[4] - $pts[2]) / ($pts[3] - $pts[1]); if ($pts[3] > $pts[1]) { //we just found slope on right, so reverse for slope on left $slope *= -1; } } $abs[] = array($pts[1], $pts[2], $slope); } else { if ($pts[0] == 8.300000000000001) { $adjy2 = $yop - $pts[4]; $adjy1 = $yop - $pts[2]; if ($adjy1 * $adjy2 > 0 && $pts[1] != $pts[3]) { $base = safepow($adjy2 / $adjy1, 1 / ($pts[3] - $pts[1])); if (abs($pts[1] - $xop) < abs($pts[3] - $xop)) { $str = $adjy1 / safepow($base, $pts[1] - $xop); } else { $str = $adjy2 / safepow($base, $pts[3] - $xop); } //$exps[] = array($str,$base); $exps[] = array($pts[1] - $xop, $adjy1, $pts[3] - $xop, $adjy2, $base); } } else { if ($pts[0] == 9 || $pts[0] == 9.1) { if ($pts[0] == 9.1) { $pts[1] -= $pts[3] - $pts[1]; $pts[2] -= $pts[4] - $pts[2]; } if ($pts[4] > $pts[2]) { $coss[] = array($pts[3], $pts[1], $pts[4], $pts[2]); } else { $coss[] = array($pts[1], $pts[3], $pts[2], $pts[4]); } } } } } } } } } } } } } if ($dots == '') { $dots = array(); } else { $dots = explode('),(', substr($dots, 1, strlen($dots) - 2)); foreach ($dots as $k => $pt) { $dots[$k] = explode(',', $pt); } } if ($odots == '') { $odots = array(); } else { $odots = explode('),(', substr($odots, 1, strlen($odots) - 2)); foreach ($odots as $k => $pt) { $odots[$k] = explode(',', $pt); } } $scores = array(); if (count($dots) + count($odots) == 0) { $extradots = 0; } else { $extradots = max((count($dots) + count($odots) - count($ansdots) - count($ansodots)) / (count($dots) + count($odots)), 0); } foreach ($ansdots as $key => $ansdot) { $scores[$key] = 0; for ($i = 0; $i < count($dots); $i++) { if (($dots[$i][0] - $ansdot[0]) * ($dots[$i][0] - $ansdot[0]) + ($dots[$i][1] - $ansdot[1]) * ($dots[$i][1] - $ansdot[1]) <= 25 * max(1, $reltolerance)) { $scores[$key] = 1 - $extradots; break; } } } foreach ($ansodots as $key => $ansodot) { $scores[$key] = 0; for ($i = 0; $i < count($odots); $i++) { if (($odots[$i][0] - $ansodot[0]) * ($odots[$i][0] - $ansodot[0]) + ($odots[$i][1] - $ansodot[1]) * ($odots[$i][1] - $ansodot[1]) <= 25 * max(1, $reltolerance)) { $scores[$key] = 1 - $extradots; break; } } } $deftol = 0.1; $defpttol = 5; foreach ($anslines as $key => $ansline) { $scores[$key] = 0; for ($i = 0; $i < count($lines); $i++) { //check slope $toladj = pow(10, -1 - 6 * abs($ansline[1])); if (abs($ansline[1] - $lines[$i][1]) / (abs($ansline[1]) + $toladj) > $deftol * $reltolerance) { continue; } if ($ansline[0] != $lines[$i][0]) { if (abs(abs($ansline[1]) - 1) < 0.4) { //check intercept if (abs($ansline[2] - $lines[$i][3]) > $defpttol * $reltolerance) { continue; } } else { continue; } } else { if (abs($ansline[2] - $lines[$i][2]) > $defpttol * $reltolerance) { continue; } } $scores[$key] = 1; break; } } foreach ($anscircs as $key => $anscirc) { $scores[$key] = 0; for ($i = 0; $i < count($circs); $i++) { if (abs($anscirc[0] - $circs[$i][0]) > $defpttol * $reltolerance) { continue; } if (abs($anscirc[1] - $circs[$i][1]) > $defpttol * $reltolerance) { continue; } if (abs($anscirc[2] - $circs[$i][2]) > $defpttol * $reltolerance) { continue; } $scores[$key] = 1; break; } } foreach ($ansvecs as $key => $ansvec) { $scores[$key] = 0; if ($ansvec[0] == 'p') { //point for ($i = 0; $i < count($vecs); $i++) { if ($vecs[$i][4] != 'v') { continue; } if (abs($ansvec[1] - $vecs[$i][0]) > $defpttol * $reltolerance) { continue; } if (abs($ansvec[2] - $vecs[$i][1]) > $defpttol * $reltolerance) { continue; } if (abs($ansvec[3] - $vecs[$i][2]) > $defpttol * $reltolerance) { continue; } if (abs($ansvec[4] - $vecs[$i][3]) > $defpttol * $reltolerance) { continue; } $scores[$key] = 1; break; } } else { if ($ansvec[0] == 'r') { //ray for ($i = 0; $i < count($vecs); $i++) { if ($vecs[$i][4] != 'r') { continue; } //make sure base point matches if (abs($ansvec[1] - $vecs[$i][0]) > $defpttol * $reltolerance) { continue; } if (abs($ansvec[2] - $vecs[$i][1]) > $defpttol * $reltolerance) { continue; } //compare slopes $correctdx = $ansvec[3] - $ansvec[1]; $correctdy = $ansvec[4] - $ansvec[2]; $studx = $vecs[$i][2] - $vecs[$i][0]; $study = $vecs[$i][3] - $vecs[$i][1]; //find angle between correct ray and stu ray $cosang = ($studx * $correctdx + $study * $correctdy) / (sqrt($studx * $studx + $study * $study) * sqrt($correctdx * $correctdx + $correctdy * $correctdy)); $ang = acos($cosang) * 57.2957795; if (abs($ang) > 1.4 * $reltolerance) { continue; } /* slope based grading if (abs($correctdy)>abs($correctdx)) { $m = $correctdx/$correctdy; $stum = $studx/$study; } else { $m = $correctdy/$correctdx; $stum =$study/$studx; } $toladj = pow(10,-1-6*abs($m)); if (abs($m-$stum)/abs($m+$toladj)>$deftol*$reltolerance) { continue; } */ $scores[$key] = 1; break; } } else { if ($ansvec[0] == 'ls') { //line segment for ($i = 0; $i < count($vecs); $i++) { if ($vecs[$i][4] != 'ls') { continue; } if (abs($ansvec[1] - $vecs[$i][0]) > $defpttol * $reltolerance && abs($ansvec[1] - $vecs[$i][2]) > $defpttol * $reltolerance) { continue; //ans x1 doesn't match either vec x } if (abs($ansvec[1] - $vecs[$i][0]) <= $defpttol * $reltolerance) { //x1 of ans matched first vec x if (abs($ansvec[2] - $vecs[$i][1]) > $defpttol * $reltolerance) { continue; } if (abs($ansvec[3] - $vecs[$i][2]) > $defpttol * $reltolerance) { continue; } if (abs($ansvec[4] - $vecs[$i][3]) > $defpttol * $reltolerance) { continue; } } else { if (abs($ansvec[2] - $vecs[$i][3]) > $defpttol * $reltolerance) { continue; } if (abs($ansvec[3] - $vecs[$i][0]) > $defpttol * $reltolerance) { continue; } if (abs($ansvec[4] - $vecs[$i][1]) > $defpttol * $reltolerance) { continue; } } $scores[$key] = 1; break; } } else { //direction vector for ($i = 0; $i < count($vecs); $i++) { if ($vecs[$i][4] != 'v') { continue; } if (abs($ansvec[1] - ($vecs[$i][2] - $vecs[$i][0])) > $defpttol * $reltolerance) { continue; } if (abs($ansvec[2] - ($vecs[$i][3] - $vecs[$i][1])) > $defpttol * $reltolerance) { continue; } $scores[$key] = 1; break; } } } } } foreach ($ansparabs as $key => $ansparab) { $scores[$key] = 0; for ($i = 0; $i < count($parabs); $i++) { if (abs($ansparab[0] - $parabs[$i][0]) > $defpttol * $reltolerance) { continue; } if (abs($ansparab[1] - $parabs[$i][1]) > $defpttol * $reltolerance) { continue; } if (abs($ansparab[2] - $parabs[$i][2]) > $defpttol * $reltolerance) { continue; } $scores[$key] = 1; break; } } foreach ($anssqrts as $key => $anssqrt) { $scores[$key] = 0; for ($i = 0; $i < count($sqrts); $i++) { if (abs($anssqrt[0] - $sqrts[$i][0]) > $defpttol * $reltolerance) { continue; } if (abs($anssqrt[1] - $sqrts[$i][1]) > $defpttol * $reltolerance) { continue; } if (abs($anssqrt[2] - $sqrts[$i][2]) > $defpttol * $reltolerance) { continue; } $scores[$key] = 1; break; } } foreach ($ansexps as $key => $ansexp) { $scores[$key] = 0; for ($i = 0; $i < count($exps); $i++) { //if (abs($ansexp[0]-$exps[$i][0])>$defpttol*$reltolerance) { // continue; //} if (abs($ansexp[1] - $exps[$i][4]) / (abs($ansexp[1] - 1) + 1.0E-18) > $deftol * $reltolerance) { continue; } //check left point if base>1 if ($ansexp[1] > 1 && abs($ansexp[0] * safepow($ansexp[1], $exps[$i][0]) - $exps[$i][1]) > $defpttol * $reltolerance) { continue; } //check right point if base<= if ($ansexp[1] <= 1 && abs($ansexp[0] * safepow($ansexp[1], $exps[$i][2]) - $exps[$i][3]) > $defpttol * $reltolerance) { continue; } $scores[$key] = 1; break; } } foreach ($anscoss as $key => $anscos) { $scores[$key] = 0; for ($i = 0; $i < count($coss); $i++) { $per = abs($anscos[0] - $anscos[1]) * 2; $adjdiff = abs($anscos[0] - $coss[$i][0]); $adjdiff = abs($adjdiff - $per * round($adjdiff / $per)); if ($adjdiff > $defpttol * $reltolerance) { continue; } $adjdiff = abs($anscos[1] - $coss[$i][1]); $adjdiff = abs($adjdiff - $per * round($adjdiff / $per)); if ($adjdiff > $defpttol * $reltolerance) { continue; } if (abs($anscos[2] - $coss[$i][2]) > $defpttol * $reltolerance) { continue; } if (abs($anscos[3] - $coss[$i][3]) > $defpttol * $reltolerance) { continue; } $scores[$key] = 1; break; } } foreach ($ansabs as $key => $aabs) { $scores[$key] = 0; for ($i = 0; $i < count($abs); $i++) { if (abs($aabs[0] - $abs[$i][0]) > $defpttol * $reltolerance) { continue; } if (abs($aabs[1] - $abs[$i][1]) > $defpttol * $reltolerance) { continue; } //check slope $toladj = pow(10, -1 - 6 * abs($aabs[2])); if (abs($aabs[2] - $abs[$i][2]) / (abs($aabs[2]) + $toladj) > $deftol * $reltolerance) { continue; } $scores[$key] = 1; break; } } $extrastuffpenalty = max((count($tplines) - count($answers)) / max(count($answers), count($tplines)), 0); } else { if ($answerformat[0] == "inequality") { list($lines, $dots, $odots, $tplines, $ineqlines) = explode(';;', $givenans); /*$x1 = 1/3*$settings[0] + 2/3*$settings[1]; $x2 = 2/3*$settings[0] + 1/3*$settings[1]; $x1p = ($x1 - $settings[0])*$pixelsperx + $imgborder; $x2p = ($x2 - $settings[0])*$pixelsperx + $imgborder; $ymid = ($settings[2]+$settings[3])/2; $ymidp = $settings[7] - ($ymid-$settings[2])*$pixelspery - $imgborder;*/ $x1 = 1 / 4 * $settings[1] + 3 / 4 * $settings[0]; $x2 = 1 / 2 * $settings[1] + 1 / 2 * $settings[0]; $x3 = 3 / 4 * $settings[1] + 1 / 4 * $settings[0]; $x1p = ($x1 - $settings[0]) * $pixelsperx + $imgborder; $x2p = ($x2 - $settings[0]) * $pixelsperx + $imgborder; $x3p = ($x3 - $settings[0]) * $pixelsperx + $imgborder; $ymid = ($settings[2] + $settings[3]) / 2; $ymidp = $settings[7] - ($ymid - $settings[2]) * $pixelspery - $imgborder; foreach ($answers as $key => $function) { if ($function == '') { continue; } $function = explode(',', $function); if ($function[0][0] == 'x' && ($function[0][1] == '<' || $function[0][1] == '>')) { $isxequals = true; $function[0] = substr($function[0], 1); } else { $isxequals = false; } if ($function[0][1] == '=') { $type = 10; $c = 2; } else { $type = 10.2; $c = 1; } $dir = $function[0][0]; if ($isxequals) { $anslines[$key] = array('x', $dir, $type, -10000, (substr($function[0], $c) - $settings[0]) * $pixelsperx + $imgborder); } else { $func = makepretty(substr($function[0], $c)); $func = mathphp($func, 'x'); $func = str_replace("(x)", '($x)', $func); $func = create_function('$x', 'return (' . $func . ');'); $y1 = $func($x1); $y2 = $func($x2); $y3 = $func($x3); $y1p = $settings[7] - ($y1 - $settings[2]) * $pixelspery - $imgborder; $y2p = $settings[7] - ($y2 - $settings[2]) * $pixelspery - $imgborder; $y3p = $settings[7] - ($y3 - $settings[2]) * $pixelspery - $imgborder; $denom = ($x1p - $x2p) * ($x1p - $x3p) * ($x2p - $x3p); $A = ($x3p * ($y2p - $y1p) + $x2p * ($y1p - $y3p) + $x1p * ($y3p - $y2p)) / $denom; if (abs($A) > 1.0E-5) { //quadratic inequality: Contributed by Cam Joyce if ($type == 10) { //switch to quadratic $type = 10.3; } else { $type = 10.4; } $B = ($x3p * $x3p * ($y1p - $y2p) + $x2p * $x2p * ($y3p - $y1p) + $x1p * $x1p * ($y2p - $y3p)) / $denom; $C = ($x2p * $x3p * ($x2p - $x3p) * $y1p + $x3p * $x1p * ($x3p - $x1p) * $y2p + $x1p * $x2p * ($x1p - $x2p) * $y3p) / $denom; $anslines[$key] = array('y', $dir, $type, $A, -$B / (2 * $A), $C - $B * $B / (4 * $A)); } else { //linear inequality $slope = ($y2p - $y1p) / ($x2p - $x1p); if (abs($slope) > 1.4) { //use x value at ymid $anslines[$key] = array('x', $dir, $type, $slope, $x1p + ($ymidp - $y1p) / $slope); } else { //use y value at x2 $anslines[$key] = array('y', $dir, $type, $slope, $y2p); } } } } if ($ineqlines == '') { $ineqlines = array(); } else { $ineqlines = explode('),(', substr($ineqlines, 1, strlen($ineqlines) - 2)); foreach ($ineqlines as $k => $val) { $pts = explode(',', $val); if ($pts[0] < 10.3) { //linear if ($pts[3] == $pts[1]) { $slope = 10000; } else { $slope = ($pts[4] - $pts[2]) / ($pts[3] - $pts[1]); } if (abs($slope) > 50) { if ($pts[5] > $pts[3]) { $dir = '>'; } else { $dir = '<'; } $ineqlines[$k] = array('x', $dir, $pts[0], -10000, $pts[1]); } else { $yatpt5 = $slope * ($pts[5] - $pts[1]) + $pts[2]; if ($yatpt5 < $pts[6]) { $dir = '<'; } else { $dir = '>'; } if (abs($slope) > 50) { $slope = -10000; } if (abs($slope) > 1) { $ineqlines[$k] = array('x', $dir, $pts[0], $slope, $pts[1] + ($ymidp - $pts[2]) / $slope, $pts[2] + ($x2p - $pts[1]) * $slope); } else { $ineqlines[$k] = array('y', $dir, $pts[0], $slope, $pts[2] + ($x2p - $pts[1]) * $slope); } } } else { //quadratic $aUser = ($pts[4] - $pts[2]) / (($pts[3] - $pts[1]) * ($pts[3] - $pts[1])); $yatpt5 = $aUser * ($pts[5] - $pts[1]) * ($pts[5] - $pts[1]) + $pts[2]; if ($yatpt5 < $pts[6]) { $dir = '<'; } else { $dir = '>'; } $ineqlines[$k] = array('y', $dir, $pts[0], $aUser, $pts[1], $pts[2]); } } } $scores = array(); $deftol = 0.1; $defpttol = 5; foreach ($anslines as $key => $ansline) { $scores[$key] = 0; for ($i = 0; $i < count($ineqlines); $i++) { if ($ansline[2] != $ineqlines[$i][2]) { continue; } if ($ansline[1] != $ineqlines[$i][1]) { continue; } if ($ansline[2] < 10.3) { //linear inequality //check slope $toladj = pow(10, -1 - 6 * abs($ansline[3])); $relerr = abs($ansline[3] - $ineqlines[$i][3]) / (abs($ansline[3]) + $toladj); if ($relerr > $deftol * $reltolerance) { continue; } if ($ansline[0] != $ineqlines[$i][0]) { if (abs(abs($ansline[3]) - 1) < 0.4) { //check intercept if (abs($ansline[4] - $ineqlines[$i][5]) > $defpttol * $reltolerance) { continue; } } else { continue; } } else { if (abs($ansline[4] - $ineqlines[$i][4]) > $defpttol * $reltolerance) { continue; } } $scores[$key] = 1; break; } else { //quadratic inequality //check values in y = a(x-p)+q $toladj = pow(10, -1 - 6 * abs($ansline[3])); $relerr = abs($ansline[3] - $ineqlines[$i][3]) / (abs($ansline[3]) + $toladj); if ($relerr > $deftol * $reltolerance) { continue; } if (abs($ansline[4] - $ineqlines[$i][4]) > $defpttol * $reltolerance) { continue; } if (abs($ansline[5] - $ineqlines[$i][5]) > $defpttol * $reltolerance) { continue; } $scores[$key] = 1; break; } } } $extrastuffpenalty = max((count($ineqlines) - count($answers)) / max(count($answers), count($ineqlines)), 0); } else { //not polygon or twopoint, continue with regular grading //evaluate all the functions in $answers foreach ($answers as $key => $function) { if ($function == '') { continue; } $function = explode(',', $function); //curves: function // function, xmin, xmax //dot: x,y // x,y,"closed" or "open" //form: function, color, xmin, xmax, startmaker, endmarker if (count($function) == 2 || count($function) == 3 && ($function[2] == 'open' || $function[2] == 'closed')) { //is dot $pixx = ($function[0] - $settings[0]) * $pixelsperx + $imgborder; $pixy = $settings[7] - ($function[1] - $settings[2]) * $pixelspery - $imgborder; if (count($function) == 2 || $function[2] == 'closed') { $ansdots[$key] = array($pixx, $pixy); } else { $ansodots[$key] = array($pixx, $pixy); } continue; } $anslines[$key] = array(); $func = makepretty($function[0]); $func = mathphp($func, 'x'); $func = str_replace("(x)", '($x)', $func); $func = create_function('$x', 'return (' . $func . ');'); if (!isset($function[1])) { $function[1] = $settings[0]; } if (!isset($function[2])) { $function[2] = $settings[1]; } $xminpix = max(2 * $imgborder, ($function[1] - $settings[0]) * $pixelsperx + $imgborder); $xmaxpix = min($settings[6] - 2 * $imgborder, ($function[2] - $settings[0]) * $pixelsperx + $imgborder); for ($k = ceil($xminpix / $step); $k * $step <= $xmaxpix; $k++) { $x = $k * $step; $coordx = ($x - $imgborder) / $pixelsperx + $settings[0] + 1.0E-10; $coordy = $func($coordx); if ($coordy > $settings[2] && $coordy < $settings[3]) { $anslines[$key][$k] = $settings[7] - ($coordy - $settings[2]) * $pixelspery - $imgborder; if (!isset($anslineptcnt[$k])) { $anslineptcnt[$k] = 1; } else { $anslineptcnt[$k]++; } $linepts++; } } $linecnt++; } //break apart student entry list($lines, $dots, $odots, $tplines, $ineqlines) = explode(';;', $givenans); if ($lines == '') { $lines = array(); } else { $lines = explode(';', $lines); foreach ($lines as $k => $line) { $lines[$k] = explode('),(', substr($line, 1, strlen($line) - 2)); foreach ($lines[$k] as $j => $pt) { $lines[$k][$j] = explode(',', $pt); } } } if ($dots == '') { $dots = array(); } else { $dots = explode('),(', substr($dots, 1, strlen($dots) - 2)); foreach ($dots as $k => $pt) { $dots[$k] = explode(',', $pt); } } if ($odots == '') { $odots = array(); } else { $odots = explode('),(', substr($odots, 1, strlen($odots) - 2)); foreach ($odots as $k => $pt) { $odots[$k] = explode(',', $pt); } } //interp the lines $linedata = array(); $totinterp = 0; foreach ($lines as $k => $line) { for ($i = 1; $i < count($line); $i++) { $leftx = min($line[$i][0], $line[$i - 1][0]); $rightx = max($line[$i][0], $line[$i - 1][0]); if ($line[$i][0] == $line[$i - 1][0]) { $m = 9999; } else { $m = ($line[$i][1] - $line[$i - 1][1]) / ($line[$i][0] - $line[$i - 1][0]); } for ($k = ceil($leftx / $step); $k * $step < $rightx; $k++) { $x = $k * $step; $y = $line[$i - 1][1] + $m * ($x - $line[$i - 1][0]); if ($y > $imgborder && $y < $settings[7] - $imgborder) { $linedata[$k][] = $y; $totinterp++; } } } } $stdevs = array(); $stcnts = array(); $scores = array(); $unmatchedanspts = array(); $unmatchedanskeys = array(); //compare lines foreach ($anslines as $key => $answerline) { $unmatchedptcnt = 0; $stdevs[$key] = 0; $stcnts[$key] = 0; foreach ($answerline as $k => $ansy) { //if there are more ans pts than drawn, want to match up better than this; //mark it for coming back to //if less ans pts than drawn, that's already accounted for in $percentoffpts if ($anslineptcnt[$k] > count($linedata[$k])) { $unmatchedanspts[$k] = 1; continue; } $minerr = $settings[7]; for ($i = 0; $i < count($linedata[$k]); $i++) { if (abs($ansy - $linedata[$k][$i]) < $minerr) { $minerr = abs($ansy - $linedata[$k][$i]); } } if ($minerr < $settings[7]) { $stdevs[$key] += $minerr * $minerr; $stcnts[$key]++; } } } //go back and match up drawn points with unmatched answer points //we have more answer points than drawn points here foreach (array_keys($unmatchedanspts) as $k) { for ($i = 0; $i < count($linedata[$k]); $i++) { $minerr = $settings[7]; $minerrkey = -1; foreach ($anslines as $key => $answerline) { if (abs($answerline[$k] - $linedata[$k][$i]) < $minerr) { $minerr = abs($answerline[$k] - $linedata[$k][$i]); $minerrkey = $key; } } if ($minerrkey > -1) { $stdevs[$minerrkey] += $minerr * $minerr; $stcnts[$minerrkey]++; } } } //time to grade! $percentunmatcheddrawn = 0; //counts extra drawn points: percent of drawn that are extras if ($totinterp > 0) { $percentunmatcheddrawn = max(($totinterp - $linepts) / $totinterp - 0.05 * $reltolerance, 0); } //divide up over all the lines $percentunmatcheddrawn = $percentunmatcheddrawn; foreach ($anslines as $key => $answerline) { if ($stcnts[$key] < 2) { $stdevs[$key] = 0; } else { $stdevs[$key] = sqrt($stdevs[$key] / ($stcnts[$key] - 1)); } $stdevpen = max(8 * ($stdevs[$key] - 5) / $settings[7], 0); if (count($answerline) == 0) { $percentunmatchedans = 1; } else { $percentunmatchedans = max((count($answerline) - $stcnts[$key]) / count($answerline), 0); } if ($percentunmatchedans < 0.05 * $reltolerance) { $percentunmatchedans = 0; } $scores[$key] = 1 - ($stdevpen + $percentunmatcheddrawn + $percentunmatchedans) / $reltolerance; //echo "Line: $key, stdev: {$stdevs[$key]}, unmatchedrawn: $percentunmatcheddrawn, unmatchedans: $percentunmatchedans <br/>"; if ($scores[$key] < 0) { $scores[$key] = 0; } else { if ($scores[$key] > 1) { $scores[$key] = 1; } } } //go through dots //echo count($dots) .','.count($odots).','.count($ansdots).','.count($ansodots).'<br/>'; if (count($dots) + count($odots) == 0) { $extradots = 0; } else { $extradots = max((count($dots) + count($odots) - count($ansdots) - count($ansodots)) / (count($dots) + count($odots)), 0); } foreach ($ansdots as $key => $ansdot) { $scores[$key] = 0; for ($i = 0; $i < count($dots); $i++) { if (($dots[$i][0] - $ansdot[0]) * ($dots[$i][0] - $ansdot[0]) + ($dots[$i][1] - $ansdot[1]) * ($dots[$i][1] - $ansdot[1]) <= 25 * max(1, $reltolerance)) { $scores[$key] = 1 - $extradots; break; } } } //and open dots foreach ($ansodots as $key => $ansdot) { $scores[$key] = 0; for ($i = 0; $i < count($odots); $i++) { if (($odots[$i][0] - $ansdot[0]) * ($odots[$i][0] - $ansdot[0]) + ($odots[$i][1] - $ansdot[1]) * ($odots[$i][1] - $ansdot[1]) <= 25 * max(1, $reltolerance)) { $scores[$key] = 1 - $extradots; break; } } } } } } if (!isset($partweights)) { $partweights = array_fill(0, count($scores), 1 / count($scores)); } else { if (!is_array($partweights)) { $partweights = explode(',', $partweights); } } $totscore = 0; foreach ($scores as $key => $score) { $totscore += $score * $partweights[$key]; } if ($extrastuffpenalty > 0) { $totscore = max($totscore * (1 - $extrastuffpenalty), 0); } if (isset($abstolerance)) { if ($totscore < $abstolerance) { return 0; } else { return 1; } } else { return $totscore; } } else { if ($anstype == "file") { if (isset($options['scoremethod'])) { if (is_array($options['scoremethod'])) { $scoremethod = $options['scoremethod'][$qn]; } else { $scoremethod = $options['scoremethod']; } } if (isset($options['answer'])) { if ($multi > 0) { $answer = $options['answer'][$qn]; } else { $answer = $options['answer']; } } if (isset($options['answerformat'])) { if (is_array($options['answerformat'])) { $answerformat = $options['answerformat'][$qn]; } else { $answerformat = $options['answerformat']; } } if ($multi > 0) { $qn = $multi * 1000 + $qn; } $filename = basename($_FILES["qn{$qn}"]['name']); $filename = preg_replace('/[^\\w\\.]/', '', $filename); $hasfile = false; require_once "../includes/filehandler.php"; if (trim($filename) == '') { $found = false; if ($_POST["lf{$qn}"] != '') { if ($multi > 0) { if (strpos($GLOBALS['lastanswers'][$multi - 1], '@FILE:' . $_POST["lf{$qn}"] . '@') !== false) { $found = true; } } else { if (strpos($GLOBALS['lastanswers'][$qn], '@FILE:' . $_POST["lf{$qn}"] . '@') !== false) { $found = true; } } } if ($found) { $GLOBALS['partlastanswer'] = '@FILE:' . $_POST["lf{$qn}"] . '@'; if ($answerformat == 'excel') { $zip = new ZipArchive(); if ($zip->open(getasidfilepath($_POST["lf{$qn}"]))) { $doc = new DOMDocument(); $doc->loadXML($zip->getFromName('xl/worksheets/sheet1.xml')); $zip->close(); } else { $GLOBALS['scoremessages'] .= _(' Unable to open Excel file'); return 0; } } $hasfile = true; } else { $GLOBALS['partlastanswer'] = ''; return 0; } } if (!$hasfile) { $extension = strtolower(strrchr($filename, ".")); $badextensions = array(".php", ".php3", ".php4", ".php5", ".bat", ".com", ".pl", ".p", ".exe"); if ($GLOBALS['scoremessages'] != '') { $GLOBALS['scoremessages'] .= '<br/>'; } $GLOBALS['scoremessages'] .= sprintf(_('Upload of %s: '), $filename); if (in_array($extension, $badextensions)) { $GLOBALS['partlastanswer'] = _('Error - Invalid file type'); $GLOBALS['scoremessages'] .= _('Error - Invalid file type'); return 0; } //if($GLOBALS['isreview']) {echo 'TRUE';} if (isset($GLOBALS['asid'])) { //going to use assessmentid/random $randstr = ''; for ($i = 0; $i < 6; $i++) { $n = rand(0, 61); if ($n < 10) { $randstr .= chr(48 + $n); } else { if ($n < 36) { $randstr .= chr(65 + $n - 10); } else { $randstr .= chr(97 + $n - 36); } } } //in case "same random seed" is selected, students can overwrite their own //files. Avoid this. if (($GLOBALS['testsettings']['shuffle'] & 4) == 4 || ($GLOBALS['testsettings']['shuffle'] & 2) == 2) { //if same random seed is set, need to check for duplicates $n = 0; do { $n++; $s3asid = $GLOBALS['testsettings']['id'] . "/{$n}"; } while (doesfileexist('assess', "adata/{$s3asid}/{$filename}")); } else { $s3asid = $GLOBALS['testsettings']['id'] . "/{$randstr}"; } } else { $GLOBALS['partlastanswer'] = _('Error - no asid'); $GLOBALS['scoremessages'] .= _('Error - no asid'); return 0; } if (is_numeric($s3asid) && $s3asid == 0) { //set in testquestion for preview $GLOBALS['partlastanswer'] = _('Error - File not uploaded in preview'); $GLOBALS['scoremessages'] .= _('Error - File not uploaded in preview'); return 0; } if (is_uploaded_file($_FILES["qn{$qn}"]['tmp_name'])) { if ($answerformat == 'excel') { $zip = new ZipArchive(); if ($zip->open($_FILES["qn{$qn}"]['tmp_name'])) { echo "opened excel"; $doc = new DOMDocument(); if ($doc->loadXML($zip->getFromName('xl/worksheets/sheet1.xml'))) { echo "read into doc"; } $zip->close(); } else { $GLOBALS['scoremessages'] .= _(' Unable to open Excel file'); return 0; } } $s3object = "adata/{$s3asid}/{$filename}"; if (storeuploadedfile("qn{$qn}", $s3object)) { $GLOBALS['partlastanswer'] = "@FILE:{$s3asid}/{$filename}@"; $GLOBALS['scoremessages'] .= _("Successful"); $hasfile = true; } else { //echo "Error storing file"; $GLOBALS['partlastanswer'] = _('Error storing file'); $GLOBALS['scoremessages'] .= _('Error storing file'); return 0; } } else { //echo "Error uploading file"; if ($_FILES["qn{$qn}"]['error'] == 2 || $_FILES["qn{$qn}"]['error'] == 1) { $GLOBALS['partlastanswer'] = _('Error uploading file - file too big'); $GLOBALS['scoremessages'] .= _('Error uploading file - file too big'); } else { $GLOBALS['partlastanswer'] = _('Error uploading file'); $GLOBALS['scoremessages'] .= _('Error uploading file'); } return 0; } } if (isset($scoremethod) && $scoremethod == 'takeanything') { return 1; } else { if ($answerformat == 'excel') { $doccells = array(); $els = $doc->getElementsByTagName('c'); foreach ($els as $el) { $doccells[$el->getAttribute('r')] = $el->getElementsByTagName('v')->item(0)->nodeValue; } $pts = 0; foreach ($answer as $cell => $val) { if (!isset($doccells[$cell])) { continue; } if (is_numeric($val)) { if (abs($val - $doccells[$cell]) < 0.01) { $pts++; } else { $GLOBALS['scoremessages'] .= "<br/>Cell {$cell} incorrect"; echo "<br/>Cell {$cell} incorrect"; } } else { if (trim($val) == trim($doccells[$cell])) { $pts++; } } } return $pts / count($answer); } else { return -2; } } } else { if ($anstype == "conditional") { $answer = $options['answer']; if (isset($options['abstolerance'])) { $abstolerance = $options['abstolerance']; } if (isset($options['reltolerance'])) { $reltolerance = $options['reltolerance']; } if (!isset($reltolerance) && !isset($abstolerance)) { $reltolerance = $defaultreltol; } if (isset($options['domain'])) { $domain = $options['domain']; } else { $domain = "-10,10"; } if (isset($options['variables'])) { $variables = $options['variables']; } else { $variables = "x"; } $anstypes = $options['anstypes']; if (!is_array($anstypes)) { $anstypes = explode(',', $anstypes); } $la = array(); foreach ($anstypes as $i => $anst) { $qnt = 1000 * ($qn + 1) + $i; if (isset($_POST["tc{$qnt}"])) { if ($anst == 'calculated') { $la[$i] = $_POST["tc{$qnt}"] . '$#$' . $_POST["qn{$qnt}"]; } else { $la[$i] = $_POST["tc{$qnt}"]; } } else { if (isset($_SESSION['choicemap'][$qnt]) && isset($_SESSION['choicemap'][$qnt][$_POST["qn{$qnt}"]])) { $la[$i] = $_POST["qn{$qnt}"] . '$!$' . $_SESSION['choicemap'][$qnt][$_POST["qn{$qnt}"]]; } else { $la[$i] = $_POST["qn{$qnt}"]; } } $la[$i] = str_replace('&', '', $la[$i]); $la[$i] = preg_replace('/#+/', '#', $la[$i]); } $GLOBALS['partlastanswer'] = implode('&', $la); if (isset($abstolerance)) { $tol = '|' . $abstolerance; } else { $tol = $reltolerance; } $correct = true; if (!is_array($answer)) { //single boolean if ($answer === true) { return 1; } else { if ($answer === false) { return 0; } else { return $answer; } } } if (is_array($answer) && is_string($answer[0])) { //if single {'function',$f,$g) type, make array $answer = array($answer); } foreach ($answer as $ans) { if (is_array($ans)) { if ($ans[0][0] == '!') { $flip = true; $ans[0] = substr($ans[0], 1); } else { $flip = false; } if ($ans[0] == 'number') { $pt = comparenumbers($ans[1], $ans[2], $tol); } else { if ($ans[0] == 'function') { $pt = comparefunctions($ans[1], $ans[2], $variables, $tol, $domain); } } if ($flip) { $pt = !$pt; } } else { $pt = $ans; } if ($pt == false) { return 0; break; } } return 1; } } } } } } } } } } } } } } } } }
function comparefunctions($a, $b, $vars = 'x', $tol = '.001', $domain = '-10,10') { if ($a == '' || $b == '') { return false; } //echo "comparing $a and $b"; if ($tol[0] == '|') { $abstolerance = floatval(substr($tol, 1)); } $type = "expression"; if (strpos($a, '=') !== false && strpos($b, '=') !== false) { $type = "equation"; } $fromto = explode(',', $domain); $variables = explode(',', $vars); $vlist = implode("|", $variables); for ($i = 0; $i < 20; $i++) { for ($j = 0; $j < count($variables); $j++) { if (isset($fromto[2]) && $fromto[2] == "integers") { $tps[$i][$j] = rand($fromto[0], $fromto[1]); } else { if (isset($fromto[2 * $j + 1])) { $tps[$i][$j] = $fromto[2 * $j] + ($fromto[2 * $j + 1] - $fromto[2 * $j]) * rand(0, 499) / 500.0 + 0.001; } else { $tps[$i][$j] = $fromto[0] + ($fromto[1] - $fromto[0]) * rand(0, 499) / 500.0 + 0.001; } } } } if ($type == 'equation') { if (substr_count($a, '=') != 1) { return false; } $a = preg_replace('/(.*)=(.*)/', '$1-($2)', $a); if (substr_count($b, '=') != 1) { return false; } $b = preg_replace('/(.*)=(.*)/', '$1-($2)', $b); } $a = mathphp(makepretty(mathphppre($a)), $vlist); $b = mathphp(makepretty(mathphppre($b)), $vlist); //echo "pretty: $a, $b"; for ($i = 0; $i < count($variables); $i++) { $a = str_replace("(" . $variables[$i] . ")", '($tp[' . $i . '])', $a); $b = str_replace("(" . $variables[$i] . ")", '($tp[' . $i . '])', $b); } $cntnana = 0; $cntnanb = 0; $correct = true; $ratios = array(); for ($i = 0; $i < 20; $i++) { for ($j = 0; $j < count($variables); $j++) { $tp[$j] = $tps[$i][$j]; } $ansa = @eval("return ({$a});"); $ansb = @eval("return ({$b});"); //echo "real: $ansa, my: $ansb <br/>"; if (isNaN($ansa)) { $cntnana++; if (isNaN($ansb)) { $cntnanb++; } continue; } //avoid NaN problems if (isNaN($ansb)) { $cntnanb++; continue; } if ($type == 'equation') { if (abs($ansa) > 1.0E-6 && is_numeric($ansb)) { $ratios[] = $ansb / $ansa; if (abs($ansb) <= 1.0E-8 && $ansa != 0) { $cntzero++; } } else { if (abs($ansa) <= 1.0E-6 && is_numeric($ansb) && abs($ansb) <= 1.0E-8) { $cntbothzero++; } } } else { if (isset($abstolerance)) { if (abs($ansa - $ansb) > $abstolerance - 1.0E-12) { $correct = false; break; } } else { if (abs($ansa - $ansb) / (abs($ansa) + 0.0001) > $tol - 1.0E-12) { $correct = false; break; } } } } //echo "$i, $ansa, $ansb, $cntnana, $cntnanb"; if (($cntnana == 20 || $cntnanb == 20) && isset($GLOBALS['teacherid'])) { echo "<p>Debug info: one function evaled to Not-a-number at all test points. Check \$domain</p>"; echo "<p>Funcs: {$a} and {$b}</p>"; return false; } if (abs($cntnana - $cntnanb) > 1) { return false; } if ($type == "equation") { if ($cntbothzero > 18) { $correct = true; } else { if (count($ratios) > 0) { if (count($ratios) == $cntzero) { $correct = false; } else { $meanratio = array_sum($ratios) / count($ratios); for ($i = 0; $i < count($ratios); $i++) { if (isset($abstolerance)) { if (abs($ratios[$i] - $meanratio) > $abstolerance - 1.0E-12) { $correct = false; break; } } else { if (abs($ratios[$i] - $meanratio) / (abs($meanratio) + 0.0001) > $tol - 1.0E-12) { $correct = false; break; } } } } } else { $correct = false; } } } if ($correct) { return true; } else { return false; } }