/* This is a software fixed-point library.
   Copyright (C) 2007 Free Software Foundation, Inc.

This file is part of GCC.

GCC 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, or (at your option) any later
version.

In addition to the permissions in the GNU General Public License, the
Free Software Foundation gives you unlimited permission to link the
compiled version of this file into combinations with other programs,
and to distribute those combinations without any restriction coming
from the use of this file.  (The General Public License restrictions
do apply in other respects; for example, they cover modification of
the file, and distribution when not linked into a combine
executable.)

GCC 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 GCC; see the file COPYING.  If not, write to the Free
Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.  */

/* This implements fixed-point arithmetic.

   Contributed by Chao-ying Fu  <fu@mips.com>.  */

/* To use this file, we need to define one of the following:
   QQ_MODE, UQQ_MODE, HQ_MODE, UHQ_MODE, SQ_MODE, USQ_MODE, DQ_MODE, UDQ_MODE,
   TQ_MODE, UTQ_MODE, HA_MODE, UHA_MODE, SA_MODE, USA_MODE, DA_MODE, UDA_MODE,
   TA_MODE, UTA_MODE.
   Then, all operators for this machine mode will be created.

   Or, we need to define FROM_* TO_* for conversions from one mode to another
   mode.  The mode could be one of the following:
   Fract: QQ, UQQ, HQ, UHQ, SQ, USQ, DQ, UDQ, TQ, UTQ
   Accum: HA, UHA, SA, USA, DA, UDA, TA, UTA
   Signed integer: QI, HI, SI, DI, TI
   Unsigned integer: UQI, UHI, USI, UDI, UTI
   Floating-point: SF, DF
   Ex: If we define FROM_QQ and TO_SI, the conversion from QQ to SI is
   generated.  */

#include "tconfig.h"
#include "tsystem.h"
#include "coretypes.h"
#include "tm.h"

#ifndef MIN_UNITS_PER_WORD
#define MIN_UNITS_PER_WORD UNITS_PER_WORD
#endif

#include "config/fixed-bit.h"

#if defined(FIXED_ADD) && defined(L_add)
FIXED_C_TYPE
FIXED_ADD (FIXED_C_TYPE a, FIXED_C_TYPE b)
{
  FIXED_C_TYPE c;
  INT_C_TYPE x, y, z;
  memcpy (&x, &a, FIXED_SIZE);
  memcpy (&y, &b, FIXED_SIZE);
  z = x + y;
#if HAVE_PADDING_BITS
  z = z << PADDING_BITS;
  z = z >> PADDING_BITS;
#endif
  memcpy (&c, &z, FIXED_SIZE);
  return c;
}
#endif /* FIXED_ADD */

#if defined(FIXED_SSADD) && defined(L_ssadd)
FIXED_C_TYPE
FIXED_SSADD (FIXED_C_TYPE a, FIXED_C_TYPE b)
{
  FIXED_C_TYPE c;
  INT_C_TYPE x, y, z;
  memcpy (&x, &a, FIXED_SIZE);
  memcpy (&y, &b, FIXED_SIZE);
  z = x + y;
  if ((((x ^ y) >> I_F_BITS) & 1) == 0)
    {
      if (((z ^ x) >> I_F_BITS) & 1)
        {
          z = 1;
          z = z << I_F_BITS;
          if (x >= 0)
            z--;
        }
    }
#if HAVE_PADDING_BITS
  z = z << PADDING_BITS;
  z = z >> PADDING_BITS;
#endif
  memcpy (&c, &z, FIXED_SIZE);
  return c;
}
#endif /* FIXED_SSADD */

#if defined(FIXED_USADD) && defined(L_usadd)
FIXED_C_TYPE
FIXED_USADD (FIXED_C_TYPE a, FIXED_C_TYPE b)
{
  FIXED_C_TYPE c;
  INT_C_TYPE x, y, z;
  memcpy (&x, &a, FIXED_SIZE);
  memcpy (&y, &b, FIXED_SIZE);
  z = x + y;
#if HAVE_PADDING_BITS
  z = z << PADDING_BITS;
  z = z >> PADDING_BITS;
#endif
  if (z < x || z < y) /* max */
    {
       z = -1;
#if HAVE_PADDING_BITS
       z = z << PADDING_BITS;
       z = z >> PADDING_BITS;
#endif
    }
  memcpy (&c, &z, FIXED_SIZE);
  return c;
}
#endif /* FIXED_USADD */

#if defined(FIXED_SUB) && defined(L_sub)
FIXED_C_TYPE
FIXED_SUB (FIXED_C_TYPE a, FIXED_C_TYPE b)
{
  FIXED_C_TYPE c;
  INT_C_TYPE x, y, z;
  memcpy (&x, &a, FIXED_SIZE);
  memcpy (&y, &b, FIXED_SIZE);
  z = x - y;
#if HAVE_PADDING_BITS
  z = z << PADDING_BITS;
  z = z >> PADDING_BITS;
#endif
  memcpy (&c, &z, FIXED_SIZE);
  return c;
}
#endif /* FIXED_SUB */

#if defined(FIXED_SSSUB) && defined(L_sssub)
FIXED_C_TYPE
FIXED_SSSUB (FIXED_C_TYPE a, FIXED_C_TYPE b)
{
  FIXED_C_TYPE c;
  INT_C_TYPE x, y, z;
  memcpy (&x, &a, FIXED_SIZE);
  memcpy (&y, &b, FIXED_SIZE);
  z = x - y;
  if (((x ^ y) >> I_F_BITS) & 1)
    {
      if (((z ^ x) >> I_F_BITS) & 1)
        {
          z = 1;
          z = z << I_F_BITS;
          if (x >= 0)
            z--;
        }
    }
#if HAVE_PADDING_BITS
  z = z << PADDING_BITS;
  z = z >> PADDING_BITS;
#endif
  memcpy (&c, &z, FIXED_SIZE);
  return c;
}
#endif /* FIXED_SSSUB */

