/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/*
** testll.c -- test suite for 64bit integer (longlong) operations
**
** Summary: testll [-d] | [-h]
**
** Where:
** -d       set debug mode on; displays individual test failures
** -v       verbose mode; displays progress in test, plus -d
** -h       gives usage message.
**
** Description:
** lltest.c tests the functions defined in NSPR 2.0's prlong.h.
**
** Successive tests begin to depend on other LL functions working
** correctly. So, ... Do not change the order of the tests as run
** from main().
**
** Caveats:
** Do not even begin to think that this is an exhaustive test!
**
** These tests try a little of everything, but not all boundary
** conditions and limits are tested.
** You want better coverage? ... Add it.
**
** ---
** Author: Lawrence Hardiman <larryh@netscape.com>.
** ---
** Revision History:
** 01-Oct-1997. Original implementation.
**
*/

#include "nspr.h"
#include "plgetopt.h"

/* --- Local Definitions --- */
#define ReportProgress(m) \
  if (verboseMode) PR_fprintf(output, (m));

/* --- Global variables --- */
static PRIntn failedAlready = 0;
static PRFileDesc* output = NULL;
static PRBool debugMode = PR_FALSE;
static PRBool verboseMode = PR_FALSE;

/*
** Constants used in tests.
*/
const PRInt64 bigZero = LL_INIT(0, 0);
const PRInt64 bigOne = LL_INIT(0, 1);
const PRInt64 bigTwo = LL_INIT(0, 2);
const PRInt64 bigSixTeen = LL_INIT(0, 16);
const PRInt64 bigThirtyTwo = LL_INIT(0, 32);
const PRInt64 bigMinusOne = LL_INIT(0xffffffff, 0xffffffff);
const PRInt64 bigMinusTwo = LL_INIT(0xffffffff, 0xfffffffe);
const PRInt64 bigNumber = LL_INIT(0x7fffffff, 0xffffffff);
const PRInt64 bigMinusNumber = LL_INIT(0x80000000, 0x00000001);
const PRInt64 bigMaxInt32 = LL_INIT(0x00000000, 0x7fffffff);
const PRInt64 big2To31 = LL_INIT(0x00000000, 0x80000000);
const PRUint64 bigZeroFox = LL_INIT(0x00000000, 0xffffffff);
const PRUint64 bigFoxFox = LL_INIT(0xffffffff, 0xffffffff);
const PRUint64 bigFoxZero = LL_INIT(0xffffffff, 0x00000000);
const PRUint64 bigEightZero = LL_INIT(0x80000000, 0x00000000);
const PRUint64 big64K = LL_INIT(0x00000000, 0x00010000);
const PRInt64 bigInt0 = LL_INIT(0x01a00000, 0x00001000);
const PRInt64 bigInt1 = LL_INIT(0x01a00000, 0x00001100);
const PRInt64 bigInt2 = LL_INIT(0x01a00000, 0x00000100);
const PRInt64 bigInt3 = LL_INIT(0x01a00001, 0x00001000);
const PRInt64 bigInt4 = LL_INIT(0x01a00001, 0x00001100);
const PRInt64 bigInt5 = LL_INIT(0x01a00001, 0x00000100);
const PRInt64 bigInt6 = LL_INIT(0xb1a00000, 0x00001000);
const PRInt64 bigInt7 = LL_INIT(0xb1a00000, 0x00001100);
const PRInt64 bigInt8 = LL_INIT(0xb1a00000, 0x00000100);
const PRInt64 bigInt9 = LL_INIT(0xb1a00001, 0x00001000);
const PRInt64 bigInt10 = LL_INIT(0xb1a00001, 0x00001100);
const PRInt64 bigInt11 = LL_INIT(0xb1a00001, 0x00000100);
const PRInt32 one = 1l;
const PRInt32 minusOne = -1l;
const PRInt32 sixteen = 16l;
const PRInt32 thirtyTwo = 32l;
const PRInt32 sixtyThree = 63l;

/*
** SetFailed() -- Report individual test failure
**
*/
static void SetFailed(char* what, char* how) {
  failedAlready = 1;
  if (debugMode) {
    PR_fprintf(output, "%s: failed: %s\n", what, how);
  }
  return;
}

