#include <stdio.h>
#include "tests/malloc.h"

/* This is a Marie Celeste instruction.  Some IBM documents think it
   exists, others don't.  The same appears to be true for
   implementations - ppc970 doesn't think it exists, but POWER5
   does. */
double do_fre ( double x )
{
  double block[2];
  block[0] = x;
  __asm__ __volatile__(
     "lfd %%f1, 0(%0)\n\t"
     ".long 0xfc200830\n\t" /* == fre %%f1,%%f1 */
     "stfd %%f1, 8(%0)"
     : /*out*/
     : /*in*/ "b" (&block[0])
     : /*trash*/ "memory", "fr1"
  );
  return block[1];
}

double do_fres ( double x )
{
  double block[2];
  block[0] = x;
  __asm__ __volatile__(
     "lfd %%f1, 0(%0)\n\t"
     "fres %%f1,%%f1\n\t"
     "stfd %%f1, 8(%0)"
     : /*out*/
     : /*in*/ "b" (&block[0])
     : /*trash*/ "memory", "fr1"
     );
  return block[1];
}

double do_frsqrte ( double x )
{
  double block[2];
  block[0] = x;
  __asm__ __volatile__(
     "lfd %%f1, 0(%0)\n\t"
     "frsqrte %%f1,%%f1\n\t"
     "stfd %%f1, 8(%0)"
     : /*out*/
     : /*in*/ "b" (&block[0])
     : /*trash*/ "memory", "fr1"
     );
  return block[1];
}

/* Another Marie Celeste insn. */
double do_frsqrtes ( double x )
{
  double block[2];
  block[0] = x;
  __asm__ __volatile__(
     "lfd %%f1, 0(%0)\n\t"
     ".long 0xec200834\n\t" /* == frsqrtes %%f1,%%f1 */
     "stfd %%f1, 8(%0)"
     : /*out*/
     : /*in*/ "b" (&block[0])
     : /*trash*/ "memory", "fr1"
     );
  return block[1];
}

////////////////////////////////////////////////////////////

void do_one ( char* name, 
              double(*f)(double), 
              double* args, int nargs, 
              char* argfmt, char* resfmt )
{
  int i;
  double a, r;
  printf("\n");

  for (i = 0; i < nargs; i++) {
    a = args[i];
    r = f(a);
    printf("%s ", name);
    printf(argfmt, a);
    printf(" -> ");
    printf(resfmt, r);
    printf("\n");
  }
}

int main ( void )
{
  int nargs = 19;
  double* args = malloc(nargs * sizeof(double));
  args[0]  =  0.0;
  args[1]  =  1.0 / 0.0; // inf
  args[2]  = -args[1]; //  -inf
  args[3]  = args[2]/args[2]; // nan
  args[4]  = -args[3]; // -nan
  args[5]  = -5e100;
  args[6]  = -5e20;
  args[7]  = -501.0;
  args[8]  = -6.0;
  args[9]  = -1.01;
  args[10] = -2e-20;
  args[11] = -2e-200;
  args[12] =  2e-200;
  args[13] =  2e-20;
  args[14] =  1.01;
  args[15] =  6.0;
  args[16] =  501.0;
  args[17] =  5e20;
  args[18] =  5e100;

  do_one( "fre",  do_fre,  args, nargs, "%e", "%4.1e");
  do_one( "fres", do_fres, args, nargs, "%e", "%4.1e");

  do_one( "frsqrte",  do_frsqrte,  args, nargs, "%e", "%4.1e");
  do_one( "frsqrtes", do_frsqrtes, args, nargs, "%e", "%4.1e");

  free(args);
  return 0;
}