module TypeProf
  module Utils
    def self.array_update(ary, idx, elem)
      idx %= ary.size
      ary[0...idx] + [elem] + ary[idx+1..-1]
    end

    module StructuralEquality
      def self.included(klass)
        klass.instance_eval do
          def new(*args)
            (@table ||= {})[[self] + args] ||= super
          end
        end
      end
    end

    class Set
      include StructuralEquality

      attr_reader :tbl

      def self.[](*values)
        tbl = {}
        values.each do |v|
          tbl[v] = true
        end
        new(tbl)
      end

      def initialize(tbl)
        @tbl = tbl
        @tbl.freeze
      end

      def each(&blk)
        @tbl.each_key(&blk)
      end

      include Enumerable

      def sum(other)
        if @tbl.size == 0
          other
        elsif other.tbl.size == 0
          self
        else
          Set.new(@tbl.merge(other.tbl))
        end
      end

      def add(new_val)
        tbl = @tbl.dup
        tbl[new_val] = true
        Set.new(tbl)
      end

      def size
        @tbl.size
      end

      def map(&blk)
        tbl = {}
        each do |elem|
          v = yield(elem)
          tbl[v] = true
        end
        Set.new(tbl)
      end

      def inspect
        s = []
        each {|v| s << v.inspect }
        "{#{ s.join(", ") }}"
      end

      def include?(elem)
        @tbl[elem]
      end

      def intersection(other)
        tbl = {}
        h = 0
        each do |elem|
          if other.include?(elem)
            tbl << elem
            h ^= elem.hash
          end
        end
        Set.new(tbl, h)
      end
    end

    class MutableSet
      def initialize(*values)
        @hash = {}
        values.each {|v| @hash[v] = v }
      end

      def each(&blk)
        @hash.each_key(&blk)
      end

      include Enumerable

      def <<(v)
        @hash[v] = true
      end

      def [](v)
        @hash[v]
      end

      def delete(v)
        @hash.delete(v)
      end

      def inspect
        s = []
        each {|v| s << v.inspect }
        "{#{ s.join(", ") }}"
      end

      def size
        @hash.size
      end

      def to_set
        Set[*@hash.keys]
      end
    end

    class HashWrapper
      include StructuralEquality

      def initialize(hash)
        @internal_hash = hash.freeze
      end

      attr_reader :internal_hash
    end

    class WorkList
      def initialize
        @heap = []
        @set = MutableSet.new
      end

      def insert(key, val)
        i = @heap.size
        @heap << [key, val]
        while i > 0 && (@heap[i][0] <=> @heap[i / 2][0]) < 0
          @heap[i], @heap[i / 2] = @heap[i / 2], @heap[i]
          i /= 2
        end
        @set << val
      end

      def member?(val)
        @set[val]
      end

      def deletemin
        return nil if @heap.empty?
        val = @heap[0][1]
        @set.delete(val)
        if @heap.size == 1
          @heap.pop
          return val
        end
        @heap[0] = @heap.pop
        i = 0
        while (j = i * 2 + 1) < @heap.size
          j += 1 if j + 1 < @heap.size && (@heap[j][0] <=> @heap[j + 1][0]) >= 0
          break if (@heap[i][0] <=> @heap[j][0]) < 0
          @heap[i], @heap[j] = @heap[j], @heap[i]
          i = j
        end
        return val
      end

      def size
        @heap.size
      end

      def empty?
        @heap.empty?
      end

      def inspect
        "#<#{ self.class }:#{ @heap.map {|_key, val| val }.inspect }>"
      end
    end
  end
end