static void ResultFailed(char* what, char* how, PRInt64 expected, PRInt64 got) {
  if (debugMode) {
    SetFailed(what, how);
    PR_fprintf(output, "Expected: 0x%llx   Got: 0x%llx\n", expected, got);
  }
  return;
}

/*
** TestAssignment() -- Test the assignment
*/
static void TestAssignment(void) {
  PRInt64 zero = LL_Zero();
  PRInt64 min = LL_MinInt();
  PRInt64 max = LL_MaxInt();
  if (!LL_EQ(zero, bigZero)) {
    SetFailed("LL_EQ(zero, bigZero)", "!=");
  }
  if (!LL_CMP(max, >, min)) {
    SetFailed("LL_CMP(max, >, min)", "!>");
  }
}

/*
** TestComparisons() -- Test the longlong comparison operations
*/
static void TestComparisons(void) {
  ReportProgress("Testing Comparisons Operations\n");

  /* test for zero */
  if (!LL_IS_ZERO(bigZero)) {
    SetFailed("LL_IS_ZERO", "Zero is not zero");
  }

  if (LL_IS_ZERO(bigOne)) {
    SetFailed("LL_IS_ZERO", "One tests as zero");
  }

  if (LL_IS_ZERO(bigMinusOne)) {
    SetFailed("LL_IS_ZERO", "Minus One tests as zero");
  }

  /* test equal */
  if (!LL_EQ(bigZero, bigZero)) {
    SetFailed("LL_EQ", "zero EQ zero");
  }

  if (!LL_EQ(bigOne, bigOne)) {
    SetFailed("LL_EQ", "one EQ one");
  }

  if (!LL_EQ(bigNumber, bigNumber)) {
    SetFailed("LL_EQ", "bigNumber EQ bigNumber");
  }

  if (!LL_EQ(bigMinusOne, bigMinusOne)) {
    SetFailed("LL_EQ", "minus one EQ minus one");
  }

  if (LL_EQ(bigZero, bigOne)) {
    SetFailed("LL_EQ", "zero EQ one");
  }

  if (LL_EQ(bigOne, bigZero)) {
    SetFailed("LL_EQ", "one EQ zero");
  }

  if (LL_EQ(bigMinusOne, bigOne)) {
    SetFailed("LL_EQ", "minus one EQ one");
  }

  if (LL_EQ(bigNumber, bigOne)) {
    SetFailed("LL_EQ", "bigNumber EQ one");
  }

  /* test not equal */
  if (LL_NE(bigZero, bigZero)) {
    SetFailed("LL_NE", "0 NE 0");
  }

  if (LL_NE(bigOne, bigOne)) {
    SetFailed("LL_NE", "1 NE 1");
  }

  if (LL_NE(bigMinusOne, bigMinusOne)) {
    SetFailed("LL_NE", "-1 NE -1");
  }

  if (LL_NE(bigNumber, bigNumber)) {
    SetFailed("LL_NE", "n NE n");
  }

  if (LL_NE(bigMinusNumber, bigMinusNumber)) {
    SetFailed("LL_NE", "-n NE -n");
  }

  if (!LL_NE(bigZero, bigOne)) {
    SetFailed("LL_NE", "0 NE 1");
  }

  if (!LL_NE(bigOne, bigMinusNumber)) {
    SetFailed("LL_NE", "1 NE -n");
  }

  /* Greater than or equal to zero */
  if (!LL_GE_ZERO(bigZero)) {
    SetFailed("LL_GE_ZERO", "0");
  }

  if (!LL_GE_ZERO(bigOne)) {
    SetFailed("LL_GE_ZERO", "1");
  }

  if (!LL_GE_ZERO(bigNumber)) {
    SetFailed("LL_GE_ZERO", "n");
  }

  if (LL_GE_ZERO(bigMinusOne)) {
    SetFailed("LL_GE_ZERO", "-1");
  }

  if (LL_GE_ZERO(bigMinusNumber)) {
    SetFailed("LL_GE_ZERO", "-n");
  }

  /* Algebraic Compare two values */
  if (!LL_CMP(bigZero, ==, bigZero)) {
    SetFailed("LL_CMP", "0 == 0");
  }

  if (LL_CMP(bigZero, >, bigZero)) {
    SetFailed("LL_CMP", "0 > 0");
  }

  if (LL_CMP(bigZero, <, bigZero)) {
    SetFailed("LL_CMP", "0 < 0");
  }

  if (LL_CMP(bigNumber, <, bigOne)) {
    SetFailed("LL_CMP", "n < 1");
  }

  if (!LL_CMP(bigNumber, >, bigOne)) {
    SetFailed("LL_CMP", "n <= 1");
  }

  if (LL_CMP(bigOne, >, bigNumber)) {
    SetFailed("LL_CMP", "1 > n");
  }

  if (LL_CMP(bigMinusNumber, >, bigNumber)) {
    SetFailed("LL_CMP", "-n > n");
  }

  if (LL_CMP(bigNumber, !=, bigNumber)) {
    SetFailed("LL_CMP", "n != n");
  }

  if (!LL_CMP(bigMinusOne, >, bigMinusTwo)) {
    SetFailed("LL_CMP", "-1 <= -2");
  }

  if (!LL_CMP(bigMaxInt32, <, big2To31)) {
    SetFailed("LL_CMP", "Max 32-bit signed int >= 2^31");
  }

  /* Two positive numbers */
  if (!LL_CMP(bigInt0, <=, bigInt0)) {
    SetFailed("LL_CMP", "LL_CMP(<=) failed");
  }

  if (!LL_CMP(bigInt0, <=, bigInt1)) {
    SetFailed("LL_CMP", "LL_CMP(<=) failed");
  }

  if (LL_CMP(bigInt0, <=, bigInt2)) {
    SetFailed("LL_CMP", "LL_CMP(<=) failed");
  }

  if (!LL_CMP(bigInt0, <=, bigInt3)) {
    SetFailed("LL_CMP", "LL_CMP(<=) failed");
  }

  if (!LL_CMP(bigInt0, <=, bigInt4)) {
    SetFailed("LL_CMP", "LL_CMP(<=) failed");
  }

  if (!LL_CMP(bigInt0, <=, bigInt5)) {
    SetFailed("LL_CMP", "LL_CMP(<=) failed");
  }

  /* Two negative numbers */
  if (!LL_CMP(bigInt6, <=, bigInt6)) {
    SetFailed("LL_CMP", "LL_CMP(<=) failed");
  }

  if (!LL_CMP(bigInt6, <=, bigInt7)) {
    SetFailed("LL_CMP", "LL_CMP(<=) failed");
  }

  if (LL_CMP(bigInt6, <=, bigInt8)) {
    SetFailed("LL_CMP", "LL_CMP(<=) failed");
  }

  if (!LL_CMP(bigInt6, <=, bigInt9)) {
    SetFailed("LL_CMP", "LL_CMP(<=) failed");
  }

  if (!LL_CMP(bigInt6, <=, bigInt10)) {
    SetFailed("LL_CMP", "LL_CMP(<=) failed");
  }

  if (!LL_CMP(bigInt6, <=, bigInt11)) {
    SetFailed("LL_CMP", "LL_CMP(<=) failed");
  }

  /* One positive, one negative */
  if (LL_CMP(bigInt0, <=, bigInt6)) {
    SetFailed("LL_CMP", "LL_CMP(<=) failed");
  }

  if (LL_CMP(bigInt0, <=, bigInt7)) {
    SetFailed("LL_CMP", "LL_CMP(<=) failed");
  }

  if (LL_CMP(bigInt0, <=, bigInt8)) {
    SetFailed("LL_CMP", "LL_CMP(<=) failed");
  }

  /* Bitwise Compare two numbers */
  if (!LL_UCMP(bigZero, ==, bigZero)) {
    SetFailed("LL_UCMP", "0 == 0");
  }

  if (LL_UCMP(bigZero, >, bigZero)) {
    SetFailed("LL_UCMP", "0 > 0");
  }

  if (LL_UCMP(bigZero, <, bigZero)) {
    SetFailed("LL_UCMP", "0 < 0");
  }

  if (LL_UCMP(bigNumber, <, bigOne)) {
    SetFailed("LL_UCMP", "n < 1");
  }

  if (!LL_UCMP(bigNumber, >, bigOne)) {
    SetFailed("LL_UCMP", "n < 1");
  }

  if (LL_UCMP(bigOne, >, bigNumber)) {
    SetFailed("LL_UCMP", "1 > n");
  }

  if (LL_UCMP(bigMinusNumber, <, bigNumber)) {
    SetFailed("LL_UCMP", "-n < n");
  }

  /* Two positive numbers */
  if (!LL_UCMP(bigInt0, <=, bigInt0)) {
    SetFailed("LL_UCMP", "LL_UCMP(<=) failed");
  }

  if (!LL_UCMP(bigInt0, <=, bigInt1)) {
    SetFailed("LL_UCMP", "LL_UCMP(<=) failed");
  }

  if (LL_UCMP(bigInt0, <=, bigInt2)) {
    SetFailed("LL_UCMP", "LL_UCMP(<=) failed");
  }

  if (!LL_UCMP(bigInt0, <=, bigInt3)) {
    SetFailed("LL_UCMP", "LL_UCMP(<=) failed");
  }

  if (!LL_UCMP(bigInt0, <=, bigInt4)) {
    SetFailed("LL_UCMP", "LL_UCMP(<=) failed");
  }

  if (!LL_UCMP(bigInt0, <=, bigInt5)) {
    SetFailed("LL_UCMP", "LL_UCMP(<=) failed");
  }

  /* Two negative numbers */
  if (!LL_UCMP(bigInt6, <=, bigInt6)) {
    SetFailed("LL_UCMP", "LL_UCMP(<=) failed");
  }

  if (!LL_UCMP(bigInt6, <=, bigInt7)) {
    SetFailed("LL_UCMP", "LL_UCMP(<=) failed");
  }

  if (LL_UCMP(bigInt6, <=, bigInt8)) {
    SetFailed("LL_UCMP", "LL_UCMP(<=) failed");
  }

  if (!LL_UCMP(bigInt6, <=, bigInt9)) {
    SetFailed("LL_UCMP", "LL_UCMP(<=) failed");
  }

  if (!LL_UCMP(bigInt6, <=, bigInt10)) {
    SetFailed("LL_UCMP", "LL_UCMP(<=) failed");
  }

  if (!LL_UCMP(bigInt6, <=, bigInt11)) {
    SetFailed("LL_UCMP", "LL_UCMP(<=) failed");
  }

  /* One positive, one negative */
  if (!LL_UCMP(bigInt0, <=, bigInt6)) {
    SetFailed("LL_UCMP", "LL_UCMP(<=) failed");
  }

  if (!LL_UCMP(bigInt0, <=, bigInt7)) {
    SetFailed("LL_UCMP", "LL_UCMP(<=) failed");
  }

  if (!LL_UCMP(bigInt0, <=, bigInt8)) {
    SetFailed("LL_UCMP", "LL_UCMP(<=) failed");
  }

  return;
}

