#include <iostream>
#include <cmath>
#include <vector>
#include <fstream>

// Funkcja 1: exp(x) * cos(0.5 * x^2)
double func1(double x) {
    return std::exp(x) * std::cos(0.5 * x * x);
}

// Funkcja 2: 1 / (12 * x^2 + 1)
double func2(double x) {
    return 1.0 / (12.0 * x * x + 1.0);
}

// Generowanie węzłów Czebyszewa
std::vector<double> chebyshevNodes(int degree, double a, double b) {
    std::vector<double> nodes(degree);
    for (int i = 0; i < degree; ++i) {
        double cos_theta = std::cos(M_PI * (2.0 * i + 1) / (2.0 * degree));
        nodes[i] = 0.5 * (a + b) + 0.5 * (b - a) * cos_theta;
    }
    return nodes;
}

// Obliczanie współczynników wielomianu Czebyszewa
std::vector<double> chebyshevCoefficients(int degree, double a, double b, double (*func)(double)) {
    std::vector<double> nodes = chebyshevNodes(degree, a, b);
    std::vector<double> coeffs(degree, 0.0);

    for (int k = 0; k < degree; ++k) {
        double sum = 0.0;
        for (int j = 0; j < degree; ++j) {
            sum += func(nodes[j]) * std::cos(M_PI * k * (2.0 * j + 1) / (2.0 * degree));
        }
        coeffs[k] = (2.0 / degree) * sum;
    }

    coeffs[0] *= 0.5; // Współczynnik dla T_0(x)
    return coeffs;
}

// Obliczanie wartości wielomianu Czebyszewa
double chebyshevPolynomial(const std::vector<double>& coeffs, double x, double a, double b) {
    double t = (2.0 * x - (a + b)) / (b - a); // Przekształcenie na [-1, 1]
    double y = 0.0;
    double T_k_minus_2 = 1.0;
    double T_k_minus_1 = t;

    for (size_t k = 0; k < coeffs.size(); ++k) {
        if (k == 0) {
            y += coeffs[k] * T_k_minus_2;
        } else if (k == 1) {
            y += coeffs[k] * T_k_minus_1;
        } else {
            double T_k = 2.0 * t * T_k_minus_1 - T_k_minus_2;
            y += coeffs[k] * T_k;
            T_k_minus_2 = T_k_minus_1;
            T_k_minus_1 = T_k;
        }
    }

    return y;
}

int main() {
    // Parametry
    double a1 = 0.0, b1 = 3.0;
    double a2 = -2.0, b2 = 2.0;
    std::vector<int> degrees = {3, 10, 40};

    // Pliki wynikowe
    std::ofstream file1("func1.dat");
    std::ofstream file2("func2.dat");

    for (int degree : degrees) {
        std::vector<double> coeffs1 = chebyshevCoefficients(degree, a1, b1, func1);
        std::vector<double> coeffs2 = chebyshevCoefficients(degree, a2, b2, func2);

        for (double x = a1; x <= b1; x += 0.01) {
            double approx1 = chebyshevPolynomial(coeffs1, x, a1, b1);
            file1 << x << " " << func1(x) << " " << approx1 << "\n";
        }

        for (double x = a2; x <= b2; x += 0.01) {
            double approx2 = chebyshevPolynomial(coeffs2, x, a2, b2);
            file2 << x << " " << func2(x) << " " << approx2 << "\n";
        }

        file1 << "\n\n";
        file2 << "\n\n";
    }

    file1.close();
    file2.close();

    std::cout << "Dane zapisane do plikow func1.dat i func2.dat. Wykresy mozna rysowac za pomoca Gnuplot." << std::endl;
    return 0;
}
