/** * \file * \brief Multi-dimensional symmetric power basis function. * A SBasisN is a polynomial f of n variables (t0,...,tn-1), * written in a particular form. Let si = ti(1-t_i). f is written as * * f = sum_p s^p a_{p}(t0,...,t_{n-1}) * * where p=(p0,...,p_{n-1}) is a multi index (called MultiDegree in the code) * s^p = prod_i si^pi, and a_p is a LinearN. * Recall a LinearN is sum over all choices xi = ti or (1-ti) of terms of form * a * x0*...*x_{n-1} * * Caution: degrees are expressed as degrees of s=t*(1-t). The real degree * (with respect to t) of the polynomial is twice that + 0 or 1 depending * whether the relevant LinearN coeff is constant or not. *//* * * Authors: * JF Barraud * Nathan Hurst * Michael Sloan * * Copyright (C) 2006-2007 authors * * This library is free software; you can redistribute it and/or * modify it either under the terms of the GNU Lesser General Public * License version 2.1 as published by the Free Software Foundation * (the "LGPL") or, at your option, under the terms of the Mozilla * Public License Version 1.1 (the "MPL"). If you do not alter this * notice, a recipient may use your version of this file under either * the MPL or the LGPL. * * You should have received a copy of the LGPL along with this library * in the file COPYING-LGPL-2.1; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * You should have received a copy of the MPL along with this library * in the file COPYING-MPL-1.1 * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY * OF ANY KIND, either express or implied. See the LGPL or the MPL for * the specific language governing rights and limitations. */ #ifndef SEEN_SBASISN_H #define SEEN_SBASISN_H #include #include #include #include <2geom/orphan-code/linearN.h> #include <2geom/linear.h>//for conversion purpose #include <2geom/sbasis.h>//for conversion purpose #include <2geom/interval.h> #include <2geom/utils.h> #include <2geom/exception.h> namespace Geom{ /** MultiDegree: * \brief multi-degree (p0,...,p^{n-1}) of a s0^p0...s_{n-1}p^{n-1} monomial. * * a "Multi_deg" is a sequence p={p0,...,p_n-1} representing the monomial * s^p = s_0^{p_0}*...*s_{n-1}^{p_{n-1}}. * Caution: the degrees are expressed with respect to si! ( in SBasis code * below, si = ti*(1-ti) ). */ template class MultiDegree{ public: unsigned p[n]; MultiDegree(){ for (unsigned i = 0; i 0; i--) { div_t d = std::div(int(q), int(sizes[i])); p[i] = d.rem; q = d.quot; } p[0] = q; } unsigned operator[](const unsigned i) const { assert(i < n); return p[i]; } unsigned& operator[](const unsigned i) { assert(i < n); return p[i]; } unsigned asIdx(unsigned const sizes[]) const{ unsigned ret = p[0]; bool in_range = (p[0]=0 && ( (1<= 0 && p[i] == 0 ) { i--; while (i>=0 && ( (1<= 0 ){ p[i]-=1; for (unsigned j = i+1; j < n; j++) { if ( !( (1< MultiDegree max(MultiDegree const &p, MultiDegree const &q){ MultiDegree ret; for (unsigned i = 0; i q[i] ? p[i] : q[i]); } return ret; } template MultiDegree operator + (MultiDegree const &p, MultiDegree const &q){ MultiDegree ret; for (unsigned i = 0; i MultiDegree operator += (MultiDegree const &p, MultiDegree const &q){ for (unsigned i = 0; i bool operator<=(MultiDegree const &p, MultiDegree const &q){ for (unsigned i = 0; i q[i]) return false; } return true; } /** * \brief Polynomials of n variables, written in SBasis form. * An SBasisN f is internaly represented as a vector of LinearN. * It should be thought of as an n-dimensional vector: the coef of s0^p0...s_{n-1}p^{n-1} * is soterd in f[p0,...,p_{n-1}]. The sizes of each dimension is stored in "sizes". * Note: an empty SBasis is identically 0. */ template class SBasisN : public std::vector >{ public: unsigned sizes[n]; SBasisN() { for (unsigned i = 0; i < n; i++) { sizes[i] = 0; } } explicit SBasisN(double a) { for (unsigned i = 0; i < n; i++) { sizes[i] = 1; } this->push_back(LinearN(a)); } SBasisN(SBasisN const & a) : std::vector >(a){ //TODO: efficient array copy?? for (unsigned i = 0; i < n; i++) { sizes[i] = a.sizes[i]; } } SBasisN(LinearN const & bo) { for (unsigned i = 0; i < n; i++) { sizes[i] = 1; } this->push_back(bo); } SBasisN(LinearN* bo) { for (unsigned i = 0; i < n; i++) { sizes[i] = 1; } this->push_back(*bo); } //---------------------------------------------- //-- Degree/Sizing facilities ------------------ //---------------------------------------------- /** * Internal recursive function used to compute partial degrees. */ bool find_non_empty_level(unsigned var, MultiDegree &fixed_degrees)const{ if (this->size()==0){ for (unsigned i = 0; i < n; i++) { fixed_degrees[i] = 0;//FIXME this should be -infty!! } return false; } if ( !((*this)[fixed_degrees.asIdx(sizes)].isZero()) ) return true; unsigned frozen = (1< 0 ){ fixed_degrees[var] -= 1; for (unsigned i = 0; i < n; i++) { if (i!=var) fixed_degrees[i] = sizes[i]-1; } if (find_non_empty_level(var, fixed_degrees)) return true; } return false;//FIXME: this should return -infty in all variables! } /** * Returns the degree of an SBasisN with respect to a given variable form its sizes. * All terms are taken into account, even eventual trailing zeros. * Note: degree is expressed with respect to s = t*(1-t), not t itself. */ unsigned quick_degree(unsigned var) const{ return ( sizes[var] > 0 ? sizes[var]-1 : 0 );//this should be -infty. } /** * Computes the multi degree of the SBasis from it's sizes. * All terms are taken into account, even eventual trailing zeros. * Note: degrees are expressed with respect to s = t*(1-t), not t itself. */ MultiDegree quick_multi_degree() const{ MultiDegree ret; if (this->size()==0) return ret;//should be -infty for all vars. for (unsigned i = 0; i < n; i++) { assert( sizes[i]>0 ); ret.p[i] = sizes[i]-1; } return ret; } /** * Returns the degree of an SBasisN with respect to a given variable. * Trailing zeros are not taken into account. * Note: degree is expressed with respect to s = t*(1-t), not t itself. */ unsigned degree(unsigned var)const{ MultiDegree degrees; for(unsigned i = 0; i < n; i++) { degrees[i] = sizes[i]-1; } if ( find_non_empty_level(var, degrees) ) return degrees[var]; else return 0;//should be -infty. } /** * Returns the *real* degree of an SBasisN with respect to a given variable. * Trailing zeros are not taken into account. * Note: degree is expressed with respect to t itself, not s = t*(1-t). * In particular: real_t_degree() = 2*degree() + 0 or 1. */ unsigned real_t_degree(unsigned var)const{ unsigned deg = 0; bool even = true; bool notfound = true; unsigned frozen = (1< degrees; for(unsigned i = 0; i < n; i++) { degrees[i] = sizes[i]-1; } while( notfound ){ if ( find_non_empty_level(var, degrees) && degrees[var]>= deg ){ deg = degrees[var]; even = (*this)[degrees.asIdx(sizes)].isConstant(var); } notfound = even && degrees.stepDown(sizes, frozen); } return 2*deg + ( even ? 0 : 1 ); } /** * Returns the *real* degrees of an SBasisN. * Trailing zeros are not taken into account. * Note: degree is expressed with respect to t itself, not s = t*(1-t). * In particular: real_t_degree() = 2*degree() + 0 or 1. */ MultiDegree real_t_degrees()const{ MultiDegreeres; for(unsigned i = 0; i < n; i++) { res[i] = real_t_degree(i); } return res; } /** * Computes the multi degree of the SBasis. * Trailing zeros are not taken into account. * Note: degree is expressed with respect to s = t*(1-t), not t itself. */ MultiDegree multi_degree() const{ MultiDegree ret; if (this->size()==0) return ret;//should be -infty for all vars. for (unsigned i = 0; i < n; i++) { ret[i] = this->degree(i); } return ret; } /** * Returns the highest degree over all variables. * Note: degree is expressed with respect to s = t*(1-t), not t itself. */ unsigned max_degree() const { if (this->size()==0) return 0;//should be -infty! unsigned d=0; for (unsigned i = 0; i < n; i++) { assert( sizes[i]>0 ); if (d < sizes[i]-1) d = sizes[i]-1; } return d; } /** * Resize an SBasisN to match new sizes. * * Caution: if a new size is smaller, the corresponding coefficients are discarded. */ void multi_resize(unsigned new_sizes[], LinearN def_value = LinearN(0.)){ SBasisN result; bool nothing_todo = true; unsigned tot_size = 1; for(unsigned i = 0; i < n; i++) { nothing_todo = nothing_todo && (sizes[i] == new_sizes[i]); result.sizes[i] = new_sizes[i]; tot_size *= new_sizes[i]; } if (nothing_todo) return; result.resize(tot_size, def_value); for(unsigned i = 0; i < tot_size; i++) { MultiDegree d( i, result.sizes ); unsigned j = d.asIdx(sizes); if ( j < this->size() ){ result[i] = (*this)[j]; } } *this = result; } //remove extra zeros void normalize() { MultiDegree max_p = multi_degree(); unsigned new_sizes[n]; for (unsigned i=0; isize()==0) return true; for(unsigned i = 0; i < this->size(); i++) { if(!(*this)[i].isZero()) return false; } return true; } inline bool isConstant() const { if (this->size()==0) return true; if(!(*this)[0].isConstant()) return false; for (unsigned i = 1; i < this->size(); i++) { if(!(*this)[i].isZero()) return false; } return true; } bool isFinite() const{ for (unsigned i = 0; i < this->size(); i++) { if(!(*this)[i].isFinite()) return false; } return true; } //------------------------------------------ //-- Evaluation methods -------------------- //------------------------------------------ /** * Returns the value of the SBasis at a given corner of [0,1]^n. * \param k describes the corner: if i-th bit is 0, ti=0, otherwise ti=1. */ inline double atCorner(unsigned k) const { if(this->size()==0) return 0.; return (*this)[0].atCorner(k); } /** * Returns the value of the SBasis at a given corner of [0,1]^n. * \param t[n] describes the corner: the values should be 0's and 1's. */ inline double atCorner(double t[]) const { if(this->size()==0) return 0.; return (*this)[0].atCorner(t); } /** * Returns a "slice" of the array. * Returns an SBasis containing all the coeff of (s-)degree \param deg in variable \param var */ //TODO: move by bigger blocks (?) but they are broken into pieces in general... SBasisN slice(unsigned const var, unsigned const deg) const{ if (deg >= sizes[var] ) return SBasisN(); SBasisN res; unsigned tot_size = 1; for (unsigned i = 0; i < n; i++) { res.sizes[i] = (i==var ? 1 : sizes[i]); tot_size *= res.sizes[i]; } res.resize( tot_size, LinearN(0.)); for (unsigned i = 0; i < tot_size; i++) { MultiDegree d(i,res.sizes); d.p[var] = deg; res[i] = (*this)[d.asIdx(sizes)]; } return res; } /** * Returns a the SBasisN obtained by setting variable \param var to 0. */ inline SBasisN at0(unsigned var=0, unsigned deg=0) const { SBasisN sl = slice(var,deg); SBasisN res; res.reserve(sl.size()); for (unsigned i = 0; i < n-1; i++) { res.sizes[i] = sizes[ ( i obtained by setting variable \param var to 1. */ inline SBasisN at1(unsigned var=0, unsigned deg=0) const { SBasisN sl = slice(var,deg); SBasisN res; res.reserve(sl.size()); for (unsigned i = 0; i < n-1; i++) { res.sizes[i] = sizes[ ( i obtained by setting variable \param var to \param t. */ inline SBasisN partialEval(double t, unsigned var=0 ) const { SBasisN sl; double s = t*(1-t); double si = 1; for (unsigned i = 0; i res; res.resize(sl.size(), LinearN(0.)); for (unsigned i = 0; i < n-1; i++) { res.sizes[i] = sizes[ ( i coeffs. Then sum up all coefficients. * \param t[n]: values of the variables. */ LinearN sumCoefs( double t[], unsigned const k, unsigned const idx) const{ LinearN a; if (k == n){ a = (*this)[idx]; return (*this)[idx]; } double s = t[k]*(1-t[k]); double si=1; for (unsigned i=0; i a = sumCoefs(t,0,0); return a.valueAt(t); } double operator()(double t[]) const { return valueAt(t); } //double valueAndDerivative(double t, double &der) const; //std::vector valueAndDerivatives(double t, unsigned n) const; //SBasisN toSBasisN() const { return SBasisN(*this); } //double tailError(unsigned tail) const; //-------------------------------------------------- //-- Coeff. manipulation --------------------------- //-------------------------------------------------- /** * Accessing the SBasisN coefficients. */ LinearN operator[](unsigned i) const { assert(i < this->size()); return std::vector >::operator[](i); } LinearN operator[](MultiDegree const &p) const { unsigned i = p.asIdx(sizes); assert(i < this->size()); return std::vector >::operator[](i); } //MUTATOR PRISON LinearN& operator[](unsigned i) { return this->at(i); } // LinearN& operator[](MultiDegree const &p) { // unsigned i = p.asIdx(sizes); // return this->at(i); // } void appendCoef(const SBasisN &a, const SBasisN &b, unsigned var=0){ unsigned new_sizes[n]; MultiDegree deg_a = a.multi_degree(), deg_b = b.multi_degree(); MultiDegree dcoef = max( deg_a, deg_b ); for (unsigned i=0; icoef_size ? sizes[i] : coef_size ); } } multi_resize(new_sizes); MultiDegree d; d[var] = sizes[var]-1; unsigned frozen_mask = (1< a_d,b_d; unsigned ia = dcoef.asIdx(a.sizes); if ( ia < a.size() ) a_d = a[ia]; unsigned ib = dcoef.asIdx(b.sizes); if ( ib < b.size() ) b_d = b[ib]; (*this)[d.asIdx(sizes)] = LinearN(a_d,b_d); }while (d.stepUp(sizes,frozen_mask)); } //private: //void derive(); // in place version }; //SBasisN<0> is a double. Specialize it out. template<> class SBasisN<0>{ public: double d; SBasisN () {} SBasisN(double d) :d(d) {} operator double() const { return d; } }; //SBasisN<1> are usual SBasis. Allow conversion. SBasis toSBasis(SBasisN<1> f){ SBasis res(f.size(), Linear()); for (unsigned i = 0; i < f.size(); i++) { res[i] = toLinear(f[i]); } return res; } //TODO: figure out how to stick this in linear, while not adding an sbasis dep template inline SBasisN LinearN::toSBasisN() const { return SBasisN(*this); } //implemented in sbasis-roots.cpp //OptInterval bounds_exact(SBasisN const &a); //OptInterval bounds_fast(SBasisN const &a, int order = 0); //OptInterval bounds_local(SBasisN const &a, const OptInterval &t, int order = 0); /** Returns a function which reverses the domain of a. \param a sbasis function useful for reversing a parameteric curve. */ //template //inline SBasisN reverse(SBasisN const &a); //IMPL: ScalableConcept template inline SBasisN operator-(const SBasisN& p) { if(p.isZero()) return SBasisN(); SBasisN result; for(unsigned i = 0; i < n; i++) { result.sizes[i] = p.sizes[i]; } result.reserve(p.size()); for(unsigned i = 0; i < p.size(); i++) { result.push_back(-p[i]); } return result; } template SBasisN operator*(SBasisN const &a, double c){ if(a.isZero()) return SBasisN(); SBasisN result; for(unsigned i = 0; i < n; i++) { result.sizes[i] = a.sizes[i]; } result.reserve(a.size()); for(unsigned i = 0; i < a.size(); i++) { result.push_back(a[i] * c); } return result; } template inline SBasisN operator*(double k, SBasisN const &a) { return a*k; } template inline SBasisN operator/(SBasisN const &a, double k) { return a*(1./k); } template SBasisN& operator*=(SBasisN& a, double c){ for(unsigned i = 0; i < a.size(); i++) a[i] *= c; return a; } template inline SBasisN& operator/=(SBasisN& a, double b) { return (a*=(1./b)); } //IMPL: AddableConcept template SBasisN operator + (const SBasisN& a, const SBasisN& b){ if( a.isZero() ) return b; if( b.isZero() ) return a; SBasisN result; MultiDegree deg = max(a.quick_multi_degree(),b.quick_multi_degree()); unsigned max_size = 1; for(unsigned i = 0; i < n; i++) { result.sizes[i] = deg[i]+1; max_size *= result.sizes[i]; } result.resize( max_size, LinearN(0.) ); for(unsigned i = 0; i < result.size(); i++) { MultiDegree p(i,result.sizes); unsigned ia = p.asIdx(a.sizes); unsigned ib = p.asIdx(b.sizes); if (ia SBasisN operator-(const SBasisN& a, const SBasisN& b){return a+(-b);} template SBasisN& operator+=(SBasisN& a, const SBasisN& b){ if(b.isZero()) return a; a = a + b; return a; } template SBasisN& operator-=(SBasisN& a, const SBasisN& b){ a += -b; return a; } //TODO: remove? template inline SBasisN operator+(const SBasisN & a, LinearN const & b) { if(b.isZero()) return a; if(a.isZero()) return b; SBasisN result(a); result[0] += b; return result; } template inline SBasisN operator-(const SBasisN & a, LinearN const & b) { if(b.isZero()) return a; if(a.isZero()) return -b; SBasisN result(a); result[0] -= b; return result; } template inline SBasisN& operator+=(SBasisN& a, const LinearN& b) { if(a.size()==0) a.push_back(b); else a[0] += b; return a; } template inline SBasisN& operator-=(SBasisN& a, const LinearN& b) { if(a.size()==0) a.push_back(-b); else a[0] -= b; return a; } //IMPL: OffsetableConcept template inline SBasisN operator+(const SBasisN & a, double b) { if(a.isZero()) return LinearN(b); SBasisN result(a); result[0] += b; return result; } template inline SBasisN operator-(const SBasisN & a, double b) { if(a.isZero()) return LinearN(-b); SBasisN result(a); result[0] -= b; return result; } template inline SBasisN& operator+=(SBasisN& a, double b) { if(a.size()==0) a.push_back(LinearN(b)); else a[0] += b; return a; } template inline SBasisN& operator-=(SBasisN& a, double b) { if(a.size()==0) a.push_back(LinearN(-b)); else a[0] -= b; return a; } template SBasisN shift(SBasisN const &a, MultiDegree sh){ SBasisN result; MultiDegree deg = a.quick_multi_degree() + sh; for(unsigned i = 0; i < n; i++) { result.sizes[i] = deg[i]+1; } unsigned max_size = deg.asIdx(result.sizes); result.resize( max_size, LinearN(0.) ); for(unsigned i = 0; i < a.size(); i++) { MultiDegree p(i,a.sizes); p+=sh; result[p.asIdx(result.sizes)]=a[i]; } return result; } template SBasisN shift(LinearN const &a, MultiDegree sh){ SBasisN result; for(unsigned i = 0; i < n; i++) { result.sizes[i] = sh[i]+1; } unsigned max_size = sh.asIdx(result.sizes); result.resize( max_size, LinearN(0.) ); result[max_size-1]=a; return result; } //shift only in one variable template SBasisN shift(LinearN const &a, unsigned sh, unsigned var){ assert( var < n ); SBasisN result; for(unsigned i = 0; i < n; i++) { result.sizes[i] = 1; } result.sizes[var] = sh+1; result.resize( sh+1, LinearN(0.) ); result[sh]=a; return result; } //truncate only in first variable template inline SBasisN truncate(SBasisN const &a, unsigned first_size) { if ( first_size <= a.sizes[0] ) return a; SBasisN c; for (unsigned i = 0; i < n; i++) { c.sizes[i] = a.sizes[i]; } c.sizes[0] = first_size; unsigned tot_size = 1; for(unsigned i = 0; i < n; i++) { tot_size*=c.sizes[i]; } c.insert(c.begin(), a.begin(), a.begin() + tot_size); return c; } template SBasisN multiply(SBasisN const &a, SBasisN const &b){ SBasisN c; MultiDegree d; MultiDegree t_deg = a.real_t_degrees() + b.real_t_degrees(); for(unsigned i = 0; i < n; i++) { d[i] = ( t_deg[i]%2 == 0 ? t_deg[i]/2 : (t_deg[i]-1)/2 ) ; } unsigned new_sizes[n], tot_size = 1; for(unsigned i = 0; i < n; i++) { //c.sizes[i] = d[i] + 1+1;//product of linears might give 1 more s in each dir!! new_sizes[i] = d[i] + 1; tot_size*=new_sizes[i]; } c.resize( tot_size, LinearN(0.) ); for(unsigned i = 0; i < n; i++) { c.sizes[i] = new_sizes[i]; } for(unsigned ai = 0; ai < a.size(); ai++) { for(unsigned bj = 0; bj < b.size(); bj++) { MultiDegree di( ai, a.sizes ); MultiDegree dj( bj, b.sizes ); //compute a[ai]*b[bj]: for(unsigned p = 0; p < (1< dr; unsigned t0 = 0, t1 = 0; for (unsigned var = 0; var < n; var++) { //if var is in mask m, no choice, take s if ( m & (1< inline SBasisN operator*(SBasisN const & a, SBasisN const & b) { return multiply(a, b); } template inline SBasisN& operator*=(SBasisN& a, SBasisN const & b) { a = multiply(a, b); return a; } template SBasisN compose(LinearN const &f, std::vector > const &t, unsigned fixed=0, unsigned flags=0 ){ assert (t.size() == n ); if (fixed == n) { return SBasisN(1.) * f[flags]; }else{ SBasisN a0 = compose(f, t, fixed+1, flags); SBasisN a1 = compose(f, t, fixed+1, flags|(1< SBasisN compose(SBasisN const &f, std::vector > const &t, unsigned const k=0, unsigned const idx = 0){ assert (t.size() == n ); if (k == n){ return compose( f[idx], t); } SBasisN a; SBasisN s = multiply( t[k], (-t[k]+1.) ); SBasisN si= SBasisN(1.); for (unsigned i=0; i inline std::ostream &operator<< (std::ostream &out_file, const MultiDegree & d) { out_file << "s^{"; for(unsigned i = 0; i < n; i++) { out_file << d[i] << (i == n-1 ? "}" : ","); } return out_file; } template inline std::ostream &operator<< (std::ostream &out_file, const SBasisN & p) { for(unsigned i = 0; i < p.size(); i++) { MultiDegree d(i, p.sizes); out_file << d << " " << p[i] << " + "; } return out_file; } //-------------------------------------------------- //-------------------------------------------------- //-------------------------------------------------- //-------------------------------------------------- //-------------------------------------------------- #if 0 // This performs a multiply and accumulate operation in about the same time as multiply. return a*b + c template SBasisN multiply_add(SBasisN const &a, SBasisN const &b, SBasisN c); template SBasisN integral(SBasisN const &c); template SBasisN derivative(SBasisN const &a); template SBasisN sqrt(SBasisN const &a, int k); // return a kth order approx to 1/a) template SBasisN reciprocal(LinearN const &a, int k); template SBasisN divide(SBasisN const &a, SBasisN const &b, int k); /** Returns the degree of the first non zero coefficient. \param a sbasis function \param tol largest abs val considered 0 \returns first non zero coefficient */ template inline unsigned valuation(SBasisN const &a, double tol=0){ unsigned val=0; while( val SBasisN compose(SBasisN const &a, SBasisN const &b); template SBasisN compose(SBasisN const &a, SBasisN const &b, unsigned k); template SBasisN inverse(SBasisN a, int k); //compose_inverse(f,g)=compose(f,inverse(g)), but is numerically more stable in some good cases... //TODO: requires g(0)=0 & g(1)=1 atm. generalization should be obvious. template SBasisN compose_inverse(SBasisN const &f, SBasisN const &g, unsigned order=2, double tol=1e-3); /** Returns the sbasis on domain [0,1] that was t on [from, to] \param a sbasis function \param from,to interval \returns sbasis */ template inline SBasisN portion(const SBasisN &t, double from, double to) { return compose(t, LinearN(from, to)); } // compute f(g) template inline SBasisN SBasisN::operator()(SBasisN const & g) const { return compose(*this, g); } template inline std::ostream &operator<< (std::ostream &out_file, const LinearN &bo) { out_file << "{" << bo[0] << ", " << bo[1] << "}"; return out_file; } template inline std::ostream &operator<< (std::ostream &out_file, const SBasisN & p) { for(unsigned i = 0; i < p.size(); i++) { out_file << p[i] << "s^" << i << " + "; } return out_file; } // These are deprecated, use sbasis-math.h versions if possible template SBasisN sin(LinearN bo, int k); template SBasisN cos(LinearN bo, int k); template std::vector roots(SBasisN const & s); template std::vector > multi_roots(SBasisN const &f, std::vector const &levels, double htol=1e-7, double vtol=1e-7, double a=0, double b=1); #endif //-------------------------------------------------- //-------------------------------------------------- //-------------------------------------------------- //-------------------------------------------------- //-------------------------------------------------- } /* Local Variables: mode:c++ c-file-style:"stroustrup" c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) indent-tabs-mode:nil fill-column:99 End: */ // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 : #endif