/**
  * Check for strings within the casstring.  This is only used in the "fobidden words" option.
  * @return bool|string true if an element of array is found in the casstring.
  */
 public function check_external_forbidden_words_literal($keywords)
 {
     if (null === $this->valid) {
         $this->validate();
     }
     // Deal with escaped commas.
     $keywords = str_replace('\\,', 'COMMA_TAG', $keywords);
     $keywords = explode(',', $keywords);
     // Replace lists of keywords with their actual values.
     $kws = array();
     foreach ($keywords as $val) {
         $kw = trim(strtolower($val));
         if (array_key_exists($kw, self::$keywordlists)) {
             $kws = array_merge($kws, self::$keywordlists[$kw]);
         } else {
             if ('COMMA_TAG' === $val) {
                 $kws[] = ',';
             } else {
                 $kws[] = trim($val);
                 // This test is case sensitive, but ignores surrounding whitespace.
             }
         }
     }
     $found = false;
     foreach ($kws as $key) {
         if (!(false === strpos($this->rawcasstring, $key))) {
             $found = true;
             $this->valid = false;
             $this->add_error(stack_string('stackCas_forbiddenWord', array('forbid' => stack_maxima_format_casstring($key))));
         }
     }
     return $found;
 }
 /**
  * Validate any attempts at this question.
  *
  * @param array $response the student response to the question.
  * @param stack_options $options CAS options to use when validating.
  * @param string $teacheranswer the teachers answer as a string representation of the evaluated expression.
  * @return stack_input_state represents the current state of the input.
  */
 public function validate_student_response($response, $options, $teacheranswer, $forbiddenkeys)
 {
     if (!is_a($options, 'stack_options')) {
         throw new stack_exception('stack_input: validate_student_response: options not of class stack_options');
     }
     $localoptions = clone $options;
     // The validation field should always come back through as a single RAW Maxima expression for each input.
     if (array_key_exists($this->name . '_val', $response)) {
         $validator = $response[$this->name . '_val'];
     } else {
         $validator = '';
     }
     $contents = $this->response_to_contents($response);
     if (array() == $contents or $this->is_blank_response($contents)) {
         return new stack_input_state(self::BLANK, array(), '', '', '', '', '');
     }
     // This method actually validates any CAS strings etc.
     list($valid, $errors, $modifiedcontents) = $this->validate_contents($contents, $forbiddenkeys);
     // If we can't get a "displayed value" back from the CAS, show the student their original expression.
     $display = stack_maxima_format_casstring($this->contents_to_maxima($contents));
     $interpretedanswer = $this->contents_to_maxima($modifiedcontents);
     $answer = new stack_cas_casstring($interpretedanswer);
     $lvarsdisp = '';
     // Send the string to the CAS.
     if ($valid) {
         if (!$this->get_parameter('sameType')) {
             $teacheranswer = null;
         }
         $singlevarchars = false;
         if (2 == $this->get_parameter('insertStars', 0)) {
             $singlevarchars = true;
         }
         // Generate an expression from which we extract the list of variables in the student's answer.
         $lvars = new stack_cas_casstring('ev(listofvars(' . $interpretedanswer . '),simp)');
         $lvars->get_valid('t', $this->get_parameter('strictSyntax', true), $this->get_parameter('insertStars', 0), $this->get_parameter('allowWords', ''));
         $answer->set_cas_validation_casstring($this->name, $this->get_parameter('forbidFloats', false), $this->get_parameter('lowestTerms', false), $singlevarchars, $teacheranswer, $this->get_parameter('allowWords', ''));
         $localoptions->set_option('simplify', false);
         $session = new stack_cas_session(array($answer, $lvars), $localoptions, 0);
         $session->instantiate();
         $session = $session->get_session();
         $answer = $session[0];
         $lvars = $session[1];
         $errors = stack_maxima_translate($answer->get_errors());
         if ('' != $errors) {
             $valid = false;
         }
         if ('' == $answer->get_value()) {
             $valid = false;
         } else {
             $display = '\\[ ' . $answer->get_display() . ' \\]';
             $interpretedanswer = $answer->get_value();
             if (!($lvars->get_value() == '[]')) {
                 $lvarsdisp = '\\( ' . $lvars->get_display() . '\\) ';
             }
         }
     }
     $note = $answer->get_answernote();
     // Answers may not contain the ? character.  CAS-strings may, but answers may not.
     // It is very useful for teachers to be able to add in syntax hints.
     if (!(strpos($interpretedanswer, '?') === false)) {
         $valid = false;
         $errors .= stack_string('qm_error');
     }
     if (!$valid) {
         $status = self::INVALID;
     } else {
         if ($this->get_parameter('mustVerify', true) && $validator != $this->contents_to_maxima($contents)) {
             $status = self::VALID;
         } else {
             $status = self::SCORE;
         }
     }
     return new stack_input_state($status, $contents, $interpretedanswer, $display, $errors, $note, $lvarsdisp);
 }
 public function instantiate()
 {
     if (null === $this->valid) {
         $this->validate();
     }
     if (!$this->valid) {
         return false;
     }
     // Lazy instantiation - only do this once...
     // Empty session.  Nothing to do.
     if ($this->instantiated || null === $this->session) {
         return true;
     }
     $connection = stack_connection_helper::make();
     $results = $connection->compute($this->construct_maxima_command());
     $this->debuginfo = $connection->get_debuginfo();
     // Now put the information back into the correct slots.
     $session = $this->session;
     $newsession = array();
     $newerrors = '';
     $allfail = true;
     $i = 0;
     // We loop over each entry in the session, not over the result.
     // This way we can add an error for missing values.
     foreach ($session as $cs) {
         $gotvalue = false;
         if ('' == $cs->get_key()) {
             $key = 'dumvar' . $i;
         } else {
             $key = $cs->get_key();
         }
         if (array_key_exists($i, $results)) {
             $allfail = false;
             // We at least got one result back from the CAS!
             $result = $results["{$i}"];
             // GOCHA!  Results have string represenations of numbers, not int....
             if ('' != $result['error'] and false === strstr($result['error'], 'clipped')) {
                 $cs->add_errors($result['error']);
                 $cs->decode_maxima_errors($result['error']);
                 $newerrors .= stack_maxima_format_casstring($cs->get_raw_casstring());
                 $newerrors .= ' ' . stack_string("stackCas_CASErrorCaused") . ' ' . $result['error'] . ' ';
             }
             if (array_key_exists('value', $result)) {
                 $val = str_replace('QMCHAR', '?', $result['value']);
                 $cs->set_value($val);
                 $gotvalue = true;
             } else {
                 $cs->add_errors(stack_string("stackCas_failedReturnOne"));
             }
             if (array_key_exists('display', $result)) {
                 // Need to add this in here also because strings may contain question mark characters.
                 $disp = str_replace('QMCHAR', '?', $result['display']);
                 $cs->set_display($disp);
             }
             if (array_key_exists('valid', $result)) {
                 $cs->set_valid($result['valid']);
             }
             if (array_key_exists('answernote', $result)) {
                 $cs->set_answernote($result['answernote']);
             }
             if (array_key_exists('feedback', $result)) {
                 $cs->set_feedback($result['feedback']);
             }
         } else {
             if (!$gotvalue) {
                 $errstr = stack_string("stackCas_failedReturn") . ' ' . stack_maxima_format_casstring($cs->get_raw_casstring());
                 $cs->add_errors($errstr);
                 $cs->set_answernote('CASFailedReturn');
                 $newerrors .= $errstr;
             }
         }
         $newsession[] = $cs;
         $i++;
     }
     $this->session = $newsession;
     if ('' != $newerrors) {
         $this->errors .= '<span class="error">' . stack_string('stackCas_CASError') . '</span>' . $newerrors;
     }
     if ($allfail) {
         $this->errors = '<span class="error">' . stack_string('stackCas_allFailed') . '</span>';
     }
     $this->instantiated = true;
 }
 public static function run_test($test)
 {
     // Note: What we would really like to do is the following.
     // $el = stack_input_factory::make('algebraic', 'sans1', 'x');
     // $el->set_parameter('insertStars', 1);
     // $el->set_parameter('strictSyntax', false);
     // $el->set_parameter('sameType', false);
     // $cs = $el->validate_student_response($test->rawstring);
     // However, we want to pull apart the bits to expose where the various errors occur.
     $cs = new stack_cas_casstring($test->rawstring);
     $cs->get_valid('s', false, 1);
     $cs->set_cas_validation_casstring('sans1', true, true, false, null);
     $phpvalid = $cs->get_valid();
     if ($phpvalid) {
         // Trim off stack_validate_typeless([..], true, true).
         $phpcasstring = $cs->get_casstring();
         $phpcasstring = substr($phpcasstring, 25);
         $phpcasstring = substr($phpcasstring, 0, strlen($phpcasstring) - 12);
         $outputphpcasstring = $phpcasstring;
     } else {
         $phpcasstring = '';
         $outputphpcasstring = 'N/A...';
     }
     $errors = $cs->get_errors();
     $passed = true;
     if ('php_true' === $test->phpvalid) {
         $expected = true;
     } else {
         $expected = false;
     }
     if ($phpvalid != $expected) {
         $passed = false;
         $errors .= ' ' . stack_string('phpvalidatemismatch');
     }
     if ($phpvalid && $phpcasstring != $test->phpcasstring) {
         $passed = false;
         $errors .= ' ' . stack_maxima_format_casstring($phpcasstring) . ' \\(\\neq \\) ' . stack_maxima_format_casstring($test->phpcasstring);
     }
     $casvalid = '';
     $caserrors = '';
     $casvalue = '';
     $casdisplay = '';
     if ($cs->get_valid()) {
         $options = new stack_options();
         $options->set_option('simplify', false);
         $session = new stack_cas_session(array($cs), $options, 0);
         $session->instantiate();
         $session = $session->get_session();
         $cs = $session[0];
         $caserrors = stack_maxima_translate($cs->get_errors());
         $casvalue = stack_maxima_format_casstring($cs->get_value());
         if ('cas_true' == $test->casvalid) {
             $casexpected = true;
         } else {
             $casexpected = false;
         }
         if ('' == $cs->get_value()) {
             $casvalid = false;
         } else {
             $casvalid = true;
         }
         if ($casexpected != $casvalid) {
             $passed = false;
             $caserrors .= ' ' . stack_string('casvalidatemismatch');
         }
         $casdisplay = $cs->get_display();
     }
     $answernote = $cs->get_answernote();
     if ($answernote != $test->ansnotes) {
         $passed = false;
         $errors .= ' ' . stack_string('ansnotemismatch');
     }
     return array($passed, $phpvalid, $phpcasstring, $errors, $casvalid, $caserrors, $casdisplay, $casvalue, $answernote);
 }