require "set"

def make_promises(ir3)
  promises = ir3.map{|node|
    arg_types = node.args.map{|a|ir3[a].type}

    impl = lambda{|*args|
      $rank = node.way.vectorize ? node.way.mod : ir3[node.args[0]].rank
      $at = arg_types[0]
      $bt = arg_types[1]
      node.way.impl[*args]
    }

    promise{
      args = node.args.map{|a| promises[a] }
      if node.way.name == "_id"
        args[0].value
      else
        zipn(node.zip_level, args, impl)
      end
    }
  }
  promises
end

class Promise
  def initialize(&block)
    @impl=block
  end
  def value
    @impl=@impl[] if Proc===@impl
    @impl
  end
  def empty
    value.empty?
  end
end

def promise(&b)
  Promise.new(&b)
end
class Object
  def const
    Promise.new{self}
  end
end

def zipn(n, a, f)
  return f[*a] if n <= 0 || a==[]
  return [] if a.any?{|i|i.empty}
  [promise{zipn(n-1, a.map{|i|i.value[0]}, f) },
   promise{zipn(n, a.map{|i|i.value[1]}, f) }]
end

def output(str)
   print(str)
end

def error_output(str)
   $stderr.write(str)
end

def repeat(a)
  (ret = [a]) << ret.const
end

def concat_map(v,rhs=Null,first=true,&b)
  if v.empty
    rhs.value
  else
    b[v.value[0],Promise.new{concat_map(v.value[1],rhs,false,&b)},first]
  end
end

def append(v,r)
  v.empty ? r.value : [v.value[0],promise{append(v.value[1], r)}]
end

def join(a,b)
  return [] if a.empty
  append(a.value[0],promise{
    if b.empty || a.value[1].empty
      []
    else
      append(b.value[0], promise{ join(a.value[1], b.value[1]) })
    end
  })
end

def starts_with(a,b) # returns remainder if match otherwise nil
  until b.empty
    return nil if a.empty || spaceship(a.value[0], b.value[0]) != 0
    a = a.value[1]
    b = b.value[1]
  end
  a
end

def cut(a,b) # fyi could be lazier since we know we return a list always
  return [a,Null] if a.empty || b.empty
  if (remainder=starts_with(a,b.value[0]))
    [Null,Promise.new{cut(remainder,b.value[1])}]
  else
    rhs=Promise.new{cut(a.value[1],b)}
    [promise{[a.value[0],Promise.new{rhs.value[0].value}]},promise{rhs.value[1].value}]
  end
end

def cut_any(a,b) # fyi could be lazier since we know we return a list always
  return [a,Null] if a.empty || b.empty
  bb = b.value[0]
  until bb.empty
    if remainder=starts_with(a,bb.value[0])
      return [Null,Promise.new{cut_any(remainder,b.value[1])}]
    end
    bb=bb.value[1]
  end
  rhs=Promise.new{cut_any(a.value[1],b)}
  [promise{[a.value[0],Promise.new{rhs.value[0].value}]},promise{rhs.value[1].value}]
end

def scan_any(a,b) # fyi could be lazier?
  until a.empty || b.empty
    bb = b.value[0]
    until bb.empty
      if remainder=starts_with(a,bb.value[0])
        return [bb.value[0], Promise.new{scan_any(remainder,b.value[1])}]
      end
      bb=bb.value[1]
    end
    a = a.value[1]
  end
  []
end

def substr_ind(a,b) # or len of a if not found
  i = 0
  until a.empty || starts_with(a,b)
    i += 1
    a = a.value[1]
  end
  i
end

def any_substr_ind(a,b) # or len of a if not found
  i = 0
  until a.empty
    bb = b
    until bb.empty
      return [i,bb.value[0]] if starts_with(a,bb.value[0])
      bb=bb.value[1]
    end
    i += 1
    a = a.value[1]
  end
  [i, Null]
end

def last(a, prev)
  until a.empty
    prev = a.value[0]
    a = a.value[1]
  end
  prev.value
end

def init(a)
  return [] if a.empty || a.value[1].empty
  [a.value[0], Promise.new{ init(a.value[1]) }]
end

