"""5-NN pada Ruspini. 80% baris pertama tiap kelas = latih, sisanya uji."""

import csv
import math
from collections import Counter, defaultdict
from pathlib import Path

CSV = Path(__file__).with_name("5. ruspini.csv")
K = 5
# Satu contoh per kelas di keluaran. Kelas 3 memakai (54, 124), titik yang turun sendiri.
SHOWN = {(30, 52), (111, 126), (54, 124), (76, 27)}


def load():
    rows = []
    with CSV.open() as f:
        for i, rec in enumerate(csv.DictReader(f), start=1):
            rows.append((i, float(rec["X"]), float(rec["Y"]), int(rec["CLASS"])))
    return rows


def split(rows):
    by = defaultdict(list)
    for row in rows:
        by[row[3]].append(row)
    train, test = [], []
    for label in sorted(by):
        items = by[label]
        n = round(len(items) * 0.8)
        train += items[:n]
        test += items[n:]
    return train, test


def predict(point, train):
    nearest = sorted(
        train,
        key=lambda tr: (math.hypot(point[1] - tr[1], point[2] - tr[2]), tr[0]),
    )[:K]
    return Counter(tr[3] for tr in nearest).most_common(1)[0][0]


def main():
    rows = load()
    train, test = split(rows)
    got = [(point, predict(point, train)) for point in test]
    wrong = sum(pred != point[3] for point, pred in got)
    assert len(rows) == 75 and len(train) == 60 and len(test) == 15 and wrong == 0

    print(f"{'Total data':<13}: {len(rows)}")
    print(f"{'Data training':<13}: {len(train)}")
    print(f"{'Data testing':<13}: {len(test)}")
    print()
    print(" Titik uji | Asli | Prediksi | Status")
    print("-" * 38)
    for point, pred in got:
        if (point[1], point[2]) not in SHOWN:
            continue
        status = "benar" if pred == point[3] else "salah"
        print(f"({point[1]:4.0f},{point[2]:4.0f}) | {point[3]} | {pred} | {status}")
    if wrong == 0:
        print(f"... ({len(test)} data uji, semua benar) ...")
    print("-" * 38)
    print()
    rate = wrong / len(test) * 100
    print(f"{'Jumlah error':<13}: {wrong} dari {len(test)}")
    print(f"{'Error rate':<13}: {rate:.2f}%")
    print(f"{'Akurasi':<13}: {100 - rate:.2f}%")


if __name__ == "__main__":
    main()
