/*

 File: cimath.cpp, 2005/10/17

 CoStLy (COmplex interval STandard functions LibrarY), Version 2.0.4

 Copyright (C) Markus Neher, markus.neher@math.uni-karlsruhe.de
               Ingo Eble,    ingoeble@web.de

 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 as published by the Free Software Foundation; either version 2
 of the License, or (at your option) any later version.

 This program 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 General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

//
//  include header files
//
#include "cimath.h" //  declaration of the complex functions
#include <limits>   //  from Standard Template Library
                    //  contains information about the numeric type 'double'

using std::numeric_limits;

using Adapt::Interval;
using Adapt::ZERO_INTERVAL;
using Adapt::ONE_INTERVAL;
using Adapt::PI;
using Adapt::HALFPI;
using Adapt::SQRT_2;
using Adapt::INV_SQRT_2;

#include "error.h"

//-----------------------------------------------------------------------------
//
//  Foreword
//
//------------------------------------------------------------------- 051007 --
//
//  1) CoStLy has been extensively tested and has been found to be reliable and
//  accurate. Of course, even though it is software for validated computations,
//  it is subject to the same potential errors as conventional software. 
//
//  The program is distributed in the hope that it will be useful, but 
//  without any warranty. 
//
//  2) If you find a bug in this program, please send me an email.
//
//  3) The comments in this library are meant to explain what's going on. For
//  additional information, see the available documentation on the CoStLy 
//  website.
//
//  4) If you use CoStLy in your work, please include a reference. Suitable 
//  references are found on the CoStLy website and on my personal homepage.
//
//  Karlsruhe, October 2005
//
//  Markus Neher
//
//-----------------------------------------------------------------------------
//
//  Preliminaries: Auxiliary functions and constants
//
//-----------------------------------------------------------------------------
//
//  Version():   CoStLy version number
//  SQRT_2:      interval constant containing sqrt(2)
//  INV_SQRT_2:  interval constant containing 1/sqrt(2)
//  MAXREAL      largest positive machine number
//  SQRT_MAXREAL sqrt( MAXREAL )
//  MINREAL      smallest positive machine number
//  SQRT_MINREAL sqrt( MINREAL )
//  EXPMAX       ln( MAXREAL )
//  HYPMAX       ln( 2*MAXREAL )
//  HYP_SQRTMAX  ln( 2*sqrt( MAXREAL ) )
//  IEPS:        1.0E-18; 1+IEPS = [1,1+eps], 
//               where eps is the smallest machine number such that 1+eps > 1
//  ONE_PLUS_EPS 1+eps
//  ONE_MINUS_EPS 1-eps/2
//
//  err_msg:     various procedures for error messages
//
//  max(x,y):    maximum of x and y (double x, y)
//  min(x,y):    minimum of x and y (double x, y)
//  real_cos(x): verified cosine function with extended real domain (x real)
//  real_sin(x): verified sine function with extended real domain (x real)
//  real_tan(x): verified tangent function with extended real domain (x real)
//  real_asinh(x): verified patched inverse hyperbolic sine function
//  atan2(x,y):  atan2( x, y ) = atan( y / x )   (x, y real)
//               over/underflow of y/x is removed before calling atan(y/x)
//
//-----------------------------------------------------------------------------

//-- CoStLy version number ------------------------------------------ 051017 --
//
std::string CoStLy::Version()
{
  return "2.0.4";
}
//
//-- end CoStLy version number ------------------------------------------------

//-- various constants ---------------------------------------------- 050930 --
//
//  the hexadecimal representations of these constants have been checked 
//  using the program costly_hexconstantstest.cpp
//
//  all constants apply to the IEEE 754 floating point format
//

inline const double& MAXREAL()
{
  static const double MAXREAL( 1.7976931348623158E+308 );
  return MAXREAL;
}

inline const double& SQRT_MAXREAL()
{
  static const double SQRT_MAXREAL( 1.3407807929942596E+154 );
  return SQRT_MAXREAL;
}

inline const double& MINREAL()
{
  static const double MINREAL( 4.9406564584124654E-324 );
  return MINREAL;
}

inline const double& SQRT_MINREAL()
{
  static const double SQRT_MINREAL( 2.2227587494850772E-162 );
  return SQRT_MINREAL;
}

inline const double& EXPMAX()
{
  static const double EXPMAX( 7.09782712893384E+2 );
  return EXPMAX;
}

inline const double& HYPMAX()
{
  static const double HYPMAX( 7.104758600739439E+2 );
  return HYPMAX;
}

inline const double& HYP_SQRTMAX()
{
  static const double HYP_SQRTMAX( 3.5558450362725194E+2 );
  return HYP_SQRTMAX;
}
//
//  interval constant such that there is noch floating point screen number
//  between 1.0 = inf( 1+IEPs ) and sup( 1+IEPS ) (may depend on hardware) 
//  in the IEEE 754 flop standard, the successor of 1.0 is 1.0+eps,
//  where  eps = 2^(-52) = 2.220446049250313e-16 
//  for simplicity, we use 1.0E-18 (safe underestimation)
//
//  IEPS is used in Atan and Acot to bound small terms. It is assumed that
//
//  1 + JEPS = [1,1+eps], where 1+eps is the smallest machine number > 1
//  and JEPS is an *arbitrary* interval with lower bound >= 0 and 
//  0 < upper bound <= eps. Under this assumption it is for example safe to
//  estimate "1.0E20 + 1" by "1.0E20 + IEPS", even though IEPS is smaller than
//  one. The same applies to 1-IEPS = [1-eps/2,1].
//
//
inline const Interval& IEPS()
{
  static const Interval IEPS( 1.0E-18 );
  return IEPS;
}

inline const double& ONE_PLUS_EPS()
{
  static const double ONE_PLUS_EPS( 1.0000000000000002220446049250313 );
  return ONE_PLUS_EPS;
}

inline const double& ONE_MINUS_EPS()
{
  static const double ONE_MINUS_EPS( 0.9999999999999998889776975374843 );
  return ONE_MINUS_EPS;
}
//
//         
//-- end various constants ----------------------------------------------------

//-- err_msg -------------------------------------------------------- 050930 --
//
inline void err_msg( const std::string& f_string, const CInterval& z )
{
  std::cout<<"Farewell from CoStLy: " + f_string + " domain violation."
	   <<std::endl;
  std::cout<<"Illegal argument z = "<<z<<std::endl<<std::endl;
  throw function_not_defined();
}
//
//-- end err_msg --------------------------------------------------------------

//-- err_msg_Root --------------------------------------------------- 050930 --
//
inline void err_msg_Root( const std::string& f_string, const int& n )
{
  std::cout<<"Farewell from CoStLy:"<<std::endl;
  std::cout<<f_string+" is undefined for negative n = "<<n<<std::endl;
  throw function_not_defined();
}
//
//-- end err_msg_Root ---------------------------------------------------------

//-- err_msg_tan ---------------------------------------------------- 050930 --
//
inline void err_msg_tan( const std::string& f_string,
			 const Interval& x, const Interval& res )
{
  std::cout<<"Farewell from CoStLy:"<<std::endl;
  std::cout<<"Overestimation of real " + f_string;
  std::cout<<" in C-XSC or filib++ library."<<std::endl;
  std::cout<<"x = "<<x<<", " + f_string + "(x) = "<<res<<std::endl;
  throw function_not_defined();
}
//
//-- end err_msg_tan ----------------------------------------------------------

//-- err_msg_pal ---------------------------------------------------- 050930 --
//
inline void err_msg_pal( const  CInterval& z, const Interval& p )
{
  std::cout<<"Farewell from CoStLy: pow_all(z,p) domain violation: ";
  std::cout<<"0^p is undefined for p<=0."<<std::endl<<std::endl;
  std::cout<<"Illegal arguments:"<<std::endl;
  std::cout<<"    z = "<<z<<std::endl;
  std::cout<<"    p =  "<<p<<std::endl<<std::endl;
  throw function_not_defined();
}
//
//-- end err_msg_pal ----------------------------------------------------------

//-- max and min ---------------------------------------------------- 040602 --
//
inline const double& max( const double& x, const double& y ) 
       { return ( x > y ? x : y ); }

inline const double& min( const double& x, const double& y ) 
       { return ( x < y ? x : y ); }
//
//-- end max and min ----------------------------------------------------------
 
//-- patched real functions ----------------------------------------- 051006 --
//
//  real cos, sin, and tan with extended argument reduction
//
//  C-XSC and filib++ both return [-1,1], if cos(x) or sin(x) are called for
//  some x with |x| > q_sint[2] := 7074237750381340 / 2097152 = 3.373253425E+09
//  ( q_sint[2] has been named and defined in the filib library )
//
//  for -q_sint[2] < x < q_sint[2], however, cos and sin are highly accurate
//  thus, we extend the real domain on which nontrivial bounds are computed
//  by some simple argument reduction scheme
//
//  more elaborate methods are well-known; however, their implemetation should
//  be done in the framework of a real functions library, not within CoStLy
//  we regard real_cos, real_sin and real_tan as by-products of CoStLy, not as
//  full members of the library
//
//  the domain of nontrivial bounds is extended to 
//    |x| <= succ_dist_2pi = 2*Pi*2^52 = 2.829695100811376E+16
//
//  this choice is motivated by the fact that for numbers with larger absolute
//  values, the distance between two consecutive IEEE 754 flop numbers is 
//  larger than 2*Pi; beyond this bound, only point arguments have a nontrivial
//  range <> [-1,1]
//
//  due to the simplicity of our argument reduction scheme, the accuracy of 
//  the computed range bounds degrades with increasing |x|
//  for |x| = succ_dist_2pi, accuracy is about half of the mantissa
//
Interval CoStLy::real_cos( const Interval& x ) //-------------------- 051006 --
//
//  real cos with extended argument reduction
//  aim: compute nontrivial range bounds for 
//       |x| <= succ_dist_2pi = 2*pi*2^52 = 2.83E+16,
//  motivation for this choice:  succ(x)-x >= 2*pi for x >= succ_dist_2pi
//
{ 
  if( sup( x ) - inf( x ) > 6.283185307179587 )
    return Interval( -1.0, 1.0 );
  else
    {
      double sup_abs_x = sup( abs( x ) );
      if( sup_abs_x < 3.373259425345106E+09 )
	return cos( x );
      else if( sup_abs_x > 2.829695100811376E+16 )
	return Interval( -1.0, 1.0 );
      else
	//  perform argument reduction
	//  we have: sup( x ) - inf( x ) < 2*Pi and 
	//           abs( x ) in [ q_sint[2], succ_dist_2pi ]
	//  argument reduction to some interval near 0 is not required
	//  it suffices to move the argument into [-q_sint[2],q_sint[2]]
	//  and let C-XSC or filib++ do the rest
	//  
	//  since maxint = 2^31-1 = 2.147483647E+09 in IEEE 754, 
	//  we subtract multiples of 
	//  2*Pi*(2^21+1) = 1.31768009165075913188432959E+07
	//  (smallest number for which redfact doesn't overflow)
	{
	  //  optimal enclosure of 2*Pi*(2^21+1)
	  //  two_pi_2t21p1_msb: 24 most significant bits of 2*Pi*(2^21+1)
	  //  bits 20-24 are zero => length of mantissa is 19 bits
	  //  two_pi_2t21p1_msb: next 52 bits of 2*Pi*(2^21+1)
	  double   two_pi_2t21p1_msb = 13176800;
	  Interval two_pi_2t21p1_lsb 
	    = Interval( 0.9165075913188433, 0.9165075913188434 );
	  double redfact = int( sup( x ) / 1.317680091650759132E+07 );
	  // x - redfact * two_pi_2t21p1_msb is an error-free calculation
	  return cos((x-redfact*two_pi_2t21p1_msb)-redfact*two_pi_2t21p1_lsb);
	}
    }
}

Interval CoStLy::real_sin( const Interval& x ) //-------------------- 051006 --
//
//  real sin with extended argument reduction
//  aim: compute nontrivial range bounds for 
//       |x| <= succ_dist_2pi = 2*pi*2^52 = 2.83E+16,
//  motivation for this choice:  succ(x)-x >= 2*pi for x >= succ_dist_2pi
//
{ 
  if( sup( x ) - inf( x ) > 6.283185307179587 )
    return Interval( -1.0, 1.0 );
  else
    {
      double sup_abs_x = sup( abs( x ) );
      if( sup_abs_x < 3.373259425345106E+09 )
	return sin( x );
      else if( sup_abs_x > 2.829695100811376E+16 )
	return Interval( -1.0, 1.0 );
      else
	//  perform argument reduction
	//  we have: sup( x ) - inf( x ) < 2*Pi and 
	//           abs( x ) in [ q_sint[2], succ_dist_2pi ]
	//  argument reduction to some interval near 0 is not required
	//  it suffices to move the argument into [-q_sint[2],q_sint[2]]
	//  and let C-XSC or filib++ do the rest
	//  
	//  since maxint = 2^31-1 = 2.147483647E+09 in IEEE 754, 
	//  we subtract multiples of 
	//  2*Pi*(2^21+1) = 1.31768009165075913188432959E+07
	//  (smallest number for which redfact doesn't overflow)
	{
	  //  optimal enclosure of 2*Pi*(2^21+1)
	  //  two_pi_2t21p1_msb: 24 most significant bits of 2*Pi*(2^21+1)
	  //  bits 20-24 are zero => length of mantissa is 19 bits
	  //  two_pi_2t21p1_msb: next 52 bits of 2*Pi*(2^21+1)
	  double   two_pi_2t21p1_msb = 13176800;
	  Interval two_pi_2t21p1_lsb 
	    = Interval( 0.9165075913188433, 0.9165075913188434 );
	  double redfact = int( sup( x ) / 1.317680091650759132E+07 );
	  // x - redfact * two_pi_2t21p1_msb is an error-free calculation
	  return sin((x-redfact*two_pi_2t21p1_msb)-redfact*two_pi_2t21p1_lsb);
	}
    }
}

Interval CoStLy::real_tan( const Interval& x ) //-------------------- 051006 --
//
//  real tan with extended argument reduction
//
{ 
  if( sup( x ) - inf( x ) > 3.141592653589794 )
    err_msg_tan( "tan", x, ZERO_INTERVAL() );
  else
    {
      double sup_abs_x = sup( abs( x ) );
      if( sup_abs_x < 3.373259425345106E+09 )
	return tan( x );
      else if( sup_abs_x > 2.829695100811376E+16 )
	err_msg_tan( "tan", x, ZERO_INTERVAL() );
      else
	//  perform argument reduction
	//  we have: sup( x ) - inf( x ) < 2*Pi and 
	//           abs( x ) in [ q_sint[2], succ_dist_2pi ]
	//  argument reduction to some interval near 0 is not required
	//  it suffices to move the argument into [-q_sint[2],q_sint[2]]
	//  and let C-XSC or filib++ do the rest
	//  
	//  since maxint = 2^31-1 = 2.147483647E+09 in IEEE 754, 
	//  we subtract multiples of 
	//  2*Pi*(2^21+1) = 1.31768009165075913188432959E+07
	//  (smallest number for which redfact doesn't overflow)
	{
	  //  optimal enclosure of 2*Pi*(2^21+1)
	  //  two_pi_2t21p1_msb: 24 most significant bits of 2*Pi*(2^21+1)
	  //  bits 20-24 are zero => length of mantissa is 19 bits
	  //  two_pi_2t21p1_msb: next 52 bits of 2*Pi*(2^21+1)
	  double   two_pi_2t21p1_msb = 13176800;
	  Interval two_pi_2t21p1_lsb 
	    = Interval( 0.9165075913188433, 0.9165075913188434 );
	  double redfact = int( sup( x ) / 1.317680091650759132E+07 );
	  // x - redfact * two_pi_2t21p1_msb is an error-free calculation
	  return tan((x-redfact*two_pi_2t21p1_msb)-redfact*two_pi_2t21p1_lsb);
	}
    }
}

Interval CoStLy::real_asinh( const Interval& x ) //------------------ 050921 --
//
//  included here due to underflow error of C-XSC asinh
//  this is meant as a crude intermediate patch
//  as soon as C-XSC asinh works correctly, this procedure will be eliminated
//
{ 
  if( inf( abs ( x ) ) >= 1.0E-150 )
    return asinh( x );
  else
    {
      double 
	ix = inf( x ), sx = sup( x ), resl, resu;
      Interval
	hxl = ix, hxu = sx;

      if( ix < -1.0E-150 )
	resl = inf( asinh( hxl ) );
      else if( ix <= 0.0 )
	resl = ix;
      else
	// 0 < ix < 1.0E-150 )
	resl = inf( hxl * ONE_MINUS_EPS() );

      if( sx > 1.0E-150 )
	resu = sup( asinh( hxu ) );
      else if( sx >= 0.0 )
	resu = sx;
      else
	//  -1.0E-150 < sx < 0 )
	resu = sup( hxu * ONE_MINUS_EPS() );
      
      return interval( resl, resu );
    }
}
//
//-- end patched real functions -----------------------------------------------

//-- atan2 ---------------------------------------------------------- 050806 --
//
//  atan2( x, y ) = atan( y / x )
//
//  overflow of y/x is considered
//  then atan(y/x) is called (real atan of C-XSC or filib++, respectively)
//
//  x > 0 is assumed here, since this procedure is called by both Arg and arg
//
//  for some intervals z with x = Re z < 0, Arg and arg have different 
//  function values; it is simpler (and faster) to handle the case distinctions
//  in the calling functions instead of performing them in this subroutine
//
Interval CoStLy::atan2( const double& x, const double& y ) 
{
  if( x <= 0.0 )
    //  user error: abort execution
    {
      std::cout<<"Farewell from CoStLy: atan2 domain violation."<<std::endl;
      std::cout<<"Illegal argument x = "<<x<<std::endl<<std::endl;
      throw function_not_defined();
    }

  if( x < 1.0 )  //  necessary condition for overflow
    {
      //  check if argument is very close to pi/2
      //  HALFPI() covers a small angle containing pi/2
      //  using 1.0E30 is well on the safe side
      if( y > 0.0 and x * 1.0E30 < y )
	//  Pi / 2
	return HALFPI();
      if( y < 0.0 and x * 1.0E30 < -y )
	//  - Pi / 2
	return -HALFPI();
    }
  //
  //  no overflow
  //  underflow is safe
  //  call real atan of C-XSC or filib++
  //
  Interval 
    hx = x,
    hy = y;

  return atan( hy / hx );
}
//
//-- end atan2 ----------------------------------------------------------------

//-----------------------------------------------------------------------------
//
//  Part I: Single-valued functions
//
//-----------------------------------------------------------------------------
//
//  power operator  pow  is not listed here, because in the CoStLy library,
//  pow  is based on the (multi-valued) logarithm
//
//-----------------------------------------------------------------------------
//
//  Section 1: Absolute value
//
//-----------------------------------------------------------------------------
//
//  the absolute value function is included here, because the absolute value
//  functions in the current versions of C-XSC or filib++ (as of 050930) don't
//  consider over/underflow exceptions
//
//-----------------------------------------------------------------------------
 
//-- abs ------------------------------------------------------------ 050922 --
//
//  absolute value function taking account of over/underflow
//
//  because |x+iy| = | |x|+i|y| | = | |y|+i|x| | = |y+ix|, signs are neglected
//  and absolute values of lower/upper bounds of real and imaginary parts may 
//  be exchanged arbitrarily 
//
//  zero argument is implicitly contained in the case distinction
//  (hence do not replace "<=" by "<")
//

Interval CoStLy::abs( const CInterval& z ) 
{
  Interval 
    rez = abs( z.re() ),
    imz = abs( z.im() );

  double 
    irez = inf( rez ), 
    srez = sup( rez ),
    iimz = inf( imz ),
    simz = sup( imz );

  double 
    resl, resu;

  //  | z | = [ sqrt( irez^2 + iimz^2 ), sqrt( srez^2 + simz^2 ) ]
  
  //  sort by size such that 0 <= irez <= iimz, 0 <= srez <= simz
  double h1;
  if( irez > iimz )
    {
      h1 = irez; irez = iimz; iimz = h1; 
    }
  
  if( srez > simz )
    {
      h1 = srez; srez = simz; simz = h1; 
    }
  
  Interval 
    hxl(irez), hxu(srez), hyl(iimz), hyu(simz);
  
  //
  //  upper bound
  //
  if( simz > 6.70390396497129E+153 )
    //  overflow
    //  6.70390396497129E+153 is slightly smaller than 2^511
    //  srez=simz=2^511 should do; we're cautious
    {
      if( srez <= simz * 1.0E-9 )
	//  srez negligible 
	{
	  if( srez == 0.0 )
	    resu = simz;
	  else
	    resu = simz * ONE_PLUS_EPS();
	}
      else
	resu = sup( simz * sqrt( 1.0 + sqr( hxu/hyu ) ) );
    }
  else
    {
      if( srez < SQRT_MINREAL() )
	//  underflow
	{
	  if( srez * 1.0E+9 <= simz )
	    //  srez negligible 
	    {
	      if( srez == 0.0 )
		resu = simz;
	      else
		resu = simz * ONE_PLUS_EPS();
	    }
	  else
	    resu = sup( simz * sqrt( 1.0 + sqr( hxu/hyu ) ) );
	}
      else
	//  neither overflow nor underflow
	resu = sup( sqrt( sqr( hxu ) + sqr( hyu ) ) );
    }
  //
  //  lower bound
  //
  if( iimz > 6.70390396497129E+153 )
    //  overflow
    //  6.70390396497129E+153 is slightly smaller than 2^511
    //  srez=simz=2^511 should do; we're cautious
    {
      if( irez <= iimz * 1.0E-9 )
	//  irez negligible 
	resl = iimz;
      else
	resl = inf( iimz * sqrt( 1.0 + sqr( hxl/hyl ) ) );
    }
  else
    {
      if( irez < SQRT_MINREAL() )
	//  underflow
	{
	  if( irez * 1.0E+9 <= iimz )
	    //  irez negligible 
	    resl = iimz;
	  else
	    resl = inf( iimz * sqrt( 1.0 + sqr( hxl/hyl ) ) );
	}
      else
	//  neither overflow nor underflow
	resl = inf( sqrt( sqr( hxl ) + sqr( hyl ) ) );
    }
  
  return Interval( resl, resu );
}
//
//-- end abs ------------------------------------------------------------------

//-----------------------------------------------------------------------------
//
//  Section 2: Square function and separable functions
//             sqr, exp, cos, sin, cosh, sinh
//
//-----------------------------------------------------------------------------
//
//  the hyperbolic functions exp, sin, cos, sinh, cosh are separable:
//  their real and imaginary parts are products of univariate real functions
//
//
//  with Re(z)=x, Im(z)=y :
//
//       sqr   :   Re(sqr(z)) = sqr(x) - sqr(y)
//                 Im(sqr(z)) = 2 * x * y
//
//       exp   :   Re(exp(z)) = exp(x) * cos(y)
//                 Im(exp(z)) = exp(x) * sin(y)
//                                             
//       cos   :   Re(cos(z)) = cos(x) * cosh(y)
//                 Im(sin(x)) = -sin(x) * sinh(y)
// 
//       sin   :   Re(sin(z)) = sin(x) * cosh(y)
//                 Im(sin(x)) = cos(x) * sinh(y)
// 
//       cosh  :   Re(cosh(z)) = cosh(x) * cos(y)
//                 Im(cosh(z)) = sinh(x) * sin(y)
//                                                    
//       sinh  :   Re(sinh(z)) = sinh(x) * cos(y)
//                 Im(sinh(z)) = cosh(x) * sin(y)
//
//-----------------------------------------------------------------------------
 
//-- sqr ------------------------------------------------------------ 040602 --
//
CInterval CoStLy::sqr( const CInterval& z ) 
{
  Interval 
    rez = z.re(),
    imz = z.im();

  return CInterval( sqr( rez ) - sqr( imz ), 2.0 * rez * imz );
}
//
//-- end sqr ------------------------------------------------------------------

//-- exp ------------------------------------------------------------ 040625 --
//
CInterval CoStLy::exp( const CInterval& z ) 
{
  Interval 
    rez = exp( z.re() ),
    imz = z.im();

  return CInterval( rez * CoStLy::real_cos( imz ), 
		    rez * CoStLy::real_sin( imz ) );
}
//
//-- end exp ------------------------------------------------------------------

//-- cos ------------------------------------------------------------ 040602 --
//
CInterval CoStLy::cos( const CInterval& z ) 
{
  Interval 
    rez = z.re(),
    imz = z.im();

  return CInterval( CoStLy::real_cos( rez ) * cosh( imz ),
		    -CoStLy::real_sin( rez ) * sinh( imz ) );
}
//
//-- end cos ------------------------------------------------------------------

//-- sin ------------------------------------------------------------ 040602 --
//
CInterval CoStLy::sin( const CInterval& z ) 
{
  Interval 
    rez = z.re(),
    imz = z.im();

  return CInterval( CoStLy::real_sin( rez ) * cosh( imz ), 
		    CoStLy::real_cos( rez ) * sinh( imz ) );
} 
//
//-- end sin ------------------------------------------------------------------

//-- cosh ----------------------------------------------------------- 040602 --
//
CInterval CoStLy::cosh( const CInterval& z ) 
{
  Interval 
    rez = z.re(),
    imz = z.im();

  return CInterval( CoStLy::real_cos( imz ) * cosh( rez ), 
		    CoStLy::real_sin( imz ) * sinh( rez ) );
}
//
//-- end cosh -----------------------------------------------------------------

//-- sinh ----------------------------------------------------------- 040602 --
//
CInterval CoStLy::sinh( const CInterval& z ) 
{
  Interval 
    rez = z.re(),
    imz = z.im();

  return CInterval( CoStLy::real_cos( imz ) * sinh( rez ), 
		    CoStLy::real_sin( imz ) * cosh( rez ) );
}
//
//-- end sinh -----------------------------------------------------------------
 	
//-----------------------------------------------------------------------------
//
//  Section 3: tan, cot, tanh, coth
//
//  the implementation of tanh and coth is based on tan and cot:
//
//  tanh( z ) = transp( i * tan( transp( i * z ) ) 
//  coth( z ) = i * cot( i * z )
//
//-----------------------------------------------------------------------------

//-- tan ------------------------------------------------------------ 051017 --
//
//  complex tangent function
//
//  implementation of this function is involved
//  with respect to over/underflow and cancellation
//
//  almost every intermediate expression requires special treatment
//
inline Interval checked_sin( //-------------------------------------- 051017 --
       const Interval& x, const Interval& y )
//
//  subroutine of tan
//  this function is only called for arguments
//  that don't contain zeros of the sine function
//  if 0 is contained in sin(x), this is due to overestimation
// 
{
  Interval sinx = CoStLy::real_sin( x );
  if( in( 0.0, sinx ) )
    //  use 1 / sin( 2x ) = sqrt( 1+sqr( tanh( 2y ) ) ) on the extremal curve
    //  introduces potential overestimation, but avoids division by zero
    sinx = 1 / sqrt( 1.0 + sqr( tanh( 2 * y ) ) );
  return sinx;
}

void horizontal_check_tan( //---------------------------------------- 050930 --
     const Interval& hy, const Interval& cosh_2y, 
     const double& irez, const double& srez, 
     const Interval& hxl, const Interval& hxu, double& resxl, double& resxu )
//
//  auxiliary procedure for complex tangent function
//  check intersections with extremal curves of tan on a horizontal boundary
//  this procedure is only called if an intersection occurs
//  in this case, sinh( 2 * hy ) <> 0.0 (poles are handled before)
//
//  there may be 1 or 2 intersections
//  intersections may lie so close to a boundary of rez that it is impossible 
//  to decide if there are 1 or 2 intersections
//  in this case, 2 intersections are assumed
//  (valid enclosure, at the expense of a potential slight overestimation)
//
//  "left"  intersection applies to intersection in (pi/4,pi/2), 
//  "right" intersection applies to intersection in (pi/2,3pi/4)
//  cosh_2y = -1 / cosh( 2*hy )
//
{
  bool both = false, left = false, right = false;

  if( srez - irez > inf( PI() ) )
    //  2 intersections
    both = true;
  else 
    {
      //  intersection iff |tan(x)| = |coth(y)|
      //  we test signs of |tan(x)| - |coth(y)| at hxl and hxu
      //  division is required to avoid overflow in horiz_l * horiz_u
      Interval 
	abs_coth_y = abs( coth( hy ) ),
	horiz_l = ONE_INTERVAL() - abs( tan( hxl ) ) / abs_coth_y,
	horiz_u = ONE_INTERVAL() - abs( tan( hxu ) ) / abs_coth_y;

      if( inf( horiz_l * horiz_u ) > 0.0 )
	//  2 intersections
	//  (none, if overestimation occurred in the calling procedure)
	both = true;
      else if( sup( horiz_l * horiz_u ) < 0.0 )
	//  1 intersection (3 intersections are PI() apart)
	//  neither of the intervals horiz_l and horiz_u contains zero
	{
	  if( inf( horiz_l ) > 0.0 )
	    //  "left" intersection
	    left = true;
	  else
	    //  "right" intersection
	    right = true;
	}
      else
	//  one intersection lies next to a boundary point
	//  (or both or none in case of overestimation)
	//  here, the task is to decide if 2 intersections occur
	//  if only one intersection takes place, then which one?
	{
	  Interval 
	    sin_2xl = CoStLy::real_sin( 2.0 * hxl ),
	    sin_2xu = CoStLy::real_sin( 2.0 * hxu );
	  
	  if( in( 0.0, horiz_l ) )
	    //  intersection on the left boundary
	    {
	      if( inf( sin_2xl ) >= 0.0 )
		// "left" intersection
		{
		  left = true;
		  //  "remove" the intersection by changing horiz_l
		  horiz_l = - ONE_INTERVAL();
		}
	      else if( sup( sin_2xl ) <= 0.0 )
		// "right" intersection
		{
		  right = true;
		  //  "remove" the intersection by changing horiz_l
		  horiz_l =  ONE_INTERVAL();
		}
	      else
		//  zero is interior point of sin_2xl
		//  if the real sine function has optimal precision,
		//  this case should not happen
		both = true;
	    }
	  
	  if( in( 0.0, horiz_u ) )
	    //  intersection on the right boundary
	    {
	      if( inf( sin_2xu ) >= 0.0 )
		// "left" intersection
		{
		  left = true;
		  //  "remove" the intersection by changing horiz_u
		  horiz_u = ONE_INTERVAL();
		}
	      else if( sup( sin_2xu ) <= 0.0 )
		// "right" intersection
		{
		  right = true;
		  //  "remove" the intersection by changing horiz_u
		  horiz_u = - ONE_INTERVAL();
		}
	      else
		//  zero is interior point of sin_2xu
		//  if the real sine function has optimal precision,
		//  this case should not happen
		both = true;
	    }
	  //
	  //  finally, check if there is a second intersection
	  //
	  if( inf( horiz_l * horiz_u ) < 0.0 )
	    both = true;
	}
    }    
  //
  //  Calculate extremal values
  //
  Interval abs_y, re_tan;
  abs_y = 2.0 * abs( hy );
  if( sup( abs_y ) < HYPMAX() )
    re_tan = 1.0 / sinh( abs_y );
  else
    //  1 / MAXREAL should do; we're cautious.
    re_tan = Interval( 0.0, 2.0 / MAXREAL() ); 
  
  //  "left" intersection, sin( 2x ) > 0
  if( left or both )
    {
      resxl = min( resxl, inf( re_tan ) );
      resxu = max( resxu, sup( re_tan ) );
    }

  //  "right" intersection, sin( 2x ) < 0
  if( right or both )
    {
      resxl = min( resxl, - sup( re_tan ) );
      resxu = max( resxu, - inf( re_tan ) );
    }
}

CInterval CoStLy::tan( const CInterval& z ) //----------------------- 051017 --
{
  Interval
    rez = z.re(),
    imz = z.im();

  double 
    irez = inf( rez ),
    srez = sup( rez ),
    iimz = inf( imz ),
    simz = sup( imz );

  //
  //  argument reduction for trivial cases
  //
  if( srez - irez >= sup( PI() ) )
    {
      irez = 0.0;
      srez = sup( PI() );
      rez = Interval( irez, srez );
    }

  Interval 
    hxl(irez), hxu(srez), hyl(iimz), hyu(simz);

  double 
    resxl, resxu, resyl, resyu;
  //
  //  1st: check for poles
  //
  Interval cos_rez = CoStLy::real_cos( rez );
  if( ( in( 0.0, imz ) ) and ( in( 0.0, cos_rez ) ) ) 
    {
      err_msg( "tan", z );
      //
      //  for testing tan, use following two lines instead of last line
      //
      //std::cout<<"tan domain violation: Illegal argument z = "<<z<<std::endl;
      //return CInterval( Interval( 0.0 ), Interval( 0.0 ) );
    }
  //
  //  2nd: real part
  //
  //  evaluate tan on vertical boundaries
  //
  Interval 
    cos_2rez = CoStLy::real_cos( 2.0 * rez ),
    abs_imz = abs( imz );
  Interval sinh_imz_2, re_tan_l, re_tan_u;
  if( inf( abs_imz ) > HYP_SQRTMAX() )
    {
      //  1 / MAXREAL should do; we're cautious
      re_tan_l = CoStLy::real_sin( 2.0*hxl ) * Interval( 0.0,2.0 / MAXREAL() );
      re_tan_u = CoStLy::real_sin( 2.0*hxu ) * Interval( 0.0,2.0 / MAXREAL() );
    }
  else
    {
      Interval aux_denom_l, aux_denom_u;
      if( sup( abs_imz ) <= HYP_SQRTMAX() )
	{
	  sinh_imz_2 = sqr( sinh( abs_imz ) );
	  aux_denom_l = 2.0 * ( sqr( CoStLy::real_cos( hxl ) ) + sinh_imz_2 );
	  if( inf( aux_denom_l ) > 0.0 )
	      re_tan_l = CoStLy::real_sin( 2.0 * hxl ) / aux_denom_l;
	  else
	    //  abort execution
	    err_msg_tan( "cos", hxl, CoStLy::real_cos( hxl ) );
	  aux_denom_u = 2.0 * ( sqr( CoStLy::real_cos( hxu ) ) + sinh_imz_2 );
	  if( inf( aux_denom_u ) > 0.0 )
	    re_tan_u = CoStLy::real_sin( 2.0 * hxu ) / aux_denom_u;
	  else
	    //  abort execution
	    err_msg_tan( "cos", hxu, CoStLy::real_cos( hxu ) );
	}
      else 
	// inf( abs_imz ) <= HYP_SQRTMAX() < sup( abs_imz )
	{
	  Interval inf_abs_imz = inf( abs_imz );
	  sinh_imz_2 = sqr( sinh( inf_abs_imz ) );
	  aux_denom_l = 2.0 * ( sqr( CoStLy::real_cos( hxl ) ) + sinh_imz_2 );
	  if( inf( aux_denom_l ) > 0.0 )
	    {
	      re_tan_l = 1.0 / aux_denom_l;
	      re_tan_l = CoStLy::real_sin(2.0*hxl)*Interval(0.0,sup(re_tan_l));
	    }
	  else
	    //  abort execution
	    err_msg_tan( "cos", hxl, CoStLy::real_cos( hxl ) );
	  aux_denom_u = 2.0 * ( sqr( CoStLy::real_cos( hxu ) ) + sinh_imz_2 );
	  if( inf( aux_denom_u ) > 0.0 )
	    {
	      re_tan_u = 1.0 / aux_denom_u;
	      re_tan_u = CoStLy::real_sin(2.0*hxu)*Interval(0.0,sup(re_tan_u));
	    }
	  else
	    //  abort execution
	    err_msg_tan( "cos", hxu, CoStLy::real_cos( hxu ) );
	}
    }
  resxl = min( inf( re_tan_l ), inf( re_tan_u ) );
  resxu = max( sup( re_tan_l ), sup( re_tan_u ) );
  //
  //  look for extremal values on horizontal boundaries
  //  if one of the horizontal boundaries is the x-axis, 
  //  then the complex tangent is the real tangent on this 
  //  boundary, and due to monotonicity, its range 
  //  is already included in the ranges of the vertical boundaries
  //
  if( irez < srez )
    {
      //  lower boundary
      if( iimz != 0.0 )
	{
	  Interval cosh_2yl;
	  
	  if( 2.0 * std::abs( iimz ) < HYPMAX() )
	    cosh_2yl = - 1.0 / cosh( 2.0 * hyl );
	  else
	    cosh_2yl = Interval( -2.0 / MAXREAL(), 0.0 );

	  if( std::abs( iimz ) > 1.0 or sup( cos_2rez ) > - 0.9 )
	    //  z is sufficiently far from a pole of tan
	    {
	      if( !disjoint( cos_2rez, cosh_2yl ) )
		//extremal curve intersects lower boundary
		horizontal_check_tan( hyl, cosh_2yl, irez, srez, hxl, hxu, 
				      resxl, resxu );
	    }
	  else
	    //  z may be close to a pole of tan
	    //  avoid cancellation
	    //  the following intersection is equivalent to the latter
	    {
	      Interval cot_rez = abs( cot( rez ) );
	      Interval tanh_yl = abs( tanh( hyl ) );
	      if( !disjoint( cot_rez, tanh_yl ) )
		//extremal curve intersects lower boundary
		horizontal_check_tan( hyl, cosh_2yl, irez, srez, hxl, hxu,
				      resxl, resxu );
	    }
	}
      //  upper boundary
      if( simz != 0.0 and simz != iimz )
	{
	  Interval cosh_2yu;

	  if( 2.0 * std::abs( simz ) < HYPMAX() )
	    cosh_2yu = - 1.0 / cosh( 2.0 * hyu );
	  else
	    cosh_2yu = Interval( -2.0 / MAXREAL(), 0.0 );

	  if( std::abs( simz ) > 1.0 or sup( cos_2rez ) > - 0.9 )
	    //  z is sufficiently far from a pole of tan
	    {
	      if( !disjoint( cos_2rez, cosh_2yu ) )
		//extremal curve intersects upper boundary
		horizontal_check_tan( hyu, cosh_2yu, irez, srez, hxl, hxu,
				      resxl, resxu );
	    }
	  else
	    //  z may be close to a pole of tan
	    //  avoid cancellation
	    {
	      Interval cot_rez = abs( cot( rez ) );
	      Interval tanh_yu = abs( tanh( hyu ) );
	      if( !disjoint( cot_rez, tanh_yu ) )
		//extremal curve intersects upper boundary
		horizontal_check_tan( hyu, cosh_2yu, irez, srez, hxl, hxu, 
				      resxl, resxu );
	    }
	}
    }
  //
  //  3rd: imaginary part
  //
  //  evaluate tan on horizontal boundaries
  //
  Interval cos_rez_2 = sqr( cos_rez );
  Interval im_tan_l, im_tan_u;

  //  lower boundary
  if( 0.0 < std::abs( iimz ) and std::abs( iimz ) < 1.0E-150 )  
    //  underflow
    { 
      Interval sinh_l = sinh( hyl );
      im_tan_l = cosh( hyl ) / ( cos_rez_2 / sinh_l + sinh_l );
    }
  else if( std::abs( iimz ) < 43.0 )  
    //  regular case; heuristic bound: sinh( 43 ) > 2.0E+18
    im_tan_l = sinh( 2.0*hyl ) / ( 2.0 * ( cos_rez_2 + sqr( sinh( hyl ) ) ) );
  else
    //  overflow
    {
      if( iimz < 0.0 )
	im_tan_l = Interval( -ONE_PLUS_EPS(), -ONE_MINUS_EPS() );
      else 
	im_tan_l = Interval( ONE_MINUS_EPS(), ONE_PLUS_EPS() );
    }

  //  upper boundary
  if( 0.0 < std::abs( simz ) and std::abs( simz ) < 1.0E-150 )  
    //  underflow
    { 
      Interval sinh_u = sinh( hyu );
      im_tan_u = cosh( hyu ) / ( cos_rez_2 / sinh_u + sinh_u );
    }
  else if( std::abs( simz ) < 43.0 )
    //  regular case; heuristic bound: sinh( 43 ) > 2.0E+18
    im_tan_u = sinh( 2.0*hyu ) / ( 2.0 * ( cos_rez_2 + sqr( sinh( hyu ) ) ) );
  else
    //  overflow
    {
      if( simz < 0.0 )
	im_tan_u = Interval( -ONE_PLUS_EPS(), -ONE_MINUS_EPS() );
      else
	im_tan_u = Interval( ONE_MINUS_EPS(), ONE_PLUS_EPS() );
    }
 
  resyl = min( inf( im_tan_l ), inf( im_tan_u ) );
  resyu = max( sup( im_tan_l ), sup( im_tan_u ) );
  //
  //  look for extremal values on vertical boundaries
  //  here, the situation is simpler than for the real part
  //  if 2 intersections with extremal curves appear,
  //  one lies in the lower half plane, the other in the upper half plane
  //
  Interval
    cos_2xl = CoStLy::real_cos( 2.0 * hxl ),
    cos_2xu = CoStLy::real_cos( 2.0 * hxu );
  Interval im_tan;

  if( iimz < 0.0 )
    //  intersection in lower half plane?
    {
      Interval imz_h = Interval( iimz, min( simz, 0.0 ) );
      Interval cosh_2imz;
      
      if( - HYP_SQRTMAX() < iimz )
	cosh_2imz = - 1.0 / cosh( 2.0 * imz_h );
      else if( - HYP_SQRTMAX() < sup( imz_h ) )
 	cosh_2imz = Interval( inf(-1.0/cosh(Interval(2.0*sup(imz_h)))), 0.0 );
      else
	cosh_2imz = Interval( - 2.0 / MAXREAL(), 0.0 );
      
      if( ( !disjoint( cosh_2imz, cos_2xl ) ) )
	//extremal curve intersects left boundary
	//in this case, sin( 2 * xl ) <> 0.0 (no poles here)
	{
	  im_tan = - 1.0 / abs( checked_sin( 2.0 * hxl, imz ) );
	  resyl = min( resyl, inf( im_tan ) );
	  resyu = max( resyu, sup( im_tan ) );
	}
      if( ( !disjoint( cosh_2imz, cos_2xu ) ) )
	//extremal curve intersects right boundary
	//in this case, sin( 2 * xu ) <> 0.0 (no poles here)
	{
	  im_tan = - 1.0 / abs( checked_sin( 2.0 * hxu, imz ) );
	  resyl = min( resyl, inf( im_tan ) );
	  resyu = max( resyu, sup( im_tan ) );
	}
    }
  if( simz > 0.0 )
    //  intersection in upper half plane?
    {
      Interval
	imz_h = Interval( max( iimz, 0.0 ), simz );
      Interval cosh_2imz;

      if( simz < HYP_SQRTMAX() )
	cosh_2imz = - 1.0 / cosh( 2.0 * imz_h );
      else if( inf( imz_h ) < HYP_SQRTMAX() )
	cosh_2imz = Interval( inf(-1.0/cosh(Interval(2.0*inf(imz_h)))), 0.0 );
      else
	cosh_2imz = Interval( - 2.0 / MAXREAL(), 0.0 );
  
      if( ( !disjoint( cosh_2imz, cos_2xl ) ) )
	//extremal curve intersects left boundary
	//in this case, sin( 2 * xl ) <> 0.0 (no poles here)
	{
	  im_tan = + 1.0 / abs( checked_sin( 2.0 * hxl, imz ) );
	  resyl = min( resyl, inf( im_tan ) );
	  resyu = max( resyu, sup( im_tan ) );
	}
      if( ( !disjoint( cosh_2imz, cos_2xu ) ) )
	//extremal curve intersects right boundary
	//in this case, sin( 2 * xu ) <> 0.0 (no poles here)
	{
	  im_tan = + 1.0 / abs( checked_sin( 2.0 * hxu, imz ) );
	  resyl = min( resyl, inf( im_tan ) );
	  resyu = max( resyu, sup( im_tan ) );
	}
    }
  
  return CInterval( Interval( resxl, resxu ), Interval( resyl, resyu ) );

}
//
//-- end tan ------------------------------------------------------------------

//-- cot ------------------------------------------------------------ 051017 --
//
//  complex cotangent function
//
//  implementation of this function is involved
//  with respect to over/underflow and cancellation
//
//  almost every intermediate expression requires special treatment
//
void horizontal_check_cot( //---------------------------------------- 051017 --
     const Interval& hy, const Interval& cosh_2y, 
     const double& irez, const double& srez, 
     const Interval& hxl, const Interval& hxu, double& resxl, double& resxu )
//
//  auxiliary procedure for complex cotangent function
//  check intersections with extremal curves of cot on a horizontal boundary
//  this procedure is only called if an intersection is assumed
//  in this case, sinh( 2 * hy ) <> 0.0 (poles are handled before)
//
//  there may be 1 or 2 intersections
//  intersections may lie so close to a boundary of rez that it is impossible 
//  to decide how many intersections occur (0, 1, or 2)
//  in this case, 2 intersections are assumed
//  (valid enclosure, at the expense of a potential overestimation)
//
//  "left"  intersection applies to intersection in (-pi/4,0)
//  "right" intersection applies to intersection in (0,pi/4) 
//  cosh_2y = -1 / cosh( 2*hy )
//
{
  bool both = false, left = false, right = false;

  if( srez - irez > inf( PI() ) )
    //  2 intersections
    both = true;
  else 
    {
      //  intersection iff |tan(x)| = |tanh(y)|
      //  we test signs of |tan(x)| - |tanh(y)| at hxl and hxu
      Interval 
	abs_tanh_y = abs( tanh( hy ) ),
	horiz_l = abs( tan( hxl ) ) - abs_tanh_y,
	horiz_u = abs( tan( hxu ) ) - abs_tanh_y;

      if( inf( horiz_l * horiz_u ) > 0.0 )
	//  2 intersections
	//  (none, if overestimation occurred in the calling procedure)
	both = true;
      else if( sup( horiz_l * horiz_u ) < 0.0 )
	//  1 intersection (3 intersections are PI() apart)
	//  neither of the intervals horiz_l and horiz_u contains zero
	{
	  if( inf( horiz_l ) > 0.0 )
	    //  "left" intersection
	    left = true;
	  else
	    //  "right" intersection
	    right = true;
	}
      else
	//  one intersection lies next to a boundary point
	//  (or both or none in case of overestimation)
	//  here, the task is to decide if 2 intersections occur
	//  if only one intersection takes place, then which one?
	{
	  Interval 
	    sin_2xl = CoStLy::real_sin( 2.0 * hxl ),
	    sin_2xu = CoStLy::real_sin( 2.0 * hxu );
	  
	  if( in( 0.0, horiz_l ) )
	    //  intersection near the left boundary
	    {
	      if( sup( sin_2xl ) <= 0.0 )
		// "left" intersection
		{
		  left = true;
		  //  "remove" the intersection by changing horiz_l
		  horiz_l = - ONE_INTERVAL();
		}
	      else if( inf( sin_2xl ) >= 0.0 )
		// "right" intersection
		{
		  right = true;
		  //  "remove" the intersection by changing horiz_l
		  horiz_l = ONE_INTERVAL();
		}
	      else
		//  zero is interior point of sin_2xl
		//  if the real sine function has optimal precision,
		//  this case should not happen
		both = true;
	    }
	  
	  if( in( 0.0, horiz_u ) )
	    //  intersection near the right boundary
	    {
	      if( sup( sin_2xu ) <= 0.0 )
		// "left" intersection
		{
		  left = true;
		  //  "remove" the intersection by changing horiz_u
		  horiz_u = ONE_INTERVAL();
		}
	      else if( inf( sin_2xu ) >= 0.0 )
		// "right" intersection
		{
		  right = true;
		  //  "remove" the intersection by changing horiz_u
		  horiz_u = - ONE_INTERVAL();
		}
	      else
		//  zero is interior point of sin_2xu
		//  if the real sine function has optimal precision,
		//  this case should not happen
		both = true;
	    }
	  //
	  //  finally, check if there is a second intersection
	  //
	  if( inf( horiz_l * horiz_u ) < 0.0 )
	    both = true;
	}
    }
  //
  //  Calculate extremal values
  //
  Interval abs_y, re_cot;
  abs_y = 2.0 * abs( hy );
  if( sup( abs_y ) < HYPMAX() )
    re_cot = 1.0 / sinh( abs_y );
  else
    //  1 / MAXREAL should do; we're cautious.
    re_cot = Interval( 0.0, 2.0 / MAXREAL() ); 
  
  //  "left" intersection, sin( 2x ) < 0
  if( left or both )
    {
      resxl = min( resxl, - sup( re_cot ) );
      resxu = max( resxu, - inf( re_cot ) );
    }
  //  "right" intersection, sin( 2x ) > 0
  if( right or both )
    {
      resxl = min( resxl, inf( re_cot ) );
      resxu = max( resxu, sup( re_cot ) );
    }
}

CInterval CoStLy::cot( const CInterval& z ) //----------------------- 051017 --
{
  Interval
    rez = z.re(),
    imz = z.im();

  double 
    irez = inf( rez ),
    srez = sup( rez ),
    iimz = inf( imz ),
    simz = sup( imz );

  //
  //  argument reduction for trivial cases
  //
  if( srez - irez >= sup( PI() ) )
    {
      irez = 0.0;
      srez = sup( PI() );
      rez = Interval( irez, srez );
    }

  Interval 
    hxl(irez), hxu(srez), hyl(iimz), hyu(simz);

  double 
    resxl, resxu, resyl, resyu;
  //
  //  1st: check for poles
  //
  Interval sin_rez = CoStLy::real_sin( rez );
  if( ( in( 0.0, imz ) ) and ( in( 0.0, sin_rez ) ) ) 
    {
      err_msg( "cot", z );
      //
      //  for testing cot, use following two lines instead of last line
      //
      //std::cout<<"cot domain violation: Illegal argument z = "<<z<<std::endl;
      //return CInterval( Interval( 0.0 ), Interval( 0.0 ) );
    }
  //
  //  2nd: real part
  //
  //  evaluate cot on vertical boundaries
  //
  Interval 
    cos_2rez = - CoStLy::real_cos( 2.0 * rez ),
    abs_imz = abs( imz );
  Interval 
    re_cot_l, re_cot_u;

  if( inf( abs_imz ) > HYP_SQRTMAX() )
    {
      //  1 / MAXREAL should do; we're cautious
      re_cot_l = CoStLy::real_sin( 2.0*hxl ) * Interval( 0.0,2.0 / MAXREAL() );
      re_cot_u = CoStLy::real_sin( 2.0*hxu ) * Interval( 0.0,2.0 / MAXREAL() );
    }
  else
    {
      Interval 
	cos_l = CoStLy::real_cos( hxl ), 
	cos_u = CoStLy::real_cos( hxu ),
	sin_l = CoStLy::real_sin( hxl ), 
	sin_u = CoStLy::real_sin( hxu ),
	abs_sin_l = abs( sin_l ), 
	abs_sin_u = abs( sin_u ), 
	sin_2l = CoStLy::real_sin( 2.0 * hxl ), 
	sin_2u = CoStLy::real_sin( 2.0 * hxu ),
	sinh_imz, sinh_imz_inf, sinh_imz_sup, sinh_imz_2;
      //
      //  the following fraction is especially tricky with respect to underflow
      //  both sin^2 and sinh^2 may underflow 
      //  to avoid overestimation, division by interval sinh_imz is replaced by
      //  division by interval endpoints
      //  sign of sin( hx% ) requires another case distinction
      //  this can be tedious for large arguments x, for which real_sin( x ) 
      //  may be an interval containing zero
      //  case distinctions are avoided by taking max/min
      //
      if( sup( abs_imz ) <= HYP_SQRTMAX() )
	{
	  sinh_imz = abs( sinh( abs_imz ) );
	  sinh_imz_inf = inf( sinh_imz );
	  sinh_imz_sup = sup( sinh_imz );
	  sinh_imz_2 = sqr( sinh( abs_imz ) );

	  if( inf( sinh_imz ) > 0.0 )
	    {
	      double aux1, aux2, aux3, aux4;
	      re_cot_l = ( 0.5 * sin_2l / sinh_imz_inf ) / 
		( abs_sin_l * ( abs_sin_l / sinh_imz_inf ) + sinh_imz_inf );
	      aux1 = inf( re_cot_l );
	      aux2 = sup( re_cot_l );
	      re_cot_l = ( 0.5* sin_2l / sinh_imz_sup ) / 
		( abs_sin_l * ( abs_sin_l / sinh_imz_sup ) + sinh_imz_sup );
	      aux3 = inf( re_cot_l );
	      aux4 = sup( re_cot_l );
	      re_cot_l = Interval( min( aux1, aux3 ), max( aux2, aux4 ) );

	      re_cot_u = ( 0.5 * sin_2u / sinh_imz_inf ) /
		( abs_sin_u * ( abs_sin_u / sinh_imz_inf ) + sinh_imz_inf );
	      aux1 = inf( re_cot_u );
	      aux2 = sup( re_cot_u );
	      re_cot_u = ( 0.5 * sin_2u / sinh_imz_sup ) / 
		( abs_sin_u * ( abs_sin_u / sinh_imz_sup ) + sinh_imz_sup );
	      aux3 = inf( re_cot_u );
	      aux4 = sup( re_cot_u );
	      re_cot_u = Interval( min( aux1, aux3 ), max( aux2, aux4 ) );
	    }
	  else
	    {
	      if( inf( abs_sin_l ) > 0.0 )
		{
		  //  potential overflow
		  if( sup( sinh_imz_2 ) < MAXREAL() * inf( abs_sin_l ) )
		      re_cot_l = cos_l / ( sin_l + sinh_imz_2 / sin_l );
		  else
		    //  we have sinh_imz_2 / |sin_l| = (0,infinity)
		    {
		      if( inf( sin_l ) > 0.0 )
			re_cot_l = cos_l * Interval( 0.0, sup( 1.0 / sin_l ) );
		      else
			re_cot_l = cos_l * Interval( inf( 1.0 / sin_l ), 0.0 );
		    }
		}
	      else if( irez == 0.0 )
		//  trivial case
		re_cot_l = 0.0;
	      else
		//  abort execution
		err_msg_tan( "sin", hxl, sin_l );

	      if( inf( abs_sin_u ) > 0.0 )
		{
		  //  potential overflow
		  if( sup( sinh_imz_2 ) < MAXREAL() * inf( abs_sin_u ) )
		    re_cot_u = cos_u / ( sin_u + sinh_imz_2 / sin_u );
		  else
		    //  we have sinh_imz_2 / |sin_u| = (0,infinity)
		    {
		      if( inf( sin_u ) > 0.0 )
			re_cot_u = cos_u * Interval( 0.0, sup( 1.0 / sin_u ) );
		      else
			re_cot_u = cos_u * Interval( inf( 1.0 / sin_u ), 0.0 );
		    }
		}
	      else if( srez == 0.0 )
		//  trivial case
		re_cot_u = 0.0;
	      else
		//  abort execution
		err_msg_tan( "sin", hxu, sin_u );
	    }
	}
      else 
	// inf( abs_imz ) <= HYP_SQRTMAX() < sup( abs_imz )
	{
	  Interval inf_abs_imz = inf( abs_imz );
	  sinh_imz_2 = sqr( sinh( inf_abs_imz ) );
	  if( inf( sinh_imz_2 ) > 0.0 )
	    {
	      re_cot_l = 0.5 / ( sqr( sin_l ) + sinh_imz_2 );
	      re_cot_l = sin_2l * Interval( 0.0, sup( re_cot_l ) );
	      re_cot_u = 0.5 / ( sqr( sin_u ) + sinh_imz_2 );
	      re_cot_u = sin_2u * Interval( 0.0, sup( re_cot_u ) );
	    }
	  else
	    {
	      if( inf( abs_sin_l ) > 0.0 )
		{
		  //  potential overflow
		  if( sup( sinh_imz_2 ) < MAXREAL() * inf( abs_sin_l ) )
		    {
		      re_cot_l = 1.0 / ( sin_l + sinh_imz_2 / sin_l );
		      if( inf( sin_l ) > 0.0 )
			re_cot_l = cos_l * Interval( 0.0, sup( re_cot_l ) );
		      else
			re_cot_l = cos_l * Interval( inf( re_cot_l ), 0.0 );
		    }
		  else
		    //  we have sinh_imz_2 / |sin_l| = (0,infinity)
		    {
		      if( inf( sin_l ) > 0.0 )
			re_cot_l = cos_l * Interval( 0.0, sup( 1.0 / sin_l ) );
		      else
			re_cot_l = cos_l * Interval( inf( 1.0 / sin_l ), 0.0 );
		    }
		}
	      else if( irez == 0.0 )
		//  trivial case
		re_cot_l = 0.0;
	      else
		//  abort execution
		err_msg_tan( "sin", hxl, sin_l );

	      if( inf( abs_sin_u ) > 0.0 )
		{
		  //  potential overflow
		  if( sup( sinh_imz_2 ) < MAXREAL() * inf( abs_sin_u ) )
		    {
		      re_cot_u = 1.0 / ( sin_u + sinh_imz_2 / sin_u );
		      if( inf( sin_u ) > 0.0 )
			re_cot_u = cos_u * Interval( 0.0, sup( re_cot_u ) );
		      else
			re_cot_u = cos_u * Interval( inf( re_cot_u ), 0.0 );
		    }
		  else
		    //  we have sinh_imz_2 / |sin_u| = (0,infinity)
		    {
		      if( inf( sin_u ) > 0.0 )
			re_cot_u = cos_u * Interval( 0.0, sup( 1.0 / sin_u ) );
		      else
			re_cot_u = cos_u * Interval( inf( 1.0 / sin_u ), 0.0 );
		    }
		}
	      else if( srez == 0.0 )
		//  trivial case
		re_cot_u = 0.0;
	      else
		//  abort execution
		err_msg_tan( "sin", hxu, sin_u );
	    }
	}
    }
  resxl = min( inf( re_cot_l ), inf( re_cot_u ) );
  resxu = max( sup( re_cot_l ), sup( re_cot_u ) );
  //
  //  look for extremal values on horizontal boundaries
  //  if one of the horizontal boundaries is the x-axis, 
  //  then the complex cotangent is the real cotangent on this 
  //  boundary, and due to monotonicity, its range 
  //  is already included in the ranges of the vertical boundaries
  //
  if( irez < srez )
    {
      //  lower boundary
      if( iimz != 0.0 )
	{
	  Interval cosh_2yl;
	  if( 2.0 * std::abs( iimz ) < HYPMAX() )
	    cosh_2yl = - 1.0 / cosh( 2.0 * hyl );
	  else
	    cosh_2yl = Interval( -2.0 / MAXREAL(), 0.0 );
	  Interval 
	    tanh_yl = abs( tanh( hyl ) ),
	    cos_rez = CoStLy::real_cos( rez ), 
	    tan_rez;
	  if( disjoint( cos_rez, ZERO_INTERVAL() ) )
	    tan_rez = abs( CoStLy::real_tan( rez ) );
	  else
	    //  we only have to compute the range of tan on intervals
	    //  where intersections with extremal curves of cot occur
	    //  on such an interval, it holds that |cos(x)| >= sqrt(2)/2
	    tan_rez = abs( CoStLy::real_sin( rez ) ) / Interval( 0.7, 1.0 );
	  if( !disjoint( tan_rez, tanh_yl ) )
	    //extremal curve intersects lower boundary
	    horizontal_check_cot( hyl, cosh_2yl, irez, srez, hxl, hxu,
				  resxl, resxu );
	}
      //  upper boundary
      if( simz != 0.0 and simz != iimz )
	{
	  Interval cosh_2yu;
	  if( 2.0 * std::abs( simz ) < HYPMAX() )
	    cosh_2yu = - 1.0 / cosh( 2.0 * hyu );
	  else
	    cosh_2yu = Interval( -2.0 / MAXREAL(), 0.0 );
	  Interval 
	    tanh_yu = abs( tanh( hyu ) ),
	    cos_rez = CoStLy::real_cos( rez ), 
	    tan_rez;
	  if( disjoint( cos_rez, ZERO_INTERVAL() ) )
	    tan_rez = abs( CoStLy::real_tan( rez ) );
	  else
	    //  we only have to compute the range of tan on intervals
	    //  where intersections with extremal curves of cot occur
	    //  on such an interval, it holds that |cos(x)| >= sqrt(2)/2
	    tan_rez = abs( CoStLy::real_sin( rez ) ) / Interval( 0.7, 1.0 );
	  if( !disjoint( tan_rez, tanh_yu ) )
	    //extremal curve intersects upper boundary
	    horizontal_check_cot( hyu, cosh_2yu, irez, srez, hxl, hxu, 
				  resxl, resxu );
	}
    }
  //
  //  3rd: imaginary part
  //
  //  evaluate cot on horizontal boundaries
  //
  Interval sin_rez_2 = sqr( sin_rez );
  Interval im_cot_l, im_cot_u;

  //  lower boundary
  if( iimz == 0.0 )
    im_cot_l = 0.0;
  else if( std::abs( iimz ) < 1.0E-150 )  
    //  underflow
    { 
      Interval 
	sinh_l = sinh( hyl ),
	abs_sin_rez = abs( sin_rez );
      im_cot_l = cosh( hyl ) /
	         ( abs_sin_rez * ( abs_sin_rez / sinh_l ) + sinh_l );
    }
  else if( std::abs( iimz ) < 43.0 )  
    //  regular case; heuristic bound: sinh( 43 ) > 2.0E+18
    im_cot_l = sinh( 2.0*hyl ) / ( 2.0 * ( sin_rez_2 + sqr( sinh( hyl ) ) ) );
  else
    //  overflow
    {
      if( iimz < 0.0 )
	im_cot_l = Interval( -ONE_PLUS_EPS(), -ONE_MINUS_EPS() );
      else 
	im_cot_l = Interval( ONE_MINUS_EPS(), ONE_PLUS_EPS() );
    }
  //  upper boundary
  if( simz == 0.0 )
    im_cot_u = 0.0;
  else if( std::abs( simz ) < 1.0E-150 )  
    //  underflow
    { 
      Interval sinh_u = sinh( hyu );
      Interval abs_sin_rez = abs( sin_rez );
      im_cot_u = cosh( hyu ) / 
	         ( abs_sin_rez * ( abs_sin_rez / sinh_u ) + sinh_u );
    }
  else if( std::abs( simz ) < 43.0 )
    //  regular case; heuristic bound: sinh( 43 ) > 2.0E+18
    im_cot_u = sinh( 2.0*hyu ) / ( 2.0 * ( sin_rez_2 + sqr( sinh( hyu ) ) ) );
  else
    //  overflow
    {
      if( simz < 0.0 )
	im_cot_u = Interval( -ONE_PLUS_EPS(), -ONE_MINUS_EPS() );
      else
	im_cot_u = Interval( ONE_MINUS_EPS(), ONE_PLUS_EPS() );
    }
  resyl = min( inf( im_cot_l ), inf( im_cot_u ) );
  resyu = max( sup( im_cot_l ), sup( im_cot_u ) );
  //
  //  look for extremal values on vertical boundaries
  //  here, the situation is simpler than for the real part
  //  if 2 intersections with extremal curves appear,
  //  one lies in the lower half plane, the other in the upper half plane
  //
  Interval im_cot, imz_h, tanh_imz, tan_xl, tan_xu;

  //  tangent of C-XSC or filib++ is not available for arguments with
  //  large absolute values
  //  in this case, we assume intersection with extremal curve by default
  //  intruduces potential overestimation, but avoids abortion
  if( std::abs( irez ) > 2.829695100811376E+16 )
    tan_xl = Interval( -MAXREAL(), MAXREAL() );
  else
    tan_xl = abs( CoStLy::real_tan( hxl ) );
  if( std::abs( srez ) > 2.829695100811376E+16 )
    tan_xu = Interval( -MAXREAL(), MAXREAL() );
  else
    tan_xu = abs( CoStLy::real_tan( hxu ) );

  if( iimz < 0.0 )
    //  intersection in lower half plane?
    {
      imz_h = Interval( max( -simz, 0.0 ), -iimz );
      tanh_imz = tanh( imz_h );
      //intersection with left boundary
      if( !disjoint( tan_xl, tanh_imz ) )
	//extremal curve intersects left boundary
	//in this case, sin( 2 * xl ) <> 0.0 (no poles here)
	{
	  im_cot = - 1.0 / abs( checked_sin( 2.0 * hxl, imz ) );
	  resyl = min( resyl, inf( im_cot ) );
	  resyu = max( resyu, sup( im_cot ) );
	}
      //intersection with right boundary
      if( !disjoint( tan_xu, tanh_imz ) )
	//extremal curve intersects left boundary
	//in this case, sin( 2 * xu ) <> 0.0 (no poles here)
	{
	  im_cot = - 1.0 / abs( checked_sin( 2.0 * hxu, imz  ) );
	  resyl = min( resyl, inf( im_cot ) );
	  resyu = max( resyu, sup( im_cot ) );
	}
    }
  if( simz > 0.0 )
    //  intersection in upper half plane?
    {
      imz_h = Interval( max( iimz, 0.0 ), simz );
      tanh_imz = tanh( imz_h );
      //intersection with left boundary
      if( !disjoint( tan_xl, tanh_imz ) )
	//extremal curve intersects left boundary
	//in this case, sin( 2 * xl ) <> 0.0 (no poles here)
	{
	  im_cot = + 1.0 / abs( checked_sin( 2.0 * hxl, imz ) );
	  resyl = min( resyl, inf( im_cot ) );
	  resyu = max( resyu, sup( im_cot ) );
	}
      //intersection with right boundary
      if( !disjoint( tan_xu, tanh_imz ) )
	//extremal curve intersects left boundary
	//in this case, sin( 2 * xu ) <> 0.0 (no poles here)
	{
	  im_cot = + 1.0 / abs( checked_sin( 2.0 * hxu, imz ) );
	  resyl = min( resyl, inf( im_cot ) );
	  resyu = max( resyu, sup( im_cot ) );
	}
    }
  
  return CInterval( Interval( resxl, resxu ), -Interval( resyl, resyu ) );

}
//
//-- end cot ------------------------------------------------------------------

//-- tct ------------------------------------------------------------ 050920 --
//
//  tan( z )  = cot( pi/2 - z )
//
//  alternative implemention for testing 
//
CInterval CoStLy::tct( const CInterval& z )
{
  return CoStLy::cot( HALFPI() - z );
}
//
//-- end tct ------------------------------------------------------------------

//-- ctn ------------------------------------------------------------ 050920 --
//
//  cot( z )  = tan( pi/2 - z )
//
//  alternative implemention for testing 
//
CInterval CoStLy::ctn( const CInterval& z )
{
  return CoStLy::tan( HALFPI() - z );
}
//
//-- end ctn ------------------------------------------------------------------

//-- tanh ----------------------------------------------------------- 040904 --
//
//  tanh( z ) = transp( i * tan( transp( i * z ) )
//
CInterval CoStLy::tanh( const CInterval& z ) 
{
  CInterval res = CoStLy::tan( CInterval( z.im(), z.re() ) ); 
  return CInterval( res.im(), res.re() );
}
//
//-- end tanh -----------------------------------------------------------------

//-- coth ----------------------------------------------------------- 050920 --
//
//  coth( z ) = i * cot( i * z )
//
CInterval CoStLy::coth( const CInterval& z ) 
{
  CInterval res = CoStLy::cot( CInterval( - z.im(), z.re() ) );  
  return( CInterval( - res.im(), res.re() ) );
}
//
//-- end coth -----------------------------------------------------------------

//-----------------------------------------------------------------------------
//
//  Part II: Multi-valued functions
//
//-----------------------------------------------------------------------------
//
//  for these functions, two different procedures are implemented
//
//  first, there is a procedure for computing the principal value 
//  of the respective function; the principal value is usually
//  analytic in a subset of the complex plane and undefined otherwise 
//
//  second, there are procedures for computing interval supersets 
//  of all function values of the respective function, where feasible
//
//-----------------------------------------------------------------------------
//
//  Section 1: Argument functions
//
//-----------------------------------------------------------------------------

//-- Arg: analytic argument function -------------------------------- 050806 --
//
//  (i)   Arg(Z) subset (-pi,pi).
//  (ii)  Arg([0,0]) = 0.
//  (iii) Arg(Z) is undefined if Z contains negative real numbers.
//  (iv)  Otherwise, Arg(Z) is the interval hull of { Arg(z) | z in Z, z<>0 }.
//
//  atan2( x, y ) = atan( y / x )
//  atan is the inverse function of tan(t), t in (-pi/2,pi/2).
//
Interval CoStLy::Arg( const CInterval& z ) 
{
  double 
    srez = sup( z.re() ),
    irez = inf( z.re() ), 
    simz = sup( z.im() ),
    iimz = inf( z.im() );

  double resl, resu;

  if( iimz > 0.0 )
    //  case I: Im(z) > 0
    {      
      resl = ( srez > 0.0 ? inf( CoStLy::atan2( srez, iimz ) ) : 
	       ( srez < 0.0 ? inf( CoStLy::atan2( -srez, -simz ) + PI() ) : 
		 inf( HALFPI() ) ) ); 
      resu = ( irez > 0.0 ? sup( CoStLy::atan2( irez, simz ) ) : 
	       ( irez < 0.0 ? sup( CoStLy::atan2( -irez, -iimz ) + PI() ) : 
		 sup( HALFPI() ) ) ); 
      return Interval( resl, resu );
    }
  else
    {
      if( simz < 0.0 )
	//  case II: Im(z) < 0
	{      
	  resl = ( irez < 0.0 ? inf( CoStLy::atan2( -irez, -simz ) - PI() ) :
		   ( irez > 0.0 ? inf( CoStLy::atan2( irez, iimz ) ) :
		     -sup( HALFPI() ) ) ); 
	  resu = ( srez < 0.0 ? sup( CoStLy::atan2( -srez, -iimz ) - PI() ) :
		   ( srez > 0.0 ? sup( CoStLy::atan2( srez, simz ) ) :
		     -inf( HALFPI() ) ) );  
	  return Interval( resl, resu );
	}
      else
	// 0 in Im(z)
	{
	  if( irez > 0.0 )
	    //  case III: Re(z) > 0 
	    //  all real values contained in z are positive
	    {
	      resl = ( iimz < 0.0 ? inf( CoStLy::atan2( irez, iimz ) ) : 0.0 );
	      return Interval( resl, sup( CoStLy::atan2( irez, simz ) ) );
	    }
	  else
	    //  z contains nonpositive real numbers 
	    {
	      if( irez < 0.0 )
		//  case IV: z contains negative real numbers
		err_msg( "Arg", z );
	      else
		//  case V: 0 in z, but z doesn't contain negative real numbers
		{
		  if( srez > 0.0 )
		    //  diam( Re(z) > 0.0 )
		    { 
		      resl = ( iimz < 0.0 ? - sup( HALFPI() ) : 0.0 );  
		      resu = ( simz > 0.0 ? sup( HALFPI() ) : 0.0 );
		      return Interval( resl, resu );
		    }
		  else
		    //  Re(z) == 0.0
		    {
		      if( iimz == 0.0 and simz == 0.0 )
			//  Z == 0
			return ZERO_INTERVAL();
		      else
			{
			  resl = ( iimz < 0.0 ? - sup( HALFPI() ) : 
				   inf( HALFPI() ) );
			  resu = ( simz > 0.0 ? sup( HALFPI() ) : 
				   -inf( HALFPI() ) );
			  return Interval( resl, resu );
			}
		    }
		}
	    }
	}
    }
}
//
//-- end Arg ------------------------------------------------------------------

//-- arg: non-analytic argument function ---------------------------- 050806 --
//
//  (i)   arg(Z) is defined for all Z in IC.
//  (ii)  arg(Z) subset [-pi,3*pi/2].
//  (iii) arg(Z) == Arg(Z) if Arg(Z) is well-defined.
//
//  atan2( x, y ) = atan( y / x )
//  atan is the inverse function of tan(t), t in (-pi/2,pi/2).
//
Interval CoStLy::arg( const CInterval& z ) 
{
  double 
    srez = sup( z.re() ),
    irez = inf( z.re() ), 
    simz = sup( z.im() ),
    iimz = inf( z.im() );

  double resl, resu;

  if( irez < 0.0 and iimz <= 0.0 and simz >= 0.0  )
    //  z contains negative real values
    {
      if( srez > 0.0 )
	//  0 in z and 0 interior point of Re(z)
	{
	  resl = ( iimz < 0.0 ? - sup( PI() ) : 0.0 );
	  resu = ( ( iimz < 0.0 and simz == 0.0 ) ? 0.0 : sup( PI() ) );
	  return Interval( resl, resu );
	}
      else
	{
	  if( iimz == simz )
	    //  z is real interval containing no positive values
	    return PI();
	  else
	    // sup( Re(z) ) <= 0, diam( Im(z) ) > 0
	    {
	      if( srez == 0.0 )
		{
		  resl = ( simz > 0.0 ? inf( HALFPI() ) : - sup( PI() ) );
		  resu = ( iimz < 0.0 ? ( simz > 0.0 ? sup( 3 * HALFPI() ) : - inf( HALFPI() ) ) : sup( PI() ) );
		  return Interval( resl, resu );
		}
	      else
		//   sup( Re(z) ) < 0, diam( Im(z) ) > 0
		{
		  resl = ( simz > 0.0 ? 
			   inf( CoStLy::atan2( -srez, -simz ) + PI() ) :
			   -sup( PI() ) );
		  resu = ( iimz < 0.0 ? ( simz > 0.0 ? sup( CoStLy::atan2( -srez, -iimz ) + PI() ) : sup( CoStLy::atan2( -srez, -iimz ) - PI() ) ) : sup( PI() ) );
		  return Interval( resl, resu );
		}
	    }
	}
    } 
  else
    //  Arg(z) is well-defined
    return  CoStLy::Arg( z );
}
//
//-- end arg ------------------------------------------------------------------

//-- arg_inclmon: non-analytic inclusion-monotone argument function - 050814 --
//  
//  (i)  arg_inclmon(Z) is defined for all Z in IC.
//  (ii) arg_inclmon(Z) = [-pi,pi] if Arg(Z) is not defined.
//
Interval CoStLy::arg_inclmon( const CInterval& z ) 
{
  if( inf( z.re() ) < 0.0 and in( 0.0, z.im() ) )
    return  Interval( -sup( PI() ), sup( PI() ) );
  else 
    return CoStLy::Arg( z );
}
//
//-- end arg_inclmon ----------------------------------------------------------

//-----------------------------------------------------------------------------
//
//  Section 2: Logarithms
//
//-----------------------------------------------------------------------------
//
//-- re_ln: real part of natural logarithm -------------------------- 050921 --
//
//  near the unit circle, ln( |z| ) suffers from cancellation
//  hence we insert the code of the absolute value function here 
//  and compute the logarithm appropriately
//
//  near |z| = 1,  ln( sqrt(1+s) ) = atanh( s/(2+s) )  is used
//
Interval CoStLy::re_ln( const CInterval& z ) 
{
  //  because |x+iy| = | |x|+i|y| | = | |y|+i|x| | = |y+ix|, 
  //  signs are neglected and absolute values of lower/upper bounds
  //  of real and imaginary parts may be exchanged arbitrarily 
  Interval 
    rez = abs( z.re() ),
    imz = abs( z.im() );
  
  double 
    irez = inf( rez ), 
    srez = sup( rez ),
    iimz = inf( imz ),
    simz = sup( imz );
  
  double 
    resl, resu;
  
  //  sort by size such that 0 <= irez <= iimz, 0 <= srez <= simz
  double h1;
  if( irez > iimz )
    {
      h1 = irez; irez = iimz; iimz = h1; 
    }
  
  if( srez > simz )
    {
      h1 = srez; srez = simz; simz = h1; 
	}
  
  //  now | z | = [ sqrt( irez^2 + iimz^2 ), sqrt( srez^2 + simz^2 ) ]
  
  Interval 
    hxl(irez), hxu(srez), hyl(iimz), hyu(simz);
  
  //
  //  upper bound
  //
  if( simz > 0.5 * SQRT_MAXREAL() )
    //  overflow
    {
      if( srez <= simz * 1.0E-9 )
	//  srez negligible 
	{
	  if( srez == 0.0 )
	    resu = sup( ln( hyu ) );
	  else
	    resu = sup( ln( Interval( simz * ONE_PLUS_EPS() ) ) );
	}
      else
	resu = sup( ln( simz * sqrt( 1.0 + sqr( hxu/hyu ) ) ) );
    }
  else if( srez < SQRT_MINREAL() )
    //  underflow
    {
      if( srez * 1.0E+9 <= simz )
	//  srez negligible 
	{
	  if( srez == 0.0 )
	    resu = sup( ln( hyu ) );
	  else if( simz == 1.0 )
	    resu = MINREAL();
	  else 
	    // no cancellation
	    resu = sup( ln( Interval( simz * ONE_PLUS_EPS() ) ) );
	}
      else
	resu = sup( ln( simz * sqrt( 1.0 + sqr( hxu/hyu ) ) ) );
    }
  else
    //  neither overflow nor underflow
    //  absolute value is fine, 
    //  but ln has cancellation problem for |z| about 1
    {
      Interval abs_z = sqr( hxu ) + sqr( hyu );
      if( inf( abs_z ) < 0.81 or inf( abs_z ) > 1.21 )
	//  everything is fine
	resu = 0.5 * sup( ln( abs_z ) );
      else
	//  |z| about 1: cancellation in ln
	//  srez^2 + simz^2 = 1 + srez^2 + ( simz-1 ) * ( simz+1 )
	//  simz - 1.0 is an error-free floperation
	{
	  Interval s = sqr( hxu ) + ( simz-1.0 ) * ( hyu+1.0 );
	  resu = sup( atanh( s / ( 2.0 + s ) ) );
	}
    }
  //
  //  lower bound
  //
  if( iimz > 0.5 * SQRT_MAXREAL() )
    //  overflow
    {
      if( irez <= iimz * 1.0E-9 )
	//  irez negligible 
	resl = inf( ln( hyl ) );
      else
	resl = inf( ln( iimz * sqrt( 1.0 + sqr( hxl/hyl ) ) ) );
    }
  else if( irez < SQRT_MINREAL() )
    //  underflow
    {
      if( irez * 1.0E+9 <= iimz )
	//  irez negligible 
	resl = inf( ln( hyl ) );
      else
	resl = inf( ln( iimz * sqrt( 1.0 + sqr( hxl/hyl ) ) ) );
    }
  else
    //  neither overflow nor underflow
    //  absolute value is fine, 
    //  but ln has cancellation problem for |z| about 1
    {
      Interval abs_z = sqr( hxl ) + sqr( hyl );
      if( inf( abs_z ) < 0.81 or inf( abs_z ) > 1.21 )
	//  everything is fine
	resl = 0.5 * inf( ln( abs_z ) );
      else
	//  |z| about 1: cancellation in ln
	//  iimz - 1.0 is an error-free floperation
	{
	  Interval s = sqr( hxl ) + ( iimz-1.0 ) * ( hyl+1.0 );
	  resl = inf( atanh( s / ( 2.0 + s ) ) );
	}
    }
  
  return Interval( resl, resu );

}
//
//-- end re_ln ----------------------------------------------------------------

//-- Ln: analytic natural logarithm --------------------------------- 050824 --
//
CInterval CoStLy::Ln( const CInterval& z ) 
{
  if( inf( z.re() ) <= 0.0 and in( 0.0, z.im() ) )
    //  z contains 0 or negative real values
    err_msg( "Ln", z );
  else
    return CInterval( CoStLy::re_ln( z ), CoStLy::Arg( z ) );
}
//
//-- end Ln -------------------------------------------------------------------

//-- ln: non-analytic natural logarithm ----------------------------- 050824 --
//
//  ln(z) is undefined if z contains zero.
//
CInterval CoStLy::ln( const CInterval& z ) 
{
  if( in( 0.0, z.re() ) and in( 0.0, z.im() ) )
    //  z contains 0
    err_msg( "ln", z );
  else
    return CInterval( CoStLy::re_ln( z ), CoStLy::arg( z ) );
}
//
//-- end ln -------------------------------------------------------------------

//-----------------------------------------------------------------------------
//
//  Section 3: Root functions
//
//-----------------------------------------------------------------------------

//-- Sqrt: analytic square root ------------------------------------- 050915 --
//
inline Interval re_Sqrt_point( //------------------------------------ 050915 --
       const double& irez, const double& iimz, 
       const Interval& rez, const Interval& imz )
//
//  real part of analytic square root of A POINT INTERVAL ONLY
//  do not use this as a general function
//  - it's only a subroutine of Sqrt and of sqrt_all
//  the calculation is void if (rez+I*imz) is not a complex number
//
{ 
  if( iimz == 0.0 )
    {
      if( irez >= 0.0 )
	return sqrt( rez );
      else
	return ZERO_INTERVAL();
    }
  else
    {
      Interval abs_z = CoStLy::abs( CInterval(rez,imz) );
      if( irez >= 0.0 )
	return INV_SQRT_2() * sqrt( abs_z + rez );
      else
	//  rez < 0  (rez is a point interval)
	{ 
	  if( iimz > 0.0 )
	    return (  iimz * INV_SQRT_2() ) / sqrt( abs_z - rez );
	  else
	    return ( -iimz * INV_SQRT_2() ) / sqrt( abs_z - rez );
	}
    }
}

inline Interval im_Sqrt_point( //------------------------------------ 050915 --
       const double& irez, const double& iimz, 
       const Interval& rez, const Interval& imz )
//
//  imaginary part of analytic square root of A POINT INTERVAL ONLY
//  do not use this as a general function
//  - it's only a subroutine of Sqrt and of sqrt_all
//  the calculation is void if (rez+I*imz) is not a complex number 
//
{  
 
  if( iimz == 0.0 )
    {
      if( irez >= 0.0 )
	return ZERO_INTERVAL();
      else
	return sqrt( -rez );
    }
   else
    {
      Interval abs_z = CoStLy::abs( CInterval(rez,imz) );
      if( irez >= 0.0 )
	return ( iimz * INV_SQRT_2() ) / sqrt( abs_z + rez );
      else
	{ 
	  if( iimz > 0.0 )
	    return INV_SQRT_2() * sqrt( abs_z - rez );
	  else
	    //  rez < 0  (rez is a point interval)
	    return - INV_SQRT_2() * sqrt( abs_z - rez );
	}
    }
}

CInterval CoStLy::Sqrt( const CInterval& z ) //---------------------- 050915 --
//
//  analytic square root function
//  Sqrt(z) is undefined if z contains negative real numbers
//
{
  double 
    irez = inf( z.re() ),
    srez = sup( z.re() ),
    iimz = inf( z.im() ),
    simz = sup( z.im() );
  Interval 
    hxl( irez ), hxu( srez ), hyl( iimz ), hyu( simz );
  double 
    resxl,  resxu, resyl, resyu;
  
  if( irez < 0.0 and iimz <= 0.0 and simz >= 0.0 )
    //  z contains negative real values
    err_msg( "Sqrt", z );
  else
    {  
      if( simz < 0.0 )
	{
	  //  z in lower half plane
	  //  min( Re ( Sqrt( z ) ) ) at upper left  corner
	  //  max( Re ( Sqrt( z ) ) ) at lower right corner
	  resxl = inf( re_Sqrt_point( irez, simz, hxl, hyu ) );
	  resxu = sup( re_Sqrt_point( srez, iimz, hxu, hyl ) );
	  //  min( Im ( Sqrt( z ) ) ) at lower left  corner
	  //  max( Im ( Sqrt( z ) ) ) at upper right corner
	  resyl = inf( im_Sqrt_point( irez, iimz, hxl, hyl ) );
	  resyu = sup( im_Sqrt_point( srez, simz, hxu, hyu ) );
	}
      else if( iimz > 0.0 )
	{
	  //  z in upper half plane
	  //  min( Re ( Sqrt( z ) ) ) at lower left  corner
	  //  max( Re ( Sqrt( z ) ) ) at upper right corner
	  resxl = inf( re_Sqrt_point( irez, iimz, hxl, hyl ) );
	  resxu = sup( re_Sqrt_point( srez, simz, hxu, hyu ) );
	  //  min( Im ( Sqrt( z ) ) ) at lower right corner
	  //  max( Im ( Sqrt( z ) ) ) at upper left  corner
	  resyl = inf( im_Sqrt_point( srez, iimz, hxu, hyl ) );
	  resyu = sup( im_Sqrt_point( irez, simz, hxl, hyu ) );
	}
      else
	{
	  //  z intersects positive real axis
	  //  min( Re ( Sqrt( z ) ) ) on real line
	  //  max( Re ( Sqrt( z ) ) ) at lower or upper right corner
	  resxl = inf( sqrt( hxl ) );
	  resxu = ( -iimz > simz ? 
		    sup( re_Sqrt_point( srez, iimz, hxu, hyl ) ) :
		    sup( re_Sqrt_point( srez, simz, hxu, hyu ) ) ); 
	  //  min( Im ( Sqrt( z ) ) ) at lower left corner
	  //  max( Im ( Sqrt( z ) ) ) at upper left corner
	  resyl = inf( im_Sqrt_point( irez, iimz, hxl, hyl ) );
	  resyu = sup( im_Sqrt_point( irez, simz, hxl, hyu ) );
	}
      return CInterval( Interval( resxl, resxu ), Interval( resyl, resyu ) );
    }
}
//
//-- end Sqrt -----------------------------------------------------------------

//-- sqrt_all ------------------------------------------------------- 050915 --
//
//  sqrt_all(z) computes a list of 2 intervals containing all square roots of z
//
std::list<CInterval> CoStLy::sqrt_all( const CInterval& z ) 
{
  double 
    irez = inf( z.re() ),
    srez = sup( z.re() ),
    iimz = inf( z.im() ),
    simz = sup( z.im() );
  Interval 
    hxl( irez ), hxu( srez ), hyl( iimz ), hyu( simz );
  double 
    resxl,  resxu, resyl, resyu;
  CInterval w;
  
  if( irez < 0.0 and iimz <= 0.0 and simz >= 0.0 ) 
    //  z contains negative real values
    {
      if( iimz == 0.0 )
	//  z in upper half plane
	//  principal values can be used
	{
	  //  min( Re ( Sqrt( z ) ) ) at lower left  corner
	  //  max( Re ( Sqrt( z ) ) ) at upper right corner
	  resxl = inf( re_Sqrt_point( irez, iimz, hxl, hyl ) );
	  resxu = sup( re_Sqrt_point( srez, simz, hxu, hyu ) );
	  //  min( Im ( Sqrt( z ) ) ) at lower right corner
	  //  max( Im ( Sqrt( z ) ) ) at upper left  corner
	  resyl = inf( im_Sqrt_point( srez, iimz, hxu, hyl ) );
	  resyu = sup( im_Sqrt_point( irez, simz, hxl, hyu ) );
	}
      else
	{
	  if( simz == 0.0 )
	    //  z in lower half plane
	    //  principal values can be used at lower corners
	    {
	      //  z in lower half plane
	      //  min( Re ( Sqrt( z ) ) ) at upper left  corner
	      //  max( Re ( Sqrt( z ) ) ) at lower right corner
	      resxl = 0.0;                              ;
	      resxu = sup( re_Sqrt_point( srez, iimz, hxu, hyl ) );
	      //  min( Im ( Sqrt( z ) ) ) at lower left  corner
	      //  max( Im ( Sqrt( z ) ) ) at upper right corner
	      resyl = inf( im_Sqrt_point( irez, iimz, hxl, hyl ) );
	      resyu = ( srez > 0.0 ? 0.0 : -inf( sqrt( -hxu ) ) );
	    }
	  else
	    //  0 is interior point of Im( z )
	    {
	      if( srez <= 0.0 )
		{
		  //  0 is no interior point of Re (z )
		  //  in quadrant III,    Re( z ) = im_Sqrt_point(|x|,y), 
		  //	                  Im( z ) = re_Sqrt_point(|x|,y)
		  //  min( Re ( Sqrt( z ) ) ) at lower right corner = Q II/III
		  //  max( Re ( Sqrt( z ) ) ) at upper right corner = Q II
		  resxl = inf( im_Sqrt_point( -srez, iimz, -hxu, hyl ) );
		  resxu = sup( re_Sqrt_point( srez, simz, hxu, hyu ) );
		  //  min( Im ( Sqrt( z ) ) ) on real line
		  //  max( Im ( Sqrt( z ) ) ) at lower or upper left corner
		  resyl = inf( sqrt( -hxu ) );
		  resyu = ( -iimz > simz ? 
			    sup( re_Sqrt_point( -irez, simz, -hxl, hyl ) ) :
			    sup( im_Sqrt_point( irez, simz, hxl, hyu ) ) ); 
		}
	      else
		//  0 is interior point of z
		//  here, the principal values apply at all corner points
		{
		  resxl = 0;
		  //  max( Re ( Sqrt( z ) ) ) at lower or upper right corner
		  resxu = ( -iimz > simz ? 
			    sup( re_Sqrt_point( srez, iimz, hxu, hyl ) ) :
			    sup( re_Sqrt_point( srez, simz, hxu, hyu ) ) ); 
		  //  min( Im ( Sqrt( z ) ) ) at lower left corner
		  //  max( Im ( Sqrt( z ) ) ) at upper left corner
		  resyl = inf( im_Sqrt_point( irez, iimz, hxl, hyl ) );
		  resyu = sup( im_Sqrt_point( irez, simz, hxl, hyu ) );
		}
	    }
	}
      w = CInterval( Interval( resxl, resxu ), Interval( resyl, resyu ) );
    }
  else
    //  Sqrt( z ) is well-defined	    
    w = Sqrt( z );
      
  std::list<CInterval> res;
  res.push_back( w ); 
  res.push_back( -w );

  return res;
}
//
//-- end sqrt_all -------------------------------------------------------------

//-- Root(z,n): analytic n-th root ---------------------------------- 050930 --
//
inline Interval re_Root_point( //------------------------------------ 050930 --
       const Interval& rez, const Interval& imz, int n ) 
//
//  real part of analytic n-th root
//  do not use this as a general function
//  - it's only a subroutine of Root(z,n) and of root_all(z,n)
//  the calculation is validated but largely overestimating 
//  if (rez+I*imz) is not a complex number
//
{   
  CInterval 
    z = CInterval(rez,imz);
  Interval 
    abs_z = CoStLy::abs( z );
  if( sup( abs_z ) == 0.0 )
    //  z == 0
    return ZERO_INTERVAL();
  else 
    return sqrt( abs_z, n ) * cos( CoStLy::Arg( z ) / n );
}

inline Interval im_Root_point(  //----------------------------------- 050930 --
       const Interval& rez, const Interval& imz, int n )
//
//  imaginary part of analytic n-th root.
//  do not use this as a general function
//  - it's only a subroutine of Root(z,n) and of root_all(z,n)
//  the calculation is validated but largely overestimating 
//  if (rez+I*imz) is not a complex number
//
{ 
  CInterval 
    z = CInterval(rez,imz);
  Interval 
    abs_z = CoStLy::abs( z );
  if( sup( abs_z ) == 0.0 )
    //  z == 0
    return ZERO_INTERVAL();
  else 
    return sqrt( abs_z, n ) * sin( CoStLy::Arg( z ) / n );
}

CInterval CoStLy::Root( const CInterval& z, int n ) //--------------- 050921 --
//
//  analytic n-th root function
//  Root(z,n) is undefined if z contains negative real numbers
//
{
  if( n == 0 ) 
    return CInterval( ONE_INTERVAL() );
  else if( n == 1 ) 
    return z;
  else if( n == 2 ) 
    return CoStLy::Sqrt( z );
  else
    {
      double 
	irez = inf( z.re() ),
	srez = sup( z.re() ),
	iimz = inf( z.im() ),
	simz = sup( z.im() );
      Interval 
	hxl( irez ), hxu( srez ), hyl( iimz ), hyu( simz );
      double 
	resxl,  resxu, resyl, resyu;
      
      if( n < 0 )
	//  Root(z,n) is undefined for negative n
	err_msg_Root( "Root(z,n)", n );
      else if( irez < 0.0 and iimz <= 0.0 and simz >= 0.0 ) 
	//  n is negative or z contains negative real values
	err_msg( "Root(z,n)", z );
      else
	{  
	  if( simz < 0.0 )
	    {
	      //  z in lower half plane
              CInterval hres = Root( CInterval( z.re(), -z.im() ), n );
	      return CInterval( hres.re(), -hres.im() );
	    }
	  else
	    {
	      if( iimz > 0.0 )
		{
		  //  z in upper half plane
		  Interval tangle = tan( ( PI() * n ) / ( 2.0 * ( n-1 ) ) );
		  double tanglel = inf( tangle ), tangleu = sup( tangle );
		  //
		  //  min( Re( Root( z ) ) )
		  //
		  if ( irez >= 0.0  or  sup( hyl / tanglel ) <= irez )
		    //  lower boundary right of phi = n*Pi/(2n-2)
		    //  min( Re( Root( z ) ) ) at lower left corner
		    resxl = inf( re_Root_point( hxl, hyl, n ) );
		  else
		    {
		      if( srez < 0.0 and inf( hyl / tangleu ) >= srez )
			//  lower boundary left of phi = n*Pi/(2n-2)
			//  min( Re( Root( z ) ) ) at lower right corner
			resxl = inf( re_Root_point( hxu, hyl, n ) );
		      else
			//  lower boundary intersects phi = n*Pi/(2n-2)
			//  min( Re( Root( z ) ) ) on intersection
			resxl = inf( re_Root_point( iimz / tangle, hyl, n ) );
		    }
		  //
		  //  max( Re( Root( z ) ) )
		  //
		  if ( irez >= 0.0 or sup( hyu / tanglel ) <= irez )
		    //  upper boundary right of phi = n*Pi/(2n-2)
		    //  max( Re( Root( z ) ) ) at upper right corner
		    resxu = sup( re_Root_point( srez, simz, n ) );
		  else
		    {
		      if ( srez < 0.0 and inf( hyu / tangleu ) >= srez )
			//  upper boundary left of phi = n*Pi/(2n-2)
			//  max( Re( Root( z ) ) ) at upper left corner
			resxu = sup( re_Root_point( hxl, hyu, n ) );
		      else
			//  upper boundary intersects phi = n*Pi/(2n-2)
			//  max( Re( Root( z ) ) ) on upper l or r corner
			resxu = max( sup( re_Root_point( hxl, hyu, n ) ),
				     sup( re_Root_point( hxu, hyu, n ) ) );
		    }
		  //
		  //  min( Im( Root( z ) ) )
		  //
		  if ( srez >= 0.0  or sup( hyl / tanglel ) <= srez )
		    //  right boundary right of phi = n*Pi/(2n-2)
		    //  min( Im( Root( z ) ) ) at lower right corner
		    resyl = inf( im_Root_point( hxu, hyl, n ) );
		  else
		    {
		      if( inf( hyu / tangleu ) >= srez )
			//  right boundary left of phi = n*Pi/(2n-2)
			//  min( Im( Root( z ) ) ) at upper right corner
			resyl = inf( im_Root_point( hxu, hyu, n ) );
		      else
			//  right boundary intersects phi = n*Pi/(2n-2)
			//  min( Im( Root( z ) ) ) on intersection
			resyl = inf( im_Root_point( hxu, srez * tangle, n ) );
		    }
		  //
		  //  max( Im( Root( z ) ) )
		  //      
		  if( irez >= 0.0 or sup( hyl / tanglel ) <= irez )
		    //  left boundary right of phi = n*Pi/(2n-2)
		    //  max( Im( Root( z ) ) ) at upper left corner
		    resyu = sup( im_Root_point( hxl, hyu, n ) );
		  else
		    {
		      if( inf( hyu / tangleu ) >= irez )
			//  left boundary left of phi = n*Pi/(2n-2)
			//  max( Im( Root( z ) ) ) at lower left corner
			resyu = sup( im_Root_point( hxl, hyl, n ) );
		      else
			//  left boundary intersects phi = n*Pi/(2n-2)
			//  max( Im( Root(z) ) ) on lower or upper left corner
			resyu = max( sup( im_Root_point( hxl, hyl, n ) ), 
				     sup( im_Root_point( hxl, hyu, n ) ) );
		    }
		}
	      else
		{
		  //  z intersects positive real axis
		  //  min( Re( Root( z ) ) ) on real line
		  //  max( Re( Root( z ) ) ) at lower or upper right corner
		  resxl = ( irez == 0.0 ? 0.0 : inf( sqrt( hxl, n ) ) );
		  resxu = ( -iimz > simz ? sup( re_Root_point( hxu, hyl,n ) ) :
			    sup( re_Root_point( hxu, hyu, n ) ) ); 
		  //  min( Im ( Root( z ) ) ) at lower left corner
		  //  max( Im ( Root( z ) ) ) at upper left corner
		  resyl = inf( im_Root_point( hxl, hyl, n ) );
		  resyu = sup( im_Root_point( hxl, hyu, n ) );
		}
	      return CInterval( Interval( resxl, resxu ), 
				Interval( resyl, resyu ) );
	    }
	}
    }
}
//
//-- end Root -----------------------------------------------------------------

//-- root_all(z,n) -------------------------------------------------- 050930 --
//
std::list<CInterval> CoStLy::root_all( const CInterval& z, int n )
//
//  root_all(z,n) computes a list of n intervals containing all n-th roots of z
//
//  for n >=3, computing the optimal interval bounds is very expensive
//  and thus not considered cost-effective
//
//  hence, the polar form is used to calculate root_all(z,n)
//  (observed overestimation of Re() and Im() in test cases: 5-15%)
//
//  z is enclosed into an interval in polar coordinates 
//  (i.e. a sector of an annulus), from which the roots 
//  (also polar intervals) are computed error-free 
//  (save roundoff, which is enclosed)
//
//  the final inclusion of the roots into a rectangular complex interval
//  involves some additional overestimation
//
{
  std::list<CInterval> res;

  if( n < 0 )
    //  root_all(z,n) is undefined for negative n
    err_msg_Root( "root_all(z,n)", n );
  else if( n == 0 )
    {
      res.push_back( CInterval( ONE_INTERVAL(), ZERO_INTERVAL() ) );
      return res;
    }
  else if( n == 1 )
    {
      res.push_back(z);
      return res;
    }
  else if( n == 2 ) 
    return CoStLy::sqrt_all( z );
  else
    {
      Interval 
      	arg_z = CoStLy::arg( z ), abs_z = CoStLy::abs( z ), root_abs_z;

      if( inf( abs_z ) == 0.0 )
	//  in some cases, the function sqrt(z,n) in the filib library 
	//  crashed here
	{
	  if( sup( abs_z ) == 0.0 )
	    root_abs_z = ZERO_INTERVAL();
	  else
	    {
	      Interval sup_abs_z = sup( abs_z );
	      root_abs_z = Interval( 0.0, sup( sqrt( sup_abs_z, n ) ) );
	    }
	}
      else
	root_abs_z = sqrt( abs_z, n );
 
      for(int k = 0; k < n; k++)
	{
	  Interval arg_k = ( arg_z + 2 * k * PI() ) / n;

	  res.push_back( CInterval( root_abs_z * cos( arg_k ),
				    root_abs_z * sin( arg_k ) ) );
	}
      return res;
    }
}
//
//-- end root_all -------------------------------------------------------------

//-- Sqrt(z,n): analytic n-th root ---------------------------------- 050930 --
//
CInterval CoStLy::Sqrt( const CInterval& z, int n ) 
//
//  Sqrt(z,n) = Root(z,n) 
//
//  included for compatibility with previous versions of CoStLy
//
{
  return CoStLy::Root(z,n);
}
//
//-- end Sqrt(z,n) ------------------------------------------------------------

//-- sqrt_all(z,n) -------------------------------------------------- 050930 --
//
std::list<CInterval> CoStLy::sqrt_all( const CInterval& z, int n )
//
//  sqrt_all(z,n) = root_all(z,n) 
//
//  included for compatibility with previous versions of CoStLy
//
{
  return CoStLy::root_all(z,n);
}
//
//-- end sqrt_all(z,n) --------------------------------------------------------

//-----------------------------------------------------------------------------
//
//  Section 4: Power functions
//
//-----------------------------------------------------------------------------

//-- power_fast ----------------------------------------------------- 050916 --
//
//  simple, validated power function for integer powers, based on exp and ln
//  medium amount of overestimation
//
CInterval CoStLy::power_fast( const CInterval& z, int n ) 
{
  if( n == 0 )
    return CInterval( ONE_INTERVAL() );
  else if( n == 1 ) 
    return z;
  else if( n == -1 ) 
    return 1.0 / z;
  else if( n == 2 ) 
    return CoStLy::sqr( z );
  else 
    //  n >= 3  or  n <= -2
    //  if z is a large interval, z^n is a distorted set, for which the 
    //  inclusion into a complex rectangle contains large overestimation 
    //  for example, if  n * Arg( z )  intersects the cartesian axes 
    //  more than once then  0  is contained in the rectangular enclosure 
    //  of z^n 
    //  for n <= -2, also inversion of z or z^n is required; 
    //  both inversions lead to large overestimation of the resulting interval
    //
    //  the computation of an optimal rectangular enclosure is implemented
    //  in power(z,n); in power_fast(z,n), z is enclosed into a sector S of 
    //  an annulus; S^n is again some sector S' of a (different) annulus; 
    //  S' is calculated exactly (apart from roundoff), and then enclosed 
    //  into a rectangle; there is a certain amount of overestimation  
    //  compared with the optimal rectangular enclosure of z^n, but the 
    //  calculations are much cheaper here
    {
      Interval abs_z = CoStLy::abs( z );

      if( n < 0 and inf( abs_z ) == 0.0 )
	//  z contains 0 
	err_msg( "power_fast", z );

      if( sup( abs_z ) == 0.0 )
        return CInterval( 0.0, 0.0 );
      else
        { 
          Interval 
	    arg_z, abs_z_n; 

          arg_z = CoStLy::arg( z ); 

	  if( inf( abs_z ) > 0.0 )
	    abs_z_n = exp( n * ln( abs_z ) );
	  else
	    {
	      Interval sup_abs_z = sup( abs_z );
	      abs_z_n = Interval( 0.0, sup( exp( n * ln( sup_abs_z ) ) ) );
	    }
	  
	  return CInterval( abs_z_n * cos( n * arg_z ),
                            abs_z_n * sin( n * arg_z ) ); 
	}
    }
}
//
//-- end power_fast -----------------------------------------------------------

//-- power ---------------------------------------------------------- 050930 --
//
//  power function for integer powers with optimal (save roundoff) accuracy
//
//  the extremal curves of Re(z^n) and Im(z^n) are straight lines defined
//  by the rays  arg(z) = k*pi/ ( 2n - 2 ), k = 0,...4n-5
//
//  intersections of these rays with the boundary of z are called 
//  "ray intersections" in the following
//
inline CInterval power_point( //------------------------------------- 050916 --
       const Interval& rez, const Interval& imz, int n )
//
//  z^n for a point interval
//  do not use this as a general function
//  - it's only a subroutine of power
//  the calculation may break down otherwise
//  the case 0 in z for negative n is handled in power(z,n)
//
{        
  if( inf( rez ) == 0.0 and inf( imz ) == 0.0 )
    //  implies z = 0
    return CInterval( ZERO_INTERVAL(), ZERO_INTERVAL() );
  else
    {
      CInterval z = CInterval( rez, imz );
      Interval abs_z = CoStLy::abs( z );
      Interval arg_z = CoStLy::arg( z ); 
      Interval abs_z_n = exp( n * ln( abs_z ) );
      
      return CInterval( abs_z_n * cos( n * arg_z ),
			abs_z_n * sin( n * arg_z ) );  
    }
}

inline void update_res( const CInterval& res, //--------------------- 050916 --
                 double& resxl, double& resxu, double& resyl, double& resyu )
//  subroutine of power(z,n)
//  update extremal values of power function
{
  resxl = min( resxl, inf( res.re() ) );
  resxu = max( resxu, sup( res.re() ) );
  resyl = min( resyl, inf( res.im() ) );
  resyu = max( resyu, sup( res.im() ) );
}

void horizontal_check_power( //-------------------------------------- 050930 --
     const Interval& hy, const double& hyy, const Interval& arg_h, 
     const double& irez, const double& srez, 
     double& resxl, double& resxu, double& resyl, double& resyu, int n )
//  subroutine of power(z,n)
//  check all relevant ray intersections on a horizontal boundary
{
  double r_int;
  int
    il1, il2, iu1, iu2;
  CInterval res;
  //  
  //  the most difficult task is to determine the relevant ray intersections 
  //  both arg_hl and n can be negative, which makes it complicated 
  //  to decide the correct indexes for the topmost intersections, etc.
  //
  //  we also have to exclude alleged intersection with the x-axis 
  //  (caused by overestimation in the calling procedure)
  //  absolute value is used to check if arg_h contains +/-pi
  //
  //  to simplify the distinction of cases, we introduce the variable 
  //  nofrays (number of rays) = abs( n-1 )
  //
  int nofrays = abs( n-1 ), nofrays2 = 2 * nofrays;
  double arg_hl = inf( nofrays2 * arg_h / PI() );
  if( arg_hl >= 0.0 )
    modf( arg_hl, &r_int );
  else
    modf( arg_hl-1, &r_int );
  il1 = int( r_int + 1.0 );
  if( (il1)%(nofrays2) == 0 )
    //  intersection of horizontal boundary with x-axis is impossible
    //  invalid overestimation of argument arg_h near x-axis has 
    //  happened in the calling procedure; remove intersection
    il1++;

  double arg_hu = sup( nofrays2 * arg_h / PI() );
  if( arg_hu >= 0.0 )
    modf( arg_hu, &r_int );
  else
    modf( arg_hu-1, &r_int );
  iu1 = int( r_int );
  if( (iu1)%(nofrays2) == 0 )
    //  intersection of horizontal boundary with x-axis is impossible
    //  invalid overestimation of argument arg_h near x-axis has 
    //  happened in the calling procedure; remove intersection
    iu1--;
  if( iu1 >= il1 )
    //  at least one ray intersection
    //  maybe more?
    {
      if( iu1 > il1 + 3 ) 
	//
	//  there are more than 4 ray intersections
	//  now we must decide which of these are relevant
	//  depending on il1, iu1, n and on the quadrants, 
	//  4 to 7 intersections must be considered
	//
	if( n > 0 )
	  //  outmost intersections are relevant
	  {
	    if( irez >= 0.0 ) 
	      //  z in quadrants I or IV: 
	      //  only 4 rightmost intersections are relevant
	      {  
		if( hyy < 0.0 )
		  //  quadrant IV
		  il1 = iu1 - 3;
		else
		  //  quadrant I
		  iu1 = il1 + 3;
	      }
	    else if( srez <= 0.0 )
	      //  z in quadrants II or III:
	      //  only 4 leftmost intersections are relevant
	      {  
		if( hyy > 0.0 )
		  //  quadrant II
		  il1 = iu1 - 3;
		else
		  //  quadrant III
		  iu1 = il1 + 3;
	      }
	    else
	      //  z intersects imaginary axis
	      //  we may have two lists of intersections
	      {  
		iu2 = iu1;
		il2 = iu2 - 3;
		iu1 = il1 + 3;
		//  remove intersection points that are contained in both lists
		if( il2 <= iu1 )
		  il2 = iu1 + 1;
		//
		//  here, list 2 is processed
		//  list 1 is processed below
		//
		for(int i = il2; i <= iu2; i++)
		  {
		    Interval cotangle = cot( ( PI() * i ) / nofrays2 );
		    res = power_point( hy * cotangle, hy, n );
		    update_res( res, resxl, resxu, resyl, resyu );
		  }
	      }
	  }
	else
	  //  n < 0:
	  //  innermost intersections are relevant
	  {
	    if( irez >= 0.0 ) 
	      //  z in quadrants I or IV: 
	      //  only 4 leftmost intersections are relevant
	      {  
		if( hyy > 0.0 )
		  //  quadrant I
		  il1 = iu1 - 3;
		else
		  //  quadrant IV
		  iu1 = il1 + 3;
	      }
	    else if( srez <= 0.0 )
	      //  z in quadrants II or III:
	      //  only 4 rightmost intersections are relevant
	      {  
		if( hyy < 0.0 )
		  //  quadrant III
		  il1 = iu1 - 3;
		else
		  //  quadrant II
		  iu1 = il1 + 3;
	      }
	    else
	      //  z intersects imaginary axis
	      //  we have one list of 5 to 7 intersections
	      {
		if( hyy > 0.0 )
		  {
		    il2 = nofrays - 3;
		    iu2 = nofrays + 3;
		  }
		else
		  {
		    il2 = -nofrays - 3;
		    iu2 = -nofrays + 3;
		  }
		//  list 1 contains all intersections
		//  list 2 is a filter for all relevant intersections
		//  store intersection of lists 1 and 2 in list 1
		il1 = ( il1 > il2 ? il1 : il2 );
		iu1 = ( iu1 < iu2 ? iu1 : iu2 );
	      }
	  }
      //
      //  list 1 has been left for processing
      //
      for(int i = il1; i <= iu1; i++)
	{
	  Interval cotangle = cot( ( PI() * i ) / nofrays2 );
	  res = power_point( hy * cotangle, hy, n );
	  update_res( res, resxl, resxu, resyl, resyu );
	}
    }
}

void vertical_check_power( //---------------------------------------- 050930 --
     const Interval& hx, const double& hxx, const Interval& arg_h,
     const double& iimz, const double& simz, 
     double& resxl, double& resxu, double& resyl, double& resyu, int n )
//  subroutine of power(z,n)
//  check all relevant ray intersections on a vertical boundary
{
  double r_int;
  int
    il1, il2, iu1, iu2;
  CInterval res;
  //  
  //  the most difficult task is to determine the relevant ray intersections 
  //  both arg_hl and n can be negative, which makes it complicated 
  //  to decide the correct indexes for the topmost intersections, etc.
  //
  //  we also have to exclude alleged intersection with the y-axis 
  //  (caused by overestimation in the calling procedure)
  //  modulo operator % is used to check if arg_h contains +/-pi/2 or 3*pi/2
  //
  //  to simplify the distinction of cases, we introduce the variable 
  //  nofrays (number of rays) = abs( n-1 )
  //
  int nofrays = abs( n-1 ), nofrays2 = 2 * nofrays;

  double arg_hl = inf( nofrays2 * arg_h / PI() );
  if( arg_hl >= 0.0 )
    modf( arg_hl, &r_int );
  else
    modf( arg_hl-1, &r_int );
  il1 = int( r_int + 1.0 );
  if( (il1+nofrays)%(nofrays2) == 0  )
    //  intersection of vertical boundary with y-axis is impossible
    //  remove alleged intersection
    il1++;

  double arg_hu = sup( nofrays2 * arg_h / PI() );
  if( arg_hu >= 0.0 )
    modf( arg_hu, &r_int );
  else
    modf( arg_hu-1, &r_int );
  iu1 = int( r_int );
  if( (iu1+nofrays)%(nofrays2) == 0  )
    //  intersection of vertical boundary with y-axis is impossible
    //  remove alleged intersection
    iu1--;

  if( iu1 >= il1 )
    //  at least one ray intersection
    //  maybe more?
    {
      if( iu1 > il1 + 3 ) 
	//
	//  there are more than 4 ray intersections
	//  now we must decide which of these are relevant
	//  depending on il1, iu1, n and on the quadrants, 
	//  4 to 7 intersections must be considered
	//
	if( n > 0 )
	  //  outmost intersections are relevant
	  {
	    if( iimz >= 0.0 ) 
	      //  z in quadrants I or II: 
	      //  only 4 topmost intersections are relevant
	      {  
		if( hxx > 0.0 )  
		  //  quadrant I
		  il1 = iu1 - 3;
		else
		  //  quadrant II
		  iu1 = il1 + 3;
	      }
	    else if( simz <= 0.0 )
	      //  z in quadrants III or IV:
	      //  only 4 lowest intersections are relevant
	      {  
		if( hxx < 0.0 ) 
		  //  quadrant III
		  il1 = iu1 - 3;
		else
		  //  quadrant IV
		  iu1 = il1 + 3;
	      }
	    else
	      //  z intersects real axis
	      //  we may have two lists of intersections
	      {  
		iu2 = iu1;
		il2 = iu2 - 3;
		iu1 = il1 + 3;
		//  remove intersection points that are contained in both lists
		if( il2 <= iu1 )
		  il2 = iu1 + 1;
		//
		//  here, list 2 is processed
		//  list 1 is processed below
		//
		for(int i = il2; i <= iu2; i++)
		  {
		    Interval tangle = tan( ( PI() * i ) / nofrays2 );
		    res = power_point( hx, hx * tangle, n );
		    update_res( res, resxl, resxu, resyl, resyu );
		  }
	      }
	  }
	else
	  //  n < 0:
	  //  innermost intersections are relevant
	  {
	    if( iimz >= 0.0 ) 
	      //  z in quadrants I or II: 
	      //  only 4 lowest intersections are relevant
	      {  
		if( hxx < 0.0 )
		  //  quadrant II
		  il1 = iu1 - 3;
		else
		  //  quadrant I
		  iu1 = il1 + 3;
	      }
	    else if( simz <= 0.0 )
	      //  z in quadrants III or IV:
	      //  only 4 topmost intersections are relevant
	      {  
		if( hxx > 0.0 )
		  //  quadrant IV
		  il1 = iu1 - 3;
		else
		  //  quadrant III
		  iu1 = il1 + 3;
	      }
	    else
	      //  z intersects real axis
	      //  we have one lists of 5 to 7 intersections
	      {
		if( hxx > 0.0 )
		  {
		    il2 = -3;
		    iu2 =  3;
		  }
		else
		  {
		    il2 = 2 * nofrays - 3;
		    iu2 = 2 * nofrays + 3;
		  }
		//  list 1 contains all intersections
		//  list 2 is a filter for all relevant intersections
		//  store intersection of lists 1 and 2 in list 1
		il1 = ( il1 > il2 ? il1 : il2 );
		iu1 = ( iu1 < iu2 ? iu1 : iu2 );
	      }
	  }
      //
      //  list 1 has been left for processing
      //
      for(int i = il1; i <= iu1; i++)
	{
	  Interval tangle = tan( ( PI() * i ) / nofrays2 );
	  res = power_point( hx, hx * tangle, n );
	  update_res( res, resxl, resxu, resyl, resyu );
	}
    }
}

CInterval CoStLy::power( const CInterval& z, int n ) //-------------- 050805 --
//
//  Power function for integer powers with optimal (save roundoff) accuracy.
//
{
  if( n == 0 )
    return CInterval( ONE_INTERVAL() );
  else if( n == 1 ) 
    return z;
  else if( n == -1 ) 
    return 1.0 / z;
  else if( n == 2 ) 
    return CoStLy::sqr( z );
  else 
    //  n >= 3  or  n <= -2
    //
    //  n may have a large absolute value and z may be a large interval 
    //  in this case, we calculate function values at specific points
    //  and look for min and max
    //  
    //  an implementation with fewer function evaluations would be possible,
    //  at the expense of an unreadable source code
    {
      Interval abs_z = CoStLy::abs( z );

      if( n < 0 and inf( abs_z ) == 0.0 )
	//  z contains 0 
	err_msg( "power", z );

      if( sup( abs_z ) == 0.0 )
        return CInterval( 0.0, 0.0 );
      else
        { 
	  double 
	    irez = inf( z.re() ),
	    srez = sup( z.re() ),
	    iimz = inf( z.im() ),
	    simz = sup( z.im() );
	  Interval 
	    hxl( irez ), hxu( srez ), hyl( iimz ), hyu( simz );
	  double 
	    resxl,  resxu, resyl, resyu;
	  CInterval res;
	  //
	  //  extremal values lie at corner points or on intersections of the 
	  //  boundary of z with any ray  arg( w ) = k*Pi/(2*(n-1)) 
	  //
	  //  first check the corners of z
	  //
	  res = power_point( hxl, hyl, n );
	  resxl = inf( res.re() );
	  resxu = sup( res.re() );
	  resyl = inf( res.im() );
	  resyu = sup( res.im() );
	  res = power_point( hxu, hyl, n );
	  update_res( res, resxl, resxu, resyl, resyu );
	  res = power_point( hxl, hyu, n );
	  update_res( res, resxl, resxu, resyl, resyu );
	  res = power_point( hxu, hyu, n );
	  update_res( res, resxl, resxu, resyl, resyu );
	  //
	  //  consider the origin, if it is a boundary point 
	  //  (negative n have been taken care of above)
	  //
	  if( irez * srez <= 0.0 and iimz * simz <= 0.0 
	      and irez * srez * iimz * simz == 0.0 )
	    update_res( CInterval( ZERO_INTERVAL(), ZERO_INTERVAL()), 
			resxl, resxu, resyl, resyu );
	  //
	  //  now check for ray intersections 
	  //  for each boundary, we must consider the function values at
	  //  up to 4 consecutive intersections in each quadrant that
	  //  contains a part of the boundary
	  //  this is done in the subroutines horizontal_cp and vertical_cp 
	  //
	  //  lower boundary
	  //  
	  Interval arg_h;
	  if( iimz != 0.0 )
	    {
	      arg_h = CoStLy::arg( CInterval( z.re(), hyl ) );
	      horizontal_check_power( hyl, iimz, arg_h, irez, srez,
				      resxl, resxu, resyl, resyu, n );
	    }
	  //  upper boundary
	  if( simz != 0.0 )
	    {
	      arg_h = CoStLy::arg( CInterval( z.re(), hyu ) );
	      horizontal_check_power( hyu, simz, arg_h, irez, srez,
				      resxl, resxu, resyl, resyu, n );
	    }
	  //  left boundary
	  if( irez != 0.0 )
	    {
	      arg_h = CoStLy::arg( CInterval( hxl, z.im() ) );
	      vertical_check_power( hxl, irez, arg_h, iimz, simz, 
				    resxl, resxu, resyl, resyu, n );
	    }
	  //  right boundary
	  if( srez != 0.0 )
	    {
	      arg_h = CoStLy::arg( CInterval( hxu, z.im() ) );
	      vertical_check_power( hxu, srez, arg_h, iimz, simz, 
				    resxl, resxu, resyl, resyu, n );
	    }
	  return CInterval( Interval( resxl, resxu ), 
			    Interval( resyl, resyu ) );
	}
    }
}
//
//-- end power ----------------------------------------------------------------

//-- Pow ------------------------------------------------------------ 050814 --
//
//  analytic power function for real interval exponent, based on Ln
//
CInterval CoStLy::Pow( const CInterval& z, const Interval& p ) 
{
  if( inf( z.re() ) <= 0.0 and in( 0.0, z.im() ) )
    //  z contains 0 or negative real values
    err_msg( "Pow", z );
  else
    return exp( p * Ln( z ) );
}
//
//-- end Pow ------------------------------------------------------------------

//-- Pow ------------------------------------------------------------ 050930 --
//
//  analytic power function for complex interval exponent, based on Ln
//
CInterval CoStLy::Pow( const CInterval& z, const CInterval& w ) 
{
  if( inf( z.re() ) <= 0.0 and in( 0.0, z.im() ) )
    //  z contains 0 or negative real values
    err_msg( "Pow", z );
  else
    return exp( w * Ln( z ) );
}
//
//-- end Pow ------------------------------------------------------------------

//-- pow_all -------------------------------------------------------- 050930 --
//
//  non-analytic power function for real interval exponent
//
//  if z doesn't contain 0, then four rectangles are computed which comprehend
//  an annulus that contains all values  zeta^phi, zeta in z, phi in p
//
//  if 0 in z and negative reals in p, then execution is aborted
//  (set of all function values spreads out on the entire complex plane)
//
std::list<CInterval> CoStLy::pow_all( const CInterval& z, const Interval& p )
{
  std::list<CInterval> res;

  Interval abs_z = CoStLy::abs( z );

  if( 0.0 < inf( abs_z ) )
    {
      Interval abs_z_p = exp( p * ln( abs_z ) );

      //  inner and outer radii of the annulus are inf/sup( abs_z_n )
      //  inscribed square has side length sqrt( 2 ) * rad_1
      Interval rad_1 = INV_SQRT_2() * inf( abs_z_p );
      Interval rad_2 = sup( abs_z_p );

      //  4 intervals covering the annulus, counter-clockwise
      res.push_back( CInterval( Interval(  inf( rad_1 ),  sup( rad_2 ) ),
				Interval( -sup( rad_1 ),  sup( rad_2 ) ) ) );
      res.push_back( CInterval( Interval( -sup( rad_2 ),  sup( rad_1 ) ),
				Interval(  inf( rad_1 ),  sup( rad_2 ) ) ) );
      res.push_back( CInterval( Interval( -sup( rad_2 ), -inf( rad_1 ) ),
				Interval( -sup( rad_2 ),  sup( rad_1 ) ) ) );
      res.push_back( CInterval( Interval( -sup( rad_1 ),  sup( rad_2 ) ),
				Interval( -sup( rad_2 ), -inf( rad_1 ) ) ) );
      return res;
    }
  else 
    //  0 in z
    {
      if( inf( p ) > 0.0 )
	//
	//  all values   zeta^phi, zeta in z, phi in p   lie in a disk
	//
	{
	  double rad_p;
	  if( sup( abs_z ) == 0.0 )
	    rad_p = 0.0;
	  else
	    {
	      Interval abs_z_p = exp( p * ln( Interval( sup( abs_z ) ) ) );
	      rad_p = sup( abs_z_p );
	    }
	  res.push_back( CInterval( Interval( - rad_p, rad_p ),
				    Interval( - rad_p, rad_p ) ) ); 
	  return res;
	}
      else
	//
	//  the set   zeta^phi, zeta in z, phi in p   is unbounded
	//  if inf( p ) < 0;  0^p is undefined for p <= 0
	//
	err_msg_pal( z, p );
    }
}
//
//-- end pow_all --------------------------------------------------------------

//-----------------------------------------------------------------------------
//
//  Section 5: Asin, Acos, Asinh, Acosh
//
//  the implementation of  Asinh and Acosh is based on Asin and Acos
//
//  Asinh( z ) = i * Asin( - i * z )
//  Acosh( z ) = i * Acos( z )
//
//  only principal values are computed
//
//-----------------------------------------------------------------------------

//-- Asin ----------------------------------------------------------- 051010 --
//
//  analytic inverse sine function
//
inline Interval re_Asin_point( //------------------------------------ 051010 --
       const double& irez, const double& iimz, 
       const Interval& rez, const Interval& imz )
//
//  auxiliary function for evaluating Asin
//  do not use this as a general function
//  - it's only a subroutine of Asin
//  the calculation is void if (rez+I*imz) is not a complex number
//
//  z = x+iy,  calculate asin( 2x / ( |z+1| + |z-1| ) )
//
{
  double 
    abs_y = std::abs( iimz );
  Interval 
    xm1, xp1, y_2, res;
  if( iimz == 0.0 )
    return asin( rez );
  else
    {
      res = 2.0 * rez / ( CoStLy::abs( CInterval(rez+1.0,imz) )
			  + CoStLy::abs( CInterval(rez-1.0,imz) ) );
      //  rectify potential overestimation near |res| = 1
      if( inf( res ) < -0.99 )  //  experimentally determined bound
	//  results get worse if -0.99 is replaced by -0.707
	//  maybe due to some overestimation in C-XSC/filib++
	//
	//  recalculate near res = -1
	//  asin( t ) = 2 * asin( sqrt( (1+t)/2 ) ) - pi/2
	//  we have x < 0
	{
	  if( irez == -1.0 )
	    //  |y| < 1.005
	    return 2.0 * asin( sqrt( abs_y/( 2.0+sqrt(4.0+sqr(imz))+abs_y ) ) )
                       - HALFPI();
	  else
	    //  x < 0, |x-1| > eps/2, |y| < 1.005 * |x|
	    //  res = -1 + (res+1)
	    {
	      xm1 = 1.0 - rez;
	      xp1 = -1.0 - rez;
	      Interval sqp, sqm;
	      sqm = xm1 * sqrt( 1.0 + sqr( abs_y/xm1 ) );
	      sqp = abs(xp1) * sqrt( 1.0 + sqr( abs_y/xp1 ) );
	      if( irez < -1.0 )
		//  xp1 > 0
		return 2.0 * asin( ( abs_y / sqrt(sqp+sqm) ) 
			   * sqrt( 0.5/(sqp+xp1)+0.5/(sqm+xm1) ) ) - HALFPI();
	      else
		//  xp1 < 0
		return 2.0 * asin( sqrt( ( abs_y*(abs_y/(sqm+xm1))+(sqp-xp1) )
				   / ( 2.0*(sqp+sqm) ) ) ) - HALFPI();
	    }
	}
      else if( sup( res ) <= 0.99 )  //  experimentally determined bound
	//  results get worse if 0.99 is replaced by 0.707
	//  maybe due to some overestimation in C-XSC/filib++
	return asin( res );
      else
	//  recalculate near res = 1
	//  asin( t ) = pi/ 2 - 2 * asin( sqrt( (1-t)/2 ) )
	//  we have x > 0
	{
	  if( irez == 1.0 )
	    //  |y| < 1.005
	      return HALFPI() - 2.0 * asin( 
		                sqrt( abs_y/(2.0+sqrt(4.0+sqr(imz))+abs_y) ) );
	  else
	    //  x > 0, |x-1| > eps/2, |y| < 1.005 * |x|
	    {
	      xm1 = rez - 1.0;
	      xp1 = rez + 1.0;
	      Interval sqp, sqm;
	      sqm = abs(xm1) * sqrt( 1.0 + sqr( abs_y/xm1 ) );
	      sqp = xp1 * sqrt( 1.0 + sqr( abs_y/xp1 ) );
	      if( irez > 1.0 )
		//  xm1 > 0
		return HALFPI() - 2.0 * asin( ( abs_y / sqrt(sqp+sqm) ) 
                                      * sqrt( 0.5/(sqp+xp1)+0.5/(sqm+xm1) ) );
	      else
		//  xm1 < 0
		return HALFPI() - 2.0 * asin( sqrt( ( abs_y*(abs_y/(sqp+xp1))
					      +(sqm-xm1) )/(2.0*(sqp+sqm)) ) );
	    }
	}
    }
}

inline Interval im_Asin_point( //------------------------------------ 050928 --
       const double& irez, const double& iimz, 
       const Interval& rez, const Interval& imz )
//
//  auxiliary function for evaluating Asin
//  do not use this as a general function
//  - it's only a subroutine of Asin and of Acosh
//  the calculation is void if (rez+I*imz) is not a complex number
//
//  z = x+iy,  t = ( |z+1| + |z-1| ) / 2 >= 1,  tm1 = 2(t-1)
//             acosh( t ) = asinh( sqrt( (t-1)(t+1) ) )
//
{
  double abs_y = std::abs( iimz );
  Interval 
    t = 0.5 * ( CoStLy::abs( CInterval(rez+1.0,imz) )
		+ CoStLy::abs( CInterval(rez-1.0,imz) ) ),
    tm1, xm1, xp1, y_2;

  if( abs_y > 1.0 or irez < -2.0 or irez > 2.0 )
    //  regular case
    return acosh( t );
  else
    //  cancellation: calculate t-1
    //  tm1 = 2*(t-1)
    //  remaining underflow in y^2 is safe
    {
      y_2 = sqr( imz );
      if( irez == -1.0 or irez == 1.0 )
	{
	  tm1 = y_2 / ( sqrt( 4.0 + y_2  ) + 2.0 ) + abs_y;
	  return CoStLy::real_asinh( sqrt( 0.5 * tm1 * ( t + 1.0 ) ) );
	}
      else 
	{
	  xm1 = 1.0 - rez;
	  xp1 = 1.0 + rez;
	  if( irez < -1.0 )
	    {
	      tm1 = y_2 / ( sqrt( sqr( xm1 ) + y_2  ) + xm1 )
	            + sqrt( sqr( xp1 ) + y_2 ) - xp1;
	      return CoStLy::real_asinh( sqrt( 0.5 * tm1 * ( t + 1.0 ) ) );
	    }
	  else if( irez > 1.0 )
	    {
	      tm1 = y_2 / ( sqrt( sqr( xp1 ) + y_2  ) + xp1  )
	            + sqrt( sqr( xm1 ) + y_2 ) - xm1;
	      return CoStLy::real_asinh( sqrt( 0.5 * tm1 * ( t + 1.0 ) ) );
	    }
	  else
	    //  -1 < irez < 1
	    {
	      tm1 = 1.0 / ( sqrt( sqr( xm1 ) + y_2  ) + xm1 )
                  + 1.0 / ( sqrt( sqr( xp1 ) + y_2  ) + xp1 );
	      return CoStLy::real_asinh( abs_y * sqrt( 0.5 * tm1*( t+1.0 ) ) );
	    }
	}
    }
}

Interval re_Asin( const Interval& rez, const Interval& imz, 
		  const double& irez, const double& srez ) //-------- 050928 --
//
//  real part of Asin( z ),  z = x + iy,  y >= 0
//
{
  double 
    iimz = inf( imz ),
    simz = sup( imz ),
    resl, resu;

  Interval 
    hxl(irez), hxu(srez), hyl(iimz), hyu(simz);

  if( iimz == 0.0 )
    //  z intersects [-1,1]
    {
      if( irez <= 0.0 )
	resl = inf( asin( hxl ) );
      else
	resl = inf( re_Asin_point( irez, simz, hxl, hyu ) );
      if( srez >= 0.0 )
	resu = sup( asin( hxu ) );
      else
	resu = sup( re_Asin_point( srez, simz, hxu, hyu ) ); 
     }
  else
    {
      if( irez >= 0.0  )
	//  left boundary in quadrant I
	//  min( Re( z ) ) at upper left corner
	resl = inf( re_Asin_point( irez, simz, hxl, hyu ) );
      else
	//  left boundary in quadrant II
	//  min( Re( z ) ) at lower left corner
	resl = inf( re_Asin_point( irez, iimz, hxl, hyl ) );
      if( srez >= 0.0  )
	//  right boundary in quadrant I
	//  max( Re( z ) ) at lower right corner
	resu = sup( re_Asin_point( srez, iimz, hxu, hyl ) );
      else
	//  right boundary in quadrant II
	//  max( Re( z ) ) at upper right corner
	resu = sup( re_Asin_point( srez, simz, hxu, hyu ) );
    }
  return Interval( resl, resu );
}

Interval im_Asin( const Interval& rez, const Interval& imz, 
		  const double& iimz, const double& simz ) //-------- 050928 --
//
//  imaginary part of Asin( z ),  z = x + iy,  x >= 0
//
{
  double 
    irez = inf( rez ),
    srez = sup( rez ),
    resl, resu;

  Interval 
    hxl(irez), hxu(srez), hyl(iimz), hyu(simz);

  if( iimz >= 0.0 )
    //  lower boundary in quadrant I
    //  min at lower left corner
    resl = inf( im_Asin_point( irez, iimz, hxl, hyl ) );
  else
    //  lower boundary in quadrant IV
    //  min at lower right corner
    resl = - sup( im_Asin_point( srez, iimz, hxu, hyl ) );
  if( simz > 0.0 )
    //  upper boundary in quadrant I
    //  max at upper right corner
    resu = sup( im_Asin_point( srez, simz, hxu, hyu ) );
  else
    //  upper boundary in quadrant IV
    //  max at upper left corner
    resu = - inf( im_Asin_point( irez, simz, hxl, hyu ) );

  return Interval( resl, resu );
}

CInterval CoStLy::Asin( const CInterval& z ) //---------------------- 050928 --
{
  Interval
    rez = z.re(),
    imz = z.im();

  double 
    irez = inf( rez ),
    srez = sup( rez ),
    iimz = inf( imz ),
    simz = sup( imz );

  //
  //  check for singularities
  //
  if( ( iimz <= 0.0 and simz >= 0.0 ) and ( irez < -1.0 or srez > 1.0 ) ) 
    err_msg( "Asin", z );
  else
    return CInterval( re_Asin( rez, abs( imz ), irez, srez ), 
		      im_Asin( abs( rez ), imz, iimz, simz ) );
}
//
//-- end Asin -----------------------------------------------------------------

//-- Acos ----------------------------------------------------------- 051010 --
//
inline Interval re_Acos_point( //------------------------------------ 051010 --
       const double& irez, const double& iimz, 
       const Interval& rez, const Interval& imz )
//
//  auxiliary function for evaluating Acos
//  do not use this as a general function
//  - it's only a subroutine of Acos and of Acosh
//  the calculation is void if (rez+I*imz) is not a complex number
//
//  z = x+iy,  calculate acos( 2x / ( |z+1| + |z-1| ) )
//
{
  double 
    abs_y = std::abs( iimz );
  Interval 
    xm1, xp1, y_2, res;
  if( iimz == 0.0 )
    {
      if( irez > 0.707 )
	return acos( rez );
      else
	//  acos(x) in C-XSC/filib shows overestimation
	return HALFPI() - asin( rez );
    }
  else
    {
      res = 2.0 * rez / ( CoStLy::abs( CInterval(rez+1.0,imz) )
			  + CoStLy::abs( CInterval(rez-1.0,imz) ) );
      //  rectify potential overestimation near |res| = 1
      if( inf( res ) < -0.99 )  //  experimentally determined bound
	//  results get worse if -0.99 is replaced by -0.707
	//  maybe due to some overestimation in C-XSC/filib++
	//
	//  recalculate near res = -1
	//  acos( t ) = pi - 2 * asin( sqrt( (1+t)/2 ) )
	//  ( = 2 * acos( sqrt( (1+t)/2 ) ): but acos(x) in C-XSC/filib shows
	//                                   overestimation for x < 0.707)
	//  we have x < 0
	{
	  if( irez == -1.0 )
	    //  |y| < 1.005
	    return PI()-2.0*asin( sqrt(abs_y/(2.0+sqrt(4.0+sqr(imz))+abs_y)) );
	  else
	    //  x < 0, |x-1| > eps/2, |y| < 1.005 * |x|
	    //  res = -1 + (res+1)
	    {
	      xm1 = 1.0 - rez;
	      xp1 = -1.0 - rez;
	      Interval sqp, sqm;
	      sqm = xm1 * sqrt( 1.0 + sqr( abs_y/xm1 ) );
	      sqp = abs( xp1 ) * sqrt( 1.0 + sqr( abs_y/xp1 ) );
	      if( irez < -1.0 )
		//  xp1 > 0
		return PI() - 2.0 * asin( ( abs_y / sqrt(sqp+sqm) ) 
			          * sqrt( 0.5/(sqp+xp1)+0.5/(sqm+xm1) ) );
	      else
		//  xp1 < 0
		return PI()-2.0*asin( sqrt((abs_y*(abs_y/(sqm+xm1))+(sqp-xp1))
					 / ( 2.0*(sqp+sqm) ) ) );
	    }
	}
      else if( sup( res ) < 0.707 )
	return HALFPI() - asin( res );
      else if( sup( res ) < 0.99 )  //  experimentally determined bound
      	return acos( res );
      else
	//  recalculate near res = 1
	//  acos( t ) = 2 * asin( sqrt( (1-t)/2 ) )
	//  we have x > 0
	{
	  if( irez == 1.0 )
	    //  |y| < 1.005
	    return 2.0 * asin( sqrt( abs_y/(2.0+sqrt(4.0+sqr(imz))+abs_y) ) );
	  else
	    //  x > 0, |x-1| > eps/2, |y| < 1.005 * |x|
	    {
	      xm1 = rez - 1.0;
	      xp1 = rez + 1.0;
	      Interval sqp, sqm;
	      sqm = abs(xm1) * sqrt( 1.0 + sqr( abs_y/xm1 ) );
	      sqp = xp1 * sqrt( 1.0 + sqr( abs_y/xp1 ) );
	      if( irez > 1.0 )
		//  xm1 > 0
		return 2.0 * asin( ( abs_y / sqrt(sqp+sqm) ) 
				   * sqrt( 0.5/(sqp+xp1)+0.5/(sqm+xm1) ) );
	      else
		//  xm1 < 0
		return 2.0 * asin( sqrt( (abs_y*(abs_y/(sqp+xp1))+(sqm-xm1)) 
					 / ( 2.0*(sqp+sqm) ) ) );
	    }
	}
    }
}

Interval re_Acos( const Interval& rez, const Interval& imz, 
		  const double& irez, const double& srez ) //-------- 051008 --
//
//  real part of Acos( z ),  z = x + iy,  y >= 0
//
{
  double 
    iimz = inf( imz ),
    simz = sup( imz ),
    resl, resu;

  Interval 
    hxl(irez), hxu(srez), hyl(iimz), hyu(simz);

if( iimz == 0.0 )
    //  z intersects [-1,1]
    {
      if( srez >= 0.99 )
	resl = inf( acos( hxu ) );
      else if( srez >= 0.0 )
	//  acos(x) in C-XSC/filib shows overestimation
	resl = inf( HALFPI() - asin( hxu ) );
      else
	resl = inf( re_Acos_point( srez, simz, hxu, hyu ) );
      if( irez <= 0.0 or simz == 0.0 )
	{
	  if( irez > 0.99 )
	    resu = sup( acos( hxl ) );
	  else
	    //  acos(x) in C-XSC/filib shows overestimation
	    resu = sup( HALFPI() - asin( hxl ) );
	}
      else
	resu = sup( re_Acos_point( irez, simz, hxl, hyu ) );
     }
   else
    {
      if( srez >= 0.0  )
	//  right boundary in quadrant I
	//  min( Re( z ) ) at lower right corner
	resl = inf( re_Acos_point( srez, iimz, hxu, hyl ) );
      else
	//  right boundary in quadrant II
	//  min( Re( z ) ) at upper right corner
	resl = inf( re_Acos_point( srez, simz, hxu, hyu ) );
      if( irez >= 0.0  )
	//  left boundary in quadrant I
	//  max( Re( z ) ) at upper left corner
	resu = sup( re_Acos_point( irez, simz, hxl, hyu ) );
      else
	//  left boundary in quadrant II
	//  max( Re( z ) ) at lower left corner
	resu = sup( re_Acos_point( irez, iimz, hxl, hyl ) );
    }
  return Interval( resl, resu );
}

CInterval CoStLy::Acos( const CInterval& z ) //---------------------- 050928 --
{
  Interval
    rez = z.re(),
    imz = z.im();

  double 
    irez = inf( rez ),
    srez = sup( rez ),
    iimz = inf( imz ),
    simz = sup( imz );

  //
  //  check for singularities
  //
  if( ( iimz <= 0.0 and simz >= 0.0 ) and ( irez < -1.0 or srez > 1.0 ) ) 
    err_msg( "Acos", z );
  else
    return CInterval( re_Acos( rez, abs( imz ), irez, srez ), 
		      -im_Asin( abs( rez ), imz, iimz, simz ) );
}
//
//-- end Acos -----------------------------------------------------------------

//-- Ascs ----------------------------------------------------------- 050929 --
//
//  Asin( z )  = pi/2 - Acos( z )
//
//  alternative implemention for testing 
//
CInterval CoStLy::Ascs( const CInterval& z )
{
  return HALFPI() - CoStLy::Acos( z );
}
//
//-- end Ascs -----------------------------------------------------------------

//-- Acsn ----------------------------------------------------------- 050929 --
//
//  Acos( z )  = pi/2 - Asin( z )
//
//  alternative implemention for testing 
//
CInterval CoStLy::Acsn( const CInterval& z )
{
  return HALFPI() - CoStLy::Asin( z );
}
//
//-- end Acsn -----------------------------------------------------------------

//-- Asinh ---------------------------------------------------------- 040730 --
//
CInterval CoStLy::Asinh( const CInterval& z )
//
//  Asinh( Z ) = i * Asin( -i * z )
//
{
  CInterval res = CoStLy::Asin( CInterval( z.im(), -z.re() ) );
  return CInterval( - res.im(), res.re() );
}
//
//-- end Asinh ----------------------------------------------------------------

//-- Acosh ---------------------------------------------------------- 050929 --
//
CInterval CoStLy::Acosh( const CInterval& z ) 
//
//  Acosh( z ) = +/- i * Acos( z )
//
{
  Interval
    rez = z.re(),
    imz = z.im();

  double 
    irez = inf( rez ),
    srez = sup( rez ),
    iimz = inf( imz ),
    simz = sup( imz );

  //
  //  check for singularities
  //
  if( ( iimz <= 0.0 and simz >= 0.0 ) and ( irez < 1.0 ) ) 
    err_msg( "Acosh", z );

  if( iimz > 0.0 )
    //  z in upper half plane
    //  iimz >= 0 does not work, since Acos( x ) is undefined for |x| > 1
    //  Acosh( z ) =  + i * ( Acos( z ) ) 
    {
      CInterval res = CoStLy::Acos( z );
      return CInterval( - res.im(), res.re() );
    }
  else if( simz < 0.0 )
    //  z in lower half plane
    //  simz <= 0 does not work, since Acos( x ) is undefined for |x| > 1
    //  Acosh( z ) =  - i * ( Acos( z ) ) 
    {
      CInterval res = CoStLy::Acos( z );
      return CInterval( res.im(), - res.re() );
    }
  else
    //
    //  z intersects [1,infinity)
    //
    {
      Interval 
	hxl(irez), hxu(srez), hyl(iimz), hyu(simz);

      double 
	resxl, resxu, resyl, resyu;

      double 
	abs_imz = max(-iimz,simz);

      //  real part
      //  min on the left on real axis, max at lower or upper right corner
      //
      resxl = inf( acosh( hxl ) );
      if( abs_imz == 0.0 )
	resxu = sup( acosh( hxu ) );
      else
	resxu = sup( im_Asin_point( srez, abs_imz, hxu, Interval(abs_imz) ) );
      //
      //  imaginary part
      //  minmum at lower left corner, maximum at upper left corner
      //
      if( iimz == 0.0 )
	resyl = 0.0;
      else
	resyl = -sup( re_Acos_point( irez, iimz, hxl, hyl ) );
      if( simz == 0.0 )
	resyu = 0.0;
      else
	resyu = sup( re_Acos_point( irez, simz, hxl, hyu ) );
      
      return CInterval( Interval( resxl, resxu ), Interval( resyl, resyu ) );
    }
}
//
//-- end Acosh ----------------------------------------------------------------

//-----------------------------------------------------------------------------
//
//  Section 6: Atan, Acot, Atanh, Acoth
//
//  the implementation of Atanh, and Acoth is based on Atan and Acot:
//
//  Atanh( z ) = - i * Atan( i * z )
//  Acoth( z ) = i * Acot( i * z )
//
//  only principal values are computed
//
//-----------------------------------------------------------------------------

//-- Atan ----------------------------------------------------------- 050926 --
//
//  analytic inverse tangent function
//
inline Interval re_Atan_point( const double& x, const Interval& hx, 
       const double& y, const Interval& hy ) //---------------------- 050926 --
//
//  real part of Atan at a point in the upper half plane
//
//  Re( Atan z ) = - Arg( 1-x^2-y^2 - 2ix ) / 2
//               = Arg( (1-x)(1+x)-y^2 + 2ix ) / 2   //  if cancellation
//               = Arg( (1-y)(1+y)-x^2 + 2ix ) / 2   //  if cancellation
//               = Arg( (1-|z|) + 2ix/(1+|z|) ) / 2  //  if over/underflow
//
{
  Interval abs_z;

  abs_z = CoStLy::abs( CInterval( hx, hy ) );
  if( sup( abs( 1.0 - abs_z ) ) < 1.0 )
    //  abs_z near 1: cancellation
    {
      if( std::abs( x ) > y )
	return 0.5*CoStLy::Arg( CInterval((1.0-hx)*(1.0+hx)-sqr(hy), 2.0*hx) );
      else
	return 0.5*CoStLy::Arg( CInterval((1.0-hy)*(1.0+hy)-sqr(hx), 2.0*hx) );
    }
  else
    //  potential underflow in imaginary part of 2*x/(1+|z|)
    //  singularities have been checked before: 
    //  using arg instead of Arg is safe (and required) here
    return 0.5 * CoStLy::arg( CInterval( 1.0-abs_z, 2.0*(x/(1.0+abs_z)) ) );
}

inline Interval im_Atan_point( const double& x, const Interval& hx, 
       const double& y, const Interval& hy ) //---------------------- 050926 --
//
//  imaginary part of Atan at a point in quadrant I
//
//  Im( Atan z ) = Ln( ( x^2+(y+1)^2 ) / ( x^2+(y-1)^2 )  ) / 4
//               = Ln( ( x^2+y^2+1 + 2*y ) / ( x^2+y^2+1 - 2*y ) ) / 4
//               = atanh( 2*y / ( x^2+y^2+1 ) ) / 2
//
{
  if( x >= 1.7320508075688772935 
      or y <= 0.2679491924311227065 or y >= 3.7320508075688772935 )
    //  argument of atan is <= 0.5: well-conditioned case
    {
     if( y == 0.0 )
	return ZERO_INTERVAL();
      else if( x <= y )
	return 0.5 * atanh( 2.0 / ( hx * ( hx / hy ) + hy + 1.0 / hy ) );
      else 
	//  underflow is safe here
	return 0.5 * atanh( 2.0 * ( hy/hx ) / ( hx + hy*(hy/hx) + 1.0 / hx ) );
    }
  else
    //  argument of ln is <= ( 7-2*sqrt(3) )/ ( 15-6*sqrt(3) ) < 0.77
    //  
    {
      if( y == 1.0 )
	//  special treatment, caused by potential underflow in sqr( hx )
	return 0.25 * ( ln( sqr( hx ) + 4.0 ) - 2.0 * ln( hx ) );
      else if( y == -1.0 )
	//  special treatment, caused by potential underflow in sqr( hx )
	return 0.25 * ( 2.0 * ln( hx ) - ln( sqr( hx ) + 4.0 ) );
      else
	//  potential underflow in sqr( hx ) is safe
	return - 0.25 * ln( ( sqr( hx ) + sqr( hy - 1.0 ) ) /
			    ( sqr( hx ) + sqr( hy + 1.0 ) ) );
    }
}

inline Interval im_Atan_extr( const double& x, const Interval& hx, 
			      const Interval& y_extr) //------------- 050925 --
//
//  imaginary part of Atan on the extremal curve y^2 = x^2+1 in quadrant I
//
//  Im( Atan z ) = Ln( ( x^2+(y+1)^2 ) / ( x^2+(y-1)^2 )  ) / 4
//               = Ln( ( y+1 ) / ( y-1 ) ) / 4
//               = acoth( y ) / 2
//
{
  if( x >= 1.7320508075688772935 )
    //  argument of atan is <= 0.5: well-conditioned case
    return 0.5 * acoth( y_extr );
  else 
    return 0.5 * ( ln( 1.0 + y_extr ) - ln( hx ) );
}

Interval re_Atan( const Interval& rez, const Interval& imz, 
		  const double& irez, const double& srez ) //-------- 050925 --
//
//  real part of Atan( z ),  z = x + iy,  y >= 0
//
{
  double 
    iimz = inf( imz ),
    simz = sup( imz ),
    resl, resu;

  Interval 
    hxl(irez), hxu(srez), hyl(iimz), hyu(simz);

  //  check intersection of lower boundary with extremal curve of Re( Atan )
  Interval x_extr;
  if( iimz > 1.0 )
    //  potential intersection with extremal curve
    {
      if( iimz > 1.0E+9 )
	//  potential overflow in sqrt( y^2 - 1.0 )
	x_extr = hyl * ( 1.0 - IEPS() );
      else if( iimz > 2.0 )
	//  evaluation without overflow nor cancellation
	x_extr = sqrt( sqr( hyl ) - 1.0 );
      else
	//  potential cancellation
	x_extr = sqrt( (hyl-1.0)*(hyl+1.0) );
    }
  else
    //  dummy value for no intersection
    x_extr = -MAXREAL();

  if( sup( x_extr ) > irez and inf( x_extr ) < srez )
    //  extremal curve intersects lower boundary in quadrant I
    //  min on intersection, max at upper left or right corner
    {
      resl = inf( 0.5 * ( PI() - atan( 1.0 / x_extr ) ) );
      resu = max( sup( re_Atan_point( irez, hxl, simz, hyu ) ),
		  sup( re_Atan_point( srez, hxu, simz, hyu ) ) );
    }
  else if( - inf( x_extr ) > irez and - sup( x_extr ) < srez )
    //  extremal curve intersects lower boundary in quadrant II
    //  max on intersection, min at lower left or right corner
    {
      resu = sup( 0.5 * ( atan( 1.0 / x_extr ) - PI() ) );
      resl = min( inf( re_Atan_point( irez, hxl, iimz, hyl ) ),
		  inf( re_Atan_point( srez, hxu, iimz, hyl ) ) );
    }
  else
    //  no intersection with extremal curve
    {
      if( irez >= 0.0 )
	{
	  //  z in quadrant I
	  //  max at upper left or right corner, 
	  //  min at lower left or right corner
	  resl = min( inf( re_Atan_point( irez, hxl, iimz, hyl ) ),
		      inf( re_Atan_point( srez, hxu, iimz, hyl ) ) );
	  resu = max( sup( re_Atan_point( irez, hxl, simz, hyu ) ),
		      sup( re_Atan_point( srez, hxu, simz, hyu ) ) );
	}
      else if( srez <= 0.0 )
	{
	  //  z in quadrant II
	  //  min at upper left or right corner, 
	  //  max at lower left or right corner
	  resl = min( inf( re_Atan_point( irez, hxl, simz, hyu ) ),
		      inf( re_Atan_point( srez, hxu, simz, hyu ) ) );
	  resu = max( sup( re_Atan_point( irez, hxl, iimz, hyl ) ),
		      sup( re_Atan_point( srez, hxu, iimz, hyl ) ) );
	}
      else
	{
	  //  z intersects y-axis
	  //  min at upper left corner, max at upper right corner
	  resl = inf( re_Atan_point( irez, hxl, simz, hyu ) );
	  resu = sup( re_Atan_point( srez, hxu, simz, hyu ) );
	}
    }
  return Interval( resl, resu );
}

Interval im_Atan( const Interval& rez, const Interval& imz, 
		  const double& iimz, const double& simz ) //-------- 050925 --
//
//  imaginary part of Atan( z ),  z = x + iy,  x >= 0
//  due to previous reflection, most of z lies in upper half plane
//
{
  double 
    irez = inf( rez ),
    srez = sup( rez ),
    resl, resu;

  Interval 
    hxl(irez), hxu(srez), hyl(iimz), hyu(simz),
    y_extr, im_atan;

  //  check intersection of left boundary with extremal curves of Im( Atan )
  if( irez > 1.0E+9 )
    //  potential overflow
    y_extr = hxl * ( 1.0 + IEPS() );
  else if( irez > 1.0E-9 )
    y_extr = sqrt( 1.0 + sqr( hxl ) );
  else
    //  underflow/cancellation
    y_extr = sqrt( 1.0 + IEPS() );

  if( iimz < 0 )
    //  z intersects positive real axis
    //  max at upper left corner or on intersection with extremal curve
    //  min at lower left corner or on intersection with extremal curve
    {
      if( inf( y_extr ) < -iimz )
	//  intersection with extremal curve in quadrant IV (min)
	//  due to previous reflection, also intersection quadrant I (max)
	{
	  resu = sup( im_Atan_extr( irez, hxl, y_extr ) );
	  resl = - resu;
	}
      else if( inf( y_extr ) < simz )
	//  intersection with extremal curve in quadrant I (max)
	//  min at lower left corner
	{
	  resu = sup( im_Atan_extr( irez, hxl, y_extr ) );
	  resl = -sup( im_Atan_point( irez, hxl, -iimz, -hyl ) );
	}
      else 
	//  no intersection with extremal curve
	//  max at upper left corner, min at lower left corner
	{
	  resu = sup( im_Atan_point( irez, hxl, simz, hyu ) );
	  resl = -sup( im_Atan_point( irez, hxl, -iimz, -hyl ) );
	}
    }
  else
    //  z in quadrant I
    {
      //  min at lower or upper right corner
      resl = min( inf( im_Atan_point( srez, hxu, iimz, hyl ) ),
		  inf( im_Atan_point( srez, hxu, simz, hyu ) ) );
      //  max on intersection with extremal curve or at l or u left corner
      if( inf( y_extr ) < simz and sup( y_extr ) > iimz )
	resu = sup( im_Atan_extr( irez, hxl, y_extr ) );
      else
	resu = max( sup( im_Atan_point( irez, hxl, iimz, hyl ) ),
		    sup( im_Atan_point( irez, hxl, simz, hyu ) ) );
    }
  return Interval( resl, resu );
}

CInterval CoStLy::Atan( const CInterval& z ) //---------------------- 050923 --
{
  Interval
    rez = z.re(),
    imz = z.im();

  double 
    irez = inf( rez ),
    srez = sup( rez ),
    iimz = inf( imz ),
    simz = sup( imz );

  //
  //  check for singularities
  //
  if( ( irez <= 0.0 and srez >= 0.0 ) and ( iimz <= -1.0 or simz >= 1.0 ) ) 
    err_msg( "Atan", z );

  if( simz >= -iimz )
    return CInterval( re_Atan( rez, abs( imz ), irez, srez ), 
		      im_Atan( abs( rez ), imz, iimz, simz ) );
  else
    //  most of z lies in lower half plane
    return CInterval( re_Atan( rez, abs( imz ), irez, srez ), 
		      -im_Atan( abs( rez ), -imz, -simz, -iimz ) );
}
//
//-- end Atan -----------------------------------------------------------------

//-- Acot ----------------------------------------------------------- 050926 --
//
//  analytic inverse cotangent function
//
//  this function is derived from Atan
//
//  for the real part, we have
//
//      Re( Acot( z ) ) = Re( Atan( 1/z ) ) = Re( Atan( (x-iy)/|z|^2 ) )
//                      = Re( Atan( (x+iy)/|z|^2 ) ) = Re( Atan( z/|z|^2 ) ) 
//
//  replacing (x,y) by (x/(x^2+y^2),y/(x^2+y^2)), we obtain
//
//      Arg( x^2+y^2-1-2ix ) instead of Arg( 1-x^2-y^2-2ix ).
//
//  for the imaginary part, it's simpler:  Im( Acot(z) ) = - Im( Atan(z) )
//
inline Interval re_Acot_point( const double& x, const Interval& hx, 
       const double& y, const Interval& hy ) //---------------------- 050926 --
//
//  real part of Acot at a point in the upper half plane
//
//  Re( Atan z ) = - Arg( x^2+y^2-1 - 2ix ) / 2
//               = Arg( (x-1)(x+1)+y^2 + 2ix ) / 2   //  if cancellation
//               = Arg( (y-1)(y+1)+x^2 + 2ix ) / 2   //  if cancellation
//               = Arg( (|z|-1) + 2ix/(1+|z|) ) / 2  //  if over/underflow
//
{
  Interval abs_z;

  abs_z = CoStLy::abs( CInterval( hx, hy ) );
  if( sup( abs( 1.0 - abs_z ) ) < 1.0 )
    //  abs_z near 1: cancellation
    {
      if( std::abs( x ) > y )
	return 0.5*CoStLy::Arg( CInterval((hx-1.0)*(1.0+hx)+sqr(hy), 2.0*hx) );
      else
	return 0.5*CoStLy::Arg( CInterval((hy-1.0)*(hy+1.0)+sqr(hx), 2.0*hx) );
    }
  else
    //  potential underflow in imaginary part of 2*x/(1+|z|)
    //  singularities have been checked before: 
    //  using arg instead of Arg is safe (and required) here
    return 0.5 * CoStLy::arg( CInterval( abs_z-1.0, 2.0*(x/(1.0+abs_z)) ) );
}

Interval re_Acot( const Interval& rez, const Interval& imz, 
		  const double& irez, const double& srez ) //-------- 050926 --
//
//  real part of Acot( z ),  z = x + iy,  y >= 0
//
{
  double 
    iimz = inf( imz ),
    simz = sup( imz ),
    resl, resu;

  Interval 
    hxl(irez), hxu(srez), hyl(iimz), hyu(simz);

  //  check intersection of lower boundary with extremal curve of Re( Atan )
  Interval x_extr;
  if( iimz > 1.0 )
    //  potential intersection with extremal curve
    {
      if( iimz > 1.0E+9 )
	//  potential overflow in sqrt( y^2 - 1.0 )
	x_extr = hyl * ( 1.0 - IEPS() );
      else if( iimz > 2.0 )
	//  evaluation without overflow nor cancellation
	x_extr = sqrt( sqr( hyl ) - 1.0 );
      else
	//  potential cancellation
	x_extr = sqrt( (hyl-1.0)*(hyl+1.0) );
    }
  else
    //  dummy value for no intersection
    x_extr = -MAXREAL();

  if( sup( x_extr ) > irez and inf( x_extr ) < srez )
    {
      //  extremal curve intersects lower boundary in quadrant I
      //  max on intersection
      resu = sup( 0.5 * atan( 1.0 / x_extr ) );
      if( - inf( x_extr ) > irez and - sup( x_extr ) < srez )
	//  extremal curve also intersects lower boundary in quadrant II
	//  min on intersection
	resl = inf( - 0.5 * atan( 1.0 / x_extr ) );
      else
	//  min at upper left or right corner
	resl = min( inf( re_Acot_point( irez, hxl, simz, hyu ) ),
		    inf( re_Acot_point( srez, hxu, simz, hyu ) ) );
    }
  else if( - inf( x_extr ) > irez and - sup( x_extr ) < srez )
    //  extremal curve intersects lower boundary in quadrant II
    //  min on intersection, max at upper left or right corner
    {
      resl = inf( - 0.5 * atan( 1.0 / x_extr ) );
      resu = max( sup( re_Acot_point( irez, hxl, simz, hyu ) ),
		  sup( re_Acot_point( srez, hxu, simz, hyu ) ) );
    }
  else
    //  no intersection with extremal curve
    {
      if( irez >= 0.0 )
	{
	  //  z in quadrant I
	  //  min at upper left or right corner, 
	  //  max at lower left or right corner
	  resl = min( inf( re_Acot_point( irez, hxl, simz, hyu ) ),
		      inf( re_Acot_point( srez, hxu, simz, hyu ) ) );
	  resu = max( sup( re_Acot_point( irez, hxl, iimz, hyl ) ),
		      sup( re_Acot_point( srez, hxu, iimz, hyl ) ) );
	}
      else if( srez <= 0.0 )
	{
	  //  z in quadrant II
	  //  min at lower left or right corner
	  //  max at upper left or right corner, 
	  resl = min( inf( re_Acot_point( irez, hxl, iimz, hyl ) ),
		      inf( re_Acot_point( srez, hxu, iimz, hyl ) ) );
	  resu = max( sup( re_Acot_point( irez, hxl, simz, hyu ) ),
		      sup( re_Acot_point( srez, hxu, simz, hyu ) ) );
	}
      else
	{
	  //  z intersects y-axis
	  //  max at upper left corner, min at upper right corner
	  resl = inf( re_Acot_point( srez, hxu, simz, hyu ) );
	  resu = sup( re_Acot_point( irez, hxl, simz, hyu ) );
	}
    }
  return Interval( resl, resu );
}

CInterval CoStLy::Acot( const CInterval& z ) //---------------------- 050926 --
{
  Interval
    rez = z.re(),
    imz = z.im();

  double 
    irez = inf( rez ),
    srez = sup( rez ),
    iimz = inf( imz ),
    simz = sup( imz );

  //
  //  check for singularities
  //
  if( ( irez <= 0.0 and srez >= 0.0 ) and ( iimz <= 1.0 and simz >= -1.0 ) ) 
    err_msg( "Acot", z );

  if( simz >= -iimz )
    return CInterval( re_Acot( rez, abs( imz ), irez, srez ), 
		      -im_Atan( abs( rez ), imz, iimz, simz ) );
  else
    //  most of z lies in lower half plane
    return CInterval( re_Acot( rez, abs( imz ), irez, srez ), 
		      im_Atan( abs( rez ), -imz, -simz, -iimz ) );
}
//
//-- end Acot -----------------------------------------------------------------

//-- Atanh ---------------------------------------------------------- 040912 --
//
CInterval CoStLy::Atanh( const CInterval& z )
//
//  Atanh( z ) = - i * Atan( i * z )
//
{
  CInterval res = CoStLy::Atan( CInterval( - z.im(), z.re() ) );
  return CInterval( res.im(), - res.re() );
}
//
//-- end Atanh ----------------------------------------------------------------

//-- Acoth ---------------------------------------------------------- 040912 --
//
CInterval CoStLy::Acoth( const CInterval& z ) 
//
//  Acoth( z ) = i * Acot( i * z )
//
{
  CInterval res = CoStLy::Acot(  CInterval( - z.im(), z.re() ) );
  return CInterval( - res.im(), res.re() );
}
//
//-- end Acoth ----------------------------------------------------------------

/*

  End of File: cimath.cpp

*/
