// Excerpts from:
// doubledouble.cc
// Excerpted and modified by Steve Vavasis, 2/00 for use in QMG 2.0.
// doubledouble package is originally written by Keith Briggs.  
// These excerpts are covered by the
// QMG license, with permission from Keith Briggs.  
// The entire doubledouble package is available on line at
//   http://www-epidem.plantsci.cam.ac.uk/~kbriggs/doubledouble.html
// and is covered by the Gnu Public License.


// KMB 98 Jan 19
// Keith Briggs.   Last revised 98 Feb 09


/*
Copyright (C) 1997 Keith Martin Briggs

Except where otherwise indicated,
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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

#include "doubledouble2.h"


// member constants
const double doubledouble::Split = 134217729.0L; // 2^27+1, for IEEE double


// Square  (faster than x*x)
doubledouble sqr(const doubledouble& x) {
  double hx,tx,C,c;
  C=doubledouble::Split*x.h(); hx=C-x.h(); hx=C-hx; tx=x.h()-hx;
  C=x.h()*x.h();
  c=((((hx*hx-C)+2.0*hx*tx))+tx*tx)+2.0*x.h()*x.l();
  hx=C+c;
  doubledouble r=doubledouble(hx,c+(C-hx));
  return r;
}

// Added by Steve Vavasis
#ifdef _MSC_VER
#pragma optimize("g", off)
#endif

// square root
doubledouble sqrt(const doubledouble& y) {
  double c,p,q,hx,tx,u,uu,cc,hi=y.h();
  if (hi<0.0) {
    doubledouble::Domain_Error d;
    throw d;
  }
  if (0.0==hi) return y;
  c=sqrt(hi);
  p=doubledouble::Split*c; hx=c-p; hx+=p; tx=c-hx;
  p=hx*hx;
  q=2.0*hx*tx;
  u=p+q;
  uu=(p-u)+q+tx*tx;
  cc=(((y.h()-u)-uu)+y.l())/(c+c);
  u=c+cc;
  doubledouble r=doubledouble(u,cc+(c-u));
  return r;
}

#ifdef _MSC_VER
#pragma optimize("g", on)
#define x86
#endif



doubledouble::FloatControlType doubledouble::set_float_control_to_53_bits() {

#ifdef x86
  volatile FloatControlType old_cw;
#ifdef _MSC_VER
  __asm fnstcw old_cw 
    ;
#else
#ifdef __GNUC__
  asm volatile ("fnstcw %0":"=m" (old_cw)); 
#else
  ERROR_MESSAGE("COMPILER UNKNOWN -- NOT KNOWN HOW TO INSTANTIATE FNSTCW ASSEMBLER INSTRUCTION FOR X86");
#endif
#endif
  volatile FloatControlType new_cw = (old_cw & ~0x300) | 0x200;
  
#ifdef _MSC_VER
  __asm fldcw new_cw
    ;
#else
#ifdef __GNUC__
  asm volatile ("fldcw %0": :"m" (new_cw));
#else
  ERROR_MESSAGE("COMPILER UNKNOWN -- NOT KNOWN HOW TO INSTANTIATE FLDCW ASSEMBLER INSTRUCTION FOR X86");
#endif
#endif
  
  return old_cw;
#else  // not X86
  return 0;
#endif
}

void doubledouble::restore_float_control(FloatControlType w) {
#ifdef x86
#ifdef _MSC_VER
  __asm fldcw w
    ;
#else
#ifdef __GNUC__
  asm volatile ("fldcw %0": :"m" (w));
#else
  ERROR_MESSAGE("COMPILER UNKNOWN -- NOT KNOWN HOW TO INSTANTIATE FLDCW ASSEMBLER INSTRUCTION FOR X86");
#endif
#endif
#endif
}
