package ONO::FW::Edu::Apps::Modules::MathBoxes;
################################################################################
# COPYRIGHT / LICENSE #
################################################################################
#
# This file is part of the ONO Software Project.
#
# Copyright (C) 2022-2026 Jos Kirps and The Joopita Project [ASBL]
# Copyright (C) 2011-2022 Jos Kirps and Joopita Research ASBL
# Copyright (C) 2006-2011 Jos Kirps and The Joopita Project
# Copyright (C) 2001-2006 Jos Kirps and The WobbleWolf Project
#
# This file, as well as many 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 ... #
################################################################################
#: This module is a front end application module wrapper that has been
#: created for the Morzino Project (morzino.com) and that has been released
#: as Open Source software.
#:
#: Note that this is NOT a self-contained application, it is only a wrapper
#: that makes heavy use of the ONO Perl Framework application engine.
use strict;
use ONO::IO;
use ONO::FW::Edu::Apps::ToolBox;
use ONO::FW::Edu::Apps::Modules::WorksheetMaker::Input;
use ONO::Lib::PDF::Draw;
use ONO::Lib::PDF::ToolBox;
use ONO::FW::Apps::Core;
use ONO::FW::Apps::Core::Graphics;
use ONO::FW::Apps::Core::Math;
use ONO::Lib::Parser;
use ONO::FW::Apps::PDF;
use ONO::FW::Apps::ToolBox;
use ONO::Lib::Data::Numbers::Roman;
use ONO::Lib::Data::Numbers::Hexa;
use ONO::Lib::Data::Numbers::Primes;
sub learn {
my (
$self,
$option,
$vars_ref,
$HTTP,
$BASE,
) = @_;
################################################################
# INIT - BEGIN
################################################################
my (
$db,
$community,
$action_url,
$form,
$format,
$canvas,
$pdfkey,
$sec,$min,$hour,
$mday,$mon,$year,
$wday,$yday,$timestamp,
$lang,
$BLK_ref,
$vars_ref,
) = ONO::FW::Apps::Core->init($vars_ref);
my %vars = %$vars_ref;
my %BLK = %$BLK_ref;
my $app = "math_boxes";
my @txt = (
"automatic",
);
if ($vars{'app_lang'} eq "de") {
@txt = (
"automatisch",
);
}
if ($vars{'app_lang'} eq "lu") {
@txt = (
"automatesch",
);
}
if ($vars{'app_lang'} eq "fr") {
@txt = (
"automatique",
);
}
ONO::FW::Apps::Core->now("",$app,$vars{'app_lang'});
my $grid_switches;
my ($num_comma,$num_delimiter) = ONO::FW::Apps::Core->local_comma_delimiter(ONO::FW::Apps::Core->detect_local($vars{'app_lang'}),$vars_ref);
if ($num_comma eq ",") {
$grid_switches .= "C";
}
my $WEB;
$WEB .= ONO::FW::Apps::Core->output_paper_save($pdfkey,$BLK_ref,\%vars,$HTTP,$BASE,"$year$mon$mday");
if (ONO::FW::Apps::Core->running(\%vars,$db)) {
$WEB .= ONO::FW::Apps::Core->blockbackbutton($BASE,$vars_ref);
#############################################################
# WORKSHEET MAKER
#############################################################
if ($vars{'output_worksheet_maker'}) {
$vars_ref = ONO::FW::Edu::Apps::Modules::WorksheetMaker::Input->vars(\%vars);
%vars = %$vars_ref;
}
#############################################################
# ONLINE TRAINING
#############################################################
if ($vars{'output_web'} || $vars{'output_paper'}) {
my $question_number = ONO::FW::Apps::Core->question_number($vars{'output_web_question_number'});
my ($focus_on,$page,$pages,%cache,$whilecounter,$stats_switches,$PDF_OPTS);
my ($stat_correct,$stat_correct_current,$stat_correct_total,$stat_wrong,$stat_todo,$stat_todo_total);
if ($vars{'output_web_next_box'}) {
$question_number++;
foreach my $key (keys %vars) {
if ($key =~ /^(app_data_box|output_web_answer)/) {
$vars{$key} = "";
}
}
$vars_ref = \%vars;
}
if ($vars{'stat_correct_total_transmit'}) {
$stat_correct_total = $vars{'stat_correct_total_transmit'};
}
if ($vars{'output_web_mistakes_transmit'}) {
$vars{'output_web_mistakes'} = $vars{'output_web_mistakes_transmit'};
}
my $questions_per_page = 1;
if ($vars{'output_paper'}) {
$PDF_OPTS = ONO::FW::Apps::Core->headers_options($BLK_ref,$vars_ref);
ONO::FW::Apps::PDF->new("$BASE/pdf/pdf-$pdfkey.pdf","",$format);
$vars{'app_input_multiple_questions_per_page'}++;
# in PDF mode questions_per_page is the number of box lines per page!
$questions_per_page = 7;
}
foreach my $copy (ONO::FW::Apps::Core->unique_copies($vars{'app_input_print_options_unique_copies'})) {
my ($pdf_x,$pdf_y) = (0,0);
my (@corr_cache,$box_counter,@nums_cache);
if ($vars{'output_paper'}) {
if ($copy > 1) {
ONO::FW::Apps::PDF->page("",$format);
}
ONO::FW::Apps::Core->headers(\%vars,$format);
ONO::FW::Apps::PDF->title($vars{'app_input_print_options_title'},$PDF_OPTS,$vars{'app_input_print_options_code'},$format);
# ONO::Lib::PDF::ToolBox->canvas_test($canvas);
}
if ($vars{'output_web'}) {
$WEB .= qq~<$form>~;
$WEB .= "###_PROGRESS_BAR_###";
if ($stat_correct_total + $vars{'stat_correct_current'} > $vars{'app_input_number_of_questions'}-1) {
$vars{'output_web_finish'}++;
}
$WEB .= ONO::FW::Apps::Core->stats("",$BLK_ref,"cTm$stats_switches");
}
if (ONO::FW::Apps::Core->question_progress_done($vars{'app_input_number_of_questions'},$question_number) || $vars{'output_web_finish'}) {
$vars{'output_progress_done'}++;
$WEB .= ONO::FW::Apps::Core->question_progress_done_msg($app,$db,$community,$form,$action_url,$stat_correct_total+$vars{'stat_correct_current'},$vars{'output_web_mistakes'},$lang,$BLK_ref,\%vars);
} else {
my $box_line_counter = 1;
for (my $i = 0; $i < $questions_per_page; $i++) {
# output paper
if ($vars{'output_paper'}) {
my $off_x;
my $max_length = (length $vars{'app_input_range_to'})+$vars{'app_input_comma_status'};
if ($vars{'app_input_numsys_status'} eq "roman") {
$max_length = 5;
}
my @box_per_line_opts = (2,3,3,3,3,3,4,4,4,4);
my $box_row_add = $vars{'app_input_range_to'}/25;
my $box_row_max = 5;
if ($vars{'app_input_range_to'} < 50) {
@box_per_line_opts = (3,4);
$box_row_add = $vars{'app_input_range_to'}/50;
$box_row_max = 4;
}
if ($vars{'app_input_range_to'} > 100) {
@box_per_line_opts = (2,3,3,4,4);
$box_row_max = 6;
}
if ($vars{'app_input_range_to'} > 250) {
@box_per_line_opts = (1,2,2,3,3,3,3,3,4,4,4);
$box_row_max = 7;
}
if ($max_length > 4) {
@box_per_line_opts = (1,2,2,3,3,3);
$box_row_max = 6;
}
if ($max_length > 5) {
@box_per_line_opts = (1,1,2);
$box_row_max = 7;
}
my $box_per_line_optnum = @box_per_line_opts;
my $box_per_line = $box_per_line_opts[int(rand($box_per_line_optnum))];
if ($pdf_y > 100 && $box_per_line == 2 && $max_length < 5) {
$box_per_line = 3;
}
my $box_cols;
my $box_rows = 3+int(rand($box_row_add));
if ($box_rows > $box_row_max) {
$box_rows = $box_row_max;
}
if ($vars{'app_input_range_to'} < 150 && $box_per_line == 2) {
$box_rows = 2+int(rand(2));
}
if ($max_length > 4 && $box_per_line == 4) {
$box_per_line = 3;
}
# special case: div
my $force_div_opt;
if ($vars{'app_input_op_div'} == 1) {
if ($box_per_line == 1 || ($vars{'app_input_range_to'} < 250 && $box_per_line == 2 && $max_length < 5)) {
$box_per_line = 3;
}
if ((int(rand(100)) > 75 || ($pdf_y > 75 && !$vars{'forced_div_opt'}))) {
$force_div_opt++;
$vars{'forced_div_opt'}++;
$box_per_line = 4;
if ($max_length > 4) {
$box_per_line = 3;
}
$box_rows = 2;
if ($vars{'app_input_range_to'} > 100 && int(rand(100)) > 33) {
$box_rows = 2+int(rand(2));
if ($vars{'app_input_range_to'} > 250 && int(rand(100)) > 33) {
$box_rows = 2+int(rand(5));
}
}
}
}
# manual box sizes
if ($vars{'app_input_select_cols'} =~ /[1-9]/) {
if ($vars{'app_input_select_cols'} =~ /r/) {
my $use_cols = $vars{'app_input_select_cols'};
$use_cols !~ s~[^0-9]~~g;
$box_cols = int(rand($use_cols-2))+3;
} else {
$box_cols = $vars{'app_input_select_cols'}+1;
}
if ($box_cols > 3 && $box_per_line > 3) {
$box_per_line = 3;
}
if ($box_cols > 5 && $box_per_line > 2) {
$box_per_line = 2;
}
if ($box_cols > 7 && $box_per_line > 1) {
$box_per_line = 1;
}
}
if ($vars{'app_input_select_rows'} =~ /[1-9]/) {
if ($vars{'app_input_select_rows'} =~ /r/) {
my $use_rows = $vars{'app_input_select_rows'};
$use_rows !~ s~[^0-9]~~g;
$box_rows = int(rand($use_rows-2))+3;
} else {
$box_rows = $vars{'app_input_select_rows'}+1;
}
}
# set the box width
my $box_width;
if ($box_per_line == 4) {
$box_width = 35-1.25;
$box_cols = 4-int($max_length)+2;
if ($box_cols < 3) {
$box_cols = 3;
}
}
if ($box_per_line == 3) {
$box_width = 50-1.67;
if ($vars{'output_worksheet_maker'}) {
$box_width = 45;
}
$box_cols = 5-int($max_length)+2;
}
if ($box_per_line == 2) {
$box_width = 80-2.5;
$box_cols = 8-int($max_length)+2;
}
if ($box_per_line == 1) {
$box_width = 170-5;
$box_cols = 12-int($max_length)+2;
}
# minimum box cols
if ($box_cols < 2) {
$box_cols = 2;
}
# if last box is too big to fit on page, make less rows
if ($pdf_y + $box_rows*9 > 190) {
my $whilecounter;
while ($whilecounter < 255 && $pdf_y + $box_rows*9 > 190) {
$box_rows--;
$whilecounter++;
}
}
# if last box is not big enough, then add rows (only in auto mode)
if ($pdf_y + $box_rows*9 > 140 && $pdf_y + $box_rows*9 < 185 && $vars{'app_input_select_rows'} =~ /auto/) {
my $whilecounter;
while ($whilecounter < 255 && $pdf_y + $box_rows*9 < 185) {
$box_rows++;
$whilecounter++;
}
}
# max sheet height
my $max_y = 180;
# worksheet maker options
if ($vars{'output_worksheet_maker'}) {
$max_y = 50;
if ($box_rows > 4) {
$box_rows = 4;
}
}
# process...
if ($box_width > 1 && $box_rows > 1 && $pdf_y < $max_y) {
ONO::FW::Apps::Core::Graphics->pdf_draw_item_number($canvas,-2,$pdf_y,$box_line_counter,"W",$vars_ref);
for (my $b = 0; $b < $box_per_line; $b++) {
my $op = ONO::FW::Apps::Core->random_operation(ONO::FW::Apps::Core->get_operations($vars_ref));
my $whilecounter;
while ($whilecounter < 16 && $op eq $vars{'last_op_used'}) {
$whilecounter++;
$op = ONO::FW::Apps::Core->random_operation(ONO::FW::Apps::Core->get_operations($vars_ref));
}
$vars{'last_op_used'} = $op;
# special case: force and / or prevent bad div conditions
if ($force_div_opt && $box_rows < 4 && int(rand(100)) > 50) {
$op = "div";
}
my $whilecounter;
if ($op eq "div" && (
$box_cols > int($vars{'app_input_range_to'}/20) ||
$box_rows > int($vars{'app_input_range_to'}/25) ||
$box_rows > 4 ||
($vars{'app_input_op_muldiv_bigger_10'} < 1 && $box_rows > 2)
)) {
while ($whilecounter < 255 && $op eq "div") {
$whilecounter++;
$op = ONO::FW::Apps::Core->random_operation(ONO::FW::Apps::Core->get_operations($vars_ref));
}
}
# $op selection done
if ($vars{'output_worksheet_maker'}) {
$pdf_y = 5;
if ($op eq "add" || $op eq "sub") {
$box_rows = 4;
}
}
my $box_rows_use = $box_rows;
if ($op eq "div") {
if ($vars{'app_input_range_to'} < 150 && $box_rows > 2) {
$box_rows_use = 2;
}
if ($vars{'app_input_range_to'} < 250 && $box_rows > 3) {
$box_rows_use = 3;
}
}
# render the page
my $COLOR = &color("",$op);
my @nums = &number_generator("",$op,$box_cols,$box_rows_use,\%vars);
my $font = 12;
if ($max_length > 3) {
$font = 11;
if ($max_length > 4) {
$font = 10;
if ($max_length > 5) {
$font = 9;
}
}
}
ONO::Lib::PDF::Draw->font('',$font);
my $grid_x1 = $pdf_x+5+$off_x;
my $grid_y1 = $pdf_y;
my $grid_x2 = $pdf_x+5+$off_x+$box_width;
my $grid_y2 = $pdf_y+$box_rows_use*9;
$corr_cache[$box_counter] = "$grid_x1\^$grid_y1\^$grid_x2\^$grid_y2\^$box_cols/$box_rows_use\^$COLOR";
@nums_cache = (@nums_cache,\@nums);
$box_counter++;
ONO::Lib::PDF::Draw->grid($canvas,$grid_x1,$grid_y1,$grid_x2,$grid_y2,"$box_cols/$box_rows_use",0.1,1,0,$COLOR,"MX$grid_switches",\@nums);
$off_x = $off_x + $box_width + 10;
# special code for worksheet maker, where width is 45 when 3 boxes in 1 row:
if ($box_width == 45) {
$off_x = $off_x + 5;
}
}
}
$pdf_y = $pdf_y + $box_rows*9 + 9;
$box_line_counter++;
}
}
if ($vars{'output_web'}) {
$WEB .= ONO::FW::Apps::Core->input_vars(\%vars);
my $op = ONO::FW::Apps::Core->random_operation(ONO::FW::Apps::Core->get_operations($vars_ref));
my $box_cols = $vars{'app_input_select_cols'}+1;
my $box_rows = $vars{'app_input_select_rows'}+1;
# automatic settings
if ($vars{'app_input_select_cols'} eq "auto") {
$box_cols = 3;
if ($vars{'app_input_range_to'} > 50) {
$box_cols = 4;
}
if ($vars{'app_input_range_to'} > 75) {
$box_cols = 5;
}
if ($vars{'app_input_range_to'} > 100) {
$box_cols = 6;
}
}
if ($vars{'app_input_select_rows'} eq "auto") {
$box_rows = 3;
if ($vars{'app_input_range_to'} > 50) {
$box_rows = 4;
}
if ($vars{'app_input_range_to'} > 100) {
$box_rows = 5;
}
}
if ($vars{'app_input_select_cols'} =~ /r/) {
my $use_cols = $vars{'app_input_select_cols'};
$use_cols !~ s~[^0-9]~~g;
$box_cols = int(rand($use_cols-2))+3;
}
if ($vars{'app_input_select_rows'} =~ /r/) {
my $use_rows = $vars{'app_input_select_rows'};
$use_rows !~ s~[^0-9]~~g;
if ($use_rows > 6) {
$use_rows = 6;
}
$box_rows = int(rand($use_rows-2))+3;
}
if ($op eq "div") {
if ($box_cols > 4) {
$box_cols = 4;
}
if ($box_rows > 2) {
$box_rows = 2;
}
}
# limits
if ($box_cols > 11) {
$box_cols = 11;
}
if ($box_rows > 6) {
$box_rows = 6;
}
# retrieve or generate data
my @nums;
if ($vars{'app_data_box_op'} && $vars{'app_data_box_cols'} && $vars{'app_data_box_rows'}) {
$op = $vars{'app_data_box_op'};
$box_cols = $vars{'app_data_box_cols'};
$box_rows = $vars{'app_data_box_rows'};
for (my $n = 0; $n < ($box_cols * $box_rows); $n++) {
$nums[$n] = $vars{"app_data_box_value_$n"};
}
} else {
@nums = &number_generator("",$op,$box_cols,$box_rows,\%vars);
}
# colors
my $COLOR = &color("",$op);
my @COL = split(/\//,$COLOR);
@COL = (ONO::Lib::PDF::Draw->color_to_html($COL[0]),ONO::Lib::PDF::Draw->color_to_html($COL[1]),ONO::Lib::PDF::Draw->color_to_html($COL[2]));
$WEB .= qq~<input type="hidden" name="app_data_box_op" value="$op">
<input type="hidden" name="app_data_box_cols" value="$box_cols">
<input type="hidden" name="app_data_box_rows" value="$box_rows">
~;
for (my $n = 0; $n < ($box_cols * $box_rows); $n++) {
$WEB .= qq~<input type="hidden" name="app_data_box_value_$n" value="$nums[$n]">~;
}
my ($TABLE,$box_counter);
my $question_counter = 0;
my $table_cell_width = int(100/$box_cols);
for (my $r = 0; $r < $box_rows; $r++) {
my $STYLE;
if ($r == 0) {
$STYLE = "background-color:$COL[1]";
}
$TABLE .= qq~ <tr class="bt bb" style="$STYLE;height:50px">~;
for (my $c = 0; $c < $box_cols; $c++) {
my $NUM = $nums[$box_counter];
if ($num_comma ne "" && $num_comma ne ".") {
$NUM =~ s~\.~\,~g;
}
my $CELL = $NUM;
my $BG;
if ($r && $c) {
$vars{"output_web_answer_$question_counter"} =~ s~\,~\.~g;
$vars{"output_web_answer_$question_counter"} = uc $vars{"output_web_answer_$question_counter"};
$vars{"output_web_answer_$question_counter"} =~ s~[^0-9\.\I\V\X\L\C\D\M\-]~~gi;
my $PLA;
if (ONO::IO->devstation) {
$PLA = qq~ placeholder="$nums[$box_counter]"~;
}
$CELL = qq~<input type="text" name="output_web_answer_$question_counter" value="$vars{"output_web_answer_$question_counter"}"$PLA class="w100 center">~;
if ($vars{"output_web_answer_$question_counter"} ne "") {
if ($vars{"output_web_answer_$question_counter"} eq $nums[$box_counter]) {
$stat_correct++;
$BG = " bg_green";
$CELL = qq~<div class="bold green">$NUM</div>
<input type="hidden" name="output_web_answer_$question_counter" value="$vars{"output_web_answer_$question_counter"}">
~;
} else {
$stat_wrong++;
$vars{'output_web_mistakes'}++;
$BG = " bg_red";
}
}
$question_counter++;
$stat_correct_current = $stat_correct;
}
my $STYLE;
if ($r && $c == 0) {
$STYLE = "background-color:$COL[2]";
}
$TABLE .= qq~ <td class="p10 bl br center$BG" style="width:$table_cell_width%;$STYLE">$CELL</td>~;
$box_counter++;
}
$TABLE .= qq~ </tr>~;
}
$WEB .= qq~<div class="p5">
<div class="inline auto">
<div class="p2 mt5 mb5" style="background-color:$COL[0];min-width:640px">
<table class="wide_table bg_paper">
$TABLE
</table>
</div>
</div>
</div>
<script>onojs_focus_first_text();</script>
~;
if ($stat_correct > $question_counter-1) {
my $stat_correct_total_calc = $stat_correct_total + $stat_correct;
$WEB .= qq~<div class="inline auto"><input type="submit" name="stats_submit" value="$BLK{'continue'}..." class="button_green"></div>
<input type="hidden" name="output_web_next_box" value="1">
<input type="hidden" name="stat_correct_total_transmit" value="$stat_correct_total_calc">
<script>
onojs_submit_onkey();
</script>
~;
if ($question_counter > $vars{'app_input_number_of_questions'}-1) {
$WEB .= qq~<input type="hidden" name="output_web_finish" value="1">~;
}
} else {
$WEB .= qq~<div class="inline auto"><input type="submit" name="stats_submit" value="$BLK{'do_correct'}" class="button_green"></div>
<input type="hidden" name="stat_correct_total_transmit" value="$stat_correct_total">
<input type="hidden" name="stat_correct_current" value="$stat_correct_current">
~;
}
$WEB .= qq~<input type="hidden" name="output_web_mistakes_transmit" value="$vars{'output_web_mistakes'}">~;
$WEB .= qq~</form>~;
}
}
if (ONO::FW::Apps::Core->print_corrections("$BASE/pdf/pdf-$pdfkey-corr.pdf","",$format,$vars_ref)) {
ONO::FW::Apps::Core->headers(\%vars,$format,"c");
ONO::FW::Apps::PDF->title($BLK{'Corrections'},$PDF_OPTS,$vars{'app_input_print_options_code'},$format);
# ONO::Lib::PDF::ToolBox->canvas_test($canvas);
my ($box_line_counter,$last_y);
for (my $c = 0; $c < $box_counter; $c++) {
my ($grid_x1,$grid_y1,$grid_x2,$grid_y2,$dim,$color) = split(/\^/,$corr_cache[$c]);
my $nums_ref = $nums_cache[$c];
my @nums = @$nums_ref;
$color = "0:250:250:250/0:222:222:222/0:233:233:233";
ONO::Lib::PDF::Draw->grid($canvas,$grid_x1,$grid_y1,$grid_x2,$grid_y2,$dim,0.1,1,0,$color,"M$grid_switches",\@nums);
if ($grid_y1 != $last_y || $last_y eq "") {
$last_y = $grid_y1;
$box_line_counter++;
ONO::FW::Apps::Core::Graphics->pdf_draw_item_number($canvas,-2,$grid_y1,$box_line_counter,"CW",$vars_ref);
}
}
}
}
if ($vars{'output_web'}) {
my $PROG = ONO::FW::Apps::Core->question_progress($vars{'app_input_number_of_questions'},$stat_correct_total + $stat_correct_current);
$WEB =~ s~###_PROGRESS_BAR_###~$PROG~;
#($stat_correct,$stat_todo_total) = ONO::FW::Apps::Core->stats_multi_pages($vars{'app_input_number_of_questions'},$stat_correct,$stat_wrong,$page,$pages);
$WEB = ONO::FW::Apps::Core->stats_replace($WEB,$stat_correct_total+$stat_correct,$stat_wrong,$stat_todo_total,$vars{'output_web_mistakes'},$page,$pages,$vars{'output_web_completed'});
}
if ($vars{'output_paper'}) {
ONO::FW::Apps::PDF->end;
$WEB .= ONO::FW::Apps::PDF->please_wait($pdfkey,\%BLK,\%vars,"",$HTTP,$BASE);
}
}
} else {
#############################################################
# EXERCISE SETUP
#############################################################
# $vars{'app_input_number_of_questions_alt_label'} = $BLK{'pages'};
if (!$vars{'app_input_number_of_questions'}) {
$vars{'app_input_number_of_questions'} = 100;
}
$WEB .= ONO::FW::Apps::Core->infopics(
"screen:320:50:50::math_boxes_screenshot_1.jpg",
"sheet:168:180:140:15:math_boxes_sheet_1.jpg",
"sheet:168:40:160:-10:math_boxes_sheet_2.jpg",
);
my $COL = "auto=$txt[0] ($BLK{'recommended'});-;";
my $ROW = "auto=$txt[0] ($BLK{'recommended'});-;";
for (my $i = 1; $i < 11; $i++) {
if ($i > 1) {
$COL .= "$i=$BLK{'always'} $i ($BLK{'if_possible'});";
}
if ($i < 6) {
$ROW .= "$i=$BLK{'always'} $i ($BLK{'if_possible'});";
}
}
$COL .= qq~-;~;
$ROW .= qq~-;~;
for (my $i = 3; $i < 11; $i++) {
$COL .= "r$i=$BLK{'random'}, $BLK{'up_to'} $i;";
$ROW .= "r$i=$BLK{'random'}, $BLK{'up_to'} $i;";
}
my $CONF .= ONO::FW::Apps::ToolBox->conf_input("range", $BLK_ref,\%vars,"",1,100,9999999);
$CONF .= ONO::FW::Apps::ToolBox->conf_input("spacer", $BLK_ref,\%vars);
$CONF .= ONO::FW::Apps::ToolBox->conf_input("ops", $BLK_ref,\%vars,"","asmd-AS-t-P");
$CONF .= ONO::FW::Apps::ToolBox->conf_input("spacer", $BLK_ref,\%vars,"","s");
$CONF .= ONO::FW::Apps::ToolBox->conf_input("moreopts", $BLK_ref,\%vars,"","bm");
$CONF .= ONO::FW::Apps::ToolBox->conf_input("numsys", $BLK_ref,\%vars,"h");
$CONF .= ONO::FW::Apps::ToolBox->conf_input("comma", $BLK_ref,\%vars,"h",3);
$CONF .= ONO::FW::Apps::ToolBox->conf_input("select", $BLK_ref,\%vars,"",$COL,$BLK{'columns'},$vars{'app_input_select_cols'},"","cols");
$CONF .= ONO::FW::Apps::ToolBox->conf_input("select", $BLK_ref,\%vars,"",$ROW,$BLK{'rows'},$vars{'app_input_select_rows'},"","rows");
$WEB .= ONO::FW::Apps::Core->conf_box(
$db,
$community,
$app,
$form,
"",
$action_url,
$CONF,
$lang,
$BLK_ref,
\%vars,
"op-ncdi-CDI-lsu-N",
);
if (!$vars{'exercise'}) {
$WEB .= ONO::FW::Apps::Core->info_display(
ONO::FW::Apps::Core->info("info_online_print",$vars{'app_lang'}),
ONO::FW::Apps::Core->info("info_indiv_diff", $vars{'app_lang'}),
);
}
}
my ($status,$WEB) = ONO::Lib::Parser->parse($WEB,$vars_ref);
$WEB .= ONO::FW::Apps::Core->vardump($vars_ref);
$WEB = ONO::FW::Edu::Apps::ToolBox->apps_gate($WEB,$vars_ref);
return $WEB;
}
###################################################################
# SUBS
###################################################################
sub number_generator {
my (
$self,
$op,
$cols,
$rows,
$vars_ref,
) = @_;
my %vars = %$vars_ref;
my (@nums,%used,$whilecounter,$smallest_row_num,$smallest_div_num);
$nums[0] = "+";
# generate big numbers
my $max = int($vars{'app_input_range_to'}*0.75)+int(rand($vars{'app_input_range_to'}*0.25));
my $big_y = int($max/2 + int(rand($max/2)));
my $big_x = $max-$big_y;
if ($op eq "add" && $big_x > $big_y) {
$big_x = $big_y;
$big_y = $max-$big_x
}
# sub
if ($op eq "sub") {
$nums[0] = "-";
$big_y = $max;
}
# num
if ($op eq "mul") {
$nums[0] = "x";
$max = int(sqrt($vars{'app_input_range_to'}));
$big_x = $max;
$big_y = $max;
if ($big_x > 10 && int(rand(100)) > 50) {
$big_x = int($big_x/1.5);
}
if ($big_x > 10 && int(rand(100)) > 50) {
$big_y = int($big_y/1.5);
}
if ($vars{'app_input_op_muldiv_bigger_10'} < 1) {
if ($big_x > 10) {
$big_x = 10;
}
if ($big_y > 10) {
$big_y = 10;
}
}
}
# div - this is a special case, as we'll need prime numbers in the X axis...
my @prim_alt = (1,2,3,4,6,8,9,12,15,18,20,21,24);
if ($op eq "div") {
$nums[0] = "/";
my @primes = ONO::Lib::Data::Numbers::Primes->get_below_100();
while ($whilecounter < 1024 && (!$smallest_div_num || $smallest_div_num > $vars{'app_input_range_to'})) {
$smallest_div_num = 1;
$whilecounter++;
my $num = $primes[int(rand(sqrt($vars{'app_input_range_to'})/2))];
if ($vars{'app_input_op_muldiv_bigger_10'} < 1) {
$num = $primes[int(rand(7))];
}
for (my $c = 1; $c < $cols; $c++) {
while ($whilecounter < 255 && $used{"p:$num"}) {
$num = $primes[int(rand(sqrt($vars{'app_input_range_to'})/2))];
if ($vars{'app_input_op_muldiv_bigger_10'} < 1) {
$num = $primes[int(rand(7))];
}
if (int(rand(100)) > 50) {
my $test = $primes[int(rand(sqrt($vars{'app_input_range_to'})/3))]*$primes[int(rand(sqrt($vars{'app_input_range_to'})/3))];
if ($vars{'app_input_op_muldiv_bigger_10'} < 1) {
$test = $primes[int(rand(7))];
}
if ($test < sqrt($vars{'app_input_range_to'})) {
$num = $test;
} else {
my $test2 = $prim_alt[int(rand(sqrt($vars{'app_input_range_to'})))];
if (!$used{"p:$test2"}) {
$num = $test2;
} else {
$num = 1;
}
}
}
$whilecounter++;
}
$nums[$c] = $num;
$used{"p:$num"}++;
$smallest_div_num = $smallest_div_num * $num;
}
}
}
# comma
if ($vars{'app_input_comma_status'} =~ /[1-9]/) {
if ($op eq "add" || $op eq "sub") {
$big_x = &add_comma("",$big_x,$vars{'app_input_comma_status'});
$big_y = &add_comma("",$big_y,$vars{'app_input_comma_status'});
if ($big_x > $big_y || ($big_x + $big_y) > $vars{'app_input_range_to'}) {
$big_x--;
}
}
if ($op eq "mul" && $big_x && $big_y) {
$big_x = &add_comma("",$big_x-1,$vars{'app_input_comma_status'});
$big_y = &add_comma("",$big_y-1,$vars{'app_input_comma_status'});
}
if ($op eq "div") {
if ($vars{'app_input_comma_status'} == 1) {
$smallest_div_num = ($smallest_div_num*$prim_alt[(1+int(rand(6)))])/10;
}
if ($vars{'app_input_comma_status'} == 2) {
my @alt = (12,15,16,18,20, 21,24,25,27,30, 32,35,36,40,45, 50,55,60,65,70,75, 80);
$smallest_div_num = ($smallest_div_num*$alt[int(rand(20))])/100;
}
if ($vars{'app_input_comma_status'} == 3) {
my @alt = (125,150,175,225,250, 275,325,350,375,425, 525);
$smallest_div_num = ($smallest_div_num*$alt[int(rand(10))])/1000;
}
my $whilecounter;
while ($whilecounter < 255 && $smallest_div_num < $vars{'app_input_range_to'}/8) {
$whilecounter++;
my $test = $smallest_div_num*$prim_alt[int(rand(3))];
if ($test < $vars{'app_input_range_to'}) {
$smallest_div_num = $test;
}
}
}
}
# position the big numbers
if ($op eq "div") {
my $pos_y = 1+int(rand($rows-1));
$nums[$pos_y*$cols] = $smallest_div_num;
} else {
my $pos_x = 1+int(rand($cols-1));
my $pos_y = 1+int(rand($rows-1));
$nums[$pos_x] = $big_x;
$nums[$pos_y*$cols] = $big_y;
$used{"c:$big_x"}++;
$used{"r:$big_y"}++;
}
# fill in missing X / Y spots
for (my $r = 1; $r < $rows; $r++) {
if ($nums[$r*$cols] eq "") {
my $num;
if ($op eq "add" || $op eq "mul") {
$num = 1+int(rand($big_y));
while ($used{"r:$num"} && $whilecounter < 255) {
$num = 1+int(rand($big_y));
$whilecounter++;
}
}
if ($op eq "sub") {
$num = int($big_y/2+1+int(rand($big_y/2)));
while ($used{"r:$num"} && $whilecounter < 255) {
$num = int($big_y/2+1+int(rand($big_y/2)));
$whilecounter++;
}
}
$num = &add_comma("",$num,$vars{'app_input_comma_status'});
if ($op eq "div") {
$num = $smallest_div_num*(1+int(rand($rows+1)));
while ($used{"r:$num"} && $whilecounter < 255) {
$num = $smallest_div_num*(1+int(rand($rows+1)));
$whilecounter++;
}
if ($num > $vars{'app_input_range_to'}) {
$num = 0;
}
}
if ($op ne "div" && $used{"c:$num"}) {
$num++;
}
$nums[$r*$cols] = ONO::FW::Apps::Core::Math->fix_fp_rounding($num);
$used{"r:$num"}++;
if ($smallest_row_num eq "" || $num < $smallest_row_num) {
$smallest_row_num = $num;
}
}
}
for (my $c = 1; $c < $cols; $c++) {
if ($nums[$c] eq "") {
my $num;
if ($op eq "add" || $op eq "mul") {
$num = 1+int(rand($big_x));
while ($used{"c:$num"} && $whilecounter < 255) {
$num = 1+int(rand($big_x));
$whilecounter++;
}
}
if ($op eq "sub") {
$num = 1+int(rand($smallest_row_num));
while ($used{"c:$num"} && $whilecounter < 255) {
$num = 1+int(rand($smallest_row_num));
$whilecounter++;
}
}
$num = &add_comma("",$num,$vars{'app_input_comma_status'});
if ($op ne "div" && $used{"c:$num"}) {
$num++;
}
$nums[$c] = $num;
$used{"c:$num"}++;
}
}
# fill in all missing numbers
for (my $r = 1; $r < $rows; $r++) {
for (my $c = 1; $c < $cols; $c++) {
if ($nums[$r*$cols+$c] eq "") {
if ($op eq "add") {
$nums[$r*$cols+$c] = $nums[$r*$cols] + $nums[$c];
}
if ($op eq "sub") {
$nums[$r*$cols+$c] = $nums[$r*$cols] - $nums[$c];
}
if ($op eq "mul") {
$nums[$r*$cols+$c] = $nums[$r*$cols] * $nums[$c];
}
if ($op eq "div") {
$nums[$r*$cols+$c] = $nums[$r*$cols] / $nums[$c];
}
}
}
}
# roman number and hexa conversion
if ($vars{'app_input_numsys_status'} eq "roman" || $vars{'app_input_numsys_status'} eq "hex") {
my $nummax = @nums;
for (my $n = 1; $n < $nummax+1; $n++) {
if ($vars{'app_input_numsys_status'} eq "roman") {
$nums[$n] = ONO::Lib::Data::Numbers::Roman->get($nums[$n],"z");
}
if ($vars{'app_input_numsys_status'} eq "hex") {
$nums[$n] = ONO::Lib::Data::Numbers::Hexa->get($nums[$n]);
}
}
}
return @nums;
}
sub add_comma {
if ($_[2] =~ /[1-9]/) {
my $comma;
if (int(rand(100)) > 10) {
$comma = int(rand(10));
if ($_[2] > 1 && int(rand(100)) > 20) {
$comma = int(rand(100));
if ($_[2] > 2 && int(rand(100)) > 30) {
$comma = int(rand(1000));
}
}
}
if ($comma) {
return $_[1].".$comma";
} else {
return $_[1];
}
} else {
return $_[1];
}
}
sub color {
my $COLOR = "0:250:250:250/0:200:244:200/0:230:244:230";
if ($_[1] eq "sub") {
$COLOR = "0:250:250:250/0:244:200:200/0:244:230:230";
}
if ($_[1] eq "mul") {
$COLOR = "0:250:250:250/0:178:211:244/0:222:233:244";
}
if ($_[1] eq "div") {
$COLOR = "0:250:250:250/0:222:200:244/0:235:230:244";
}
return $COLOR;
}
###################################################################
#### THAT'S IT :-D
###################################################################
1;
__END__