-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathstring_calculator.cpp
455 lines (425 loc) · 13.2 KB
/
string_calculator.cpp
1
2
3
4
5
6
7
8
9
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
#include <iostream>
#include <string>
#include "./string_calculator.h"
using std::cout, std::endl, std::wstring;
unsigned int digit_to_decimal(wchar_t digit) {
// wchar_t to int
if (!isdigit(digit))
{
throw std::invalid_argument("Character entered is not a digit.");
}
return digit - L'0';
}
wchar_t decimal_to_digit(unsigned int decimal) {
if (decimal > 9)
{
throw std::invalid_argument("Decimal entered is greater than 9 or less than 0.");
}
return decimal + L'0';
}
wstring trim_leading_zeros(wstring num) {
bool wasNeg = false;
wstring result = L"";
if (num.find(L"-") != wstring::npos) // Number represented is negative
{
wasNeg = true;
num.erase(0,1); // initially delete the negative sign
}
size_t i = 0;
while (num.at(i) == L'0')
{
i++;
if (i == num.length()) // if the number entered consists soley of 0's
{
return L"0";
}
}
while (i != num.length())
{
result += num.at(i);
i++;
}
if (wasNeg)
{
result.insert(0,L"-");
}
return result;
}
wstring add(wstring lhs, wstring rhs) {
// Important variables
int carryOver = 0;
int leftSide = 0;
int rightSide = 0;
int sumOfLeftAndRight = 0;
wstring result = L"";
// Condition variables
bool carryOverExists = true;
bool negFound = false;
// Iteration variables
int leftIterate = (int)lhs.length() - 1;
int rightIterate = (int)rhs.length() - 1;
// Dealing with negatives
if( (lhs.find(L'-') != wstring::npos && rhs.find(L'-') != wstring::npos) || // both are negative
(rhs.find(L'-') != wstring::npos && lhs.find(L'0') == 0) || // right is negative, left is 0
(lhs.find(L'-') != wstring::npos && rhs.find(L'0') == 0) // right is 0, left is negative
)
{
lhs.erase(0,1); // deletes negative sign so digit to decimal will have no issues
rhs.erase(0,1);
negFound = true; // marked as a negative number so it will be added back at the end
leftIterate -= 1; // accounts for deleted part, length has to be subtract 1 or be out of range
rightIterate -= 1;
}
else if (lhs.find(L'-') != wstring::npos && rhs.find(L'-') == wstring::npos) // if left is neg and right is not
{
lhs.erase(0,1);
result = subtract(lhs,rhs);
if ((lhs.length() == rhs.length() && rhs > lhs) || lhs.length() < rhs.length())
result.erase(0,1);
else
result.insert(0, L"-");
return trim_leading_zeros(result);
}
else if (lhs.find(L'-') == wstring::npos && rhs.find(L'-') != wstring::npos) // left is positive and right is negative, simply subtract
{
rhs.erase(0,1);
result = subtract(lhs,rhs);
if ((lhs.length() == rhs.length() && rhs < lhs) || lhs.length() > rhs.length()) // conditions for the result to be positive
{
if (result.find(L'-') != wstring::npos)
result.erase(0,1);
}
return result;
}
while (carryOverExists) {
if (leftIterate >= 0) // if the left iteration is not negative (not out of range), then we grab the furthest right number available
{
leftSide = digit_to_decimal(lhs.at(leftIterate));
}
if (rightIterate >= 0)
{
rightSide = digit_to_decimal(rhs.at(rightIterate));
}
if (leftIterate < 0) // if left iteration goes below 0, left side will always be 0 (no numbers there)
{
leftSide = 0;
}
if (rightIterate < 0)
{
rightSide = 0;
}
sumOfLeftAndRight = leftSide + rightSide + carryOver; // carry over is 1, added on, if not then it is 0
carryOver = 0; // resets carry over for next iteration
if (sumOfLeftAndRight >= 10) // if the sum is greater than 10, then there is a carry over
{
sumOfLeftAndRight = sumOfLeftAndRight % 10;
result += decimal_to_digit(sumOfLeftAndRight);
carryOver = 1;
}
else if (leftIterate < 0 && rightIterate < 0) // if both iterations are less than 0, there's nothing left in the strings
{
carryOverExists = false; // ends the loop
result += decimal_to_digit(sumOfLeftAndRight);
}
else // only one is less than 0 or both still have some to go
{
result += decimal_to_digit(sumOfLeftAndRight);
}
leftIterate -= 1; // iterates through left wstring
rightIterate -= 1;
}
wstring finalResult = L"";
int i = (int)result.length() - 1; // result wstring is currently backwards due to push_back, this will mirror it
while (i >= 0)
{
finalResult += result.at(i);
i -= 1;
}
finalResult = trim_leading_zeros(finalResult);
if (negFound) // if a negative existed before, we simply slap it back in
{
finalResult.insert(0, L"-");
}
return finalResult;
}
wstring subtract(wstring lhs, wstring rhs) {
// Important variables
int negativeCarryOver = 0;
int leftSide = 0;
int rightSide = 0;
int leftMinusRight = 0;
wstring temp = L"";
wstring result = L"";
wstring addResult = L"";
// Condition variables
bool carryOverExists = true;
bool negFound = false;
// Iteration variables
int leftIterate = (int)lhs.length() - 1;
int rightIterate = (int)rhs.length() - 1;
// Dealing with negatives
if (lhs.find(L'-') != wstring::npos && rhs.find(L'-') != wstring::npos) { // if both are negative
lhs.erase(0,1); // delete the negative signs
rhs.erase(0,1);
leftIterate -= 1; // iterate to account for deletion
rightIterate -= 1;
if ((lhs.length() == rhs.length() && rhs >= lhs) || rhs.length() > lhs.length())
{ // if the right hand side is >= left hand side, then the result will be positive. Ex: -5 - (-10), note -5 > -1 when comparing strings
lhs.swap(rhs);
std::swap(leftIterate,rightIterate);
}
else // final result is negative
negFound = true;
}
else if (lhs.find(L'-') == wstring::npos && rhs.find(L'-') == wstring::npos) // both are positive
{
if ((lhs.length() == rhs.length() && rhs > lhs) || rhs.length() > lhs.length()) // if the right hand side is > left hand side, the result will be negative
{ // swap them and slap on the negative at the end. Otherwise, we don't have to do anything
negFound = true;
lhs.swap(rhs);
std::swap(leftIterate,rightIterate);
}
}
else if (lhs.find(L'-') != wstring::npos && rhs.find(L'-') == wstring::npos) // left is negative and right is not
{
lhs.erase(0,1);
result = add(lhs,rhs);
result.insert(0, L"-");
return result;
}
else if (lhs.find(L'-') == wstring::npos && rhs.find(L'-') != wstring::npos) // right is negative, left is not
{
rhs.erase(0,1);
result = add(lhs,rhs);
return result;
}
while (carryOverExists) {
if (leftIterate >= 0) // if the left iteration is not negative (not out of range)
{
leftSide = digit_to_decimal(lhs.at(leftIterate));
}
if (rightIterate >= 0)
{
rightSide = digit_to_decimal(rhs.at(rightIterate));
}
if (leftIterate < 0) // if left iteration goes below 0, left side will always be 0 (no numbers there)
{
leftSide = 0;
}
if (rightIterate < 0)
{
rightSide = 0;
}
leftMinusRight = leftSide - rightSide - negativeCarryOver;
if (leftMinusRight < 0)
{
leftMinusRight = (leftSide + 10) - rightSide - negativeCarryOver;
result += decimal_to_digit(leftMinusRight);
negativeCarryOver = 1;
}
else if (leftIterate < 0 && rightIterate < 0) // if both iterations are less than 0, there's nothing left in the strings
{
carryOverExists = false; // ends the loop
result += decimal_to_digit(leftMinusRight);
}
else
{
negativeCarryOver = 0;
result += decimal_to_digit(leftMinusRight);
}
leftIterate -= 1; // iterates through left wstring
rightIterate -= 1;
}
wstring finalResult = L"";
int i = (int)result.length() - 1;
while (i >= 0) // reverses the appending
{
finalResult += result.at(i);
i -= 1;
}
finalResult = trim_leading_zeros(finalResult);
if (negFound) // if a negative existed before, we simply slap it back in
{
finalResult.insert(0, L"-");
}
return finalResult;
}
wstring multiply(wstring lhs, wstring rhs) {
int topSide = 0;
int bottomSide = 0;
int product = 0;
int carryOver = 0;
wstring result = L"";
bool negFound = false;
if (rhs.length() < lhs.length()) // swap them so the bigger number is on top
{
swap(lhs,rhs);
}
if (lhs.find(L'-') != wstring::npos && rhs.find(L'-') != wstring::npos) // if both are negative, it's ok because result is pos
{
rhs.erase(0,1); // deletes the negative
lhs.erase(0,1);
}
else if (rhs.find(L'-') != wstring::npos && lhs.find(L'-') == wstring::npos) // right is negative left positive
{
rhs.erase(0,1);
negFound = true; // negFound is so we can slap a negative on at the end
}
else if (lhs.find(L'-') != wstring::npos && rhs.find(L'-') == wstring::npos) // left is negative, right positive
{
lhs.erase(0,1);
negFound = true;
}
wstring sum = L"0";
int counter = 1;
for (int i = (int)lhs.length() - 1; i >= 0; i--) // nested for loop, one for the bigger number (top number) and one for the smaller number (bottom)
{
for (int j = (int)rhs.length() - 1; j >= 0; j--)
{
topSide = digit_to_decimal(rhs.at(j));
bottomSide = digit_to_decimal(lhs.at(i));
product = topSide * bottomSide + carryOver;
carryOver = 0; // resets carryOver so we don't get same carry over each iteration
if (product >= 10) // carry over exists
{
if (j == 0)
{
// if we're at the end of the big number, then there's nothing to add carry over to, so we append the entire thing
int loopProduct = product;
while (loopProduct != 0)
{
result += decimal_to_digit(loopProduct % 10); // append last digit then the next digit
loopProduct /= 10;
}
}
else
{
carryOver = product / 10; // only append last digit and add the next digit as carry over
result += decimal_to_digit(product % 10);
}
}
else
{
result += decimal_to_digit(product); // no carry over, simply append it
}
}
int j = (int)result.length() - 1; // reverses the result since it's currently backwards
wstring newResult = L"";
while (j >= 0)
{
newResult += result.at(j);
j--;
}
sum = add(sum,newResult); // add the sum to the normal result (not the reversed one) each time
result = L"";
for (int k = 0; k < counter; k++) // number of zeroes to add is equal to the outer loop (add one zero after first iteration (first row))
result += L"0";
counter++; // increments the counter so we know the next iteration will need 2 zeroes (second row)
}
if (negFound)
{
sum.insert(0, L"-");
}
return sum;
}
wstring numTimes(wstring lhs, wstring result) {
if (lhs == result)
return L"1";
if (result == L"0" || compare(lhs,result)) // if lhs > result, ie: 5 and 2, then return 0
return L"0";
wstring i = L"1";
wstring product = multiply(lhs,i);
while (!compare(product,result)) // while (product is less than the result)
{
i = add(i, L"1");
product = multiply(lhs,i);
}
if (product == result) // bandaid solution (i is 1 too much i product > result at the end)
return i;
return subtract(i, L"1");
}
bool compare(wstring lhs, wstring rhs) { // true if lhs > rhs
if (lhs.length() > rhs.length()) // if they're different lengths
return true;
if (rhs.length() > lhs.length())
return false;
wstring a = L"";
wstring b = L"";
for (size_t i = 0; i < lhs.length(); i++) // compares digit by digit
{
a = lhs.at(i);
b = rhs.at(i);
if (stoi(a) < stoi(b))
return false;
if (stoi(a) > stoi(b))
return true;
}
return true;
}
wstring division(wstring dividend, wstring divisor) {
if (divisor == L"0")
throw std::invalid_argument("Error: Division by 0.");
if (dividend == L"0")
return L"0";
wstring quotient = L""; // result
wstring factorTimes = L"0";
wstring dividendParse = L"";
bool negFound = false;
if (dividend.find(L"-") != wstring::npos && divisor.find(L"-") != wstring::npos) // both are negative, result will be positive
{
dividend.erase(0,1);
divisor.erase(0,1);
}
else if (dividend.find(L"-") != wstring::npos && divisor.find(L"-") == wstring::npos) // dividend is negative, result will be negative
{
dividend.erase(0,1);
negFound = true;
}
else if (dividend.find(L"-") == wstring::npos && divisor.find(L"-") != wstring::npos)
{
divisor.erase(0,1);
negFound = true;
}
size_t origLength = dividend.length();
for (size_t i = 0; i < origLength; i++)
{
dividendParse.push_back(dividend.at(i)); // essentially dragging down the next number
//cout << "dividendParse: " << dividendParse << endl;
factorTimes = numTimes(divisor,dividendParse); // how many times divisor goes into that part of the dividend
//cout << "numTimes: " << factorTimes << endl;
//cout << "divisor * numTimes: " << multiply(divisor,factorTimes) << endl;
if (factorTimes == L"0") // not factorable
{
quotient.append(L"0");
continue;
}
quotient.append(factorTimes);
dividendParse = subtract(dividendParse,multiply(divisor,factorTimes));
//cout << "dividendParse after subtract: " << dividendParse << endl << endl;
if (dividendParse == L"0")
{
dividendParse = L""; // resets dividendParse
}
}
if (negFound)
quotient.insert(0, L"-");
return trim_leading_zeros(quotient);
}
wstring modulus(wstring dividend, wstring divisor) {
return subtract(dividend,multiply(division(dividend,divisor),divisor));
}
wstring power(wstring lhs, wstring rhs) {
if (rhs == L"0")
return L"1";
if (rhs.find(L"-") != wstring::npos)
throw std::invalid_argument("Error: does not support negative powers.");
if (lhs == L"1")
return L"1";
wstring result = lhs;
for (;;) {
result = multiply(result, lhs);
rhs = subtract(rhs, L"1");
if (rhs == L"1") break;
}
return result;
}