#include <stdio.h>
#include <stdint.h>
#include "dfp_utils.h"

/* Test various DFP ops:
   - extract biased exponent 64/128 bit
   - extract significance 64/128 bit
   - insert biased exponent 64/128 bit
   - load and test 64/128 bit
   - shift left/right 64/128 bit
   - reround 64/128 bit
*/

void eedtr(_Decimal64 in)
{
  long out;
  asm volatile(".insn rre, 0xb3e50000, %[out], %[in]\n\t"
               :[out] "=d" (out) :[in] "f" (in));
  printf("EEDTR ");
  DFP_VAL_PRINT(in, _Decimal64);
  printf(" -> %ld\n", out);
}

void eextr(_Decimal128 in)
{
  long out;
  asm volatile(".insn rre, 0xb3ed0000, %[out], %[in]\n\t"
               :[out] "=d" (out) :[in] "f" (in));
  printf("EEXTR ");
  DFP_VAL_PRINT(in, _Decimal128);
  printf(" -> %ld\n", out);
}

void esdtr(_Decimal64 in)
{
  long out;
  asm volatile(".insn rre, 0xb3e70000, %[out], %[in]\n\t"
               :[out] "=d" (out) :[in] "f" (in));
  printf("ESDTR ");
  DFP_VAL_PRINT(in, _Decimal64);
  printf(" -> %ld\n", out);
}

void esxtr(_Decimal128 in)
{
  long out;
  asm volatile(".insn rre, 0xb3ef0000, %[out], %[in]\n\t"
               :[out] "=d" (out) :[in] "f" (in));
  printf("ESXTR ");
  DFP_VAL_PRINT(in, _Decimal128);
  printf(" -> %ld\n", out);
}

void iedtr(_Decimal64 in, long amount)
{
  _Decimal64 out;

  asm volatile (".insn rrf, 0xb3f60000, %[out], %[amount], %[in], 0\n\t"
                :[out]"=f"(out)
                :[in]"f"(in), [amount]"d"(amount));

  printf("IEDTR ");
  DFP_VAL_PRINT(in, _Decimal64);
  printf(", %ld -> ", amount);
  DFP_VAL_PRINT(out, _Decimal64);
  printf("\n");
}

void iextr(_Decimal128 in, long amount)
{
  _Decimal128 out;

  asm volatile (".insn rrf, 0xb3fe0000, %[out], %[amount], %[in], 0\n\t"
                :[out]"=f"(out)
                :[in]"f"(in), [amount]"d"(amount));

  printf("IEXTR ");
  DFP_VAL_PRINT(in, _Decimal128);
  printf(", %ld -> ", amount);
  DFP_VAL_PRINT(out, _Decimal128);
  printf("\n");
}

void ltdtr(_Decimal64 in)
{
  _Decimal64 out;
  int cc;
  asm volatile(".insn rre, 0xb3d60000, %[out], %[in]\n\t"
               "ipm %1\n\t"
               "srl %1,28\n\t"
               :[out] "=d" (out), "=d" (cc)
               :[in] "f" (in));
  printf("LTDTR ");
  DFP_VAL_PRINT(in, _Decimal64);
  printf(" -> %d\n", cc);
}

void ltxtr(_Decimal128 in)
{
  _Decimal128 out;
  int cc;
  asm volatile(".insn rre, 0xb3de0000, %[out], %[in]\n\t"
               "ipm %1\n\t"
               "srl %1,28\n\t"
               :[out] "=f" (out), "=d" (cc)
               :[in] "f" (in));
  printf("LTXTR ");
  DFP_VAL_PRINT(in, _Decimal128);
  printf(" -> %d\n", cc);
}

