コード例 #1
0
 /**
  * Process the RandomInteger expression.
  *
  * * Throws an ExpressionProcessingException if 'min' is greater than 'max'.
  * * Throws an ExpressionProcessingException if a variable reference is not found in the current state.
  * * Throws an ExpressionProcessingException if a variable reference's value is not an integer.
  *
  * @return integer A random integer value.
  * @throws \qtism\runtime\expressions\ExpressionProcessingException
  */
 public function process()
 {
     $expr = $this->getExpression();
     $min = $expr->getMin();
     $max = $expr->getMax();
     $step = $expr->getStep();
     $state = $this->getState();
     $min = gettype($min) === 'integer' ? $min : $state[Utils::sanitizeVariableRef($min)]->getValue();
     $max = gettype($max) === 'integer' ? $max : $state[Utils::sanitizeVariableRef($max)]->getValue();
     $step = gettype($step) === 'integer' ? $step : $state[Utils::sanitizeVariableRef($step)]->getValue();
     if (gettype($min) === 'integer' && gettype($max) === 'integer' && gettype($step) === 'integer') {
         if ($min > $max) {
             $msg = "'min':'{$min}' is greater than 'max':'{$max}'.";
             throw new ExpressionProcessingException($msg, $this, ExpressionProcessingException::LOGIC_ERROR);
         }
         if ($step === 1) {
             return new QtiInteger(mt_rand($min, $max));
         } else {
             $distance = $min < 0 ? $max + abs($min) : $max - $min;
             $mult = mt_rand(0, intval(floor($distance / $step)));
             $random = new QtiInteger($min + $mult * $step);
             return $random;
         }
     } else {
         $msg = "At least one of the following variables is not an integer: 'min', 'max', 'step' while processing RandomInteger.";
         throw new ExpressionProcessingException($msg, $this, ExpressionProcessingException::WRONG_VARIABLE_BASETYPE);
     }
 }
コード例 #2
0
 /**
  * Process the RandomFloat expression.
  *
  * * Throws an ExpressionProcessingException if 'min' is greater than 'max'.
  *
  * @return float A Random float value.
  * @throws \qtism\runtime\expressions\ExpressionProcessingException
  */
 public function process()
 {
     $expr = $this->getExpression();
     $min = $expr->getMin();
     $max = $expr->getMax();
     $state = $this->getState();
     $min = is_float($min) ? $min : $state[Utils::sanitizeVariableRef($min)]->getValue();
     $max = is_float($max) ? $max : $state[Utils::sanitizeVariableRef($max)]->getValue();
     if (is_float($min) && is_float($max)) {
         if ($min <= $max) {
             return new Float($min + lcg_value() * abs($max - $min));
         } else {
             $msg = "'min':'{$min}' is greater than 'max':'{$max}'.";
             throw new ExpressionProcessingException($msg, $this, ExpressionProcessingException::LOGIC_ERROR);
         }
     } else {
         $msg = "At least one of the following values is not a float: 'min', 'max'.";
         throw new ExpressionProcessingException($msg, $this, ExpressionProcessingException::WRONG_VARIABLE_BASETYPE);
     }
 }
コード例 #3
0
ファイル: IndexProcessor.php プロジェクト: nagyist/qti-sdk
 /**
  * Process the Index operator.
  *
  * @return mixed|null A QTIRuntime compliant scalar value. NULL is returned if expression->n exceeds the number of values in the container or the sub-expression is NULL.
  * @throws \qtism\runtime\expressions\operators\OperatorProcessingException
  */
 public function process()
 {
     $operands = $this->getOperands();
     if ($operands->containsNull()) {
         return null;
     }
     if ($operands->exclusivelyOrdered() === false) {
         $msg = "The Index operator only accepts values with a cardinality of ordered.";
         throw new OperatorProcessingException($msg, $this, OperatorProcessingException::WRONG_CARDINALITY);
     }
     $n = $this->getExpression()->getN();
     if (gettype($n) === 'string') {
         // The value of $n comes from the state.
         $state = $this->getState();
         if (($index = $state[ProcessingUtils::sanitizeVariableRef($n)]) !== null) {
             if ($index instanceof QtiInteger) {
                 $n = $index->getValue();
             } else {
                 $msg = "The value '{$index}' is not an integer. Ordered containers can be only accessed by integers.";
                 throw new OperatorProcessingException($msg, $this, OperatorProcessingException::WRONG_VARIABLE_BASETYPE);
             }
         } else {
             $msg = "Unknown variable reference '{$n}'.";
             throw new OperatorProcessingException($msg, $this, OperatorProcessingException::NONEXISTENT_VARIABLE);
         }
     }
     if ($n < 1) {
         $msg = "The value of 'n' must be a non-zero postive integer, '{$n}' given.";
         throw new OperatorProcessingException($msg, $this, OperatorProcessingException::LOGIC_ERROR);
     }
     $n = $n - 1;
     // QTI indexes begin at 1...
     if ($n > count($operands[0]) - 1) {
         // As per specs, if n exceeds the number of values in the container,
         // the result of the index operator is NULL.
         return null;
     }
     return $operands[0][$n];
 }