/*
**  TestLogicalOperations() -- Tests for AND, OR, ...
**
*/
static void TestLogicalOperations(void) {
  PRUint64 result, result2;

  ReportProgress("Testing Logical Operations\n");

  /* Test AND */
  LL_AND(result, bigZero, bigZero);
  if (!LL_IS_ZERO(result)) {
    ResultFailed("LL_AND", "0 & 0", bigZero, result);
  }

  LL_AND(result, bigOne, bigOne);
  if (LL_IS_ZERO(result)) {
    ResultFailed("LL_AND", "1 & 1", bigOne, result);
  }

  LL_AND(result, bigZero, bigOne);
  if (!LL_IS_ZERO(result)) {
    ResultFailed("LL_AND", "1 & 1", bigZero, result);
  }

  LL_AND(result, bigMinusOne, bigMinusOne);
  if (!LL_UCMP(result, ==, bigMinusOne)) {
    ResultFailed("LL_AND", "-1 & -1", bigMinusOne, result);
  }

  /* test OR */
  LL_OR(result, bigZero, bigZero);
  if (!LL_IS_ZERO(result)) {
    ResultFailed("LL_OR", "0 | 1", bigZero, result);
  }

  LL_OR(result, bigZero, bigOne);
  if (LL_IS_ZERO(result)) {
    ResultFailed("LL_OR", "0 | 1", bigOne, result);
  }

  LL_OR(result, bigZero, bigMinusNumber);
  if (!LL_UCMP(result, ==, bigMinusNumber)) {
    ResultFailed("LL_OR", "0 | -n", bigMinusNumber, result);
  }

  LL_OR(result, bigMinusNumber, bigZero);
  if (!LL_UCMP(result, ==, bigMinusNumber)) {
    ResultFailed("LL_OR", "-n | 0", bigMinusNumber, result);
  }

  /* test XOR */
  LL_XOR(result, bigZero, bigZero);
  if (LL_UCMP(result, !=, bigZero)) {
    ResultFailed("LL_XOR", "0 ^ 0", bigZero, result);
  }

  LL_XOR(result, bigOne, bigZero);
  if (LL_UCMP(result, !=, bigOne)) {
    ResultFailed("LL_XOR", "1 ^ 0", bigZero, result);
  }

  LL_XOR(result, bigMinusNumber, bigZero);
  if (LL_UCMP(result, !=, bigMinusNumber)) {
    ResultFailed("LL_XOR", "-n ^ 0", bigMinusNumber, result);
  }

  LL_XOR(result, bigMinusNumber, bigMinusNumber);
  if (LL_UCMP(result, !=, bigZero)) {
    ResultFailed("LL_XOR", "-n ^ -n", bigMinusNumber, result);
  }

  /* test OR2.  */
  result = bigZero;
  LL_OR2(result, bigOne);
  if (LL_UCMP(result, !=, bigOne)) {
    ResultFailed("LL_OR2", "(r=0) |= 1", bigOne, result);
  }

  result = bigOne;
  LL_OR2(result, bigNumber);
  if (LL_UCMP(result, !=, bigNumber)) {
    ResultFailed("LL_OR2", "(r=1) |= n", bigNumber, result);
  }

  result = bigMinusNumber;
  LL_OR2(result, bigMinusNumber);
  if (LL_UCMP(result, !=, bigMinusNumber)) {
    ResultFailed("LL_OR2", "(r=-n) |= -n", bigMinusNumber, result);
  }

  /* test NOT */
  LL_NOT(result, bigMinusNumber);
  LL_NOT(result2, result);
  if (LL_UCMP(result2, !=, bigMinusNumber)) {
    ResultFailed("LL_NOT", "r != ~(~-n)", bigMinusNumber, result);
  }

  /* test Negation */
  LL_NEG(result, bigMinusNumber);
  LL_NEG(result2, result);
  if (LL_CMP(result2, !=, bigMinusNumber)) {
    ResultFailed("LL_NEG", "r != -(-(-n))", bigMinusNumber, result);
  }

  return;
}