#if defined(FIXED_USSUB) && defined(L_ussub)
FIXED_C_TYPE
FIXED_USSUB (FIXED_C_TYPE a, FIXED_C_TYPE b)
{
  FIXED_C_TYPE c;
  INT_C_TYPE x, y, z;
  memcpy (&x, &a, FIXED_SIZE);
  memcpy (&y, &b, FIXED_SIZE);
  z = x - y;
  if (x < y)
    z = 0;
#if HAVE_PADDING_BITS
  z = z << PADDING_BITS;
  z = z >> PADDING_BITS;
#endif
  memcpy (&c, &z, FIXED_SIZE);
  return c;
}
#endif /* FIXED_USSUB */

#if defined(FIXED_SATURATE1) && defined(L_saturate1)
void
FIXED_SATURATE1 (DINT_C_TYPE *a)
{
  DINT_C_TYPE max, min;
  max = (DINT_C_TYPE)1 << I_F_BITS;
  max = max - 1;
#if MODE_UNSIGNED == 0
  min = (DINT_C_TYPE)1 << (2 * FIXED_WIDTH - 1);
  min = min >> (2 * FIXED_WIDTH - 1 - I_F_BITS);
#else
  min = 0;
#endif
  if (*a > max)
    *a = max;
  else if (*a < min)
    *a = min;
}
#endif /* FIXED_SATURATE1 */

#if defined(FIXED_SATURATE2) && defined(L_saturate2)
void
FIXED_SATURATE2 (INT_C_TYPE *high, INT_C_TYPE *low)
{
  INT_C_TYPE r_max, s_max, r_min, s_min;
  r_max = 0;
#if (MODE_UNSIGNED == 0) || HAVE_PADDING_BITS
  s_max = (INT_C_TYPE)1 << I_F_BITS;
  s_max = s_max - 1;
#else
  s_max = -1;
#endif
#if MODE_UNSIGNED == 0
  r_min = -1;
  s_min = (INT_C_TYPE)1 << (FIXED_WIDTH - 1);
  s_min = s_min >> (FIXED_WIDTH - 1 - I_F_BITS);
#else
  r_min = 0;
  s_min = 0;
#endif

  if (*high > r_max
      || (*high == r_max && (UINT_C_TYPE)(*low) > (UINT_C_TYPE)s_max))
    {
      *high = r_max;
      *low = s_max;
    }
  else if (*high < r_min ||
	   (*high == r_min && (UINT_C_TYPE)(*low) < (UINT_C_TYPE)s_min))
    {
      *high = r_min;
      *low = s_min;
    }
}
#endif /* FIXED_SATURATE2 */

#if defined(FIXED_MULHELPER) && defined(L_mulhelper)
FIXED_C_TYPE
FIXED_MULHELPER (FIXED_C_TYPE a, FIXED_C_TYPE b, word_type satp)
{
  FIXED_C_TYPE c;
  INT_C_TYPE x, y;

#if defined (DINT_C_TYPE)
  INT_C_TYPE z;
  DINT_C_TYPE dx, dy, dz;
  memcpy (&x, &a, FIXED_SIZE);
  memcpy (&y, &b, FIXED_SIZE);
  dx = (DINT_C_TYPE) x;
  dy = (DINT_C_TYPE) y;
  dz = dx * dy;
  /* Round the result by adding (1 << (FBITS -1)).  */
  dz += ((DINT_C_TYPE) 1 << (FBITS - 1));
  dz = dz >> FBITS;
  if (satp)
    FIXED_SATURATE1 (&dz);

  z = (INT_C_TYPE) dz;
#if HAVE_PADDING_BITS
  z = z << PADDING_BITS;
  z = z >> PADDING_BITS;
#endif
  memcpy (&c, &z, FIXED_SIZE);
  return c;

#else /* No DINT_C_TYPE */
  /* The result of multiplication expands to two INT_C_TYPE.  */
  INTunion aa, bb;
  INTunion a_high, a_low, b_high, b_low;
  INTunion high_high, high_low, low_high, low_low;
  INTunion r, s, temp1, temp2;
  INT_C_TYPE carry = 0;
  INT_C_TYPE z;

  memcpy (&x, &a, FIXED_SIZE);
  memcpy (&y, &b, FIXED_SIZE);

  /* Decompose a and b.  */
  aa.ll = x;
  bb.ll = y;

  a_high.s.low = aa.s.high;
  a_high.s.high = 0;
  a_low.s.low = aa.s.low;
  a_low.s.high = 0;
  b_high.s.low = bb.s.high;
  b_high.s.high = 0;
  b_low.s.low = bb.s.low;
  b_low.s.high = 0;

  /* Perform four multiplications.  */
  low_low.ll = a_low.ll * b_low.ll;
  low_high.ll = a_low.ll * b_high.ll;
  high_low.ll = a_high.ll * b_low.ll;
  high_high.ll = a_high.ll * b_high.ll;

  /* Accumulate four results to {r, s}.  */
  temp1.s.high = high_low.s.low;
  temp1.s.low = 0;
  s.ll = low_low.ll + temp1.ll;
  if ((UINT_C_TYPE) s.ll < (UINT_C_TYPE) low_low.ll
      || (UINT_C_TYPE) s.ll < (UINT_C_TYPE) temp1.ll)
    carry ++; /* Carry.  */
  temp1.ll = s.ll;
  temp2.s.high = low_high.s.low;
  temp2.s.low = 0;
  s.ll = temp1.ll + temp2.ll;
  if ((UINT_C_TYPE) s.ll < (UINT_C_TYPE) temp1.ll
      || (UINT_C_TYPE) s.ll < (UINT_C_TYPE) temp2.ll)
    carry ++; /* Carry.  */

  temp1.s.low = high_low.s.high;
  temp1.s.high = 0;
  r.ll = high_high.ll + temp1.ll;
  temp1.s.low = low_high.s.high;
  temp1.s.high = 0;
  r.ll = r.ll + temp1.ll + carry;

#if MODE_UNSIGNED == 0
  /* For signed types, we need to add neg(y) to r, if x < 0.  */
  if (x < 0)
    r.ll = r.ll - y;
  /* We need to add neg(x) to r, if y < 0.  */
  if (y < 0)
    r.ll = r.ll - x;
#endif

  /* Round the result by adding (1 << (FBITS -1)).  */
  temp1.ll = s.ll;
  s.ll += ((INT_C_TYPE) 1 << (FBITS -1));
  if ((UINT_C_TYPE) s.ll < (UINT_C_TYPE) temp1.ll
      || (UINT_C_TYPE) s.ll < (UINT_C_TYPE) ((INT_C_TYPE) 1 << (FBITS -1)))
    r.ll += 1;

  /* Shift right the result by FBITS.  */
#if FBITS == FIXED_WIDTH
  /* This happens only for unsigned types without any padding bits.
     So, it is safe to set r.ll to 0 as it is logically shifted right.  */
  s.ll = r.ll;
  r.ll = 0;
#else
  s.ll = ((UINT_C_TYPE)s.ll) >> FBITS;
  temp1.ll = r.ll << (FIXED_WIDTH - FBITS);
  s.ll = s.ll | temp1.ll;
  r.ll = r.ll >> FBITS;
#endif

  if (satp)
    FIXED_SATURATE2 (&r.ll, &s.ll);

  z = (INT_C_TYPE) s.ll;
#if HAVE_PADDING_BITS
  z = z << PADDING_BITS;
  z = z >> PADDING_BITS;
#endif
  memcpy (&c, &z, FIXED_SIZE);
  return c;
#endif
}
#endif /* FIXED_MULHELPER */

