diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/binary_integer_over_array.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/binary_integer_over_array.hpp new file mode 100644 index 0000000000..375a435b28 --- /dev/null +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/binary_integer_over_array.hpp @@ -0,0 +1,926 @@ + +#ifndef NIL__MULTIPRECISION__BINARY_INTEGER_OVER_ARRAY_HPP +#define NIL__MULTIPRECISION__BINARY_INTEGER_OVER_ARRAY_HPP + +#include +#include +#include +#include +#include +#include + +#include "detail/exponential_limits.hpp" + +namespace nil { +namespace multiprecision { + + +/// This class represents a big integer, which stores data as binary +/// digits in an 'std::array<>' class. This way, the data is sotred in +/// the stack memory. +template< typename LimbType, unsigned int LimbsCount, typename DblLimbType > +class binary_integer_over_array +{ +public: + typedef LimbType limb_type; + static constexpr unsigned int limbs_count = LimbsCount; + typedef DblLimbType dbl_limb_type; + + typedef binary_integer_over_array< LimbType, LimbsCount, DblLimbType > + this_type; // Synonym for this class + +protected: + typedef std::array< limb_type, limbs_count > limbs_type; + + /// How many bits there are in a single limb. + static constexpr unsigned int limb_bits = sizeof(limb_type) * CHAR_BIT; + + /// The array, which stores all limbs of the integer. + /// '_limbs[0]' corresponds to the least significant limb. + limbs_type _limbs; + +protected: + /// Increments by one the most-significant limbs, starting from 'limb_index'. + /// This is the same as adding "2^(limb_index * limb_bits)" to the + /// entire number. + this_type& increment_from_limb( unsigned int limb_index ) { + limb_type* ptr = _limbs.data() + limb_index; + limb_type* const ptr_end = _limbs.data() + limbs_count; + for ( ; ptr < ptr_end; ++ptr ) { + ++(*ptr); + if ( *ptr != 0 ) + break; // No longer carry + } + return *this; + } + + /// Decrements by one the most-significant limbs, starting from 'limb_index'. + /// This is the same as subtracting "2^(limb_index * limb_bits)" from the + /// entire number. + this_type& decrement_from_limb( unsigned int limb_index ) { + limb_type* ptr = _limbs.data() + limb_index; + limb_type* const ptr_end = _limbs.data() + limbs_count; + for ( ; ptr < ptr_end; ++ptr ) { + if ( *ptr > 0 ) { + --(*ptr); + break; // No longer carry + } + --(*ptr); + } + return *this; + } + + /// Constructs value of this object from given 'value', which can + /// have any size (in bytes) + template< typename RawT > + this_type& construct_from_raw( RawT value ) { + constexpr unsigned int value_bits = sizeof(value) * CHAR_BIT; + // Count of bits in 'value'. + std::fill_n( _limbs.data(), limbs_count, (limb_type)0 ); + // At first, reset the number to 0. + // Check different cases + if constexpr ( value_bits <= limb_bits ) + _limbs[ 0 ] = value; + else if constexpr ( value_bits <= limb_bits * 2 ) { + // We must fill 2 limbs + _limbs[ 0 ] = (limb_type)value; + _limbs[ 1 ] = (limb_type)(value >> limb_bits); + } + else if constexpr ( value_bits <= limb_bits * 4 ) { + // We must fill 4 limbs + _limbs[ 0 ] = (limb_type)value; + _limbs[ 1 ] = (limb_type)(value >>= limb_bits); + _limbs[ 2 ] = (limb_type)(value >>= limb_bits); + _limbs[ 3 ] = (limb_type)(value >>= limb_bits); + } + else { + // The general case, we iterate over limbs + limb_type* limb_ptr = _limbs.data(); + *(limb_ptr++) = (limb_type)value; + while ( value != 0 && limb_ptr < _limbs.data() + limbs_count ) + *(limb_ptr++) = (limb_type)(value >>= limb_bits); + } + return *this; + } + + /// Converts value of this number into raw type 'RawT', which can + /// have arbitrary number of bits. + template< typename RawT > + RawT convert_to_raw() const { + constexpr unsigned int value_bits = sizeof(value) * CHAR_BIT; + // Count of bits in 'value'. + // Check different cases + if constexpr ( value_bits <= limb_bits ) + return _limbs[ 0 ]; + else if constexpr ( value_bits <= limb_bits * 2 ) + return (RawT(_limbs[ 1 ]) << limb_bits) + | RawT(_limbs[ 0 ]); + else if constexpr ( value_bits <= limb_bits * 4 ) + return (RawT(_limbs[ 3 ]) << (limb_bits*3)) + | (RawT(_limbs[ 2 ]) << (limb_bits*2)) + | (RawT(_limbs[ 1 ]) << limb_bits) + | RawT(_limbs[ 0 ]); + else { + RawT result( 0 ); + for ( const limb_type* ptr = _limbs.data() + limbs_count - 1; + ptr >= _limbs.data(); + --ptr ) { + result *= (dbl_limb_type)(1 << limb_bits); + result += *ptr; + } + return result; + } + } + +public: + /// Default constructor + /// Initializes to 0. + this_type() { + std::fill_n( _limbs.data(), limbs_count, (limb_type)0 ); + } + + /// Conversion constructors + this_type( unsigned char value ) + { construct_from_raw( value ); } + this_type( char value ) + { construct_from_raw( value ); } + this_type( unsigned short value ) + { construct_from_raw( value ); } + this_type( short value ) + { construct_from_raw( value ); } + this_type( unsigned int value ) + { construct_from_raw( value ); } + this_type( int value ) + { construct_from_raw( value ); } + this_type( unsigned long long value ) + { construct_from_raw( value ); } + this_type( long long value ) + { construct_from_raw( value ); } + + /// Constructor + /// Creates the big integer equal to 'value'. +// this_type( limb_type value ) { +// std::fill_n( _limbs.data(), limbs_count, 0 ); +// _limbs[ 0 ] = value; // Place to the last limb +// } + + /// Constructor + /// Creates the big integer equal to 'value'. + // Note: We declare this constructor as explicit, because otherwise + // it causes a lot of ambiguity with constructor from "limb_type". +// explicit this_type( dbl_limb_type value ) { +// std::fill_n( _limbs.data(), limbs_count, 0 ); +// _limbs[ 1 ] = (limb_type)(value >> limb_bits); +// _limbs[ 0 ] = (limb_type)value; +// } + + /// Constructor + /// Allows to specify value of every limb separately. + /// The unspecified most significant limbs are filled with 0. + this_type( std::initializer_list< limb_type > digits ) { + // Place the specified limbs, in reverse order + std::reverse_copy( digits.begin(), digits.end(), _limbs.data() ); + // Fill upper limbs with '0' + std::fill( _limbs.data() + digits.size(), _limbs.data() + limbs_count, 0 ); + } + + /// Convertion constructor + /// Narrows or widens provided other big integer object. + template< unsigned int OtherLimbsCount > + this_type( const binary_integer_over_array< LimbType, OtherLimbsCount, DblLimbType >& other ) { + if constexpr ( limbs_count < other.limbs_count ) { + // Narrowing + std::copy_n( other._limbs.data(), limbs_count, _limbs.data() ); + } + else { + // Widening + std::copy_n( other._limbs.data(), other.limbs_count, _limbs.data() ); + std::fill( _limbs.data() + other.limbs_count, _limbs.data() + limbs_count, 0 ); + } + } + + /// Assignment operators + this_type& operator=( unsigned char value ) + { return construct_from_raw( value ); } + this_type& operator=( char value ) + { return construct_from_raw( value ); } + this_type& operator=( unsigned short value ) + { return construct_from_raw( value ); } + this_type& operator=( short value ) + { return construct_from_raw( value ); } + this_type& operator=( unsigned int value ) + { return construct_from_raw( value ); } + this_type& operator=( int value ) + { return construct_from_raw( value ); } + this_type& operator=( unsigned long long value ) + { return construct_from_raw( value ); } + this_type& operator=( long long value ) + { return construct_from_raw( value ); } + + /// Assignment operators +// this_type& operator=( limb_type value ) { +// std::fill_n( _limbs.data(), limbs_count, 0 ); +// _limbs[ 0 ] = value; // Place into the last limb +// return *this; +// } +// this_type& operator=( dbl_limb_type value ) { +// std::fill_n( _limbs.data(), limbs_count, 0 ); +// _limbs[ 1 ] = (limb_type)(value >> limb_bits); +// _limbs[ 0 ] = (limb_type)value; +// return *this; +// } + + /// Access to underlying array. + limb_type* data() { + return _limbs.data(); + } + const limb_type* data() const { + return _limbs.data(); + } + + /// Access to separate limbs, by index. + limb_type& operator[]( unsigned int index ) { + return _limbs[ index ]; + } + limb_type operator[]( unsigned int index ) const { + return _limbs[ index ]; + } + + // Addition + this_type& operator+=( const this_type& rhs ) { + limb_type carry( 0 ); + limb_type* this_ptr = _limbs.data(); + limb_type* const this_ptr_end = _limbs.data() + limbs_count; + const limb_type* rhs_ptr = rhs._limbs.data(); + for ( ; this_ptr < this_ptr_end; + ++this_ptr, ++rhs_ptr ) { + if ( carry ) { + (*this_ptr) += (*rhs_ptr); + if ( *this_ptr < *rhs_ptr ) { // there is a carry + ++(*this_ptr); // Use previous carry + // Keep it as current carry + } + else { + ++(*this_ptr); + if ( *this_ptr != 0 ) + carry = 0; // Reset previous carry + } + } + else { + (*this_ptr) += (*rhs_ptr); + if ( *this_ptr < *rhs_ptr ) // there is a carry + carry = 1; + } + } + return *this; + } + + // Subtraction + this_type& operator-=( const this_type& rhs ) { + limb_type carry( 0 ); + limb_type* this_ptr = _limbs.data(); + limb_type* const this_ptr_end = _limbs.data() + limbs_count; + const limb_type* rhs_ptr = rhs._limbs.data(); + for ( ; this_ptr < this_ptr_end; + ++this_ptr, ++rhs_ptr ) { + if ( carry ) { + if ( *this_ptr < *rhs_ptr ) { // there will be a carry + (*this_ptr) -= (*rhs_ptr); + --(*this_ptr); // Use previous carry + // Keep it as current carry + } + else { + (*this_ptr) -= (*rhs_ptr); + if ( *this_ptr != 0 ) + carry = 0; // Reset previous carry + --(*this_ptr); + } + } + else { + if ( *this_ptr < *rhs_ptr ) // there will be a carry + carry = 1; + (*this_ptr) -= (*rhs_ptr); + } + } + return *this; + } + + /// Addition by regular number + this_type& operator+=( limb_type rhs ) { + limb_type* ptr = _limbs.data(); + (*ptr) += rhs; + if ( *ptr < rhs ) // There was a carry + increment_from_limb( 1 ); + return *this; + } + + /// Prefix increment + this_type& operator++() { + return increment_from_limb( 0 ); + } + + /// Postfix increment + this_type operator++( int ) { + this_type result( *this ); + ++(*this); + return result; + } + + /// Prefix decrement + this_type& operator--() { + return decrement_from_limb( 0 ); + } + + /// Postfix decrement + this_type operator--( int ) { + this_type result( *this ); + --(*this); + return result; + } + + /// Subtraction by regular number + this_type& operator-=( limb_type rhs ) { + limb_type* ptr = _limbs.data(); + if ( *ptr < rhs ) { // There will be a carry + (*ptr) -= rhs; + decrement_from_limb( 1 ); + } + else + (*ptr) -= rhs; + return *this; + } + + /// Regular multiplication of big integers with different limbs count. + /// Caller is able to control number of the least significant limbs + /// of result of multiplication. + template< unsigned int R, unsigned int U1, unsigned int U2 > + static binary_integer_over_array< limb_type, R, dbl_limb_type > + multiply_by_limbs( + const binary_integer_over_array< limb_type, U1, dbl_limb_type >& a, + const binary_integer_over_array< limb_type, U2, dbl_limb_type >& b ) { + binary_integer_over_array< limb_type, R, dbl_limb_type > r; + limb_type *r_low_ptr = r._limbs.data(), + *r_high_ptr = r._limbs.data() + 1; + for ( int r_index = 0; + r_index < R; + ++r_index, ++r_low_ptr, ++r_high_ptr ) { + // Now we are calculating "r[ r_index ]" + int a_index = r_index; + int b_index = 0; + if ( a_index >= U1 ) { // 'a' hasn't that many limbs + b_index = a_index - (U1 - 1); + a_index = U1 - 1; + } + if ( b_index >= U2 ) // 'b' hasn't that many limbs too + break; // All necessary limbs of result are calculated + const int a_last_index = 0; + const int b_last_index = U2 - 1; + // Iterate in parallel over a in [a_index -> a_last_index], + // and over 'b' in [b_index -> b_last_index]. + for ( ; a_index >= a_last_index && b_index <= b_last_index; + --a_index, ++b_index ) { + // Now we need to multiply 'a[ a_index ]' over 'b[ b_index ]', + // and add the result to (*r_high_ptr, *r_low_ptr). + dbl_limb_type tmp( a[ a_index ] ); + tmp *= b[ b_index ]; + // Add to '*r_low_ptr' + assert( r_low_ptr < r._limbs.data() + R ); + *r_low_ptr += (limb_type)tmp; + if ( *r_low_ptr < (limb_type)tmp ) // There was an overflow + r.increment_from_limb( r_index + 1 ); + // Add to '*r_high_ptr' + if ( r_high_ptr < r._limbs.data() + R ) { // Check that the result + // can fit also the high bits of multiplication + tmp >>= limb_bits; + *r_high_ptr += (limb_type)tmp; + if ( *r_high_ptr < (limb_type)tmp ) // There was an overflow + r.increment_from_limb( r_index + 2 ); + } + } + } + return r; + } + + /// Regular multiplication of big integer on a primitive value. + /// Caller is able to control number of the least significant limbs + /// of result of multiplication. + template< unsigned int R, unsigned int U1 > + static binary_integer_over_array< limb_type, R, dbl_limb_type > + multiply_by_limbs( + const binary_integer_over_array< limb_type, U1, dbl_limb_type >& a, + const limb_type b ) { + binary_integer_over_array< limb_type, R, dbl_limb_type > r; + limb_type *r_low_ptr = r._limbs.data(), + *r_high_ptr = r._limbs.data() + 1; + for ( unsigned int r_index = 0; + r_index < R; + ++r_index, ++r_low_ptr, ++r_high_ptr ) { + if ( r_index >= U1 ) // 'a' hasn't that many limbs + break; // All necessary limbs of result are calculated + // Now we need to multiply 'a[ r_index ]' over 'b', + // and add the result to (*r_high_ptr, *r_low_ptr). + dbl_limb_type tmp( a[ r_index ] ); + tmp *= b; + // Add to '*r_low_ptr' + assert( r_low_ptr < r._limbs.data() + R ); + *r_low_ptr += (limb_type)tmp; + if ( *r_low_ptr < (limb_type)tmp ) // There was an overflow + r.increment_from_limb( r_index + 1 ); + // Add to '*r_high_ptr' + if ( r_high_ptr < r._limbs.data() + R ) { // Check that the result + // can fit also the high bits of multiplication + tmp >>= limb_bits; + *r_high_ptr += (limb_type)tmp; + if ( *r_high_ptr < (limb_type)tmp ) // There was an overflow + r.increment_from_limb( r_index + 2 ); + } + } + return r; + } + + /// Multiplication + this_type& operator*=( const this_type& rhs ) { + // We can't multiply inplace, so write in a temporary + this_type result + = multiply_by_limbs< limbs_count >( *this, rhs ); + (*this) = result; + return *this; + } + + /// Multiplication over a primitive + this_type& operator*=( limb_type rhs ) { + // We can't multiply inplace, so write in a temporary + this_type result + = multiply_by_limbs< limbs_count >( *this, rhs ); + (*this) = result; + return *this; + } + + /// Divides this number over 'b'. + /// Result is returned via the return value + /// Modulo of the division is stored in *this. + /// Note, this is not a constant-method. + this_type div_mod( const this_type& b ) { + auto b_mult( b ); // Multiple "b*(2^power)" + unsigned int power( 0 ); + // Find the smallest multiple of 'b', which is greater than 'a' + while ( b_mult <= (*this) ) { + b_mult <<= 1; + ++power; + } + // Subtract the multiples of 'b' in reverse order from 'a' + binary_integer_over_array< LimbType, LimbsCount, DblLimbType > quot; + while ( power > 0 ) { + b_mult >>= 1; + --power; + if ( *this >= b_mult ) { + (*this) -= b_mult; + quot.set_bit( power ); + } + } + return quot; + } + + /// Remainder calculation operator + this_type& operator%=( const this_type& b ) { + this_type b_mult( b ); // Multiple "b*(2^power)" + unsigned int power( 0 ); + // Find the smallest multiple of 'b', which is greater than '*this' + while ( b_mult <= *this ) { + b_mult <<= 1; + ++power; + } + // Subtract the multiples of 'b' in reverse order from '*this' + while ( power > 0 ) { + b_mult >>= 1; + --power; + if ( *this >= b_mult ) + *this -= b_mult; + } + return *this; + } + + /// Division calculation operator + this_type& operator/=( const this_type& b ) { + return *this = div_mod( b ); + } + + /// Bit shift to right + this_type& operator>>=( unsigned int bits ) { + assert( bits < limb_bits ); + // We are not going to shift too long. + const unsigned int bits_complement = limb_bits - bits; + limb_type *ptr = _limbs.data(), + *next_ptr = _limbs.data() + 1; + limb_type* const ptr_end = _limbs.data() + limbs_count; + for ( ; next_ptr < ptr_end; + ++ptr, ++next_ptr ) { + (*ptr) >>= bits; + (*ptr) |= (*next_ptr) << bits_complement; + } + (*ptr) >>= bits; // The most-significant limb + return *this; + } + + /// Bit shift to left + this_type& operator<<=( unsigned int bits ) { + assert( bits < limb_bits ); + // We are not going to shift too long. + const unsigned int bits_complement = limb_bits - bits; + limb_type *ptr = _limbs.data() + limbs_count - 1, + *next_ptr = _limbs.data() + limbs_count - 2; + limb_type* const ptr_end = _limbs.data(); + for ( ; next_ptr >= ptr_end; + --ptr, --next_ptr ) { + (*ptr) <<= bits; + (*ptr) |= (*next_ptr) >> bits_complement; + } + (*ptr) <<= bits; // The least-significant limb + return *this; + } + + /// Sets bit at 'index' to 1. + this_type& set_bit( unsigned int index ) { + // Obtain parts of 'index' + unsigned int limb_index = index / limb_bits; + assert( limb_index < limbs_count ); + unsigned int index_in_limb = index - (limb_index * limb_bits); + assert( index_in_limb < limb_bits ); + // Set + _limbs[ limb_index ] |= limb_type( 1 ) << index_in_limb; + return *this; + } + + /// Unsets bit at 'index' to 0. + this_type& unset_bit( unsigned int index ) { + // Obtain parts of 'index' + unsigned int limb_index = index / limb_bits; + assert( limb_index < limbs_count ); + unsigned int index_in_limb = index - (limb_index * limb_bits); + assert( index_in_limb < limb_bits ); + // Unset + _limbs[ limb_index ] &= ~( limb_type( 1 ) << index_in_limb ); + return *this; + } + + /// Checks if bit at 'index' is 1. + bool test_bit( unsigned int index ) const { + // Obtain parts of 'index' + unsigned int limb_index = index / limb_bits; + assert( limb_index < limbs_count ); + unsigned int index_in_limb = index - (limb_index * limb_bits); + assert( index_in_limb < limb_bits ); + // Check + return _limbs[ limb_index ] & (limb_type( 1 ) << index_in_limb); + } + + /// Conversion operators + explicit operator char() const + { return convert_to_raw< char >(); } + explicit operator unsigned char() const + { return convert_to_raw< unsigned char >(); } + explicit operator short() const + { return convert_to_raw< short >(); } + explicit operator unsigned short() const + { return convert_to_raw< unsigned short >(); } + explicit operator int() const + { return convert_to_raw< int >(); } + explicit operator unsigned int() const + { return convert_to_raw< unsigned int >(); } + explicit operator long long() const + { return convert_to_raw< long long >(); } + explicit operator unsigned long long() const + { return convert_to_raw< unsigned long long >(); } + + /// Convertion operator into 'limb_type' +// operator limb_type() const { +// return _limbs[ 0 ]; // Take only the last limb +// } + + /// Convertion operator into 'dbl_limb_type' + // Note: We declare this conversion operator as explciit, because otherwise + // it causes a lot of ambiguity with the conversion operator to "limb_type". +// explicit operator dbl_limb_type() const { +// dbl_limb_type result( _limbs[ 1 ] ); // Take one before last limb +// result <<= limb_bits; +// result |= _limbs[ 0 ]; // and the last one. +// return result; +// } + + /// Conversion to boolean type. + explicit operator bool() const { + return std::find( _limbs.cbegin(), _limbs.cend(), (limb_type)0 ) + != _limbs.end(); + } + +}; + + +// Global operators + + +template< typename LimbType, unsigned int LimbsCount, typename DblLimbType > +inline binary_integer_over_array< LimbType, LimbsCount, DblLimbType > +operator+( + const binary_integer_over_array< LimbType, LimbsCount, DblLimbType >& lhs, + const binary_integer_over_array< LimbType, LimbsCount, DblLimbType >& rhs ) +{ + auto result( lhs ); + result += rhs; + return result; +} + +template< typename LimbType, unsigned int LimbsCount, typename DblLimbType > +inline binary_integer_over_array< LimbType, LimbsCount, DblLimbType > +operator+( + const binary_integer_over_array< LimbType, LimbsCount, DblLimbType >& lhs, + LimbType rhs ) +{ + auto result( lhs ); + result += rhs; + return result; +} + +template< typename LimbType, unsigned int LimbsCount, typename DblLimbType > +inline binary_integer_over_array< LimbType, LimbsCount, DblLimbType > +operator+( + LimbType lhs, + const binary_integer_over_array< LimbType, LimbsCount, DblLimbType >& rhs ) +{ + auto result( rhs ); + result += lhs; + return result; +} + +template< typename LimbType, unsigned int LimbsCount, typename DblLimbType > +inline binary_integer_over_array< LimbType, LimbsCount, DblLimbType > +operator-( + const binary_integer_over_array< LimbType, LimbsCount, DblLimbType >& lhs, + const binary_integer_over_array< LimbType, LimbsCount, DblLimbType >& rhs ) +{ + auto result( lhs ); + result -= rhs; + return result; +} + +template< typename LimbType, unsigned int LimbsCount, typename DblLimbType > +inline binary_integer_over_array< LimbType, LimbsCount, DblLimbType > +operator-( + const binary_integer_over_array< LimbType, LimbsCount, DblLimbType >& lhs, + LimbType rhs ) +{ + auto result( lhs ); + result -= rhs; + return result; +} + +template< typename LimbType, unsigned int LimbsCount, typename DblLimbType > +inline binary_integer_over_array< LimbType, LimbsCount, DblLimbType > +operator*( + const binary_integer_over_array< LimbType, LimbsCount, DblLimbType >& lhs, + const binary_integer_over_array< LimbType, LimbsCount, DblLimbType >& rhs ) +{ + return binary_integer_over_array< LimbType, LimbsCount, DblLimbType > + ::multiply_by_limbs< LimbsCount >( lhs, rhs ); +} + +template< typename LimbType, unsigned int LimbsCount, typename DblLimbType > +inline binary_integer_over_array< LimbType, LimbsCount, DblLimbType > +operator*( + const binary_integer_over_array< LimbType, LimbsCount, DblLimbType >& lhs, + LimbType rhs ) +{ + return binary_integer_over_array< LimbType, LimbsCount, DblLimbType > + ::multiply_by_limbs< LimbsCount >( lhs, rhs ); +} + +template< typename LimbType, unsigned int LimbsCount, typename DblLimbType > +inline binary_integer_over_array< LimbType, LimbsCount, DblLimbType > +operator*( + LimbType lhs, + const binary_integer_over_array< LimbType, LimbsCount, DblLimbType >& rhs ) +{ + return binary_integer_over_array< LimbType, LimbsCount, DblLimbType > + ::multiply_by_limbs< LimbsCount >( rhs, lhs ); +} + +template< typename LimbType, unsigned int LimbsCount, typename DblLimbType > +inline binary_integer_over_array< LimbType, LimbsCount, DblLimbType > +operator%( + const binary_integer_over_array< LimbType, LimbsCount, DblLimbType >& a, + const binary_integer_over_array< LimbType, LimbsCount, DblLimbType >& b ) +{ + auto result( a ); + result %= b; + return result; +} + +template< typename LimbType, unsigned int LimbsCount, typename DblLimbType > +inline binary_integer_over_array< LimbType, LimbsCount, DblLimbType > +operator/( + const binary_integer_over_array< LimbType, LimbsCount, DblLimbType >& a, + const binary_integer_over_array< LimbType, LimbsCount, DblLimbType >& b ) +{ + auto a_copy( a ); + return a_copy.div_mod( b ); +} + + +// Comparison + + +template< typename LimbType, unsigned int LimbsCount, typename DblLimbType > +inline bool operator==( + const binary_integer_over_array< LimbType, LimbsCount, DblLimbType >& lhs, + const binary_integer_over_array< LimbType, LimbsCount, DblLimbType >& rhs ) +{ + return std::equal( + lhs.data(), lhs.data() + lhs.limbs_count, + rhs.data() ); +} + +template< typename LimbType, unsigned int LimbsCount, typename DblLimbType > +inline bool operator!=( + const binary_integer_over_array< LimbType, LimbsCount, DblLimbType >& lhs, + const binary_integer_over_array< LimbType, LimbsCount, DblLimbType >& rhs ) +{ + return ! (lhs == rhs); +} + +template< typename LimbType, unsigned int LimbsCount, typename DblLimbType > +inline bool operator<( + const binary_integer_over_array< LimbType, LimbsCount, DblLimbType >& lhs, + const binary_integer_over_array< LimbType, LimbsCount, DblLimbType >& rhs ) +{ + const LimbType* lhs_ptr = lhs.data() + lhs.limbs_count - 1; + const LimbType* rhs_ptr = rhs.data() + rhs.limbs_count - 1; + const LimbType* lhs_ptr_end = lhs.data(); + for ( ; lhs_ptr >= lhs_ptr_end; + --lhs_ptr, --rhs_ptr ) { + if ( *lhs_ptr != *rhs_ptr ) + return *lhs_ptr < *rhs_ptr; + } + return false; // Values are equal +} + +template< typename LimbType, unsigned int LimbsCount, typename DblLimbType > +inline bool operator<=( + const binary_integer_over_array< LimbType, LimbsCount, DblLimbType >& lhs, + const binary_integer_over_array< LimbType, LimbsCount, DblLimbType >& rhs ) +{ + return ! (rhs < lhs); +} + +template< typename LimbType, unsigned int LimbsCount, typename DblLimbType > +inline bool operator>( + const binary_integer_over_array< LimbType, LimbsCount, DblLimbType >& lhs, + const binary_integer_over_array< LimbType, LimbsCount, DblLimbType >& rhs ) +{ + return rhs < lhs; +} + +template< typename LimbType, unsigned int LimbsCount, typename DblLimbType > +inline bool operator>=( + const binary_integer_over_array< LimbType, LimbsCount, DblLimbType >& lhs, + const binary_integer_over_array< LimbType, LimbsCount, DblLimbType >& rhs ) +{ + return ! (lhs < rhs); +} + + +// Bit operators + + +template< typename LimbType, unsigned int LimbsCount, typename DblLimbType > +inline binary_integer_over_array< LimbType, LimbsCount, DblLimbType > +operator<<( + const binary_integer_over_array< LimbType, LimbsCount, DblLimbType >& lhs, + unsigned int offset ) { + auto result( lhs ); + result <<= offset; + return result; +} + +template< typename LimbType, unsigned int LimbsCount, typename DblLimbType > +inline binary_integer_over_array< LimbType, LimbsCount, DblLimbType > +operator>>( + const binary_integer_over_array< LimbType, LimbsCount, DblLimbType >& lhs, + unsigned int offset ) { + auto result( lhs ); + result >>= offset; + return result; +} + + +// Stream operators + + +template< typename LimbType, unsigned int LimbsCount, typename DblLimbType > +inline std::ostream& operator<<( + std::ostream& ostr, + binary_integer_over_array< LimbType, LimbsCount, DblLimbType > a ) +{ + typedef binary_integer_over_array< LimbType, LimbsCount, DblLimbType > + number_type; // Type of the number being printed + // Handle case of "0" separately. + if ( a == number_type(0) ) { + ostr << '0'; + return ostr; + } + // The general case + static constexpr LimbType max_power_of_10 + = detail::exponential_limits< LimbType, 10 >::max_power; + // The maximal power of 10, that can fit in one limb. + static constexpr unsigned int max_exponent + = detail::exponential_limits< LimbType, 10 >::max_exponent; + // Exponent of that maximal power. + std::array< LimbType, LimbsCount * 2 + 2 > decimals; + // Sequence of portions of the number, each representing + // some substring of its decimal representation. + int L = 0; // Index over "decimals" array + // Obtain sequence of decimal portions + binary_integer_over_array< LimbType, LimbsCount, DblLimbType > a_next; + while ( a != number_type(0) ) { + a_next = a.div_mod( max_power_of_10 ); + decimals[ L++ ] = a[ 0 ]; + a = a_next; + } + // Print them + assert( L > 0 ); + ostr << (unsigned int)decimals[ --L ]; // The first portion goes without leading 0s. + for ( --L; L >= 0; --L ) { + ostr.width( max_exponent ); // The others go with leading 0s. + ostr.fill( '0' ); + ostr << (unsigned int)decimals[ L ]; + } + return ostr; +} + +template< typename LimbType, unsigned int LimbsCount, typename DblLimbType > +inline std::istream& operator>>( + std::istream& istr, + binary_integer_over_array< LimbType, LimbsCount, DblLimbType >& a ) +{ + static constexpr LimbType max_power_of_10 + = detail::exponential_limits< LimbType, 10 >::max_power; + // The maximal power of 10, that can fit in one limb. + static constexpr unsigned int max_exponent + = detail::exponential_limits< LimbType, 10 >::max_exponent; + // Exponent of that maximal power. + // Read all the digits into string + static std::string number_dec; + // Decimal string of the number being parsed + istr >> number_dec; + assert( ! number_dec.empty() ); + // Divide the string into portions + std::array< LimbType, LimbsCount * 2 + 2 > decimals; + // Sequence of portions of the number, each representing + // some substring of its decimal representation. + int L = 0; // Index over "decimals" array + // we can read one more "full" portions + static std::istringstream is_stream; // Stream, used to read a portion + unsigned int limb_value; + while ( number_dec.length() >= max_exponent ) { + is_stream.str( number_dec.substr( + number_dec.length() - max_exponent ) ); + is_stream.seekg( 0 ); + number_dec.erase( + number_dec.length() - max_exponent, max_exponent ); + is_stream >> limb_value; + decimals[ L++ ] = limb_value; + L %= decimals.size(); // Reset index to 0, on overflow + } + // read the last (possibly partial) portion + if ( ! number_dec.empty() ) { + is_stream.str( number_dec ); + is_stream.seekg( 0 ); + number_dec.clear(); + is_stream >> limb_value; + decimals[ L++ ] = limb_value; + L %= decimals.size(); // Reset index to 0, on overflow + } + // Accumulate portions in the number + if ( L == 0 ) { + // There definitely was an overflow + a = (LimbType)0; + } + else { + // The regular case + a = decimals[ --L ]; + for ( --L; L >= 0; --L ) { + a *= max_power_of_10; + a += decimals[ L ]; + } + } + return istr; + + // ToDo: Later this functionality can be optimized, to not used + // internal 'std::istringstream' object. Just read raw characters + // instead. +} + + +} +} + +#endif // NIL__MULTIPRECISION__BINARY_INTEGER_OVER_ARRAY_HPP diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/binary_modular_integer_over_array.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/binary_modular_integer_over_array.hpp new file mode 100644 index 0000000000..41dda8ce19 --- /dev/null +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/binary_modular_integer_over_array.hpp @@ -0,0 +1,359 @@ + +#ifndef NIL__MULTIPRECISION__BINARY_MODULAR_INTEGER_OVER_ARRAY_HPP +#define NIL__MULTIPRECISION__BINARY_MODULAR_INTEGER_OVER_ARRAY_HPP + +#include +#include +#include + +#include "binary_integer_over_array.hpp" + +namespace nil { +namespace multiprecision { + + +/// Names of the integers, which are used as modules, in modular arithmetic. +enum class modules_names : unsigned int +{ + MODULE_A, + MODULE_B, + MODULE_C +}; + +/// Values of the integers, which are used as modules, in modular arithmetic. +template< typename BigIntegerType > +class modules_values +{ +public: + /// Value of modules + static constexpr BigIntegerType values[] = { + + } +}; + + +/// This class is ordinary implementation of modular big number. +/// It derives a regular 'BigIntegerType', and adds modular behavior. +template< typename BigIntegerType, unsigned int ModuleIndex > +class binary_modular_integer_over_array : public BigIntegerType +{ +public: + typedef BigIntegerType big_integer_type; + typedef BigIntegerType base_type; + static constexpr unsigned int _module_index = ModuleIndex; + typedef binary_modular_integer_over_array< + BigIntegerType, ModuleIndex > this_type; + + typedef typename base_type::limb_type limb_type; + static constexpr unsigned int _limbs_count = base_type::limbs_count; + typedef typename base_type::dbl_limb_type dbl_limb_type; + + /// The module of this big integer + static constexpr big_integer_type _module + = modules_values< big_integer_type >.values[ _module_index ]; + + // Check that keeping double module will not overflow the regular + // big integer. + static_assert( + _module * 2 > _module, + "Implementation of this class requires that a value twice larger than the module" + " can still fit in the underlying big integer." ); + +protected: + /// Normalizes this modular number (brings it into range [0, module) ), + /// assuming that currently it might be at most twice larger than the module. + void normalize_once_down() { + if ( _module <= *this ) + base_type::operator-=( _module ); + // Check that this normalization was enough + assert( *this < _module ); + } + + /// Normalizes this modular number (brings it into range [0, module) ), + /// assuming that currently it might be "negative", in range (-module, 0). + void normalize_once_up() { + if ( _module <= *this ) + base_type::operator+=( _module ); + // Check that this normalization was enough + assert( *this < _module ); + } + + /// Normalizes this modular number (brings it into range [0, module) ), + /// assuming that currently it might be many times larger than the module. +/* void normalize_multiple() { + static std::vector< base_type > modules_multiples( + 1, _module ); + // Multiples of the module, where every next value + // is twice larger from previous one. + // Find the smallest multiple, larger than current value + int i = 0; + while ( i < (int)modules_multiples.size() + && modules_multiples[ i ] <= *this ) + ++i; + // ... append new multiples, if necessary + if ( i == (int)modules_multiples.size() ) { + do { + modules_multiples.push_back( + modules_multiples.back() + modules_multiples.back() ); + } while ( modules_multiples.back() <= *this ); + i = (int)modules_multiples.size() - 1; + } + // ... check that in any case we are on a greater multiple + assert( modules_multiples[ i ] > *this ); + // Start reducing + for ( --i; i >= 0; --i ) + if ( modules_multiples[ i ] <= *this ) + (*this) -= modules_multiples[ i ]; + }*/ + + /// Normalizes 'value' (brings it into range [0, module) ), + /// assuming that currently it might be many times larger than the module. + /// For example, if a raw multiplication was performed on 2 modular numbers. + static void reduce( binary_integer_over_array< + limb_type, + _limbs_count * 2, + dbl_limb_type >& value ) { + static std::vector< base_type > modules_multiples( + 1, _module ); + // Multiples of the module, where every next value + // is twice larger from previous one. + // Find the smallest multiple, larger than current value + int i = 0; + while ( i < (int)modules_multiples.size() + && modules_multiples[ i ] <= value ) + ++i; + // ... append new multiples, if necessary + if ( i == (int)modules_multiples.size() ) { + do { + modules_multiples.push_back( + modules_multiples.back() + modules_multiples.back() ); + } while ( modules_multiples.back() <= value ); + i = (int)modules_multiples.size() - 1; + } + // ... check that in any case we are on a greater multiple + assert( modules_multiples[ i ] > value ); + // Start reducing + for ( --i; i >= 0; --i ) + if ( modules_multiples[ i ] <= value ) + value -= modules_multiples[ i ]; + } + + /// Constructs value of this object from given 'value', which can + /// have any size (in bytes) + template< typename RawT > + this_type& construct_from_raw( RawT value ) { + base_type::construct_from_raw( value ); + normalize_once_down(); + return *this; + } + +public: + /// Default constructor + /// Initializes to 0. + this_type() { + std::fill_n( _limbs.data(), _limbs_count, (limb_type)0 ); + } + + /// Conversion constructors + this_type( unsigned char value ) + { construct_from_raw( value ); } + this_type( char value ) + { construct_from_raw( value ); } + this_type( unsigned short value ) + { construct_from_raw( value ); } + this_type( short value ) + { construct_from_raw( value ); } + this_type( unsigned int value ) + { construct_from_raw( value ); } + this_type( int value ) + { construct_from_raw( value ); } + this_type( unsigned long long value ) + { construct_from_raw( value ); } + this_type( long long value ) + { construct_from_raw( value ); } + + /// Constructor + /// Allows to specify value of every limb separately. + /// The unspecified most significant limbs are filled with 0. + this_type( std::initializer_list< limb_type > digits ) + : base_type( digits ) { + // Normalize + reduce( static_cast< base_type& >( *this ) ); + } + + // We assume that no need will arise for calling conversion constructor + // with same limb types but different limbs count. + template< unsigned int OtherLimbsCount > + this_type( const binary_modular_integer_over_array< + LimbType, + OtherLimbsCount, + DblLimbType >& ); + + /// Assignment operators + this_type& operator=( unsigned char value ) + { return construct_from_raw( value ); } + this_type& operator=( char value ) + { return construct_from_raw( value ); } + this_type& operator=( unsigned short value ) + { return construct_from_raw( value ); } + this_type& operator=( short value ) + { return construct_from_raw( value ); } + this_type& operator=( unsigned int value ) + { return construct_from_raw( value ); } + this_type& operator=( int value ) + { return construct_from_raw( value ); } + this_type& operator=( unsigned long long value ) + { return construct_from_raw( value ); } + this_type& operator=( long long value ) + { return construct_from_raw( value ); } + + /// Addition + this_type& operator+=( const this_type& rhs ) { + base_type::operator+=( rhs ); + normalize_once_down(); + return *this; + } + + // Addition by regular number + this_type& operator+=( limb_type rhs ) { + base_type::operator+=( rhs ); + normalize_once_down(); + return *this; + } + + /// Subtraction + this_type& operator-=( const this_type& rhs ) { + base_type::operator-=( rhs ); + normalize_once_up(); + return *this; + } + + // Subtraction by regular number + this_type& operator-=( limb_type rhs ) { + base_type::operator-=( rhs ); + normalize_once_up(); + return *this; + } + + /// Increment + this_type& operator++() { + base_type::operator++(); + if ( *this == _module ) // Possible reset + *this = (limb_type)0; + return *this; + } + + // ... postfix version + this_type operator++( int ) { + this_type result( *this ); + ++(*this); + return result; + } + + /// Decrement + this_type& operator--() { + if ( *this == 0 ) // Prepare for possible reset + *this = _module; + base_type::operator--(); + return *this; + } + + // ... postfix version + this_type operator--(int) { + this_type result(*this); + --(*this); + return result; + } + + /// Multiplication + this_type& operator*=( const this_type& rhs ) { + auto result = (*this) * rhs; + return (*this) = result; + } + + // ... over a primitive type + this_type& operator*=( limb_type rhs ) { + auto result = (*this) * rhs; + return (*this) = result; + } + +}; + + +// Global operators + + +template< typename LimbType, unsigned int LimbsCount, typename DblLimbType > +inline binary_modular_integer_over_array< LimbType, LimbsCount, DblLimbType > +operator*( + const binary_modular_integer_over_array< LimbType, LimbsCount, DblLimbType >& lhs, + const binary_modular_integer_over_array< LimbType, LimbsCount, DblLimbType >& rhs ) +{ + // Just some shortcuts to types of the arguments + typedef binary_modular_integer_over_array< LimbType, LimbsCount, DblLimbType > + this_type; + typedef typename this_type::base_type base_type; + // Perform regular multiplication + binary_integer_over_array< LimbType, LimbsCount * 2, DblLimbType > result_raw + = base_type::multiply_by_limbs< LimbsCount * 2 >( lhs, rhs ); + // Reduce it + this_type::reduce( result_raw ); + assert( result_raw < this_type::_module ); + return this_type( result_raw ); +} + +template< typename LimbType, unsigned int LimbsCount, typename DblLimbType > +inline binary_modular_integer_over_array< LimbType, LimbsCount, DblLimbType > +operator*( + const binary_modular_integer_over_array< LimbType, LimbsCount, DblLimbType >& lhs, + LimbType rhs ) +{ + // Just some shortcuts to types of the arguments + typedef binary_modular_integer_over_array< LimbType, LimbsCount, DblLimbType > + this_type; + typedef typename this_type::base_type base_type; + // Perform regular multiplication + binary_integer_over_array< LimbType, LimbsCount * 2, DblLimbType > result_raw + = base_type::multiply_by_limbs< LimbsCount * 2 >( lhs, rhs ); + // Reduce it + this_type::reduce( result_raw ); + assert( result_raw < this_type::_module ); + return this_type( result_raw ); +} + +template< typename LimbType, unsigned int LimbsCount, typename DblLimbType > +inline binary_modular_integer_over_array< LimbType, LimbsCount, DblLimbType > +operator*( + LimbType lhs, + const binary_modular_integer_over_array< LimbType, LimbsCount, DblLimbType >& rhs ) +{ + return rhs * lhs; +} + + +/** + * ToDo: + * + * Add global addition / subtraction operators + * + * In base class, add casting to twice more limbs count and twice less limbs count + * + * Decide about changing template arguments of this class to: + * LimbType, + * LimbsCount, + * DblLimbType, + * instead of relying on BigIntegerType. + * + * Check that conversion from 'base_type' to 'this_type' can work. + * ... perhaps make it explicit. + * + * Check that comparison between 'modular' and 'regular' works as expected. + * + * + * + */ + +} +} + +#endif // NIL__MULTIPRECISION__BINARY_MODULAR_INTEGER_OVER_ARRAY_HPP diff --git a/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/detail/exponential_limits.hpp b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/detail/exponential_limits.hpp new file mode 100644 index 0000000000..55b55a1bed --- /dev/null +++ b/crypto3/libs/multiprecision/include/nil/crypto3/multiprecision/detail/exponential_limits.hpp @@ -0,0 +1,57 @@ + +#ifndef NIL__MULTIPRECISION__DETAIL__EXPONENTIAL_LIMITS_HPP +#define NIL__MULTIPRECISION__DETAIL__EXPONENTIAL_LIMITS_HPP + +#include + +namespace nil { +namespace multiprecision { +namespace detail { + + +/// This class is inteded for calculation of maximal powers of 'base', +/// that can fit in provided number type 'ValueType' +template< typename ValueType, unsigned int Base = 10 > +class exponential_limits +{ +public: + typedef ValueType value_type; + static constexpr unsigned int base = Base; + typedef exponential_limits< ValueType, Base > this_type; + +private: + /// Calculates all necessary member variables of this class, + /// and returns them. + static constexpr std::pair< value_type, unsigned int > calculate() { + value_type p = 1; + unsigned int e = 0; + while ( true ) { + // Check if we can increment exponent + value_type altered_p( p ); + altered_p *= base; + altered_p /= base; + if ( altered_p != p ) + break; + // Increment it + p *= base; + ++e; + } + return std::make_pair( p, e ); + } + +public: + // The maximal power of 'Base', which can fit in 'ValueType'. + static constexpr value_type max_power + = calculate().first; + + // The exponent of that power. + static constexpr unsigned int max_exponent + = calculate().second; +}; + + +} +} +} + +#endif // NIL__MULTIPRECISION__DETAIL__EXPONENTIAL_LIMITS_HPP diff --git a/crypto3/libs/multiprecision/test/binary_integer_over_array_test.hpp b/crypto3/libs/multiprecision/test/binary_integer_over_array_test.hpp new file mode 100644 index 0000000000..d32bcc7443 --- /dev/null +++ b/crypto3/libs/multiprecision/test/binary_integer_over_array_test.hpp @@ -0,0 +1,736 @@ + +#ifndef NIL__TEST__MULTIPRECISION__BINARY_INTEGER_OVER_ARRAY_TEST_HPP +#define NIL__TEST__MULTIPRECISION__BINARY_INTEGER_OVER_ARRAY_TEST_HPP + +#include + +#include "../../unit_test/include.hpp" + +#include "../../multiprecision/binary_integer_over_array.hpp" + +namespace nil { +namespace test { +namespace multiprecision { + + +/// Runs addition and subtraction unit tests on provided type 'IntT'. +template< typename IntT > +inline void test_big_integer_add_sub() +{ + typedef typename IntT::limb_type limb_type; + typedef typename IntT::dbl_limb_type dbl_limb_type; + + // Regular addition and subtraction + { + IntT a( (dbl_limb_type)1'000 ), b( (dbl_limb_type)2'000 ); + IntT c( (dbl_limb_type)3'000 ); + + NIL_CHECK_EQUAL( a+b, c ); + NIL_CHECK_EQUAL( b+a, c ); + NIL_CHECK_EQUAL( c-a, b ); + NIL_CHECK_EQUAL( c-b, a ); + } + { + IntT a( (dbl_limb_type)527 ), b( (dbl_limb_type)179 ); + IntT c( (dbl_limb_type)706 ); + + NIL_CHECK_EQUAL( a+b, c ); + NIL_CHECK_EQUAL( b+a, c ); + NIL_CHECK_EQUAL( c-a, b ); + NIL_CHECK_EQUAL( c-b, a ); + } + { + IntT a( (dbl_limb_type)321 ), b( (dbl_limb_type)123 ); + IntT c( (dbl_limb_type)444 ); + + NIL_CHECK_EQUAL( a+b, c ); + NIL_CHECK_EQUAL( b+a, c ); + NIL_CHECK_EQUAL( c-a, b ); + NIL_CHECK_EQUAL( c-b, a ); + } + { + IntT a( (dbl_limb_type)998 ), b( (dbl_limb_type)997 ); + IntT c( (dbl_limb_type)1995 ); + + NIL_CHECK_EQUAL( a+b, c ); + NIL_CHECK_EQUAL( b+a, c ); + NIL_CHECK_EQUAL( c-a, b ); + NIL_CHECK_EQUAL( c-b, a ); + } + + // Duplicating values + { + IntT a( (dbl_limb_type)144 ); + IntT b( a ); + IntT c( (dbl_limb_type)288 ); + + NIL_CHECK_EQUAL( a+b, c ); + NIL_CHECK_EQUAL( b+a, c ); + NIL_CHECK_EQUAL( c-a, b ); + NIL_CHECK_EQUAL( c-b, a ); + } + { + IntT a( (dbl_limb_type)2'018 ); + IntT b( a ); + IntT c( (dbl_limb_type)4'036 ); + + NIL_CHECK_EQUAL( a+b, c ); + NIL_CHECK_EQUAL( b+a, c ); + NIL_CHECK_EQUAL( c-a, b ); + NIL_CHECK_EQUAL( c-b, a ); + } + + // Adding zero + { + IntT a( (dbl_limb_type)384 ), b( (dbl_limb_type)0 ); + IntT c( (dbl_limb_type)384 ); + + NIL_CHECK_EQUAL( a+b, c ); + NIL_CHECK_EQUAL( b+a, c ); + NIL_CHECK_EQUAL( c-a, b ); + NIL_CHECK_EQUAL( c-b, a ); + } + { + IntT a( (dbl_limb_type)0 ), b( (dbl_limb_type)0 ); + IntT c( (dbl_limb_type)0 ); + + NIL_CHECK_EQUAL( a+b, c ); + NIL_CHECK_EQUAL( b+a, c ); + NIL_CHECK_EQUAL( c-a, b ); + NIL_CHECK_EQUAL( c-b, a ); + } +} + + +/// Runs addition and subtraction unit tests on provided type 'IntT'. +/// The provided type is expected to hold integer data by limbs, +/// each of 8 bits. +template< typename IntT > +inline void test_8_bit_big_integer_add_sub() +{ + typedef typename IntT::limb_type limb_type; + typedef typename IntT::dbl_limb_type dbl_limb_type; + + // "Digit"-based operations + { + IntT a({ 255, 255 }), b({ 255, 255 }); + IntT c({ 1, 255, 254 }); + + NIL_CHECK_EQUAL( a+b, c ); + NIL_CHECK_EQUAL( b+a, c ); + NIL_CHECK_EQUAL( c-a, b ); + NIL_CHECK_EQUAL( c-b, a ); + } + { + IntT a({ 5, 187, 203 }), b({ 167, 219, 42 }); + IntT c({ 173, 150, 245 }); + + NIL_CHECK_EQUAL( a+b, c ); + NIL_CHECK_EQUAL( b+a, c ); + NIL_CHECK_EQUAL( c-a, b ); + NIL_CHECK_EQUAL( c-b, a ); + } + { + IntT a({ 255, 255, 255 }), b({ 1 }); + IntT c({ 1, 0, 0, 0 }); + + NIL_CHECK_EQUAL( a+b, c ); + NIL_CHECK_EQUAL( b+a, c ); + NIL_CHECK_EQUAL( c-a, b ); + NIL_CHECK_EQUAL( c-b, a ); + } + { + IntT a({ 204, 10, 203 }), b({ 20, 198, 35 }); + IntT c({ 224, 208, 238 }); + + NIL_CHECK_EQUAL( a+b, c ); + NIL_CHECK_EQUAL( b+a, c ); + NIL_CHECK_EQUAL( c-a, b ); + NIL_CHECK_EQUAL( c-b, a ); + } +} + + +/// Runs multiplication, division, modulo unit tests on provided type 'IntT'. +template< typename IntT > +inline void test_big_integer_mul_div_mod() +{ + typedef typename IntT::limb_type limb_type; + typedef typename IntT::dbl_limb_type dbl_limb_type; + + // Regular mult, div (without remainder) + { + dbl_limb_type a, b; + unsigned long long c; + + a = 503; + b = 72; + c = (unsigned long long)a * b; + + NIL_CHECK_EQUAL( IntT(a) * IntT(b), IntT(c) ); + NIL_CHECK_EQUAL( IntT(b) * IntT(a), IntT(c) ); + NIL_CHECK_EQUAL( IntT(c) / IntT(a), IntT(b) ); + NIL_CHECK_EQUAL( IntT(c) / IntT(b), IntT(a) ); + NIL_CHECK_EQUAL( IntT(c) % IntT(a), IntT() ); + NIL_CHECK_EQUAL( IntT(c) % IntT(b), IntT() ); + + a = 82; + b = 14; + c = (unsigned long long)a * b; + + NIL_CHECK_EQUAL( IntT(a) * IntT(b), IntT(c) ); + NIL_CHECK_EQUAL( IntT(b) * IntT(a), IntT(c) ); + NIL_CHECK_EQUAL( IntT(c) / IntT(a), IntT(b) ); + NIL_CHECK_EQUAL( IntT(c) / IntT(b), IntT(a) ); + NIL_CHECK_EQUAL( IntT(c) % IntT(a), IntT() ); + NIL_CHECK_EQUAL( IntT(c) % IntT(b), IntT() ); + + a = 128; + b = 127; + c = (unsigned long long)a * b; + + NIL_CHECK_EQUAL( IntT(a) * IntT(b), IntT(c) ); + NIL_CHECK_EQUAL( IntT(b) * IntT(a), IntT(c) ); + NIL_CHECK_EQUAL( IntT(c) / IntT(a), IntT(b) ); + NIL_CHECK_EQUAL( IntT(c) / IntT(b), IntT(a) ); + NIL_CHECK_EQUAL( IntT(c) % IntT(a), IntT() ); + NIL_CHECK_EQUAL( IntT(c) % IntT(b), IntT() ); + + a = 17'006; + b = 12; + c = (unsigned long long)a * b; + + NIL_CHECK_EQUAL( IntT(a) * IntT(b), IntT(c) ); + NIL_CHECK_EQUAL( IntT(b) * IntT(a), IntT(c) ); + NIL_CHECK_EQUAL( IntT(c) / IntT(a), IntT(b) ); + NIL_CHECK_EQUAL( IntT(c) / IntT(b), IntT(a) ); + NIL_CHECK_EQUAL( IntT(c) % IntT(a), IntT() ); + NIL_CHECK_EQUAL( IntT(c) % IntT(b), IntT() ); + + a = 39; + b = 3'459; + c = (unsigned long long)a * b; + + NIL_CHECK_EQUAL( IntT(a) * IntT(b), IntT(c) ); + NIL_CHECK_EQUAL( IntT(b) * IntT(a), IntT(c) ); + NIL_CHECK_EQUAL( IntT(c) / IntT(a), IntT(b) ); + NIL_CHECK_EQUAL( IntT(c) / IntT(b), IntT(a) ); + NIL_CHECK_EQUAL( IntT(c) % IntT(a), IntT() ); + NIL_CHECK_EQUAL( IntT(c) % IntT(b), IntT() ); + + a = 17'006; + b = 3'459; + c = (unsigned long long)a * b; + + NIL_CHECK_EQUAL( IntT(a) * IntT(b), IntT(c) ); + NIL_CHECK_EQUAL( IntT(b) * IntT(a), IntT(c) ); + NIL_CHECK_EQUAL( IntT(c) / IntT(a), IntT(b) ); + NIL_CHECK_EQUAL( IntT(c) / IntT(b), IntT(a) ); + NIL_CHECK_EQUAL( IntT(c) % IntT(a), IntT() ); + NIL_CHECK_EQUAL( IntT(c) % IntT(b), IntT() ); + + a = 1; + b = 6'083; + c = (unsigned long long)a * b; + + NIL_CHECK_EQUAL( IntT(a) * IntT(b), IntT(c) ); + NIL_CHECK_EQUAL( IntT(b) * IntT(a), IntT(c) ); + NIL_CHECK_EQUAL( IntT(c) / IntT(a), IntT(b) ); + NIL_CHECK_EQUAL( IntT(c) / IntT(b), IntT(a) ); + NIL_CHECK_EQUAL( IntT(c) % IntT(a), IntT() ); + NIL_CHECK_EQUAL( IntT(c) % IntT(b), IntT() ); + + a = 7'094; + b = 0; + c = (unsigned long long)a * b; + + NIL_CHECK_EQUAL( IntT(a) * IntT(b), IntT(c) ); + NIL_CHECK_EQUAL( IntT(b) * IntT(a), IntT(c) ); + NIL_CHECK_EQUAL( IntT(c) / IntT(a), IntT(b) ); + //NIL_CHECK_EQUAL( IntT(c) / IntT(b), IntT(a) ); // can't divide over 0 + NIL_CHECK_EQUAL( IntT(c) % IntT(a), IntT() ); + //NIL_CHECK_EQUAL( IntT(c) % IntT(b), IntT() ); // can't divide over 0 + } + + // Regular division, modulo and mult. + { + dbl_limb_type a, b, quot, mod; + + a = 12'053; + b = 706; + quot = a / b; + mod = a % b; + + NIL_CHECK_EQUAL( IntT(a) / IntT(b), IntT(quot) ); + NIL_CHECK_EQUAL( IntT(a) % IntT(b), IntT(mod) ); + NIL_CHECK_EQUAL( IntT(a), IntT(b) * IntT(quot) + IntT(mod) ); + NIL_CHECK_EQUAL( IntT(a) - IntT(mod), IntT(quot) * IntT(b) ); + NIL_CHECK_EQUAL( (IntT(a) - IntT(mod)) / IntT(quot), IntT(b) ); + + a = 50'003; + b = 15'034; + quot = a / b; + mod = a % b; + + NIL_CHECK_EQUAL( IntT(a) / IntT(b), IntT(quot) ); + NIL_CHECK_EQUAL( IntT(a) % IntT(b), IntT(mod) ); + NIL_CHECK_EQUAL( IntT(a), IntT(b) * IntT(quot) + IntT(mod) ); + NIL_CHECK_EQUAL( IntT(a) - IntT(mod), IntT(quot) * IntT(b) ); + NIL_CHECK_EQUAL( (IntT(a) - IntT(mod)) / IntT(quot), IntT(b) ); + NIL_CHECK_EQUAL( IntT(a) - IntT(mod), IntT(quot) * IntT(b) ); + NIL_CHECK_EQUAL( (IntT(a) - IntT(mod)) / IntT(quot), IntT(b) ); + + a = 48'006; + b = 1; + quot = a / b; + mod = a % b; + + NIL_CHECK_EQUAL( IntT(a) / IntT(b), IntT(quot) ); + NIL_CHECK_EQUAL( IntT(a) % IntT(b), IntT(mod) ); + NIL_CHECK_EQUAL( IntT(a), IntT(b) * IntT(quot) + IntT(mod) ); + NIL_CHECK_EQUAL( IntT(a) - IntT(mod), IntT(quot) * IntT(b) ); + NIL_CHECK_EQUAL( (IntT(a) - IntT(mod)) / IntT(quot), IntT(b) ); + + a = 48'005; + b = 2; + quot = a / b; + mod = a % b; + + NIL_CHECK_EQUAL( IntT(a) / IntT(b), IntT(quot) ); + NIL_CHECK_EQUAL( IntT(a) % IntT(b), IntT(mod) ); + NIL_CHECK_EQUAL( IntT(a), IntT(b) * IntT(quot) + IntT(mod) ); + NIL_CHECK_EQUAL( IntT(a) - IntT(mod), IntT(quot) * IntT(b) ); + NIL_CHECK_EQUAL( (IntT(a) - IntT(mod)) / IntT(quot), IntT(b) ); + + a = 839; + b = 839; + quot = a / b; + mod = a % b; + + NIL_CHECK_EQUAL( IntT(a) / IntT(b), IntT(quot) ); + NIL_CHECK_EQUAL( IntT(a) % IntT(b), IntT(mod) ); + NIL_CHECK_EQUAL( IntT(a), IntT(b) * IntT(quot) + IntT(mod) ); + + a = 1'056; + b = 2'008; + quot = a / b; + mod = a % b; + + NIL_CHECK_EQUAL( IntT(a) / IntT(b), IntT(quot) ); + NIL_CHECK_EQUAL( IntT(a) % IntT(b), IntT(mod) ); + NIL_CHECK_EQUAL( IntT(a), IntT(b) * IntT(quot) + IntT(mod) ); + NIL_CHECK_EQUAL( IntT(a) - IntT(mod), IntT(quot) * IntT(b) ); + //NIL_CHECK_EQUAL( (IntT(a) - IntT(mod)) / IntT(quot), IntT(b) ); + // This check is skipped here + } + + // Test chains of multiplication / division + { + dbl_limb_type a; + + a = 12'053; + + NIL_CHECK_EQUAL( IntT(a) * IntT(2) / IntT(2) * IntT(3) / IntT(3), IntT(a) ); + NIL_CHECK_EQUAL( IntT(a) * IntT(2) * IntT(3) / IntT(6), IntT(a) ); + NIL_CHECK_EQUAL( IntT(a) * IntT(5) * IntT(17) / IntT(5) / IntT(17), IntT(a) ); + NIL_CHECK_EQUAL( IntT(a) * IntT(42) * IntT(98) / IntT(42) / IntT(49) / IntT(2), IntT(a) ); + + a = 5'026; + + NIL_CHECK_EQUAL( IntT(a) * IntT(2) / IntT(2) * IntT(3) / IntT(3), IntT(a) ); + NIL_CHECK_EQUAL( IntT(a) * IntT(2) * IntT(3) / IntT(6), IntT(a) ); + NIL_CHECK_EQUAL( IntT(a) * IntT(5) * IntT(17) / IntT(5) / IntT(17), IntT(a) ); + NIL_CHECK_EQUAL( IntT(a) * IntT(42) * IntT(98) / IntT(42) / IntT(49) / IntT(2), IntT(a) ); + + a = 9; + + NIL_CHECK_EQUAL( IntT(a) * IntT(2) / IntT(2) * IntT(3) / IntT(3), IntT(a) ); + NIL_CHECK_EQUAL( IntT(a) * IntT(2) * IntT(3) / IntT(6), IntT(a) ); + NIL_CHECK_EQUAL( IntT(a) * IntT(5) * IntT(17) / IntT(5) / IntT(17), IntT(a) ); + NIL_CHECK_EQUAL( IntT(a) * IntT(42) * IntT(98) / IntT(42) / IntT(49) / IntT(2), IntT(a) ); + + a = 0; + + NIL_CHECK_EQUAL( IntT(a) * IntT(2) / IntT(2) * IntT(3) / IntT(3), IntT(a) ); + NIL_CHECK_EQUAL( IntT(a) * IntT(2) * IntT(3) / IntT(6), IntT(a) ); + NIL_CHECK_EQUAL( IntT(a) * IntT(5) * IntT(17) / IntT(5) / IntT(17), IntT(a) ); + NIL_CHECK_EQUAL( IntT(a) * IntT(42) * IntT(98) / IntT(42) / IntT(49) / IntT(2), IntT(a) ); + + a = 1; + + NIL_CHECK_EQUAL( IntT(a) * IntT(2) / IntT(2) * IntT(3) / IntT(3), IntT(a) ); + NIL_CHECK_EQUAL( IntT(a) * IntT(2) * IntT(3) / IntT(6), IntT(a) ); + NIL_CHECK_EQUAL( IntT(a) * IntT(5) * IntT(17) / IntT(5) / IntT(17), IntT(a) ); + NIL_CHECK_EQUAL( IntT(a) * IntT(42) * IntT(98) / IntT(42) / IntT(49) / IntT(2), IntT(a) ); + } + +} + + +/// Runs increment and decrement unit tests on provided integer type 'IntT'. +template< typename IntT > +inline void test_big_integer_inc_dec() +{ + typedef typename IntT::limb_type limb_type; + typedef typename IntT::dbl_limb_type dbl_limb_type; + + // Regular increments and decrements + { + dbl_limb_type a_raw; + IntT a; + + a_raw = 5'046; + a = a_raw; + + ++a; + ++a; + a_raw += 2; + NIL_CHECK_EQUAL( a, IntT(a_raw) ); + ++a; + ++a; + a_raw += 2; + NIL_CHECK_EQUAL( a, IntT(a_raw) ); + --a; + --a_raw; + NIL_CHECK_EQUAL( a, IntT(a_raw) ); + --a; + --a; + a_raw -= 2; + NIL_CHECK_EQUAL( a, IntT(a_raw) ); + } + + // Inc, dec near zero + { + dbl_limb_type a_raw; + IntT a; + + a_raw = 0; + a = a_raw; + + ++a; + a++; + a--; + --a; + NIL_CHECK_EQUAL( a, IntT() ); + + a++; + ++a; + a_raw += 2; + NIL_CHECK_EQUAL( a, IntT(a_raw) ); + + a--; + --a_raw; + NIL_CHECK_EQUAL( a, IntT(a_raw) ); + } +} + + +/// Runs increment and decrement unit tests on provided integer type 'IntT', +/// which is expected to habe 8-bit limb. +template< typename IntT > +inline void test_8_bit_big_integer_inc_dec() +{ + typedef typename IntT::limb_type limb_type; + typedef typename IntT::dbl_limb_type dbl_limb_type; + + // Inc, dec with carries + { + IntT a({ 255, 254 }), b({ 255, 255 }), c({ 1, 0, 0 }); + + ++a; + NIL_CHECK_EQUAL( a, b ); + ++a; + NIL_CHECK_EQUAL( a, c ); + + ++a; + NIL_CHECK_NOT_EQUAL( a, c ); + --a; + NIL_CHECK_EQUAL( a, c ); + + a--; + a--; + + --b; + NIL_CHECK_EQUAL( a, b ); + --b; + NIL_CHECK_NOT_EQUAL( a, b ); + } +} + + +/// Runs various bit shifts on provided integer type 'IntT'. +template< typename IntT > +inline void test_big_integer_bit_shifts() +{ + typedef typename IntT::limb_type limb_type; + typedef typename IntT::dbl_limb_type dbl_limb_type; + + // Regular bit shifts + { + //dbl_limb_type a_raw; + IntT a; + + a = (dbl_limb_type)5; + a <<= 1; + NIL_CHECK_EQUAL( a, IntT(10) ); + + a <<= 4; + NIL_CHECK_EQUAL( a, IntT(10 * 16) ); + + a <<= 5; + NIL_CHECK_EQUAL( a, IntT(10 * 16 * 32) ); + + a >>= 2; + NIL_CHECK_EQUAL( a, IntT(10 * 16 * 8) ); + + a >>= 4; + NIL_CHECK_EQUAL( a, IntT(10 * 8) ); + + a >>= 2; + NIL_CHECK_EQUAL( a, IntT(10 * 2) ); + + a >>= 4; + NIL_CHECK_EQUAL( a, IntT(1) ); + + a >>= 1; + NIL_CHECK_EQUAL( a, IntT(0) ); + + a <<= 3; + NIL_CHECK_EQUAL( a, IntT(0) ); + } + + // Random checks + { + IntT a = 51'056; + NIL_CHECK_NOT_EQUAL( a >> 1, a ); + NIL_CHECK_EQUAL( a >> 1 << 1, a ); + NIL_CHECK_EQUAL( a << 1 >> 1, a ); + + a = 512 + 64; + NIL_CHECK_EQUAL( a >> 1 >> 1, a >> 2 ); + NIL_CHECK_EQUAL( a << 2, a << 1 << 1 ); + NIL_CHECK_NOT_EQUAL( a << 4, a << 2 << 1 ); + NIL_CHECK_EQUAL( a << 3, a << 2 << 1 ); + } + + // ToDo: This unit test must be continued + // + // + +} + + +/// Runs various bit operations on provided integer type 'IntT'. +template< typename IntT > +inline void test_big_integer_bit_operations() +{ + typedef typename IntT::limb_type limb_type; + typedef typename IntT::dbl_limb_type dbl_limb_type; + + // Regular bit checks + { + //dbl_limb_type a_raw; + IntT a; + + a = (dbl_limb_type)5; + NIL_CHECK( a.test_bit( 2 ) ); + NIL_CHECK( ! a.test_bit( 1 ) ); + NIL_CHECK( a.test_bit( 0 ) ); + + a *= (dbl_limb_type)2; + NIL_CHECK( a.test_bit( 3 ) ); + NIL_CHECK( ! a.test_bit( 2 ) ); + + a.set_bit( 0 ); + NIL_CHECK_EQUAL( a, IntT((dbl_limb_type)11) ); + + a.unset_bit( 1 ); + NIL_CHECK_EQUAL( a, IntT((dbl_limb_type)9) ); + + a.set_bit( 10 ); + NIL_CHECK_EQUAL( a, IntT((dbl_limb_type)(9 + 1024)) ); + + a.unset_bit( 10 ); + a.unset_bit( 0 ); + NIL_CHECK_EQUAL( a, IntT((dbl_limb_type)8) ); + + } + + // ToDo: This unit test must be continued + // + // +} + + +/// Runs various decimal output operations on provided +/// integer type 'IntT'. +template< typename IntT > +void test_big_integer_output() +{ + IntT a; + std::ostringstream ostr; + + // Regular tests + { + a = 87; + ostr << a; + NIL_CHECK_EQUAL( ostr.str(), "87" ); + + a = 1045; + ostr.str(""); + ostr << a; + NIL_CHECK_EQUAL( ostr.str(), "1045" ); + + a = 58'314; + ostr.str(""); + ostr << a; + NIL_CHECK_EQUAL( ostr.str(), "58314" ); + + a = 156'001; + ostr.str(""); + ostr << a; + NIL_CHECK_EQUAL( ostr.str(), "156001" ); + + a = 58'508'124; + ostr.str(""); + ostr << a; + NIL_CHECK_EQUAL(ostr.str(), "58508124" ); + + a = 1'078'998'004; + ostr.str(""); + ostr << a; + NIL_CHECK_EQUAL(ostr.str(), "1078998004" ); + } + + // Specific tests + { + a = 8; // 1-digit + ostr.str(""); + ostr << a; + NIL_CHECK_EQUAL( ostr.str(), "8" ); + + a = 0; // zero value + ostr.str(""); + ostr << a; + NIL_CHECK_EQUAL( ostr.str(), "0" ); + + a = 10'000'000; // an exponent + ostr.str(""); + ostr << a; + NIL_CHECK_EQUAL( ostr.str(), "10000000" ); + + a = 9'999; // one less than an exponent + ostr.str(""); + ostr << a; + NIL_CHECK_EQUAL( ostr.str(), "9999" ); + } +} + + +/// Runs various decimal output operations on provided +/// integer type 'IntT'. +template< typename IntT > +void test_big_integer_input() +{ + IntT a; + std::istringstream istr; + + // Regular tests + { + istr.seekg( 0 ); + istr.str( "86" ); + istr >> a; + NIL_CHECK_EQUAL( a, IntT(86) ); + + istr.seekg( 0 ); + istr.str( "1045" ); + istr >> a; + NIL_CHECK_EQUAL( a, IntT(1'045) ); + + istr.seekg( 0 ); + istr.str( "324006" ); + istr >> a; + NIL_CHECK_EQUAL( a, IntT(324'006) ); + + istr.seekg( 0 ); + istr.str( "2056345" ); + istr >> a; + NIL_CHECK_EQUAL( a, IntT(2'056'345) ); + + istr.seekg( 0 ); + istr.str( "1824500012" ); + istr >> a; + NIL_CHECK_EQUAL( a, IntT(1'824'500'012) ); + } + + // Corner cases + { + istr.seekg( 0 ); + istr.str( "5" ); // 1-digit + istr >> a; + NIL_CHECK_EQUAL( a, IntT(5) ); + + istr.seekg( 0 ); + istr.str( "0" ); // zero + istr >> a; + NIL_CHECK_EQUAL( a, IntT(0) ); + + istr.seekg( 0 ); + istr.str( "100000000" ); // an exponent + istr >> a; + NIL_CHECK_EQUAL( a, IntT(100'000'000) ); + + istr.seekg( 0 ); + istr.str( "99999" ); // one less than an exponent + istr >> a; + NIL_CHECK_EQUAL( a, IntT(99'999) ); + } + +} + + +/// Runs tests on "binary_integer_over_array<>" template. +inline void test_binary_integer_over_array() +{ + using nil::multiprecision::binary_integer_over_array; + + { + typedef binary_integer_over_array< unsigned char, 4, unsigned short > + int_t; + + test_big_integer_add_sub< int_t >(); + test_8_bit_big_integer_add_sub< int_t >(); + test_big_integer_mul_div_mod< int_t >(); + test_big_integer_inc_dec< int_t >(); + test_8_bit_big_integer_inc_dec< int_t >(); + test_big_integer_bit_shifts< int_t >(); + test_big_integer_bit_operations< int_t >(); + test_big_integer_output< int_t >(); + test_big_integer_input< int_t >(); + } + + { + typedef binary_integer_over_array< unsigned short, 4, unsigned int > + int_t; + + test_big_integer_add_sub< int_t >(); + //test_8_bit_big_integer_add_sub< int_t >(); + // Commented out because this test suite it + // intended for 8-bit limbs. + test_big_integer_mul_div_mod< int_t >(); + test_big_integer_inc_dec< int_t >(); + //test_8_bit_big_integer_inc_dec< int_t >(); + // Commented out for the same reason. + test_big_integer_bit_shifts< int_t >(); + test_big_integer_bit_operations< int_t >(); + test_big_integer_output< int_t >(); + test_big_integer_input< int_t >(); + } +} + + +} +} +} + +#endif // NIL__TEST__MULTIPRECISION__BINARY_INTEGER_OVER_ARRAY_TEST_HPP diff --git a/crypto3/libs/multiprecision/test/detail_exponential_limits_test.hpp b/crypto3/libs/multiprecision/test/detail_exponential_limits_test.hpp new file mode 100644 index 0000000000..10a3d17b9f --- /dev/null +++ b/crypto3/libs/multiprecision/test/detail_exponential_limits_test.hpp @@ -0,0 +1,60 @@ + +#ifndef NIL__TEST__MULTIPRECISION__DETAIL_EXPONENTIAL_LIMITS_TEST_HPP +#define NIL__TEST__MULTIPRECISION__DETAIL_EXPONENTIAL_LIMITS_TEST_HPP + +#include "../../unit_test/include.hpp" + +#include "../../multiprecision/detail/exponential_limits.hpp" + +namespace nil { +namespace test { +namespace multiprecision { + + + +/// Runs tests on "detail::exponential_limits<>" template. +inline void test_detail_exponential_limits() +{ + using nil::multiprecision::detail::exponential_limits; + + // Testing for "unsigned char" + { + exponential_limits< unsigned char, 10 > limits; + + // Checking precision of the primitive type + NIL_CHECK_EQUAL( sizeof(unsigned char) * CHAR_BIT, 8 ); + + NIL_CHECK_EQUAL( limits.max_power, 100 ); + NIL_CHECK_EQUAL( limits.max_exponent, 2 ); + } + + // Testing for "unsigned short" + { + exponential_limits< unsigned short, 10 > limits; + + // Checking precision of the primitive type + NIL_CHECK_EQUAL( sizeof(unsigned short) * CHAR_BIT, 16 ); + + NIL_CHECK_EQUAL( limits.max_power, 10'000 ); + NIL_CHECK_EQUAL( limits.max_exponent, 4 ); + } + + // Testing for "unsigned int" + { + exponential_limits< unsigned int, 10 > limits; + + // Checking precision of the primitive type + NIL_CHECK_EQUAL( sizeof(unsigned int) * CHAR_BIT, 32 ); + + NIL_CHECK_EQUAL( limits.max_power, 1'000'000'000 ); + NIL_CHECK_EQUAL( limits.max_exponent, 9 ); + } + +} + + +} +} +} + +#endif // NIL__TEST__MULTIPRECISION__DETAIL_EXPONENTIAL_LIMITS_TEST_HPP