diff --git a/lib/prime.rb b/lib/prime.rb index fd9d6ac..f26c9cf 100644 --- a/lib/prime.rb +++ b/lib/prime.rb @@ -16,6 +16,56 @@ require "forwardable" class Integer + # Returns the positive divisors of +self+ + # + # == Example + # 6.divisors #=> [1, 2, 3, 6] + # 7.divisors #=> [1, 7] + # 8.divisors #=> [1, 2, 4, 8] + def divisors + if self < 1_000_000 + return divisors_for_small_integer if self > 0 + return abs.divisors if self < 0 + raise ArgumentError.new("The divisors of 0 is all integer except 0") + end + + if prime? + [1, self] + else + xs = prime_division.map{ |p, n| Array.new(n + 1){ |e| p**e } } + x = xs.pop + x.product(*xs).map{ |t| t.inject(:*) }.sort + end + end + + private def divisors_for_small_integer + # O(√n) algorithm + n = self + s = Integer.sqrt(n) + res1 = [] + res2 = [] + i = 1 + while i <= s + if self % i == 0 + res1 << i + res2.unshift(n / i) + end + i += 1 + end + res1.pop if s * s == n + res1.concat(res2) + end + + # Iterates the given block for each divisor of +self+. + # + # == Example + # ds = [] + # 10.each_divisor{ |d| ds << d } + # ds #=> [1, 2, 5, 10] + def each_divisor + block_given? ? divisors.each{ |d| yield(d) } : enum_for(:each_divisor) + end + # Re-composes a prime factorization and returns the product. # # See Prime#int_from_prime_division for more details. diff --git a/test/test_prime.rb b/test/test_prime.rb index a0e8af5..7631566 100644 --- a/test/test_prime.rb +++ b/test/test_prime.rb @@ -214,6 +214,73 @@ def test_rewind end class TestInteger < Test::Unit::TestCase + def test_divisors + assert_equal [1], 1.divisors + assert_equal [1, 2], 2.divisors + assert_equal [1, 3], 3.divisors + assert_equal [1, 2, 4], 4.divisors + assert_equal [1, 5], 5.divisors + assert_equal [1, 2, 3, 6], 6.divisors + assert_equal [1, 7], 7.divisors + assert_equal [1, 2, 4, 8], 8.divisors + assert_equal [1, 3, 9], 9.divisors + assert_equal [1, 2, 5, 10], 10.divisors + assert_equal [1, 2, 4, 5, 10, 20, 25, 50, 100], 100.divisors + + # large prime number + assert_equal [1, 2**31-1], (2**31-1).divisors + assert_equal [1, 1_000_000_007], 1_000_000_007.divisors + assert_equal [1, 1_000_000_009], 1_000_000_009.divisors + assert_equal [1, 67_280_421_310_721], 67_280_421_310_721.divisors + + # large composite number + p1 = 2**13-1 + p2 = 2**17-1 + assert_equal [1, p1, p2, p1 * p2], (p1 * p2).divisors + + assert_raises(ArgumentError){ 0.divisors } + assert_equal [1], -1.divisors + assert_equal [1, 2], -2.divisors + assert_equal [1, 3], -3.divisors + assert_equal [1, 2, 4], -4.divisors + assert_equal [1, 2, 3, 6], -6.divisors + assert_equal [1, 1_000_000_007], -1_000_000_007.divisors + end + + def test_each_divisor_with_block + ds = [] + 1.each_divisor{ |d| ds << d } + assert_equal [1], ds + + ds = [] + 10.each_divisor{ |d| ds << d } + assert_equal [1, 2, 5, 10], ds + + assert_equal [1, 3], 3.each_divisor{} + assert_raises(ArgumentError){ 0.each_divisor{} } + + ds = [] + -2.each_divisor{ |d| ds << d } + assert_equal [1, 2], ds + end + + def test_each_divisor_without_blcok + enum = 5.each_divisor + assert_respond_to(enum, :each) + assert_kind_of(Enumerable, enum) + + assert_equal [1], 1.each_divisor.to_a + assert_equal [1, 3], 3.each_divisor.to_a + assert_equal [1, 2, 4], 4.each_divisor.to_a + assert_equal [1, 2, 3, 6], 6.each_divisor.to_a + + assert_raises(ArgumentError){ 0.each_divisor.to_a } + assert_equal [1], -1.each_divisor.to_a + assert_equal [1, 2], -2.each_divisor.to_a + assert_equal [1, 2, 4], -4.each_divisor.to_a + assert_equal [1, 2, 3, 6], -6.each_divisor.to_a + end + def test_prime_division pd = PRIMES.inject(&:*).prime_division assert_equal PRIMES.map{|p| [p, 1]}, pd