Example #1
0
 /**
  * Process the Repeat operator.
  *
  * Note: NULL values are simply ignored. If all sub-expressions are NULL, NULL is
  * returned.
  *
  * @return \qtism\runtime\common\OrderedContainer An ordered container filled sequentially by evaluating each sub-expressions, repeated a 'numberRepeats' of times. NULL is returned if all sub-expressions are NULL or numberRepeats < 1.
  * @throws \qtism\runtime\expressions\operators\OperatorProcessingException
  */
 public function process()
 {
     $operands = $this->getOperands();
     // get the value of numberRepeats
     $expression = $this->getExpression();
     $numberRepeats = $expression->getNumberRepeats();
     if (gettype($numberRepeats) === 'string') {
         // Variable reference found.
         $state = $this->getState();
         $varName = Utils::sanitizeVariableRef($numberRepeats);
         $varValue = $state[$varName];
         if (is_null($varValue) === true) {
             $msg = "The variable with name '{$varName}' could not be resolved.";
             throw new OperatorProcessingException($msg, $this);
         } elseif ($varValue instanceof Integer) {
             $msg = "The variable with name '{$varName}' is not an integer value.";
             throw new OperatorProcessingException($msg, $this);
         }
         $numberRepeats = $varValue->getValue();
     }
     if ($numberRepeats < 1) {
         return null;
     }
     $result = null;
     for ($i = 0; $i < $numberRepeats; $i++) {
         $refType = null;
         foreach ($operands as $operand) {
             // If null, ignore
             if (is_null($operand) || $operand instanceof Container && $operand->isNull()) {
                 continue;
             }
             // Check cardinality.
             if ($operand->getCardinality() !== Cardinality::SINGLE && $operand->getCardinality() !== Cardinality::ORDERED) {
                 $msg = "The Repeat operator only accepts operands with a single or ordered cardinality.";
                 throw new OperatorProcessingException($msg, $this, OperatorProcessingException::WRONG_CARDINALITY);
             }
             // Check baseType.
             $currentType = RuntimeUtils::inferBaseType($operand);
             if ($refType !== null && $currentType !== $refType) {
                 $msg = "The Repeat operator only accepts operands with the same baseType.";
                 throw new OperatorProcessingException($msg, $this, OperatorProcessingException::WRONG_BASETYPE);
             } elseif (is_null($result)) {
                 $refType = $currentType;
                 $result = new OrderedContainer($refType);
             }
             // Okay we are good...
             $operandCardinality = RuntimeUtils::inferCardinality($operand);
             if ($operandCardinality !== Cardinality::ORDERED) {
                 $operand = new OrderedContainer($currentType, array($operand));
             }
             foreach ($operand as $o) {
                 $result[] = $o instanceof QtiDatatype ? clone $o : $o;
             }
         }
     }
     if (isset($result) && $result->isNull() !== true) {
         return $result;
     } else {
         return null;
     }
 }
Example #2
0
 /**
  * Process the RoundTo operator.
  *
  * An OperatorProcessingException will be thrown if:
  *
  * * The given operand is not a numeric value.
  * * The cardinality of the operand is not single.
  * * The value of the 'figures' attribute comes from a templateVariable which does not exist or is not numeric or null.
  *
  * @return null|float A single float with the value nearest to that of the expression's value or NULL if the sub-expression is NaN.
  * @throws \qtism\runtime\expressions\operators\OperatorProcessingException
  */
 public function process()
 {
     $operands = $this->getOperands();
     $state = $this->getState();
     $operand = $operands[0];
     // If the value is null, return null.
     if ($operands->containsNull()) {
         return null;
     }
     if (!$operands->exclusivelySingle()) {
         $msg = "The RoundTo operator accepts 1 operand with single cardinality.";
         throw new OperatorProcessingException($msg, $this, OperatorProcessingException::WRONG_CARDINALITY);
     }
     // Accept only numerical operands.
     if (!$operands->exclusivelyNumeric()) {
         $msg = "The RoundTo operand accepts 1 operand with numerical baseType.";
         throw new OperatorProcessingException($msg, $this, OperatorProcessingException::WRONG_BASETYPE);
     }
     // As per QTI 2.1 spec...
     if (is_nan($operand->getValue())) {
         return null;
     } elseif (is_infinite($operand->getValue())) {
         return $operand;
     }
     $roundingMode = $this->getExpression()->getRoundingMode();
     $figures = $this->getExpression()->getFigures();
     if (gettype($figures) === 'string') {
         // try to recover the value from the state.
         $figuresIdentifier = Utils::sanitizeVariableRef($figures);
         $figures = $state[$figuresIdentifier];
         if (is_null($figures)) {
             $msg = "The variable '{$figuresIdentifier}' used to set up the 'figures' attribute is null or nonexisting.";
             throw new OperatorProcessingException($msg, $this, OperatorProcessingException::NONEXISTENT_VARIABLE);
         } elseif (!$figures instanceof Integer) {
             $msg = "The variable '{$figuresIdentifier}' used to set up the 'figures' attribute is not an integer.";
             throw new OperatorProcessingException($msg, $this, OperatorProcessingException::WRONG_VARIABLE_BASETYPE);
         }
         $figures = $figures->getValue();
     }
     if ($roundingMode === RoundingMode::SIGNIFICANT_FIGURES) {
         if ($figures <= 0) {
             // As per QTI 2.1 spec.
             $msg = "The 'figures' attribute must be a non-zero positive integer when mode 'significantFigures' is used, '{$figures}' given.";
             throw new OperatorProcessingException($msg, $this, OperatorProcessingException::LOGIC_ERROR);
         }
         if ($operand->getValue() == 0) {
             return new Float(0.0);
         }
         $d = ceil(log10($operand->getValue() < 0 ? -$operand->getValue() : $operand->getValue()));
         $power = $figures - intval($d);
         $magnitude = pow(10, $power);
         $shifted = round($operand->getValue() * $magnitude);
         return new Float(floatval($shifted / $magnitude));
     } else {
         // As per QTI 2.1 spec.
         if ($figures < 0) {
             $msg = "The 'figures' attribute must be a integer greater than or equal to zero when mode 'decimalPlaces' is used, '{$figures}' given.";
             throw new OperatorProcessingException($msg, $this);
         }
         return new Float(round($operand->getValue(), $figures));
     }
 }