#if defined(FIXED_MUL) && defined(L_mul)
FIXED_C_TYPE
FIXED_MUL (FIXED_C_TYPE a, FIXED_C_TYPE b)
{
  return FIXED_MULHELPER (a, b, 0);
}
#endif /* FIXED_MUL */

#if defined(FIXED_SSMUL) && defined(L_ssmul)
FIXED_C_TYPE
FIXED_SSMUL (FIXED_C_TYPE a, FIXED_C_TYPE b)
{
  return FIXED_MULHELPER (a, b, 1);
}
#endif /* FIXED_SSMUL */

#if defined(FIXED_USMUL) && defined(L_usmul)
FIXED_C_TYPE
FIXED_USMUL (FIXED_C_TYPE a, FIXED_C_TYPE b)
{
  return FIXED_MULHELPER (a, b, 1);
}
#endif /* FIXED_USMUL */

#if defined(FIXED_DIVHELPER) && defined(L_divhelper)
FIXED_C_TYPE
FIXED_DIVHELPER (FIXED_C_TYPE a, FIXED_C_TYPE b, word_type satp)
{
  FIXED_C_TYPE c;
  INT_C_TYPE x, y;
  INT_C_TYPE z;

#if defined (DINT_C_TYPE)
  DINT_C_TYPE dx, dy, dz;
  memcpy (&x, &a, FIXED_SIZE);
  memcpy (&y, &b, FIXED_SIZE);
  dx = (DINT_C_TYPE) x;
  dy = (DINT_C_TYPE) y;
  dx = dx << FBITS;
  dz = dx / dy;
  if (satp)
    FIXED_SATURATE1 (&dz);
  z = (INT_C_TYPE) dz;
#if HAVE_PADDING_BITS
  z = z << PADDING_BITS;
  z = z >> PADDING_BITS;
#endif
  memcpy (&c, &z, FIXED_SIZE);
  return c;

#else /* No DINT_C_TYPE */
  INT_C_TYPE pos_a, pos_b, r, s;
  INT_C_TYPE quo_r, quo_s, mod, temp;
  word_type i;
#if MODE_UNSIGNED == 0
  word_type num_of_neg = 0;
#endif

  memcpy (&x, &a, FIXED_SIZE);
  memcpy (&y, &b, FIXED_SIZE);
  pos_a = x;
  pos_b = y;

#if MODE_UNSIGNED == 0
  /* If a < 0, negate a.  */
  if (pos_a < 0)
    {
      pos_a = -pos_a;
      num_of_neg ++;
    }
  /* If b < 0, negate b.  */
  if (pos_b < 0)
    {
      pos_b = -pos_b;
      num_of_neg ++;
    }
#endif

  /* Left shift pos_a to {r, s} by FBITS.  */
#if FBITS == FIXED_WIDTH
  /* This happens only for unsigned types without any padding bits.  */
  r = pos_a;
  s = 0;
#else
  s = pos_a << FBITS;
  r = pos_a >> (FIXED_WIDTH - FBITS);
#endif

  /* Unsigned divide r by pos_b to quo_r.  The remainder is in mod.  */
  quo_r = (UINT_C_TYPE)r / (UINT_C_TYPE)pos_b;
  mod = (UINT_C_TYPE)r % (UINT_C_TYPE)pos_b;
  quo_s = 0;

  for (i = 0; i < FIXED_WIDTH; i++)
    {
      /* Record the leftmost bit of mod.  */
      word_type leftmost_mode = (mod >> (FIXED_WIDTH - 1)) & 1;
      /* Shift left mod by 1 bit.  */
      mod = mod << 1;
      /* Test the leftmost bit of s to add to mod.  */
      if ((s >> (FIXED_WIDTH - 1)) & 1)
	mod ++;
      /* Shift left quo_s by 1 bit.  */
      quo_s = quo_s << 1;
      /* Try to calculate (mod - pos_b).  */
      temp = mod - pos_b;
      if (leftmost_mode || (UINT_C_TYPE)mod >= (UINT_C_TYPE)pos_b)
	{
	  quo_s ++;
	  mod = temp;
	}
      /* Shift left s by 1 bit.  */
      s = s << 1;
    }

#if MODE_UNSIGNED == 0
    if (num_of_neg == 1)
      {
	quo_s = -quo_s;
	if (quo_s == 0)
	  quo_r = -quo_r;
	else
	  quo_r = ~quo_r;
      }
#endif
  if (satp)
    FIXED_SATURATE2 (&quo_r, &quo_s);
  z = quo_s;
#if HAVE_PADDING_BITS
  z = z << PADDING_BITS;
  z = z >> PADDING_BITS;
#endif
  memcpy (&c, &z, FIXED_SIZE);
  return c;
#endif
}
#endif /* FIXED_DIVHELPER */

