/*****************************************************************************/
/* SISCO SOFTWARE MODULE HEADER **********************************************/
/*****************************************************************************/
/*   (c) Copyright Systems Integration Specialists Company, Inc.,            */
/*            2005 All Rights Reserved                                       */
/*                                                                           */
/* MODULE NAME : QuadLib.c                                                   */
/* PRODUCT(S)  : Standard Time Management Library                            */
/*                                                                           */
/* MODULE DESCRIPTION:                                                       */
/*   64-bit Math Simulation Library.                                         */
/*   Provided for platforms without native 64-bit support.                   */
/*                                                                           */
/* MODIFICATION LOG:                                                         */
/*   Date    Who  Rev  Comments                                              */
/* --------  ---  ---  ----------------------------------------------------- */
/* 06/07/06  RLH   02  Numerous features added, see stdtime.doc              */
/* 01/18/06  RLH   01  Created                                               */
/*****************************************************************************/

#include "stdtime.h"

#ifdef QUADLIB_ENABLED

int32_t QuadLibU64Cmp (QUADLIB_U64 x, QUADLIB_U64 y)
  {
  if (x.hi.u < y.hi.u) return -1;
  if (x.hi.u > y.hi.u) return 1;

  if (x.lo.u < y.lo.u) return -1;
  if (x.lo.u > y.lo.u) return 1;

  return 0;
  }

int32_t QuadLibI64Cmp (QUADLIB_I64 x, QUADLIB_I64 y)
  {
  if (x.hi.i < y.hi.i) return -1;
  if (x.hi.i > y.hi.i) return 1;

  /* the unsigned compare on lo.u works correctly if x.hi.i == y.hi.i */
  /* in case of negative numbers, when lo increases in unsigned */
  /* magnitude, the 64-bit value is less negative, and so is a */
  /* greater signed value, so nothing special needs to be done */

  if (x.lo.u < y.lo.u) return -1;
  if (x.lo.u > y.lo.u) return 1;

  return 0;
  }

#endif /* QUADLIB_ENABLED */


/*****************************************************************************/
/*  GET/SET                                                                  */
/*****************************************************************************/


#ifndef QUADLIB_ENABLED

/* get hi value of a 64-bit value when host has native support */

QUADLIB_STATIC int32_t      QuadLibI64GetHi (QUADLIB_I64 x)
  {
  return (int32_t) QUADLIB_I64_HI(x);

  } /* QuadLibI64GetHi */

QUADLIB_STATIC uint32_t     QuadLibU64GetHi (QUADLIB_U64 x)
  {
  return (uint32_t) QUADLIB_U64_HI(x);

  } /* QuadLibU64GetHi */

/* get lo value of a 64-bit value when host has native support */

QUADLIB_STATIC int32_t      QuadLibI64GetLo (QUADLIB_I64 x)
  {
  return (int32_t) QUADLIB_I64_LO(x);

  } /* QuadLibI64GetLo */

QUADLIB_STATIC uint32_t     QuadLibU64GetLo (QUADLIB_U64 x)
  {
  return (uint32_t) QUADLIB_U64_LO(x);

  } /* QuadLibU64GetLo */


/* set hi value of a 64-bit value when host has native support */

void QuadLibI64SetHi (QUADLIB_I64 *x, int32_t y)
  {
  (*(QUADLIB_I64_TYPE *)(x)).hi.i = y;

  } /* QuadLibI64SetHi */

void QuadLibU64SetHi (QUADLIB_U64 *x, uint32_t y)
  {
  (*(QUADLIB_I64_TYPE *)(x)).hi.u = y;

  } /* QuadLibU64SetHi */

/* set lo value of a 64-bit value when host has native support */

void QuadLibI64SetLo (QUADLIB_I64 *x, int32_t y)
  {
  (*(QUADLIB_I64_TYPE *)(x)).lo.i = y;

  } /* QuadLibI64SetLo */

void QuadLibU64SetLo (QUADLIB_U64 *x, uint32_t y)
  {
  (*(QUADLIB_I64_TYPE *)(x)).lo.u = y;

  } /* QuadLibU64SetLo */

#endif /* not QUADLIB_ENABLED */


/*****************************************************************************/
/*  CAST                                                                     */
/*****************************************************************************/


#ifdef QUADLIB_ENABLED


/* 64-bit to 64-bit conversions */


QUADLIB_STATIC QUADLIB_I64  QuadLibI64CastI64N(void * x, size_t n)
  {
  QUADLIB_I64               result;

  /* determine type of cast based on sizeof(x) */

  switch (n)
    {
    case 8:
        QUADLIB_I64_HI(result) = QUADLIB_I64_HI(x);
        QUADLIB_I64_LO(result) = QUADLIB_I64_LO(x);
        break;

    case 2:
        QUADLIB_I64_HI(result) =
        QUADLIB_I64_LO(result) = (*(short *)(&x));
        QUADLIB_I64_HI(result) >>= 31;  /* sign propagation */
        break;

    case 1:
        QUADLIB_I64_HI(result) =
        QUADLIB_I64_LO(result) = (*(char *)(&x));
        QUADLIB_I64_HI(result) >>= 31;  /* sign propagation */
        break;

    default:
        /* assume length 4 */
        QUADLIB_I64_HI(result) =
        QUADLIB_I64_LO(result) = (*(long *)(&x));
        QUADLIB_I64_HI(result) >>= 31;  /* sign propagation */
        break;
    }

  return result;

  } /* QuadLibI64CastI64N */