/*
**  TestConversion() -- Test Conversion Operations
**
*/
static void TestConversion(void) {
  PRInt64 result;
  PRInt64 resultU;
  PRInt32 result32;
  PRUint32 resultU32;
  float resultF;
  PRFloat64 resultD;

  ReportProgress("Testing Conversion Operations\n");

  /* LL_L2I  -- Convert to signed 32bit */
  LL_L2I(result32, bigOne);
  if (result32 != one) {
    SetFailed("LL_L2I", "r != 1");
  }

  LL_L2I(result32, bigMinusOne);
  if (result32 != minusOne) {
    SetFailed("LL_L2I", "r != -1");
  }

  /* LL_L2UI -- Convert 64bit to unsigned 32bit */
  LL_L2UI(resultU32, bigMinusOne);
  if (resultU32 != (PRUint32)minusOne) {
    SetFailed("LL_L2UI", "r != -1");
  }

  LL_L2UI(resultU32, bigOne);
  if (resultU32 != (PRUint32)one) {
    SetFailed("LL_L2UI", "r != 1");
  }

  /* LL_L2F  -- Convert to 32bit floating point */
  LL_L2F(resultF, bigOne);
  if (resultF != 1.0) {
    SetFailed("LL_L2F", "r != 1.0");
  }

  LL_L2F(resultF, bigMinusOne);
  if (resultF != -1.0) {
    SetFailed("LL_L2F", "r != 1.0");
  }

  /* LL_L2D  -- Convert to 64bit floating point */
  LL_L2D(resultD, bigOne);
  if (resultD != 1.0L) {
    SetFailed("LL_L2D", "r != 1.0");
  }

  LL_L2D(resultD, bigMinusOne);
  if (resultD != -1.0L) {
    SetFailed("LL_L2D", "r != -1.0");
  }

  /* LL_I2L  -- Convert 32bit signed to 64bit signed */
  LL_I2L(result, one);
  if (LL_CMP(result, !=, bigOne)) {
    SetFailed("LL_I2L", "r != 1");
  }

  LL_I2L(result, minusOne);
  if (LL_CMP(result, !=, bigMinusOne)) {
    SetFailed("LL_I2L", "r != -1");
  }

  /* LL_UI2L -- Convert 32bit unsigned to 64bit unsigned */
  LL_UI2L(resultU, (PRUint32)one);
  if (LL_CMP(resultU, !=, bigOne)) {
    SetFailed("LL_UI2L", "r != 1");
  }

  /* [lth.] This did not behave as expected, but it is correct
   */
  LL_UI2L(resultU, (PRUint32)minusOne);
  if (LL_CMP(resultU, !=, bigZeroFox)) {
    ResultFailed("LL_UI2L", "r != -1", bigZeroFox, resultU);
  }

  /* LL_F2L  -- Convert 32bit float to 64bit signed */
  LL_F2L(result, 1.0);
  if (LL_CMP(result, !=, bigOne)) {
    SetFailed("LL_F2L", "r != 1");
  }

  LL_F2L(result, -1.0);
  if (LL_CMP(result, !=, bigMinusOne)) {
    SetFailed("LL_F2L", "r != -1");
  }

  /* LL_D2L  -- Convert 64bit Float to 64bit signed */
  LL_D2L(result, 1.0L);
  if (LL_CMP(result, !=, bigOne)) {
    SetFailed("LL_D2L", "r != 1");
  }

  LL_D2L(result, -1.0L);
  if (LL_CMP(result, !=, bigMinusOne)) {
    SetFailed("LL_D2L", "r != -1");
  }

  return;
}

