         :when (-> dsym .curvature2D .isZero)]
     dsym))
 
-(defn azul-syms-raw []
-  (for [ds octa-syms o2t boundary-mappings] (apply-to-template ds o2t)))
-
 (def azul-syms
-  (letfn [(step [acc ds]
-                (let [[seen _] acc
-                      key (.invariant ds)]
-                  (if (seen key)
-                    [seen false]
-                    [(conj seen key) ds])))]
-         (for [entry (reductions step [#{} false] (azul-syms-raw))
-               :when (second entry)]
-           (second entry))))
+  (let [raw (for [ds octa-syms o2t boundary-mappings] (on-template ds o2t))]
+    (for [ds (unique raw #(-> %1 .minimal .invariant))]
+      (-> ds .dual .minimal .canonical))))
 
 (defn -main []
   (do

    (doseq [D (keys tmp2oct)]
      (if (not (.definesV tmp 1 2 D))
        (.redefineV tmp 1 2 D (.v ds 1 2 (tmp2oct D)))))
    (-> tmp .dual .minimal .canonical)))

(def octa-sets (filter #(-> %1 max-curvature .isNegative not)
                       (lazy-seq (new CombineTiles octagon))))

      (zipmap (drop (mod (- 19 p) 16) octagon-boundary)
              (take 16 template-boundary)))))

(defn on-template [ds oct2tmp]
  (let [tmp2oct (clojure.set/map-invert oct2tmp)
        tmp (DynamicDSymbol. template)]
    (doseq [D (keys tmp2oct)]
