ONO::FW::Edu::Apps::Modules::Arithmetic

package ONO::FW::Edu::Apps::Modules::Arithmetic;
################################################################################
# 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::FW::Edu::Apps::ToolBox;
use ONO::FW::Edu::Apps::Modules::WorksheetMaker::Input;

use ONO::FW::Apps::ToolBox;

use ONO::Lib::PDF::Gen;
use ONO::Lib::PDF::Draw;
use ONO::Lib::PDF::ToolBox;

use ONO::FW::Apps::Core;
use ONO::FW::Apps::Core::Graphics;

use ONO::Lib::Parser;
use ONO::FW::Apps::PDF;
use ONO::IO;
use ONO::Lib::UI::Check;
use ONO::Lib::UI::Select;

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 = "arithmetic";

ONO::FW::Apps::Core->now("",$app,$vars{'app_lang'});

my @txt = (
"use parentheses",
);

if ($vars{'app_lang'} eq "de") {
@txt = (
"Klammern nutzen",
);
}

if ($vars{'app_lang'} eq "lu") {
@txt = (
"Klammere notzen",
);
}

if ($vars{'app_lang'} eq "fr") {
@txt = (
"utiliser parenthèses",
);
}

my ($num_comma,$num_delimiter) = ONO::FW::Apps::Core->local_comma_delimiter(ONO::FW::Apps::Core->detect_local($vars{'app_lang'}),$vars_ref);

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);

(
$vars{'app_input_range_from'},
$vars{'app_input_range_to'},

) = ONO::FW::Apps::Core->range_check(
8999999,
9999999,
$vars{'app_input_range_from'},
$vars{'app_input_range_to'},
$vars{'app_input_negative'},
$vars{'app_input_numsys_status'},
);

#############################################################
# 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 (
$op_symbol_opts,$focus_on,
$page,$pages,%cache,
$whilecounter,$stats_switches,
$PDF_OPTS,
$qfield_single,$qpara_single,
$stat_correct,$stat_wrong,$stat_todo,$stat_todo_total,
$paper_cards_x,$paper_cards_y,$pos_cards_x,$pos_cards_y,
);

my $questions_per_page = 1;

if ($vars{'output_paper'}) {

$PDF_OPTS = ONO::FW::Apps::Core->headers_options($BLK_ref,$vars_ref);

if ($option eq "cards") {
$format = ONO::Lib::PDF::Gen->open("$BASE/pdf/pdf-$pdfkey.pdf",0,0,0,$vars{'app_input_paper_format_status'},"L");
$canvas = ONO::Lib::PDF::ToolBox->canvas_init_max($format,"L");
($paper_cards_x,$paper_cards_y) = ONO::Lib::PDF::ToolBox->papersize_mm($format,"LR");
&cards_grid("",$canvas,$paper_cards_x/3,$paper_cards_y/3);
$pos_cards_x = $paper_cards_x/6;
$pos_cards_y = $paper_cards_y/6;
} else {
ONO::FW::Apps::PDF->new("$BASE/pdf/pdf-$pdfkey.pdf","",$format);
}

$vars{'app_input_multiple_questions_per_page'}++;
$questions_per_page = 60;
if ($vars{'app_input_extype_status'} eq "tri") {
$questions_per_page = 40;
}
if ($option eq "cards") {
$questions_per_page = 9;
}
$op_symbol_opts = "p";

}

