/**
 This file is part of Kig, a KDE program for Interactive Geometry...
 Copyright (C) 2002  Maurizio Paolini <paolini@dmf.unicatt.it>

 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
 USA
**/

#include "conic-common.h"

#include <math.h>

#include "common.h"
#include "kigtransform.h"

#include <cmath>
#include <algorithm>

#include <config-kig.h>

#ifdef HAVE_IEEEFP_H
#include <ieeefp.h>
#endif

ConicCartesianData::ConicCartesianData(
  const ConicPolarData& polardata
  )
{
  double ec = polardata.ecostheta0;
  double es = polardata.esintheta0;
  double p = polardata.pdimen;
  double fx = polardata.focus1.x;
  double fy = polardata.focus1.y;

  double a = 1 - ec*ec;
  double b = 1 - es*es;
  double c = - 2*ec*es;
  double d = - 2*p*ec;
  double e = - 2*p*es;
  double f = - p*p;

  f += a*fx*fx + b*fy*fy + c*fx*fy - d*fx - e*fy;
  d -= 2*a*fx + c*fy;
  e -= 2*b*fy + c*fx;

  coeffs[0] = a;
  coeffs[1] = b;
  coeffs[2] = c;
  coeffs[3] = d;
  coeffs[4] = e;
  coeffs[5] = f;
}

ConicPolarData::ConicPolarData( const ConicCartesianData& cartdata )
{
  double a = cartdata.coeffs[0];
  double b = cartdata.coeffs[1];
  double c = cartdata.coeffs[2];
  double d = cartdata.coeffs[3];
  double e = cartdata.coeffs[4];
  double f = cartdata.coeffs[5];

  // 1. Compute theta (tilt of conic)
  double theta = std::atan2(c, b - a)/2;

  // now I should possibly add pi/2...
  double costheta = std::cos(theta);
  double sintheta = std::sin(theta);
  // compute new coefficients (c should now be zero)
  double aa = a*costheta*costheta + b*sintheta*sintheta - c*sintheta*costheta;
  double bb = a*sintheta*sintheta + b*costheta*costheta + c*sintheta*costheta;
  if (aa*bb < 0)
  {   // we have a hyperbola we need to check the correct orientation
    double dd = d*costheta - e*sintheta;
    double ee = d*sintheta + e*costheta;
    double xc = - dd / ( 2*aa );
    double yc = - ee / ( 2*bb );
    double ff = f + aa*xc*xc + bb*yc*yc + dd*xc + ee*yc;
    if (ff*aa > 0)    // wrong orientation
    {
      if (theta > 0) theta -= M_PI/2;
      else theta += M_PI/2;
      costheta = cos(theta);
      sintheta = sin(theta);
      aa = a*costheta*costheta + b*sintheta*sintheta - c*sintheta*costheta;
      bb = a*sintheta*sintheta + b*costheta*costheta + c*sintheta*costheta;
    }
  }
  else
  {
    if ( std::fabs (bb) < std::fabs (aa) )
    {
      if (theta > 0) theta -= M_PI/2;
      else theta += M_PI/2;
      costheta = cos(theta);
      sintheta = sin(theta);
      aa = a*costheta*costheta + b*sintheta*sintheta - c*sintheta*costheta;
      bb = a*sintheta*sintheta + b*costheta*costheta + c*sintheta*costheta;
    }
  }

  double cc = 2*(a - b)*sintheta*costheta +
              c*(costheta*costheta - sintheta*sintheta);
  //  cc should be zero!!!   cout << "cc = " << cc << "\n";
  double dd = d*costheta - e*sintheta;
  double ee = d*sintheta + e*costheta;

  a = aa;
  b = bb;
  c = cc;
  d = dd;
  e = ee;

  // now b cannot be zero (otherwise conic is degenerate)
  a /= b;
  c /= b;
  d /= b;
  e /= b;
  f /= b;
  b = 1.0;

  // 2. compute y coordinate of focuses

  double yf = - e/2;

  // new values:
  f += yf*yf + e*yf;
  e += 2*yf;   // this should be zero!

  // now: a > 0 -> ellipse
  //      a = 0 -> parabola
  //      a < 0 -> hyperbola

  double eccentricity = sqrt(1.0 - a);

  double sqrtdiscrim = sqrt(d*d - 4*a*f);
  if (d < 0.0) sqrtdiscrim = -sqrtdiscrim;
  double xf = (4*a*f - 4*f - d*d)/(d + eccentricity*sqrtdiscrim) / 2;

  // 3. the focus needs to be rotated back into position
  focus1.x = xf*costheta + yf*sintheta;
  focus1.y = -xf*sintheta + yf*costheta;

  // 4. final touch: the pdimen
  pdimen = -sqrtdiscrim/2;

  ecostheta0 = eccentricity*costheta;
  esintheta0 = -eccentricity*sintheta;
  if ( pdimen < 0)
  {
    pdimen = -pdimen;
    ecostheta0 = -ecostheta0;
    esintheta0 = -esintheta0;
  }
}