QUADLIB_STATIC QUADLIB_U64  QuadLibU64CastU64N(void * x, size_t n)
  {
  QUADLIB_U64               result;

  /* determine type of cast based on sizeof(x) */

  switch (n)
    {
    case 8:
        QUADLIB_I64_HI(result) = QUADLIB_I64_HI(x);
        QUADLIB_I64_LO(result) = QUADLIB_I64_LO(x);
        break;

    case 2:
        QUADLIB_I64_HI(result) = 0;
        QUADLIB_I64_LO(result) = (*(unsigned short *)(&x));
        break;

    case 1:
        QUADLIB_I64_HI(result) = 0;
        QUADLIB_I64_LO(result) = (*(unsigned char *)(&x));
        break;

    default:
        /* assume length 4 */
        QUADLIB_I64_HI(result) = 0;
        QUADLIB_I64_LO(result) = (*(unsigned long *)(&x));
        break;
    }

  return result;

  } /* QuadLibU64CastU64N */


QUADLIB_STATIC QUADLIB_I64N QuadLibI64NCastI64(QUADLIB_I64 x)
  {
  QUADLIB_I64N              result;

#ifdef QUADLIB_NATIVE64_ENABLED
  QUADLIB_I64_HI(result) = QUADLIB_I64_HI(x);
  QUADLIB_I64_LO(result) = QUADLIB_I64_LO(x);

#else
  /* x is a 32-bit value, so we can only return 32 bits of I64 value */
  result = (QUADLIB_I64N) QUADLIB_I64_LO(x);

#endif
  return result;

  } /* QuadLibI64NCastI64 */


QUADLIB_STATIC QUADLIB_U64N QuadLibU64NCastU64(QUADLIB_U64 x)
  {
  QUADLIB_U64N              result;

#ifdef QUADLIB_NATIVE64_ENABLED
  QUADLIB_U64_HI(result) = QUADLIB_U64_HI(x);
  QUADLIB_U64_LO(result) = QUADLIB_U64_LO(x);

#else
  /* x is a 32-bit value, so we can only return 32 bits of I64 value */
  result = (QUADLIB_U64N) QUADLIB_U64_LO(x);

#endif
  return result;

  } /* QuadLibU64NCastU64 */


/* upsize casts: 32 to 64 bit */


QUADLIB_STATIC QUADLIB_I64  QuadLibI64CastI32 (int32_t x)
  {
  QUADLIB_I64               result;

  result.lo.i = x;
  result.hi.i = x;
  result.hi.i >>= 31;       /* for signed, x propagation */

  return result;
  } /* QuadLibI64CastI32 */


QUADLIB_STATIC QUADLIB_U64  QuadLibU64CastU32 (uint32_t x)
  {
  QUADLIB_U64               result;

  result.hi.u = 0;          /* for unsigned, 0 propagation */
  result.lo.u = x;

  return result;
  } /* QuadLibU64CastU32 */


/* downsize casts: 64 to 32 bit */


QUADLIB_STATIC int32_t      QuadLibI32CastI64 (QUADLIB_I64 x)
  {
  return x.lo.i;
  } /* QuadLibI32CastI64 */


QUADLIB_STATIC uint32_t     QuadLibU32CastU64 (QUADLIB_U64 x)
  {
  return x.lo.u;
  } /* QuadLibU32CastU64 */


#endif /* QUADLIB_ENABLED */


/*****************************************************************************/
/*            QuadLibU64Not                                                  */
/*  form 1's complement of 64-bit value                                      */
/*****************************************************************************/


#ifdef QUADLIB_ENABLED

QUADLIB_STATIC QUADLIB_U64  QuadLibU64Not (QUADLIB_U64 x)
  {
  QUADLIB_U64               result;

  result.hi.u = ~(x.hi.u);
  result.lo.u = ~(x.lo.u);
  return result;
  }

#endif /* QUADLIB_ENABLED */

/*****************************************************************************/
/*            QuadLibU64Neg                                                  */
/*  form 2's complement of 64-bit value                                      */
/*****************************************************************************/

#ifdef QUADLIB_ENABLED

QUADLIB_STATIC QUADLIB_U64  QuadLibU64Neg (QUADLIB_U64 x)
  {
  QUADLIB_U64               result;

  result.hi.u = ~(x.hi.u);
  result.lo.u = (~(x.lo.u)) + 1;

  /* if x.lo.u came in as 0, its complement is 0xFFFFFFFF */
  /* if we then increment it, it will become 0 again */
  /* so, if result.lo.u is zero now, there was a carry */

  if (result.lo.u == 0)
    {
    result.hi.u++;
    }

  return result;
  } /* QuadLibU64Neg */

#endif /* QUADLIB_ENABLED */

/*****************************************************************************/
/*            QuadLibU64Abs                                                  */
/*  form absolute value of 64-bit value                                      */
/*****************************************************************************/

#ifdef QUADLIB_ENABLED

QUADLIB_STATIC QUADLIB_U64  QuadLibU64Abs (QUADLIB_U64 x)
  {
  if (x.hi.i < 0)               /* value is negative */
    {
    return QUADLIB_U64_NEG (x);
    }

  return x;                     /* value is positive, do not negate */

  } /* QuadLibU64Abs */

#else /* not QUADLIB_ENABLED */

QUADLIB_STATIC QUADLIB_U64  QuadLibU64Abs (QUADLIB_U64 x)
  {
  if (x < 0)                    /* value is negative */
    {
    return (QUADLIB_U64) (-(QUADLIB_I64) x);
    /* negate native I64 value */
    }

  return x;                     /* value is positive, do not negate */

  } /* QuadLibU64Abs */

#endif /* QUADLIB_ENABLED */

