Add support for multiprecision integer arithmetic and binary data encoding

This commit is contained in:
Vinnie Falco
2013-09-13 10:42:56 -07:00
parent a45fc47682
commit 1dd2836f1b
9 changed files with 1281 additions and 183 deletions

View File

@@ -323,7 +323,9 @@
<ClInclude Include="..\..\modules\beast_core\zip\zlib\zlib.h" />
<ClInclude Include="..\..\modules\beast_core\zip\zlib\zutil.h" />
<ClInclude Include="..\..\modules\beast_crypto\beast_crypto.h" />
<ClInclude Include="..\..\modules\beast_crypto\math\BinaryEncoding.h" />
<ClInclude Include="..\..\modules\beast_crypto\math\UnsignedInteger.h" />
<ClInclude Include="..\..\modules\beast_crypto\math\UnsignedIntegerCalc.h" />
<ClInclude Include="..\..\modules\beast_db\beast_db.h" />
<ClInclude Include="..\..\modules\beast_db\keyvalue\beast_KeyvaDB.h" />
<ClInclude Include="..\..\modules\beast_extras\beast_extras.h" />
@@ -1265,6 +1267,12 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\modules\beast_crypto\beast_crypto.cpp" />
<ClCompile Include="..\..\modules\beast_crypto\math\BinaryEncoding.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\modules\beast_crypto\math\UnsignedInteger.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>

View File

@@ -1028,6 +1028,12 @@
<ClInclude Include="..\..\modules\beast_crypto\math\UnsignedInteger.h">
<Filter>beast_crypto\math</Filter>
</ClInclude>
<ClInclude Include="..\..\modules\beast_crypto\math\UnsignedIntegerCalc.h">
<Filter>beast_crypto\math</Filter>
</ClInclude>
<ClInclude Include="..\..\modules\beast_crypto\math\BinaryEncoding.h">
<Filter>beast_crypto\math</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\modules\beast_core\containers\beast_AbstractFifo.cpp">
@@ -1552,6 +1558,9 @@
<ClCompile Include="..\..\modules\beast_crypto\math\UnsignedInteger.cpp">
<Filter>beast_crypto\math</Filter>
</ClCompile>
<ClCompile Include="..\..\modules\beast_crypto\math\BinaryEncoding.cpp">
<Filter>beast_crypto\math</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Text Include="..\..\TODO.txt" />

View File

@@ -24,6 +24,7 @@
namespace beast
{
#include "math/BinaryEncoding.cpp"
#include "math/UnsignedInteger.cpp"
}

View File

@@ -44,7 +44,9 @@
namespace beast
{
# include "math/UnsignedIntegerCalc.h"
#include "math/UnsignedInteger.h"
#include "math/BinaryEncoding.h"
}

View File