foreach my $copy (ONO::FW::Apps::Core->unique_copies($vars{'app_input_print_options_unique_copies'})) {

my (@op,@op_symbol,@op2,@op_symbol2,@num1,@num2,@num3,@res,@hres);

my ($pdf_x,$pdf_y) = (5,8);
my @corr_cache;

if ($vars{'output_paper'}) {

if ($option eq "cards") {
if ($copy > 1) {
ONO::Lib::PDF::Gen->page($format,"L");
}
} else {
if ($copy > 1) {
ONO::FW::Apps::PDF->page("",$format,$canvas,$vars_ref);
}
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::FW::Apps::Core->unique_copies_info($canvas,$copy,$vars_ref);
ONO::FW::Apps::Core::Graphics->pdf_draw_item_numbers($canvas,-2,0,1,4,"HV",$vars_ref);
}

# ONO::Lib::PDF::ToolBox->canvas_test($canvas);

}

if ($vars{'output_web'}) {

$WEB .= qq~<$form>~;

$op_symbol_opts = "c";

if ($vars{'app_input_multiple_questions_per_page'}) {

($stats_switches,$questions_per_page,$page,$pages) = ONO::FW::Apps::Core->multi_page_logic($page,\%vars);

$WEB .= ONO::FW::Apps::Core->stats("",$BLK_ref,"ctm$stats_switches");

$WEB .= ONO::FW::Apps::Core->multi_frame_start($vars{'output_web_finish'});

} else {

$WEB .= ONO::FW::Apps::Core->question_progress($vars{'app_input_number_of_questions'},$question_number);

}

}

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,$vars{'app_input_number_of_questions'},$vars{'output_web_mistakes'},$lang,$BLK_ref,$vars_ref);

} else {

$WEB .= ONO::FW::Apps::Core->timer($vars{'output_web'},$vars{'output_web_timer_start'});

for (my $i = 0; $i < $questions_per_page; $i++) {

if ($vars{'output_web'}) {

$stat_todo++;

$vars{"output_web_answer_$i"} = ONO::FW::Apps::Core->clean_answer_math($vars{"output_web_answer_$i"},"r");

if ($vars{'app_input_multiple_questions_per_page'}) {

if ($vars{'output_web_next_page'}) {

$vars{"output_web_answer_$i"} = "";

} else {

$op[$i] = $vars{"output_web_cache_op_$i"};
$op2[$i] = $vars{"output_web_cache_op2_$i"};
$num1[$i] = $vars{"output_web_cache_num1_$i"};
$num2[$i] = $vars{"output_web_cache_num2_$i"};
$num3[$i] = $vars{"output_web_cache_num3_$i"};
$res[$i] = $vars{"output_web_cache_res_$i"};

}

} else {

my $expected_result = $vars{"output_web_cache_res_$i"};
if ($vars{"output_web_cache_qfield_$i"} == 1) {
$expected_result = $vars{"output_web_cache_num1_$i"};
if ($vars{"output_web_cache_op_$i"} eq "mul" && $vars{"output_web_cache_num2_$i"} == 0) {
$expected_result = "*";
}
}

if ($vars{"output_web_cache_qfield_$i"} == 2) {
$expected_result = $vars{"output_web_cache_num2_$i"};
if ($vars{"output_web_cache_op_$i"} eq "div" && $vars{"output_web_cache_num1_$i"} == 0) {
$expected_result = "*";
}
if ($vars{"output_web_cache_op_$i"} eq "mul" && $vars{"output_web_cache_num1_$i"} == 0) {
$expected_result = "*";
}
}

if (ONO::IO->devstation) {
$WEB .= qq~dev: $vars{"output_web_answer"} vs $expected_result~;
}

$vars{"output_web_answer"} =~ s~\,~\.~g;

if ($vars{"output_web_answer"} eq "" || $vars{"output_web_answer"} != $expected_result) {

if ($vars{"output_web_answer"} ne "" && $expected_result eq "*") {

$question_number++;
$vars{'output_web_answer_status'} = 1;
$vars{'output_web_answer'} = "";
$vars{"output_web_cache_qfield_$i"} = "";

} else {

$vars{'output_web_answer_status'} = 2;
$op[$i] = $vars{"output_web_cache_op_$i"};
$op2[$i] = $vars{"output_web_cache_op2_$i"};
$num1[$i] = $vars{"output_web_cache_num1_$i"};
$num2[$i] = $vars{"output_web_cache_num2_$i"};
$num3[$i] = $vars{"output_web_cache_num3_$i"};
$res[$i] = $vars{"output_web_cache_res_$i"};
$qfield_single = $vars{"output_web_cache_qfield_$i"};
$qpara_single = $vars{"output_web_cache_qpara_$i"};

}

} else {

$question_number++;
$vars{'output_web_answer_status'} = 1;
$vars{'output_web_answer'} = "";
$vars{"output_web_cache_qfield_$i"} = "";

}

}

}

if (!$op[$i]) {
$op[$i] = ONO::FW::Apps::Core->random_operation(ONO::FW::Apps::Core->get_operations($vars_ref));
}
$op_symbol[$i] = ONO::FW::Apps::Core->operation_symbol($op[$i],$op_symbol_opts);

$vars{'app_input_tens_barrier_long'}++;

my $DEBUG;

if (ONO::IO->devstation) {

# $WEB .= qq~$vars{"output_web_cache_res_$i"} -- $res[$i] -- $num1[$i],$num2[$i] -- $cache{'num1'},$cache{'num2'}<br>~;

}

if ($op[$i] eq "add") {

($num1[$i],$num2[$i],$res[$i],$cache{'num1'},$cache{'num2'},$DEBUG) = ONO::FW::Apps::Core->generate_add($i,$num1[$i],$num2[$i],$op[$i],$res[$i],$cache{'num1'},$cache{'num2'},\%vars,$vars{'output_web_continue'});
# $WEB .= ONO::IO->devprint($DEBUG);

}

if ($op[$i] eq "sub") {

($num1[$i],$num2[$i],$res[$i],$cache{'num1'},$cache{'num2'},$DEBUG) = ONO::FW::Apps::Core->generate_sub($i,$num1[$i],$num2[$i],$op[$i],$res[$i],$cache{'num1'},$cache{'num2'},\%vars,$vars{'output_web_continue'});
# $WEB .= ONO::IO->devprint($DEBUG);

}

if ($op[$i] eq "mul" || $op[$i] eq "div") {

($num1[$i],$num2[$i],$res[$i],$cache{'num1'},$cache{'num2'},$DEBUG) = ONO::FW::Apps::Core->generate_muldiv($i,$num1[$i],$num2[$i],$op[$i],$res[$i],$cache{'num1'},$cache{'num2'},\%vars,$vars{'output_web_continue'});
# $WEB .= ONO::IO->devprint($DEBUG);
}

($num1[$i],$num2[$i],$res[$i]) = ONO::FW::Apps::Core->numsys($num1[$i],$num2[$i],$res[$i],$vars{'app_input_numsys_status'});

if (ONO::IO->devstation) {

# $WEB .= qq~$vars{"output_web_cache_res_$i"} -- $res[$i] -- $num1[$i],$num2[$i] -- $cache{'num1'},$cache{'num2'}<br>~;

}

# $WEB .= ONO::IO->devprint("$num1[$i],$num2[$i],$res[$i]<br>");

# special mode vars

my $class = "tar";
my $width = 75;
my $qfield = 3;
my $qanswer = $res[$i];
my $qpara;

# special mode: var

if ($vars{'app_input_extype_status'} eq "var") {
if ($vars{"output_web_cache_qfield_$i"}) {
$qfield = $vars{"output_web_cache_qfield_$i"};
} else {
$qfield = 1+int(rand(3));
}
if ($qfield) {
$width = 50;
$class = "center";
if ($qfield == 1) {
$qanswer = $num1[$i];
if ($op[$i] eq "mul" && $num2[$i] == 0) {
$qanswer = "*";
}
}
if ($qfield == 2) {
$qanswer = $num2[$i];
if ($op[$i] eq "div" && $num1[$i] == 0) {
$qanswer = "*";
}
if ($op[$i] eq "mul" && $num1[$i] == 0) {
$qanswer = "*";
}
}
}
}

# hints

$hres[$i] = $qanswer;

# ...

if (!$qfield_single) {
$qfield_single = $qfield;
}

# special mode: tri

if ($vars{'app_input_extype_status'} eq "tri") {

if ($vars{"output_web_cache_qpara_$i"}) {
$qpara = $vars{"output_web_cache_qpara_$i"};
} else {

# 0 - no parentheses
# 1 - para 1
# 2 - para 2
# 3 - para all (technically same as 0)

$qpara = 0;

if ($vars{'app_input_extype_parentheses'}) {

$qpara = int(rand(4));
if ($qpara == 0 || $qpara == 3) {
$qpara = int(rand(4));
}
}
}

if ($num3[$i] eq "" || $op2[$i] eq "") {

$res[$i] = 999999999;

my ($whilecounter,$exclude,$debug);

while (($res[$i] < $vars{'app_input_range_from'} || $res[$i] > $vars{'app_input_range_to'} || length $res[$i] > 9 || $exclude) && $whilecounter < 512) {

$whilecounter++;

$exclude = "";

$op2[$i] = ONO::FW::Apps::Core->random_operation(ONO::FW::Apps::Core->get_operations($vars_ref));
$num3[$i] = ONO::FW::Apps::Core->random_number(1,$vars{'app_input_range_to'},1,9999999,\%vars);

if ($op2[$i] eq "div" && $num3[$i] == 0) {
$op2[$i] = "add";
}

if ($whilecounter > 200 && $whilecounter < 300) {
$op[$i] = ONO::FW::Apps::Core->random_operation(ONO::FW::Apps::Core->get_operations($vars_ref));
}
if ($whilecounter > 300) {
$op[$i] = "add";
$op2[$i] = "add";
}
if ($whilecounter > 400) {
$num1[$i] = int(rand($vars{'app_input_range_to'}/4));
$num2[$i] = int(rand($vars{'app_input_range_to'}/4));
$num3[$i] = int(rand($vars{'app_input_range_to'}/4));
}

# fix op2/3 divisions

if ($op2[$i] eq "div" && $num3[$i] > $num2[$i]) {
$num3[$i] = $num2[$i];
foreach my $opt ('2','3','5','7','11','13') {
if ($num2[$i]%$opt == 0) {
$num3[$i] = int($num2[$i]/$opt);
}
}
}

my $exop1 = ONO::FW::Apps::Core->operation_symbol($op[$i],"x");
my $exop2 = ONO::FW::Apps::Core->operation_symbol($op2[$i],"x");

# exclude negative numbers

if (!$vars{'app_input_negative'}) {

if ($qpara == 1 && $op[$i] eq "sub" && ($num1[$i]-$num2[$i]) < 0) {
$exclude++;
}
if ($qpara == 2 && $op2[$i] eq "sub" && ($num2[$i]-$num3[$i]) < 0) {
$exclude++;
}

}

if (!$exclude) {

# make sure that $string can't contain any malicious code !!!

my $string;

my $string = "$num1[$i]$exop1$num2[$i]$exop2$num3[$i]";
if ($qpara == 1) {
$string = "($num1[$i]$exop1$num2[$i])$exop2$num3[$i]";
}
if ($qpara == 2) {
$string = "$num1[$i]$exop1($num2[$i]$exop2$num3[$i])";
}

$string =~ s~[^0-9\(\)\+\-\*\/\.]~~g;
$string =~ s~\-\-~\+~g;
$string =~ s~\+\+~\+~g;
$string =~ s~\+\-~\-~g;
$string =~ s~\-\+~\-~g;

$res[$i] = eval($string);

if (length $res[$i] > 8) {
$exclude++;
}

}

if ($vars{'app_input_comma_status'} < 1 && $res[$i] =~ /\./) {
$res[$i] = 999999999;
}

}

if (!$res[$i] || $res[$i] > $vars{'app_input_range_to'}) {

$num1[$i] = 1+int(rand($vars{'app_input_range_to'}/3));
$num2[$i] = 1+int(rand($vars{'app_input_range_to'}/3));
$num3[$i] = 1+int(rand($vars{'app_input_range_to'}/4));

$op[$i] = "add";
$op2[$i] = "add";

$res[$i] = $num1[$i]+$num2[$i]+$num3[$i];

}

$qanswer = $res[$i];

}

$op_symbol[$i] = ONO::FW::Apps::Core->operation_symbol($op[$i],$op_symbol_opts);
$op_symbol2[$i] = ONO::FW::Apps::Core->operation_symbol($op2[$i],$op_symbol_opts);

}

if (!$qpara_single) {
$qpara_single = $qpara;
}

# output paper

if ($vars{'output_paper'}) {

my $max_number_length = ONO::FW::Apps::Core->number_length($vars{'app_input_range_to'},$vars{'app_input_comma_status'});

my @pos = (14,17,26,28,12, 15,26);
if ($vars{'app_input_extype_status'} ne "tri") {
if ($max_number_length > 3) {
@pos = (12,15,27,28,12, 13,26,"...");
}
if ($max_number_length > 4) {
@pos = (13,16,29,30,11, 13,28,"...");
if ($max_number_length > 5) {
@pos = (13,15,29,30,10, 13,28,".....");
}
if ($max_number_length > 6) {
@pos = (13,15,29,30,8, 13,28,".....");
}
ONO::Lib::PDF::Draw->font('',$pos[4]);
}
}

$num1[$i] = ONO::FW::Apps::Core->make_human_readable($num1[$i],$num_comma,$num_delimiter);
$num2[$i] = ONO::FW::Apps::Core->make_human_readable($num2[$i],$num_comma,$num_delimiter);
$num3[$i] = ONO::FW::Apps::Core->make_human_readable($num3[$i],$num_comma,$num_delimiter);
$res[$i] = ONO::FW::Apps::Core->make_human_readable($res[$i],$num_comma,$num_delimiter);

$corr_cache[$i] = "$op[$i]\^$op_symbol[$i]\^$op2[$i]\^$op_symbol2[$i]\^$num1[$i]\^$num2[$i]\^$num3[$i]\^$res[$i]\^$pdf_x\^$pdf_y\^$qpara\^";
$corr_cache[$i] .= "$pos[0]\^$pos[1]\^$pos[2]\^$pos[3]\^$pos[4]\^$pos[5]\^$pos[6]\^$pos[7]\^";

if ($option eq "cards") {

ONO::Lib::PDF::Draw->font('HB',24);

if ($op[$i] eq "mul") {
ONO::Lib::PDF::Draw->circle($canvas,$pos_cards_x,$pos_cards_y-2,1,1,1);
} else {
ONO::Lib::PDF::Draw->text($canvas,$pos_cards_x,$pos_cards_y, $op_symbol[$i],"c");
}

ONO::Lib::PDF::Draw->text($canvas,$pos_cards_x-$paper_cards_x/12,$pos_cards_y, $num1[$i],"c");
ONO::Lib::PDF::Draw->text($canvas,$pos_cards_x+$paper_cards_x/12,$pos_cards_y, $num2[$i],"c");

$pos_cards_x = $pos_cards_x + $paper_cards_x/3;
if (($i-2)%3 == 0) {
$pos_cards_x = $paper_cards_x/6;
$pos_cards_y = $pos_cards_y + $paper_cards_y/3;
}

} else {

if ($vars{'app_input_extype_status'} eq "tri") {

# tri mode uses a more static display

if (!$vars{'app_input_result_hints'} || $i < 20) {

ONO::Lib::PDF::Draw->text_math_symbol($canvas,$pdf_x+19.5, $pdf_y-1.25, 2.5, $op_symbol[$i]);

ONO::Lib::PDF::Draw->text_math_symbol($canvas,$pdf_x+38.5, $pdf_y-1.25, 2.5, $op_symbol2[$i]);

ONO::Lib::PDF::Draw->text($canvas,$pdf_x+58, $pdf_y, "=");

my @pos = (0,0,0);

if ($qpara == 1) {
ONO::Lib::PDF::Draw->text($canvas,$pdf_x+4, $pdf_y, "(","center");
ONO::Lib::PDF::Draw->text($canvas,$pdf_x+34, $pdf_y, ")","center");
@pos = (2,-2,0);
}
if ($qpara == 2) {
ONO::Lib::PDF::Draw->text($canvas,$pdf_x+23, $pdf_y, "(","center");
ONO::Lib::PDF::Draw->text($canvas,$pdf_x+54, $pdf_y, ")","center");
@pos = (0,2,-2);
}
if ($qpara == 3) {
ONO::Lib::PDF::Draw->text($canvas,$pdf_x+4, $pdf_y, "(","center");
ONO::Lib::PDF::Draw->text($canvas,$pdf_x+54, $pdf_y, ")","center");
@pos = (2,0,-2);
}

if ($max_number_length > 5) {
ONO::Lib::PDF::Draw->font('',11);
}
if ($max_number_length > 6) {
ONO::Lib::PDF::Draw->font('',10);
}
if ($max_number_length > 7) {
ONO::Lib::PDF::Draw->font('',9);
}

ONO::Lib::PDF::Draw->text($canvas,$pdf_x+10+$pos[0], $pdf_y, $num1[$i],"center");
ONO::Lib::PDF::Draw->text($canvas,$pdf_x+29+$pos[1], $pdf_y, $num2[$i],"center");
ONO::Lib::PDF::Draw->text($canvas,$pdf_x+48+$pos[2], $pdf_y, $num3[$i],"center");

ONO::Lib::PDF::Draw->font('',9);
ONO::Lib::PDF::Draw->text($canvas,$pdf_x+63, $pdf_y, "...................");
ONO::Lib::PDF::Draw->font('',12);

}

} else {

# normal modes (non-tri)

if (!$vars{'app_input_result_hints'} || $i < 20 || $i > 39) {

ONO::Lib::PDF::Draw->text_math_symbol($canvas,$pdf_x+$pos[1], $pdf_y-1.25, 2.5, $op_symbol[$i]);
ONO::Lib::PDF::Draw->text($canvas,$pdf_x+$pos[3], $pdf_y, "=");

if ($vars{'app_input_numsys_status'} eq "roman") {
ONO::Lib::PDF::Draw->font('',9);
$pos[0]++;
$pos[2]++;
if (length $num1[$i] > 4 || length $num2[$i] > 4) {
ONO::Lib::PDF::Draw->font('',7.5);
}
if (length $num1[$i] > 5 || length $num2[$i] > 5) {
ONO::Lib::PDF::Draw->font('',6);
}
}

if ($qfield == 2 || $qfield == 3) {
ONO::Lib::PDF::Draw->text($canvas,$pdf_x+$pos[0], $pdf_y, $num1[$i],"r");
}
if ($qfield == 1 || $qfield == 3) {
ONO::Lib::PDF::Draw->text($canvas,$pdf_x+$pos[2], $pdf_y, $num2[$i],"r");
}
if ($qfield == 3) {
ONO::Lib::PDF::Draw->font('',9);
ONO::Lib::PDF::Draw->text($canvas,$pdf_x+33, $pdf_y, "...................");
ONO::Lib::PDF::Draw->font('',$pos[4]);
} else {
ONO::Lib::PDF::Draw->text($canvas,$pdf_x+33, $pdf_y, $res[$i]);
}
if ($qfield == 1 || $qfield == 2) {
ONO::Lib::PDF::Draw->font('',9);
if ($qfield == 1) {
ONO::Lib::PDF::Draw->text($canvas,$pdf_x+$pos[5], $pdf_y, ".........$pos[7]","r");
}
if ($qfield == 2) {
ONO::Lib::PDF::Draw->text($canvas,$pdf_x+$pos[6], $pdf_y, ".......$pos[7]","r");
}
ONO::Lib::PDF::Draw->font('',$pos[4]);
}

}

}

}

$pdf_y = $pdf_y + 8;
if (($i+1)%5 == 0) {
$pdf_y = $pdf_y + 7.5;
}

if ($pdf_y > 190) {
$pdf_x = $pdf_x + 57.5;
if ($vars{'app_input_extype_status'} eq "tri") {
$pdf_x = $pdf_x + 25;
}
$pdf_y = 8;
}

}

if ($vars{'output_web'}) {

my $hidden = "hidden";
if (ONO::IO->devstation) {
#$hidden = "text";
}

$WEB .= qq~<input type="$hidden" name="output_web_cache_op_$i" value="$op[$i]">
<input type="hidden" name="output_web_cache_op2_$i" value="$op2[$i]">
<input type="$hidden" name="output_web_cache_num1_$i" value="$num1[$i]">
<input type="$hidden" name="output_web_cache_num2_$i" value="$num2[$i]">
<input type="hidden" name="output_web_cache_num3_$i" value="$num3[$i]">
<input type="$hidden" name="output_web_cache_res_$i" value="$res[$i]">
<input type="hidden" name="output_web_cache_qfield_$i" value="$qfield">
<input type="hidden" name="output_web_cache_qpara_$i" value="$qpara">
~;

if ($vars{'app_input_multiple_questions_per_page'}) {

my ($BG,$is_correct,$TRI);

my $PLA = "?";
if (ONO::IO->devstation) {
$PLA = $qanswer;
}

my $FIELD = qq~<input type="text" id="output_web_answer_${i}_id" name="output_web_answer_$i" value="$vars{"output_web_answer_$i"}"
maxlength="7" min="-9999999" max="9999999" class="$class" style="width:${width}px" placeholder="$PLA">~;

if ($vars{"output_web_answer_$i"} =~ /[A-Z0-9\.]/) {
$BG = " bg_red";
$stat_todo--;
if ($vars{"output_web_answer_$i"} eq $qanswer || $qanswer eq "*") {
$BG = " bg_green";
$qanswer = ONO::FW::Apps::Core->make_human_readable($qanswer,$num_comma,$num_delimiter);
my $show = $qanswer;
if ($qanswer eq "*") {
$show = $vars{"output_web_answer_$i"};
}
$FIELD = qq~<div class="bold green">$show</div><input type="hidden" name="output_web_answer_$i" value="$vars{"output_web_answer_$i"}">~;
$stat_correct++;
$is_correct++;
} else {
$stat_wrong++;
$vars{'output_web_mistakes'}++;

# is this REALLY wrong? recalculate, so that we'll catch caching errors...

if (
($op[$i] eq "add" && $vars{"output_web_answer_$i"} == $num1[$i]+$num2[$i]) ||
($op[$i] eq "sub" && $vars{"output_web_answer_$i"} == $num1[$i]-$num2[$i])
) {

$stat_wrong--;
$vars{'output_web_mistakes'}--;
$BG = " bg_green";
$qanswer = ONO::FW::Apps::Core->make_human_readable($qanswer,$num_comma,$num_delimiter);
$FIELD = qq~<div class="bold green">$qanswer</div><input type="hidden" name="output_web_answer_$i" value="$vars{"output_web_answer_$i"}">~;
$stat_correct++;
$is_correct++;
}

}
}

if (!$is_correct && !$focus_on) {
$FIELD .= qq~<script>document.getElementById('output_web_answer_${i}_id').focus();</script>~;
$focus_on++;
}

$num1[$i] = ONO::FW::Apps::Core->make_human_readable($num1[$i],$num_comma,$num_delimiter);
$num2[$i] = ONO::FW::Apps::Core->make_human_readable($num2[$i],$num_comma,$num_delimiter);

my $CLASS = "pad5_2 nowrap";

my @PAR;

if ($vars{'app_input_extype_status'} eq "tri") {

my @PARS = ("","","","");
$CLASS = "pad5_2 center nowrap";

if ($qpara == 1) {
@PARS = ("(","",")","");
}
if ($qpara == 2) {
@PARS = ("","(","",")");
}
if ($qpara == 3) {
@PARS = ("(","","",")");
}
for (my $i = 0; $i < 4; $i++) {
$PAR[$i] = qq~<td class="bold center">$PARS[$i]</td>~;
}

$TRI = qq~<td class="center">$op_symbol2[$i]</td>
<td class="center pad5_2">$num3[$i]</td>
~;
}

my @QFIELD = (
qq~<td class="$CLASS $class">$num1[$i]</td>~,
qq~<td class="$CLASS $class">$num2[$i]</td>~,
qq~<td class="$CLASS w20">$FIELD</td>~,
);

if ($qfield == 1) {
@QFIELD = (
qq~<td class="$CLASS $class">$FIELD</td>~,
qq~<td class="$CLASS $class">$num2[$i]</td>~,
qq~<td class="$CLASS w20">$res[$i]</td>~,
);
}
if ($qfield == 2) {
@QFIELD = (
qq~<td class="$CLASS $class">$num1[$i]</td>~,
qq~<td class="$CLASS $class">$FIELD</td>~,
qq~<td class="$CLASS w20">$res[$i]</td>~,
);
}

$WEB .= qq~<tr class="bt bb$BG">
<td class="w80"></td>
$PAR[0]
$QFIELD[0]
<td class="center">$op_symbol[$i]</td>
$PAR[1]
$QFIELD[1]
$PAR[2]
$TRI
$PAR[3]
<td>=</td>
$QFIELD[2]
<td><div class="block32"></div></td>
</tr>
~;

}

if ($i == 9) {

$WEB .= ONO::FW::Apps::Core->multi_frame_split;

}

}

}

if ($vars{'output_paper'} && $vars{'app_input_result_hints'}) {

for (my $y = 0; $y < 4; $y++) {

if ($vars{'app_input_extype_status'} eq "tri") {
my @block = ONO::FW::Apps::Core::Graphics->pdf_draw_result_hints_resblock(\@res,$y);
ONO::FW::Apps::Core::Graphics->pdf_draw_result_hints($canvas,100,$y*47.5+2,70,40,1,5,\@block,"",$num_comma);
} else {
my @block = ONO::FW::Apps::Core::Graphics->pdf_draw_result_hints_resblock(\@hres,$y,$y+8);
ONO::FW::Apps::Core::Graphics->pdf_draw_result_hints($canvas,60,$y*47.5+2,55,40,2,5,\@block,"",$num_comma);
}

}

}

if ($vars{'output_web'}) {

$WEB .= ONO::FW::Apps::Core->multi_frame_end($vars{'output_web_finish'},$vars{'app_input_multiple_questions_per_page'});

$WEB .= ONO::FW::Apps::Core->input_vars(\%vars);

if ($questions_per_page == 1) {

my @TDS = (
qq~<td class="pad10_2 bold">$num1[0]</td>
<td class="pad10_2 bold">$op_symbol[0]</td>
<td class="pad10_2 bold">$num2[0]</td>
<td class="pad10_2 bold">=</td>
~,
"",
);

if ($qfield_single == 1) {
@TDS = (
qq~~,
qq~<td class="pad10_2 bold">$op_symbol[0]</td>
<td class="pad10_2 bold">$num2[0]</td>
<td class="pad10_2 bold">=</td>
<td class="pad10_2 bold">$res[0]</td>
~,
);
}

if ($qfield_single == 2) {
@TDS = (
qq~<td class="pad10_2 bold">$num1[0]</td>
<td class="pad10_2 bold">$op_symbol[0]</td>
~,
qq~<td class="pad10_2 bold">=</td>
<td class="pad10_2 bold">$res[0]</td>
~,
);
}

if ($vars{'app_input_extype_status'} eq "tri") {

my @PARA;
if ($qpara_single == 1) {
@PARA = (qq~<td>(</td>~,"",qq~<td>)</td>~,"");
}
if ($qpara_single == 2) {
@PARA = ("",qq~<td>(</td>~,"",qq~<td>)</td>~);
}
if ($qpara_single == 3) {
@PARA = (qq~<td>(</td>~,"","",qq~<td>)</td>~);
}

@TDS = (
qq~$PARA[0]
<td class="pad10_2 bold">$num1[0]</td>
<td class="pad10_2 bold">$op_symbol[0]</td>
$PARA[1]
<td class="pad10_2 bold">$num2[0]</td>
$PARA[2]
<td class="pad10_2 bold">$op_symbol2[0]</td>
<td class="pad10_2 bold">$num3[0]</td>
$PARA[3]
<td class="pad10_2 bold">=</td>
~,
"",
);
}

my $maxlength = length $num1[0];
if (length $num2[0] > $maxlength) {
$maxlength = length $num2[0];
}
if (length $num3[0] > $maxlength) {
$maxlength = length $num3[0];
}

$WEB .= ONO::FW::Apps::Core->check_single(
$TDS[0],
$TDS[1],
50,
$maxlength+1,
$BLK_ref,
\%vars,
"n",
);

} else {

$WEB .= ONO::FW::Apps::Core->check_multi($BLK_ref,$stat_todo+$stat_wrong,$page,$pages,"",\%vars);

}

$WEB .= qq~</form>~;

}

}

