#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_TRIGONOMETRY_H

#include <math.h>
#include <stdio.h>

#define  PI   3.14159265358979323846
#define  SPI  (PI/FT_ANGLE_PI)

/* the precision in 16.16 fixed-point checks. Expect between 2 and 5 */
/* noise LSB bits during operations, due to rounding errors..        */
#define  THRESHOLD  64

  static  error = 0;

  static void
  test_cos( void )
  {
    FT_Fixed  f1, f2;
    double    d1, d2;
    int       i;

    for ( i = 0; i < FT_ANGLE_2PI; i += 0x10000 )
    {
      f1 = FT_Cos(i);
      d1 = f1/65536.0;
      d2 = cos( i*SPI );
      f2 = (FT_Fixed)(d2*65536.0);

      if ( abs( f2-f1 ) > THRESHOLD )
      {
        error = 1;
        printf( "FT_Cos[%3d] = %.7f  cos[%3d] = %.7f\n",
                (i >> 16), f1/65536.0, (i >> 16), d2 );
      }
    }
  }



  static void
  test_sin( void )
  {
    FT_Fixed  f1, f2;
    double    d1, d2;
    int       i;

    for ( i = 0; i < FT_ANGLE_2PI; i += 0x10000 )
    {
      f1 = FT_Sin(i);
      d1 = f1/65536.0;
      d2 = sin( i*SPI );
      f2 = (FT_Fixed)(d2*65536.0);

      if ( abs( f2-f1 ) > THRESHOLD )
      {
        error = 1;
        printf( "FT_Sin[%3d] = %.7f  sin[%3d] = %.7f\n",
                (i >> 16), f1/65536.0, (i >> 16), d2 );
      }
    }
  }


  static void
  test_tan( void )
  {
    FT_Fixed  f1, f2;
    double    d1, d2;
    int       i;

    for ( i = 0; i < FT_ANGLE_PI2-0x2000000; i += 0x10000 )
    {
      f1 = FT_Tan(i);
      d1 = f1/65536.0;
      d2 = tan( i*SPI );
      f2 = (FT_Fixed)(d2*65536.0);

      if ( abs( f2-f1 ) > THRESHOLD )
      {
        error = 1;
        printf( "FT_Tan[%3d] = %.7f  tan[%3d] = %.7f\n",
                (i >> 16), f1/65536.0, (i >> 16), d2 );
      }
    }
  }


  static void
  test_atan2( void )
  {
    FT_Fixed  c2, s2;
    double    l, a, c1, s1;
    int       i, j;

    for ( i = 0; i < FT_ANGLE_2PI; i += 0x10000 )
    {
      l  = 5.0;
      a  = i*SPI;

      c1 = l * cos(a);
      s1 = l * sin(a);

      c2 = (FT_Fixed)(c1*65536.0);
      s2 = (FT_Fixed)(s1*65536.0);

      j  = FT_Atan2( c2, s2 );
      if ( j < 0 )
        j += FT_ANGLE_2PI;

      if ( abs( i - j ) > 1 )
      {
        printf( "FT_Atan2( %.7f, %.7f ) = %.5f, atan = %.5f\n",
                c2/65536.0, s2/65536.0, j/65536.0, i/65536.0 );
      }
    }
  }

  static void
  test_unit( void )
  {
    FT_Vector  v;
    double     a, c1, s1;
    FT_Fixed   c2, s2;
    int        i;

    for ( i = 0; i < FT_ANGLE_2PI; i += 0x10000 )
    {
      FT_Vector_Unit( &v, i );
      a  = ( i*SPI );
      c1 = cos(a);
      s1 = sin(a);
      c2 = (FT_Fixed)(c1*65536.0);
      s2 = (FT_Fixed)(s1*65536.0);

      if ( abs( v.x-c2 ) > THRESHOLD ||
           abs( v.y-s2 ) > THRESHOLD )
      {
        error = 1;
        printf( "FT_Vector_Unit[%3d] = ( %.7f, %.7f )  vec = ( %.7f, %.7f )\n",
                (i >> 16),
                v.x/65536.0, v.y/65536.0,
                c1, s1 );
      }
    }
  }


  static void
  test_length( void )
  {
    FT_Vector  v;
    FT_Fixed   l, l2;
    int        i;

    for ( i = 0; i < FT_ANGLE_2PI; i += 0x10000 )
    {
      l   = (FT_Fixed)(500.0*65536.0);
      v.x = (FT_Fixed)( l * cos( i*SPI ) );
      v.y = (FT_Fixed)( l * sin( i*SPI ) );
      l2  = FT_Vector_Length( &v );

      if ( abs( l2-l ) > THRESHOLD )
      {
        error = 1;
        printf( "FT_Length( %.7f, %.7f ) = %.5f, length = %.5f\n",
                v.x/65536.0, v.y/65536.0, l2/65536.0, l/65536.0 );
      }
    }
  }


  static void
  test_rotate( void )
  {
    FT_Fixed  c2, s2, c4, s4;
    FT_Vector v;
    double    l, ra, a, c1, s1, cra, sra, c3, s3;
    int       i, j, rotate;

    for ( rotate = 0; rotate < FT_ANGLE_2PI; rotate += 0x10000 )
    {
      ra  = rotate*SPI;
      cra = cos( ra );
      sra = sin( ra );

      for ( i = 0; i < FT_ANGLE_2PI; i += 0x10000 )
      {
        l  = 500.0;
        a  = i*SPI;

        c1 = l * cos(a);
        s1 = l * sin(a);

        v.x = c2 = (FT_Fixed)(c1*65536.0);
        v.y = s2 = (FT_Fixed)(s1*65536.0);

        FT_Vector_Rotate( &v, rotate );

        c3 = c1 * cra - s1 * sra;
        s3 = c1 * sra + s1 * cra;

        c4 = (FT_Fixed)(c3*65536.0);
        s4 = (FT_Fixed)(s3*65536.0);

        if ( abs( c4 - v.x ) > THRESHOLD ||
             abs( s4 - v.y ) > THRESHOLD )
        {
          error = 1;
          printf( "FT_Rotate( (%.7f,%.7f), %.5f ) = ( %.7f, %.7f ), rot = ( %.7f, %.7f )\n",
                  c1, s1, ra,
                  c2/65536.0, s2/65536.0,
                  c4/65536.0, s4/65536.0 );
        }
      }
    }
  }


  int main( void )
  {
    test_cos();
    test_sin();
    test_tan();
    test_atan2();
    test_unit();
    test_length();
    test_rotate();

    if (!error)
      printf( "trigonometry test ok !\n" );

    return !error;
  }