FalseChars = {0=>1,9=>1,10=>1,11=>1,12=>1,13=>1,32=>1}
def truthy(type, value)
  return !value.empty? if Array === value
  case type
    when IntType
      return value != 0
    when CharType
      !FalseChars.include?(value)
    else raise
  end
end

def reverse(a)
  sofar=[]
  until a.empty
    sofar = [a.value[0],sofar.const]
    a = a.value[1]
  end
  sofar
end

def padv(a,b)
  [
    promise{ a.empty ? b.value : a.value[0].value },
    promise{ padv( promise{ a.empty ? [] : a.value[1].value }, b) }
  ]
end

def product(a, ans=1)
  if a.empty
    ans
  elsif a.value[0].value == 0
    0
  else
    product(a.value[1], a.value[0].value*ans)
  end
end

def pow(a,b)
  # manually implement this op so that it is consistent and doesn't return rationals / overflow
  return 1 if b.value == 0
  a,b = a.value,b.value
  return 0 if b < 0 && a == 0
  neg_exponent = b < 0
  b = b.abs

  y = 1
  while b >= 1
    y *= a if b%2 == 1
    a *= a
    b /= 2
  end
  neg_exponent ? 1 / y : y
end

def prints(value)
  while !value.empty?
    v = value[0].value
    output int_to_char(v)
    value = value[1].value
  end
end

Null = [].const

def str_to_lazy_list(s,rhs=Null)
  to_lazy_list(s.chars.map(&:ord), rhs)
end

def to_lazy_list(l, rhs=Null, ind=0)
  ind >= l.size ? rhs.value : [l[ind].const, Promise.new{to_lazy_list(l, rhs, ind+1)}]
end

def to_lazy_list_rec(l, rhs=Null, ind=0)
  return str_to_lazy_list(l) if String === l
  return l if !(Array === l)
  ind >= l.size ? rhs.value : [to_lazy_list_rec(l[ind]).const, Promise.new{to_lazy_list_rec(l, rhs, ind+1)}]
end

def to_eager_str(v)
  to_eager_list(v.const).map{|i|int_to_char(i)}.join
end

def to_eager_list(a)
  sofar=[]
  until a.empty
    sofar << a.value[0].value
    a = a.value[1]
  end
  sofar
end

def to_eager_list_rec(a)
  return a.value unless Array===a.value
  sofar=[]
  until a.empty
    sofar << to_eager_list_rec(a.value[0])
    a = a.value[1]
  end
  sofar
end

def str(i)
  to_lazy_list(i.value.to_s.bytes.to_a)
end

def is_digit(i)
  i>=48 && i<58
end

def split_non_digits(s)
  return [] if s.empty
  v,found,s2=read_num(s)
  return [] if !found
  [v.const,Promise.new{split_non_digits(s2)}]
end

def read_num(s)
  multiplier=1
  until s.empty || is_digit(s.value[0].value)
    if s.value[0].value == ?-.ord
      multiplier *= -1
    else
      multiplier = 1
    end
    s = s.value[1]
  end
  v = 0
  found_int = false
  until s.empty || !is_digit(s.value[0].value)
    found_int = true
    v = v*10+s.value[0].value-48
    s = s.value[1]
  end

  [multiplier * v, found_int, s]
end

def lines(s)
  return [] if s.empty

  after = Promise.new{lines(s.value[1])}
  if s.value[0].value == 10
    [Null, after]
  else
    [Promise.new{
      after.empty ? [s.value[0], Null] : [s.value[0], after.value[0]]
     },
     Promise.new{
      after.empty ? [] : after.value[1].value
     }]
   end
end

def take(n, a)
  return [] if n < 1 || a.empty
  [a.value[0], Promise.new{ take(n-1, a.value[1]) }]
end

def drop(n, a)
  while n>=1 && !a.empty
    n-=1
    a=a.value[1]
  end
  a.value
end

def indices(a, b, i=0)
  until a.empty
    return [i.const, promise{ indices(a.value[1],b,i+1) }] if spaceship(a.value[0],b) == 0
    i += 1
    a = a.value[1]
  end
  return []
end

def len(a)
  return 0 if a.empty?
  return 1+len(a[1].value)
end

def sort_by(a,b)
  fromby(sort(toby(a,b),true))
end