if (ONO::FW::Apps::Core->print_corrections("$BASE/pdf/pdf-$pdfkey-corr.pdf","",$format,$vars_ref)) {

if ($option eq "cards") {
ONO::Lib::PDF::Gen->page($format,"L");
&cards_grid("",$canvas,$paper_cards_x/3,$paper_cards_y/3);
$pos_cards_x = $paper_cards_x-$paper_cards_x/6;
$pos_cards_y = $paper_cards_y/6;
} else {
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::FW::Apps::Core->unique_copies_info($canvas,$copy,$vars_ref);
ONO::FW::Apps::Core::Graphics->pdf_draw_item_numbers($canvas,-2,0,1,4,"CHV",$vars_ref);
}

# ONO::Lib::PDF::ToolBox->canvas_test($canvas);

for (my $c = 0; $c < $questions_per_page; $c++) {

my ($op,$op_symbol,$op2,$op_symbol2,$num1,$num2,$num3,$res,$pdf_x,$pdf_y,$qpara,@pos) = split(/\^/,$corr_cache[$c]);

if ($option eq "cards") {

ONO::Lib::PDF::Draw->font('HB',24);

ONO::Lib::PDF::Draw->text($canvas,$pos_cards_x,$pos_cards_y, $res,"c");

$pos_cards_x = $pos_cards_x - $paper_cards_x/3;
if (($c-2)%3 == 0) {
$pos_cards_x = $paper_cards_x-$paper_cards_x/6;
$pos_cards_y = $pos_cards_y + $paper_cards_y/3;
}

} else {

if ($c == 0) {
ONO::Lib::PDF::Draw->font('',$pos[4]);
}

if ($vars{'app_input_extype_status'} eq "tri") {

if (!$vars{'app_input_result_hints'} || $c < 20) {

my @para;
if ($qpara == 1) {
@para = ("(","",")","");
}
if ($qpara == 2) {
@para = ("","(","",")");
}
if ($qpara == 3) {
@para = ("(","","",")");
}

ONO::Lib::PDF::Draw->font('',9);

ONO::Lib::PDF::Draw->text($canvas,$pdf_x+55, $pdf_y,"$para[0]$num1 $op_symbol $para[1]$num2$para[2] $op_symbol2 $num3$para[3]","r");
ONO::Lib::PDF::Draw->text($canvas,$pdf_x+60, $pdf_y,"=","center");
ONO::Lib::PDF::Draw->text($canvas,$pdf_x+65, $pdf_y,"$res");

}

} else {

if (!$vars{'app_input_result_hints'} || $c < 20 || $c > 39) {

ONO::Lib::PDF::Draw->text_math_symbol($canvas,$pdf_x+$pos[1], $pdf_y-1.25, 2.5, $op_symbol);

ONO::Lib::PDF::Draw->font('',$pos[4]);

if ($vars{'app_input_numsys_status'} eq "roman") {
ONO::Lib::PDF::Draw->font('',8);
$pos[2]++;
}

ONO::Lib::PDF::Draw->text($canvas,$pdf_x+$pos[0], $pdf_y,$num1,"r");
ONO::Lib::PDF::Draw->text($canvas,$pdf_x+$pos[2], $pdf_y,$num2,"r");

ONO::Lib::PDF::Draw->text($canvas,$pdf_x+$pos[3], $pdf_y,"=");
ONO::Lib::PDF::Draw->font('HO');
ONO::Lib::PDF::Draw->text($canvas,$pdf_x+50, $pdf_y,$res,"r");
ONO::Lib::PDF::Draw->font('H',12);

}

}
}
}
}
}

