Merge remote-tracking branch 'upstream/master'
commit
303628eee0
|
@ -1,8 +1,12 @@
|
|||
package DDG::Goodie::Calculator;
|
||||
# ABSTRACT: do simple arthimetical calculations
|
||||
|
||||
use DDG::Goodie;
|
||||
|
||||
zci is_cached => 1;
|
||||
use List::Util qw( all first max );
|
||||
use Math::Trig;
|
||||
|
||||
zci is_cached => 1;
|
||||
zci answer_type => "calc";
|
||||
|
||||
primary_example_queries '$3.43+$34.45';
|
||||
|
@ -12,10 +16,10 @@ name 'Calculator';
|
|||
code_url 'https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/Calculator.pm';
|
||||
category 'calculations';
|
||||
topics 'math';
|
||||
attribution web => [ 'https://www.duckduckgo.com', 'DuckDuckGo' ],
|
||||
github => [ 'https://github.com/duckduckgo', 'duckduckgo'],
|
||||
twitter => ['http://twitter.com/duckduckgo', 'duckduckgo'];
|
||||
|
||||
attribution
|
||||
web => ['https://www.duckduckgo.com', 'DuckDuckGo'],
|
||||
github => ['https://github.com/duckduckgo', 'duckduckgo'],
|
||||
twitter => ['http://twitter.com/duckduckgo', 'duckduckgo'];
|
||||
|
||||
triggers query_nowhitespace => qr<
|
||||
^
|
||||
|
@ -25,9 +29,10 @@ triggers query_nowhitespace => qr<
|
|||
|
||||
(?: [0-9 \. ,]* )
|
||||
(?: gross | dozen | pi | e | c |)
|
||||
[\( \) x X * % + / \^ 0-9 \. , \$ -]*
|
||||
|
||||
(?(1) (?: -? [0-9 \. ,]+ |) |)
|
||||
(?: [\( \) x X * % + / \^ \$ -] | times | divided by | plus | minus | cos | sin | tan | cotan | log | ln | log10 | exp )+
|
||||
(?: [\( \) x X * % + / \^ \$ -] | times | divided by | plus | minus | cos | sin | tan | cotan | log | ln | log[_]?\d{1,3} | exp | tanh | sec | csc)+
|
||||
|
||||
(?: [0-9 \. ,]* )
|
||||
(?: gross | dozen | pi | e | c |)
|
||||
|
@ -37,6 +42,60 @@ triggers query_nowhitespace => qr<
|
|||
$
|
||||
>xi;
|
||||
|
||||
# This is probably YAGNI territory, but since I have to reference it in two places
|
||||
# and there are a multitude of other notation systems (although some break the
|
||||
# 'thousands' assumption) I am going to pretend that I do need it.
|
||||
# If it could fit more than one the first in order gets preference.
|
||||
my @known_styles = ({
|
||||
id => 'perl',
|
||||
decimal => '\.',
|
||||
sub_decimal => '.',
|
||||
thousands => ',',
|
||||
sub_thousands => ',',
|
||||
},
|
||||
{
|
||||
id => 'euro',
|
||||
decimal => ',',
|
||||
sub_decimal => ',',
|
||||
thousands => '\.',
|
||||
sub_thousands => '.',
|
||||
},
|
||||
);
|
||||
|
||||
my $perl_style = first { $_->{id} eq 'perl' } @known_styles;
|
||||
foreach my $style (@known_styles) {
|
||||
$style->{fit_check} = _well_formed_for_style_func($style);
|
||||
$style->{precision} = _precision_for_style_func($style);
|
||||
$style->{make_safe} = _prepare_for_computation_func($style, $perl_style);
|
||||
$style->{make_pretty} = _display_style_func($style, $perl_style);
|
||||
}
|
||||
|
||||
# This is not as good an idea as I might think.
|
||||
# Luckily it will someday be able to be tokenized so this won't apply.
|
||||
my $all_seps = join('', map { $_->{decimal} . $_->{thousands} } @known_styles);
|
||||
|
||||
my $numbery = qr/^[\d$all_seps]+$/;
|
||||
my $funcy = qr/[[a-z]+\(|log[_]?\d{1,3}\(|\^/; # Stuff that looks like functions.
|
||||
|
||||
my %named_operations = (
|
||||
'\^' => '**',
|
||||
'x' => '*',
|
||||
'times' => '*',
|
||||
'minus' => '-',
|
||||
'plus' => '+',
|
||||
'divided\sby' => '/',
|
||||
'ln' => 'log', # perl log() is natural log.
|
||||
);
|
||||
|
||||
my %named_constants = (
|
||||
dozen => 12,
|
||||
e => 2.71828182845904523536028747135266249, # This should be computed.
|
||||
pi => pi, # pi constant from Math::Trig
|
||||
gross => 144,
|
||||
);
|
||||
|
||||
my $ored_constants = join('|', keys %named_constants); # For later substitutions
|
||||
|
||||
handle query_nowhitespace => sub {
|
||||
my $results_html;
|
||||
my $results_no_html;
|
||||
|
@ -44,147 +103,180 @@ handle query_nowhitespace => sub {
|
|||
|
||||
$query =~ s/^(?:whatis|calculate|solve|math)//;
|
||||
|
||||
if($query !~ /[xX]\s*[\*\%\+\-\/\^]/ && $query !~ /^-?[\d]{2,3}\.\d+,\s?-?[\d]{2,3}\.\d+$/) {
|
||||
if ($query !~ /[xX]\s*[\*\%\+\-\/\^]/ && $query !~ /^-?[\d]{2,3}\.\d+,\s?-?[\d]{2,3}\.\d+$/) {
|
||||
my $tmp_result = '';
|
||||
|
||||
# Grab expression.
|
||||
my $tmp_expr = $query;
|
||||
my $tmp_expr = spacing($query, 1);
|
||||
|
||||
# First replace named operations with their computable equivalents.
|
||||
while (my ($name, $operation) = each %named_operations) {
|
||||
$tmp_expr =~ s# $name # $operation #xig;
|
||||
}
|
||||
|
||||
#do the following substitutions in $tmp_expr:
|
||||
#^ -> **
|
||||
#x|times -> *
|
||||
#minus -> -
|
||||
#plus -> +
|
||||
#dividedby -> /
|
||||
$tmp_expr =~ s# \^ # ** #xg;
|
||||
$tmp_expr =~ s# (?:x|times) # * #xig;
|
||||
$tmp_expr =~ s# minus # - #xig;
|
||||
$tmp_expr =~ s# plus # + #xig;
|
||||
$tmp_expr =~ s# dividedby # \/ #xig;
|
||||
$tmp_expr =~ s#log[_]?(\d{1,3})#(1/log($1))*log#xg; # Arbitrary base logs.
|
||||
$tmp_expr =~ s/ (\d+?)E(-?\d+)([^\d]|\b) /\($1 * 10**$2\)$3/xg; # E == *10^n
|
||||
$tmp_expr =~ s/\$//g; # Remove $s.
|
||||
$tmp_expr =~ s/=$//; # Drop =.
|
||||
|
||||
# sub in constants
|
||||
# e sub is lowercase only because E == *10^n
|
||||
$tmp_expr =~ s/ ((?:\d+?|\s))E(-?\d+)([^\d]) /\($1 * 10**$2\)$3 /xg;
|
||||
$tmp_expr =~ s/ (\d+?)e([^A-Za-z]) /$1 * 2.71828182845904523536028747135266249 $2 /xg;
|
||||
$tmp_expr =~ s/ ([^A-Za-z])e([^A-Za-z]) /$1 2.71828182845904523536028747135266249 $2 /xg;
|
||||
$tmp_expr =~ s/ \be\b /2.71828182845904523536028747135266249 /xg;
|
||||
$tmp_expr =~ s/ (\d+?)c([^A-Za-z]) /$1 * 299792458 $2 /xig;
|
||||
$tmp_expr =~ s/ ([^A-Za-z])c([^A-Za-z]) /$1 299792458 $2 /xig;
|
||||
$tmp_expr =~ s/ \bc\b /299792458 /xig;
|
||||
$tmp_expr =~ s/ (\d+?)pi /$1 * 3.14159265358979323846264338327950288 /xig;
|
||||
$tmp_expr =~ s/ pi /3.14159265358979323846264338327950288 /xig;
|
||||
$tmp_expr =~ s/ (\d+?)gross /$1 * 144 /xig;
|
||||
$tmp_expr =~ s/ (\d+?)dozen /$1 * 12 /xig;
|
||||
$tmp_expr =~ s/ dozen /12 /xig;
|
||||
$tmp_expr =~ s/ gross /144 /xig;
|
||||
# Now sub in constants
|
||||
while (my ($name, $constant) = each %named_constants) {
|
||||
$tmp_expr =~ s# (\d+?)\s+$name # $1 * $constant #xig;
|
||||
$tmp_expr =~ s#\b$name\b# $constant #ig;
|
||||
}
|
||||
|
||||
# Commas into periods.
|
||||
$tmp_expr =~ s/(\d+),(\d{1,2})(?!\d)/$1.$2/g;
|
||||
my @numbers = grep { $_ =~ $numbery } (split /\s+/, $tmp_expr);
|
||||
my $style = display_style(@numbers);
|
||||
return unless $style;
|
||||
|
||||
# Drop commas.
|
||||
$tmp_expr =~ s/[\,\$]//g;
|
||||
$tmp_expr = $style->{make_safe}->($tmp_expr);
|
||||
# Using functions makes us want answers with more precision than our inputs indicate.
|
||||
my $precision = ($query =~ $funcy) ? undef : max(map { $style->{precision}->($_) } @numbers);
|
||||
|
||||
# Drop =.
|
||||
$tmp_expr =~ s/=$//;
|
||||
eval {
|
||||
# e.g. sin(100000)/100000 completely makes this go haywire.
|
||||
alarm(1);
|
||||
$tmp_result = eval($tmp_expr);
|
||||
};
|
||||
|
||||
# Drop leading 0s.
|
||||
# 2011.11.09 fix for 21 + 15 x 0 + 5
|
||||
$tmp_expr =~ s/(?<!\.)(?<![0-9])0([1-9])/$1/;
|
||||
# Guard against non-result results
|
||||
return unless (defined $tmp_result && $tmp_result ne 'inf');
|
||||
# Guard against very small floats which will not be rounded.
|
||||
# 0-9 check for http://yegg.duckduckgo.com/?q=%243.43%20%2434.45&format=json
|
||||
return unless (defined $precision || ($tmp_result =~ /^(?:\-|)[0-9\.]+$/));
|
||||
|
||||
eval {
|
||||
# e.g. sin(100000)/100000 completely makes this go haywire.
|
||||
alarm(1);
|
||||
$tmp_result = eval($tmp_expr);
|
||||
};
|
||||
|
||||
# 0-9 check for http://yegg.duckduckgo.com/?q=%243.43%20%2434.45&format=json
|
||||
if (defined $tmp_result && $tmp_result ne 'inf' && $tmp_result =~ /^(?:\-|)[0-9\.]+$/) {
|
||||
# Precisian.
|
||||
my $precisian = 0;
|
||||
|
||||
# too clever -- .5 ^ 2 not working right.
|
||||
if (0) {
|
||||
while ($query =~ /\.(\d+)/g) {
|
||||
my $decimal = length($1);
|
||||
$precisian = $decimal if $decimal > $precisian;
|
||||
}
|
||||
|
||||
$tmp_result = sprintf( '%0.' . $precisian . 'f', $tmp_result ) if $precisian;
|
||||
}
|
||||
|
||||
# Dollars.
|
||||
if ($query =~ /^\$/) {
|
||||
$tmp_result = qq(\$$tmp_result);
|
||||
}
|
||||
|
||||
# Query for display.
|
||||
my $tmp_q = $query;
|
||||
|
||||
# Drop equals.
|
||||
$tmp_q =~ s/\=$//;
|
||||
$tmp_q =~ s/((?:\d+?|\s))E(-?\d+)/\($1 * 10^$2\)/;
|
||||
|
||||
# Copy
|
||||
$results_no_html = $results_html = $tmp_q;
|
||||
|
||||
# Superscript (before spacing).
|
||||
$results_html =~ s/\^([^\)]+)/<sup>$1<\/sup>/g;
|
||||
$results_html =~ s/\^(\d+|\b(?:e|c|dozen|gross|pi)\b)/<sup>$1<\/sup>/g;
|
||||
|
||||
($results_no_html, $results_html) = (spacing($results_no_html), spacing($results_html));
|
||||
return if $results_no_html =~ /^\s/;
|
||||
|
||||
# Add commas.
|
||||
$tmp_result = commify($tmp_result);
|
||||
|
||||
# Now add it back.
|
||||
$results_no_html .= ' = ';
|
||||
$results_html .= ' = ';
|
||||
|
||||
$results_html = qq(<div>$results_html<a href="javascript:;" onClick="document.x.q.value='$tmp_result';document.x.q.focus();">$tmp_result</a></div>);
|
||||
return $results_no_html . $tmp_result, html => $results_html, heading => "Calculator";
|
||||
}
|
||||
# Ok, this might be overkill on flexibility.
|
||||
$tmp_result = sprintf('%0' . $perl_style->{sub_decimal} . $precision . 'f', $tmp_result) if ($precision);
|
||||
# Dollars.
|
||||
$tmp_result = '$' . $tmp_result if ($query =~ /^\$/);
|
||||
|
||||
# Query for display.
|
||||
my $tmp_q = $query;
|
||||
|
||||
# Drop equals.
|
||||
$tmp_q =~ s/\=$//;
|
||||
$tmp_q =~ s/((?:\d+?|\s))E(-?\d+)/\($1 * 10^$2\)/;
|
||||
|
||||
# Copy
|
||||
$results_no_html = $results_html = $tmp_q;
|
||||
|
||||
# Superscript (before spacing).
|
||||
$results_html =~ s/\^([^\)]+)/<sup>$1<\/sup>/g;
|
||||
$results_html =~ s/\^(\d+|\b(?:$ored_constants)\b)/<sup>$1<\/sup>/g;
|
||||
|
||||
($results_no_html, $results_html) = map { spacing($_) } ($results_no_html, $results_html);
|
||||
return if $results_no_html =~ /^\s/;
|
||||
|
||||
# Add proper separators.
|
||||
$tmp_result = $style->{make_pretty}->($tmp_result);
|
||||
|
||||
# Now add = back.
|
||||
$results_no_html .= ' = ';
|
||||
$results_html .= ' = ';
|
||||
|
||||
$results_html =
|
||||
qq(<div>$results_html<a href="javascript:;" onClick="document.x.q.value='$tmp_result';document.x.q.focus();">$tmp_result</a></div>);
|
||||
return $results_no_html . $tmp_result,
|
||||
html => $results_html,
|
||||
heading => "Calculator";
|
||||
}
|
||||
|
||||
return;
|
||||
};
|
||||
|
||||
#extra math functions
|
||||
sub tan {
|
||||
my $x = $_[0];
|
||||
return sin($x)/cos($x);
|
||||
}
|
||||
|
||||
sub cotan {
|
||||
my $x = $_[0];
|
||||
return cos($x)/sin($x);
|
||||
}
|
||||
|
||||
sub log10 {
|
||||
my $x = $_[0];
|
||||
return log($x)/log(10);
|
||||
}
|
||||
|
||||
#function to add a comma every 3 digits
|
||||
#commify '12345' -> '12,345'
|
||||
sub commify {
|
||||
my $text = reverse $_[0];
|
||||
$text =~ s/(\d\d\d)(?=\d)(?!\d*\.)/$1,/g;
|
||||
return scalar reverse $text;
|
||||
}
|
||||
|
||||
#separates symbols with a space
|
||||
#spacing '1+1' -> '1 + 1'
|
||||
sub spacing {
|
||||
my $text = shift;
|
||||
my ($text, $space_for_parse) = @_;
|
||||
|
||||
$text =~ s/(\s*(?<!<)(?:[\+\-\^xX\*\/\%]|times|plus|minus|dividedby)+\s*)/ $1 /ig;
|
||||
$text =~ s/dividedby/divided by/ig;
|
||||
$text =~ s/(\d+?)((?:dozen|pi|gross|e|c))/$1 $2/ig;
|
||||
$text =~ s/\bc\b/speed of light/ig;
|
||||
$text =~ s/\s*dividedby\s*/ divided by /ig;
|
||||
$text =~ s/(\d+?)((?:dozen|pi|gross))/$1 $2/ig;
|
||||
$text =~ s/(\d+?)e/$1 e/g; # E == *10^n
|
||||
$text =~ s/([\(\)\$])/ $1 /g if ($space_for_parse);
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
|
||||
# Takes an array of numbers and returns which style to use for parse and display
|
||||
# If there are conflicting answers among the array, will return undef.
|
||||
sub display_style {
|
||||
my @numbers = @_;
|
||||
|
||||
my $style; # By default, assume we don't understand the numbers.
|
||||
|
||||
STYLE:
|
||||
foreach my $test_style (@known_styles) {
|
||||
if (all { $test_style->{fit_check}->($_) } @numbers) {
|
||||
# All of our numbers fit this style. Since we have them in preference order
|
||||
# we can pick it and move on.
|
||||
$style = $test_style;
|
||||
last STYLE;
|
||||
}
|
||||
}
|
||||
return $style;
|
||||
}
|
||||
|
||||
# Returns a function which evaluates whether a number fits a certain style.
|
||||
sub _well_formed_for_style_func {
|
||||
my $style = shift;
|
||||
my ($decimal, $thousands) = ($style->{decimal}, $style->{thousands});
|
||||
|
||||
return sub {
|
||||
my $number = shift;
|
||||
return (
|
||||
$number =~ /^[\d$thousands$decimal]+$/
|
||||
# Only contains things we understand.
|
||||
&& ($number !~ /$thousands/ || ($number !~ /$thousands\d{1,2}\b/ && $number !~ /$thousands\d{4,}/))
|
||||
# You can leave out thousands breaks, but the ones you put in must be in the right place.
|
||||
# Note that this does not confirm that they put all the 'required' ones in.
|
||||
&& ($number !~ /$decimal/ || $number !~ /$decimal(?:.*)?(?:$decimal|$thousands)/)
|
||||
# You can omit the decimal but you cannot have another decimal or thousands after:
|
||||
) ? 1 : 0;
|
||||
};
|
||||
}
|
||||
|
||||
# Returns function which given a number in a certain style, makes it nice for human eyes.
|
||||
sub _display_style_func {
|
||||
my ($style, $perl_style) = @_;
|
||||
my ($decimal, $sub_decimal, $sub_thousands, $perl_dec) =
|
||||
(@{$style}{qw(decimal sub_decimal sub_thousands)}, $perl_style->{decimal}); # Unpacked for easier regex-building
|
||||
|
||||
return sub {
|
||||
my $text = shift;
|
||||
$text = reverse $text;
|
||||
$text =~ s/$perl_dec/$sub_decimal/g;
|
||||
$text =~ s/(\d\d\d)(?=\d)(?!\d*$decimal)/$1$sub_thousands/g;
|
||||
|
||||
return scalar reverse $text;
|
||||
};
|
||||
}
|
||||
|
||||
# Returns function which given a number in a certain style, makes it safe for perl eval.
|
||||
sub _prepare_for_computation_func {
|
||||
my ($style, $perl_style) = @_;
|
||||
my ($decimal, $thousands, $perl_dec) = (@{$style}{qw(decimal thousands)}, $perl_style->{sub_decimal});
|
||||
|
||||
return sub {
|
||||
my $number_text = shift;
|
||||
|
||||
$number_text =~ s/$thousands//g; # Remove thousands seps, since they are just visual.
|
||||
$number_text =~ s/$decimal/$perl_dec/g; # Make sure decimal mark is something perl knows how to use.
|
||||
|
||||
return $number_text;
|
||||
};
|
||||
}
|
||||
|
||||
# Returns function which given a number, determines the number of places after the decimal.
|
||||
sub _precision_for_style_func {
|
||||
my ($style) = @_;
|
||||
my $decimal = $style->{decimal};
|
||||
|
||||
return sub {
|
||||
my $number_text = shift;
|
||||
|
||||
return ($number_text =~ /$decimal(\d+)/) ? length($1) : 0;
|
||||
};
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
347
t/Calculator.t
347
t/Calculator.t
|
@ -4,95 +4,272 @@ use strict;
|
|||
use warnings;
|
||||
use Test::More;
|
||||
use DDG::Test::Goodie;
|
||||
use DDG::Goodie::Calculator; # For function subtests.
|
||||
|
||||
zci answer_type => 'calc';
|
||||
zci is_cached => 1;
|
||||
zci is_cached => 1;
|
||||
|
||||
subtest 'display format selection' => sub {
|
||||
my $ds_name = 'DDG::Goodie::Calculator::display_style';
|
||||
my $ds = \&$ds_name;
|
||||
|
||||
is($ds->('4,431', '4.321')->{id}, 'perl', '4,431 and 4.321 is perl');
|
||||
is($ds->('4,431', '4.32')->{id}, 'perl', '4,431 and 4.32 is perl');
|
||||
is($ds->('4,431', '4,32')->{id}, 'euro', '4,431 and 4,32 is euro');
|
||||
is($ds->('4534,345.0', '1',)->{id}, 'perl', '4534,345.0 should have another comma, not enforced; call it perl.');
|
||||
is($ds->('4,431', '4,32', '5,42')->{id}, 'euro', '4,431 and 4,32 and 5,42 is nicely euro-style');
|
||||
is($ds->('4,431', '4.32', '5.42')->{id}, 'perl', '4,431 and 4.32 and 5.42 is nicely perl-style');
|
||||
|
||||
is($ds->('5234534.34.54', '1',), undef, '5234534.34.54 and 1 has a mal-formed number, so we cannot proceed');
|
||||
is($ds->('4,431', '4,32', '4.32'), undef, '4,431 and 4,32 and 4.32 is confusingly ambig; no style');
|
||||
is($ds->('4,431', '4.32.10', '5.42'), undef, '4,431 and 4.32.10 is hard to figure; no style');
|
||||
is($ds->('4,431', '4,32,100', '5.42'), undef, '4,431 and 4,32,100 and 5.42 has a mal-formed number, so no go.');
|
||||
is($ds->('4,431', '4,32,100', '5,42'), undef, '4,431 and 4,32,100 and 5,42 is too crazy to work out; no style');
|
||||
};
|
||||
|
||||
ddg_goodie_test(
|
||||
[qw( DDG::Goodie::Calculator )],
|
||||
'what is 2-2' => test_zci(
|
||||
"2 - 2 = 0",
|
||||
heading => 'Calculator',
|
||||
html => qq(<div>2 - 2 = <a href="javascript:;" onClick="document.x.q.value='0';document.x.q.focus();">0</a></div>)
|
||||
),
|
||||
'solve 2+2' => test_zci(
|
||||
"2 + 2 = 4",
|
||||
heading => 'Calculator',
|
||||
html => qq(<div>2 + 2 = <a href="javascript:;" onClick="document.x.q.value='4';document.x.q.focus();">4</a></div>)
|
||||
),
|
||||
'2^8' => test_zci(
|
||||
"2 ^ 8 = 256",
|
||||
heading => 'Calculator',
|
||||
html => qq(<div>2<sup>8</sup> = <a href="javascript:;" onClick="document.x.q.value='256';document.x.q.focus();">256</a></div>)
|
||||
),
|
||||
'2 *7' => test_zci(
|
||||
"2 * 7 = 14",
|
||||
heading => 'Calculator',
|
||||
html => qq(<div>2 * 7 = <a href="javascript:;" onClick="document.x.q.value='14';document.x.q.focus();">14</a></div>)
|
||||
),
|
||||
'1 dozen * 2' => test_zci(
|
||||
"1 dozen * 2 = 24",
|
||||
heading => 'Calculator',
|
||||
html => qq(<div>1 dozen * 2 = <a href="javascript:;" onClick="document.x.q.value='24';document.x.q.focus();">24</a></div>)
|
||||
),
|
||||
'dozen + dozen' => test_zci(
|
||||
"dozen + dozen = 24",
|
||||
heading => 'Calculator',
|
||||
html => qq(<div>dozen + dozen = <a href="javascript:;" onClick="document.x.q.value='24';document.x.q.focus();">24</a></div>)
|
||||
),
|
||||
'2divided by 4' => test_zci(
|
||||
"2 divided by 4 = 0.5",
|
||||
heading => 'Calculator',
|
||||
html => qq(<div>2 divided by 4 = <a href="javascript:;" onClick="document.x.q.value='0.5';document.x.q.focus();">0.5</a></div>)
|
||||
),
|
||||
'(2c) + pi' => test_zci(
|
||||
"(2 speed of light) + pi = 599,584,919.141593",
|
||||
heading => 'Calculator',
|
||||
html => qq(<div>(2 speed of light) + pi = <a href="javascript:;" onClick="document.x.q.value='599,584,919.141593';document.x.q.focus();">599,584,919.141593</a></div>)
|
||||
),
|
||||
'2^dozen' => test_zci(
|
||||
"2 ^ dozen = 4,096",
|
||||
heading => 'Calculator',
|
||||
html => qq(<div>2<sup>dozen</sup> = <a href="javascript:;" onClick="document.x.q.value='4,096';document.x.q.focus();">4,096</a></div>)
|
||||
),
|
||||
'2^2' => test_zci(
|
||||
"2 ^ 2 = 4",
|
||||
heading => 'Calculator',
|
||||
html => qq(<div>2<sup>2</sup> = <a href="javascript:;" onClick="document.x.q.value='4';document.x.q.focus();">4</a></div>)
|
||||
),
|
||||
'2^0.2' => test_zci(
|
||||
"2 ^ 0.2 = 1.14869835499704",
|
||||
heading => 'Calculator',
|
||||
html => qq(<div>2<sup>0.2</sup> = <a href="javascript:;" onClick="document.x.q.value='1.14869835499704';document.x.q.focus();">1.14869835499704</a></div>)
|
||||
),
|
||||
'cos(0)' => test_zci(
|
||||
"cos(0) = 1",
|
||||
heading => 'Calculator',
|
||||
html => qq(<div>cos(0) = <a href="javascript:;" onClick="document.x.q.value='1';document.x.q.focus();">1</a></div>)
|
||||
),
|
||||
'tan(1)' => test_zci(
|
||||
"tan(1) = 1.5574077246549",
|
||||
heading => 'Calculator',
|
||||
html => qq(<div>tan(1) = <a href="javascript:;" onClick="document.x.q.value='1.5574077246549';document.x.q.focus();">1.5574077246549</a></div>)
|
||||
),
|
||||
'sin(1)' => test_zci(
|
||||
"sin(1) = 0.841470984807897",
|
||||
heading => 'Calculator',
|
||||
html => qq(<div>sin(1) = <a href="javascript:;" onClick="document.x.q.value='0.841470984807897';document.x.q.focus();">0.841470984807897</a></div>)
|
||||
),
|
||||
'$3.43+$34.45' => test_zci(
|
||||
'$3.43 + $34.45 = $37.88',
|
||||
heading => 'Calculator',
|
||||
html => qq(<div>\$3.43 + \$34.45 = <a href="javascript:;" onClick="document.x.q.value='\$37.88';document.x.q.focus();">\$37.88</a></div>)
|
||||
),
|
||||
'64*343' => test_zci(
|
||||
'64 * 343 = 21,952',
|
||||
heading => 'Calculator',
|
||||
html => qq(<div>64 * 343 = <a href="javascript:;" onClick="document.x.q.value='21,952';document.x.q.focus();">21,952</a></div>),
|
||||
),
|
||||
'//' => undef,
|
||||
dividedbydividedby => undef,
|
||||
[qw( DDG::Goodie::Calculator )],
|
||||
'what is 2-2' => test_zci(
|
||||
"2 - 2 = 0",
|
||||
heading => 'Calculator',
|
||||
html => qq(<div>2 - 2 = <a href="javascript:;" onClick="document.x.q.value='0';document.x.q.focus();">0</a></div>)
|
||||
),
|
||||
'solve 2+2' => test_zci(
|
||||
"2 + 2 = 4",
|
||||
heading => 'Calculator',
|
||||
html => qq(<div>2 + 2 = <a href="javascript:;" onClick="document.x.q.value='4';document.x.q.focus();">4</a></div>)
|
||||
),
|
||||
'2^8' => test_zci(
|
||||
"2 ^ 8 = 256",
|
||||
heading => 'Calculator',
|
||||
html => qq(<div>2<sup>8</sup> = <a href="javascript:;" onClick="document.x.q.value='256';document.x.q.focus();">256</a></div>)
|
||||
),
|
||||
'2 *7' => test_zci(
|
||||
"2 * 7 = 14",
|
||||
heading => 'Calculator',
|
||||
html => qq(<div>2 * 7 = <a href="javascript:;" onClick="document.x.q.value='14';document.x.q.focus();">14</a></div>)
|
||||
),
|
||||
'1 dozen * 2' => test_zci(
|
||||
"1 dozen * 2 = 24",
|
||||
heading => 'Calculator',
|
||||
html => qq(<div>1 dozen * 2 = <a href="javascript:;" onClick="document.x.q.value='24';document.x.q.focus();">24</a></div>)
|
||||
),
|
||||
'dozen + dozen' => test_zci(
|
||||
"dozen + dozen = 24",
|
||||
heading => 'Calculator',
|
||||
html => qq(<div>dozen + dozen = <a href="javascript:;" onClick="document.x.q.value='24';document.x.q.focus();">24</a></div>)
|
||||
),
|
||||
'2divided by 4' => test_zci(
|
||||
"2 divided by 4 = 0.5",
|
||||
heading => 'Calculator',
|
||||
html => qq(<div>2 divided by 4 = <a href="javascript:;" onClick="document.x.q.value='0.5';document.x.q.focus();">0.5</a></div>)
|
||||
),
|
||||
'2^dozen' => test_zci(
|
||||
"2 ^ dozen = 4,096",
|
||||
heading => 'Calculator',
|
||||
html => qq(<div>2<sup>dozen</sup> = <a href="javascript:;" onClick="document.x.q.value='4,096';document.x.q.focus();">4,096</a></div>)
|
||||
),
|
||||
'2^2' => test_zci(
|
||||
"2 ^ 2 = 4",
|
||||
heading => 'Calculator',
|
||||
html => qq(<div>2<sup>2</sup> = <a href="javascript:;" onClick="document.x.q.value='4';document.x.q.focus();">4</a></div>)
|
||||
),
|
||||
'2^0.2' => test_zci(
|
||||
"2 ^ 0.2 = 1.14869835499704",
|
||||
heading => 'Calculator',
|
||||
html =>
|
||||
qq(<div>2<sup>0.2</sup> = <a href="javascript:;" onClick="document.x.q.value='1.14869835499704';document.x.q.focus();">1.14869835499704</a></div>)
|
||||
),
|
||||
'cos(0)' => test_zci(
|
||||
"cos(0) = 1",
|
||||
heading => 'Calculator',
|
||||
html => qq(<div>cos(0) = <a href="javascript:;" onClick="document.x.q.value='1';document.x.q.focus();">1</a></div>)
|
||||
),
|
||||
'tan(1)' => test_zci(
|
||||
"tan(1) = 1.5574077246549",
|
||||
heading => 'Calculator',
|
||||
html =>
|
||||
qq(<div>tan(1) = <a href="javascript:;" onClick="document.x.q.value='1.5574077246549';document.x.q.focus();">1.5574077246549</a></div>)
|
||||
),
|
||||
'tanh(1)' => test_zci(
|
||||
"tanh(1) = 0.761594155955765",
|
||||
heading => 'Calculator',
|
||||
html =>
|
||||
qq(<div>tanh(1) = <a href="javascript:;" onClick="document.x.q.value='0.761594155955765';document.x.q.focus();">0.761594155955765</a></div>)
|
||||
),
|
||||
'cotan(1)' => test_zci(
|
||||
"cotan(1) = 0.642092615934331",
|
||||
heading => 'Calculator',
|
||||
html =>
|
||||
qq(<div>cotan(1) = <a href="javascript:;" onClick="document.x.q.value='0.642092615934331';document.x.q.focus();">0.642092615934331</a></div>)
|
||||
),
|
||||
'sin(1)' => test_zci(
|
||||
"sin(1) = 0.841470984807897",
|
||||
heading => 'Calculator',
|
||||
html =>
|
||||
qq(<div>sin(1) = <a href="javascript:;" onClick="document.x.q.value='0.841470984807897';document.x.q.focus();">0.841470984807897</a></div>)
|
||||
),
|
||||
'csc(1)' => test_zci(
|
||||
"csc(1) = 1.18839510577812",
|
||||
heading => 'Calculator',
|
||||
html =>
|
||||
qq(<div>csc(1) = <a href="javascript:;" onClick="document.x.q.value='1.18839510577812';document.x.q.focus();">1.18839510577812</a></div>)
|
||||
),
|
||||
'sec(1)' => test_zci(
|
||||
"sec(1) = 1.85081571768093",
|
||||
heading => 'Calculator',
|
||||
html =>
|
||||
qq(<div>sec(1) = <a href="javascript:;" onClick="document.x.q.value='1.85081571768093';document.x.q.focus();">1.85081571768093</a></div>)
|
||||
),
|
||||
'log(3)' => test_zci(
|
||||
"log(3) = 1.09861228866811",
|
||||
heading => 'Calculator',
|
||||
html =>
|
||||
qq(<div>log(3) = <a href="javascript:;" onClick="document.x.q.value='1.09861228866811';document.x.q.focus();">1.09861228866811</a></div>)
|
||||
),
|
||||
'ln(3)' => test_zci(
|
||||
"ln(3) = 1.09861228866811",
|
||||
heading => 'Calculator',
|
||||
html =>
|
||||
qq(<div>ln(3) = <a href="javascript:;" onClick="document.x.q.value='1.09861228866811';document.x.q.focus();">1.09861228866811</a></div>)
|
||||
),
|
||||
'log10(100.00)' => test_zci(
|
||||
"log10(100.00) = 2",
|
||||
heading => 'Calculator',
|
||||
html => qq(<div>log10(100.00) = <a href="javascript:;" onClick="document.x.q.value='2';document.x.q.focus();">2</a></div>)
|
||||
),
|
||||
'log_10(100.00)' => test_zci(
|
||||
"log_10(100.00) = 2",
|
||||
heading => 'Calculator',
|
||||
html => qq(<div>log_10(100.00) = <a href="javascript:;" onClick="document.x.q.value='2';document.x.q.focus();">2</a></div>)
|
||||
),
|
||||
'log_2(16)' => test_zci(
|
||||
"log_2(16) = 4",
|
||||
heading => 'Calculator',
|
||||
html => qq(<div>log_2(16) = <a href="javascript:;" onClick="document.x.q.value='4';document.x.q.focus();">4</a></div>)
|
||||
),
|
||||
'log_23(25)' => test_zci(
|
||||
"log_23(25) = 1.0265928122321",
|
||||
heading => 'Calculator',
|
||||
html =>
|
||||
qq(<div>log_23(25) = <a href="javascript:;" onClick="document.x.q.value='1.0265928122321';document.x.q.focus();">1.0265928122321</a></div>)
|
||||
),
|
||||
'log23(25)' => test_zci(
|
||||
"log23(25) = 1.0265928122321",
|
||||
heading => 'Calculator',
|
||||
html =>
|
||||
qq(<div>log23(25) = <a href="javascript:;" onClick="document.x.q.value='1.0265928122321';document.x.q.focus();">1.0265928122321</a></div>)
|
||||
),
|
||||
'$3.43+$34.45' => test_zci(
|
||||
'$3.43 + $34.45 = $37.88',
|
||||
heading => 'Calculator',
|
||||
html => qq(<div>\$3.43 + \$34.45 = <a href="javascript:;" onClick="document.x.q.value='\$37.88';document.x.q.focus();">\$37.88</a></div>)
|
||||
),
|
||||
'$3.45+$34.45' => test_zci(
|
||||
'$3.45 + $34.45 = $37.90',
|
||||
heading => 'Calculator',
|
||||
html => qq(<div>\$3.45 + \$34.45 = <a href="javascript:;" onClick="document.x.q.value='\$37.90';document.x.q.focus();">\$37.90</a></div>)
|
||||
),
|
||||
'$3+$34' => test_zci(
|
||||
'$3 + $34 = $37',
|
||||
heading => 'Calculator',
|
||||
html => qq(<div>\$3 + \$34 = <a href="javascript:;" onClick="document.x.q.value='\$37';document.x.q.focus();">\$37</a></div>)
|
||||
),
|
||||
'$3,4+$34,4' => test_zci(
|
||||
'$3,4 + $34,4 = $37,8',
|
||||
heading => 'Calculator',
|
||||
html => qq(<div>\$3,4 + \$34,4 = <a href="javascript:;" onClick="document.x.q.value='\$37,8';document.x.q.focus();">\$37,8</a></div>)
|
||||
),
|
||||
'64*343' => test_zci(
|
||||
'64 * 343 = 21,952',
|
||||
heading => 'Calculator',
|
||||
html => qq(<div>64 * 343 = <a href="javascript:;" onClick="document.x.q.value='21,952';document.x.q.focus();">21,952</a></div>),
|
||||
),
|
||||
'1E2 + 1' => test_zci(
|
||||
'(1 * 10 ^ 2) + 1 = 101',
|
||||
heading => 'Calculator',
|
||||
html => qq(<div>(1 * 10<sup>2</sup>) + 1 = <a href="javascript:;" onClick="document.x.q.value='101';document.x.q.focus();">101</a></div>),
|
||||
),
|
||||
'1 + 1E2' => test_zci(
|
||||
'1 + (1 * 10 ^ 2) = 101',
|
||||
heading => 'Calculator',
|
||||
html => qq(<div>1 + (1 * 10<sup>2</sup>) = <a href="javascript:;" onClick="document.x.q.value='101';document.x.q.focus();">101</a></div>),
|
||||
),
|
||||
'2 * 3 + 1E2' => test_zci(
|
||||
'2 * 3 + (1 * 10 ^ 2) = 106',
|
||||
heading => 'Calculator',
|
||||
html =>
|
||||
qq(<div>2 * 3 + (1 * 10<sup>2</sup>) = <a href="javascript:;" onClick="document.x.q.value='106';document.x.q.focus();">106</a></div>),
|
||||
),
|
||||
'1E2 + 2 * 3' => test_zci(
|
||||
'(1 * 10 ^ 2) + 2 * 3 = 106',
|
||||
heading => 'Calculator',
|
||||
html =>
|
||||
qq(<div>(1 * 10<sup>2</sup>) + 2 * 3 = <a href="javascript:;" onClick="document.x.q.value='106';document.x.q.focus();">106</a></div>),
|
||||
),
|
||||
'1E2 / 2' => test_zci(
|
||||
'(1 * 10 ^ 2) / 2 = 50',
|
||||
heading => 'Calculator',
|
||||
html => qq(<div>(1 * 10<sup>2</sup>) / 2 = <a href="javascript:;" onClick="document.x.q.value='50';document.x.q.focus();">50</a></div>),
|
||||
),
|
||||
'2 / 1E2' => test_zci(
|
||||
'2 / (1 * 10 ^ 2) = 0.02',
|
||||
heading => 'Calculator',
|
||||
html => qq(<div>2 / (1 * 10<sup>2</sup>) = <a href="javascript:;" onClick="document.x.q.value='0.02';document.x.q.focus();">0.02</a></div>),
|
||||
),
|
||||
'424334+2253828' => test_zci(
|
||||
'424334 + 2253828 = 2,678,162',
|
||||
heading => 'Calculator',
|
||||
html => qq(<div>424334 + 2253828 = <a href="javascript:;" onClick="document.x.q.value='2,678,162';document.x.q.focus();">2,678,162</a></div>),
|
||||
),
|
||||
'4.243,34+22.538,28' => test_zci(
|
||||
'4.243,34 + 22.538,28 = 26.781,62',
|
||||
heading => 'Calculator',
|
||||
html =>
|
||||
qq(<div>4.243,34 + 22.538,28 = <a href="javascript:;" onClick="document.x.q.value='26.781,62';document.x.q.focus();">26.781,62</a></div>),
|
||||
),
|
||||
'sin(1,0) + 1,05' => test_zci(
|
||||
'sin(1,0) + 1,05 = 1,8914709848079',
|
||||
heading => 'Calculator',
|
||||
html =>
|
||||
qq(<div>sin(1,0) + 1,05 = <a href="javascript:;" onClick="document.x.q.value='1,8914709848079';document.x.q.focus();">1,8914709848079</a></div>),
|
||||
),
|
||||
'21 + 15 x 0 + 5' => test_zci(
|
||||
'21 + 15 x 0 + 5 = 26',
|
||||
heading => 'Calculator',
|
||||
html => qq(<div>21 + 15 x 0 + 5 = <a href="javascript:;" onClick="document.x.q.value='26';document.x.q.focus();">26</a></div>),
|
||||
),
|
||||
'0.8158 - 0.8157' => test_zci(
|
||||
'0.8158 - 0.8157 = 0.0001',
|
||||
heading => 'Calculator',
|
||||
html => qq(<div>0.8158 - 0.8157 = <a href="javascript:;" onClick="document.x.q.value='0.0001';document.x.q.focus();">0.0001</a></div>),
|
||||
),
|
||||
'2,90 + 4,6' => test_zci(
|
||||
'2,90 + 4,6 = 7,50',
|
||||
heading => 'Calculator',
|
||||
html => qq(<div>2,90 + 4,6 = <a href="javascript:;" onClick="document.x.q.value='7,50';document.x.q.focus();">7,50</a></div>),
|
||||
),
|
||||
'2,90 + sec(4,6)' => test_zci(
|
||||
'2,90 + sec(4,6) = -6,01642861135959',
|
||||
heading => 'Calculator',
|
||||
html =>
|
||||
qq(<div>2,90 + sec(4,6) = <a href="javascript:;" onClick="document.x.q.value='-6,01642861135959';document.x.q.focus();">-6,01642861135959</a></div>),
|
||||
),
|
||||
'100 - 96.54' => test_zci(
|
||||
'100 - 96.54 = 3.46',
|
||||
heading => 'Calculator',
|
||||
html => qq(<div>100 - 96.54 = <a href="javascript:;" onClick="document.x.q.value='3.46';document.x.q.focus();">3.46</a></div>),
|
||||
),
|
||||
'1. + 1.' => test_zci(
|
||||
'1. + 1. = 2',
|
||||
heading => 'Calculator',
|
||||
html => qq(<div>1. + 1. = <a href="javascript:;" onClick="document.x.q.value='2';document.x.q.focus();">2</a></div>),
|
||||
),
|
||||
'sin(1.0) + 1,05' => undef,
|
||||
'4,24,334+22,53,828' => undef,
|
||||
'5234534.34.54+1' => undef,
|
||||
'//' => undef,
|
||||
dividedbydividedby => undef,
|
||||
);
|
||||
|
||||
done_testing;
|
||||
|
||||
|
|
Loading…
Reference in New Issue