---------------------------------------------------------------------------- 
-- HNU CE 데이터베이스(2025년 2학기 02분반): FD 관련 프로그래밍 과제 
---------------------------------------------------------------------------- 
-- 이름: 이재혁 
-- 학번: 20210501 
---------------------------------------------------------------------------- 
 
import  Data. List
-- representing sets withi lists 
 
subset xs ys 
=  null   ( xs\\ys
) 	-- subset test in terms of set subtraction  
-- functional dependency A B C -> D E encoded as (["A","B","C"],["D","E"]) 
type  FD =  ( [ Attr] ,  [ Attr] ) 
type  Attr 
=  String    -- attributes are represented as strings  
 
--  3.2.4절 Algorithm 3.7 
closure ::  [ FD]  ->  [ Attr]  ->  [ Attr]    
closure fds xs =  if  xs/= xs1 then  closure fds xs1 else  sort xs
	where  xs1 
=  foldr  union xs 
[ cs 
|  ( bs
, cs
)  <-  fds
,  bs `subset` xs
]   
-- fd가 기존의 fds로부터 유도 가능한지 검사 (closure 활용한 간단한 함수) 
is
_ derived
_ from 
::  FD 
->  [ FD
]  ->  Bool is
_ derived
_ from fd fds 
=  all   ( `
elem ` closureSet
)  rhs
  where 
    closureSet =  closure fds lhs
 
-- 3.2.7절 관련 fds의 basis인지 검사 
is
_ basis
_ of 
::  [ FD
]  ->  [ FD
]  ->  Bool is_ basis_ of basis fds = 
    equivalent basis fds &&              
  where 
 
    nonredundant fd 
=  not   ( is
_ derived
_ from fd 
( filter   ( /=  fd
)  basis
) )  
    equivalent a b = 
        all   ( `is
_ derived
_ from` a
)  b 
&&          all   ( `is
_ derived
_ from` b
)  a
  
--  3.2.7절 basis가 미니멀한지 검사 
is
_ minimal 
::  [ FD
]  ->  Bool is_ minimal basis = 
    all  singletonRHS basis 
&&              all  nonredundant basis 
&&            where 
    singletonRHS 
( _,  rhs
)  =  length  rhs 
==  1  
    nonredundant fd 
