Algoritam NelsonOppen:
------------------------------------------------------------------------

Ulaz: skup baznih jednakosti E i skup termova T koji sadrzi sve 
termove koji se javljaju u E (ali ne obavezno samo njih) i koji je
zatvoren za podtermove.

Izlaz: relacija kongruencije R medju termovima iz T indukovana 
jednakostima iz E.

Pocetak:

Pocetno stanje relacije R je da svaki element iz T cini zasebnu klasu 
ekvivalencije.

KreirajUseListe();

Za sve jednakosti s = t iz E:
 ako je find(s) != find(t) pozovi Merge(s,t)

Kraj;

Funkcija Merge(s,t):
-------------------

Pocetak:

P_s = U { use(s') | find(s') == find(s) }
P_t = U { use(t') | find(t') == find(t) }

Pozovi union(s,t); // spaja dve klase ekvivalencije relacije R u jednu

Za sve parove (s1,t1) iz P_s * P_t
  ako je find(s1) != find(t1) i Congruent(s1, t1) == true
    pozovi Merge(s1, t1);

Kraj;

Funkcija Congruent(s, t):
------------------------

Pocetak:

Ako su vodeci funkcijski simboli u s i t razliciti, vrati false;

Ako je za bilo koji od odgovarajucih neposrednih podtermova si i ti
find(si) != find(ti) vrati false;

U suprotnom, na kraju vrati true;

Kraj;
-----------------------------------------------------------------------

0) E = { f(a,b) = a, f(b, a) = b }.  E |= f(f(a,b),f(b,a)) = a

E = { f(a,b) = a, f(b, a) = b }
T = {a, b, f(a,b), f(b,a), f(f(a,b), f(b,a)) }

use(a) = {f(a,b), f(b,a)}
use(b) = {f(a,b), f(b,a)}
use(f(a,b)) = {f(f(a,b), f(b,a))}
use(f(b,a)) = {f(f(a,b), f(b,a))}
use(f(f(a,b),f(b,a)))) = {}

R = {a} , {b} , {f(a,b)}, {f(b,a)}, {f(f(a,b),f(b,a))}

Jednakost f(a,b) = a:
---------------------
find(f(a,b)) != find(a) => Merge(f(a,b), a):
--------------------------------------------
    P_f(a,b) = {f(f(a,b), f(b,a))}
    P_a  = { f(a,b), f(b,a) }

    union(f(a,b), a)

R = {a, f(a,b)} , {b} , {f(b,a)}, {f(f(a,b),f(b,a))}

    Za svaki par iz P_f(a,b) x P_a:

    find(f(f(a,b), f(b,a))) != find(f(a,b))
    Congruent(f(f(a,b), f(b,a)), f(a,b))
    ------------------------------------
        f = f
        find(f(a,b)) = find(a) 
        find(f(b,a)) != find(b)    => false
    ------------------------------------
    
    find(f(f(a,b), f(b,a))) != find(f(b,a))
    Congruent(f(f(a,b), f(b,a)), f(b,a))
    -------------------------------------
        f = f
        find(f(a,b)) != find(b) => false
    --------------------------------------
------------------------------------------
Jednakost: f(b, a) = b
find(f(b,a)) != find(b) => Merge(f(b,a), b)
--------------------------------------------
   P_f(b,a) = { f(f(a,b),f(b,a)) }
   P_b      = { f(a,b), f(b,a) }

   union(f(b,a), b)