#if defined(FIXED_DIV) && defined(L_div)
FIXED_C_TYPE
FIXED_DIV (FIXED_C_TYPE a, FIXED_C_TYPE b)
{
  return FIXED_DIVHELPER (a, b, 0);
}
#endif /* FIXED_DIV */


#if defined(FIXED_UDIV) && defined(L_udiv)
FIXED_C_TYPE
FIXED_UDIV (FIXED_C_TYPE a, FIXED_C_TYPE b)
{
  return FIXED_DIVHELPER (a, b, 0);
}
#endif /* FIXED_UDIV */

#if defined(FIXED_SSDIV) && defined(L_ssdiv)
FIXED_C_TYPE
FIXED_SSDIV (FIXED_C_TYPE a, FIXED_C_TYPE b)
{
  return FIXED_DIVHELPER (a, b, 1);
}
#endif /* FIXED_SSDIV */

#if defined(FIXED_USDIV) && defined(L_usdiv)
FIXED_C_TYPE
FIXED_USDIV (FIXED_C_TYPE a, FIXED_C_TYPE b)
{
  return FIXED_DIVHELPER (a, b, 1);
}
#endif /* FIXED_USDIV */

#if defined(FIXED_NEG) && defined(L_neg)
FIXED_C_TYPE
FIXED_NEG (FIXED_C_TYPE a)
{
  FIXED_C_TYPE c;
  INT_C_TYPE x, z;
  memcpy (&x, &a, FIXED_SIZE);
  z = -x;
#if HAVE_PADDING_BITS
  z = z << PADDING_BITS;
  z = z >> PADDING_BITS;
#endif
  memcpy (&c, &z, FIXED_SIZE);
  return c;
}
#endif /* FIXED_NEG */

#if defined(FIXED_SSNEG) && defined(L_ssneg)
FIXED_C_TYPE
FIXED_SSNEG (FIXED_C_TYPE a)
{
  FIXED_C_TYPE c;
  INT_C_TYPE x, y, z;
  memcpy (&y, &a, FIXED_SIZE);
  x = 0;
  z = x - y;
  if (((x ^ y) >> I_F_BITS) & 1)
    {
      if (((z ^ x) >> I_F_BITS) & 1)
        {
          z = 1;
          z = z << I_F_BITS;
          if (x >= 0)
            z--;
        }
    }
#if HAVE_PADDING_BITS
  z = z << PADDING_BITS;
  z = z >> PADDING_BITS;
#endif
  memcpy (&c, &z, FIXED_SIZE);
  return c;
}
#endif /* FIXED_SSNEG */

#if defined(FIXED_USNEG) && defined(L_usneg)
FIXED_C_TYPE
FIXED_USNEG (FIXED_C_TYPE a __attribute__ ((__unused__)))
{
  FIXED_C_TYPE c;
  INT_C_TYPE z;
  z = 0;
  memcpy (&c, &z, FIXED_SIZE);
  return c;
}
#endif /* FIXED_USNEG */

#if defined(FIXED_ASHLHELPER) && defined(L_ashlhelper)
FIXED_C_TYPE
FIXED_ASHLHELPER (FIXED_C_TYPE a, word_type b, word_type satp)
{
  FIXED_C_TYPE c;
  INT_C_TYPE x, z;

#if defined (DINT_C_TYPE)
  DINT_C_TYPE dx, dz;
  memcpy (&x, &a, FIXED_SIZE);
  dx = (DINT_C_TYPE) x;
  if (b >= FIXED_WIDTH)
    dz = dx << FIXED_WIDTH;
  else
    dz = dx << b;
  if (satp)
    FIXED_SATURATE1 (&dz);
  z = (INT_C_TYPE) dz;
#if HAVE_PADDING_BITS
  z = z << PADDING_BITS;
  z = z >> PADDING_BITS;
#endif
  memcpy (&c, &z, FIXED_SIZE);
  return c;

#else /* No DINT_C_TYPE */
  INT_C_TYPE r, s;
  memcpy (&x, &a, FIXED_SIZE);
  /* We need to shift left x by b bits to {r, s}.  */
  if (b >= FIXED_WIDTH)
    {
      r = b;
      s = 0;
    }
  else
    {
      s = x << b;
      r = x >> (FIXED_WIDTH - b);
    }
  if (satp)
    FIXED_SATURATE2 (&r, &s);
  z = s;
#if HAVE_PADDING_BITS
  z = z << PADDING_BITS;
  z = z >> PADDING_BITS;
#endif
  memcpy (&c, &z, FIXED_SIZE);
  return c;
#endif
}
#endif /* FIXED_ASHLHELPER */

#if defined(FIXED_ASHL) && defined(L_ashl)
FIXED_C_TYPE
FIXED_ASHL (FIXED_C_TYPE a, word_type b)
{
  return FIXED_ASHLHELPER (a, b, 0);
}
#endif /* FIXED_ASHL */

