require "./run.rb"

Types = [EmptyType, IntType, CharType]
Ranks = [0,1,2,3,4,5,6]

ops = Ops.keys + Ops.keys.map{|k|k+","} + Ops.keys.map{|k|k+",,"} + Ops.keys.map{|k|k+",,,"}

# this also tests for non ambiguous results by calling it with all possible inputs types

# check that invariants hold
# increase type: >= ret type
# increase rank: type stays the same, >= ret rank
# error is max value

def get_ret(ways, arg_types, arg_ranks, str)
  ways = filter_ways_by_type(ways, arg_types)
  if ways.empty?
    [nil, nil]
  else
    ret_types = ways.map{|way| spec_to_ret(way, arg_types) }
    raise "%s %p %p" % [ways.map(&:name)*" ", arg_types, ret_types] if ret_types.uniq.size > 1
    way = find_way_by_rank(ways, arg_types, arg_ranks, str)
    result_rank = calc_ret_rank(way, arg_ranks, arg_types)

    [ret_types[0], result_rank]
  end
end

def is_sorted_empty_removed(a)
  a=a.map{|t1|t1.flatten.uniq[0]}.compact
  a == a.sort
end
# 1 arg ops
ops.each{|k| token = Token.new(k)
  op = lookup_op(k)
  next if !op
  next if op.parse_nargs != 1

  x = Types.map{|t| Ranks.map{|r| get_ret(op.ways, [t], [r], k) }}
  types = x.map{|y|y.map{|a|a[0]}}
  ranks = x.map{|y|y.map{|a|a[1]}}

  (puts "%s type changed from rank change"%k; eval"types.each{|t|p t}") if !types.all?{|t|
  (t-[nil]).uniq.size <= 1 }
 (puts "%k type invariant failed"%k;) if !is_sorted_empty_removed(types)
 puts "%s rank invariant failed"%k if !ranks.all?{|r| r.compact.sort == r.compact}
}
# 2 arg ops
ops.each{|k| token = Token.new(k)
  op = lookup_op(k)
  next if !op
  next if op.name == "peek" || op.name == "mpeek"
  next if op.parse_nargs < 2
  x = Types.map{|t1| Types.map{|t2| Ranks.map{|r1| Ranks.map{|r2|
    t = [t1,t2]
    r = [r1,r2]
    (r << r2; t << t2) if op.parse_nargs == 3 # hack for testing if/else easily make both args same
    get_ret(op.ways, t, r, k)
  }}}}
  types = x.map{|x1|x1.map{|x2|x2.map{|x3|x3.map{|x4|x4[0]}}}}
  ranks = x.map{|x1|x1.map{|x2|x2.map{|x3|x3.map{|x4|x4[1]}}}}

  puts "%s type changed from rank change"%k if types.any?{|t1| t1.any?{|r|
  r.flatten.compact.uniq.size > 1 }}
  puts "%s type invariant failed"%k if (types.any?{|t1| !is_sorted_empty_removed(t1) } || types.transpose.any?{|t1| !is_sorted_empty_removed(t1) })
  puts "%s rank invariant failed"%k if ranks.any?{|t1| t1.any?{|t2|
    t2.any?{|r1| r1.compact.sort != r1.compact } || t2.transpose.any?{|r1| r1.compact.sort != r1.compact }
  }}
}