=  not   ( is
_ derived
_ from fd 
( remove fd basis
) )  
    minimalLHS ( lhs,  rhs)  = 
            let  lhs' = lhs \\  [a] 
            in if null lhs'  
                then  True      
                else  not   ( is
_ derived
_ from 
( lhs
', rhs) basis)          ) lhs 
 
    remove x = filter (/= x) 
 
-- 3.2.8절 Algorithm 3.12 
project_FDs :: [Attr] -> [FD] -> [Attr] -> [FD] 
project_FDs as fds as1 =  
  let 
    subsets = filter (not . null) (subsequences as1) 
    projected = nub [ (x, [a]) 
                    | x <- subsets 
                    , let xplus = closure fds x 
                    , a <- (xplus \\  x) `intersect` as1 
                    ] 
 
    minimizeLHS fd@(lhs,rhs) =  
      let smaller = [ (lhs \\  [a], rhs) | a <- lhs, length lhs > 1,is_derived_from (lhs \\  [a], rhs) projected ] 
      in case smaller of 
           (fd' :_ )  ->  minimizeLHS fd'  
           []      -> fd 
    minimalT = filter (\f d -> not (is_derived_from fd (filter (/= fd) projected)))  
               (map minimizeLHS projected) 
  in nub minimalT 
-- the main function for running test examples 
main = do 
	-- 
	putStrLn "== my Example for is_minimal test ==========================" 
	let basis = [ (["A"], ["B"]), (["B"], ["C"]) ] 
	putStrLn $ "B = " ++ showFDs basis 
	putStrLn $ "B is minimal : " ++ show (is_minimal basis) 
	putStrLn "" 
 
	let basis = [ (["A", "D"], ["C", "W"]), (["B"], ["C"]) ] 
	putStrLn $ "B = " ++ showFDs basis 
	putStrLn $ "B is minimal : " ++ show (is_minimal basis) 
	putStrLn "" 
	-- 
	putStrLn "== my Example for project_FDs test =========================" 
 
	let l_attrs= ["A", "B", "C", "D", "E", "F"]                 
	let s_fds = [ (["A"], ["B"]) ,(["C"], ["D"]), (["E"], ["E"])]  
	let l1_attrs = ["A", "B", "C"]                    
 
	putStrLn $ "L = " ++ showAttrSet l_attrs 
	putStrLn $ "S = " ++ showFDs s_fds 
	putStrLn $ "L1 = " ++ showAttrSet l1_attrs 
 
	let s1_fds = project_FDs l_attrs s_fds l1_attrs 
	putStrLn $ "S1 = " ++ showFDs s1_fds 
	putStrLn "" 
 
 
	let l_attrs2 = ["A","B","C","D","E","F","G"] 
	let s_fds2 = [ (["A"], ["C"]), (["D"], ["E"]), (["E","F"], ["G"]) ] 
	let l1_attrs2 = ["A","D","E"] 
 
	putStrLn $ "L = " ++ showAttrSet l_attrs2 
	putStrLn $ "S = " ++ showFDs s_fds2 
	putStrLn $ "L1 = " ++ showAttrSet l1_attrs2 
 
	let s2_fds = project_FDs l_attrs2 s_fds2 l1_attrs2 
	putStrLn $ "S1 = " ++ showFDs s2_fds 
	putStrLn "" 
 
	-- 
 
-- helper functions for pretty printing 
showFD :: FD -> String 
showFD (as,bs) = concat $ intersperse " " (as ++ "->" : bs) 
 
showFDs :: [FD] -> String 
showFDs fds = "{" ++ concat (intersperse ", " $ map showFD fds) ++ "}" 
 
showAttrSet :: [Attr] -> String 
showAttrSet as = "{" ++ concat (intersperse "," as) ++ "}" 
 
 
				LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQotLSBITlUgQ0Ug642w7J207YSw67Kg7J207IqkKDIwMjXrhYQgMu2Vmeq4sCAwMuu2hOuwmCk6IEZEIOq0gOugqCDtlITroZzqt7jrnpjrsI0g6rO87KCcCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KLS0g7J2066aEOiDsnbTsnqztmIEKLS0g7ZWZ67KIOiAyMDIxMDUwMQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgppbXBvcnQgRGF0YS5MaXN0Ci0tIHJlcHJlc2VudGluZyBzZXRzIHdpdGhpIGxpc3RzCgpzdWJzZXQgeHMgeXMgPSBudWxsICh4c1xceXMpCS0tIHN1YnNldCB0ZXN0IGluIHRlcm1zIG9mIHNldCBzdWJ0cmFjdGlvbgoKLS0gZnVuY3Rpb25hbCBkZXBlbmRlbmN5IEEgQiBDIC0+IEQgRSBlbmNvZGVkIGFzIChbIkEiLCJCIiwiQyJdLFsiRCIsIkUiXSkKdHlwZSBGRCA9IChbQXR0cl0sIFtBdHRyXSkKdHlwZSBBdHRyID0gU3RyaW5nICAtLSBhdHRyaWJ1dGVzIGFyZSByZXByZXNlbnRlZCBhcyBzdHJpbmdzCgoKLS0gIDMuMi407KCIIEFsZ29yaXRobSAzLjcKY2xvc3VyZSA6OiBbRkRdIC0+IFtBdHRyXSAtPiBbQXR0cl0gICAKY2xvc3VyZSBmZHMgeHMgPSBpZiB4cy89eHMxIHRoZW4gY2xvc3VyZSBmZHMgeHMxIGVsc2Ugc29ydCB4cwoJd2hlcmUgeHMxID0gZm9sZHIgdW5pb24geHMgW2NzIHwgKGJzLGNzKSA8LSBmZHMsIGJzIGBzdWJzZXRgIHhzXQoKLS0gZmTqsIAg6riw7KG07J2YIGZkc+uhnOu2gO2EsCDsnKDrj4Qg6rCA64ql7ZWc7KeAIOqygOyCrCAoY2xvc3VyZSDtmZzsmqntlZwg6rCE64uo7ZWcIO2VqOyImCkKaXNfZGVyaXZlZF9mcm9tIDo6IEZEIC0+IFtGRF0gLT4gQm9vbAppc19kZXJpdmVkX2Zyb20gZmQgZmRzID0gYWxsIChgZWxlbWAgY2xvc3VyZVNldCkgcmhzCiAgd2hlcmUKICAgIGxocyA9IGZzdCBmZAogICAgcmhzID0gc25kIGZkCiAgICBjbG9zdXJlU2V0ID0gY2xvc3VyZSBmZHMgbGhzCiAKLS0gMy4yLjfsoIgg6rSA66CoIGZkc+ydmCBiYXNpc+yduOyngCDqsoDsgqwKaXNfYmFzaXNfb2YgOjogW0ZEXSAtPiBbRkRdIC0+IEJvb2wKaXNfYmFzaXNfb2YgYmFzaXMgZmRzID0KICAgIGVxdWl2YWxlbnQgYmFzaXMgZmRzICYmICAgICAgICAgICAgIAogICAgYWxsIG5vbnJlZHVuZGFudCBiYXNpcyAgICAgICAgICAgICAgICAKICB3aGVyZQogICAgCiAgICBub25yZWR1bmRhbnQgZmQgPSBub3QgKGlzX2Rlcml2ZWRfZnJvbSBmZCAoZmlsdGVyICgvPSBmZCkgYmFzaXMpKQoKICAgIGVxdWl2YWxlbnQgYSBiID0KICAgICAgICBhbGwgKGBpc19kZXJpdmVkX2Zyb21gIGEpIGIgJiYKICAgICAgICBhbGwgKGBpc19kZXJpdmVkX2Zyb21gIGIpIGEKCi0tICAzLjIuN+ygiCBiYXNpc+qwgCDrr7jri4jrqYDtlZzsp4Ag6rKA7IKsCmlzX21pbmltYWwgOjogW0ZEXSAtPiBCb29sCmlzX21pbmltYWwgYmFzaXMgPQogICAgYWxsIHNpbmdsZXRvblJIUyBiYXNpcyAmJiAgICAgICAgCiAgICBhbGwgbm9ucmVkdW5kYW50IGJhc2lzICYmICAgICAgICAKICAgIGFsbCBtaW5pbWFsTEhTIGJhc2lzICAgICAgICAgICAgIAogIHdoZXJlCiAgICBzaW5nbGV0b25SSFMgKF8sIHJocykgPSBsZW5ndGggcmhzID09IDEKCiAgICBub25yZWR1bmRhbnQgZmQgPSBub3QgKGlzX2Rlcml2ZWRfZnJvbSBmZCAocmVtb3ZlIGZkIGJhc2lzKSkKCiAgICBtaW5pbWFsTEhTIChsaHMsIHJocykgPQogICAgICAgIGFsbCAoXGEgLT4KICAgICAgICAgICAgbGV0IGxocycgPSBsaHMgXFwgW2FdCiAgICAgICAgICAgIGluIGlmIG51bGwgbGhzJyAKICAgICAgICAgICAgICAgIHRoZW4gVHJ1ZSAgICAgIAogICAgICAgICAgICAgICAgZWxzZSBub3QgKGlzX2Rlcml2ZWRfZnJvbSAobGhzJywgcmhzKSBiYXNpcykKICAgICAgICApIGxocwoKICAgIHJlbW92ZSB4ID0gZmlsdGVyICgvPSB4KQoKLS0gMy4yLjjsoIggQWxnb3JpdGhtIDMuMTIKcHJvamVjdF9GRHMgOjogW0F0dHJdIC0+IFtGRF0gLT4gW0F0dHJdIC0+IFtGRF0KcHJvamVjdF9GRHMgYXMgZmRzIGFzMSA9IAogIGxldAogICAgc3Vic2V0cyA9IGZpbHRlciAobm90IC4gbnVsbCkgKHN1YnNlcXVlbmNlcyBhczEpCiAgICBwcm9qZWN0ZWQgPSBudWIgWyAoeCwgW2FdKQogICAgICAgICAgICAgICAgICAgIHwgeCA8LSBzdWJzZXRzCiAgICAgICAgICAgICAgICAgICAgLCBsZXQgeHBsdXMgPSBjbG9zdXJlIGZkcyB4CiAgICAgICAgICAgICAgICAgICAgLCBhIDwtICh4cGx1cyBcXCB4KSBgaW50ZXJzZWN0YCBhczEKICAgICAgICAgICAgICAgICAgICBdCiAgICAgICAgICAgICAgICAgICAgCiAgICBtaW5pbWl6ZUxIUyBmZEAobGhzLHJocykgPSAKICAgICAgbGV0IHNtYWxsZXIgPSBbIChsaHMgXFwgW2FdLCByaHMpIHwgYSA8LSBsaHMsIGxlbmd0aCBsaHMgPiAxLGlzX2Rlcml2ZWRfZnJvbSAobGhzIFxcIFthXSwgcmhzKSBwcm9qZWN0ZWQgXQogICAgICBpbiBjYXNlIHNtYWxsZXIgb2YKICAgICAgICAgICAoZmQnOl8pIC0+IG1pbmltaXplTEhTIGZkJyAKICAgICAgICAgICBbXSAgICAgIC0+IGZkCiAgICBtaW5pbWFsVCA9IGZpbHRlciAoXGZkIC0+IG5vdCAoaXNfZGVyaXZlZF9mcm9tIGZkIChmaWx0ZXIgKC89IGZkKSBwcm9qZWN0ZWQpKSkgCiAgICAgICAgICAgICAgIChtYXAgbWluaW1pemVMSFMgcHJvamVjdGVkKQogIGluIG51YiBtaW5pbWFsVAotLSB0aGUgbWFpbiBmdW5jdGlvbiBmb3IgcnVubmluZyB0ZXN0IGV4YW1wbGVzCm1haW4gPSBkbwoJLS0KCXB1dFN0ckxuICI9PSBteSBFeGFtcGxlIGZvciBpc19taW5pbWFsIHRlc3QgPT09PT09PT09PT09PT09PT09PT09PT09PT0iCglsZXQgYmFzaXMgPSBbIChbIkEiXSwgWyJCIl0pLCAoWyJCIl0sIFsiQyJdKSBdCglwdXRTdHJMbiAkICJCID0gIiArKyBzaG93RkRzIGJhc2lzCglwdXRTdHJMbiAkICJCIGlzIG1pbmltYWwgOiAiICsrIHNob3cgKGlzX21pbmltYWwgYmFzaXMpCglwdXRTdHJMbiAiIgoJCglsZXQgYmFzaXMgPSBbIChbIkEiLCAiRCJdLCBbIkMiLCAiVyJdKSwgKFsiQiJdLCBbIkMiXSkgXQoJcHV0U3RyTG4gJCAiQiA9ICIgKysgc2hvd0ZEcyBiYXNpcwoJcHV0U3RyTG4gJCAiQiBpcyBtaW5pbWFsIDogIiArKyBzaG93IChpc19taW5pbWFsIGJhc2lzKQoJcHV0U3RyTG4gIiIKCS0tCglwdXRTdHJMbiAiPT0gbXkgRXhhbXBsZSBmb3IgcHJvamVjdF9GRHMgdGVzdCA9PT09PT09PT09PT09PT09PT09PT09PT09IgoKCWxldCBsX2F0dHJzPSBbIkEiLCAiQiIsICJDIiwgIkQiLCAiRSIsICJGIl0gICAgICAgICAgICAgICAgCglsZXQgc19mZHMgPSBbIChbIkEiXSwgWyJCIl0pICwoWyJDIl0sIFsiRCJdKSwgKFsiRSJdLCBbIkUiXSldIAoJbGV0IGwxX2F0dHJzID0gWyJBIiwgIkIiLCAiQyJdICAgICAgICAgICAgICAgICAgIAoKCXB1dFN0ckxuICQgIkwgPSAiICsrIHNob3dBdHRyU2V0IGxfYXR0cnMKCXB1dFN0ckxuICQgIlMgPSAiICsrIHNob3dGRHMgc19mZHMKCXB1dFN0ckxuICQgIkwxID0gIiArKyBzaG93QXR0clNldCBsMV9hdHRycwoKCWxldCBzMV9mZHMgPSBwcm9qZWN0X0ZEcyBsX2F0dHJzIHNfZmRzIGwxX2F0dHJzCglwdXRTdHJMbiAkICJTMSA9ICIgKysgc2hvd0ZEcyBzMV9mZHMKCXB1dFN0ckxuICIiCgoKCWxldCBsX2F0dHJzMiA9IFsiQSIsIkIiLCJDIiwiRCIsIkUiLCJGIiwiRyJdCglsZXQgc19mZHMyID0gWyAoWyJBIl0sIFsiQyJdKSwgKFsiRCJdLCBbIkUiXSksIChbIkUiLCJGIl0sIFsiRyJdKSBdCglsZXQgbDFfYXR0cnMyID0gWyJBIiwiRCIsIkUiXQoKCXB1dFN0ckxuICQgIkwgPSAiICsrIHNob3dBdHRyU2V0IGxfYXR0cnMyCglwdXRTdHJMbiAkICJTID0gIiArKyBzaG93RkRzIHNfZmRzMgoJcHV0U3RyTG4gJCAiTDEgPSAiICsrIHNob3dBdHRyU2V0IGwxX2F0dHJzMgoJCglsZXQgczJfZmRzID0gcHJvamVjdF9GRHMgbF9hdHRyczIgc19mZHMyIGwxX2F0dHJzMgoJcHV0U3RyTG4gJCAiUzEgPSAiICsrIHNob3dGRHMgczJfZmRzCglwdXRTdHJMbiAiIgoKCS0tCgotLSBoZWxwZXIgZnVuY3Rpb25zIGZvciBwcmV0dHkgcHJpbnRpbmcKc2hvd0ZEIDo6IEZEIC0+IFN0cmluZwpzaG93RkQgKGFzLGJzKSA9IGNvbmNhdCAkIGludGVyc3BlcnNlICIgIiAoYXMgKysgIi0+IiA6IGJzKQoKc2hvd0ZEcyA6OiBbRkRdIC0+IFN0cmluZwpzaG93RkRzIGZkcyA9ICJ7IiArKyBjb25jYXQgKGludGVyc3BlcnNlICIsICIgJCBtYXAgc2hvd0ZEIGZkcykgKysgIn0iCgpzaG93QXR0clNldCA6OiBbQXR0cl0gLT4gU3RyaW5nCnNob3dBdHRyU2V0IGFzID0gInsiICsrIGNvbmNhdCAoaW50ZXJzcGVyc2UgIiwiIGFzKSArKyAifSIK