/*****************************************************************************/
/*            QuadLibU64Mul                                                  */
/*  multiply two unsigned 64-bit values.                                     */
/*  form partial products from 16-bit values to limit results to 32 bits.    */
/*                                                                           */
/*  method: let ABCD mean four 16-bit values with 'one', and WXYZ in 'two'   */
/*  then, one * two = ABCD * WXYZ.  in "long-hand" this means:               */
/*                                                                           */
/*  (let xxL mean the Left 16 bits of a word, and xxR the Right 16 bits)     */
/*                                                                           */
/*                  A   B   C   D                                            */
/*                  W   X   Y   Z                                            */
/*                  --------------                                           */
/*                  AZ  BZ  CZ  DZ                                           */
/*              AY  BY  CY  DY                                               */
/*          AX  BX  CX  DX                                                   */
/*      AW  BW  CW  DW                                                       */
/*                                                                           */
/*  express the partial sums as split L/R pairs, and discard partial sums    */
/*  that exceed the 64-bit result capacity. ("discard" means discard from    */
/*  the explanation; the discarded values are not actually calculated.)      */
/*                                                                           */
/*                  A   B   C   D                                            */
/*                  W   X   Y   Z                                            */
/*                  --------------                                           */
/*                  AZR BZR CZR DZR                                          */
/*                  BZL CZL DZL                                              */
/*                  BYR CYR DYR                                              */
/*                  CYL DYL                                                  */
/*                  CXR DXR                                                  */
/*                  DXL                                                      */
/*                  DWR                                                      */
/*                                                                           */
/*  since there is no carry out of the left 32-bit value (the AB column),    */
/*  there is no need to split the left-most (AB column) 32-bit values into   */
/*  16/16.  so, we optimize the list of partial sums as follows, (where      */
/*  (xxL:xxR represents an unsplit 32-bit value), and reorganize the         */
/*  diagram to show the left/right relationships better:                     */
/*                                                                           */
/*                  A   B   C   D                                            */
/*                  W   X   Y   Z                                            */
/*                  --------------                                           */
/*                  AZR                                                      */
/*                  bzL:bzR                                                  */
/*                      CZL CZR                                              */
/*                          DZL DZR                                          */
/*                  --------------                                           */
/*                  BYR                                                      */
/*                  cyL:cyR                                                  */
/*                      DYL DYR                                              */
/*                  --------------                                           */
/*                  CXR                                                      */
/*                  dxL:dxR                                                  */
/*                  --------------                                           */
/*                  DWR                                                      */
/*                                                                           */
/*  finally, add up the columns, and store the sums in a 64-bit value        */
/*                                                                           */
/*****************************************************************************/

#ifdef QUADLIB_ENABLED

#define QUADLIB_U32_LWORD(x)        ( ((uint32_t)(x)) >> 16)
#define QUADLIB_U32_RWORD(x)        ((x) & 0xFFFF)

QUADLIB_STATIC QUADLIB_U64  QuadLibU64Mul (
  QUADLIB_U64               /*I*/ one,
  QUADLIB_U64               /*I*/ two)
  {
  QUADLIB_U64               result;

  uint32_t                  a, b, c, d, w, x, y, z;
  uint32_t                  ab, bc, cd;
  uint32_t                  az, bz, cz, dz;
  uint32_t                      by, cy, dy;
  uint32_t                          cx, dx;
  uint32_t                              dw;

  uint32_t                  azr, czr, dzr;
  uint32_t                  czl, dzl;
  uint32_t                  byr, dyr;
  uint32_t                  dyl;
  uint32_t                  cxr;
  uint32_t                  dwr;


  a = QUADLIB_U32_LWORD(one.hi.u);
  b = QUADLIB_U32_RWORD(one.hi.u);
  c = QUADLIB_U32_LWORD(one.lo.u);
  d = QUADLIB_U32_RWORD(one.lo.u);

  w = QUADLIB_U32_LWORD(two.hi.u);
  x = QUADLIB_U32_RWORD(two.hi.u);
  y = QUADLIB_U32_LWORD(two.lo.u);
  z = QUADLIB_U32_RWORD(two.lo.u);


  az = a * z;   bz = b * z;   cz = c * z;   dz = d * z;
                by = b * y;   cy = c * y;   dy = d * y;
                              cx = c * x;   dx = d * x;
                                            dw = d * w;
  azr = QUADLIB_U32_RWORD(az);
  czr = QUADLIB_U32_RWORD(cz);
  dzr = QUADLIB_U32_RWORD(dz);
  czl = QUADLIB_U32_LWORD(cz);
  dzl = QUADLIB_U32_LWORD(dz);
  byr = QUADLIB_U32_RWORD(by);
  dyr = QUADLIB_U32_RWORD(dy);
  dyl = QUADLIB_U32_LWORD(dy);
  cxr = QUADLIB_U32_RWORD(cx);
  dwr = QUADLIB_U32_RWORD(dw);

  bc = czr + dzl + dyr;

  ab = bc >> 16;
  cd = bc << 16;

  result.lo.u = cd  + dzr;

  result.hi.u = ab  + bz  + cy  + dx
              + czl + dyl
            + ((azr + byr + cxr + dwr) << 16);

  return result;

  } /* QuadLibU64Mul */

#undef QUADLIB_U32_LWORD
#undef QUADLIB_U32_RWORD

#endif /* QUADLIB_ENABLED */

/*****************************************************************************/
/*            QuadLibU64Add                                                  */
/*  add two unsigned 64-bit values                                           */
/*****************************************************************************/

#ifdef QUADLIB_ENABLED

QUADLIB_STATIC QUADLIB_U64  QuadLibU64Add (
  QUADLIB_U64               /*I*/ one,
  QUADLIB_U64               /*I*/ two)
  {
  QUADLIB_U64               sum;
  uint32_t                  carry;

  sum.lo.u = one.lo.u + two.lo.u;
  sum.hi.u = one.hi.u + two.hi.u;

  /* calculate carry using boolean algebra */

  carry =
    ( (one.lo.u & two.lo.u) | ((one.lo.u ^ two.lo.u) & (~sum.lo.u)) ) >> 31;

  sum.hi.u += carry;

  return sum;

  } /* QuadLibU64Add */

#endif /* QUADLIB_ENABLED */