void qadtr(_Decimal64 op, _Decimal64 quan, uint8_t rm)
{
  _Decimal64 out;

  asm volatile (
                ".insn rrf, 0xb3f50000, %[out], %[quan], %[op], %[rm]\n\t"
                :[out]"=f"(out)
                :[op]"f"(op), [quan]"f"(quan), [rm]"d"(rm)
                );
  printf("QADTR ");
  DFP_VAL_PRINT(op, _Decimal64);
  printf(", ");
  DFP_VAL_PRINT(quan, _Decimal64);
  printf(", %x -> ", rm);
  DFP_VAL_PRINT(out, _Decimal64);
  printf("\n");
}

void quantize64(_Decimal64 op, _Decimal64 quan)
{
  uint8_t i;

  for (i = 0; i < 16; i++)
    qadtr(op, quan, i);
}

void qaxtr(_Decimal128 op, _Decimal128 quan, uint8_t rm)
{
  _Decimal128 out;

  asm volatile (
                ".insn rrf, 0xb3fd0000, %[out], %[quan], %[op], %[rm]\n\t"
                :[out]"=f"(out)
                :[op]"f"(op), [quan]"f"(quan), [rm]"d"(rm)
                );
  printf("QAXTR ");
  DFP_VAL_PRINT(op, _Decimal128);
  printf(", ");
  DFP_VAL_PRINT(quan, _Decimal128);
  printf(", %x -> ", rm);
  DFP_VAL_PRINT(out, _Decimal128);
  printf("\n");
}

void quantize128(_Decimal128 op, _Decimal128 quan)
{
  uint8_t i;

  for (i = 0; i < 16; i++)
    qaxtr(op, quan, i);
}

void rrdtr(_Decimal64 op, uint8_t sig, uint8_t rm)
{
  _Decimal64 out;

  asm volatile (
                ".insn rrf, 0xb3f70000, %[out], %[sig], %[op], %[rm]\n\t"
                :[out]"=f"(out)
                :[op]"f"(op), [sig]"d"(sig), [rm]"d"(rm)
                );
  printf("RRDTR ");
  DFP_VAL_PRINT(op, _Decimal64);
  printf(", %d, %x -> ", sig, rm);
  DFP_VAL_PRINT(out, _Decimal64);
  printf("\n");
}

void reround64(_Decimal64 op, uint8_t sig)
{
  uint8_t i;

  for (i = 0; i < 16; i++)
    rrdtr(op, sig, i);
}

void rrxtr(_Decimal128 op, uint8_t sig, uint8_t rm)
{
  _Decimal128 out;

  asm volatile (
                ".insn rrf, 0xb3ff0000, %[out], %[sig], %[op], %[rm]\n\t"
                :[out]"=f"(out)
                :[op]"f"(op), [sig]"d"(sig), [rm]"d"(rm)
                );
  printf("RRXTR ");
  DFP_VAL_PRINT(op, _Decimal128);
  printf(", %d, %x -> ", sig, rm);
  DFP_VAL_PRINT(out, _Decimal128);
  printf("\n");
}

void reround128(_Decimal128 op, uint8_t sig)
{
  uint8_t i;

  for (i = 0; i < 16; i++)
    rrxtr(op, sig, i);
}

void sldt(_Decimal64 in, unsigned long amount)
{
  _Decimal64 out;
  int *shift = (int *) amount;

  asm volatile (".insn rxf, 0xed0000000040, %[out], %[in], 0(%[amount])\n\t"
                :[out]"=f"(out)
                :[in]"f"(in),[amount]"a"(shift));

  printf("SLDT ");
  DFP_VAL_PRINT(in, _Decimal64);
  printf(" -> ");
  DFP_VAL_PRINT(out, _Decimal64);
  printf("\n");
}

void slxt(_Decimal128 in, unsigned long amount)
{
  _Decimal128 out;
  int *shift = (int *) amount;

  asm volatile (".insn rxf, 0xed0000000048, %[out], %[in], 0(%[amount])\n\t"
                :[out]"=f"(out)
                :[in]"f"(in),[amount]"a"(shift));

  printf("SLXT ");
  DFP_VAL_PRINT(in, _Decimal128);
  printf(" -> ");
  DFP_VAL_PRINT(out, _Decimal128);
  printf("\n");
}