const ConicCartesianData calcConicThroughPoints (
  const std::vector<Coordinate>& points,
  const LinearConstraints c1,
  const LinearConstraints c2,
  const LinearConstraints c3,
  const LinearConstraints c4,
  const LinearConstraints c5 )
{
  assert( 0 < points.size() && points.size() <= 5 );
  // points is a vector of up to 5 points through which the conic is
  // constrained.
  // this routine should compute the coefficients in the cartesian equation
  //    a x^2 + b y^2 + c xy + d x + e y + f = 0
  // they are defined up to a multiplicative factor.
  // since we don't know (in advance) which one of them is nonzero, we
  // simply keep all 6 parameters, obtaining a 5x6 linear system which
  // we solve using gaussian elimination with complete pivoting
  // If there are too few, then we choose some cool way to fill in the
  // empty parts in the matrix according to the LinearConstraints
  // given..

  // 5 rows, 6 columns..
  double row0[6];
  double row1[6];
  double row2[6];
  double row3[6];
  double row4[6];
  double *matrix[5] = {row0, row1, row2, row3, row4};
  double solution[6];
  int scambio[6];
  LinearConstraints constraints[] = {c1, c2, c3, c4, c5};

  int numpoints = points.size();
  int numconstraints = 5;

  // fill in the matrix elements
  for ( int i = 0; i < numpoints; ++i )
  {
    double xi = points[i].x;
    double yi = points[i].y;
    matrix[i][0] = xi*xi;
    matrix[i][1] = yi*yi;
    matrix[i][2] = xi*yi;
    matrix[i][3] = xi;
    matrix[i][4] = yi;
    matrix[i][5] = 1.0;
  }

  for ( int i = 0; i < numconstraints; i++ )
  {
    if (numpoints >= 5) break;    // don't add constraints if we have enough
    for (int j = 0; j < 6; ++j) matrix[numpoints][j] = 0.0;
    // force the symmetry axes to be
    // parallel to the coordinate system (zero tilt): c = 0
    if (constraints[i] == zerotilt) matrix[numpoints][2] = 1.0;
    // force a parabola (if zerotilt): b = 0
    if (constraints[i] == parabolaifzt) matrix[numpoints][1] = 1.0;
    // force a circle (if zerotilt): a = b
    if (constraints[i] == circleifzt) {
      matrix[numpoints][0] = 1.0;
      matrix[numpoints][1] = -1.0; }
    // force an equilateral hyperbola: a + b = 0
    if (constraints[i] == equilateral) {
      matrix[numpoints][0] = 1.0;
      matrix[numpoints][1] = 1.0; }
    // force symmetry about y-axis: d = 0
    if (constraints[i] == ysymmetry) matrix[numpoints][3] = 1.0;
    // force symmetry about x-axis: e = 0
    if (constraints[i] == xsymmetry) matrix[numpoints][4] = 1.0;

    if (constraints[i] != noconstraint) ++numpoints;
  }

  if ( ! GaussianElimination( matrix, numpoints, 6, scambio ) )
    return ConicCartesianData::invalidData();
  // fine della fase di eliminazione
  BackwardSubstitution( matrix, numpoints, 6, scambio, solution );

  // now solution should contain the correct coefficients..
  return ConicCartesianData( solution );
}