#if defined(FIXED_ASHR) && defined(L_ashr)
FIXED_C_TYPE
FIXED_ASHR (FIXED_C_TYPE a, word_type b)
{
  FIXED_C_TYPE c;
  INT_C_TYPE x, z;
  memcpy (&x, &a, FIXED_SIZE);
  z = x >> b;
#if HAVE_PADDING_BITS
  z = z << PADDING_BITS;
  z = z >> PADDING_BITS;
#endif
  memcpy (&c, &z, FIXED_SIZE);
  return c;
}
#endif /* FIXED_ASHR */

#if defined(FIXED_LSHR) && defined(L_lshr)
FIXED_C_TYPE
FIXED_LSHR (FIXED_C_TYPE a, word_type b)
{
  FIXED_C_TYPE c;
  INT_C_TYPE x, z;
  memcpy (&x, &a, FIXED_SIZE);
  z = x >> b;
#if HAVE_PADDING_BITS
  z = z << PADDING_BITS;
  z = z >> PADDING_BITS;
#endif
  memcpy (&c, &z, FIXED_SIZE);
  return c;
}
#endif /* FIXED_LSHR */

#if defined(FIXED_SSASHL) && defined(L_ssashl)
FIXED_C_TYPE
FIXED_SSASHL (FIXED_C_TYPE a, word_type b)
{
  return FIXED_ASHLHELPER (a, b, 1);
}
#endif /* FIXED_SSASHL */

#if defined(FIXED_USASHL) && defined(L_usashl)
FIXED_C_TYPE
FIXED_USASHL (FIXED_C_TYPE a, word_type b)
{
  return FIXED_ASHLHELPER (a, b, 1);
}
#endif /* FIXED_USASHL */

#if defined(FIXED_CMP) && defined(L_cmp)
word_type
FIXED_CMP (FIXED_C_TYPE a, FIXED_C_TYPE b)
{
  INT_C_TYPE x, y;
  memcpy (&x, &a, FIXED_SIZE);
  memcpy (&y, &b, FIXED_SIZE);

  if (x < y)
    return 0;
  else if (x > y)
    return 2;

  return 1;
}
#endif /* FIXED_CMP */

/* Fixed -> Fixed.  */
#if defined(FRACT) && defined(L_fract) && FROM_TYPE == 4 && TO_TYPE == 4
TO_FIXED_C_TYPE
FRACT (FROM_FIXED_C_TYPE a)
{
  TO_FIXED_C_TYPE c;
  FROM_INT_C_TYPE x;
  TO_INT_C_TYPE z;
  int shift_amount;
  memcpy (&x, &a, FROM_FIXED_SIZE);
#if TO_FBITS > FROM_FBITS  /* Need left shift.  */
  shift_amount = TO_FBITS - FROM_FBITS;
  z = (TO_INT_C_TYPE) x;
  z = z << shift_amount;
#else /* TO_FBITS <= FROM_FBITS.  Need right Shift.  */
  shift_amount = FROM_FBITS - TO_FBITS;
  x = x >> shift_amount;
  z = (TO_INT_C_TYPE) x;
#endif /* TO_FBITS > FROM_FBITS  */

#if TO_HAVE_PADDING_BITS
  z = z << TO_PADDING_BITS;
  z = z >> TO_PADDING_BITS;
#endif
  memcpy (&c, &z, TO_FIXED_SIZE);
  return c;
}
#endif /* FRACT && FROM_TYPE == 4 && TO_TYPE == 4  */

