(ns nal.deriver.normalization
  (:require [clojure.string :as s]
            [clojure.set :as set]
            [clojure.core.match :as m]))

operators that should be movet from infix to prefix position

(def operators
  #{'&| '--> '<-> '==> 'retro-impl 'pred-impl 'seq-conj 'inst 'prop 'inst-prop
    'int-image 'ext-image '=/> '=|> '| '<=> '</> '<|> '- 'int-dif '|| '&&
    'ext-inter 'conj})

commutative: <-> <=> <|> & | && || not commutative: --> ==> =/> => &/ - ~

(def commutative-ops #{'<-> '<=> '<|> '| '|| 'conj 'ext-inter})

Makes transformaitions like [A --> B] to [--> A B].

(defn infix->prefix
  [premise]
  (if (coll? premise)
    (let [[f s & tail] premise]
      (map infix->prefix
           (if (operators s)
             (concat [s f] tail)
             premise)))
    premise))

Checks if symbol starts with --, true for --A, --X

(defn- neg-symbol?
  [el]
  (and (not= el '-->) (symbol? el) (s/starts-with? (str el) "--")))

Removes -- from symbols with negation: (trim-negation '--A) => A

(defn- trim-negation
  [el]
  (symbol (s/join (drop 2 (str el)))))
(defn neg [el] (list '-- el))

Replaces negations's "new notation".

(defn replace-negation
  [statement]
  (cond
    (neg-symbol? statement) (neg (trim-negation statement))
    (or (vector? statement) (and (seq? statement) (not= '-- (first statement))))
    (:st
      (reduce
        (fn [{:keys [prev st] :as ac} el]
          (if (= '-- el)
            (assoc ac :prev true)
            (->> [(cond prev (neg el)
                        (coll? el) (replace-negation el)
                        (neg-symbol? el) (neg (trim-negation el))
                        :else el)]
                 (concat st)
                 (assoc ac :prev false :st))))
        {:prev false :st '()}
        statement))
    :else statement))
(defn sort-commutative [conclusions]
  (if (coll? conclusions)
    (let [f (first conclusions)]
      (if (commutative-ops f)
        (vec (conj (sort-by hash (drop 1 conclusions)) f))
        conclusions))
    conclusions))

https://gist.github.com/TonyLo1/a3f8e05458c5e90c2e72

(defn union
  ([c1 c2] (sort-by hash (set (concat c1 c2))))
  ([op c1 c2] (vec (conj (union c1 c2) op))))
(defn diff
  ([c1 c2] (into '() (set/difference (set c1) (set c2))))
  ([op c1 c2] (vec (conj (diff c1 c2) op))))
(defn reduce-ext-inter
  [st]
  (m/match
    st
    [_ t] t
    [_ ['ext-inter & l1] ['ext-inter & l2]] (union 'ext-inter l1 l2)
    [_ ['ext-inter & l1] l2] (union 'ext-inter l1 [l2])
    [_ l1 ['ext-inter & l2]] (union 'ext-inter [l1] l2)
    [_ ['int-set & l1] ['int-set & l2]] (union 'int-set l1 l2)
    ;[_ ['ext-set & l1] ['ext-set & l2]] (union 'ext-set l1 l2)
    :else st))
(defn reduce-int-inter
  [st]
  (m/match st
    ['| t] t
    ['| ['| & l1] ['| & l2]] (union '| l1 l2)
    ['| ['| & l1] l2] (union '| l1 [l2])
    ['| l1 ['| & l2]] (union '| [l1] l2)
    ['| ['int-set & l1] ['int-set & l2]] (union 'int-set l1 l2)
    ['| ['ext-set & l1] ['ext-set & l2]] (union 'ext-set l1 l2)
    :else st))
(defn reduce-int-dif
  [st]
  (m/match st
    [_ ['int-set & l1] ['int-set & l2]] (diff 'int-set l1 l2)
    :else st))
(defn reduce-ext-dif
  [st]
  (m/match st
    [_ ['ext-set & l1] ['ext-set & l2]] (diff 'ext-set l1 l2)
    :else st))
(defn reduce-symilarity
  [st]
  (m/match st
    ['<-> ['ext-set s] ['ext-set p]] ['<-> s p]
    ['<-> ['int-set s] ['int-set p]] ['<-> s p]
    :else st))
(defn reduce-production
  [st]
  (m/match st
    ['* ['* & l1] & l2] (vec (conj (concat l1 l2) '*))
    :else st))
(defn reduce-image
  [st]
  (m/match st
    [_ ['* t1 t2] t3] (if (and (= t2 t3) (not= t1 t2)) t1 st)
    :else st))
(defn reduce-neg
  [st]
  (m/match st
    ['-- ['-- t]] t
    :else st))
(defn reduce-or
  [st]
  (m/match st
    ['|| t] t
    ['|| ['|| & l1] ['|| & l2]] (union '|| l1 l2)
    ['|| ['|| & l1] l2] (union '|| l1 [l2])
    ['|| l1 ['|| & l2]] (union '|| [l1] l2)
    ['|| t1 t2] (if (= t1 t2) t1 st)
    :else st))
(defn reduce-and
  [st]
  (m/match st
    ['conj t] t
    ['conj ['conj & l1] ['conj & l2]] (union 'conj l1 l2)
    ['conj ['conj & l1] l2] (union 'conj l1 [l2])
    ['conj l1 ['conj & l2]] (union 'conj [l1] l2)
    ['conj t1 t2] (if (= t1 t2) t1 st)
    :else st))
(defn reduce-seq-conj
  [st]
  (let [cnt (count st)]
    (cond
      (= 3 cnt) (second st)
      (odd? cnt) (vec (butlast st))
      :else st)))
(def reducible-ops
  {'ext-inter `reduce-ext-inter
   '|         `reduce-int-inter
   '-         `reduce-ext-dif
   'int-dif   `reduce-int-dif
   '<->       `reduce-symilarity
   '*         `reduce-production
   'int-image `reduce-image
   'ext-image `reduce-image
   '--        `reduce-neg
   'conj      `reduce-and
   '||        `reduce-or})
(defn reduce-ops
  [st]
  (let [f (first st)]
    (case f
      ext-inter (reduce-ext-inter st)
      | (reduce-int-inter st)
      - (reduce-ext-dif st)
      int-dif (reduce-int-dif st)
      <-> (reduce-symilarity st)
      * (reduce-production st)
      int-image (reduce-image st)
      ext-image (reduce-image st)
      -- (reduce-neg st)
      conj (reduce-and st)
      || (reduce-or st)
      st)))