const ConicPolarData calcConicBFFP(
  const std::vector<Coordinate>& args,
  int type )
{
  assert( args.size() >= 2 && args.size() <= 3 );
  assert( type == 1 || type == -1 );

  ConicPolarData ret;

  Coordinate f1 = args[0];
  Coordinate f2 = args[1];
  Coordinate d;
  double eccentricity, d1, d2, dl;

  Coordinate f2f1 = f2 - f1;
  double f2f1l = f2f1.length();
  ret.ecostheta0 = f2f1.x / f2f1l;
  ret.esintheta0 = f2f1.y / f2f1l;

  if ( args.size() == 3 )
  {
    d = args[2];
    d1 = ( d - f1 ).length();
    d2 = ( d - f2 ).length();
    dl = fabs( d1 + type * d2 );
    eccentricity = f2f1l/dl;
  }
  else
  {
    if ( type > 0 ) eccentricity = 0.7; else eccentricity = 2.0;
    dl = f2f1l/eccentricity;
  }

  double rhomax = (dl + f2f1l) /2.0;

  ret.ecostheta0 *= eccentricity;
  ret.esintheta0 *= eccentricity;
  ret.pdimen = type*(1 - eccentricity)*rhomax;
  ret.focus1 = type == 1 ? f1 : f2;
  return ret;
}

const LineData calcConicPolarLine (
  const ConicCartesianData& data,
  const Coordinate& cpole,
  bool& valid )
{
  double x = cpole.x;
  double y = cpole.y;
  double a = data.coeffs[0];
  double b = data.coeffs[1];
  double c = data.coeffs[2];
  double d = data.coeffs[3];
  double e = data.coeffs[4];
  double f = data.coeffs[5];

  double alpha = 2*a*x + c*y + d;
  double beta = c*x + 2*b*y + e;
  double gamma = d*x + e*y + 2*f;

  double normsq = alpha*alpha + beta*beta;

  if (normsq < 1e-10)          // line at infinity
  {
    valid = false;
    return LineData();
  }
  valid = true;

  Coordinate reta = -gamma/normsq * Coordinate (alpha, beta);
  Coordinate retb = reta + Coordinate (-beta, alpha);
  return LineData( reta, retb );
}

const Coordinate calcConicPolarPoint (
  const ConicCartesianData& data,
  const LineData& polar )
{
  Coordinate p1 = polar.a;
  Coordinate p2 = polar.b;

  double alpha = p2.y - p1.y;
  double beta = p1.x - p2.x;
  double gamma = p1.y*p2.x - p1.x*p2.y;

  double a11 = data.coeffs[0];
  double a22 = data.coeffs[1];
  double a12 = data.coeffs[2]/2.0;
  double a13 = data.coeffs[3]/2.0;
  double a23 = data.coeffs[4]/2.0;
  double a33 = data.coeffs[5];

//  double detA = a11*a22*a33 - a11*a23*a23 - a22*a13*a13 - a33*a12*a12 +
//                2*a12*a23*a13;

  double a11inv = a22*a33 - a23*a23;
  double a22inv = a11*a33 - a13*a13;
  double a33inv = a11*a22 - a12*a12;
  double a12inv = a23*a13 - a12*a33;
  double a23inv = a12*a13 - a23*a11;
  double a13inv = a12*a23 - a13*a22;

  double x = a11inv*alpha + a12inv*beta + a13inv*gamma;
  double y = a12inv*alpha + a22inv*beta + a23inv*gamma;
  double z = a13inv*alpha + a23inv*beta + a33inv*gamma;

  if (fabs(z) < 1e-10)          // point at infinity
  {
    return Coordinate::invalidCoord();
  }

  x /= z;
  y /= z;
  return Coordinate (x, y);
}