/* Fixed -> Fixed with saturation.  */
#if defined(SATFRACT) && defined(L_satfract) && FROM_TYPE == 4 && TO_TYPE == 4
TO_FIXED_C_TYPE
SATFRACT (FROM_FIXED_C_TYPE a)
{
  TO_FIXED_C_TYPE c;
  TO_INT_C_TYPE z;
  FROM_INT_C_TYPE x;
#if FROM_MODE_UNSIGNED == 0
  BIG_SINT_C_TYPE high, low;
  BIG_SINT_C_TYPE max_high, max_low;
  BIG_SINT_C_TYPE min_high, min_low;
#else
  BIG_UINT_C_TYPE high, low;
  BIG_UINT_C_TYPE max_high, max_low;
  BIG_UINT_C_TYPE min_high, min_low;
#endif
#if TO_FBITS > FROM_FBITS
  BIG_UINT_C_TYPE utemp;
#endif
#if TO_MODE_UNSIGNED == 0
  BIG_SINT_C_TYPE stemp;
#endif
#if TO_FBITS != FROM_FBITS
  int shift_amount;
#endif
  memcpy (&x, &a, FROM_FIXED_SIZE);

  /* Step 1. We need to store x to {high, low}.  */
#if FROM_MODE_UNSIGNED == 0
  low = (BIG_SINT_C_TYPE) x;
  if (x < 0)
    high = -1;
  else
    high = 0;
#else
  low = (BIG_UINT_C_TYPE) x;
  high = 0;
#endif

  /* Step 2. We need to shift {high, low}.  */
#if TO_FBITS > FROM_FBITS /* Left shift.  */
  shift_amount = TO_FBITS - FROM_FBITS;
  utemp = (BIG_UINT_C_TYPE) low;
  utemp = utemp >> (BIG_WIDTH - shift_amount);
  high = ((BIG_UINT_C_TYPE)(high << shift_amount)) | utemp;
  low = low << shift_amount;
#elif TO_FBITS < FROM_FBITS /* Right shift.  */
  shift_amount = FROM_FBITS - TO_FBITS;
  low = low >> shift_amount;
#endif

  /* Step 3. Compare {high, low} with max and  min of TO_FIXED_C_TYPE.  */
  max_high = 0;
#if BIG_WIDTH > TO_FIXED_WIDTH || TO_MODE_UNSIGNED == 0 || TO_HAVE_PADDING_BITS
  max_low = (BIG_UINT_C_TYPE)1 << TO_I_F_BITS;
  max_low = max_low - 1;
#else
  max_low = -1;
#endif

#if TO_MODE_UNSIGNED == 0
  min_high = -1;
  stemp = (BIG_SINT_C_TYPE)1 << (BIG_WIDTH - 1);
  stemp = stemp >> (BIG_WIDTH - 1 - TO_I_F_BITS);
  min_low = stemp;
#else
  min_high = 0;
  min_low = 0;
#endif

#if FROM_MODE_UNSIGNED == 0 && TO_MODE_UNSIGNED == 0
  /* Signed -> Signed.  */
  if ((BIG_SINT_C_TYPE) high > (BIG_SINT_C_TYPE) max_high
      || ((BIG_SINT_C_TYPE) high == (BIG_SINT_C_TYPE) max_high
	  && (BIG_UINT_C_TYPE) low > (BIG_UINT_C_TYPE) max_low))
    low = max_low; /* Maximum.  */
  else if ((BIG_SINT_C_TYPE) high < (BIG_SINT_C_TYPE) min_high
	   || ((BIG_SINT_C_TYPE) high == (BIG_SINT_C_TYPE) min_high
	       && (BIG_UINT_C_TYPE) low < (BIG_UINT_C_TYPE) min_low))
    low = min_low; /* Minimum.  */
#elif FROM_MODE_UNSIGNED == 1 && TO_MODE_UNSIGNED == 1
  /* Unigned -> Unsigned.  */
  if ((BIG_UINT_C_TYPE) high > (BIG_UINT_C_TYPE) max_high
      || ((BIG_UINT_C_TYPE) high == (BIG_UINT_C_TYPE) max_high
	  && (BIG_UINT_C_TYPE) low > (BIG_UINT_C_TYPE) max_low))
    low = max_low; /* Maximum.  */
#elif FROM_MODE_UNSIGNED == 0 && TO_MODE_UNSIGNED == 1
  /* Signed -> Unsigned.  */
  if (x < 0)
    low = 0; /* Minimum.  */
  else if ((BIG_UINT_C_TYPE) high > (BIG_UINT_C_TYPE) max_high
	   || ((BIG_UINT_C_TYPE) high == (BIG_UINT_C_TYPE) max_high
	       && (BIG_UINT_C_TYPE) low > (BIG_UINT_C_TYPE) max_low))
    low = max_low; /* Maximum.  */
#elif FROM_MODE_UNSIGNED == 1 && TO_MODE_UNSIGNED == 0
  /* Unsigned -> Signed.  */
  if ((BIG_SINT_C_TYPE) high < 0)
    low = max_low; /* Maximum.  */
  else if ((BIG_SINT_C_TYPE) high > (BIG_SINT_C_TYPE) max_high
	   || ((BIG_SINT_C_TYPE) high == (BIG_SINT_C_TYPE) max_high
	       && (BIG_UINT_C_TYPE) low > (BIG_UINT_C_TYPE) max_low))
    low = max_low; /* Maximum.  */
#endif

  /* Step 4. Store the result.  */
  z = (TO_INT_C_TYPE) low;
#if TO_HAVE_PADDING_BITS
  z = z << TO_PADDING_BITS;
  z = z >> TO_PADDING_BITS;
#endif
  memcpy (&c, &z, TO_FIXED_SIZE);
  return c;
}
#endif /* defined(SATFRACT) && FROM_TYPE == 4 && TO_TYPE == 4  */

/* Fixed -> Int.  */
#if defined(FRACT) && defined(L_fract) && FROM_TYPE == 4 && TO_TYPE == 1
TO_INT_C_TYPE
FRACT (FROM_FIXED_C_TYPE a)
{
  FROM_INT_C_TYPE x;
  TO_INT_C_TYPE z;
  FROM_INT_C_TYPE i = 0;
  memcpy (&x, &a, FROM_FIXED_SIZE);

#if FROM_MODE_UNSIGNED == 0
  if (x < 0)
    {
#if FROM_FIXED_WIDTH == FROM_FBITS
      if (x != 0)
	i = 1;
#else
      if (((FROM_INT_C_TYPE)(x << (FROM_FIXED_WIDTH - FROM_FBITS))) != 0)
	i = 1;
#endif
    }
#endif

#if FROM_FIXED_WIDTH == FROM_FBITS
  x = 0;
#else
  x = x >> FROM_FBITS;
#endif
  x = x + i;
  z = (TO_INT_C_TYPE) x;
  return z;
}
#endif /* defined(FRACT) && FROM_TYPE == 4 && TO_TYPE == 1  */

/* Fixed -> Unsigned int.  */
#if defined(FRACTUNS) && defined(L_fractuns) && FROM_TYPE == 4 && TO_TYPE == 2
TO_INT_C_TYPE
FRACTUNS (FROM_FIXED_C_TYPE a)
{
  FROM_INT_C_TYPE x;
  TO_INT_C_TYPE z;
  FROM_INT_C_TYPE i = 0;
  memcpy (&x, &a, FROM_FIXED_SIZE);

#if FROM_MODE_UNSIGNED == 0
  if (x < 0)
    {
#if FROM_FIXED_WIDTH == FROM_FBITS
      if (x != 0)
	i = 1;
#else
      if (((FROM_INT_C_TYPE)(x << (FROM_FIXED_WIDTH - FROM_FBITS))) != 0)
	i = 1;
#endif
    }
#endif

#if FROM_FIXED_WIDTH == FROM_FBITS
  x = 0;
#else
  x = x >> FROM_FBITS;
#endif
  x = x + i;
  z = (TO_INT_C_TYPE) x;
  return z;
}
#endif /* defined(FRACTUNS) && FROM_TYPE == 4 && TO_TYPE == 2  */