R = {a, f(a,b)} , {b, f(b,a)}, {f(f(a,b),f(b,a))}

   Par: f(f(a,b), f(b,a)), f(a,b)
   find(f(f(a,b), f(b,a))) != f(a,b)
   Congruent(f(f(a,b), f(b,a)), f(a,b)
   -----------------------------------
       f = f
       find(f(a,b)) = find(a) 
       find(f(b,a)) = find(b)  => true
   ------------------------------------
         Merge(f(f(a,b),f(b,a)), f(a,b)):
         --------------------------------
           P_f(f(a,b), f(b,a)) = { }
           P_f(a,b) = { f(a,b), f(b,a), f(f(a,b),f(b,a)) }

           union(f(f(a,b), f(b,a)), f(a,b))

	   R = {a, f(a,b), f(f(a,b),f(b,a))} , {b, f(b,a)}
  
           Nema parova
         ---------------------------------
   Par: f(f(a,b), f(b,a)), f(b,a)
   find(f(f(a,b), f(b,a))) != f(b,a)
   Congruent(f(f(a,b), f(b,a)), f(b,a))
   -----------------------------------
       f = f
       find(f(a,b)) != find(b)  => false
   ------------------------------------
 ----------------------------------------

Na kraju dobijamo relaciju:

R = {a, f(a,b), f(f(a,b),f(b,a))} , {b, f(b,a)}

Iz ovoga sledi da vazi: E |= f(f(a,b),f(b,a)) = a 

1) (Ax)(Ay)(y = f(x) /\ x = g(y) ==> x = g(f(x)))

Formula je valjana akko je njena negacija nezadovoljiva:
(Ex)(Ey)(y = f(x) /\ x = g(y) /\ x != g(f(x)))

Nakon skolemizacije (na dalje smatramo x i y konstantama):
(y = f(x) /\ x = g(y) /\ x != g(f(x)))

-----------------------------------------------------
NAPOMENA: formula se dalje transformise u DNF:

A1 \/ A2 \/ ... \/ An

gde je svaka od Ak:

Ak = E1 /\ E2 /\ ... /\ D1 /\ D2 ... 

gde su Ei jednakosti a Dj razlicitosti. Za svaku od
konjunkcija Ak proveravamo zadovoljivost. Formula u
DNF-u je nezadovoljiva akko su sve Ak nezadovoljive.
Za ispitivanje (ne)zadovoljivosti konjunkcije Ak
koristimo NelsonOppen-ov algoritam za kongruentno
zatvorenje. Za skup E uzimamo sve jednakosti Ei iz
konjunkcije, a za skup termova T uzimamo skup koji
obuhvata sve termove koji se pojavljuju bilo u 
jednakostima Ei bilo u razlicitostima Dj i koji je
zatvoren za podtermove. Nakon formiranja kongruentnog
zatvorenja za svaku od razlicitosti proveravamo da li
su joj leva i desna strana u istoj klasi ekvivalencije.
Ako bar za jednu jesu, tada je formula nezadovoljiva.
----------------------------------------------------

E = { y = f(x), x = g(y) }
T = { x, y, f(x), g(y), g(f(x)) }

