Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions lib/prime.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
67 changes: 67 additions & 0 deletions test/test_prime.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down