Process the current expression.

@return float|integer|null The greatest of the operand values or NULL if any of the operand values is NULL.
@throws \qtism\runtime\expressions\operators\OperatorProcessingException
764153
public function process()
    {
        $operands = $this->getOperands();

        if ($operands->containsNull() === true) {
            return null;
        }

        if ($operands->anythingButRecord() === false) {
            $msg = "The Max operator only accept values with a cardinality of single, multiple or ordered.";
            throw new OperatorProcessingException($msg, $this, OperatorProcessingException::WRONG_CARDINALITY);
        }

        if ($operands->exclusivelyNumeric() === false) {
            // As per QTI 2.1 spec, If any of the sub-expressions is not a numerical value, then the result is NULL.
            return null;
        }

        // As per QTI 2.1 spec,
        // The result is a single float, or, if all sub-expressions are of
        // integer type, a single integer.
        $integerCount = 0;
        $valueCount = 0;
        $max = -PHP_INT_MAX;
        foreach ($operands as $operand) {
            if (!$operand instanceof Container) {
                $baseType = ($operand instanceof QtiFloat) ? BaseType::FLOAT : BaseType::INTEGER;
                $value = new MultipleContainer($baseType, array($operand));
            } else {
                $value = $operand;
            }

            foreach ($value as $v) {

                if (is_null($v)) {
                    return null;
                }

                $valueCount++;
                $integerCount += ($v instanceof QtiInteger) ? 1 : 0;

                if ($v->getValue() > $max) {
                    $max = $v->getValue();
                }
            }
        }

        return ($integerCount === $valueCount) ? new QtiInteger(intval($max)) : new QtiFloat(floatval($max));
    }
449270
public function parse()
    {
        $raw = $this->getRaw();
        $count = count($raw);
        for ($i = 7; $i < $count; $i++) {
            $line = $raw[$i];

            $process = new Process(
                $line[11],
                $line[0],
                floatval($line[8]),
                floatval($line[9])
            );

            $this->processes[] = $process;
        }
    }
791432
public function process()
    {
        $expr = $this->getExpression();
        $state = $this->getState();

        $var = $state->getVariable($expr->getIdentifier());

        return ($var === null) ? null : $var->getDefaultValue();
    }
265365
public function process()
    {
        $operands = $this->getOperands();

        if ($operands->containsNull() === true) {
            return null;
        }

        if ($operands->exclusivelySingle() === false) {
            $msg = "The Round operator only accepts operands with a single cardinality.";
            throw new OperatorProcessingException($msg, $this, OperatorProcessingException::WRONG_CARDINALITY);
        }

        if ($operands->exclusivelyNumeric() === false) {
            $msg = "The Round operator only accepts operands with baseType integer or float.";
            throw new OperatorProcessingException($msg, $this, OperatorProcessingException::WRONG_BASETYPE);
        }

        $operand = $operands[0];
        $mode = ($operand->getValue() >= 0) ? PHP_ROUND_HALF_UP : PHP_ROUND_HALF_DOWN;

        return new QtiInteger(intval(round($operand->getValue(), 0, $mode)));
    }
1182957
public function parse()
    {
        // Note: Order is very important here!
        $operators = [
            Expression::OPERATOR_GTE,
            Expression::OPERATOR_LTE,
            Expression::OPERATOR_NOT,
            Expression::OPERATOR_EQ,
            Expression::OPERATOR_GT,
            Expression::OPERATOR_LT
        ];

        foreach ($operators as $operator) {
            $pos = strpos($this->rawExpression, $operator);

            if ($pos) {

                return new Expression(
                    substr($this->rawExpression, 0, $pos),
                    $operator,
                    substr($this->rawExpression, $pos + strlen($operator))
                );
            }
        }

        // Throw an exception if an operator can't be found
        throw new InvalidExpressionException();
    }
53774
public function process()
    {
        parent::process();

        try {
            $value = $this->getNumericValue($this->value);
        } catch (SwatIntegerOverflowException $e) {
            $value = null;
        }

        if ($value !== null) {
            if (
                $this->minimum_value !== null &&
                $value < $this->minimum_value
            ) {
                $message = $this->getValidationMessage('below-minimum');
                $minimum_value = str_replace(
                    '%',
                    '%%',
                    $this->getDisplayValue($this->minimum_value)
                );

                $message->primary_content = sprintf(
                    $message->primary_content,
                    $minimum_value
                );

                $this->addMessage($message);
            }
            if (
                $this->maximum_value !== null &&
                $value > $this->maximum_value
            ) {
                $message = $this->getValidationMessage('above-maximum');
                $maximum_value = str_replace(
                    '%',
                    '%%',
                    $this->getDisplayValue($this->maximum_value)
                );

                $message->primary_content = sprintf(
                    $message->primary_content,
                    $maximum_value
                );

                $this->addMessage($message);
            }
        }
    }