/* Int -> Fixed.  */
#if defined(FRACT) && defined(L_fract) && FROM_TYPE == 1 && TO_TYPE == 4
TO_FIXED_C_TYPE
FRACT (FROM_INT_C_TYPE a)
{
  TO_FIXED_C_TYPE c;
  TO_INT_C_TYPE z;
  z = (TO_INT_C_TYPE) a;
#if TO_FIXED_WIDTH == TO_FBITS
  z = 0;
#else
  z = z << TO_FBITS;
#endif
#if TO_HAVE_PADDING_BITS
  z = z << TO_PADDING_BITS;
  z = z >> TO_PADDING_BITS;
#endif
  memcpy (&c, &z, TO_FIXED_SIZE);
  return c;
}
#endif /* defined(FRACT) && FROM_TYPE == 1 && TO_TYPE == 4  */

/* Signed int -> Fixed with saturation.  */
#if defined(SATFRACT) && defined(L_satfract) &&FROM_TYPE == 1 && TO_TYPE == 4
TO_FIXED_C_TYPE
SATFRACT (FROM_INT_C_TYPE a)
{
  TO_FIXED_C_TYPE c;
  TO_INT_C_TYPE z;
  FROM_INT_C_TYPE x = a;
  BIG_SINT_C_TYPE high, low;
  BIG_SINT_C_TYPE max_high, max_low;
  BIG_SINT_C_TYPE min_high, min_low;
#if TO_MODE_UNSIGNED == 0
  BIG_SINT_C_TYPE stemp;
#endif
#if BIG_WIDTH != TO_FBITS
  BIG_UINT_C_TYPE utemp;
  int shift_amount;
#endif

  /* Step 1. We need to store x to {high, low}.  */
  low = (BIG_SINT_C_TYPE) x;
  if (x < 0)
    high = -1;
  else
    high = 0;

  /* Step 2. We need to left shift {high, low}.  */
#if BIG_WIDTH == TO_FBITS
  high = low;
  low = 0;
#else
  shift_amount = TO_FBITS;
  utemp = (BIG_UINT_C_TYPE) low;
  utemp = utemp >> (BIG_WIDTH - shift_amount);
  high = ((BIG_UINT_C_TYPE)(high << shift_amount)) | utemp;
  low = low << shift_amount;
#endif

  /* Step 3. Compare {high, low} with max and  min of TO_FIXED_C_TYPE.  */
  max_high = 0;
#if BIG_WIDTH > TO_FIXED_WIDTH || TO_MODE_UNSIGNED == 0 || TO_HAVE_PADDING_BITS
  max_low = (BIG_UINT_C_TYPE)1 << TO_I_F_BITS;
  max_low = max_low - 1;
#else
  max_low = -1;
#endif

#if TO_MODE_UNSIGNED == 0
  min_high = -1;
  stemp = (BIG_SINT_C_TYPE)1 << (BIG_WIDTH - 1);
  stemp = stemp >> (BIG_WIDTH - 1 - TO_I_F_BITS);
  min_low = stemp;
#else
  min_high = 0;
  min_low = 0;
#endif

#if TO_MODE_UNSIGNED == 0
  /* Signed -> Signed.  */
  if ((BIG_SINT_C_TYPE) high > (BIG_SINT_C_TYPE) max_high
      || ((BIG_SINT_C_TYPE) high == (BIG_SINT_C_TYPE) max_high
          && (BIG_UINT_C_TYPE) low > (BIG_UINT_C_TYPE) max_low))
    low = max_low; /* Maximum.  */
  else if ((BIG_SINT_C_TYPE) high < (BIG_SINT_C_TYPE) min_high
           || ((BIG_SINT_C_TYPE) high == (BIG_SINT_C_TYPE) min_high
               && (BIG_UINT_C_TYPE) low < (BIG_UINT_C_TYPE) min_low))
    low = min_low; /* Minimum.  */
#else
  /* Signed -> Unsigned.  */
  if (x < 0)
    low = 0; /* Minimum.  */
  else if ((BIG_UINT_C_TYPE) high > (BIG_UINT_C_TYPE) max_high
           || ((BIG_UINT_C_TYPE) high == (BIG_UINT_C_TYPE) max_high
               && (BIG_UINT_C_TYPE) low > (BIG_UINT_C_TYPE) max_low))
    low = max_low; /* Maximum.  */
#endif

  /* Step 4. Store the result.  */
  z = (TO_INT_C_TYPE) low;
#if TO_HAVE_PADDING_BITS
  z = z << TO_PADDING_BITS;
  z = z >> TO_PADDING_BITS;
#endif
  memcpy (&c, &z, TO_FIXED_SIZE);
  return c;
}
#endif /* defined(SATFRACT) && FROM_TYPE == 1 && TO_TYPE == 4  */

/* Unsigned int -> Fixed.  */
#if defined(FRACTUNS) && defined(L_fractuns) &&FROM_TYPE == 2 && TO_TYPE == 4
TO_FIXED_C_TYPE
FRACTUNS (FROM_INT_C_TYPE a)
{
  TO_FIXED_C_TYPE c;
  TO_INT_C_TYPE z;
  z = (TO_INT_C_TYPE) a;
#if TO_FIXED_WIDTH == TO_FBITS
  z = 0;
#else
  z = z << TO_FBITS;
#endif
#if TO_HAVE_PADDING_BITS
  z = z << TO_PADDING_BITS;
  z = z >> TO_PADDING_BITS;
#endif
  memcpy (&c, &z, TO_FIXED_SIZE);
  return c;
}
#endif /* defined(FRACTUNS) && FROM_TYPE == 2 && TO_TYPE == 4  */