static void ShiftCompileOnly() {
  /*
  ** This function is only compiled, never called.
  ** The real test is to see if it compiles w/o
  ** warnings. This is no small feat, by the way.
  */
  PRInt64 ia, ib;
  PRUint64 ua, ub;
  LL_SHR(ia, ib, 32);
  LL_SHL(ia, ib, 32);

  LL_USHR(ua, ub, 32);
  LL_ISHL(ia, 49, 32);

} /* ShiftCompileOnly */

/*
**  TestShift() -- Test Shifting Operations
**
*/
static void TestShift(void) {
  static const PRInt64 largeTwoZero = LL_INIT(0x00000002, 0x00000000);
  PRInt64 result;
  PRUint64 resultU;

  ReportProgress("Testing Shifting Operations\n");

  /* LL_SHL  -- Shift left algebraic */
  LL_SHL(result, bigOne, one);
  if (LL_CMP(result, !=, bigTwo)) {
    ResultFailed("LL_SHL", "r != 2", bigOne, result);
  }

  LL_SHL(result, bigTwo, thirtyTwo);
  if (LL_CMP(result, !=, largeTwoZero)) {
    ResultFailed("LL_SHL", "r != twoZero", largeTwoZero, result);
  }

  /* LL_SHR  -- Shift right algebraic */
  LL_SHR(result, bigFoxZero, thirtyTwo);
  if (LL_CMP(result, !=, bigMinusOne)) {
    ResultFailed("LL_SHR", "r != -1", bigMinusOne, result);
  }

  LL_SHR(result, bigTwo, one);
  if (LL_CMP(result, !=, bigOne)) {
    ResultFailed("LL_SHR", "r != 1", bigOne, result);
  }

  LL_SHR(result, bigFoxFox, thirtyTwo);
  if (LL_CMP(result, !=, bigMinusOne)) {
    ResultFailed("LL_SHR", "r != -1 (was ff,ff)", bigMinusOne, result);
  }

  /* LL_USHR -- Logical shift right */
  LL_USHR(resultU, bigZeroFox, thirtyTwo);
  if (LL_UCMP(resultU, !=, bigZero)) {
    ResultFailed("LL_USHR", "r != 0 ", bigZero, result);
  }

  LL_USHR(resultU, bigFoxFox, thirtyTwo);
  if (LL_UCMP(resultU, !=, bigZeroFox)) {
    ResultFailed("LL_USHR", "r != 0 ", bigZeroFox, result);
  }

  /* LL_ISHL -- Shift a 32bit integer into a 64bit result */
  LL_ISHL(resultU, minusOne, thirtyTwo);
  if (LL_UCMP(resultU, !=, bigFoxZero)) {
    ResultFailed("LL_ISHL", "r != ff,00 ", bigFoxZero, result);
  }

  LL_ISHL(resultU, one, sixtyThree);
  if (LL_UCMP(resultU, !=, bigEightZero)) {
    ResultFailed("LL_ISHL", "r != 80,00 ", bigEightZero, result);
  }

  LL_ISHL(resultU, one, sixteen);
  if (LL_UCMP(resultU, !=, big64K)) {
    ResultFailed("LL_ISHL", "r != 64K ", big64K, resultU);
  }

  return;
}