コード例 #4
0
 /**
  * Process the EqualRounded operator.
  *
  * @return boolean|null A boolean with a value of true if the two expressions are numerically equal after rounding and false if they are not. If either sub-expression is NULL, the operator results in NULL.
  * @throws \qtism\runtime\expressions\operators\OperatorProcessingException
  */
 public function process()
 {
     $operands = $this->getOperands();
     if ($operands->containsNull()) {
         return null;
     }
     if ($operands->exclusivelySingle() === false) {
         $msg = "The EqualRounded operator only accepts operands with a single cardinality.";
         throw new OperatorProcessingException($msg, $this, OperatorProcessingException::WRONG_CARDINALITY);
     }
     if ($operands->exclusivelyNumeric() === false) {
         $msg = "The EqualRounded operator only accepts operands with an integer or float baseType.";
         throw new OperatorProcessingException($msg, $this, OperatorProcessingException::WRONG_BASETYPE);
     }
     // delegate the rounding to the RoundTo operator.
     $expression = $this->getExpression();
     $roundingMode = $expression->getRoundingMode();
     $figures = $expression->getFigures();
     if (gettype($figures) === 'string') {
         // Variable reference to deal with.
         $state = $this->getState();
         $varName = Utils::sanitizeVariableRef($figures);
         $varValue = $state[$varName];
         if (is_null($varValue) === true) {
             $msg = "The variable with name '{$varName}' could not be resolved.";
             throw new OperatorProcessingException($msg, $this, OperatorProcessingException::NONEXISTENT_VARIABLE);
         } elseif (!$varValue instanceof Integer) {
             $msg = "The variable with name '{$varName}' is not an integer.";
             throw new OperatorProcessingException($msg, $this, OperatorProcessingException::WRONG_VARIABLE_BASETYPE);
         }
         $figures = $varValue->getValue();
     }
     $rounded = new OperandsCollection();
     // will contain the rounded operands.
     foreach ($operands as $operand) {
         $baseType = RuntimeUtils::inferBaseType($operand);
         $subExpression = new BaseValue($baseType, $operand);
         $roundToExpression = new RoundTo(new ExpressionCollection(array($subExpression)), $figures, $roundingMode);
         $roundToProcessor = new RoundToProcessor($roundToExpression, new OperandsCollection(array($operand)));
         try {
             $rounded[] = $roundToProcessor->process();
         } catch (OperatorProcessingException $e) {
             $msg = "An error occured while rounding '{$operand}'.";
             throw new OperatorProcessingException($msg, $this, OperatorProcessingException::LOGIC_ERROR, $e);
         }
     }
     return new Boolean($rounded[0]->getValue() == $rounded[1]->getValue());
 }
コード例 #5
0
 /**
  * Process the Equal operator.
  * 
  * @return boolean|null Whether the two expressions are numerically equal and false if they are not or NULL if either sub-expression is NULL.
  * @throws OperatorProcessingException
  */
 public function process()
 {
     $operands = $this->getOperands();
     if ($operands->containsNull() === true) {
         return null;
     }
     if ($operands->exclusivelySingle() === false) {
         $msg = "The Equal operator only accepts operands with a single cardinality.";
         throw new OperatorProcessingException($msg, $this, OperatorProcessingException::WRONG_CARDINALITY);
     }
     if ($operands->exclusivelyNumeric() === false) {
         $msg = "The Equal operator only accepts operands with an integer or float baseType";
         throw new OperatorProcessingException($msg, $this, OperatorProcessingException::WRONG_BASETYPE);
     }
     $operand1 = $operands[0];
     $operand2 = $operands[1];
     $expression = $this->getExpression();
     if ($expression->getToleranceMode() === ToleranceMode::EXACT) {
         return new Boolean($operand1->getValue() == $operand2->getValue());
     } else {
         $tolerance = $expression->getTolerance();
         if (gettype($tolerance[0]) === 'string') {
             $strTolerance = $tolerance;
             $tolerance = array();
             // variableRef to handle.
             $state = $this->getState();
             $tolerance0Name = Utils::sanitizeVariableRef($strTolerance[0]);
             $varValue = $state[$tolerance0Name];
             if (is_null($varValue)) {
                 $msg = "The variable with name '{$tolerance0Name}' could not be resolved.";
                 throw new OperatorProcessingException($msg, $this, OperatorProcessingException::NONEXISTENT_VARIABLE);
             } else {
                 if (!$varValue instanceof Float) {
                     $msg = "The variable with name '{$tolerance0Name}' is not a float.";
                     throw new OperatorProcessingException($msg, $this, OperatorProcessingException::WRONG_VARIABLE_BASETYPE);
                 }
             }
             $tolerance[] = $varValue->getValue();
             if (isset($strTolerance[1]) && gettype($strTolerance[1]) === 'string') {
                 // A second variableRef to handle.
                 $tolerance1Name = Utils::sanitizeVariableRef($strTolerance[1]);
                 if (($varValue = $state[$tolerance1Name]) !== null && $varValue instanceof Float) {
                     $tolerance[] = $varValue->getValue();
                 }
             }
         }
         if ($expression->getToleranceMode() === ToleranceMode::ABSOLUTE) {
             $t0 = $operand1->getValue() - $tolerance[0];
             $t1 = $operand1->getValue() + (isset($tolerance[1]) ? $tolerance[1] : $tolerance[0]);
             $moreThanLower = $expression->doesIncludeLowerBound() ? $operand2->getValue() >= $t0 : $operand2->getValue() > $t0;
             $lessThanUpper = $expression->doesIncludeUpperBound() ? $operand2->getValue() <= $t1 : $operand2->getValue() < $t1;
             return new Boolean($moreThanLower && $lessThanUpper);
         } else {
             // Tolerance mode RELATIVE
             $tolerance = $expression->getTolerance();
             $t0 = $operand1->getValue() * (1 - $tolerance[0] / 100);
             $t1 = $operand1->getValue() * (1 + (isset($tolerance[1]) ? $tolerance[1] : $tolerance[0]) / 100);
             $moreThanLower = $expression->doesIncludeLowerBound() ? $operand2->getValue() >= $t0 : $operand2->getValue() > $t0;
             $lessThanUpper = $expression->doesIncludeUpperBound() ? $operand2->getValue() <= $t1 : $operand2->getValue() < $t1;
             return new Boolean($moreThanLower && $lessThanUpper);
         }
     }
 }
