     (seqable? c) (apply ({'+ + '- - '* * '/ /} (first c))
                         (map #(constant-from-expr has ha %) (rest c)))))
 
-(defn guard-satisfied? [rel v10 v20 c]
+(defn simple-guard-satisfied? [rel v10 v20 c]
   (let [diff (- v10 v20)]
     (case rel
       :gt (> diff c)

        intervals))

(defn flatten-intervals [intervals]
  (reduce (fn [sofar interval]
            (if (simple? interval)
              (conj sofar interval)
              (into sofar (flatten-intervals interval))))
          []
          intervals))

(defn merge-overlapping [intervals]
  (let [intervals (flatten-intervals intervals)
        intervals (sort-intervals intervals)
        [last-i merged] (reduce (fn [[[amin amax :as a] merged] [bmin bmax :as b]]
                                  (if (intersection a b)
                                    [[amin (.max js/Math amax bmax)] merged]
                                    [[bmin bmax] (conj merged a)]))
                                [(first intervals) []]
                                (rest intervals))]
    (if (empty? intervals)
      []
      (conj merged last-i))))

(defn constrain-times [interval]
  (if (simple? interval)

        ; mentioned above, they might have calculated their new intervals based on stale information.
        ; calculating intervals is idempotent and has no second-order effects so it is fine to do it repeatedly
        ; and it also suffices to do it a single time once all the HAs are updated with new times, values and flows.
        ; todo: cache these?
        deps (into #{} (filter (fn [k]
                                 (some transitioned-ids (ha-dependencies (get has k))))
                               (keys has)))