const Coordinate calcConicLineIntersect( const ConicCartesianData& c,
                                         const LineData& l,
					 double knownparam,
                                         int which )
{
  assert( which == 1 || which == -1 || which == 0 );

  double aa = c.coeffs[0];
  double bb = c.coeffs[1];
  double cc = c.coeffs[2];
  double dd = c.coeffs[3];
  double ee = c.coeffs[4];
  double ff = c.coeffs[5];

  double x = l.a.x;
  double y = l.a.y;
  double dx = l.b.x - l.a.x;
  double dy = l.b.y - l.a.y;

  double aaa = aa*dx*dx + bb*dy*dy + cc*dx*dy;
  double bbb = 2*aa*x*dx + 2*bb*y*dy + cc*x*dy + cc*y*dx + dd*dx + ee*dy;
  double ccc = aa*x*x + bb*y*y + cc*x*y + dd*x + ee*y + ff;

  double t;
  if ( which == 0 )  /* i.e. we have a known intersection */
  {
    t = - bbb/aaa - knownparam;
    return l.a + t*(l.b - l.a);
  }

  double discrim = bbb*bbb - 4*aaa*ccc;
  if (discrim < 0.0)
  {
    return Coordinate::invalidCoord();
  }
  else
  {
    if ( which*bbb > 0 )
    {
      t = bbb + which*sqrt(discrim);
      t = - 2*ccc/t;
    } else {
      t = -bbb + which*sqrt(discrim);
      t /= 2*aaa;
      /* mp: this threshold test for a point at infinity allows to
       * solve Bug https://bugs.kde.org/show_bug.cgi?id=316693
       */
      if (fabs(t) > 1e15) return Coordinate::invalidCoord();
    }

    return l.a + t*(l.b - l.a);
  }
}

ConicPolarData::ConicPolarData(
  const Coordinate& f, double d,
  double ec, double es )
  : focus1( f ), pdimen( d ), ecostheta0( ec ), esintheta0( es )
{
}

ConicPolarData::ConicPolarData()
  : focus1(), pdimen( 0 ), ecostheta0( 0 ), esintheta0( 0 )
{
}

const ConicPolarData calcConicBDFP(
  const LineData& directrix,
  const Coordinate& cfocus,
  const Coordinate& cpoint )
{
  ConicPolarData ret;

  Coordinate ba = directrix.dir();
  double bal = ba.length();
  ret.ecostheta0 = -ba.y / bal;
  ret.esintheta0 = ba.x / bal;

  Coordinate pa = cpoint - directrix.a;

  double distpf = (cpoint - cfocus).length();
  double distpd = ( pa.y*ba.x - pa.x*ba.y)/bal;

  double eccentricity = distpf/distpd;
  ret.ecostheta0 *= eccentricity;
  ret.esintheta0 *= eccentricity;

  Coordinate fa = cfocus - directrix.a;
  ret.pdimen = ( fa.y*ba.x - fa.x*ba.y )/bal;
  ret.pdimen *= eccentricity;
  ret.focus1 = cfocus;

  return ret;
}

ConicCartesianData::ConicCartesianData( const double incoeffs[6] )
{
  std::copy( incoeffs, incoeffs + 6, coeffs );
}

const LineData calcConicAsymptote(
  const ConicCartesianData &data,
  int which, bool &valid )
{
  assert( which == -1 || which == 1 );

  LineData ret;
  double a=data.coeffs[0];
  double b=data.coeffs[1];
  double c=data.coeffs[2];
  double d=data.coeffs[3];
  double e=data.coeffs[4];

  double normabc = a*a + b*b + c*c;
  double delta = c*c - 4*a*b;
  if (fabs(delta) < 1e-6*normabc) { valid = false; return ret; }

  double yc = (2*a*e - c*d)/delta;
  double xc = (2*b*d - c*e)/delta;
  // let c be nonnegative; we no longer need d, e, f.
  if (c < 0)
  {
    c *= -1;
    a *= -1;
    b *= -1;
  }

  if ( delta < 0 )
  {
    valid = false;
    return ret;
  }

  double sqrtdelta = sqrt(delta);
  Coordinate displacement;
  if (which > 0)
    displacement = Coordinate(-2*b, c + sqrtdelta);
  else
    displacement = Coordinate(c + sqrtdelta, -2*a);
  ret.a = Coordinate(xc, yc);
  ret.b = ret.a + displacement;
  return ret;
}

const ConicCartesianData calcConicByAsymptotes(
  const LineData& line1,
  const LineData& line2,
  const Coordinate& p )
{
  Coordinate p1 = line1.a;
  Coordinate p2 = line1.b;
  double x = p.x;
  double y = p.y;

  double c1 = p1.x*p2.y - p2.x*p1.y;
  double b1 = p2.x - p1.x;
  double a1 = p1.y - p2.y;

  p1 = line2.a;
  p2 = line2.b;

  double c2 = p1.x*p2.y - p2.x*p1.y;
  double b2 = p2.x - p1.x;
  double a2 = p1.y - p2.y;

  double a = a1*a2;
  double b = b1*b2;
  double c = a1*b2 + a2*b1;
  double d = a1*c2 + a2*c1;
  double e = b1*c2 + c1*b2;

  double f = a*x*x + b*y*y + c*x*y + d*x + e*y;
  f = -f;

  return ConicCartesianData( a, b, c, d, e, f );
}

