// $Id: long.h,v 1.25 2004/02/17 10:34:43 elliott-oss Exp $ -*- c++ -*-
//
// This software is subject to the terms of the IBM Jikes Compiler
// License Agreement available at the following URL:
// http://ibm.com/developerworks/opensource/jikes.
// Copyright (C) 1996, 2003 IBM Corporation and others.  All Rights Reserved.
// You must accept the terms of that agreement to use this software.
//

#ifndef long_INCLUDED
#define long_INCLUDED

#include "platform.h"

#ifdef HAVE_JIKES_NAMESPACE
namespace Jikes { // Open namespace Jikes block
#endif

class IEEEdouble;
class IEEEfloat;

class LongInt;
class ULongInt;

class Int
{
public:
    // Max value of i4
    static inline const i4 MAX_INT()
    {
        return 0x7FFFFFFF;
    }

    // Min value of i4
    static inline const i4 MIN_INT()
    {
        return 0x80000000;
    }
};

class BaseLong
{
protected:
    enum
    {
        SHORT_MASK = 0xFFFF, // mask for lower half of i4
        SIGN_BIT = 0x80000000 // sign bit
    };

    union
    {
        double double_value;
#ifndef HAVE_64BIT_TYPES
        u4 word[2];
#else
        u8 words;
#endif // HAVE_64BIT_TYPES
    } value;

    // Set the high word only. Does not modify the low word!
    inline void setHighWord(u4 high)
    {
#ifndef HAVE_64BIT_TYPES
# ifndef WORDS_BIGENDIAN
        value.word[1] = high;
# else
        value.word[0] = high;
# endif
#else
        setHighAndLowWords(high, LowWord());
#endif // HAVE_64BIT_TYPES
    }

    // Set the low word only. Does not modify the high word!
    inline void setLowWord(u4 low)
    {
#ifndef HAVE_64BIT_TYPES
# ifndef WORDS_BIGENDIAN
        value.word[0] = low;
# else
        value.word[1] = low;
# endif
#else
        setHighAndLowWords(HighWord(), low);
#endif // HAVE_64BIT_TYPES
    }

    // Set the value for both words.
    inline void setHighAndLowWords(u4 high, u4 low)
    {
#ifndef HAVE_64BIT_TYPES
# ifndef WORDS_BIGENDIAN
        value.word[1] = high;
        value.word[0] = low;
# else
        value.word[0] = high;
        value.word[1] = low;
# endif
#else
        value.words = (((u8) high) << 32) | low;
#endif // HAVE_64BIT_TYPES
    }

    // Set the value for both words.
    inline void setHighAndLowWords(const BaseLong& op)
    {
#ifndef HAVE_64BIT_TYPES
        value.word[0] = op.value.word[0];
        value.word[1] = op.value.word[1];
#else
        value.words = op.value.words;
#endif // HAVE_64BIT_TYPES
    }

public:

#ifndef HAVE_64BIT_TYPES
# ifndef WORDS_BIGENDIAN
    inline u4 HighWord() const { return value.word[1]; }
    inline u4 LowWord() const { return value.word[0]; }
# else
    inline u4 HighWord() const { return value.word[0]; }
    inline u4 LowWord() const { return value.word[1]; }
# endif
#else
    inline u4 HighWord() const { return (u4) (value.words >> 32); }
    inline u4 LowWord() const { return (u4) value.words; }
    inline u8 Words() const { return value.words; }
#endif // HAVE_64BIT_TYPES

    double DoubleView() const { return value.double_value; }

    BaseLong(u4 high, u4 low);
    BaseLong(u4 a); // no sign extension
    BaseLong(i4 a); // sign extends
    // Forwarding constructor, if needed, so that BaseLong(0) works.
#ifndef TYPE_I4_IS_INT
    inline BaseLong(int i) { *this = BaseLong((i4) i); }
#endif // TYPE_I4_IS_INT
    inline BaseLong() {} // construct without initializing
#ifdef HAVE_64BIT_TYPES
    inline BaseLong(u8 a) { value.words = a; } // construct in one step
#endif // HAVE_64BIT_TYPES

    BaseLong operator+(const BaseLong) const; // binary addition
    BaseLong operator+() const;     // unary plus
    BaseLong& operator+=(const BaseLong); // add and assign
    BaseLong operator++(int);      // postfix increment
    BaseLong operator++();     // prefix increment

    BaseLong operator-(const BaseLong) const; // binary subtraction
    BaseLong operator-() const;     // unary minus
    BaseLong& operator-=(const BaseLong); // subtract and assign
    BaseLong operator--(int);      // postfix decrement
    BaseLong operator--();     // prefix decrement

    BaseLong operator*(const BaseLong) const; // multiplication
    BaseLong& operator*=(const BaseLong); // multiply and assign

    //
    // NOTE: To match the JLS, mask the argument with
    // Semantic::LONG_SHIFT_MASK (0x3f)
    //
    BaseLong operator<<(int) const;      // left shift
    BaseLong& operator<<=(int);      // left shift and assign

    bool operator==(const BaseLong) const; // equal
    bool operator!=(const BaseLong) const; // not equal
    bool operator!() const;     // logical complement

