Create new instant answer for combinations and permutations

master
Collin Richards 2015-01-21 05:52:15 -05:00
parent 71dedeac11
commit 3798fb6dab
2 changed files with 147 additions and 0 deletions

View File

@ -0,0 +1,90 @@
package DDG::Goodie::Combination;
# ABSTRACT: Compute combinations and permutations
# Start at https://duck.co/duckduckhack/goodie_overview if you are new
# to instant answer development
use DDG::Goodie;
with 'DDG::GoodieRole::NumberStyler';
zci answer_type => "combination";
zci is_cached => 1;
name "Combination";
description "Computes combinations and permutations.";
primary_example_queries "10 choose 3", "25 permute 16";
secondary_example_queries "16 permutation 3";
category "calculations";
topics "math";
code_url "https://github.com/duckduckgo/zeroclickinfo-goodies/blob/master/lib/DDG/Goodie/Combination.pm";
attribution github => ["richardscollin", "Collin Richards"];
triggers any => "any", "choose", "permute", "permutation";
my $number_re = number_style_regex();
# Handle statement
handle query => sub {
my $query = $_;
return unless /^($number_re) (choose|permute|permutation) ($number_re)$/i;
my $style = number_style_for($1,$3);
return unless $style; #Cannot determine number format
my $operation = lc $2;
if ($operation eq 'permutation') {
$operation = 'permute';#standardizes output for tests
}
my $p1 = $style->for_computation($1);
my $p2 = $style->for_computation($3);
#Ensure both are non-negative integers
return unless $p1 =~ /^\d+$/ && $p2 =~ /^\d+$/;
#Do not try to calculate undefined combinations
return unless $p1 >= $p2;
my $result;
my %struct_ans;
if ('choose' eq $operation) {
$result = $style->for_display(choose($p1, $p2));
} else { #must be permute
$result = $style->for_display(permute($p1, $p2));
}
return $result,
structured_answer => {
input => [$style->for_display($p1) . " $operation " . $style->for_display($p2)],
operation => $operation,
result => $style->with_html($result),
};
};
#Computes the cumulative product of the numbers from $_[0] to $_[1] inclusive
#Do not call when first parameter is greater then second.
sub cumprod {
my $acc = 1;
for (my $i = $_[0]; $i <= $_[1]; $i++) {
$acc *= $i;
}
return $acc;
}
sub choose {
my ($n, $k) = @_;
#optimiztion combination is semetric
my $diff = $n - $k;
if ($k > $diff) {
$k = $diff;
}
return cumprod($n - $k + 1, $n) / cumprod(1, $k);
}
sub permute {
my ($n, $k) = @_;
return cumprod($n - $k + 1, $n);
}
1;

57
t/Combination.t Normal file
View File

@ -0,0 +1,57 @@
#!/usr/bin/env perl
use strict;
use warnings;
use Test::More;
use DDG::Test::Goodie;
zci answer_type => "combination";
zci is_cached => 1;
ddg_goodie_test(
[qw( DDG::Goodie::Combination )],
# At a minimum, be sure to include tests for all:
# - primary_example_queries
# - secondary_example_queries
'10 choose 3' => test_zci('120',
structured_answer => {
input => ['10 choose 3'],
operation => 'choose',
result => '120',
}
),
'25 permute 16' => test_zci('4.27447366714368 * 10^19',
structured_answer => {
input => ["25 permute 16"],
operation => "permute",
result => "4.27447366714368 * 10<sup>19</sup>"
}
),
'16 permutation 3' => test_zci('3,360',
structured_answer => {
input => ["16 permute 3"],
operation => "permute",
result => "3,360"
}
),
'15 permutation 0' => test_zci('1',
structured_answer => {
input => ["15 permute 0"],
operation => "permute",
result => "1"
}
),
'1,000 choose 2' => test_zci('499,500',
structured_answer => {
input => ["1,000 choose 2"],
operation => "choose",
result => "499,500"
}
),
'0 choose 100' => undef,
'10 choose 100' => undef,
'10.5 choose 1' => undef,
'1.000,5 choose 2' => undef,
);
done_testing;