657812
public function process()
    {
        $operands = $this->getOperands();

        if ($operands->containsNull() === true) {
            return null;
        } elseif ($operands->anythingButRecord() === false) {
            $msg = "The Product operator only accepts operands with a single, multiple or ordered cardinality.";
            throw new OperatorProcessingException($msg, $this, OperatorProcessingException::WRONG_CARDINALITY);
        } elseif ($operands->exclusivelyNumeric() === false) {
            $msg = "The Product operator only accepts operands with integer or float baseType.";
            throw new OperatorProcessingException($msg, $this, OperatorProcessingException::WRONG_BASETYPE);
        }

        $returnValue = 1;

        foreach ($this->getOperands() as $operand) {
            if ($operand instanceof QtiScalar) {
                $returnValue *= $operand->getValue();
            } else {
                foreach ($operand as $val) {
                    $returnValue *= $val->getValue();
                }
            }
        }

        return (is_int($returnValue)) ? new QtiInteger($returnValue) : new QtiFloat($returnValue);
    }
22742
public function process()
    {
        if ($this->isDerivedAttribute($this->attribute)) {
            return $this->createAgeVerification($this->attribute);
        }
        return null;
    }
527475
public function process()
    {
        $operands = $this->getOperands();

        if ($operands->containsNull() === true) {
            return null;
        }

        if ($operands->exclusivelySingle() === false) {
            $msg = "The Subtract operator only accepts operands with a single cardinality";
            throw new OperatorProcessingException($msg, $this, OperatorProcessingException::WRONG_CARDINALITY);
        }

        if ($operands->exclusivelyNumeric() === false) {
            $msg = "The Subtract operator only accepts operands with a baseType of integer or float";
            throw new OperatorProcessingException($msg, $this, OperatorProcessingException::WRONG_BASETYPE);
        }

        $operand1 = $operands[0];
        $operand2 = $operands[1];

        $subtract = $operand1->getValue() - $operand2->getValue();

        return (is_int($subtract) === true) ? new QtiInteger($subtract) : new QtiFloat($subtract);
    }
14279
public function process()
    {
        $operands = $this->getOperands();

        if ($operands->containsNull() === true) {
            return null;
        }

        if ($operands->sameBaseType() === false) {
            $msg = "The Member operator only accepts values with the same baseType.";
            throw new OperatorProcessingException($msg, $this, OperatorProcessingException::WRONG_BASETYPE);
        }

        $operand1 = $operands[0];
        $operand2 = $operands[1];

        // The first expression must have single cardinality.
        if (CommonUtils::inferCardinality($operand1) !== Cardinality::SINGLE) {
            $msg = "The first operand of the Member operator must have a single cardinality.";
            throw new OperatorProcessingException($msg, $this, OperatorProcessingException::WRONG_CARDINALITY);
        }

        // The second expression must have multiple or ordered cardinality.
        $cardinality = CommonUtils::inferCardinality($operand2);
        if ($cardinality === Cardinality::SINGLE) {
            $operand2 = new MultipleContainer($operand1->getBaseType(), array($operand2));
        } elseif ($cardinality !== Cardinality::MULTIPLE && $cardinality !== Cardinality::ORDERED) {
            $msg = "The second operand of the Member operator must have a single, multiple or ordered cardinality.";
            throw new OperatorProcessingException($msg, $this, OperatorProcessingException::WRONG_CARDINALITY);
        }

        return new QtiBoolean($operand2->contains($operand1));
    }
538934
public function process()
    {
        $operands = $this->getOperands();

        if ($operands->containsNull()) {
            return null;
        }

        if ($operands->exclusivelySingle() === false) {
            $msg = "The Not Expression only accept operands with single cardinality.";
            throw new OperatorProcessingException($msg, $this, OperatorProcessingException::WRONG_CARDINALITY);
        }

        if ($operands->exclusivelyBoolean() === false) {
            $msg = "The Not Expression only accept operands with boolean baseType.";
            throw new OperatorProcessingException($msg, $this, OperatorProcessingException::WRONG_BASETYPE);
        }

        $operand = $operands[0];

        return new QtiBoolean(!$operand->getValue());
    }