/*****************************************************************************/
/*            QuadLibU64Sub                                                  */
/*  subtract two unsigned 64-bit values                                      */
/*  method: negate second operand, and then add values                       */
/*****************************************************************************/

#ifdef QUADLIB_ENABLED

QUADLIB_STATIC QUADLIB_U64  QuadLibU64Sub (
  QUADLIB_U64               /*I*/ one,
  QUADLIB_U64               /*I*/ two)
  {
  return QuadLibU64Add (one, QUADLIB_U64_NEG (two));
  } /* QuadLibU64Sub */

#endif /* QUADLIB_ENABLED */

/*****************************************************************************/
/*            QuadLibU64Add1                                                 */
/*  increment a 64-bit value                                                 */
/*  method: add a constant 1 to a 64-bit value                               */
/*****************************************************************************/

#ifdef QUADLIB_ENABLED

QUADLIB_STATIC QUADLIB_U64  QuadLibU64Add1 (
  QUADLIB_U64               /*I*/ x)
  {
  QUADLIB_U64               result;

  result.hi.u = x.hi.u;
  result.lo.u = x.lo.u + 1;

  if (result.lo.u == 0)
    {
    /* x.lo.u must have had all 1 bits, so a carry occurred */
    result.hi.u++;
    }

  return result;

  } /* QuadLibU64Add1 */

#endif /* QUADLIB_ENABLED */

/*****************************************************************************/
/*            QuadLibU64Sub1                                                 */
/*  decrement a 64-bit value                                                 */
/*  method: add a constant -1 to a 64-bit value                              */
/*****************************************************************************/

#ifdef QUADLIB_ENABLED

QUADLIB_STATIC QUADLIB_U64  QuadLibU64Sub1 (
  QUADLIB_U64               /*I*/ x)
  {
  QUADLIB_U64               result;

  result.hi.u = x.hi.u;
  result.lo.u = x.lo.u - 1;

  if (x.lo.u == 0)
    {
    /* x.lo.u was 0, so a borrow occurred */
    result.hi.u--;
    }

  return result;

  } /* QuadLibU64Sub1 */

#endif /* QUADLIB_ENABLED */

/*****************************************************************************/
/*            QuadLibU64SHR                                                  */
/*  shift an unsigned 64-bit value right by n bits                           */
/*****************************************************************************/

#ifdef QUADLIB_ENABLED

QUADLIB_STATIC QUADLIB_U64  QuadLibU64SHR (
  QUADLIB_U64               /*I*/ value,
  int32_t                   /*I*/ shift)
  {
  QUADLIB_U64               result = QUADLIB_ZERO;

  if (shift == 0)
    {
    return value;
    }

  if (shift < 0)
    {
    return QuadLibU64SHL (value, -shift);
    }

  if (shift > 63)
    {
    return result;          /* all bits shifted out, return 0 */
    }

  for (result = value; shift > 0; shift--)
    {
    result.lo.u >>= 1;

    if (result.hi.u & 1)
      {
      /* hi has low-order 1 about to be shifted out */
      result.lo.u |= QUADLIB_U32_HIGHBIT;
      }

    result.hi.u >>= 1;      /* unsigned shift right */
    }

  return result;

  } /* QuadLibU64SHR */

#endif /* QUADLIB_ENABLED */

/*****************************************************************************/
/*            QuadLibI64SHR                                                  */
/*  shift a signed 64-bit value right by n bits                              */
/*****************************************************************************/

#ifdef QUADLIB_ENABLED

QUADLIB_STATIC QUADLIB_I64  QuadLibI64SHR (
  QUADLIB_I64               /*I*/ value,
  int32_t                   /*I*/ shift)
  {
  QUADLIB_I64               result = QUADLIB_ZERO;

  if (shift == 0)
    {
    return value;
    }

  if (shift < 0)
    {
    return QuadLibU64SHL (value, -shift);
    }

  if (shift > 63)
    {
    return result;          /* all bits shifted out return 0 */
    }

  for (result = value; shift > 0; shift--)
    {
    result.lo.u >>= 1;

    if (result.hi.u & 1)
      {
      /* hi has low-order 1 about to be shifted out */
      result.lo.u |= QUADLIB_U32_HIGHBIT;
      }

    result.hi.i >>= 1;      /* signed shift right */
    }

  return result;

  } /* QuadLibI64SHR */

#endif /* QUADLIB_ENABLED */

/*****************************************************************************/
/*            QuadLibU64SHL                                                  */
/*  shift an unsigned 64-bit value left by n bits                            */
/*****************************************************************************/

#ifdef QUADLIB_ENABLED

QUADLIB_STATIC QUADLIB_U64  QuadLibU64SHL (
  QUADLIB_U64               /*I*/ value,
  int32_t                   /*I*/ shift)
  {
  QUADLIB_U64               result = QUADLIB_ZERO;

  if (shift == 0)
    {
    return value;
    }

  if (shift < 0)
    {
    return QuadLibU64SHR (value, -shift);
    }

  if (shift > 63)
    {
    return result;          /* all bits shifted out return 0 */
    }

  for (result = value; shift > 0; shift--)
    {
    QUADLIB_U64_SHL1_EQ_INLINE (result);
    }

  return result;

  } /* QuadLibU64SHL */

#endif /* QUADLIB_ENABLED */

/*****************************************************************************/
/*            QuadLibI64SHR1                                                 */
/*  shift a signed 64-bit value right by 1 bit                               */
/*****************************************************************************/

#ifdef QUADLIB_ENABLED

QUADLIB_STATIC QUADLIB_I64  QuadLibI64SHR1 (
  QUADLIB_I64               /*I*/ value)
  {
  QUADLIB_I64_SHR1_EQ_INLINE (value);

  return value;

  } /* QuadLibI64SHR1 */

#endif /* QUADLIB_ENABLED */

