from __future__ import annotations

import random

from synaptopus.adaline import AdalineNetwork, MadalineNetwork


def test_adaline_learns_bipolar_and() -> None:
    network = AdalineNetwork.random(
        input_size=2,
        learning_rate=0.2,
        rng=random.Random(5),
    )
    samples = (
        ((-1.0, -1.0), -1.0),
        ((-1.0, 1.0), -1.0),
        ((1.0, -1.0), -1.0),
        ((1.0, 1.0), 1.0),
    )

    for _ in range(30):
        for inputs, target in samples:
            network.train_step(inputs, target)

    predictions = {
        inputs: network.classify(inputs)
        for inputs, _ in samples
    }

    assert predictions[(-1.0, -1.0)] == -1
    assert predictions[(-1.0, 1.0)] == -1
    assert predictions[(1.0, -1.0)] == -1
    assert predictions[(1.0, 1.0)] == 1


def test_adaline_round_trips_through_dict() -> None:
    network = AdalineNetwork.random(
        input_size=3,
        learning_rate=0.15,
        rng=random.Random(8),
    )

    restored = AdalineNetwork.from_dict(network.to_dict())

    assert restored.input_size == network.input_size
    assert restored.weights == network.weights
    assert restored.bias == network.bias


def test_madaline_learns_bipolar_or() -> None:
    network = MadalineNetwork.random(
        input_size=2,
        hidden_unit_count=2,
        learning_rate=0.15,
        rng=random.Random(9),
    )
    samples = (
        ((-1.0, -1.0), -1),
        ((-1.0, 1.0), 1),
        ((1.0, -1.0), 1),
        ((1.0, 1.0), 1),
    )

    for _ in range(60):
        for inputs, target in samples:
            network.train_step(inputs, target)

    predictions = {
        inputs: network.classify(inputs)
        for inputs, _ in samples
    }

    assert predictions[(-1.0, -1.0)] == -1
    assert predictions[(-1.0, 1.0)] == 1
    assert predictions[(1.0, -1.0)] == 1
    assert predictions[(1.0, 1.0)] == 1
