#!/usr/bin/ruby

#
## https://rosettacode.org/wiki/Smith_numbers
#

var primes = Enumerator({ |callback|
    static primes = Hash()
    var p = 2
    loop {
        callback(p)
        p = (primes{p} := p.next_prime)
    }
})

func factors(remainder) {

    remainder == 1 && return([remainder])

    gather {
        primes.each { |factor|
            if (factor*factor > remainder) {
                take(remainder) if (remainder > 1)
                break
            }

            while (factor.divides(remainder)) {
                take(factor)
                break if ((remainder /= factor) == 1)
            }
        }
    }
}

func is_smith_number(n) {
    !n.is_prime && (n.sumdigits == factors(n).join.to_n.sumdigits)
}

var s = range(2, 1000).grep { is_smith_number(_) }
say "#{s.len} Smith numbers below 10_000"
say "First 10: #{s.first(10)}"
say "Last  10: #{s.last(10)}"

assert_eq(s.first(10), [4, 22, 27, 58, 85, 94, 121, 166, 202, 265])
assert_eq(s.last(10), [778, 825, 852, 861, 895, 913, 915, 922, 958, 985])
