lib/hamster/vector.rb



require "forwardable"
require "hamster/undefined"
require "hamster/immutable"
require "hamster/enumerable"

module Hamster
  def self.vector(*items)
    items.reduce(EmptyVector) { |vector, item| vector.add(item) }
  end

  class Vector
    extend Forwardable
    include Immutable
    include Enumerable

    BLOCK_SIZE = 32
    INDEX_MASK = BLOCK_SIZE - 1
    BITS_PER_LEVEL = 5

    attr_reader :size
    def_delegator :self, :size, :length

    def initialize
      @levels = 0
      @root = []
      @size = 0
    end

    def empty?
      @size == 0
    end
    def_delegator :self, :empty?, :null?

    def first
      get(0)
    end
    def_delegator :self, :first, :head

    def last
      get(-1)
    end

    def add(item)
      transform do
        update_leaf_node(@size, item)
        @size += 1
      end
    end
    def_delegator :self, :add, :<<
    def_delegator :self, :add, :cons

    # def delete(index)
    # end

    def set(index, item = Undefined)
      return set(index, yield(get(index))) if item.equal?(Undefined)
      raise IndexError if empty? || index == @size
      raise IndexError if index.abs > @size
      return set(@size + index, item) if index < 0
      transform do
        update_leaf_node(index, item)
      end
    end

    def get(index)
      return nil if empty? || index == @size
      return nil if index.abs > @size
      return get(@size + index) if index < 0
      leaf_node_for(@root, root_index_bits, index)[index & INDEX_MASK]
    end
    def_delegator :self, :get, :[]
    def_delegator :self, :get, :at

    def each(&block)
      return self unless block_given?
      traverse_depth_first(&block)
      nil
    end

    def map(&block)
      return self unless block_given?
      reduce(EmptyVector) { |vector, item| vector.add(yield(item)) }
    end
    def_delegator :self, :map, :collect

    def filter
      return self unless block_given?
      reduce(EmptyVector) { |vector, item| yield(item) ? vector.add(item) : vector }
    end

    def clear
      EmptyVector
    end

    def inspect
      to_a.inspect
    end

    def eql?(other)
      return true if other.equal?(self)
      return false unless instance_of?(other.class) && @size == other.size
      @root.eql?(other.instance_variable_get(:@root))
    end
    def_delegator :self, :eql?, :==

    private

    def traverse_depth_first(node = @root, level = @levels, &block)
      return node.each(&block) if level == 0
      node.each { |child| traverse_depth_first(child, level - 1, &block) }
    end

    def leaf_node_for(node, child_index_bits, index)
      return node if child_index_bits == 0
      child_index = (index >> child_index_bits) & INDEX_MASK
      leaf_node_for(node[child_index], child_index_bits - BITS_PER_LEVEL, index)
    end

    def update_leaf_node(index, item)
      copy_leaf_node_for(new_root, root_index_bits, index)[index & INDEX_MASK] = item
    end

    def copy_leaf_node_for(node, child_index_bits, index)
      return node if child_index_bits == 0
      child_index = (index >> child_index_bits) & INDEX_MASK
      child_node = node[child_index]
      if child_node
        child_node = child_node.dup
      else
        child_node = []
      end
      node[child_index] = child_node
      copy_leaf_node_for(child_node, child_index_bits - BITS_PER_LEVEL, index)
    end

    def new_root
      if full?
        @levels += 1
        @root = [@root]
      else
        @root = @root.dup
      end
    end

    def full?
      (@size >> root_index_bits) > 0
    end

    def root_index_bits
      @levels * BITS_PER_LEVEL
    end
  end

  EmptyVector = Hamster::Vector.new
end