#ifndef EMPATHY_UTIL_BIGINT_H
#define EMPATHY_UTIL_BIGINT_H
namespace empathy::util
{
using llvm::APInt;
using llvm::APSInt;
// This is a wrapper around llvm::APSInt used to represent compile time integer with "infinite" width.
//
// It wraps the various operations so that they work on integers of different sizes, where the smallest
// one is automatically sign extended first.
//
// It also wraps arithmetic operations to automatically extend the size to make sure that the result
// won't overflow.
//
// They are always signed.
class BigInt
{
public:
BigInt()
{
m_value.setIsSigned( true );
}
template< typename T >
BigInt( T&& v ) :
m_value( forward< T >( v ) )
{
m_value.setIsSigned( true );
}
static BigInt FromBinString( const string& str );
static BigInt FromDecString( const string& str );
static BigInt FromHexString( const string& str );
static BigInt FromU64( uint64_t val )
{
auto apsint = APSInt::getUnsigned( val );
apsint.zext( 65 );
return move( apsint );
}
uint64_t ToU64() const { return m_value.getLimitedValue(); }
const auto& getAPSInt() const { return m_value; }
operator const APSInt&() { return m_value; }
auto getActiveBits() const { return m_value.getActiveBits(); }
auto getMinSignedBits() const { return m_value.getMinSignedBits(); }
bool isNegative() const { return m_value.isNegative(); }
auto zext( uint32_t size ) const { return m_value.zext( size ); }
auto sext( uint32_t size ) const { return m_value.sext( size ); }
friend ostream& operator<<( ostream& out, const BigInt& bi )
{
return out << bi.m_value.toString( 10 );
}
private:
template< typename F >
static auto applyOpOnMixedSizes(
const BigInt& lhs, const BigInt& rhs, F&& func )
{
if( lhs.m_value.getBitWidth() < rhs.m_value.getBitWidth() )
return func( lhs.m_value.sext( rhs.m_value.getBitWidth() ), rhs.m_value );
if( lhs.m_value.getBitWidth() > rhs.m_value.getBitWidth() )
return func( lhs.m_value, rhs.m_value.sext( lhs.m_value.getBitWidth() ) );
return func( lhs.m_value, rhs.m_value );
}
template< typename F >
static auto extendAndApplyOp( uint32_t wantedSize,
const BigInt& lhs, const BigInt& rhs, F&& func )
{
return func( lhs.m_value.sext( wantedSize ), rhs.m_value.sext( wantedSize ) );
}
public:
auto operator==( const BigInt& rhs ) const
{
return applyOpOnMixedSizes( *this, rhs,
[]( const APInt& l, const APInt& r ) { return l == r; } );
}
auto operator&( const BigInt& rhs ) const
{
return applyOpOnMixedSizes( *this, rhs,
[]( const APInt& l, const APInt& r ) { return l & r; } );
}
auto operator|( const BigInt& rhs ) const
{
return applyOpOnMixedSizes( *this, rhs,
[]( const APInt& l, const APInt& r ) { return l | r; } );
}
auto operator^( const BigInt& rhs ) const
{
return applyOpOnMixedSizes( *this, rhs,
[]( const APInt& l, const APInt& r ) { return l ^ r; } );
}
auto shl( const BigInt& rhs ) const
{
return sext( getMinSignedBits() + rhs.ToU64() ).shl( rhs.m_value );
}
auto ashr( const BigInt& rhs ) const
{
return m_value.ashr( rhs.m_value );
}
private:
llvm::APSInt m_value;
};
}
namespace std
{
template<> struct hash< empathy::util::BigInt >
{
size_t operator()( const empathy::util::BigInt& x ) const
{
return llvm::hash_value( x.getAPSInt() );
}
};
}
#endif