import numpy as np

# Define inputs for all truth tables
inputs = np.array([
    [0, 0],
    [0, 1],
    [1, 0],
    [1, 1]
])

# Define all possible truth tables as target outputs
truth_tables = [
    [0, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0], [0, 0, 1, 1],
    [0, 1, 0, 0], [0, 1, 0, 1], [0, 1, 1, 0], [0, 1, 1, 1],
    [1, 0, 0, 0], [1, 0, 0, 1], [1, 0, 1, 0], [1, 0, 1, 1],
    [1, 1, 0, 0], [1, 1, 0, 1], [1, 1, 1, 0], [1, 1, 1, 1]
]

# Define the perceptron learning function
def perceptron_learning(inputs, targets, learning_rate=0.1, max_epochs=1000):
    # Initialize weights and bias with random values (can be negative)
    weights = np.random.uniform(-1, 1, inputs.shape[1])
    bias = np.random.uniform(-1, 1)
    
    for epoch in range(max_epochs):
        error_count = 0
        for i, input_vector in enumerate(inputs):
            # Compute the perceptron output
            linear_combination = np.dot(input_vector, weights) + bias
            output = 1 if linear_combination >= 0 else 0
            
            # Calculate the error
            error = targets[i] - output
            
            # Update weights and bias if there is an error
            if error != 0:
                weights += learning_rate * error * input_vector
                bias += learning_rate * error
                error_count += 1
        
        # If no errors, learning is complete
        if error_count == 0:
            break
    
    return weights, bias, epoch + 1

# Test perceptron for all truth tables
results = []
for idx, table in enumerate(truth_tables):
    print(f"\n=== Truth Table {idx + 1}: Targets = {table} ===")
    weights, bias, epochs = perceptron_learning(inputs, table)
    
    # Test the final perceptron on inputs
    outputs = [1 if np.dot(input_vector, weights) + bias >= 0 else 0 for input_vector in inputs]
    
    # Display results
    print(f"Final Weights: {weights}")
    print(f"Final Bias: {bias}")
    print(f"Epochs to Learn: {epochs}")
    print(f"Final Test Output: {outputs}")
    
    # Store the results
    results.append({
        "truth_table": table,
        "weights": weights.tolist(),
        "bias": bias,
        "epochs": epochs,
        "outputs": outputs
    })
