           ;;words (vec (filter .blank? (flatten (map #(prepare-word page-width %) words-raw))))
           words (vec (flatten (map #(prepare-word page-width %) words-raw)))
           word-lens (vec (map count words))
-          justified-text (tj-inner word-lens page-width)]
+          justified-text (tj-inner page-width word-lens)]
         ; (.log js/console "words" (count words) ":" words)
         ; (.log js/console "state" (count justified-text) ":" justified-text)
         (update-state


(defonce state (atom []))

(defn- set-state [new-state]
  (swap! state (fn [] new-state)))
(defn- clear-state [] (set-state []))
(defn- update-state [new-lines]
  (set-state (concat @state new-lines)))
  ; (println "new state" @state))


;;;;

;; cache results (memo)
;; f.e. result for 'justify text after X word is: [lines]'
(def tj-memo (atom {}))

(defn- clear-memo [] (reset! tj-memo {}))
(defn- add-memo   [arg-val return-val] (swap! tj-memo #(assoc % arg-val return-val)))
(defn- has-memo?  [arg-val] (contains? @tj-memo arg-val)) ;; TODO use let-if instead, to search for value once
(defn- get-memo   [arg-val] (get @tj-memo arg-val))

(defn- tj-inner
  "fits words into lines so that whole text looks pretty
  @return [`pretty` metric value, [#words in line]]"
  ([page-width words]
    (clear-memo)
    (second (tj-inner page-width words 0)))
  ([page-width words word-idx]
    (cond
      (empty? words) [0.0 []]
      (has-memo? word-idx) (get-memo word-idx)
      :else
        (reduce
          (fn [acc comma-placement-idx]
             (let [line (subvec words 0 comma-placement-idx)
                   rest-of-text (subvec words comma-placement-idx)
                   next-word-idx (+ word-idx comma-placement-idx)
                   [sub-prob-ugliness sub-prob-lines] (tj-inner page-width rest-of-text next-word-idx)
                   ugliness (+ (badness line page-width) sub-prob-ugliness)
                   solution-proposal (cons comma-placement-idx sub-prob-lines)]
              ; (print "sub" line "|words in line" (count words))
              (add-memo next-word-idx [sub-prob-ugliness sub-prob-lines])
              (if (< ugliness (first acc)) [ugliness solution-proposal] acc)))
          [js/Infinity []]
          (map inc (range (count words))) )))) ;; TODO (dec (count words)) ?


(defn- prepare-text