@@ -0,0 +1,395 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
/** Generic algorithms for base encoding and decoding. */
class BinaryEncoding
{
public:
/** Concept: Conversion
X denotes a Conversion class, a is a value of type X,
i is an integral type.
Requirements:
Expression Type Notes/Contracts
------------- ----------- ------------------
X a;
X::radix size_type constexpr
a.map (i) char maps base numeral to a char
*/
/** Encode the unsigned integer into a string using the specified conversion. */
template <typename Conversion, std::size_t Bytes>
static std::string encode (UnsignedInteger <Bytes> v, Conversion c = Conversion ())
{
// bi is destroyed in this process
typename UnsignedInteger <Bytes>::CalcType bi (v.toCalcType ());
std::size_t const radix (Conversion::radix);
std::string s;
s.reserve (bi.size() * 3); // guess
while (bi.isNotZero ())
{
std::size_t const m (bi % radix);
bi /= radix;
s.push_back (c.map (m));
}
std::reverse (s.begin(), s.end());
return s;
}
/** Decode the string into an unsigned integer.
The size must match exactly
@return `true` on success.
*/
template <typename Conversion, std::size_t Bytes>
static bool decode (UnsignedInteger <Bytes>& rv,
std::string const& s, Conversion c = Conversion ())
{
typename UnsignedInteger <Bytes>::CalcType bi (rv.toCalcType (false));
std::size_t const radix (Conversion::radix);
bi.clear ();
for (std::string::const_iterator iter (s.begin()); iter != s.end(); ++iter)
{
int const v (c.invert (*iter));
if (v == -1)
return false;
bi *= radix;
bi += v;
}
bi.toCanonical();
return true;
}
};
//------------------------------------------------------------------------------
// Some common code
template <class Conversion>
class BaseConversion
{
public:
char map (std::size_t i) const
{
return Conversion::alphabet () [i];
}
int invert (char c) const
{
return Conversion::inverse_alphabet () [c];
}
static std::vector <int> const& inverse_alphabet ()
{
static std::vector <int> t (invert (Conversion::alphabet(), Conversion::radix));
return t;
}
/** Build the inverse mapping table from characters to digits. */
static std::vector <int> invert (std::string const& alphabet, std::size_t radix)
{
std::vector <int> table (256, -1);
for (std::size_t i (0); i < radix; ++i)
table [alphabet [i]] = i;
return table;
}
};
//------------------------------------------------------------------------------
/** Foolproof hexadecimal encoding and decoding facility.
This is to check the correctness of the more complex converters.
*/
class HexEncoding
{
public:
template <std::size_t Bytes>
static std::string encode (UnsignedInteger <Bytes> const& v)
{
std::string s;
uint8 const* src (v.cbegin()-1);
char const* const tab (alphabet().c_str());
s.reserve (Bytes * 2);
for (std::size_t bytes (v.sizeInBytes);bytes--;)
{
uint8 const v (*++src);
s.push_back (tab [v>>4]);
s.push_back (tab [v&0x0f]);
}
return s;
}
template <std::size_t Bytes>
static bool decode (UnsignedInteger <Bytes>& rv,
std::string const& s)
{
// can't have an odd size
if (s.size() & 1)
return false;
uint8* dest (rv.begin()-1);
int const* const tab (&inverse_alphabet().front());
for (std::string::const_iterator iter (s.begin()); iter != s.end();)
{
int const n1 (tab [*iter++]);
if (n1 == -1)
return false;
int const n2 (tab [*iter++]);
if (n2 == -1)
return false;
*++dest = ((uint8)((n1<<4)|n2));
}
return true;
}
static std::string const& alphabet ()
{
static std::string s ("0123456789ABCDEF");
return s;
}
static std::vector <int> const& inverse_alphabet ()
{
static std::vector <int> s (invert (alphabet(), 16));
return s;
}
/** Build the inverse mapping table from characters to digits. */
static std::vector <int> invert (std::string const& alphabet, std::size_t radix)
{
std::vector <int> table (256, -1);
for (std::size_t i (0); i < radix; ++i)
table [alphabet [i]] = i;
return table;
}
};
//------------------------------------------------------------------------------
/** Base58 conversion used by Bitcoin. */
class BitcoinBase58Conversion : public BaseConversion <BitcoinBase58Conversion>
{
public:
static std::size_t const radix = 58;
char const* name () const
{
return "BitcoinBase58";
}
static std::string const& alphabet ()
{
static std::string s (
"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
);
return s;
}
};
//------------------------------------------------------------------------------
/** Base58 conversion used by Ripple. */
class RippleBase58Conversion : public BaseConversion <RippleBase58Conversion>
{
public:
static std::size_t const radix = 58;
char const* name () const
{
return "RippleBase58";
}
static std::string const& alphabet ()
{
static std::string s (
"rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz"
);
return s;
}
};
//------------------------------------------------------------------------------
class Base64Conversion : public BaseConversion <Base64Conversion>
{
public:
static std::size_t const radix = 64;
char const* name () const
{
return "Base64";
}
static std::string const& alphabet ()
{
static std::string s (
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
);
return s;
}
};
//------------------------------------------------------------------------------
class Base16Conversion : public BaseConversion <Base16Conversion>
{
public:
static std::size_t const radix = 16;
char const* name () const
{
return "Hex";
}
static std::string const& alphabet ()
{
static std::string s (
"0123456789ABCDEF"
);
return s;
}
};
//------------------------------------------------------------------------------
class BinaryEncodingTests : public UnitTest
{
public:
// This is a baseline for the other tests
template <std::size_t Bytes>
void testBase16 ()
{
beginTestCase ("base16");
for (int i = 0; i < 50; ++i)
{
typedef UnsignedInteger <Bytes> UInt;
UInt v0;
random().fillBitsRandomly (v0.begin(), UInt::sizeInBytes);
std::string const good (HexEncoding::encode (v0));
UInt v1;
bool const success (HexEncoding::decode (v1, good));
if (expect (success))
{
expect (v0 == v1);
Base16Conversion c;
std::string const check (BinaryEncoding::encode (v0, c));
if (! expect (good == check))
logMessage (String ("expected ") + good + " but got " + check);
}
}
}
template <std::size_t Bytes>
void testBase64Bytes (
std::string const& vin, std::string const& vout,
Base64Conversion c = Base64Conversion ())
{
typedef UnsignedInteger <Bytes> UInt;
UInt v1 (vin.c_str());
std::string const s1 (BinaryEncoding::encode (v1, c));
logMessage (vout + " to " + s1);
expect (vout == s1);
UInt v2;
bool const success (BinaryEncoding::decode (v2, vout, c));
if (expect (success))
{
std::string const s2 (BinaryEncoding::encode (v2, c));
logMessage (vin + " to " + s2);
//expect (vin == v2);
}
}
void testBase64 ()
{
beginTestCase ("Base64");
// input (uint)
std::string const vin [] = {
"","f","fo","foo","foob","fooba","foobar"
};
// output (encoded string)
std::string const vout [] = {
"","Zg==","Zm8=","Zm9v","Zm9vYg==","Zm9vYmE=","Zm9vYmFy"
};
//testBase64Bytes <0> (vin [0], vout [0]);
testBase64Bytes <1> (vin [1], vout [1]);
testBase64Bytes <2> (vin [2], vout [2]);
testBase64Bytes <3> (vin [3], vout [3]);
testBase64Bytes <4> (vin [4], vout [4]);
testBase64Bytes <5> (vin [5], vout [5]);
testBase64Bytes <6> (vin [6], vout [6]);
}
template <typename Conversion, std::size_t Bytes>
void testEncode (Conversion c = Conversion())
{
typedef UnsignedInteger <Bytes> UInt;
beginTestCase (String (c.name()) + " <" + String::fromNumber (Bytes) + ">");
for (int i = 0; i < 50; ++i)
{
UInt v1;
random().fillBitsRandomly (v1.begin(), UInt::sizeInBytes);
std::string const s1 (BinaryEncoding::encode (v1, c));
UInt v2;
bool success (BinaryEncoding::decode (v2, s1, c));
if (expect (success))
expect (v1 == v2);
}
}
void runTest ()
{
testBase16 <10> ();
#if 0
testEncode <Base16Conversion, 1> ();
testEncode <Base16Conversion, 2> ();
testEncode <Base16Conversion, 3> ();
testEncode <Base16Conversion, 4> ();
testEncode <Base16Conversion, 5> ();
testEncode <Base16Conversion, 6> ();
testEncode <Base16Conversion, 7> ();
testEncode <Base16Conversion, 8> ();
testEncode <Base16Conversion, 9> ();
testEncode <Base16Conversion, 10> ();
testBase64 ();
testEncode <Base64Conversion, 20> ();
testEncode <Base64Conversion, 33> ();
testEncode <RippleBase58Conversion, 20> ();
testEncode <RippleBase58Conversion, 33> ();
testEncode <BitcoinBase58Conversion, 20> ();
testEncode <BitcoinBase58Conversion, 33> ();
#endif
}
BinaryEncodingTests () : UnitTest ("BinaryEncoding", "beast", runManual)
{
}
};
static BinaryEncodingTests BinaryEncodingTests;

