/*
 *  Eukleides  version 1.0.3
 *  Copyright (c) Christian Obrecht 2000-2004
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <stdlib.h>
#include <math.h>
#include "types.h"
#include "geometry.h"
#include "parser.tab.h"

extern void warning (char *);

extern void yyerror (char *);

extern void save (void *);

int Snd_degree (double *x1, double *x2, double a, double b, double c)
{
    double d;
    if (a == 0)
	return 0;
    d = b * b - 4 * a * c;
    if (d < 0)
	return 0;
    if (d == 0)
      {
	  *x1 = *x2 = -.5 * b / a;
	  return 1;
      }
    *x1 = (-b - sqrt (d)) / (2 * a);
    *x2 = (-b + sqrt (d)) / (2 * a);
    return 2;
}

double Pow (double x, double y)
{
    if (x < 0)
	return pow (x, rint (y));
    return pow (x, y);
}

double Sin (double x)
{
    return sin (RAD (x));
}

double Cos (double x)
{
    return cos (RAD (x));
}

double Tan (double x)
{
    return tan (RAD (x));
}

double Asin (double x)
{
    return DEG (asin (x));
}

double Acos (double x)
{
    return DEG (acos (x));
}

double Atan (double x)
{
    return DEG (atan (x));
}

double PP_distance (_point * A, _point * B)
{
    return LENG (A->x - B->x, A->y - B->y);
}

double PL_distance (_point * A, _line * l)
{
    return fabs ((l->y - A->y) * Cos (l->angle) -
		 (l->x - A->x) * Sin (l->angle));
}

double V_length (_vector * v)
{
    return LENG (v->x, v->y);
}

double S_length (_segment * s)
{
    return LENG (s->x1 - s->x2, s->y1 - s->y2);
}

double eccentricity (_conic * C)
{
    switch (C->kind)
      {
      case PARABOLA:
	  return 1;
      case ELLIPSE:
	  return sqrt (1 - C->b * C->b / (C->a * C->a));
      case HYPERBOLA:
	  return sqrt (1 + C->b * C->b / (C->a * C->a));
      }
    return 1;
}

double height (_point * A, _point * B, _point * C)
{
    double a, b, c;
    a = LENG (B->x - C->x, B->y - C->y);
    b = LENG (A->x - C->x, A->y - C->y);
    c = LENG (B->x - A->x, B->y - A->y);
    return sqrt ((a + b + c) * (a + b - c) * (a + c - b) * (b + c - a))
		/ (2 * b);
}

double angle (double x, double y)
{
    if ((ZERO (x)) && (ZERO (y)))
      {
	  warning ("undefined angle.");
	  return 0;
      }
    if (y >= 0)
      {
	  if (ZERO (x))
	      return 90;
	  else if (x > 0)
	      return DEG (atan (y / x));
	  else
	      return 180 + DEG (atan (y / x));
      }
    else
      {
	  if (ZERO (x))
	      return -90;
	  else if (x > 0)
	      return DEG (atan (y / x));
	  else
	      return -180 + DEG (atan (y / x));
      }
}

double format (double a)
{
    if (a > 180)
	return a - 360;
    else if (a <= -180)
	return a + 360;
    else
	return a;
}

double V_angle (_vector * v)
{
    return angle (v->x, v->y);
}

double S_angle (_segment * s)
{
    return angle (s->x2 - s->x1, s->y2 - s->y1);
}

double Co_angle (_conic * C)
{
    if (C->kind == PARABOLA)
	return format (C->d + 90);
    else
	return C->d;
}

double VV_angle (_vector * v1, _vector * v2)
{
    return format (V_angle (v2) - V_angle (v1));
}

double T_angle (_point * A, _point * B, _point * C)
{
    return format (angle (C->x - B->x, C->y - B->y) -
		   angle (A->x - B->x, A->y - B->y));
}

double C_argument (_circle * C, _point * A)
{
    return angle (A->x - C->x, A->y - C->y);
}

double Co_argument (_conic * C, _point * A)
{
    double c0, s0, x, y, z;

    c0 = Cos (C->d);
    s0 = Sin (C->d);
    x = (A->x - C->x) * c0 + (A->y - C->y) * s0;
    y = -(A->x - C->x) * s0 + (A->y - C->y) * c0;

    switch (C->kind)
      {
      case PARABOLA:
	  return x;
      case ELLIPSE:
	  if (ZERO (x))
	      return (y < 0) ? (-M_PI_2) : M_PI_2;
	  z = atan (C->a * y / (C->b * x));
	  if (x > 0)
	      return z;
	  else if (y < 0)
	      return -M_PI + z;
	  else
	      return M_PI + z;
      case HYPERBOLA:
	  z = atan (y / C->b);
	  if (x > 0)
	      return z;
	  else
	      return z + M_PI;
      }
    return 0;
}

double scalar (_vector * v1, _vector * v2)
{
    return v1->x * v2->x + v1->y * v2->y;
}

void P_assignment (symrec * s, _point * A)
{
    s->type = POINT;
    s->object.point = A;
}

_point *P_new (double x, double y)
{
    _point *ptr;

    ptr = (_point *) (malloc (sizeof (_point)));
    ptr->x = x;
    ptr->y = y;
    save ((void *) ptr);
    return ptr;
}

_point *P_polar (double r, double a)
{
    return P_new (r * Cos (a), r * Sin (a));
}

_point *P_line (_line * l, double d)
{
    return P_new (l->x + d * Cos (l->angle), l->y + d * Sin (l->angle));
}

_point *P_segment (_segment * s, double d)
{
    double a;
    a = S_angle (s);
    return P_new (s->x1 + d * Cos (a), s->y1 + d * Sin (a));
}

_point *P_circle (_circle * c, double a)
{
    return P_new (c->x + c->radius * Cos (a), c->y + c->radius * Sin (a));
}

_point *P_conic (_conic * C, double a)
{
    double c0, s0, A, B, x, y;

    c0 = Cos (C->d);
    s0 = Sin (C->d);
    A = C->a;
    B = C->b;

    switch (C->kind)
      {
      case PARABOLA:
	  x = a;
	  y = 2 * A * a * a;
	  break;
      case ELLIPSE:
	  x = A * cos (a);
	  y = B * sin (a);
	  break;
      case HYPERBOLA:
	  if (ZERO (cos (a)))
	      yyerror ("undefined point.");
	  else
	    {
		x = -A / cos (a);
		y = B * tan (a);
	    }
      }
    return P_new (C->x + c0 * x - s0 * y, C->y + s0 * x + c0 * y);
}

_point *P_translation (_point * A, _vector * v)
{
    return P_new (A->x + v->x, A->y + v->y);
}

_point *P_reflection (_point * A, _line * l)
{
    double a, le;

    a = format (2 * l->angle - angle (A->x - l->x, A->y - l->y));
    le = LENG (A->x - l->x, A->y - l->y);
    return P_new (l->x + le * Cos (a), l->y + le * Sin (a));
}

_point *P_rotation (_point * A, _point * O, double a)
{
    double c, s, x, y;
    c = Cos (a);
    s = Sin (a);
    x = A->x - O->x;
    y = A->y - O->y;
    return P_new (O->x + c * x - s * y, O->y + s * x + c * y);
}

_point *P_L_projection (_point * A, _line * l)
{
    _line *tmp;

    tmp = L_L_perpendicular (l, A);
    return P_LL_intersection (l, tmp);
}

_point *P_LL_projection (_point * A, _line * l1, _line * l2)
{
    _line *tmp;

    tmp = L_L_parallel (l2, A);
    return P_LL_intersection (l1, tmp);
}

_point *P_homothecy (_point * A, _point * O, double k)
{
    return P_new (O->x + k * (A->x - O->x), O->y + k * (A->y - O->y));
}

_point *P_2_barycenter (_point * A, double al, _point * B, double be)
{
    double s;
    s = al + be;
    if (ZERO (s))
      {
	  yyerror ("undefined barycenter.");
	  return NULL;
      }
    return P_new ((al * A->x + be * B->x) / s, (al * A->y + be * B->y) / s);
}

_point *P_3_barycenter (_point * A, double al, _point * B, double be,
			_point * C, double ga)
{
    double s;
    s = al + be + ga;
    if (ZERO (s))
      {
	  yyerror ("undefined barycenter.");
	  return NULL;
      }
    return P_new ((al * A->x + be * B->x + ga * C->x) / s,
		  (al * A->y + be * B->y + ga * C->y) / s);
}

_point *P_4_barycenter (_point * A, double al, _point * B, double be,
			_point * C, double ga, _point * D, double de)
{
    double s;
    s = al + be + ga + de;
    if (ZERO (s))
      {
	  yyerror ("undefined barycenter.");
	  return NULL;
      }
    return P_new ((al * A->x + be * B->x + ga * C->x + de * D->x) / s,
		  (al * A->y + be * B->y + ga * C->y + de * D->y) / s);
}

_point *P_LL_intersection (_line * l1, _line * l2)
{
    double c1, c2, s1, s2, d1, d2, d;
    c1 = Cos (l1->angle);
    c2 = Cos (l2->angle);
    s1 = Sin (l1->angle);
    s2 = Sin (l2->angle);
    d = c1 * s2 - c2 * s1;
    if (ZERO (d))
      {
	  yyerror ("parallel lines.");
	  return NULL;
      }
    d1 = s1 * l1->x - c1 * l1->y;
    d2 = s2 * l2->x - c2 * l2->y;
    return P_new ((c1 * d2 - c2 * d1) / d, (s1 * d2 - s2 * d1) / d);
}

_point *P_abscissa (_line * l, double x)
{
    if (ZERO (Cos (l->angle)))
      {
	  yyerror ("undefined point.");
	  return NULL;
      }
    return P_new (x, Tan (l->angle) * (x - l->x) + l->y);
}

_point *P_ordinate (_line * l, double y)
{
    if (ZERO (Sin (l->angle)))
      {
	  yyerror ("undefined point.");
	  return NULL;
      }
    return P_new ((y - l->y) / Tan (l->angle) + l->x, y);
}

_point *P_midpoint (_segment * s)
{
    return P_new ((s->x1 + s->x2) / 2, (s->y1 + s->y2) / 2);
}

_point *P_begin (_segment * s)
{
    return P_new (s->x1, s->y1);
}

_point *P_end (_segment * s)
{
    return P_new (s->x2, s->y2);
}

_point *P_center (_circle * c)
{
    return P_new (c->x, c->y);
}

_point *P_Co_center (_conic * C)
{
    return P_new (C->x, C->y);
}

_point *P_orthocenter (_point * A, _point * B, _point * C)
{
    _line *tmp1, *tmp2;

    tmp1 = L_altitude (A, B, C);
    tmp2 = L_altitude (B, A, C);
    return P_LL_intersection (tmp1, tmp2);
}

void V_assignment (symrec * s, _vector * v)
{
    s->type = VECTOR;
    s->object.vector = v;
}

_vector *V_NN_new (double x, double y)
{
    _vector *ptr;
    ptr = (_vector *) (malloc (sizeof (_vector)));
    ptr->x = x;
    ptr->y = y;
    save ((void *) ptr);
    return ptr;
}

_vector *V_NA_new (double l, double a)
{
    return V_NN_new (l * Cos (a), l * Sin (a));
}

_vector *V_PP_new (_point * A, _point * B)
{
    return V_NN_new (B->x - A->x, B->y - A->y);
}

_vector *V_L_new (_line * l)
{
    return V_NN_new (Cos (l->angle), Sin (l->angle));
}

_vector *V_S_new (_segment * s)
{
    return V_NN_new (s->x2 - s->x1, s->y2 - s->y1);
}

_vector *V_rotation (_vector * u, double a)
{
    return V_NN_new (Cos (a) * u->x - Sin (a) * u->y,
		     Sin (a) * u->x + Cos (a) * u->y);
}

_vector *V_add (_vector * u, _vector * v)
{
    return V_NN_new (u->x + v->x, u->y + v->y);
}

_vector *V_sub (_vector * u, _vector * v)
{
    return V_NN_new (u->x - v->x, u->y - v->y);
}

_vector *V_mult (double k, _vector * u)
{
    return V_NN_new (k * u->x, k * u->y);
}

void L_assignment (symrec * s, _line * l)
{
    s->type = LINE;
    s->object.line = l;
}

_line *L_NNA_new (double x, double y, double a)
{
    _line *ptr;
    ptr = (_line *) (malloc (sizeof (_line)));
    ptr->x = x;
    ptr->y = y;
    ptr->angle = a;
    save ((void *) ptr);
    return ptr;
}

_line *L_PA_new (_point * A, double a)
{
    return L_NNA_new (A->x, A->y, a);
}

_line *L_PP_new (_point * A, _point * B)
{
    return L_PA_new (A, angle (B->x - A->x, B->y - A->y));
}

_line *L_PV_new (_point * A, _vector * v)
{
    return L_PA_new (A, V_angle (v));
}

_line *L_S_new (_segment * s)
{
    return L_NNA_new (s->x1, s->y1, S_angle (s));
}

_line *L_CA_new (_circle * c, double a)
{
    return L_NNA_new (c->x + c->radius * Cos (a), c->y + c->radius * Sin (a),
		      format (a + 90));
}

_line *L_Co_new (_conic * C, double a)
{
    double c0, s0, A, B, x, y, d;

    c0 = Cos (C->d);
    s0 = Sin (C->d);
    A = C->a;
    B = C->b;

    switch (C->kind)
      {
      case PARABOLA:
	  x = a;
	  y = 2 * A * a * a;
	  d = angle (1, 4 * A * a);
	  break;
      case ELLIPSE:
	  x = A * cos (a);
	  y = B * sin (a);
	  d = angle (-A * sin (a), B * cos (a));
	  break;
      case HYPERBOLA:
	  if (ZERO (cos (a)))
	      x = y = 0;
	  else
	    {
		x = -A / cos (a);
		y = B * tan (a);
	    }
	  d = angle (-A * sin (a), B);
      }
    return L_NNA_new (C->x + c0 * x - s0 * y, C->y + s0 * x + c0 * y,
		      C->d + d);
}

_line *L_translation (_line * l, _vector * v)
{
    return L_NNA_new (l->x + v->x, l->y + v->y, l->angle);
}

_line *L_reflection (_line * l, _line * l1)
{
    _point *tmp1, *tmp2;

    tmp1 = P_new (l->x, l->y);
    tmp2 = P_reflection (tmp1, l1);
    return L_PA_new (tmp2, format (2 * l1->angle - l->angle));
}

_line *L_rotation (_line * l, _point * O, double a)
{
    _point *tmp1, *tmp2;

    tmp1 = P_new (l->x, l->y);
    tmp2 = P_rotation (tmp1, O, a);
    return L_PA_new (tmp2, format (l->angle + a));
}

_line *L_homothecy (_line * l, _point * O, double k)
{
    _point *tmp1, *tmp2;

    tmp1 = P_new (l->x, l->y);
    tmp2 = P_homothecy (tmp1, O, k);
    return L_PA_new (tmp2, l->angle);
}

_line *L_L_parallel (_line * l, _point * A)
{
    return L_PA_new (A, l->angle);
}

_line *L_S_parallel (_segment * s, _point * A)
{
    return L_PA_new (A, S_angle (s));
}

_line *L_L_perpendicular (_line * l, _point * A)
{
    return L_PA_new (A, format (l->angle + 90));
}

_line *L_S_perpendicular (_segment * s, _point * A)
{
    return L_PA_new (A, format (S_angle (s) + 90));
}

_line *L_S_bisector (_segment * s)
{
    return L_NNA_new ((s->x1 + s->x2) / 2, (s->y1 + s->y2) / 2,
		      format (S_angle (s) + 90));
}

_line *L_P_bisector (_point * A, _point * B, _point * C)
{
    _line *ptr;
    ptr = L_PP_new (B, A);
    ptr->angle = format (ptr->angle + T_angle (A, B, C) / 2);
    return ptr;
}

_line *L_LL_bisector (_line * l1, _line * l2)
{
    _point *A;
    double a;

    if (ZERO (format (l1->angle - l2->angle)))
	return L_NNA_new ((l1->x + l2->x) / 2, (l1->y + l2->y) / 2,
			  l1->angle);
    A = P_LL_intersection (l1, l2);
    if (abs (format (l1->angle - l2->angle)) > 90)
	a = format ((l1->angle + l2->angle) / 2 + 90);
    else
	a = format ((l1->angle + l2->angle) / 2);
    return L_PA_new (A, a);
}

_line *L_altitude (_point * A, _point * B, _point * C)
{
    return L_PA_new (A, format (angle (B->x - C->x, B->y - C->y) + 90));
}

_line *L_median (_point * A, _point * B, _point * C)
{
    _point *tmp;
    tmp = P_2_barycenter (B, 1, C, 1);
    return L_PP_new (A, tmp);
}

_line *L_invert (_line * l)
{
    return L_NNA_new (l->x, l->y, format (l->angle - 180));
}

void S_assignment (symrec * s, _segment * sg)
{
    s->type = SEGMENT;
    s->object.segment = sg;
}

_segment *S_N_new (double x1, double y1, double x2, double y2)
{
    _segment *ptr;
    ptr = (_segment *) (malloc (sizeof (_segment)));
    ptr->x1 = x1;
    ptr->y1 = y1;
    ptr->x2 = x2;
    ptr->y2 = y2;
    save ((void *) ptr);
    return ptr;
}

_segment *S_P_new (_point * A, _point * B)
{
    return S_N_new (A->x, A->y, B->x, B->y);
}

_segment *S_V_new (_point * A, _vector * v)
{
    return S_N_new (A->x, A->y, A->x + v->x, A->y + v->y);
}

_segment *S_NA_new (_point * A, double l, double a)
{
    return S_N_new (A->x, A->y, A->x + l * Cos (a), A->y + l * Sin (a));
}

_segment *S_CA_new (_circle * c, double a)
{
    return S_N_new (c->x, c->y, c->x + c->radius * Cos (a),
		    c->y + c->radius * Sin (a));
}

_segment *S_translation (_segment * s, _vector * v)
{
    return S_N_new (s->x1 + v->x, s->y1 + v->y, s->x2 + v->x, s->y2 + v->y);
}

_segment *S_reflection (_segment * s, _line * l)
{
    _point *tmp1, *tmp2, *tmp3, *tmp4;

    tmp1 = P_new (s->x1, s->y1);
    tmp2 = P_new (s->x2, s->y2);
    tmp3 = P_reflection (tmp1, l);
    tmp4 = P_reflection (tmp2, l);
    return S_P_new (tmp3, tmp4);
}

_segment *S_rotation (_segment * s, _point * O, double a)
{
    _point *tmp1, *tmp2, *tmp3, *tmp4;

    tmp1 = P_new (s->x1, s->y1);
    tmp2 = P_new (s->x2, s->y2);
    tmp3 = P_rotation (tmp1, O, a);
    tmp4 = P_rotation (tmp2, O, a);
    return S_P_new (tmp3, tmp4);
}

_segment *S_homothecy (_segment * s, _point * O, double k)
{
    _point *tmp1, *tmp2, *tmp3, *tmp4;

    tmp1 = P_new (s->x1, s->y1);
    tmp2 = P_new (s->x2, s->y2);
    tmp3 = P_homothecy (tmp1, O, k);
    tmp4 = P_homothecy (tmp2, O, k);
    return S_P_new (tmp3, tmp4);
}

_segment *S_invert (_segment * s)
{
    return S_N_new (s->x2, s->y2, s->x1, s->y1);
}

void C_assignment (symrec * s, _circle * c)
{
    s->type = CIRCLE;
    s->object.circle = c;
}

_circle *C_NNN_new (double x, double y, double r)
{
    _circle *ptr;
    ptr = (_circle *) (malloc (sizeof (_circle)));
    ptr->x = x;
    ptr->y = y;
    ptr->radius = r;
    save ((void *) ptr);
    return ptr;
}

_circle *C_PP_new (_point * A, _point * B)
{
    return C_NNN_new ((A->x + B->x) / 2, (A->y + B->y) / 2,
		      PP_distance (A, B) / 2);
}

_circle *C_PPP_new (_point * A, _point * B, _point * C)
{
    _line *tmp1, *tmp2;
    _point *tmp3;

    tmp1 =
	L_NNA_new ((A->x + B->x) / 2, (A->y + B->y) / 2,
		   format (angle (A->x - B->x, A->y - B->y) + 90));
    tmp2 =
	L_NNA_new ((C->x + B->x) / 2, (C->y + B->y) / 2,
		   format (angle (C->x - B->x, C->y - B->y) + 90));
    tmp3 = P_LL_intersection (tmp1, tmp2);
    return C_NNN_new (tmp3->x, tmp3->y,
		      LENG (A->x - tmp3->x, A->y - tmp3->y));
}

_circle *C_PN_new (_point * A, double r)
{
    return C_NNN_new (A->x, A->y, r);
}

_circle *C_translation (_circle * c, _vector * v)
{
    return C_NNN_new (c->x + v->x, c->y + v->y, c->radius);
}

_circle *C_reflection (_circle * c, _line * l)
{
    _point *tmp1, *tmp2;

    tmp1 = P_new (c->x, c->y);
    tmp2 = P_reflection (tmp1, l);
    return C_NNN_new (tmp2->x, tmp2->y, c->radius);
}

_circle *C_rotation (_circle * c, _point * O, double a)
{
    _point *tmp1, *tmp2;

    tmp1 = P_new (c->x, c->y);
    tmp2 = P_rotation (tmp1, O, a);
    return C_NNN_new (tmp2->x, tmp2->y, c->radius);
}

_circle *C_homothecy (_circle * c, _point * O, double k)
{
    _point *tmp1, *tmp2;

    tmp1 = P_new (c->x, c->y);
    tmp2 = P_homothecy (tmp1, O, k);
    return C_NNN_new (tmp2->x, tmp2->y, k * c->radius);
}

_circle *C_incircle (_point * A, _point * B, _point * C)
{
    _line *tmp1, *tmp2;
    _point *tmp3;
    double a, b, c;

    tmp1 = L_P_bisector (A, B, C);
    tmp2 = L_P_bisector (B, C, A);
    tmp3 = P_LL_intersection (tmp1, tmp2);
    a = LENG (B->x - C->x, B->y - C->y);
    b = LENG (A->x - C->x, A->y - C->y);
    c = LENG (B->x - A->x, B->y - A->y);
    return C_NNN_new (tmp3->x, tmp3->y,
		      sqrt ((a + b - c) * (a + c - b) * (b + c - a)
			    / (a + b + c)) / 2);
}


void Co_assignment (symrec * s, _conic * C)
{
    s->type = CONIC;
    s->object.conic = C;
}

_conic *Co_N_new (double x, double y, double d, double a, double b, int k)
{
    _conic *ptr;
    ptr = (_conic *) (malloc (sizeof (_conic)));
    ptr->x = x;
    ptr->y = y;
    ptr->d = d;
    ptr->a = a;
    ptr->b = b;
    ptr->kind = k;
    save ((void *) ptr);
    return ptr;
}

_conic *Pa_new (_point * A, _line * l)
{
    _point *tmp1;
    _vector *tmp2;
    if (ZERO (PL_distance (A, l)))
	yyerror ("undefined conic curve.");
    tmp1 = P_L_projection (A, l);
    tmp2 = V_PP_new (tmp1, A);
    return Co_N_new ((A->x + tmp1->x) / 2, (A->y + tmp1->y) / 2,
		     format (V_angle (tmp2) - 90), .25 / V_length (tmp2), 0,
		     PARABOLA);
}

_conic *Co_new (_point * A, _line * l, double e)
{
    _point *tmp1;
    _vector *tmp2;
    double d, f, g;

    if (ZERO (PL_distance (A, l)))
	yyerror ("undefined conic curve.");
    if (ZERO (fabs (e) - 1))
	return Pa_new (A, l);
    tmp1 = P_L_projection (A, l);
    tmp2 = V_PP_new (A, tmp1);
    d = V_angle (tmp2);
    g = V_length (tmp2);
    f = e * e / (1 - e * e);

    return Co_N_new (A->x - Cos (d) * f * g, A->y - Sin (d) * f * g, d,
		     sqrt (f * g * g / (1 - e * e)), sqrt (fabs (f * g * g)),
		     (fabs (e) < 1) ? ELLIPSE : HYPERBOLA);
}

_conic *Co_PP_new (_point * A, _point * B, double a)
{
    _segment *tmp1;
    _point *tmp2;
    double b, c;
    int k;

    tmp1 = S_P_new (A, B);
    tmp2 = P_midpoint (tmp1);
    c = S_length (tmp1) / 2;

    if (ZERO (a))
	return Co_N_new (0, 0, 0, 0, 0, -1);
    if (c < fabs (a))
      {
	  b = sqrt (a * a - c * c);
	  k = ELLIPSE;
      }
    else
      {
	  b = sqrt (c * c - a * a);
	  k = HYPERBOLA;
      }
    return Co_N_new (tmp2->x, tmp2->y, S_angle (tmp1), fabs (a), b, k);
}

_conic *Co_translation (_conic * C, _vector * v)
{
    return Co_N_new (C->x + v->x, C->y + v->y, C->d, C->a, C->b, C->kind);
}

_conic *Co_reflection (_conic * C, _line * l)
{
    _point *tmp1, *tmp2;

    tmp1 = P_new (C->x, C->y);
    tmp2 = P_reflection (tmp1, l);
    return Co_N_new (tmp2->x, tmp2->y, format (2 * l->angle - C->d), C->a,
		     C->b, C->kind);
}

_conic *Co_rotation (_conic * C, _point * O, double a)
{
    _point *tmp1, *tmp2;

    tmp1 = P_new (C->x, C->y);
    tmp2 = P_rotation (tmp1, O, a);
    return Co_N_new (tmp2->x, tmp2->y, format (a + C->d), C->a, C->b,
		     C->kind);
}

_conic *Co_homothecy (_conic * C, _point * O, double k)
{
    _point *tmp1, *tmp2;

    tmp1 = P_new (C->x, C->y);
    tmp2 = P_homothecy (tmp1, O, k);
    return Co_N_new (tmp2->x, tmp2->y, C->d, k * C->a, k * C->b, C->kind);
}


void LC_intersection (symrec * s1, symrec * s2, _line * l, _circle * c)
{
    double r, d, a, h, p;

    r = c->radius;
    d = LENG (c->x - l->x, c->y - l->y);
    a = format (angle (c->x - l->x, c->y - l->y) - l->angle);
    h = d * Sin (a);
    if (h > r)
	yyerror ("undefined points.");
    p = sqrt (r * r - h * h);
    P_assignment (s1, P_line (l, d * Cos (a) - p));
    P_assignment (s2, P_line (l, d * Cos (a) + p));
}

void CC_intersection (symrec * s1, symrec * s2, _circle * c1, _circle * c2)
{
    double d, r1, r2, h, p, a;

    d = LENG (c1->x - c2->x, c1->y - c2->y);
    r1 = c1->radius;
    r2 = c2->radius;
    if ((d > r1 + r2) || (d < fabs (r1 - r2)))
	yyerror ("undefined points.");
    h = sqrt ((r1 + r2 + d) * (r1 + d - r2) * (r1 + r2 - d) * (r2 + d - r1))
	     / (2 * d);
    p = sqrt (r1 * r1 - h * h) * (r2 * r2 > r1 * r1 + d * d ? -1 : 1);
    a = angle (c2->x - c1->x, c2->y - c1->y);
    P_assignment (s1,
		  P_new (c1->x + p * Cos (a) - h * Sin (a),
			 c1->y + p * Sin (a) + h * Cos (a)));
    P_assignment (s2,
		  P_new (c1->x + p * Cos (a) + h * Sin (a),
			 c1->y + p * Sin (a) - h * Cos (a)));
}

void LCo_intersection (symrec * s1, symrec * s2, _line * l, _conic * C)
{
    double c0, s0, dx, dy, a, b, c, A, B, a2, b2, c2, A2, B2, x1, y1, x2, y2;

    c0 = Cos (C->d);
    s0 = Sin (C->d);
    dx = l->x - C->x;
    dy = l->y - C->y;
    a = -Sin (l->angle - C->d);
    b = Cos (l->angle - C->d);
    c = (-c0 * dx - s0 * dy) * a + (s0 * dx - c0 * dy) * b;
    A = C->a;
    B = C->b;
    a2 = a * a;
    b2 = b * b;
    c2 = c * c;
    A2 = A * A;
    B2 = B * B;

    switch (C->kind)
      {
      case PARABOLA:
	  if (ZERO (b))
	    {
		x1 = x2 = -c / a;
		y1 = y2 = 2 * A * c2 / a2;
	    }
	  else
	    {
		if (Snd_degree (&x1, &x2, 2 * b * A, a, c))
		  {
		      y1 = -(a * x1 + c) / b;
		      y2 = -(a * x2 + c) / b;
		  }
		else
		    yyerror ("undefined points.");
	    }
	  break;
      case ELLIPSE:
	  if (ZERO (b))
	    {
		x1 = x2 = -c / a;
		y1 = B * sqrt (1 - x1 * x1 / A2);
		y2 = -y1;
	    }
	  else
	    {
		if (Snd_degree
		    (&x1, &x2, a2 * A2 + b2 * B2, 2 * a * c * A2,
		     A2 * (c2 - b2 * B2)))
		  {
		      y1 = -(a * x1 + c) / b;
		      y2 = -(a * x2 + c) / b;
		  }
		else
		    yyerror ("undefined points.");
	    }
	  break;
      case HYPERBOLA:
	  if (ZERO (b))
	    {
		x1 = x2 = -c / a;
		y1 = B * sqrt (x1 * x1 / A2 - 1);
		y2 = -y1;

	    }
	  else
	    {
		if (Snd_degree
		    (&x1, &x2, -a2 * A2 + b2 * B2, -2 * a * c * A2,
		     -A2 * (c2 + b2 * B2)))
		  {
		      y1 = -(a * x1 + c) / b;
		      y2 = -(a * x2 + c) / b;
		  }
		else
		    yyerror ("undefined points.");
	    }
      }
    P_assignment (s1,
		  P_new (C->x + c0 * x1 - s0 * y1, C->y + s0 * x1 + c0 * y1));
    P_assignment (s2,
		  P_new (C->x + c0 * x2 - s0 * y2, C->y + s0 * x2 + c0 * y2));
}

void vertices (symrec * s1, symrec * s2, _conic * C)
{
    _point *tmp;

    if (C->kind == PARABOLA)
      {
	  tmp = P_new (C->x, C->y);
	  P_assignment (s1, tmp);
	  P_assignment (s2, tmp);
      }
    else
      {
	  P_assignment (s1,
			P_new (C->x + C->a * Cos (C->d),
			       C->y + C->a * Sin (C->d)));
	  P_assignment (s2,
			P_new (C->x - C->a * Cos (C->d),
			       C->y - C->a * Sin (C->d)));
      }
}

void foci (symrec * s1, symrec * s2, _conic * C)
{
    _point *tmp;
    double c;

    if (C->kind == PARABOLA)
      {
	  tmp =
	      P_new (C->x - .5 * C->a * Sin (C->d),
		     C->y + .5 * C->a * Cos (C->d));
	  P_assignment (s1, tmp);
	  P_assignment (s2, tmp);
      }
    else
      {
	  if (C->kind == ELLIPSE)
	      c = sqrt (C->a * C->a - C->b * C->b);
	  else
	      c = sqrt (C->a * C->a + C->b * C->b);
	  P_assignment (s1,
			P_new (C->x + c * Cos (C->d), C->y + c * Sin (C->d)));
	  P_assignment (s2,
			P_new (C->x - c * Cos (C->d), C->y - c * Sin (C->d)));
      }
}

void
T_assignment (symrec * s, double x, double y, 
	      double h, double p, double l, double a)
{
    P_assignment (s,
		  P_new (x + l * (p * Cos (a) - h * Sin (a)),
			 y + l * (h * Cos (a) + p * Sin (a))));
}

void
T_assignments (symrec * s1, symrec * s2, double x, double y, double b,
	      double h, double p, double l, double a)
{
    P_assignment (s1, P_new (x + l * b * Cos (a), y + l * b * Sin (a)));
    P_assignment (s2,
		  P_new (x + l * (p * Cos (a) - h * Sin (a)),
			 y + l * (h * Cos (a) + p * Sin (a))));
}

void T_check (symrec * s1, symrec * s2)
{
    if (s1->type != POINT) {
	P_assignment (s1, P_new (0, 0));
	P_assignment (s2, P_new (6, 0));
    }
    if (s2->type != POINT)
	P_assignment (s2, P_new (s1->object.point->x+6, s1->object.point->y));
}
  
void T_scalenes (symrec * s1, symrec * s2, symrec * s3, double l, double a)
{
    if (s1->type != POINT)
	P_assignment (s1, P_new (0, 0));
    T_assignments (s2, s3, s1->object.point->x, s1->object.point->y, 1,
		  .61237244, .375, l, a);
}

void
T_3N_triangle (symrec * s1, symrec * s2, symrec * s3, double l1, double l2,
	      double l3, double a)
{
    double h, p;
    
    if (ZERO (l1) || (l1 > l2 + l3) || (l2 > l1 + l3) || (l3 > l1 + l2))
	yyerror ("invalid length.");
    if (s1->type != POINT)
	P_assignment (s1, P_new (0, 0));
    h = sqrt ((l1 + l2 + l3) * (l1 + l3 - l2) * (l1 + l2 - l3) * (l2 + l3 - l1))
	 / (2 * l1);
    p = sqrt (fabs(l3 * l3 - h * h))
	 * ((l2 * l2 > (l1 * l1 + l3 * l3)) ? -1 : 1);
    T_assignments (s2, s3, s1->object.point->x, s1->object.point->y, l1, h, p,
		  1, a);
}

void
T_2N_triangle (symrec * s1, symrec * s2, symrec * s3, double l2, double l3)
{
    double a, l1, h, p;
   
    T_check (s1, s2);
    l1 = PP_distance(s1->object.point,s2->object.point);
    if (ZERO (l1) || (l1 > l2 + l3) || (l2 > l1 + l3) || (l3 > l1 + l2))
	yyerror ("invalid length.");
    a = angle (s2->object.point->x-s1->object.point->x,
	       s2->object.point->y-s1->object.point->y);
    h = sqrt ((l1 + l2 + l3) * (l1 + l3 - l2) * (l1 + l2 - l3) * (l2 + l3 - l1))
	 / (2 * l1);
    p = sqrt (fabs(l3 * l3 - h * h))
	 * ((l2 * l2 > (l1 * l1 + l3 * l3)) ? -1 : 1);
    T_assignment (s3, s1->object.point->x, s1->object.point->y, h, p, 1, a);
}

void
T_2A_triangle (symrec * s1, symrec * s2, symrec * s3, double l, double a1,
	      double a2, double a)
{
    _line *tmp1, *tmp2;

    if (ZERO (Sin (a1 + a2)))
	yyerror ("invalid angle.");
    if (s1->type != POINT)
	P_assignment (s1, P_new (0, 0));
    tmp1 = L_PA_new (s1->object.point, a1 + a);
    P_assignment (s2,
		  P_new (s1->object.point->x + l * Cos (a),
			 s1->object.point->y + l * Sin (a)));
    tmp2 = L_PA_new (s2->object.point, format (180 - a2 + a));
    P_assignment (s3, P_LL_intersection (tmp1, tmp2));
}

void
T_A_triangle (symrec * s1, symrec * s2, symrec * s3, double a1, double a2)
{
    _line *tmp1, *tmp2;
    
    double a;
    if (ZERO (Sin (a1 + a2)))
	yyerror ("invalid angle.");
    T_check (s1, s2);
    a = angle (s2->object.point->x-s1->object.point->x,
	       s2->object.point->y-s1->object.point->y);    
    tmp1 = L_PA_new (s1->object.point, a1 + a);
    tmp2 = L_PA_new (s2->object.point, format (180 - a2 + a));
    P_assignment (s3, P_LL_intersection (tmp1, tmp2));
}

void
T_2N_right (symrec * s1, symrec * s2, symrec * s3, double l1, double l2,
	    double a)
{
    if (s1->type != POINT)
	P_assignment (s1, P_new (0, 0));
    T_assignments (s2, s3, s1->object.point->x, s1->object.point->y, l1, l2, 0,
		  1, a);
}

void
T_N_right (symrec * s1, symrec * s2, symrec * s3, double l2)
{
    double a;
    T_check (s1, s2);
    a = angle (s2->object.point->x-s1->object.point->x,
	       s2->object.point->y-s1->object.point->y);
    T_assignment (s3, s1->object.point->x, s1->object.point->y, l2, 0,
		  1, a);
}

void
T_NA_isosceles (symrec * s1, symrec * s2, symrec * s3, double l1, double a1,
	       double a)
{
    if (ZERO (Cos (a1)))
	yyerror ("invalid angle.");
    else {
	if (s1->type != POINT)
	    P_assignment (s1, P_new (0, 0));
	T_assignments (s2, s3, s1->object.point->x, s1->object.point->y, l1,
		      l1 * Tan (a1) / 2, l1 / 2, 1, a);
    }
}

void
T_A_isosceles (symrec * s1, symrec * s2, symrec * s3, double a1)
{
    double a, l1;

    if (ZERO (Cos (a1)))
	yyerror ("invalid angle.");
    else {
	T_check (s1, s2);
	a = angle (s2->object.point->x-s1->object.point->x,
		   s2->object.point->y-s1->object.point->y);
	l1 = PP_distance(s1->object.point,s2->object.point);
	T_assignment (s3, s1->object.point->x, s1->object.point->y,
		      l1 * Tan (a1) / 2, l1 / 2, 1, a);
    }
}

void
T_2N_isosceles (symrec * s1, symrec * s2, symrec * s3, double l1, double l2,
	       double a)
{
    if (l1 > 2 * l2)
	yyerror ("invalid length.");
    else {
	if (s1->type != POINT)
	    P_assignment (s1, P_new (0, 0));
	T_assignments (s2, s3, s1->object.point->x, s1->object.point->y, l1,
		      sqrt (l2 * l2 - l1 * l1 / 4), l1 / 2, 1, a);
    }
}

void
T_N_isosceles (symrec * s1, symrec * s2, symrec * s3, double l2)
{
    double a, l1;

    if (l1 > 2 * l2)
	yyerror ("invalid length.");
    else {
	T_check (s1, s2);
	a = angle (s2->object.point->x-s1->object.point->x,
		   s2->object.point->y-s1->object.point->y);
	l1 = PP_distance(s1->object.point,s2->object.point);
	T_assignment (s3, s1->object.point->x, s1->object.point->y,
		      sqrt (l2 * l2 - l1 * l1 / 4), l1 / 2, 1, a);
    }
}

void T_E_equilateral (symrec * s1, symrec * s2, symrec * s3)
{
    double l, a;
    a = angle (s2->object.point->x-s1->object.point->x,
	       s2->object.point->y-s1->object.point->y);
    l = PP_distance(s1->object.point,s2->object.point);
    T_assignment (s3, s1->object.point->x, s1->object.point->y,
	      .866025, .5, l, a);
}

void T_equilateral (symrec * s1, symrec * s2, symrec * s3, double l, double a)
{
    if (s1->type != POINT)
	P_assignment (s1, P_new (0, 0));
    T_assignments (s2, s3, s1->object.point->x, s1->object.point->y, 1,
	      .866025, .5, l, a);
}

void
Q_E_parallelogram (symrec * s1, symrec * s2, symrec * s3, symrec * s4)
{
    if (s1->type != POINT) {
	P_assignment (s1, P_new (0, 0));
	P_assignment (s2, P_new (5, 0));
	P_assignment (s3, P_new (6.0352, 3.8637));
    }
    if (s2->type != POINT)
	P_assignment (s2, P_new (s1->object.point->x+5, s1->object.point->y));
    if (s2->type != POINT || s3->type != POINT)
	P_assignment (s3, s2->object.point);
    P_assignment (s4, P_new (
	s1->object.point->x + (s3->object.point->x - s2->object.point->x),
	s1->object.point->y + (s3->object.point->y - s2->object.point->y)));
}

void
Q_S_parallelogram (symrec * s1, symrec * s2, symrec * s3, symrec * s4,
		   double l2, double a1)
{
    double l1, a, xu, xv, yu, yv;
    if (s1->type != POINT) {
	P_assignment (s1, P_new (0, 0));
	P_assignment (s2, P_new (5, 0));
    }
    if (s2->type != POINT)
	P_assignment (s2, P_new (s1->object.point->x+5, s1->object.point->y));
    a = angle (s2->object.point->x-s1->object.point->x,
	       s2->object.point->y-s1->object.point->y);
    l1 = PP_distance(s1->object.point,s2->object.point);
    xu = l1 * Cos (a);
    xv = l2 * Cos (a + a1);
    yu = l1 * Sin (a);
    yv = l2 * Sin (a + a1);
    P_assignment (s3,
		  P_new (s1->object.point->x + xu + xv,
			 s1->object.point->y + yu + yv));
    P_assignment (s4,
		  P_new (s1->object.point->x + xv, s1->object.point->y + yv));
}

void
Q_parallelogram (symrec * s1, symrec * s2, symrec * s3, symrec * s4,
		 double l1, double l2, double a1, double a)
{
    double xu, xv, yu, yv;

    xu = l1 * Cos (a);
    xv = l2 * Cos (a + a1);
    yu = l1 * Sin (a);
    yv = l2 * Sin (a + a1);
    if (s1->type != POINT)
	P_assignment (s1, P_new (0, 0));
    P_assignment (s2,
		  P_new (s1->object.point->x + xu, s1->object.point->y + yu));
    P_assignment (s3,
		  P_new (s1->object.point->x + xu + xv,
			 s1->object.point->y + yu + yv));
    P_assignment (s4,
		  P_new (s1->object.point->x + xv, s1->object.point->y + yv));
}

void
Q_V_parallelogram (symrec * s1, symrec * s2, symrec * s3, symrec * s4,
		   _vector * u, _vector * v)
{
    if (s1->type != POINT)
	P_assignment (s1, P_new (0, 0));
    P_assignment (s2,
		  P_new (s1->object.point->x + u->x,
			 s1->object.point->y + u->y));
    P_assignment (s3,
		  P_new (s1->object.point->x + u->x + v->x,
			 s1->object.point->y + u->y + v->y));
    P_assignment (s4,
		  P_new (s1->object.point->x + v->x,
			 s1->object.point->y + v->y));
}

void
Q_E_rectangle (symrec * s1, symrec * s2, symrec * s3, symrec * s4)
{
    if (s1->type != POINT) {
	P_assignment (s1, P_new (0, 0));
	P_assignment (s2, P_new (6, 0));
	P_assignment (s3, P_new (6, 3.7082));
    }
    if (s2->type != POINT)
	P_assignment (s2, P_new (s1->object.point->x+6, s1->object.point->y));
    if (s3->type != POINT)
	P_assignment (s3, s2->object.point);
    P_assignment (s4, P_new (
	s1->object.point->x + (s3->object.point->x - s2->object.point->x),
	s1->object.point->y + (s3->object.point->y - s2->object.point->y)));
}

void
Q_S_rectangle (symrec * s1, symrec * s2, symrec * s3, symrec * s4,
		   double l2)
{
    double l1, a, xu, xv, yu, yv;
    if (s1->type != POINT) {
	P_assignment (s1, P_new (0, 0));
	P_assignment (s2, P_new (6, 0));
    }
    if (s2->type != POINT)
	P_assignment (s2, P_new (s1->object.point->x+6, s1->object.point->y));
    a = angle (s2->object.point->x-s1->object.point->x,
	       s2->object.point->y-s1->object.point->y);
    l1 = PP_distance(s1->object.point,s2->object.point);
    xu = l1 * Cos (a);
    xv = -l2 * Sin (a);
    yu = l1 * Sin (a);
    yv = l2 * Cos (a);
    P_assignment (s3,
		  P_new (s1->object.point->x + xu + xv,
			 s1->object.point->y + yu + yv));
    P_assignment (s4,
		  P_new (s1->object.point->x + xv, s1->object.point->y + yv));
}

void
Q_square (symrec * s1, symrec * s2, symrec * s3, symrec * s4)
{
    if (s1->type != POINT) {
	P_assignment (s1, P_new (0, 0));
	P_assignment (s2, P_new (4, 0));
    }
    if (s2->type != POINT)
	P_assignment (s2, P_new (s1->object.point->x+4, s1->object.point->y));
    Q_S_rectangle (s1, s2, s3, s4,
	    PP_distance(s1->object.point,s2->object.point));
}

void pentagon (symrec * s1, symrec * s2, symrec * s3, symrec * s4,
	       symrec * s5, _point * O, double r, double a)
{
    P_assignment (s1, P_new (O->x + r * Cos (a), O->y + r * Sin (a)));
    P_assignment (s2,
		  P_new (O->x + r * Cos (a + 72), O->y + r * Sin (a + 72)));
    P_assignment (s3,
		  P_new (O->x + r * Cos (a + 144), O->y + r * Sin (a + 144)));
    P_assignment (s4,
		  P_new (O->x + r * Cos (a + 216), O->y + r * Sin (a + 216)));
    P_assignment (s5,
		  P_new (O->x + r * Cos (a + 288), O->y + r * Sin (a + 288)));
}

void hexagon (symrec * s1, symrec * s2, symrec * s3, symrec * s4, symrec * s5,
	      symrec * s6, _point * O, double r, double a)
{
    P_assignment (s1, P_new (O->x + r * Cos (a), O->y + r * Sin (a)));
    P_assignment (s2,
		  P_new (O->x + r * Cos (a + 60), O->y + r * Sin (a + 60)));
    P_assignment (s3,
		  P_new (O->x + r * Cos (a + 120), O->y + r * Sin (a + 120)));
    P_assignment (s4,
		  P_new (O->x + r * Cos (a + 180), O->y + r * Sin (a + 180)));
    P_assignment (s5,
		  P_new (O->x + r * Cos (a + 240), O->y + r * Sin (a + 240)));
    P_assignment (s6,
		  P_new (O->x + r * Cos (a + 300), O->y + r * Sin (a + 300)));
}


syntax highlighted by Code2HTML, v. 0.9.1