use(x) = { f(x) }
use(y) = { g(y) }
use(f(x)) = { g(f(x)) }
use(g(y)) = { }
use(g(f(x)) = { }

Klase ekvivalencije relacije R u pocetnom stanju:

R: { x } , { y } , { f(x) }, { g(y) }, { g(f(x)) }

find(y) = y != find(f(x)) = f(x)

Merge(y, f(x)):
---------------

   Py = { g(y) }

   P_f(x) = { g(f(x)) }

   union(y, f(x)):
   ---------------
   R: { x }, { y, f(x) } , { g(y) }, { g(f(x)) }

   Za sve parove iz Py i P_f(x): 
  
   par (g(y), g(f(x))

   find(g(y)) != find(g(f(x)))

   Congruent(g(y), g(f(x))):
   -------------------------

      g = g

      find(y) = find(f(x)) = y  ==> true
   --------------------------

   Merge(g(y), g(f(x))):
   ---------------------

       P_g(y) = { }

       P_g(f(x)) = { }

      union(g(y), g(f(x))):
      ---------------------
      R: { x }, { y, f(x) } , { g(y),  g(f(x)) }
     
      Za svaki par iz P_g(y) i P_g(f(x)):
      
      nema parova
   -----------------------
   
   nema vise parova u P_y, P_f(x)
--------------------------------------------

Prelazimo na sledecu jednakost iz E:

find(x) != find(g(y))

Merge(x, g(y)):
---------------

P_x = { f(x) }

P_g(y) = { }

union(x, g(y)):

R: { y, f(x) } , {x,  g(y),  g(f(x)) }

Nema parova u P_x, P_g(y) 
-----------------------------------------

Ovim se algoritam zavrsava i kongruentno zatvorenje polazne
relacije je:
R: { y, f(x) } , {x,  g(y),  g(f(x)) }

Odavde sledi da E |= x = g(f(x)). Kako u konjunkciji imamo
razlicitost x != g(f(x)) sledi da je formula nezadovoljiva, pa
je polazna formula valjana.
-----------------------------------------------------------

2) (Ax) (x = f(x) ==> x = f(f(f(x))))

Negacija:
(Ex) (x = f(x) /\ x != f(f(f(x))))

Nakon skolemizacije:
(x = f(x) /\ x != f(f(f(x))))

DNF:

x = f(x) /\ x != f(f(f(x)))

E = { x = f(x) }
T = { x, f(x), f(f(x)), f(f(f(x))) }

use(x) = { f(x) }
use(f(x)) = { f(f(x)) }
use(f(f(x))) = { f(f(f(x))) }
use(f(f(f(x)))) = {}

R: { x } { f(x) } { f(f(x)) } { f(f(f(x))) }

find(x) != find(f(x))

Merge(x, f(x)):
---------------

P_x = { f(x) }
P_f(x) = { f(f(x)) }

union(x, f(x)):

R: { x , f(x) } { f(f(x)) } { f(f(f(x))) }

za sve parove iz P_x, P_f(x)

  (f(x), f(f(x)))
   
   find(f(x)) != find(f(f(x)))
   
   Congruent(f(x), f(f(x))):
   -------------------------
   f = f

   find(x) = find(f(x))  ==> true
   ---------------------------

   Merge(f(x), f(f(x))):
   ---------------------

   P_f(x) = { f(x), f(f(x)) }
   P_f(f(x)) = { f(f(f(x))) }

   union(f(x), f(f(x))):

   R: { x , f(x),  f(f(x)) } { f(f(f(x))) }

   za sve parove iz P_f(x), P_f(f(x)):
   
   (f(x), f(f(f(x)))):

   find(f(x)) != find(f(f(f(x))))

      Congruent(f(x), f(f(f(x)))):
      ----------------------------

       f = f

       find(x) = find(f(f(x))) ==> true
      --------------------------------

      Merge(f(x), f(f(f(x)))):
      ------------------------

      P_f(x) = { f(x), f(f(x)), f(f(f(x))) }
      P_f(f(f(x))) = { }

      union(f(X), f(f(f(x)))):
 
      R: { x , f(x),  f(f(x)), f(f(f(x))) }

      nema parova u P_f(x), P_f(f(f(x)))
      ---------------------------

  par f(f(x)), f(f(f(x)))

  find(f(f(x))) = find(f(f(f(x)))) pa ne pozivamo Merge
  -------------------------------------------------------

nema vise parova u P_x, P_f(x)
----------------------------------------------------

nema vise jednakosti u E. Kraj algoritma. Kongruentno 
zatvorenje relacije je: R: { x , f(x),  f(f(x)), f(f(f(x))) }
----------------------------------------------------

Odavde sledi da je E |= x = f(f(f(x))), pa s obzirom na
razlicitost x != f(f(f(x))) u polaznoj konjunkciji sledi
da je formula nezadovoljiva, tj. pocetna formula je valjana.
-------------------------------------------------------------

3) (Ax)(Ay)((y = f(x) /\ x = f(y)) => y = f(f(x)))

Negacija:
(Ex)(Ey)(y = f(x) /\ x = f(y) /\ y != f(f(x)))

Skolemizacija:
(y = f(x) /\ x = f(y) /\ y != f(f(x)))


DNF:
y = f(x) /\ x = f(y) /\ y != f(f(x))

E = { y = f(x), x = f(y) }
T = {x, y, f(x), f(y), f(f(x)) }