void srdt(_Decimal64 in, unsigned long amount)
{
  _Decimal64 out;
  int *shift = (int *) amount;

  asm volatile (".insn rxf, 0xed0000000041, %[out], %[in], 0(%[amount])\n\t"
                :[out]"=f"(out)
                :[in]"f"(in),[amount]"a"(shift));

  printf("SRDT ");
  DFP_VAL_PRINT(in, _Decimal64);
  printf(" -> ");
  DFP_VAL_PRINT(out, _Decimal64);
  printf("\n");
}

void srxt(_Decimal128 in, unsigned long amount)
{
  _Decimal128 out;
  int *shift = (int *) amount;

  asm volatile (".insn rxf, 0xed0000000049, %[out], %[in], 0(%[amount])\n\t"
                :[out]"=f"(out)
                :[in]"f"(in),[amount]"a"(shift));

  printf("SRXT ");
  DFP_VAL_PRINT(in, _Decimal128);
  printf(" -> ");
  DFP_VAL_PRINT(out, _Decimal128);
  printf("\n");
}

int main() {
  _Decimal64 d64 = 50.0005DD;
  _Decimal128 d128 = 50.0005DL;

  eedtr(d64);
  eedtr(-d64);
  eedtr(0.DD);
  eextr(d128);
  eextr(-d128);
  eextr(0.DL);

  esdtr(d64);
  esdtr(-d64);
  esdtr(0.DD);
  esxtr(d128);
  esxtr(-d128);
  esxtr(0.DL);

  ltdtr(d64);
  ltdtr(-d64);
  ltdtr(0.0DD);
  ltxtr(d128);
  ltxtr(-d128);
  ltxtr(0.0DL);

  d64 = 12345678.54321DD;
  sldt(d64, 10);
  sldt(-d64, 2);
  sldt(0.DD, 2);
  sldt(-0.DD, 2);

  srdt(d64, 5);
  srdt(-d64, 2);
  srdt(0.DD, 2);
  srdt(-0.DD, 2);

  d128 = 12345678.54321DL;
  slxt(d128, 10);
  slxt(-d128, 2);
  slxt(0.DL, 2);
  slxt(-0.DL, 2);

  srxt(d128, 10);
  srxt(-d128, 2);
  srxt(0.DL, 2);
  srxt(-0.DL, 2);

  d64 = 5.000005DD;
  iedtr(d64, 391);
  iedtr(d64, 392);
  iedtr(d64, 393);
  iedtr(-d64, 391);
  iedtr(-d64, 392);
  iedtr(-d64, 393);
  iedtr(0.DD, 393);
  iedtr(-0.DD, 393);
  iedtr(1.DD, 393);

  d128 = 5.000005DL;
  iextr(d128, 6169);
  iextr(d128, 6170);
  iextr(d128, 6171);
  iextr(-d128, 6169);
  iextr(-d128, 6170);
  iextr(-d128, 6171);
  iextr(0.DL, 6171);
  iextr(-0.DL, 6171);
  iextr(1.DL, 6171);

  d64 = 2.171234DD;
  quantize64(d64, 0.001DD);
  quantize64(-d64, 0.001DD);
  quantize64(-d64, 0.DD);
  quantize64(0.DD, 0.001DD);

  d128 = 26365343648.171234DL;
  quantize128(d128, 230.01DL);
  quantize128(-d128, 230.01DL);
  quantize128(d128, 0.DL);
  quantize128(-0.DL, 230.01DL);

  d64 = 2.174598DD;
  reround64(d64, 3);
  reround64(d64, 4);
  reround64(d64, 5);
  reround64(-d64, 3);
  reround64(-d64, 4);
  reround64(-d64, 5);
  reround64(0.DD, 0);

  d128 = 2.174598DL;
  reround128(d128, 3);
  reround128(d128, 4);
  reround128(d128, 5);
  reround128(-d128, 3);
  reround128(-d128, 4);
  reround128(-d128, 5);
  reround128(0.DL, 0);

  return 0;
}