/*****************************************************************************/
/*            QuadLibU64SHR1                                                 */
/*  shift an unsigned 64-bit value right by 1 bit                            */
/*****************************************************************************/

#ifdef QUADLIB_ENABLED

QUADLIB_STATIC QUADLIB_U64  QuadLibU64SHR1 (
  QUADLIB_U64               /*I*/ value)
  {
  QUADLIB_U64_SHR1_EQ_INLINE (value);

  return value;

  } /* QuadLibU64SHR1 */

#endif /* QUADLIB_ENABLED */

/*****************************************************************************/
/*            QuadLibU64SHL1                                                 */
/*  shift an unsigned 64-bit value left by 1 bit                             */
/*****************************************************************************/

#ifdef QUADLIB_ENABLED

QUADLIB_STATIC QUADLIB_U64  QuadLibU64SHL1 (
  QUADLIB_U64               /*I*/ value)
  {
  QUADLIB_U64_SHL1_EQ_INLINE (value);

  return value;

  } /* QuadLibU64SHL1 */

#endif /* QUADLIB_ENABLED */

/*****************************************************************************/
/*            QuadLibI64SHR4                                                 */
/*  shift a signed 64-bit value right by 1 bit                               */
/*****************************************************************************/

#ifdef QUADLIB_ENABLED

QUADLIB_STATIC QUADLIB_I64  QuadLibI64SHR4 (
  QUADLIB_I64               /*I*/ value)
  {
  QUADLIB_I64_SHR4_EQ_INLINE (value);

  return value;

  } /* QuadLibI64SHR4 */

#endif /* QUADLIB_ENABLED */

/*****************************************************************************/
/*            QuadLibU64SHR4                                                 */
/*  shift an unsigned 64-bit value right by 1 bit                            */
/*****************************************************************************/

#ifdef QUADLIB_ENABLED

QUADLIB_STATIC QUADLIB_U64  QuadLibU64SHR4 (
  QUADLIB_U64               /*I*/ value)
  {
  QUADLIB_U64_SHR4_EQ_INLINE (value);

  return value;

  } /* QuadLibU64SHR4 */

#endif /* QUADLIB_ENABLED */

/*****************************************************************************/
/*            QuadLibU64SHL4                                                 */
/*  shift an unsigned 64-bit value left by 1 bit                             */
/*****************************************************************************/

#ifdef QUADLIB_ENABLED

QUADLIB_STATIC QUADLIB_U64  QuadLibU64SHL4 (
  QUADLIB_U64               /*I*/ value)
  {
  QUADLIB_U64_SHL4_EQ_INLINE (value);

  return value;

  } /* QuadLibU64SHL4 */

#endif /* QUADLIB_ENABLED */

/*****************************************************************************/
/*            QuadLibU64Mul10                                                */
/*  multiple an unsigned 64-bit value by 10                                  */
/*  method: get value*2 and value*8 by shifting, and then add them           */
/*****************************************************************************/

#ifdef QUADLIB_ENABLED

QUADLIB_STATIC QUADLIB_U64  QuadLibU64Mul10 (
  QUADLIB_U64               /*I*/ value)
  {
  QUADLIB_U64               value2;
  QUADLIB_U64               value8;
  QUADLIB_U64               result;

  value2 = value;
  QUADLIB_U64_SHL1_EQ_INLINE (value2);

  value8 = value2;
  QUADLIB_U64_SHL1_EQ_INLINE (value8);
  QUADLIB_U64_SHL1_EQ_INLINE (value8);

  result = QuadLibU64Add (value8, value2);
  return result;

  } /* QuadLibU64Mul10 */

#endif /* QUADLIB_ENABLED */

/*****************************************************************************/
/*            StrAToQuadLibU64                                               */
/*  define a portable 64-bit unsigned atoi convervion                        */
/*****************************************************************************/

/* StrAToQuadLibU64 always defined */

QUADLIB_STATIC QUADLIB_U64  StrAToQuadLibU64 (
  char *                    /*I*/ str)
  {
  /* extract a decimal string and return unsigned int64 value */

  QUADLIB_U64               result = QUADLIB_ZERO;
  QUADLIB_U64               digit = QUADLIB_ZERO;
  int32_t                   n;

  if (str == NULL)
    {
    return result;
    }

  while (isspace (*str))
    {
    str++;                  /* skip over whitespace */
    }

  /* handle hex value if present */

  if ( (str[0] == '0') && ((str[1] == 'x') || (str[1] == 'X')) )
    {
    for (str+=2; *str; str++)
      {
      n = *str;

      if ((n >= '0') && (n <= '9'))
        {
        n -= '0';
        }

      else if ((n >= 'A') && (n <= 'F'))
        {
        n = n - 'A' + 10;
        }

      else if ((n >= 'a') && (n <= 'f'))
        {
        n = n - 'a' + 10;
        }

      else
        {
        break;
        }

      QUADLIB_I64_LO(digit) = n;
      QUADLIB_U64_SHL1_EQ_INLINE (result);
      QUADLIB_U64_ADD_EQ (result, digit);
      } /* for */

    return result;
    }

  for (; *str; str++)
    {
    n = (*str) - '0';

    if ((n < 0) || (n > 9))
      {
      break;
      }

    QUADLIB_I64_LO(digit) = n;
    QUADLIB_U64_MUL10_EQ (result);
    QUADLIB_U64_ADD_EQ (result, digit);
    }

  return result;

  } /* StrAToQuadLibU64 */

/* StrAToQuadLibU64 always defined */

/*****************************************************************************/
/*            StrAToQuadLibI64                                               */
/*  define a portable 64-bit signed atoi convervion                          */
/*****************************************************************************/

/* StrAToQuadLibI64 always defined */

