diff --git a/exercises/practice/perfect-numbers/.meta/config.json b/exercises/practice/perfect-numbers/.meta/config.json index 5965a7d2a9..b07eaa7576 100644 --- a/exercises/practice/perfect-numbers/.meta/config.json +++ b/exercises/practice/perfect-numbers/.meta/config.json @@ -10,7 +10,8 @@ "iHiD", "Insti", "kotp", - "tryantwit" + "tryantwit", + "themetar" ], "files": { "solution": [ diff --git a/exercises/practice/perfect-numbers/.meta/example.rb b/exercises/practice/perfect-numbers/.meta/example.rb index 6a9c39027e..5c400f841b 100644 --- a/exercises/practice/perfect-numbers/.meta/example.rb +++ b/exercises/practice/perfect-numbers/.meta/example.rb @@ -1,24 +1,25 @@ -class PerfectNumber - def self.classify(num) - raise 'not a natural number' if num < 0 - aliquot_sum = aliquot_sum(num) - aliquot_sum == num ? "perfect" : aliquot_sum < num ? "deficient" : 'abundant' - end +module PerfectNumber + def self.classify(number) + raise ArgumentError, 'Classification is only possible for positive integers.' unless number > 0 + + aliquot_sum = get_divisors(number).sum - def self.aliquot_sum(num) - get_divisors(num).reduce(:+) + if aliquot_sum == number + "perfect" + else + aliquot_sum < number ? "deficient" : 'abundant' + end end - private + def self.get_divisors(number) + return [] if number == 1 + + (2..Math.sqrt(number)).each_with_object [1] do |n, divisors| + div, mod = number.divmod n + next unless mod.zero? - def self.get_divisors(num) - divisors = [0] - (1..Math.sqrt(num)).each do |n| - if num % n == 0 - divisors << n - divisors << num/n unless (n == 1) || (num/n) == n - end + divisors << n + divisors << div unless div == n end - divisors end end diff --git a/exercises/practice/perfect-numbers/.meta/test_template.erb b/exercises/practice/perfect-numbers/.meta/test_template.erb new file mode 100644 index 0000000000..0ce19306cf --- /dev/null +++ b/exercises/practice/perfect-numbers/.meta/test_template.erb @@ -0,0 +1,18 @@ +require 'minitest/autorun' +require_relative 'perfect_numbers' + +class PerfectNumberTest < Minitest::Test +<% json["cases"].each do |group| %> + <% group["cases"].each do |sub_case| %> + def test_<%= underscore(sub_case["description"]) %> + <%= skip? %> + <%- if sub_case["expected"].is_a?(Hash) && sub_case["expected"].key?("error") -%> + error = assert_raises(ArgumentError) { PerfectNumber.<%= underscore(sub_case["property"]) %>(<%= sub_case["input"]["number"] %>) } + assert_equal '<%= sub_case["expected"]["error"] %>', error.message + <%- else -%> + assert_equal '<%= sub_case["expected"] %>', PerfectNumber.<%= underscore(sub_case["property"]) %>(<%= sub_case["input"]["number"] %>) + <%- end -%> + end + <% end %> +<% end %> +end diff --git a/exercises/practice/perfect-numbers/perfect_numbers_test.rb b/exercises/practice/perfect-numbers/perfect_numbers_test.rb index 98fada3bb1..e44b403a40 100644 --- a/exercises/practice/perfect-numbers/perfect_numbers_test.rb +++ b/exercises/practice/perfect-numbers/perfect_numbers_test.rb @@ -2,21 +2,70 @@ require_relative 'perfect_numbers' class PerfectNumberTest < Minitest::Test - def test_initialize_perfect_number - assert_raises RuntimeError do - PerfectNumber.classify(-1) - end + def test_smallest_perfect_number_is_classified_correctly + # skip + assert_equal 'perfect', PerfectNumber.classify(6) end - def test_classify_deficient - assert_equal 'deficient', PerfectNumber.classify(13) + def test_medium_perfect_number_is_classified_correctly + skip + assert_equal 'perfect', PerfectNumber.classify(28) end - def test_classify_perfect - assert_equal 'perfect', PerfectNumber.classify(28) + def test_large_perfect_number_is_classified_correctly + skip + assert_equal 'perfect', PerfectNumber.classify(33_550_336) end - def test_classify_abundant + def test_smallest_abundant_number_is_classified_correctly + skip assert_equal 'abundant', PerfectNumber.classify(12) end + + def test_medium_abundant_number_is_classified_correctly + skip + assert_equal 'abundant', PerfectNumber.classify(30) + end + + def test_large_abundant_number_is_classified_correctly + skip + assert_equal 'abundant', PerfectNumber.classify(33_550_335) + end + + def test_smallest_prime_deficient_number_is_classified_correctly + skip + assert_equal 'deficient', PerfectNumber.classify(2) + end + + def test_smallest_non_prime_deficient_number_is_classified_correctly + skip + assert_equal 'deficient', PerfectNumber.classify(4) + end + + def test_medium_deficient_number_is_classified_correctly + skip + assert_equal 'deficient', PerfectNumber.classify(32) + end + + def test_large_deficient_number_is_classified_correctly + skip + assert_equal 'deficient', PerfectNumber.classify(33_550_337) + end + + def test_edge_case_no_factors_other_than_itself_is_classified_correctly + skip + assert_equal 'deficient', PerfectNumber.classify(1) + end + + def test_zero_is_rejected_as_it_is_not_a_positive_integer + skip + error = assert_raises(ArgumentError) { PerfectNumber.classify(0) } + assert_equal 'Classification is only possible for positive integers.', error.message + end + + def test_negative_integer_is_rejected_as_it_is_not_a_positive_integer + skip + error = assert_raises(ArgumentError) { PerfectNumber.classify(-1) } + assert_equal 'Classification is only possible for positive integers.', error.message + end end