def toby(a,b)
  return Null if a.empty || b.empty
  Promise.new{ [[a.value[0], b.value[0]], toby(a.value[1], b.value[1])] }
end

def fromby(a)
  return [] if a == []
  [Promise.new{a[0][0].value}, Promise.new{fromby(a[1].value)}]
end

# It would be very interesting and useful to design a more lazy sorting algorithm
# so that you can select ith element in O(n) total time after sorting a list.
def sort(a,by=false)
  return [] if a.empty
  return a.value if a.value[1].empty
  n=len(a.value)
  left=take(n/2, a).const
  right=drop(n/2, a).const
  merge(sort(left,by),sort(right,by),by)
end

def merge(a,b,by)
  return b if a==[]
  return a if b==[]
  if (by ? spaceship(a[0][1], b[0][1]) : spaceship(a[0], b[0])) <= 0
    [a[0], Promise.new{merge(a[1].value,b,by)}]
  else
    [b[0], Promise.new{merge(a,b[1].value,by)}]
  end
end

def spaceship(a,b)
  if Array === a.value
    until a.empty && b.empty
      return -1 if a.empty
      return 1 if b.empty
      s0 = spaceship(a.value[0],b.value[0])
      return s0 if s0 != 0
      a = a.value[1]
      b = b.value[1]
    end
    return 0
  else
    a.value<=>b.value
  end
end

def inspectv(t,rank,value,rhs=Null)
  if t == CharType && rank == 1
    ['"'.ord.const, Promise.new{
      concat_map(value,Promise.new{str_to_lazy_list('"',rhs)}){|v,r,first|
       str_to_lazy_list(escape_str_char(v.value),r)
      }
    }]
  elsif rank > 0 #List
    ["[".ord.const, Promise.new{
      concat_map(value,Promise.new{str_to_lazy_list("]",rhs)}){|v,r,first|
        first ?
          inspectv(t,rank-1,v,r) :
          [','.ord.const,Promise.new{inspectv(t,rank-1,v,r)}]
      }
    }]
  elsif t == EmptyType
    value.value # take the value so it can throw
    raise
  elsif t == IntType
      str_to_lazy_list(value.value.to_s,rhs)
  elsif t == CharType
    str_to_lazy_list(inspect_char(value.value),rhs)
  else
    raise
  end
end

def filter(a,b,bt)
  until a.empty || b.empty
    if truthy(bt,b.value[0].value)
      return [a.value[0],Promise.new{ filter(a.value[1],b.value[1],bt) }]
    else
      a = a.value[1]
      b = b.value[1]
    end
  end
  []
end

def take_while(a,b,bt)
  return [] if a.empty || b.empty
  if truthy(bt,b.value[0].value)
   [a.value[0],Promise.new{ take_while(a.value[1],b.value[1],bt) }]
  else
    []
  end
end

def drop_until(a,b,bt)
  until a.empty || b.empty
    if truthy(bt,b.value[0].value)
      return a.value
    else
      a = a.value[1]
      b = b.value[1]
    end
  end
  []
end

def chunk_while(a,b,btype)
  return [Null, Null] if a.empty
  rest = promise { chunk_while(a.value[1], promise{ b.empty ? [] : b.value[1].value }, btype) }
  truthy = promise { b.empty || truthy(btype,b.value[0].value) }
  [promise{ [a.value[0], promise{ truthy.value ? rest.value[0].value : []}] },
   promise{ truthy.value ? rest.value[1].value : rest.value }]
end

def strip(a,at)
  lstrip = drop_until(a,a,at)
  b = reverse(lstrip.const).const
  reverse(drop_until(b,b,at).const)
end

# these aren't as lazy as possible, but it gets to use hashes
def isuniq(a,h=Hash.new(-1))
  return [] if a.empty
  [(h[to_eager_list_rec(a.value[0])]+=1) == 0 ? 1.const : 0.const, Promise.new{isuniq(a.value[1], h)}]
end

def set_diff(a,b)
  counts = Hash.new(0)
  to_eager_list_rec(b).each{|be| counts[be] += 1 }
  diff_helper(a, counts)
end

def diff_helper(a, counts)
  while !a.empty && counts[ae=to_eager_list_rec(a.value[0])] > 0
    a = a.value[1]
    counts[ae] -= 1
  end
  return [] if a.empty
  [a.value[0], Promise.new{ diff_helper(a.value[1], counts) }]
