2014-07-23 13:14:40 -07:00
package DDG::Goodie::CalcRoots ;
2014-08-20 11:45:33 -07:00
# ABSTRACT: compute the n-th root of a number
2014-07-23 13:14:40 -07:00
2015-02-22 12:09:29 -08:00
use strict ;
2014-07-23 13:14:40 -07:00
use DDG::Goodie ;
use Lingua::EN::Numericalize ;
2014-07-25 13:46:33 -07:00
primary_example_queries 'square root of 9' ;
description 'calculate the nth root' ;
name 'CalcRoots' ;
code_url 'https://github.com/duckduckgo/zeroclickinfo-goodie/blob/master/lib/DDG/Goodie/CalcRoots.pm' ;
attribution github = > [ 'https://github.com/duckduckgo' , 'duckduckgo' ] ;
category 'calculations' ;
topics 'math' ;
2014-07-23 13:14:40 -07:00
triggers any = > 'root' ;
zci is_cached = > 1 ;
zci answer_type = > 'root' ;
handle query = > sub {
2014-08-18 14:53:55 -07:00
# The 'root' trigger is very ambigous so this regex provides the specific triggers
return unless m/^((?:.*square|.*cube(?:d|)|.*th|.*rd|.*nd|.*st|.*[0-9]+)) root(?: of|) (?!of)(.*)/i ;
2014-10-28 11:36:15 -07:00
2014-08-18 14:53:55 -07:00
# Common phrases that won't be caught by str2nbr()
my % function = (
'square' = > 2 ,
'cubed' = > 3 ,
'cube' = > 3 ,
) ;
2014-10-28 11:36:15 -07:00
2014-08-18 14:53:55 -07:00
# Seperate the exponent and base courtesy of the above regex
my $ exp = $ 1 ;
my $ base = $ 2 ;
2015-02-28 07:44:47 -08:00
my $ sign = "" ;
2014-10-28 11:36:15 -07:00
# Figure out what number the exponent is
2014-08-18 14:53:55 -07:00
if ( $ exp =~ m/negative\s|minus\s|\A-/i ) {
2015-02-28 07:44:47 -08:00
$ sign = "-" ;
$ exp = $ function { $' } ? $ function { $' } : str2nbr ( $' ) ;
2014-08-18 14:53:55 -07:00
}
else {
$ exp = $ function { $ exp } ? $ function { $ exp } : str2nbr ( $ exp ) ;
}
# You can't take a zeroeth root
return if $ exp == 0 ;
# There are separate cases here.
# 1. Negative base and even exponent (imaginary numbers)
# 2. Negative base and odd exponenet
# 3. Positive base
2014-10-28 11:36:15 -07:00
2014-08-18 14:53:55 -07:00
if ( $ base =~ m/negative\s|minus\s|\A-/i && $ exp % 2 == 0 ) {
2014-10-28 11:36:15 -07:00
2014-08-18 14:53:55 -07:00
# Figure out what number the base is
$ base = $' ;
$ base = str2nbr ( $ base ) if $ base =~ /[^0-9]/ ;
2014-10-28 11:36:15 -07:00
2014-08-18 14:53:55 -07:00
# Solve using the absolute value of the base
$ base = abs ( $ base ) ;
my $ calc = $ base ** ( 1 / $ exp ) ;
2015-02-28 07:44:47 -08:00
if ( $ sign eq "-" ) { $ calc = - $ calc ; }
2014-10-28 11:36:15 -07:00
2014-08-18 14:53:55 -07:00
# If the result is a whole number (n), the answer is n*i
if ( ( $ calc - int ( $ calc ) ) == 0 ) {
2015-02-28 07:44:47 -08:00
return structured ( $ sign . $ exp , "-$base" , "The $sign$exp-root of -$base is $calc i" , "$sign<sup>$exp</sup>√-$base = $calc<em>i</em>" ) ;
2014-08-18 14:53:55 -07:00
}
# Try and simplify the radical
2015-02-28 07:44:47 -08:00
my $ count = int ( abs ( $ calc ) ) ;
2014-08-18 14:53:55 -07:00
while ( $ count > 1 ) {
2014-10-28 11:36:15 -07:00
2014-08-18 14:53:55 -07:00
# See if the current number raised to the given exponent is a factor of our base. If it is, the answer is n * i * exponent-root(the other factor)
my $ newBase = $ base / ( $ count ** $ exp ) ;
if ( ( $ newBase - int ( $ newBase ) ) == 0 ) {
2015-02-28 07:44:47 -08:00
return structured ( $ sign . $ exp , "-$base" , "The $sign$exp-root of -$base is $count * i * the $sign$exp-root of $newBase." , "$sign<sup>$exp</sup>√-$base = $sign$count<em>i</em>⋅<sup>$exp</sup>√$newBase" ) ;
2014-08-18 14:53:55 -07:00
}
$ count - - ;
}
# Can't be solved or simplified via the above methods
2015-02-28 07:44:47 -08:00
return structured ( $ sign . $ exp , "-$base" , "The $sign$exp-root of -$base is i * the $sign$exp-root of $base" , "$sign<sup>$exp</sup>√-$base = $sign<em>i</em>⋅<sup>$exp</sup>√$base" ) ;
2014-08-18 14:53:55 -07:00
}
elsif ( $ base =~ m/negative\s|minus\s|\A-/i && $ exp % 2 != 0 ) {
2014-10-28 11:36:15 -07:00
2014-08-18 14:53:55 -07:00
# Solve normally
$ base = $' ;
$ base = str2nbr ( $ base ) if $ base =~ m/[^0-9]/ ;
$ base =~ s/[^0-9\.]//g ;
if ( $ base ne '' ) {
2014-10-28 11:36:15 -07:00
2014-08-18 14:53:55 -07:00
my $ calc = $ base ** ( 1 / $ exp ) * - 1 ;
2015-02-28 07:44:47 -08:00
if ( $ sign eq "-" ) { $ calc = - $ calc ; }
my $ secondsign = "-" ; #sign of the calcuated answer
if ( $ calc > 0 ) { $ secondsign = "" ; }
# Try and simplify the radical if answer is not a whole number
unless ( ( $ calc - int ( $ calc ) ) == 0 ) {
my $ count = int ( abs ( $ calc ) ) ;
2015-02-26 09:12:43 -08:00
while ( $ count > 1 ) {
2014-10-28 11:36:15 -07:00
2015-02-26 09:12:43 -08:00
# See if the current number raised to the given exponent is a factor of our base. If it is, we can give them a simplified version of the radical in addition to the answer.
my $ newBase = $ base / ( $ count ** $ exp ) ;
2014-10-28 11:36:15 -07:00
2015-02-26 09:12:43 -08:00
if ( ( $ newBase - int ( $ newBase ) ) == 0 ) {
2015-02-28 07:44:47 -08:00
return structured ( $ sign . $ exp , "-$base" , "The $sign$exp-root of -$base is $calc ($secondsign$count times the $sign$exp-root of $newBase)." , qq|$sign<sup>$exp</sup>√-$base = <a href="javascript:;" onclick="document.x.q.value='$calc';document.x.q.focus();">$calc</a> ($secondsign$count⋅<sup>$exp</sup>√$newBase)| ) ;
2015-02-26 09:12:43 -08:00
}
2014-08-18 14:53:55 -07:00
2015-02-26 09:12:43 -08:00
$ count - - ;
2015-02-28 07:44:47 -08:00
}
2014-08-18 14:53:55 -07:00
}
2015-02-28 07:44:47 -08:00
return structured ( $ sign . $ exp , "-$base" , "The $sign$exp-root of -$base is $calc." , qq|$sign<sup>$exp</sup>√-$base = <a href="javascript:;" onclick="document.x.q.value='$calc';document.x.q.focus();">$calc</a>| ) ;
2014-08-18 14:53:55 -07:00
}
}
elsif ( $ exp =~ m/[0-9]+/ ) {
2014-10-28 11:36:15 -07:00
2014-08-18 14:53:55 -07:00
# Solve normally
$ base = str2nbr ( $ base ) if $ base =~ m/[^0-9]/ ;
$ base =~ s/[^0-9\.]//g ;
if ( $ base ne '' ) {
my $ calc = $ base ** ( 1 / $ exp ) ;
2015-02-28 07:44:47 -08:00
if ( $ sign eq "-" ) { $ calc = - $ calc ; }
2015-02-26 09:12:43 -08:00
#If the answer is not a whole number, try to simplify the radical
2015-02-28 07:44:47 -08:00
unless ( ( $ calc - int ( $ calc ) ) == 0 ) {
my $ count = int ( abs ( $ calc ) ) ;
2015-02-26 09:12:43 -08:00
while ( $ count > 1 ) {
2014-10-28 11:36:15 -07:00
2015-02-26 09:12:43 -08:00
# See if the current number raised to the given exponent is a factor of our base. If it is, we can give them a simplified version of the radical in addition to the answer.
my $ newBase = $ base / ( $ count ** $ exp ) ;
2014-10-28 11:36:15 -07:00
2015-02-26 09:12:43 -08:00
if ( ( $ newBase - int ( $ newBase ) ) == 0 ) {
2015-02-28 07:44:47 -08:00
return structured ( $ sign . $ exp , $ base , "The $sign$exp-root of $base is $calc ($sign$count times the $exp-root of $newBase)." , qq|$sign<sup>$exp</sup>√$base = <a href="javascript:;" onclick="document.x.q.value='$calc';document.x.q.focus();">$calc</a> ($sign$count⋅<sup>$exp</sup>√$newBase)| ) ;
2015-02-26 09:12:43 -08:00
}
2014-08-18 14:53:55 -07:00
2015-02-26 09:12:43 -08:00
$ count - - ;
2014-08-18 14:53:55 -07:00
}
}
2015-02-28 07:44:47 -08:00
return structured ( $ sign . $ exp , $ base , "The $sign$exp-root of $base is $calc." , qq|$sign<sup>$exp</sup>√$base = <a href="javascript:;" onclick="document.x.q.value='$calc';document.x.q.focus();">$calc</a>| ) ;
2014-08-18 14:53:55 -07:00
}
}
2014-10-28 11:36:15 -07:00
return ;
2014-07-23 13:14:40 -07:00
} ;
2015-02-26 08:18:36 -08:00
sub structured {
my ( $ exp , $ base , $ text , $ html ) = @ _ ;
2015-02-26 09:12:43 -08:00
return $ text ,
heading = > "Root Calculator" ,
structured_answer = > {
input = > [ "$exp-root of $base" ] ,
operation = > 'Calculate' ,
result = > $ html ,
} ;
2015-02-26 08:18:36 -08:00
}
2015-02-28 07:44:47 -08:00
1 ;