/* Unsigned int -> Fixed with saturation.  */
#if defined(SATFRACTUNS) && defined(L_satfractuns) && FROM_TYPE == 2 && TO_TYPE == 4
TO_FIXED_C_TYPE
SATFRACTUNS (FROM_INT_C_TYPE a)
{
  TO_FIXED_C_TYPE c;
  TO_INT_C_TYPE z;
  FROM_INT_C_TYPE x = a;
  BIG_UINT_C_TYPE high, low;
  BIG_UINT_C_TYPE max_high, max_low;
#if BIG_WIDTH != TO_FBITS
  BIG_UINT_C_TYPE utemp;
  int shift_amount;
#endif

  /* Step 1. We need to store x to {high, low}.  */
  low = (BIG_UINT_C_TYPE) x;
  high = 0;

  /* Step 2. We need to left shift {high, low}.  */
#if BIG_WIDTH == TO_FBITS
  high = low;
  low = 0;
#else
  shift_amount = TO_FBITS;
  utemp = (BIG_UINT_C_TYPE) low;
  utemp = utemp >> (BIG_WIDTH - shift_amount);
  high = ((BIG_UINT_C_TYPE)(high << shift_amount)) | utemp;
  low = low << shift_amount;
#endif

  /* Step 3. Compare {high, low} with max and  min of TO_FIXED_C_TYPE.  */
  max_high = 0;
#if BIG_WIDTH > TO_FIXED_WIDTH || TO_MODE_UNSIGNED == 0 || TO_HAVE_PADDING_BITS
  max_low = (BIG_UINT_C_TYPE)1 << TO_I_F_BITS;
  max_low = max_low - 1;
#else
  max_low = -1;
#endif

#if TO_MODE_UNSIGNED == 1
  /* Unigned -> Unsigned.  */
  if ((BIG_UINT_C_TYPE) high > (BIG_UINT_C_TYPE) max_high
      || ((BIG_UINT_C_TYPE) high == (BIG_UINT_C_TYPE) max_high
          && (BIG_UINT_C_TYPE) low > (BIG_UINT_C_TYPE) max_low))
    low = max_low; /* Maximum.  */
#else
  /* Unsigned -> Signed.  */
  if ((BIG_SINT_C_TYPE) high < 0)
    low = max_low; /* Maximum.  */
  else if ((BIG_SINT_C_TYPE) high > (BIG_SINT_C_TYPE) max_high
           || ((BIG_SINT_C_TYPE) high == (BIG_SINT_C_TYPE) max_high
               && (BIG_UINT_C_TYPE) low > (BIG_UINT_C_TYPE) max_low))
    low = max_low; /* Maximum.  */
#endif

  /* Step 4. Store the result.  */
  z = (TO_INT_C_TYPE) low;
#if TO_HAVE_PADDING_BITS
  z = z << TO_PADDING_BITS;
  z = z >> TO_PADDING_BITS;
#endif
  memcpy (&c, &z, TO_FIXED_SIZE);
  return c;
}
#endif /* defined(SATFRACTUNS) && FROM_TYPE == 2 && TO_TYPE == 4  */

/* Fixed -> Float.  */
#if defined(FRACT) && defined(L_fract) && FROM_TYPE == 4 && TO_TYPE == 3
TO_FLOAT_C_TYPE
FRACT (FROM_FIXED_C_TYPE a)
{
  FROM_INT_C_TYPE x;
  TO_FLOAT_C_TYPE z;
  memcpy (&x, &a, FROM_FIXED_SIZE);
  z = (TO_FLOAT_C_TYPE) x;
  z = z / BASE;
  return z;
}
#endif /* defined(FRACT) && FROM_TYPE == 4 && TO_TYPE == 3  */

/* Float -> Fixed.  */
#if defined(FRACT) && defined(L_fract) && FROM_TYPE == 3 && TO_TYPE == 4
TO_FIXED_C_TYPE
FRACT (FROM_FLOAT_C_TYPE a)
{
  FROM_FLOAT_C_TYPE temp;
  TO_INT_C_TYPE z;
  TO_FIXED_C_TYPE c;

  temp = a * BASE;
  z = (TO_INT_C_TYPE) temp;
#if TO_HAVE_PADDING_BITS
  z = z << TO_PADDING_BITS;
  z = z >> TO_PADDING_BITS;
#endif
  memcpy (&c, &z, TO_FIXED_SIZE);
  return c;
}
#endif /* defined(FRACT) && FROM_TYPE == 3 && TO_TYPE == 4  */

/* Float -> Fixed with saturation.  */
#if defined(SATFRACT) && defined(L_satfract) && FROM_TYPE == 3 && TO_TYPE == 4
TO_FIXED_C_TYPE
SATFRACT (FROM_FLOAT_C_TYPE a)
{
  FROM_FLOAT_C_TYPE temp;
  TO_INT_C_TYPE z;
  TO_FIXED_C_TYPE c;

  if (a >= FIXED_MAX)
    {
#if TO_MODE_UNSIGNED == 0 || TO_HAVE_PADDING_BITS
      z = (TO_INT_C_TYPE)1 << TO_I_F_BITS;
      z = z - 1;
#else
      z = -1;
#endif
    }
  else if (a <= FIXED_MIN)
    {
#if TO_MODE_UNSIGNED == 0
      z = (TO_INT_C_TYPE)1 << TO_I_F_BITS;
#else
      z = 0;
#endif
    }
  else
    {
      temp = a * BASE;
      z = (TO_INT_C_TYPE) temp;
    }

#if TO_HAVE_PADDING_BITS
  z = z << TO_PADDING_BITS;
  z = z >> TO_PADDING_BITS;
#endif
  memcpy (&c, &z, TO_FIXED_SIZE);
  return c;
}
#endif /* defined(SATFRACT) && FROM_TYPE == 3 && TO_TYPE == 4  */

