opal/corelib/enumerator/arithmetic_sequence.rb

Summary

Maintainability
B
5 hrs
Test Coverage
# backtick_javascript: true
# use_strict: true

class ::Enumerator
  class self::ArithmeticSequence < self
    `Opal.prop(self.$$prototype, '$$is_arithmetic_seq', true)`

    `var inf = Infinity`

    # @private
    def initialize(range, step = undefined, creation_method = :step)
      @creation_method = creation_method
      if range.is_a? ::Array
        @step_arg1, @step_arg2, @topfx, @bypfx = *range
        @receiver_num = step
        @step = 1

        @range = if @step_arg2
                   @step = @step_arg2
                   (@receiver_num..@step_arg1)
                 elsif @step_arg1
                   (@receiver_num..@step_arg1)
                 else
                   (@receiver_num..nil)
                 end
      else
        @skipped_arg = true unless step
        @range, @step = range, step || 1
      end

      @object = self

      ::Kernel.raise ArgumentError, "step can't be 0" if @step == 0
      unless @step.respond_to? :to_int
        ::Kernel.raise ArgumentError, "no implicit conversion of #{@step.class} " \
                                      'into Integer'
      end
    end

    attr_reader :step

    def begin
      @range.begin
    end

    def end
      @range.end
    end

    def exclude_end?
      @range.exclude_end?
    end

    # @private
    def _lesser_than_end?(val)
      end_ = self.end || `inf`
      if step > 0
        exclude_end? ? val < end_ : val <= end_
      else
        exclude_end? ? val > end_ : val >= end_
      end
    end

    # @private
    def _greater_than_begin?(val)
      begin_ = self.begin || -`inf`
      if step > 0
        val > begin_
      else
        val < begin_
      end
    end

    def first(count = undefined)
      iter = self.begin || -`inf`

      return _lesser_than_end?(iter) ? iter : nil unless count

      out = []

      while _lesser_than_end?(iter) && count > 0
        out << iter
        iter += step
        count -= 1
      end

      out
    end

    def each(&block)
      return self unless block_given?

      case self.begin
      when nil
        ::Kernel.raise TypeError, "nil can't be coerced into Integer"
      end

      iter = self.begin || -`inf`

      while _lesser_than_end?(iter)
        yield iter
        iter += step
      end
      self
    end

    def last(count = undefined)
      case self.end
      when `inf`, -`inf`
        ::Kernel.raise ::FloatDomainError, self.end
      when nil
        ::Kernel.raise ::RangeError, 'cannot get the last element of endless arithmetic sequence'
      end

      iter = self.end - ((self.end - self.begin) % step)
      iter -= step unless _lesser_than_end?(iter)

      return _greater_than_begin?(iter) ? iter : nil unless count

      out = []

      while _greater_than_begin?(iter) && count > 0
        out << iter
        iter -= step
        count -= 1
      end

      out.reverse
    end

    def size
      step_sign = step > 0 ? 1 : -1

      if !_lesser_than_end?(self.begin)
        0
      elsif [-`inf`, `inf`].include?(step)
        1
      elsif [-`inf` * step_sign, nil].include?(self.begin) ||
            [`inf` * step_sign, nil].include?(self.end)
        `inf`
      else
        iter = self.end - ((self.end - self.begin) % step)
        iter -= step unless _lesser_than_end?(iter)
        ((iter - self.begin) / step).abs.to_i + 1
      end
    end

    def ==(other)
      self.class == other.class &&
        self.begin == other.begin &&
        self.end == other.end &&
        step == other.step &&
        exclude_end? == other.exclude_end?
    end

    def hash
      [ArithmeticSequence, self.begin, self.end, step, exclude_end?].hash
    end

    def inspect
      if @receiver_num
        args = if @step_arg2
                 "(#{@topfx}#{@step_arg1.inspect}, #{@bypfx}#{@step_arg2.inspect})"
               elsif @step_arg1
                 "(#{@topfx}#{@step_arg1.inspect})"
               end

        "(#{@receiver_num.inspect}.#{@creation_method}#{args})"
      else
        args = unless @skipped_arg
                 "(#{@step})"
               end
        "((#{@range.inspect}).#{@creation_method}#{args})"
      end
    end

    alias === ==
    alias eql? ==
  end
end