/**
 * Trieda implementujuca Fourierov rad na usecke. Obmedzujeme sa len
 * na funkcie typu "struna s pevnymi koncami". Teda matematicky povedane
 * zaujimame sa len o Fourierove rady funkcii, ktore su nulove
 * v okrajovych bodoch usecky. Vystacime preto so sinusovkami,
 * cosinusove vlny netreba
 * Sucasne nam to umozni simulovat dynamiku struny.
 */
public class Fourier {
  /**
   * Dlzka "struny". Uvazujeme funkcie definovane na usecke (0,L), ktore su nulove
   * v okrajovych bodoch. Defaultova hodnota 1.
   */
  public static double L=1;

  /**
   * Maximalny pocet clenov Fourierovho radu, ktory budeme uvazovat.
   * Defaultova hodnota je 100
   */
  public int nmax = 100;
  /**
   * pole do ktoreho sa ulozia vypocitane Fourierove koeficienty
   * v sulade s matematickym znacenim koeficienty zacinaju od indexu 1
   * hodnota coef[0] je teda nedefinovana
   */
  public double coef[];

  /**
   * constuctor
   * @param nmax int udava pocet clenov Fourierovho radu, ktory budeme uvazovat
   * @param L double dlzka "struny"
   */
  public Fourier(int nmax, double L) {
    this.L = L;
    this.nmax = nmax;
    coef = new double[nmax + 1]; //inicializuje pole koeficientov
  }
  /**
   * Vrati hodnotu funkcie, ktora je sumou Fourierovho radu (presnejsie
   * sumou prvych nmax clenov Fourierovho radu
   * @param x double nezavisle premenna
   * @return double hodnota fourierovho radu
   */
  public double fcn(double x) {
    double sum = 0;
    for (int i = 1; i <= nmax; i++) {
      sum = sum + coef[i] * F(i, x);
    }
    return sum;
  }

  /**
   * Vypocita Fourierove koeficienty funkcie, ktora je definovana objektom
   * implementujucim interface Integrable. Vypocitane koeficienty ulozi do pola
   * this.coef[]
   * @param f Integrable Interface-objekt typu Integrable definujuci  funkciu,
   * ktorej Fourierov rad hladame
   */
  public void makeTransform(Integrable f) {
    for (int i = 1; i <= nmax; i++) {
      Integrable podint = new podint(f, i);
      coef[i] = 2. / L * Integral.Integrate(podint, 0., L);
    }
  }

  /**
   * Funkcia reprezentujuca n-ty fourierov sinusovy mod na strune dlzky L
   * @param n int zadava rad modu, n>=1,n<=nmax
   * @param x double nezavisle premenna
   * @return double hodnota Fourierovej n-tej sinusovky
   */
  public static double F(int n, double x) {
    return (Math.sin(x * Math.PI * n / L));
  }

  /**
   * Vrati vlnovu dlzku prislusnu n-tej Fourierovej sinusovke
   * @param n int rad sinusovky, n>=1,n<=nmax
   * @return double vlnova dlzka
   */
  public static double lambda(int n) {
    return 2. * L / n;
  }

  /**
   * Privatna vnorena trieda reprezentujuca podintegralnu funkciu integralu
   * sluziaceho na vypocet Fourierovho koeficientu, teda je to sucin
   * Fourierovej sinusovky a funkcie, ktorej Fourierov rad hladame
   * Ide teda o triedu, ktorou pre vnutorne potreby realizujeme interface Integrable
   */
  private class podint
      implements Integrable {
    Integrable f;
    int n;
    public podint(Integrable f, int n) {
      this.f = f;
      this.n = n;
    }

    public double fcn(double x) {
      return (f.fcn(x) * F(n, x));
    }
  }

  /**
   * metoda main nie je sucastou API, je to len pomocna metoda na testovanie
   * ale sucasne ukazuje aj pouzitie triedy
   * @param args String[]
   */
  public static void main(String[] args) {
    Fourier fou = new Fourier(10, 1.);
    test f = new test();
    fou.makeTransform(f);
    System.out.println(f.fcn(0.15) + " " + fou.fcn(0.15));
  }

  /**
   * pomocna vnorena trieda sluzi len na testovanie Fourierovej transformacie
   */
  private static class test
      implements Integrable {
    public double fcn(double x) {
      if (x < 0.5) {
        return x;
      }
      else {
        return (1. - x);
      }
    }
  }

}
