Skip to content

Commit

Permalink
replace regex for string ctor
Browse files Browse the repository at this point in the history
  • Loading branch information
universenox authored and sdavtaker committed Aug 4, 2019
1 parent 12d8792 commit c384098
Show file tree
Hide file tree
Showing 4 changed files with 164 additions and 147 deletions.
176 changes: 139 additions & 37 deletions include/real/exact_number.hpp
Expand Up @@ -9,6 +9,7 @@
#include <type_traits>
#include <limits>
#include <iterator>
#include <cctype>

namespace boost {
namespace real {
Expand Down Expand Up @@ -621,8 +622,6 @@ namespace boost {
/// ctor from any integral type
template<typename I, typename std::enable_if_t<std::is_integral<I>::value>>
exact_number(I x) {
static_assert(std::numeric_limits<I>::is_integer);

if (x < 0)
positive = false;
else
Expand All @@ -635,46 +634,148 @@ namespace boost {
x /= BASE;
}
}

// returns {integer_part, decimal_part, exponent, is_positive}
constexpr static std::tuple<std::string_view, std::string_view, exponent_t, bool> number_from_string(std::string_view number) {
std::string_view integer_part;
std::string_view decimal_part;

explicit exact_number (const std::string& number) {
std::regex decimal("((\\+|-)?[[:digit:]]*)(\\.(([[:digit:]]+)?))?((e|E)(((\\+|-)?)[[:digit:]]+))?");
if (!std::regex_match (number, decimal))
throw boost::real::invalid_string_number_exception();
//Know at this point that representation is valid
std::string decimal_part = regex_replace(number, decimal, "$5");
std::string integer_part = regex_replace(number, decimal, "$1");
std::string exp = regex_replace(number, decimal, "$8");
int add_exponent = exp.length() == 0 ? 0 : std::stoi(exp);
if (integer_part[0] == '+') {
positive = true;
integer_part = integer_part.substr(1);
}
else if (integer_part[0] == '-') {
int exponent = 0;
bool exp_positive = true;
bool positive = true;

bool on_integer = false;
bool has_exponent = false;
bool has_decimal = false;
bool has_sign = false;

size_t index = 0;

size_t integer_count = 0;

size_t decimal_start_index = 0; // pos of first number past '.'
size_t decimal_count = 0;

size_t integer_rhs_zeros = 0;
size_t integer_lhs_zeros = 0;
size_t decimal_lhs_zeros = 0;
size_t decimal_rhs_zeros = 0;

if (number[index] == '-') {
positive = false;
integer_part = integer_part.substr(1);
has_sign = true;
index++;
} else if (number[index] == '+') {
// is already positive
has_sign = true;
index++;
}
integer_part = regex_replace(integer_part, std::regex("(0?+)([[:digit:]]?+)"), "$2");
size_t i = decimal_part.length() - 1;
while (decimal_part[i] == '0' && i > 0) {
--i;

// first digit must be nonzero.

for (; index < number.size(); index++) {
// we should not have any characters in the input except 'e' and '.', and
// '.' can only come before e, and these characters can only occur once

// handle '.' and 'e'
if (!has_exponent) {
if(number[index] == 'e') {
has_exponent = true;

if (number[index+1] == '-') {
exp_positive = false;
index++;
} else if (number[index+1] == '+') {
index++;
}
continue;

} else if (!has_decimal) {
if(number[index] == '.') {
has_decimal = true;
decimal_start_index = index + 1;
continue;
}
}
}

// handle other characters, and numbers
if (!std::isdigit(number[index])) {
// if the first number was 0, it may have been octal
if(number[has_sign] == '0') {
throw octal_input_not_supported_exception();
} else {
throw invalid_string_number_exception();
}
} else { // number[index] is a digit
if (has_exponent) {
exponent *= 10;
exponent += number[index] - '0';
if (exponent < 0) {
throw exponent_overflow_exception();
}
continue;

} else if (has_decimal) {
if (number[index] == '0') {
if (decimal_count == 0) {
decimal_lhs_zeros++;
continue;
} else {
decimal_rhs_zeros++;
continue;
}
} else {
decimal_count++;
decimal_count += decimal_rhs_zeros;
decimal_rhs_zeros = 0;
continue;
}
} else { // we're on the integral part
if (number[index] == '0') {
if (integer_count == 0) {
integer_lhs_zeros++;
continue;
} else {
integer_rhs_zeros++;
continue;
}
} else {
integer_count++;
integer_count += integer_rhs_zeros;
integer_rhs_zeros = 0;
continue;
}
}
}
}
decimal_part = decimal_part.substr(0, i + 1);
//decimal and integer parts are stripped of zeroes
exponent = integer_part.length() + add_exponent;
if (decimal_part.empty()) {
i = integer_part.length() - 1;
while (integer_part[i] == '0' && i > 0)
--i;
integer_part = integer_part.substr(0, i + 1);

if (!exp_positive) {
exponent *= -1;
}
if (integer_part.empty()) {
i = 0;
while (decimal_part[i] == '0' && i < decimal_part.length()) {
++i;
--exponent;
}
decimal_part = decimal_part.substr(i);

exponent += integer_count;

if (integer_count == 0) {
decimal_start_index += decimal_lhs_zeros;
exponent -= decimal_lhs_zeros;
} else {
exponent += integer_rhs_zeros;
}

integer_part = number.substr(has_sign + integer_lhs_zeros, integer_count);
decimal_part = number.substr(decimal_start_index, decimal_count);

return {integer_part, decimal_part, exponent, positive};
}

/**
* Takes a number of the form sign AeB, or A, where A is the desired number in base 10 and
* B is the integral exponent, and sign is + or -, and constructs an exact_number
*/
constexpr explicit exact_number (const std::string& number) {
auto [integer_part, decimal_part, exponent, positive] = number_from_string((std::string_view)number);

if (integer_part.empty() && decimal_part.empty()) {
digits = {0};
exponent = 0;
Expand All @@ -686,7 +787,8 @@ namespace boost {
for (const auto& c : decimal_part ) {
digits.push_back(c - '0');
}
}
}


/**
* @brief *Copy constructor:* It constructs a new boost::real::exact_number that is a copy of the
Expand Down
70 changes: 11 additions & 59 deletions include/real/real.hpp
Expand Up @@ -57,7 +57,7 @@ namespace boost {
* operator "==" but for those cases where the class is not able to decide the value of the
* result before reaching the maximum precision, a precision_exception is thrown.
*/

/// @TODO: replace T with something more descriptive
template <typename T = int>
class real {
private:
Expand All @@ -84,79 +84,31 @@ namespace boost {
*/
real(const real& other) : _real_p(other._real_p) {};


/**
* @brief String constructor
*
* @param number - a valid string representing a number.
*
* @throws boost::real::invalid_string_number exception if string doesn't represent a valid number
*/

real(const std::string& number) {
bool positive = true;
std::regex decimal("((\\+|-)?[[:digit:]]*)(\\.(([[:digit:]]+)?))?((e|E)(((\\+|-)?)[[:digit:]]+))?");
if (!std::regex_match (number, decimal))
throw boost::real::invalid_string_number_exception();
//Know at this point that representation is valid
std::string decimal_part = regex_replace(number, decimal, "$5");
std::string integer_part = regex_replace(number, decimal, "$1");
std::string exp = regex_replace(number, decimal, "$8");
int add_exponent = exp.length() == 0 ? 0 : std::stoi(exp);
if (integer_part[0] == '+') {
positive = true;
integer_part = integer_part.substr(1);
}
else if (integer_part[0] == '-') {
positive = false;
integer_part = integer_part.substr(1);
}
integer_part = regex_replace(integer_part, std::regex("(0?+)([[:digit:]]?+)"), "$2");
size_t i = decimal_part.length() - 1;
while (decimal_part[i] == '0' && i >= 0) {
--i;
}
decimal_part = decimal_part.substr(0, i + 1);
//decimal and integer parts are stripped of zeroes
int exponent = integer_part.length() + add_exponent;
if (decimal_part.empty()) {
i = integer_part.length() - 1;
while (integer_part[i] == '0' && i >= 0)
--i;
integer_part = integer_part.substr(0, i + 1);
}
if (integer_part.empty()) {
i = 0;
while (decimal_part[i] == '0' && i < decimal_part.length()) {
++i;
--exponent;
}
decimal_part = decimal_part.substr(i);
}
if (integer_part.empty() && decimal_part.empty())
exponent = 0;
auto [integer_part, decimal_part, exponent, positive] = exact_number<>::number_from_string(number);

if ((int)(decimal_part.length() + integer_part.length()) <= exponent) {
this->_real_p = std::make_shared<real_data<T>>(real_explicit<T>(integer_part, decimal_part, exponent, positive));
}
else {
//this->_real_p = std::make_shared<real_data>(real_operation(this->_real_p, other._real_p, OPERATION::DIVISION));
} else {
int zeroes = decimal_part.length() + integer_part.length() - exponent;
std::string denominator = "1";
for (int i = 0; i<zeroes; ++i)
denominator = denominator + "0";
std::string numerator = integer_part + decimal_part;
// source of inefficiency. copying, casting.
std::string numerator = (std::string) std::string(integer_part).c_str() + (std::string) std::string(decimal_part);
if (!positive)
numerator = "-" + numerator;
std::shared_ptr<real_data<T>> lhs = std::make_shared<real_data<T>>(real_explicit<T>(numerator));
std::shared_ptr<real_data<T>> rhs = std::make_shared<real_data<T>>(real_explicit<T>(denominator));

/*
if(this->_real_p.use_count() > 1) {
this->_real_p = std::make_shared<real_data>(real_data(*this->_real_p));
}
this->_real_p =
std::make_shared<real_data>(real_operation(this->_real_p, other._real_p, OPERATION::DIVISION));
*/
//this->_real_p = std::make_shared<real_data<T>>(real_explicit<T>(integer_part, decimal_part, exponent, positive));

this->_real_p = std::make_shared<real_data<T>>(real_operation(lhs, rhs, OPERATION::DIVISION));
}
}
Expand Down Expand Up @@ -764,15 +716,15 @@ namespace boost {
}
}

inline boost::real::real<int> operator "" _r(long double x) {
inline auto operator "" _r(long double x) {
return boost::real::real<int>(std::to_string(x));
}

inline boost::real::real<int> operator "" _r(unsigned long long x) {
inline auto operator "" _r(unsigned long long x) {
return boost::real::real<int>(std::to_string(x));
}

inline boost::real::real<int> operator "" _r(const char* x, size_t len) {
inline auto operator "" _r(const char* x, size_t len) {
return boost::real::real<int>(x);
}

Expand Down
10 changes: 10 additions & 0 deletions include/real/real_exception.hpp
Expand Up @@ -72,6 +72,16 @@ namespace boost {
return "The current precision exceeds the bounds of the exponent type";
}
};

struct octal_input_not_supported_exception : public std::exception {

const char * what () const throw () override {
return "The string input began with 0 - octal input is not supported at this time";

}
};


}
}

Expand Down

0 comments on commit c384098

Please sign in to comment.