    BaseLong operator~() const;     // bitwise complement
    BaseLong operator^(const BaseLong) const; // bitwise XOR
    BaseLong& operator^=(const BaseLong); // bitwise XOR and assign
    BaseLong operator|(const BaseLong) const; // bitwise OR
    BaseLong& operator|=(const BaseLong); // bitwise OR and assign
    BaseLong operator&(const BaseLong) const; // bitwise AND
    BaseLong& operator&=(const BaseLong); // bitwise AND and assign

    bool operator&&(const BaseLong) const; // logical AND (not short-circuit)
    bool operator||(const BaseLong) const; // logical OR (not short circuit)

    static void Divide(const BaseLong&, const BaseLong&, BaseLong&, BaseLong&);

    operator LongInt() const;          // Cast to LongInt
    operator ULongInt() const;         // Cast to ULongInt

    // mirrors java.lang.Long, useful in hashing
    inline i4 hashCode() const { return HighWord() ^ LowWord(); }
};


class LongInt : public BaseLong
{
public:

    inline LongInt(u4 a, u4 b) : BaseLong(a, b) {}
    inline LongInt(u4 a) : BaseLong(a) {} // no sign extension
    inline LongInt(i4 a) : BaseLong(a) {} // sign extends
    // Forwarding constructor, if needed, so that LongInt(0) works.
#ifndef TYPE_I4_IS_INT
    inline LongInt(int i) { *this = LongInt((i4) i); }
#endif // TYPE_I4_IS_INT
#ifdef HAVE_EXPLICIT
    explicit
#endif
             inline LongInt(const BaseLong& a) : BaseLong(a) {}
    inline LongInt() : BaseLong() {} // uninitialized
    // narrowing conversion of double to long
#ifdef HAVE_EXPLICIT
    explicit
#endif
             LongInt(const IEEEdouble&);

#ifdef HAVE_EXPLICIT
    explicit
#endif
    LongInt(const IEEEfloat&); // narrowing conversion of float to long

#ifdef HAVE_64BIT_TYPES
    inline LongInt(u8 a) : BaseLong(a) {} // construct in one step
#endif // HAVE_64BIT_TYPES

    //
    // These constants are generated when first used.  The memory they
    // use can be reclaimed with ConstantCleanup().
    //
    static inline const LongInt MAX_LONG()
    {
        return max_long_const ? *max_long_const
            : *(max_long_const = new LongInt(0x7FFFFFFF, 0xFFFFFFFF));
    }
    static inline const LongInt MIN_LONG()
    {
        return min_long_const ? *min_long_const
            : *(min_long_const = new LongInt(0x80000000, 0x00000000));
    }
    static void ConstantCleanup()
    {
        // The explicit casts are there to workaround a MSVC
        // behaviour which doesn't allow an implicit cast
        // from const X* to void* for the delete operator.
        delete (LongInt*) max_long_const;
        delete (LongInt*) min_long_const;
    }


private:
    static const LongInt* max_long_const;
    static const LongInt* min_long_const;

public:
    LongInt operator/(const LongInt) const; // divide
    LongInt& operator/=(const LongInt); // divide and assign

    LongInt operator%(const LongInt) const; // modulus
    LongInt& operator%=(const LongInt); // modulus and assign

    //
    // NOTE: To match the JLS, mask the argument with
    // Semantic::LONG_SHIFT_MASK (0x3f)
    //
    LongInt operator>>(int) const;     // right shift
    LongInt& operator>>=(int);     // right shift and assign

    bool operator<(const LongInt) const; // less-than
    bool operator>(const LongInt) const; // greater-than
    bool operator<=(const LongInt) const; // less-than or equal
    bool operator>=(const LongInt) const; // greater-than or equal
};


class ULongInt : public BaseLong
{
public:

    inline ULongInt(u4 a, u4 b) : BaseLong(a, b) {}
    inline ULongInt(u4 a) : BaseLong(a) {} // no sign extension
    inline ULongInt(i4 a) : BaseLong(a) {} // sign extended
    // Forwarding constructor, if needed, so that ULongInt(0) works.
#ifndef TYPE_I4_IS_INT
    inline ULongInt(int i) { *this = ULongInt((i4) i); }
#endif // TYPE_I4_IS_INT
    inline ULongInt() : BaseLong() {} // uninitialized
#ifdef HAVE_64BIT_TYPES
    inline ULongInt(u8 a) : BaseLong(a) {} // construct in one step
#endif // HAVE_64BIT_TYPES

    ULongInt operator/(const ULongInt) const; // divide
    ULongInt& operator/=(const ULongInt); // divide and assign

    ULongInt operator%(const ULongInt) const; // modulus
    ULongInt& operator%=(const ULongInt); // modulus and assign

    //
    // NOTE: To match the JLS, mask the argument with
    // Semantic::LONG_SHIFT_MASK (0x3f)
    //
    ULongInt operator>>(int) const;      // right shift
    ULongInt& operator>>=(int);      // right shift and assign

    bool operator<(const ULongInt) const; // less-than
    bool operator>(const ULongInt) const; // greater-than
    bool operator<=(const ULongInt) const; // less-than or equal
    bool operator>=(const ULongInt) const; // greater-than or equal
};

#ifdef HAVE_JIKES_NAMESPACE
} // Close namespace Jikes block
#endif

#endif // long_INCLUDED
