#pragma once
#include "./numerical.h"

struct Polyfit1 {
  double c0;
  double c1;

#ifdef __cplusplus
  Polyfit1(double _c0, double _c1)
    : c0(_c0), c1(_c1)
  {
  }
  Polyfit1()
    : c0(0.0), c1(0.0)
  {
  }

  inline double operator()(double t) const {
    return c0 + t * (c1);
  }
  inline double deriv(double t) const {
    return c1;
  }
  vector<R> coeffs() const;
#endif
};

struct Polyfit2 {
  double c0;
  double c1;
  double c2;

#ifdef __cplusplus
  Polyfit2(double _c0, double _c1, double _c2)
    : c0(_c0), c1(_c1), c2(_c2)
  {
  }
  Polyfit2()
    : c0(0.0), c1(0.0), c2(0.0)
  {
  }

  inline double operator()(double t) const {
    return c0 + t * (c1 + (t * c2));
  }
  inline double deriv(double t) const {
    return c1 + 2.0 * c2 * t;
  }
  Polyfit1 deriv() const;
  vector<R> coeffs() const;
#endif
};


struct Polyfit3 {
  double c0;
  double c1;
  double c2;
  double c3;

#ifdef __cplusplus
  Polyfit3(double _c0, double _c1, double _c2, double _c3)
    : c0(_c0), c1(_c1), c2(_c2), c3(_c3)
  {
  }
  Polyfit3()
    : c0(0.0), c1(0.0), c2(0.0), c3(0.0)
  {
  }

  inline double operator()(double t) const {
    return c0 + t * (c1 + (t * (c2 + t * c3)));
  }
  inline double deriv(double t) const {
    return c1 + 2.0 * c2 * t + 3.0 * c3 * t * t;
  }
  Polyfit2 deriv() const;
  vector<R> coeffs() const;
#endif
};

struct Polyfit4 {
  double c0;
  double c1;
  double c2;
  double c3;
  double c4;

#ifdef __cplusplus
  Polyfit4(double _c0, double _c1, double _c2, double _c3, double _c4)
    : c0(_c0), c1(_c1), c2(_c2), c3(_c3), c4(_c4)
  {
  }
  Polyfit4()
    : c0(0.0), c1(0.0), c2(0.0), c3(0.0), c4(0.0)
  {
  }

  inline double operator()(double t) const {
      return c0 + t * (c1 + (t * (c2 + t * (c3 + t * c4))));
  }
  inline double deriv(double t) const {
    return c1 + t * (2.0 * c2 + t * (3.0 * c3 + t * (4.0 * c4)));
  }
  Polyfit3 deriv() const;
  vector<R> coeffs() const;
#endif
};


struct Polyfit5 {
  double c0;
  double c1;
  double c2;
  double c3;
  double c4;
  double c5;

#ifdef __cplusplus
  Polyfit5(double _c0, double _c1, double _c2, double _c3, double _c4, double _c5)
    : c0(_c0), c1(_c1), c2(_c2), c3(_c3), c4(_c4), c5(_c5)
  {
  }
  Polyfit5()
    : c0(0.0), c1(0.0), c2(0.0), c3(0.0), c4(0.0), c5(0.0)
  {
  }

  inline double operator()(double t) const {
    return c0 + t * (c1 + (t * (c2 + t * (c3 + t * (c4 + t * c5)))));
  }
  inline double deriv(double t) const {
    return c1 + t * (2.0 * c2 + t * (3.0 * c3 + t * (4.0 * c4 + t * (5.0 * c5))));
  }
  Polyfit4 deriv() const;
  vector<R> coeffs() const;
#endif
};

#ifdef __cplusplus
Polyfit1 mkPolyfit1(vector<R> const &xs, vector<R> const &ys);
Polyfit2 mkPolyfit2(vector<R> const &xs, vector<R> const &ys);
Polyfit3 mkPolyfit3(vector<R> const &xs, vector<R> const &ys);
Polyfit4 mkPolyfit4(vector<R> const &xs, vector<R> const &ys);
Polyfit5 mkPolyfit5(vector<R> const &xs, vector<R> const &ys);


ostream & operator <<(ostream &s, const Polyfit1 &a);
ostream & operator <<(ostream &s, const Polyfit2 &a);
ostream & operator <<(ostream &s, const Polyfit3 &a);
ostream & operator <<(ostream &s, const Polyfit4 &a);
ostream & operator <<(ostream &s, const Polyfit5 &a);
#endif