QUADLIB_STATIC QUADLIB_I64  StrAToQuadLibI64 (
  char *                    /*I*/ str)
  {
  /* extract a decimal string and return unsigned int64 value */

  QUADLIB_I64               result = QUADLIB_ZERO;
  QUADLIB_I64               digit = QUADLIB_ZERO;
  char                      sign = ' ';
  int32_t                   n;

  if (str == NULL)
    {
    return result;
    }

  while (isspace (*str))
    {
    str++;                  /* skip over whitespace */
    }

  if ((*str == '+') || (*str == '-'))
    {
    sign = *str;
    str++;                  /* skip over sign */
    }

  /* handle hex value if present */

  if ( (str[0] == '0') && ((str[1] == 'x') || (str[1] == 'X')) )
    {
    for (str+=2; *str; str++)
      {
      n = *str;

      if ((n >= '0') && (n <= '9'))
        {
        n -= '0';
        }

      else if ((n >= 'A') && (n <= 'F'))
        {
        n = n - 'A' + 10;
        }

      else if ((n >= 'a') && (n <= 'f'))
        {
        n = n - 'a' + 10;
        }

      else
        {
        break;
        }

      QUADLIB_I64_LO(digit) = n;
      QUADLIB_U64_SHL1_EQ_INLINE (result);
      QUADLIB_U64_ADD_EQ (result, digit);
      } /* for */

    if (sign == '-')
      {
      result = QUADLIB_I64_NEG (result);
      }

    return result;
    }

  for (; *str; str++)
    {
    n = (*str) - '0';

    if ((n < 0) || (n > 9))
      {
      break;
      }

    QUADLIB_I64_LO(digit) = n;
    QUADLIB_I64_MUL10_EQ (result);
    QUADLIB_I64_ADD_EQ (result, digit);
    }

  if (sign == '-')
    {
    result = QUADLIB_I64_NEG (result);
    }

  return result;

  } /* StrAToQuadLibI64 */

/* StrAToQuadLibI64 always defined */


/*****************************************************************************/
/*            QuadLibU64ToFixedStrA                                          */
/*  define a portable 64-bit unsigned itoa convervion                        */
/*****************************************************************************/

/* QuadLibU64ToFixedStrA always defined */

QUADLIB_STATIC void         QuadLibU64ToFixedStrA (
  QUADLIB_U64               /*I*/ qValue,
  QUADLIB_STRINGA *         /*O*/ pString)
  {
  QUADLIB_U64               value = qValue;
  int32_t                   i;
  int32_t                   n;

  int32_t                   xdigit;
  uint8_t                   sum [QUADLIB_STRING_LEN] = {0};
  uint8_t                   carry;

  char *                    factor;
  static char *             factor_table[16] =
    {
    "00000000000000000001",
    "00000000000000000016",
    "00000000000000000256",
    "00000000000000004096",
    "00000000000000065536",
    "00000000000001048576",
    "00000000000016777216",
    "00000000000268435456",
    "00000000004294967296",
    "00000000068719476736",
    "00000001099511627776",
    "00000017592186044416",
    "00000281474976710656",
    "00004503599627370496",
    "00072057594037927936",
    "01152921504606846976",
    };

  /* grab 16 hex digits one at a time */
  /* obtain the decimal factor for a '1' in that position */
  /* multiply each decimal digit of the factor by the hex digit */
  /* and add to sum */

  for (i=0; i < 16; i++)
    {
    if (QUADLIB_U64_EQ_0 (value))
      {
      break;
      }

    xdigit = QUADLIB_U64_LO (value) & 0x0F;
    QUADLIB_U64_SHR4_EQ (value);

    if (xdigit == 0)
      {
      continue;     /* nothing to do */
      }

    factor = factor_table [i];

    /* highest value in any position is (15*9)+9 = 135+9 = 144 */
    /* with a carry in of 9, the highest value is 153 */
    /* recall that factor is ASCII when calculating the sum */

    carry = 0;

#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4244)
/* silence the VC compiler warning message: */
/* "conversion from 'int ' to 'unsigned __int8', possible loss of data" */
#endif

    for (n = QUADLIB_STRING_LEN-1; n >= 0; n--)
      {
      sum[n] += (uint8_t) (((factor[n] & 0x0F) * xdigit) + carry);
      carry = sum[n] / 10;
      sum[n] %= 10;
      }
    }

#ifdef _MSC_VER
#pragma warning(pop)
#endif

  /* copy sum to output, converting to ASCII */

  for (n = 0; n < QUADLIB_STRING_LEN; n++)
    {
    pString->str[n] = (char) ((sum[n] & 0x0F) + '0');
    }

  pString->str[QUADLIB_STRING_LEN] = 0;

  } /* QuadLibU64ToFixedStrA */


/*****************************************************************************/
/*            QuadLibI64ToFixedStrA                                          */
/*  define a portable 64-bit signed itoa convervion                          */
/*****************************************************************************/

/* QuadLibI64ToFixedStrA always defined */

QUADLIB_STATIC void         QuadLibI64ToFixedStrA (
  QUADLIB_I64               /*I*/ qValue,
  QUADLIB_STRINGA *         /*O*/ pString)
  {
  char                      sign = '0';

  if (QUADLIB_I64_LT_0 (qValue))
    {
    qValue = QUADLIB_I64_NEG (qValue);
    sign = '-';
    }

  QuadLibU64ToFixedStrA (* (QUADLIB_U64 *) &qValue, pString);
  pString->str[0] = sign;

  } /* QuadLibI64ToFixedStrA */


/*****************************************************************************/
/*            QuadLibU64ToStrA                                               */
/*  define a portable 64-bit signed itoa convervion, with zero suppression   */
/*****************************************************************************/

/* QuadLibU64ToStrA always defined */

