/*
**  Copyright (C) 1999-2008 F. Blomquist, M. Braeuer, M. Grimmer,
**                          W. Hofschuster, W. Kraemer
**                          Wiss. Rechnen/Softwaretechnologie
**                          Universitaet Wuppertal, Germany   
**
**  This library is free software; you can redistribute it and/or
**  modify it under the terms of the GNU Library General Public
**  License as published by the Free Software Foundation; either
**  version 2 of the License, or (at your option) any later version.
**
**  This library is distributed in the hope that it will be useful,
**  but WITHOUT ANY WARRANTY; without even the implied warranty of
**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
**  Library General Public License for more details.
**
**  You should have received a copy of the GNU Library General Public
**  License along with this library; if not, write to the Free
**  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

//////////////////////////////////////////////////////////////
//
//       Implementation of class itaylor in itaylor.cpp     
//
//////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////
//     Updated by F. Blomquist, M. Grimmer
//     Extended version 05.03.2006 by M. Grimmer
////////////////////////////////////////////////////////////////////////

#include "itaylor.hpp"

///////////////////////////////////////////////////////////////
//
//                     class itaylor
//
///////////////////////////////////////////////////////////////

namespace taylor {

ivector itaylor::faks(1);
int itaylor::initialized=0;

void itaylor::initialize()
{
 Resize(itaylor::faks,0,170);
 itaylor::faks[0]=interval(1.0);
 itaylor::faks[1]=interval(1.0);

 for(int i=2; i<=170; i++)
   itaylor::faks[i]=itaylor::faks[i-1]*interval(i);
}

//-----------------------------------------------------------------------

// Constructors:

itaylor::itaylor()
{
 if(!itaylor::initialized){itaylor::initialize();itaylor::initialized=1;};
}

//-----------------------------------------------------------------------

itaylor::itaylor(int order)
{
 if(!itaylor::initialized){itaylor::initialize();itaylor::initialized=1;};
 if(order<0 || order>170) 
  {
    std::cerr << "itaylor::itaylor: incorrect order! 0<=order<=170" 
              << std::endl;
    exit(1);
  }
 p=order;
 Resize(tayl,0,p);
}

//-----------------------------------------------------------------------

itaylor::itaylor(const itaylor& s)
{
 if(!itaylor::initialized){itaylor::initialize();itaylor::initialized=1;};
 p=s.p;
 Resize(tayl,0,p);
 tayl=s.tayl;
}

//-----------------------------------------------------------------------

itaylor::itaylor(int order, const real& value)
{
 if(!itaylor::initialized){itaylor::initialize();itaylor::initialized=1;};
 if(order<0 || order>170) 
  {
    std::cerr << "itaylor::itaylor: incorrect order! 0<=order<=170" 
              << std::endl;
    exit(1);
  }
 p=order;
 Resize(tayl,0,p);
 
 interval interval_value=interval(value);
 tayl[0]=interval_value;
 if(p>0)
   {
     tayl[1]=interval(1.0);
     for(int i=2; i<=Ub(tayl);i++) tayl[i]=interval(0.0);
   }
}

//-----------------------------------------------------------------------

itaylor::itaylor(int order, const interval& value)
{
 if(!itaylor::initialized){itaylor::initialize();itaylor::initialized=1;};
 if(order<0 || order>170) 
  {
    std::cerr << "itaylor::itaylor: incorrect order! 0<=order<=170" 
              << std::endl;
    exit(1);
  }
 p=order;
 Resize(tayl,0,p);
 tayl[0]=value;
 if(p>0)
   { 
     tayl[1]=interval(1.0);
     for(int i=2; i<=Ub(tayl);i++) tayl[i]=interval(0.0);
   }
}

//-----------------------------------------------------------------------

// Functions for initialization of independent variables:
itaylor var_itaylor(int ord, const real& x)
{
    itaylor erg(ord,x);
    return erg;
}

//-----------------------------------------------------------------------

itaylor var_itaylor(int ord, const interval& x)
{
    itaylor erg(ord,x);
    return erg;
}

//-----------------------------------------------------------------------


// Functions for initialization of constants:
itaylor const_itaylor(int ord, const real& c)
{
 itaylor erg(ord);
 erg.tayl[0]=interval(c);
 for(int i=1; i<=Ub(erg.tayl);i++) erg.tayl[i]=interval(0.0);
 return erg;
}

//-----------------------------------------------------------------------

itaylor const_itaylor(int ord, const interval& c)
{
 itaylor erg(ord);
 erg.tayl[0]=c;
 for(int i=1; i<=Ub(erg.tayl);i++) erg.tayl[i]=interval(0.0);
 return erg;
}
//-----------------------------------------------------------------------



//-----------------------------------------------------------------------
// assignment operators
//-----------------------------------------------------------------------

itaylor itaylor::operator=(const itaylor& s)
{
 p=s.p;
 Resize(tayl,0,s.p);
 tayl=s.tayl;
 return *this;
}

itaylor itaylor::operator=(int n)
{
 tayl[0] = interval(n);
 for (int j=1; j<=p; j++) tayl[j]=0.0;
 return *this;
}

itaylor itaylor::operator=(const real& x)
{
 tayl[0] = interval(x);
 for (int j=1; j<=p; j++) tayl[j]=0.0;
 return *this;
}

itaylor itaylor::operator=(const interval& x)
{
 tayl[0] = x;
 for (int j=1; j<=p; j++) tayl[j]=0.0;
 return *this;
}

itaylor itaylor::operator=(const ivector& iv) //added,mg,2005-08
                                              //const since C-XSC 2.1,mg,2006-02

{ 
 p=VecLen(iv)-1;
 Resize(tayl,0,p);
 tayl=iv;
 return *this;
}

//-----------------------------------------------------------------------
// relational operators
//-----------------------------------------------------------------------

int itaylor::operator==(itaylor& it)     //added, mg2005-08
{
  return ((p==it.p)&&(tayl==it.tayl));
}

int itaylor::operator!=(itaylor& it)     //added, mg2005-08
{
  return (!((p==it.p)&&(tayl==it.tayl)));
}

int itaylor::operator<=(itaylor& it)     //added, mg2005-08
{
  return ((p==it.p)&&(tayl<=it.tayl));
}

int itaylor::operator<(itaylor& it)      //added, mg2005-08
{
  return ((p==it.p)&&(tayl<it.tayl));
}

int itaylor::operator>=(itaylor& it)     //added, mg2005-08
{
  return ((p==it.p)&&(tayl>=it.tayl));
}

int itaylor::operator>(itaylor& it)      //added, mg2005-08
{
  return ((p==it.p)&&(tayl>it.tayl));
}

//-----------------------------------------------------------------------
// component access
//-----------------------------------------------------------------------

interval& itaylor::operator[](int n) //added, mg2005-08
// r/w access -> not const 
{
 return tayl[n];
}


//-----------------------------------------------------------------------

// class components:

// returning the maximal order
int get_order(const itaylor& x)
{
 return x.p;
}

//-----------------------------------------------------------------------

// returning all Taylor-coefficients by an interval vector
ivector get_all_coef(const itaylor& x)
{
 return x.tayl;
}

//-----------------------------------------------------------------------

// returning Taylor-coefficient of order j
interval get_j_coef(const itaylor& x, int j)
{
 return x.tayl[j];
}

//-----------------------------------------------------------------------

// returning derivative of order j,  j <= 170;
interval get_j_derive(const itaylor& x, int j)
{
  return x.tayl[j]*itaylor::faks[j];
}
interval get_j_derivative(const itaylor& x, int j)
{
  return x.tayl[j]*itaylor::faks[j];
}

//-----------------------------------------------------------------------

// Output of all taylor coefficients 
void print_itaylor(const itaylor& x)
{
 std::cerr <<"Output itaylor of order " << x.p << " " << std::endl;
 for(int i=Lb(x.tayl); i<=Ub(x.tayl);i++) 
  {
   std::cerr << "i  " << i << "  component: " << x.tayl[i] << std::endl;
  };
 std::cerr << std::endl;
}

void print_itaylor(std::ostream& os, const itaylor& x, int width, int digits)  
                                             // added, mg2005,2006
{
 os <<"Ausgabe itaylor der Ordnung " << x.p << " " << std::endl;
 for(int i=Lb(x.tayl); i<=Ub(x.tayl);i++) 
  {
   os << "i  " << i << "  component: ";
   if (width>0||digits>0) os << SetPrecision(width,digits);
   os << x.tayl[i] << std::endl;
  };
 os << std::endl;
}

//-----------------------------------------------------------------------

std::ostream& operator<< (std::ostream& os, itaylor& x)
                                             // added, mg2005-08
{
 os <<"[itaylor object, order " << x.p << ":]" << std::endl;
 for(int i=Lb(x.tayl); i<=Ub(x.tayl);i++) 
  {
   os << "[" << i << "]";
   os << x.tayl[i] << std::endl;
  };
 os << std::endl;
 return os;
}                                


//-----------------------------------------------------------------------

// Overloading of operators:

//-----------------------------------------------------------------------

// - operator:
itaylor operator-(const itaylor& x)
{
 int order=get_order(x);
 itaylor erg(order);
 for(int j=Lb(x.tayl); j<=Ub(x.tayl); j++) erg.tayl[j]= -x.tayl[j];
 return erg;
}

//-----------------------------------------------------------------------

// Operators with two operands:  +,-,*,/  for (itaylor, itaylor):
// All operands are independent variables.
itaylor operator-(const itaylor& x, const itaylor& y)
{
 int order1=get_order(x);
 int order2=get_order(y);
 if(order1 != order2) 
  {
   std::cerr << "Error in itaylor, operator - : different orders " 
             << std::endl;
   exit(1);
  };

 itaylor erg(order1);
 for(int j=Lb(x.tayl); j<=Ub(x.tayl); j++) erg.tayl[j]= x.tayl[j]-y.tayl[j];
 return erg; 
}

//-----------------------------------------------------------------------

itaylor operator+(const itaylor& x, const itaylor& y)
{
 int order1=get_order(x);
 int order2=get_order(y);
 if(order1 != order2) 
  {
   std::cerr << "Error in itaylor, operator + : different orders " 
             << std::endl;
   exit(1);
  };

 itaylor erg(order1);
 for(int j=Lb(x.tayl); j<=Ub(x.tayl); j++) erg.tayl[j]= x.tayl[j]+y.tayl[j];
 return erg; 
}

//-----------------------------------------------------------------------

itaylor operator*(const itaylor& x, const itaylor& y)
{
 int order1=get_order(x);
 int order2=get_order(y);
 if(order1 != order2) 
  {
   std::cerr << "Error in itaylor, operator * : different orders " 
             << std::endl;
   exit(1);
  };

 itaylor erg(order1);
 interval sum; 
 idotprecision sum_idot; // for accumulate(...), scalar product

 for(int j=0; j<=order1; j++) 
 {
  sum_idot=interval(0);
  for(int i=0; i<=j; i++)
   {
    accumulate(sum_idot, x.tayl[i],y.tayl[j-i]);
   }
  rnd(sum_idot,sum);
  erg.tayl[j]= sum;
 }
 return erg; 
}

//-----------------------------------------------------------------------

itaylor operator/(const itaylor& x, const itaylor& y)
{
 int order1(get_order(x));
 int order2(get_order(y));
 if(order1 != order2) 
  {
   std::cerr << "Error in itaylor, operator / : different orders " 
             << std::endl;
   exit(1);
  };
 
 if(0 <= y.tayl[0]) 
  {
   std::cerr << "Error in itaylor, operator / : 0 in interval " << std::endl;
   exit(1);
  };

 itaylor erg(order1);
 interval sum; 
 idotprecision sum_idot; // for accumulate(...), scalar product

 for(int j=0; j<=order1; j++) 
 {
  sum_idot=interval(0);
  for(int i=1; i<=j; i++)
   {
    accumulate(sum_idot, y.tayl[i],erg.tayl[j-i]);
   }
  rnd(sum_idot,sum);
  erg.tayl[j]= (x.tayl[j]-sum)/y.tayl[0];
 }
 return erg;
}

//-----------------------------------------------------------------------

// Operators with two operands:   +,-,*,/  for (interval, itaylor):
// The operand of type interval is assumed to be a constant and not
// an independent variable!

itaylor operator-(const interval& x, const itaylor& y)
{
    int order(get_order(y));
    itaylor erg(order);
    erg = -y;
    erg.tayl[0] = x - y.tayl[0];
    return erg;
}

//-----------------------------------------------------------------------

itaylor operator+(const interval& x, const itaylor& y)
{
    int order(get_order(y));
    itaylor erg(order);
    erg = y;
    erg.tayl[0] = x + y.tayl[0];
    return erg;
}

//-----------------------------------------------------------------------

itaylor operator*(const interval& x, const itaylor& y)
{
    int order(get_order(y));
    itaylor erg(order);
    for (int j=0; j<=order; j++) erg.tayl[j] = x*y.tayl[j];
    return erg;
}

//-----------------------------------------------------------------------

itaylor operator/(const interval& x, const itaylor& y)
{
    if (0<=y.tayl[0])
    {
	std::cerr << "Error in itaylor, operator / : 0 in interval " 
                  << std::endl;
	exit(1);
    }; 
    idotprecision idot;
    interval sum;
    int order(get_order(y));
    itaylor w(order);
    w.tayl[0] = x / y.tayl[0];
    for (int k=1; k<=order; k++)
    {
	idot=0;
	for (int j=1; j<=k; j++) 
	    accumulate(idot,y.tayl[j],w.tayl[k-j]);
	rnd(idot,sum);
	w.tayl[k] = -sum / y.tayl[0];
    }
    return w;
}

//-----------------------------------------------------------------------

// Operators with two operands:   +,-,*,/  for (itaylor, interval):
// The operand of type interval is assumed to be a constant and not
// an independent variable!

itaylor operator-(const itaylor& x, const interval& y)
{
    int order(get_order(x));
    itaylor erg(order);
    erg = x;
    erg.tayl[0] = x.tayl[0] - y;
    return erg;
}

//-----------------------------------------------------------------------

itaylor operator+(const itaylor& x, const interval& y)
{
    int order(get_order(x));
    itaylor erg(order);
    erg = x;
    erg.tayl[0] = x.tayl[0] + y;
    return erg;
}

//-----------------------------------------------------------------------

itaylor operator*(const itaylor& x, const interval& y)
{
    int order(get_order(x));
    itaylor erg(order);
    for (int j=0; j<=order; j++) erg.tayl[j] = x.tayl[j]*y;
    return erg;
}

//-----------------------------------------------------------------------

itaylor operator/(const itaylor& x, const interval& y)
{
    int order(get_order(x));
    itaylor erg(order);
    for (int j=0; j<=order; j++) erg.tayl[j] = x.tayl[j]/y;
    return erg;
}

//-----------------------------------------------------------------------

// Operators with two operands:   +,-,*,/  for (real, itaylor):
// The operand of type real is assumed to be a constant and not
// an independent variable!

itaylor operator-(const real& x, const itaylor& y)
{
    int order(get_order(y));
    itaylor erg(order);
    erg = -y;
    erg.tayl[0] = x - y.tayl[0];
    return erg;
}

//-----------------------------------------------------------------------

itaylor operator+(const real& x, const itaylor& y)
{
    int order(get_order(y));
    itaylor erg(order);
    erg = y;
    erg.tayl[0] = x + y.tayl[0];
    return erg;
}

//-----------------------------------------------------------------------

itaylor operator*(const real& x, const itaylor& y)
{
    int order(get_order(y));
    itaylor erg(order);
    for (int j=0; j<=order; j++) erg.tayl[j] = x*y.tayl[j];
    return erg;
}

//-----------------------------------------------------------------------

itaylor operator/(const real& x, const itaylor& y)
{
    if (0<=y.tayl[0])
    {
	std::cerr << "Error in itaylor, operator / : 0 in interval " 
                  << std::endl;
	exit(1);
    }; 
    idotprecision idot;
    interval sum;
    int order(get_order(y));
    itaylor w(order);
    w.tayl[0] = x / y.tayl[0];
    for (int k=1; k<=order; k++)
    {
	idot=0;
	for (int j=1; j<=k; j++) 
	    accumulate(idot,y.tayl[j],w.tayl[k-j]);
	rnd(idot,sum);
	w.tayl[k] = -sum / y.tayl[0];
    }
    return w;
}

//-----------------------------------------------------------------------

// Operators with two operands:   +,-,*,/  for (itaylor, real):
// The operand of type real is assumed to be a constant and not
// an independent variable!

itaylor operator-(const itaylor& x, const real& y)
{
    int order(get_order(x));
    itaylor erg(order);
    erg = x;
    erg.tayl[0] = x.tayl[0] - y;
    return erg;
}

//-----------------------------------------------------------------------

itaylor operator+(const itaylor& x, const real& y)
{
    int order(get_order(x));
    itaylor erg(order);
    erg = x;
    erg.tayl[0] = x.tayl[0] + y;
    return erg;
}

//-----------------------------------------------------------------------

itaylor operator*(const itaylor& x, const real& y)
{
    int order(get_order(x));
    itaylor erg(order);
    for (int j=0; j<=order; j++) erg.tayl[j] = x.tayl[j]*y;
    return erg;
}

//-----------------------------------------------------------------------

itaylor operator/(const itaylor& x, const real& y)
{
    if (y==0)
    {
	std::cerr << "Error in itaylor: division by 0" 
                  << std::endl;
	exit(1);
    };
    int order(get_order(x));
    itaylor erg(order);
    for (int j=0; j<=order; j++) erg.tayl[j] = x.tayl[j]/y;
    return erg;
}

// Operators with two operands:   +,-,*,/  for (int, itaylor):
// The operand of type real is assumed to be a constant and not
// an independent variable!

itaylor operator-(int x, const itaylor& y)
{
    int order(get_order(y));
    itaylor erg(order);
    erg = -y;
    erg.tayl[0] = interval(x) - y.tayl[0];
    return erg;
}

//-----------------------------------------------------------------------

itaylor operator+(int x, const itaylor& y)
{
    int order(get_order(y));
    itaylor erg(order);
    erg = y;
    erg.tayl[0] = interval(x) + y.tayl[0];
    return erg;
}

//-----------------------------------------------------------------------

itaylor operator*(int x, const itaylor& y)
{
    int order(get_order(y));
    itaylor erg(order);
    for (int j=0; j<=order; j++) erg.tayl[j] = interval(x)*y.tayl[j];
    return erg;
}

//-----------------------------------------------------------------------

itaylor operator/(int x, const itaylor& y)
{
    if (0<=y.tayl[0])
    {
	std::cerr << "Error in itaylor, operator / : 0 in interval " 
                  << std::endl;
	exit(1);
    }; 
    idotprecision idot;
    interval sum;
    int order(get_order(y));
    itaylor w(order);
    w.tayl[0] = interval(x) / y.tayl[0];
    for (int k=1; k<=order; k++)
    {
	idot=0;
	for (int j=1; j<=k; j++) 
	    accumulate(idot,y.tayl[j],w.tayl[k-j]);
	rnd(idot,sum);
	w.tayl[k] = -sum / y.tayl[0];
    }
    return w;
}

//-----------------------------------------------------------------------

// Operators with two operands:   +,-,*,/  for (itaylor, int):
// The operand of type real is assumed to be a constant and not
// an independent variable!

itaylor operator-(const itaylor& x, int y)
{
    int order(get_order(x));
    itaylor erg(order);
    erg = x;
    erg.tayl[0] = x.tayl[0] - interval(y);
    return erg;
}

//-----------------------------------------------------------------------

itaylor operator+(const itaylor& x, int y)
{
    int order(get_order(x));
    itaylor erg(order);
    erg = x;
    erg.tayl[0] = x.tayl[0] + interval(y);
    return erg;
}

//-----------------------------------------------------------------------

itaylor operator*(const itaylor& x, int y)
{
    int order(get_order(x));
    itaylor erg(order);
    for (int j=0; j<=order; j++) erg.tayl[j] = x.tayl[j]*interval(y);
    return erg;
}

//-----------------------------------------------------------------------

itaylor operator/(const itaylor& x, int y)
{
    if (y==0)
    {
	std::cerr << "Error in itaylor: division by 0" 
                  << std::endl;
	exit(1);
    };
    int order(get_order(x));
    itaylor erg(order);
    for (int j=0; j<=order; j++) erg.tayl[j] = x.tayl[j]/interval(y);
    return erg;
}

//-----------------------------------------------------------------------

// Overloading the standard functions:

//-----------------------------------------------------------------------

// Help function
void f_g_u(const itaylor& f, const itaylor& g, const itaylor& u, int nb_function)
{
 int order1=get_order(f);
 int order2=get_order(g);
 int order3=get_order(u);

 // The following errors should be caught before 
 // but for security here again:
 if(order1 != order2) 
  {
   std::cerr << "Error1 in f_g_u: different orders " 
             << std::endl;
   exit(1);
  };

 if(order3 != order2) 
  {
   std::cerr << "Error2 in f_g_u: different orders " << std::endl;
   exit(1);
  };

 if(0 <= g.tayl[0]) 
  {
   std::cerr << "Error in f_g_u : wrong argument " << std::endl;
   exit(1);
  }; 

 switch(nb_function) // element No. 0
   {
    case _i_ln:f.tayl[0]=ln(u.tayl[0]); break;

    case _i_tan:f.tayl[0]=tan(u.tayl[0]); break;
    case _i_cot:f.tayl[0]=cot(u.tayl[0]); break;

    case _i_asin:f.tayl[0]=asin(u.tayl[0]); break;
    case _i_acos:f.tayl[0]=acos(u.tayl[0]); break;
    case _i_atan:f.tayl[0]=atan(u.tayl[0]); break;
    case _i_acot:f.tayl[0]=acot(u.tayl[0]); break;

    case _i_tanh:f.tayl[0]=tanh(u.tayl[0]); break;
    case _i_coth:f.tayl[0]=coth(u.tayl[0]); break; 

    case _i_asinh:f.tayl[0]=asinh(u.tayl[0]); break;
    case _i_acosh:f.tayl[0]=acosh(u.tayl[0]); break;
    case _i_atanh:f.tayl[0]=atanh(u.tayl[0]); break;

    case _i_acoth:f.tayl[0]=acoth(u.tayl[0]); break;
   }
 
 // remaining elements:
 interval sum; 
 for(int j=1; j<=Ub(f.tayl); j++) 
 {
     sum = interval(0);
     for(int i=1; i<=j-1; i++)
	 sum += interval(i)*f.tayl[i]*g.tayl[j-i];
     f.tayl[j] = (u.tayl[j]-sum/interval(j)) / g.tayl[0];
 }
}

// sqr-function
itaylor sqr(const itaylor& x)
{
    idotprecision idot;
    interval sum;
    int order(get_order(x)),m;
    itaylor erg(order);

    erg.tayl[0] = sqr(x.tayl[0]);
    for (int k=1; k<=order; k++)
    {
	m = (k+1) / 2;
	idot = 0;
	for (int j=0; j<=m-1; j++) 
	    accumulate(idot,x.tayl[j],x.tayl[k-j]);
	rnd(idot,sum);
	times2pown(sum,1);  // Multiplication with 2
	erg.tayl[k] = sum;
	if (k%2==0) erg.tayl[k] += sqr(x.tayl[m]); // k even 
    }
    return erg; 
}

//-----------------------------------------------------------------------

// Square-root

itaylor sqrt(const itaylor& x)
{
    idotprecision idot;
    interval sum,h;
    int order(get_order(x)),m;
    itaylor erg(order);
    if (0<=x.tayl[0]) 
    {
	std::cerr << "Error in itaylor, sqrt: 0 in interval" << std::endl;
	exit(1);
    };
    erg.tayl[0] = sqrt(x.tayl[0]);
    h = erg.tayl[0];
    times2pown(h,1);
    for (int k=1; k<=order; k++)
    {
	m = (k+1) / 2;
	idot = 0;
	for (int j=1; j<=m-1; j++) 
	    accumulate(idot,erg.tayl[j],erg.tayl[k-j]);
	rnd(idot,sum);
	times2pown(sum,1);  // Multiplication with 2
	erg.tayl[k] = sum;
	if (k%2==0) erg.tayl[k] += sqr(erg.tayl[m]); // k even 
	erg.tayl[k] = (x.tayl[k]-erg.tayl[k]) / h;
    }
    return erg;
}

//-----------------------------------------------------------------------

// sqrt(x,n)

itaylor sqrt(const itaylor& x, int n)
{
    int order(get_order(x));
    itaylor erg(order);
    if (0<=x.tayl[0]) 
    {
	std::cerr << "Error in itaylor, sqrt(x,n): 0 in interval" << std::endl;
	exit(1);
    };
    erg.tayl[0] = sqrt(x.tayl[0],n); // element No. 0
    for (int k=1; k<=order; k++)
    {
	erg.tayl[k] = 0;
	for (int j=0; j<=k-1; j++)
	    erg.tayl[k] += (interval(k-j)/real(n)-interval(j))
		           * erg.tayl[j] * x.tayl[k-j];
	erg.tayl[k] /= (interval(k)*x.tayl[0]);
    }
    return erg;
}
//-----------------------------------------------------------------------

// sqrt(1-x^2):
itaylor sqrt1mx2(const itaylor& x)
{
    idotprecision idot;
    interval sum,h;
    int order(get_order(x)),m;
    itaylor erg(order), g(order);

    if (Inf(x.tayl[0])<=-1 || Sup(x.tayl[0])>=1)
    {
	std::cerr << "Error in itaylor, sqrt1mx2: wrong argument" << std::endl;
	exit(1);
    };
    erg.tayl[0]=sqrt(real(1)-sqr(x.tayl[0])); // =sqrt1mx2(x.tayl[0]); Blomi 
    h = real(-1)/erg.tayl[0];
    times2pown(h,-1);
    g = sqr(x);
    for (int k=1; k<=order; k++)
    {
	m = (k+1)/2;
	idot = 0;
	for (int j=1; j<=m-1; j++) 
	    accumulate(idot,erg.tayl[j],erg.tayl[k-j]);
	rnd(idot,sum);
	times2pown(sum,1);  // Multiplication with 2
	erg.tayl[k] = sum;
	if (k%2==0) erg.tayl[k] += sqr(erg.tayl[m]); // k even 
	erg.tayl[k] = (g.tayl[k]+erg.tayl[k])*h;
    }
    return erg;
}

//-----------------------------------------------------------------------

// sqrt(x^2-1):
itaylor sqrtx2m1(const itaylor& x)
{
    const real c = 30.0; 
    idotprecision idot;
    interval sum,h;
    int order(get_order(x)),m;
    itaylor erg(order), g(order);

    if (Disjoint(x.tayl[0],interval(-1,1))==0)
    {
	std::cerr << "Error in itaylor, sqrtx2m1: wrong argument" << std::endl;
	exit(1);
    };

    if (Inf(x.tayl[0])>c) erg = x*sqrt1mx2(real(1)/x);
    else if (Sup(x.tayl[0])<-c) erg = -x*sqrt1mx2(real(1)/x);
    else {
	erg.tayl[0]=sqrt(sqr(x.tayl[0])-real(1)); // =sqrtx2m1(x.tayl[0]); Blomi 
	g = sqr(x);
	h = real(1)/erg.tayl[0];
	times2pown(h,-1);
	for (int k=1; k<=order; k++)
	{
	    m = (k+1)/2;
	    idot = 0;
	    for (int j=1; j<=m-1; j++) 
		accumulate(idot,erg.tayl[j],erg.tayl[k-j]);
	    rnd(idot,sum);
	    times2pown(sum,1);  // Multiplication with 2
	    erg.tayl[k] = sum;
	    if (k%2==0) erg.tayl[k] += sqr(erg.tayl[m]); // k even 
	    erg.tayl[k] = (g.tayl[k]-erg.tayl[k])*h;
	}
    }
    return erg;
}

// sqrt(1+x)-1
itaylor sqrtp1m1(const itaylor& x)
{
    int order(get_order(x)),m;
    itaylor erg(order);
    idotprecision idot;
    interval h,Ne;

    if (Inf(x.tayl[0])<=-1)
    {
	std::cerr << "Error in itaylor, sqrtp1m1: wrong argument" << std::endl;
	exit(1);
    };

    erg.tayl[0] = sqrtp1m1(x.tayl[0]);
    Ne = real(1.0)+erg.tayl[0];
    times2pown(Ne,1);

    for (int k=1; k<=order; k++)
    {
	m = (k+1)/2;
	idot = 0;
	for (int j=1; j<=m-1; j++)
	    accumulate(idot,erg.tayl[j],erg.tayl[k-j]);
	rnd(idot,h);
	times2pown(h,1);
	erg.tayl[k] = h;
	if (k%2==0) erg.tayl[k] += sqr(erg.tayl[m]);
	erg.tayl[k] = (x.tayl[k]-erg.tayl[k]) / Ne; 
    }
    return erg;
} // sqrtp1m1

//-----------------------------------------------------------------------

// power-function
itaylor pow(const itaylor& x, const interval& alpha)
{
    int order(get_order(x));
    itaylor erg(order);

    if (0<=x.tayl[0])
    {
	std::cerr << "Error in itaylor, pow(x,a): 0 in interval x" 
                  << std::endl;
	exit(1);
    };

    erg.tayl[0] = pow(x.tayl[0],alpha); // element No. 0
    for (int k=1; k<=order; k++)
    {
	erg.tayl[k] = 0;
	for (int j=0; j<=k-1; j++)
	    erg.tayl[k] += (interval(k-j)*alpha-interval(j))
		           * erg.tayl[j] * x.tayl[k-j];
	erg.tayl[k] /= (interval(k)*x.tayl[0]);
    }
    return erg;
}


//-----------------------------------------------------------------------

// Exponential-function
itaylor exp(const itaylor& x)
{
    int order(get_order(x));
    itaylor erg(order);

    erg.tayl[0] = exp(x.tayl[0]); // element No. 0; function value
    for(int k=1; k<=order; k++)
    {
	erg.tayl[k] = 0;
	for(int j=0; j<=k-1; j++)
	    erg.tayl[k] += interval(k-j)*erg.tayl[j]*x.tayl[k-j]; 
	erg.tayl[k] /= interval(k);
    }
    return erg; 
}

//-----------------------------------------------------------------------

// exp(x)-1;
itaylor expm1(const itaylor& x)
{
    int order(get_order(x));
    itaylor erg(order);

    erg.tayl[0] = exp(x.tayl[0]); // element No. 0; function value
    for(int k=1; k<=order; k++)
    {
	erg.tayl[k] = 0;
	for(int j=0; j<=k-1; j++)
	    erg.tayl[k] += interval(k-j)*erg.tayl[j]*x.tayl[k-j]; 
	erg.tayl[k] /= interval(k);
    }
    erg.tayl[0] = exp(x.tayl[0])-real(1); // = expm1(x.tayl[0]); Blomi
    return erg; 
}

//-----------------------------------------------------------------------

// Logarithm-function
itaylor ln(const itaylor& x)
{
    int order=get_order(x);
    itaylor f(order);

    f_g_u(f,x,x,_i_ln);
  
    return f;
}

//-----------------------------------------------------------------------

// ln(1+x)
itaylor lnp1(const itaylor& x)
{
    int order(get_order(x));
    itaylor erg(order), g(order);

    g = interval(1) + x;
    erg.tayl[0] = lnp1(x.tayl[0]);
    for (int k=1; k<=order; k++)
    {
	erg.tayl[k] = 0;
	for (int j=1; j<=k-1; j++)
	    erg.tayl[k] += interval(j) * erg.tayl[j] * g.tayl[k-j];
	erg.tayl[k] = (x.tayl[k]-erg.tayl[k]/interval(k)) / g.tayl[0];
    }
    return erg;
}

//-----------------------------------------------------------------------

// Sinus-function
itaylor sin(const itaylor& x)
{
    int order(get_order(x));
    itaylor erg1(order);   // sin
    itaylor erg2(order);   // cos
    interval s1,s2;

    erg1.tayl[0]=sin(x.tayl[0]); // Element No. 0:  erg1 (sin)
    erg2.tayl[0]=cos(x.tayl[0]); // Element No. 0:  erg2 (cos)

    // remainig elements: 
    for(int j=1; j<=Ub(x.tayl); j++) 
    {
	s1=s2=interval(0);
	for(int i=0; i<=j-1; i++)
	{
	    s1 += interval(j-i) * erg2.tayl[i] * x.tayl[j-i];
	    s2 += interval(j-i) * erg1.tayl[i] * x.tayl[j-i];
	}
	erg1.tayl[j]= s1/interval(j);
	erg2.tayl[j]= real(-1.0)/interval(j)*s2;
    }
    return erg1; 
}

//-----------------------------------------------------------------------

// Cosinus-function
itaylor cos(const itaylor& x)
{
    int order(get_order(x));
    itaylor erg1(order);   // sin
    itaylor erg2(order);   // cos
    interval s1,s2;

    erg1.tayl[0]=sin(x.tayl[0]); // Element No. 0:  erg1 (sin)
    erg2.tayl[0]=cos(x.tayl[0]); // Element No. 0:  erg2 (cos)

    // remainig elements: 
    for(int j=1; j<=Ub(x.tayl); j++) 
    {
	s1=s2=interval(0);
	for(int i=0; i<=j-1; i++)
	{
	    s1 += interval(j-i) * erg2.tayl[i] * x.tayl[j-i];
	    s2 += interval(j-i) * erg1.tayl[i] * x.tayl[j-i];
	}
	erg1.tayl[j]= s1/interval(j);
	erg2.tayl[j]= real(-1.0)/interval(j)*s2;
    }
 return erg2; 
}

//-----------------------------------------------------------------------

// Tangens-function
itaylor tan(const itaylor& x)
{
    int order=get_order(x);
    itaylor f(order);
    itaylor g(order);

    g=sqr(cos(x));

    if(0 <= g.tayl[0]) 
    {
	std::cerr << "Error in itaylor, tan : wrong argument" << std::endl;
	exit(1);
    };  
 
    f_g_u(f,g,x,_i_tan);
  
    return f;
}

//-----------------------------------------------------------------------

// Cotangens-function
itaylor cot(const itaylor& x)
{
    int order=get_order(x);
    itaylor f(order);
    itaylor g(order);

    g=-sqr(sin(x));

    if(0 <= g.tayl[0]) 
    {
	std::cerr << "Error in itaylor, cot : wrong argument" << std::endl;
	exit(1);
    };  
 
    f_g_u(f,g,x,_i_cot);
  
    return f;
}

//-----------------------------------------------------------------------

// Sinushyperbolicus-function
itaylor sinh(const itaylor& x)
{
    int order(get_order(x));
    itaylor erg1(order);  // sinh
    itaylor erg2(order);  // cosh
    interval s1,s2;

    erg1.tayl[0]=sinh(x.tayl[0]); // element No. 0:  erg1 (sinh)
    erg2.tayl[0]=cosh(x.tayl[0]); // element No. 0:  erg2 (cosh)

    // remainig elements: 
    for(int j=1; j<=Ub(x.tayl); j++) 
    {
	s1=s2=interval(0);
	for(int i=0; i<=j-1; i++)
	{
	    s1 += interval(j-i) * erg2.tayl[i] * x.tayl[j-i];
	    s2 += interval(j-i) * erg1.tayl[i] * x.tayl[j-i];
	}
	erg1.tayl[j]= s1/interval(j);
	erg2.tayl[j]= s2/interval(j);
    }
    return erg1; 
}

//-----------------------------------------------------------------------

// Cosinushyperbolicus-function
itaylor cosh(const itaylor& x)
{
    int order(get_order(x));
    itaylor erg1(order); // sinh
    itaylor erg2(order); // cosh
    interval s1,s2;

    erg1.tayl[0]=sinh(x.tayl[0]); // element No. 0:  erg1 (sinh)
    erg2.tayl[0]=cosh(x.tayl[0]); // element No. 0:  erg2 (cosh)

    // remaining elements: 
    for(int j=1; j<=Ub(x.tayl); j++) 
    {
	s1=s2=interval(0);
	for(int i=0; i<=j-1; i++)
	{
	    s1 += interval(j-i) * erg2.tayl[i] * x.tayl[j-i];
	    s2 += interval(j-i) * erg1.tayl[i] * x.tayl[j-i];
	}
	erg1.tayl[j]= s1/interval(j);
	erg2.tayl[j]= s2/interval(j);
    }
    return erg2; 
}

//-----------------------------------------------------------------------

//Tangenshyperbolicus-function
itaylor tanh(const itaylor& x)
{
    int order=get_order(x);
    itaylor f(order);
    itaylor g(order);

    g=sqr(cosh(x)); 
 
    f_g_u(f,g,x,_i_tanh);
  
    return f;
}

//-----------------------------------------------------------------------

// Cotangenshyperbolicus-function
itaylor coth(const itaylor& x)
{
    int order=get_order(x);
    itaylor f(order);
    itaylor g(order);

    g=-sqr(sinh(x));

    if(0 <= g.tayl[0]) 
    {
	std::cerr << "Error in itaylor, coth : wrong argument " << std::endl;
	exit(1);
    };  
 
    f_g_u(f,g,x,_i_coth);
  
    return f;
}

//-----------------------------------------------------------------------

//Arcsinusfunktion
itaylor asin(const itaylor& x)
{
    int order=get_order(x);
    itaylor f(order);
    itaylor g(order);

    g=sqrt1mx2(x);

    if(0 <= g.tayl[0]) 
    {
	std::cerr << "Error in itaylor, asin : wrong argument " << std::endl;
	exit(1);
    };  
 
    f_g_u(f,g,x,_i_asin);
  
    return f;
}

//-----------------------------------------------------------------------

//Arccosinusfunktion
itaylor acos(const itaylor& x)
{
    int order=get_order(x);
    itaylor f(order);
    itaylor g(order);

    g = -sqrt1mx2(x);

    if(0 <= g.tayl[0]) 
    {
	std::cerr << "Error in itaylor, acos : wrong argument " << std::endl;
	exit(1);
    };  
 
    f_g_u(f,g,x,_i_acos);
  
    return f;
}

//-----------------------------------------------------------------------

//Arctan-function
itaylor atan(const itaylor& x)
{
    int order=get_order(x);
    itaylor f(order);
    itaylor g(order);

    g=interval(1.0)+sqr(x);

    f_g_u(f,g,x,_i_atan);
  
    return f;
}

//-----------------------------------------------------------------------

//Arccotan-function
itaylor acot(const itaylor& x)
{
    int order=get_order(x);
    itaylor f(order);
    itaylor g(order);

    g=-(interval(1.0)+sqr(x));

    f_g_u(f,g,x,_i_acot);
  
    return f;
}

//-----------------------------------------------------------------------

//Areasinh-function
itaylor asinh(const itaylor& x)
{
    int order=get_order(x);
    itaylor f(order);
    itaylor g(order);

    g=sqrt1px2(x);

    f_g_u(f,g,x,_i_asinh);
  
    return f;
}

//-----------------------------------------------------------------------

//Areacosh-function
itaylor acosh(const itaylor& x)
{
    int order=get_order(x);
    itaylor f(order);
    itaylor g(order);

    g=sqrtx2m1(x);

    if(0 <= g.tayl[0]) 
    {
	std::cerr << "Error in itaylor, acosh : wrong argument " << std::endl;
	exit(1);
    };  
 
    f_g_u(f,g,x,_i_acosh);
  
    return f;
}

//-----------------------------------------------------------------------

//Areatanh-function
itaylor atanh(const itaylor& x)
{
    int order=get_order(x);
    itaylor f(order);
    itaylor g(order);

    g=interval(1.0)-sqr(x);

    if(0 <= g.tayl[0]) 
    {
	std::cerr << "Error in itaylor, atanh : wrong argument " << std::endl;
	exit(1);
    };  
 
    f_g_u(f,g,x,_i_atanh);
  
    return f;
}

//-----------------------------------------------------------------------

//Areacotanh-function
itaylor acoth(const itaylor& x)
{
    int order=get_order(x);
    itaylor f(order);
    itaylor g(order);

    g=interval(1.0)-sqr(x);

    if(0 <= g.tayl[0]) 
    {
	std::cerr << "Error in itaylor, acoth : wrong argument " << std::endl;
	exit(1);
    };  

    f_g_u(f,g,x,_i_acoth);
  
    return f;
}

//-----------------------------------------------------------------------

//Error function "erf" //added, mg2006-03
itaylor erf(const itaylor& x)
{
    int order(get_order(x));
    itaylor erg(order);
    itaylor g(order);

    g=exp(-sqr(x));
   
    erg.tayl[0] = erf(x.tayl[0]); // element No. 0; function value
    
    for(int k=1; k<=order; k++)
    {
        erg.tayl[k] = 0;
        
	for(int j=0; j<=k-1; j++)
	    erg.tayl[k] += interval(k-j)*g.tayl[j]*x.tayl[k-j]; 
	erg.tayl[k] = 2*erg.tayl[k]/(sqrt(Pi())*interval(k));
    }
    return erg; 
}

//-----------------------------------------------------------------------

//Complementary Error function "erfc" //added, mg2006-03
itaylor erfc(const itaylor& x)
{
    int order(get_order(x));
    itaylor erg(order);

    erg=interval(1)-erf(x); 

    return erg;
}


//-----------------------------------------------------------------------

// sqrt(1+x^2)
itaylor sqrt1px2(const itaylor& x)
{
    int order(get_order(x));
    itaylor erg(order);
    const real c = 500.0;

    if (Inf(x.tayl[0]) > c) erg = x*sqrt(real(1)+real(1)/sqr(x));
    else if (Sup(x.tayl[0]) < -c) erg = -x*sqrt(real(1)+real(1)/sqr(x)); 
    else erg = sqrt(real(1)+sqr(x));  
    return erg;
}



//-----------------------------------------------------------------------

} // end namespace taylor
