43 private string $result,
44 private ?
string $range_min_txt,
45 private ?
string $range_max_txt,
46 private float $tolerance,
48 private ?
string $formula,
49 private float $points,
50 private int $precision,
51 private bool $rating_simple =
true,
52 private ?
float $rating_sign =
null,
53 private ?
float $rating_value =
null,
54 private ?
float $rating_unit =
null,
55 private float $result_type = 0
58 $this->main_tpl = $DIC->ui()->mainTemplate();
60 $this->
lng = $DIC->language();
64 if ($rating_sign ===
null) {
65 $this->rating_sign = 33;
67 if ($rating_value ===
null) {
68 $this->rating_value = 34;
70 if ($rating_unit ===
null) {
71 $this->rating_unit = 33;
78 preg_match_all(
'/\$r\d+/mi', $formula, $matches);
80 foreach ($matches[0] as $result) {
82 $this->main_tpl->setOnScreenMessage(
'failure', $this->
lng->txt(
'errRecursionInResult'));
86 if (!isset($results[$result]) || !$results[$result] instanceof
self) {
87 $this->main_tpl->setOnScreenMessage(
'failure', $this->
lng->txt(
'errFormulaQuestion'));
91 $formula = preg_replace(
92 '/\\' . $result .
'(?!\d)/m',
98 return "({$formula})";
104 if ($question_id > 0) {
109 if (preg_match_all(
"/(\\\$v\\d+)/ims", $formula, $matches)) {
110 foreach ($matches[1] as $variable) {
111 $varObj = $variables[$variable];
112 if (!is_object($varObj) || !is_numeric($varObj->getValue())) {
115 $value = $varObj->getBaseValue();
116 $formula = preg_replace(
"/\\\$" . substr($variable, 1) .
"(?![0-9]+)/",
"(" . $value .
")" .
"\\1", $formula);
120 $math->suppress_errors =
true;
122 $formula = str_replace(
",",
".", $formula);
123 $result = $math->evaluate($formula);
130 if ($use_precision ==
true) {
145 while ($i < 1000 && !$inRange) {
147 if (preg_match_all(
"/(\\\$v\\d+)/ims", $formula, $matches)) {
148 foreach ($matches[1] as $variable) {
149 $varObj = $variables[$variable];
150 if (!is_object($varObj)) {
153 $varObj->setRandomValue();
154 $formula = preg_replace(
"/\\\$" . substr($variable, 1) .
"(?![0-9]+)/",
"(" . $varObj->getBaseValue() .
")" .
"\\1", $formula);
158 $math->suppress_errors =
true;
159 $result = $math->evaluate($formula);
160 $inRange = (is_numeric($result)) ?
true :
false;
182 for ($i = 0; $i < 1000; $i++) {
184 if (preg_match_all(
"/(\\\$v\\d+)/ims", $formula, $matches)) {
185 foreach ($matches[1] as $variable) {
186 $varObj = $variables[$variable];
187 if (!is_object($varObj)) {
190 $varObj->setRandomValue();
191 $formula = preg_replace(
"/\\\$" . substr($variable, 1) .
"(?![0-9]+)/",
"(" . $varObj->getBaseValue() .
")" .
"\\1", $formula);
195 $math->suppress_errors =
true;
196 $result = $math->evaluate($formula);
197 if (($range_min ==
null) || ($result < $range_min)) {
198 $range_min = $result;
200 if (($range_max ==
null) || ($result > $range_max)) {
201 $range_max = $result;
222 if ($value ===
null || 0 == strlen($value)) {
225 $value = str_replace(
' ',
'', $value);
229 $check_valid_chars =
true;
231 if (preg_match_all(
"/(\\\$v\\d+)/ims", $formula, $matches)) {
232 foreach ($matches[1] as $variable) {
233 $varObj = $variables[$variable];
234 if (!is_object($varObj) || !is_numeric($varObj->getValue())) {
238 if ($varObj->getUnit() !=
null) {
240 if ($varObj->getUnit()->getBaseUnit() != -1) {
241 $tmp_value = $varObj->getValue() * $varObj->getUnit()->getFactor();
243 $tmp_value = $varObj->getValue();
246 $tmp_value = $varObj->getValue();
249 $formula = preg_replace(
"/\\\$" . substr($variable, 1) .
"(?![0-9]+)/",
"(" . $tmp_value .
")" .
"\\1", $formula);
254 $math->suppress_errors =
true;
255 $result = $math->evaluate($formula);
256 $resultWithRespectedUnit = $result;
262 if ($this->
getUnit()->getBaseUnit() != -1) {
269 if ($unit->getFactor() != 1 && strlen(trim($unit->getFactor())) != 1) {
271 $resultWithRespectedUnit =
ilMath::_div($result, $unit->getFactor());
275 $result = substr($result, 0, strlen($resultWithRespectedUnit));
278 $has_valid_chars = preg_match(
"/^-?([0-9]*)(,|\\.|\\/){0,1}([0-9]*)([eE][\\+|-]([0-9])+)?$/", $value, $matches);
279 if (!$has_valid_chars) {
280 $check_valid_chars =
false;
282 (isset($matches[2]) && $matches[2] ==
'/') &&
283 (isset($matches[4]) && strtolower($matches[4]) ==
"e") &&
284 (!isset($matches[1]) || !strlen($matches[1]) || !isset($matches[3]) || !strlen($matches[3]) || $matches[3] == 0)) {
285 $check_valid_chars =
false;
291 if (substr_count($value,
'.') == 1 || substr_count($value,
',') == 1) {
293 $frac_value = str_replace(
',',
'.', $exp_val);
295 $frac_value = $value;
298 if (substr_count($value,
'/') >= 1) {
299 $check_fraction =
false;
301 $check_fraction =
true;
307 $exp_val = explode(
'/', $value);
308 if (count($exp_val) == 1) {
311 if (ilMath::_equals($frac_value, $resultWithRespectedUnit, $this->
getPrecision())) {
312 $check_fraction =
true;
314 $check_fraction =
false;
326 $frac_value = str_replace(
',',
'.', $frac_value);
328 if (ilMath::_equals($frac_value, $resultWithRespectedUnit, $this->
getPrecision())) {
329 $check_fraction =
true;
333 if (!self::isCoprimeFraction($exp_val[0], $exp_val[1])) {
334 $check_fraction =
false;
339 if (substr_count($value,
'.') >= 1 || substr_count($value,
',') >= 1) {
340 $check_fraction =
false;
346 if (substr_count($value,
'.') == 1 || substr_count($value,
',') == 1) {
347 $frac_value = str_replace(
',',
'.', $value);
348 } elseif (substr_count($value,
'/') == 1) {
349 $exp_val = explode(
'/', $value);
360 $frac_value = $value;
363 $check_fraction =
true;
367 if (is_object($unit)) {
368 if (isset($frac_value)) {
369 $value =
ilMath::_mul($frac_value, $unit->getFactor(), 100);
373 $frac_value = ilMath::_round($frac_value, $this->
getPrecision());
374 $resultWithRespectedUnit = ilMath::_round($resultWithRespectedUnit, $this->
getPrecision());
377 if (isset($frac_value)) {
389 if (is_object($unit)) {
390 if ($unit->getId() != $this->
getUnit()->getId()) {
395 return $checkvalue && $checkunit && $check_fraction && $check_valid_chars;
398 protected function isInTolerance($user_answer, $expected, $tolerated_percentage): bool
402 $lower_boundary =
ilMath::_sub($expected, $tolerance_abs);
403 $upper_boundary =
ilMath::_add($expected, $tolerance_abs);
405 return $lower_boundary <= $user_answer
406 && $user_answer <= $upper_boundary;
411 return ($v1 >= 0.0 && $v2 >= 0.0) || ($v1 <= 0.0 && $v2 <= 0.0);
420 string $answer_value,
425 return $this->
isCorrect($variables, $results, $answer_value, $answer_unit)
438 $base1 instanceof assFormulaQuestionUnit
439 && $base2 instanceof assFormulaQuestionUnit
440 && $base1->getId() === $base2->getId()
446 if ($float_value ===
null) {
450 if ($this->
checkSign($result, $float_value)) {
474 case self::RESULT_DEC:
476 case self::RESULT_FRAC:
477 case self::RESULT_CO_FRAC:
478 $exp_val = explode(
'/', $value);
481 count($exp_val) === 1 ? 1 : $exp_val[1],
485 case self::RESULT_NO_SELECTION:
487 $exp_val = explode(
'/', $value);
489 if (count($exp_val) === 2
490 && (
float) $exp_val[1] === 0.0) {
496 count($exp_val) === 1 ? 1 : $exp_val[1],
502 $this->
refinery->kindlyTo()->float(),
504 ])->
transform(round($value, $this->precision));
513 return [
"points" => 0];
518 if (preg_match_all(
"/(\\\$v\\d+)/ims", $formula, $matches)) {
519 foreach ($matches[1] as $variable) {
520 $varObj = $variables[$variable];
521 $formula = preg_replace(
"/\\\$" . substr($variable, 1) .
"(?![0-9]+)/",
"(" . $varObj->getBaseValue() .
")" .
"\\1", $formula);
525 $math->suppress_errors =
true;
526 $result = $math->evaluate($formula);
530 if (is_object($unit)) {
537 $totalpoints += $points;
538 $details[
'sign'] = $points;
542 $totalpoints += $points;
543 $details[
'value'] = $points;
546 $base1 = $units[$unit];
547 if (is_object($base1)) {
548 $base1 = $units[$base1->getBaseUnit()];
550 $base2 = $units[$this->
getUnit()->getBaseUnit()];
551 if (is_object($base1) && is_object($base2) && $base1->getId() == $base2->getId()) {
553 $totalpoints += $points;
554 $details[
'unit'] = $points;
557 $details[
'points'] = $totalpoints;
568 $this->result = $result;
573 return $this->result;
578 if ($range_min ===
null) {
583 $math->suppress_errors =
true;
584 $this->range_min = (float) $math->evaluate($range_min);
603 if ($range_max ===
null) {
608 $math->suppress_errors =
true;
609 $this->range_max = (float) $math->evaluate($range_max);
628 $this->tolerance = $tolerance;
633 return $this->tolerance;
648 $this->formula = $formula;
653 return $this->formula;
658 $this->points = $points;
663 return $this->points;
668 $this->rating_simple = $rating_simple;
673 return $this->rating_simple;
678 $this->rating_sign = $rating_sign;
683 return $this->rating_sign;
688 $this->rating_value = $rating_value;
693 return $this->rating_value;
698 $this->rating_unit = $rating_unit;
703 return $this->rating_unit;
708 $this->precision = $precision;
713 return $this->precision;
718 $this->result_type = $a_result_type;
723 return (
int) $this->result_type;
728 $this->range_max_txt = $range_max_txt;
733 return $this->range_max_txt;
738 $this->range_min_txt = $range_min_txt;
743 return $this->range_min_txt;
749 $ilDB = $DIC[
'ilDB'];
754 FROM il_qpl_qst_fq_res 755 WHERE question_fi = %s 758 [$a_qst_id, $a_result]
763 return $row[
'result_type'];
768 $gcd = self::getGreatestCommonDivisor(abs($numerator), abs($denominator));
770 return $gcd == 1 ? true :
false;
775 if (empty($decimal_value)) {
779 $to_string = (string) $decimal_value;
780 $is_negative = strpos($to_string,
'-') === 0;
782 $decimal_value = substr($decimal_value, 1);
788 $b = 1 / $decimal_value;
793 $h1 =
$a * $h1 + $h2;
796 $k1 =
$a * $k1 + $k2;
799 }
while ((abs($decimal_value - $h1 / $k1) > $decimal_value * $tolerance) || ($k1 < 0 ||
$b < 0));
805 $checkResult = ($h1 / $k1);
808 $result =
'-' . $result;
809 $checkResult = ($h1 / $k1) * -1;
811 if ($to_string == $checkResult .
'' || $checkResult .
'' == $result) {
814 return [$to_string,$result];
821 return self::getGreatestCommonDivisor(
$b,
$a %
$b);
831 $ilDB = $DIC[
'ilDB'];
835 SELECT * FROM il_qpl_qst_fq_res_unit 836 WHERE question_fi = %s 844 $this->available_units[$row[
'result']][] = $row[
'unit_fi'] ;
static _add($left_operand, $right_operand, int $scale=50)
static _div($left_operand, $right_operand, int $scale=50)
while($session_entry=$r->fetchRow(ilDBConstants::FETCHMODE_ASSOC)) return null
static _sub($left_operand, $right_operand, int $scale=50)
$a
thx to https://mlocati.github.io/php-cs-fixer-configurator for the examples
static _mul($left_operand, $right_operand, int $scale=50)