Intro:
I have a homework assignment to create a type for "ints larger than existing types can store."
We should store the numbers in an array of digits(backward for easier math logic).
I'm using a dynamic array to store the info in the object.
I am required to overload at least the +, -, * , <, >, << and >> operators.
Also .cpp and .h files must be separate.
Problem:
Not too sure how to overload the >> operator based on the class attributes and manipulation needed.
BigIntegers.h
#include <string>
#include <iostream>
typedef int* BigIntPtr;
class BigIntegers {
private:
int size; // based on string size, if neg string size -1
BigIntPtr number; // dynamic array ptr
bool isNeg; // set default to false, assumes a positive number
public:
explicit BigIntegers(std::string num = "");
BigIntegers(const BigIntegers &bi);
~BigIntegers();
friend std::istream &operator>>(std::istream &is, BigIntegers &bi) {
/**
* using eg "is >> bi.data;" doesn't seem viable given the data manipulation needed
* see constructor
*/
std::string input;
getline(is,input);
bi = BigIntegers(input);
return is;
}
friend std::ostream &operator<<(std::ostream &os, const BigIntegers &bi) {
if(bi.isNeg) //add sign if needed
os << '-';
for(int s=bi.size-1;s>-1;s--) //print reverse
{
os << bi.number[s];
}
return os;
}
};
BigIntegers.cpp
#include <algorithm>
#include "BigIntegers.h"
BigIntegers::BigIntegers(std::string num) {
//if null
if(num.empty())
{
size = 0;
number = NULL;
isNeg = 0;
return;
}
//determine if its negative
if (num.find('-') == 0)
{
num.erase(remove(num.begin(),num.end(), '-'),num.end());
isNeg =true;
}else {isNeg= false;}
size = num.length();
number = new int[size];
//add array backwards for math optimization
std::string rev; rev.assign(num.rbegin(),num.rend());
for(int i = 0; i < size; i++)
{
number[i]=rev[i]-'0';
}
}
BigIntegers::~BigIntegers() {
delete [] number;
size =0;
isNeg =0;
}
#include <iostream>
#include "BigIntegers.h"
using std::cout;
using std::cin;
using std::string;
int main() {
//basic functionality test
string stringInt = "123456";
string stringIntNeg = "-99987654321";
BigIntegers test1(stringInt);
cout << test1 << "\n";
BigIntegers test2(stringIntNeg);
cout << test2 << "\n";
//iostream test
cout << "Enter a big integer in the form 123456 or -123456.\n";
BigIntegers test3;
cin >> test3;
cout << test3 << "\n";
return 0;
}
output
pr4_bigIntegers\cmake-build-debug\pr4_bigIntegers.exe
123456
-12345678987654321
Enter a big integer in the form 123456 or -123456.
5789256
-57883070081-2144186072
Process finished with exit code 0
Note:
Additionally, sometimes the output is almost correct but negative or some other garbage values are included. eg) cin >> 5314 , cout <<-5314
edit - I've realized that after 4 digits the garbage is introduced. The experimentation continues.
Assignment Instructions - (for additional context, this is a direct copy/paste)
The existing types of integers in C++ cannot store very large integers. We need a new type that can store these large integers we possibly need in dealing with scientific problems.
You can represent an integer by storing the integer as an array of digits.
Design and implement a class for integer arithmetic in which a number is implemented as an array of digits. Each entry of the array will be a digit from 0 to 9 (inclusive).
The number represented is the concatenation of the digits in the array.
You are required to overload at least the +, -, * , <, >, << and >> operators for this class. Try to overload the division operator /.
Do not forget to implement the gang of three: assignment operator, copy constructor and destructor.
Your division operator is an integer operator so that it returns the integer part of the quotient. You need to understand that the purpose of this class is to store large integers so you should not convert your array representation into regular integer representation during the process of overloading these operators. Again, we assume that these integers cannot be handled by using the build-in integer types so your explicit constructor should have a string type parameter, not an integer type parameter, and get each character from the string, convert it to a digit and store it to your array. To perform operations easily, you may want to store an integer in reversed order in your array.
Use dynamic array to store your integer.
Include professional documentation of your code and proper indentation
Separate your header file from implementation file
Test every aspect of your class.
Email clarification from teacher
Just answer a couple of questions from some of you.
The integers are signed because when you do your subtraction you may get a negative integer. So use the first spot of the array to store 0 or 1 (0 for negative and 1 for positive).
The instructions do not allow you to convert the string parameter to an integer. I mean that you should not convert string s="123456" to int n=123456. But, you have to convert character 1 into integer 1, ..., character 6 into integer 6 and store each into your array.
Your overloaded >> operator seems to be correct.
Your second problem: The minus sign is no garbage.
bool isNeg; // set default to false, assumes a positive number
You never set it to false. I debugged your code, and the solution is simple:
BigIntegers::BigIntegers(std::string num) : isNeg(false) {
//your constructor stuff
}
I suggest using a dynamic array of type unsigned short instead.
Saving each digit in an integer each ranging from -2.14 Billion to 2.14 Billion is overkill will require a lot of memory. You don't need negative values to store. You may consider using chars, as you can convert each integer digit into a char and backward but less memory is required. The most memory-efficient way is perhaps an Enum ranging from 0 to 9.
Your class would require storing a number greater than a long long (8 bytes) at least 64 bytes (number array) + 4 bytes for the size variable + 1 bit (isNeg). This is quite large for a number :)
According to your task, you are not allowed to use integers. So you have to change it :)
My approach exploits the fact that each element of an enum class can be converted to an integer type according to its index in the enum class definition (and vice versa). Eventually, each integer can be converted to a char. Thus, there are still integers but you can spot them hardly.
enum class Digit{Zero,One,Two,Three,Four,Five,Six,Seven,Eight,Nine};
typedef Digit* BigIntPtr;
class BigIntegers
{
public:
explicit BigIntegers(std::string num = "");
BigIntegers(const BigIntegers& src);
BigIntegers& operator=(const BigIntegers&src);
~BigIntegers();
friend bool operator<(const BigIntegers& lhs, const BigIntegers& rhs);
friend bool operator>(const BigIntegers& lhs, const BigIntegers& rhs);
friend BigIntegers operator+(const BigIntegers& lhs, const BigIntegers& rhs);
friend BigIntegers operator-(const BigIntegers& lhs, const BigIntegers&rhs);
friend std::istream &operator>>(std::istream &is, BigIntegers &bi);
friend std::ostream &operator<<(std::ostream &os, const BigIntegers &bi);
private:
size_t size; // based on string size, if neg string size -1
BigIntPtr number; // dynamic array ptr
bool isNeg; // set default to false, assumes a positive number
};
Your .cpp file:
#include <algorithm>
#include "bigint.h"
using namespace std;
BigIntegers::BigIntegers(std::string num):isNeg(false)
{
//if null
if (num.empty())
{
size = 0;
number = NULL;
isNeg = 0;
return;
}
//determine if its negative
if (num.find('-') == 0)
{
num.erase(remove(num.begin(), num.end(), '-'), num.end());
isNeg = true;
}
size = num.length();
number = new Digit[size];
//add array backwards for math optimization
std::string rev; rev.assign(num.rbegin(), num.rend());
Digit * aux = number;
std::for_each (rev.begin(),rev.end(),[&](const char c)
{
*number = Digit(c - '0');
number++;
});
number = aux;
}
BigIntegers::BigIntegers(const BigIntegers & src)
: size(src.size), number{new Digit[src.size]}, isNeg(src.isNeg)
{
for (auto i = number, j = src.number; i < number + size; i++, j++)
{
*i = *j;
}
}
BigIntegers & BigIntegers::operator=(const BigIntegers & src)
{
if (this == &src)
return *this;
size = src.size;
isNeg = src.isNeg;
if (number != NULL) delete[] number;
number = new Digit[src.size];
for (auto i = number, j = src.number; i < number + size; i++,j++)
{
*i = *j;
}
return *this;
}
BigIntegers::~BigIntegers()
{
delete[] number;
}
bool operator<(const BigIntegers & lhs, const BigIntegers & rhs)
{
if (lhs.size > rhs.size) return false;
if (lhs.size < rhs.size) return true;
for (auto i = lhs.number + lhs.size - 1, j = rhs.number + rhs.size - 1; i >= lhs.number || j >= rhs.number; i--, j--)
{
if (char(*i) > char(*j))return false;
if (char(*i) < char(*j))return true;
}
return false;
}
bool operator>(const BigIntegers & lhs, const BigIntegers & rhs)
{
return !(lhs < rhs);
}
BigIntegers operator+(const BigIntegers & lhs, const BigIntegers & rhs)
{
string value = "";
Digit aux = Digit::Zero;
for (auto i = lhs.number, j = rhs.number; i < lhs.number + lhs.size || j < rhs.number + rhs.size; i++, j++)
{
char c = char(aux);
c += i < lhs.number + lhs.size ? char(*i) : char(0);
c += j < rhs.number + rhs.size ? char(*j) : char(0);
aux = Digit(0);
if (c > 9)
{
aux = Digit::One;
c -= 10;
}
// 48 is '0' in Ascii table
value += (c+48);
}
if (aux == Digit::One)
{
value += '1';
}
reverse(value.begin(), value.end());
return BigIntegers(value);
}
BigIntegers operator-(const BigIntegers & lhs, const BigIntegers & rhs)
{
bool reverse = false;
if (lhs < rhs)reverse = true;
const BigIntegers& bigger = reverse ? rhs : lhs;
const BigIntegers& smaller = reverse ? lhs : rhs;
Digit aux = Digit::Zero;
std::string value = "";
for (auto i = bigger.number, j = smaller.number; i < bigger.number+bigger.size; i++, j++)
{
char c1 = char(*i);
char c2 = j < smaller.number+smaller.size ? char(*j) : 0;
c2 += char(aux);
aux = Digit::Zero;
if (c1 < c2)
{
aux = Digit::One;
c1 = c1 + 10 - c2;
}
else
{
c1 -= c2;
}
if (c1 > 0 || i < bigger.number + bigger.size - 1)
{
// if condition is to avoid leading zeros
value += (c1 + 48);
}
}
if (reverse)value += "-";
std::reverse(value.begin(), value.end());
return BigIntegers(value);
}
istream& operator>>(istream& is, BigIntegers& bi)
{
std::string input;
getline(is, input);
bi = BigIntegers(input);
return is;
}
std::ostream &operator<<(std::ostream &os, const BigIntegers &bi) {
if (bi.isNeg) //add sign if needed
os << '-';
for (int s = bi.size - 1; s > -1; s--) //print reverse
{
os << static_cast<int>(bi.number[s]);
}
return os;
}
As you may notice, I replaced all for loops using an integer ;)
At least I am not using the int keyword once. However, something like +10 is of course a const integer.
Up to this point, I have not yet been able to determine that storing the digits in reverse is advantageous.
The multiplication is up to you. Nice task :)
This overload is what solved the problem of the garbage values.
BigIntegers &BigIntegers::operator=(const BigIntegers &bi) {
//if number is already assigned here
if(this==&bi)
return *this;
//else assign the number
size = bi.size; isNeg = bi.isNeg;
if (number != nullptr) delete[] number;
number= new int[bi.size];
for (auto i = number, j = bi.number; i < number+size; i++, j++)
*i = *j;
return *this;
}
Related
I am trying to implement a Division Binary Algorithm.
The code has some Logical Errors which I am still trying to figure out how to fix them.
myuint operator/(const myuint<T>& x)
{
myuint<T> temp;
myuint<T> result;
int count = 0;
for(int i = bits.size() - 1 ; i >= 0; --i)
{
temp.bits.push_back(bits[i]);
if(temp >= x)
{
count++;
*this = *this - x;
temp = 0;
}
}
result = count;
return result;
}
I am also overloading the >=, >, and == operators for the division.
The logical problem most probably is in the for loop . What should I do? Thanks
Full code can be accessible from here
== EDIT
What I am trying to achieve is this.
*this is 10100 (20 in decimal)
x is 100 (4 in decimal)
Get the first Bit (1).
Compare it to x
If the bit is greater than the value of x, count++, subtract x from *this. And then Start the loop again which a different *this size.
If the bit is small, then we move to the bit next to it so, now we have 2 bits (10) and we compare it to x.
Then I return the value of count which represents this number of divisions to reach 0.
Not a full answer, but here is the algorithm that you need to implement:
myuint div(const myuint& x, const myuint& y)
{
if (y == 0)
throw "division by zero";
myuint res = 0;
myuint one = 1;
unsigned int xLength = x.bitLength();
unsigned int yLength = y.bitLength();
while (xLength > yLength)
{
res += one << (xLength - yLength - 1);
x -= y << (xLength - yLength - 1);
xLength = x.bitLength();
}
if (x >= y)
return res+1;
return res;
}
So, I found a simple implementation of how to go around Binary Division.
The idea is that you subtract the LSH from the RHS until LHS is smaller RHS and keep a hold for the many times you subtracted RHS from LSH.
myuint operator/(const myuint<T>& x)
{
myuint<T> LHS = *this;
myuint<T> RHS = x;
myuint<T> result;
int count = 0;
bool flag = true;
if(LHS == RHS)
{
return 1;
}
else
{
do
{
if(LHS >= RHS)
{
LHS = LHS - RHS;
count++;
}
else if(LHS < RHS)
{
flag = false;
}
}while(flag);
}
result = count;
return result;
}
This may not be the most efficient way. But it gets the job done.
I have a vector declared containing n integers.
vector <int> tostore[n];
I want to store all the numbers in the vector inside a string in the format of their subscripts, like 12345..n
Example:
vector <int> store_vec{1,2,3,4,5};
int store_str; //to store the digits in order from vector store_vec
cout<<store_str;
Desired Output:
12345
How do I store it in store_str without printing it?
Instead of using an integer, which if it is 32 bits wide will only be able to store 8-9 digits, you could instead build a string that has all of the elements combined like
vector <int> store_vec{1,2,3,4,5};
std::string merged;
merged.reserve(store_vec.size());
for (auto num : store_vec)
merged += '0' + num;
// now merged is "12345"
One way would be to just multiply by 10 each iteration
int result = 0;
for (auto it : tostore)
{
result = result * 10 + it;
}
As mentioned in comments, a more robust approach would be concatenating to an actual string, or at least using a 64-bit integer.
Since you confirmed that store_vec only contains single digit numbers, a simple way of doing this would be :
std::vector<uint8_t> store_vec = {1,2,3,4,5};
std::string str = std::accumulate(store_vec.begin(), store_vec.end(), std::string{},
[](const std::string& res, uint8_t num){ return res + char('0' + num); });
int resnum = atoi(str.c_str());
or basically use the str resulting with accumulate since it already represent the sequence.
Since you know that each value in tostore will only be a single digit, you could use int_8 or uint8_t data types to store the values instead. This way you can still perform arithmetic on the values within the vectors (so long as the result of the arithmetic falls within the range of -128 to 127 or 0 to 255 respectively, see integer types for more details). These data types have the advantage of being only a single byte long, allowing your vector to potentially be more densely packed and faster to traverse. You can then use std::cout << unsigned(tostore[n]) to cast the integer into a character for display. The whole thing would look something like this
#include <iostream>
#include <type_traits>
#include <vector>
int main()
{
std::vector<uint8_t> tostore;
tostore.reserve(32);
for(int i = 0; i < 32; ++i) {
tostore.push_back(i % 10);
}
for(uint i = 0; i < tostore.size(); ++i) {
std::cout << unsigned(tostore[i]);
}
}
Alternatively, if you know that your digit will always be positive it opens a whole new range of possibilities. When converting an integer to a list of characters a program needs to break the integer into its individual digits and then add 48 to that digits value to find its ascii character code equivalent (see asciiTable for more details). The process of splitting the integer into its base 10 digits may be too cumbersome (you decide) if you plan to display these characters often or perform only a few arithmetic operations on the data. In this case you could create a struct that stores the value of the integer as a char data type but performs arithmetic with the data as if it were an integer. This way, when printing the values no operations need to be performed to format the data properly and the only operations that need to be done to format the data for arithmetic operations are simple subtractions by 48 which are very fast. Such a struct could look something like this:
#include <iostream>
#include <type_traits>
#include <vector>
struct notANumber {
char data;
notANumber() {}
notANumber(const int& a) : data(a + 48) {}
notANumber(const char& a) : data(a) {}
notANumber operator+(const notANumber& b) {
notANumber c;
c.data = b.data + c.data - 48;
return c;
}
notANumber operator-(const notANumber& b) {
notANumber c;
c.data = b.data - c.data + 48;
return c;
}
notANumber operator*(const notANumber& b) {
notANumber c;
c.data = (b.data - 48) * (c.data - 48) + 48;
return c;
}
notANumber operator/(const notANumber& b) {
notANumber c;
c.data = (b.data - 48) / (c.data - 48) + 48;
return c;
}
};
int operator+(const int& a, const notANumber& b) {
int c;
c = a + b.data - 48;
return c;
}
int operator-(const int& a, const notANumber& b) {
int c;
c = a - b.data + 48;
return c;
}
int operator*(const int& a, const notANumber& b) {
int c;
c = a * (b.data - 48);
return c;
}
int operator/(const int& a, const notANumber& b) {
int c;
c = a / (b.data - 48);
return c;
}
int main()
{
std::vector<notANumber> tostore;
tostore.reserve(32);
for(int i = 0; i < 32; ++i) {
tostore.push_back(i % 10);
}
std::cout.write(reinterpret_cast<char*>(tostore.data()), tostore.size());
}
Now this might not be what you are looking for, but I hope that it does showcase an important aspect of programming which is "the more you know about the data you are working with, the more you can optimize the program" so make sure you have a good feel for the range of the data you are working with and what operations you are going to be doing with it most often (arithmetic or printing for example) and the cost of those operations.
I have a vector with digits of number, vector represents big integer in system with base 2^32. For example:
vector <unsigned> vec = {453860625, 469837947, 3503557200, 40}
This vector represent this big integer:
base = 2 ^ 32
3233755723588593872632005090577 = 40 * base ^ 3 + 3503557200 * base ^ 2 + 469837947 * base + 453860625
How to get this decimal representation in string?
Here is an inefficient way to do what you want, get a decimal string from a vector of word values representing an integer of arbitrary size.
I would have preferred to implement this as a class, for better encapsulation and so math operators could be added, but to better comply with the question, this is just a bunch of free functions for manipulating std::vector<unsigned> objects. This does use a typedef BiType as an alias for std::vector<unsigned> however.
Functions for doing the binary division make up most of this code. Much of it duplicates what can be done with std::bitset, but for bitsets of arbitrary size, as vectors of unsigned words. If you want to improve efficiency, plug in a division algorithm which does per-word operations, instead of per-bit. Also, the division code is general-purpose, when it is only ever used to divide by 10, so you could replace it with special-purpose division code.
The code generally assumes a vector of unsigned words and also that the base is the maximum unsigned value, plus one. I left a comment wherever things would go wrong for smaller bases or bases which are not a power of 2 (binary division requires base to be a power of 2).
Also, I only tested for 1 case, the one you gave in the OP -- and this is new, unverified code, so you might want to do some more testing. If you find a problem case, I'll be happy to fix the bug here.
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
namespace bigint {
using BiType = std::vector<unsigned>;
// cmp compares a with b, returning 1:a>b, 0:a==b, -1:a<b
int cmp(const BiType& a, const BiType& b) {
const auto max_size = std::max(a.size(), b.size());
for(auto i=max_size-1; i+1; --i) {
const auto wa = i < a.size() ? a[i] : 0;
const auto wb = i < b.size() ? b[i] : 0;
if(wa != wb) { return wa > wb ? 1 : -1; }
}
return 0;
}
bool is_zero(BiType& bi) {
for(auto w : bi) { if(w) return false; }
return true;
}
// canonize removes leading zero words
void canonize(BiType& bi) {
const auto size = bi.size();
if(!size || bi[size-1]) return;
for(auto i=size-2; i+1; --i) {
if(bi[i]) {
bi.resize(i + 1);
return;
}
}
bi.clear();
}
// subfrom subtracts b from a, modifying a
// a >= b must be guaranteed by caller
void subfrom(BiType& a, const BiType& b) {
unsigned borrow = 0;
for(std::size_t i=0; i<b.size(); ++i) {
if(b[i] || borrow) {
// TODO: handle error if i >= a.size()
const auto w = a[i] - b[i] - borrow;
// this relies on the automatic w = w (mod base),
// assuming unsigned max is base-1
// if this is not the case, w must be set to w % base here
borrow = w >= a[i];
a[i] = w;
}
}
for(auto i=b.size(); borrow; ++i) {
// TODO: handle error if i >= a.size()
borrow = !a[i];
--a[i];
// a[i] must be set modulo base here too
// (this is automatic when base is unsigned max + 1)
}
}
// binary division and its helpers: these require base to be a power of 2
// hi_bit_set is base/2
// the definition assumes CHAR_BIT == 8
const auto hi_bit_set = unsigned(1) << (sizeof(unsigned) * 8 - 1);
// shift_right_1 divides bi by 2, truncating any fraction
void shift_right_1(BiType& bi) {
unsigned carry = 0;
for(auto i=bi.size()-1; i+1; --i) {
const auto next_carry = (bi[i] & 1) ? hi_bit_set : 0;
bi[i] >>= 1;
bi[i] |= carry;
carry = next_carry;
}
// if carry is nonzero here, 1/2 was truncated from the result
canonize(bi);
}
// shift_left_1 multiplies bi by 2
void shift_left_1(BiType& bi) {
unsigned carry = 0;
for(std::size_t i=0; i<bi.size(); ++i) {
const unsigned next_carry = !!(bi[i] & hi_bit_set);
bi[i] <<= 1; // assumes high bit is lost, i.e. base is unsigned max + 1
bi[i] |= carry;
carry = next_carry;
}
if(carry) { bi.push_back(1); }
}
// sets an indexed bit in bi, growing the vector when required
void set_bit_at(BiType& bi, std::size_t index, bool set=true) {
std::size_t widx = index / (sizeof(unsigned) * 8);
std::size_t bidx = index % (sizeof(unsigned) * 8);
if(bi.size() < widx + 1) { bi.resize(widx + 1); }
if(set) { bi[widx] |= unsigned(1) << bidx; }
else { bi[widx] &= ~(unsigned(1) << bidx); }
}
// divide divides n by d, returning the result and leaving the remainder in n
// this is implemented using binary division
BiType divide(BiType& n, BiType d) {
if(is_zero(d)) {
// TODO: handle divide by zero
return {};
}
std::size_t shift = 0;
while(cmp(n, d) == 1) {
shift_left_1(d);
++shift;
}
BiType result;
do {
if(cmp(n, d) >= 0) {
set_bit_at(result, shift);
subfrom(n, d);
}
shift_right_1(d);
} while(shift--);
canonize(result);
canonize(n);
return result;
}
std::string get_decimal(BiType bi) {
std::string dec_string;
// repeat division by 10, using the remainder as a decimal digit
// this will build a string with digits in reverse order, so
// before returning, it will be reversed to correct this.
do {
const auto next_bi = divide(bi, {10});
const char digit_value = static_cast<char>(bi.size() ? bi[0] : 0);
dec_string.push_back('0' + digit_value);
bi = next_bi;
} while(!is_zero(bi));
std::reverse(dec_string.begin(), dec_string.end());
return dec_string;
}
}
int main() {
bigint::BiType my_big_int = {453860625, 469837947, 3503557200, 40};
auto dec_string = bigint::get_decimal(my_big_int);
std::cout << dec_string << '\n';
}
Output:
3233755723588593872632005090577
I am trying to add two polynomials in C++ and I'm lost as to where to even start. So the user can enter values for a polynomial, they do not need to be in order or anything.
For example it can be:
Poly 1: 2x^5 + 5x^2 - 2x + 9
Poly 2: x^2 + 0
I have coefficient and exponent stored in the class (objects) private fields. So would I look at the first exponent in Poly 1, search Poly 2 for same exponent first and if found add them? Then go on to the second term?
Code as requested: (NOTE: The implementation is currently wrong and I need help on how to work through this problem.)
#include <cmath>
#include <iostream>
using namespace std;
class polynomial
{
public:
polynomial();
polynomial(int);
polynomial(int exponent[], int coefficient[], int);
~polynomial();
polynomial &operator = (const polynomial &obj);
int evaluate(double uservalue);
polynomial operator+(const polynomial &obj) const;
void operator-(const polynomial &obj) const;
void operator*(const polynomial &obj) const;
friend istream & operator>> (istream & in, polynomial &obj);
friend ostream & operator<< (ostream & out, const polynomial &obj);
friend void growone(polynomial &obj);
private:
int *coefficient;
int *exponent;
int size;
};
And the implementation
polynomial polynomial::operator+(const polynomial &obj) const
{
bool matchFound = false;
polynomial tmp;
if (size >= obj.size) {
for (int i = 0; i < size; i++) {
if (i >= tmp.size) {
growone(tmp);
}
for(int y = 0; i < obj.size; i++) {
if (exponent[i] == obj.exponent[y]) {
tmp.coefficient[i] = (coefficient[i]+obj.coefficient[y]);
tmp.exponent[i] = exponent[i];
tmp.size++;
matchFound = true;
}
}
if (matchFound == false) {
tmp.coefficient[i] = coefficient[i];
tmp.exponent[i] = exponent[i];
tmp.size++;
}
matchFound = false;
}
} else {
}
return tmp;
}
You're not really using the power of C++ to its fullest there. A good rule is that, if you are using non-smart pointers for anything, you should step back and have a rethink.
It's no accident that a large number of questions about C on Stack Overflow have to do about problems with pointers :-)
The other point I would add is that it may be worth actually wasting a small amount of memory to greatly simplify your code. By that, I mean don't try to create sparse arrays to hold your terms (coefficient-exponent pairs). Instead, allow each expression to hold every term up to its maximum, with the coefficients for the unused ones simply set to zero. Unless you have an expression like 4x99999999999999 + 3, the amount of extra memory taken may be worth it.
To that end, I'd propose using a std::vector for just the coefficients starting at an exponent of zero. The exponents are actually decided by the position within the vector. So the expression 4x9 - 17x3 + 3 would be stored as the vector:
{3, 0, 0, -17, 0, 0, 0, 0, 0, 4}
0 <------- exponent -------> 9
That actually makes the task of adding and subtracting polynomials incredibly easy since the coefficients are all nicely lined up.
So, with that in mind, let's introduce a cut-down class for showing how it's done:
#include <iostream>
#include <vector>
using std::cout;
using std::vector;
using std::ostream;
class Polynomial {
public:
Polynomial();
Polynomial(size_t expon[], int coeff[], size_t sz);
~Polynomial();
Polynomial &operator=(const Polynomial &poly);
Polynomial operator+(const Polynomial &poly) const;
friend ostream &operator<<(ostream &os, const Polynomial &poly);
private:
std::vector<int> m_coeff;
};
The default constructor (and the destructor) are very simple, we just ensure that an initialised polynomial always has at least one term:
Polynomial::Polynomial() { m_coeff.push_back(0); }
Polynomial::~Polynomial() {}
The constructor-from-array is a little more complex because we want to, as early as possible, turn the user's "anything goes" format into something we can use easily:
Polynomial::Polynomial(size_t expon[], int coeff[], size_t sz) {
// Work out largest exponent and size vector accordingly.
auto maxExpon = 0;
for (size_t i = 0; i < sz; ++i) {
if (expon[i] > maxExpon) {
maxExpon = expon[i];
}
}
m_coeff.resize(maxExpon + 1, 0);
// Fill in coefficients.
for (size_t i = 0; i < sz; ++i) {
m_coeff[expon[i]] = coeff[i];
}
}
Now you'll see why we made the decision to not use sparse arrays and to place the zero exponent at the start of the vector. Bothe operator= and operator+ are simple because they already know where all terms are:
Polynomial &Polynomial::operator=(const Polynomial &poly) {
if (this != &poly) {
m_coeff.clear();
for (int coeff: poly.m_coeff) {
m_coeff.push_back(coeff);
}
}
return *this;
}
Polynomial Polynomial::operator+(const Polynomial &poly) const {
// Create sum with required size.
size_t thisSize = this->m_coeff.size();
size_t polySize = poly.m_coeff.size();
Polynomial sum;
if (thisSize > polySize) {
sum.m_coeff.resize(thisSize, 0);
} else {
sum.m_coeff.resize(polySize, 0);
}
// Do the actual sum (ignoring terms beyond each limit).
for (size_t idx = 0; idx < sum.m_coeff.size(); ++idx) {
if (idx < thisSize) sum.m_coeff[idx] += this->m_coeff[idx];
if (idx < polySize) sum.m_coeff[idx] += poly.m_coeff[idx];
}
return sum;
}
Now we just need to complete this with an output function and a small test mainline:
ostream &operator<< (ostream &os, const Polynomial &poly) {
bool firstTerm = true;
if (poly.m_coeff.size() == 1 && poly.m_coeff[0] == 0) {
os << "0";
return os;
}
for (size_t idx = poly.m_coeff.size(); idx > 0; --idx) {
if (poly.m_coeff[idx - 1] != 0) {
if (firstTerm) {
os << poly.m_coeff[idx - 1];
} else if (poly.m_coeff[idx - 1] == 1) {
os << " + ";
if (idx == 1) {
os << poly.m_coeff[idx - 1];
}
} else if (poly.m_coeff[idx - 1] == -1) {
os << " - ";
} else if (poly.m_coeff[idx - 1] < 0) {
os << " - " << -poly.m_coeff[idx - 1];
} else {
os << " + " << poly.m_coeff[idx - 1];
}
if (idx > 1) {
os << "x";
if (idx > 2) {
os << "^" << idx - 1;
}
}
firstTerm = false;
}
}
return os;
}
int main() {
int c1[] = {1, 2, 3, 4, 5};
size_t e1[] = {3, 1, 4, 0, 9};
Polynomial p1(e1, c1, (size_t)5);
cout << "Polynomial 1 is " << p1 << " (p1)\n";
int c2[] = {6, 7, 8};
size_t e2[] = {3, 7, 9};
Polynomial p2(e2, c2, (size_t)3);
cout << "Polynomial 2 is " << p2 << " (p2)\n";
Polynomial p3 = p1 + p2;
cout << "Polynomial 3 is " << p3 << " (p3 = p1 = p2);
}
And the output, which I've slightly reformatted to show like terms, shows it in action:
Polynomial 1 is 5x^9 + 3x^4 + x^3 + 2x + 4
Polynomial 2 is 8x^9 + 7x^7 + 6x^3
===================================
Polynomial 3 is 13x^9 + 7x^7 + 3x^4 + 7x^3 + 2x + 4
I am looking for a very simple C++ class implementing an unsigned integer with arbitrary precision and just the post increment operator.
I know there are library for arbitrary precision integer arithmetic but my needs are quite simple and I prefer to avoid the weight of a full library.
It seems to me that my current implementation is still not simple enough and not elegant enough. What do you suggest?
#include <vector>
#include <string>
#include <algorithm>
class UNat
{
public:
static const char base = 10;
UNat( const char* n )
{
std::string s(n);
std::reverse(s.begin(),s.end());
for ( size_t i = 0; i < s.length(); i++ ) {
n_.push_back(s[i]-'0');
}
}
UNat& operator++(int)
{
bool carry = false;
bool finished = false;
for ( size_t i = 0; i < n_.size() && !finished; i++ ) {
n_[i] = add(n_[i],1,carry);
if ( carry ) {
finished = false;
} else {
finished = true;
}
}
if ( carry ) {
n_.push_back(1);
}
return *this;
}
std::string to_string() const
{
std::string r(n_.begin(), n_.end());
std::reverse(r.begin(),r.end());
std::for_each(r.begin(), r.end(), [](char& d) { d+='0';});
return r;
}
private:
char add( const char& a, const char& b, bool& carry )
{
char cc = a + b;
if ( cc >= base ) {
carry = true;
cc -= base;
} else {
carry = false;
}
return cc;
}
std::vector< char > n_;
};
std::ostream& operator<<(std::ostream& oss, const UNat& n)
{
oss << n.to_string();
return oss;
}
#include <iostream>
int main()
{
UNat n("0");
std::cout << n++ << "\n";
std::cout << UNat("9")++ << "\n";
std::cout << UNat("99")++ << "\n";
std::cout << UNat("19")++ << "\n";
std::cout << UNat("29")++ << "\n";
std::cout << UNat("39")++ << "\n";
return 0;
}
In order to avoid returning the mutated value, save a local copy and return it:
UNat operator++(int)
{
UNat copy = *this;
// ....
return copy;
} // don't return by reference
Comparatively, the prefix operator does return by reference.
UNat& operator++ ()
{
// ....
return *this;
}
Some tips and tricks from Arbitrary-precision arithmetic Explanation:
1/ When adding or multiplying numbers, pre-allocate the maximum space
needed then reduce later if you find it's too much. For example,
adding two 100-"digit" (where digit is an int) numbers will never give
you more than 101 digits. Multiply a 12-digit number by a 3 digit
number will never generate more than 15 digits (add the digit counts).
An alternative implementation for your addition function can look like this:
c->lastdigit = std::max(a->lastdigit, b->lastdigit)+1;
carry = 0;
for (i=0; i<=(c->lastdigit); i++) {
c->digits[i] = (char) (carry+a->digits[i]+b->digits[i]) % 10;
carry = (carry + a->digits[i] + b->digits[i]) / 10;
}
The code implements what's generally known as Binary Coded Decimal (BCD). Very simple, and, as you say, the implementation can be much simpler if you only want to increment and don't need general addition. To simplify even further, use the internal character representations for the digits '0' through '9' instead of the values 0 through 9. And for another simplification, store the characters in a std::string:
for (int index = 0; index < digits.size(); ++index) {
if (digits[index] != '9') {
++digits[index];
break;
}
digits[index] = '0';
}
if (index == digits.size())
digits.push_back('1');
This makes the stream inserter nearly trivial.