Calculator: Various Fixes (#4349)
* Fix weird error message. * Stop algebra overtriggering. * Fix overflow error. * Fixed tests. * Conclude big patch. * Patch review bugs.master
parent
588c354075
commit
9c581d9564
|
@ -14,13 +14,14 @@ my $calc_regex = qr/^(free)?\s?(online)?\s?calc(ulator)?\s?(online)?\s?(free)?$/
|
|||
triggers query => $calc_regex;
|
||||
|
||||
triggers query => qr'^
|
||||
(?: [0-9 () τ π e √ x × ∙ ⋅ * + \- ÷ / \^ \$ £ € \. \, _ ! = % ]+ |
|
||||
(?: [0-9 () τ π e √ x × ∙ ⋅ * + \- ÷ / \^ \$ £ € \. \, _ ! = % °]+ |
|
||||
\d+\%=?$ |
|
||||
what\sis| calculat(e|or) | solve | math | log\sof | fact(?:orial?)?(\s+of)? |
|
||||
times | mult | multiply | divided\sby | plus | minus | cos | tau |
|
||||
sin | tan | cotan | log | ln | exp | tanh |
|
||||
sin | sinh | tan | log | ln | exp | tanh | arctan | atan | arccos | acos | asin | arcsin | cosh |
|
||||
deg(?:rees?)? | rad(?:ians?)? |
|
||||
sec | csc | squared | sqrt | \d+\s?mod(?:ulo)?\s?\d+ | gross | dozen | pi |
|
||||
squared | sqrt | cbrt | \d+\s?mod(?:ulo)?\s?\d+ | dozen | pi |
|
||||
(?:cub(?:ed?|ic)|sq(?:uare)?)\s?r(?:oo)?t(?:\sof)? | cubed? |
|
||||
score){2,}$
|
||||
'xi;
|
||||
|
||||
|
@ -28,7 +29,6 @@ my $number_re = number_style_regex();
|
|||
|
||||
my %named_operations = (
|
||||
'\^' => '**',
|
||||
'x' => '*',
|
||||
'×' => '*',
|
||||
'∙' => '*',
|
||||
'⋅' => '*', # Can be mistaken for dot operator
|
||||
|
@ -46,7 +46,6 @@ my %named_operations = (
|
|||
|
||||
my %named_constants = (
|
||||
dozen => 12,
|
||||
gross => 144,
|
||||
score => 20,
|
||||
);
|
||||
|
||||
|
@ -91,9 +90,9 @@ sub spacing {
|
|||
my ($text, $space_for_parse) = @_;
|
||||
|
||||
$text =~ s/\s{2,}/ /g;
|
||||
$text =~ s/(\s*(?<!<)(?:[\+\^xX×∙⋅\*\/÷]|(?<!\de)\-|times|plus|minus|divided\s*by)+\s*)/ $1 /ig;
|
||||
$text =~ s/(\s*(?<!<)(?:[\+\^∙⋅\*\/÷]|(?<!\de)\-|times|plus|minus|divided\s*by)+\s*)/ $1 /ig;
|
||||
$text =~ s/\s*dividedby\s*/ divided by /ig;
|
||||
$text =~ s/(\d+?)((?:dozen|pi|gross|squared|score))/$1 $2/ig;
|
||||
$text =~ s/(\d+?)((?:dozen|pi|squared|score))/$1 $2/ig;
|
||||
$text =~ s/([\(\)])/ $1 /g if $space_for_parse;
|
||||
$text =~ s/\|/,/g;
|
||||
$text =~ s/\s{2,}/ /g;
|
||||
|
@ -110,7 +109,7 @@ sub rewriteQuery {
|
|||
$text =~ s/minus/-/g;
|
||||
$text =~ s/times|mult/×/g;
|
||||
$text =~ s/divided\s?by/÷/g;
|
||||
$text =~ s/(cos|tau|τ|sin|tan|cotan|log|ln|exp|tanh|π|sec|csc|squared|sqrt|gross|dozen|pi|e|score)\s*\1/$1/g;
|
||||
$text =~ s/(a?cos|tau|τ|a?sinh?|a?tan|log|ln|exp|tanh|π|squared|sqrt|dozen|pi|e|score)\s*\1/$1/g;
|
||||
$text =~ s|([x × ∙ ⋅ % + ÷ / \^ \$ £ € \. \, _ =])\s*\1|$1|gx;
|
||||
|
||||
return $text;
|
||||
|
@ -128,6 +127,18 @@ sub rewriteFunctions {
|
|||
$query =~ s/fact\(?(\d+)\)?/$1!/;
|
||||
$query =~ s/fact(?:orial?)?(?:\s+of)?\s+?(\(?\d+\)?)?/$1!/;
|
||||
|
||||
$query =~ s/exp\s?\(?(\d+)\)?/exp($1)/i;
|
||||
|
||||
$query =~ s/cube\s?\((.+)\)/cube($1)/gi;
|
||||
$query =~ s/cube\s?(\d+|e|pi|tau)/cube($1)/gi;
|
||||
$query =~ s/(\d+|e|pi|tau)\scubed/cube($1)/gi;
|
||||
|
||||
# Preprocesses cbrt / sqrt
|
||||
$query =~ s/(?:cu(?:be)?d?|cubic)\s?r(?:oo)?t(?:\sof)?/cbrt/ig;
|
||||
$query =~ s/sq(?:uare)?\s?r(?:oo)?t(?:\sof)?/sqrt/ig;
|
||||
$query =~ s/(cbrt|sqrt)\s?\((.+)\)/$1($2)/i;
|
||||
$query =~ s/(cbrt|sqrt)\s?(\d+|e|pi|tau)/$1($2)/i;
|
||||
|
||||
# Preprocesses Log/Ln
|
||||
$query =~ s/log\sof\s(\d+)/log($1)/i;
|
||||
$query =~ s/log10\((\d+)\)/log($1)/i;
|
||||
|
@ -138,10 +149,13 @@ sub rewriteFunctions {
|
|||
$query =~ s/ln\s?(\d+)/ln($1)/i;
|
||||
|
||||
# Preprocesses Trig functions
|
||||
$query =~ s/arctan/atan/ig;
|
||||
$query =~ s/arccos/acos/ig;
|
||||
$query =~ s/arcsin/asin/ig;
|
||||
$query =~ s/(deg)rees?/$1/ig;
|
||||
$query =~ s/rad(?:ians?)?//ig;
|
||||
|
||||
$query =~ s/(sin|cos|tan)\s?(\d+(?: deg)?)/$1($2)/ig;
|
||||
$query =~ s/(a?sinh?|a?cosh?|a?tanh?|tan)\s?(\d+(?: deg)?)/$1($2)/ig;
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
@ -163,9 +177,10 @@ handle query => sub {
|
|||
};
|
||||
}
|
||||
|
||||
# Help support the query if there is degrees involed
|
||||
$query =~ s/°/ deg/gi;
|
||||
return if ( m/deg(rees?)?|°/i && m/rad(ians?)?/); # we don't support a mix of degrees and radians in the same query
|
||||
$query = rewriteFunctions($query);
|
||||
|
||||
# throw out obvious non-calculations immediately
|
||||
return if $query =~ qr/(\$(.+)?(?=£|€))|(£(.+)?(?=\$|€))|(€(.+)?(?=\$|£))/; # only let one currency type through
|
||||
return if $req->query_lc =~ /^0x/i; # hex maybe?
|
||||
|
@ -175,6 +190,8 @@ handle query => sub {
|
|||
return if $query =~ m/^(\+?\d{1,2}(\s|-)?|\(\d{2})?\(?\d{3,4}\)?(\s|-)?\d{3}(\s|-)?\d{3,4}(\s?x\d+)?$/; # Probably are searching for a phone number, not making a calculation
|
||||
return if $query =~ m/(\d+)\s+(\d+)/; # if spaces between numbers then bail
|
||||
return if $query =~ m/^\)|\($/; # shouldn't open with a closing brace or finish with an opening brace
|
||||
return if $query =~ m/(a?cosh?|tau|a?sin|a?tan|log|ln|exp|tanh|cbrt|cubed?)e?$/i; # stops empty functions at end or with <func>e
|
||||
return if $query =~ m#(?:x(\^|/)|(\^|/)x)#; # stops triggering on what is most likely algebra
|
||||
|
||||
# some shallow preprocessing of the query
|
||||
$query =~ s/^(?:what is|calculat(e|or)|solve|math)//i;
|
||||
|
@ -187,11 +204,14 @@ handle query => sub {
|
|||
return if $query =~ m{[x × ∙ ⋅ * + \- ÷ / \^ \$ £ € \. ,]{3,}}ix;
|
||||
return if $query =~ m/\$[^\d\.]/;
|
||||
return if $query =~ m/\(\)/;
|
||||
return if $query =~ m/^e\d+/;
|
||||
return if $query =~ m#(\+|-|\*|/|x|\^)\)#xi; # can't have an operator before a closing paren
|
||||
return if $query =~ m{//};
|
||||
return if $query =~ m/(^|[^\d])!/g;
|
||||
return if $query =~ m/0x[A-Za-z]{2,}/;
|
||||
return if $query =~ m/X\d+/;
|
||||
return if $query =~ m/9\/11/; # date edge case
|
||||
return if $query =~ m/\d+e\+\d+/;
|
||||
return if $query =~ m{(?:7|9)/11}; # date edge case, US supermarket
|
||||
return if $query =~ m/.+=.+/; # check there isn't something on both sides of the equals sign
|
||||
return if $query =~ /^(?:minus|-|\+)\d+$/;
|
||||
|
||||
|
@ -202,8 +222,8 @@ handle query => sub {
|
|||
while (my ($name, $operation) = each %named_operations) {
|
||||
$query =~ s#$name#$operation#xig; # We want these ones to show later.
|
||||
}
|
||||
|
||||
return if ($tmp_expr eq $query) && ($query !~ /\de|fact|cos|tan|sin|log|ln|mod|!/i); # If it didn't get spaced out, there are no operations to be done.
|
||||
$query =~ s/(?<!e)x(?!p)/\*/ig; # stops the x in exp being converted to *
|
||||
return if ($tmp_expr eq $query) && ($query !~ /\de|fact|a?cos|a?tan|sin|cbrt|cubed?|log|ln|exp|mod|!/i); # If it didn't get spaced out, there are no operations to be done.
|
||||
|
||||
# Now sub in constants
|
||||
while (my ($name, $constant) = each %named_constants) {
|
||||
|
@ -214,7 +234,7 @@ handle query => sub {
|
|||
return unless $style;
|
||||
|
||||
my $spaced_query = prepare_for_frontend($query, $style);
|
||||
return if scalar(split(" ", $spaced_query)) < 2 && ($spaced_query !~ /^(fact|csc|sec|sqrt|cos|tan|sin|log|ln|mod|modulo|\d+\!$)/i);
|
||||
return if scalar(split(" ", $spaced_query)) < 2 && ($spaced_query !~ /^(fact|cubed?|cbrt|sqrt|exp|a?cosh?|a?tan|a?sin|log|ln|mod|modulo|\d+\!$)/i);
|
||||
|
||||
return '', structured_answer => {
|
||||
data => {
|
||||
|
|
|
@ -105,12 +105,12 @@ DDH.calculator = DDH.calculator || {};
|
|||
.replace(/<sup>2<\/sup>/g, '^2')
|
||||
.replace(/<sup>3<\/sup>/g, '^3')
|
||||
.replace(/<sup>(((-?(\d*.)?(\d+))|([πe(log|ln\(\d+\))]))+)<\/sup>/g, RewriteExpression.exponent)
|
||||
.replace(/(EE) (\d+(\.\d{1,})?)/g, RewriteExpression.ee)
|
||||
.replace(/(EE) ([^)]+)/g, RewriteExpression.ee)
|
||||
|
||||
// 5. handles scientific calculation functions
|
||||
.replace(/log(?:\(([^),]+)\)|\s(\d+))/g, RewriteExpression.log10)
|
||||
.replace(/ln\(?([^)]+)\)?/g, RewriteExpression.log)
|
||||
.replace(/(sin|cos|tan)\(?([^)]+)\)?/g, RewriteExpression.trig)
|
||||
.replace(/(a?sinh?|a?cosh?|a?tanh?)\(?([^)]+)\)?/g, RewriteExpression.trig)
|
||||
.replace(/(\d+)\s?mod(?:ulo)?\s?(\d+)?/g, 'mod($1,$2)')
|
||||
|
||||
// 6. handles constants
|
||||
|
@ -458,15 +458,17 @@ DDH.calculator = DDH.calculator || {};
|
|||
try {
|
||||
var total = math.eval(
|
||||
normalizedExpression
|
||||
).toString()
|
||||
).toFixed(11)
|
||||
total = parseFloat(total).toString();
|
||||
var tree = math.parse(normalizedExpression);
|
||||
var parsed_expression = tree.toString({parenthesis: 'all'});
|
||||
// remove rounding from expression
|
||||
parsed_expression = parsed_expression.replace(/(round\((.+)\), 11)/, '$2');
|
||||
} catch(err) {
|
||||
if(!expressionFromSearchBar) {
|
||||
display.value = "Error";
|
||||
ExpressionParser.setExpression();
|
||||
setCButtonState("C");
|
||||
return false;
|
||||
} else {
|
||||
display.value = "";
|
||||
evaluated = true;
|
||||
|
@ -476,9 +478,10 @@ DDH.calculator = DDH.calculator || {};
|
|||
'calculator', {
|
||||
q: DDG.get_query_encoded()
|
||||
});
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
ExpressionParser.setExpression(parsed_expression);
|
||||
evaluated = true;
|
||||
setCButtonState("C");
|
||||
|
@ -497,6 +500,7 @@ DDH.calculator = DDH.calculator || {};
|
|||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Clear
|
||||
*
|
||||
|
@ -735,6 +739,10 @@ DDH.calculator = DDH.calculator || {};
|
|||
var expression = display.value.split(" ");
|
||||
if(expression[expression.length-1].indexOf(".") > -1) { return false; }
|
||||
}
|
||||
// if it's an operator, we'll leave the yRootState
|
||||
if(yRootState === true && Utils.isOperand(element)) {
|
||||
yRootState = false;
|
||||
}
|
||||
|
||||
// if element is math function or square root, increment paren total
|
||||
if(Utils.isMathFunction(element) || element === "√(") {
|
||||
|
@ -994,6 +1002,7 @@ DDH.calculator = DDH.calculator || {};
|
|||
calculator(evt);
|
||||
setFocus();
|
||||
e.stopImmediatePropagation();
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
$calcInputTrap.keydown(function(e){
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
<div class="tile__tabs">
|
||||
|
||||
<!-- scientific calculations -->
|
||||
<!-- scientific keyboard -->
|
||||
<div class="tile__calc__col tile__tab__sci">
|
||||
<span class="tile__ctrl__toggle">
|
||||
<span>RAD</span>
|
||||
|
@ -27,54 +27,54 @@
|
|||
</label>
|
||||
<span>DEG</span>
|
||||
</span>
|
||||
<button class="tile__ctrl__btn" data-cmd="META_PAR_OPEN" value="(">(</button>
|
||||
<button class="tile__ctrl__btn" data-cmd="META_PAR_CLOSE" value=")">)</button>
|
||||
<button class="tile__ctrl__btn" value="(">(</button>
|
||||
<button class="tile__ctrl__btn" value=")">)</button>
|
||||
|
||||
<button class="tile__ctrl__btn" data-cmd="FN_SIN" value="sin(">sin</button>
|
||||
<button class="tile__ctrl__btn" data-cmd="FN_COS" value="cos(">cos</button>
|
||||
<button class="tile__ctrl__btn" data-cmd="FN_TAN" value="tan(">tan</button>
|
||||
<button class="tile__ctrl__btn" data-cmd="CONST_PI" value="π">π</button>
|
||||
<button class="tile__ctrl__btn" value="sin(">sin</button>
|
||||
<button class="tile__ctrl__btn" value="cos(">cos</button>
|
||||
<button class="tile__ctrl__btn" value="tan(">tan</button>
|
||||
<button class="tile__ctrl__btn" value="π">π</button>
|
||||
|
||||
<button class="tile__ctrl__btn" data-cmd="FN_FACT" value="!">x!</button>
|
||||
<button class="tile__ctrl__btn" data-cmd="FN_POW_2" value="<sup>2</sup>">x<sup>2</sup></button>
|
||||
<button class="tile__ctrl__btn" data-cmd="FN_POW_3" value="<sup>3</sup>">x<sup>3</sup></button>
|
||||
<button class="tile__ctrl__btn" data-cmd="FN_POW_N" value="<sup>□</sup>">x<sup>y</sup></button>
|
||||
<button class="tile__ctrl__btn" value="!">x!</button>
|
||||
<button class="tile__ctrl__btn" value="<sup>2</sup>">x<sup>2</sup></button>
|
||||
<button class="tile__ctrl__btn" value="<sup>3</sup>">x<sup>3</sup></button>
|
||||
<button class="tile__ctrl__btn" value="<sup>□</sup>">x<sup>y</sup></button>
|
||||
|
||||
<button class="tile__ctrl__btn" data-cmd="FN_DIV_1" value="1/(">1/x</button>
|
||||
<button class="tile__ctrl__btn" data-cmd="FN_SQRT" value="√(">√x</button>
|
||||
<button class="tile__ctrl__btn" data-cmd="FN_SQRT_N" value="<sup>□</sup>√"><sup>x</sup>√y</button>
|
||||
<button class="tile__ctrl__btn" data-cmd="FN_EE" value="EE">EE</button>
|
||||
<button class="tile__ctrl__btn" value="1/(">1/x</button>
|
||||
<button class="tile__ctrl__btn" value="√(">√x</button>
|
||||
<button class="tile__ctrl__btn" value="<sup>□</sup>√"><sup>x</sup>√y</button>
|
||||
<button class="tile__ctrl__btn" value="EE">EE</button>
|
||||
|
||||
<button class="tile__ctrl__btn" data-cmd="FN_LOG" value="log(">log</button>
|
||||
<button class="tile__ctrl__btn" data-cmd="FN_LN" value="ln(">ln</button>
|
||||
<button class="tile__ctrl__btn" data-cmd="FN_POW_E" value="e<sup>□</sup>">e<sup>x</sup></button>
|
||||
<button class="tile__ctrl__btn" data-cmd="CONST_E" value="e">e</button>
|
||||
<button class="tile__ctrl__btn" value="log(">log</button>
|
||||
<button class="tile__ctrl__btn" value="ln(">ln</button>
|
||||
<button class="tile__ctrl__btn" value="e<sup>□</sup>">e<sup>x</sup></button>
|
||||
<button class="tile__ctrl__btn" value="e">e</button>
|
||||
</div>
|
||||
|
||||
<!-- basic calculations -->
|
||||
<!-- numeric keyboard -->
|
||||
<div class="tile__calc__col tile__tab__basic">
|
||||
<button class="tile__ctrl__btn tile__ctrl--double" id="clear_button" data-cmd="META_CLEAR" value="C">C</button>
|
||||
<button class="tile__ctrl__btn tile__ctrl--ops" data-cmd="OP_PCT" value="%">%</button>
|
||||
<button class="tile__ctrl__btn tile__ctrl--important" data-cmd="OP_DIV" value="÷">÷</button>
|
||||
<button class="tile__ctrl__btn tile__ctrl--double" id="clear_button" value="C">C</button>
|
||||
<button class="tile__ctrl__btn tile__ctrl--ops" value="%">%</button>
|
||||
<button class="tile__ctrl__btn tile__ctrl--important" value="÷">÷</button>
|
||||
|
||||
<button class="tile__ctrl__btn" value="7">7</button>
|
||||
<button class="tile__ctrl__btn" value="8">8</button>
|
||||
<button class="tile__ctrl__btn" value="9">9</button>
|
||||
<button class="tile__ctrl__btn tile__ctrl--important" data-cmd="OP_MULT" value="×">×</button>
|
||||
<button class="tile__ctrl__btn tile__ctrl--important" value="×">×</button>
|
||||
|
||||
<button class="tile__ctrl__btn" value="4">4</button>
|
||||
<button class="tile__ctrl__btn" value="5">5</button>
|
||||
<button class="tile__ctrl__btn" value="6">6</button>
|
||||
<button class="tile__ctrl__btn tile__ctrl--important" data-cmd="OP_MINUS" value="-">−</button>
|
||||
<button class="tile__ctrl__btn tile__ctrl--important" value="-">−</button>
|
||||
|
||||
<button class="tile__ctrl__btn" value="1">1</button>
|
||||
<button class="tile__ctrl__btn" value="2">2</button>
|
||||
<button class="tile__ctrl__btn" value="3">3</button>
|
||||
<button class="tile__ctrl__btn tile__ctrl--important" data-cmd="OP_PLUS" value="+">+</button>
|
||||
<button class="tile__ctrl__btn tile__ctrl--important" value="+">+</button>
|
||||
|
||||
<button class="tile__ctrl__btn tile__ctrl--double" value="0">0</button>
|
||||
<button class="tile__ctrl__btn" value=".">.</button>
|
||||
<button class="tile__ctrl__btn tile__ctrl--important" data-cmd="META_PROCEED" value="=">=</button>
|
||||
<button class="tile__ctrl__btn tile__ctrl--important" value="=">=</button>
|
||||
</div>
|
||||
|
||||
<!-- the ledger (history) section of the calculators UI -->
|
||||
|
@ -82,4 +82,4 @@
|
|||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -90,12 +90,6 @@ ddg_goodie_test(
|
|||
'sin(1)' => build_test(
|
||||
'sin(1)'
|
||||
),
|
||||
'csc(1)' => build_test(
|
||||
'csc(1)'
|
||||
),
|
||||
'sec(1)' => build_test(
|
||||
'sec(1)'
|
||||
),
|
||||
'$3.43+$34.45' => build_test(
|
||||
'$3.43 + $34.45'
|
||||
),
|
||||
|
@ -147,9 +141,6 @@ ddg_goodie_test(
|
|||
'2,90 + 4,6' => build_test(
|
||||
'2.90 + 4.6'
|
||||
),
|
||||
'2,90 + sec(4,6)' => build_test(
|
||||
'2.90 + sec(4.6)'
|
||||
),
|
||||
'100 - 96.54' => build_test(
|
||||
'100 - 96.54'
|
||||
),
|
||||
|
@ -422,8 +413,43 @@ ddg_goodie_test(
|
|||
'cos(103*232+22)+2' => build_test(
|
||||
'cos(103 * 232 + 22) + 2'
|
||||
),
|
||||
'cos(103*232+22)+2' => build_test(
|
||||
'cos(103 * 232 + 22) + 2'
|
||||
),
|
||||
'square root of 25' => build_test(
|
||||
'sqrt(25)'
|
||||
),
|
||||
'77 * square root 25 + 2' => build_test(
|
||||
'77 * sqrt(25) + 2'
|
||||
),
|
||||
'77 * cube rt 25 + 2' => build_test(
|
||||
'77 * cbrt(25) + 2'
|
||||
),
|
||||
'cubic rt of 90' => build_test(
|
||||
'cbrt(90)'
|
||||
),
|
||||
'99 cubed' => build_test(
|
||||
'cube(99)'
|
||||
),
|
||||
'2 + cube 66 + 2' => build_test(
|
||||
'2 + cube(66) + 2'
|
||||
),
|
||||
|
||||
|
||||
'e2e4' => undef,
|
||||
'cosh(4+-)' => undef,
|
||||
'232 * 2 cube' => undef, # /cube/ can't be at end, only /cubed/
|
||||
'sine' => undef,
|
||||
'loge' => undef,
|
||||
'lne' => undef, # a search with high click through
|
||||
'cose' => undef,
|
||||
'tane' => undef,
|
||||
'1e+6' => undef,
|
||||
'7/11' => undef, # an ambiguity. Probably looking for US store
|
||||
'sec^2-1' => undef,
|
||||
'(x^2-1)*(5x^4+2x+1)' => undef,
|
||||
'88y * 1312' => undef,
|
||||
'23x+3x' => undef,
|
||||
'3a / 8b' => undef,
|
||||
'tan of 88 degrees and radians' => undef,
|
||||
'sin 88 degrees + sin 10 radians' => undef,
|
||||
'1432 / 28 2' => undef,
|
||||
|
|
Loading…
Reference in New Issue