const LineData calcConicRadical( const ConicCartesianData& cequation1,
                                 const ConicCartesianData& cequation2,
                                 int which, int zeroindex, bool& valid )
{
  assert( which == 1 || which == -1 );
  assert( 0 < zeroindex && zeroindex < 4 );
  LineData ret;
  valid = true;

  double a = cequation1.coeffs[0];
  double b = cequation1.coeffs[1];
  double c = cequation1.coeffs[2];
  double d = cequation1.coeffs[3];
  double e = cequation1.coeffs[4];
  double f = cequation1.coeffs[5];

  double a2 = cequation2.coeffs[0];
  double b2 = cequation2.coeffs[1];
  double c2 = cequation2.coeffs[2];
  double d2 = cequation2.coeffs[3];
  double e2 = cequation2.coeffs[4];
  double f2 = cequation2.coeffs[5];

// background: the family of conics c + lambda*c2 has members that
// degenerate into a union of two lines. The values of lambda giving
// such degenerate conics is the solution of a third degree equation.
// The coefficients of such equation are given by:
// (Thanks to Dominique Devriese for the suggestion of this approach)
// domi: (And thanks to Maurizio for implementing it :)

  double df = 4*a*b*f - a*e*e - b*d*d - c*c*f + c*d*e;
  double cf = 4*a2*b*f + 4*a*b2*f + 4*a*b*f2
     - 2*a*e*e2 - 2*b*d*d2 - 2*f*c*c2
     - a2*e*e - b2*d*d - f2*c*c
     + c2*d*e + c*d2*e + c*d*e2;
  double bf = 4*a*b2*f2 + 4*a2*b*f2 + 4*a2*b2*f
     - 2*a2*e2*e - 2*b2*d2*d - 2*f2*c2*c
     - a*e2*e2 - b*d2*d2 - f*c2*c2
     + c*d2*e2 + c2*d*e2 + c2*d2*e;
  double af = 4*a2*b2*f2 - a2*e2*e2 - b2*d2*d2 - c2*c2*f2 + c2*d2*e2;

// assume both conics are nondegenerate, renormalize so that af = 1

  df /= af;
  cf /= af;
  bf /= af;
  af = 1.0;   // not needed, just for consistency

// computing the coefficients of the Sturm sequence

  double p1a = 2*bf*bf - 6*cf;
  double p1b = bf*cf - 9*df;
  double p0a = cf*p1a*p1a + p1b*(3*p1b - 2*bf*p1a);
  double fval, fpval, lambda;

  if (p0a < 0 && p1a < 0)
  {
    // -+--   ---+
    valid = false;
    return ret;
  }

  lambda = -bf/3.0;    //inflection point
  double displace = 1.0;
  if (p1a > 0)         // with two stationary points
  {
    displace += sqrt(p1a);  // should be enough.  The important
                            // thing is that it is larger than the
                            // semidistance between the stationary points
  }
  // compute the value at the inflection point using Horner scheme
  fval = bf + lambda;              // b + x
  fval = cf + lambda*fval;         // c + xb + xx
  fval = df + lambda*fval;         // d + xc + xxb + xxx

  if (fabs(p0a) < 1e-7)
  {   // this is the case if we intersect two vertical parabolas!
    p0a = 1e-7;  // fall back to the one zero case
  }
  if (p0a < 0)
  {
    // we have three roots..
    // we select the one we want ( defined by mzeroindex.. )
    lambda += ( 2 - zeroindex )* displace;
  }
  else
  {
    // we have just one root
    if( zeroindex > 1 )  // cannot find second and third root
    {
      valid = false;
      return ret;
    }

    if (fval > 0)      // zero on the left
    {
      lambda -= displace;
    } else {           // zero on the right
      lambda += displace;
    }

    // p0a = 0 means we have a root with multiplicity two
  }

//
// find a root of af*lambda^3 + bf*lambda^2 + cf*lambda + df = 0
// (use a Newton method starting from lambda = 0.  Hope...)
//

  double delta;

  int iterations = 0;
  const int maxiterations = 30;
  while (iterations++ < maxiterations)   // using Newton, iterations should be very few
  {
    // compute value of function and polinomial
    fval = fpval = af;
    fval = bf + lambda*fval;         // b + xa
    fpval = fval + lambda*fpval;     // b + 2xa
    fval = cf + lambda*fval;         // c + xb + xxa
    fpval = fval + lambda*fpval;     // c + 2xb + 3xxa
    fval = df + lambda*fval;         // d + xc + xxb + xxxa

    delta = fval/fpval;
    lambda -= delta;
    if (fabs(delta) < 1e-6) break;
  }
  if (iterations >= maxiterations) { valid = false; return ret; }

  // now we have the degenerate conic: a, b, c, d, e, f

  a += lambda*a2;
  b += lambda*b2;
  c += lambda*c2;
  d += lambda*d2;
  e += lambda*e2;
  f += lambda*f2;

  // domi:
  // this is the determinant of the matrix of the new conic.  It
  // should be zero, for the new conic to be degenerate.
  df = 4*a*b*f - a*e*e - b*d*d - c*c*f + c*d*e;

  //lets work in homogeneous coordinates...

  double dis1 = e*e - 4*b*f;
  double maxval = fabs(dis1);
  int maxind = 1;
  double dis2 = d*d - 4*a*f;
  if (fabs(dis2) > maxval)
  {
    maxval = fabs(dis2);
    maxind = 2;
  }
  double dis3 = c*c - 4*a*b;
  if (fabs(dis3) > maxval)
  {
    maxval = fabs(dis3);
    maxind = 3;
  }
  // one of these must be nonzero (otherwise the matrix is ...)
  // exchange elements so that the largest is the determinant of the
  // first 2x2 minor
  double temp;
  switch (maxind)
  {
    case 1:  // exchange 1 <-> 3
    temp = a; a = f; f = temp;
    temp = c; c = e; e = temp;
    temp = dis1; dis1 = dis3; dis3 = temp;
    break;

    case 2:  // exchange 2 <-> 3
    temp = b; b = f; f = temp;
    temp = c; c = d; d = temp;
    temp = dis2; dis2 = dis3; dis3 = temp;
    break;
  }

  // domi:
  // this is the negative of the determinant of the top left of the
  // matrix.  If it is 0, then the conic is a parabola, if it is < 0,
  // then the conic is an ellipse, if positive, the conic is a
  // hyperbola.  In this case, it should be positive, since we have a
  // degenerate conic, which is a degenerate case of a hyperbola..
  // note that it is negative if there is no valid conic to be
  // found ( e.g. not enough intersections.. )
  //  double discrim = c*c - 4*a*b;

  if (dis3 < 0)
  {
    // domi:
    // i would put an assertion here, but well, i guess it doesn't
    // really matter, and this prevents crashes if the math i still
    // recall from high school happens to be wrong :)
    valid = false;
    return ret;
  };

  double r[3];   // direction of the null space
  r[0] = 2*b*d - c*e;
  r[1] = 2*a*e - c*d;
  r[2] = dis3;

  // now remember the switch:
  switch (maxind)
  {
    case 1:  // exchange 1 <-> 3
    temp = a; a = f; f = temp;
    temp = c; c = e; e = temp;
    temp = dis1; dis1 = dis3; dis3 = temp;
    temp = r[0]; r[0] = r[2]; r[2] = temp;
    break;

    case 2:  // exchange 2 <-> 3
    temp = b; b = f; f = temp;
    temp = c; c = d; d = temp;
    temp = dis2; dis2 = dis3; dis3 = temp;
    temp = r[1]; r[1] = r[2]; r[2] = temp;
    break;
  }

  // Computing a Householder reflection transformation that
  // maps r onto [0, 0, k]

  double w[3];
  double rnormsq = r[0]*r[0] + r[1]*r[1] + r[2]*r[2];
  double k = sqrt( rnormsq );
  if ( k*r[2] < 0) k = -k;
  double wnorm = sqrt( 2*rnormsq + 2*k*r[2] );
  w[0] = r[0]/wnorm;
  w[1] = r[1]/wnorm;
  w[2] = (r[2] + k)/wnorm;

  // matrix transformation using Householder matrix, the resulting
  // matrix is zero on third row and column
  // [q0,q1,q2]^t = A w
  // alpha = w^t A w
  double q0 = a*w[0] + c*w[1]/2 + d*w[2]/2;
  double q1 = b*w[1] + c*w[0]/2 + e*w[2]/2;
  double alpha = a*w[0]*w[0] + b*w[1]*w[1] + c*w[0]*w[1] +
                 d*w[0]*w[2] + e*w[1]*w[2] + f*w[2]*w[2];
  double a00 = a - 4*w[0]*q0 + 4*w[0]*w[0]*alpha;
  double a11 = b - 4*w[1]*q1 + 4*w[1]*w[1]*alpha;
  double a01 = c/2 - 2*w[1]*q0 - 2*w[0]*q1 + 4*w[0]*w[1]*alpha;

  double dis = a01*a01 - a00*a11;
  assert ( dis >= 0 );
  double sqrtdis = sqrt( dis );
  double px, py;
  if ( which*a01 > 0 )
  {
    px = a01 + which*sqrtdis;
    py = a11;
  } else {
    px = a00;
    py = a01 - which*sqrtdis;
  }
  double p[3];   // vector orthogonal to one of the two planes
  double pscalw = w[0]*px + w[1]*py;
  p[0] = px - 2*pscalw*w[0];
  p[1] = py - 2*pscalw*w[1];
  p[2] =    - 2*pscalw*w[2];

  // "r" is the solution of the equation A*(x,y,z) = (0,0,0) where
  // A is the matrix of the degenerate conic.  This is what we
  // called in the conic theory we saw in high school a "double
  // point".  It has the unique property that any line going through
  // it is a "polar line" of the conic at hand.  It only exists for
  // degenerate conics.  It has another unique property that if you
  // take any other point on the conic, then the line between it and
  // the double point is part of the conic.
  // this is what we use here: we find the double point ( ret.a
  // ), and then find another points on the conic.

  ret.a = -p[2]/(p[0]*p[0] + p[1]*p[1]) * Coordinate (p[0],p[1]);
  ret.b = ret.a + Coordinate (-p[1],p[0]);
  valid = true;

  return ret;
}

