import numpy as np


def mean_normalization(Dataset):
    max_first = np.max(Dataset[:, 1])
    max_second = np.max(Dataset[:, 2])
    min_first = np.min(Dataset[:, 1])
    min_second = np.min(Dataset[:, 2])
    mean_first = np.mean(Dataset[:, 1])
    mean_second = np.mean(Dataset[:, 2])
    for data in Dataset:
        data[1] = (data[1] - mean_first) / (max_first - min_first)
        data[2] = (data[2] - mean_second) / (max_second - min_second)
    return Dataset, [max_first, min_first, mean_first, max_second, min_second, mean_second]


def sigmoid(x):
    return 1 / (1 + np.exp(-x))


# Call for each training set, Individually.

def compute_cost(X, y, theta):
    m = len(y)
    h = sigmoid(np.dot(X, theta))
    cost = (1 / m) * (np.dot(-y.T, np.log(h)) - np.dot((1 - y).T, np.log(1 - h)))
    return cost


# Call for each training set, Individually.


def gradient_descent(X, y, theta, learning_rate, num_iterations):
    m = len(y)
    cost_history = []
    for i in range(num_iterations):
        theta = theta - (learning_rate / m) * (X.T @ (sigmoid(X @ theta) - y))
        cost_history.append([compute_cost(X, y, theta)])
    return cost_history, theta


def prediction(data, optimum_theta):
    data[1] = (data[1] - max_min_list[2]) / (max_min_list[0] - max_min_list[1])
    data[2] = (data[2] - max_min_list[5]) / (max_min_list[3] - max_min_list[4])
    sigmoid_value = sigmoid(data @ optimum_theta)
    if sigmoid_value >= 0.5:
        print("Student will be admitted.")
    else:
        print("Student will be rejected.")


def initial_data():
    dataset = np.loadtxt("Datasets/data1.txt", dtype=str)
    Dataset = []
    result = []

    for data in dataset:
        data = data.split(",")
        Dataset.append([1, float(data[0]), float(data[1])])
        result.append([float(data[2])])
    Dataset = np.array(Dataset, dtype=np.float64)
    result = np.array(result, dtype=np.float64)
    Dataset, max_min_list = mean_normalization(Dataset)
    return Dataset, result, max_min_list


def accuracy(X, y, theta, cutoff):
    pred = [sigmoid(np.dot(X, theta)) >= cutoff]
    acc = np.mean(pred == y)
    print(acc * 100)


X, y, max_min_list = initial_data()
m, n = X.shape
learning_rate = 0.1
num_iterations = 400
theta = np.zeros((n, 1))
cost_history, optimum_theta = gradient_descent(X, y, theta, learning_rate, num_iterations)
predict = np.array([1, 82.3, 93.3])
prediction(predict, optimum_theta)

