// geometry.asy

// Copyright (C) 2007
// Author: Philippe IVALDI 2007/09/01
// http://www.piprime.fr/

// This program is free software ; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation ; either version 3 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
// Lesser General Public License for more details.

// You should have received a copy of the GNU Lesser 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

// COMMENTARY:
// An Asymptote geometry module.

// THANKS:
// Special thanks to Olivier Guibé for his help in mathematical issues.

// BUGS:

// CODE:

import math;
import markers;

// A rotation in the direction dir limited to [-90,90]
// This is useful for rotating text along a line in the direction dir.
private transform rotate(explicit pair dir)
{
  real angle=degrees(dir);
  if(angle > 90 && angle < 270) angle -= 180;
  return rotate(angle);
} 

// *=======================================================*
// *........................HEADER.........................*
/*<asyxml><variable type="real" signature="epsgeo"><code></asyxml>*/
real epsgeo = 10 * sqrt(realEpsilon);/*<asyxml></code><documentation>Variable used in the approximate calculations.</documentation></variable></asyxml>*/

/*<asyxml><function type="void" signature="addMargins(picture,real,real,real,real)"><code></asyxml>*/
void addMargins(picture pic = currentpicture,
                real lmargin = 0, real bmargin = 0,
                real rmargin = lmargin, real tmargin = bmargin,
                bool rigid = true, bool allObject = true)
{/*<asyxml></code><documentation>Add margins to 'pic' with respect to
   the current bounding box of 'pic'.
   If 'rigid' is false, margins are added iff an infinite curve will
   be prolonged on the margin.
   If 'allObject' is false, fixed - size objects (such as labels and
   arrowheads) will be ignored.</documentation></function></asyxml>*/
  pair m = allObject ? truepoint(pic, SW) : point(pic, SW);
  pair M = allObject ? truepoint(pic, NE) : point(pic, NE);
  if(rigid) {
    draw(m - inverse(pic.calculateTransform()) * (lmargin, bmargin), invisible);
    draw(M + inverse(pic.calculateTransform()) * (rmargin, tmargin), invisible);
  } else pic.addBox(m, M, -(lmargin, bmargin), (rmargin, tmargin));
}

real approximate(real t)
{
  real ot = t;
  if(abs(t - ceil(t)) < epsgeo) ot = ceil(t);
  else if(abs(t - floor(t)) < epsgeo) ot = floor(t);
  return ot;
}

real[] approximate(real[] T)
{
  return map(approximate, T);
}

/*<asyxml><function type="real" signature="binomial(real,real)"><code></asyxml>*/
real binomial(real n, real k)
{/*<asyxml></code><documentation>Return n!/((n - k)!*k!)</documentation></function></asyxml>*/
  return gamma(n + 1)/(gamma(n - k + 1) * gamma(k + 1));
}

/*<asyxml><function type="real" signature="rf(real,real,real)"><code></asyxml>*/
real rf(real x, real y, real z)
{/*<asyxml></code><documentation>Computes Carlson's elliptic integral of the first kind.
   x, y, and z must be non negative, and at most one can be zero.</documentation></function></asyxml>*/
  real ERRTOL = 0.0025,
    TINY = 1.5e-38,
    BIG = 3e37,
    THIRD = 1/3,
    C1 = 1/24,
    C2 = 0.1,
    C3 = 3/44,
    C4 = 1/14;
  real alamb, ave, delx, dely, delz, e2, e3, sqrtx, sqrty, sqrtz, xt, yt, zt;
  if(min(x, y, z) < 0 || min(x + y, x + z, y + z) < TINY ||
     max(x, y, z) > BIG) abort("rf: invalid arguments.");
  xt = x;
  yt = y;
  zt = z;
  do {
    sqrtx = sqrt(xt);
    sqrty = sqrt(yt);
    sqrtz = sqrt(zt);
    alamb = sqrtx * (sqrty + sqrtz) + sqrty * sqrtz;
    xt = 0.25 * (xt + alamb);
    yt = 0.25 * (yt + alamb);
    zt = 0.25 * (zt + alamb);
    ave = THIRD * (xt + yt + zt);
    delx = (ave - xt)/ave;
    dely = (ave - yt)/ave;
    delz = (ave - zt)/ave;
  } while(max(fabs(delx), fabs(dely), fabs(delz)) > ERRTOL);
  e2 = delx * dely - delz * delz;
  e3 = delx * dely * delz;
  return (1.0 + (C1 * e2 - C2 - C3 * e3) * e2 + C4 * e3)/sqrt(ave);
}

/*<asyxml><function type="real" signature="rd(real,real,real)"><code></asyxml>*/
real rd(real x, real y, real z)
{/*<asyxml></code><documentation>Computes Carlson's elliptic integral of the second kind.
   x and y must be positive, and at most one can be zero.
   z must be non negative.</documentation></function></asyxml>*/
  real ERRTOL = 0.0015,
    TINY = 1e-25,
    BIG = 4.5 * 10.0^21,
    C1 = (3/14),
    C2 = (1/6),
    C3 = (9/22),
    C4 = (3/26),
    C5 = (0.25 * C3),
    C6 = (1.5 * C4);
  real alamb, ave, delx, dely, delz, ea, eb, ec, ed, ee, fac, sqrtx, sqrty,
    sqrtz, sum, xt, yt, zt;
  if (min(x, y) < 0 || min(x + y, z) < TINY || max(x, y, z) > BIG)
    abort("rd: invalid arguments");
  xt = x;
  yt = y;
  zt = z;
  sum = 0;
  fac = 1;
  do {
    sqrtx = sqrt(xt);
    sqrty = sqrt(yt);
    sqrtz = sqrt(zt);
    alamb = sqrtx * (sqrty + sqrtz) + sqrty * sqrtz;
    sum += fac/(sqrtz * (zt + alamb));
    fac = 0.25 * fac;
    xt = 0.25 * (xt + alamb);
    yt = 0.25 * (yt + alamb);
    zt = 0.25 * (zt + alamb);
    ave = 0.2 * (xt + yt + 3.0 * zt);
    delx = (ave - xt)/ave;
    dely = (ave - yt)/ave;
    delz = (ave - zt)/ave;
  } while (max(fabs(delx), fabs(dely), fabs(delz)) > ERRTOL);
  ea = delx * dely;
  eb = delz * delz;
  ec = ea - eb;
  ed = ea - 6 * eb;
  ee = ed + ec + ec;
  return 3 * sum + fac * (1.0 + ed * (-C1 + C5 * ed - C6 * delz * ee)
                    +delz * (C2 * ee + delz * (-C3 * ec + delz * C4 * ea)))/(ave * sqrt(ave));
}

/*<asyxml><function type="real" signature="elle(real,real)"><code></asyxml>*/
real elle(real phi, real k)
{/*<asyxml></code><documentation>Legendre elliptic integral of the 2nd kind,
   evaluated using Carlson's functions RD and RF.
   The argument ranges are -infinity < phi < +infinity, 0 <= k * sin(phi) <= 1.</documentation></function></asyxml>*/
  real result;
  if (phi >= 0 && phi <= pi/2) {
    real cc, q, s;
    s = sin(phi);
    cc = cos(phi)^2;
    q = (1 - s * k) * (1 + s * k);
    result = s * (rf(cc, q, 1) - (s * k)^2 * rd(cc, q, 1)/3);
  } else
    if (phi <= pi && phi >= 0) {
      result = 2 * elle(pi/2, k) - elle(pi - phi, k);
    } else
      if (phi <= 3 * pi/2 && phi >= 0) {
        result = 2 * elle(pi/2, k) + elle(phi - pi, k);
      } else
        if (phi <= 2 * pi && phi >= 0) {
          result = 4 * elle(pi/2, k) - elle(2 * pi - phi, k);
        } else
          if (phi >= 0) {
            int nb = floor(0.5 * phi/pi);
            result = nb * elle(2 * pi, k) + elle(phi%(2 * pi), k);
          } else result = -elle(-phi, k);
  return result;
}

/*<asyxml><function type="pair[]" signature="intersectionpoints(pair,pair,real,real,real,real,real,real)"><code></asyxml>*/
pair[] intersectionpoints(pair A, pair B,
                          real a, real b, real c, real d, real f, real g)
{/*<asyxml></code><documentation>Intersection points with the line (AB) and the quadric curve
   a * x^2 + b * x * y + c * y^2 + d * x + f * y + g = 0 given in the default coordinate system</documentation></function></asyxml>*/
  pair[] op;
  real ap = B.y - A.y,
    bpp = A.x - B.x,
    cp = A.y * B.x - A.x * B.y;
  real sol[];
  if (abs(ap) > epsgeo) {
    real aa = ap * c + a * bpp^2/ap - b * bpp,
      bb = ap * f - bpp * d + 2 * a * bpp * cp/ap - b * cp,
      cc = ap * g - cp * d + a * cp^2/ap;
    sol = quadraticroots(aa, bb, cc);
    for (int i = 0; i < sol.length; ++i) {
      op.push((-bpp * sol[i]/ap - cp/ap, sol[i]));
    }
  } else {
    real aa = a * bpp,
      bb = d * bpp - b * cp,
      cc = g * bpp - cp * f + c * cp^2/bpp;
    sol = quadraticroots(aa, bb, cc);
    for (int i = 0; i < sol.length; ++i) {
      op.push((sol[i], -cp/bpp));
    }
  }
  return op;
}

/*<asyxml><function type="pair[]" signature="intersectionpoints(pair,pair,real[])"><code></asyxml>*/
pair[] intersectionpoints(pair A, pair B, real[] equation)
{/*<asyxml></code><documentation>Return the intersection points of the line AB with
   the conic whose an equation is
   equation[0] * x^2 + equation[1] * x * y + equation[2] * y^2 + equation[3] * x + equation[4] * y + equation[5] = 0</documentation></function></asyxml>*/
  if(equation.length != 6) abort("intersectionpoints: bad length of array for a conic equation.");
  return intersectionpoints(A, B, equation[0], equation[1], equation[2],
                            equation[3], equation[4], equation[5]);
}
// *........................HEADER.........................*
// *=======================================================*

// *=======================================================*
// *......................COORDINATES......................*

real EPS = sqrt(realEpsilon);

/*<asyxml><typedef type = "convert" return = "pair" params = "pair"><code></asyxml>*/
typedef pair convert(pair);/*<asyxml></code><documentation>Function type to convert pair in an other coordinate system.</documentation></typedef></asyxml>*/
/*<asyxml><typedef type = "abs" return = "real" params = "pair"><code></asyxml>*/
typedef real abs(pair);/*<asyxml></code><documentation>Function type to calculate modulus of pair.</documentation></typedef></asyxml>*/
/*<asyxml><typedef type = "dot" return = "real" params = "pair, pair"><code></asyxml>*/
typedef real dot(pair, pair);/*<asyxml></code><documentation>Function type to calculate dot product.</documentation></typedef></asyxml>*/
/*<asyxml><typedef type = "polar" return = "pair" params = "real, real"><code></asyxml>*/
typedef pair polar(real, real);/*<asyxml></code><documentation>Function type to calculate the coordinates from the polar coordinates.</documentation></typedef></asyxml>*/

/*<asyxml><struct signature="coordsys"><code></asyxml>*/
struct coordsys
{/*<asyxml></code><documentation>This structure represents a coordinate system in the plane.</documentation></asyxml>*/
  /*<asyxml><method type = "pair" signature="relativetodefault(pair)"><code></asyxml>*/
  restricted convert relativetodefault = new pair(pair m){return m;};/*<asyxml></code><documentation>Convert a pair given relatively to this coordinate system to
                                                                     the pair relatively to the default coordinate system.</documentation></method></asyxml>*/
  /*<asyxml><method type = "pair" signature="defaulttorelativet(pair)"><code></asyxml>*/
  restricted convert defaulttorelative = new pair(pair m){return m;};/*<asyxml></code><documentation>Convert a pair given relatively to the default coordinate system to
                                                                     the pair relatively to this coordinate system.</documentation></method></asyxml>*/
  /*<asyxml><method type = "real" signature="dot(pair,pair)"><code></asyxml>*/
  restricted dot dot = new real(pair m, pair n){return dot(m, n);};/*<asyxml></code><documentation>Return the dot product of this coordinate system.</documentation></method></asyxml>*/
  /*<asyxml><method type = "real" signature="abs(pair)"><code></asyxml>*/
  restricted abs abs = new real(pair m){return abs(m);};/*<asyxml></code><documentation>Return the modulus of a pair in this coordinate system.</documentation></method></asyxml>*/
  /*<asyxml><method type = "pair" signature="polar(real,real)"><code></asyxml>*/
  restricted polar polar = new pair(real r, real a){return (r * cos(a), r * sin(a));};/*<asyxml></code><documentation>Polar coordinates routine of this coordinate system.</documentation></method></asyxml>*/
  /*<asyxml><property type = "pair" signature="O,i,j"><code></asyxml>*/
  restricted pair O = (0, 0), i = (1, 0), j = (0, 1);/*<asyxml></code><documentation>Origin and units vector.</documentation></property></asyxml>*/
  /*<asyxml><method type = "void" signature="init(convert,convert,polar,dot)"><code></asyxml>*/
  void init(convert rtd, convert dtr,
            polar polar, dot dot)
  {/*<asyxml></code><documentation>The default constructor of the coordinate system.</documentation></method></asyxml>*/
    this.relativetodefault = rtd;
    this.defaulttorelative = dtr;
    this.polar = polar;
    this.dot = dot;
    this.abs = new real(pair m){return sqrt(dot(m, m));};;
    this.O = rtd((0, 0));
    this.i = rtd((1, 0)) - O;
    this.j = rtd((0, 1)) - O;
  }
}/*<asyxml></struct></asyxml>*/

/*<asyxml><operator type = "bool" signature="==(coordsys,coordsys)"><code></asyxml>*/
bool operator ==(coordsys c1, coordsys c2)
{/*<asyxml></code><documentation>Return true iff the coordinate system have the same origin and units vector.</documentation></operator></asyxml>*/
  return c1.O == c2.O && c1.i == c2.i && c1.j == c2.j;
}

/*<asyxml><function type="coordsys" signature="cartesiansystem(pair,pair,pair)"><code></asyxml>*/
coordsys cartesiansystem(pair O = (0, 0), pair i, pair j)
{/*<asyxml></code><documentation>Return the Cartesian coordinate system (O, i, j).</documentation></function></asyxml>*/
  coordsys R;
  real[][] P = {{0, 0}, {0, 0}};
  real[][] iP;
  P[0][0] = i.x;
  P[0][1] = j.x;
  P[1][0] = i.y;
  P[1][1] = j.y;
  iP = inverse(P);
  real ni = abs(i);
  real nj = abs(j);
  real ij = angle(j) - angle(i);

  pair rtd(pair m)
  {
    return O + (P[0][0] * m.x + P[0][1] * m.y, P[1][0] * m.x + P[1][1] * m.y);
  }

  pair dtr(pair m)
  {
    m-=O;
    return (iP[0][0] * m.x + iP[0][1] * m.y, iP[1][0] * m.x + iP[1][1] * m.y);
  }

  pair polar(real r, real a)
  {
    real ca = sin(ij - a)/(ni * sin(ij));
    real sa = sin(a)/(nj * sin(ij));
    return r * (ca, sa);
  }

  real tdot(pair m, pair n)
  {
    return m.x * n.x * ni^2 + m.y * n.y * nj^2 + (m.x * n.y + n.x * m.y) * dot(i, j);
  }

  R.init(rtd, dtr, polar, tdot);
  return R;
}


/*<asyxml><function type="void" signature="show(picture,Label,Label,Label,coordsys,pen,pen,pen,pen,pen)"><code></asyxml>*/
void show(picture pic = currentpicture, Label lo = "$O$",
          Label li = "$\vec{\imath}$",
          Label lj = "$\vec{\jmath}$",
          coordsys R,
          pen dotpen = currentpen, pen xpen = currentpen, pen ypen = xpen,
          pen ipen = red,
          pen jpen = ipen,
          arrowbar arrow = Arrow)
{/*<asyxml></code><documentation>Draw the components (O, i, j, x - axis, y - axis) of 'R'.</documentation></function></asyxml>*/
  unravel R;
  dot(pic, O, dotpen);
  drawline(pic, O, O + i, xpen);
  drawline(pic, O, O + j, ypen);
  draw(pic, li, O--(O + i), ipen, arrow);
  Label lj = lj.copy();
  lj.align(lj.align, unit(I * j));
  draw(pic, lj, O--(O + j), jpen, arrow);
  draw(pic, lj, O--(O + j), jpen, arrow);
  Label lo = lo.copy();
  lo.align(lo.align, -2 * dir(O--O + i, O--O + j));
  lo.p(dotpen);
  label(pic, lo, O);
}

/*<asyxml><operator type = "pair" signature="/(pair,coordsys)"><code></asyxml>*/
pair operator /(pair p, coordsys R)
{/*<asyxml></code><documentation>Return the xy - coordinates of 'p' relatively to
   the coordinate system 'R'.
   For example, if R = cartesiansystem((1, 2), (1, 0), (0, 1)), (0, 0)/R is (-1, -2).</documentation></operator></asyxml>*/
  return R.defaulttorelative(p);
}

/*<asyxml><operator type = "pair" signature="*(coordsys,pair)"><code></asyxml>*/
pair operator *(coordsys R, pair p)
{/*<asyxml></code><documentation>Return the coordinates of 'p' given in the
   xy - coordinates 'R'.
   For example, if R = cartesiansystem((1, 2), (1, 0), (0, 1)), R * (0, 0) is (1, 2).</documentation></operator></asyxml>*/
  return R.relativetodefault(p);
}

/*<asyxml><operator type = "path" signature="*(coordsys,path)"><code></asyxml>*/
path operator *(coordsys R, path g)
{/*<asyxml></code><documentation>Return the reconstructed path applying R * pair to each node, pre and post control point of 'g'.</documentation></operator></asyxml>*/
  guide og = R * point(g, 0);
  real l = length(g);
  for(int i = 1; i <= l; ++i)
    {
      pair P = R * point(g, i);
      pair post = R * postcontrol(g, i - 1);
      pair pre = R * precontrol(g, i);
      if(i == l && (cyclic(g)))
        og = og..controls post and pre..cycle;
      else
        og = og..controls post and pre..P;
    }
  return og;
}

/*<asyxml><operator type = "coordsys" signature="*(transform,coordsys)"><code></asyxml>*/
coordsys operator *(transform t,coordsys R)
{/*<asyxml></code><documentation>Provide transform * coordsys.
   Note that shiftless(t) is applied to R.i and R.j.</documentation></operator></asyxml>*/
  coordsys oc;
  oc = cartesiansystem(t * R.O, shiftless(t) * R.i, shiftless(t) * R.j);
  return oc;
}

/*<asyxml><constant type = "coordsys" signature="defaultcoordsys"><code></asyxml>*/
restricted coordsys defaultcoordsys = cartesiansystem(0, (1, 0), (0, 1));/*<asyxml></code><documentation>One can always refer to the default coordinate system using this constant.</documentation></constant></asyxml>*/
/*<asyxml><variable type="coordsys" signature="currentcoordsys"><code></asyxml>*/
coordsys currentcoordsys = defaultcoordsys;/*<asyxml></code><documentation>The coordinate system used by default.</documentation></variable></asyxml>*/

/*<asyxml><struct signature="point"><code></asyxml>*/
struct point
{/*<asyxml></code><documentation>This structure replaces the pair to embed its coordinate system.
   For example, if 'P = point(cartesiansystem((1, 2), i, j), (0, 0))',
   P is equal to the pair (1, 2).</documentation></asyxml>*/
  /*<asyxml><property type = "coordsys" signature="coordsys"><code></asyxml>*/
  coordsys coordsys;/*<asyxml></code><documentation>The coordinate system of this point.</documentation></property><property type = "pair" signature="coordinates"><code></asyxml>*/
  restricted pair coordinates;/*<asyxml></code><documentation>The coordinates of this point relatively to the coordinate system 'coordsys'.</documentation></property><property type = "real" signature="x, y"><code></asyxml>*/
  restricted real x, y;/*<asyxml></code><documentation>The xpart and the ypart of 'coordinates'.</documentation></property></asyxml>*/
  /*<asyxml><method type = "" signature="init(coordsys,pair)"><code><property type = "real" signature="m"><code></asyxml>*/
  real m = 1;/*<asyxml></code><documentation>Used to cast mass<->point.</documentation></property></asyxml>*/
  void init(coordsys R, pair coordinates, real mass)
  {/*<asyxml></code><documentation>The constructor.</documentation></method></asyxml>*/
    this.coordsys = R;
    this.coordinates = coordinates;
    this.x = coordinates.x;
    this.y = coordinates.y;
    this.m = mass;
  }
}/*<asyxml></struct></asyxml>*/

/*<asyxml><function type="point" signature="point(coordsys,pair,real)"><code></asyxml>*/
point point(coordsys R, pair p, real m = 1)
{/*<asyxml></code><documentation>Return the point which has the coodinates 'p' in the
   coordinate system 'R' and the mass 'm'.</documentation></function></asyxml>*/
  point op;
  op.init(R, p, m);
  return op;
}

/*<asyxml><function type="point" signature="point(explicit pair,real)"><code></asyxml>*/
point point(explicit pair p, real m)
{/*<asyxml></code><documentation>Return the point which has the coodinates 'p' in the current
   coordinate system and the mass 'm'.</documentation></function></asyxml>*/
  point op;
  op.init(currentcoordsys, p, m);
  return op;
}

/*<asyxml><function type="point" signature="point(coordsys,explicit point,real)"><code></asyxml>*/
point point(coordsys R, explicit point M, real m = M.m)
{/*<asyxml></code><documentation>Return the point of 'R' which has the coordinates of 'M' and the mass 'm'.
   Do not confuse this routine with the further routine 'changecoordsys'.</documentation></function></asyxml>*/
  point op;
  op.init(R, M.coordinates, M.m);
  return op;
}

/*<asyxml><function type="point" signature="changecoordsys(coordsys,point)"><code></asyxml>*/
point changecoordsys(coordsys R, point M)
{/*<asyxml></code><documentation>Return the point 'M' in the coordinate system 'coordsys'.
   In other words, the returned point marks the same plot as 'M' does.</documentation></function></asyxml>*/
  point op;
  coordsys mco = M.coordsys;
  op.init(R, R.defaulttorelative(mco.relativetodefault(M.coordinates)), M.m);
  return op;
}

/*<asyxml><function type="pair" signature="pair coordinates(point)"><code></asyxml>*/
pair coordinates(point M)
{/*<asyxml></code><documentation>Return the coordinates of 'M' in its coordinate system.</documentation></function></asyxml>*/
  return M.coordinates;
}

/*<asyxml><function type="bool" signature="bool samecoordsys(bool...point[])"><code></asyxml>*/
bool samecoordsys(bool warn = true ... point[] M)
{/*<asyxml></code><documentation>Return true iff all the points have the same coordinate system.
   If 'warn' is true and the coordinate systems are different, a warning is sent.</documentation></function></asyxml>*/
  bool ret = true;
  coordsys t = M[0].coordsys;
  for (int i = 1; i < M.length; ++i) {
    ret = (t == M[i].coordsys);
    if(!ret) break;
    t = M[i].coordsys;
  }
  if(warn && !ret)
    warning("coodinatesystem",
            "the coordinate system of two objects are not the same.
The operation will be done relative to the default coordinate system.");
  return ret;
}

/*<asyxml><function type="point[]" signature="standardizecoordsys(coordsys,bool...point[])"><code></asyxml>*/
point[] standardizecoordsys(coordsys R = currentcoordsys,
                            bool warn = true ... point[] M)
{/*<asyxml></code><documentation>Return the points with the same coordinate system 'R'.
   If 'warn' is true and the coordinate systems are different, a warning is sent.</documentation></function></asyxml>*/
  point[] op = new point[];
  op = M;
  if(!samecoordsys(warn ... M))
    for (int i = 1; i < M.length; ++i)
      op[i] = changecoordsys(R, M[i]);
  return op;
}

/*<asyxml><operator type = "pair" signature="cast(point)"><code></asyxml>*/
pair operator cast(point P)
{/*<asyxml></code><documentation>Cast point to pair.</documentation></operator></asyxml>*/
  return P.coordsys.relativetodefault(P.coordinates);
}

/*<asyxml><operator type = "pair[]" signature="cast(point[])"><code></asyxml>*/
pair[] operator cast(point[] P)
{/*<asyxml></code><documentation>Cast point[] to pair[].</documentation></operator></asyxml>*/
  pair[] op;
  for (int i = 0; i < P.length; ++i) {
    op.push((pair)P[i]);
  }
  return op;
}

/*<asyxml><operator type = "point" signature="cast(pair)"><code></asyxml>*/
point operator cast(pair p)
{/*<asyxml></code><documentation>Cast pair to point relatively to the current coordinate
   system 'currentcoordsys'.</documentation></operator></asyxml>*/
  return point(currentcoordsys, p);
}

/*<asyxml><operator type = "point[]" signature="cast(pair[])"><code></asyxml>*/
point[] operator cast(pair[] p)
{/*<asyxml></code><documentation>Cast pair[] to point[] relatively to the current coordinate
   system 'currentcoordsys'.</documentation></operator></asyxml>*/
  pair[] op;
  for (int i = 0; i < p.length; ++i) {
    op.push((point)p[i]);
  }
  return op;
}

/*<asyxml><function type="pair" signature="locate(point)"><code></asyxml>*/
pair locate(point P)
{/*<asyxml></code><documentation>Return the coordinates of 'P' in the default coordinate system.</documentation></function></asyxml>*/
  return P.coordsys * P.coordinates;
}

/*<asyxml><function type="point" signature="locate(pair)"><code></asyxml>*/
point locate(pair p)
{/*<asyxml></code><documentation>Return the point in the current coordinate system 'currentcoordsys'.</documentation></function></asyxml>*/
  return p; //automatic casting 'pair to point'.
}

/*<asyxml><operator type = "point" signature="*(real,explicit point)"><code></asyxml>*/
point operator *(real x, explicit point P)
{/*<asyxml></code><documentation>Multiply the coordinates (not the mass) of 'P' by 'x'.</documentation></operator></asyxml>*/
  return point(P.coordsys, x * P.coordinates, P.m);
}

/*<asyxml><operator type = "point" signature="/(explicit point,real)"><code></asyxml>*/
point operator /(explicit point P, real x)
{/*<asyxml></code><documentation>Divide the coordinates (not the mass) of 'P' by 'x'.</documentation></operator></asyxml>*/
  return point(P.coordsys, P.coordinates/x, P.m);
}

/*<asyxml><operator type = "point" signature="/(real,explicit point)"><code></asyxml>*/
point operator /(real x, explicit point P)
{/*<asyxml></code><documentation></documentation></operator></asyxml>*/
  return point(P.coordsys, x/P.coordinates, P.m);
}

/*<asyxml><operator type = "point" signature="-(explicit point)"><code></asyxml>*/
point operator -(explicit point P)
{/*<asyxml></code><documentation>-P. The mass is inchanged.</documentation></operator></asyxml>*/
  return point(P.coordsys, -P.coordinates, P.m);
}

/*<asyxml><operator type = "point" signature="+(explicit point,explicit point)"><code></asyxml>*/
point operator +(explicit point P1, explicit point P2)
{/*<asyxml></code><documentation>Provide 'point + point'.
   If the two points haven't the same coordinate system, a warning is sent and the
   returned point has the default coordinate system 'defaultcoordsys'.
   The masses are added.</documentation></operator></asyxml>*/
  point[] P = standardizecoordsys(P1, P2);
  coordsys R = P[0].coordsys;
  return point(R, P[0].coordinates + P[1].coordinates, P1.m + P2.m);
}

/*<asyxml><operator type = "point" signature="+(explicit point,explicit pair)"><code></asyxml>*/
point operator +(explicit point P1, explicit pair p2)
{/*<asyxml></code><documentation>Provide 'point + pair'.
   The pair 'p2' is supposed to be coordinates relatively to the coordinates system of 'P1'.
   The mass is not changed.</documentation></operator></asyxml>*/
  coordsys R = currentcoordsys;
  return point(R, P1.coordinates + point(R, p2).coordinates, P1.m);
}
point operator +(explicit pair p1, explicit point p2)
{
  return p2 + p1;
}

/*<asyxml><operator type = "point" signature="-(explicit point,explicit point)"><code></asyxml>*/
point operator -(explicit point P1, explicit point P2)
{/*<asyxml></code><documentation>Provide 'point - point'.</documentation></operator></asyxml>*/
  return P1 + (-P2);
}

/*<asyxml><operator type = "point" signature="-(explicit point,explicit pair)"><code></asyxml>*/
point operator -(explicit point P1, explicit pair p2)
{/*<asyxml></code><documentation>Provide 'point - pair'.
   The pair 'p2' is supposed to be coordinates relatively to the coordinates system of 'P1'.</documentation></operator></asyxml>*/
  return P1 + (-p2);
}
point operator -(explicit pair p1, explicit point P2)
{
  return p1 + (-P2);
}

/*<asyxml><operator type = "point" signature="*(transform,explicit point)"><code></asyxml>*/
point operator *(transform t, explicit point P)
{/*<asyxml></code><documentation>Provide 'transform * point'.
   Note that the transforms scale, xscale, yscale and rotate are carried out relatively
   the default coordinate system 'defaultcoordsys' which is not desired for point
   defined in an other coordinate system.
   On can use scale(real, point), xscale(real, point), yscale(real, point), rotate(real, point),
   scaleO(real), xscaleO(real), yscaleO(real) and rotateO(real) (described further)
   to change the coordinate system of reference.</documentation></operator></asyxml>*/
  coordsys R = P.coordsys;
  return point(R, (t * locate(P))/R, P.m);
}

/*<asyxml><operator type = "point" signature="*(explicit point,explicit point)"><code></asyxml>*/
point operator *(explicit point P1, explicit point P2)
{/*<asyxml></code><documentation>Provide 'point * point'.
   The resulted mass is the mass of P2</documentation></operator></asyxml>*/
  point[] P = standardizecoordsys(P1, P2);
  coordsys R = P[0].coordsys;
  return point(R, P[0].coordinates * P[1].coordinates, P2.m);
}

/*<asyxml><operator type = "point" signature="*(explicit point,explicit pair)"><code></asyxml>*/
point operator *(explicit point P1, explicit pair p2)
{/*<asyxml></code><documentation>Provide 'point * pair'.
   The pair 'p2' is supposed to be the coordinates of
   the point in the coordinates system of 'P1'.
   'pair * point' is also defined.</documentation></operator></asyxml>*/
  point P = point(P1.coordsys, p2, P1.m);
  return P1 * P;
}
point operator *(explicit pair p1, explicit point p2)
{
  return p2 * p1;
}

/*<asyxml><operator type = "bool" signature="==(explicit point,explicit point)"><code></asyxml>*/
bool operator ==(explicit point M, explicit point N)
{/*<asyxml></code><documentation>Provide the test 'M == N' wish returns true iff MN < EPS</documentation></operator></asyxml>*/
  return abs(locate(M) - locate(N)) < EPS;
}

/*<asyxml><operator type = "bool" signature="!=(explicit point,explicit point)"><code></asyxml>*/
bool operator !=(explicit point M, explicit point N)
{/*<asyxml></code><documentation>Provide the test 'M != N' wish return true iff MN >= EPS</documentation></operator></asyxml>*/
  return !(M == N);
}

/*<asyxml><operator type = "guide" signature="cast(point)"><code></asyxml>*/
guide operator cast(point p)
{/*<asyxml></code><documentation>Cast point to guide.</documentation></operator></asyxml>*/
  return locate(p);
}

/*<asyxml><operator type = "path" signature="cast(point)"><code></asyxml>*/
path operator cast(point p)
{/*<asyxml></code><documentation>Cast point to path.</documentation></operator></asyxml>*/
  return locate(p);
}

/*<asyxml><function type="void" signature="dot(picture,Label,explicit point,align,string,pen)"><code></asyxml>*/
void dot(picture pic = currentpicture, Label L, explicit point Z,
         align align = NoAlign,
         string format = defaultformat, pen p = currentpen)
{/*<asyxml></code><documentation></documentation></function></asyxml>*/
  Label L = L.copy();
  L.position(locate(Z));
  if(L.s == "") {
    if(format == "") format = defaultformat;
    L.s = "("+format(format, Z.x)+", "+format(format, Z.y)+")";
  }
  L.align(align, E);
  L.p(p);
  dot(pic, locate(Z), p);
  add(pic, L);
}

/*<asyxml><function type="real" signature="abs(coordsys,pair)"><code></asyxml>*/
real abs(coordsys R, pair m)
{/*<asyxml></code><documentation>Return the modulus |m| in the coordinate system 'R'.</documentation></function></asyxml>*/
  return R.abs(m);
}

/*<asyxml><function type="real" signature="abs(explicit point)"><code></asyxml>*/
real abs(explicit point M)
{/*<asyxml></code><documentation>Return the modulus |M| in its coordinate system.</documentation></function></asyxml>*/
  return M.coordsys.abs(M.coordinates);
}

/*<asyxml><function type="real" signature="length(explicit point)"><code></asyxml>*/
real length(explicit point M)
{/*<asyxml></code><documentation>Return the modulus |M| in its coordinate system (same as 'abs').</documentation></function></asyxml>*/
  return M.coordsys.abs(M.coordinates);
}

/*<asyxml><function type="point" signature="conj(explicit point)"><code></asyxml>*/
point conj(explicit point M)
{/*<asyxml></code><documentation>Conjugate.</documentation></function></asyxml>*/
  return point(M.coordsys, conj(M.coordinates), M.m);
}

/*<asyxml><function type="real" signature="degrees(explicit point,coordsys,bool)"><code></asyxml>*/
real degrees(explicit point M, coordsys R = M.coordsys, bool warn = true)
{/*<asyxml></code><documentation>Return the angle of M (in degrees) relatively to 'R'.</documentation></function></asyxml>*/
  return (degrees(locate(M) - R.O, warn) - degrees(R.i))%360;
}

/*<asyxml><function type="real" signature="angle(explicit point,coordsys,bool)"><code></asyxml>*/
real angle(explicit point M, coordsys R = M.coordsys, bool warn = true)
{/*<asyxml></code><documentation>Return the angle of M (in radians) relatively to 'R'.</documentation></function></asyxml>*/
  return radians(degrees(M, R, warn));
}

/*<asyxml><function type="bool" signature="finite(explicit point)"><code></asyxml>*/
bool finite(explicit point p)
{/*<asyxml></code><documentation>Avoid to compute 'finite((pair)(infinite_point))'.</documentation></function></asyxml>*/
  return finite(p.coordinates);
}

/*<asyxml><function type="real" signature="dot(point,point)"><code></asyxml>*/
real dot(point A, point B)
{/*<asyxml></code><documentation>Return the dot product in the coordinate system of 'A'.</documentation></function></asyxml>*/
  point[] P = standardizecoordsys(A.coordsys, A, B);
  return P[0].coordsys.dot(P[0].coordinates, P[1].coordinates);
}

/*<asyxml><function type="real" signature="dot(point,explicit pair)"><code></asyxml>*/
real dot(point A, explicit pair B)
{/*<asyxml></code><documentation>Return the dot product in the default coordinate system.
   dot(explicit pair, point) is also defined.</documentation></function></asyxml>*/
  return dot(locate(A), B);
}
real dot(explicit pair A, point B)
{
  return dot(A, locate(B));
}

/*<asyxml><function type="transforms" signature="rotateO(real)"><code></asyxml>*/
transform rotateO(real a)
{/*<asyxml></code><documentation>Rotation around the origin of the current coordinate system.</documentation></function></asyxml>*/
  return rotate(a, currentcoordsys.O);
};

/*<asyxml><function type="transform" signature="projection(point,point)"><code></asyxml>*/
transform projection(point A, point B)
{/*<asyxml></code><documentation>Return the orthogonal projection on the line (AB).</documentation></function></asyxml>*/
  pair dir = unit(locate(A) - locate(B));
  pair a = locate(A);
  real cof = dir.x * a.x + dir.y * a.y;
  real tx = a.x - dir.x * cof;
  real txx = dir.x^2;
  real txy = dir.x * dir.y;
  real ty = a.y - dir.y * cof;
  real tyx = txy;
  real tyy = dir.y^2;
  transform t = (tx, ty, txx, txy, tyx, tyy);
  return t;
}

/*<asyxml><function type="transform" signature="projection(point,point,point,point,bool)"><code></asyxml>*/
transform projection(point A, point B, point C, point D, bool safe = false)
{/*<asyxml></code><documentation>Return the (CD) parallel projection on (AB).
   If 'safe = true' and (AB)//(CD) return the identity.
   If 'safe = false' and (AB)//(CD) return an infinity scaling.</documentation></function></asyxml>*/
  pair a = locate(A);
  pair u = unit(locate(B) - locate(A));
  pair v = unit(locate(D) - locate(C));
  real c = u.x * a.y - u.y * a.x;
  real d = (conj(u) * v).y;
  if (abs(d) < epsgeo) {
    return safe ? identity() : scale(infinity);
  }
  real tx = c * v.x/d;
  real ty = c * v.y/d;
  real txx = u.x * v.y/d;
  real txy = -u.x * v.x/d;
  real tyx = u.y * v.y/d;
  real tyy = -u.y * v.x/d;
  transform t = (tx, ty, txx, txy, tyx, tyy);
  return t;
}

/*<asyxml><function type="transform" signature="scale(real,point)"><code></asyxml>*/
transform scale(real k, point M)
{/*<asyxml></code><documentation>Homothety.</documentation></function></asyxml>*/
  pair P = locate(M);
  return shift(P) * scale(k) * shift(-P);
}

/*<asyxml><function type="transform" signature="xscale(real,point)"><code></asyxml>*/
transform xscale(real k, point M)
{/*<asyxml></code><documentation>xscale from 'M' relatively to the x - axis of the coordinate system of 'M'.</documentation></function></asyxml>*/
  pair P = locate(M);
  real a = degrees(M.coordsys.i);
  return (shift(P) * rotate(a)) * xscale(k) * (rotate(-a) * shift(-P));
}

/*<asyxml><function type="transform" signature="yscale(real,point)"><code></asyxml>*/
transform yscale(real k, point M)
{/*<asyxml></code><documentation>yscale from 'M' relatively to the y - axis of the coordinate system of 'M'.</documentation></function></asyxml>*/
  pair P = locate(M);
  real a = degrees(M.coordsys.j) - 90;
  return (shift(P) * rotate(a)) * yscale(k) * (rotate(-a) * shift(-P));
}

/*<asyxml><function type="transform" signature="scale(real,point,point,point,point,bool)"><code></asyxml>*/
transform scale(real k, point A, point B, point C, point D, bool safe = false)
{/*<asyxml></code><documentation><url href = "http://fr.wikipedia.org/wiki/Affinit%C3%A9_%28math%C3%A9matiques%29"/>
   (help me for English translation...)
   If 'safe = true' and (AB)//(CD) return the identity.
   If 'safe = false' and (AB)//(CD) return a infinity scaling.</documentation></function></asyxml>*/
  pair a = locate(A);
  pair u = unit(locate(B) - locate(A));
  pair v = unit(locate(D) - locate(C));
  real c = u.x * a.y - u.y * a.x;
  real d = (conj(u) * v).y;
  real d = (conj(u) * v).y;
  if (abs(d) < epsgeo) {
    return safe ? identity() : scale(infinity);
  }
  real tx = (1 - k) * c * v.x/d;
  real ty = (1 - k) * c * v.y/d;
  real txx = (1 - k) * u.x * v.y/d + k;
  real txy = (k - 1) * u.x * v.x/d;
  real tyx = (1 - k) * u.y * v.y/d;
  real tyy = (k - 1) * u.y * v.x/d + k;
  transform t = (tx, ty, txx, txy, tyx, tyy);
  return t;
}

/*<asyxml><function type="transform" signature="scaleO(real)"><code></asyxml>*/
transform scaleO(real x)
{/*<asyxml></code><documentation>Homothety from the origin of the current coordinate system.</documentation></function></asyxml>*/
  return scale(x, (0, 0));
}

/*<asyxml><function type="transform" signature="xscaleO(real)"><code></asyxml>*/
transform xscaleO(real x)
{/*<asyxml></code><documentation>xscale from the origin and relatively to the current coordinate system.</documentation></function></asyxml>*/
  return scale(x, (0, 0), (0, 1), (0, 0), (1, 0));
}

/*<asyxml><function type="transform" signature="yscaleO(real)"><code></asyxml>*/
transform yscaleO(real x)
{/*<asyxml></code><documentation>yscale from the origin and relatively to the current coordinate system.</documentation></function></asyxml>*/
  return scale(x, (0, 0), (1, 0), (0, 0), (0, 1));
}

/*<asyxml><struct signature="vector"><code></asyxml>*/
struct vector
{/*<asyxml></code><documentation>Like a point but casting to pair, adding etc does not take account
   of the origin of the coordinate system.</documentation><property type = "point" signature="v"><code></asyxml>*/
  point v;/*<asyxml></code><documentation>Coordinates as a point (embed coordinate system and pair).</documentation></property></asyxml>*/
}/*<asyxml></struct></asyxml>*/

/*<asyxml><operator type = "point" signature="cast(vector)"><code></asyxml>*/
point operator cast(vector v)
{/*<asyxml></code><documentation>Cast vector 'v' to point 'M' so that OM = v.</documentation></operator></asyxml>*/
  return v.v;
}

/*<asyxml><operator type = "vector" signature="cast(pair)"><code></asyxml>*/
vector operator cast(pair v)
{/*<asyxml></code><documentation>Cast pair to vector relatively to the current coordinate
   system 'currentcoordsys'.</documentation></operator></asyxml>*/
  vector ov;
  ov.v = point(currentcoordsys, v);
  return ov;
}

/*<asyxml><operator type = "vector" signature="cast(explicit point)"><code></asyxml>*/
vector operator cast(explicit point v)
{/*<asyxml></code><documentation>A point can be interpreted like a vector using the code
   '(vector)a_point'.</documentation></operator></asyxml>*/
  vector ov;
  ov.v = v;
  return ov;
}

/*<asyxml><operator type = "pair" signature="cast(explicit vector)"><code></asyxml>*/
pair operator cast(explicit vector v)
{/*<asyxml></code><documentation>Cast vector to pair (the coordinates of 'v' in the default coordinate system).</documentation></operator></asyxml>*/
  return locate(v.v) - v.v.coordsys.O;
}

/*<asyxml><operator type = "align" signature="cast(vector)"><code></asyxml>*/
align operator cast(vector v)
{/*<asyxml></code><documentation>Cast vector to align.</documentation></operator></asyxml>*/
  return (pair)v;
}

/*<asyxml><function type="vector" signature="vector(coordsys, pair)"><code></asyxml>*/
vector vector(coordsys R = currentcoordsys, pair v)
{/*<asyxml></code><documentation>Return the vector of 'R' which has the coordinates 'v'.</documentation></function></asyxml>*/
  vector ov;
  ov.v = point(R, v);
  return ov;
}

/*<asyxml><function type="vector" signature="vector(point)"><code></asyxml>*/
vector vector(point M)
{/*<asyxml></code><documentation>Return the vector OM, where O is the origin of the coordinate system of 'M'.
   Useful to write 'vector(P - M);' instead of '(vector)(P - M)'.</documentation></function></asyxml>*/
  return M;
}

/*<asyxml><function type="point" signature="point(explicit vector)"><code></asyxml>*/
point point(explicit vector u)
{/*<asyxml></code><documentation>Return the point M so that OM = u, where O is the origin of the coordinate system of 'u'.</documentation></function></asyxml>*/
  return u.v;
}

/*<asyxml><function type="pair" signature="locate(explicit vector)"><code></asyxml>*/
pair locate(explicit vector v)
{/*<asyxml></code><documentation>Return the coordinates of 'v' in the default coordinate system (like casting vector to pair).</documentation></function></asyxml>*/
  return (pair)v;
}

/*<asyxml><function type="void" signature="show(Label,pen,arrowbar)"><code></asyxml>*/
void show(Label L, vector v, pen p = currentpen, arrowbar arrow = Arrow)
{/*<asyxml></code><documentation>Draw the vector v (from the origin of its coordinate system).</documentation></function></asyxml>*/
  coordsys R = v.v.coordsys;
  draw(L, R.O--v.v, p, arrow);
}

/*<asyxml><function type="vector" signature="changecoordsys(coordsys,vector)"><code></asyxml>*/
vector changecoordsys(coordsys R, vector v)
{/*<asyxml></code><documentation>Return the vector 'v' relatively to coordinate system 'R'.</documentation></function></asyxml>*/
  vector ov;
  ov.v = point(R, (locate(v) + R.O)/R);
  return ov;
}

/*<asyxml><operator type = "vector" signature="*(real,explicit vector)"><code></asyxml>*/
vector operator *(real x, explicit vector v)
{/*<asyxml></code><documentation>Provide real * vector.</documentation></operator></asyxml>*/
  return x * v.v;
}

/*<asyxml><operator type = "vector" signature="/(explicit vector,real)"><code></asyxml>*/
vector operator /(explicit vector v, real x)
{/*<asyxml></code><documentation>Provide vector/real</documentation></operator></asyxml>*/
  return v.v/x;
}

/*<asyxml><operator type = "vector" signature="*(transform t,explicit vector)"><code></asyxml>*/
vector operator *(transform t, explicit vector v)
{/*<asyxml></code><documentation>Provide transform * vector.</documentation></operator></asyxml>*/
  return t * v.v;
}

/*<asyxml><operator type = "vector" signature="*(explicit point,explicit vector)"><code></asyxml>*/
vector operator *(explicit point M, explicit vector v)
{/*<asyxml></code><documentation>Provide point * vector</documentation></operator></asyxml>*/
  return M * v.v;
}

/*<asyxml><operator type = "point" signature="+(explicit point,explicit vector)"><code></asyxml>*/
point operator +(point M, explicit vector v)
{/*<asyxml></code><documentation>Return 'M' shifted by 'v'.</documentation></operator></asyxml>*/
  return shift(locate(v)) * M;
}

/*<asyxml><operator type = "point" signature="-(explicit point,explicit vector)"><code></asyxml>*/
point operator -(point M, explicit vector v)
{/*<asyxml></code><documentation>Return 'M' shifted by '-v'.</documentation></operator></asyxml>*/
  return shift(-locate(v)) * M;
}

/*<asyxml><operator type = "vector" signature="-(explicit vector)"><code></asyxml>*/
vector operator -(explicit vector v)
{/*<asyxml></code><documentation>Provide -v.</documentation></operator></asyxml>*/
  return -v.v;
}

/*<asyxml><operator type = "point" signature="+(explicit pair,explicit vector)"><code></asyxml>*/
point operator +(explicit pair m, explicit vector v)
{/*<asyxml></code><documentation>The pair 'm' is supposed to be the coordinates of
   a point in the current coordinates system 'currentcoordsys'.
   Return this point shifted by the vector 'v'.</documentation></operator></asyxml>*/
  return locate(m) + v;
}

/*<asyxml><operator type = "point" signature="-(explicit pair,explicit vector)"><code></asyxml>*/
point operator -(explicit pair m, explicit vector v)
{/*<asyxml></code><documentation>The pair 'm' is supposed to be the coordinates of
   a point in the current coordinates system 'currentcoordsys'.
   Return this point shifted by the vector '-v'.</documentation></operator></asyxml>*/
  return m + (-v);
}

/*<asyxml><operator type = "vector" signature="+(explicit vector,explicit vector)"><code></asyxml>*/
vector operator +(explicit vector v1, explicit vector v2)
{/*<asyxml></code><documentation>Provide vector + vector.
   If the two vector haven't the same coordinate system, the returned
   vector is relative to the default coordinate system (without warning).</documentation></operator></asyxml>*/
  coordsys R = v1.v.coordsys;
  if(samecoordsys(false, v1, v2)){R = defaultcoordsys;}
  return vector(R, (locate(v1) + locate(v2))/R);
}

/*<asyxml><operator type = "vector" signature="-(explicit vector, explicit vector)"><code></asyxml>*/
vector operator -(explicit vector v1, explicit vector v2)
{/*<asyxml></code><documentation>Provide vector - vector.
   If the two vector haven't the same coordinate system, the returned
   vector is relative to the default coordinate system (without warning).</documentation></operator></asyxml>*/
  return v1 + (-v2);
}

/*<asyxml><operator type = "bool" signature="==(explicit vector,explicit vector)"><code></asyxml>*/
bool operator ==(explicit vector u, explicit vector v)
{/*<asyxml></code><documentation>Return true iff |u - v|<EPS.</documentation></operator></asyxml>*/
  return abs(u - v) < EPS;
}

/*<asyxml><function type="bool" signature="collinear(vector,vector)"><code></asyxml>*/
bool collinear(vector u, vector v)
{/*<asyxml></code><documentation>Return 'true' iff the vectors 'u' and 'v' are collinear.</documentation></function></asyxml>*/
  return abs(ypart((conj((pair)u) * (pair)v))) < EPS;
}

/*<asyxml><function type="vector" signature="unit(point)"><code></asyxml>*/
vector unit(point M)
{/*<asyxml></code><documentation>Return the unit vector according to the modulus of its coordinate system.</documentation></function></asyxml>*/
  return M/abs(M);
}

/*<asyxml><function type="vector" signature="unit(vector)"><code></asyxml>*/
vector unit(vector u)
{/*<asyxml></code><documentation>Return the unit vector according to the modulus of its coordinate system.</documentation></function></asyxml>*/
  return u.v/abs(u.v);
}

/*<asyxml><function type="real" signature="degrees(vector,coordsys,bool)"><code></asyxml>*/
real degrees(vector v,
             coordsys R = v.v.coordsys,
             bool warn = true)
{/*<asyxml></code><documentation>Return the angle of 'v' (in degrees) relatively to 'R'.</documentation></function></asyxml>*/
  return (degrees(locate(v), warn) - degrees(R.i))%360;
}

/*<asyxml><function type="real" signature="angle(vector,coordsys,bool)"><code></asyxml>*/
real angle(explicit vector v,
           coordsys R = v.v.coordsys,
           bool warn = true)
{/*<asyxml></code><documentation>Return the angle of 'v' (in radians) relatively to 'R'.</documentation></function></asyxml>*/
  return radians(degrees(v, R, warn));
}

/*<asyxml><function type="vector" signature="conj(explicit vector)"><code></asyxml>*/
vector conj(explicit vector u)
{/*<asyxml></code><documentation>Conjugate.</documentation></function></asyxml>*/
  return conj(u.v);
}

/*<asyxml><function type="transform" signature="rotate(explicit vector)"><code></asyxml>*/
transform rotate(explicit vector dir)
{/*<asyxml></code><documentation>A rotation in the direction 'dir' limited to [-90, 90]
   This is useful for rotating text along a line in the direction dir.
   rotate(explicit point dir) is also defined.
   </documentation></function></asyxml>*/
  return rotate(locate(dir));
}
transform rotate(explicit point dir){return rotate(locate(vector(dir)));}
// *......................COORDINATES......................*
// *=======================================================*

// *=======================================================*
// *.........................BASES.........................*
/*<asyxml><variable type="point" signature="origin"><code></asyxml>*/
point origin = point(defaultcoordsys, (0, 0));/*<asyxml></code><documentation>The origin of the current coordinate system.</documentation></variable></asyxml>*/

/*<asyxml><function type="point" signature="origin(coordsys)"><code></asyxml>*/
point origin(coordsys R = currentcoordsys)
{/*<asyxml></code><documentation>Return the origin of the coordinate system 'R'.</documentation></function></asyxml>*/
  return point(R, (0, 0)); //use automatic casting;
}

/*<asyxml><variable type="real" signature="linemargin"><code></asyxml>*/
real linemargin = 0;/*<asyxml></code><documentation>Margin used to draw lines.</documentation></variable></asyxml>*/
/*<asyxml><function type="real" signature="linemargin()"><code></asyxml>*/
real linemargin()
{/*<asyxml></code><documentation>Return the margin used to draw lines.</documentation></function></asyxml>*/
  return linemargin;
}

/*<asyxml><variable type="pen" signature="addpenline"><code></asyxml>*/
pen addpenline = squarecap;/*<asyxml></code><documentation>Add this property to the drawing pen of "finish" lines.</documentation></variable></asyxml>*/
pen addpenline(pen p) {
  return addpenline + p;
}

/*<asyxml><variable type="pen" signature="addpenarc"><code></asyxml>*/
pen addpenarc = squarecap;/*<asyxml></code><documentation>Add this property to the drawing pen of arcs.</documentation></variable></asyxml>*/
pen addpenarc(pen p) {return addpenarc + p;}

/*<asyxml><variable type="string" signature="defaultmassformat"><code></asyxml>*/
string defaultmassformat = "$\left(%L;%.4g\right)$";/*<asyxml></code><documentation>Format used to construct the default label of masses.</documentation></variable></asyxml>*/

/*<asyxml><function type="int" signature="sgnd(real)"><code></asyxml>*/
int sgnd(real x)
{/*<asyxml></code><documentation>Return the -1 if x < 0, 1 if x >= 0.</documentation></function></asyxml>*/
  return (x == 0) ? 1 : sgn(x);
}
int sgnd(int x)
{
  return (x == 0) ? 1 : sgn(x);
}

/*<asyxml><function type="bool" signature="defined(pair)"><code></asyxml>*/
bool defined(point P)
{/*<asyxml></code><documentation>Return true iff the coordinates of 'P' are finite.</documentation></function></asyxml>*/
  return finite(P.coordinates);
}

/*<asyxml><function type="bool" signature="onpath(picture,path,point,pen)"><code></asyxml>*/
bool onpath(picture pic = currentpicture, path g, point M, pen p = currentpen)
{/*<asyxml></code><documentation>Return true iff 'M' is on the path drawn with the pen 'p' in 'pic'.</documentation></function></asyxml>*/
  transform t = inverse(pic.calculateTransform());
  return intersect(g, shift(locate(M)) * scale(linewidth(p)/2) * t * unitcircle).length > 0;
}

/*<asyxml><function type="bool" signature="sameside(point,point,point)"><code></asyxml>*/
bool sameside(point M, point N, point O)
{/*<asyxml></code><documentation>Return 'true' iff 'M' and 'N' are same side of the point 'O'.</documentation></function></asyxml>*/
  pair m = M, n = N, o = O;
  return dot(m - o, n - o) >= -epsgeo;
}

/*<asyxml><function type="bool" signature="between(point,point,point)"><code></asyxml>*/
bool between(point M, point O, point N)
{/*<asyxml></code><documentation>Return 'true' iff 'O' is between 'M' and 'N'.</documentation></function></asyxml>*/
  return (!sameside(N, M, O) || M == O || N == O);
}


typedef path pathModifier(path);
pathModifier NoModifier = new path(path g){return g;};

private void Drawline(picture pic = currentpicture, Label L = "", pair P, bool dirP = true, pair Q, bool dirQ = true,
                      align align = NoAlign, pen p = currentpen,
                      arrowbar arrow = None,
                      Label legend = "", marker marker = nomarker,
                      pathModifier pathModifier = NoModifier)
{/* Add the two parameters 'dirP' and 'dirQ' to the native routine
    'drawline' of the module 'math'.
    Segment [PQ] will be prolonged in direction of P if 'dirP = true', in
    direction of Q if 'dirQ = true'.
    If 'dirP = dirQ = true', the behavior is that of the native 'drawline'.
    Add all the other parameters of 'Draw'.*/
  pic.add(new void (frame f, transform t, transform T, pair m, pair M) {
      picture opic;
      // Reduce the bounds by the size of the pen.
      m -= min(p) - (linemargin(), linemargin()); M -= max(p) + (linemargin(), linemargin());

      // Calculate the points and direction vector in the transformed space.
      t = t * T;
      pair z = t * P;
      pair q = t * Q;
      pair v = q - z;
      // path g;
      pair ptp, ptq;
      real cp = dirP ? 1:0;
      real cq = dirQ ? 1:0;
      // Handle horizontal and vertical lines.
      if(v.x == 0) {
        if(m.x <= z.x && z.x <= M.x)
          if (dot(v, m - z) < 0) {
            ptp = (z.x, z.y + cp * (m.y - z.y));
            ptq = (z.x, q.y + cq * (M.y - q.y));
          } else {
            ptq = (z.x, q.y + cq * (m.y - q.y));
            ptp = (z.x, z.y + cp * (M.y - z.y));
          }
      } else if(v.y == 0) {
        if (dot(v, m - z) < 0) {
          ptp = (z.x + cp * (m.x - z.x), z.y);
          ptq = (q.x + cq * (M.x - q.x), z.y);
        } else {
          ptq = (q.x + cq * (m.x - q.x), z.y);
          ptp = (z.x + cp * (M.x - z.x), z.y);
        }
      } else {
        // Calculate the maximum and minimum t values allowed for the
        // parametric equation z + t * v
        real mx = (m.x - z.x)/v.x, Mx = (M.x - z.x)/v.x;
        real my = (m.y - z.y)/v.y, My = (M.y - z.y)/v.y;
        real tmin = max(v.x > 0 ? mx : Mx, v.y > 0 ? my : My);
        real tmax = min(v.x > 0 ? Mx : mx, v.y > 0 ? My : my);
        pair pmin = z + tmin * v;
        pair pmax = z + tmax * v;
        if(tmin <= tmax) {
          ptp = z + cp * tmin * v;
          ptq = z + (cq == 0 ? v:tmax * v);
        }
      }
      path g = ptp--ptq;
      if (length(g)>0)
        {
          if(L.s != "") {
            Label lL = L.copy();
            if(L.defaultposition) lL.position(Relative(.9));
            lL.p(p);
            lL.out(opic, g);
          }
          g = pathModifier(g);
          if(linetype(p).length == 0){
            pair m = midpoint(g);
            pen tp;
            tp = dirP ? p : addpenline(p);
            draw(opic, pathModifier(m--ptp), tp);
            tp = dirQ ? p : addpenline(p);
            draw(opic, pathModifier(m--ptq), tp);
          } else {
            draw(opic, g, p);
          }
          marker.markroutine(opic, marker.f, g);
          arrow(opic, g, p, NoMargin);
          add(f, opic.fit());
        }
    });
}

/*<asyxml><function type="void" signature="clipdraw(picture,Label,path,align,pen,arrowbar,arrowbar,real,real,Label,marker)"><code></asyxml>*/
void clipdraw(picture pic = currentpicture, Label L = "", path g,
              align align = NoAlign, pen p = currentpen,
              arrowbar arrow = None, arrowbar bar = None,
              real xmargin = 0, real ymargin = xmargin,
              Label legend = "", marker marker = nomarker)
{/*<asyxml></code><documentation>Draw the path 'g' on 'pic' clipped to the bounding box of 'pic'.</documentation></function></asyxml>*/
  if(L.s != "") {
    picture tmp;
    label(tmp, L, g, p);
    add(pic, tmp);
  }
  pic.add(new void (frame f, transform t, transform T, pair m, pair M) {
      // Reduce the bounds by the size of the pen and the margins.
      m += min(p) + (xmargin, ymargin); M -= max(p) + (xmargin, ymargin);
      path bound = box(m, M);
      picture tmp;
      draw(tmp, "", t * T * g, align, p, arrow, bar, NoMargin, legend, marker);
      clip(tmp, bound);
      add(f, tmp.fit());
    });
}

/*<asyxml><function type="void" signature="distance(picture pic,Label,point,point,bool,real,pen,pen,arrow)"><code></asyxml>*/
void distance(picture pic = currentpicture, Label L = "", point A, point B,
              bool rotated = true, real offset = 3mm,
              pen p = currentpen, pen joinpen = invisible,
              arrowbar arrow = Arrows(NoFill))
{/*<asyxml></code><documentation>Draw arrow between A and B (from FAQ).</documentation></function></asyxml>*/
  pair A = A, B = B;
  path g = A--B;
  transform Tp = shift(-offset * unit(B - A) * I);
  pic.add(new void(frame f, transform t) {
      picture opic;
      path G = Tp * t * g;
      transform id = identity();
      transform T = rotated ? rotate(B - A) : id;
      Label L = L.copy();
      L.align(L.align, Center);
      if(abs(ypart((conj(A - B) * L.align.dir))) < epsgeo && L.filltype == NoFill)
        L.filltype = UnFill(1);
      draw(opic, T * L, G, p, arrow, Bars, PenMargins);
      pair Ap = t * A, Bp = t * B;
      draw(opic, (Ap--Tp * Ap)^^(Bp--Tp * Bp), joinpen);
      add(f, opic.fit());
    }, true);
  pic.addBox(min(g), max(g), Tp * min(p), Tp * max(p));
}

/*<asyxml><variable type="real" signature="perpfactor"><code></asyxml>*/
real perpfactor = 1;/*<asyxml></code><documentation>Factor for drawing perpendicular symbol.</documentation></variable></asyxml>*/
/*<asyxml><function type="void" signature="perpendicularmark(picture,point,explicit pair,explicit pair,real,pen,margin,filltype)"><code></asyxml>*/
void perpendicularmark(picture pic = currentpicture, point z,
                       explicit pair align,
                       explicit pair dir = E, real size = 0,
                       pen p = currentpen,
                       margin margin = NoMargin,
                       filltype filltype = NoFill)
{/*<asyxml></code><documentation>Draw a perpendicular symbol at z aligned in the direction align
   relative to the path z--z + dir.
   dir(45 + n * 90), where n in N*, are common values for 'align'.</documentation></function></asyxml>*/
  p = squarecap + p;
  if(size == 0) size = perpfactor * 3mm + sqrt(1 + linewidth(p)) - 1;
  frame apic;
  pair d1 = size * align * unit(dir) * dir(-45);
  pair d2 = I * d1;
  path g = d1--d1 + d2--d2;
  g = margin(g, p).g;
  draw(apic, g, p);
  if(filltype != NoFill) filltype.fill(apic, (relpoint(g, 0) - relpoint(g, 0.5)+
                                             relpoint(g, 1))--g--cycle, p + solid);
  add(pic, apic, locate(z));
}

/*<asyxml><function type="void" signature="perpendicularmark(picture,point,vector,vector,real,pen,margin,filltype)"><code></asyxml>*/
void perpendicularmark(picture pic = currentpicture, point z,
                       vector align,
                       vector dir = E, real size = 0,
                       pen p = currentpen,
                       margin margin = NoMargin,
                       filltype filltype = NoFill)
{/*<asyxml></code><documentation>Draw a perpendicular symbol at z aligned in the direction align
   relative to the path z--z + dir.
   dir(45 + n * 90), where n in N, are common values for 'align'.</documentation></function></asyxml>*/
  perpendicularmark(pic, z, (pair)align, (pair)dir, size,
                    p, margin, filltype);
}

/*<asyxml><function type="void" signature="perpendicularmark(picture,point,explicit pair,path,real,pen,margin,filltype)"><code></asyxml>*/
void perpendicularmark(picture pic = currentpicture, point z, explicit pair align, path g,
                       real size = 0, pen p = currentpen,
                       margin margin = NoMargin,
                       filltype filltype = NoFill)
{/*<asyxml></code><documentation>Draw a perpendicular symbol at z aligned in the direction align
   relative to the path z--z + dir(g, 0).
   dir(45 + n * 90), where n in N, are common values for 'align'.</documentation></function></asyxml>*/
  perpendicularmark(pic, z, align, dir(g, 0), size, p, margin, filltype);
}

/*<asyxml><function type="void" signature="perpendicularmark(picture,point,vector,path,real,pen,margin,filltype)"><code></asyxml>*/
void perpendicularmark(picture pic = currentpicture, point z, vector align, path g,
                       real size = 0, pen p = currentpen,
                       margin margin = NoMargin,
                       filltype filltype = NoFill)
{/*<asyxml></code><documentation>Draw a perpendicular symbol at z aligned in the direction align
   relative to the path z--z + dir(g, 0).
   dir(45 + n * 90), where n in N, are common values for 'align'.</documentation></function></asyxml>*/
  perpendicularmark(pic, z, (pair)align, dir(g, 0), size, p, margin, filltype);
}

/*<asyxml><function type="void" signature="markrightangle(picture,point,point,point,real,pen,margin,filltype)"><code></asyxml>*/
void markrightangle(picture pic = currentpicture, point A, point O,
                    point B, real size = 0, pen p = currentpen,
                    margin margin = NoMargin,
                    filltype filltype = NoFill)
{/*<asyxml></code><documentation>Mark the angle AOB with a perpendicular symbol.</documentation></function></asyxml>*/
  pair Ap = A, Bp = B, Op = O;
  pair dir = Ap - Op;
  real a1 = degrees(dir);
  pair align = rotate(-a1) * unit(dir(Op--Ap, Op--Bp));
  if (margin == NoMargin)
    margin = TrueMargin(linewidth(currentpen)/2, linewidth(currentpen)/2);
  perpendicularmark(pic = pic, z = O, align = align,
                    dir = dir, size = size, p = p,
                    margin = margin, filltype = filltype);
}

/*<asyxml><function type="bool" signature="simeq(point,point,real)"><code></asyxml>*/
bool simeq(point A, point B, real fuzz = epsgeo)
{/*<asyxml></code><documentation>Return true iff abs(A - B) < fuzz.
   This routine is used internally to know if two points are equal, in particular by the operator == in 'point == point'.</documentation></function></asyxml>*/
  return (abs(A - B) < fuzz);
}
bool simeq(point a, real b, real fuzz = epsgeo)
{
  coordsys R = a.coordsys;
  return (abs(a - point(R, ((pair)b)/R)) < fuzz);
}

/*<asyxml><function type="pair" signature="attract(pair,path,real)"><code></asyxml>*/
pair attract(pair m, path g, real fuzz = 0)
{/*<asyxml></code><documentation>Return the nearest point (A PAIR) of 'm' which is on the path g.
   'fuzz' is the argument 'fuzz' of 'intersect'.</documentation></function></asyxml>*/
  if(intersect(m, g, fuzz).length > 0) return m;
  pair p;
  real step = 1, r = 0;
  real[] t;
  static real eps = sqrt(realEpsilon);
  do {// Find a radius for intersection
    r += step;
    t = intersect(shift(m) * scale(r) * unitcircle, g);
  } while(t.length <= 0);
  p = point(g, t[1]);
  real rm = 0, rM = r;
  while(rM - rm > eps) {
    r = (rm + rM)/2;
    t = intersect(shift(m) * scale(r) * unitcircle, g, fuzz);
    if(t.length <= 0) {
      rm = r;
    } else {
      rM = r;
      p = point(g, t[1]);
    }
  }
  return p;
}

/*<asyxml><function type="point" signature="attract(point,path,real)"><code></asyxml>*/
point attract(point M, path g, real fuzz = 0)
{/*<asyxml></code><documentation>Return the nearest point (A POINT) of 'M' which is on the path g.
   'fuzz' is the argument 'fuzz' of 'intersect'.</documentation></function></asyxml>*/
  return point(M.coordsys, attract(locate(M), g)/M.coordsys);
}

/*<asyxml><function type="real[]" signature="intersect(path,explicit pair)"><code></asyxml>*/
real[] intersect(path g, explicit pair p, real fuzz = 0)
{/*<asyxml></code><documentation></documentation></function></asyxml>*/
  fuzz = fuzz <= 0 ? sqrt(realEpsilon) : fuzz;
  real[] or;
  real r = realEpsilon;
  do{
    or = intersect(g, shift(p) * scale(r) * unitcircle, fuzz);
    r *= 2;
  } while(or.length == 0);
  return or;
}

/*<asyxml><function type="real[]" signature="intersect(path,explicit point)"><code></asyxml>*/
real[] intersect(path g, explicit point P, real fuzz = epsgeo)
{/*<asyxml></code><documentation></documentation></function></asyxml>*/
  return intersect(g, locate(P), fuzz);
}
// *.........................BASES.........................*
// *=======================================================*

// *=======================================================*
// *.........................LINES.........................*
/*<asyxml><struct signature="line"><code></asyxml>*/
struct line
{/*<asyxml></code><documentation>This structure provides the objects line, semi - line and segment oriented from A to B.
   All the calculus with this structure will be as exact as Asymptote can do.
   For a full precision, you must not cast 'line' to 'path' excepted for drawing routines.</documentation></asyxml>*/
  /*<asyxml><property type = "point" signature="A,B"><code></asyxml>*/
  restricted point A,B;/*<asyxml></code><documentation>Two line's points with same coordinate system.</documentation></property><property type = "bool" signature="extendA,extendB"><code></asyxml>*/
  bool extendA,extendB;/*<asyxml></code><documentation>If true,extend 'l' in direction of A (resp. B).</documentation></property><property type = "vector" signature="u,v"><code></asyxml>*/
  restricted vector u,v;/*<asyxml></code><documentation>u = unit(AB) = direction vector,v = normal vector.</documentation></property><property type = "real" signature="a,b,c"><code></asyxml>*/
  restricted real a,b,c;/*<asyxml></code><documentation>Coefficients of the equation ax + by + c = 0 in the coordinate system of 'A'.</documentation></property><property type = "real" signature="slope,origin"><code></asyxml>*/
  restricted real slope, origin;/*<asyxml></code><documentation>Slope and ordinate at the origin.</documentation></property></asyxml>*/
  /*<asyxml><method type = "line" signature="copy()"><code></asyxml>*/
  line copy()
  {/*<asyxml></code><documentation>Copy a line in a new instance.</documentation></method></asyxml>*/
    line l = new line;
    l.A = A;
    l.B = B;
    l.a = a;
    l.b = b;
    l.c = c;
    l.slope = slope;
    l.origin = origin;
    l.u = u;
    l.v = v;
    l.extendA = extendA;
    l.extendB = extendB;
    return l;
  }

  /*<asyxml><method type = "void" signature="init(point,bool,point,bool)"><code></asyxml>*/
  void init(point A, bool extendA = true, point B, bool extendB = true)
  {/*<asyxml></code><documentation>Initialize line.
     If 'extendA' is true, the "line" is infinite in the direction of A.</documentation></method></asyxml>*/
    point[] P = standardizecoordsys(A, B);
    this.A = P[0];
    this.B = P[1];
    this.a = B.y - A.y;
    this.b = A.x - B.x;
    this.c = A.y * B.x - A.x * B.y;
    this.slope= (this.b == 0) ? infinity : -this.a/this.b;
    this.origin = (this.b == 0) ? (this.c == 0) ? 0:infinity : -this.c/this.b;
    this.u = unit(P[1]-P[0]);
    //     int tmp = sgnd(this.slope);
    //     this.u = (dot((pair)this.u, N) >= 0) ? tmp * this.u : -tmp * this.u;
    this.v = rotate(90, point(P[0].coordsys, (0, 0))) * this.u;
    this.extendA = extendA;
    this.extendB = extendB;
  }
}/*<asyxml></struct></asyxml>*/

/*<asyxml><function type="line" signature="line(point,bool,point,bool)"><code></asyxml>*/
line line(point A, bool extendA = true, point B, bool extendB = true)
{/*<asyxml></code><documentation>Return the line passing through 'A' and 'B'.
   If 'extendA' is true, the "line" is infinite in the direction of A.
   A "line" can be half-line or segment.</documentation></function></asyxml>*/
  if (A == B) abort("line: the points must be distinct.");
  line l;
  l.init(A, extendA, B, extendB);
  return l;
}

/*<asyxml><struct signature="segment"><code></asyxml>*/
struct segment
{/*<asyxml></code><documentation><look href = "struct line"/>.</documentation></asyxml>*/
  restricted point A, B;// Extremity.
  restricted vector u, v;// u = direction vector, v = normal vector.
  restricted real a, b, c;// Coefficients of the équation ax + by + c = 0
  restricted real slope, origin;
  segment copy()
  {
    segment s = new segment;
    s.A = A;
    s.B = B;
    s.a = a;
    s.b = b;
    s.c = c;
    s.slope = slope;
    s.origin = origin;
    s.u = u;
    s.v = v;
    return s;
  }

  void init(point A, point B)
  {
    line l;
    l.init(A, B);
    this.A = l.A; this.B = l.B;
    this.a = l.a; this.b = l.b; this.c = l.c;
    this.slope = l.slope; this.origin = l.origin;
    this.u = l.u; this.v = l.v;
  }
}/*<asyxml></struct></asyxml>*/

/*<asyxml><function type="segment" signature="segment(point,point)"><code></asyxml>*/
segment segment(point A, point B)
{/*<asyxml></code><documentation>Return the segment whose the extremities are A and B.</documentation></function></asyxml>*/
  segment s;
  s.init(A, B);
  return s;
}

/*<asyxml><function type="real" signature="length(segment)"><code></asyxml>*/
real length(segment s)
{/*<asyxml></code><documentation>Return the length of 's'.</documentation></function></asyxml>*/
  return abs(s.A - s.B);
}

/*<asyxml><operator type = "line" signature="cast(segment)"><code></asyxml>*/
line operator cast(segment s)
{/*<asyxml></code><documentation>A segment is casted to a "finite line".</documentation></operator></asyxml>*/
  return line(s.A, false, s.B, false);
}

/*<asyxml><operator type = "segment" signature="cast(line)"><code></asyxml>*/
segment operator cast(line l)
{/*<asyxml></code><documentation>Cast line 'l' to segment [l.A l.B].</documentation></operator></asyxml>*/
  return segment(l.A, l.B);
}

/*<asyxml><operator type = "line" signature="*(transform,line)"><code></asyxml>*/
line operator *(transform t, line l)
{/*<asyxml></code><documentation>Provide transform * line</documentation></operator></asyxml>*/
  return line(t * l.A, l.extendA, t * l.B, l.extendB);
}
/*<asyxml><operator type = "line" signature="/(line,real)"><code></asyxml>*/
line operator /(line l, real x)
{/*<asyxml></code><documentation>Provide l/x.
   Return the line passing through l.A/x and l.B/x.</documentation></operator></asyxml>*/
  return line(l.A/x, l.extendA, l.B/x, l.extendB);
}
line operator /(line l, int x){return line(l.A/x, l.B/x);}
/*<asyxml><operator type = "line" signature="*(real,line)"><code></asyxml>*/
line operator *(real x, line l)
{/*<asyxml></code><documentation>Provide x * l.
   Return the line passing through x * l.A and x * l.B.</documentation></operator></asyxml>*/
  return line(x * l.A, l.extendA, x * l.B, l.extendB);
}
line operator *(int x, line l){return line(x * l.A, l.extendA, x * l.B, l.extendB);}

/*<asyxml><operator type = "line" signature="*(point,line)"><code></asyxml>*/
line operator *(point M, line l)
{/*<asyxml></code><documentation>Provide point * line.
   Return the line passing through unit(M) * l.A and unit(M) * l.B.</documentation></operator></asyxml>*/
  return line(unit(M) * l.A, l.extendA, unit(M) * l.B, l.extendB);
}
/*<asyxml><operator type = "line" signature="+(line,point)"><code></asyxml>*/
line operator +(line l, vector u)
{/*<asyxml></code><documentation>Provide line + vector (and so line + point).
   Return the line 'l' shifted by 'u'.</documentation></operator></asyxml>*/
  return line(l.A + u, l.extendA, l.B + u, l.extendB);
}
/*<asyxml><operator type = "line" signature="-(line,vector)"><code></asyxml>*/
line operator -(line l, vector u)
{/*<asyxml></code><documentation>Provide line - vector (and so line - point).
   Return the line 'l' shifted by '-u'.</documentation></operator></asyxml>*/
  return line(l.A - u, l.extendA, l.B - u, l.extendB);
}

/*<asyxml><operator type = "line[]" signature="^^(line,line)"><code></asyxml>*/
line[] operator ^^(line l1, line l2)
{/*<asyxml></code><documentation>Provide line^^line.
   Return the line array {l1, l2}.</documentation></operator></asyxml>*/
  line[] ol;
  ol.push(l1); ol.push(l2);
  return ol;
}

/*<asyxml><operator type = "line[]" signature="^^(line,line[])"><code></asyxml>*/
line[] operator ^^(line l1, line[] l2)
{/*<asyxml></code><documentation>Provide line^^line[].
   Return the line array {l1, l2[0], l2[1]...}.
   line[]^^line is also defined.</documentation></operator></asyxml>*/
  line[] ol;
  ol.push(l1);
  for (int i = 0; i < l2.length; ++i) {
    ol.push(l2[i]);
  }
  return ol;
}
line[] operator ^^(line[] l2, line l1)
{
  line[] ol = l2;
  ol.push(l1);
  return ol;
}

/*<asyxml><operator type = "line[]" signature="^^(line,line[])"><code></asyxml>*/
line[] operator ^^(line l1[], line[] l2)
{/*<asyxml></code><documentation>Provide line[]^^line[].
   Return the line array {l1[0], l1[1], ..., l2[0], l2[1], ...}.</documentation></operator></asyxml>*/
  line[] ol = l1;
  for (int i = 0; i < l2.length; ++i) {
    ol.push(l2[i]);
  }
  return ol;
}

/*<asyxml><function type="bool" signature="sameside(point,point,line)"><code></asyxml>*/
bool sameside(point M, point P, line l)
{/*<asyxml></code><documentation>Return 'true' iff 'M' and 'N' are same side of the line (or on the line) 'l'.</documentation></function></asyxml>*/
  pair A = l.A, B = l.B, m = M, p = P;
  pair mil = (A + B)/2;
  pair mA = rotate(90, mil) * A;
  pair mB = rotate(-90, mil) * A;
  return (abs(m - mA) <= abs(m - mB)) == (abs(p - mA) <= abs(p - mB));
  // transform proj = projection(l.A, l.B);
  // point Mp = proj * M;
  // point Pp = proj * P;
  // dot(Mp);dot(Pp);
  // return dot(locate(Mp - M), locate(Pp - P)) >= 0;
}

/*<asyxml><function type="line" signature="line(segment)"><code></asyxml>*/
line line(segment s)
{/*<asyxml></code><documentation>Return the line passing through 's.A'
   and 's.B'.</documentation></function></asyxml>*/
  return line(s.A, s.B);
}
/*<asyxml><function type="segment" signature="segment(line)"><code></asyxml>*/
segment segment(line l)
{/*<asyxml></code><documentation>Return the segment whose extremities
   are 'l.A' and 'l.B'.</documentation></function></asyxml>*/
  return segment(l.A, l.B);
}

/*<asyxml><function type="point" signature="midpoint(segment)"><code></asyxml>*/
point midpoint(segment s)
{/*<asyxml></code><documentation>Return the midpoint of 's'.</documentation></function></asyxml>*/
  return 0.5 * (s.A + s.B);
}

/*<asyxml><function type="void" signature="write(line)"><code></asyxml>*/
void write(explicit line l)
{/*<asyxml></code><documentation>Write some informations about 'l'.</documentation></function></asyxml>*/
  write("A = "+(string)((pair)l.A));
  write("Extend A = "+(l.extendA ? "true" : "false"));
  write("B = "+(string)((pair)l.B));
  write("Extend B = "+(l.extendB ? "true" : "false"));
  write("u = "+(string)((pair)l.u));
  write("v = "+(string)((pair)l.v));
  write("a = "+(string) l.a);
  write("b = "+(string) l.b);
  write("c = "+(string) l.c);
  write("slope = "+(string) l.slope);
  write("origin = "+(string) l.origin);
}

/*<asyxml><function type="void" signature="write(explicit segment)"><code></asyxml>*/
void write(explicit segment s)
{/*<asyxml></code><documentation>Write some informations about 's'.</documentation></function></asyxml>*/
  write("A = "+(string)((pair)s.A));
  write("B = "+(string)((pair)s.B));
  write("u = "+(string)((pair)s.u));
  write("v = "+(string)((pair)s.v));
  write("a = "+(string) s.a);
  write("b = "+(string) s.b);
  write("c = "+(string) s.c);
  write("slope = "+(string) s.slope);
  write("origin = "+(string) s.origin);
}

/*<asyxml><operator type = "bool" signature="==(line,line)"><code></asyxml>*/
bool operator ==(line l1, line l2)
{/*<asyxml></code><documentation>Provide the test 'line == line'.</documentation></operator></asyxml>*/
  return (collinear(l1.u, l2.u) &&
          abs(ypart((locate(l1.A) - locate(l1.B))/(locate(l1.A) - locate(l2.B)))) < epsgeo &&
          l1.extendA == l2.extendA && l1.extendB == l2.extendB);
}

/*<asyxml><operator type = "bool" signature="!=(line,line)"><code></asyxml>*/
bool operator !=(line l1, line l2)
{/*<asyxml></code><documentation>Provide the test 'line != line'.</documentation></operator></asyxml>*/
  return !(l1 == l2);
}

/*<asyxml><operator type = "bool" signature="@(point,line)"><code></asyxml>*/
bool operator @(point m, line l)
{/*<asyxml></code><documentation>Provide the test 'point @ line'.
   Return true iff 'm' is on the 'l'.</documentation></operator></asyxml>*/
  point M = changecoordsys(l.A.coordsys, m);
  if (abs(l.a * M.x + l.b * M.y + l.c) >= epsgeo) return false;
  if (l.extendA && l.extendB) return true;
  if (!l.extendA && !l.extendB) return between(l.A, M, l.B);
  if (l.extendA) return sameside(M, l.A, l.B);
  return sameside(M, l.B, l.A);
}

/*<asyxml><function type="coordsys" signature="coordsys(line)"><code></asyxml>*/
coordsys coordsys(line l)
{/*<asyxml></code><documentation>Return the coordinate system in which 'l' is defined.</documentation></function></asyxml>*/
  return l.A.coordsys;
}

/*<asyxml><function type="line" signature="reverse(line)"><code></asyxml>*/
line reverse(line l)
{/*<asyxml></code><documentation>Permute the points 'A' and 'B' of 'l' and so its orientation.</documentation></function></asyxml>*/
  return line(l.B, l.extendB, l.A, l.extendA);
}

/*<asyxml><function type="line" signature="extend(line)"><code></asyxml>*/
line extend(line l)
{/*<asyxml></code><documentation>Return the infinite line passing through 'l.A' and 'l.B'.</documentation></function></asyxml>*/
  line ol = l.copy();
  ol.extendA = true;
  ol.extendB = true;
  return ol;
}

/*<asyxml><function type="line" signature="complementary(explicit line)"><code></asyxml>*/
line complementary(explicit line l)
{/*<asyxml></code><documentation>Return the complementary of a half-line with respect of
   the full line 'l'.</documentation></function></asyxml>*/
  if (l.extendA && l.extendB)
    abort("complementary: the parameter is not a half-line.");
  point origin = l.extendA ? l.B : l.A;
  point ptdir = l.extendA ?
    rotate(180, l.B) * l.A : rotate(180, l.A) * l.B;
  return line(origin, false, ptdir);
}

/*<asyxml><function type="line[]" signature="complementary(explicit segment)"><code></asyxml>*/
line[] complementary(explicit segment s)
{/*<asyxml></code><documentation>Return the two half-lines of origin 's.A' and 's.B' respectively.</documentation></function></asyxml>*/
  line[] ol = new line[2];
  ol[0] = complementary(line(s.A, false, s.B));
  ol[1] = complementary(line(s.A, s.B, false));
  return ol;
}

/*<asyxml><function type="line" signature="Ox(coordsys)"><code></asyxml>*/
line Ox(coordsys R = currentcoordsys)
{/*<asyxml></code><documentation>Return the x-axis of 'R'.</documentation></function></asyxml>*/
  return line(point(R, (0, 0)), point(R, E));
}
/*<asyxml><constant type = "line" signature="Ox"><code></asyxml>*/
restricted line Ox = Ox();/*<asyxml></code><documentation>the x-axis of
                          the default coordinate system.</documentation></constant></asyxml>*/

/*<asyxml><function type="line" signature="Oy(coordsys)"><code></asyxml>*/
line Oy(coordsys R = currentcoordsys)
{/*<asyxml></code><documentation>Return the y-axis of 'R'.</documentation></function></asyxml>*/
  return line(point(R, (0, 0)), point(R, N));
}
/*<asyxml><constant type = "line" signature="Oy"><code></asyxml>*/
restricted line Oy = Oy();/*<asyxml></code><documentation>the y-axis of
                          the default coordinate system.</documentation></constant></asyxml>*/

/*<asyxml><function type="line" signature="line(real,point)"><code></asyxml>*/
line line(real a, point A = point(currentcoordsys, (0, 0)))
{/*<asyxml></code><documentation>Return the line passing through 'A' with an
   angle (in the coordinate system of A) 'a' in degrees.
   line(point, real) is also defined.</documentation></function></asyxml>*/
  return line(A, A + point(A.coordsys, A.coordsys.polar(1, radians(a))));
}
line line(point A = point(currentcoordsys, (0, 0)), real a)
{
  return line(a, A);
}
line line(int a, point A = point(currentcoordsys, (0, 0)))
{
  return line((real)a, A);
}

/*<asyxml><function type="line" signature="line(coordsys,real,real)"><code></asyxml>*/
line line(coordsys R = currentcoordsys, real slope, real origin)
{/*<asyxml></code><documentation>Return the line defined by slope and y-intercept relative to 'R'.</documentation></function></asyxml>*/
  if (slope == infinity || slope == -infinity)
    abort("The slope is infinite. Please, use the routine 'vline'.");
  return line(point(R, (0, origin)), point(R, (1, origin + slope)));
}

/*<asyxml><function type="line" signature="line(coordsys,real,real,real)"><code></asyxml>*/
line line(coordsys R = currentcoordsys, real a, real b, real c)
{/*<asyxml></code><documentation>Retrun the line defined by equation relative to 'R'.</documentation></function></asyxml>*/
  if (a == 0 && b == 0) abort("line: inconsistent equation...");
  pair M;
  M = (a == 0) ? (0, -c/b) : (-c/a, 0);
  return line(point(R, M), point(R, M + (-b, a)));
}

/*<asyxml><function type="line" signature="vline(coordsys)"><code></asyxml>*/
line vline(coordsys R = currentcoordsys)
{/*<asyxml></code><documentation>Return a vertical line in 'R' passing through the origin of 'R'.</documentation></function></asyxml>*/
  point P = point(R, (0, 0));
  point PP = point(R, (R.O + N)/R);
  return line(P, PP);
}
/*<asyxml><constant type = "line" signature="vline"><code></asyxml>*/
restricted line vline = vline();/*<asyxml></code><documentation>The vertical line in the current coordinate system passing
                                through the origin of this system.</documentation></constant></asyxml>*/

/*<asyxml><function type="line" signature="hline(coordsys)"><code></asyxml>*/
line hline(coordsys R = currentcoordsys)
{/*<asyxml></code><documentation>Return a horizontal line in 'R' passing through the origin of 'R'.</documentation></function></asyxml>*/
  point P = point(R, (0, 0));
  point PP = point(R, (R.O + E)/R);
  return line(P, PP);
}
/*<asyxml><constant type = "line" signature="hline"><code></asyxml>*/
line hline = hline();/*<asyxml></code><documentation>The horizontal line in the current coordinate system passing
                     through the origin of this system.</documentation></constant></asyxml>*/

/*<asyxml><function type="line" signature="changecoordsys(coordsys,line)"><code></asyxml>*/
line changecoordsys(coordsys R, line l)
{/*<asyxml></code><documentation>Return the line 'l' in the coordinate system 'R'.</documentation></function></asyxml>*/
  point A = changecoordsys(R, l.A);
  point B = changecoordsys(R, l.B);
  return line(A, B);
}

/*<asyxml><function type="transform" signature="scale(real,line,line,bool)"><code></asyxml>*/
transform scale(real k, line l1, line l2, bool safe = false)
{/*<asyxml></code><documentation>Return the dilatation with respect to
   'l1' in the direction of 'l2'.</documentation></function></asyxml>*/
  return scale(k, l1.A, l1.B, l2.A, l2.B, safe);
}

/*<asyxml><function type="transform" signature="reflect(line)"><code></asyxml>*/
transform reflect(line l)
{/*<asyxml></code><documentation>Return the reflect about the line 'l'.</documentation></function></asyxml>*/
  return reflect((pair)l.A, (pair)l.B);
}

/*<asyxml><function type="transform" signature="reflect(line,line)"><code></asyxml>*/
transform reflect(line l1, line l2, bool safe = false)
{/*<asyxml></code><documentation>Return the reflect about the line
   'l1' in the direction of 'l2'.</documentation></function></asyxml>*/
  return scale(-1.0, l1, l2, safe);
}


/*<asyxml><function type="point[]" signature="intersectionpoints(line,path)"><code></asyxml>*/
point[] intersectionpoints(line l, path g)
{/*<asyxml></code><documentation>Return all points of intersection of the line 'l' with the path 'g'.</documentation></function></asyxml>*/
  // TODO utiliser la version 1.44 de intersections(path g, pair p, pair q)
  // real [] t = intersections(g, l.A, l.B);
  // coordsys R = coordsys(l);
  // return sequence(new point(int n){return point(R, point(g, t[n])/R);}, t.length);
  real [] t;
  pair[] op;
  pair A = l.A;
  pair B = l.B;
  real dy = B.y - A.y,
    dx = A.x - B.x,
    lg = length(g);

  for (int i = 0; i < lg; ++i)
    {
      pair z0 = point(g, i),
        z1 = point(g, i + 1),
        c0 = postcontrol(g, i),
        c1 = precontrol(g, i + 1),
        t3 = z1 - z0 - 3 * c1 + 3 * c0,
        t2 = 3 * z0 + 3 * c1 - 6 * c0,
        t1 = 3 * c0 - 3z0;
      real a = dy * t3.x + dx * t3.y,
        b = dy * t2.x + dx * t2.y,
        c = dy * t1.x + dx * t1.y,
        d = dy * z0.x + dx * z0.y + A.y * B.x - A.x * B.y;

      t = cubicroots(a, b, c, d);
      for (int j = 0; j < t.length; ++j)
        if (
            t[j]>=0
            && (
                t[j]<1
                || (
                    t[j] == 1
                    && (i == lg - 1)
                    && !cyclic(g)
                    )
                )
            ) {
          op.push(point(g, i + t[j]));
        }
    }

  point[] opp;
  for (int i = 0; i < op.length; ++i)
    opp.push(point(coordsys(l), op[i]/coordsys(l)));
  return opp;
}

/*<asyxml><function type="point" signature="intersectionpoint(line,line)"><code></asyxml>*/
point intersectionpoint(line l1, line l2)
{/*<asyxml></code><documentation>Return the point of intersection of line 'l1' with 'l2'.
   If 'l1' and 'l2' have an infinity or none point of intersection,
   this routine return (infinity, infinity).</documentation></function></asyxml>*/
  point[] P = standardizecoordsys(l1.A, l1.B, l2.A, l2.B);
  coordsys R = P[0].coordsys;
  pair p = extension(P[0], P[1], P[2], P[3]);
  if(finite(p)){
    point p = point(R, p/R);
    if (p @ l1 && p @ l2) return p;
  }
  return point(R, (infinity, infinity));
}

/*<asyxml><function type="line" signature="parallel(point,line)"><code></asyxml>*/
line parallel(point M, line l)
{/*<asyxml></code><documentation>Return the line parallel to 'l' passing through 'M'.</documentation></function></asyxml>*/
  point A, B;
  if (M.coordsys != coordsys(l))
    {
      A = changecoordsys(M.coordsys, l.A);
      B = changecoordsys(M.coordsys, l.B);
    } else {A = l.A;B = l.B;}
  return line(M, M - A + B);
}

/*<asyxml><function type="line" signature="parallel(point,explicit vector)"><code></asyxml>*/
line parallel(point M, explicit vector dir)
{/*<asyxml></code><documentation>Return the line of direction 'dir' and passing through 'M'.</documentation></function></asyxml>*/
  return line(M, M + locate(dir));
}

/*<asyxml><function type="line" signature="parallel(point,explicit pair)"><code></asyxml>*/
line parallel(point M, explicit pair dir)
{/*<asyxml></code><documentation>Return the line of direction 'dir' and passing through 'M'.</documentation></function></asyxml>*/
  return line(M, M + vector(currentcoordsys, dir));
}

/*<asyxml><function type="bool" signature="parallel(line,line)"><code></asyxml>*/
bool parallel(line l1, line l2, bool strictly = false)
{/*<asyxml></code><documentation>Return 'true' if 'l1' and 'l2' are (strictly ?) parallel.</documentation></function></asyxml>*/
  bool coll = collinear(l1.u, l2.u);
  return strictly ? coll && (l1 != l2) : coll;
}

/*<asyxml><function type="bool" signature="concurrent(...line[])"><code></asyxml>*/
bool concurrent(... line[] l)
{/*<asyxml></code><documentation>Returns true if all the lines 'l' are concurrent.</documentation></function></asyxml>*/
  if (l.length < 3) abort("'concurrent' needs at least for three lines ...");
  pair point = intersectionpoint(l[0], l[1]);
  bool conc;
  for (int i = 2; i < l.length; ++i) {
    pair pt = intersectionpoint(l[i - 1], l[i]);
    conc = simeq(pt, point);
    if (!conc) break;
  }
  return conc;
}

/*<asyxml><function type="transform" signature="projection(line)"><code></asyxml>*/
transform projection(line l)
{/*<asyxml></code><documentation>Return the orthogonal projection on 'l'.</documentation></function></asyxml>*/
  return projection(l.A, l.B);
}

/*<asyxml><function type="transform" signature="projection(line,line,bool)"><code></asyxml>*/
transform projection(line l1, line l2, bool safe = false)
{/*<asyxml></code><documentation>Return the projection on (AB) in parallel of (CD).
   If 'safe = true' and (l1)//(l2) return the identity.
   If 'safe = false' and (l1)//(l2) return a infinity scaling.</documentation></function></asyxml>*/
  return projection(l1.A, l1.B, l2.A, l2.B, safe);
}

/*<asyxml><function type="transform" signature="vprojection(line,bool)"><code></asyxml>*/
transform vprojection(line l, bool safe = false)
{/*<asyxml></code><documentation>Return the projection on 'l' in parallel of N--S.
   If 'safe' is 'true' the projected point keeps the same place if 'l'
   is vertical.</documentation></function></asyxml>*/
  coordsys R = defaultcoordsys;
  return projection(l, line(point(R, N), point(R, S)), safe);
}

/*<asyxml><function type="transform" signature="hprojection(line,bool)"><code></asyxml>*/
transform hprojection(line l, bool safe = false)
{/*<asyxml></code><documentation>Return the projection on 'l' in parallel of E--W.
   If 'safe' is 'true' the projected point keeps the same place if 'l'
   is horizontal.</documentation></function></asyxml>*/
  coordsys R = defaultcoordsys;
  return projection(l, line(point(R, E), point(R, W)), safe);
}

/*<asyxml><function type="line" signature="perpendicular(point,line)"><code></asyxml>*/
line perpendicular(point M, line l)
{/*<asyxml></code><documentation>Return the perpendicular line of 'l' passing through 'M'.</documentation></function></asyxml>*/
  point Mp = projection(l) * M;
  point A = Mp == l.A ? l.B : l.A;
  return line(Mp, rotate(90, Mp) * A);
}

/*<asyxml><function type="line" signature="perpendicular(point,explicit vector)"><code></asyxml>*/
line perpendicular(point M, explicit vector normal)
{/*<asyxml></code><documentation>Return the line passing through 'M'
   whose normal is \param{normal}.</documentation></function></asyxml>*/
  return perpendicular(M, line(M, M + locate(normal)));
}

/*<asyxml><function type="line" signature="perpendicular(point,explicit pair)"><code></asyxml>*/
line perpendicular(point M, explicit pair normal)
{/*<asyxml></code><documentation>Return the line passing through 'M'
   whose normal is \param{normal} (given in the currentcoordsys).</documentation></function></asyxml>*/
  return perpendicular(M, line(M, M + vector(currentcoordsys, normal)));
}

/*<asyxml><function type="bool" signature="perpendicular(line,line)"><code></asyxml>*/
bool perpendicular(line l1, line l2)
{/*<asyxml></code><documentation>Return 'true' if 'l1' and 'l2' are perpendicular.</documentation></function></asyxml>*/
  return abs(dot(locate(l1.u), locate(l2.u))) < epsgeo ;
}

/*<asyxml><function type="real" signature="angle(line,coordsys)"><code></asyxml>*/
real angle(line l, coordsys R = coordsys(l))
{/*<asyxml></code><documentation>Return the angle of the oriented line 'l',
   in radian, in the interval ]-pi, pi] and relatively to 'R'.</documentation></function></asyxml>*/
  return angle(l.u, R, false);
}

/*<asyxml><function type="real" signature="degrees(line,coordsys,bool)"><code></asyxml>*/
real degrees(line l, coordsys R = coordsys(l))
{/*<asyxml></code><documentation>Returns the angle of the oriented line 'l' in degrees,
   in the interval [0, 360[ and relatively to 'R'.</documentation></function></asyxml>*/
  return degrees(angle(l, R));
}

/*<asyxml><function type="real" signature="sharpangle(line,line)"><code></asyxml>*/
real sharpangle(line l1, line l2)
{/*<asyxml></code><documentation>Return the measure in radians of the sharp angle formed by 'l1' and 'l2'.</documentation></function></asyxml>*/
  vector u1 = l1.u;
  vector u2 = (dot(l1.u, l2.u) < 0) ? -l2.u : l2.u;
  real a12 = angle(locate(u2)) - angle(locate(u1));
  a12 = a12%(sgnd(a12) * pi);
  if (a12 <= -pi/2) {
    a12 += pi;
  } else if (a12 > pi/2) {
    a12 -= pi;
  }
  return a12;
}

/*<asyxml><function type="real" signature="angle(line,line)"><code></asyxml>*/
real angle(line l1, line l2)
{/*<asyxml></code><documentation>Return the measure in radians of oriented angle (l1.u, l2.u).</documentation></function></asyxml>*/
  return angle(locate(l2.u)) - angle(locate(l1.u));
}

/*<asyxml><function type="real" signature="degrees(line,line)"><code></asyxml>*/
real degrees(line l1, line l2)
{/*<asyxml></code><documentation>Return the measure in degrees of the
   angle formed by the oriented lines 'l1' and 'l2'.</documentation></function></asyxml>*/
  return degrees(angle(l1, l2));
}

/*<asyxml><function type="real" signature="sharpdegrees(line,line)"><code></asyxml>*/
real sharpdegrees(line l1, line l2)
{/*<asyxml></code><documentation>Return the measure in degrees of the sharp angle formed by 'l1' and 'l2'.</documentation></function></asyxml>*/
  return degrees(sharpangle(l1, l2));
}

/*<asyxml><function type="line" signature="bisector(line,line,real,bool)"><code></asyxml>*/
line bisector(line l1, line l2, real angle = 0, bool sharp = true)
{/*<asyxml></code><documentation>Return the bisector of the angle formed by 'l1' and 'l2'
   rotated by the angle 'angle' (in degrees) around intersection point of 'l1' with 'l2'.
   If 'sharp' is true (the default), this routine returns the bisector of the sharp angle.
   Note that the returned line inherit of coordinate system of 'l1'.</documentation></function></asyxml>*/
  line ol;
  if (l1 == l2) return l1;
  point A = intersectionpoint(l1, l2);
  if (finite(A)) {
    if(sharp) ol = rotate(sharpdegrees(l1, l2)/2 + angle, A) * l1;
    else {
      coordsys R = coordsys(l1);
      pair a = A, b = A + l1.u, c = A + l2.u;
      pair pp = extension(a, a + dir(a--b, a--c), b, b + dir(b--a, b--c));
      return rotate(angle, A) * line(A, point(R, pp/R));
    }
  } else {
    ol = l1;
  }
  return ol;
}

/*<asyxml><function type="line" signature="sector(int,int,line,line,real,bool)"><code></asyxml>*/
line sector(int n = 2, int p = 1, line l1, line l2, real angle = 0, bool sharp = true)
{/*<asyxml></code><documentation>Return the p-th nth-sector of the angle
   formed by the oriented line 'l1' and 'l2'
   rotated by the angle 'angle' (in degrees) around the intersection point of 'l1' with 'l2'.
   If 'sharp' is true (the default), this routine returns the bisector of the sharp angle.
   Note that the returned line inherit of coordinate system of 'l1'.</documentation></function></asyxml>*/
  line ol;
  if (l1 == l2) return l1;
  point A = intersectionpoint(l1, l2);
  if (finite(A)) {
    if(sharp) ol = rotate(p * sharpdegrees(l1, l2)/n + angle, A) * l1;
    else {
      ol = rotate(p * degrees(l1, l2)/n + angle, A) * l1;
    }
  } else {
    ol = l1;
  }
  return ol;
}

/*<asyxml><function type="line" signature="bisector(point,point,point,point,real)"><code></asyxml>*/
line bisector(point A, point B, point C, point D, real angle = 0, bool sharp = true)
{/*<asyxml></code><documentation>Return the bisector of the angle formed by the lines (AB) and (CD).
   <look href = "#bisector(line, line, real, bool)"/>.</documentation></function></asyxml>*/
  point[] P = standardizecoordsys(A, B, C, D);
  return bisector(line(P[0], P[1]), line(P[2], P[3]), angle, sharp);
}

/*<asyxml><function type="line" signature="bisector(segment,real)"><code></asyxml>*/
line bisector(segment s, real angle = 0)
{/*<asyxml></code><documentation>Return the bisector of the segment line 's' rotated by 'angle' (in degrees) around the
   midpoint of 's'.</documentation></function></asyxml>*/
  coordsys R = coordsys(s);
  point m = midpoint(s);
  vector dir = rotateO(90) * unit(s.A - m);
  return rotate(angle, m) * line(m + dir, m - dir);
}

/*<asyxml><function type="line" signature="bisector(point,point,real)"><code></asyxml>*/
line bisector(point A, point B, real angle = 0)
{/*<asyxml></code><documentation>Return the bisector of the segment line [AB] rotated by 'angle' (in degrees) around the
   midpoint of [AB].</documentation></function></asyxml>*/
  point[] P = standardizecoordsys(A, B);
  return bisector(segment(P[0], P[1]), angle);
}

/*<asyxml><function type="real" signature="distance(point,line)"><code></asyxml>*/
real distance(point M, line l)
{/*<asyxml></code><documentation>Return the distance from 'M' to 'l'.
   distance(line, point) is also defined.</documentation></function></asyxml>*/
  point A = changecoordsys(defaultcoordsys, l.A);
  point B = changecoordsys(defaultcoordsys, l.B);
  line ll = line(A, B);
  pair m = locate(M);
  return abs(ll.a * m.x + ll.b * m.y + ll.c)/sqrt(ll.a^2 + ll.b^2);
}

real distance(line l, point M)
{
  return distance(M, l);
}

/*<asyxml><function type="void" signature="draw(picture,Label,line,bool,bool,align,pen,arrowbar,Label,marker)"><code></asyxml>*/
void draw(picture pic = currentpicture, Label L = "",
          line l, bool dirA = l.extendA, bool dirB = l.extendB,
          align align = NoAlign, pen p = currentpen,
          arrowbar arrow = None,
          Label legend = "", marker marker = nomarker,
          pathModifier pathModifier = NoModifier)
{/*<asyxml></code><documentation>Draw the line 'l' without altering the size of picture pic.
   The boolean parameters control the infinite section.
   The global variable 'linemargin' (default value is 0) allows to modify
   the bounding box in which the line must be drawn.</documentation></function></asyxml>*/
  if(!(dirA || dirB)) draw(l.A--l.B, invisible);// l is a segment.
  Drawline(pic, L, l.A, dirP = dirA, l.B, dirQ = dirB,
           align, p, arrow,
           legend, marker, pathModifier);
}

/*<asyxml><function type="void" signature="draw(picture,Label[], line[], align,pen[], arrowbar,Label,marker)"><code></asyxml>*/
void draw(picture pic = currentpicture, Label[] L = new Label[], line[] l,
          align align = NoAlign, pen[] p = new pen[],
          arrowbar arrow = None,
          Label[] legend = new Label[], marker marker = nomarker,
          pathModifier pathModifier = NoModifier)
{/*<asyxml></code><documentation>Draw each lines with the corresponding pen.</documentation></function></asyxml>*/
  for (int i = 0; i < l.length; ++i) {
    draw(pic, L.length>0 ? L[i] : "", l[i],
         align, p = p.length>0 ? p[i] : currentpen,
         arrow, legend.length>0 ? legend[i] : "", marker,
         pathModifier);
  }
}

/*<asyxml><function type="void" signature="draw(picture,Label[], line[], align,pen,arrowbar,Label,marker)"><code></asyxml>*/
void draw(picture pic = currentpicture, Label[] L = new Label[], line[] l,
          align align = NoAlign, pen p,
          arrowbar arrow = None,
          Label[] legend = new Label[], marker marker = nomarker,
          pathModifier pathModifier = NoModifier)
{/*<asyxml></code><documentation>Draw each lines with the same pen 'p'.</documentation></function></asyxml>*/
  pen[] tp = sequence(new pen(int i){return p;}, l.length);
  draw(pic, L, l, align, tp, arrow, legend, marker, pathModifier);
}

/*<asyxml><function type="void" signature="show(picture,line,pen)"><code></asyxml>*/
void show(picture pic = currentpicture, line l, pen p = red)
{/*<asyxml></code><documentation>Draw some informations of 'l'.</documentation></function></asyxml>*/
  dot("$A$", (pair)l.A, align = -locate(l.v), p);
  dot("$B$", (pair)l.B, align = -locate(l.v), p);
  draw(l, dotted);
  draw("$\vec{u}$", locate(l.A)--locate(l.A + l.u), p, Arrow);
  draw("$\vec{v}$", locate(l.A)--locate(l.A + l.v), p, Arrow);
}

/*<asyxml><function type="point[]" signature="sameside(point,line,line)"><code></asyxml>*/
point[] sameside(point M, line l1, line l2)
{/*<asyxml></code><documentation>Return two points on 'l1' and 'l2' respectively.
   The first point is from the same side of M relatively to 'l2',
   the second point is from the same side of M relatively to 'l1'.</documentation></function></asyxml>*/
  point[] op;
  coordsys R1 = coordsys(l1);
  coordsys R2 = coordsys(l2);
  if (parallel(l1, l2)) {
    op.push(projection(l1) * M);
    op.push(projection(l2) * M);
  } else {
    point O = intersectionpoint(l1, l2);
    if (M @ l2) op.push((sameside(M, O + l1.u, l2)) ? O + l1.u : rotate(180, O) * (O + l1.u));
    else op.push(projection(l1, l2) * M);
    if (M @ l1) op.push((sameside(M, O + l2.u, l1)) ? O + l2.u : rotate(180, O) * (O + l2.u));
    else {op.push(projection(l2, l1) * M);}
  }
  return op;
}

/*<asyxml><function type="void" signature="markangle(picture,Label,int,real,real,explicit line,explicit line,explicit pair,arrowbar,pen,filltype,margin,marker)"><code></asyxml>*/
void markangle(picture pic = currentpicture,
               Label L = "", int n = 1, real radius = 0, real space = 0,
               explicit line l1, explicit line l2, explicit pair align = dir(1),
               arrowbar arrow = None, pen p = currentpen,
               filltype filltype = NoFill,
               margin margin = NoMargin, marker marker = nomarker)
{/*<asyxml></code><documentation>Mark the angle (l1, l2) aligned in the direction 'align' relative to 'l1'.
   Commune values for 'align' are dir(real).</documentation></function></asyxml>*/
  if (parallel(l1, l2, true)) return;
  real al = degrees(l1, defaultcoordsys);
  pair O, A, B;
  if (radius == 0) radius = markangleradius(p);
  real d = degrees(locate(l1.u));
  align = rotate(d) * align;
  if (l1 == l2) {
    O = midpoint(segment(l1.A, l1.B));
    A = l1.A;B = l1.B;
    if (sameside(rotate(sgn(angle(B-A)) * 45, O) * A, O + align, l1)) {radius = -radius;}
  } else {
    O = intersectionpoint(extend(l1), extend(l2));
    pair R = O + align;
    point [] ss = sameside(point(coordsys(l1), R/coordsys(l1)), l1, l2);
    A = ss[0];
    B = ss[1];
  }
  markangle(pic = pic, L = L, n = n, radius = radius, space = space,
            O = O, A = A, B = B,
            arrow = arrow, p = p, filltype = filltype,
            margin = margin, marker = marker);
}

/*<asyxml><function type="void" signature="markangle(picture,Label,int,real,real,explicit line,explicit line,explicit vector,arrowbar,pen,filltype,margin,marker)"><code></asyxml>*/
void markangle(picture pic = currentpicture,
               Label L = "", int n = 1, real radius = 0, real space = 0,
               explicit line l1, explicit line l2, explicit vector align,
               arrowbar arrow = None, pen p = currentpen,
               filltype filltype = NoFill,
               margin margin = NoMargin, marker marker = nomarker)
{/*<asyxml></code><documentation>Mark the angle (l1, l2) in the direction 'dir' given relatively to 'l1'.</documentation></function></asyxml>*/
  markangle(pic, L, n, radius, space, l1, l2, (pair)align, arrow,
            p, filltype, margin, marker);
}

/*<asyxml><function type="void" signature="markangle(picture,Label,int,real,real,line,line,arrowbar,pen,filltype,margin,marker)"><code></asyxml>*/
// void markangle(picture pic = currentpicture,
//                Label L = "", int n = 1, real radius = 0, real space = 0,
//                explicit line l1, explicit line l2,
//                arrowbar arrow = None, pen p = currentpen,
//                filltype filltype = NoFill,
//                margin margin = NoMargin, marker marker = nomarker)
// {/*<asyxml></code><documentation>Mark the oriented angle (l1, l2).</documentation></function></asyxml>*/
//   if (parallel(l1, l2, true)) return;
//   real al = degrees(l1, defaultcoordsys);
//   pair O, A, B;
//   if (radius == 0) radius = markangleradius(p);
//   real d = degrees(locate(l1.u));
//   if (l1 == l2) {
//     O = midpoint(segment(l1.A, l1.B));
//   } else {
//     O = intersectionpoint(extend(l1), extend(l2));
//   }
//   A = O + locate(l1.u);
//   B = O + locate(l2.u);
//   markangle(pic = pic, L = L, n = n, radius = radius, space = space,
//             O = O, A = A, B = B,
//             arrow = arrow, p = p, filltype = filltype,
//             margin = margin, marker = marker);
// }

/*<asyxml><function type="void" signature="perpendicularmark(picture,line,line,real,pen,int,margin,filltype)"><code></asyxml>*/
void perpendicularmark(picture pic = currentpicture, line l1, line l2,
                       real size = 0, pen p = currentpen, int quarter = 1,
                       margin margin = NoMargin, filltype filltype = NoFill)
{/*<asyxml></code><documentation>Draw a right angle at the intersection point of lines and
   aligned in the 'quarter' nth quarter of circle formed by 'l1.u' and
   'l2.u'.</documentation></function></asyxml>*/
  point P = intersectionpoint(l1, l2);
  pair align = rotate(90 * (quarter - 1)) * dir(45);
  perpendicularmark(P, align, locate(l1.u), size, p, margin, filltype);
}
// *.........................LINES.........................*
// *=======================================================*

// *=======================================================*
// *........................CONICS.........................*
/*<asyxml><struct signature="bqe"><code></asyxml>*/
struct bqe
{/*<asyxml></code><documentation>Bivariate Quadratic Equation.</documentation></asyxml>*/
  /*<asyxml><property type = "real[]" signature="a"><code></asyxml>*/
  real[] a;/*<asyxml></code><documentation>a[0] * x^2 + a[1] * x * y + a[2] * y^2 + a[3] * x + a[4] * y + a[5] = 0</documentation></property><property type = "coordsys" signature="coordsys"><code></asyxml>*/
  coordsys coordsys;/*<asyxml></code></property></asyxml>*/
}/*<asyxml></struct></asyxml>*/

/*<asyxml><function type="bqe" signature="bqe(coordsys,real,real,real,real,real,real)"><code></asyxml>*/
bqe bqe(coordsys R = currentcoordsys,
        real a, real b, real c, real d, real e, real f)
{/*<asyxml></code><documentation>Return the bivariate quadratic equation
   a[0] * x^2 + a[1] * x * y + a[2] * y^2 + a[3] * x + a[4] * y + a[5] = 0
   relatively to the coordinate system R.</documentation></function></asyxml>*/
  bqe obqe;
  obqe.coordsys = R;
  obqe.a = new real[] {a, b, c, d, e, f};
  return obqe;
}

/*<asyxml><function type="bqe" signature="changecoordsys(coordsys,bqe)"><code></asyxml>*/
bqe changecoordsys(coordsys R, bqe bqe)
{/*<asyxml></code><documentation>Returns the bivariate quadratic equation relatively to 'R'.</documentation></function></asyxml>*/
  pair i = coordinates(changecoordsys(R, vector(defaultcoordsys,
                                             bqe.coordsys.i)));
  pair j = coordinates(changecoordsys(R, vector(defaultcoordsys,
                                             bqe.coordsys.j)));
  pair O = coordinates(changecoordsys(R, point(defaultcoordsys,
                                            bqe.coordsys.O)));
  real a = bqe.a[0], b = bqe.a[1], c = bqe.a[2], d = bqe.a[3], f = bqe.a[4], g = bqe.a[5];
  real ux = i.x, uy = i.y;
  real vx = j.x, vy = j.y;
  real ox = O.x, oy = O.y;
  real D = ux * vy - uy * vx;
  real ap = (a * vy^2 - b * uy * vy + c * uy^2)/D^2;
  real bpp = (-2 * a * vx * vy + b * ux * vy + b * uy * vx - 2 * c * ux * uy)/D^2;
  real cp = (a * vx^2 - b * ux * vx + c * ux^2)/D^2;
  real dp = (-2a * ox * vy^2 + 2a * oy * vx * vy + 2b * ox * uy * vy-
           b * oy * ux * vy - b * oy * uy * vx - 2c * ox * uy^2 + 2c * oy * uy * ux)/D^2+
    (d * vy - f * uy)/D;
  real fp = (2a * ox * vx * vy - b * ox * ux * vy - 2a * oy * vx^2-
           b * ox * uy * vx + 2 * b * oy * ux * vx + 2c * ox * ux * uy - 2c * oy * ux^2)/D^2+
    (f * ux - d * vx)/D;
  g = (a * ox^2 * vy^2 - 2a * ox * oy * vx * vy - b * ox^2 * uy * vy + b * ox * oy * ux * vy+
     a * oy^2 * vx^2 + b * ox * oy * uy * vx - b * oy^2 * ux * vx + c * ox^2 * uy^2-
     2 * c * ox * oy * ux * uy + c * oy^2 * ux^2)/D^2+
    (d * oy * vx + f * ox * uy - d * ox * vy - f * oy * ux)/D + g;
  bqe obqe;
  obqe.a = approximate(new real[] {ap, bpp, cp, dp, fp, g});
  obqe.coordsys = R;
  return obqe;
}

/*<asyxml><function type="bqe" signature="bqe(point,point,point,point,point)"><code></asyxml>*/
bqe bqe(point M1, point M2, point M3, point M4, point M5)
{/*<asyxml></code><documentation>Return the bqe of conic passing through the five points (if possible).</documentation></function></asyxml>*/
  coordsys R;
  pair[] pts;
  if (samecoordsys(M1, M2, M3, M4, M5)) {
    R = M1.coordsys;
    pts= new pair[] {M1.coordinates, M2.coordinates, M3.coordinates, M4.coordinates, M5.coordinates};
  } else {
    R = defaultcoordsys;
    pts= new pair[] {M1, M2, M3, M4, M5};
  }
  real[][] M;
  real[] x;
  bqe bqe;
  bqe.coordsys = R;
  for (int i = 0; i < 5; ++i) {// Try a = -1
    M[i] = new real[] {pts[i].x * pts[i].y, pts[i].y^2, pts[i].x, pts[i].y, 1};
    x[i] = pts[i].x^2;
  }
  if(abs(determinant(M)) < 1e-5) {// Try c = -1
    for (int i = 0; i < 5; ++i) {
      M[i] = new real[] {pts[i].x^2, pts[i].x * pts[i].y, pts[i].x, pts[i].y, 1};
      x[i] = pts[i].y^2;
    }
    real[] coef = solve(M, x);
    bqe.a = new real[] {coef[0], coef[1], -1, coef[2], coef[3], coef[4]};
  } else {
    real[] coef = solve(M, x);
    bqe.a = new real[] {-1, coef[0], coef[1], coef[2], coef[3], coef[4]};
  }
  bqe.a = approximate(bqe.a);
  return bqe;
}

/*<asyxml><function type="bool" signature="samecoordsys(bool...bqe[])"><code></asyxml>*/
bool samecoordsys(bool warn = true ... bqe[] bqes)
{/*<asyxml></code><documentation>Return true if all the bivariate quadratic equations have the same coordinate system.</documentation></function></asyxml>*/
  bool ret = true;
  coordsys t = bqes[0].coordsys;
  for (int i = 1; i < bqes.length; ++i) {
    ret = (t == bqes[i].coordsys);
    if(!ret) break;
    t = bqes[i].coordsys;
  }
  if(warn && !ret)
    warning("coodinatesystem",
            "the coordinate system of two bivariate quadratic equations are not
the same. The operation will be done relatively to the default coordinate
system.");
  return ret;
}

/*<asyxml><function type="real[]" signature="realquarticroots(real,real,real,real,real)"><code></asyxml>*/
real[] realquarticroots(real a, real b, real c, real d, real e)
{/*<asyxml></code><documentation>Return the real roots of the quartic equation ax^4 + b^x3 + cx^2 + dx = 0.</documentation></function></asyxml>*/
  static real Fuzz = sqrt(realEpsilon);
  pair[] zroots = quarticroots(a, b, c, d, e);
  real[] roots;
  real p(real x){return a * x^4 + b * x^3 + c * x^2 + d * x + e;}
  real prime(real x){return 4 * a * x^3 + 3 * b * x^2 + 2 * c * x + d;}
  real x;
  bool search = true;
  int n;
  void addroot(real x)
  {
    bool exist = false;
    for (int i = 0; i < roots.length; ++i) {
      if(abs(roots[i]-x) < 1e-5) {exist = true; break;}
    }
    if(!exist) roots.push(x);
  }
  for(int i = 0; i < zroots.length; ++i) {
    if(zroots[i].y == 0 || abs(p(zroots[i].x)) < Fuzz) addroot(zroots[i].x);
    else {
      if(abs(zroots[i].y) < 1e-3) {
        x = zroots[i].x;
        search = true;
        n = 200;
        while(search) {
          real tx = abs(p(x)) < Fuzz ? x : newton(iterations = n, p, prime, x);
          if(tx < realMax) {
            if(abs(p(tx)) < Fuzz) {
              addroot(tx);
              search = false;
            } else if(n < 200) n *=2;
            else {
              search = false;
            }
          } else search = false; //It's not a real root.
        }
      }
    }
  }
  return roots;
}

/*<asyxml><function type="point[]" signature="intersectionpoints(bqe,bqe)"><code></asyxml>*/
point[] intersectionpoints(bqe bqe1, bqe bqe2)
{/*<asyxml></code><documentation>Return the interscetion of the two conic sections whose equations are 'bqe1' and 'bqe2'.</documentation></function></asyxml>*/
  coordsys R = bqe1.coordsys;
  bqe lbqe1, lbqe2;
  real[] a, b;
  if(R != bqe2.coordsys) {
    R = currentcoordsys;
    a = changecoordsys(R, bqe1).a;
    b = changecoordsys(R, bqe2).a;
  } else {
    a = bqe1.a;
    b = bqe2.a;
  }
  static real e = 100 * sqrt(realEpsilon);
  real[] x, y, c;
  point[] P;
  if(abs(a[0]-b[0]) > e || abs(a[1]-b[1]) > e || abs(a[2]-b[2]) > e) {
    c = new real[] {-2 * a[0]*a[2]*b[0]*b[2]+a[0]*a[2]*b[1]^2 - a[0]*a[1]*b[2]*b[1]+a[1]^2 * b[0]*b[2]-
                  a[2]*a[1]*b[0]*b[1]+a[0]^2 * b[2]^2 + a[2]^2 * b[0]^2,
                  -a[2]*a[1]*b[0]*b[4]-a[2]*a[4]*b[0]*b[1]-a[1]*a[3]*b[2]*b[1]+2 * a[0]*a[2]*b[1]*b[4]-
                  a[0]*a[1]*b[2]*b[4]+a[1]^2 * b[2]*b[3]-2 * a[2]*a[3]*b[0]*b[2]-2 * a[0]*a[2]*b[2]*b[3]+
                  a[2]*a[3]*b[1]^2 - a[2]*a[1]*b[1]*b[3]+2 * a[1]*a[4]*b[0]*b[2]+2 * a[2]^2 * b[0]*b[3]-
                  a[0]*a[4]*b[2]*b[1]+2 * a[0]*a[3]*b[2]^2,
                  -a[3]*a[4]*b[2]*b[1]+a[2]*a[5]*b[1]^2 - a[1]*a[5]*b[2]*b[1]-a[1]*a[3]*b[2]*b[4]+
                  a[1]^2 * b[2]*b[5]-2 * a[2]*a[3]*b[2]*b[3]+2 * a[2]^2 * b[0]*b[5]+2 * a[0]*a[5]*b[2]^2 + a[3]^2 * b[2]^2-
                  2 * a[2]*a[5]*b[0]*b[2]+2 * a[1]*a[4]*b[2]*b[3]-a[2]*a[4]*b[1]*b[3]-2 * a[0]*a[2]*b[2]*b[5]+
                  a[2]^2 * b[3]^2 + 2 * a[2]*a[3]*b[1]*b[4]-a[2]*a[4]*b[0]*b[4]+a[4]^2 * b[0]*b[2]-a[2]*a[1]*b[3]*b[4]-
                  a[2]*a[1]*b[1]*b[5]-a[0]*a[4]*b[2]*b[4]+a[0]*a[2]*b[4]^2,
                  -a[4]*a[5]*b[2]*b[1]+a[2]*a[3]*b[4]^2 + 2 * a[3]*a[5]*b[2]^2 - a[2]*a[1]*b[4]*b[5]-
                  a[2]*a[4]*b[3]*b[4]+2 * a[2]^2 * b[3]*b[5]-2 * a[2]*a[3]*b[2]*b[5]-a[3]*a[4]*b[2]*b[4]-
                  2 * a[2]*a[5]*b[2]*b[3]-a[2]*a[4]*b[1]*b[5]+2 * a[1]*a[4]*b[2]*b[5]-a[1]*a[5]*b[2]*b[4]+
                  a[4]^2 * b[2]*b[3]+2 * a[2]*a[5]*b[1]*b[4],
                  -2 * a[2]*a[5]*b[2]*b[5]+a[4]^2 * b[2]*b[5]+a[5]^2 * b[2]^2 - a[4]*a[5]*b[2]*b[4]+a[2]*a[5]*b[4]^2+
                  a[2]^2 * b[5]^2 - a[2]*a[4]*b[4]*b[5]};
    x = realquarticroots(c[0], c[1], c[2], c[3], c[4]);
  } else {
    if(abs(b[4]-a[4]) > e){
      real D = (b[4]-a[4])^2;
      c = new real[] {(a[0]*b[4]^2 + (-a[1]*b[3]-2 * a[0]*a[4]+a[1]*a[3]) * b[4]+a[2]*b[3]^2+
                     (a[1]*a[4]-2 * a[2]*a[3]) * b[3]+a[0]*a[4]^2 - a[1]*a[3]*a[4]+a[2]*a[3]^2)/D,
                    -((a[1]*b[4]-2 * a[2]*b[3]-a[1]*a[4]+2 * a[2]*a[3]) * b[5]-a[3]*b[4]^2 + (a[4]*b[3]-a[1]*a[5]+a[3]*a[4]) * b[4]+(2 * a[2]*a[5]-a[4]^2) * b[3]+(a[1]*a[4]-2 * a[2]*a[3]) * a[5])/D,
                    a[2]*(a[5]-b[5])^2/D + a[4]*(a[5]-b[5])/(b[4]-a[4]) + a[5]};
      x = quadraticroots(c[0], c[1], c[2]);
    } else {
      if(abs(a[3]-b[3]) > e) {
        real D = b[3]-a[3];
        c = new real[] {a[2], (-a[1]*b[5] + a[4]*b[3] + a[1]*a[5] - a[3]*a[4])/D,
                      a[0]*(a[5]-b[5])^2/D^2 + a[3]*(a[5]-b[5])/D + a[5]};
        y = quadraticroots(c[0], c[1], c[2]);
        for (int i = 0; i < y.length; ++i) {
          c = new real[] {a[0], a[1]*y[i]+a[3], a[2]*y[i]^2 + a[4]*y[i]+a[5]};
          x = quadraticroots(c[0], c[1], c[2]);
          for (int j = 0; j < x.length; ++j) {
            if(abs(b[0]*x[j]^2 + b[1]*x[j]*y[i]+b[2]*y[i]^2 + b[3]*x[j]+b[4]*y[i]+b[5]) < 1e-5)
              P.push(point(R, (x[j], y[i])));
          }
        }
        return P;
      } else {
        if(abs(a[5]-b[5]) < e) abort("intersectionpoints: intersection of identical conics.");
      }
    }
  }
  for (int i = 0; i < x.length; ++i) {
    c = new real[] {a[2], a[1]*x[i]+a[4], a[0]*x[i]^2 + a[3]*x[i]+a[5]};
    y = quadraticroots(c[0], c[1], c[2]);
    for (int j = 0; j < y.length; ++j) {
      if(abs(b[0]*x[i]^2 + b[1]*x[i]*y[j]+b[2]*y[j]^2 + b[3]*x[i]+b[4]*y[j]+b[5]) < 1e-5)
        P.push(point(R, (x[i], y[j])));
    }
  }
  return P;
}

/*<asyxml><struct signature="conic"><code></asyxml>*/
struct conic
{/*<asyxml></code><documentation></documentation><property type = "real" signature="e,p,h"><code></asyxml>*/
  real e, p, h;/*<asyxml></code><documentation>BE CAREFUL: h = distance(F, D) and p = h * e (http://en.wikipedia.org/wiki/Ellipse)
                 While http://mathworld.wolfram.com/ takes p = distance(F,D).</documentation></property><property type = "point" signature="F"><code></asyxml>*/
  point F;/*<asyxml></code><documentation>Focus.</documentation></property><property type = "line" signature="D"><code></asyxml>*/
  line D;/*<asyxml></code><documentation>Directrix.</documentation></property><property type = "line" signature="l"><code></asyxml>*/
  line[] l;/*<asyxml></code><documentation>Case of degenerated conic (not yet implemented !).</documentation></property></asyxml>*/
}/*<asyxml></struct></asyxml>*/

bool degenerate(conic c)
{
  return !finite(c.p) || !finite(c.h);
}

/*ANCconic conic(point, line, real)ANC*/
conic conic(point F, line l, real e)
{/*DOC
   The conic section define by the eccentricity 'e', the focus 'F'
   and the directrix 'l'.
   Note that an eccentricity equal to 0 defines a circle centered at F,
   with a radius equal at the distance from 'F' to 'l'.
   If the coordinate system of 'F' and 'l' are not identical, the conic is
   attached to 'defaultcoordsys'.
   DOC*/
  if(e < 0) abort("conic: 'e' can't be negative.");
  conic oc;
  point[] P = standardizecoordsys(F, l.A, l.B);
  line ll;
  ll = line(P[1], P[2]);
  oc.e = e < epsgeo ? 0 : e; // Handle case of circle.
  oc.F = P[0];
  oc.D = ll;
  oc.h = distance(P[0], ll);
  oc.p = abs(e) < epsgeo ? oc.h : e * oc.h;
  return oc;
}

/*<asyxml><struct signature="circle"><code></asyxml>*/
struct circle
{/*<asyxml></code><documentation>All the calculus with this structure will be as exact as Asymptote can do.
   For a full precision, you must not cast 'circle' to 'path' excepted for drawing routines.</documentation></asyxml>*/
  /*<asyxml><property type = "point" signature="C"><code></asyxml>*/
  point C;/*<asyxml></code><documentation>Center</documentation></property><property><code></asyxml>*/
  real r;/*<asyxml></code><documentation>Radius</documentation></property><property><code></asyxml>*/
  line l;/*<asyxml></code><documentation>If the radius is infinite, this line is used instead of circle.</documentation></property></asyxml>*/
}/*<asyxml></struct></asyxml>*/

bool degenerate(circle c)
{
  return !finite(c.r);
}

line line(circle c){
  if(finite(c.r)) abort("Circle can not be casted to line here.");
  return c.l;
}

/*<asyxml><struct signature="ellipse"><code></asyxml>*/
struct ellipse
{/*<asyxml></code><documentation>Look at <html><a href = "http://mathworld.wolfram.com/Ellipse.html">http://mathworld.wolfram.com/Ellipse.html</a></html></documentation></asyxml>*/
  /*<asyxml><property type = "point" signature="F1,F2,C"><code></asyxml>*/
  restricted point F1,F2,C;/*<asyxml></code><documentation>Foci and center.</documentation></property><property type = "real" signature="a,b,c,e,p"><code></asyxml>*/
  restricted real a,b,c,e,p;/*<asyxml></code></property><property type = "real" signature="angle"><code></asyxml>*/
  restricted real angle;/*<asyxml></code><documentation>Value is degrees(F1 - F2).</documentation></property><property type = "line" signature="D1,D2"><code></asyxml>*/
  restricted line D1,D2;/*<asyxml></code><documentation>Directrices.</documentation></property><property type = "line" signature="l"><code></asyxml>*/
  line l;/*<asyxml></code><documentation>If one axis is infinite, this line is used instead of ellipse.</documentation></property></asyxml>*/
  /*<asyxml><method type = "void" signature="init(point,point,real)"><code></asyxml>*/
  void init(point f1, point f2, real a)
  {/*<asyxml></code><documentation>Ellipse given by foci and semimajor axis</documentation></method></asyxml>*/
    point[] P = standardizecoordsys(f1, f2);
    this.F1 = P[0];
    this.F2 = P[1];
    this.angle = abs(P[1]-P[0]) < 10 * epsgeo ? 0 : degrees(P[1]-P[0]);
    this.C = (P[0] + P[1])/2;
    this.a = a;
    if(!finite(a)) {
      this.l = line(P[0], P[1]);
      this.b = infinity;
      this.e = 0;
      this.c = 0;
    } else {
      this.c = abs(C - P[0]);
      this.b = this.c < epsgeo ? a : sqrt(a^2 - c^2); // Handle case of circle.
      this.e = this.c < epsgeo ? 0 : this.c/a; // Handle case of circle.
      if(this.e >= 1) abort("ellipse.init: wrong parameter: e >= 1.");
      this.p = a * (1 - this.e^2);
      if (this.c != 0) {// directrix is not set for a circle.
        point A = this.C + (a^2/this.c) * unit(P[0]-this.C);
        this.D1 = line(A, A + rotateO(90) * unit(A - this.C));
        this.D2 = reverse(rotate(180, C) * D1);
      }
    }
  }
}/*<asyxml></struct></asyxml>*/

bool degenerate(ellipse el)
{
  return (!finite(el.a) || !finite(el.b));
}

/*<asyxml><struct signature="parabola"><code></asyxml>*/
struct parabola
{/*<asyxml></code><documentation>Look at <html><a href = "http://mathworld.wolfram.com/Parabola.html">http://mathworld.wolfram.com/Parabola.html</a></html></documentation><property type = "point" signature="F,V"><code></asyxml>*/
  restricted point F,V;/*<asyxml></code><documentation>Focus and vertex</documentation></property><property type = "real" signature="a,p,e = 1"><code></asyxml>*/
  restricted real a,p,e = 1;/*<asyxml></code></property><property type = "real" signature="angle"><code></asyxml>*/
  restricted real angle;/*<asyxml></code><documentation>Angle, in degrees, of the line (FV).</documentation></property><property type = "line" signature="D"><code></asyxml>*/
  restricted line D;/*<asyxml></code><documentation>Directrix</documentation></property><property type = "pair" signature="bmin,bmax"><code></asyxml>*/
  pair bmin, bmax;/*<asyxml></code><documentation>The (left, bottom) and (right, top) coordinates of region bounding box for drawing the parabola.
                    If unset the current picture bounding box is used instead.</documentation></property></asyxml>*/

  /*<asyxml><method type = "void" signature="init(point,line)"><code></asyxml>*/
  void init(point F, line directrix)
  {/*<asyxml></code><documentation>Parabola given by focus and directrix.</documentation></method></asyxml>*/
    point[] P = standardizecoordsys(F, directrix.A, directrix.B);
    line l = line(P[1], P[2]);
    this.F = P[0];
    this.D = l;
    this.a = distance(P[0], l)/2;
    this.p = 2 * a;
    this.V = 0.5 * (F + projection(D) * P[0]);
    this.angle = degrees(F - V);
  }
}/*<asyxml></struct></asyxml>*/

/*<asyxml><struct signature="hyperbola"><code></asyxml>*/
struct hyperbola
{/*<asyxml></code><documentation><html>Look at <a href = "http://mathworld.wolfram.com/Hyperbola.html">http://mathworld.wolfram.com/Hyperbola.html</a></html></documentation><property type = "point" signature="F1,F2"><code></asyxml>*/
  restricted point F1,F2;/*<asyxml></code><documentation>Foci.</documentation></property><property type = "point" signature="C,V1,V2"><code></asyxml>*/
  restricted point C,V1,V2;/*<asyxml></code><documentation>Center and vertices.</documentation></property><property type = "real" signature="a,b,c,e,p"><code></asyxml>*/
  restricted real a,b,c,e,p;/*<asyxml></code><documentation></documentation></property><property type = "real" signature="angle"><code></asyxml>*/
  restricted real angle;/*<asyxml></code><documentation>Angle,in degrees,of the line (F1F2).</documentation></property><property type = "line" signature="D1,D2,A1,A2"><code></asyxml>*/
  restricted line D1,D2,A1,A2;/*<asyxml></code><documentation>Directrices and asymptotes.</documentation></property><property type = "pair" signature="bmin,bmax"><code></asyxml>*/
  pair bmin, bmax; /*<asyxml></code><documentation>The (left, bottom) and (right, top) coordinates of region bounding box for drawing the hyperbola.
                     If unset the current picture bounding box is used instead.</documentation></property></asyxml>*/

  /*<asyxml><method type = "void" signature="init(point,point,real)"><code></asyxml>*/
  void init(point f1, point f2, real a)
  {/*<asyxml></code><documentation>Hyperbola given by foci and semimajor axis.</documentation></method></asyxml>*/
    point[] P = standardizecoordsys(f1, f2);
    this.F1 = P[0];
    this.F2 = P[1];
    this.angle = degrees(F2 - F1);
    this.a = a;
    this.C = (P[0] + P[1])/2;
    this.c = abs(C - P[0]);
    this.e = this.c/a;
    if(this.e <= 1) abort("hyperbola.init: wrong parameter: e <= 1.");
    this.b = a * sqrt(this.e^2 - 1);
    this.p = a * (this.e^2 - 1);
    point A = this.C + (a^2/this.c) * unit(P[0]-this.C);
    this.D1 = line(A, A + rotateO(90) * unit(A - this.C));
    this.D2 = reverse(rotate(180, C) * D1);
    this.V1 = C + a * unit(F1 - C);
    this.V2 = C + a * unit(F2 - C);
    this.A1 = line(C, V1 + b * unit(rotateO(-90) * (C - V1)));
    this.A2 = line(C, V1 + b * unit(rotateO(90) * (C - V1)));
  }
}/*<asyxml></struct></asyxml>*/

/*<asyxml><variable type="int" signature="conicnodesfactor"><code></asyxml>*/
int conicnodesfactor = 1;/*<asyxml></code><documentation>Factor for the node number of all conics.</documentation></variable></asyxml>*/

/*<asyxml><variable type="int" signature="circlenodesnumberfactor"><code></asyxml>*/
int circlenodesnumberfactor = 100;/*<asyxml></code><documentation>Factor for the node number of circles.</documentation></variable></asyxml>*/
/*<asyxml><function type="int" signature="circlenodesnumber(real)"><code></asyxml>*/
int circlenodesnumber(real r)
{/*<asyxml></code><documentation>Return the number of nodes for drawing a circle of radius 'r'.</documentation></function></asyxml>*/
  if (circlenodesnumberfactor < 100)
    warning("circlenodesnumberfactor",
            "variable 'circlenodesnumberfactor' may be too small.");
  int oi = ceil(circlenodesnumberfactor * abs(r)^0.1);
  oi = 45 * floor(oi/45);
  return oi == 0 ? 4 : conicnodesfactor * oi;
}

/*<asyxml><function type="int" signature="circlenodesnumber(real,real,real)"><code></asyxml>*/
int circlenodesnumber(real r, real angle1, real angle2)
{/*<asyxml></code><documentation>Return the number of nodes to draw a circle arc.</documentation></function></asyxml>*/
  return (r > 0) ?
    ceil(circlenodesnumber(r) * abs(angle1 - angle2)/360) :
    ceil(circlenodesnumber(r) * abs((1 - abs(angle1 - angle2)/360)));
}

/*<asyxml><variable type="int" signature="ellispenodesnumberfactor"><code></asyxml>*/
int ellipsenodesnumberfactor = 250;/*<asyxml></code><documentation>Factor for the node number of ellispe (non-circle).</documentation></variable></asyxml>*/
/*<asyxml><function type="int" signature="ellipsenodesnumber(real,real)"><code></asyxml>*/
int ellipsenodesnumber(real a, real b)
{/*<asyxml></code><documentation>Return the number of nodes to draw a ellipse of axis 'a' and 'b'.</documentation></function></asyxml>*/
  if (ellipsenodesnumberfactor < 250)
    write("ellipsenodesnumberfactor",
          "variable 'ellipsenodesnumberfactor' maybe too small.");
  int tmp = circlenodesnumberfactor;
  circlenodesnumberfactor = ellipsenodesnumberfactor;
  int oi = circlenodesnumber(max(abs(a), abs(b))/min(abs(a), abs(b)));
  circlenodesnumberfactor = tmp;
  return conicnodesfactor * oi;
}

/*<asyxml><function type="int" signature="ellipsenodesnumber(real,real,real)"><code></asyxml>*/
int ellipsenodesnumber(real a, real b, real angle1, real angle2, bool dir)
{/*<asyxml></code><documentation>Return the number of nodes to draw an ellipse arc.</documentation></function></asyxml>*/
  real d;
  real da = angle2 - angle1;
  if(dir) {
    d = angle1 < angle2 ? da : 360 + da;
  } else {
    d = angle1 < angle2 ? -360 + da : da;
  }
  int n = floor(ellipsenodesnumber(a, b) * abs(d)/360);
  return n < 5 ? 5 : n;
}

/*<asyxml><variable type="int" signature="parabolanodesnumberfactor"><code></asyxml>*/
int parabolanodesnumberfactor = 100;/*<asyxml></code><documentation>Factor for the number of nodes of parabolas.</documentation></variable></asyxml>*/
/*<asyxml><function type="int" signature="parabolanodesnumber(parabola,real,real)"><code></asyxml>*/
int parabolanodesnumber(parabola p, real angle1, real angle2)
{/*<asyxml></code><documentation>Return the number of nodes for drawing a parabola.</documentation></function></asyxml>*/
  return conicnodesfactor * floor(0.01 * parabolanodesnumberfactor * abs(angle1 - angle2));
}

/*<asyxml><variable type="int" signature="hyperbolanodesnumberfactor"><code></asyxml>*/
int hyperbolanodesnumberfactor = 100;/*<asyxml></code><documentation>Factor for the number of nodes of hyperbolas.</documentation></variable></asyxml>*/
/*<asyxml><function type="int" signature="hyperbolanodesnumber(hyperbola,real,real)"><code></asyxml>*/
int hyperbolanodesnumber(hyperbola h, real angle1, real angle2)
{/*<asyxml></code><documentation>Return the number of nodes for drawing an hyperbola.</documentation></function></asyxml>*/
  return conicnodesfactor * floor(0.01 * hyperbolanodesnumberfactor * abs(angle1 - angle2)/h.e);
}

/*<asyxml><operator type = "conic" signature="+(conic,explicit point)"><code></asyxml>*/
conic operator +(conic c, explicit point M)
{/*<asyxml></code><documentation></documentation></operator></asyxml>*/
  return conic(c.F + M, c.D + M, c.e);
}
/*<asyxml><operator type = "conic" signature="-(conic,explicit point)"><code></asyxml>*/
conic operator -(conic c, explicit point M)
{/*<asyxml></code><documentation></documentation></operator></asyxml>*/
  return conic(c.F - M, c.D - M, c.e);
}
/*<asyxml><operator type = "conic" signature="+(conic,explicit pair)"><code></asyxml>*/
conic operator +(conic c, explicit pair m)
{/*<asyxml></code><documentation></documentation></operator></asyxml>*/
  point M = point(c.F.coordsys, m);
  return conic(c.F + M, c.D + M, c.e);
}
/*<asyxml><operator type = "conic" signature="-(conic,explicit pair)"><code></asyxml>*/
conic operator -(conic c, explicit pair m)
{/*<asyxml></code><documentation></documentation></operator></asyxml>*/
  point M = point(c.F.coordsys, m);
  return conic(c.F - M, c.D - M, c.e);
}
/*<asyxml><operator type = "conic" signature="+(conic,vector)"><code></asyxml>*/
conic operator +(conic c, vector v)
{/*<asyxml></code><documentation></documentation></operator></asyxml>*/
  return conic(c.F + v, c.D + v, c.e);
}
/*<asyxml><operator type = "conic" signature="-(conic,vector)"><code></asyxml>*/
conic operator -(conic c, vector v)
{/*<asyxml></code><documentation></documentation></operator></asyxml>*/
  return conic(c.F - v, c.D - v, c.e);
}

/*<asyxml><function type="coordsys" signature="coordsys(conic)"><code></asyxml>*/
coordsys coordsys(conic co)
{/*<asyxml></code><documentation>Return the coordinate system of 'co'.</documentation></function></asyxml>*/
  return co.F.coordsys;
}

/*<asyxml><function type="conic" signature="changecoordsys(coordsys,conic)"><code></asyxml>*/
conic changecoordsys(coordsys R, conic co)
{/*<asyxml></code><documentation>Change the coordinate system of 'co' to 'R'</documentation></function></asyxml>*/
  line l = changecoordsys(R, co.D);
  point F = changecoordsys(R, co.F);
  return conic(F, l, co.e);
}

/*<asyxml><typedef type = "polarconicroutine" return = "path" params = "conic, real, real, int, bool"><code></asyxml>*/
typedef path polarconicroutine(conic co, real angle1, real angle2, int n, bool direction);/*<asyxml></code><documentation>Routine type used to draw conics from 'angle1' to 'angle2'</documentation></typedef></asyxml>*/

/*<asyxml><function type="path" signature="arcfromfocus(conic,real,real,int,bool)"><code></asyxml>*/
path arcfromfocus(conic co, real angle1, real angle2, int n = 400, bool direction = CCW)
{/*<asyxml></code><documentation>Return the path of the conic section 'co' from angle1 to angle2 in degrees,
   drawing in the given direction, with n nodes.</documentation></function></asyxml>*/
  guide op;
  if (n < 1) return op;
  if (angle1 > angle2) {
    path g = arcfromfocus(co, angle2, angle1, n, !direction);
    return g == nullpath ? g : reverse(g);
  }
  point O = projection(co.D) * co.F;
  pair i = unit(locate(co.F) - locate(O));
  pair j = rotate(90) * i;
  coordsys Rp = cartesiansystem(co.F, i, j);
  real a1 = direction ? radians(angle1) : radians(angle2);
  real a2 = direction ? radians(angle2) : radians(angle1) + 2 * pi;
  real step = n == 1 ? 0 : (a2 - a1)/(n - 1);
  real a, r;
  for (int i = 0; i < n; ++i) {
    a = a1 + i * step;
    if(co.e >= 1) {
      r = 1 - co.e * cos(a);
      if(r > epsgeo) {
        r = co.p/r;
        op = op--Rp * Rp.polar(r, a);
      }
    } else {
      r = co.p/(1 - co.e * cos(a));
      op = op..Rp * Rp.polar(r, a);
    }
  }
  if(co.e < 1 && abs(abs(a2 - a1) - 2 * pi) < epsgeo) op = (path)op..cycle;

  return (direction ? op : op == nullpath ? op :reverse(op));
}

/*<asyxml><variable type="polarconicroutine" signature="currentpolarconicroutine"><code></asyxml>*/
polarconicroutine currentpolarconicroutine = arcfromfocus;/*<asyxml></code><documentation>Default routine used to cast conic section to path.</documentation></variable></asyxml>*/

/*<asyxml><function type="point" signature="angpoint(conic,real)"><code></asyxml>*/
point angpoint(conic co, real angle)
{/*<asyxml></code><documentation>Return the point of 'co' whose the angular (in degrees)
   coordinate is 'angle' (mesured from the focus of 'co', relatively
   to its 'natural coordinate system').</documentation></function></asyxml>*/
  coordsys R = coordsys(co);
  return point(R, point(arcfromfocus(co, angle, angle, 1, CCW), 0)/R);
}

/*<asyxml><operator type = "bool" signature="@(point,conic)"><code></asyxml>*/
bool operator @(point M, conic co)
{/*<asyxml></code><documentation>Return true iff 'M' on 'co'.</documentation></operator></asyxml>*/
  if(co.e == 0) return abs(abs(co.F - M) - co.p) < 10 * epsgeo;
  return abs(co.e * distance(M, co.D) - abs(co.F - M)) < 10 * epsgeo;
}

/*<asyxml><function type="coordsys" signature="coordsys(ellipse)"><code></asyxml>*/
coordsys coordsys(ellipse el)
{/*<asyxml></code><documentation>Return the coordinate system of 'el'.</documentation></function></asyxml>*/
  return el.F1.coordsys;
}

/*<asyxml><function type="coordsys" signature="canonicalcartesiansystem(ellipse)"><code></asyxml>*/
coordsys canonicalcartesiansystem(ellipse el)
{/*<asyxml></code><documentation>Return the canonical cartesian system of the ellipse 'el'.</documentation></function></asyxml>*/
  if(degenerate(el)) return cartesiansystem(el.l.A, el.l.u, el.l.v);
  pair O = locate(el.C);
  pair i = el.e == 0 ? el.C.coordsys.i : unit(locate(el.F1) - O);
  pair j = rotate(90) * i;
  return cartesiansystem(O, i, j);
}

/*<asyxml><function type="coordsys" signature="canonicalcartesiansystem(parabola)"><code></asyxml>*/
coordsys canonicalcartesiansystem(parabola p)
{/*<asyxml></code><documentation>Return the canonical cartesian system of a parabola,
   so that Origin = vertex of 'p' and directrix: x = -a.</documentation></function></asyxml>*/
  point A = projection(p.D) * p.F;
  pair O = locate((A + p.F)/2);
  pair i = unit(locate(p.F) - O);
  pair j = rotate(90) * i;
  return cartesiansystem(O, i, j);
}

/*<asyxml><function type="coordsys" signature="canonicalcartesiansystem(hyperbola)"><code></asyxml>*/
coordsys canonicalcartesiansystem(hyperbola h)
{/*<asyxml></code><documentation>Return the canonical cartesian system of an hyperbola.</documentation></function></asyxml>*/
  pair O = locate(h.C);
  pair i = unit(locate(h.F2) - O);
  pair j = rotate(90) * i;
  return cartesiansystem(O, i, j);
}

/*<asyxml><function type="ellipse" signature="ellipse(point,point,real)"><code></asyxml>*/
ellipse ellipse(point F1, point F2, real a)
{/*<asyxml></code><documentation>Return the ellipse whose the foci are 'F1' and 'F2'
   and the semimajor axis is 'a'.</documentation></function></asyxml>*/
  ellipse oe;
  oe.init(F1, F2, a);
  return oe;
}

/*<asyxml><constant type = "bool" signature="byfoci,byvertices"><code></asyxml>*/
restricted bool byfoci = true, byvertices = false;/*<asyxml></code><documentation>Constants useful for the routine 'hyperbola(point P1, point P2, real ae, bool byfoci = byfoci)'</documentation></constant></asyxml>*/

/*<asyxml><function type="hyperbola" signature="hyperbola(point,point,real,bool)"><code></asyxml>*/
hyperbola hyperbola(point P1, point P2, real ae, bool byfoci = byfoci)
{/*<asyxml></code><documentation>if 'byfoci = true':
   return the hyperbola whose the foci are 'P1' and 'P2'
   and the semimajor axis is 'ae'.
   else return the hyperbola whose vertexes are 'P1' and 'P2' with eccentricity 'ae'.</documentation></function></asyxml>*/
  hyperbola oh;
  point[] P = standardizecoordsys(P1, P2);
  if(byfoci) {
    oh.init(P[0], P[1], ae);
  } else {
    real a = abs(P[0]-P[1])/2;
    vector V = unit(P[0]-P[1]);
    point F1 = P[0] + a * (ae - 1) * V;
    point F2 = P[1]-a * (ae - 1) * V;
    oh.init(F1, F2, a);
  }
  return oh;
}

/*<asyxml><function type="ellipse" signature="ellipse(point,point,point)"><code></asyxml>*/
ellipse ellipse(point F1, point F2, point M)
{/*<asyxml></code><documentation>Return the ellipse passing through 'M' whose the foci are 'F1' and 'F2'.</documentation></function></asyxml>*/
  point P[] = standardizecoordsys(false, F1, F2, M);
  real a = abs(F1 - M) + abs(F2 - M);
  return ellipse(F1, F2, finite(a) ? a/2 : a);
}

/*<asyxml><function type="ellipse" signature="ellipse(point,real,real,real)"><code></asyxml>*/
ellipse ellipse(point C, real a, real b, real angle = 0)
{/*<asyxml></code><documentation>Return the ellipse centered at 'C' with semimajor axis 'a' along C--C + dir(angle),
   semiminor axis 'b' along the perpendicular.</documentation></function></asyxml>*/
  ellipse oe;
  coordsys R = C.coordsys;
  angle += degrees(R.i);
  if(a < b) {angle += 90; real tmp = a; a = b; b = tmp;}
  if(finite(a) && finite(b)) {
    real c = sqrt(abs(a^2 - b^2));
    point f1, f2;
    if(abs(a - b) < epsgeo) {
      f1 = C; f2 = C;
    } else {
      f1 = point(R, (locate(C) + rotate(angle) * (-c, 0))/R);
      f2 = point(R, (locate(C) + rotate(angle) * (c, 0))/R);
    }
    oe.init(f1, f2, a);
  } else {
    if(finite(b) || !finite(a)) oe.init(C, C + R.polar(1, angle), infinity);
    else oe.init(C, C + R.polar(1, 90 + angle), infinity);
  }
  return oe;
}

/*<asyxml><function type="ellipse" signature="ellipse(bqe)"><code></asyxml>*/
ellipse ellipse(bqe bqe)
{/*<asyxml></code><documentation>Return the ellipse a[0] * x^2 + a[1] * xy + a[2] * y^2 + a[3] * x + a[4] * y + a[5] = 0
   given in the coordinate system of 'bqe' with a[i] = bque.a[i].
   <url href = "http://mathworld.wolfram.com/QuadraticCurve.html"/>
   <url href = "http://mathworld.wolfram.com/Ellipse.html"/>.</documentation></function></asyxml>*/
  bqe lbqe = changecoordsys(defaultcoordsys, bqe);
  real a = lbqe.a[0], b = lbqe.a[1]/2, c = lbqe.a[2], d = lbqe.a[3]/2, f = lbqe.a[4]/2, g = lbqe.a[5];
  coordsys R = bqe.coordsys;
  string message = "ellipse: the given equation is not an equation of an ellipse.";
  real u = b^2 * g + d^2 * c + f^2 * a;
  real delta = a * c * g + b * f * d + d * b * f - u;
  if(abs(delta) < epsgeo) abort(message);
  real j = b^2 - a * c;
  real i = a + c;
  real dd = j * (sgnd(c - a) * sqrt((a - c)^2 + 4 * (b^2)) - c-a);
  real ddd = j * (-sgnd(c - a) * sqrt((a - c)^2 + 4 * (b^2)) - c-a);

  if(abs(ddd) < epsgeo || abs(dd) < epsgeo ||
     j >= -epsgeo || delta/sgnd(i) > 0) abort(message);

  real x = (c * d - b * f)/j, y = (a * f - b * d)/j;
  // real dir = abs(b) < epsgeo ? 0 : pi/2-0.5 * acot(0.5 * (c-a)/b);
  real dir = abs(b) < epsgeo ? 0 : 0.5 * acot(0.5 * (c - a)/b);
  if(dir * (c - a) * b < 0) dir = dir < 0 ? dir + pi/2 : dir - pi/2;
  real cd = cos(dir), sd = sin(dir);
  real t = a * cd^2 - 2 * b * cd * sd + c * sd^2;
  real tt = a * sd^2 + 2 * b * cd * sd + c * cd^2;
  real gg = -g + ((d * cd - f * sd)^2)/t + ((d * sd + f * cd)^2)/tt;
  t = t/gg; tt = tt/gg;
  // The equation of the ellipse is t * (x - center.x)^2 + tt * (y - center.y)^2 = 1;
  real aa, bb;
  aa = sqrt(2 * (u - 2 * b * d * f - a * c * g)/dd);
  bb = sqrt(2 * (u - 2 * b * d * f - a * c * g)/ddd);
  a = t > tt ? max(aa, bb) : min(aa, bb);
  b = t > tt ? min(aa, bb) : max(aa, bb);
  return ellipse(point(R, (x, y)/R),
                 a, b, degrees(pi/2 - dir - angle(R.i)));
}

/*<asyxml><function type="ellipse" signature="ellipse(point,point,point,point,point)"><code></asyxml>*/
ellipse ellipse(point M1, point M2, point M3, point M4, point M5)
{/*<asyxml></code><documentation>Return the ellipse passing through the five points (if possible)</documentation></function></asyxml>*/
  return ellipse(bqe(M1, M2, M3, M4, M5));
}

/*<asyxml><function type="bool" signature="inside(ellipse,point)"><code></asyxml>*/
bool inside(ellipse el, point M)
{/*<asyxml></code><documentation>Return 'true' iff 'M' is inside 'el'.</documentation></function></asyxml>*/
  return abs(el.F1 - M) + abs(el.F2 - M) - 2 * el.a < -epsgeo;
}

/*<asyxml><function type="bool" signature="inside(parabola,point)"><code></asyxml>*/
bool inside(parabola p, point M)
{/*<asyxml></code><documentation>Return 'true' if 'M' is inside 'p'.</documentation></function></asyxml>*/
  return distance(p.D, M) - abs(p.F - M) > epsgeo;
}

/*<asyxml><function type="parabola" signature="parabola(point,line)"><code></asyxml>*/
parabola parabola(point F, line l)
{/*<asyxml></code><documentation>Return the parabola whose focus is 'F' and directrix is 'l'.</documentation></function></asyxml>*/
  parabola op;
  op.init(F, l);
  return op;
}

/*<asyxml><function type="parabola" signature="parabola(point,point)"><code></asyxml>*/
parabola parabola(point F, point vertex)
{/*<asyxml></code><documentation>Return the parabola whose focus is 'F' and vertex is 'vertex'.</documentation></function></asyxml>*/
  parabola op;
  point[] P = standardizecoordsys(F, vertex);
  point A = rotate(180, P[1]) * P[0];
  point B = A + rotateO(90) * unit(P[1]-A);
  op.init(P[0], line(A, B));
  return op;
}

/*<asyxml><function type="parabola" signature="parabola(point,real,real)"><code></asyxml>*/
parabola parabola(point F, real a, real angle)
{/*<asyxml></code><documentation>Return the parabola whose focus is F, latus rectum is 4a and
   the angle of the axis of symmetry (in the coordinate system of F) is 'angle'.</documentation></function></asyxml>*/
  parabola op;
  coordsys R = F.coordsys;
  point A = F - point(R, R.polar(2a, radians(angle)));
  point B = A + point(R, R.polar(1, radians(90 + angle)));
  op.init(F, line(A, B));
  return op;
}

/*<asyxml><function type="bool" signature="isparabola(bqe)"><code></asyxml>*/
bool isparabola(bqe bqe)
{/*<asyxml></code><documentation>Return true iff 'bqe' is the equation of a parabola.</documentation></function></asyxml>*/
  bqe lbqe = changecoordsys(defaultcoordsys, bqe);
  real a = lbqe.a[0], b = lbqe.a[1]/2, c = lbqe.a[2], d = lbqe.a[3]/2, f = lbqe.a[4]/2, g = lbqe.a[5];
  real delta = a * c * g + b * f * d + d * b * f - (b^2 * g + d^2 * c + f^2 * a);
  return (abs(delta) > epsgeo && abs(b^2 - a * c) < epsgeo);
}

/*<asyxml><function type="parabola" signature="parabola(bqe)"><code></asyxml>*/
parabola parabola(bqe bqe)
{/*<asyxml></code><documentation>Return the parabola a[0]x^2 + a[1]xy + a[2]y^2 + a[3]x + a[4]y + a[5]] = 0 (a[n] means bqe.a[n]).
   <url href = "http://mathworld.wolfram.com/QuadraticCurve.html"/>
   <url href = "http://mathworld.wolfram.com/Parabola.html"/></documentation></function></asyxml>*/
  bqe lbqe = changecoordsys(defaultcoordsys, bqe);
  real a = lbqe.a[0], b = lbqe.a[1]/2, c = lbqe.a[2], d = lbqe.a[3]/2, f = lbqe.a[4]/2, g = lbqe.a[5];
  string message = "parabola: the given equation is not an equation of a parabola.";
  real delta = a * c * g + b * f * d + d * b * f - (b^2 * g + d^2 * c + f^2 * a);
  if(abs(delta) < 10 * epsgeo || abs(b^2 - a * c) > 10 * epsgeo) abort(message);
  real dir = abs(b) < epsgeo ? 0 : 0.5 * acot(0.5 * (c - a)/b);
  if(dir * (c - a) * b < 0) dir = dir < 0 ? dir + pi/2 : dir - pi/2;
  real cd = cos(dir), sd = sin(dir);
  real ap = a * cd^2 - 2 * b * cd * sd + c * sd^2;
  real cp = a * sd^2 + 2 * b * cd * sd + c * cd^2;
  real dp = d * cd - f * sd;
  real fp = d * sd + f * cd;
  real gp = g;
  parabola op;
  coordsys R = bqe.coordsys;
  // The equation of the parabola is ap * x'^2 + cp * y'^2 + 2dp * x'+2fp * y'+gp = 0
  if (abs(ap) < epsgeo) {/* directrix parallel to the rotated(dir) y-axis
                            equation: (y-vertex.y)^2 = 4 * a * (x-vertex)
                         */
    pair pvertex = rotate(degrees(-dir)) * (0.5(-gp + fp^2/cp)/dp, -fp/cp);
    real a = -0.5 * dp/cp;
    point vertex = point(R, pvertex/R);
    point focus = point(R, (pvertex + a * expi(-dir))/R);
    op = parabola(focus, vertex);

  } else {/* directrix parallel to the rotated(dir) x-axis
             equation: (x-vertex)^2 = 4 * a * (y-vertex.y)
          */
    pair pvertex = rotate(degrees(-dir)) * (-dp/ap, 0.5 * (-gp + dp^2/ap)/fp);
    real a = -0.5 * fp/ap;
    point vertex = point(R, pvertex/R);
    point focus = point(R, (pvertex + a * expi(pi/2 - dir))/R);
    op = parabola(focus, vertex);
  }
  return op;
}

/*<asyxml><function type="parabola" signature="parabola(point,point,point,line)"><code></asyxml>*/
parabola parabola(point M1, point M2, point M3, line l)
{/*<asyxml></code><documentation>Return the parabola passing through the three points with its directix
   parallel to the line 'l'.</documentation></function></asyxml>*/
  coordsys R;
  pair[] pts;
  if (samecoordsys(M1, M2, M3)) {
    R = M1.coordsys;
  } else {
    R = defaultcoordsys;
  }
  real gle = degrees(l);
  coordsys Rp = cartesiansystem(R.O, rotate(gle) * R.i, rotate(gle) * R.j);
  pts = new pair[] {coordinates(changecoordsys(Rp, M1)),
                  coordinates(changecoordsys(Rp, M2)),
                  coordinates(changecoordsys(Rp, M3))};
  real[][] M;
  real[] x;
  for (int i = 0; i < 3; ++i) {
    M[i] = new real[] {pts[i].x, pts[i].y, 1};
    x[i] = -pts[i].x^2;
  }
  real[] coef = solve(M, x);
  return parabola(changecoordsys(R, bqe(Rp, 1, 0, 0, coef[0], coef[1], coef[2])));
}

/*<asyxml><function type="parabola" signature="parabola(point,point,point,point,point)"><code></asyxml>*/
parabola parabola(point M1, point M2, point M3, point M4, point M5)
{/*<asyxml></code><documentation>Return the parabola passing through the five points.</documentation></function></asyxml>*/
  return parabola(bqe(M1, M2, M3, M4, M5));
}

/*<asyxml><function type="hyperbola" signature="hyperbola(point,real,real,real)"><code></asyxml>*/
hyperbola hyperbola(point C, real a, real b, real angle = 0)
{/*<asyxml></code><documentation>Return the hyperbola centered at 'C' with semimajor axis 'a' along C--C + dir(angle),
   semiminor axis 'b' along the perpendicular.</documentation></function></asyxml>*/
  hyperbola oh;
  coordsys R = C.coordsys;
  angle += degrees(R.i);
  real c = sqrt(a^2 + b^2);
  point f1 = point(R, (locate(C) + rotate(angle) * (-c, 0))/R);
  point f2 = point(R, (locate(C) + rotate(angle) * (c, 0))/R);
  oh.init(f1, f2, a);
  return oh;
}

/*<asyxml><function type="hyperbola" signature="hyperbola(bqe)"><code></asyxml>*/
hyperbola hyperbola(bqe bqe)
{/*<asyxml></code><documentation>Return the hyperbola a[0]x^2 + a[1]xy + a[2]y^2 + a[3]x + a[4]y + a[5]] = 0 (a[n] means bqe.a[n]).
   <url href = "http://mathworld.wolfram.com/QuadraticCurve.html"/>
   <url href = "http://mathworld.wolfram.com/Hyperbola.html"/></documentation></function></asyxml>*/
  bqe lbqe = changecoordsys(defaultcoordsys, bqe);
  real a = lbqe.a[0], b = lbqe.a[1]/2, c = lbqe.a[2], d = lbqe.a[3]/2, f = lbqe.a[4]/2, g = lbqe.a[5];
  string message = "hyperbola: the given equation is not an equation of a hyperbola.";
  real delta = a * c * g + b * f * d + d * b * f - (b^2 * g + d^2 * c + f^2 * a);
  if(abs(delta) < 10 * epsgeo || abs(b^2 - a * c) < 0) abort(message);
  real dir = abs(b) < epsgeo ? 0 : 0.5 * acot(0.5 * (c - a)/b);
  real cd = cos(dir), sd = sin(dir);
  real ap = a * cd^2 - 2 * b * cd * sd + c * sd^2;
  real cp = a * sd^2 + 2 * b * cd * sd + c * cd^2;
  real dp = d * cd - f * sd;
  real fp = d * sd + f * cd;
  real gp = -g + dp^2/ap + fp^2/cp;
  hyperbola op;
  coordsys R = bqe.coordsys;
  real j = b^2 - a * c;
  point C = point(R, ((c * d - b * f)/j, (a * f - b * d)/j)/R);
  real aa = gp/ap, bb = gp/cp;
  real a = sqrt(abs(aa)), b = sqrt(abs(bb));
  if(aa < 0) {dir -= pi/2; aa = a; a = b; b = aa;}
  return hyperbola(C, a, b, degrees(-dir - angle(R.i)));
}

/*<asyxml><function type="hyperbola" signature="hyperbola(point,point,point,point,point)"><code></asyxml>*/
hyperbola hyperbola(point M1, point M2, point M3, point M4, point M5)
{/*<asyxml></code><documentation>Return the hyperbola passing through the five points (if possible).</documentation></function></asyxml>*/
  return hyperbola(bqe(M1, M2, M3, M4, M5));
}

/*<asyxml><function type="hyperbola" signature="conj(hyperbola)"><code></asyxml>*/
hyperbola conj(hyperbola h)
{/*<asyxml></code><documentation>Conjugate.</documentation></function></asyxml>*/
  return hyperbola(h.C, h.b, h.a, 90 + h.angle);
}

/*<asyxml><function type="circle" signature="circle(explicit point,real)"><code></asyxml>*/
circle circle(explicit point C, real r)
{/*<asyxml></code><documentation>Circle given by center and radius.</documentation></function></asyxml>*/
  circle oc = new circle;
  oc.C = C;
  oc.r = r;
  if(!finite(r)) oc.l = line(C, C + vector(C.coordsys, (1, 0)));
  return oc;
}

/*<asyxml><function type="circle" signature="circle(point,point)"><code></asyxml>*/
circle circle(point A, point B)
{/*<asyxml></code><documentation>Return the circle of diameter AB.</documentation></function></asyxml>*/
  real r;
  circle oc;
  real a = abs(A), b = abs(B);
  if(finite(a) && finite(b)) {
    oc = circle((A + B)/2, abs(A - B)/2);
  } else {
    oc.r = infinity;
    if(finite(abs(A))) oc.l = line(A, A + unit(B));
    else {
      if(finite(abs(B))) oc.l = line(B, B + unit(A));
      else if(finite(abs(A - B)/2)) oc = circle((A + B)/2, abs(A - B)/2); else
        oc.l = line(A, B);
    }
  }
  return oc;
}

/*<asyxml><function type="circle" signature="circle(segment)"><code></asyxml>*/
circle circle(segment s)
{/*<asyxml></code><documentation>Return the circle of diameter 's'.</documentation></function></asyxml>*/
  return circle(s.A, s.B);
}

/*<asyxml><function type="point" signature="circumcenter(point,point,point)"><code></asyxml>*/
point circumcenter(point A, point B, point C)
{/*<asyxml></code><documentation>Return the circumcenter of triangle ABC.</documentation></function></asyxml>*/
  point[] P = standardizecoordsys(A, B, C);
  coordsys R = P[0].coordsys;
  pair a = A, b = B, c = C;
  pair mAB = (a + b)/2;
  pair mAC = (a + c)/2;
  pair pp = extension(mAB, rotate(90, mAB) * a, mAC, rotate(90, mAC) * c);
  return point(R, pp/R);
}

/*<asyxml><function type="circle" signature="circle(point,point,point)"><code></asyxml>*/
circle circle(point A, point B, point C)
{/*<asyxml></code><documentation>Return the circumcircle of the triangle ABC.</documentation></function></asyxml>*/
  if(collinear(A - B, A - C)) {
    circle oc;
    oc.r = infinity;
    oc.C = (A + B + C)/3;
    oc.l = line(oc.C, oc.C == A ? B : A);
    return oc;
  }
  point c = circumcenter(A, B, C);
  return circle(c, abs(c - A));
}

/*<asyxml><function type="circle" signature="circumcircle(point,point,point)"><code></asyxml>*/
circle circumcircle(point A, point B, point C)
{/*<asyxml></code><documentation>Return the circumcircle of the triangle ABC.</documentation></function></asyxml>*/
  return circle(A, B, C);
}

/*<asyxml><operator type = "circle" signature="*(real,explicit circle)"><code></asyxml>*/
circle operator *(real x, explicit circle c)
{/*<asyxml></code><documentation>Multiply the radius of 'c'.</documentation></operator></asyxml>*/
  return finite(c.r) ? circle(c.C, x * c.r) : c;
}
circle operator *(int x, explicit circle c)
{
  return finite(c.r) ? circle(c.C, x * c.r) : c;
}
/*<asyxml><operator type = "circle" signature="/(explicit circle,real)"><code></asyxml>*/
circle operator /(explicit circle c, real x)
{/*<asyxml></code><documentation>Divide the radius of 'c'</documentation></operator></asyxml>*/
  return finite(c.r) ? circle(c.C, c.r/x) : c;
}
circle operator /(explicit circle c, int x)
{
  return finite(c.r) ? circle(c.C, c.r/x) : c;
}
/*<asyxml><operator type = "circle" signature="+(explicit circle,explicit point)"><code></asyxml>*/
circle operator +(explicit circle c, explicit point M)
{/*<asyxml></code><documentation>Translation of 'c'.</documentation></operator></asyxml>*/
  return circle(c.C + M, c.r);
}
/*<asyxml><operator type = "circle" signature="-(explicit circle,explicit point)"><code></asyxml>*/
circle operator -(explicit circle c, explicit point M)
{/*<asyxml></code><documentation>Translation of 'c'.</documentation></operator></asyxml>*/
  return circle(c.C - M, c.r);
}
/*<asyxml><operator type = "circle" signature="+(explicit circle,pair)"><code></asyxml>*/
circle operator +(explicit circle c, pair m)
{/*<asyxml></code><documentation>Translation of 'c'.
   'm' represent coordinates in the coordinate system where 'c' is defined.</documentation></operator></asyxml>*/
  return circle(c.C + m, c.r);
}
/*<asyxml><operator type = "circle" signature="-(explicit circle,pair)"><code></asyxml>*/
circle operator -(explicit circle c, pair m)
{/*<asyxml></code><documentation>Translation of 'c'.
   'm' represent coordinates in the coordinate system where 'c' is defined.</documentation></operator></asyxml>*/
  return circle(c.C - m, c.r);
}
/*<asyxml><operator type = "circle" signature="+(explicit circle,vector)"><code></asyxml>*/
circle operator +(explicit circle c, vector m)
{/*<asyxml></code><documentation>Translation of 'c'.</documentation></operator></asyxml>*/
  return circle(c.C + m, c.r);
}
/*<asyxml><operator type = "circle" signature="-(explicit circle,vector)"><code></asyxml>*/
circle operator -(explicit circle c, vector m)
{/*<asyxml></code><documentation>Translation of 'c'.</documentation></operator></asyxml>*/
  return circle(c.C - m, c.r);
}
/*<asyxml><operator type = "real" signature="^(point,explicit circle)"><code></asyxml>*/
real operator ^(point M, explicit circle c)
{/*<asyxml></code><documentation>The power of 'M' with respect to the circle 'c'</documentation></operator></asyxml>*/
  return xpart((abs(locate(M) - locate(c.C)), c.r)^2);
}
/*<asyxml><operator type = "bool" signature="@(point,explicit circle)"><code></asyxml>*/
bool operator @(point M, explicit circle c)
{/*<asyxml></code><documentation>Return true iff 'M' is on the circle 'c'.</documentation></operator></asyxml>*/
  return finite(c.r) ?
    abs(abs(locate(M) - locate(c.C)) - abs(c.r)) <= 10 * epsgeo :
    M @ c.l;
}

/*<asyxml><operator type = "ellipse" signature="cast(circle)"><code></asyxml>*/
ellipse operator cast(circle c)
{/*<asyxml></code><documentation></documentation></operator></asyxml>*/
  return finite(c.r) ? ellipse(c.C, c.r, c.r, 0) : ellipse(c.l.A, c.l.B, infinity);
}

/*<asyxml><operator type = "circle" signature="cast(ellipse)"><code></asyxml>*/
circle operator cast(ellipse el)
{/*<asyxml></code><documentation></documentation></operator></asyxml>*/
  circle oc;
  bool infb = (!finite(el.a) || !finite(el.b));
  if(!infb && abs(el.a - el.b) > epsgeo)
    abort("Can not cast ellipse with different axis values to circle");
  oc = circle(el.C, infb ? infinity : el.a);
  oc.l = el.l.copy();
  return oc;
}

/*<asyxml><operator type = "ellipse" signature="cast(conic)"><code></asyxml>*/
ellipse operator cast(conic co)
{/*<asyxml></code><documentation>Cast a conic to an ellipse (can be a circle).</documentation></operator></asyxml>*/
  if(degenerate(co) && co.e < 1) return ellipse(co.l[0].A, co.l[0].B, infinity);
  ellipse oe;
  if(co.e < 1) {
    real a = co.p/(1 - co.e^2);
    real c = co.e * a;
    vector v = co.D.v;
    if(!sameside(co.D.A + v, co.F, co.D)) v = -v;
    point f2 = co.F + 2 * c * v;
    f2 = changecoordsys(co.F.coordsys, f2);
    oe = a == 0 ? ellipse(co.F, co.p, co.p, 0) : ellipse(co.F, f2, a);
  } else
    abort("casting: The conic section is not an ellipse.");
  return oe;
}

/*<asyxml><operator type = "parabola" signature="cast(conic)"><code></asyxml>*/
parabola operator cast(conic co)
{/*<asyxml></code><documentation>Cast a conic to a parabola.</documentation></operator></asyxml>*/
  parabola op;
  if(abs(co.e - 1) > epsgeo) abort("casting: The conic section is not a parabola.");
  op.init(co.F, co.D);
  return op;
}

/*<asyxml><operator type = "conic" signature="cast(parabola)"><code></asyxml>*/
conic operator cast(parabola p)
{/*<asyxml></code><documentation>Cast a parabola to a conic section.</documentation></operator></asyxml>*/
  return conic(p.F, p.D, 1);
}

/*<asyxml><operator type = "hyperbola" signature="cast(conic)"><code></asyxml>*/
hyperbola operator cast(conic co)
{/*<asyxml></code><documentation>Cast a conic section to an hyperbola.</documentation></operator></asyxml>*/
  hyperbola oh;
  if(co.e > 1) {
    real a = co.p/(co.e^2 - 1);
    real c = co.e * a;
    vector v = co.D.v;
    if(sameside(co.D.A + v, co.F, co.D)) v = -v;
    point f2 = co.F + 2 * c * v;
    f2 = changecoordsys(co.F.coordsys, f2);
    oh = hyperbola(co.F, f2, a);
  } else
    abort("casting: The conic section is not an hyperbola.");
  return oh;
}

/*<asyxml><operator type = "conic" signature="cast(hyperbola)"><code></asyxml>*/
conic operator cast(hyperbola h)
{/*<asyxml></code><documentation>Hyperbola to conic section.</documentation></operator></asyxml>*/
  return conic(h.F1, h.D1, h.e);
}

/*<asyxml><operator type = "conic" signature="cast(ellipse)"><code></asyxml>*/
conic operator cast(ellipse el)
{/*<asyxml></code><documentation>Ellipse to conic section.</documentation></operator></asyxml>*/
  conic oc;
  if(abs(el.c) > epsgeo) {
    real x = el.a^2/el.c;
    point O = (el.F1 + el.F2)/2;
    point A = O + x * unit(el.F1 - el.F2);
    oc = conic(el.F1, perpendicular(A, line(el.F1, el.F2)), el.e);
  } else {//The ellipse is a circle
    coordsys R = coordsys(el);
    point M = el.F1 + point(R, R.polar(el.a, 0));
    line l = line(rotate(90, M) * el.F1, M);
    oc = conic(el.F1, l, 0);
  }
  if(degenerate(el)) {
    oc.p = infinity;
    oc.h = infinity;
    oc.l = new line[]{el.l};
  }
  return oc;
}

/*<asyxml><operator type = "conic" signature="cast(circle)"><code></asyxml>*/
conic operator cast(circle c)
{/*<asyxml></code><documentation>Circle to conic section.</documentation></operator></asyxml>*/
  return (conic)((ellipse)c);
}

/*<asyxml><operator type = "circle" signature="cast(conic)"><code></asyxml>*/
circle operator cast(conic c)
{/*<asyxml></code><documentation>Conic section to circle.</documentation></operator></asyxml>*/
  ellipse el = (ellipse)c;
  circle oc;
  if(abs(el.a - el.b) < epsgeo) {
    oc = circle(el.C, el.a);
    if(degenerate(c)) oc.l = c.l[0];
  }
  else abort("Can not cast this conic to a circle");
  return oc;
}

/*<asyxml><operator type = "ellipse" signature="*(transform,ellipse)"><code></asyxml>*/
ellipse operator *(transform t, ellipse el)
{/*<asyxml></code><documentation>Provide transform * ellipse.</documentation></operator></asyxml>*/
  if(!degenerate(el)) {
    point[] ep;
    for (int i = 0; i < 360; i += 72) {
      ep.push(t * angpoint(el, i));
    }
    ellipse oe = ellipse(ep[0], ep[1], ep[2], ep[3], ep[4]);
    if(angpoint(oe, 0) != ep[0]) return ellipse(oe.F2, oe.F1, oe.a);
    return oe;
  }
  return ellipse(t * el.l.A, t * el.l.B, infinity);
}

/*<asyxml><operator type = "parabola" signature="*(transform,parabola)"><code></asyxml>*/
parabola operator *(transform t, parabola p)
{/*<asyxml></code><documentation>Provide transform * parabola.</documentation></operator></asyxml>*/
  point[] P;
  P.push(t * angpoint(p, 45));
  P.push(t * angpoint(p, -45));
  P.push(t * angpoint(p, 180));
  parabola op = parabola(P[0], P[1], P[2], t * p.D);
  op.bmin = p.bmin;
  op.bmax = p.bmax;

  return op;
}

/*<asyxml><operator type = "ellipse" signature="*(transform,circle)"><code></asyxml>*/
ellipse operator *(transform t, circle c)
{/*<asyxml></code><documentation>Provide transform * circle.
   For example, 'circle C = scale(2) * circle' and 'ellipse E = xscale(2) * circle' are valid
   but 'circle C = xscale(2) * circle' is invalid.</documentation></operator></asyxml>*/
  return t * ((ellipse)c);
}

/*<asyxml><operator type = "hyperbola" signature="*(transform,hyperbola)"><code></asyxml>*/
hyperbola operator *(transform t, hyperbola h)
{/*<asyxml></code><documentation>Provide transform * hyperbola.</documentation></operator></asyxml>*/
  if (t == identity()) {
    return h;
  }

  point[] ep;
  for (int i = 90; i <= 270; i += 45) {
    ep.push(t * angpoint(h, i));
  }

  hyperbola oe = hyperbola(ep[0], ep[1], ep[2], ep[3], ep[4]);
  if(angpoint(oe, 90) != ep[0]) {
    oe = hyperbola(oe.F2, oe.F1, oe.a);
  }

  oe.bmin = h.bmin;
  oe.bmax = h.bmax;

  return oe;
}

/*<asyxml><operator type = "conic" signature="*(transform,conic)"><code></asyxml>*/
conic operator *(transform t, conic co)
{/*<asyxml></code><documentation>Provide transform * conic.</documentation></operator></asyxml>*/
  if(co.e < 1) return (t * ((ellipse)co));
  if(co.e == 1) return (t * ((parabola)co));
  return (t * ((hyperbola)co));
}

/*<asyxml><operator type = "ellipse" signature="*(real,ellipse)"><code></asyxml>*/
ellipse operator *(real x, ellipse el)
{/*<asyxml></code><documentation>Identical but more efficient (rapid) than 'scale(x, el.C) * el'.</documentation></operator></asyxml>*/
  return degenerate(el) ? el : ellipse(el.C, x * el.a, x * el.b, el.angle);
}

/*<asyxml><operator type = "ellipse" signature="/(ellipse,real)"><code></asyxml>*/
ellipse operator /(ellipse el, real x)
{/*<asyxml></code><documentation>Identical but more efficient (rapid) than 'scale(1/x, el.C) * el'.</documentation></operator></asyxml>*/
  return degenerate(el) ? el : ellipse(el.C, el.a/x, el.b/x, el.angle);
}

/*<asyxml><function type="path" signature="arcfromcenter(ellipse,real,real,int,bool)"><code></asyxml>*/
path arcfromcenter(ellipse el, real angle1, real angle2,
                   bool direction=CCW,
                   int n=ellipsenodesnumber(el.a,el.b,angle1,angle2,direction))
{/*<asyxml></code><documentation>Return the path of the ellipse 'el' from angle1 to angle2 in degrees,
   drawing in the given direction, with n nodes.
   The angles are mesured relatively to the  axis (C,x-axis) where C is
   the center of the ellipse.</documentation></function></asyxml>*/
  if(degenerate(el)) abort("arcfromcenter: can not convert degenerated ellipse to path.");
  if (angle1 > angle2)
    return reverse(arcfromcenter(el, angle2, angle1, !direction, n));

  guide op;
  coordsys Rp=coordsys(el);
  if (n < 1) return op;

  interpolate join = operator ..;
  real stretch = max(el.a/el.b, el.b/el.a);

  if (stretch > 10) {
    n *= floor(stretch/5);
    join = operator --;
  }

  real a1 = direction ? radians(angle1) : radians(angle2);
  real a2 = direction ? radians(angle2) : radians(angle1) + 2 * pi;
  real step=(a2 - a1)/(n != 1 ? n-1 : 1);
  real a, r;
  real da = radians(el.angle);

  for (int i=0; i < n; ++i) {
    a = a1 + i * step;
    r = el.b/sqrt(1 - (el.e * cos(a))^2);
    op = join(op, Rp*Rp.polar(r, da + a));
  }

  return shift(el.C.x*Rp.i + el.C.y*Rp.j) * (direction ? op : reverse(op));
}

/*<asyxml><function type="path" signature="arcfromcenter(hyperbola,real,real,int,bool)"><code></asyxml>*/
path arcfromcenter(hyperbola h, real angle1, real angle2,
                   int n = hyperbolanodesnumber(h, angle1, angle2),
                   bool direction = CCW)
{/*<asyxml></code><documentation>Return the path of the hyperbola 'h' from angle1 to angle2 in degrees,
   drawing in the given direction, with n nodes.
   The angles are mesured relatively to the axis (C, x-axis) where C is
   the center of the hyperbola.</documentation></function></asyxml>*/
  guide op;
  coordsys Rp = coordsys(h);
  if (n < 1) return op;
  if (angle1 > angle2) {
    path g = reverse(arcfromcenter(h, angle2, angle1, n, !direction));
    return g == nullpath ? g : reverse(g);
  }
  real a1 = direction ? radians(angle1) : radians(angle2);
  real a2 = direction ? radians(angle2) : radians(angle1) + 2 * pi;
  real step = (a2 - a1)/(n != 1 ? n - 1 : 1);
  real a, r;
  typedef guide interpolate(... guide[]);
  interpolate join = operator ..;
  real da = radians(h.angle);
  for (int i = 0; i < n; ++i) {
    a = a1 + i * step;
    r = (h.b * cos(a))^2 - (h.a * sin(a))^2;
    if(r > epsgeo) {
      r = sqrt(h.a^2 * h.b^2/r);
      op = join(op, Rp * Rp.polar(r, a + da));
      join = operator ..;
    } else join = operator --;
  }
  return shift(h.C.x * Rp.i + h.C.y * Rp.j)*
    (direction ? op : op == nullpath ? op : reverse(op));
}

/*<asyxml><function type="path" signature="arcfromcenter(explicit conic,real,real,int,bool)"><code></asyxml>*/
path arcfromcenter(explicit conic co, real angle1, real angle2,
                   int n, bool direction = CCW)
{/*<asyxml></code><documentation>Use arcfromcenter(ellipse, ...) or arcfromcenter(hyperbola, ...) depending of
   the eccentricity of 'co'.</documentation></function></asyxml>*/
  path g;
  if(co.e < 1)
    g = arcfromcenter((ellipse)co, angle1,
                    angle2, direction, n);
  else if(co.e > 1)
    g = arcfromcenter((hyperbola)co, angle1,
                    angle2, n, direction);
  else abort("arcfromcenter: does not exist for a parabola.");
  return g;
}

/*<asyxml><constant type = "polarconicroutine" signature="fromCenter"><code></asyxml>*/
restricted polarconicroutine fromCenter = arcfromcenter;/*<asyxml></code><documentation></documentation></constant></asyxml>*/
/*<asyxml><constant type = "polarconicroutine" signature="fromFocus"><code></asyxml>*/
restricted polarconicroutine fromFocus = arcfromfocus;/*<asyxml></code><documentation></documentation></constant></asyxml>*/

/*<asyxml><function type="bqe" signature="equation(ellipse)"><code></asyxml>*/
bqe equation(ellipse el)
{/*<asyxml></code><documentation>Return the coefficients of the equation of the ellipse in its coordinate system:
   bqe.a[0] * x^2 + bqe.a[1] * x * y + bqe.a[2] * y^2 + bqe.a[3] * x + bqe.a[4] * y + bqe.a[5] = 0.
   One can change the coordinate system of 'bqe' using the routine 'changecoordsys'.</documentation></function></asyxml>*/
  pair[] pts;
  for (int i = 0; i < 360; i += 72)
    pts.push(locate(angpoint(el, i)));

  real[][] M;
  real[] x;
  for (int i = 0; i < 5; ++i) {
    M[i] = new real[] {pts[i].x * pts[i].y, pts[i].y^2, pts[i].x, pts[i].y, 1};
    x[i] = -pts[i].x^2;
  }
  real[] coef = solve(M, x);
  bqe bqe = changecoordsys(coordsys(el),
                         bqe(defaultcoordsys,
                             1, coef[0], coef[1], coef[2], coef[3], coef[4]));
  bqe.a = approximate(bqe.a);
  return bqe;
}

/*<asyxml><function type="bqe" signature="equation(parabola)"><code></asyxml>*/
bqe equation(parabola p)
{/*<asyxml></code><documentation>Return the coefficients of the equation of the parabola in its coordinate system.
   bqe.a[0] * x^2 + bqe.a[1] * x * y + bqe.a[2] * y^2 + bqe.a[3] * x + bqe.a[4] * y + bqe.a[5] = 0
   One can change the coordinate system of 'bqe' using the routine 'changecoordsys'.</documentation></function></asyxml>*/
  coordsys R = canonicalcartesiansystem(p);
  parabola tp = changecoordsys(R, p);
  point A = projection(tp.D) * point(R, (0, 0));
  real a = abs(A);
  return changecoordsys(coordsys(p),
                        bqe(R, 0, 0, 1, -4 * a, 0, 0));
}

/*<asyxml><function type="bqe" signature="equation(hyperbola)"><code></asyxml>*/
bqe equation(hyperbola h)
{/*<asyxml></code><documentation>Return the coefficients of the equation of the hyperbola in its coordinate system.
   bqe.a[0] * x^2 + bqe.a[1] * x * y + bqe.a[2] * y^2 + bqe.a[3] * x + bqe.a[4] * y + bqe.a[5] = 0
   One can change the coordinate system of 'bqe' using the routine 'changecoordsys'.</documentation></function></asyxml>*/
  coordsys R = canonicalcartesiansystem(h);
  return changecoordsys(coordsys(h),
                        bqe(R, 1/h.a^2, 0, -1/h.b^2, 0, 0, -1));
}

/*<asyxml><operator type = "path" signature="cast(ellipse)"><code></asyxml>*/
path operator cast(ellipse el)
{/*<asyxml></code><documentation>Cast ellipse to path.</documentation></operator></asyxml>*/
  if(degenerate(el))
    abort("Casting degenerated ellipse to path is not possible.");
  int n = el.e == 0 ? circlenodesnumber(el.a) : ellipsenodesnumber(el.a, el.b);
  return arcfromcenter(el, 0.0, 360, CCW, n)&cycle;
}

/*<asyxml><operator type = "path" signature="cast(circle)"><code></asyxml>*/
path operator cast(circle c)
{/*<asyxml></code><documentation>Cast circle to path.</documentation></operator></asyxml>*/
  return (path)((ellipse)c);
}

/*<asyxml><function type="real[]" signature="bangles(picture,parabola)"><code></asyxml>*/
real[] bangles(picture pic = currentpicture, parabola p)
{/*<asyxml></code><documentation>Return the array {ma, Ma} where 'ma' and 'Ma' are respectively
   the smaller and the larger angles for which the parabola 'p' is included
   in the bounding box of the picture 'pic'.</documentation></function></asyxml>*/
  pair bmin, bmax;
  pair[] b;
  if (p.bmin == p.bmax) {
    bmin = pic.userMin();
    bmax = pic.userMax();
  } else {
    bmin = p.bmin;bmax = p.bmax;
  }
  if(bmin.x == bmax.x || bmin.y == bmax.y || !finite(abs(bmin)) || !finite(abs(bmax)))
    return new real[] {0, 0};
  b[0] = bmin;
  b[1] = (bmax.x, bmin.y);
  b[2] = bmax;
  b[3] = (bmin.x, bmax.y);
  real[] eq = changecoordsys(defaultcoordsys, equation(p)).a;
  pair[] inter;
  for (int i = 0; i < 4; ++i) {
    pair[] tmp = intersectionpoints(b[i], b[(i + 1)%4], eq);
    for (int j = 0; j < tmp.length; ++j) {
      if(dot(b[i]-tmp[j], b[(i + 1)%4]-tmp[j]) <= epsgeo)
        inter.push(tmp[j]);
    }
  }
  pair F = p.F, V = p.V;
  real d = degrees(F - V);
  real[] a = sequence(new real(int n){
      return (360 - d + degrees(inter[n]-F))%360;
    }, inter.length);
  real ma = a.length != 0 ? min(a) : 0, Ma= a.length != 0 ? max(a) : 0;
  return new real[] {ma, Ma};
}

/*<asyxml><function type="real[][]" signature="bangles(picture,hyperbola)"><code></asyxml>*/
real[][] bangles(picture pic = currentpicture, hyperbola h)
{/*<asyxml></code><documentation>Return the array {{ma1, Ma1}, {ma2, Ma2}} where 'maX' and 'MaX' are respectively
   the smaller and the bigger angles (from h.FX) for which the hyperbola 'h' is included
   in the bounding box of the picture 'pic'.</documentation></function></asyxml>*/
  pair bmin, bmax;
  pair[] b;
  if (h.bmin == h.bmax) {
    bmin = pic.userMin();
    bmax = pic.userMax();
  } else {
    bmin = h.bmin;bmax = h.bmax;
  }
  if(bmin.x == bmax.x || bmin.y == bmax.y || !finite(abs(bmin)) || !finite(abs(bmax)))
    return new real[][] {{0, 0}, {0, 0}};
  b[0] = bmin;
  b[1] = (bmax.x, bmin.y);
  b[2] = bmax;
  b[3] = (bmin.x, bmax.y);
  real[] eq = changecoordsys(defaultcoordsys, equation(h)).a;
  pair[] inter0, inter1;
  pair C = locate(h.C);
  pair F1 = h.F1;
  for (int i = 0; i < 4; ++i) {
    pair[] tmp = intersectionpoints(b[i], b[(i + 1)%4], eq);
    for (int j = 0; j < tmp.length; ++j) {
      if(dot(b[i]-tmp[j], b[(i + 1)%4]-tmp[j]) <= epsgeo) {
        if(dot(F1 - C, tmp[j]-C) > 0) inter0.push(tmp[j]);
        else inter1.push(tmp[j]);
      }
    }
  }
  real d = degrees(F1 - C);
  real[] ma, Ma;
  pair[][] inter = new pair[][] {inter0, inter1};
  for (int i = 0; i < 2; ++i) {
    real[] a = sequence(new real(int n){
        return (360 - d + degrees(inter[i][n]-F1))%360;
      }, inter[i].length);
    ma[i] = a.length != 0 ? min(a) : 0;
    Ma[i] = a.length != 0 ? max(a) : 0;
  }
  return new real[][] {{ma[0], Ma[0]}, {ma[1], Ma[1]}};
}

/*<asyxml><operator type = "path" signature="cast(parabola)"><code></asyxml>*/
path operator cast(parabola p)
{/*<asyxml></code><documentation>Cast parabola to path.
   If possible, the returned path is restricted to the actual bounding box
   of the current picture if the variables 'p.bmin' and 'p.bmax' are not set else
   the bounding box of box(p.bmin, p.bmax) is used instead.</documentation></operator></asyxml>*/
  real[] bangles = bangles(p);
  int n = parabolanodesnumber(p, bangles[0], bangles[1]);
  return arcfromfocus(p, bangles[0], bangles[1], n, CCW);
}


/*<asyxml><function type="void" signature="draw(picture,Label,circle,align,pen,arrowbar,arrowbar,margin,Label,marker)"><code></asyxml>*/
void draw(picture pic = currentpicture, Label L = "", circle c,
          align align = NoAlign, pen p = currentpen,
          arrowbar arrow = None, arrowbar bar = None,
          margin margin = NoMargin, Label legend = "", marker marker = nomarker)
{/*<asyxml></code><documentation></documentation></function></asyxml>*/
  if(degenerate(c)) draw(pic, L, c.l, align, p, arrow, legend, marker);
  else draw(pic, L, (path)c, align, p, arrow, bar, margin, legend, marker);
}

/*<asyxml><function type="void" signature="draw(picture,Label,ellipse,align,pen,arrowbar,arrowbar,margin,Label,marker)"><code></asyxml>*/
void draw(picture pic = currentpicture, Label L = "", ellipse el,
          align align = NoAlign, pen p = currentpen,
          arrowbar arrow = None, arrowbar bar = None,
          margin margin = NoMargin, Label legend = "", marker marker = nomarker)
{/*<asyxml></code><documentation></documentation>Draw the ellipse 'el' if it is not degenerated else draw 'el.l'.</function></asyxml>*/
  if(degenerate(el)) draw(pic, L, el.l, align, p, arrow, legend, marker);
  else draw(pic, L, (path)el, align, p, arrow, bar, margin, legend, marker);
}

/*<asyxml><function type="void" signature="draw(picture,Label,parabola,align,pen,arrowbar,arrowbar,margin,Label,marker)"><code></asyxml>*/
void draw(picture pic = currentpicture, Label L = "", parabola parabola,
          align align = NoAlign, pen p = currentpen,
          arrowbar arrow = None, arrowbar bar = None,
          margin margin = NoMargin, Label legend = "", marker marker = nomarker)
{/*<asyxml></code><documentation>Draw the parabola 'p' on 'pic' without (if possible) altering the
   size of picture pic.</documentation></function></asyxml>*/
  pic.add(new void (frame f, transform t, transform T, pair m, pair M) {
      // Reduce the bounds by the size of the pen and the margins.
      m -= min(p); M -= max(p);
      parabola.bmin = inverse(t) * m;
      parabola.bmax = inverse(t) * M;
      picture tmp;
      path pp = t * ((path) (T * parabola));

      if (pp != nullpath) {
        draw(tmp, L, pp, align, p, arrow, bar, NoMargin, legend, marker);
        add(f, tmp.fit());
      }
    }, true);

  pair m = pic.userMin(), M = pic.userMax();
  if(m != M) {
    pic.addBox(truepoint(SW), truepoint(NE));
  }
}

/*<asyxml><operator type = "path" signature="cast(hyperbola)"><code></asyxml>*/
path operator cast(hyperbola h)
{/*<asyxml></code><documentation>Cast hyperbola to path.
   If possible, the returned path is restricted to the actual bounding box
   of the current picture unless the variables 'h.bmin' and 'h.bmax'
   are set; in this case the bounding box of box(h.bmin, h.bmax) is used instead.
   Only the branch on the side of 'h.F1' is considered.</documentation></operator></asyxml>*/
  real[][] bangles = bangles(h);
  int n = hyperbolanodesnumber(h, bangles[0][0], bangles[0][1]);
  return arcfromfocus(h, bangles[0][0], bangles[0][1], n, CCW);
}

/*<asyxml><function type="void" signature="draw(picture,Label,hyperbola,align,pen,arrowbar,arrowbar,margin,Label,marker)"><code></asyxml>*/
void draw(picture pic = currentpicture, Label L = "", hyperbola h,
          align align = NoAlign, pen p = currentpen,
          arrowbar arrow = None, arrowbar bar = None,
          margin margin = NoMargin, Label legend = "", marker marker = nomarker)
{/*<asyxml></code><documentation>Draw the hyperbola 'h' on 'pic' without (if possible) altering the
   size of the picture pic.</documentation></function></asyxml>*/
  pic.add(new void (frame f, transform t, transform T, pair m, pair M) {
      // Reduce the bounds by the size of the pen and the margins.
      m -= min(p); M -= max(p);
      h.bmin = inverse(t) * m;
      h.bmax = inverse(t) * M;
      path hp;

      picture tmp;
      hp = t * ((path) (T * h));
      if (hp != nullpath) {
        draw(tmp, L, hp, align, p, arrow, bar, NoMargin, legend, marker);
      }

      hyperbola ht = hyperbola(h.F2, h.F1, h.a);
      ht.bmin = h.bmin;
      ht.bmax = h.bmax;

      hp = t * ((path) (T * ht));
      if (hp != nullpath) {
        draw(tmp, "", hp, align, p, arrow, bar, NoMargin, marker);
      }

      add(f, tmp.fit());
    }, true);

  pair m = pic.userMin(), M = pic.userMax();
  if(m != M)
    pic.addBox(truepoint(SW), truepoint(NE));
}

/*<asyxml><function type="void" signature="draw(picture,Label,explicit conic,align,pen,arrowbar,arrowbar,margin,Label,marker)"><code></asyxml>*/
void draw(picture pic = currentpicture, Label L = "", explicit conic co,
          align align = NoAlign, pen p = currentpen,
          arrowbar arrow = None, arrowbar bar = None,
          margin margin = NoMargin, Label legend = "", marker marker = nomarker)
{/*<asyxml></code><documentation>Use one of the routine 'draw(ellipse, ...)',
   'draw(parabola, ...)' or 'draw(hyperbola, ...)' depending of the value of eccentricity of 'co'.</documentation></function></asyxml>*/
  if(co.e == 0)
    draw(pic, L, (circle)co, align, p, arrow, bar, margin, legend, marker);
  else
    if(co.e < 1) draw(pic, L, (ellipse)co, align, p, arrow, bar, margin, legend, marker);
    else
      if(co.e == 1) draw(pic, L, (parabola)co, align, p, arrow, bar, margin, legend, marker);
      else
        if(co.e > 1) draw(pic, L, (hyperbola)co, align, p, arrow, bar, margin, legend, marker);
        else abort("draw: unknown conic.");
}

/*<asyxml><function type="int" signature="conicnodesnumber(conic,real,real)"><code></asyxml>*/
int conicnodesnumber(conic co, real angle1, real angle2, bool dir = CCW)
{/*<asyxml></code><documentation>Return the number of node to draw a conic arc.</documentation></function></asyxml>*/
  int oi;
  if(co.e == 0) {
    circle c = (circle)co;
    oi = circlenodesnumber(c.r, angle1, angle2);
  } else if(co.e < 1) {
    ellipse el = (ellipse)co;
    oi = ellipsenodesnumber(el.a, el.b, angle1, angle2, dir);
  } else if(co.e == 1) {
    parabola p = (parabola)co;
    oi = parabolanodesnumber(p, angle1, angle2);
  } else {
    hyperbola h = (hyperbola)co;
    oi = hyperbolanodesnumber(h, angle1, angle2);
  }
  return oi;
}

/*<asyxml><operator type = "path" signature="cast(conic)"><code></asyxml>*/
path operator cast(conic co)
{/*<asyxml></code><documentation>Cast conic section to path.</documentation></operator></asyxml>*/
  if(co.e < 1) return (path)((ellipse)co);
  if(co.e == 1) return (path)((parabola)co);
  return (path)((hyperbola)co);
}

/*<asyxml><function type="bqe" signature="equation(explicit conic)"><code></asyxml>*/
bqe equation(explicit conic co)
{/*<asyxml></code><documentation>Return the coefficients of the equation of conic section in its coordinate system:
   bqe.a[0] * x^2 + bqe.a[1] * x * y + bqe.a[2] * y^2 + bqe.a[3] * x + bqe.a[4] * y + bqe.a[5] = 0.
   One can change the coordinate system of 'bqe' using the routine 'changecoordsys'.</documentation></function></asyxml>*/
  bqe obqe;
  if(co.e == 0)
    obqe = equation((circle)co);
  else
    if(co.e < 1) obqe = equation((ellipse)co);
    else
      if(co.e == 1) obqe = equation((parabola)co);
      else
        if(co.e > 1) obqe = equation((hyperbola)co);
        else abort("draw: unknown conic.");
  return obqe;
}

/*<asyxml><function type="string" signature="conictype(bqe)"><code></asyxml>*/
string conictype(bqe bqe)
{/*<asyxml></code><documentation>Returned values are "ellipse" or "parabola" or "hyperbola"
   depending of the conic section represented by 'bqe'.</documentation></function></asyxml>*/
  bqe lbqe = changecoordsys(defaultcoordsys, bqe);
  string os = "degenerated";
  real a = lbqe.a[0], b = lbqe.a[1]/2, c = lbqe.a[2], d = lbqe.a[3]/2, f = lbqe.a[4]/2, g = lbqe.a[5];
  real delta = a * c * g + b * f * d + d * b * f - (b^2 * g + d^2 * c + f^2 * a);
  if(abs(delta) < 10 * epsgeo) return os;
  real J = a * c - b^2;
  real I = a + c;
  if(J > epsgeo) {
    if(delta/I < -epsgeo);
    os = "ellipse";
  } else {
    if(abs(J) < epsgeo) os = "parabola"; else os = "hyperbola";
  }
  return os;
}

/*<asyxml><function type="conic" signature="conic(point,point,point,point,point)"><code></asyxml>*/
conic conic(point M1, point M2, point M3, point M4, point M5)
{/*<asyxml></code><documentation>Return the conic passing through 'M1', 'M2', 'M3', 'M4' and 'M5' if the conic is not degenerated.</documentation></function></asyxml>*/
  bqe bqe = bqe(M1, M2, M3, M4, M5);
  string ct = conictype(bqe);
  if(ct == "degenerated") abort("conic: degenerated conic passing through five points.");
  if(ct == "ellipse") return ellipse(bqe);
  if(ct == "parabola") return parabola(bqe);
  return hyperbola(bqe);
}

/*<asyxml><function type="coordsys" signature="canonicalcartesiansystem(hyperbola)"><code></asyxml>*/
coordsys canonicalcartesiansystem(explicit conic co)
{/*<asyxml></code><documentation>Return the canonical cartesian system of the conic 'co'.</documentation></function></asyxml>*/
  if(co.e < 1) return canonicalcartesiansystem((ellipse)co);
  else if(co.e == 1) return canonicalcartesiansystem((parabola)co);
  return canonicalcartesiansystem((hyperbola)co);
}

/*<asyxml><function type="bqe" signature="canonical(bqe)"><code></asyxml>*/
bqe canonical(bqe bqe)
{/*<asyxml></code><documentation>Return the bivariate quadratic equation relative to the
   canonical coordinate system of the conic section represented by 'bqe'.</documentation></function></asyxml>*/
  string type = conictype(bqe);
  if(type == "") abort("canonical: the equation can not be performed.");
  bqe obqe;
  if(type == "ellipse") {
    ellipse el = ellipse(bqe);
    obqe = changecoordsys(canonicalcartesiansystem(el), equation(el));
  } else {
    if(type == "parabola") {
      parabola p = parabola(bqe);
      obqe = changecoordsys(canonicalcartesiansystem(p), equation(p));
    } else {
      hyperbola h = hyperbola(bqe);
      obqe = changecoordsys(canonicalcartesiansystem(h), equation(h));
    }
  }
  return obqe;
}

/*<asyxml><function type="conic" signature="conic(bqe)"><code></asyxml>*/
conic conic(bqe bqe)
{/*<asyxml></code><documentation>Return the conic section represented by the bivariate quartic equation 'bqe'.</documentation></function></asyxml>*/
  string type = conictype(bqe);
  if(type == "") abort("canonical: the equation can not be performed.");
  conic oc;
  if(type == "ellipse") {
    oc = ellipse(bqe);
  } else {
    if(type == "parabola") oc = parabola(bqe); else oc = hyperbola(bqe);
  }
  return oc;
}

/*<asyxml><function type="real" signature="arclength(circle)"><code></asyxml>*/
real arclength(circle c)
{/*<asyxml></code><documentation></documentation></function></asyxml>*/
  return c.r * 2 * pi;
}

/*<asyxml><function type="real" signature="focusToCenter(ellipse,real)"><code></asyxml>*/
real focusToCenter(ellipse el, real a)
{/*<asyxml></code><documentation>Return the angle relatively to the center of 'el' for the angle 'a'
   given relatively to the focus of 'el'.</documentation></function></asyxml>*/
  pair p = point(fromFocus(el, a, a, 1, CCW), 0);
  pair c = locate(el.C);
  real d = degrees(p - c) - el.angle;
  d = abs(d) < epsgeo ? 0 : d; // Avoid -1e-15
  return d%(sgnd(a) * 360);
}

/*<asyxml><function type="real" signature="centerToFocus(ellipse,real)"><code></asyxml>*/
real centerToFocus(ellipse el, real a)
{/*<asyxml></code><documentation>Return the angle relatively to the focus of 'el' for the angle 'a'
   given relatively to the center of 'el'.</documentation></function></asyxml>*/
  pair P = point(fromCenter(el, a, a, 1, CCW), 0);
  pair F1 = locate(el.F1);
  pair F2 = locate(el.F2);
  real d = degrees(P - F1) - degrees(F2 - F1);
  d = abs(d) < epsgeo ? 0 : d; // Avoid -1e-15
  return d%(sgnd(a) * 360);
}

/*<asyxml><function type="real" signature="arclength(ellipse)"><code></asyxml>*/
real arclength(ellipse el)
{/*<asyxml></code><documentation></documentation></function></asyxml>*/
  return degenerate(el) ? infinity : 4 * el.a * elle(pi/2, el.e);
}

/*<asyxml><function type="real" signature="arclength(ellipse,real,real,bool,polarconicroutine)"><code></asyxml>*/
real arclength(ellipse el, real angle1, real angle2,
               bool direction = CCW,
               polarconicroutine polarconicroutine = currentpolarconicroutine)
{/*<asyxml></code><documentation>Return the length of the arc of the ellipse between 'angle1'
   and 'angle2'.
   'angle1' and 'angle2' must be in the interval ]-360;+oo[ if polarconicroutine = fromFocus,
   ]-oo;+oo[ if polarconicroutine = fromCenter.</documentation></function></asyxml>*/
  if(degenerate(el)) return infinity;
  if(angle1 > angle2) return arclength(el, angle2, angle1, !direction, polarconicroutine);
  //   path g;int n = 1000;
  //   if(el.e == 0) g = arcfromcenter(el, angle1, angle2, n, direction);
  //   if(el.e != 1) g = polarconicroutine(el, angle1, angle2, n, direction);
  //   write("with path = ", arclength(g));
  if(polarconicroutine == fromFocus) {
    //   dot(point(fromFocus(el, angle1, angle1, 1, CCW), 0), 2mm + blue);
    //   dot(point(fromFocus(el, angle2, angle2, 1, CCW), 0), 2mm + blue);
    //   write("fromfocus1 = ", angle1);
    //   write("fromfocus2 = ", angle2);
    real gle1 = focusToCenter(el, angle1);
    real gle2 = focusToCenter(el, angle2);
    if((gle1 - gle2) * (angle1 - angle2) > 0) {
      angle1 = gle1; angle2 = gle2;
    } else {
      angle1 = gle2; angle2 = gle1;
    }
    //   dot(point(fromCenter(el, angle1, angle1, 1, CCW), 0), 1mm + red);
    //   dot(point(fromCenter(el, angle2, angle2, 1, CCW), 0), 1mm + red);
    //   write("fromcenter1 = ", angle1);
    //   write("fromcenter2 = ", angle2);
  }
  if(angle1 < 0 || angle2 < 0) return arclength(el, 180 + angle1, 180 + angle2, direction, fromCenter);
  real a1 = direction ? angle1 : angle2;
  real a2 = direction ? angle2 : angle1 + 360;
  real elleq = el.a * elle(pi/2, el.e);
  real S(real a)
  {//Return the arclength from 0 to the angle 'a' (in degrees)
    // given form the center of the ellipse.
    real gle = atan(el.a * Tan(a)/el.b)+
      pi * (((a%90 == 0 && a != 0) ? floor(a/90) - 1 : floor(a/90)) -
          ((a%180 == 0) ? 0 : floor(a/180)) -
          (a%360 == 0 ? floor(a/(360)) : 0));
    /* // Uncomment to visualize the used branches
       unitsize(2cm, 1cm);
       import graph;

       real xmin = 0, xmax = 3pi;

       xlimits( xmin, xmax);
       ylimits( 0, 10);
       yaxis( "y" , LeftRight(), RightTicks(pTick=.8red, ptick = lightgrey, extend = true));
       xaxis( "x - value", BottomTop(), Ticks(Label("$%.2f$", red), Step = pi/2, step = pi/4, pTick=.8red, ptick = lightgrey, extend = true));

       real p2 = pi/2;
       real f(real t)
       {
       return atan(0.6 * tan(t))+
       pi * ((t%p2 == 0 && t != 0) ? floor(t/p2) - 1 : floor(t/p2)) -
       ((t%pi == 0) ? 0 : pi * floor(t/pi)) - (t%(2pi) == 0 ? pi * floor(t/(2 * pi)) : 0);
       }

       draw(graph(f, xmin, xmax, 100));
       write(degrees(f(pi/2)));
       write(degrees(f(pi)));
       write(degrees(f(3pi/2)));
       write(degrees(f(2pi)));
       draw(graph(new real(real t){return t;}, xmin, xmax, 3));
    */
    return elleq - el.a * elle(pi/2 - gle, el.e);
  }
  return S(a2) - S(a1);
}

/*<asyxml><function type="real" signature="arclength(parabola,real)"><code></asyxml>*/
real arclength(parabola p, real angle)
{/*<asyxml></code><documentation>Return the arclength from 180 to 'angle' given from focus in the
   canonical coordinate system of 'p'.</documentation></function></asyxml>*/
  real a = p.a; /* In canonicalcartesiansystem(p) the equation of p
                 is x = y^2/(4a) */
  // integrate(sqrt(1 + (x/(2 * a))^2), x);
  real S(real t){return 0.5 * t * sqrt(1 + t^2/(4 * a^2)) + a * asinh(t/(2 * a));}
  real R(real gle){return 2 * a/(1 - Cos(gle));}
  real t = Sin(angle) * R(angle);
  return S(t);
}

/*<asyxml><function type="real" signature="arclength(parabola,real,real)"><code></asyxml>*/
real arclength(parabola p, real angle1, real angle2)
{/*<asyxml></code><documentation>Return the arclength from 'angle1' to 'angle2' given from
   focus in the canonical coordinate system of 'p'</documentation></function></asyxml>*/
  return arclength(p, angle1) - arclength(p, angle2);
}

/*<asyxml><function type="real" signature="arclength(parabola p)"><code></asyxml>*/
real arclength(parabola p)
{/*<asyxml></code><documentation>Return the length of the arc of the parabola bounded to the bounding
   box of the current picture.</documentation></function></asyxml>*/
  real[] b = bangles(p);
  return arclength(p, b[0], b[1]);
}
// *........................CONICS.........................*
// *=======================================================*

// *=======================================================*
// *.......................ABSCISSA........................*
/*<asyxml><struct signature="abscissa"><code></asyxml>*/
struct abscissa
{/*<asyxml></code><documentation>Provide abscissa structure on a curve used in the routine-like 'point(object, abscissa)'
   where object can be 'line','segment','ellipse','circle','conic'...</documentation><property type = "real" signature="x"><code></asyxml>*/
  real x;/*<asyxml></code><documentation>The abscissa value.</documentation></property><property type = "int" signature="system"><code></asyxml>*/
  int system;/*<asyxml></code><documentation>0 = relativesystem; 1 = curvilinearsystem; 2 = angularsystem; 3 = nodesystem</documentation></property><property type = "polarconicroutine" signature="polarconicroutine"><code></asyxml>*/
  polarconicroutine polarconicroutine = fromCenter;/*<asyxml></code><documentation>The routine used with angular system and two foci conic section.
                                                   Possible values are 'formCenter' and 'formFocus'.</documentation></property></asyxml>*/
  /*<asyxml><method type = "abscissa" signature="copy()"><code></asyxml>*/
  abscissa copy()
  {/*<asyxml></code><documentation>Return a copy of this abscissa.</documentation></method></asyxml>*/
    abscissa oa = new abscissa;
    oa.x = this.x;
    oa.system = this.system;
    oa.polarconicroutine = this.polarconicroutine;
    return oa;
  }
}/*<asyxml></struct></asyxml>*/

/*<asyxml><constant type = "int" signature="relativesystem,curvilinearsystem,angularsystem,nodesystem"><code></asyxml>*/
restricted int relativesystem = 0, curvilinearsystem = 1, angularsystem = 2, nodesystem = 3;/*<asyxml></code><documentation>Constant used to set the abscissa system.</documentation></constant></asyxml>*/

/*<asyxml><operator type = "abscissa" signature="cast(explicit position)"><code></asyxml>*/
abscissa operator cast(explicit position position)
{/*<asyxml></code><documentation>Cast position to abscissa.
   If 'position' is relative, the abscissa is relative else it's a curvilinear abscissa.</documentation></operator></asyxml>*/
  abscissa oarcc;
  oarcc.x = position.position.x;
  oarcc.system = position.relative ? relativesystem : curvilinearsystem;
  return oarcc;
}

/*<asyxml><operator type = "abscissa" signature="+(real,explicit abscissa)"><code></asyxml>*/
abscissa operator +(real x, explicit abscissa a)
{/*<asyxml></code><documentation>Provide 'real + abscissa'.
   Return abscissa b so that b.x = a.x + x.
   +(explicit abscissa, real), -(real, explicit abscissa) and -(explicit abscissa, real) are also defined.</documentation></operator></asyxml>*/
  abscissa oa = a.copy();
  oa.x = a.x + x;
  return oa;
}

abscissa operator +(explicit abscissa a, real x)
{
  return x + a;
}
abscissa operator +(int x, explicit abscissa a)
{
  return ((real)x) + a;
}

/*<asyxml><operator type = "abscissa" signature="-(explicit abscissa a)"><code></asyxml>*/
abscissa operator -(explicit abscissa a)
{/*<asyxml></code><documentation>Return the abscissa b so that b.x = -a.x.</documentation></operator></asyxml>*/
  abscissa oa;
  oa.system = a.system;
  oa.x = -a.x;
  return oa;
}

abscissa operator -(real x, explicit abscissa a)
{
  abscissa oa;
  oa.system = a.system;
  oa.x = x - a.x;
  return oa;
}
abscissa operator -(explicit abscissa a, real x)
{
  abscissa oa;
  oa.system = a.system;
  oa.x = a.x - x;
  return oa;
}
abscissa operator -(int x, explicit abscissa a)
{
  return ((real)x) - a;
}

/*<asyxml><operator type = "abscissa" signature="*(real,abscissa)"><code></asyxml>*/
abscissa operator *(real x, explicit abscissa a)
{/*<asyxml></code><documentation>Provide 'real * abscissa'.
   Return abscissa b so that b.x = x * a.x.
   *(explicit abscissa, real), /(real, explicit abscissa) and /(explicit abscissa, real) are also defined.</documentation></operator></asyxml>*/
  abscissa oa;
  oa.system = a.system;
  oa.x = a.x * x;
  return oa;
}
abscissa operator *(explicit abscissa a, real x)
{
  return x * a;
}

abscissa operator /(real x, explicit abscissa a)
{
  abscissa oa;
  oa.system = a.system;
  oa.x = x/a.x;
  return oa;
}
abscissa operator /(explicit abscissa a, real x)
{
  abscissa oa;
  oa.system = a.system;
  oa.x = a.x/x;
  return oa;
}

abscissa operator /(int x, explicit abscissa a)
{
  return ((real)x)/a;
}

/*<asyxml><function type="abscissa" signature="relabscissa(real)"><code></asyxml>*/
abscissa relabscissa(real x)
{/*<asyxml></code><documentation>Return a relative abscissa.</documentation></function></asyxml>*/
  return (abscissa)(Relative(x));
}
abscissa relabscissa(int x)
{
  return (abscissa)(Relative(x));
}

/*<asyxml><function type="abscissa" signature="curabscissa(real)"><code></asyxml>*/
abscissa curabscissa(real x)
{/*<asyxml></code><documentation>Return a curvilinear abscissa.</documentation></function></asyxml>*/
  return (abscissa)((position)x);
}
abscissa curabscissa(int x)
{
  return (abscissa)((position)x);
}

/*<asyxml><function type="abscissa" signature="angabscissa(real,polarconicroutine)"><code></asyxml>*/
abscissa angabscissa(real x, polarconicroutine polarconicroutine = currentpolarconicroutine)
{/*<asyxml></code><documentation>Return a angular abscissa.</documentation></function></asyxml>*/
  abscissa oarcc;
  oarcc.x = x;
  oarcc.polarconicroutine = polarconicroutine;
  oarcc.system = angularsystem;
  return oarcc;
}
abscissa angabscissa(int x, polarconicroutine polarconicroutine = currentpolarconicroutine)
{
  return angabscissa((real)x, polarconicroutine);
}

/*<asyxml><function type="abscissa" signature="nodabscissa(real)"><code></asyxml>*/
abscissa nodabscissa(real x)
{/*<asyxml></code><documentation>Return an abscissa as time on the path.</documentation></function></asyxml>*/
  abscissa oarcc;
  oarcc.x = x;
  oarcc.system = nodesystem;
  return oarcc;
}
abscissa nodabscissa(int x)
{
  return nodabscissa((real)x);
}

/*<asyxml><operator type = "abscissa" signature="cast(real)"><code></asyxml>*/
abscissa operator cast(real x)
{/*<asyxml></code><documentation>Cast real to abscissa, precisely 'nodabscissa'.</documentation></operator></asyxml>*/
  return nodabscissa(x);
}
abscissa operator cast(int x)
{
  return nodabscissa((real)x);
}

/*<asyxml><function type="point" signature="point(circle,abscissa)"><code></asyxml>*/
point point(circle c, abscissa l)
{/*<asyxml></code><documentation>Return the point of 'c' which has the abscissa 'l.x'
   according to the abscissa system 'l.system'.</documentation></function></asyxml>*/
  coordsys R = c.C.coordsys;
  if (l.system == nodesystem)
    return point(R, point((path)c, l.x)/R);
  if (l.system == relativesystem)
    return c.C + point(R, R.polar(c.r, 2 * pi * l.x));
  if (l.system == curvilinearsystem)
    return c.C + point(R, R.polar(c.r, l.x/c.r));
  if (l.system == angularsystem)
    return c.C + point(R, R.polar(c.r, radians(l.x)));
  abort("point: bad abscissa system.");
  return (0, 0);
}

/*<asyxml><function type="point" signature="point(ellipse,abscissa)"><code></asyxml>*/
point point(ellipse el, abscissa l)
{/*<asyxml></code><documentation>Return the point of 'el' which has the abscissa 'l.x'
   according to the abscissa system 'l.system'.</documentation></function></asyxml>*/
  if(el.e == 0) return point((circle)el, l);
  coordsys R = coordsys(el);
  if (l.system == nodesystem)
    return point(R, point((path)el, l.x)/R);
  if (l.system == relativesystem) {
    return point(el, curabscissa((l.x%1) * arclength(el)));
  }
  if (l.system == curvilinearsystem) {
    real a1 = 0, a2 = 360, cx = 0;
    real aout = a1;
    real x = abs(l.x)%arclength(el);
    while (abs(cx - x) > epsgeo) {
      aout = (a1 + a2)/2;
      cx = arclength(el, 0, aout, CCW, fromCenter); //fromCenter is speeder
      if(cx > x) a2 = (a1 + a2)/2; else a1 = (a1 + a2)/2;
    }
    path pel = fromCenter(el, sgn(l.x) * aout, sgn(l.x) * aout, 1, CCW);
    return point(R, point(pel, 0)/R);
  }
  if (l.system == angularsystem) {
    return point(R, point(l.polarconicroutine(el, l.x, l.x, 1, CCW), 0)/R);
  }
  abort("point: bad abscissa system.");
  return (0, 0);
}

/*<asyxml><function type="point" signature="point(parabola,abscissa)"><code></asyxml>*/
point point(parabola p, abscissa l)
{/*<asyxml></code><documentation>Return the point of 'p' which has the abscissa 'l.x'
   according to the abscissa system 'l.system'.</documentation></function></asyxml>*/
  coordsys R = coordsys(p);
  if (l.system == nodesystem)
    return point(R, point((path)p, l.x)/R);
  if (l.system == relativesystem) {
    real[] b = bangles(p);
    real al = sgn(l.x) > 0 ? arclength(p, 180, b[1]) : arclength(p, 180, b[0]);
    return point(p, curabscissa(abs(l.x) * al));
  }
  if (l.system == curvilinearsystem) {
    real a1 = 1e-3, a2 = 360 - 1e-3, cx = infinity;
    while (abs(cx - l.x) > epsgeo) {
      cx = arclength(p, 180, (a1 + a2)/2);
      if(cx > l.x) a2 = (a1 + a2)/2; else a1 = (a1 + a2)/2;
    }
    path pp = fromFocus(p, a1, a1, 1, CCW);
    return point(R, point(pp, 0)/R);
  }
  if (l.system == angularsystem) {
    return point(R, point(fromFocus(p, l.x, l.x, 1, CCW), 0)/R);
  }
  abort("point: bad abscissa system.");
  return (0, 0);
}

/*<asyxml><function type="point" signature="point(hyperbola,abscissa)"><code></asyxml>*/
point point(hyperbola h, abscissa l)
{/*<asyxml></code><documentation>Return the point of 'h' which has the abscissa 'l.x'
   according to the abscissa system 'l.system'.</documentation></function></asyxml>*/
  coordsys R = coordsys(h);
  if (l.system == nodesystem)
    return point(R, point((path)h, l.x)/R);
  if (l.system == relativesystem) {
    abort("point(hyperbola, relativeSystem) is not implemented...
Try relpoint((path)your_hyperbola, x);");
  }
  if (l.system == curvilinearsystem) {
    abort("point(hyperbola, curvilinearSystem) is not implemented...");
  }
  if (l.system == angularsystem) {
    return point(R, point(l.polarconicroutine(h, l.x, l.x, 1, CCW), 0)/R);
  }
  abort("point: bad abscissa system.");
  return (0, 0);
}

/*<asyxml><function type="abscissa" signature="point(conic,point)"><code></asyxml>*/
point point(explicit conic co, abscissa l)
{/*<asyxml></code><documentation>Return the curvilinear abscissa of 'M' on the conic 'co'.</documentation></function></asyxml>*/
  if(co.e == 0) return point((circle)co, l);
  if(co.e < 1) return point((ellipse)co, l);
  if(co.e == 1) return point((parabola)co, l);
  return point((hyperbola)co, l);
}


/*<asyxml><function type="point" signature="point(line,abscissa)"><code></asyxml>*/
point point(line l, abscissa x)
{/*<asyxml></code><documentation>Return the point of 'l' which has the abscissa 'l.x' according to the abscissa system 'l.system'.
   Note that the origin is l.A, and point(l, relabscissa(x)) returns l.A + x.x * vector(l.B - l.A).</documentation></function></asyxml>*/
  coordsys R = l.A.coordsys;
  if (x.system == nodesystem)
    return l.A + (x.x < 0 ? 0 : x.x > 1 ? 1 : x.x) * vector(l.B - l.A);
  if (x.system == relativesystem)
    return l.A + x.x * vector(l.B - l.A);
  if (x.system == curvilinearsystem)
    return l.A + x.x * l.u;
  if (x.system == angularsystem)
    abort("point: what the meaning of angular abscissa on line ?.");
  abort("point: bad abscissa system.");
  return (0, 0);
}

/*<asyxml><function type="point" signature="point(line,real)"><code></asyxml>*/
point point(line l, explicit real x)
{/*<asyxml></code><documentation>Return the point between node l.A and l.B (x <= 0 means l.A, x >=1 means l.B).</documentation></function></asyxml>*/
  return point(l, nodabscissa(x));
}
point point(line l, explicit int x)
{
  return point(l, nodabscissa(x));
}

/*<asyxml><function type="circle" signature="point(explicit circle,explicit real)"><code></asyxml>*/
point point(explicit circle c, explicit real x)
{/*<asyxml></code><documentation>Return the point between node floor(x) and floor(x) + 1.</documentation></function></asyxml>*/
  return point(c, nodabscissa(x));
}
point point(explicit circle c, explicit int x)
{
  return point(c, nodabscissa(x));
}

/*<asyxml><function type="point" signature="point(explicit ellipse,explicit real)"><code></asyxml>*/
point point(explicit ellipse el, explicit real x)
{/*<asyxml></code><documentation>Return the point between node floor(x) and floor(x) + 1.</documentation></function></asyxml>*/
  return point(el, nodabscissa(x));
}
point point(explicit ellipse el, explicit int x)
{
  return point(el, nodabscissa(x));
}

/*<asyxml><function type="point" signature="point(explicit parabola,explicit real)"><code></asyxml>*/
point point(explicit parabola p, explicit real x)
{/*<asyxml></code><documentation>Return the point between node floor(x) and floor(x) + 1.</documentation></function></asyxml>*/
  return point(p, nodabscissa(x));
}
point point(explicit parabola p, explicit int x)
{
  return point(p, nodabscissa(x));
}

/*<asyxml><function type="point" signature="point(explicit hyperbola,explicit real)"><code></asyxml>*/
point point(explicit hyperbola h, explicit real x)
{/*<asyxml></code><documentation>Return the point between node floor(x) and floor(x) + 1.</documentation></function></asyxml>*/
  return point(h, nodabscissa(x));
}
point point(explicit hyperbola h, explicit int x)
{
  return point(h, nodabscissa(x));
}

/*<asyxml><function type="point" signature="point(explicit conic,explicit real)"><code></asyxml>*/
point point(explicit conic co, explicit real x)
{/*<asyxml></code><documentation>Return the point between node floor(x) and floor(x) + 1.</documentation></function></asyxml>*/
  point op;
  if(co.e == 0) op = point((circle)co, nodabscissa(x));
  else if(co.e < 1) op = point((ellipse)co, nodabscissa(x));
  else if(co.e == 1) op = point((parabola)co, nodabscissa(x));
  else op = point((hyperbola)co, nodabscissa(x));
  return op;
}
point point(explicit conic co, explicit int x)
{
  return point(co, (real)x);
}

/*<asyxml><function type="point" signature="relpoint(line,real)"><code></asyxml>*/
point relpoint(line l, real x)
{/*<asyxml></code><documentation>Return the relative point of 'l' (0 means l.A,
   1 means l.B, x means l.A + x * vector(l.B - l.A) ).</documentation></function></asyxml>*/
  return point(l, Relative(x));
}

/*<asyxml><function type="point" signature="relpoint(explicit circle,real)"><code></asyxml>*/
point relpoint(explicit circle c, real x)
{/*<asyxml></code><documentation>Return the relative point of 'c' (0 means origin, 1 means end).
   Origin is c.center + c.r * (1, 0).</documentation></function></asyxml>*/
  return point(c, Relative(x));
}

/*<asyxml><function type="point" signature="relpoint(explicit ellipse,real)"><code></asyxml>*/
point relpoint(explicit ellipse el, real x)
{/*<asyxml></code><documentation>Return the relative point of 'el' (0 means origin, 1 means end).</documentation></function></asyxml>*/
  return point(el, Relative(x));
}

/*<asyxml><function type="point" signature="relpoint(explicit parabola,real)"><code></asyxml>*/
point relpoint(explicit parabola p, real x)
{/*<asyxml></code><documentation>Return the relative point of the path of the parabola
   bounded by the bounding box of the current picture.
   0 means origin, 1 means end, where the origin is the vertex of 'p'.</documentation></function></asyxml>*/
  return point(p, Relative(x));
}

/*<asyxml><function type="point" signature="relpoint(explicit hyperbola,real)"><code></asyxml>*/
point relpoint(explicit hyperbola h, real x)
{/*<asyxml></code><documentation>Not yet implemented... <look href = "point(hyperbola, abscissa)"/></documentation></function></asyxml>*/
  return point(h, Relative(x));
}

/*<asyxml><function type="point" signature="relpoint(explicit conic,explicit real)"><code></asyxml>*/
point relpoint(explicit conic co, explicit real x)
{/*<asyxml></code><documentation>Return the relative point of 'co' (0 means origin, 1 means end).</documentation></function></asyxml>*/
  point op;
  if(co.e == 0) op = point((circle)co, Relative(x));
  else if(co.e < 1) op = point((ellipse)co, Relative(x));
  else if(co.e == 1) op = point((parabola)co, Relative(x));
  else op = point((hyperbola)co, Relative(x));
  return op;
}
point relpoint(explicit conic co, explicit int x)
{
  return relpoint(co, (real)x);
}

/*<asyxml><function type="point" signature="angpoint(explicit circle,real)"><code></asyxml>*/
point angpoint(explicit circle c, real x)
{/*<asyxml></code><documentation>Return the point of 'c' in the direction 'x' measured in degrees.</documentation></function></asyxml>*/
  return point(c, angabscissa(x));
}

/*<asyxml><function type="point" signature="angpoint(explicit ellipse,real,polarconicroutine)"><code></asyxml>*/
point angpoint(explicit ellipse el, real x,
               polarconicroutine polarconicroutine = currentpolarconicroutine)
{/*<asyxml></code><documentation>Return the point of 'el' in the direction 'x'
   measured in degrees according to 'polarconicroutine'.</documentation></function></asyxml>*/
  return el.e == 0 ? angpoint((circle) el, x) : point(el, angabscissa(x, polarconicroutine));
}

/*<asyxml><function type="point" signature="angpoint(explicit parabola,real)"><code></asyxml>*/
point angpoint(explicit parabola p, real x)
{/*<asyxml></code><documentation>Return the point of 'p' in the direction 'x' measured in degrees.</documentation></function></asyxml>*/
  return point(p, angabscissa(x));
}

/*<asyxml><function type="point" signature="angpoint(explicit hyperbola,real,polarconicroutine)"><code></asyxml>*/
point angpoint(explicit hyperbola h, real x,
               polarconicroutine polarconicroutine = currentpolarconicroutine)
{/*<asyxml></code><documentation>Return the point of 'h' in the direction 'x'
   measured in degrees according to 'polarconicroutine'.</documentation></function></asyxml>*/
  return point(h, angabscissa(x, polarconicroutine));
}

/*<asyxml><function type="point" signature="curpoint(line,real)"><code></asyxml>*/
point curpoint(line l, real x)
{/*<asyxml></code><documentation>Return the point of 'l' which has the curvilinear abscissa 'x'.
   Origin is l.A.</documentation></function></asyxml>*/
  return point(l, curabscissa(x));
}

/*<asyxml><function type="point" signature="curpoint(explicit circle,real)"><code></asyxml>*/
point curpoint(explicit circle c, real x)
{/*<asyxml></code><documentation>Return the point of 'c' which has the curvilinear abscissa 'x'.
   Origin is c.center + c.r * (1, 0).</documentation></function></asyxml>*/
  return point(c, curabscissa(x));
}

/*<asyxml><function type="point" signature="curpoint(explicit ellipse,real)"><code></asyxml>*/
point curpoint(explicit ellipse el, real x)
{/*<asyxml></code><documentation>Return the point of 'el' which has the curvilinear abscissa 'el'.</documentation></function></asyxml>*/
  return point(el, curabscissa(x));
}

/*<asyxml><function type="point" signature="curpoint(explicit parabola,real)"><code></asyxml>*/
point curpoint(explicit parabola p, real x)
{/*<asyxml></code><documentation>Return the point of 'p' which has the curvilinear abscissa 'x'.
   Origin is the vertex of 'p'.</documentation></function></asyxml>*/
  return point(p, curabscissa(x));
}

/*<asyxml><function type="point" signature="curpoint(conic,real)"><code></asyxml>*/
point curpoint(conic co, real x)
{/*<asyxml></code><documentation>Return the point of 'co' which has the curvilinear abscissa 'x'.</documentation></function></asyxml>*/
  point op;
  if(co.e == 0) op = point((circle)co, curabscissa(x));
  else if(co.e < 1) op = point((ellipse)co, curabscissa(x));
  else if(co.e == 1) op = point((parabola)co, curabscissa(x));
  else op = point((hyperbola)co, curabscissa(x));
  return op;
}

/*<asyxml><function type="abscissa" signature="angabscissa(circle,point)"><code></asyxml>*/
abscissa angabscissa(circle c, point M)
{/*<asyxml></code><documentation>Return the angular abscissa of 'M' on the circle 'c'.</documentation></function></asyxml>*/
  if(!(M @ c)) abort("angabscissa: the point is not on the circle.");
  abscissa oa;
  oa.system = angularsystem;
  oa.x = degrees(M - c.C);
  if(oa.x < 0) oa.x+=360;
  return oa;
}

/*<asyxml><function type="abscissa" signature="angabscissa(ellipse,point,polarconicroutine)"><code></asyxml>*/
abscissa angabscissa(ellipse el, point M,
                     polarconicroutine polarconicroutine = currentpolarconicroutine)
{/*<asyxml></code><documentation>Return the angular abscissa of 'M' on the ellipse 'el' according to 'polarconicroutine'.</documentation></function></asyxml>*/
  if(!(M @ el)) abort("angabscissa: the point is not on the ellipse.");
  abscissa oa;
  oa.system = angularsystem;
  oa.polarconicroutine = polarconicroutine;
  oa.x = polarconicroutine == fromCenter ? degrees(M - el.C) : degrees(M - el.F1);
  oa.x -= el.angle;
  if(oa.x < 0) oa.x += 360;
  return oa;
}

/*<asyxml><function type="abscissa" signature="angabscissa(hyperbola,point,polarconicroutine)"><code></asyxml>*/
abscissa angabscissa(hyperbola h, point M,
                     polarconicroutine polarconicroutine = currentpolarconicroutine)
{/*<asyxml></code><documentation>Return the angular abscissa of 'M' on the hyperbola 'h' according to 'polarconicroutine'.</documentation></function></asyxml>*/
  if(!(M @ h)) abort("angabscissa: the point is not on the hyperbola.");
  abscissa oa;
  oa.system = angularsystem;
  oa.polarconicroutine = polarconicroutine;
  oa.x = polarconicroutine == fromCenter ? degrees(M - h.C) : degrees(M - h.F1) + 180;
  oa.x -= h.angle;
  if(oa.x < 0) oa.x += 360;
  return oa;
}

/*<asyxml><function type="abscissa" signature="angabscissa(parabola,point)"><code></asyxml>*/
abscissa angabscissa(parabola p, point M)
{/*<asyxml></code><documentation>Return the angular abscissa of 'M' on the parabola 'p'.</documentation></function></asyxml>*/
  if(!(M @ p)) abort("angabscissa: the point is not on the parabola.");
  abscissa oa;
  oa.system = angularsystem;
  oa.polarconicroutine = fromFocus;// Not used
  oa.x = degrees(M - p.F);
  oa.x -= p.angle;
  if(oa.x < 0) oa.x += 360;
  return oa;
}

/*<asyxml><function type="abscissa" signature="angabscissa(conic,point)"><code></asyxml>*/
abscissa angabscissa(explicit conic co, point M)
{/*<asyxml></code><documentation>Return the angular abscissa of 'M' on the conic 'co'.</documentation></function></asyxml>*/
  if(co.e == 0) return angabscissa((circle)co, M);
  if(co.e < 1) return angabscissa((ellipse)co, M);
  if(co.e == 1) return angabscissa((parabola)co, M);
  return angabscissa((hyperbola)co, M);
}

/*<asyxml><function type="abscissa" signature="curabscissa(line,point)"><code></asyxml>*/
abscissa curabscissa(line l, point M)
{/*<asyxml></code><documentation>Return the curvilinear abscissa of 'M' on the line 'l'.</documentation></function></asyxml>*/
  if(!(M @ extend(l))) abort("curabscissa: the point is not on the line.");
  abscissa oa;
  oa.system = curvilinearsystem;
  oa.x = sgn(dot(M - l.A, l.B - l.A)) * abs(M - l.A);
  return oa;
}

/*<asyxml><function type="abscissa" signature="curabscissa(circle,point)"><code></asyxml>*/
abscissa curabscissa(circle c, point M)
{/*<asyxml></code><documentation>Return the curvilinear abscissa of 'M' on the circle 'c'.</documentation></function></asyxml>*/
  if(!(M @ c)) abort("curabscissa: the point is not on the circle.");
  abscissa oa;
  oa.system = curvilinearsystem;
  oa.x = pi * angabscissa(c, M).x * c.r/180;
  return oa;
}

/*<asyxml><function type="abscissa" signature="curabscissa(ellipse,point)"><code></asyxml>*/
abscissa curabscissa(ellipse el, point M)
{/*<asyxml></code><documentation>Return the curvilinear abscissa of 'M' on the ellipse 'el'.</documentation></function></asyxml>*/
  if(!(M @ el)) abort("curabscissa: the point is not on the ellipse.");
  abscissa oa;
  oa.system = curvilinearsystem;
  real a = angabscissa(el, M, fromCenter).x;
  oa.x = arclength(el, 0, a, fromCenter);
  oa.polarconicroutine = fromCenter;
  return oa;
}

/*<asyxml><function type="abscissa" signature="curabscissa(parabola,point)"><code></asyxml>*/
abscissa curabscissa(parabola p, point M)
{/*<asyxml></code><documentation>Return the curvilinear abscissa of 'M' on the parabola 'p'.</documentation></function></asyxml>*/
  if(!(M @ p)) abort("curabscissa: the point is not on the parabola.");
  abscissa oa;
  oa.system = curvilinearsystem;
  real a = angabscissa(p, M).x;
  oa.x = arclength(p, 180, a);
  oa.polarconicroutine = fromFocus; // Not used.
  return oa;
}

/*<asyxml><function type="abscissa" signature="curabscissa(conic,point)"><code></asyxml>*/
abscissa curabscissa(conic co, point M)
{/*<asyxml></code><documentation>Return the curvilinear abscissa of 'M' on the conic 'co'.</documentation></function></asyxml>*/
  if(co.e > 1) abort("curabscissa: not implemented for this hyperbola.");
  if(co.e == 0) return curabscissa((circle)co, M);
  if(co.e < 1) return curabscissa((ellipse)co, M);
  return curabscissa((parabola)co, M);
}

/*<asyxml><function type="abscissa" signature="nodabscissa(line,point)"><code></asyxml>*/
abscissa nodabscissa(line l, point M)
{/*<asyxml></code><documentation>Return the node abscissa of 'M' on the line 'l'.</documentation></function></asyxml>*/
  if(!(M @ (segment)l)) abort("nodabscissa: the point is not on the segment.");
  abscissa oa;
  oa.system = nodesystem;
  oa.x = abs(M - l.A)/abs(l.A - l.B);
  return oa;
}

/*<asyxml><function type="abscissa" signature="nodabscissa(circle,point)"><code></asyxml>*/
abscissa nodabscissa(circle c, point M)
{/*<asyxml></code><documentation>Return the node abscissa of 'M' on the circle 'c'.</documentation></function></asyxml>*/
  if(!(M @ c)) abort("nodabscissa: the point is not on the circle.");
  abscissa oa;
  oa.system = nodesystem;
  oa.x = intersect((path)c, locate(M))[0];
  return oa;
}

/*<asyxml><function type="abscissa" signature="nodabscissa(ellipse,point)"><code></asyxml>*/
abscissa nodabscissa(ellipse el, point M)
{/*<asyxml></code><documentation>Return the node abscissa of 'M' on the ellipse 'el'.</documentation></function></asyxml>*/
  if(!(M @ el)) abort("nodabscissa: the point is not on the ellipse.");
  abscissa oa;
  oa.system = nodesystem;
  oa.x = intersect((path)el, M)[0];
  return oa;
}

/*<asyxml><function type="abscissa" signature="nodabscissa(parabola,point)"><code></asyxml>*/
abscissa nodabscissa(parabola p, point M)
{/*<asyxml></code><documentation>Return the node abscissa OF 'M' on the parabola 'p'.</documentation></function></asyxml>*/
  if(!(M @ p)) abort("nodabscissa: the point is not on the parabola.");
  abscissa oa;
  oa.system = nodesystem;
  path pg = p;
  real[] t = intersect(pg, M, 1e-5);
  if(t.length == 0) abort("nodabscissa: the point is not on the path of the parabola.");
  oa.x = t[0];
  return oa;
}

/*<asyxml><function type="abscissa" signature="nodabscissa(conic,point)"><code></asyxml>*/
abscissa nodabscissa(conic co, point M)
{/*<asyxml></code><documentation>Return the node abscissa of 'M' on the conic 'co'.</documentation></function></asyxml>*/
  if(co.e > 1) abort("nodabscissa: not implemented for hyperbola.");
  if(co.e == 0) return nodabscissa((circle)co, M);
  if(co.e < 1) return nodabscissa((ellipse)co, M);
  return nodabscissa((parabola)co, M);
}


/*<asyxml><function type="abscissa" signature="relabscissa(line,point)"><code></asyxml>*/
abscissa relabscissa(line l, point M)
{/*<asyxml></code><documentation>Return the relative abscissa of 'M' on the line 'l'.</documentation></function></asyxml>*/
  if(!(M @ extend(l))) abort("relabscissa: the point is not on the line.");
  abscissa oa;
  oa.system = relativesystem;
  oa.x = sgn(dot(M - l.A, l.B - l.A)) * abs(M - l.A)/abs(l.A - l.B);
  return oa;
}

/*<asyxml><function type="abscissa" signature="relabscissa(circle,point)"><code></asyxml>*/
abscissa relabscissa(circle c, point M)
{/*<asyxml></code><documentation>Return the relative abscissa of 'M' on the circle 'c'.</documentation></function></asyxml>*/
  if(!(M @ c)) abort("relabscissa: the point is not on the circle.");
  abscissa oa;
  oa.system = relativesystem;
  oa.x = angabscissa(c, M).x/360;
  return oa;
}

/*<asyxml><function type="abscissa" signature="relabscissa(ellipse,point)"><code></asyxml>*/
abscissa relabscissa(ellipse el, point M)
{/*<asyxml></code><documentation>Return the relative abscissa of 'M' on the ellipse 'el'.</documentation></function></asyxml>*/
  if(!(M @ el)) abort("relabscissa: the point is not on the ellipse.");
  abscissa oa;
  oa.system = relativesystem;
  oa.x = curabscissa(el, M).x/arclength(el);
  oa.polarconicroutine = fromFocus;
  return oa;
}

/*<asyxml><function type="abscissa" signature="relabscissa(conic,point)"><code></asyxml>*/
abscissa relabscissa(conic co, point M)
{/*<asyxml></code><documentation>Return the relative abscissa of 'M'
   on the conic 'co'.</documentation></function></asyxml>*/
  if(co.e > 1) abort("relabscissa: not implemented for hyperbola and parabola.");
  if(co.e == 1) return relabscissa((parabola)co, M);
  if(co.e == 0) return relabscissa((circle)co, M);
  return relabscissa((ellipse)co, M);
}
// *.......................ABSCISSA........................*
// *=======================================================*

// *=======================================================*
// *.........................ARCS..........................*
/*<asyxml><struct signature="arc"><code></asyxml>*/
struct arc {
  /*<asyxml></code><documentation>Implement oriented ellipse (included circle) arcs.
    All the calculus with this structure will be as exact as Asymptote can do.
    For a full precision, you must not cast 'arc' to 'path' excepted for drawing routines.
    </documentation><property type = "ellipse" signature="el"><code></asyxml>*/
  ellipse el;/*<asyxml></code><documentation>The support of the arc.</documentation></property><property type = "real" signature="angle0"><code></asyxml>*/
  restricted real angle0 = 0;/*<asyxml></code><documentation>Internal use: rotating a circle does not modify the origin point,this variable stocks the eventual angle rotation. This value is not used for ellipses which are not circles.</documentation></property><property type = "real" signature="angle1,angle2"><code></asyxml>*/
  restricted  real angle1, angle2;/*<asyxml></code><documentation>Values (in degrees) in ]-360, 360[.</documentation></property><property type = "bool" signature="direction"><code></asyxml>*/
  bool direction = CCW;/*<asyxml></code><documentation>The arc will be drawn from 'angle1' to 'angle2' rotating in the direction 'direction'.</documentation></property><property type = "polarconicroutine" signature="polarconicroutine"><code></asyxml>*/
  polarconicroutine polarconicroutine = currentpolarconicroutine;/*<asyxml></code><documentation>The routine to which the angles refer.
                                                                 If 'el' is a circle 'fromCenter' is always used.</documentation></property></asyxml>*/

  /*<asyxml><method type = "void" signature="setangles(real,real,real)"><code></asyxml>*/
  void setangles(real a0, real a1, real a2)
  {/*<asyxml></code><documentation>Set the angles.</documentation></method></asyxml>*/
    if (a1 < 0 && a2 < 0) {
      a1 += 360;
      a2 += 360;
    }
    this.angle0 = a0%(sgnd(a0) * 360);
    this.angle1 = a1%(sgnd(a1) * 360);
    this.angle2 = a2%(sgnd(2) * 360);
  }

  /*<asyxml><method type = "void" signature="init(ellipse,real,real,real,polarconicroutine,bool)"><code></asyxml>*/
  void init(ellipse el, real angle0 = 0, real angle1, real angle2,
            polarconicroutine polarconicroutine,
            bool direction = CCW)
  {/*<asyxml></code><documentation>Constructor.</documentation></method></asyxml>*/
    if(abs(angle1 - angle2) > 360) abort("arc: |angle1 - angle2| > 360.");
    this.el = el;
    this.setangles(angle0, angle1, angle2);
    this.polarconicroutine = polarconicroutine;
    this.direction = direction;
  }

  /*<asyxml><method type = "arc" signature="copy()"><code></asyxml>*/
  arc copy()
  {/*<asyxml></code><documentation>Copy the arc.</documentation></method></asyxml>*/
    arc oa = new arc;
    oa.el = this.el;
    oa.direction = this.direction;
    oa.polarconicroutine = this.polarconicroutine;
    oa.angle1 = this.angle1;
    oa.angle2 = this.angle2;
    oa.angle0 = this.angle0;
    return oa;
  }
}/*<asyxml></struct></asyxml>*/

/*<asyxml><function type="polarconicroutine" signature="polarconicroutine(ellipse)"><code></asyxml>*/
polarconicroutine polarconicroutine(conic co)
{/*<asyxml></code><documentation>Return the default routine used to draw a conic.</documentation></function></asyxml>*/
  if(co.e == 0) return fromCenter;
  if(co.e == 1) return fromFocus;
  return currentpolarconicroutine;
}

/*<asyxml><function type="arc" signature="arc(ellipse,real,real,polarconicroutine,bool)"><code></asyxml>*/
arc arc(ellipse el, real angle1, real angle2,
        polarconicroutine polarconicroutine = polarconicroutine(el),
        bool direction = CCW)
{/*<asyxml></code><documentation>Return the ellipse arc from 'angle1' to 'angle2' with respect to 'polarconicroutine' and rotating in the direction 'direction'.</documentation></function></asyxml>*/
  arc oa;
  oa.init(el, 0, angle1, angle2, polarconicroutine, direction);
  return oa;
}

/*<asyxml><function type="arc" signature="complementary(arc)"><code></asyxml>*/
arc complementary(arc a)
{/*<asyxml></code><documentation>Return the complementary of 'a'.</documentation></function></asyxml>*/
  arc oa;
  oa.init(a.el, a.angle0, a.angle2, a.angle1, a.polarconicroutine, a.direction);
  return oa;
}

/*<asyxml><function type="arc" signature="reverse(arc)"><code></asyxml>*/
arc reverse(arc a)
{/*<asyxml></code><documentation>Return arc 'a' oriented in reverse direction.</documentation></function></asyxml>*/
  arc oa;
  oa.init(a.el, a.angle0, a.angle2, a.angle1, a.polarconicroutine, !a.direction);
  return oa;
}

/*<asyxml><function type="real" signature="degrees(arc)"><code></asyxml>*/
real degrees(arc a)
{/*<asyxml></code><documentation>Return the measure in degrees of the oriented arc 'a'.</documentation></function></asyxml>*/
  real or;
  real da = a.angle2 - a.angle1;
  if(a.direction) {
    or = a.angle1 < a.angle2 ? da : 360 + da;
  } else {
    or = a.angle1 < a.angle2 ? -360 + da : da;
  }
  return or;
}

/*<asyxml><function type="real" signature="angle(a)"><code></asyxml>*/
real angle(arc a)
{/*<asyxml></code><documentation>Return the measure in radians of the oriented arc 'a'.</documentation></function></asyxml>*/
  return radians(degrees(a));
}

/*<asyxml><function type="int" signature="arcnodesnumber(explicit arc)"><code></asyxml>*/
int arcnodesnumber(explicit arc a)
{/*<asyxml></code><documentation>Return the number of nodes to draw the arc 'a'.</documentation></function></asyxml>*/
  return ellipsenodesnumber(a.el.a, a.el.b, a.angle1, a.angle2, a.direction);
}

private path arctopath(arc a, int n)
{
  if(a.el.e == 0) return arcfromcenter(a.el, a.angle0 + a.angle1, a.angle0 + a.angle2, a.direction, n);
  if(a.el.e != 1) return a.polarconicroutine(a.el, a.angle1, a.angle2, n, a.direction);
  return arcfromfocus(a.el, a.angle1, a.angle2, n, a.direction);
}

/*<asyxml><function type="point" signature="angpoint(arc,real)"><code></asyxml>*/
point angpoint(arc a, real angle)
{/*<asyxml></code><documentation>Return the point given by its angular position (in degrees) relative to the arc 'a'.
   If 'angle > degrees(a)' or 'angle < 0' the returned point is on the extended arc.</documentation></function></asyxml>*/
  pair p;
  if(a.el.e == 0) {
    real gle = a.angle0 + a.angle1 + (a.direction ? angle : -angle);
    p = point(arcfromcenter(a.el, gle, gle, CCW, 1), 0);
  }
  else {
    real gle = a.angle1 + (a.direction ? angle : -angle);
    p = point(a.polarconicroutine(a.el, gle, gle, 1, CCW), 0);
  }
  return point(coordsys(a.el), p/coordsys(a.el));
}

/*<asyxml><operator type = "path" signature="cast(explicit arc)"><code></asyxml>*/
path operator cast(explicit arc a)
{/*<asyxml></code><documentation>Cast arc to path.</documentation></operator></asyxml>*/
  return arctopath(a, arcnodesnumber(a));
}

/*<asyxml><operator type = "guide" signature="cast(explicit arc)"><code></asyxml>*/
guide operator cast(explicit arc a)
{/*<asyxml></code><documentation>Cast arc to guide.</documentation></operator></asyxml>*/
  return arctopath(a, arcnodesnumber(a));
}

/*<asyxml><operator type = "arc" signature="*(transform,explicit arc)"><code></asyxml>*/
arc operator *(transform t, explicit arc a)
{/*<asyxml></code><documentation>Provide transform * arc.</documentation></operator></asyxml>*/
  pair[] P, PP;
  path g = arctopath(a, 3);
  real a0, a1 = a.angle1, a2 = a.angle2, ap1, ap2;
  bool dir = a.direction;
  P[0] = t * point(g, 0);
  P[1] = t * point(g, 2);
  ellipse el = t * a.el;
  arc oa;
  a0 = (a.angle0 + angle(shiftless(t)))%360;
  pair C;
  if(a.polarconicroutine == fromCenter) C = el.C; else C = el.F1;
  real d = abs(locate(el.F2 - el.F1)) > epsgeo ?
    degrees(locate(el.F2 - el.F1)) : a0 + degrees(el.C.coordsys.i);
  ap1 = (degrees(P[0]-C, false) - d)%360;
  ap2 = (degrees(P[1]-C, false) - d)%360;
  oa.init(el, a0, ap1, ap2, a.polarconicroutine, dir);
  g = arctopath(oa, 3);
  PP[0] = point(g, 0);
  PP[1] = point(g, 2);
  if((a1 - a2) * (ap1 - ap2) < 0) {// Handle reflection.
    dir=!a.direction;
    oa.init(el, a0, ap1, ap2, a.polarconicroutine, dir);
  }
  return oa;
}

/*<asyxml><operator type = "arc" signature="*(real,explicit arc)"><code></asyxml>*/
arc operator *(real x, explicit arc a)
{/*<asyxml></code><documentation>Provide real * arc.
   Return the arc subtracting and adding '(x - 1) * degrees(a)/2' to 'a.angle1' and 'a.angle2' respectively.</documentation></operator></asyxml>*/
  real a1, a2, gle;
  gle = (x - 1) * degrees(a)/2;
  a1 = a.angle1 - gle;
  a2 = a.angle2 + gle;
  arc oa;
  oa.init(a.el, a.angle0, a1, a2, a.polarconicroutine, a.direction);
  return oa;
}
arc operator *(int x, explicit arc a){return (real)x * a;}
/*<asyxml><operator type = "arc" signature="/(real,explicit arc)"><code></asyxml>*/
arc operator /(explicit arc a, real x)
{/*<asyxml></code><documentation>Provide arc/real.
   Return the arc subtracting and adding '(1/x - 1) * degrees(a)/2' to 'a.angle1' and 'a.angle2' respectively.</documentation></operator></asyxml>*/
  return (1/x) * a;
}
/*<asyxml><operator type = "arc" signature="+(explicit arc,point)"><code></asyxml>*/
arc operator +(explicit arc a, point M)
{/*<asyxml></code><documentation>Provide arc + point.
   Return shifted arc.
   'operator +(explicit arc, point)', 'operator +(explicit arc, vector)' and 'operator -(explicit arc, vector)' are also defined.</documentation></operator></asyxml>*/
  return shift(M) * a;
}
arc operator -(explicit arc a, point M){return a + (-M);}
arc operator +(explicit arc a, vector v){return shift(locate(v)) * a;}
arc operator -(explicit arc a, vector v){return a + (-v);}


/*<asyxml><operator type = "bool" signature="@(point,arc)"><code></asyxml>*/
bool operator @(point M, arc a)
{/*<asyxml></code><documentation>Return true iff 'M' is on the arc 'a'.</documentation></operator></asyxml>*/
  if (!(M @ a.el)) return false;
  coordsys R = defaultcoordsys;
  path ap = arctopath(a, 3);
  line l = line(point(R, point(ap, 0)), point(R, point(ap, 2)));
  return sameside(M, point(R, point(ap, 1)), l);
}

/*<asyxml><function type="void" signature="draw(picture,Label,arc,align,pen,arrowbar,arrowbar,margin,Label,marker)"><code></asyxml>*/
void draw(picture pic = currentpicture, Label L = "", arc a,
          align align = NoAlign, pen p = currentpen,
          arrowbar arrow = None, arrowbar bar = None, margin margin = NoMargin,
          Label legend = "", marker marker = nomarker)
{/*<asyxml></code><documentation>Draw 'arc' adding the pen returned by 'addpenarc(p)' to the pen 'p'.
   <look href = "#addpenarc"/></documentation></function></asyxml>*/
  draw(pic, L, (path)a, align, addpenarc(p), arrow, bar, margin, legend, marker);
}

/*<asyxml><function type="real" signature="arclength(arc)"><code></asyxml>*/
real arclength(arc a)
{/*<asyxml></code><documentation>The arc length of 'a'.</documentation></function></asyxml>*/
  return arclength(a.el, a.angle1, a.angle2, a.direction, a.polarconicroutine);
}

private point ppoint(arc a, real x)
{// Return the point of the arc proportionally to its length.
  point oP;
  if(a.el.e == 0) { // Case of circle.
    oP = angpoint(a, x * abs(degrees(a)));
  } else { // Ellipse and not circle.
    if(!a.direction) {
      transform t = reflect(line(a.el.F1, a.el.F2));
      return t * ppoint(t * a, x);
    }

    real angle1 = a.angle1, angle2 = a.angle2;
    if(a.polarconicroutine == fromFocus) {
      //       dot(point(fromFocus(a.el, angle1, angle1, 1, CCW), 0), 2mm + blue);
      //       dot(point(fromFocus(a.el, angle2, angle2, 1, CCW), 0), 2mm + blue);
      //       write("fromfocus1 = ", angle1);
      //       write("fromfocus2 = ", angle2);
      real gle1 = focusToCenter(a.el, angle1);
      real gle2 = focusToCenter(a.el, angle2);
      if((gle1 - gle2) * (angle1 - angle2) > 0) {
        angle1 = gle1; angle2 = gle2;
      } else {
        angle1 = gle2; angle2 = gle1;
      }
      //       write("fromcenter1 = ", angle1);
      //       write("fromcenter2 = ", angle2);
      //       dot(point(fromCenter(a.el, angle1, angle1, 1, CCW), 0), 1mm + red);
      //       dot(point(fromCenter(a.el, angle2, angle2, 1, CCW), 0), 1mm + red);
    }

    if(angle1 > angle2) {
      arc ta = a.copy();
      ta.polarconicroutine = fromCenter;
      ta.setangles(a0 = a.angle0, a1 = angle1 - 360, a2 = angle2);
      return ppoint(ta, x);
    }
    ellipse co = a.el;
    real gle, a1, a2, cx = 0;
    bool direction;
    if(x >= 0) {
      a1 = angle1;
      a2 = a1 + 360;
      direction = CCW;
    } else {
      a1 = angle1 - 360;
      a2 = a1 - 360;
      direction = CW;
    }
    gle = a1;
    real L = arclength(co, angle1, angle2, a.direction, fromCenter);
    real tx = L * abs(x)%arclength(co);
    real aout = a1;
    while(abs(cx - tx) > epsgeo) {
      aout = (a1 + a2)/2;
      cx = abs(arclength(co, gle, aout, direction, fromCenter));
      if(cx > tx) a2 = (a1 + a2)/2 ; else a1 = (a1 + a2)/2;
    }
    pair p = point(arcfromcenter(co, aout, aout, CCW, 1), 0);
    oP = point(coordsys(co), p/coordsys(co));
  }
  return oP;
}

/*<asyxml><function type="point" signature="point(arc,abscissa)"><code></asyxml>*/
point point(arc a, abscissa l)
{/*<asyxml></code><documentation>Return the point of 'a' which has the abscissa 'l.x'
   according to the abscissa system 'l.system'.
   Note that 'a.polarconicroutine' is used instead of 'l.polarconicroutine'.
   <look href = "#struct abscissa"/></documentation></function></asyxml>*/
  real posx;
  arc ta = a.copy();
  ellipse co = a.el;
  if (l.system == relativesystem) {
    posx = l.x;
  } else
    if (l.system == curvilinearsystem) {
      real tl;
      if(co.e == 0) {
        tl = curabscissa(a.el, angpoint(a.el, a.angle0 + a.angle1)).x;
        return curpoint(a.el, tl + (a.direction ? l.x : -l.x));
      } else {
        tl = curabscissa(a.el, angpoint(a.el, a.angle1, a.polarconicroutine)).x;
        return curpoint(a.el, tl + (a.direction ? l.x : -l.x));
      }
    } else
      if (l.system == nodesystem) {
        coordsys R = coordsys(co);
        return point(R, point((path)a, l.x)/R);
      } else
        if (l.system == angularsystem) {
          return angpoint(a, l.x);
        } else abort("point: bad abscissa system.");
  return ppoint(ta, posx);
}


/*<asyxml><function type="point" signature="point(arc,real)"><code></asyxml>*/
point point(arc a, real x)
{/*<asyxml></code><documentation>Return the point between node floor(t) and floor(t) + 1.</documentation></function></asyxml>*/
  return point(a, nodabscissa(x));
}
pair point(explicit arc a, int x)
{
  return point(a, nodabscissa(x));
}

/*<asyxml><function type="point" signature="relpoint(arc,real)"><code></asyxml>*/
point relpoint(arc a, real x)
{/*<asyxml></code><documentation>Return the relative point of 'a'.
   If x > 1 or x < 0, the returned point is on the extended arc.</documentation></function></asyxml>*/
  return point(a, relabscissa(x));
}

/*<asyxml><function type="point" signature="curpoint(arc,real)"><code></asyxml>*/
point curpoint(arc a, real x)
{/*<asyxml></code><documentation>Return the point of 'a' which has the curvilinear abscissa 'x'.
   If x < 0 or x > arclength(a), the returned point is on the extended arc.</documentation></function></asyxml>*/
  return point(a, curabscissa(x));
}

/*<asyxml><function type="abscissa" signature="angabscissa(arc,point)"><code></asyxml>*/
abscissa angabscissa(arc a, point M)
{/*<asyxml></code><documentation>Return the angular abscissa of 'M' according to the arc 'a'.</documentation></function></asyxml>*/
  if(!(M @ a.el))
    abort("angabscissa: the point is not on the extended arc.");
  abscissa oa;
  oa.system = angularsystem;
  oa.polarconicroutine = a.polarconicroutine;
  real am = angabscissa(a.el, M, a.polarconicroutine).x;
  oa.x = (am - a.angle1 - (a.el.e == 0 ? a.angle0 : 0))%360;
  oa.x = a.direction ? oa.x : 360 - oa.x;
  return oa;
}

/*<asyxml><function type="abscissa" signature="curabscissa(arc,point)"><code></asyxml>*/
abscissa curabscissa(arc a, point M)
{/*<asyxml></code><documentation>Return the curvilinear abscissa according to the arc 'a'.</documentation></function></asyxml>*/
  ellipse el = a.el;
  if(!(M @ el))
    abort("angabscissa: the point is not on the extended arc.");
  abscissa oa;
  oa.system = curvilinearsystem;
  real xm = curabscissa(el, M).x;
  real a0 = el.e == 0 ? a.angle0 : 0;
  real am = curabscissa(el, angpoint(el, a.angle1 + a0, a.polarconicroutine)).x;
  real l = arclength(el);
  oa.x = (xm - am)%l;
  oa.x = a.direction ? oa.x : l - oa.x;
  return oa;
}

/*<asyxml><function type="abscissa" signature="nodabscissa(arc,point)"><code></asyxml>*/
abscissa nodabscissa(arc a, point M)
{/*<asyxml></code><documentation>Return the node abscissa according to the arc 'a'.</documentation></function></asyxml>*/
  if(!(M @ a))
    abort("nodabscissa: the point is not on the arc.");
  abscissa oa;
  oa.system = nodesystem;
  oa.x = intersect((path)a, M)[0];
  return oa;
}

/*<asyxml><function type="abscissa" signature="relabscissa(arc,point)"><code></asyxml>*/
abscissa relabscissa(arc a, point M)
{/*<asyxml></code><documentation>Return the relative abscissa according to the arc 'a'.</documentation></function></asyxml>*/
  ellipse el = a.el;
  if(!( M @ el))
    abort("relabscissa: the point is not on the prolonged arc.");
  abscissa oa;
  oa.system = relativesystem;
  oa.x = curabscissa(a, M).x/arclength(a);
  return oa;
}

/*<asyxml><function type="void" signature="markarc(picture,Label,int,real,real,arc,arrowbar,pen,pen,margin,marker)"><code></asyxml>*/
void markarc(picture pic = currentpicture,
             Label L = "",
             int n = 1, real radius = 0, real space = 0,
             arc a,
             pen sectorpen = currentpen,
             pen markpen = sectorpen,
             margin margin = NoMargin,
             arrowbar arrow = None,
             marker marker = nomarker)
{/*<asyxml></code><documentation></documentation></function></asyxml>*/
  real Da = degrees(a);
  pair p1 = point(a, 0);
  pair p2 = relpoint(a, 1);
  pair c = a.polarconicroutine == fromCenter ? locate(a.el.C) : locate(a.el.F1);
  if(radius == 0) radius = markangleradius(markpen);
  if(abs(Da) > 180) radius = -radius;
  radius = (a.direction ? 1 : -1) * sgnd(Da) * radius;
  draw(c--p1^^c--p2, sectorpen);
  markangle(pic = pic, L = L, n = n, radius = radius, space = space,
            A = p1, O = c, B = p2,
            arrow = arrow, p = markpen, margin = margin,
            marker = marker);
}
// *.........................ARCS..........................*
// *=======================================================*

// *=======================================================*
// *........................MASSES.........................*
/*<asyxml><struct signature="mass"><code></asyxml>*/
struct mass {/*<asyxml></code><documentation></documentation><property type = "point" signature="M"><code></asyxml>*/
  point M;/*<asyxml></code><documentation></documentation></property><property type = "real" signature="m"><code></asyxml>*/
  real m;/*<asyxml></code><documentation></documentation></property></asyxml>*/
}/*<asyxml></struct></asyxml>*/

/*<asyxml><function type="mass" signature="mass(point,real)"><code></asyxml>*/
mass mass(point M, real m)
{/*<asyxml></code><documentation>Constructor of mass point.</documentation></function></asyxml>*/
  mass om;
  om.M = M;
  om.m = m;
  return om;
}

/*<asyxml><operator type = "point" signature="cast(mass)"><code></asyxml>*/
point operator cast(mass m)
{/*<asyxml></code><documentation>Cast mass point to point.</documentation></operator></asyxml>*/
  point op;
  op = m.M;
  op.m = m.m;
  return op;
}
/*<asyxml><function type="point" signature="point(explicit mass)"><code></asyxml>*/
point point(explicit mass m){return m;}/*<asyxml></code><documentation>Cast
                                         'm' to point</documentation></function></asyxml>*/

/*<asyxml><operator type = "mass" signature="cast(point)"><code></asyxml>*/
mass operator cast(point M)
{/*<asyxml></code><documentation>Cast point to mass point.</documentation></operator></asyxml>*/
  mass om;
  om.M = M;
  om.m = M.m;
  return om;
}
/*<asyxml><function type="mass" signature="mass(explicit point)"><code></asyxml>*/
mass mass(explicit point P)
{/*<asyxml></code><documentation>Cast 'P' to mass.</documentation></function></asyxml>*/
  return mass(P, P.m);
}

/*<asyxml><operator type = "point[]" signature="cast(mass[])"><code></asyxml>*/
point[] operator cast(mass[] m)
{/*<asyxml></code><documentation>Cast mass[] to point[].</documentation></operator></asyxml>*/
  point[] op;
  for(mass am : m) op.push(point(am));
  return op;
}

/*<asyxml><operator type = "mass[]" signature="cast(point[])"><code></asyxml>*/
mass[] operator cast(point[] P)
{/*<asyxml></code><documentation>Cast point[] to mass[].</documentation></operator></asyxml>*/
  mass[] om;
  for(point op : P) om.push(mass(op));
  return om;
}

/*<asyxml><function type="mass" signature="mass(coordsys,explicit pair,real)"><code></asyxml>*/
mass mass(coordsys R, explicit pair p, real m)
{/*<asyxml></code><documentation>Return the mass which has coordinates
   'p' with respect to 'R' and weight 'm'.</documentation></function></asyxml>*/
  return point(R, p, m);// Using casting.
}

/*<asyxml><operator type = "mass" signature="cast(pair)"><code></asyxml>*/
mass operator cast(pair m){return mass((point)m, 1);}/*<asyxml></code><documentation>Cast pair to mass point.</documentation></operator></asyxml>*/
/*<asyxml><operator type = "path" signature="cast(mass)"><code></asyxml>*/
path operator cast(mass M){return M.M;}/*<asyxml></code><documentation>Cast mass point to path.</documentation></operator></asyxml>*/
/*<asyxml><operator type = "guide" signature="cast(mass)"><code></asyxml>*/
guide operator cast(mass M){return M.M;}/*<asyxml></code><documentation>Cast mass to guide.</documentation></operator></asyxml>*/

/*<asyxml><operator type = "mass" signature="+(mass,mass)"><code></asyxml>*/
mass operator +(mass M1, mass M2)
{/*<asyxml></code><documentation>Provide mass + mass.
   mass - mass is also defined.</documentation></operator></asyxml>*/
  return mass(M1.M + M2.M, M1.m + M2.m);
}
mass operator -(mass M1, mass M2)
{
  return mass(M1.M - M2.M, M1.m - M2.m);
}

/*<asyxml><operator type = "mass" signature="*(real,mass)"><code></asyxml>*/
mass operator *(real x, explicit mass M)
{/*<asyxml></code><documentation>Provide real * mass.
   The resulted mass is the mass of 'M' multiplied by 'x' .
   mass/real, mass + real and mass - real are also defined.</documentation></operator></asyxml>*/
  return mass(M.M, x * M.m);
}
mass operator *(int x, explicit mass M){return mass(M.M, x * M.m);}
mass operator /(explicit mass M, real x){return mass(M.M, M.m/x);}
mass operator /(explicit mass M, int x){return mass(M.M, M.m/x);}
mass operator +(explicit mass M, real x){return mass(M.M, M.m + x);}
mass operator +(explicit mass M, int x){return mass(M.M, M.m + x);}
mass operator -(explicit mass M, real x){return mass(M.M, M.m - x);}
mass operator -(explicit mass M, int x){return mass(M.M, M.m - x);}
/*<asyxml><operator type = "mass" signature="*(transform,mass)"><code></asyxml>*/
mass operator *(transform t, mass M)
{/*<asyxml></code><documentation>Provide transform * mass.</documentation></operator></asyxml>*/
  return mass(t * M.M, M.m);
}

/*<asyxml><function type="mass" signature="masscenter(... mass[])"><code></asyxml>*/
mass masscenter(... mass[] M)
{/*<asyxml></code><documentation>Return the center of the masses 'M'.</documentation></function></asyxml>*/
  point[] P;
  for (int i = 0; i < M.length; ++i)
    P.push(M[i].M);
  P = standardizecoordsys(currentcoordsys, true ... P);
  real m = M[0].m;
  point oM = M[0].m * P[0];
  for (int i = 1; i < M.length; ++i) {
    oM += M[i].m * P[i];
    m += M[i].m;
  }
  if (m == 0) abort("masscenter: the sum of masses is null.");
  return mass(oM/m, m);
}

/*<asyxml><function type="string" signature="massformat(string,string,mass)"><code></asyxml>*/
string massformat(string format = defaultmassformat,
                  string s, mass M)
{/*<asyxml></code><documentation>Return the string formated by 'format' with the mass value.
   In the parameter 'format', %L will be replaced by 's'.
   <look href = "#defaultmassformat"/>.</documentation></function></asyxml>*/
  return format == "" ? s :
    format(replace(format, "%L", replace(s, "$", "")), M.m);
}

/*<asyxml><function type="void" signature="label(picture,Label,explicit mass,align,string,pen,filltype)"><code></asyxml>*/
void label(picture pic = currentpicture, Label L, explicit mass M,
           align align = NoAlign, string format = defaultmassformat,
           pen p = nullpen, filltype filltype = NoFill)
{/*<asyxml></code><documentation>Draw label returned by massformat(format, L, M) at coordinates of M.
   <look href = "#massformat(string, string, mass)"/>.</documentation></function></asyxml>*/
  Label lL = L.copy();
  lL.s = massformat(format, lL.s, M);
  Label L = Label(lL, M.M, align, p, filltype);
  add(pic, L);
}

/*<asyxml><function type="void" signature="dot(picture,Label,explicit mass,align,string,pen)"><code></asyxml>*/
void dot(picture pic = currentpicture, Label L, explicit mass M, align align = NoAlign,
         string format = defaultmassformat, pen p = currentpen)
{/*<asyxml></code><documentation>Draw a dot with label 'L' as
   label(picture, Label, explicit mass, align, string, pen, filltype) does.
   <look href = "#label(picture, Label, mass, align, string, pen, filltype)"/>.</documentation></function></asyxml>*/
  Label lL = L.copy();
  lL.s = massformat(format, lL.s, M);
  lL.position(locate(M.M));
  lL.align(align, E);
  lL.p(p);
  dot(pic, M.M, p);
  add(pic, lL);
}
// *........................MASSES.........................*
// *=======================================================*

// *=======================================================*
// *.......................TRIANGLES.......................*
/*<asyxml><function type="point" signature="orthocentercenter(point,point,point)"><code></asyxml>*/
point orthocentercenter(point A, point B, point C)
{/*<asyxml></code><documentation>Return the orthocenter of the triangle ABC.</documentation></function></asyxml>*/
  point[] P = standardizecoordsys(A, B, C);
  coordsys R = P[0].coordsys;
  pair pp = extension(A, projection(P[1], P[2]) * P[0], B, projection(P[0], P[2]) * P[1]);
  return point(R, pp/R);
}

/*<asyxml><function type="point" signature="centroid(point,point,point)"><code></asyxml>*/
point centroid(point A, point B, point C)
{/*<asyxml></code><documentation>Return the centroid of the triangle ABC.</documentation></function></asyxml>*/
  return (A + B + C)/3;
}

/*<asyxml><function type="point" signature="incenter(point,point,point)"><code></asyxml>*/
point incenter(point A, point B, point C)
{/*<asyxml></code><documentation>Return the center of the incircle of the triangle ABC.</documentation></function></asyxml>*/
  point[] P = standardizecoordsys(A, B, C);
  coordsys R = P[0].coordsys;
  pair a = A, b = B, c = C;
  pair pp = extension(a, a + dir(a--b, a--c), b, b + dir(b--a, b--c));
  return point(R, pp/R);
}

/*<asyxml><function type="real" signature="inradius(point,point,point)"><code></asyxml>*/
real inradius(point A, point B, point C)
{/*<asyxml></code><documentation>Return the radius of the incircle of the triangle ABC.</documentation></function></asyxml>*/
  point IC = incenter(A, B, C);
  return abs(IC - projection(A, B) * IC);
}

/*<asyxml><function type="circle" signature="incircle(point,point,point)"><code></asyxml>*/
circle incircle(point A, point B, point C)
{/*<asyxml></code><documentation>Return the incircle of the triangle ABC.</documentation></function></asyxml>*/
  point IC = incenter(A, B, C);
  return circle(IC, abs(IC - projection(A, B) * IC));
}

/*<asyxml><function type="point" signature="excenter(point,point,point)"><code></asyxml>*/
point excenter(point A, point B, point C)
{/*<asyxml></code><documentation>Return the center of the excircle of the triangle tangent with (AB).</documentation></function></asyxml>*/
  point[] P = standardizecoordsys(A, B, C);
  coordsys R = P[0].coordsys;
  pair a = A, b = B, c = C;
  pair pp = extension(a, a + rotate(90) * dir(a--b, a--c), b, b + rotate(90) * dir(b--a, b--c));
  return point(R, pp/R);
}

/*<asyxml><function type="real" signature="exradius(point,point,point)"><code></asyxml>*/
real exradius(point A, point B, point C)
{/*<asyxml></code><documentation>Return the radius of the excircle of the triangle ABC with (AB).</documentation></function></asyxml>*/
  point EC = excenter(A, B, C);
  return abs(EC - projection(A, B) * EC);
}

/*<asyxml><function type="circle" signature="excircle(point,point,point)"><code></asyxml>*/
circle excircle(point A, point B, point C)
{/*<asyxml></code><documentation>Return the excircle of the triangle ABC tangent with (AB).</documentation></function></asyxml>*/
  point center = excenter(A, B, C);
  real radius = abs(center - projection(B, C) * center);
  return circle(center, radius);
}

private int[] numarray = {1, 2, 3};
numarray.cyclic = true;

/*<asyxml><struct signature="triangle"><code></asyxml>*/
struct triangle {/*<asyxml></code><documentation></documentation></asyxml>*/

  /*<asyxml><struct signature="vertex"><code></asyxml>*/
  struct vertex {/*<asyxml></code><documentation>Structure used to communicate the vertex of a triangle.</documentation><property type = "int" signature="n"><code></asyxml>*/
    int n;/*<asyxml></code><documentation>1 means VA,2 means VB,3 means VC,4 means VA etc...</documentation></property><property type = "triangle" signature="triangle"><code></asyxml>*/
    triangle t;/*<asyxml></code><documentation>The triangle to which the vertex refers.</documentation></property></asyxml>*/
  }/*<asyxml></struct></asyxml>*/

  /*<asyxml><property type = "point" signature="A,B,C"><code></asyxml>*/
  restricted point A, B, C;/*<asyxml></code><documentation>The vertices of the triangle (as point).</documentation></property><property type = "vertex" signature="VA, VB, VC"><code></asyxml>*/
  restricted vertex VA, VB, VC;/*<asyxml></code><documentation>The vertices of the triangle (as vertex).
                                 Note that the vertex structure contains the triangle to wish it refers.</documentation></property></asyxml>*/
  VA.n = 1;VB.n = 2;VC.n = 3;

  /*<asyxml><method type = "vertex" signature="vertex(int)"><code></asyxml>*/
  vertex vertex(int n)
  {/*<asyxml></code><documentation>Return numbered vertex.
     'n' is 1 means VA, 2 means VB, 3 means VC, 4 means VA etc...</documentation></method></asyxml>*/
    n = numarray[n - 1];
    if(n == 1) return VA;
    else if(n == 2) return VB;
    return VC;
  }

  /*<asyxml><method type = "point" signature="point(int)"><code></asyxml>*/
  point point(int n)
  {/*<asyxml></code><documentation>Return numbered point.
     n is 1 means A, 2 means B, 3 means C, 4 means A etc...</documentation></method></asyxml>*/
    n = numarray[n - 1];
    if(n == 1) return A;
    else if(n == 2) return B;
    return C;
  }

  /*<asyxml><method type = "void" signature="init(point,point,point)"><code></asyxml>*/
  void init(point A, point B, point C)
  {/*<asyxml></code><documentation>Constructor.</documentation></method></asyxml>*/
    point[] P = standardizecoordsys(A, B, C);
    this.A = P[0];
    this.B = P[1];
    this.C = P[2];
    VA.t = this; VB.t = this; VC.t = this;
  }

  /*<asyxml><method type = "void" signature="operator init(point,point,point)"><code></asyxml>*/
  void operator init(point A, point B, point C)
  {/*<asyxml></code><documentation>For backward compatibility.
     Provide the routine 'triangle(point A, point B, point C)'.</documentation></method></asyxml>*/
    this.init(A, B, C);
  }

  /*<asyxml><method type = "void" signature="init(real,real,real,real,point)"><code></asyxml>*/
  void operator init(real b, real alpha, real c, real angle = 0, point A = (0, 0))
  {/*<asyxml></code><documentation>For backward compatibility.
     Provide the routine 'triangle(real b, real alpha, real c, real angle = 0, point A = (0, 0))
     which returns the triangle ABC rotated by 'angle' (in degrees) and where b = AC, degrees(A) = alpha, AB = c.</documentation></method></asyxml>*/
    coordsys R = A.coordsys;
    this.init(A, A + R.polar(c, radians(angle)), A + R.polar(b, radians(angle + alpha)));
  }

  /*<asyxml><method type = "real" signature="a(),b(),c()"><code></asyxml>*/
  real a()
  {/*<asyxml></code><documentation>Return the length BC.
     b() and c() are also defined and return the length AC and AB respectively.</documentation></method></asyxml>*/
    return length(C - B);
  }
  real b() {return length(A - C);}
  real c() {return length(B - A);}

  private real det(pair a, pair b) {return a.x * b.y - a.y * b.x;}

  /*<asyxml><method type = "real" signature="area()"><code></asyxml>*/
  real area()
  {/*<asyxml></code><documentation></documentation></method></asyxml>*/
    pair a = locate(A), b = locate(B), c = locate(C);
    return 0.5 * abs(det(a, b) + det(b, c) + det(c, a));
  }

  /*<asyxml><method type = "real" signature="alpha(),beta(),gamma()"><code></asyxml>*/
  real alpha()
  {/*<asyxml></code><documentation>Return the measure (in degrees) of the angle A.
     beta() and gamma() are also defined and return the measure of the angles B and C respectively.</documentation></method></asyxml>*/
    return degrees(acos((b()^2 + c()^2 - a()^2)/(2b() * c())));
  }
  real beta()  {return degrees(acos((c()^2 + a()^2 - b()^2)/(2c() * a())));}
  real gamma() {return degrees(acos((a()^2 + b()^2 - c()^2)/(2a() * b())));}

  /*<asyxml><method type = "path" signature="Path()"><code></asyxml>*/
  path Path()
  {/*<asyxml></code><documentation>The path of the triangle.</documentation></method></asyxml>*/
    return A--C--B--cycle;
  }

  /*<asyxml><struct signature="side"><code></asyxml>*/
  struct side
  {/*<asyxml></code><documentation>Structure used to communicate the side of a triangle.</documentation><property type = "int" signature="n"><code></asyxml>*/
    int n;/*<asyxml></code><documentation>1 or 0 means [AB],-1 means [BA],2 means [BC],-2 means [CB] etc.</documentation></property><property type = "triangle" signature="triangle"><code></asyxml>*/
    triangle t;/*<asyxml></code><documentation>The triangle to which the side refers.</documentation></property></asyxml>*/
  }/*<asyxml></struct></asyxml>*/

  /*<asyxml><property type = "side" signature="AB"><code></asyxml>*/
  side AB;/*<asyxml></code><documentation>For the routines using the structure 'side', triangle.AB means 'side AB'.
            BA, AC, CA etc are also defined.</documentation></property></asyxml>*/
  AB.n = 1; AB.t = this;
  side BA; BA.n = -1; BA.t = this;
  side BC; BC.n = 2; BC.t = this;
  side CB; CB.n = -2; CB.t = this;
  side CA; CA.n = 3; CA.t = this;
  side AC; AC.n = -3; AC.t = this;

  /*<asyxml><method type = "side" signature="side(int)"><code></asyxml>*/
  side side(int n)
  {/*<asyxml></code><documentation>Return numbered side.
     n is 1 means AB, -1 means BA, 2 means BC, -2 means CB, etc.</documentation></method></asyxml>*/
    if(n == 0) abort('Invalid side number.');
    int an = numarray[abs(n)-1];
    if(an == 1) return n > 0 ? AB : BA;
    else if(an == 2) return n > 0 ? BC : CB;
    return n > 0 ? CA : AC;
  }

  /*<asyxml><method type = "line" signature="line(int)"><code></asyxml>*/
  line line(int n)
  {/*<asyxml></code><documentation>Return the numbered line.</documentation></method></asyxml>*/
    if(n == 0) abort('Invalid line number.');
    int an = numarray[abs(n)-1];
    if(an == 1) return n > 0 ? line(A, B) : line(B, A);
    else if(an == 2) return n > 0 ? line(B, C) : line(C, B);
    return n > 0 ? line(C, A) : line(A, C);
  }

}/*<asyxml></struct></asyxml>*/

from triangle unravel side; // The structure 'side' is now available outside the triangle structure.
from triangle unravel vertex; // The structure 'vertex' is now available outside the triangle structure.

triangle[] operator ^^(triangle[] t1, triangle t2)
{
  triangle[] T;
  for (int i = 0; i < t1.length; ++i) T.push(t1[i]);
  T.push(t2);
  return T;
}

triangle[] operator ^^(... triangle[] t)
{
  triangle[] T;
  for (int i = 0; i < t.length; ++i) {
    T.push(t[i]);
  }
  return T;
}

/*<asyxml><operator type = "line" signature="cast(side)"><code></asyxml>*/
line operator cast(side side)
{/*<asyxml></code><documentation>Cast side to (infinite) line.
   Most routine with line parameters works with side parameters.
   One can use the code 'segment(a_side)' to obtain a line segment.</documentation></operator></asyxml>*/
  triangle t = side.t;
  return t.line(side.n);
}

/*<asyxml><function type="line" signature="line(explicit side)"><code></asyxml>*/
line line(explicit side side)
{/*<asyxml></code><documentation>Return 'side' as line.</documentation></function></asyxml>*/
  return (line)side;
}

/*<asyxml><function type="segment" signature="segment(explicit side)"><code></asyxml>*/
segment segment(explicit side side)
{/*<asyxml></code><documentation>Return 'side' as segment.</documentation></function></asyxml>*/
  return (segment)(line)side;
}

/*<asyxml><operator type = "point" signature="cast(vertex)"><code></asyxml>*/
point operator cast(vertex V)
{/*<asyxml></code><documentation>Cast vertex to point.
   Most routine with point parameters works with vertex parameters.</documentation></operator></asyxml>*/
  return V.t.point(V.n);
}

/*<asyxml><function type="point" signature="point(explicit vertex)"><code></asyxml>*/
point point(explicit vertex V)
{/*<asyxml></code><documentation>Return the point corresponding to the vertex 'V'.</documentation></function></asyxml>*/
  return (point)V;
}

/*<asyxml><function type="side" signature="opposite(vertex)"><code></asyxml>*/
side opposite(vertex V)
{/*<asyxml></code><documentation>Return the opposite side of vertex 'V'.</documentation></function></asyxml>*/
  return V.t.side(numarray[abs(V.n)]);
}

/*<asyxml><function type="vertex" signature="opposite(side)"><code></asyxml>*/
vertex opposite(side side)
{/*<asyxml></code><documentation>Return the opposite vertex of side 'side'.</documentation></function></asyxml>*/
  return side.t.vertex(numarray[abs(side.n) + 1]);
}

/*<asyxml><function type="point" signature="midpoint(side)"><code></asyxml>*/
point midpoint(side side)
{/*<asyxml></code><documentation></documentation></function></asyxml>*/
  return midpoint(segment(side));
}

/*<asyxml><operator type = "triangle" signature="*(transform,triangle)"><code></asyxml>*/
triangle operator *(transform T, triangle t)
{/*<asyxml></code><documentation>Provide transform * triangle.</documentation></operator></asyxml>*/
  return triangle(T * t.A, T * t.B, T * t.C);
}

/*<asyxml><function type="triangle" signature="triangleAbc(real,real,real,real,point)"><code></asyxml>*/
triangle triangleAbc(real alpha, real b, real c, real angle = 0, point A = (0, 0))
{/*<asyxml></code><documentation>Return the triangle ABC rotated by 'angle' with BAC = alpha, AC = b and AB = c.</documentation></function></asyxml>*/
  triangle T;
  coordsys R = A.coordsys;
  T.init(A, A + R.polar(c, radians(angle)), A + R.polar(b, radians(angle + alpha)));
  return T;
}

/*<asyxml><function type="triangle" signature="triangleabc(real,real,real,real,point)"><code></asyxml>*/
triangle triangleabc(real a, real b, real c, real angle = 0, point A = (0, 0))
{/*<asyxml></code><documentation>Return the triangle ABC rotated by 'angle' with BC = a, AC = b and AB = c.</documentation></function></asyxml>*/
  triangle T;
  coordsys R = A.coordsys;
  T.init(A, A + R.polar(c, radians(angle)), A + R.polar(b, radians(angle) + acos((b^2 + c^2 - a^2)/(2 * b * c))));
  return T;
}

/*<asyxml><function type="triangle" signature="triangle(line,line,line)"><code></asyxml>*/
triangle triangle(line l1, line l2, line l3)
{/*<asyxml></code><documentation>Return the triangle defined by three line.</documentation></function></asyxml>*/
  point P1, P2, P3;
  P1 = intersectionpoint(l1, l2);
  P2 = intersectionpoint(l1, l3);
  P3 = intersectionpoint(l2, l3);
  if(!(defined(P1) && defined(P2) && defined(P3))) abort("triangle: two lines are parallel.");
  return triangle(P1, P2, P3);
}

/*<asyxml><function type="point" signature="foot(vertex)"><code></asyxml>*/
point foot(vertex V)
{/*<asyxml></code><documentation>Return the endpoint of the altitude from V.</documentation></function></asyxml>*/
  return projection((line)opposite(V)) * ((point)V);
}

/*<asyxml><function type="point" signature="foot(side)"><code></asyxml>*/
point foot(side side)
{/*<asyxml></code><documentation>Return the endpoint of the altitude on 'side'.</documentation></function></asyxml>*/
  return projection((line)side) * point(opposite(side));
}

/*<asyxml><function type="line" signature="altitude(vertex)"><code></asyxml>*/
line altitude(vertex V)
{/*<asyxml></code><documentation>Return the altitude passing through 'V'.</documentation></function></asyxml>*/
  return line(point(V), foot(V));
}

/*<asyxml><function type="line" signature="altitude(vertex)"><code></asyxml>*/
line altitude(side side)
{/*<asyxml></code><documentation>Return the altitude cutting 'side'.</documentation></function></asyxml>*/
  return altitude(opposite(side));
}

/*<asyxml><function type="point" signature="orthocentercenter(triangle)"><code></asyxml>*/
point orthocentercenter(triangle t)
{/*<asyxml></code><documentation>Return the orthocenter of the triangle t.</documentation></function></asyxml>*/
  return orthocentercenter(t.A, t.B, t.C);
}

/*<asyxml><function type="point" signature="centroid(triangle)"><code></asyxml>*/
point centroid(triangle t)
{/*<asyxml></code><documentation>Return the centroid of the triangle 't'.</documentation></function></asyxml>*/
  return (t.A + t.B + t.C)/3;
}

/*<asyxml><function type="point" signature="circumcenter(triangle)"><code></asyxml>*/
point circumcenter(triangle t)
{/*<asyxml></code><documentation>Return the circumcenter of the triangle 't'.</documentation></function></asyxml>*/
  return circumcenter(t.A, t.B, t.C);
}

/*<asyxml><function type="circle" signature="circle(triangle)"><code></asyxml>*/
circle circle(triangle t)
{/*<asyxml></code><documentation>Return the circumcircle of the triangle 't'.</documentation></function></asyxml>*/
  return circle(t.A, t.B, t.C);
}

/*<asyxml><function type="circle" signature="circumcircle(triangle)"><code></asyxml>*/
circle circumcircle(triangle t)
{/*<asyxml></code><documentation>Return the circumcircle of the triangle 't'.</documentation></function></asyxml>*/
  return circle(t.A, t.B, t.C);
}

/*<asyxml><function type="point" signature="incenter(triangle)"><code></asyxml>*/
point incenter(triangle t)
{/*<asyxml></code><documentation>Return the center of the incircle of the triangle 't'.</documentation></function></asyxml>*/
  return incenter(t.A, t.B, t.C);
}

/*<asyxml><function type="real" signature="inradius(triangle)"><code></asyxml>*/
real inradius(triangle t)
{/*<asyxml></code><documentation>Return the radius of the incircle of the triangle 't'.</documentation></function></asyxml>*/
  return inradius(t.A, t.B, t.C);
}

/*<asyxml><function type="circle" signature="incircle(triangle)"><code></asyxml>*/
circle incircle(triangle t)
{/*<asyxml></code><documentation>Return the the incircle of the triangle 't'.</documentation></function></asyxml>*/
  return incircle(t.A, t.B, t.C);
}

/*<asyxml><function type="point" signature="excenter(side,triangle)"><code></asyxml>*/
point excenter(side side)
{/*<asyxml></code><documentation>Return the center of the excircle tangent with the side 'side' of its triangle.
   side = 0 means AB, 1 means AC, other means BC.
   One must use the predefined sides t.AB, t.AC where 't' is a triangle....</documentation></function></asyxml>*/
  point op;
  triangle t = side.t;
  int n = numarray[abs(side.n) - 1];
  if(n == 1) op = excenter(t.A, t.B, t.C);
  else  if(n == 2) op = excenter(t.B, t.C, t.A);
  else op = excenter(t.C, t.A, t.B);
  return op;
}

/*<asyxml><function type="real" signature="exradius(side,triangle)"><code></asyxml>*/
real exradius(side side)
{/*<asyxml></code><documentation>Return radius of the excircle tangent with the side 'side' of its triangle.
   side = 0 means AB, 1 means BC, other means CA.
   One must use the predefined sides t.AB, t.AC where 't' is a triangle....</documentation></function></asyxml>*/
  real or;
  triangle t = side.t;
  int n = numarray[abs(side.n) - 1];
  if(n == 1) or = exradius(t.A, t.B, t.C);
  else  if(n == 2) or = exradius(t.B, t.C, t.A);
  else or = exradius(t.A, t.C, t.B);
  return or;
}

/*<asyxml><function type="circle" signature="excircle(side,triangle)"><code></asyxml>*/
circle excircle(side side)
{/*<asyxml></code><documentation>Return the excircle tangent with the side 'side' of its triangle.
   side = 0 means AB, 1 means AC, other means BC.
   One must use the predefined sides t.AB, t.AC where 't' is a triangle....</documentation></function></asyxml>*/
  circle oc;
  int n = numarray[abs(side.n) - 1];
  triangle t = side.t;
  if(n == 1) oc = excircle(t.A, t.B, t.C);
  else  if(n == 2) oc = excircle(t.B, t.C, t.A);
  else oc = excircle(t.A, t.C, t.B);
  return oc;
}

/*<asyxml><struct signature="trilinear"><code></asyxml>*/
struct trilinear
{/*<asyxml></code><documentation>Trilinear coordinates 'a:b:c' relative to triangle 't'.
   <url href = "http://mathworld.wolfram.com/TrilinearCoordinates.html"/></documentation><property type = "real" signature="a,b,c"><code></asyxml>*/
  real a,b,c;/*<asyxml></code><documentation>The trilinear coordinates.</documentation></property><property type = "triangle" signature="t"><code></asyxml>*/
  triangle t;/*<asyxml></code><documentation>The reference triangle.</documentation></property></asyxml>*/
}/*<asyxml></struct></asyxml>*/

/*<asyxml><function type="trilinear" signature="trilinear(triangle,real,real,real)"><code></asyxml>*/
trilinear trilinear(triangle t, real a, real b, real c)
{/*<asyxml></code><documentation>Return the trilinear coordinates relative to 't'.
   <url href = "http://mathworld.wolfram.com/TrilinearCoordinates.html"/></documentation></function></asyxml>*/
  trilinear ot;
  ot.a = a; ot.b = b; ot.c = c;
  ot.t = t;
  return ot;
}

/*<asyxml><function type="trilinear" signature="trilinear(triangle,point)"><code></asyxml>*/
trilinear trilinear(triangle t, point M)
{/*<asyxml></code><documentation>Return the trilinear coordinates of 'M' relative to 't'.
   <url href = "http://mathworld.wolfram.com/TrilinearCoordinates.html"/></documentation></function></asyxml>*/
  trilinear ot;
  pair m = locate(M);
  int sameside(pair A, pair B, pair m, pair p)
  {// Return 1 if 'm' and 'p' are same side of line (AB) else return -1.
    pair mil = (A + B)/2;
    pair mA = rotate(90, mil) * A;
    pair mB = rotate(-90, mil) * A;
    return (abs(m - mA) <= abs(m - mB)) == (abs(p - mA) <= abs(p - mB)) ? 1 : -1;
  }
  real det(pair a, pair b) {return a.x * b.y - a.y * b.x;}
  real area(pair a, pair b, pair c){return 0.5 * abs(det(a, b) + det(b, c) + det(c, a));}
  pair A = t.A, B = t.B, C = t.C;
  real t1 = area(B, C, m), t2 = area(C, A, m), t3 = area(A, B, m);
  ot.a = sameside(B, C, A, m) * t1/t.a();
  ot.b = sameside(A, C, B, m) * t2/t.b();
  ot.c = sameside(A, B, C, m) * t3/t.c();
  ot.t = t;
  return ot;
}

/*<asyxml><function type="void" signature="write(trilinear)"><code></asyxml>*/
void write(trilinear tri)
{/*<asyxml></code><documentation></documentation></function></asyxml>*/
  write(format("%f : ", tri.a) + format("%f : ", tri.b) + format("%f", tri.c));
}

/*<asyxml><function type="point" signature="trilinear(triangle,real,real,real)"><code></asyxml>*/
point point(trilinear tri)
{/*<asyxml></code><documentation>Return the trilinear coordinates relative to 't'.
   <url href = "http://mathworld.wolfram.com/TrilinearCoordinates.html"/></documentation></function></asyxml>*/
  triangle t = tri.t;
  return masscenter(0.5 * t.a() * mass(t.A, tri.a),
                    0.5 * t.b() * mass(t.B, tri.b),
                    0.5 * t.c() * mass(t.C, tri.c));
}

/*<asyxml><function type="int[]" signature="tricoef(side)"><code></asyxml>*/
int[] tricoef(side side)
{/*<asyxml></code><documentation>Return an array of integer (values are 0 or 1) which represents 'side'.
   For example, side = t.BC will be represented by {0, 1, 1}.</documentation></function></asyxml>*/
  int[] oi;
  int n = numarray[abs(side.n) - 1];
  oi.push((n == 1 || n == 3) ? 1 : 0);
  oi.push((n == 1 || n == 2) ? 1 : 0);
  oi.push((n == 2 || n == 3) ? 1 : 0);
  return oi;
}

/*<asyxml><operator type = "point" signature="cast(trilinear)"><code></asyxml>*/
point operator cast(trilinear tri)
{/*<asyxml></code><documentation>Cast trilinear to point.
   One may use the routine 'point(trilinear)' to force the casting.</documentation></operator></asyxml>*/
  return point(tri);
}

/*<asyxml><typedef type = "centerfunction" return = "real" params = "real, real, real"><code></asyxml>*/
typedef real centerfunction(real, real, real);/*<asyxml></code><documentation><url href = "http://mathworld.wolfram.com/TriangleCenterFunction.html"/></documentation></typedef></asyxml>*/

/*<asyxml><function type="trilinear" signature="trilinear(triangle,centerfunction,real,real,real)"><code></asyxml>*/
trilinear trilinear(triangle t, centerfunction f, real a = t.a(), real b = t.b(), real c = t.c())
{/*<asyxml></code><documentation><url href = "http://mathworld.wolfram.com/TriangleCenterFunction.html"/></documentation></function></asyxml>*/
  return trilinear(t, f(a, b, c), f(b, c, a), f(c, a, b));
}

/*<asyxml><function type="point" signature="symmedian(triangle)"><code></asyxml>*/
point symmedian(triangle t)
{/*<asyxml></code><documentation>Return the symmedian point of 't'.</documentation></function></asyxml>*/
  point A, B, C;
  real a = t.a(), b = t.b(), c = t.c();
  A = trilinear(t, 0, b, c);
  B = trilinear(t, a, 0, c);
  return intersectionpoint(line(t.A, A), line(t.B, B));
}

/*<asyxml><function type="point" signature="symmedian(side)"><code></asyxml>*/
point symmedian(side side)
{/*<asyxml></code><documentation>The symmedian point on the side 'side'.</documentation></function></asyxml>*/
  triangle t = side.t;
  int n = numarray[abs(side.n) - 1];
  if(n == 1) return trilinear(t, t.a(), t.b(), 0);
  if(n == 2) return trilinear(t, 0, t.b(), t.c());
  return trilinear(t, t.a(), 0, t.c());
}

/*<asyxml><function type="line" signature="symmedian(vertex)"><code></asyxml>*/
line symmedian(vertex V)
{/*<asyxml></code><documentation>Return the symmedian passing through 'V'.</documentation></function></asyxml>*/
  return line(point(V), symmedian(V.t));
}

/*<asyxml><function type="triangle" signature="cevian(triangle,point)"><code></asyxml>*/
triangle cevian(triangle t, point P)
{/*<asyxml></code><documentation>Return the Cevian triangle with respect of 'P'
   <url href = "http://mathworld.wolfram.com/CevianTriangle.html"/>.</documentation></function></asyxml>*/
  trilinear tri = trilinear(t, locate(P));
  point A = point(trilinear(t, 0, tri.b, tri.c));
  point B = point(trilinear(t, tri.a, 0, tri.c));
  point C = point(trilinear(t, tri.a, tri.b, 0));
  return triangle(A, B, C);
}

/*<asyxml><function type="point" signature="cevian(side,point)"><code></asyxml>*/
point cevian(side side, point P)
{/*<asyxml></code><documentation>Return the Cevian point on 'side' with respect of 'P'.</documentation></function></asyxml>*/
  triangle t = side.t;
  trilinear tri = trilinear(t, locate(P));
  int[] s = tricoef(side);
  return point(trilinear(t, s[0] * tri.a, s[1] * tri.b, s[2] * tri.c));
}

/*<asyxml><function type="line" signature="cevian(vertex,point)"><code></asyxml>*/
line cevian(vertex V, point P)
{/*<asyxml></code><documentation>Return line passing through 'V' and its Cevian image with respect of 'P'.</documentation></function></asyxml>*/
  return line(point(V), cevian(opposite(V), P));
}

/*<asyxml><function type="point" signature="gergonne(triangle)"><code></asyxml>*/
point gergonne(triangle t)
{/*<asyxml></code><documentation>Return the Gergonne point of 't'.</documentation></function></asyxml>*/
  real f(real a, real b, real c){return 1/(a * (b + c - a));}
  return point(trilinear(t, f));
}

/*<asyxml><function type="point[]" signature="fermat(triangle)"><code></asyxml>*/
point[] fermat(triangle t)
{/*<asyxml></code><documentation>Return the Fermat points of 't'.</documentation></function></asyxml>*/
  point[] P;
  real A = t.alpha(), B = t.beta(), C = t.gamma();
  P.push(point(trilinear(t, 1/Sin(A + 60), 1/Sin(B + 60), 1/Sin(C + 60))));
  P.push(point(trilinear(t, 1/Sin(A - 60), 1/Sin(B - 60), 1/Sin(C - 60))));
  return P;
}

/*<asyxml><function type="point" signature="isotomicconjugate(triangle,point)"><code></asyxml>*/
point isotomicconjugate(triangle t, point M)
{/*<asyxml></code><documentation><url href = "http://mathworld.wolfram.com/IsotomicConjugate.html"/></documentation></function></asyxml>*/
  if(!inside(t.Path(), locate(M))) abort("isotomic: the point must be inside the triangle.");
  trilinear tr = trilinear(t, M);
  return point(trilinear(t, 1/(t.a()^2 * tr.a), 1/(t.b()^2 * tr.b), 1/(t.c()^2 * tr.c)));
}

/*<asyxml><function type="line" signature="isotomic(vertex,point)"><code></asyxml>*/
line isotomic(vertex V, point M)
{/*<asyxml></code><documentation><url href = "http://mathworld.wolfram.com/IsotomicConjugate.html"/>.</documentation></function></asyxml>*/
  side op = opposite(V);
  return line(V, rotate(180, midpoint(op)) * cevian(op, M));
}

/*<asyxml><function type="point" signature="isotomic(side,point)"><code></asyxml>*/
point isotomic(side side, point M)
{/*<asyxml></code><documentation><url href = "http://mathworld.wolfram.com/IsotomicConjugate.html"/></documentation></function></asyxml>*/
  return intersectionpoint(isotomic(opposite(side), M), side);
}

/*<asyxml><function type="triangle" signature="isotomic(triangle,point)"><code></asyxml>*/
triangle isotomic(triangle t, point M)
{/*<asyxml></code><documentation><url href = "http://mathworld.wolfram.com/IsotomicConjugate.html"/></documentation></function></asyxml>*/
  return triangle(isotomic(t.BC, M), isotomic(t.CA, M), isotomic(t.AB, M));
}

/*<asyxml><function type="point" signature="isogonalconjugate(triangle,point)"><code></asyxml>*/
point isogonalconjugate(triangle t, point M)
{/*<asyxml></code><documentation><url href = "http://mathworld.wolfram.com/IsogonalConjugate.html"/></documentation></function></asyxml>*/
  trilinear tr = trilinear(t, M);
  return point(trilinear(t, 1/tr.a, 1/tr.b, 1/tr.c));
}

/*<asyxml><function type="point" signature="isogonal(side,point)"><code></asyxml>*/
point isogonal(side side, point M)
{/*<asyxml></code><documentation><url href = "http://mathworld.wolfram.com/IsogonalConjugate.html"/></documentation></function></asyxml>*/
  return cevian(side, isogonalconjugate(side.t, M));
}

/*<asyxml><function type="line" signature="isogonal(vertex,point)"><code></asyxml>*/
line isogonal(vertex V, point M)
{/*<asyxml></code><documentation><url href = "http://mathworld.wolfram.com/IsogonalConjugate.html"/></documentation></function></asyxml>*/
  return line(V, isogonal(opposite(V), M));
}

/*<asyxml><function type="triangle" signature="isogonal(triangle,point)"><code></asyxml>*/
triangle isogonal(triangle t, point M)
{/*<asyxml></code><documentation><url href = "http://mathworld.wolfram.com/IsogonalConjugate.html"/></documentation></function></asyxml>*/
  return triangle(isogonal(t.BC, M), isogonal(t.CA, M), isogonal(t.AB, M));
}

/*<asyxml><function type="triangle" signature="pedal(triangle,point)"><code></asyxml>*/
triangle pedal(triangle t, point M)
{/*<asyxml></code><documentation>Return the pedal triangle of 'M' in 't'.
   <url href = "http://mathworld.wolfram.com/PedalTriangle.html"/></documentation></function></asyxml>*/
  return triangle(projection(t.BC) * M, projection(t.AC) * M, projection(t.AB) * M);
}

/*<asyxml><function type="triangle" signature="pedal(triangle,point)"><code></asyxml>*/
line pedal(side side, point M)
{/*<asyxml></code><documentation>Return the pedal line of 'M' cutting 'side'.
   <url href = "http://mathworld.wolfram.com/PedalTriangle.html"/></documentation></function></asyxml>*/
  return line(M, projection(side) * M);
}

/*<asyxml><function type="triangle" signature="antipedal(triangle,point)"><code></asyxml>*/
triangle antipedal(triangle t, point M)
{/*<asyxml></code><documentation><url href = "http://mathworld.wolfram.com/AntipedalTriangle.html"/></documentation></function></asyxml>*/
  trilinear Tm = trilinear(t, M);
  real a = Tm.a, b = Tm.b, c = Tm.c;
  real CA = Cos(t.alpha()), CB = Cos(t.beta()), CC = Cos(t.gamma());
  point A = trilinear(t, -(b + a * CC) * (c + a * CB), (c + a * CB) * (a + b * CC), (b + a * CC) * (a + c * CB));
  point B = trilinear(t, (c + b * CA) * (b + a * CC), -(c + b * CA) * (a + b * CC), (a + b * CC) * (b + c * CA));
  point C = trilinear(t, (b + c * CA) * (c + a * CB), (a + c * CB) * (c + b * CA), -(a + c * CB) * (b + c * CA));
  return triangle(A, B, C);
}

/*<asyxml><function type="triangle" signature="extouch(triangle)"><code></asyxml>*/
triangle extouch(triangle t)
{/*<asyxml></code><documentation>Return the extouch triangle of the triangle 't'.
   The extouch triangle of 't' is the triangle formed by the points
   of tangency of a triangle 't' with its excircles.</documentation></function></asyxml>*/
  point A, B, C;
  real a = t.a(), b = t.b(), c = t.c();
  A = trilinear(t, 0, (a - b + c)/b, (a + b - c)/c);
  B = trilinear(t, (-a + b + c)/a, 0, (a + b - c)/c);
  C = trilinear(t, (-a + b + c)/a, (a - b + c)/b, 0);
  return triangle(A, B, C);
}

/*<asyxml><function type="triangle" signature="extouch(triangle)"><code></asyxml>*/
triangle incentral(triangle t)
{/*<asyxml></code><documentation>Return the incentral triangle of the triangle 't'.
   It is the triangle whose vertices are determined by the intersections of the
   reference triangle's angle bisectors with the respective opposite sides.</documentation></function></asyxml>*/
  point A, B, C;
  // real a = t.a(), b = t.b(), c = t.c();
  A = trilinear(t, 0, 1, 1);
  B = trilinear(t, 1, 0, 1);
  C = trilinear(t, 1, 1, 0);
  return triangle(A, B, C);
}

/*<asyxml><function type="triangle" signature="extouch(side)"><code></asyxml>*/
triangle extouch(side side)
{/*<asyxml></code><documentation>Return the triangle formed by the points of tangency of the triangle referenced by 'side' with its excircles.
   One vertex of the returned triangle is on the segment 'side'.</documentation></function></asyxml>*/
  triangle t = side.t;
  transform p1 = projection((line)t.AB);
  transform p2 = projection((line)t.AC);
  transform p3 = projection((line)t.BC);
  point EP = excenter(side);
  return triangle(p3 * EP, p2 * EP, p1 * EP);
}

/*<asyxml><function type="point" signature="bisectorpoint(side)"><code></asyxml>*/
point bisectorpoint(side side)
{/*<asyxml></code><documentation>The intersection point of the angle bisector from the
   opposite point of 'side' with the side 'side'.</documentation></function></asyxml>*/
  triangle t = side.t;
  int n = numarray[abs(side.n) - 1];
  if(n == 1) return trilinear(t, 1, 1, 0);
  if(n == 2) return trilinear(t, 0, 1, 1);
  return trilinear(t, 1, 0, 1);
}

/*<asyxml><function type="line" signature="bisector(vertex,real)"><code></asyxml>*/
line bisector(vertex V, real angle = 0)
{/*<asyxml></code><documentation>Return the interior bisector passing through 'V' rotated by angle (in degrees)
   around 'V'.</documentation></function></asyxml>*/
  return rotate(angle, point(V)) * line(point(V), incenter(V.t));
}

/*<asyxml><function type="line" signature="bisector(side)"><code></asyxml>*/
line bisector(side side)
{/*<asyxml></code><documentation>Return the bisector of the line segment 'side'.</documentation></function></asyxml>*/
  return bisector(segment(side));
}

/*<asyxml><function type="point" signature="intouch(side)"><code></asyxml>*/
point intouch(side side)
{/*<asyxml></code><documentation>The point of tangency on the side 'side' of its incircle.</documentation></function></asyxml>*/
  triangle t = side.t;
  real a = t.a(), b = t.b(), c = t.c();
  int n = numarray[abs(side.n) - 1];
  if(n == 1) return trilinear(t, b * c/(-a + b + c), a * c/(a - b + c), 0);
  if(n == 2) return trilinear(t, 0, a * c/(a - b + c), a * b/(a + b - c));
  return trilinear(t, b * c/(-a + b + c), 0, a * b/(a + b - c));
}

/*<asyxml><function type="triangle" signature="intouch(triangle)"><code></asyxml>*/
triangle intouch(triangle t)
{/*<asyxml></code><documentation>Return the intouch triangle of the triangle 't'.
   The intouch triangle of 't' is the triangle formed by the points
   of tangency of a triangle 't' with its incircles.</documentation></function></asyxml>*/
  point A, B, C;
  real a = t.a(), b = t.b(), c = t.c();
  A = trilinear(t, 0, a * c/(a - b + c), a * b/(a + b - c));
  B = trilinear(t, b * c/(-a + b + c), 0, a * b/(a + b - c));
  C = trilinear(t, b * c/(-a + b + c), a * c/(a - b + c), 0);
  return triangle(A, B, C);
}

/*<asyxml><function type="triangle" signature="tangential(triangle)"><code></asyxml>*/
triangle tangential(triangle t)
{/*<asyxml></code><documentation>Return the tangential triangle of the triangle 't'.
   The tangential triangle of 't' is the triangle formed by the lines
   tangent to the circumcircle of the given triangle 't' at its vertices.</documentation></function></asyxml>*/
  point A, B, C;
  real a = t.a(), b = t.b(), c = t.c();
  A = trilinear(t, -a, b, c);
  B = trilinear(t, a, -b, c);
  C = trilinear(t, a, b, -c);
  return triangle(A, B, C);
}

/*<asyxml><function type="triangle" signature="medial(triangle t)"><code></asyxml>*/
triangle medial(triangle t)
{/*<asyxml></code><documentation>Return the triangle whose vertices are midpoints of the sides of 't'.</documentation></function></asyxml>*/
  return triangle(midpoint(t.BC), midpoint(t.AC), midpoint(t.AB));
}

/*<asyxml><function type="line" signature="median(vertex)"><code></asyxml>*/
line median(vertex V)
{/*<asyxml></code><documentation>Return median from 'V'.</documentation></function></asyxml>*/
  return line(point(V), midpoint(segment(opposite(V))));
}

/*<asyxml><function type="line" signature="median(side)"><code></asyxml>*/
line median(side side)
{/*<asyxml></code><documentation>Return median from the opposite vertex of 'side'.</documentation></function></asyxml>*/
  return median(opposite(side));
}

/*<asyxml><function type="triangle" signature="orthic(triangle)"><code></asyxml>*/
triangle orthic(triangle t)
{/*<asyxml></code><documentation>Return the triangle whose vertices are endpoints of the altitudes from each of the vertices of 't'.</documentation></function></asyxml>*/
  return triangle(foot(t.BC), foot(t.AC), foot(t.AB));
}

/*<asyxml><function type="triangle" signature="symmedial(triangle)"><code></asyxml>*/
triangle symmedial(triangle t)
{/*<asyxml></code><documentation>Return the symmedial triangle of 't'.</documentation></function></asyxml>*/
  point A, B, C;
  real a = t.a(), b = t.b(), c = t.c();
  A = trilinear(t, 0, b, c);
  B = trilinear(t, a, 0, c);
  C = trilinear(t, a, b, 0);
  return triangle(A, B, C);
}

/*<asyxml><function type="triangle" signature="anticomplementary(triangle)"><code></asyxml>*/
triangle anticomplementary(triangle t)
{/*<asyxml></code><documentation>Return the triangle which has the given triangle 't' as its medial triangle.</documentation></function></asyxml>*/
  real a = t.a(), b = t.b(), c = t.c();
  real ab = a * b, bc = b * c, ca = c * a;
  point A = trilinear(t, -bc, ca, ab);
  point B = trilinear(t, bc, -ca, ab);
  point C = trilinear(t, bc, ca, -ab);
  return triangle(A, B, C);
}

/*<asyxml><function type="point[]" signature="intersectionpoints(triangle,line,bool)"><code></asyxml>*/
point[] intersectionpoints(triangle t, line l, bool extended = false)
{/*<asyxml></code><documentation>Return the intersection points.
   If 'extended' is true, the sides are lines else the sides are segments.
   intersectionpoints(line, triangle, bool) is also defined.</documentation></function></asyxml>*/
  point[] OP;
  void addpoint(point P)
  {
    if(defined(P)) {
      bool exist = false;
      for (int i = 0; i < OP.length; ++i) {
        if(P == OP[i]) {exist = true; break;}
      }
      if(!exist) OP.push(P);
    }
  }
  if(extended) {
    for (int i = 1; i <= 3; ++i) {
      addpoint(intersectionpoint(t.line(i), l));
    }
  } else {
    for (int i = 1; i <= 3; ++i) {
      addpoint(intersectionpoint((segment)t.line(i), l));
    }
  }
  return OP;
}

point[] intersectionpoints(line l, triangle t, bool extended = false)
{
  return intersectionpoints(t, l, extended);
}

/*<asyxml><function type="vector" signature="dir(vertex)"><code></asyxml>*/
vector dir(vertex V)
{/*<asyxml></code><documentation>The direction (towards the outside of the triangle) of the interior angle bisector of 'V'.</documentation></function></asyxml>*/
  triangle t = V.t;
  if(V.n == 1) return vector(defaultcoordsys, (-dir(t.A--t.B, t.A--t.C)));
  if(V.n == 2) return vector(defaultcoordsys, (-dir(t.B--t.A, t.B--t.C)));
  return vector(defaultcoordsys, (-dir(t.C--t.A, t.C--t.B)));
}

/*<asyxml><function type="void" signature="lvoid label(picture,Label,vertex,pair,real,pen,filltype)"><code></asyxml>*/
void label(picture pic = currentpicture, Label L, vertex V,
           pair align = dir(V),
           real alignFactor = 1,
           pen p = nullpen, filltype filltype = NoFill)
{/*<asyxml></code><documentation>Draw 'L' on picture 'pic' at vertex 'V' aligned by 'alignFactor * align'.</documentation></function></asyxml>*/
  label(pic, L, locate(point(V)), alignFactor * align, p, filltype);
}

/*<asyxml><function type="void" signature="label(picture,Label,Label,Label,triangle,real,real,pen,filltype)"><code></asyxml>*/
void label(picture pic = currentpicture, Label LA = "$A$",
           Label LB = "$B$", Label LC = "$C$",
           triangle t,
           real alignAngle = 0,
           real alignFactor = 1,
           pen p = nullpen, filltype filltype = NoFill)
{/*<asyxml></code><documentation>Draw labels LA, LB and LC aligned in the rotated (by 'alignAngle' in degrees) direction
   (towards the outside of the triangle) of the interior angle bisector of vertices.
   One can  individually modify the alignment by setting the Label parameter 'align'.</documentation></function></asyxml>*/
  Label lla = LA.copy();
  lla.align(lla.align, rotate(alignAngle) * locate(dir(t.VA)));
  label(pic, LA, t.VA, align = lla.align.dir, alignFactor = alignFactor, p, filltype);
  Label llb = LB.copy();
  llb.align(llb.align, rotate(alignAngle) * locate(dir(t.VB)));
  label(pic, llb, t.VB, align = llb.align.dir, alignFactor = alignFactor, p, filltype);
  Label llc = LC.copy();
  llc.align(llc.align, rotate(alignAngle) * locate(dir(t.VC)));
  label(pic, llc, t.VC, align = llc.align.dir, alignFactor = alignFactor, p, filltype);
}

/*<asyxml><function type="void" signature="show(picture,Label,Label,Label,Label,Label,Label,triangle,pen,filltype)"><code></asyxml>*/
void show(picture pic = currentpicture,
          Label LA = "$A$", Label LB = "$B$", Label LC = "$C$",
          Label La = "$a$", Label Lb = "$b$", Label Lc = "$c$",
          triangle t, pen p = currentpen, filltype filltype = NoFill)
{/*<asyxml></code><documentation>Draw triangle and labels of sides and vertices.</documentation></function></asyxml>*/
  pair a = locate(t.A), b = locate(t.B), c = locate(t.C);
  draw(pic, a--b--c--cycle, p);
  label(pic, LA, a, -dir(a--b, a--c), p, filltype);
  label(pic, LB, b, -dir(b--a, b--c), p, filltype);
  label(pic, LC, c, -dir(c--a, c--b), p, filltype);
  pair aligna = I * unit(c - b), alignb = I * unit(c - a), alignc = I * unit(b - a);
  pair mAB = locate(midpoint(t.AB)), mAC = locate(midpoint(t.AC)), mBC = locate(midpoint(t.BC));
  label(pic, La, b--c, align = rotate(dot(a - mBC, aligna) > 0 ? 180 :0) * aligna, p);
  label(pic, Lb, a--c, align = rotate(dot(b - mAC, alignb) > 0 ? 180 :0) * alignb, p);
  label(pic, Lc, a--b, align = rotate(dot(c - mAB, alignc) > 0 ? 180 :0) * alignc, p);
}

/*<asyxml><function type="void" signature="draw(picture,triangle,pen,marker)"><code></asyxml>*/
void draw(picture pic = currentpicture, triangle t, pen p = currentpen, marker marker = nomarker)
{/*<asyxml></code><documentation>Draw sides of the triangle 't' on picture 'pic' using pen 'p'.</documentation></function></asyxml>*/
  draw(pic, t.Path(), p, marker);
}

/*<asyxml><function type="void" signature="draw(picture,triangle[],pen,marker)"><code></asyxml>*/
void draw(picture pic = currentpicture, triangle[] t, pen p = currentpen, marker marker = nomarker)
{/*<asyxml></code><documentation>Draw sides of the triangles 't' on picture 'pic' using pen 'p'.</documentation></function></asyxml>*/
  for(int i = 0; i < t.length; ++i) draw(pic, t[i], p, marker);
}

/*<asyxml><function type="void" signature="drawline(picture,triangle,pen)"><code></asyxml>*/
void drawline(picture pic = currentpicture, triangle t, pen p = currentpen)
{/*<asyxml></code><documentation>Draw lines of the triangle 't' on picture 'pic' using pen 'p'.</documentation></function></asyxml>*/
  draw(t, p);
  draw(pic, line(t.A, t.B), p);
  draw(pic, line(t.A, t.C), p);
  draw(pic, line(t.B, t.C), p);
}

/*<asyxml><function type="void" signature="dot(picture,triangle,pen)"><code></asyxml>*/
void dot(picture pic = currentpicture, triangle t, pen p = currentpen)
{/*<asyxml></code><documentation>Draw a dot at each vertex of 't'.</documentation></function></asyxml>*/
  dot(pic, t.A^^t.B^^t.C, p);
}
// *.......................TRIANGLES.......................*
// *=======================================================*

// *=======================================================*
// *.......................INVERSIONS......................*
/*<asyxml><function type="point" signature="inverse(real k,point,point)"><code></asyxml>*/
point inverse(real k, point A, point M)
{/*<asyxml></code><documentation>Return the inverse point of 'M' with respect to point A and inversion radius 'k'.</documentation></function></asyxml>*/
  return A + k/conj(M - A);
}

/*<asyxml><function type="point" signature="radicalcenter(circle,circle)"><code></asyxml>*/
point radicalcenter(circle c1, circle c2)
{/*<asyxml></code><documentation><url href = "http://fr.wikipedia.org/wiki/Puissance_d'un_point_par_rapport_%C3%A0_un_cercle"/></documentation></function></asyxml>*/
  point[] P = standardizecoordsys(c1.C, c2.C);
  real k = c1.r^2 - c2.r^2;
  pair C1 = locate(c1.C);
  pair C2 = locate(c2.C);
  pair oop = C2 - C1;
  pair K = (abs(oop) == 0) ?
    (infinity, infinity) :
    midpoint(C1--C2) + 0.5 * k * oop/dot(oop, oop);
  return point(P[0].coordsys, K/P[0].coordsys);
}

/*<asyxml><function type="line" signature="radicalline(circle,circle)"><code></asyxml>*/
line radicalline(circle c1, circle c2)
{/*<asyxml></code><documentation><url href = "http://fr.wikipedia.org/wiki/Puissance_d'un_point_par_rapport_%C3%A0_un_cercle"/></documentation></function></asyxml>*/
  if (c1.C == c2.C) abort("radicalline: the centers must be distinct");
  return perpendicular(radicalcenter(c1, c2), line(c1.C, c2.C));
}

/*<asyxml><function type="point" signature="radicalcenter(circle,circle,circle)"><code></asyxml>*/
point radicalcenter(circle c1, circle c2, circle c3)
{/*<asyxml></code><documentation><url href = "http://fr.wikipedia.org/wiki/Puissance_d'un_point_par_rapport_%C3%A0_un_cercle"/></documentation></function></asyxml>*/
  return intersectionpoint(radicalline(c1, c2), radicalline(c1, c3));
}

/*<asyxml><struct signature="inversion"><code></asyxml>*/
struct inversion
{/*<asyxml></code><documentation>http://mathworld.wolfram.com/Inversion.html</documentation></asyxml>*/
  point C;
  real k;
}/*<asyxml></struct></asyxml>*/

/*<asyxml><function type="inversion" signature="inversion(real,point)"><code></asyxml>*/
inversion inversion(real k, point C)
{/*<asyxml></code><documentation>Return the inversion with respect to 'C' having inversion radius 'k'.</documentation></function></asyxml>*/
  inversion oi;
  oi.k = k;
  oi.C = C;
  return oi;
}
/*<asyxml><function type="inversion" signature="inversion(real,point)"><code></asyxml>*/
inversion inversion(point C, real k)
{/*<asyxml></code><documentation>Return the inversion with respect to 'C' having inversion radius 'k'.</documentation></function></asyxml>*/
  return inversion(k, C);
}

/*<asyxml><function type="inversion" signature="inversion(circle,circle)"><code></asyxml>*/
inversion inversion(circle c1, circle c2, real sgn = 1)
{/*<asyxml></code><documentation>Return the inversion which transforms 'c1' to
   . 'c2' and positive inversion radius if 'sgn > 0';
   . 'c2' and negative inversion radius if 'sgn < 0';
   . 'c1' and 'c2' to 'c2' if 'sgn = 0'.</documentation></function></asyxml>*/
  if(sgn == 0) {
    point O = radicalcenter(c1, c2);
    return inversion(O^c1, O);
  }
  real a = abs(c1.r/c2.r);
  if(sgn > 0) {
    point O = c1.C + a/abs(1 - a) * (c2.C - c1.C);
    return inversion(a * abs(abs(O - c2.C)^2 - c2.r^2), O);
  }
  point O = c1.C + a/abs(1 + a) * (c2.C - c1.C);
  return inversion(-a * abs(abs(O - c2.C)^2 - c2.r^2), O);
}

/*<asyxml><function type="inversion" signature="inversion(circle,circle,circle)"><code></asyxml>*/
inversion inversion(circle c1, circle c2, circle c3)
{/*<asyxml></code><documentation>Return the inversion which transform 'c1' to 'c1', 'c2' to 'c2' and 'c3' to 'c3'.</documentation></function></asyxml>*/
  point Rc = radicalcenter(c1, c2, c3);
  return inversion(Rc, Rc^c1);
}

circle operator cast(inversion i){return circle(i.C, sgn(i.k) * sqrt(abs(i.k)));}
/*<asyxml><function type="circle" signature="circle(inversion)"><code></asyxml>*/
circle circle(inversion i)
{/*<asyxml></code><documentation>Return the inversion circle of 'i'.</documentation></function></asyxml>*/
  return i;
}

inversion operator cast(circle c)
{
  return inversion(sgn(c.r) * c.r^2, c.C);
}
/*<asyxml><function type="inversion" signature="inversion(circle)"><code></asyxml>*/
inversion inversion(circle c)
{/*<asyxml></code><documentation>Return the inversion represented by the circle of 'c'.</documentation></function></asyxml>*/
  return c;
}

/*<asyxml><operator type = "point" signature="*(inversion,point)"><code></asyxml>*/
point operator *(inversion i, point P)
{/*<asyxml></code><documentation>Provide inversion * point.</documentation></operator></asyxml>*/
  return inverse(i.k, i.C, P);
}

void lineinversion()
{
  warning("lineinversion", "the inversion of the line is not a circle.
The returned circle has an infinite radius, circle.l has been set.");
}


/*<asyxml><function type="circle" signature="inverse(real,point,line)"><code></asyxml>*/
circle inverse(real k, point A, line l)
{/*<asyxml></code><documentation>Return the inverse circle of 'l' with
   respect to point 'A' and inversion radius 'k'.</documentation></function></asyxml>*/
  if(A @ l) {
    lineinversion();
    circle C = circle(A, infinity);
    C.l = l;
    return C;
  }
  point Ap = inverse(k, A, l.A), Bp = inverse(k, A, l.B);
  return circle(A, Ap, Bp);
}

/*<asyxml><operator type = "circle" signature="*(inversion,line)"><code></asyxml>*/
circle operator *(inversion i, line l)
{/*<asyxml></code><documentation>Provide inversion * line for lines that don't pass through the inversion center.</documentation></operator></asyxml>*/
  return inverse(i.k, i.C, l);
}

/*<asyxml><function type="circle" signature="inverse(real,point,circle)"><code></asyxml>*/
circle inverse(real k, point A, circle c)
{/*<asyxml></code><documentation>Return the inverse circle of 'c' with
   respect to point A and inversion radius 'k'.</documentation></function></asyxml>*/
  if(degenerate(c)) return inverse(k, A, c.l);
  if(A @ c) {
    lineinversion();
    point M = rotate(180, c.C) * A, Mp = rotate(90, c.C) * A;
    circle oc = circle(A, infinity);
    oc.l = line(inverse(k, A, M), inverse(k, A, Mp));
    return oc;
  }
  point[] P = standardizecoordsys(A, c.C);
  real s = k/((P[1].x - P[0].x)^2 + (P[1].y - P[0].y)^2 - c.r^2);
  return circle(P[0] + s * (P[1]-P[0]), abs(s) * c.r);
}

/*<asyxml><operator type = "circle" signature="*(inversion,circle)"><code></asyxml>*/
circle operator *(inversion i, circle c)
{/*<asyxml></code><documentation>Provide inversion * circle.</documentation></operator></asyxml>*/
  return inverse(i.k, i.C, c);
}
// *.......................INVERSIONS......................*
// *=======================================================*

// *=======================================================*
// *........................FOOTER.........................*
/*<asyxml><function type="point[]" signature="intersectionpoints(line,circle)"><code></asyxml>*/
point[] intersectionpoints(line l, circle c)
{/*<asyxml></code><documentation>Note that the line 'l' may be a segment by casting.
   intersectionpoints(circle, line) is also defined.</documentation></function></asyxml>*/
  if(degenerate(c)) return new point[]{intersectionpoint(l, c.l)};
  point[] op;
  coordsys R = samecoordsys(l.A, c.C) ?
    l.A.coordsys : defaultcoordsys;
  coordsys Rp = defaultcoordsys;
  circle cc = circle(changecoordsys(Rp, c.C), c.r);
  point proj = projection(l) * c.C;
  if(proj @ cc) { // The line is a tangente of the circle.
    if(proj @ l) op.push(proj);// line may be a segement...
  } else {
    coordsys Rc = cartesiansystem(c.C, (1, 0), (0, 1));
    line ll = changecoordsys(Rc, l);
    pair[] P = intersectionpoints(ll.A.coordinates, ll.B.coordinates,
                                1, 0, 1, 0, 0, -c.r^2);
    for (int i = 0; i < P.length; ++i) {
      point inter = changecoordsys(R, point(Rc, P[i]));
      if(inter @ l) op.push(inter);
    }
  }
  return op;
}

point[] intersectionpoints(circle c, line l)
{
  return intersectionpoints(l, c);
}

/*<asyxml><function type="point[]" signature="intersectionpoints(line,ellipse)"><code></asyxml>*/
point[] intersectionpoints(line l, ellipse el)
{/*<asyxml></code><documentation>Note that the line 'l' may be a segment by casting.
   intersectionpoints(ellipse, line) is also defined.</documentation></function></asyxml>*/
  if(el.e == 0) return intersectionpoints(l, (circle)el);
  if(degenerate(el)) return new point[]{intersectionpoint(l, el.l)};
  point[] op;
  coordsys R = samecoordsys(l.A, el.C) ? l.A.coordsys : defaultcoordsys;
  coordsys Rp = defaultcoordsys;
  line ll = changecoordsys(Rp, l);
  ellipse ell = changecoordsys(Rp, el);
  circle C = circle(ell.C, ell.a);
  point[] Ip = intersectionpoints(ll, C);
  if (Ip.length > 0 &&
      (perpendicular(ll, line(ell.F1, Ip[0])) ||
       perpendicular(ll, line(ell.F2, Ip[0])))) {
    // http://www.mathcurve.com/courbes2d/ellipse/ellipse.shtml
    //  Définition tangentielle par antipodaire de cercle.
    // 'l' is a tangent of 'el'
    transform t = scale(el.a/el.b, el.F1, el.F2, el.C, rotate(90, el.C) * el.F1);
    point inter = inverse(t) * intersectionpoints(C, t * ll)[0];
    if(inter @ l) op.push(inter);
  } else {
    coordsys Rc = canonicalcartesiansystem(el);
    line ll = changecoordsys(Rc, l);
    pair[] P = intersectionpoints(ll.A.coordinates, ll.B.coordinates,
                                1/el.a^2, 0, 1/el.b^2, 0, 0, -1);
    for (int i = 0; i < P.length; ++i) {
      point inter = changecoordsys(R, point(Rc, P[i]));
      if(inter @ l) op.push(inter);
    }
  }
  return op;
}

point[] intersectionpoints(ellipse el, line l)
{
  return intersectionpoints(l, el);
}

/*<asyxml><function type="point[]" signature="intersectionpoints(line,parabola)"><code></asyxml>*/
point[] intersectionpoints(line l, parabola p)
{/*<asyxml></code><documentation>Note that the line 'l' may be a segment by casting.
   intersectionpoints(parabola, line) is also defined.</documentation></function></asyxml>*/
  point[] op;
  coordsys R = coordsys(p);
  bool tgt = false;
  line ll = changecoordsys(R, l),
    lv = parallel(p.V, p.D);
  point M = intersectionpoint(lv, ll), tgtp;
  if(finite(M)) {// Test if 'l' is tangent to 'p'
    line l1 = bisector(line(M, p.F));
    line l2 = rotate(90, M) * lv;
    point P = intersectionpoint(l1, l2);
    tgtp = rotate(180, P) * p.F;
    tgt = (tgtp @ l);
  }
  if(tgt) {
    if(tgtp @ l) op.push(tgtp);
  } else {
    real[] eq = changecoordsys(defaultcoordsys, equation(p)).a;
    pair[] tp = intersectionpoints(locate(l.A), locate(l.B), eq);
    point inter;
    for (int i = 0; i < tp.length; ++i) {
      inter = point(R, tp[i]/R);
      if(inter @ l) op.push(inter);
    }
  }
  return op;
}

point[] intersectionpoints(parabola p, line l)
{
  return intersectionpoints(l, p);
}

/*<asyxml><function type="point[]" signature="intersectionpoints(line,hyperbola)"><code></asyxml>*/
point[] intersectionpoints(line l, hyperbola h)
{/*<asyxml></code><documentation>Note that the line 'l' may be a segment by casting.
   intersectionpoints(hyperbola, line) is also defined.</documentation></function></asyxml>*/
  point[] op;
  coordsys R = coordsys(h);
  point A = intersectionpoint(l, h.A1), B = intersectionpoint(l, h.A2);
  point M = midpoint(segment(A, B));
  bool tgt = M @ h;
  if(tgt) {
    if(M @ l) op.push(M);
  } else {
    real[] eq = changecoordsys(defaultcoordsys, equation(h)).a;
    pair[] tp = intersectionpoints(locate(l.A), locate(l.B), eq);
    point inter;
    for (int i = 0; i < tp.length; ++i) {
      inter = point(R, tp[i]/R);
      if(inter @ l) op.push(inter);
    }
  }
  return op;
}

point[] intersectionpoints(hyperbola h, line l)
{
  return intersectionpoints(l, h);
}

/*<asyxml><function type="point[]" signature="intersectionpoints(line,conic)"><code></asyxml>*/
point[] intersectionpoints(line l, conic co)
{/*<asyxml></code><documentation>Note that the line 'l' may be a segment by casting.
   intersectionpoints(conic, line) is also defined.</documentation></function></asyxml>*/
  point[] op;
  if(co.e < 1) op = intersectionpoints((ellipse)co, l);
  else
    if(co.e == 1) op = intersectionpoints((parabola)co, l);
    else op = intersectionpoints((hyperbola)co, l);
  return op;
}

point[] intersectionpoints(conic co, line l)
{
  return intersectionpoints(l, co);
}

/*<asyxml><function type="point[]" signature="intersectionpoints(conic,conic)"><code></asyxml>*/
point[] intersectionpoints(conic co1, conic co2)
{/*<asyxml></code><documentation>Return the intersection points of the two conics.</documentation></function></asyxml>*/
  if(degenerate(co1)) return intersectionpoints(co1.l[0], co2);
  if(degenerate(co2)) return intersectionpoints(co1, co2.l[0]);
  return intersectionpoints(equation(co1), equation(co2));
}

/*<asyxml><function type="point[]" signature="intersectionpoints(triangle,conic,bool)"><code></asyxml>*/
point[] intersectionpoints(triangle t, conic co, bool extended = false)
{/*<asyxml></code><documentation>Return the intersection points.
   If 'extended' is true, the sides are lines else the sides are segments.
   intersectionpoints(conic, triangle, bool) is also defined.</documentation></function></asyxml>*/
  if(degenerate(co)) return intersectionpoints(t, co.l[0], extended);
  point[] OP;
  void addpoint(point P[])
  {
    for (int i = 0; i < P.length; ++i) {
      if(defined(P[i])) {
        bool exist = false;
        for (int j = 0; j < OP.length; ++j) {
          if(P[i] == OP[j]) {exist = true; break;}
        }
        if(!exist) OP.push(P[i]);
      }}}
  if(extended) {
    for (int i = 1; i <= 3; ++i) {
      addpoint(intersectionpoints(t.line(i), co));
    }
  } else {
    for (int i = 1; i <= 3; ++i) {
      addpoint(intersectionpoints((segment)t.line(i), co));
    }
  }
  return OP;
}

point[] intersectionpoints(conic co, triangle t, bool extended = false)
{
  return intersectionpoints(t, co, extended);
}

/*<asyxml><function type="point[]" signature="intersectionpoints(ellipse,ellipse)"><code></asyxml>*/
point[] intersectionpoints(ellipse a, ellipse b)
{/*<asyxml></code><documentation></documentation></function></asyxml>*/
  // if(degenerate(a)) return intersectionpoints(a.l, b);
  // if(degenerate(b)) return intersectionpoints(a, b.l);;
  return intersectionpoints((conic)a, (conic)b);
}
/*<asyxml><function type="point[]" signature="intersectionpoints(ellipse,circle)"><code></asyxml>*/
point[] intersectionpoints(ellipse a, circle b)
{/*<asyxml></code><documentation></documentation></function></asyxml>*/
  // if(degenerate(a)) return intersectionpoints(a.l, b);
  // if(degenerate(b)) return intersectionpoints(a, b.l);;
  return intersectionpoints((conic)a, (conic)b);
}
/*<asyxml><function type="point[]" signature="intersectionpoints(circle,ellipse)"><code></asyxml>*/
point[] intersectionpoints(circle a, ellipse b)
{/*<asyxml></code><documentation></documentation></function></asyxml>*/
  return intersectionpoints(b, a);
}
/*<asyxml><function type="point[]" signature="intersectionpoints(ellipse,parabola)"><code></asyxml>*/
point[] intersectionpoints(ellipse a, parabola b)
{/*<asyxml></code><documentation></documentation></function></asyxml>*/
  // if(degenerate(a)) return intersectionpoints(a.l, b);
  return intersectionpoints((conic)a, (conic)b);
}
/*<asyxml><function type="point[]" signature="intersectionpoints(parabola,ellipse)"><code></asyxml>*/
point[] intersectionpoints(parabola a, ellipse b)
{/*<asyxml></code><documentation></documentation></function></asyxml>*/
  return intersectionpoints(b, a);
}
/*<asyxml><function type="point[]" signature="intersectionpoints(ellipse,hyperbola)"><code></asyxml>*/
point[] intersectionpoints(ellipse a, hyperbola b)
{/*<asyxml></code><documentation></documentation></function></asyxml>*/
  // if(degenerate(a)) return intersectionpoints(a.l, b);
  return intersectionpoints((conic)a, (conic)b);
}
/*<asyxml><function type="point[]" signature="intersectionpoints(hyperbola,ellipse)"><code></asyxml>*/
point[] intersectionpoints(hyperbola a, ellipse b)
{/*<asyxml></code><documentation></documentation></function></asyxml>*/
  return intersectionpoints(b, a);
}

/*<asyxml><function type="point[]" signature="intersectionpoints(circle,parabola)"><code></asyxml>*/
point[] intersectionpoints(circle a, parabola b)
{/*<asyxml></code><documentation></documentation></function></asyxml>*/
  return intersectionpoints((conic)a, (conic)b);
}
/*<asyxml><function type="point[]" signature="intersectionpoints(parabola,circle)"><code></asyxml>*/
point[] intersectionpoints(parabola a, circle b)
{/*<asyxml></code><documentation></documentation></function></asyxml>*/
  return intersectionpoints((conic)a, (conic)b);
}
/*<asyxml><function type="point[]" signature="intersectionpoints(circle,hyperbola)"><code></asyxml>*/
point[] intersectionpoints(circle a, hyperbola b)
{/*<asyxml></code><documentation></documentation></function></asyxml>*/
  return intersectionpoints((conic)a, (conic)b);
}
/*<asyxml><function type="point[]" signature="intersectionpoints(hyperbola,circle)"><code></asyxml>*/
point[] intersectionpoints(hyperbola a, circle b)
{/*<asyxml></code><documentation></documentation></function></asyxml>*/
  return intersectionpoints((conic)a, (conic)b);
}

/*<asyxml><function type="point[]" signature="intersectionpoints(parabola,parabola)"><code></asyxml>*/
point[] intersectionpoints(parabola a, parabola b)
{/*<asyxml></code><documentation></documentation></function></asyxml>*/
  return intersectionpoints((conic)a, (conic)b);
}
/*<asyxml><function type="point[]" signature="intersectionpoints(parabola,hyperbola)"><code></asyxml>*/
point[] intersectionpoints(parabola a, hyperbola b)
{/*<asyxml></code><documentation></documentation></function></asyxml>*/
  return intersectionpoints((conic)a, (conic)b);
}
/*<asyxml><function type="point[]" signature="intersectionpoints(hyperbola,parabola)"><code></asyxml>*/
point[] intersectionpoints(hyperbola a, parabola b)
{/*<asyxml></code><documentation></documentation></function></asyxml>*/
  return intersectionpoints((conic)a, (conic)b);
}
/*<asyxml><function type="point[]" signature="intersectionpoints(hyperbola,hyperbola)"><code></asyxml>*/
point[] intersectionpoints(hyperbola a, hyperbola b)
{/*<asyxml></code><documentation></documentation></function></asyxml>*/
  return intersectionpoints((conic)a, (conic)b);
}

/*<asyxml><function type="point[]" signature="intersectionpoints(circle,circle)"><code></asyxml>*/
point[] intersectionpoints(circle c1, circle c2)
{/*<asyxml></code><documentation></documentation></function></asyxml>*/
  if(degenerate(c1))
    return degenerate(c2) ?
      new point[]{intersectionpoint(c1.l, c2.l)} : intersectionpoints(c1.l, c2);
  if(degenerate(c2)) return intersectionpoints(c1, c2.l);
  return (c1.C == c2.C) ?
    new point[] :
    intersectionpoints(radicalline(c1, c2), c1);
}

/*<asyxml><function type="line" signature="tangent(circle,abscissa)"><code></asyxml>*/
line tangent(circle c, abscissa x)
{/*<asyxml></code><documentation>Return the tangent of 'c' at 'point(c, x)'.</documentation></function></asyxml>*/
  if(c.r == 0) abort("tangent: a circle with a radius equals zero has no tangent.");
  point M = point(c, x);
  return line(rotate(90, M) * c.C, M);
}

/*<asyxml><function type="line[]" signature="tangents(circle,point)"><code></asyxml>*/
line[] tangents(circle c, point M)
{/*<asyxml></code><documentation>Return the tangents of 'c' passing through 'M'.</documentation></function></asyxml>*/
  line[] ol;
  if(inside(c, M)) return ol;
  if(M @ c) {
    ol.push(tangent(c, relabscissa(c, M)));
  } else {
    circle cc = circle(c.C, M);
    point[] inter = intersectionpoints(c, cc);
    for (int i = 0; i < inter.length; ++i)
      ol.push(tangents(c, inter[i])[0]);
  }
  return ol;
}

/*<asyxml><function type="point" signature="point(circle,point)"><code></asyxml>*/
point point(circle c, point M)
{/*<asyxml></code><documentation>Return the intersection point of 'c'
   with the half-line '[c.C M)'.</documentation></function></asyxml>*/
  return intersectionpoints(c, line(c.C, false, M))[0];
}

/*<asyxml><function type="line" signature="tangent(circle,point)"><code></asyxml>*/
line tangent(circle c, point M)
{/*<asyxml></code><documentation>Return the tangent of 'c' at the
   intersection point of the half-line'[c.C M)'.</documentation></function></asyxml>*/
  return tangents(c, point(c, M))[0];
}

/*<asyxml><function type="point" signature="point(circle,explicit vector)"><code></asyxml>*/
point point(circle c, explicit vector v)
{/*<asyxml></code><documentation>Return the intersection point of 'c'
   with the half-line '[c.C v)'.</documentation></function></asyxml>*/
  return point(c, c.C + v);
}

/*<asyxml><function type="line" signature="tangent(circle,explicit vector)"><code></asyxml>*/
line tangent(circle c, explicit vector v)
{/*<asyxml></code><documentation>Return the tangent of 'c' at the
   point M so that vec(c.C M) is collinear to 'v' with the same sense.</documentation></function></asyxml>*/
  line ol = tangent(c, c.C + v);
  return dot(ol.v, v) > 0 ? ol : reverse(ol);
}

/*<asyxml><function type="line" signature="tangent(ellipse,abscissa)"><code></asyxml>*/
line tangent(ellipse el, abscissa x)
{/*<asyxml></code><documentation>Return the tangent of 'el' at 'point(el, x)'.</documentation></function></asyxml>*/
  point M = point(el, x);
  line l1 = line(el.F1, M);
  line l2 = line(el.F2, M);
  line ol = (l1 == l2) ? perpendicular(M, l1) : bisector(l1, l2, 90, false);
  return ol;
}

/*<asyxml><function type="line[]" signature="tangents(ellipse,point)"><code></asyxml>*/
line[] tangents(ellipse el, point M)
{/*<asyxml></code><documentation>Return the tangents of 'el' passing through 'M'.</documentation></function></asyxml>*/
  line[] ol;
  if(inside(el, M)) return ol;
  if(M @ el) {
    ol.push(tangent(el, relabscissa(el, M)));
  } else {
    point Mp = samecoordsys(M, el.F2) ?
      M : changecoordsys(el.F2.coordsys, M);
    circle c = circle(Mp, abs(el.F1 - Mp));
    circle cc = circle(el.F2, 2 * el.a);
    point[] inter = intersectionpoints(c, cc);
    for (int i = 0; i < inter.length; ++i) {
      line tl = line(inter[i], el.F2, false);
      point[] P = intersectionpoints(tl, el);
      ol.push(line(Mp, P[0]));
    }
  }
  return ol;
}

/*<asyxml><function type="line" signature="tangent(parabola,abscissa)"><code></asyxml>*/
line tangent(parabola p, abscissa x)
{/*<asyxml></code><documentation>Return the tangent of 'p' at 'point(p, x)' (use the Wells method).</documentation></function></asyxml>*/
  line lt = rotate(90, p.V) * line(p.V, p.F);
  point P = point(p, x);
  if(P == p.V) return lt;
  point M = midpoint(segment(P, p.F));
  line l = rotate(90, M) * line(P, p.F);
  return line(P, projection(lt) * M);
}

/*<asyxml><function type="line[]" signature="tangents(parabola,point)"><code></asyxml>*/
line[] tangents(parabola p, point M)
{/*<asyxml></code><documentation>Return the tangent of 'p' at 'M' (use the Wells method).</documentation></function></asyxml>*/
  line[] ol;
  if(inside(p, M)) return ol;
  if(M @ p) {
    ol.push(tangent(p, angabscissa(p, M)));
  }
  else {
    point Mt = changecoordsys(coordsys(p), M);
    circle c = circle(Mt, p.F);
    line l = rotate(90, p.V) * line(p.V, p.F);
    point[] R = intersectionpoints(l, c);
    for (int i = 0; i < R.length; ++i) {
      ol.push(line(Mt, R[i]));
    }
    // An other method: http://www.du.edu/~jcalvert/math/parabola.htm
    //   point[] R = intersectionpoints(p.directrix, c);
    //   for (int i = 0; i < R.length; ++i) {
    //     ol.push(bisector(segment(p.F, R[i])));
    //   }
  }
  return ol;
}

/*<asyxml><function type="line" signature="tangent(hyperbola,abscissa)"><code></asyxml>*/
line tangent(hyperbola h, abscissa x)
{/*<asyxml></code><documentation>Return the tangent of 'h' at 'point(p, x)'.</documentation></function></asyxml>*/
  point M = point(h, x);
  line ol = bisector(line(M, h.F1), line(M, h.F2));
  if(sameside(h.F1, h.F2, ol) || ol == line(h.F1, h.F2)) ol = rotate(90, M) * ol;
  return ol;
}

/*<asyxml><function type="line[]" signature="tangents(hyperbola,point)"><code></asyxml>*/
line[] tangents(hyperbola h, point M)
{/*<asyxml></code><documentation>Return the tangent of 'h' at 'M'.</documentation></function></asyxml>*/
  line[] ol;
  if(M @ h) {
    ol.push(tangent(h, angabscissa(h, M, fromCenter)));
  } else {
    coordsys cano = canonicalcartesiansystem(h);
    bqe bqe = changecoordsys(cano, equation(h));
    real a = abs(1/(bqe.a[5] * bqe.a[0])), b = abs(1/(bqe.a[5] * bqe.a[2]));
    point Mp = changecoordsys(cano, M);
    real x0 = Mp.x, y0 = Mp.y;
    if(abs(x0) > epsgeo) {
      real c0 = a * y0^2/(b * x0)^2 - 1/b,
        c1 = 2 * a * y0/(b * x0^2), c2 = a/x0^2 - 1;
      real[] sol = quadraticroots(c0, c1, c2);
      for (real y:sol) {
        point tmp = changecoordsys(coordsys(h), point(cano, (a * (1 + y * y0/b)/x0, y)));
        ol.push(line(M, tmp));
      }
    } else if(abs(y0) > epsgeo) {
      real y = -b/y0, x = sqrt(a * (1 + b/y0^2));
      ol.push(line(M, changecoordsys(coordsys(h), point(cano, (x, y)))));
      ol.push(line(M, changecoordsys(coordsys(h), point(cano, (-x, y)))));
    }}
  return ol;
}

/*<asyxml><function type="point[]" signature="intersectionpoints(conic,arc)"><code></asyxml>*/
point[] intersectionpoints(conic co, arc a)
{/*<asyxml></code><documentation>intersectionpoints(arc, circle) is also defined.</documentation></function></asyxml>*/
  point[] op;
  point[] tp = intersectionpoints(co, (conic)a.el);
  for (int i = 0; i < tp.length; ++i)
    if(tp[i] @ a) op.push(tp[i]);
  return op;
}

point[] intersectionpoints(arc a, conic co)
{
  return intersectionpoints(co, a);
}

/*<asyxml><function type="point[]" signature="intersectionpoints(arc,arc)"><code></asyxml>*/
point[] intersectionpoints(arc a1, arc a2)
{/*<asyxml></code><documentation></documentation></function></asyxml>*/
  point[] op;
  point[] tp = intersectionpoints(a1.el, a2.el);
  for (int i = 0; i < tp.length; ++i)
    if(tp[i] @ a1 && tp[i] @ a2) op.push(tp[i]);
  return op;
}


/*<asyxml><function type="point[]" signature="intersectionpoints(line,arc)"><code></asyxml>*/
point[] intersectionpoints(line l, arc a)
{/*<asyxml></code><documentation>intersectionpoints(arc, line) is also defined.</documentation></function></asyxml>*/
  point[] op;
  point[] tp = intersectionpoints(a.el, l);
  for (int i = 0; i < tp.length; ++i)
    if(tp[i] @ a && tp[i] @ l) op.push(tp[i]);
  return op;
}

point[] intersectionpoints(arc a, line l)
{
  return intersectionpoints(l, a);
}

/*<asyxml><function type="point" signature="arcsubtendedcenter(point,point,real)"><code></asyxml>*/
point arcsubtendedcenter(point A, point B, real angle)
{/*<asyxml></code><documentation>Return the center of the arc retuned
   by the 'arcsubtended' routine.</documentation></function></asyxml>*/
  point OM;
  point[] P = standardizecoordsys(A, B);
  angle = angle%(sgnd(angle) * 180);
  line bis = bisector(P[0], P[1]);
  line AB = line(P[0], P[1]);
  return intersectionpoint(bis, rotate(90 - angle, A) * AB);
}

/*<asyxml><function type="arc" signature="arcsubtended(point,point,real)"><code></asyxml>*/
arc arcsubtended(point A, point B, real angle)
{/*<asyxml></code><documentation>Return the arc circle from which the segment AB is saw with
   the angle 'angle'.
   If the point 'M' is on this arc, the oriented angle (MA, MB) is
   equal to 'angle'.</documentation></function></asyxml>*/
  point[] P = standardizecoordsys(A, B);
  line AB = line(P[0], P[1]);
  angle = angle%(sgnd(angle) * 180);
  point C = arcsubtendedcenter(P[0], P[1], angle);
  real BC = degrees(B - C)%360;
  real AC = degrees(A - C)%360;
  return arc(circle(C, abs(B - C)), BC, AC, angle > 0 ? CCW : CW);
}

/*<asyxml><function type="arc" signature="arccircle(point,point,point)"><code></asyxml>*/
arc arccircle(point A, point M, point B)
{/*<asyxml></code><documentation>Return the CCW arc circle 'AB' passing through 'M'.</documentation></function></asyxml>*/
  circle tc = circle(A, M, B);
  real a = degrees(A - tc.C);
  real b = degrees(B - tc.C);
  real m = degrees(M - tc.C);

  arc oa = arc(tc, a, b);
  // TODO : use cross product to determine CWW or CW
  if (!(M @ oa)) {
    oa.direction = !oa.direction;
  }

  return oa;
}

/*<asyxml><function type="arc" signature="arc(ellipse,abscissa,abscissa,bool)"><code></asyxml>*/
arc arc(ellipse el, explicit abscissa x1, explicit abscissa x2, bool direction = CCW)
{/*<asyxml></code><documentation>Return the arc from 'point(c, x1)' to 'point(c, x2)' in the direction 'direction'.</documentation></function></asyxml>*/
  real a = degrees(point(el, x1) - el.C);
  real b = degrees(point(el, x2) - el.C);
  arc oa = arc(el, a - el.angle, b - el.angle, fromCenter, direction);
  return oa;
}

/*<asyxml><function type="arc" signature="arc(ellipse,point,point,bool)"><code></asyxml>*/
arc arc(ellipse el, point M, point N, bool direction = CCW)
{/*<asyxml></code><documentation>Return the arc from 'M' to 'N' in the direction 'direction'.
   The points 'M' and 'N' must belong to the ellipse 'el'.</documentation></function></asyxml>*/
  return arc(el, relabscissa(el, M), relabscissa(el, N), direction);
}

/*<asyxml><function type="arc" signature="arccircle(point,point,real,bool)"><code></asyxml>*/
arc arccircle(point A, point B, real angle, bool direction = CCW)
{/*<asyxml></code><documentation>Return the arc circle centered on A
   from B to rotate(angle, A) * B in the direction 'direction'.</documentation></function></asyxml>*/
  point M = rotate(angle, A) * B;
  return arc(circle(A, abs(A - B)), B, M, direction);
}

/*<asyxml><function type="arc" signature="arc(explicit arc,abscissa,abscissa)"><code></asyxml>*/
arc arc(explicit arc a, abscissa x1, abscissa x2)
{/*<asyxml></code><documentation>Return the arc from 'point(a, x1)' to 'point(a, x2)' traversed in the direction of the arc direction.</documentation></function></asyxml>*/
  real a1 = angabscissa(a.el, point(a, x1), a.polarconicroutine).x;
  real a2 = angabscissa(a.el, point(a, x2), a.polarconicroutine).x;
  return arc(a.el, a1, a2, a.polarconicroutine, a.direction);
}

/*<asyxml><function type="arc" signature="arc(explicit arc,point,point)"><code></asyxml>*/
arc arc(explicit arc a, point M, point N)
{/*<asyxml></code><documentation>Return the arc from 'M' to 'N'.
   The points 'M' and 'N' must belong to the arc 'a'.</documentation></function></asyxml>*/
  return arc(a, relabscissa(a, M), relabscissa(a, N));
}

/*<asyxml><function type="arc" signature="inverse(real,point,segment)"><code></asyxml>*/
arc inverse(real k, point A, segment s)
{/*<asyxml></code><documentation>Return the inverse arc circle of 's'
   with respect to point A and inversion radius 'k'.</documentation></function></asyxml>*/
  point Ap = inverse(k, A, s.A), Bp = inverse(k, A, s.B),
    M = inverse(k, A, midpoint(s));
  return arccircle(Ap, M, Bp);
}

/*<asyxml><operator type = "arc" signature="*(inversion,segment)"><code></asyxml>*/
arc operator *(inversion i, segment s)
{/*<asyxml></code><documentation>Provide
   inversion * segment.</documentation></operator></asyxml>*/
  return inverse(i.k, i.C, s);
}

/*<asyxml><operator type = "path" signature="*(inversion,triangle)"><code></asyxml>*/
path operator *(inversion i, triangle t)
{/*<asyxml></code><documentation>Provide inversion * triangle.</documentation></operator></asyxml>*/
  return (path)(i * segment(t.AB))--
    (path)(i * segment(t.BC))--
    (path)(i * segment(t.CA))&cycle;
}

/*<asyxml><function type="path" signature="compassmark(pair,pair,real,real)"><code></asyxml>*/
path compassmark(pair O, pair A, real position, real angle = 10)
{/*<asyxml></code><documentation>Return an arc centered on O with the angle 'angle' so that the position
   of 'A' on this arc makes an angle 'position * angle'.</documentation></function></asyxml>*/
  real a = degrees(A - O);
  real pa = (a - position * angle)%360,
    pb = (a - (position - 1) * angle)%360;
  real t1 = intersect(unitcircle, (0, 0)--2 * dir(pa))[0];
  real t2 = intersect(unitcircle, (0, 0)--2 * dir(pb))[0];
  int n = length(unitcircle);
  if(t1 >= t2) t1 -= n;
  return shift(O) * scale(abs(O - A)) * subpath(unitcircle, t1, t2);
}

/*<asyxml><function type="line" signature="tangent(explicit arc,abscissa)"><code></asyxml>*/
line tangent(explicit arc a, abscissa x)
{/*<asyxml></code><documentation>Return the tangent of 'a' at 'point(a, x)'.</documentation></function></asyxml>*/
  abscissa ag = angabscissa(a, point(a, x));
  return tangent(a.el, ag + a.angle1 + (a.el.e == 0 ? a.angle0 : 0));
}

/*<asyxml><function type="line" signature="tangent(explicit arc,point)"><code></asyxml>*/
line tangent(explicit arc a, point M)
{/*<asyxml></code><documentation>Return the tangent of 'a' at 'M'.
   The points 'M' must belong to the arc 'a'.</documentation></function></asyxml>*/
  return tangent(a, angabscissa(a, M));
}

// *=======================================================*
// *.......Routines for compatibility with original geometry module........*

path square(pair z1, pair z2)
{
  pair v = z2 - z1;
  pair z3 = z2 + I * v;
  pair z4 = z3 - v;
  return z1--z2--z3--z4--cycle;
}

// Draw a perpendicular symbol at z aligned in the direction align
// relative to the path z--z + dir.
void perpendicular(picture pic = currentpicture, pair z, pair align,
                   pair dir = E, real size = 0, pen p = currentpen,
                   margin margin = NoMargin, filltype filltype = NoFill)
{
  perpendicularmark(pic, (point) z, align, dir, size, p, margin, filltype);
}


// Draw a perpendicular symbol at z aligned in the direction align
// relative to the path z--z + dir(g, 0)
void perpendicular(picture pic = currentpicture, pair z, pair align, path g,
                   real size = 0, pen p = currentpen, margin margin = NoMargin,
                   filltype filltype = NoFill)
{
  perpendicularmark(pic, (point) z, align, dir(g, 0), size, p, margin, filltype);
}

// Return an interior arc BAC of triangle ABC, given a radius r > 0.
// If r < 0, return the corresponding exterior arc of radius |r|.
path arc(explicit pair B, explicit pair A, explicit pair C, real r)
{
  return arc(A, r, degrees(B - A), degrees(C - A));
}

// *.......End of compatibility routines........*
// *=======================================================*

// *........................FOOTER.........................*
// *=======================================================*
