#include <stdio.h>   
#include <math.h>  
#include <conio.h>  
#include <string.h> 
#include <ctype.h> 

// визначає новий тип даних complex, який представляє комплексне число.
typedef struct {float real; float imag;} complex;

// Функція для ділення комплексних чисел
complex divide(complex a, complex b) 
{
    complex result;  
    // b.real є виразом, що вказує на реальну частину комплексного числа b
    float denominator = b.real * b.real + b.imag * b.imag; 
    result.real = (a.real * b.real + a.imag * b.imag) / denominator;
    result.imag = (a.imag * b.real - a.real * b.imag) / denominator;
    return result;
}

void printComplex(complex z) 
{
    printf("(%.4f) + i(%.4f)", z.real, z.imag);
}

// отримання коректного числового введення від користувача
float getValidInput(const char *prompt, int allowZero, int allowFraction) 
{
    double value;  
    char input[50]; 
    int isValidInput = 0;  
    while (!isValidInput) 
    {
        printf("%s", prompt);
        
        // забезпечення коректного та безпечного отримання введеного користувачем числа
        // перевірка, чи fgets успішно зчитав рядок зі стандартного вводу
        if (!fgets(input, sizeof(input), stdin)) 
        {
            printf("Error reading input.\n");
            continue;
        }
        // конвертація отриманого рядка input у число з плаваючою точкою (float) за допомогою функції sscanf
        if (sscanf(input, "%lf", &value) == 1) 
        {
            isValidInput = 1; 
        } 
        else 
        {
            printf("Invalid input. Please enter a number.\n");
        }
    }
    return value;
}

// отримання вірного вибору опції від користувача
char getValidOption() 
{
    char option; // Змінна, в яку буде збережено вибір користувача
    int isValid_Option = 0;
    
    while (!isValid_Option) 
    {
        printf("Choose the formula (1-4): ");
        if (scanf(" %c", &option) != 1 || option < '1' || option > '4') 
        {
            printf("Invalid option. Please enter a number between 1 and 4.\n");
            // Очищення буфера вводу, яке викликається, якщо користувач ввів більше одного символу
            while (getchar() != '\n');
        } 
        else 
        {
            isValid_Option = 1; 
        }
    }
    return option;
}

int main() 
{
    double L; // індуктивність
    double C; // ємність
    double R, R1, R2; // резистор
    double f; // частота
    double omega; // кругова частота
    complex Z; // комплексний опір
    complex a, b, c, d; // змінні, які використовуються для обчислення комплексного опору 
    char continueProgram; // визначення, чи користувач хоче продовжити
    
    do 
    {
        double f_min; // мінімальнa частотa
        f_min = getValidInput("Enter the minimum frequency f_min: ", 1, 0);
        double f_max; // максимальнa частотa 
        f_max = getValidInput("Enter the maximum frequency f_max: ", 1, 0);
        double df; // крок зміни частоти
        df = getValidInput("Enter the frequency step df: ", 0, 0);
        char option = getValidOption(); // змінна, яка вказує користувачеві на обрану формулу
        
        if (option == '1' || option == '2') 
        {
            printf("Enter the values of L, C, R: ");
            scanf("%lf %lf %lf", &L, &C, &R);
        } 
        else 
        {
            printf("Enter the values of L, C, R1, R2: ");
            scanf("%lf %lf %lf %lf", &L, &C, &R1, &R2);
        }
        
        double f0; // резонансна частотa
        f0 = 1.0 / (2.0 * M_PI * sqrt(L * C));
        
        // виведення заголовка таблиці
        // %10s - вирівнюється по правому краю, %-10s - вирівнюється по лівому краю.
        printf("\n%-10s\t%-10s\t%-15s\t%-15s\t%-10s\n", "f", "omega", "Z (re)", "Z (im)", "f0");
        
        for (f = f_min; f <= f_max; f += df) 
        {
            omega = 2.0 * M_PI * f;
            
            // обчислення комплексного опору Z для різних значень частоти f в заданому діапазоні
            switch (option) {
                case '1':
                    Z.real = (L / C - R / (omega * C)) / (R + omega * L - 1 / (omega * C));
                    Z.imag = (-R / (omega * C)) / (R + omega * L - 1 / (omega * C));
                    break;
                case '2':
                    Z.real = (L / C + R / (omega * C)) / (R + omega * L - 1 / (omega * C));
                    Z.imag = (R / (omega * C)) / (R + omega * L - 1 / (omega * C));
                    break;
                case '3':
                    a.real = R1 * R2;
                    a.imag = 0;
                    b.real = R1 * (omega * L - 1 / (omega * C));
                    b.imag = 0;
                    c.real = R1 + R2;
                    c.imag = omega * L - 1 / (omega * C);
                    d.real = c.real * c.real + c.imag * c.imag; 
                    Z.real = (a.real * c.real + b.real * c.imag) / d.real;
                    Z.imag = (b.real * c.real - a.real * c.imag) / d.real;
                    break;
                case '4':
                    a.real = R1 * R2 + L / C;
                    a.imag = 0;
                    b.real = R1 * (omega * R1 * L - R2 / (omega * C));
                    b.imag = 0;
                    c.real = R1 + R2;
                    c.imag = omega * L - 1 / (omega * C);
                    d.real = c.real * c.real + c.imag * c.imag;
                    Z.real = (a.real * c.real + b.real * c.imag) / d.real;
                    Z.imag = (b.real * c.real - a.real * c.imag) / d.real;
                    break;
        }
            printf("%-10.4f\t%-10.4f\t%-e\t%-e\t%-10.4f\n", f, omega, Z.real, Z.imag, f0);
        }
        
        printf("\nDo you want to continue? (y/n): ");
        scanf(" %c", &continueProgram); 
        while (getchar() != '\n'); 
    } 
    
    while (continueProgram == 'y' || continueProgram == 'Y');
    printf("Goodbye!\n");
    return 0;
}