use(x) = { f(x) }
use(y) = { f(y) }
use(f(x)) = { f(f(x)) }
use(f(y)) = {}
use(f(f(x))) = {}

R: {x} , {y}, {f(x)}, {f(y)}, {f(f(x))}

find(y) != find(f(x))

Merge(y, f(x))
--------------

P_y = { f(y) }
P_f(x) = { f(f(x)) }

union(y, f(x)):
---------------
R: {x} , {y, f(x)}, {f(y)}, {f(f(x))}

za sve parove u P_y, P_f(x)
 
  par (f(y), f(f(x)))
  
  find(f(y)) != f(f(x))

  Congruent(f(y), f(f(x))):
  -------------------------
  f = f
  find(y) = find(f(x)) ==> true
  -------------------------

  Merge(f(y), f(f(x))):
  ---------------------

  P_f(y) = {}
  P_f(f(x)) = {}

  union(f(y), f(f(x))):
  --------------------- 
  R: {x} , {y, f(x)}, {f(y), f(f(x))}

  nema parova u P_f(y), P_f(f(x))
  ---------------------------------

nema vise parova u P_y, P_f(x)
----------------------------------

Prelazimo na sledecu jednakost iz E

find(x) != find(f(y))

Merge(x, f(y)):
---------------

P_x = {f(x)}
P_f(y) = {}

union(x, f(y)):
---------------
R: {y, f(x)}, {x, f(y), f(f(x))}

nema parova u P_x, P_f(y)
---------------------------------

nema vise jednakosti u E. Algoritam je
zavrsen. Kongruentno zatvorenje relacije je
R: {y, f(x)}, {x, f(y), f(f(x))}

Odavde ne sledi da je E |= y = f(f(x)) jer su
y i f(f(x)) u razlicitim klasama ekvivalencije.
Otuda konjunkcija nije nezadovoljiva, pa je 
DNF zadovoljiva, a polazna formula nije valjana.

4) (Ax)(f(x,y) = f(y,x) /\ g(x,y) = f(f(x,y),f(y,x)) /\ 
   	       	           g(y,x) = f(f(y,x), f(x,y)) => g(x,y) = g(y,x))


Negacija i skolemizacija:

f(x,y) = f(y,x) /\ g(x,y) = f(f(x,y),f(y,x)) /\
g(y,x) = f(f(y,x), f(x,y)) /\ g(x,y) != g(y,x)

E = { f(x,y) = f(y,x),
      g(x,y) = f(f(x,y),f(y,x)),
      g(y,x) = f(f(y,x), f(x,y))
    }

T = { x, y, f(x, y), f(y, x), g(x, y), g(y, x), f(f(x,y),f(y,x)),
      f(f(y,x),f(x,y)) }

R = { x }, { y }, { f(x, y) }, { f(y, x) }, { g(x, y) }, 
    { g(y, x) }, { f(f(x,y),f(y,x)) }, { f(f(y,x),f(x,y)) }

use(x) = { f(x, y), f(y, x), g(x, y), g(y, x) }
use(y) = { f(x, y), f(y, x), g(x, y), g(y, x) }
use(f(x,y)) = { f(f(x,y), f(y,x)), f(f(y,x),f(x,y)) }
use(f(y,x)) = { f(f(x,y), f(y,x)), f(f(y,x),f(x,y)) }
use(g(x,y)) = { }
use(g(y,x)) = { }
use(f(f(x,y),f(y,x))) = { }
use(f(f(y,x),f(x,y))) = { }
---------------------------------------------------------------

