import random
import time
import matplotlib.pyplot as plt


def read_input(filename: str):
    with open(filename, "r") as f:
        return [int(line.strip()) for line in f]


def smallest_difference_brute_force(array):
    """
       Returns the minimum difference between any two array elements

       Args:
           array (list): The values to be considered.

       Tests:
           >>> array = [-100, -10, 10, 100]
           >>> smallest_difference_brute_force(array)
           20

           >>> array = [-10,10]
           >>> smallest_difference_brute_force(array)
           20
    """
    # Your code here...
    pass


def smallest_difference_faster(array):
    """
       Returns the minimum difference between any two array elements

       Args:
           array (list): The values to be considered.

       Tests:
           >>> array = [-100, -10, 10, 100]
           >>> smallest_difference_brute_force(array)
           20

           >>> array = [-10,10]
           >>> smallest_difference_brute_force(array)
           20
    """
    # Your code here...
    pass

###############################################################################
###############################################################################
###############################################################################


def time_measurement(algorithm, size_range, verifier=None):
    """
       Performs timed tests and returns time measurements.

       Args:
           algorithm: The sorting algorithm to be tested.
           size_range (range): specifies the input sizes for the measurements.

       Returns:
           list: list of execution times.

    """
    results = []
    # Loop from 'skip' up to 'range_n', stepping by 'skip'
    for n in size_range:
        input = [random.randint(0, 10000) for _ in range(n)]
        start_time = time.time()
        output = algorithm(input)
        end_time = time.time()
        results.append(end_time - start_time)

        # Verify that the array was actually sorted
        if verifier is not None:
            if not verifier(input, output):
                raise Exception("Verifier complained!")

    return results


def plot_results(x_values, algorithm_data, use_log_scale=False):
    """
    Plots the execution times of the sorting algorithms.

    Args:
        x_values (list): The input sizes (n) tested.
        algorithm_data (list): Pairs of algorithm names and runtime data.
        use_log_scale (bool): If True, plots the Y-axis on a logarithmic scale.
    """
    plt.figure(figsize=(10, 6))

    # Plot algorithms
    for (algo_name, times) in algorithm_data:
        plt.plot(x_values, times, label=algo_name, linewidth=2)

    # Adjust axis and titles based on the scale choice
    if use_log_scale:
        plt.yscale('log')
        plt.ylabel('Execution Time (seconds) [Log Scale]')
        plt.title('Time Complexity (Logarithmic Scale)')
    else:
        plt.ylabel('Execution Time (seconds)')
        plt.title('Time Complexity')

    # Format the rest of the chart
    plt.xlabel('Input Size (n)')
    plt.legend()
    plt.grid(True, linestyle='--', alpha=0.7)

    # Display the plot
    plt.tight_layout()
    plt.show()


def compare_runtimes(use_log_scale=False):
    size_range = range(100, 10001, 100)

    print("Measuring brute force approach... (This might take some time)")
    brute_force_times = time_measurement(
        smallest_difference_brute_force, size_range)
    print("Done...")

    print("Measuring faster approach ... (hopefully this is faster)")
    faster_times = time_measurement(smallest_difference_faster, size_range)
    print("Done...")

    times = [("Brute Force", brute_force_times), ("Faster", faster_times)]
    # Generate the x-axis values to match the 'n' sizes used in the loop
    x_values = list(size_range)

    # Create the plot
    plot_results(x_values, times, use_log_scale)


if __name__ == "__main__":
    compare_runtimes()
    # print(smallest_difference_faster(read_input('input.txt')))
