                      transitions))))
 
 (defn enter-state [has ha state now]
+  (println "enter state" (keys has) (:id ha) state now)
   (let [now (floor-time now time-unit)
+        _ (assert (>= now (:entry-time ha)) "Time must be monotonic")
         ; set the current state to this state
         ; set ha's entry-time to the current moment
         ha (assoc (extrapolate ha now) :entry-time now

        has (:objects scene)
        [min-t transitions] (reduce (fn [[min-t transitions] {[start _] :interval :as trans}]
                                      (cond
                                        (< start min-t) [start [trans]]
                                        (= start min-t) [min-t (conj transitions trans)]
                                        :else [min-t transitions]))
                                    [Infinity []]
                                    (map #(next-transition % qthen inputs) (vals has)))]
    (cond
      (> min-t qnow) (assoc scene :now now)
      (= min-t qnow) (assoc scene :now now
                                  :objects (follow-transitions has transitions))
      :else (update-scene (assoc scene :now min-t
                                       :objects (follow-transitions has transitions))
                          now
                          inputs)
      )))

(defonce scene-a (atom {}))

(defn current-state [ha]
  (get ha (:state ha)))

(defn extrapolate [ha now]
  (let [;req (:next-required-transition ha)
        ;_ (assert (or (nil? req) (<= now (first (:interval req)))) "Extrapolating beyond next transition of HA")
        s (:state ha)
        flows (:flows (get ha s))
        delta (- now (:entry-time ha))
        new-vals (map (fn [[variable flow]]
                        [variable (+ (get ha variable) (* flow delta))])
                      flows)]
    (merge ha (into {} new-vals))))

(defn get-var-and-flow [has ha k]
  (assert has "Must provide has")
  (cond
    (nil? k) [0 0]
    (keyword? k) [(quantize (get ha k) precision) (get-in ha [(:state ha) :flows k] 0)]
    (vector? k) (let [other-ha (get has (first k))
                      other-state (:state other-ha)
                      et (:entry-time ha)
                      other-et (:entry-time other-ha)
                      ex-other-ha (extrapolate other-ha et)
                      [v _f] (get-var-and-flow has other-ha (second k))
                      [exv f] (get-var-and-flow has ex-other-ha (second k))
                      ]
                  #_(println "remote var and flow" k "from" (keys has) "in state" other-state "=" [v f]
                             "extrapolated from" other-et "to" et "=" [exv f])
                  [exv f])
    :else (assert (or (nil? k) (keyword? k) (vector? k)) "Unrecognized variable lookup type")))

(defn constant-from-expr [has ha c]
  (cond
    (number? c) c
    (and (vector? c) (= (first c) :d)) (* frame-length (second (apply get-var-and-flow has ha (rest c))))
    (vector? c) (first (get-var-and-flow has ha c))
    (seqable? c) (apply ({'+ + '- - '* * '/ /} (first c))
                        (map #(constant-from-expr has ha %) (rest c)))))

(defn guard-satisfied? [rel v10 v20 c]
  (let [diff (- v10 v20)]
    (case rel
      :gt (> diff c)
      :geq (>= diff c)
      :leq (<= diff c)
      :lt (< diff c))))

(defn simple-interval [has ha simple-guard]
  (let [rel (first simple-guard)
        c (constant-from-expr has ha (last simple-guard))
        ;_ (println "Constant" (last simple-guard) "is" c)
        ;_ (println ":d :x is" (constant-from-expr has ha [:d :x]))
        v1k (second simple-guard)
        v2k (if (= (count simple-guard) 4) (third simple-guard) nil)
        [v10 v1f] (get-var-and-flow has ha v1k)
        [v20 v2f] (get-var-and-flow has ha v2k)
        ; _ (println (str "(" v10 " + " v1f "t) - (" v20 " + " v2f "t) " (case rel :gt ">" :geq ">=" :leq "<=" :lt "<") " " c))
        ; by algebra: A0+fA*dt-B0-fB*dt-C~0 --> (fA-fB)*dt ~ (-A0 + B0 + C) --> dt ~ (-A0 + B0 + C)/(fA-fB)
        denom (- v1f v2f)
        rhs (/ (+ (- v10) v20 c)
               denom)
        ; since we are dividing by denom, flip rel if denom (a constant) < 0
        rel (if (< denom 0)
              (case rel
                :gt :lt
