# ML Program 1 - Candidate Elimination Algorithm
import pandas as pd
import numpy as np

def covers(h, x):
    """Check if hypothesis h covers instance x"""
    return all(h[i] == '?' or h[i] == x[i] for i in range(len(h)))

def more_general_or_equals(h1, h2):
    """Check if h1 is more general than or equal to h2"""
    return all(h1[i] == h2[i] or h1[i] == '?' for i in range(len(h1)))

data = pd.read_csv("ML1-Dataset.csv")
print(data)
print("--------------------------------------------------------------------------------")
print(data.shape)

X = data.iloc[:, 1:-1]
print(X)

Y = data.iloc[:, -1]
print(Y)

# Initialize variables/hypothesis
nums_attributes = X.shape[1]
S = ['0'] * nums_attributes
G = [['?'] * nums_attributes]

# Candidate Elimination algorithm
for i, x in X.iterrows():
    x = list(x)
    if Y[i] == "Yes":  # Positive Example
        # Remove G hypothesis that do not cover x
        G = [g for g in G if covers(g, x)]

        # Generalize S Minimally
        for j in range(nums_attributes):
            if S[j] == '0':
                S[j] = x[j]
            elif S[j] != x[j]:
                S[j] = '?'

    else:  # Negative Example
        G_new = []

        for g in G:
            if covers(g, x):
                for j in range(nums_attributes):
                    if g[j] == '?':
                        if S[j] != '?' and S[j] != x[j]:
                            g_copy = g.copy()
                            g_copy[j] = S[j]

                            # Valid specialization checks
                            if more_general_or_equals(g_copy, S) and not covers(g_copy, x):
                                G_new.append(g_copy)
            else:
                G_new.append(g)

        G = G_new

# Remove Duplicate
G = [list(g) for g in set(tuple(g) for g in G)]

print("Final Specific hypothesis (S):")
print(S, "\n")
print("---------------------------------------------------")
print("\nFinal General hypothesis (G):")
for g in G:
    print(g)
