require "spec_helper"

describe "sieve of Eratosthenes" do

  # http://golang.org/doc/go_tutorial.html#tmp_353

  it "should work using Channel primitives" do

    # send the sequence 2,3,4, ... to returned channel
    def generate(channels)
      ch = channel!(Integer)
      channels << ch
      go!{ i = 1; loop { ch << i+= 1} }

      return ch
    end

    # filter out input values divisible by *prime*, send rest to returned channel
    def filter(in_channel, prime, channels)
      out = channel!(Integer)
      channels << out

      go! do
        loop do
          i, _ = in_channel.receive
          out << i if (i % prime) != 0
        end
      end

      return out
    end

    def sieve(channels)
      out = channel!(Integer)
      channels << out

      go! do
        ch = generate(channels)
        loop do
          prime, _ = ch.receive
          out << prime
          ch = filter(ch, prime, channels)
        end
      end

      return out
    end

    # run the sieve
    n = 20
    nth = false
    channels = []

    primes = sieve(channels)
    result = []

    if nth
      n.times { primes.receive }
      puts primes.receive[0]
    else
      loop do
        p, _ = primes.receive

        if p <= n
          result << p
        else
          break
        end
      end
    end

    expect(result).to eq([2,3,5,7,11,13,17,19])
    channels.each(&:close)
  end

  it "should work with Ruby blocks" do

    # send the sequence 2,3,4, ... to returned channel
    generate = Proc.new do |channels|
      ch = channel!(Integer)
      channels << ch

      go! do
        i = 1
        loop { ch << i+= 1 }
      end

      ch
    end

    # filter out input values divisible by *prime*, send rest to returned channel
    filtr = Proc.new do |in_channel, prime, channels|
      out = channel!(Integer)
      channels << out

      go! do
        loop do
          i, _ = in_channel.receive
          out << i if (i % prime) != 0
        end
      end

      out
    end

    sieve = Proc.new do |channels|
      out = channel!(Integer)
      channels << out

      go! do
        ch = generate.call(channels)

        loop do
          prime, _ = ch.receive
          out << prime
          ch = filtr.call(ch, prime, channels)
        end
      end

      out
    end

    # run the sieve
    n = 20
    nth = false
    channels = []

    primes = sieve.call(channels)
    result = []

    if nth
      n.times { primes.receive }
      puts primes.receive[0]
    else
      loop do
        p, _ = primes.receive

        if p <= n
          result << p
        else
          break
        end
      end
    end

    expect(result).to eq([2,3,5,7,11,13,17,19])
    channels.each(&:close)
  end
end
