#ifndef STLPLUS_INF
#define STLPLUS_INF
////////////////////////////////////////////////////////////////////////////////

//   Author:    Andy Rushton
//   Copyright: (c) Southampton University 1999-2004
//              (c) Andy Rushton           2004 onwards
//   License:   BSD License, see ../docs/license.html

//   An infinite-precision integer class. This allows calculations on large
//   integers to be performed without overflow.

//   this class can throw the following exceptions:
//     std::out_of_range
//     std::overflow_error
//     std::invalid_argument
//     stlplus::divide_by_zero    // why doesn't std have this?
//   all of these are derivations of the baseclass:
//     std::logic_error
//   So you can catch all of them by catching the baseclass

//   Warning: inf was never intended to be fast, it is just for programs which
//   need a bit of infinite-precision integer arithmetic. For high-performance
//   processing, use the Gnu Multi-Precision (GMP) library. The inf type is just
//   easier to integrate and is already ported to all platforms and compilers
//   that STLplus is ported to.

////////////////////////////////////////////////////////////////////////////////
#include "portability_fixes.hpp"
#include "portability_exceptions.hpp"
#include <string>
#include <iostream>

////////////////////////////////////////////////////////////////////////////////

namespace stlplus
{

////////////////////////////////////////////////////////////////////////////////

  class inf
  {
  public:

    //////////////////////////////////////////////////////////////////////////////
    // constructors and assignments initialise the inf

    // the void constructor initialises to zero, the others initialise to the
    // value of the C integer type or the text value contained in the string

    inf(void);
    explicit inf(short);
    explicit inf(unsigned short);
    explicit inf(int);
    explicit inf(unsigned);
    explicit inf(long);
    explicit inf(unsigned long);
    // exceptions: std::invalid_argument
    explicit inf(const std::string&) ;
    inf(const inf&);

    ~inf(void);

    // assignments with equivalent behaviour to the constructors

    inf& operator = (short);
    inf& operator = (unsigned short);
    inf& operator = (int);
    inf& operator = (unsigned);
    inf& operator = (long);
    inf& operator = (unsigned long);
    // exceptions: std::invalid_argument
    inf& operator = (const std::string&) ;
    inf& operator = (const inf&);

    //////////////////////////////////////////////////////////////////////////////
    // conversions back to the C types
    // truncate: controls the behaviour when the value is too long for the result
    //           true: truncate the value
    //           false: throw an exception

    // exceptions: std::overflow_error
    short to_short(bool truncate = true) const ;
    // exceptions: std::overflow_error
    unsigned short to_unsigned_short(bool truncate = true) const ;

    // exceptions: std::overflow_error
    int to_int(bool truncate = true) const ;
    // exceptions: std::overflow_error
    unsigned to_unsigned(bool truncate = true) const ;

    // exceptions: std::overflow_error
    long to_long(bool truncate = true) const ;
    // exceptions: std::overflow_error
    unsigned long to_unsigned_long(bool truncate = true) const ;

    //////////////////////////////////////////////////////////////////////////////
    // bitwise manipulation

    void resize(unsigned bits);
    void reduce(void);

    // the number of significant bits in the value
    unsigned bits (void) const;
    unsigned size (void) const;

    // the number of bits that can be accessed by the bit() method (=bits() rounded up to the next byte)
    unsigned indexable_bits(void) const;

    // exceptions: std::out_of_range
    bool bit (unsigned index) const ;
    // exceptions: std::out_of_range
    bool operator [] (unsigned index) const ;

    // exceptions: std::out_of_range
    void set (unsigned index) ;
    // exceptions: std::out_of_range
    void clear (unsigned index) ;
    // exceptions: std::out_of_range
    void preset (unsigned index, bool value) ;

    // exceptions: std::out_of_range
    inf slice(unsigned low, unsigned high) const ;

    //////////////////////////////////////////////////////////////////////////////
    // tests for common values or ranges

    bool negative (void) const;
    bool natural (void) const;
    bool positive (void) const;
    bool zero (void) const;
    bool non_zero (void) const;

    // tests used in if(i) and if(!i)
//  operator bool (void) const;
    bool operator ! (void) const;

    //////////////////////////////////////////////////////////////////////////////
    // comparisons

    bool operator == (const inf&) const;
    bool operator != (const inf&) const;
    bool operator < (const inf&) const;
    bool operator <= (const inf&) const;
    bool operator > (const inf&) const;
    bool operator >= (const inf&) const;

    //////////////////////////////////////////////////////////////////////////////
    // bitwise logic operations

    inf& invert (void);
    inf operator ~ (void) const;

    inf& operator &= (const inf&);
    inf operator & (const inf&) const;

    inf& operator |= (const inf&);
    inf operator | (const inf&) const;

    inf& operator ^= (const inf&);
    inf operator ^ (const inf&) const;

    inf& operator <<= (unsigned shift);
    inf operator << (unsigned shift) const;

    inf& operator >>= (unsigned shift);
    inf operator >> (unsigned shift) const;

    //////////////////////////////////////////////////////////////////////////////
    // arithmetic operations

    inf& negate (void);
    inf operator - (void) const;

    inf& abs(void);
    friend inf abs(const inf&);

    inf& operator += (const inf&);
    inf operator + (const inf&) const;

    inf& operator -= (const inf&);
    inf operator - (const inf&) const;

    inf& operator *= (const inf&);
    inf operator * (const inf&) const;

    // exceptions: divide_by_zero
    inf& operator /= (const inf&) ;
    // exceptions: divide_by_zero
    inf operator / (const inf&) const ;

    // exceptions: divide_by_zero
    inf& operator %= (const inf&) ;
    // exceptions: divide_by_zero
    inf operator % (const inf&) const ;

    // combined division operator - returns the result pair(quotient,remainder) in one go
    // exceptions: divide_by_zero
    std::pair<inf,inf> divide(const inf&) const ;

    //////////////////////////////////////////////////////////////////////////////
    // pre- and post- increment and decrement

    inf& operator ++ (void);
    inf operator ++ (int);
    inf& operator -- (void);
    inf operator -- (int);

    //////////////////////////////////////////////////////////////////////////////
    // string representation and I/O

    std::string image_debug(void) const;

    // conversion to a string representation
    // radix must be 10, 2, 8 or 16
    // exceptions: std::invalid_argument
    std::string to_string(unsigned radix = 10) const;

    // conversion from a string
    // radix == 0 - radix is deduced from the input - assumed 10 unless number is prefixed by 0b, 0 or 0x
    // however, you can specify the radix to be 10, 2, 8 or 16 to force that interpretation
    // exceptions: std::invalid_argument
    inf& from_string(const std::string&, unsigned radix = 0);

    //////////////////////////////////////////////////////////////////////////////
  private:
    std::string m_data;
  public:
    const std::string& get_bytes(void) const;
    void set_bytes(const std::string&);
  };

  ////////////////////////////////////////////////////////////////////////////////
  // redefine friends for gcc v4.1

  inf abs(const inf&);

  ////////////////////////////////////////////////////////////////////////////////

  std::ostream& operator << (std::ostream&, const inf&);
  std::istream& operator >> (std::istream&, inf&);

  ////////////////////////////////////////////////////////////////////////////////

} // end namespace stlplus

#endif