/*
**  TestArithmetic() -- Test arithmetic operations.
**
*/
static void TestArithmetic(void) {
  PRInt64 largeVal = LL_INIT(0x00000001, 0xffffffff);
  PRInt64 largeValPlusOne = LL_INIT(0x00000002, 0x00000000);
  PRInt64 largeValTimesTwo = LL_INIT(0x00000003, 0xfffffffe);
  PRInt64 largeMultCand = LL_INIT(0x00000000, 0x7fffffff);
  PRInt64 largeMinusMultCand = LL_INIT(0xffffffff, 0x10000001);
  PRInt64 largeMultCandx64K = LL_INIT(0x00007fff, 0xffff0000);
  PRInt64 largeNumSHL5 = LL_INIT(0x0000001f, 0xffffffe0);
  PRInt64 result, result2;

  /* Addition */
  LL_ADD(result, bigOne, bigOne);
  if (LL_CMP(result, !=, bigTwo)) {
    ResultFailed("LL_ADD", "r != 1 + 1", bigTwo, result);
  }

  LL_ADD(result, bigMinusOne, bigOne);
  if (LL_CMP(result, !=, bigZero)) {
    ResultFailed("LL_ADD", "r != -1 + 1", bigOne, result);
  }

  LL_ADD(result, largeVal, bigOne);
  if (LL_CMP(result, !=, largeValPlusOne)) {
    ResultFailed("LL_ADD", "lVP1 != lV + 1", largeValPlusOne, result);
  }

  /* Subtraction */
  LL_SUB(result, bigOne, bigOne);
  if (LL_CMP(result, !=, bigZero)) {
    ResultFailed("LL_SUB", "r != 1 - 1", bigZero, result);
  }

  LL_SUB(result, bigTwo, bigOne);
  if (LL_CMP(result, !=, bigOne)) {
    ResultFailed("LL_SUB", "r != 2 - 1", bigOne, result);
  }

  LL_SUB(result, largeValPlusOne, bigOne);
  if (LL_CMP(result, !=, largeVal)) {
    ResultFailed("LL_SUB", "r != lVP1 - 1", largeVal, result);
  }

  /* Multiply */
  LL_MUL(result, largeVal, bigTwo);
  if (LL_CMP(result, !=, largeValTimesTwo)) {
    ResultFailed("LL_MUL", "r != lV*2", largeValTimesTwo, result);
  }

  LL_MUL(result, largeMultCand, big64K);
  if (LL_CMP(result, !=, largeMultCandx64K)) {
    ResultFailed("LL_MUL", "r != lV*64K", largeMultCandx64K, result);
  }

  LL_NEG(result2, largeMultCand);
  LL_MUL(result, largeMultCand, bigMinusOne);
  if (LL_CMP(result, !=, result2)) {
    ResultFailed("LL_MUL", "r != -lMC", result2, result);
  }

  LL_SHL(result2, bigZeroFox, 5);
  LL_MUL(result, bigZeroFox, bigThirtyTwo);
  if (LL_CMP(result, !=, largeNumSHL5)) {
    ResultFailed("LL_MUL", "r != 0f<<5", largeNumSHL5, result);
  }

  /* LL_DIV() Division */
  LL_DIV(result, bigOne, bigOne);
  if (LL_CMP(result, !=, bigOne)) {
    ResultFailed("LL_DIV", "1 != 1", bigOne, result);
  }

  LL_DIV(result, bigNumber, bigOne);
  if (LL_CMP(result, !=, bigNumber)) {
    ResultFailed("LL_DIV", "r != n / 1", bigNumber, result);
  }

  LL_DIV(result, bigNumber, bigMinusOne);
  if (LL_CMP(result, !=, bigMinusNumber)) {
    ResultFailed("LL_DIV", "r != n / -1", bigMinusNumber, result);
  }

  LL_DIV(result, bigMinusNumber, bigMinusOne);
  if (LL_CMP(result, !=, bigNumber)) {
    ResultFailed("LL_DIV", "r != -n / -1", bigNumber, result);
  }

  LL_SHL(result2, bigZeroFox, 5);
  LL_DIV(result, result2, bigOne);
  if (LL_CMP(result, !=, result2)) {
    ResultFailed("LL_DIV", "0f<<5 != 0f<<5", result2, result);
  }

  LL_SHL(result2, bigZeroFox, 5);
  LL_NEG(result2, result2);
  LL_DIV(result, result2, bigOne);
  if (LL_CMP(result, !=, result2)) {
    ResultFailed("LL_DIV", "-0f<<5 != -0f<<5", result2, result);
  }

  LL_SHL(result2, bigZeroFox, 17);
  LL_DIV(result, result2, bigMinusOne);
  LL_NEG(result2, result2);
  if (LL_CMP(result, !=, result2)) {
    ResultFailed("LL_DIV", "-0f<<17 != -0f<<17", result2, result);
  }

  /* LL_MOD() Modulo Division */
  LL_ADD(result2, bigThirtyTwo, bigOne);
  LL_MOD(result, result2, bigSixTeen);
  if (LL_CMP(result, !=, bigOne)) {
    ResultFailed("LL_MOD", "r != 1", bigSixTeen, result);
  }

  LL_MUL(result2, bigZeroFox, bigThirtyTwo);
  LL_ADD(result2, result2, bigSixTeen);
  LL_MOD(result, result2, bigThirtyTwo);
  if (LL_CMP(result, !=, bigSixTeen)) {
    ResultFailed("LL_MOD", "r != 16", bigSixTeen, result);
  }

  /* LL_UDIVMOD */
  LL_DIV(result, bigOne, bigOne);
  if (LL_CMP(result, !=, bigOne)) {
    ResultFailed("LL_DIV", "r != 16", bigSixTeen, result);
  }

  return;
}