コード例 #6
0
ファイル: AnyNProcessor.php プロジェクト: hutnikau/qti-sdk
 /**
  * Process the AnyN processor.
  *
  * @return boolean|null A boolean value of true if at least min of the sub-expressions are true and at most max of the sub-expressions are true. NULL is returned if the correct value for the operator cannot be determined.
  * @throws \qtism\runtime\expressions\operators\OperatorProcessingException
  */
 public function process()
 {
     $operands = $this->getOperands();
     // Retrieve the values of min and max.
     $min = $this->getExpression()->getMin();
     $max = $this->getExpression()->getMax();
     // @todo write a generic method to retrieve variable references.
     if (is_string($min) === true) {
         // variable reference for 'min' to handle.
         $state = $this->getState();
         $varName = Utils::sanitizeVariableRef($min);
         $varValue = $state[$varName];
         if (is_null($varValue)) {
             $msg = "The variable with name '{$varName}' could not be resolved or is null.";
             throw new OperatorProcessingException($msg, $this, OperatorProcessingException::NONEXISTENT_VARIABLE);
         } elseif (!$varValue instanceof Integer) {
             $msg = "The variable with name '{$varName}' is not an integer.";
             throw new OperatorProcessingException($msg, $this, OperatorProcessingException::WRONG_BASETYPE);
         } else {
             $min = $varValue->getValue();
         }
     }
     if (is_string($max) === true) {
         // variable reference for 'max' to handle.
         $state = $this->getState();
         $varName = Utils::sanitizeVariableRef($max);
         $varValue = $state[$varName];
         if (is_null($varValue)) {
             $msg = "The variable with name '{$varName}' could not be resolved or is null.";
             throw new OperatorProcessingException($msg, $this, OperatorProcessingException::NONEXISTENT_VARIABLE);
         } elseif (!$varValue instanceof Integer) {
             $msg = "The variable with name '{$varName}' is not an integer.";
             throw new OperatorProcessingException($msg, $this, OperatorProcessingException::WRONG_VARIABLE_BASETYPE);
         } else {
             $max = $varValue->getValue();
         }
     }
     $nullCount = 0;
     $trueCount = 0;
     foreach ($operands as $operand) {
         if (is_null($operand)) {
             $nullCount++;
             continue;
         } elseif ($operand instanceof Boolean) {
             if ($operand->getValue() === true) {
                 $trueCount++;
             }
         } else {
             // Not null, not a boolean, we have a problem...
             $msg = "The AnyN operator only accepts values with cardinality single and baseType boolean.";
             throw new OperatorProcessingException($msg, $this, OperatorProcessingException::WRONG_BASETYPE_OR_CARDINALITY);
         }
     }
     if ($trueCount >= $min && $trueCount <= $max) {
         return new Boolean(true);
     } else {
         // Should we return false or null?
         if ($trueCount + $nullCount >= $min && $trueCount + $nullCount <= $max) {
             // It could have match if nulls were true values.
             return null;
         } else {
             return new Boolean(false);
         }
     }
 }
コード例 #7
0
 /**
  * @dataProvider sanitizeVariableRefInvalidProvider
  */
 public function testSanitizeVariableRefInvalid($value)
 {
     $this->setExpectedException('\\InvalidArgumentException');
     $ref = Utils::sanitizeVariableRef($value);
 }