From 96bfdab046c357996513c2c492c71a5816e332a5 Mon Sep 17 00:00:00 2001 From: Zaahir Moolla Date: Wed, 9 Aug 2017 12:46:46 -0400 Subject: [PATCH] Revert "Roman numerals (improvements) (#4402)" (#4412) This reverts commit 026279bb6a6aafcad461b60f1e3eda9ce5dc03dc. --- lib/DDG/Goodie/Roman.pm | 81 +++-------- share/goodie/roman/roman.css | 29 ---- share/goodie/roman/roman.handlebars | 11 -- share/goodie/roman/roman.js | 218 ---------------------------- t/Roman.t | 48 +++--- 5 files changed, 34 insertions(+), 353 deletions(-) delete mode 100644 share/goodie/roman/roman.css delete mode 100644 share/goodie/roman/roman.handlebars delete mode 100644 share/goodie/roman/roman.js diff --git a/lib/DDG/Goodie/Roman.pm b/lib/DDG/Goodie/Roman.pm index 89eaf20b3..befc04f0b 100755 --- a/lib/DDG/Goodie/Roman.pm +++ b/lib/DDG/Goodie/Roman.pm @@ -5,82 +5,35 @@ use strict; use DDG::Goodie; use Roman; -use List::Util qw/any/; use utf8; -triggers startend => "roman", "roman numeral", "roman numerals", "roman number", - "arabic", "arabic numeral", "arabic numerals", "arabic number"; +triggers any => "roman", "arabic"; zci is_cached => 1; zci answer_type => "roman_numeral_conversion"; -handle query => sub { - my ($query) = @_; +handle remainder => sub { + my $in = uc shift; + $in =~ s/(?:\s*|in|to|numerals?|number)//gi; - # By default, we convert from roman to arabic. - my $input = 'roman'; - my $input_value = ''; - my $output = 'arabic'; - my $output_value = ''; - - # These two lists are used to load the converter without any answer. - my @roman_to_arabic = ( - qr/^roman$/i, - qr/^convert\s+(?:into|to)\s+arabic\s*(numerals?)?$/i - ); - my @arabic_to_roman = ( - qr/^arabic$/i, - qr/^convert\s+(?:into|to)\s+roman\s*(numerals?)?$/i - ); - - # These two lists are used to load the converter with an answer. - my @roman_number_to_arabic = ( - qr/^convert\s+(\D+)\s+(?:into|in|to)\s*arabic\s*(numerals?)?/i, - qr/^roman\s+(?:numerals?)?\s*(\D+)$/i, - qr/^arabic\s+(?:numerals?)?\s*(\D+)$/i, - qr/^(\D+)\s+(?:into|in|to)?\s+arabic\s*(numerals?)?/i - ); - my @arabic_number_to_roman = ( - qr/^convert\s+(\d+)\s+(?:into|in|to)\s*roman\s*(numerals?)?/i, - qr/^roman\s+(?:numerals?)?\s*(\d+)$/i, - qr/^arabic\s+(?:numerals?)?\s*(\d+)$/i, - qr/^(\d+)\s+(?:into|in|to)?\s+roman\s*(numerals?)?/i - ); - - if (any { $query =~ $_ } @roman_to_arabic) { - # Default settings, nothing to do. - } elsif (any { $query =~ $_ } @arabic_to_roman) { - $input = 'arabic'; - $output = 'roman'; - } elsif (any { ($input_value) = $query =~ $_ } @roman_number_to_arabic) { - if (isroman $input_value) { - $input_value = uc $input_value; - $output_value = arabic $input_value; - } else { - $input_value = ''; - } - } elsif (any { ($input_value) = $query =~ $_ } @arabic_number_to_roman) { - $input = 'arabic'; - $output = 'roman'; - $input_value = $input_value; - $output_value = Roman $input_value; - } else { - # In this case, we do not trigger the ia. - return undef; + return unless $in; + + my $out; + if ($in =~ /^\d+$/) { + $out = uc(roman($in)); + } elsif ($in =~ /^[mdclxvi]+$/i) { + $in = uc($in); + $out = arabic($in); } + return unless $out; - return 'roman numeral converter', structured_answer => { + return $out . ' (roman numeral conversion)', structured_answer => { data => { - input => $input, - input_value => $input_value, - output => $output, - output_value => $output_value + title => $out, + subtitle => "Roman numeral conversion: $in" }, templates => { - group => 'text', - options => { - subtitle_content => 'DDH.roman.roman' - } + group => 'text' } }; }; diff --git a/share/goodie/roman/roman.css b/share/goodie/roman/roman.css deleted file mode 100644 index e0a8f82c1..000000000 --- a/share/goodie/roman/roman.css +++ /dev/null @@ -1,29 +0,0 @@ -.converter { - font-size: 1.5em; - font-weight: bold; -} - -.converter__input, .converter__output { - float: left; - width: 30%; -} - -.converter__input__label, .converter__output__label { - display: block; - text-align: center; -} - -.converter__input__field, .converter__output__field { - display: block; - width: 100%; - height: 1.5em; - font-size: 1.5em; - text-align: center; -} - -.converter__equal { - float: left; - width: 10%; - margin-top: 2em; - text-align: center; -} diff --git a/share/goodie/roman/roman.handlebars b/share/goodie/roman/roman.handlebars deleted file mode 100644 index c36750e2d..000000000 --- a/share/goodie/roman/roman.handlebars +++ /dev/null @@ -1,11 +0,0 @@ -
-
- - -
-
=
-
- - -
-
diff --git a/share/goodie/roman/roman.js b/share/goodie/roman/roman.js deleted file mode 100644 index 4d3f5ca23..000000000 --- a/share/goodie/roman/roman.js +++ /dev/null @@ -1,218 +0,0 @@ -DDH.roman = DDH.roman || {}; - -(function(DDH) { - "use strict"; - - /* Reference: https://en.wikipedia.org/wiki/Roman_numerals - * Inputs can be between 1 and 3999. - * We rely on the substractive notation. - */ - - var romanTable = { - 'I': 1, - 'V': 5, - 'X': 10, - 'L': 50, - 'C': 100, - 'D': 500, - 'M': 1000 - }; - - /* The arabicTable is used to convert an arabic digit into roman characters. - * The field 'before' contains the substractive notation. Hence, we need - * specific cases when converting to roman notation for the digits 4 and 9. - */ - - var arabicTable = { - 1: {digit: 'I', before: 'IV'}, - 5: {digit: 'V'}, - 10: {digit: 'X', before: 'XL'}, - 50: {digit: 'L'}, - 100: {digit: 'C', before: 'CD'}, - 500: {digit: 'D'}, - 1000: {digit: 'M'} - }; - - /* validationTable and conversionTable are used to avoid nested conditionals - * in the conversion process. - */ - - var validationTable = { - 'roman': isRoman, - 'arabic': isArabic - }; - - var conversionTable = { - 'roman': { 'arabic': romanToArabic }, - 'arabic': { 'roman': arabicToRoman } - }; - - /* Roman regular expression - * - * It is composed of 4 blocks: - * - M{0,3} -> from M to MMM - * - (?:D?C{0,3}|C[DM]) -> from C to CM - * - (?:L?X{0,3}|X[LC]) -> from X to XC - * - (?:V?I{0,3}|I[VX]) -> from I to IX - */ - - function isRoman(input) { - var r = /^M{0,3}(?:D?C{0,3}|C[DM])(?:L?X{0,3}|X[LC])(?:V?I{0,3}|I[VX])$/i; - return input != '' && - input.match(r); - } - - /* romanToArabic expects a non-null string representing a roman number. - * - * The algorithm relies on the order of the characters. If c is the - * current character, then there are two cases: Either, c is smaller - * than the previous character we add the value of the roman character. - * Or, c is greater than the previous character. In this case, we are - * in a substractive notation context. So we substract the previously - * added value and we also substract the value of the previous value to - * the value of the current character (hence the -2 * ...). - * The first previous value is initialized to the greatest possible value, - * 1000, to ensure the additive notation context at the beginning. - */ - - function romanToArabic(roman) { - var romanDigits = roman.toUpperCase().split(''); - var previousArabicValue = 1000; - - return romanDigits.reduce(function(acc, romanDigit) { - var arabicValue = romanTable[romanDigit]; - var nextAcc = acc; - - if (arabicValue <= previousArabicValue) { - nextAcc += arabicValue; - } else { - nextAcc += -2 * previousArabicValue + arabicValue; - } - previousArabicValue = arabicValue; - - return nextAcc; - }, 0); - } - - function isArabic(input) { - return input != '0' && - input.match(/\d+/) && - parseInt(input) <= 3999; - } - - /* arabicToRoman expects a non-null string composed of digits. - * - * Because of the restriction on roman numbers, we limit arabic numbers to - * 3999. - * - * Arabic numbers use base 10 and can be expressed by the following - * expression n = a x 10^3 + b x 10^2 + c x 10 + d where a, b, c, d - * are digits between 0 and 9. Hence, we need to consider specific - * cases to convert to roman numbers. - * The process goes from left to right. - * If the value of the current digit is 4 or 9, we need to switch to - * the substractive notation. - * If the value of the current digit is 5, we need to use the right - * component in the roman notation (V, L, D). - * The remaining cases are straightforward. - */ - - function arabicToRoman(arabic) { - var arabicValue = parseInt(arabic); - var previousComponent; - var romanDigits = ''; - - [1000, 100, 10, 1].forEach(function (component) { - var leftDigit = Math.floor(arabicValue / component); - - if (leftDigit >= 1 && leftDigit <= 3) { - var romanDigit = arabicTable[component].digit.repeat(leftDigit); - romanDigits += romanDigit; - } else if (leftDigit == 4) { - var romanDigit = arabicTable[component].before; - romanDigits += romanDigit; - } else if (leftDigit == 5) { - var romanDigit = arabicTable[component * 5].digit; - romanDigits += romanDigit; - } else if (leftDigit >= 6 && leftDigit <= 8) { - var romanDigit = arabicTable[component * 5].digit; - var end = arabicTable[component].digit.repeat(leftDigit - 5); - romanDigits += romanDigit + end; - } else if (leftDigit == 9) { - var romanDigit = arabicTable[previousComponent].digit; - romanDigits += arabicTable[component].digit + romanDigit; - } else { - /* if 0 then there is nothing to do. */ - } - - previousComponent = component; - arabicValue -= component * leftDigit; - }); - - return romanDigits; - } - - function upperCaseFirstLetter (string) { - var first = string.charAt(0); - return first.toUpperCase() + string.slice(1); - } - - function buildConverter(input, output) { - var $root = DDH.getDOM('roman'); - - return { - inputLabel: upperCaseFirstLetter(input), - isInputValid: validationTable[input], - input: $root.find('.converter__input__field'), - outputLabel: upperCaseFirstLetter(output), - isOutputValid: validationTable[output], - output: $root.find('.converter__output__field'), - inputToOutput: conversionTable[input][output], - outputToInput: conversionTable[output][input] - }; - } - - DDH.roman.build = function(ops) { - var input = ops.data.input; - var output = ops.data.output; - var inputValue = ops.data.input_value; - var outputValue = ops.data.output_value; - - return { - onShow: function() { - var $converter = buildConverter(input, output); - - $('.converter__input__label').html($converter.inputLabel); - $('.converter__output__label').html($converter.outputLabel); - $converter.input.val(inputValue); - $converter.output.val(outputValue); - - $converter.input.keyup(function (e) { - var input = $converter.input.val(); - if (input == '') { - $converter.output.val(''); - } else if (! $converter.isInputValid(input)) { - $converter.output.val(''); - } else { - var output = $converter.inputToOutput(input); - $converter.output.val(output); - } - }); - - $converter.output.keyup(function (e) { - var output = $converter.output.val(); - - if (output == '') { - $converter.input.val(''); - } else if (! $converter.isOutputValid(output)) { - $converter.input.val(''); - } else { - var input = $converter.outputToInput(output); - $converter.input.val(input); - } - }); - } - }; - }; - -})(DDH); \ No newline at end of file diff --git a/t/Roman.t b/t/Roman.t index 9954564f8..77928740c 100755 --- a/t/Roman.t +++ b/t/Roman.t @@ -10,46 +10,32 @@ zci answer_type => 'roman_numeral_conversion'; zci is_cached => 1; sub build_test { - my ($input, $input_value, $output, $output_value) = @_; - return test_zci('roman numeral converter', structured_answer => { + my ($text, $input, $answer) = @_; + return test_zci($text, structured_answer => { data => { - input => $input, - input_value => $input_value, - output => $output, - output_value => $output_value + title => $answer, + subtitle => "Roman numeral conversion: $input" }, templates => { - group => 'text', - options => { - subtitle_content => 'DDH.roman.roman' - } + group => 'text' } }); } ddg_goodie_test( [qw( DDG::Goodie::Roman )], - 'roman 15' => build_test('arabic', '15', 'roman', 'XV'), - "roman xii" => build_test('roman', 'XII', 'arabic', '12'), - "roman mmcml" => build_test('roman', 'MMCML', 'arabic', '2950'), - "roman 2344" => build_test('arabic', '2344', 'roman', 'MMCCCXLIV'), - "arabic cccxlvi" => build_test('roman', 'CCCXLVI', 'arabic', '346'), - 'roman MCCCXXXVII' => build_test('roman', 'MCCCXXXVII', 'arabic','1337'), - 'roman numeral 10' => build_test('arabic', '10', 'roman', 'X'), - 'roman numeral XVI' => build_test('roman', 'XVI', 'arabic', '16'), - 'roman 1337' => build_test('arabic', '1337', 'roman','MCCCXXXVII'), - 'roman IV' => build_test('roman', 'IV', 'arabic', '4'), - '1492 in roman numerals' => build_test('arabic', '1492', 'roman', 'MCDXCII'), - '10 into roman' => build_test('arabic', '10', 'roman', 'X'), - '3999 in roman numeral' => build_test('arabic', '3999', 'roman', 'MMMCMXCIX'), - 'xiii to arabic' => build_test('roman', 'XIII', 'arabic', '13'), - 'CXIII to arabic' => build_test('roman', 'CXIII', 'arabic', '113'), - 'convert XXX into arabic' => build_test('roman', 'XXX', 'arabic', '30'), - 'convert MMCC to arabic' => build_test('roman', 'MMCC', 'arabic', '2200'), - 'convert 15 to roman' => build_test('arabic', '15', 'roman', 'XV'), - 'convert 30 into roman' => build_test('arabic', '30', 'roman', 'XXX'), - 'convert to arabic' => build_test('roman', '', 'arabic', ''), - 'foo to arabic numerals' => build_test('roman', '', 'arabic', '') + 'roman 155' => build_test('CLV (roman numeral conversion)', '155', 'CLV'), + "roman xii" => build_test("12 (roman numeral conversion)", 'XII', '12'), + "roman mmcml" => build_test("2950 (roman numeral conversion)", 'MMCML', '2950'), + "roman 2344" => build_test("MMCCCXLIV (roman numeral conversion)", '2344', 'MMCCCXLIV'), + "arabic cccxlvi" => build_test("346 (roman numeral conversion)", 'CCCXLVI', '346'), + 'roman numeral MCCCXXXVII' => build_test('1337 (roman numeral conversion)', 'MCCCXXXVII', '1337'), + 'roman 1337' => build_test('MCCCXXXVII (roman numeral conversion)', '1337', 'MCCCXXXVII'), + 'roman IV' => build_test('4 (roman numeral conversion)', 'IV', '4'), + '10 in roman numeral' => build_test('X (roman numeral conversion)', '10', 'X'), + '11 in roman numerals' => build_test('XI (roman numeral conversion)', '11', 'XI'), + 'xiii to arabic' => build_test('13 (roman numeral conversion)', 'XIII', '13'), + '20 to roman numerals' => build_test('XX (roman numeral conversion)', '20', 'XX'), ); done_testing;