static void TestWellknowns(void) {
  PRInt64 max = LL_MAXINT, min = LL_MININT, zero = LL_ZERO;
  PRInt64 mmax = LL_MaxInt(), mmin = LL_MinInt(), mzero = LL_Zero();
  if (LL_NE(max, mmax)) {
    ResultFailed("max, mmax", "max != mmax", max, mmax);
  }
  if (LL_NE(min, mmin)) {
    ResultFailed("min, mmin", "min != mmin", max, mmin);
  }
  if (LL_NE(zero, mzero)) {
    ResultFailed("zero, mzero", "zero != mzero", zero, mzero);
  }
} /* TestWellknowns */

/*
** Initialize() -- Initialize the test case
**
** Parse command line options
**
*/
static PRIntn Initialize(PRIntn argc, char** argv) {
  PLOptState* opt = PL_CreateOptState(argc, argv, "dvh");
  PLOptStatus os;

  /*
  ** Parse command line options
  */
  while (PL_OPT_EOL != (os = PL_GetNextOpt(opt))) {
    if (PL_OPT_BAD == os) {
      continue;
    }
    switch (opt->option) {
      case 'd': /* set debug mode */
        debugMode = PR_TRUE;
        break;

      case 'v': /* set verbose mode */
        verboseMode = PR_TRUE;
        debugMode = PR_TRUE;
        break;

      case 'h': /* user wants some guidance */
      default:
        PR_fprintf(output, "You get help.\n");
        return (1);
    }
  }
  PL_DestroyOptState(opt);
  return (0);
}

int main(int argc, char** argv) {
  output = PR_GetSpecialFD(PR_StandardError);

  if (Initialize(argc, argv)) {
    return (1);
  }

  TestAssignment();
  TestComparisons();
  TestLogicalOperations();
  TestConversion();
  TestShift();
  TestArithmetic();
  TestWellknowns();

  /*
  ** That's all folks!
  */
  if (failedAlready) {
    PR_fprintf(output, "FAIL\n");
  } else {
    PR_fprintf(output, "PASS\n");
  }
  return failedAlready;
} /* end main() */
