#pragma once #include #include #include #include #include #include #include namespace cgv { namespace math { /** * polynomials are stored in vectors in the following order: * [a,b,c,d] ...a*x^3 + b*x^2 + c*x + d */ /// evaluate a polynomial p at x /// p is the vector of length n+1 whose elements are the coefficients /// of the polynomial in descending powers. /// evaluation is done by using the horner scheme template T poly_val(const vec& p, const T& x) { assert(p.size() > 0); T y=p(0); for(unsigned i = 1; i vec poly_val(const vec& p, const vec& x) { assert(p.size() > 0); vec y(x.size()); y.fill(p(0)); for(unsigned i = 1; i vec poly_mult(const vec& u, const vec& v) { unsigned s =u.size() + v.size()-1; vec w(s); for(unsigned k = 1; k <= s; k++) { w(k-1) = 0; int j = 0; int jmin = (int)k+1-(int)v.size(); if(jmin < 1) jmin = 1; int jmax = (int)u.size(); if (jmax > (int)k) jmax=k; for(int j = jmin; j <=jmax; j++) w(k-1)+=u(j-1)*v(k-j); } return w; } ///polynomial division ///f(x)/g(x) = q(x) + r(x)/g(x) ///returns true if r(x)=0 (rest 0) template bool poly_div(const vec& f, const vec& g, vec& q, vec& r) { if(g.size() > f.size()) { q.resize(1); q(0)=0; r=f; return false; } else { vec p = f; //std::cout << p << std::endl; q.resize(f.size()-g.size()+1); for(unsigned j = 0; g.size()+j <=p.size();j++) { q(j) = p(j)/g(0); for(unsigned i = j; i < g.size()+j; i++) { p(i)= p(i)- q(j)*g(i-j); } //std::cout < 0) { a++;s--; } if(s == 0) { r.resize(1); r(0)=0; return true; } else { r=p.sub_vec(a,s); return false; } } } //returns addition of polynomials u and v template vec poly_add(const vec& u, const vec& v) { unsigned n = u.size(); unsigned m = v.size(); vec r; if(n >= m) { r.resize(n); unsigned i = 0; unsigned d = n-m; for(;i < d;i++) r(i) = u(i); for(;i < n;i++) r(i) = u(i)+v(i-d); }else { r.resize(m); unsigned i = 0; unsigned d = m-n; for(;i < d;i++) r(i) = v(i); for(;i < m;i++) r(i) = v(i)+u(i-d); } return r; } //returns subtraction of polynomials u and v template vec poly_sub(const vec& u, const vec& v) { unsigned n = u.size(); unsigned m = v.size(); vec r; if(n >= m) { r.resize(n); unsigned i = 0; unsigned d = n-m; for(;i < d;i++) r(i) = u(i); for(;i < n;i++) r(i) = u(i)-v(i-d); }else { r.resize(m); unsigned i = 0; unsigned d = m-n; for(;i < d;i++) r(i) = -v(i); for(;i < m;i++) r(i) = u(i-d)-v(i); } return r; } //analytic derivation of polynomial p template vec poly_der(const vec& p) { assert(p.size() > 0); unsigned n = p.size(); if(n <= 1) { vec q(1); q(0) = 0; return q; } else { unsigned m = n-1; vec q(m); for(unsigned i = 0; i< m; i++) q(i)= p(i)*(m-i); return q; } } //analytic integrate polynomial p using integration constant k template vec poly_int(const vec& p, const T& k=0) { assert(p.size() > 0); unsigned m = p.size()+1; vec q(m); for(unsigned i = 0; i < m-1; i++) q(i)= p(i)/(m-i-1); q(m-1)=k; return q; } ///returns bernstein polynomial (bezier basis) template vec bernstein_polynomial(unsigned j, unsigned g) { using namespace cgv::math; vec p; p.zeros(j+1); p(0)=1.0;// p = t^j for(unsigned i = 0; i < g-j; i++) p = poly_mult(p, vec(-1.0,1)); //p=p*(1-t) return nchoosek(g,j)*p; } //returns lagrange basis polynomial template vec lagrange_basis_polynomial(unsigned i, const vec& u) { using namespace cgv::math; //polynomial p = 1 vec p(1); p(0)=1.0; unsigned g = u.size()-1; for(unsigned j = 0; j <= g; j++) { if(i == j) continue; p = poly_mult(p, vec(1.0,-u(j))) / (u(i)-u(j)); } return p; } template vec newton_basis_polynomial(unsigned i, const vec& u) { using namespace cgv::math; //polynomial p = 1 vec p(1); p(0)=1.0; unsigned g = u.size()-1; for(unsigned j = 0; j <= g; j++) { p = poly_mult(p, vec(1.0,-u(j))); } return p; } //returns the vandermonde matrix which can be used for fitting //polynomials (for large order polynomials use orthogonal basis instead) //x = [ 1 2 3 4] -> V(i,j) x(i)^(n-j) V is n x n with n = x.size() template mat vander(const vec& x) { return vander(x.size()-1,x); } //returns vandermonde matrix template mat vander(unsigned degree,const vec& x) { assert(x.size() >= 1); unsigned n = x.size(); cgv::math::mat V(n,degree+1); for(unsigned i = 0; i < n; i++) { V(i,degree) = (T)1; for(int j = (int)degree-1; j>= 0; j--) V(i,j)=V(i,j+1)*x(i); } return V; } //fit a polynomial of degree n into data such that E = sum_i ||p(X(i)) - Y(i)||² is minimized //uses vandermonde matrix and svd solver //large degrees can be numerically instable keep n < 10 //otherwise use orthogonal basis for fitting template vec poly_fit(unsigned n,const vec&X, const vec&Y) { mat V = cgv::math::vander(n,X); mat A; vec b,p; AtA(V,A); Atx(V,Y,b); svd_solve(A,b,p); return p; } } }