class Fps

  
  
  
  
  
  
  
  def initialize(const: nil, delta: nil, iota: nil, init: nil, enum: nil)
    
    case
      when const
        @coeffenum = make_const(const)
      when delta
        @coeffenum = make_delta(delta)
      when iota
        @coeffenum = make_iota(iota)
      when init
        @coeffenum = make_init(init)
      when enum
        @coeffenum = enum
      else
        @coeffenum = make_const(0)
    end
    
    @coeffenum.instance_eval do
      def [](index)
        self.drop(index).first
      end
    end
  end

  
  def [](index)
    @coeffenum.drop(index).first
  end

  
  def +(other)
    other = convert(other)
    Fps.new(enum:
      Enumerator.new do |yielder, inx: 0|
        loop do
          yielder.yield(@coeffenum[inx] + other[inx])
          inx += 1
        end
      end.lazy)
  end

  
  def -(other)
    other = convert(other)
    Fps.new(enum:
      Enumerator.new do |yielder, inx: 0|
        loop do
          yielder.yield(@coeffenum[inx] - other[inx])
          inx += 1
        end
      end.lazy)
  end

  
  def *(other)
    other = convert(other)
    Fps.new(enum:
      Enumerator.new do |yielder, inx: 0|
        loop do
          coeff = (0..inx).reduce(0) { |sum, i| sum + (@coeffenum[i] * other[inx - i]) }
          yielder.yield(coeff)
          inx += 1
        end
      end.lazy)
  end

  
  def /(other)
    other = convert(other)
    Fps.new(enum:
      Enumerator.new do |yielder, inx: 1|
        coeffs = [ Rational(@coeffenum[0], other[0]) ]
        yielder.yield(coeffs[-1])
        loop do
          coeffs <<
            Rational(
              @coeffenum[inx] -
                (1..inx).reduce(0) { |sum, i| sum + (other[i] * coeffs[inx - i]) },
              other[0])
          yielder.yield(coeffs[-1])
          inx += 1
        end
      end.lazy)
  end

  
  def deriv()
    Fps.new(enum:
      Enumerator.new do |yielder, inx: 0|
        iota = Fps.new(iota: 1)
        loop do
          yielder.yield(@coeffenum[inx + 1] * iota[inx])
          inx += 1
        end
      end.lazy)
  end

  
  def integ()
    Fps.new(enum:
      Enumerator.new do |yielder, inx: 0|
        iota = Fps.new(iota: 1)
        yielder.yield(Rational(0, 1))
        loop do
          yielder.yield(Rational(@coeffenum[inx], iota[inx]))
          inx += 1
        end
      end.lazy)
  end

  
  def assign(other)
    other = convert(other)
    @coeffenum = other.get_enum
  end

  
  def coerce(other)
    if other.kind_of?(Numeric)
      [ Fps.new(delta: other), self ]
    else
      raise TypeError 'non-numeric can\'t be coerced into FPS type'
    end
  end

  
  def to_i()
    @coeffenum[0].to_i
  end

  
  def to_f()
    @coeffenum[0].to_f
  end

  
  def to_r()
    @coeffenum[0].to_r
  end

  
  def to_s(count = 0)
    if count <= 0
      super()
    else
      retstr = ''
      count.times do |inx|
        coeff = (@coeffenum[inx].to_r.denominator == 1)? @coeffenum[inx].to_i: @coeffenum[inx]
        if!(coeff.zero?)
          prefix = (retstr!= '')? ' ': ''
          coeffstr =
            ((coeff.abs == 1) && (inx!= 0))? '': 
          suffix = (inx == 0)? '': (inx == 1)? 'x': 
          if coeff < 0
            prefix << ((retstr!= '')? '- ': '-')
          else
            prefix << ((retstr!= '')? '+ ': '')
          end
          retstr << 
        end
      end
      (retstr == '')? '0': retstr
    end
  end

  
  def eval(x, count)
    @coeffenum.first(count).each_with_index.reduce(0) { |sum, (coeff, inx) | sum + coeff * x**inx }
  end

  
  def method_missing(name, *args, &block)
    @coeffenum.send(name, *args, &block)
  end

  
  def respond_to_missing?(name, incl_priv)
    @coeffenum.respond_to?(name, incl_priv)
  end

protected

  
  def get_enum()
    @coeffenum
  end

private

  
  
  def make_const(n)
    Enumerator.new do |yielder|
      loop { yielder.yield(n) }
    end.lazy
  end

  
  
  def make_delta(n)
    Enumerator.new do |yielder|
      yielder.yield(n)
      loop { yielder.yield(0) }
    end.lazy
  end

  
  
  def make_iota(n)
    Enumerator.new do |yielder, i: n|
      loop { yielder.yield(i); i += 1 }
    end.lazy
  end

  
  
  def make_init(array)
    Enumerator.new do |yielder, inx: -1|
      loop { yielder.yield((inx < (array.length - 1))? array[inx += 1]: 0) }
    end.lazy
  end

  
  def convert(other)
    if other.kind_of?(Fps)
      other
    else
      if other.kind_of?(Numeric)
        Fps.new(delta: other)
      else
        raise TypeError 'non-numeric can\'t be converted to FPS type'
      end
    end
  end

end