package ONO::FW::Apps::Core::Math;
################################################################################
# COPYRIGHT / LICENSE #
################################################################################
#
# This file is part of the ONO Software Project.
#
# Copyright (C) 2000-2025 Jos KIRPS [ www.kirps.com | jos_AT_kirps_DOT_com ]
# and The Joopita Project [ www.joopita.org | contact_AT_joopita_DOT_com ]
#
# This file, as well as other parts of the ONO Software Project or related
# elements, are FREE SOFTWARE available under the ARTISTIC LICENSE 2.0.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
# For the full license, see /ono/osr/license/LICENSE.txt, or write to
# jos_AT_kirps_DOT_com or contact_AT_joopita_DOT_com.
#
################################################################################
# END OF COPYRIGHT / LICENSE, HERE COMES THE CODE ... #
################################################################################
use strict;
use ONO::IO;
###################################################################
#
###################################################################
#: This module offers functions used by math-related ONO apps.
sub calc {
#: Math data to string.
my (
$self,
$num1,
$op,
$num2,
) = @_;
my $string = "$num1 $op $num2";
$string =~ s~ add ~ + ~;
$string =~ s~ sub ~ - ~;
$string =~ s~ mul ~ * ~;
$string =~ s~ div ~ / ~;
return eval($string);
}
sub comma_limit {
my (
$self,
$num,
$limit,
) = @_;
#: Limit comma number.
if ($num =~ /^(.*)(\.|\,)(.*)$/) {
my ($int,$sep,$comma) = ($1,$2,$3);
if ($limit == 0) {
$num = $int;
} else {
if (length $comma > $limit) {
$comma = substr($comma,0,$limit);
$num = "$int$sep$comma";
}
}
}
return $num;
}
sub fix_fp_rounding {
my $num = $_[1];
my $force_non_comma_number = $_[2];
#: Fix bad floating point rounding problems. Note that
#: force_non_comma_number can be used to process only the
#: part behind the comma.
if ($num =~ /^(.*)(\.|\,)(.*)$/) {
my ($int,$sep,$comma) = ($1,$2,$3);
if (length $comma > 12) {
# in case of 999999999X...
if ($comma =~ /999(1|2|3|4|5|6|7|8|9)$/) {
my $add = 10-$1;
$num = $num+"0.0000000000000$add"
}
# in case of 000000000X...
if ($comma =~ s/000(1|2|3|4|5|6|7|8|9)$//) {
my $whilecounter;
while ($comma =~ s~0$~~ && $whilecounter < 256) {
$whilecounter++;
$comma =~ s~0$~~;
}
$num = "$int$sep$comma";
}
}
# add some further brute force:
$num =~ s/000000000(.*)$//;
$num =~ s/999999999(.*)$//;
}
if ($force_non_comma_number) {
# in case of X999999999...
if ($num =~ s/999999(.*)$//) {
$num++;
}
# in case of 000000000X...
if ($num =~ s/000(1|2|3|4|5|6|7|8|9)$//) {
my $whilecounter;
while ($num =~ s~0$~~ && $whilecounter < 256) {
$whilecounter++;
$num =~ s~0$~~;
}
}
}
if (!$num) {
$num = 0;
}
return $num;
}
sub float_to_decimal {
my $num = $_[1];
my $comma;
if ($num =~ s/\,/\./) {
$comma = ",";
}
#: Floating point to decimal conversion.
#: This piece of code has been found here:
#: stackoverflow_DOT_com/ questions/ 22976776/ convert-scientific-notation-to-decimal-not-integer-in-bash-perl
if ($num =~ /^([+-]?)(\d*)(\.?)(\d*)[Ee]([-+]?\d+)$/) {
my ($sign,$int,$period,$dec,$exp) = ($1,$2,$3,$4,$5);
if ($exp < 0) {
my $len = 1 - $exp;
$int = ('0' x ($len - length $int)) . $int if $len > length $int;
substr $int, $exp, 0, '.';
$num = $sign.$int.$dec;
} elsif ($exp > 0) {
$dec .= '0' x ($exp - length $dec) if $exp > length $dec;
substr $dec, $exp, 0, '.' if $exp < length $dec;
$num = $sign.$int.$dec;
} else {
$num = $sign.$int.$period.$dec;
}
}
if ($comma) {
$num =~ s~\.~$comma~;
}
return $num;
}
sub generate_add {
my (
$self,
$i,
$num1,
$num2,
$op,
$res,
$cache1,
$cache2,
$vars_ref,
) = @_;
my %vars = %$vars_ref;
#: Number generator - addition
my $whilecounter;
my $DEBUG = "GenAdd: $num1, $num2, $op:";
if ($num1 eq "" || $num2 eq "") {
while (($num1 eq "" || $num2 eq "" || ($num1 == $cache1 && $num2 == $cache2)) && $whilecounter < 256) {
$whilecounter++;
$num1 = &random_number("",1,$vars{'app_input_range_to'},1,9999999,$vars_ref);
$num2 = &random_number("",1,$vars{'app_input_range_to'},1,9999999,$vars_ref,"a");
while ($num1 + $num2 > $vars{'app_input_range_to'} && $whilecounter < 512) {
$whilecounter++;
if (int(rand(10)) < 5) {
$num1 = int($num1/(1+rand(2)));
} else {
$num2 = int($num2/(1+rand(2)));
}
}
while ($num1 + $num2 < $vars{'app_input_range_from'} && $whilecounter < 512) {
$whilecounter++;
if (int(rand(10)) < 5) {
$num1 = int($num1*1.25);
} else {
$num2 = int($num2*1.25);
}
}
}
if ($vars{'app_input_tens_barrier'} eq "0") {
if ($num2 > 9 && !$vars{'app_input_tens_barrier_long'}) {
$num2 = int(rand(9));
}
my $offset = (length $num2) - (length $num1);
for (my $i = (length $num1)-1; $i > -2; $i--) {
if (substr($num1,$i,1) + substr($num2,$i+$offset,1) > 9) {
substr($num2,$i+$offset,1) = 9 - substr($num1,$i,1);
}
}
while ($num2 =~ s~^0~~ && $whilecounter < 256) {
$num2 =~ s~^0~~;
$whilecounter++;
}
if (!$num2) {
$num2 = 0;
}
}
if ($vars{'app_input_comma_rand'}) {
$vars{'app_input_comma_status'} = int(rand(4));
}
for (my $n = 1; $n < 3; $n++) {
my $comma_status = $vars{'app_input_comma_status'};
if ($n == 1) {
if ($comma_status > 0 && $num1 !~ /\./) {
$num1--;
if ($num1 < 0) {
$num1 = 0;
}
$num1 = $num1.".".int(rand(9))+1;
}
}
if ($n == 2) {
if ($vars{'app_input_comma_status_alt'} =~ /^(no|1|2|3|rand)$/) {
$comma_status = $vars{'app_input_comma_status_alt'};
if ($vars{'app_input_comma_status_alt'} eq "rand") {
$comma_status = int(rand(4));
}
}
if ($comma_status > 0 && $num2 !~ /\./) {
$num2--;
if ($num2 < 0) {
$num2 = 0;
}
$num2 = $num2.".".int(rand(9))+1;
}
}
}
$cache1 = $num1;
$cache2 = $num2;
}
$num1 = &round_up("",$num1,$vars_ref);
$num2 = &round_up("",$num2,$vars_ref,"a");
if ($num1 =~ /e/ || $num2 =~ /e/ || $res =~ /e/) {
$num1 = int(rand(100));
$num2 = int(rand(100));
$res = $num1 + $num2;
}
if ($res eq "") {
$res = &fix_fp_rounding("",$num1 + $num2);
}
if ($num1 eq "") {
$res = $num2;
}
if ($num2 eq "") {
$res = $num1;
}
return ($num1,$num2,$res,$cache1,$cache2,"$DEBUG<br>");
}
sub generate_sub {
my (
$self,
$i,
$num1,
$num2,
$op,
$res,
$cache1,
$cache2,
$vars_ref,
) = @_;
#: Number generator - subtraction
my %vars = %$vars_ref;
my $whilecounter;
my $DEBUG = "GenSub: $num1, $num2, $op:";
while (($num1 eq "" || $num2 eq "" || ($num1 == $cache1 && $num2 == $cache2)) && $whilecounter < 16) {
$whilecounter++;
$num1 = &random_number("",$vars{'app_input_range_from'},$vars{'app_input_range_to'},2,9999999,$vars_ref);
$num2 = &random_number("",1,$num1,1,9999999,$vars_ref,"a");
}
if ($vars{'app_input_tens_barrier'} eq "0") {
if ($num2 > 9 && !$vars{'app_input_tens_barrier_long'}) {
$num2 = int(rand(9));
}
my $offset = (length $num2) - (length $num1);
for (my $i = (length $num1)-1; $i > -1; $i--) {
if (substr($num2,$i+$offset,1) > substr($num1,$i,1)) {
substr($num2,$i+$offset,1) = 0;
}
}
while ($num2 =~ s~^0~~ && $whilecounter < 256) {
$num2 =~ s~^0~~;
$whilecounter++;
}
if (!$num2) {
$num2 = 0;
}
}
# try to prevent second number to be zero too often...
# note that this will ALWAYS respect the tens barrier!
if (!$num2 && $num1 !~ /\./ && $num2 !~ /\./) {
if ($num1 > 10) {
my $one = int(rand(substr($num1,(length $num1)-1,1)));
if (!$one) {
$one = int(rand(substr($num1,(length $num1)-1,1)));
}
my $two = int(rand(substr($num1,(length $num1)-2,1)));
if (!$two) {
$two = int(rand(substr($num1,(length $num1)-2,1)));
}
$num2 = "$two$one";
} else {
if ($num1 > 1) {
$num2 = int(rand($num1));
if (!$num2) {
$num2 = int(rand($num1));
}
}
}
$num2 =~ s/^0//;
if (!$num2) {
$num2 = 0;
}
}
if ($vars{'app_input_comma_rand'}) {
$vars{'app_input_comma_status'} = int(rand(4));
}
if ($vars{'app_input_comma_status'} > 0 && $num1 !~ /\./ && $num2 !~ /\./) {
$num2--;
if ($num2 < 0) {
$num2 = 0;
}
$num1 = $num1.".".int(rand(9))+1;
$num2 = $num2.".".int(rand(9))+1;
}
$cache1 = $num1;
$cache2 = $num2;
$num1 = &round_up("",$num1,$vars_ref);
$num2 = &round_up("",$num2,$vars_ref,"a");
if ($num2 > $num1) {
my $cache = $num1;
$num1 = $num2;
$num2 = $cache;
$res = &fix_fp_rounding("",$num1 - $num2);
}
if ($num1 =~ /e/ || $num2 =~ /e/ || $res =~ /e/) {
$num1 = int(rand(100));
$res = int(rand(100));
$num2 = $res - $num1;
}
if ($res eq "") {
$res = &fix_fp_rounding("",$num1 - $num2);
}
if ($num1 == $num2) {
$res = 0;
}
return ($num1,$num2,$res,$cache1,$cache2,"$DEBUG<br>");
}
sub generate_muldiv {
my (
$self,
$i,
$num1,
$num2,
$op,
$res,
$cache1,
$cache2,
$vars_ref,
$force_flush,
) = @_;
#: Number generator - multiplication and division
my %vars = %$vars_ref;
my $whilecounter;
my $DEBUG = "GenMulDiv: $num1, $num2, $op:";
if ($vars{"output_web_cache_num1_$i"} eq "" || $vars{"output_web_cache_num2_$i"} eq "") {
while (($num1 eq "" || $num2 eq "" || ($num1 == $cache1 && $num2 == $cache2)) && $whilecounter < 1024) {
$whilecounter++;
$num1 = &random_number("",1,$vars{'app_input_range_to'},1,9999999,$vars_ref);
$num2 = &random_number("",1,$vars{'app_input_range_to'},1,9999999,$vars_ref,"a");
my $divide;
if (!$vars{'app_input_op_muldiv_bigger_10'}) {
$num1 = int(rand(11));
$num2 = int(rand(11));
}
while ($num1 * $num2 > $vars{'app_input_range_to'} && $whilecounter < 1024) {
$whilecounter++;
$divide++;
$num1 = $num1/(1+rand(3));
$num2 = $num2/(1+rand(3));
}
if ($vars{'app_input_comma_rand'}) {
$vars{'app_input_comma_status'} = int(rand(4));
}
if ($divide) {
$num1 = &comma_limit("",$num1,int(rand($vars{'app_input_comma_status'})));
$num2 = &comma_limit("",$num2,int(rand($vars{'app_input_comma_status'})));
}
if ($vars{'app_input_comma_status'} > 0 && $num1 !~ /\./ && $num2 !~ /\./) {
$num1--;
$num2--;
if ($num1 < 0) {
$num1 = 0;
}
if ($num2 < 0) {
$num2 = 0;
}
$num1 = $num1.".".int(rand(9))+1;
$num2 = $num2.".".int(rand(9))+1;
}
}
$DEBUG .= "step1:$num1,$num2, ";
$cache1 = $num1;
$cache2 = $num2;
$num1 = &round_up("",$num1,$vars_ref);
$num2 = &round_up("",$num2,$vars_ref,"a");
my $whilecounter;
while (length "$num1$num2" > 10 && $whilecounter < 512) {
$whilecounter++;
if (int(rand(10)) > 5) {
$num1 =~ s~^[1-9]([1-9])~$1~;
} else {
$num2 =~ s~^[1-9]([1-9])~$1~;
}
}
$DEBUG .= "step2:$num1,$num2, ";
# force numbers to have commas if possible
if ($vars{'app_input_comma_status'} > 0) {
if ($num1 !~ /\./) {
$num1 = $num1/10;
}
if ($num2 !~ /\./) {
$num2 = $num2/10;
}
if ($vars{'app_input_comma_status'} > 1 && length "$num1$num2" < 6) {
if ($num1 =~ /\./) {
$num1 .= int(rand(9));
} else {
$num1 .= ".".int(rand(9));
}
if (length "$num1$num2" < 6) {
if ($num2 =~ /\./) {
$num2 .= int(rand(9));
} else {
$num2 .= ".".int(rand(9));
}
}
}
}
$DEBUG .= "step3:$num1,$num2, ";
# fix bad res
if ($res eq "") {
$res = &fix_fp_rounding("",$num1 * $num2);
}
$DEBUG .= "step4:$num1,$num2, ";
# special case: mulrow selector mode
if (($vars{'app_input_comma_status'} eq "" || $vars{'app_input_comma_status'} eq "no") && $vars{'app_input_op_muldiv_bigger_10'} < 1) {
my @mains = (1,2,3,4,5,6,7,8,9,10);
if ($vars{'app_input_mulrow_main'} =~ /^(.*?)-(.*?)$/) {
@mains = ();
for (my $m = $1; $m < $2+1; $m++) {
@mains = (@mains,$m);
if ($m > 1) {
@mains = (@mains,$m);
}
}
}
if ($vars{'app_input_mulrow_main'} =~ /\:/) {
@mains = split(/\:/,$vars{'app_input_mulrow_main'})
}
my $mains_num = @mains;
my @rows;
for (my $r = 1; $r < 21; $r++) {
if ($vars{"app_input_mulrow_$r"}) {
@rows = (@rows,$r);
if ($r > 1) {
@rows = (@rows,$r);
}
}
}
my $rows_num = @rows;
if ($mains_num && $rows_num) {
my $whilecounter;
while ($whilecounter < 256 && (!$whilecounter || $res < $vars{'app_input_range_from'} || $res > $vars{'app_input_range_to'})) {
$whilecounter++;
$num1 = $mains[int(rand($mains_num))];
$num2 = $rows[int(rand($rows_num))];
$res = $num1 * $num2;
}
}
}
$DEBUG .= "step5:$num1,$num2, ";
if ($op eq "div" && (!$vars{"output_web_cache_res_$i"} || $force_flush)) {
my $num = $num1;
$num1 = $res;
$res = $num;
if ($num2 > 15 && $num2 > $res) {
my $num = $num2;
$num2 = $res;
$res = $num;
}
if ($num2 == 0) {
$num2 = 1;
}
}
$DEBUG .= "step6:$num1,$num2, ";
if ($num1 =~ /e/ || $num2 =~ /e/ || $res =~ /e/) {
if ($op eq "mul") {
$num1 = 10;
$num2 = 10;
$res = 100;
} else {
$num1 = 100;
$num2 = 10;
$res = 10;
}
}
if ($vars{'app_input_extype_status'} ne "tri") {
if ($op eq "div") {
if ($num1 == 0) {
$res = 0;
}
if ($num2 == 0) {
$num2 = 1;
$cache2 = 1;
$res = $num1;
}
}
}
$DEBUG .= "return:$num1,$num2, ";
}
return ($num1,$num2,$res,$cache1,$cache2,"$DEBUG,$num1,$num2,$force_flush<br>");
}
sub random_number {
my (
$self,
$from,
$to,
$min,
$max,
$vars_ref,
$switches,
) = @_;
#: Random number generator.
#:
#: Switches:
#:
#: -a use _alt for digits, comma, round_up, but only if not empty
my %vars = %$vars_ref;
my $whilecounter;
if ($switches =~ /a/) {
if ($vars{'app_input_digits_status_alt'} =~ /[0-9]/) {
$vars{'app_input_digits_status'} = $vars{'app_input_digits_status_alt'};
}
if ($vars{'app_input_comma_status_alt'} =~ /[0-9]/ || $vars{'app_input_comma_status_alt'} =~ /rand/) {
$vars{'app_input_comma_status'} = $vars{'app_input_comma_status_alt'};
}
}
my $num = $from+int(rand($to-$from));
if ($vars{'app_input_comma_rand'}) {
$vars{'app_input_comma_status'} = int(rand(4));
}
if ($vars{'app_input_comma_status'} == 1) {
$num = $from+int((rand($to-$from)/2));
}
if ($vars{'app_input_comma_status'} == 2) {
$num = $from+int((rand($to-$from)/3));
}
if ($vars{'app_input_comma_status'} == 3) {
$num = $from+int((rand($to-$from)/4));
}
# check digits - step 1 of 2
if ($vars{'app_input_digits_status'}) {
while (length $num < $vars{'app_input_digits_status'} && $whilecounter < 10) {
$whilecounter++;
$num = $num.int(rand(10));
}
if (length $num > $vars{'app_input_digits_status'}) {
$num = substr($num,0,$vars{'app_input_digits_status'});
}
}
# check min + max
if ($min && $num < $min) {
$num = $min;
}
while ($max && $num > $max && $whilecounter < 255) {
$whilecounter++;
$num = int($num/2);
}
# check digits - step 2 of 2 (as min/max may have killed one digit)
if ($vars{'app_input_digits_status'} && length $num < $vars{'app_input_digits_status'}) {
$num = int(rand(substr($max,0,1))).$num;
}
if ($vars{'app_input_comma_status'} > 0) {
my $digits = int(rand($vars{'app_input_comma_status'}+1));
if ($digits == 0) {
# make zero digits less probable
$digits = int(rand($vars{'app_input_comma_status'}+1));
}
# tune probabilites even more if status 2 or 3...
if ($vars{'app_input_comma_status'} > 1 && $digits < 2) {
$digits = 1+int(rand($vars{'app_input_comma_status'}));
}
# tune probabilites even more if status 3...
if ($vars{'app_input_comma_status'} == 3 && $digits < 3) {
$digits = 2+int(rand($vars{'app_input_comma_status'}-1));
}
if ($digits) {
if ($num > 0) {
$num--;
}
$num .= ".";
for (my $c = 0; $c < $digits; $c++) {
if ($c == $digits-1) {
$num .= int(rand(9))+1;
} else {
$num .= int(rand(10));
}
}
}
}
if ($vars{'app_input_negative'} && int(rand(100)) > 50) {
$num = -$num;
}
if ($num =~ /e/) {
$num = int(rand(100));
}
return $num;
}
sub round_up {
my (
$self,
$num,
$vars_ref,
$switches,
) = @_;
#: Round up a number.
#:
#: Switches:
#:
#: -a use _alt for digits, comma, round_up, but only if not empty
my %vars = %$vars_ref;
if ($switches =~ /a/) {
if ($vars{'app_input_roundup_status_alt'} =~ /^(no|10|100)$/) {
$vars{'app_input_roundup_status'} = $vars{'app_input_roundup_status_alt'};
}
}
if (!$vars{'app_input_numsys'}) {
if ($vars{'app_input_roundup_status'} == 10) {
$num = int($num/10)."0";
}
if ($vars{'app_input_roundup_status'} == 100) {
$num = int($num/100)."00";
}
my $whilecounter;
while ($num =~ s/^00/0/ && $whilecounter < 256) {
$whilecounter++;
$num =~ s/^00/0/;
}
}
return $num;
}
sub make_human_readable {
my (
$self,
$num,
$num_comma,
$num_delimiter,
) = @_;
#: Make a number human readable.
if ($num =~ /[0-9]/ && length $num > 3) {
my ($int,$sep,$comma);
if ($num =~ /^(.*)(\.|\,)(.*)$/) {
($int,$sep,$comma) = ($1,$2,$3);
} else {
$int = $num;
}
if (length $int > 3) {
my $new = $int;
if (length $new > 6) {
if (length $new > 9) {
$new = substr($int,0,(length $int)-9)."\^".substr($int,(length $int)-9,3)."\^".substr($int,(length $int)-6,3)."\^".substr($int,(length $int)-3,3);
} else {
$new = substr($int,0,(length $int)-6)."\^".substr($int,(length $int)-6,3)."\^".substr($int,(length $int)-3,3);
}
} else {
$new = substr($int,0,(length $int)-3)."\^".substr($int,(length $int)-3,3);
}
if ($comma) {
$num = "$new$sep$comma";
} else {
$num = $new;
}
}
}
if ($num_comma ne ".") {
$num =~ s~\.~$num_comma~g;
}
$num =~ s~-\^~-~;
$num =~ s~\^~$num_delimiter~g;
return $num;
}
sub get_operations {
#: Get an array of allowed operations.
my $vars_ref = $_[1];
my %vars = %$vars_ref;
my (@opts,$found);
foreach my $opt ('add','sub','mul','div') {
if ($vars{"app_input_op_$opt"}) {
$found++;
@opts = (@opts,$opt);
}
}
if (!$found) {
@opts = ('add');
}
return @opts;
}
sub operation_symbol {
#: Display an operation symbol (HTML and PDF)
#:
#: Switches:
#:
#: -A add is NOT default
#: -c colorize
#: -e empty becomes question mark
#: -p print / pdf mode
#: -s use slash as division symbol
#: -x executable (simple code)
my $symbol = "+";
if ($_[2] =~ /A/) {
$symbol = "";
}
if ($_[1] eq "add") {
$symbol = "+";
}
if ($_[1] eq "sub") {
$symbol = "–"; # long dash
if ($_[2] =~ /p/) {
$symbol = "-"; # simple minus
}
if ($_[2] =~ /x/) {
$symbol = "-";
}
}
if ($_[1] eq "mul") {
$symbol = "•";
if ($_[2] =~ /p/) {
$symbol = "x"; # should be replaced by circle / bullet code
}
if ($_[2] =~ /x/) {
$symbol = "*";
}
}
if ($_[1] eq "div") {
$symbol = ":"; # division - colon
if ($_[2] =~ /s/) {
$symbol = "/"; # division - slash
}
if ($_[2] =~ /x/) {
$symbol = "/";
}
}
if ($_[1] eq "eq" || $_[1] eq "=") {
$symbol = "=";
}
if ($_[1] eq "lt" || $_[1] eq "<") {
$symbol = "<";
}
if ($_[1] eq "gt" || $_[1] eq ">") {
$symbol = ">";
}
if ($_[1] eq "empty") {
$symbol = "";
if ($_[2] =~ /e/) {
$symbol = "?";
}
}
if ($_[2] =~ /c/) {
if ($_[1] eq "sub" || $_[1] eq "div") {
$symbol = qq~<span class="lightred">$symbol</span>~;
} else {
$symbol = qq~<span class="lightgreen">$symbol</span>~;
}
}
return $symbol;
}
sub range_check {
my (
$self,
$max_from,
$max_to,
$from,
$to,
$negative,
$numsys,
) = @_;
#: Check if number matches predefined range.
if ($from > $max_from) {
$from = $max_from;
}
if ($to > $max_to) {
$to = $max_to;
}
if ($from > $to) {
$from = $to;
}
if ($numsys eq "roman" && $from < 1) {
$from = 1;
}
if ($numsys eq "roman" && $to > 3999) {
$to = 3999;
}
if ($negative) {
$from = -$to;
}
return ($from,$to);
}
sub range_list {
my (
$self,
$i,
$vars_ref,
$split_mode,
) = @_;
#: Check number range - special list mode.
#: Split_mode is simply the total number of elements on a page
#: (6, 9, or 12), x vs y will then be calculated
my %vars = %$vars_ref;
my ($num,@nums);
foreach my $opt (split(/,/,",$vars{'app_input_range_list'},")) {
$opt =~ s~[^0-9]~~g;
if ($opt =~ /[0-9]/ && $opt < 99999) {
@nums = (@nums,$opt);
}
}
$num = $nums[$i];
if ($split_mode == 6) {
if ($i == 1) { $num = $nums[3]}
if ($i == 2) { $num = $nums[1]}
if ($i == 3) { $num = $nums[4]}
if ($i == 4) { $num = $nums[2]}
if ($i == 5) { $num = $nums[5]}
}
if ($split_mode == 9) {
if ($i == 1) { $num = $nums[3]}
if ($i == 2) { $num = $nums[6]}
if ($i == 3) { $num = $nums[1]}
if ($i == 4) { $num = $nums[4]}
if ($i == 5) { $num = $nums[7]}
if ($i == 6) { $num = $nums[2]}
if ($i == 7) { $num = $nums[5]}
if ($i == 8) { $num = $nums[8]}
}
if ($split_mode == 12) {
if ($i == 1) { $num = $nums[3]}
if ($i == 2) { $num = $nums[6]}
if ($i == 3) { $num = $nums[9]}
if ($i == 4) { $num = $nums[1]}
if ($i == 5) { $num = $nums[4]}
if ($i == 6) { $num = $nums[7]}
if ($i == 7) { $num = $nums[10]}
if ($i == 8) { $num = $nums[2]}
if ($i == 9) { $num = $nums[5]}
if ($i == 10) { $num = $nums[8]}
}
if ($num eq "") {
my $nums_num = @nums;
$num = $nums[int(rand($nums_num))];
if ($num eq "") {
$num = $nums[0];
if ($num eq "") {
$num = 10;
}
}
}
return $num;
}
sub compare_buttons {
my (
$self,
$i,
$CORR,
$SOL,
$SYM,
$stat_wrong,
$questions_per_page,
$vars_ref,
) = @_;
#: Display comparision buttons.
my %vars = %$vars_ref;
my $BIG;
if ($questions_per_page == 1) {
$BIG = " button_big";
}
my @ALTS = (
qq~<div id="but_alt_0_$i" class="hide button_red$BIG button_left ml5"><</div>~,
qq~<div id="but_alt_1_$i" class="hide button_red$BIG button_middle">=</div>~,
qq~<div id="but_alt_2_$i" class="hide button_red$BIG button_right mr5">></div>~,
);
my $MIS = "count_mistake();";
my @ALTJS = ($MIS,$MIS,$MIS);
$ALTS[$CORR] = qq~<div id="but_alt_${CORR}_$i" class="hide button_green$BIG ml10 mr10">$SOL</div>~;
$ALTJS[$CORR] = qq~onojs_hide('but_ini_0_$i');onojs_hide('but_ini_1_$i');onojs_hide('but_ini_2_$i');
onojs_hide('but_alt_0_$i');onojs_hide('but_alt_1_$i');onojs_hide('but_alt_2_$i');
onojs_inline('but_alt_${CORR}_$i');count_correct();update_answered('$i','2','0');
~;
if ($questions_per_page == 1) {
$ALTJS[$CORR] .= qq~onojs_submit_delayed(2);onojs_setsrc('loading_img','/ono/osr/images/loading/wheel_32.gif');onojs_class('loading_img','block32');onojs_hide('check_custom_button');~;
}
my @BUTS = (
qq~<div id="but_ini_0_$i" class="button$BIG button_left ml5" onclick="onojs_hide('but_ini_0_$i');onojs_inline('but_alt_0_$i');update_answered('$i','1','1');$ALTJS[0]"><</div>$ALTS[0]~,
qq~<div id="but_ini_1_$i" class="button$BIG button_middle" onclick="onojs_hide('but_ini_1_$i');onojs_inline('but_alt_1_$i');update_answered('$i','1','2');$ALTJS[1]">=</div>$ALTS[1]~,
qq~<div id="but_ini_2_$i" class="button$BIG button_right mr5" onclick="onojs_hide('but_ini_2_$i');onojs_inline('but_alt_2_$i');update_answered('$i','1','3');$ALTJS[2]">></div>$ALTS[2]~,
);
if ($vars{"answered_$i"} == 1) {
$stat_wrong++;
for (my $b = 0; $b < 3; $b++) {
my $b2 = $b+1;
if ($vars{"answered_${i}_err_$b2"}) {
my @DAT = ('middle',"=");
if ($b2 == 1) {
@DAT = ('left ml5',"<");
}
if ($b2 == 3) {
@DAT = ('right mr5',">");
}
$BUTS[$b] = qq~<div id="but_ini_${b}_$i" class="button_red button_$DAT[0]">$DAT[1]</div>~;
}
}
}
if ($vars{"answered_$i"} == 2) {
@BUTS = (
qq~<div class="button_green ml10 mr10">$SYM</div>~,
);
}
return @BUTS;
}
sub compare_input {
my (
$self,
$stat_correct,
$stat_mistakes,
$stat_wrong,
$stat_todo,
) = @_;
#: Compare input field code (also good for debugging).
my $hidden = "hidden";
if (ONO::IO->devstation) {
# $hidden = "text";
}
return qq~<input type="$hidden" id="count_cor_id" name="output_web_count_cor" value="$stat_correct">
<input type="$hidden" id="count_mis_id" name="output_web_count_mis" value="$stat_mistakes">
<input type="$hidden" id="count_wro_id" name="output_web_count_wro" value="$stat_wrong">
<input type="$hidden" id="count_tod_id" name="output_web_count_tod" value="$stat_todo">
~;
}
sub compare_script {
#: Compare numbers script.
return qq~<script>
function count_correct() {
var correct = Math.floor(onojs_getvalue('count_cor_id')) + 1;
onojs_setvalue('count_cor_id',correct);
update_stat_viewer();
}
function count_mistake() {
var mistakes = Math.floor(onojs_getvalue('count_mis_id')) + 1;
onojs_setvalue('count_mis_id',mistakes);
update_stat_viewer();
}
function update_answered(id,num,err) {
onojs_setvalue('answered_id_'+id,num);
if (err > 0) {
onojs_setvalue('answered_id_'+id+'_err_'+err,'1');
}
update_stat_viewer();
}
function update_stat_viewer() {
onojs_sethtml('total_correct',onojs_getvalue('count_cor_id'));
var mistakes = onojs_getvalue('count_mis_id');
if (mistakes < 1) {
mistakes = 0;
}
onojs_sethtml('output_web_total_mistakes',mistakes);
var todo = 20;
var wrong = 0;
for (a = 0; a < 20; a++) {
if (Math.floor(onojs_getvalue('answered_id_'+a)) == 1) {
todo--;
wrong++;
}
if (Math.floor(onojs_getvalue('answered_id_'+a)) == 2) {
todo--;
}
}
onojs_sethtml('total_todo',todo);
onojs_sethtml('output_web_total_wrong',wrong);
}
</script>
~;
}
###################################################################
# THAT'S IT :-D
###################################################################
1;
__END__