View File

@@ -0,0 +1,24 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef BEAST_CRYPTO_BINARYENCODING_H_INCLUDED
#define BEAST_CRYPTO_BINARYENCODING_H_INCLUDED
#endif

View File

@@ -17,6 +17,327 @@
*/
//==============================================================================
//------------------------------------------------------------------------------
/* Copyright (c) 2013 the authors listed at the following URL, and/or
the authors of referenced articles or incorporated external code:
http://en.literateprograms.org/Arbitrary-precision_integer_arithmetic_(C)?action=history&offset=20100923155004
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Retrieved from: http://en.literateprograms.org/Arbitrary-precision_integer_arithmetic_(C)?oldid=16902
*/
namespace multiprecsion
{
#if 0
//------------------------------------------------------------------------------
typedef unsigned short component_t;
typedef unsigned long double_component_t;
#define MAX_COMPONENT ((component_t)(-1))
#define COMPONENT_BITS (sizeof(component_t)*CHAR_BIT)
#define LOG_2_10 3.3219280948873623478703194294894
#define MIN(x,y) ((x)<(y) ? (x) : (y))
#define MAX(x,y) ((x)>(y) ? (x) : (y))
typedef struct {
component_t* c; /* least-significant word first */
int num_components;
} integer;
integer create_integer(int components);
void free_integer(integer i);
void set_zero_integer(integer i);
void copy_integer(integer source, integer target);
void add_integer(integer left, integer right, integer result);
void subtract_integer(integer left, integer right, integer result);
void multiply_small_integer(integer left, component_t right, integer result);
void multiply_integer(integer left, integer right, integer result);
int compare_integers(integer left, integer right);
void shift_left_one_integer(integer arg);
void shift_right_one_integer(integer arg);
component_t mod_small_integer(integer left, component_t right);
void mod_integer(integer left, integer right, integer result);
void divide_small_integer(integer left, component_t right, integer result);
integer string_to_integer(char* s);
char* integer_to_string(integer x);
int is_zero_integer(integer x);
//------------------------------------------------------------------------------
integer create_integer(int components) {
integer result;
result.num_components = components;
result.c = (component_t*)malloc(sizeof(component_t)*components);
return result;
}
void free_integer(integer i) {
free(i.c);
}
void set_zero_integer(integer i) {
memset(i.c, 0, sizeof(component_t)*i.num_components);
}
int is_zero_integer(integer x) {
int i;
for(i=0; i < x.num_components; i++) {
if (x.c[i] != 0) return 0;
}
return 1;
}
void copy_integer(integer source, integer target) {
memmove(target.c, source.c,
sizeof(component_t)*MIN(source.num_components, target.num_components));
if (target.num_components > source.num_components) {
memset(target.c + source.num_components, 0,
sizeof(component_t)*(target.num_components - source.num_components));
}
}
void add_integer(integer left, integer right, integer result) {
double_component_t carry = 0;
int i;
for(i=0; i<left.num_components || i<right.num_components || carry != 0; i++) {
double_component_t partial_sum = carry;
carry = 0;
if (i < left.num_components) partial_sum += left.c[i];
if (i < right.num_components) partial_sum += right.c[i];
if (partial_sum > MAX_COMPONENT) {
partial_sum &= MAX_COMPONENT;
carry = 1;
}
result.c[i] = (component_t)partial_sum;
}
for ( ; i < result.num_components; i++) { result.c[i] = 0; }
}
void subtract_integer(integer left, integer right, integer result) {
int borrow = 0;
int i;
for(i=0; i<left.num_components; i++) {
double_component_t lhs = left.c[i];
double_component_t rhs = (i < right.num_components) ? right.c[i] : 0;
if (borrow) {
if (lhs <= rhs) {
/* leave borrow set to 1 */
lhs += (MAX_COMPONENT + 1) - 1;
} else {
borrow = 0;
lhs--;
}
}
if (lhs < rhs) {
borrow = 1;
lhs += MAX_COMPONENT + 1;
}
result.c[i] = lhs - rhs;
}
for ( ; i < result.num_components; i++) { result.c[i] = 0; }
}
void multiply_small_integer(integer left, component_t right, integer result) {
double_component_t carry = 0;
int i;
for(i=0; i<left.num_components || carry != 0; i++) {
double_component_t partial_sum = carry;
carry = 0;
if (i < left.num_components) partial_sum += left.c[i]*right;
carry = partial_sum >> COMPONENT_BITS;
result.c[i] = (component_t)(partial_sum & MAX_COMPONENT);
}
for ( ; i < result.num_components; i++) { result.c[i] = 0; }
}
void multiply_integer(integer left, integer right, integer result) {
int i, lidx, ridx;
double_component_t carry = 0;
int max_size_no_carry;
int left_max_component = left.num_components - 1;
int right_max_component = right.num_components - 1;
while(left.c[left_max_component] == 0) left_max_component--;
while(right.c[right_max_component] == 0) right_max_component--;
max_size_no_carry = left_max_component + right_max_component;
for(i=0; i <= max_size_no_carry || carry != 0; i++) {
double_component_t partial_sum = carry;
carry = 0;
lidx = MIN(i, left_max_component);
ridx = i - lidx;
while(lidx >= 0 && ridx <= right_max_component) {
partial_sum += ((double_component_t)left.c[lidx])*right.c[ridx];
carry += partial_sum >> COMPONENT_BITS;
partial_sum &= MAX_COMPONENT;
lidx--; ridx++;
}
result.c[i] = partial_sum;
}
for ( ; i < result.num_components; i++) { result.c[i] = 0; }
}
int compare_integers(integer left, integer right) {
int i = MAX(left.num_components - 1, right.num_components - 1);
for ( ; i >= 0; i--) {
component_t left_comp =
(i < left.num_components) ? left.c[i] : 0;
component_t right_comp =
(i < right.num_components) ? right.c[i] : 0;
if (left_comp < right_comp)
return -1;
else if (left_comp > right_comp)
return 1;
}
return 0;
}
void shift_left_one_integer(integer arg) {
int i;
arg.c[arg.num_components - 1] <<= 1;
for (i = arg.num_components - 2; i >= 0; i--) {
arg.c[i + 1] |= arg.c[i] >> (COMPONENT_BITS - 1);
arg.c[i] <<= 1;
}
}
void shift_right_one_integer(integer arg) {
int i;
arg.c[0] >>= 1;
for (i = 1; i < arg.num_components; i++) {
arg.c[i - 1] |= (arg.c[i] & 1) << (COMPONENT_BITS - 1);
arg.c[i] >>= 1;
}
}
component_t mod_small_integer(integer left, component_t right) {
double_component_t mod_two_power = 1;
double_component_t result = 0;
int i, bit;
for(i=0; i<left.num_components; i++) {
for(bit=0; bit<COMPONENT_BITS; bit++) {
if ((left.c[i] & (1 << bit)) != 0) {
result += mod_two_power;
if (result >= right) {
result -= right;
}
}
mod_two_power <<= 1;
if (mod_two_power >= right) {
mod_two_power -= right;
}
}
}
return (component_t)result;
}
void mod_integer(integer left, integer right, integer result) {
integer mod_two_power = create_integer(right.num_components + 1);
int i, bit;
set_zero_integer(result);
set_zero_integer(mod_two_power);
mod_two_power.c[0] = 1;
for(i=0; i<left.num_components; i++) {
for(bit=0; bit<COMPONENT_BITS; bit++) {
if ((left.c[i] & (1 << bit)) != 0) {
add_integer(result, mod_two_power, result);
if (compare_integers(result, right) >= 0) {
subtract_integer(result, right, result);
}
}
shift_left_one_integer(mod_two_power);
if (compare_integers(mod_two_power, right) >= 0) {
subtract_integer(mod_two_power, right, mod_two_power);
}
}
}
free_integer(mod_two_power);
}
void divide_small_integer(integer left, component_t right, integer result) {
double_component_t dividend = 0;
int i;
for (i = left.num_components - 1; i >= 0; i--) {
dividend |= left.c[i];
result.c[i] = dividend/right;
dividend = (dividend % right) << COMPONENT_BITS;
}
}
integer string_to_integer(char* s) {
integer result = create_integer((int)ceil(LOG_2_10*strlen(s)/COMPONENT_BITS));
set_zero_integer(result);
integer digit = create_integer(1);
int i;
for (i = 0; s[i] != '\0'; i++) {
multiply_small_integer(result, 10, result);
digit.c[0] = s[i] - '0';
add_integer(result, digit, result);
}
free_integer(digit);
return result;
}
char* integer_to_string(integer x) {
int i, result_len;
char* result =
(char*)malloc((int)ceil(COMPONENT_BITS*x.num_components/LOG_2_10) + 2);
integer ten = create_integer(1);
ten.c[0] = 10;
if (is_zero_integer(x)) {
strcpy(result, "0");
} else {
for (i = 0; !is_zero_integer(x); i++) {
result[i] = (char)mod_small_integer(x, 10) + '0';
divide_small_integer(x, 10, x);
}
result[i] = '\0';
}
result_len = strlen(result);
for(i=0; i < result_len/2; i++) {
char temp = result[i];
result[i] = result[result_len - i - 1];
result[result_len - i - 1] = temp;
}
free_integer(ten);
return result;
}
#endif
}
//------------------------------------------------------------------------------
class UnsignedIntegerTests : public UnitTest
{
public:
@@ -27,21 +348,21 @@ public:
template <unsigned int Bytes>
void runTest ()
{
typedef UnsignedInteger <Bytes> UInt;
String s;
s << "Bytes=" << String(Bytes);
beginTestCase (s);
UnsignedInteger <Bytes> zero;
UInt zero;
zero.fill (0);
expect (zero.isZero (), "should be zero");
expect (! zero.isNotZero (), "sould not be non-zero");
UnsignedInteger <Bytes> one;
one.clear ();
one [Bytes - 1] = 1;
expect (one == UnsignedInteger <Bytes>::createFromInteger (1U), "should be equal");
UInt one (UInt::createFromInteger (1U));
expect (one == UInt::createFromInteger (1U), "should be equal");
expect (! one.isZero (), "should not be zero");
expect (one.isNotZero (), "sould be non-zero");
@@ -54,23 +375,13 @@ public:
expect (zero == zero, "should be equal");
expect (zero != one, "should not be equal");
expect ((zero | zero) == zero, "should be zero");
expect ((zero | one) != zero, "should not be zero");
expect ((one | one) != zero, "should not be zero");
expect (zero == UInt::createFromInteger (0U), "should be zero");
expect (one == UInt::createFromInteger (1U), "should be one");
expect (one != UInt::createFromInteger (2U), "should not be two");
expect ((one & zero) == zero, "should be zero");
expect ((one & one) == one, "should be one");
expect ((zero & zero) == zero, "should be zero");
expect (zero == UnsignedInteger <Bytes>::createFromInteger (0U), "should be zero");
expect (one == UnsignedInteger <Bytes>::createFromInteger (1U), "should be one");
expect (one != UnsignedInteger <Bytes>::createFromInteger (2U), "should not be two");
UnsignedInteger <Bytes> largest = UnsignedInteger <Bytes>::createFilled (0xff);
UInt largest = UInt::createFilled (0xff);
expect (largest > zero && largest > one, "should be greater");
expect (~largest == zero, "should be zero");
expect (~one < largest, "should be less");
}
void runTest()

View File

@@ -17,15 +17,18 @@
*/
//==============================================================================
#ifndef BEAST_UNSIGNEDINTEGER_H_INCLUDED
#define BEAST_UNSIGNEDINTEGER_H_INCLUDED
#ifndef BEAST_CRYPTO_UNSIGNEDINTEGER_H_INCLUDED
#define BEAST_CRYPTO_UNSIGNEDINTEGER_H_INCLUDED
/** Represents a set of bits of fixed size.
Integer representations are stored in network / big endian byte order.
@note The number of bits represented can only be a multiple of 8.
@tparam Bytes The number of bytes of storage.
The data is stored in "canonical" format which is network (big endian)
byte order, most significant byte first.
In this implementation the pointer to the beginning of the canonical format
may not be aligned.
*/
template <size_t Bytes>
template <std::size_t Bytes>
class UnsignedInteger : public SafeBool <UnsignedInteger <Bytes> >
{
public:
@@ -36,10 +39,16 @@ public:
sizeInBytes = Bytes
};
// The underlying integer type we use when converting to calculation format.
typedef uint32 IntCalcType;
// The type of object resulting from a conversion to calculation format.
typedef UnsignedIntegerCalc <IntCalcType> CalcType;
// Standard container compatibility
typedef uint8 value_type;
typedef value_type* iterator;
typedef value_type const* const_iterator;
typedef uint8 ValueType;
typedef ValueType* iterator;
typedef ValueType const* const_iterator;
/** Hardened hash function for use with HashMap.
The seed is used to make the hash unpredictable. This prevents
@@ -61,14 +70,14 @@ public:
}
/** Generates a simple hash from an UnsignedInteger. */
HashValue generateHash (UnsignedInteger <Bytes> const& key) const noexcept
HashValue generateHash (UnsignedInteger const& key) const
{
HashValue hash;
Murmur::Hash (key.cbegin (), key.sizeInBytes, m_seed, &hash);
return hash;
}
HashValue operator() (UnsignedInteger <Bytes> const& key) const noexcept
HashValue operator() (UnsignedInteger const& key) const
{
HashValue hash;
Murmur::Hash (key.cbegin (), key.sizeInBytes, m_seed, &hash);
@@ -84,12 +93,12 @@ public:
/** Construct the object.
The values are uninitialized.
*/
UnsignedInteger () noexcept
UnsignedInteger ()
{
}
/** Construct a copy. */
UnsignedInteger (UnsignedInteger <Bytes> const& other) noexcept
UnsignedInteger (UnsignedInteger const& other)
{
this->operator= (other);
}
@@ -101,37 +110,31 @@ public:
/** @{ */
explicit UnsignedInteger (void const* buf)
{
m_values [0] = 0; // clear any pad bytes
std::memcpy (begin(), buf, Bytes);
}
template <typename T>
explicit UnsignedInteger (T const* buf)
{
m_values [0] = 0; // clear any pad bytes
std::memcpy (begin(), buf, Bytes);
}
/** @} */
/** Construct from a sequence. */
template <class ForwardIterator>
UnsignedInteger (ForwardIterator start, ForwardIterator finish)
/** Assign from another UnsignedInteger. */
UnsignedInteger& operator= (UnsignedInteger const& other)
{
bassert (std::distance (start, finish) == Bytes);
std::copy (start, finish, begin());
}
/** Assign from another value. */
UnsignedInteger <Bytes>& operator= (UnsignedInteger const& other) noexcept
{
std::memcpy (begin(), other.begin(), Bytes);
// Perform an aligned, all inclusive copy that includes padding.
std::copy (other.m_values, other.m_values + CalcCount, m_values);
return *this;
}
/** Create from an integer type.
@invariant IntegerType must be an unsigned integer type.
*/
template <class IntegerType>
static UnsignedInteger <Bytes> createFromInteger (IntegerType value)
static UnsignedInteger createFromInteger (IntegerType value)
{
static_bassert (Bytes >= sizeof (IntegerType));
UnsignedInteger <Bytes> result;
@@ -143,31 +146,39 @@ public:
/** Construct with a filled value.
*/
static UnsignedInteger <Bytes> createFilled (value_type value)
static UnsignedInteger createFilled (ValueType value)
{
UnsignedInteger <Bytes> result;
UnsignedInteger result;
result.fill (value);
return result;
}
/** Fill with a particular byte value. */
void fill (value_type value) noexcept
void fill (ValueType value)
{
std::fill (begin(), end(), value);
IntCalcType c;
memset (&c, value, sizeof (c));
std::fill (m_values, m_values + CalcCount, c);
}
/** Clear the contents to zero. */
void clear () noexcept
void clear ()
{
fill (0);
std::fill (m_values, m_values + CalcCount, 0);
}
/** Convert to calculation format. */
CalcType toCalcType (bool convert = true)
{
return CalcType::fromCanonical (m_values, Bytes, convert);
}
/** Determine if all bits are zero. */
bool isZero () const noexcept
bool isZero () const
{
for (int i = 0; i < Bytes; ++i)
{
if (m_byte [i] != 0)
if (m_values [i] != 0)
return false;
}
@@ -175,7 +186,7 @@ public:
}
/** Determine if any bit is non-zero. */
bool isNotZero () const noexcept
bool isNotZero () const
{
return ! isZero ();
}
@@ -184,198 +195,107 @@ public:
@return `true` if any bit is non-zero.
@see SafeBool
*/
bool asBoolean () const noexcept
bool asBoolean () const
{
return isNotZero ();
}
/** Access a particular byte. */
value_type& getByte (int byteIndex) noexcept
{
bassert (byteIndex >= 0 && byteIndex < Bytes);
return m_byte [byteIndex];
}
/** Access a particular byte as `const`. */
value_type getByte (int byteIndex) const noexcept
{
bassert (byteIndex >= 0 && byteIndex < Bytes);
return m_byte [byteIndex];
}
/** Access a particular byte. */
value_type& operator[] (int byteIndex) noexcept
{
return getByte (byteIndex);
}
/** Access a particular byte as `const`. */
value_type const operator[] (int byteIndex) const noexcept
{
return getByte (byteIndex);
}
/** Get an iterator to the beginning. */
iterator begin () noexcept
iterator begin ()
{
return &m_byte [0];
return get();
}
/** Get an iterator to past-the-end. */
iterator end () noexcept
iterator end ()
{
return &m_byte [Bytes];
return get()+Bytes;
}
/** Get a const iterator to the beginning. */
const_iterator begin () const noexcept
const_iterator begin () const
{
return &m_byte [0];
return get();
}
/** Get a const iterator to past-the-end. */
const_iterator end () const noexcept
const_iterator end () const
{
return &m_byte [Bytes];
return get()+Bytes;
}
/** Get a const iterator to the beginning. */
const_iterator cbegin () const noexcept
const_iterator cbegin () const
{
return &m_byte [0];
return get();
}
/** Get a const iterator to past-the-end. */
const_iterator cend () const noexcept
const_iterator cend () const
{
return &m_byte [Bytes];
return get()+Bytes;
}
/** Compare two objects. */
int compare (UnsignedInteger <Bytes> const& other) const noexcept
/** Compare two objects of equal size.
The comparison is performed using a numeric lexicographical comparison.
*/
int compare (UnsignedInteger const& other) const
{
return memcmp (cbegin (), other.cbegin (), Bytes);
}
/** Determine equality. */
bool operator== (UnsignedInteger <Bytes> const& other) const noexcept
bool operator== (UnsignedInteger const& other) const
{
return compare (other) == 0;
}
/** Determine inequality. */
bool operator!= (UnsignedInteger <Bytes> const& other) const noexcept
bool operator!= (UnsignedInteger const& other) const
{
return compare (other) != 0;
}
/** Ordered comparison. */
bool operator< (UnsignedInteger <Bytes> const& other) const noexcept
bool operator< (UnsignedInteger const& other) const
{
return compare (other) < 0;
}
/** Ordered comparison. */
bool operator<= (UnsignedInteger <Bytes> const& other) const noexcept
bool operator<= (UnsignedInteger const& other) const
{
return compare (other) <= 0;
}
/** Ordered comparison. */
bool operator> (UnsignedInteger <Bytes> const& other) const noexcept
bool operator> (UnsignedInteger const& other) const
{
return compare (other) > 0;
}
/** Ordered comparison. */
bool operator>= (UnsignedInteger <Bytes> const& other) const noexcept
bool operator>= (UnsignedInteger const& other) const
{
return compare (other) >= 0;
}
/** Perform bitwise logical-not. */
UnsignedInteger <Bytes> operator~ () const noexcept
{
UnsignedInteger <Bytes> result;
for (int i = 0; i < Bytes; ++i)
result [i] = ~getByte (i);
return result;
}
/** Perform bitwise logical-or. */
UnsignedInteger <Bytes>& operator|= (UnsignedInteger <Bytes> const& rhs) noexcept
{
for (int i = 0; i < Bytes; ++i)
getByte (i) |= rhs [i];
return *this;
}
/** Perform bitwise logical-or. */
UnsignedInteger <Bytes> operator| (UnsignedInteger <Bytes> const& rhs) const noexcept
{
UnsignedInteger <Bytes> result;
for (int i = 0; i < Bytes; ++i)
result [i] = getByte (i) | rhs [i];
return result;
}
/** Perform bitwise logical-and. */
UnsignedInteger <Bytes>& operator&= (UnsignedInteger <Bytes> const& rhs) noexcept
{
for (int i = 0; i < Bytes; ++i)
getByte (i) &= rhs [i];
return *this;
}
/** Perform bitwise logical-and. */
UnsignedInteger <Bytes> operator& (UnsignedInteger <Bytes> const& rhs) const noexcept
{
UnsignedInteger <Bytes> result;
for (int i = 0; i < Bytes; ++i)
result [i] = getByte (i) & rhs [i];
return result;
}
/** Perform bitwise logical-xor. */
UnsignedInteger <Bytes>& operator^= (UnsignedInteger <Bytes> const& rhs) noexcept
{
for (int i = 0; i < Bytes; ++i)
getByte (i) ^= rhs [i];
return *this;
}
/** Perform bitwise logical-xor. */
UnsignedInteger <Bytes> operator^ (UnsignedInteger <Bytes> const& rhs) const noexcept
{
UnsignedInteger <Bytes> result;
for (int i = 0; i < Bytes; ++i)
result [i] = getByte (i) ^ rhs [i];
return result;
}
// VFALCO TODO:
//
// increment, decrement, add, subtract
// negate
// other stuff that makes sense from base_uint <>
// missing stuff that built-in integers do
//
private:
value_type m_byte [Bytes];
static std::size_t const CalcCount = (Bytes + sizeof (IntCalcType) - 1) / sizeof (IntCalcType);
ValueType* get ()
{
return (reinterpret_cast <ValueType*> (&m_values [0])) +
((sizeof(IntCalcType)-(Bytes&(sizeof(IntCalcType)-1)))&(sizeof(IntCalcType)-1));
}
ValueType const* get () const
{
return (reinterpret_cast <ValueType const*> (&m_values [0])) +
((sizeof(IntCalcType)-(Bytes&(sizeof(IntCalcType)-1)))&(sizeof(IntCalcType)-1));
}
IntCalcType m_values [CalcCount];
};
#endif

View File

@@ -0,0 +1,428 @@
//------------------------------------------------------------------------------
/*
This file is part of Beast: https://github.com/vinniefalco/Beast
Copyright 2013, Vinnie Falco <vinnie.falco@gmail.com>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#ifndef BEAST_CRYPTO_UNSIGNEDINTEGERCALC_H_INCLUDED
#define BEAST_CRYPTO_UNSIGNEDINTEGERCALC_H_INCLUDED
namespace detail
{
template <typename UInt>
struct DoubleWidthUInt;
template <>
struct DoubleWidthUInt <uint16>
{
typedef uint32 type;
};
template <>
struct DoubleWidthUInt <uint32>
{
typedef uint64 type;
};
}
/** Multiprecision unsigned integer suitable for calculations.
The data is stored in "calculation" format, which means it can be
readily used for performing calculations, but no raw access to the
bytes are provided. To transmit a serialized unsigned integer or
perform base encodings, it must be converted back into UnsignedInteger.
The number is represented as a series of native UInt unsigned integer
types, in order of increasing significance.
This is a lightweight object, storage and ownership of the underlying
data buffer is an external responsibility. The makes the class cheap to
copy and pass by value.
A consequence of this ownership model is that arithmetics operators
which return results by value cannot be included in the interface.
*/
template <typename UInt>
class UnsignedIntegerCalc : public SafeBool <UnsignedIntegerCalc <UInt> >
{
public:
typedef typename detail::DoubleWidthUInt <UInt>::type UIntBig;
typedef std::size_t size_type;
static UInt const maxUInt = ((UInt)(-1));
static size_type const numBits = (sizeof(UInt)*8);
//--------------------------------------------------------------------------
/** Construct an empty integer / zero bits. */
UnsignedIntegerCalc ()
: m_size (0)
, m_values (nullptr)
{
}
/** Construct a reference to an existing buffer. */
UnsignedIntegerCalc (UnsignedIntegerCalc const& other)
: m_size (other.m_size)
, m_values (other.m_values)
{
}
/** Construct from an existing array of values.
The existing data must already be in the "calculation" format.
*/
UnsignedIntegerCalc (size_type count, UInt* values)
: m_size (count)
, m_values (values)
{
}
/** Convert to calculation format from canonical format.
This overwrites the callers memory without transferring ownership.
Canonical format is defined as a big endian byte oriented
multiprecision integer format. The buffer should point to the
beginning of the storage area and not the beginning of the canonical
data. Bytes is the desired canonical bytes.
*/
static UnsignedIntegerCalc fromCanonical (
void* buffer, size_type const bytes, bool swizzle = true)
{
UInt* const values (reinterpret_cast <UInt*> (buffer));
size_type const count ((bytes + sizeof (UInt) - 1) / sizeof (UInt));
if (swizzle)
{
// Zero fill the possibly uninitialized pad bytes
memset (buffer, 0,
((sizeof(UInt)-(bytes&(sizeof(UInt)-1)))&(sizeof(UInt)-1)));
// Swap and swizzle
UInt* lo (values);
UInt* hi (values + count - 1);
while (lo < hi)
{
std::swap (*lo, *hi);
*lo++ = fromNetworkByteOrder <UInt> (*lo);
*hi-- = fromNetworkByteOrder <UInt> (*hi);
}
if (lo == hi)
*lo = fromNetworkByteOrder <UInt> (*lo);
}
return UnsignedIntegerCalc (count, values);
}
/** Convert the buffer back into canonical format.
Since ownership was never transferred, the caller's data is
restored to its original format. Typically this will be done
as the last step of a series of operations.
*/
void toCanonical ()
{
// Swap and swizzle
UInt* lo (m_values);
UInt* hi (m_values + m_size - 1);
while (lo < hi)
{
std::swap (*lo, *hi);
*lo++ = toNetworkByteOrder <UInt> (*lo);
*hi-- = toNetworkByteOrder <UInt> (*hi);
}
if (lo == hi)
*lo = toNetworkByteOrder <UInt> (*lo);
}
/** Assign a value from another integer.
@note This does not transfer the reference to the buffer, it
copies the values from one buffer to the other.
*/
UnsignedIntegerCalc& operator= (UnsignedIntegerCalc const& other)
{
bassert (other.size() <= size());
size_type n (size());
UInt* dest (m_values + size());
for (; n-- > other.size();)
*--dest = 0;
UInt const* rhs (other.m_values + n);
for (; n--;)
*--dest = *--rhs;
return *this;
}
/** Returns `true` if this represents the number zero. */
bool isZero () const
{
for (size_type n (size()); n--;)
if (m_values [n] != 0)
return false;
return true;
}
/** Returns `true` if this represents any number other than zero. */
bool isNotZero () const
{
return ! isZero ();
}
/** Safe conversion to `bool`, `true` means a non-zero value. */
bool asBoolean () const
{
return isNotZero ();
}
/** Returns `true` if the buffer has 0 values. */
bool empty () const
{
return m_size == 0;
}
/** Returns the size of the buffer, in values. */
size_type size () const
{
return m_size;
}
/** Safe array indexing to arbitrary positions.
If the index is out of range, zero is returned.
*/
UInt operator[] (size_type n) const
{
if (n >= 0 && n < size())
return m_values [n];
return 0;
}
/** Universal comparison.
The comparison is performed numerically.
The return values have the same meaning as memcmp().
*/
int compare (UnsignedIntegerCalc const& other) const
{
if (size() == 0)
{
if (other.size() == 0)
return 0;
return -1;
}
else if (other.size() == 0)
{
return 1;
}
for (size_type n (std::max (size(), other.size())); n--;)
{
UInt lhs ((*this)[n]);
UInt rhs (other[n]);
if (lhs < rhs)
return -1;
else if (lhs > rhs)
return 1;
}
return 0;
}
/** Determine equality. */
bool operator== (UnsignedIntegerCalc const& other) const
{
return compare (other) == 0;
}
/** Determine inequality. */
bool operator!= (UnsignedIntegerCalc const& other) const
{
return compare (other) != 0;
}
/** Ordered comparison. */
bool operator< (UnsignedIntegerCalc const& other) const
{
return compare (other) < 0;
}
/** Ordered comparison. */
bool operator<= (UnsignedIntegerCalc const& other) const
{
return compare (other) <= 0;
}
/** Ordered comparison. */
bool operator> (UnsignedIntegerCalc const& other) const
{
return compare (other) > 0;
}
/** Ordered comparison. */
bool operator>= (UnsignedIntegerCalc const& other) const
{
return compare (other) >= 0;
}
/** Assign zero. */
void clear ()
{
UInt* dest (m_values - 1);
for (size_type n (size()); n--;)
*++dest = 0;
}
/** Perform bitwise logical-not. */
/*
UnsignedIntegerCalc& not ()
{
unaryAssign (Not());
return *this;
}
*/
/** Perform bitwise logical-or. */
UnsignedIntegerCalc& operator|= (UnsignedIntegerCalc const& rhs)
{
binaryAssign (rhs, Or());
return *this;
}
/** Perform bitwise logical-and. */
UnsignedIntegerCalc& operator&= (UnsignedIntegerCalc const& rhs)
{
binaryAssign (rhs, And());
return *this;
}
/** Perform bitwise logical-xor. */
UnsignedIntegerCalc& operator^= (UnsignedIntegerCalc const& rhs)
{
binaryAssign (rhs, Xor());
return *this;
}
/** Perform addition. */
UnsignedIntegerCalc& operator+= (UnsignedIntegerCalc const& v)
{
UIntBig carry (0);
UInt* lhs (m_values);
UInt const* rhs (v.m_values - 1);
for (size_type n (0); n<size() || n<v.size(); ++n)
{
UIntBig part (carry);
carry = 0;
if (n < size())
part += *lhs;
if (n < v.size())
part += *++rhs;
if (part > maxUInt)
{
part &= maxUInt;
carry = 1;
}
*lhs++ = UInt (part);
}
bassert (carry == 0) // overflow
return *this;
}
/** Perform small addition. */
UnsignedIntegerCalc& operator+= (UInt rhs)
{
UnsignedIntegerCalc const v (1, &rhs);
return operator+= (v);
}
/** Perform small multiply. */
UnsignedIntegerCalc& operator*= (UInt rhs)
{
UIntBig carry (0);
UInt* lhs (m_values - 1);
for (size_type n (size()); n--;)
{
UIntBig part (carry);
carry = 0;
part += (*++lhs) * UIntBig(rhs);
carry = part >> numBits;
*lhs = UInt (part & maxUInt);
}
bassert (carry == 0); // overflow
return *this;
}
/** Small division. */
UnsignedIntegerCalc operator/= (UInt rhs)
{
UIntBig dividend (0);
UInt* lhs (m_values+size());
for (size_type n (size()); n--;)
{
dividend |= *--lhs;
*lhs = UInt (dividend / rhs);
dividend = (dividend % rhs) << numBits;
}
return *this;
}
/** Small modulus. */
UInt operator% (UInt rhs) const
{
UIntBig modsq = 1;
UIntBig result = 0;
UInt const* lhs (m_values);
for (size_type n (size()); n--; ++lhs)
{
for (int bit (0); bit < numBits; ++bit)
{
if (((*lhs) & (1 << bit)) != 0)
{
result += modsq;
if (result >= rhs)
result -= rhs;
}
modsq <<= 1;
if (modsq >= rhs)
modsq -= rhs;
}
}
return UInt (result);
}
private:
struct Not { void operator() (UInt& rv) const { rv = ~rv; } };
struct Or { void operator() (UInt& lhs, UInt rhs) const { lhs |= rhs; } };
struct And { void operator() (UInt& lhs, UInt rhs) const { lhs &= rhs; } };
struct Xor { void operator() (UInt& lhs, UInt rhs) const { lhs ^= rhs; } };
template <class Operator>
void unaryAssign (Operator op = Operator())
{
UInt* dest (m_values-1);
for (size_type n (size()); n--;)
op (*++dest);
}
template <class Operator>
void binaryAssign (UnsignedIntegerCalc const& other, Operator op = Operator ())
{
UInt* dest (m_values + size());
size_type n (size());
for (; n-- > other.size();)
*--dest = 0;
UInt const* rhs (other.m_values + n);
for (UInt const* rhs (other.m_values + n); n--;)
op (*--dest, *--rhs);
}
size_type m_size;
UInt* m_values;
};
#endif