QUADLIB_STATIC void         QuadLibU64ToStrA (
  QUADLIB_U64               /*I*/ qValue,
  QUADLIB_STRINGA *         /*O*/ pString)
  {
  QUADLIB_STRINGA           s;
  int32_t                   source = 0;
  int32_t                   target = 0;

  QuadLibU64ToFixedStrA (qValue, &s);

  /* copy back value, suppressing leading zeros */

  while (s.str [source] == '0')
    {
    source++;
    }

  if (s.str [source] == 0)
    {
    pString->str [target++] = '0';  /* source is all zero */
    }

  while (s.str [source])
    {
    pString->str [target++] = s.str [source++];
    }

  pString->str [target] = 0;

  } /* QuadLibU64ToStrA */


/*****************************************************************************/
/*            QuadLibI64ToStrA                                               */
/*  define a portable 64-bit signed itoa convervion, with zero suppression   */
/*****************************************************************************/

/* QuadLibI64ToStrA always defined */

QUADLIB_STATIC void         QuadLibI64ToStrA (
  QUADLIB_I64               /*I*/ qValue,
  QUADLIB_STRINGA *         /*O*/ pString)
  {
  QUADLIB_STRINGA           s;
  int32_t                   source = 0;
  int32_t                   target = 0;

  QuadLibI64ToFixedStrA (qValue, &s);

  /* copy back value, suppressing leading zeros */

  if (s.str [source] == '-')
    {
    pString->str [target++] = s.str [source++];
    }

  while (s.str [source] == '0')
    {
    source++;
    }

  if (s.str [source] == 0)
    {
    pString->str [target++] = '0';  /* source is all zero */
    }

  while (s.str [source])
    {
    pString->str [target++] = s.str [source++];
    }

  pString->str [target] = 0;

  } /* QuadLibI64ToStrA */


/*****************************************************************************/
/*            QuadLibU64DivMod                                               */
/*  divide two unsigned 64-bit values to produce a quotient and remainder    */
/*****************************************************************************/

#ifdef QUADLIB_ENABLED

QUADLIB_STATIC QUADLIB_U64  QuadLibU64DivMod (
  QUADLIB_U64               u64Dividend,
  QUADLIB_U64               u64Divisor,
  QUADLIB_U64               *pu64Remainder )
  {
  int32_t                   Count;
  QUADLIB_U64               A_restore;

  QUADLIB_U64               A = QUADLIB_ZERO;
  QUADLIB_U64               M = u64Divisor;
  QUADLIB_U64               Q = u64Dividend;


  if ((u64Divisor.hi.u == 0)
  &&  (u64Divisor.lo.u == 0))
    {
    *pu64Remainder = A;         /* 0 */
    return A;                   /* 0 */
    }


  for (Count=64; Count; Count--)
    {
    /* shift A:Q left */

    QUADLIB_U64_SHL1_EQ_INLINE (A);

    if (Q.hi.i < 0)             /* Q has high-order 1 bit */
      {
      A = QuadLibU64Add1(A);    /* A gets low-order 1 bit */
      }

    QUADLIB_U64_SHL1_EQ_INLINE (Q); /* Q gets low-order 0 bit */

    A_restore = A;

    A = QuadLibU64Sub(A, M);

    if (A.hi.i < 0)             /* A has high-order 1 bit */
      {
      A = A_restore;            /* Q has low-order 0 bit */
      }

    else
      {
      Q = QuadLibU64Add1(Q);    /* Q gets low-order 1 bit */
      }

    } /* for */

    *pu64Remainder = A;
    return Q;

  } /* QuadLibU64DivMod */

#endif /* QUADLIB_ENABLED */

/*****************************************************************************/
/*            QuadLibU64Div                                                  */
/*  divide two signed 64-bit values to produce an unsigned quotient          */
/*****************************************************************************/

#ifdef QUADLIB_ENABLED

QUADLIB_STATIC QUADLIB_U64  QuadLibU64Div (
  QUADLIB_U64               u64Dividend,
  QUADLIB_U64               u64Divisor)
  {
  QUADLIB_U64               u64Remainder;      /* dummy argument */

  return QuadLibU64DivMod (u64Dividend, u64Divisor, &u64Remainder);

  } /*QuadLibU64Div */

#endif /* QUADLIB_ENABLED */

/*****************************************************************************/
/*            QuadLibU64Mod                                                  */
/*  divide two signed 64-bit values to produce unsigned remainder (modulus)  */
/*****************************************************************************/

#ifdef QUADLIB_ENABLED

QUADLIB_STATIC QUADLIB_U64  QuadLibU64Mod (
  QUADLIB_U64               u64Dividend,
  QUADLIB_U64               u64Divisor)
  {
  QUADLIB_U64               u64Remainder;

  QuadLibU64DivMod (u64Dividend, u64Divisor, &u64Remainder);

  return u64Remainder;

  } /*QuadLibU64Mod */

#endif /* QUADLIB_ENABLED */

/*****************************************************************************/
/*            QuadLibI64DivMod                                               */
/*  divide two signed 64-bit values to produce a quotient and remainder      */
/*****************************************************************************/

#ifdef QUADLIB_ENABLED

QUADLIB_STATIC QUADLIB_I64  QuadLibI64DivMod (
  QUADLIB_I64               s64Dividend,
  QUADLIB_I64               s64Divisor,
  QUADLIB_I64               *ps64Remainder )
  {
  QUADLIB_I64               s64Quotient;
  QUADLIB_U64               u64Dividend;
  QUADLIB_U64               u64Divisor;
  int32_t                   sign = 0;

  if (s64Dividend.hi.i < 0)
    {
    u64Dividend = QUADLIB_I64_NEG (s64Dividend);
    sign = 1;
    }

  else
    {
    u64Dividend = s64Dividend;
    }

  if (s64Divisor.hi.i < 0)
    {
    u64Divisor = QUADLIB_I64_NEG (s64Divisor);
    sign ^= 1;
    }

  else
    {
    u64Divisor = s64Divisor;
    }

  s64Quotient = QuadLibU64DivMod (u64Dividend, u64Divisor, ps64Remainder);

  if (sign)
    {
    /* signs of dividend and divisor are different, result is neg */
    s64Quotient = QUADLIB_I64_NEG (s64Quotient);
    }

  /* remainder takes sign of dividend */

  if (s64Dividend.hi.i < 0)
    {
    *ps64Remainder = QUADLIB_I64_NEG (*ps64Remainder);
    }

  return s64Quotient;

} /* QuadLibI64DivMod */