Jednakost f(x,y) = f(y,x)
-------------------------
      Merge(f(x,y), f(y,x)):
      ----------------------
      P(f(x,y) = { f(f(x,y), f(y,x)), f(f(y,x),f(x,y)) }
      P(f(y,x) = { f(f(x,y), f(y,x)), f(f(y,x),f(x,y)) }

      union(f(x,y), f(y,x))
      ---------------------
      R = { x }, { y }, { f(x, y),  f(y, x) }, { g(x, y) }, 
      { g(y, x) }, { f(f(x,y),f(y,x)) }, { f(f(y,x),f(x,y)) }

      Par: f(f(x,y), f(y,x)), f(f(y,x),f(x,y) )
      -----------------------------------------
      find(f(f(x,y), f(y,x))) != find(f(f(y,x),f(x,y)))
            Cogruent(f(f(x,y), f(y,x)), f(f(y,x),f(x,y))):
      	    ------------------
      	    f = f
      	    find(f(x,y)) = find(f(y,x)) = f(x,y)
      	    return true
      	    ------------------
            Merge (..., ...):
            -----------------

            P(f(f(x,y),f(y,x))) = { }
            P(f(f(y,x),f(x,y))) = { }

       	   union(f(f(x,y), f(y,x)), f(f(y,x),f(x,y)))
 	   -------------------------------------------
	   R = { x }, { y }, { f(x, y),  f(y, x) }, { g(x, y) }, 
           { g(y, x) }, { f(f(x,y),f(y,x)), f(f(y,x),f(x,y)) }
	   ------------------
     --------------------------------------------------------

Jednakost:  g(x,y) = f(f(x,y),f(y,x)),

      find(g(x,y) != find(f(f(x,y),f(y,x)))
      Merge(g(x,y), f(f(x,y),f(y,x))):
      --------------------------------
 
      P(g(x,y)) = { }
      P(f(f(x,y),f(y,x))) = { }

      union(g(x,y), f(f(x,y),f(y,x))):
      --------------------------------
      R = { x }, { y }, { f(x, y),  f(y, x) }, 
      { g(y, x) }, { g(x, y), f(f(x,y),f(y,x)), f(f(y,x),f(x,y)) }
      ---------------------------------

Jednakost: g(y,x) = f(f(y,x), f(x,y))
       find(g(y,x)) != f(f(y,x),f(x,y))
       Merge(g(y,x), f(f(y,x), f(x,y)))
       --------------------------------
       P(g(y,x)) = { }
       P(f(f(y,x), f(x,y))) = { }

       union(g(y,x),f(f(y,x), f(x,y)))
       -------------------------------
       R = { x }, { y }, { f(x, y),  f(y, x) }, 
       { g(y, x),  g(x, y), f(f(x,y),f(y,x)), f(f(y,x),f(x,y)) }
      ----------------------------------
-----------------------------------------------------------------

Kraj:
      R = { x }, { y }, { f(x, y),  f(y, x) }, 
      { g(y, x),  g(x, y), f(f(x,y),f(y,x)), f(f(y,x),f(x,y)) }

Sledi da je E |= g(y,x) = g(x,y), pa je negirana polazna formula 
nezadovoljiva, a polazna formula je valjana.


5) Proveriti da li je sledeca formula zadovoljiva u logici prvog reda sa
jednakoscu:

f(g(a)) = g(f(a)) /\ g(g(a)) = f(a) /\ f(f(a)) = g(a) /\ g(f(g(g(a)))) != f(a)


T = { a, f(a), g(a), f(g(a)), g(f(a)), g(g(a)), f(f(a)), f(g(g(a))), g(f(g(g(a)))) }

E = { f(g(a)) = g(f(a)), g(g(a)) = f(a), f(f(a)) = g(a) }

use(a) = {f(a), g(a)}
use(f(a)) = { f(f(a)), g(f(a)) }
use(g(a)) = { g(g(a)), f(g(a)) }
use(f(g(a))) = { }
use(g(f(a))) = { }
use(f(f(a))) = { }
use(g(g(a))) = {f(g(g(a)))}
use(f(g(g(a))))} = {g(f(g(g(a))))}
use(g(f(g(g(a))))) = { }

R = { a }, { f(a)}, {g(a)}, {f(g(a))}, {g(f(a))}, {g(g(a))}, {f(f(a))}, {f(g(g(a)))}, {g(f(g(g(a))))}

Jednakost f(g(a)) = g(f(a)):
find(f(g(a))) != find(g(f(a))) =>
   Merge(f(g(a)), g(f(a)))
   -----------------------

   P_f(g(a)) = { }
   P_g(f(a)) = { }

   union(f(g(a)), g(f(a)));
   R = { a }, { f(a)}, {g(a)}, {f(g(a)), g(f(a))}, {g(g(a))}, {f(f(a))}, {f(g(g(a)))}, {g(f(g(g(a))))}

   nema parova
   -----------------------
Jednakost g(g(a)) = f(a):
find(g(g(a))) != find(f(a)) =>
   Merge(g(g(a)), f(a)):
   ---------------------

   P_g(g(a)) = {f(g(g(a)))}
   P_f(a) = { f(f(a)), g(f(a)) }

   union(g(g(a)), f(a));
   R = { a }, { f(a), g(g(a))}, {g(a)}, {f(g(a)), g(f(a))}, {f(f(a))}, {f(g(g(a)))}, {g(f(g(g(a))))}

   Par f(g(g(a))), f(f(a)):
   find(f(g(g(a)))) != find(f(f(a)))
       Congruent(f(g(g(a)))), f(f(a)):
       ----------------------------------
       f = f
       find(g(g(a))) = find(f(a)) => true
       ----------------------------------
       Merge(f(g(g(a))), f(f(a))):
       ----------------------------------

       P_f(g(g(a))) = { g(f(g(g(a)))) }
       P_f(f(a)) = { }

       union(f(g(g(a))), f(f(a)));
       R = { a }, { f(a), g(g(a))}, {g(a)}, {f(g(a)), g(f(a))}, {f(f(a)), f(g(g(a)))}, {g(f(g(g(a))))}

       nema parova
       ---------------------------------
   Par f(g(g(a))), g(f(a)
   find(f(g(g(a)))) != find(g(g(a)))
       Congruent(f(g(g(a))), g(f(a))):
       -------------------------------
       f != g => false
       -------------------------------
   ------------------------------------------     
Jednakost f(f(a)) = g(a)
find(f(f(a))) != find(g(a)) =>
   Merge(f(f(a)), g(a)):
   ----------------------

   P_f(f(a)) = { g(f(g(g(a)))) }
   P_g(a) = { f(g(a)), g(g(a)) }

   union(f(f(a)), g(a));
   R = { a }, { f(a), g(g(a))}, {f(g(a)), g(f(a))}, {g(a), f(f(a)), f(g(g(a)))}, {g(f(g(g(a))))}

   Par g(f(g(g(a)))), f(g(a)):
   find(g(f(g(g(a))))) != find(f(g(a)))
      Congruent(g(f(g(g(a)))), f(g(a))):
      ----------------------------------
      g != f => false
      ----------------------------------
   Par g(f(g(g(a)))), g(g(a)):
   find(g(f(g(g(a))))) != find(g(g(a)))
      Congruent(g(f(g(g(a)))), g(g(a))):
      ----------------------------------
      g = g
      find(f(g(g(a)))) = find(g(a)) => true
      ---------------------------------
      Merge(g(f(g(g(a)))), g(g(a))):
      ------------------------------

      P_g(f(g(g(a)))) = { }
      P_g(g(a)) = { f(g(g(a))), f(f(a)), g(f(a)) }

      union(g(f(g(g(a)))), g(g(a)));
      R = { a }, {f(g(a)), g(f(a))}, {g(a), f(f(a)), f(g(g(a)))}, { f(a), g(g(a)), g(f(g(g(a))))}

      nema parova
      ------------------------------
   -----------------------------------
nema vise jednakosti
---------------------------------------

R = { a }, {f(g(a)), g(f(a))}, {g(a), f(f(a)), f(g(g(a)))}, { f(a), g(g(a)), g(f(g(g(a))))}

Sledi da mora biti g(f(g(g(a)))) = f(a), odakle je polazna formula nezadovoljiva, jer
sadrzi razlicitost g(f(g(g(a)))) != f(a).
