X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=src%2Fstlplus%2Fportability%2Finf.cpp;fp=src%2Fstlplus%2Fportability%2Finf.cpp;h=80b047556783330867ef7dd05c514d7986ef1943;hb=6b0a0d0efafe34d48ab344fca3b479553bd4e62c;hp=0000000000000000000000000000000000000000;hpb=85783316365181491a3e3c0c63659972477cebba;p=chaz%2Fyoink diff --git a/src/stlplus/portability/inf.cpp b/src/stlplus/portability/inf.cpp new file mode 100644 index 0000000..80b0475 --- /dev/null +++ b/src/stlplus/portability/inf.cpp @@ -0,0 +1,1482 @@ +//////////////////////////////////////////////////////////////////////////////// + +// Author: Andy Rushton +// Copyright: (c) Southampton University 1999-2004 +// (c) Andy Rushton 2004-2009 +// License: BSD License, see ../docs/license.html + +// The integer is represented as a sequence of bytes. They are stored such that +// element 0 is the lsB, which makes sense when seen as an integer offset but +// is counter-intuitive when you think that a string is usually read from left +// to right, 0 to size-1, in which case the lsB is on the *left*. + +// This solution is compatible with 32-bit and 64-bit machines with either +// little-endian or big-endian representations of integers. + +// Problem: I'm using std::string, which is an array of char. However, char is +// not well-defined - it could be signed or unsigned. + +// In fact, there's no requirement for a char to even be one byte - it can be +// any size of one byte or more. However, it's just impossible to make any +// progress with that naffness (thanks to the C non-standardisation committee) +// and the practice is that char on every platform/compiler I've ever come +// across is that char = byte. + +// The algorithms here use unsigned char to represent bit-patterns so I have to +// be careful to type-cast from char to unsigned char a lot. I use a typedef to +// make life easier. + +//////////////////////////////////////////////////////////////////////////////// +#include "inf.hpp" +#include +//////////////////////////////////////////////////////////////////////////////// + +namespace stlplus +{ + + //////////////////////////////////////////////////////////////////////////////// + // choose a sensible C type for a byte + + typedef unsigned char byte; + + //////////////////////////////////////////////////////////////////////////////// + // local functions + + // removes leading bytes that don't contribute to the value to create the minimum string representation + static void reduce_string(std::string& data) + { + while(data.size() > 1 && + ((byte(data[data.size()-1]) == byte(0) && byte(data[data.size()-2]) < byte(128)) || + (byte(data[data.size()-1]) == byte(255) && byte(data[data.size()-2]) >= byte(128)))) + { + data.erase(data.end()-1); + } + } + + // generic implementations of type conversions from integer type to internal representation + // data: integer value for conversion + // result: internal representation + + template + static void convert_from_signed(const T& data, std::string& result) + { + result.erase(); + bool lsb_first = little_endian(); + byte* address = (byte*)&data; + for (size_t i = 0; i < sizeof(T); i++) + { + size_t offset = (lsb_first ? i : (sizeof(T) - i - 1)); + result.append(1,address[offset]); + } + reduce_string(result); + } + + template + static void convert_from_unsigned(const T& data, std::string& result) + { + result.erase(); + bool lsb_first = little_endian(); + byte* address = (byte*)&data; + for (size_t i = 0; i < sizeof(T); i++) + { + size_t offset = (lsb_first ? i : (sizeof(T) - i - 1)); + result.append(1,address[offset]); + } + // inf is signed - so there is a possible extra sign bit to add + result.append(1,std::string::value_type(0)); + reduce_string(result); + } + + // generic implementations of type conversions from internal representation to an integer type + // data : string representation of integer + // result: integer result of conversion + // return: flag indicating success - false = overflow + + template + bool convert_to_signed(const std::string& data, T& result) + { + bool lsb_first = little_endian(); + byte* address = (byte*)&result; + for (size_t i = 0; i < sizeof(T); i++) + { + size_t offset = lsb_first ? i : (sizeof(T) - i - 1); + if (i < data.size()) + address[offset] = byte(data[i]); + else if (data.empty() || (byte(data[data.size()-1]) < byte(128))) + address[offset] = byte(0); + else + address[offset] = byte(255); + } + return data.size() <= sizeof(T); + } + + template + bool convert_to_unsigned(const std::string& data, T& result) + { + bool lsb_first = little_endian(); + byte* address = (byte*)&result; + for (size_t i = 0; i < sizeof(T); i++) + { + size_t offset = lsb_first ? i : (sizeof(T) - i - 1); + if (i < data.size()) + address[offset] = byte(data[i]); + else + address[offset] = byte(0); + } + return data.size() <= sizeof(T); + } + + //////////////////////////////////////////////////////////////////////////////// + // Conversions to string + + static char to_char [] = "0123456789abcdefghijklmnopqrstuvwxyz"; + static int from_char [] = + { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, + -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 + }; + + static void convert_to_string(const stlplus::inf& data, std::string& result, unsigned radix = 10) + throw(std::invalid_argument) + { + // only support the C-style radixes plus 0b for binary + if (radix != 2 && radix != 8 && radix != 10 && radix != 16) + throw std::invalid_argument("invalid radix value"); + inf local_i = data; + // untangle all the options + bool binary = radix == 2; + bool octal = radix == 8; + bool hex = radix == 16; + // the C representations for binary, octal and hex use 2's-complement representation + // all other represenations use sign-magnitude + if (hex || octal || binary) + { + // bit-pattern representation + // this is the binary representation optionally shown in octal or hex + // first generate the binary by masking the bits + for (unsigned j = local_i.bits(); j--; ) + result += (local_i.bit(j) ? '1' : '0'); + // the result is now the full width of the type - e.g. int will give a 32-bit result + // now interpret this as either binary, octal or hex and add the prefix + if (binary) + { + // trim down to the smallest string that preserves the value + while (true) + { + // do not trim to less than 1 bit (sign only) + if (result.size() <= 1) break; + // only trim if it doesn't change the sign and therefore the value + if (result[0] != result[1]) break; + result.erase(0,1); + } + // add the prefix + result.insert((std::string::size_type)0, "0b"); + } + else if (octal) + { + // the result is currently binary + // trim down to the smallest string that preserves the value + while (true) + { + // do not trim to less than 2 bits (sign plus 1-bit magnitude) + if (result.size() <= 2) break; + // only trim if it doesn't change the sign and therefore the value + if (result[0] != result[1]) break; + result.erase(0,1); + } + // also ensure that the binary is a multiple of 3 bits to make the conversion to octal easier + while (result.size() % 3 != 0) + result.insert((std::string::size_type)0, 1, result[0]); + // now convert to octal + std::string octal_result; + for (unsigned i = 0; i < result.size()/3; i++) + { + // yuck - ugly or what? + if (result[i*3] == '0') + { + if (result[i*3+1] == '0') + { + if (result[i*3+2] == '0') + octal_result += '0'; + else + octal_result += '1'; + } + else + { + if (result[i*3+2] == '0') + octal_result += '2'; + else + octal_result += '3'; + } + } + else + { + if (result[i*3+1] == '0') + { + if (result[i*3+2] == '0') + octal_result += '4'; + else + octal_result += '5'; + } + else + { + if (result[i*3+2] == '0') + octal_result += '6'; + else + octal_result += '7'; + } + } + } + result = octal_result; + // add the prefix + result.insert((std::string::size_type)0, "0"); + } + else + { + // similar to octal + while (true) + { + // do not trim to less than 2 bits (sign plus 1-bit magnitude) + if (result.size() <= 2) break; + // only trim if it doesn't change the sign and therefore the value + if (result[0] != result[1]) break; + result.erase(0,1); + } + // pad to a multiple of 4 characters + while (result.size() % 4 != 0) + result.insert((std::string::size_type)0, 1, result[0]); + // now convert to hex + std::string hex_result; + for (unsigned i = 0; i < result.size()/4; i++) + { + // yuck - ugly or what? + if (result[i*4] == '0') + { + if (result[i*4+1] == '0') + { + if (result[i*4+2] == '0') + { + if (result[i*4+3] == '0') + hex_result += '0'; + else + hex_result += '1'; + } + else + { + if (result[i*4+3] == '0') + hex_result += '2'; + else + hex_result += '3'; + } + } + else + { + if (result[i*4+2] == '0') + { + if (result[i*4+3] == '0') + hex_result += '4'; + else + hex_result += '5'; + } + else + { + if (result[i*4+3] == '0') + hex_result += '6'; + else + hex_result += '7'; + } + } + } + else + { + if (result[i*4+1] == '0') + { + if (result[i*4+2] == '0') + { + if (result[i*4+3] == '0') + hex_result += '8'; + else + hex_result += '9'; + } + else + { + if (result[i*4+3] == '0') + hex_result += 'a'; + else + hex_result += 'b'; + } + } + else + { + if (result[i*4+2] == '0') + { + if (result[i*4+3] == '0') + hex_result += 'c'; + else + hex_result += 'd'; + } + else + { + if (result[i*4+3] == '0') + hex_result += 'e'; + else + hex_result += 'f'; + } + } + } + } + result = hex_result; + // add the prefix + result.insert((std::string::size_type)0, "0x"); + } + } + else + { + // convert to sign-magnitude + // the representation is: + // [sign]magnitude + bool negative = local_i.negative(); + local_i.abs(); + // create a representation of the magnitude by successive division + inf inf_radix(radix); + do + { + std::pair divided = local_i.divide(inf_radix); + unsigned remainder = divided.second.to_unsigned(); + char digit = to_char[remainder]; + result.insert((std::string::size_type)0, 1, digit); + local_i = divided.first; + } + while(!local_i.zero()); + // add the prefixes + // add a sign only for negative values + if (negative) + result.insert((std::string::size_type)0, 1, '-'); + } + } + + //////////////////////////////////////////////////////////////////////////////// + // Conversions FROM string + + void convert_from_string(const std::string& str, inf& result, unsigned radix = 10) throw(std::invalid_argument) + { + result = 0; + // only support the C-style radixes plus 0b for binary + // a radix of 0 means deduce the radix from the input - assume 10 + if (radix != 0 && radix != 2 && radix != 8 && radix != 10 && radix != 16) + throw std::invalid_argument("invalid radix value"); + unsigned i = 0; + // the radix passed as a parameter is just the default - it can be + // overridden by the C prefix + // Note: a leading zero is the C-style prefix for octal - I only make this + // override the default when the default radix is not specified + // first check for a C-style prefix + bool c_style = false; + if (i < str.size() && str[i] == '0') + { + // binary or hex + if (i+1 < str.size() && tolower(str[i+1]) == 'x') + { + c_style = true; + radix = 16; + i += 2; + } + else if (i+1 < str.size() && tolower(str[i+1]) == 'b') + { + c_style = true; + radix = 2; + i += 2; + } + else if (radix == 0) + { + c_style = true; + radix = 8; + i += 1; + } + } + if (radix == 0) + radix = 10; + if (c_style) + { + // the C style formats are bit patterns not integer values - these need + // to be sign-extended to get the right value + std::string binary; + if (radix == 2) + { + for (unsigned j = i; j < str.size(); j++) + { + switch(str[j]) + { + case '0': + binary += '0'; + break; + case '1': + binary += '1'; + break; + default: + throw std::invalid_argument("invalid binary character in string " + str); + } + } + } + else if (radix == 8) + { + for (unsigned j = i; j < str.size(); j++) + { + switch(str[j]) + { + case '0': + binary += "000"; + break; + case '1': + binary += "001"; + break; + case '2': + binary += "010"; + break; + case '3': + binary += "011"; + break; + case '4': + binary += "100"; + break; + case '5': + binary += "101"; + break; + case '6': + binary += "110"; + break; + case '7': + binary += "111"; + break; + default: + throw std::invalid_argument("invalid octal character in string " + str); + } + } + } + else + { + for (unsigned j = i; j < str.size(); j++) + { + switch(tolower(str[j])) + { + case '0': + binary += "0000"; + break; + case '1': + binary += "0001"; + break; + case '2': + binary += "0010"; + break; + case '3': + binary += "0011"; + break; + case '4': + binary += "0100"; + break; + case '5': + binary += "0101"; + break; + case '6': + binary += "0110"; + break; + case '7': + binary += "0111"; + break; + case '8': + binary += "1000"; + break; + case '9': + binary += "1001"; + break; + case 'a': + binary += "1010"; + break; + case 'b': + binary += "1011"; + break; + case 'c': + binary += "1100"; + break; + case 'd': + binary += "1101"; + break; + case 'e': + binary += "1110"; + break; + case 'f': + binary += "1111"; + break; + default: + throw std::invalid_argument("invalid hex character in string " + str); + } + } + } + // now convert the value + result.resize(binary.size()); + for (unsigned j = 0; j < binary.size(); j++) + result.preset(binary.size() - j - 1, binary[j] == '1'); + } + else + { + // sign-magnitude representation + // now scan for a sign and find whether this is a negative number + bool negative = false; + if (i < str.size()) + { + switch (str[i]) + { + case '-': + negative = true; + i++; + break; + case '+': + i++; + break; + } + } + for (; i < str.size(); i++) + { + result *= inf(radix); + unsigned char ascii = (unsigned char)str[i]; + int ch = from_char[ascii] ; + if (ch == -1) + throw std::invalid_argument("invalid decimal character in string " + str); + result += inf(ch); + } + if (negative) + result.negate(); + } + } + + //////////////////////////////////////////////////////////////////////////////// + // constructors - mostly implemented in terms of the assignment operators + + inf::inf(void) + { + // void constructor initialises to zero - represented as a single-byte value containing zero + m_data.append(1,std::string::value_type(0)); + } + + inf::inf(short r) + { + operator=(r); + } + + inf::inf(unsigned short r) + { + operator=(r); + } + + inf::inf(int r) + { + operator=(r); + } + + inf::inf(unsigned r) + { + operator=(r); + } + + inf::inf(long r) + { + operator=(r); + } + + inf::inf(unsigned long r) + { + operator=(r); + } + + inf::inf (const std::string& r) throw(std::invalid_argument) + { + operator=(r); + } + + inf::inf(const inf& r) + { +#ifdef __BORLANDC__ + // work round bug in Borland compiler - copy constructor fails if string + // contains null characters, so do my own copy + for (unsigned i = 0; i < r.m_data.size(); i++) + m_data += r.m_data[i]; +#else + m_data = r.m_data; +#endif + } + + //////////////////////////////////////////////////////////////////////////////// + + inf::~inf(void) + { + } + + //////////////////////////////////////////////////////////////////////////////// + // assignments convert from iteger types to internal representation + + inf& inf::operator = (short r) + { + convert_from_signed(r, m_data); + return *this; + } + + inf& inf::operator = (unsigned short r) + { + convert_from_unsigned(r, m_data); + return *this; + } + + inf& inf::operator = (int r) + { + convert_from_signed(r, m_data); + return *this; + } + + inf& inf::operator = (unsigned r) + { + convert_from_unsigned(r, m_data); + return *this; + } + + inf& inf::operator = (long r) + { + convert_from_signed(r, m_data); + return *this; + } + + inf& inf::operator = (unsigned long r) + { + convert_from_unsigned(r, m_data); + return *this; + } + + inf& inf::operator = (const std::string& r) throw(std::invalid_argument) + { + convert_from_string(r, *this); + return *this; + } + + inf& inf::operator = (const inf& r) + { + m_data = r.m_data; + return *this; + } + + //////////////////////////////////////////////////////////////////////////////// + + short inf::to_short(bool truncate) const throw(std::overflow_error) + { + short result = 0; + if (!convert_to_signed(m_data, result)) + if (!truncate) + throw std::overflow_error("stlplus::inf::to_short"); + return result; + } + + unsigned short inf::to_unsigned_short(bool truncate) const throw(std::overflow_error) + { + unsigned short result = 0; + if (!convert_to_unsigned(m_data, result)) + if (!truncate) + throw std::overflow_error("stlplus::inf::to_unsigned_short"); + return result; + } + + int inf::to_int(bool truncate) const throw(std::overflow_error) + { + int result = 0; + if (!convert_to_signed(m_data, result)) + if (!truncate) + throw std::overflow_error("stlplus::inf::to_int"); + return result; + } + + unsigned inf::to_unsigned(bool truncate) const throw(std::overflow_error) + { + unsigned result = 0; + if (!convert_to_unsigned(m_data, result)) + if (!truncate) + throw std::overflow_error("stlplus::inf::to_unsigned"); + return result; + } + + long inf::to_long(bool truncate) const throw(std::overflow_error) + { + long result = 0; + if (!convert_to_signed(m_data, result)) + if (!truncate) + throw std::overflow_error("stlplus::inf::to_long"); + return result; + } + + unsigned long inf::to_unsigned_long(bool truncate) const throw(std::overflow_error) + { + unsigned long result = 0; + if (!convert_to_unsigned(m_data, result)) + if (!truncate) + throw std::overflow_error("stlplus::inf::to_unsigned_long"); + return result; + } + + //////////////////////////////////////////////////////////////////////////////// + // resize the inf regardless of the data + + void inf::resize(unsigned bits) + { + if (bits == 0) bits = 1; + unsigned bytes = (bits+7)/8; + byte extend = negative() ? byte(255) : byte (0); + while(bytes > m_data.size()) + m_data.append(1,extend); + } + + // reduce the bit count to the minimum needed to preserve the value + + void inf::reduce(void) + { + reduce_string(m_data); + } + + //////////////////////////////////////////////////////////////////////////////// + // the number of significant bits in the number + + unsigned inf::bits (void) const + { + // The number of significant bits in the integer value - this is the number + // of indexable bits less any redundant sign bits at the msb + // This does not assume that the inf has been reduced to its minimum form + unsigned result = indexable_bits(); + bool sign = bit(result-1); + while (result > 1 && (sign == bit(result-2))) + result--; + return result; + } + + unsigned inf::size(void) const + { + return bits(); + } + + unsigned inf::indexable_bits (void) const + { + return 8 * unsigned(m_data.size()); + } + + //////////////////////////////////////////////////////////////////////////////// + // bitwise operations + + bool inf::bit (unsigned index) const throw(std::out_of_range) + { + if (index >= indexable_bits()) + throw std::out_of_range(std::string("stlplus::inf::bit")); + // first split the offset into byte offset and bit offset + unsigned byte_offset = index/8; + unsigned bit_offset = index%8; + return (byte(m_data[byte_offset]) & (byte(1) << bit_offset)) != 0; + } + + bool inf::operator [] (unsigned index) const throw(std::out_of_range) + { + return bit(index); + } + + void inf::set (unsigned index) throw(std::out_of_range) + { + if (index >= indexable_bits()) + throw std::out_of_range(std::string("stlplus::inf::set")); + // first split the offset into byte offset and bit offset + unsigned byte_offset = index/8; + unsigned bit_offset = index%8; + m_data[byte_offset] |= (byte(1) << bit_offset); + } + + void inf::clear (unsigned index) throw(std::out_of_range) + { + if (index >= indexable_bits()) + throw std::out_of_range(std::string("stlplus::inf::clear")); + // first split the offset into byte offset and bit offset + unsigned byte_offset = index/8; + unsigned bit_offset = index%8; + m_data[byte_offset] &= (~(byte(1) << bit_offset)); + } + + void inf::preset (unsigned index, bool value) throw(std::out_of_range) + { + if (value) + set(index); + else + clear(index); + } + + inf inf::slice(unsigned low, unsigned high) const throw(std::out_of_range) + { + if (low >= indexable_bits()) + throw std::out_of_range(std::string("stlplus::inf::slice: low index")); + if (high >= indexable_bits()) + throw std::out_of_range(std::string("stlplus::inf::slice: high index")); + inf result; + if (high >= low) + { + // create a result the right size and filled with sign bits + std::string::size_type result_size = (high-low+1+7)/8; + result.m_data.erase(); + byte extend = bit(high) ? byte(255) : byte (0); + while (result.m_data.size() < result_size) + result.m_data.append(1,extend); + // now set the relevant bits + for (unsigned i = low; i <= high; i++) + result.preset(i-low, bit(i)); + } + return result; + } + + //////////////////////////////////////////////////////////////////////////////// + // testing operations + + bool inf::negative (void) const + { + return bit(indexable_bits()-1); + } + + bool inf::natural (void) const + { + return !negative(); + } + + bool inf::positive (void) const + { + return natural() && !zero(); + } + + bool inf::zero (void) const + { + for (std::string::size_type i = 0; i < m_data.size(); i++) + if (m_data[i] != 0) + return false; + return true; + } + + bool inf::non_zero (void) const + { + return !zero(); + } + + bool inf::operator ! (void) const + { + return zero(); + } + + //////////////////////////////////////////////////////////////////////////////// + // comparison operators + + bool inf::operator == (const inf& r) const + { + // Two infs are equal if they are numerically equal, even if they are + // different sizes (i.e. they could be non-reduced values). + // This makes life a little more complicated than if I could assume that values were reduced. + byte l_extend = negative() ? byte(255) : byte (0); + byte r_extend = r.negative() ? byte(255) : byte (0); + std::string::size_type bytes = maximum(m_data.size(),r.m_data.size()); + for (std::string::size_type i = bytes; i--; ) + { + byte l_byte = (i < m_data.size() ? byte(m_data[i]) : l_extend); + byte r_byte = (i < r.m_data.size() ? byte(r.m_data[i]) : r_extend); + if (l_byte != r_byte) + return false; + } + return true; + } + + bool inf::operator != (const inf& r) const + { + return !operator==(r); + } + + bool inf::operator < (const inf& r) const + { + // This could be implemented in terms of subtraction. However, it can be + // simplified since there is no need to calculate the accurate difference, + // just the direction of the difference. I compare from msB down and as + // soon as a byte difference is found, that defines the ordering. The + // problem is that in 2's-complement, all negative values are greater than + // all natural values if you just do a straight unsigned comparison. I + // handle this by doing a preliminary test for different signs. + + // For example, a 3-bit signed type has the coding: + // 000 = 0 + // ... + // 011 = 3 + // 100 = -4 + // ... + // 111 = -1 + + // So, for natural values, the ordering of the integer values is the + // ordering of the bit patterns. Similarly, for negative values, the + // ordering of the integer values is the ordering of the bit patterns + // However, the bit patterns for the negative values are *greater than* + // the natural values. This is a side-effect of the naffness of + // 2's-complement representation + + // first handle the case of comparing two values with different signs + bool l_sign = negative(); + bool r_sign = r.negative(); + if (l_sign != r_sign) + { + // one argument must be negative and the other natural + // the left is less if it is the negative one + return l_sign; + } + // the arguments are the same sign + // so the ordering is a simple unsigned byte-by-byte comparison + // However, this is complicated by the possibility that the values could be different lengths + byte l_extend = l_sign ? byte(255) : byte (0); + byte r_extend = r_sign ? byte(255) : byte (0); + std::string::size_type bytes = maximum(m_data.size(),r.m_data.size()); + for (std::string::size_type i = bytes; i--; ) + { + byte l_byte = (i < m_data.size() ? byte(m_data[i]) : l_extend); + byte r_byte = (i < r.m_data.size() ? byte(r.m_data[i]) : r_extend); + if (l_byte != r_byte) + return l_byte < r_byte; + } + // if I get here, the two are equal, so that is not less-than + return false; + } + + bool inf::operator <= (const inf& r) const + { + return !(r < *this); + } + + bool inf::operator > (const inf& r) const + { + return r < *this; + } + + bool inf::operator >= (const inf& r) const + { + return !(*this < r); + } + + //////////////////////////////////////////////////////////////////////////////// + // logical operators + + inf& inf::invert (void) + { + for (std::string::size_type i = 0; i < m_data.size(); i++) + m_data[i] = ~m_data[i]; + return *this; + } + + inf inf::operator ~ (void) const + { + inf result(*this); + result.invert(); + return result; + } + + inf& inf::operator &= (const inf& r) + { + // bitwise AND is extended to the length of the largest argument + byte l_extend = negative() ? byte(255) : byte (0); + byte r_extend = r.negative() ? byte(255) : byte (0); + std::string::size_type bytes = maximum(m_data.size(),r.m_data.size()); + for (std::string::size_type i = 0; i < bytes; i++) + { + byte l_byte = (i < m_data.size() ? byte(m_data[i]) : l_extend); + byte r_byte = (i < r.m_data.size() ? byte(r.m_data[i]) : r_extend); + byte result = l_byte & r_byte; + if (i < m_data.size()) + m_data[i] = result; + else + m_data.append(1,result); + } + // now reduce the result + reduce(); + return *this; + } + + inf inf::operator & (const inf& r) const + { + inf result(*this); + result &= r; + return result; + } + + inf& inf::operator |= (const inf& r) + { + // bitwise OR is extended to the length of the largest argument + byte l_extend = negative() ? byte(255) : byte (0); + byte r_extend = r.negative() ? byte(255) : byte (0); + std::string::size_type bytes = maximum(m_data.size(),r.m_data.size()); + for (std::string::size_type i = 0; i < bytes; i++) + { + byte l_byte = (i < m_data.size() ? byte(m_data[i]) : l_extend); + byte r_byte = (i < r.m_data.size() ? byte(r.m_data[i]) : r_extend); + byte result = l_byte | r_byte; + if (i < m_data.size()) + m_data[i] = result; + else + m_data.append(1,result); + } + // now reduce the result + reduce(); + return *this; + } + + inf inf::operator | (const inf& r) const + { + inf result(*this); + result |= r; + return result; + } + + inf& inf::operator ^= (const inf& r) + { + // bitwise XOR is extended to the length of the largest argument + byte l_extend = negative() ? byte(255) : byte (0); + byte r_extend = r.negative() ? byte(255) : byte (0); + std::string::size_type bytes = maximum(m_data.size(),r.m_data.size()); + for (std::string::size_type i = 0; i < bytes; i++) + { + byte l_byte = (i < m_data.size() ? byte(m_data[i]) : l_extend); + byte r_byte = (i < r.m_data.size() ? byte(r.m_data[i]) : r_extend); + byte result = l_byte ^ r_byte; + if (i < m_data.size()) + m_data[i] = result; + else + m_data.append(1,result); + } + // now reduce the result + reduce(); + return *this; + } + + inf inf::operator ^ (const inf& r) const + { + inf result(*this); + result ^= r; + return result; + } + + //////////////////////////////////////////////////////////////////////////////// + // shift operators all preserve the value by increasing the word size + + inf& inf::operator <<= (unsigned shift) + { + // left shift is a shift towards the msb, with 0s being shifted in at the lsb + // split this into a byte shift followed by a bit shift + + // first expand the value to be big enough for the result + std::string::size_type new_size = (indexable_bits() + shift + 7) / 8; + byte extend = negative() ? byte(255) : byte (0); + while (m_data.size() < new_size) + m_data.append(1,extend); + // now do the byte shift + unsigned byte_shift = shift/8; + if (byte_shift > 0) + { + for (std::string::size_type b = new_size; b--; ) + m_data[b] = (b >= byte_shift) ? m_data[b-byte_shift] : byte(0); + } + // and finally the bit shift + unsigned bit_shift = shift%8; + if (bit_shift > 0) + { + for (std::string::size_type b = new_size; b--; ) + { + byte current = byte(m_data[b]); + byte previous = b > 0 ? m_data[b-1] : byte(0); + m_data[b] = (current << bit_shift) | (previous >> (8 - bit_shift)); + } + } + // now reduce the result + reduce(); + return *this; + } + + inf inf::operator << (unsigned shift) const + { + inf result(*this); + result <<= shift; + return result; + } + + inf& inf::operator >>= (unsigned shift) + { + // right shift is a shift towards the lsb, with sign bits being shifted in at the msb + // split this into a byte shift followed by a bit shift + + // a byte of sign bits + byte extend = negative() ? byte(255) : byte (0); + // do the byte shift + unsigned byte_shift = shift/8; + if (byte_shift > 0) + { + for (std::string::size_type b = 0; b < m_data.size(); b++) + m_data[b] = (b + byte_shift < m_data.size()) ? m_data[b+byte_shift] : extend; + } + // and finally the bit shift + unsigned bit_shift = shift%8; + if (bit_shift > 0) + { + for (std::string::size_type b = 0; b < m_data.size(); b++) + { + byte current = byte(m_data[b]); + byte next = ((b+1) < m_data.size()) ? m_data[b+1] : extend; + byte shifted = (current >> bit_shift) | (next << (8 - bit_shift)); + m_data[b] = shifted; + } + } + // now reduce the result + reduce(); + return *this; + } + + inf inf::operator >> (unsigned shift) const + { + inf result(*this); + result >>= shift; + return result; + } + + //////////////////////////////////////////////////////////////////////////////// + // negation operators + + inf& inf::negate (void) + { + // do 2's-complement negation + // equivalent to inversion plus one + invert(); + operator += (inf(1)); + return *this; + } + + inf inf::operator - (void) const + { + inf result(*this); + result.negate(); + return result; + } + + inf& inf::abs(void) + { + if (negative()) negate(); + return *this; + } + + inf abs(const inf& i) + { + inf result = i; + result.abs(); + return result; + } + + //////////////////////////////////////////////////////////////////////////////// + // addition operators + + inf& inf::operator += (const inf& r) + { + // do 2's-complement addition + // Note that the addition can give a result that is larger than either argument + byte carry = 0; + std::string::size_type max_size = maximum(m_data.size(),r.m_data.size()); + byte l_extend = negative() ? byte(255) : byte (0); + byte r_extend = r.negative() ? byte(255) : byte (0); + for (std::string::size_type i = 0; i < max_size; i++) + { + byte l_byte = (i < m_data.size() ? byte(m_data[i]) : l_extend); + byte r_byte = (i < r.m_data.size() ? byte(r.m_data[i]) : r_extend); + // calculate the addition in a type that is bigger than a byte in order to catch the carry-out + unsigned short result = ((unsigned short)(l_byte)) + ((unsigned short)(r_byte)) + carry; + // now truncate the result to get the lsB + if (i < m_data.size()) + m_data[i] = byte(result); + else + m_data.append(1,byte(result)); + // and capture the carry out by grabbing the second byte of the result + carry = byte(result >> 8); + } + // if the result overflowed or underflowed, add an extra byte to catch it + unsigned short result = ((unsigned short)(l_extend)) + ((unsigned short)(r_extend)) + carry; + if (byte(result) != (negative() ? byte(255) : byte(0))) + m_data.append(1,byte(result)); + // now reduce the result + reduce(); + return *this; + } + + inf inf::operator + (const inf& r) const + { + inf result(*this); + result += r; + return result; + } + + //////////////////////////////////////////////////////////////////////////////// + // subtraction operators + + inf& inf::operator -= (const inf& r) + { + // subtraction is defined in terms of negation and addition + inf negated = -r; + operator += (negated); + return *this; + } + + inf inf::operator - (const inf& r) const + { + inf result(*this); + result -= r; + return result; + } + + //////////////////////////////////////////////////////////////////////////////// + // multiplication operators + + inf& inf::operator *= (const inf& r) + { + // 2's complement multiplication + // one day I'll do a more efficient version than this based on the underlying representation + inf left(*this); + inf right = r; + // make the right value natural but preserve its sign for later + bool right_negative = right.negative(); + right.abs(); + // implemented as a series of conditional additions + operator = (0); + // left.resize(right.bits() + left.bits() - 1); + left <<= right.bits()-1; + for (unsigned i = right.bits(); i--; ) + { + if (right[i]) + operator += (left); + left >>= 1; + } + if (right_negative) + negate(); + // now reduce the result + reduce(); + return *this; + } + + inf inf::operator * (const inf& r) const + { + inf result(*this); + result *= r; + return result; + } + + //////////////////////////////////////////////////////////////////////////////// + // division and remainder operators + + std::pair inf::divide(const inf& right) const throw(divide_by_zero) + { + if (right.zero()) + throw divide_by_zero("stlplus::inf::divide"); + inf numerator(*this); + inf denominator = right; + // make the numerator natural but preserve the sign for later + bool numerator_negative = numerator.negative(); + numerator.abs(); + // same with the denominator + bool denominator_negative = denominator.negative(); + denominator.abs(); + // the quotient and remainder will form the result + // start with the quotiont zero and the remainder equal to the whole of the + // numerator, then do trial subtraction from this + inf quotient; + inf remainder = numerator; + // there's nothing more to do if the numerator is smaller than the denominator + // but otherwise do the division + if (numerator.bits() >= denominator.bits()) + { + // make the quotient big enough to take the result + quotient.resize(numerator.bits()); + // start with the numerator shifted to the far left + unsigned shift = numerator.bits() - denominator.bits(); + denominator <<= shift; + // do the division by repeated subtraction, + for (unsigned i = shift+1; i--; ) + { + if (remainder >= denominator) + { + remainder -= denominator; + quotient.set(i); + } + denominator >>= 1; + } + } + // now adjust the signs + // x/(-y) == (-x)/y == -(x/y) + if (numerator_negative != denominator_negative) + quotient.negate(); + quotient.reduce(); + // x%(-y) == x%y and (-x)%y == -(x%y) + if (numerator_negative) + remainder.negate(); + remainder.reduce(); + return std::pair(quotient,remainder); + } + + inf& inf::operator /= (const inf& r) throw(divide_by_zero) + { + std::pair result = divide(r); + operator=(result.first); + return *this; + } + + inf inf::operator / (const inf& r) const throw(divide_by_zero) + { + std::pair result = divide(r); + return result.first; + } + + inf& inf::operator %= (const inf& r) throw(divide_by_zero) + { + std::pair result = divide(r); + operator=(result.second); + return *this; + } + + inf inf::operator % (const inf& r) const throw(divide_by_zero) + { + std::pair result = divide(r); + return result.second; + } + + //////////////////////////////////////////////////////////////////////////////// + // prefix (void) and postfix (int) operators + + inf& inf::operator ++ (void) + { + operator += (inf(1)); + return *this; + } + + inf inf::operator ++ (int) + { + inf old(*this); + operator += (inf(1)); + return old; + } + + inf& inf::operator -- (void) + { + operator -= (inf(1)); + return *this; + } + + inf inf::operator -- (int) + { + inf old(*this); + operator -= (inf(1)); + return old; + } + + //////////////////////////////////////////////////////////////////////////////// + // string representation and I/O routines + + std::string inf::to_string(unsigned radix) const + throw(std::invalid_argument) + { + std::string result; + convert_to_string(*this, result, radix); + return result; + } + + inf& inf::from_string(const std::string& value, unsigned radix) + throw(std::invalid_argument) + { + convert_from_string(value, *this, radix); + return *this; + } + + std::ostream& operator << (std::ostream& str, const inf& i) + { + try + { + // get radix + unsigned radix = 10; + if (str.flags() & std::ios_base::oct) + radix = 8; + if (str.flags() & std::ios_base::hex) + radix = 16; + // the field width is handled by iostream, so I don't need to handle it as well + // generate the string representation then print it + str << i.to_string(radix); + } + catch(const std::invalid_argument) + { + str.setstate(std::ios_base::badbit); + } + return str; + } + + std::istream& operator >> (std::istream& str, inf& i) + { + try + { + // get radix + unsigned radix = 10; + if (str.flags() & std::ios_base::oct) + radix = 8; + if (str.flags() & std::ios_base::hex) + radix = 16; + // now get the string image of the value + std::string image; + str >> image; + // and convert to inf + i.from_string(image, radix); + } + catch(const std::invalid_argument) + { + str.setstate(std::ios_base::badbit); + } + return str; + } + + //////////////////////////////////////////////////////////////////////////////// + // diagnostic dump + // just convert to hex + + std::string inf::image_debug(void) const + { + // create this dump in the human-readable form, i.e. msB to the left + std::string result = "0x"; + for (std::string::size_type i = m_data.size(); i--; ) + { + byte current = m_data[i]; + byte msB = (current & byte(0xf0)) >> 4; + result += to_char[msB]; + byte lsB = (current & byte(0x0f)); + result += to_char[lsB]; + } + return result; + } + + const std::string& inf::get_bytes(void) const + { + return m_data; + } + + void inf::set_bytes(const std::string& data) + { + m_data = data; + } + +} // end namespace stlplus