#!ruby
=begin

The Quiz was real fun and I spent quite a lot of time on it.  Below
source is rather easy, but believe me I tried lots of different ways
(such as dynamic binary knapsacks etc.), most of them suck because they
need to many method calls.

The code takes about 30 seconds to find all Weird Numbers up to 10_000:

             70, 836, 4030, 5830, 7192, 7912, 9272.

The code doesn't scale rather well, the caches would need to be
optimized for that.

=end

class Integer
  # 70, 836, 4030, 5830, 7192, 7912, 9272, 10430
  def weird?
    !has_semiperfect? && !weird2?
  end

  SEMIPERFECT = {nil => 'shortcut'}

  def weird2?(divisors=proper_divisors)
    return true  if divisors.any? { |x| SEMIPERFECT[x] }
    if brute(self, divisors)
      SEMIPERFECT[self] = true
    else
      false
    end
  end

  SMALL_SEMIPERFECT = [6, 20, 28]    # + [88, 104, 272]

  def has_semiperfect?
    SMALL_SEMIPERFECT.any? { |v| self % v == 0 }
  end

  def proper_divisors
    d = []
    sum = 0
    2.upto(Math.sqrt(self)) { |i|
      if self % i == 0
        d << i << (self / i)
        sum += i + (self / i)
      end
    }
    
    return [nil]  unless sum > self
    d << 1
  end

  def brute(max, values)
    values.sort!

    values.delete max / 2
    max = max / 2

    s = values.size
    (2**s).downto(0) { |n|
      sum = 0
      s.times { |i| sum += values[i] * n[i] }
      return true  if sum == max
    }
    false
  end
end

if ARGV[0]
  n = Integer(ARGV[0])
else
  n = 10_000
end

2.step(n, 2) { |i|
  p i  if i.weird?
}

__END__
-- 
Christian Neukirchen  <chneukirchen / gmail.com>  http://chneukirchen.org