ONO::Lib::Math

package ONO::Lib::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 CGI;
use strict;

use ONO::Lib::Math::HTML;

use ONO::Lib::Data::Numbers::Primes;

#: This library contains everything related to mathematics.

sub calc {

#: Basic operations with two numbers.
#: See also calc_string(), calc_first(), calc_second().

my (
$self,
$num1,
$num2,
$op,
) = @_;

my $res = 0;

if ($op eq "add" || $op eq "+") {
$res = $num1 + $num2;
}
if ($op eq "sub" || $op eq "-") {
$res = $num1 - $num2;
}
if ($op eq "mul" || $op eq "*" || $op eq "x") {
$res = $num1 * $num2;
}
if (($op eq "div" || $op eq ":" || $op eq "/") && $num2) {
$res = $num1 / $num2;
}

return $res;

}

sub calc_string {

#: Similar to calc(), but allows to execute a string including more
#: complex calculations, like ( 3 x 3) + ( 10 / 2 ) = ... for example.

my $string = lc $_[1];

$string =~ s~add~\+~g;
$string =~ s~sub~\-~g;
$string =~ s~(mul|x)~\*~g;
$string =~ s~(div|:)~\/~g;

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

if ($string =~ /[0-9]/) {
return eval($string);
} else {
return "";
}

}

sub calc_first {

my (
$self,
$num1,
$num2,
$op,
) = @_;

#: Like calc(), but return the first element ( ? + a = b ).

my $res = 0;

if ($op eq "add" || $op eq "+") {
$res = $num2 - $num1;
}
if ($op eq "sub" || $op eq "-") {
$res = $num2 + $num1;
}
if (($op eq "mul" || $op eq "*" || $op eq "x") && $num1) {
$res = $num2 / $num1;
}
if ($op eq "div" || $op eq ":" || $op eq "/") {
$res = $num1 * $num2;
}

return $res;

}

sub calc_second {

my (
$self,
$num1,
$num2,
$op,
) = @_;

#: Like calc(), but return the second element ( a + ? = c ).

my $res = 0;

if ($op eq "add" || $op eq "+") {
$res = $num2 - $num1;
}
if ($op eq "sub" || $op eq "-") {
$res = $num1 - $num2;
}
if (($op eq "mul" || $op eq "*" || $op eq "x") && $num1) {
$res = $num2 / $num1;
}
if (($op eq "div" || $op eq ":" || $op eq "/") && $num2) {
$res = $num1 / $num2;
}

return $res;

}

sub comp {

my (
$self,
$num1,
$num2,
) = @_;

#: Compare two numbers, returns, eq, lt, or gt.

my $res;

if ($num1 == $num2) {
$res = "eq";
}
if ($num1 < $num2) {
$res = "lt";
}
if ($num1 > $num2) {
$res = "gt";
}

return $res;

}

sub fraction_reducer {

#: Reduce a fraction.

my (
$self,
$a,
$b,
$optimize_negatives,
) = @_;

my $abs_a = abs($a);
my $abs_b = abs($b);

if ($abs_a > 0 && $abs_a == $abs_b) {

if ($a > 0 && $b > 0 || $a < 0 && $b < 0) {
($a,$b) = (1,1);
}
if ($a > 0 && $b < 0) {
($a,$b) = (1,-1);
}
if ($a < 0 && $b > 0) {
($a,$b) = (-1,1);
}

} else {

if ($a == 0) {

($a,$b) = (0,1);

} else {

foreach my $prime (ONO::Lib::Data::Numbers::Primes->get()) {

if ($prime < $abs_a+1 && $prime < $abs_b+1 && $abs_a > 1 && $abs_b > 1) {

my $whilecounter;
while ($whilecounter < 256 && $a%$prime == 0 && $b%$prime == 0) {
$whilecounter++;
$a = $a/$prime;
$b = $b/$prime;
}
}
}
}
}

if ($optimize_negatives) {
if ($a < 0 && $b < 0) {
$a = abs($a);
$b = abs($b);
} else {
if ($b < 0) {
$a = -$a;
$b = -$b;
}
}
}

return ($a,$b);

}

sub op_to_symbol {

#: Operation (add, sub, mul, div) to symbol.
#:
#: -C colorize (with -H only)
#: -H html mode (universal mode is optimized for PDF)

my $SYM = $_[1];

if ($_[2] =~ /H/) {

if ($_[2] =~ /C/) {

$SYM =~ s~add~<span class="lightgreen">+</span>~;
$SYM =~ s~sub~<span class="lightred">—</span>~;
$SYM =~ s~mul~<span class="lightgreen">•</span>~;
$SYM =~ s~div~<span class="lightred">:</span>~;

$SYM =~ s~eq~=~;
$SYM =~ s~lt~<span class="lightblue"><</span>~;
$SYM =~ s~gt~<span class="lightblue">></span>~;

} else {

$SYM =~ s~add~+~;
$SYM =~ s~sub~—~;
$SYM =~ s~mul~•~;
$SYM =~ s~div~:~;

$SYM =~ s~eq~=~;
$SYM =~ s~lt~<~;
$SYM =~ s~gt~>~;

}

} else {

$SYM =~ s~add~+~;
$SYM =~ s~sub~-~;
$SYM =~ s~mul~x~;
$SYM =~ s~div~:~;

$SYM =~ s~eq~=~;
$SYM =~ s~lt~\>~;
$SYM =~ s~gt~\<~;

}

return $SYM;

}

###############################################################################
# end of script
###############################################################################

1;

__END__