const ConicCartesianData calcConicTransformation (
  const ConicCartesianData& data, const Transformation& t, bool& valid )
{
  double a[3][3];
  double b[3][3];

  a[1][1] = data.coeffs[0];
  a[2][2] = data.coeffs[1];
  a[1][2] = a[2][1] = data.coeffs[2]/2;
  a[0][1] = a[1][0] = data.coeffs[3]/2;
  a[0][2] = a[2][0] = data.coeffs[4]/2;
  a[0][0] = data.coeffs[5];

  Transformation ti = t.inverse( valid );
  if ( ! valid ) return ConicCartesianData();

  double supnorm = 0.0;
  for (int i = 0; i < 3; i++)
  {
    for (int j = 0; j < 3; j++)
    {
      b[i][j] = 0.;
      for (int ii = 0; ii < 3; ii++)
      {
        for (int jj = 0; jj < 3; jj++)
        {
	  b[i][j] += a[ii][jj]*ti.data( ii, i )*ti.data( jj, j );
	}
      }
      if ( std::fabs( b[i][j] ) > supnorm ) supnorm = std::fabs( b[i][j] );
    }
  }

  for (int i = 0; i < 3; i++)
  {
    for (int j = 0; j < 3; j++)
    {
      b[i][j] /= supnorm;
    }
  }

  return ConicCartesianData ( b[1][1], b[2][2], b[1][2] + b[2][1],
                              b[0][1] + b[1][0], b[0][2] + b[2][0], b[0][0] );
}

ConicCartesianData::ConicCartesianData()
{
}

bool operator==( const ConicPolarData& lhs, const ConicPolarData& rhs )
{
  return lhs.focus1 == rhs.focus1 &&
         lhs.pdimen == rhs.pdimen &&
     lhs.ecostheta0 == rhs.ecostheta0 &&
     lhs.esintheta0 == rhs.esintheta0;
}

ConicCartesianData ConicCartesianData::invalidData()
{
  ConicCartesianData ret;
  ret.coeffs[0] = double_inf;
  return ret;
}

bool ConicCartesianData::valid() const
{
  return std::isfinite( coeffs[0] );
}

