(def-cartesian-product route%components
    (ifaces addresses)
    (constructor make%route%component)
    (accessors out%iface next%hop%addr)
    (theory networks))

(def-language routed-network-language
    (embedded-language networks)
    (sorts
      (routers "[sets[addresses] -> route%components]")
      )
    (constants
      (host%router "[hosts -> routers]")
      ))


(def-theory routed-networks 
    (language routed-network-language)
    (component-theories networks datagrams)
    (axioms
      (host_router-is-total
        "total_q{host%router, [hosts -> routers]}"
        d-r-convergence)
      (elements-of-host_router-domain-are-disjoint
        "forall(h:hosts,s_1,s_2:sets[addresses], 
              #(host%router(h)(s_1)) and #(host%router(h)(s_2)) and not s_1 = s_2
                implies 
              s_1 disj s_2)")
      (out_iface-is-an-interface-of-host
        "forall(h:hosts, s:sets[addresses],
              #(host%router(h)(s)) 
                implies
              out%iface(host%router(h)(s)) in ifaces%of%host(h))")
      (next_hop_addr-is-an-address-of-out_iface-spnet
        "forall(h:hosts, s:sets[addresses],
              #(host%router(h)(s)) 
                implies
              next%hop%addr(host%router(h)(s))
                in
              addresses%of%spnet(iface%spnet(out%iface(host%router(h)(s)))))")
      ))


(def-constant next%hop%iface 
    "lambda(c:route%components, addr%to%iface(next%hop%addr(c)))"
    (theory routed-networks))


(def-constant routing%fn 
    "lambda(r:[sets[addresses] -> route%components], d:datagrams,
          iota(c:route%components, 
              forsome(addr%set:sets[addresses],
                  destination%address(d) in addr%set
                    and
                  c = r(addr%set))))"
    (theory routed-networks))


(def-theorem host-of-out_iface 
    "forall(h:hosts, d:datagrams,
          #(routing%fn(host%router(h),d))
            implies
          iface%host(out%iface(routing%fn(host%router(h),d)))=h)"
    (theory routed-networks)
    (proof
      (
        (unfold-single-defined-constant-globally routing%fn)
        direct-and-antecedent-inference-strategy
        (eliminate-defined-iota-expression 0 w)
        (antecedent-inference "with(p:prop,forsome(addr%set:sets[addresses],p));")
        (instantiate-theorem out_iface-is-an-interface-of-host ("h" "addr%set"))
        (weaken (3 2))
        (incorporate-antecedent "with(f:ifaces,f) in with(f:sets[ifaces],f);")
        (unfold-single-defined-constant-globally ifaces%of%host)
        simplify-insistently
        direct-and-antecedent-inference-strategy
        (backchain "with(f:ifaces,f)=with(f:pre%ifaces,f);")
        simplify
        )))


(def-theorem next_hop_iface-lemma 
    "forall(h:hosts, d:datagrams, i:ifaces, a:addresses,
          routing%fn(host%router(h),d) = make%route%component(i,a)
            implies
          next%hop%iface(routing%fn(host%router(h),d)) == addr%to%iface(a))"
    (theory routed-networks)
    (proof
      (
        direct-and-antecedent-inference-strategy
        (unfold-single-defined-constant-globally next%hop%iface)
        (backchain "with(p:prop,p);")
        simplify
        )))