#endif /* QUADLIB_ENABLED */

/*****************************************************************************/
/*            QuadLibI64Div                                                  */
/*  divide two signed 64-bit values to produce a signed quotient             */
/*****************************************************************************/

#ifdef QUADLIB_ENABLED

QUADLIB_STATIC QUADLIB_I64  QuadLibI64Div (
  QUADLIB_I64               s64Dividend,
  QUADLIB_I64               s64Divisor)
  {
  QUADLIB_U64               s64Remainder;      /* dummy argument */

  return QuadLibI64DivMod (s64Dividend, s64Divisor, &s64Remainder);

  } /*QuadLibI64Div */

#endif /* QUADLIB_ENABLED */

/*****************************************************************************/
/*            QuadLibI64Mod                                                  */
/*  divide two signed 64-bit values to produce a signed remainder (modulus)  */
/*****************************************************************************/

#ifdef QUADLIB_ENABLED

QUADLIB_STATIC QUADLIB_I64  QuadLibI64Mod (
  QUADLIB_I64               s64Dividend,
  QUADLIB_I64               s64Divisor)
  {
  QUADLIB_I64               s64Remainder;

  QuadLibI64DivMod (s64Dividend, s64Divisor, &s64Remainder);

  return s64Remainder;

  } /*QuadLibI64Mod */

#endif /* QUADLIB_ENABLED */

/*****************************************************************************/
/*            QuadLibU64DivMod - COMPATIBILITY MODE                          */
/*            QuadLibI64DivMod - COMPATIBILITY MODE                          */
/*  native I64 divide/modulus functions with I64 simulation not in effect    */
/*****************************************************************************/

#ifndef QUADLIB_ENABLED

QUADLIB_STATIC QUADLIB_U64          QuadLibU64DivMod (
  QUADLIB_U64                       u64Dividend,
  QUADLIB_U64                       u64Divisor,
  QUADLIB_U64                       *pu64Remainder)
  {
  if (pu64Remainder != NULL)
    {
    *pu64Remainder = u64Dividend % u64Divisor;
    }

  return u64Dividend / u64Divisor;

  } /* QuadLibU64DivMod */

QUADLIB_STATIC QUADLIB_I64          QuadLibI64DivMod (
  QUADLIB_I64                       s64Dividend,
  QUADLIB_I64                       s64Divisor,
  QUADLIB_I64                       *ps64Remainder)
  {
  if (ps64Remainder != NULL)
    {
    *ps64Remainder = s64Dividend % s64Divisor;
    }

  return s64Dividend / s64Divisor;

  } /* QuadLibI64DivMod */

#endif /* not QUADLIB_ENABLED */


/*****************************************************************************/
/*  AND                                                                      */
/*****************************************************************************/

#ifdef QUADLIB_ENABLED

QUADLIB_STATIC QUADLIB_U64          QuadLibU64And (
  QUADLIB_U64                       /*I*/ one,
  QUADLIB_U64                       /*I*/ two)
  {
  QUADLIB_U64                       result;

  result.hi.u = one.hi.u & two.hi.u;
  result.lo.u = one.lo.u & two.lo.u;

  return result;
  }

QUADLIB_STATIC QUADLIB_U64          QuadLibU64AndNot (
  QUADLIB_U64                       /*I*/ one,
  QUADLIB_U64                       /*I*/ two)
  {
  QUADLIB_U64                       result;

  result.hi.u = one.hi.u & (~two.hi.u);
  result.lo.u = one.lo.u & (~two.lo.u);

  return result;
  }

#endif /* QUADLIB_ENABLED */

/*****************************************************************************/
/*  OR                                                                       */
/*****************************************************************************/

#ifdef QUADLIB_ENABLED

QUADLIB_STATIC QUADLIB_U64          QuadLibU64Or (
  QUADLIB_U64                       /*I*/ one,
  QUADLIB_U64                       /*I*/ two)
  {
  QUADLIB_U64                       result;

  result.hi.u = one.hi.u | two.hi.u;
  result.lo.u = one.lo.u | two.lo.u;

  return result;
  }

QUADLIB_STATIC QUADLIB_U64          QuadLibU64OrNot (
  QUADLIB_U64                       /*I*/ one,
  QUADLIB_U64                       /*I*/ two)
  {
  QUADLIB_U64                       result;

  result.hi.u = one.hi.u | (~two.hi.u);
  result.lo.u = one.lo.u | (~two.lo.u);

  return result;
  }

#endif /* QUADLIB_ENABLED */

/*****************************************************************************/
/*  XOR                                                                      */
/*****************************************************************************/

#ifdef QUADLIB_ENABLED

QUADLIB_STATIC QUADLIB_U64          QuadLibU64Xor (
  QUADLIB_U64                       /*I*/ one,
  QUADLIB_U64                       /*I*/ two)
  {
  QUADLIB_U64                       result;

  result.hi.u = one.hi.u ^ two.hi.u;
  result.lo.u = one.lo.u ^ two.lo.u;

  return result;
  }

QUADLIB_STATIC QUADLIB_U64          QuadLibU64XorNot (
  QUADLIB_U64                       /*I*/ one,
  QUADLIB_U64                       /*I*/ two)
  {
  QUADLIB_U64                       result;

  result.hi.u = one.hi.u ^ (~two.hi.u);
  result.lo.u = one.lo.u ^ (~two.lo.u);

  return result;
  }

#endif /* QUADLIB_ENABLED */