end

def range(a,b)
  return [] if a>=b
  [a.const, Promise.new{range(a+1,b)}]
end

def range_from(a)
  [a.const, Promise.new{range_from(a+1)}]
end

def group_while(a,b,btype)
  return [Null,Null] if a.empty || b.empty
  b0true = Promise.new{ truthy(btype,b.value[0].value)}
  rhs = Promise.new{ group_while(a.value[1],b.value[1],btype) }
  [
    Promise.new{
      if b0true.value
        [a.value[0], rhs.value[0]]
      else
        []
      end
    },
    Promise.new{
      if b0true.value
        rhs.value[1].value
      else
        rhs.value
      end
    }
  ]
end

def reshape(a, b)
  return [] if b.empty
  n = b.value[0].value
  return [] if n>0 && a.empty
  return [Null, promise{ reshape(a, b.value[1]) }] if n <= 0
  [promise{ take(n, a) },
   promise{
    d = drop(n-1, a)
    # took more than all, next is []
    d == [] ? [] : reshape(d[1], b.value[1])
   }
  ]
end

def map(a,&b)
  a.empty ? [] : [Promise.new{b[a.value[0]]}, Promise.new{map(a.value[1],&b)}]
end

def zero(rank,type=nil)
  return [] if rank > 0
  type == CharType ? 32 : 0
  # empty type also returns 0 and ops are constructed so that their type would also be int
end

def concat(a)
  concat_map(a){|i,r,first|append(i,r)}
end

def rstrip(a)
  return [] if a.empty
  rest = promise{ rstrip(a.value[1]) }
  return [] if a.value[0].empty && rest.empty
  [a.value[0], rest]
end

def inf_tails(a)
  ans=[a, promise{ map(ans.const){|e| map(e){|e2| e2.empty ? [] : e2.value[1].value }} }]
end

def transpose(a,at,rank)
  z = promise{ map(promise{ inf_tails(a) }){|e| rstrip(e) } }
  t = promise{ map(z){|e| map(e){|e2| e2.empty ? zero(rank,at) : e2.value[0].value } } }
  take_while(t,t,at)
end

def square_root(x,n)
  raise IogiiError.new "negative square root" if x < 0
  return 0 if x==0
  guess = x
  while (r=(x/guess+guess)/n) < guess
    guess = r
  end
  guess
end

CharClasses = [
  [*'0'..'9'],
  [*'a'..'z'],
  [*'A'..'Z'],
  [*' '..126.chr] - ([*'a'..'z'] + [*'A'..'Z'] + [*'0'..'9'] + [' ',"\n"]),
  ["\n",' '],
].map{|chars| chars.map(&:ord).to_set }

def char_class(a, used = [false] * CharClasses.size)
  return [] if a.empty
  i = (0...CharClasses.size).find{|i| CharClasses[i].include? a.value[0].value }
  if !i || used[i]
    char_class(a.value[1], used)
  else
    used[i] = true
    [ to_lazy_list([*CharClasses[i]].sort).const, promise{ char_class(a.value[1], used) } ]
  end
end

def is_char_class(a, b)
  clazz = CharClasses.find{|clazz| clazz.include?(a.value)  }
  return 0 if !clazz
  until b.empty
    return 1 if clazz.include?(b.value[0].value)
    b = b.value[1]
  end
  return 0
end

def to_base(a,b)
  to_base_h(a.abs,b,a<=>0)
end

def to_base_h(a,b,sign)
  return [] if a==0
  digit = a%b*sign
  [digit.const, Promise.new{to_base_h((a-digit*sign)/b,b,sign)}]
end

def from_base(a,b)
  x=0
  until a.empty?
    x=x*b.value+a[0].value
    a=a[1].value
  end
  x
end

def undigits(a)
  from_base(concat(map(a){|i|
    x = reverse(to_base(i.value,10).const)
    x.empty? ? [0.const, Null] : x
  }.const), 10.const)
end

def read_stdin(input)
  c=input.getc
  if c.nil?
    []
  else
    [c.ord.const, Promise.new{ read_stdin(input) }]
  end
end