if ($vars{'output_web'}) {

($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,$stat_wrong,$stat_todo_total,$vars{'output_web_mistakes'},$page,$pages,$vars{'output_web_completed'});

}

if ($vars{'output_paper'}) {

ONO::FW::Apps::PDF->end($canvas,\%vars);

$WEB .= ONO::FW::Apps::Core->output_paper($pdfkey,\%BLK,\%vars,$HTTP,$BASE);

}

}

if ($vars{'output_web'}) {
ONO::FW::Apps::Core->varstore($vars{'username'},$vars{'virclass'},$vars{'anonymous'},\%vars);
}

} else {

#############################################################
# EXERCISE SETUP
#############################################################

if (!$vars{'app_input_number_of_questions'}) {
$vars{'app_input_number_of_questions'} = 20;
if (ONO::IO->device eq "phone") {
$vars{'app_input_number_of_questions'} = 10;
}
}

if ($option ne "cards") {

$WEB .= ONO::FW::Apps::Core->infopics(
"screen:320:50:50::arithmetic_screenshot_1.jpg",
"sheet:168:180:110:15:arithmetic_sheet_1.jpg",
"sheet:168:40:140:-10:arithmetic_sheet_1.jpg",
);

}

my (%setup,$hide);
$setup{'button_std_script'} = qq~onojs_hide('app_toggle_parentheses');~;
$setup{'button_var_script'} = qq~onojs_hide('app_toggle_parentheses');~;
$setup{'button_tri_script'} = qq~onojs_inline('app_toggle_parentheses');~;
if ($vars{'app_input_extype_status'} ne "tri") {
$hide = " hide";
}
my @tabs = ("std:green:a + b = ?","var:green:a + ? = c","tri:green:(a + b) • c = ?");
my $CUSTOM = qq~<div class="inline">~;
$CUSTOM .= ONO::Lib::UI::Select->buttons(
"app_input_extype",
$vars{'app_input_extype_status'},
\@tabs,
"",
"f",
$BLK_ref,
\%setup,
);
my $SWITCH = ONO::Lib::UI::Check->switch("app_input_extype_parentheses",$vars{'app_input_extype_parentheses'},"m");
$CUSTOM .= qq~</div>
<div id="app_toggle_parentheses" class="inline$hide">
<table class="default_table ml10">
<tr>
<td class="p0">$SWITCH</td>
<td></td>
<td class="p0">$txt[0] ()</td>
</tr>
</table>
</div>
~;

my $hide = "h";
if ("$vars{'app_input_digits_status'}$vars{'app_input_digits_status_alt'}" =~ /[1-9]/) {
$hide = "";
}
if ("$vars{'app_input_roundup_status'}$vars{'app_input_roundup_status_alt'}" =~ /[1-9]/) {
$hide = "";
}

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-rR");
$CONF .= ONO::FW::Apps::ToolBox->conf_input("spacer", $BLK_ref,\%vars,"","s");
$CONF .= ONO::FW::Apps::ToolBox->conf_input("tensbarrier", $BLK_ref,\%vars,"","S");
$CONF .= ONO::FW::Apps::ToolBox->conf_input("negative", $BLK_ref,\%vars,"h");
# $CONF .= ONO::FW::Apps::ToolBox->conf_input("spacer", $BLK_ref,\%vars,"","s");
if ($option ne "cards") {
$CONF .= ONO::FW::Apps::ToolBox->conf_input("custom", $BLK_ref,\%vars,"","",$CUSTOM);
# $CONF .= ONO::FW::Apps::ToolBox->conf_input("spacer", $BLK_ref,\%vars,"","s");
}
if ($hide) {
$CONF .= ONO::FW::Apps::ToolBox->conf_input("moreopts", $BLK_ref,\%vars,"","bm");
}
if ($option ne "cards") {
$CONF .= ONO::FW::Apps::ToolBox->conf_input("numsys", $BLK_ref,\%vars,"$hide");
}
$CONF .= ONO::FW::Apps::ToolBox->conf_input("digitsdual", $BLK_ref,\%vars,"$hide",3);
$CONF .= ONO::FW::Apps::ToolBox->conf_input("commadual", $BLK_ref,\%vars,"C$hide",3);
$CONF .= ONO::FW::Apps::ToolBox->conf_input("roundupdual", $BLK_ref,\%vars,"$hide");
$CONF .= ONO::FW::Apps::ToolBox->conf_input("resulthints", $BLK_ref,\%vars,"","S");

my $switches = "op-ncdi-CDI-lsu-NO";
if ($option eq "cards") {
$switches = "p-nc-C-NO-Q";
}

$WEB .= ONO::FW::Apps::Core->conf_box(
$db,
$community,
$app,
$form,
"",
$action_url,
$CONF,
$lang,
$BLK_ref,
\%vars,
$switches,
);
if ($option ne "cards" && !$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 cards_grid {

my (
$self,
$canvas,
$paper_cards_x,
$paper_cards_y,
) = @_;

ONO::Lib::PDF::Draw->line($canvas,0, $paper_cards_y, $paper_cards_x*3, $paper_cards_y);
ONO::Lib::PDF::Draw->line($canvas,0, $paper_cards_y*2, $paper_cards_x*3, $paper_cards_y*2);

ONO::Lib::PDF::Draw->line($canvas,$paper_cards_x, 0, $paper_cards_x, $paper_cards_y*3);
ONO::Lib::PDF::Draw->line($canvas,$paper_cards_x*2,0, $paper_cards_x*2, $paper_cards_y*3);

}

###################################################################
#### THAT'S IT :-D
###################################################################

1;

__END__