39587
public function process()
    {
        parent::process();

        if (!$this->processValue()) {
            return;
        }

        if ($this->required && $this->isSensitive()) {
            // When values are not serialized, an empty string is treated as
            // null. As a result, you should not use a null value and an empty
            // string value in the same flydown except when using serialized
            // values.
            if (
                ($this->serialize_values && $this->value === null) ||
                (!$this->serialize_values && $this->value == '')
            ) {
                $this->addMessage($this->getValidationMessage('required'));
            }
        }
    }
785472
public function castFloat()
    {
        $castFunction = function ($value) {
            return (float) $value;
        };

        if (is_numeric($this->value)) {
            return $castFunction($this->value);
        }

        return $this->handleMismatch($castFunction, 'double');
    }
325067
public function process()
    {
        $operands = $this->getOperands();

        if (($c = count($operands)) < 2) {
            $msg = "The 'qtism.runtime.expressions.operators.custom.Implode' custom operator takes 2 sub-expressions as parameters, ${c} given.";
            throw new OperatorProcessingException($msg, $this, OperatorProcessingException::NOT_ENOUGH_OPERANDS);
        } elseif ($operands->containsNull() === true) {
            return null;
        } elseif ($operands->exclusivelyString() === false) {
            $msg = "The 'qtism.runtime.expressions.operators.custom.Implode' custom operator only accepts operands with a string baseType.";
            throw new OperatorProcessingException($msg, $this, OperatorProcessingException::WRONG_BASETYPE);
        } elseif ($operands[0]->getCardinality() !== Cardinality::SINGLE) {
            $msg = "The 'qtism.runtime.expressions.operators.custom.Implode' custom operator only accepts a first operand with single cardinality.";
            throw new OperatorProcessingException($msg, $this, OperatorProcessingException::WRONG_CARDINALITY);
        } elseif ($operands[1]->getCardinality() !== Cardinality::MULTIPLE && $operands[1]->getCardinality() !== Cardinality::ORDERED) {
            $msg = "The 'qtism.runtime.expressions.operators.custom.Implode' custom operator only accepts a second operand with multiple or ordered cardinality.";
            throw new OperatorProcessingException($msg, $this, OperatorProcessingException::WRONG_CARDINALITY);
        }

        $glue = $operands[0]->getValue();
        $pieces = $operands[1];

        // Note: implode() is binary-safe \0/!
        $string = implode($glue, $pieces->getArrayCopy());

        return new QtiString($string);
    }
950891
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 QtiBoolean($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);
                } elseif (!$varValue instanceof QtiFloat) {
                    $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 QtiFloat) {
                        $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 QtiBoolean($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 QtiBoolean($moreThanLower && $lessThanUpper);
            }
        }
    }
108805
public function process()
    {
        $operands = $this->getOperands();
        $allFalse = true;

        foreach ($operands as $op) {
            if ($op instanceof Container) {
                $msg = "The Or Expression only accept operands with single cardinality.";
                throw new OperatorProcessingException($msg, $this, OperatorProcessingException::WRONG_CARDINALITY);
            } elseif ($op === null) {
                continue;
            } else {
                if (!$op instanceof QtiBoolean) {
                    $msg = "The Or Expression only accept operands with boolean baseType.";
                    throw new OperatorProcessingException($msg, $this, OperatorProcessingException::WRONG_BASETYPE);
                } else {
                    if ($op->getValue() !== false) {
                        $allFalse = false;
                    }
                }
            }
        }

        if ($allFalse === true && $operands->containsNull() === true) {
            return null;
        }

        foreach ($operands as $operand) {
            if ($operand !== null && $operand->getValue() === true) {
                return new QtiBoolean(true);
            }
        }

        return new QtiBoolean(false);
    }
287223
public function process()
    {
        $testSession = $this->getState();
        $itemSubset = $this->getItemSubset();
        $numberSelected = 0;

        foreach ($itemSubset as $item) {
            $itemSessions = $testSession->getAssessmentItemSessions($item->getIdentifier());
            
            if ($itemSessions !== false) {
                foreach ($itemSessions as $itemSession) {
                    if ($itemSession->isSelected() === true) {
                        $numberSelected++;
                    }
                }    
            }
        }

        return new QtiInteger($numberSelected);
    }