forked from microsoft/calculator
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathCalcInput.cpp
317 lines (273 loc) · 8.75 KB
/
CalcInput.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
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include "pch.h"
#include "Header Files/CalcEngine.h"
using namespace std;
using namespace CalcEngine;
constexpr int C_NUM_MAX_DIGITS = MAX_STRLEN;
constexpr int C_EXP_MAX_DIGITS = 4;
void CalcNumSec::Clear()
{
value.clear();
m_isNegative = false;
}
void CalcInput::Clear()
{
m_base.Clear();
m_exponent.Clear();
m_hasExponent = false;
m_hasDecimal = false;
m_decPtIndex = 0;
}
bool CalcInput::TryToggleSign(bool isIntegerMode, wstring_view maxNumStr)
{
// Zero is always positive
if (m_base.IsEmpty())
{
m_base.IsNegative(false);
m_exponent.IsNegative(false);
}
else if (m_hasExponent)
{
m_exponent.IsNegative(!m_exponent.IsNegative());
}
else
{
// When in integer only mode, it isn't always allowed to toggle, as toggling can cause the num to be out of
// bounds. For eg. in byte -128 is valid, but when it toggled it becomes 128, which is more than 127.
if (isIntegerMode && m_base.IsNegative())
{
// Decide if this additional digit will fit for the given bit width
if (m_base.value.size() >= maxNumStr.size() && m_base.value.back() > maxNumStr.back())
{
// Last digit is more than the allowed positive number. Fail
return false;
}
}
m_base.IsNegative(!m_base.IsNegative());
}
return true;
}
bool CalcInput::TryAddDigit(unsigned int value, uint32_t radix, bool isIntegerMode, wstring_view maxNumStr, long wordBitWidth, int maxDigits)
{
// Convert from an integer into a character
// This includes both normal digits and alpha 'digits' for radixes > 10
auto chDigit = static_cast<wchar_t>((value < 10) ? (L'0' + value) : (L'A' + value - 10));
CalcNumSec* pNumSec;
size_t maxCount;
if (m_hasExponent)
{
pNumSec = &m_exponent;
maxCount = C_EXP_MAX_DIGITS;
}
else
{
pNumSec = &m_base;
maxCount = maxDigits;
// Don't include the decimal point in the count. In that way you can enter the maximum allowed precision.
// Precision doesn't include decimal point.
if (HasDecimalPt())
{
maxCount++;
}
// First leading 0 is not counted in input restriction as the output can be of that form
// See NumberToString algorithm. REVIEW: We don't have such input restriction mimicking based on output of NumberToString for exponent
// NumberToString can give 10 digit exponent, but we still restrict the exponent here to be only 4 digits.
if (!pNumSec->IsEmpty() && pNumSec->value.front() == L'0')
{
maxCount++;
}
}
// Ignore leading zeros
if (pNumSec->IsEmpty() && (value == 0))
{
return true;
}
if (pNumSec->value.size() < maxCount)
{
pNumSec->value += chDigit;
return true;
}
// if we are in integer mode, within the base, and we're on the last digit then
// there are special cases where we can actually add one more digit.
if (isIntegerMode && pNumSec->value.size() == maxCount && !m_hasExponent)
{
bool allowExtraDigit = false;
if (radix == 8)
{
switch (wordBitWidth % 3)
{
case 1:
// in 16 or 64bit word size, if the first digit is a 1 we can enter 6 (16bit) or 22 (64bit) digits
allowExtraDigit = (pNumSec->value.front() == L'1');
break;
case 2:
// in 8 or 32bit word size, if the first digit is a 3 or less we can enter 3 (8bit) or 11 (32bit) digits
allowExtraDigit = (pNumSec->value.front() <= L'3');
break;
}
}
else if (radix == 10)
{
// If value length is at least the max, we know we can't add another digit.
if(pNumSec->value.size() < maxNumStr.size())
{
// Compare value to substring of maxNumStr of value.size() length.
// If cmpResult > 0:
// eg. max is "127", and the current number is "20". first digit itself says we are out.
// Additional digit is not possible
// If cmpResult < 0:
// Success case. eg. max is "127", and current number is say "11". The second digit '1' being <
// corresponding digit '2', means all digits are possible to append, like 119 will still be < 127
// If cmpResult == 0:
// Undecided still. The case when max is "127", and current number is "12". Look for the new number being 7 or less to allow
auto cmpResult = pNumSec->value.compare(0, wstring::npos, maxNumStr, 0, pNumSec->value.size());
if (cmpResult < 0)
{
allowExtraDigit = true;
}
else if (cmpResult == 0)
{
auto lastChar = maxNumStr[pNumSec->value.size()];
if (chDigit <= lastChar)
{
allowExtraDigit = true;
}
else if (pNumSec->IsNegative() && chDigit <= lastChar + 1)
{
// Negative value case, eg. max is "127", and current number is "-12". Then 8 is also valid, as the range
// is always from -(max+1)...max in signed mode
allowExtraDigit = true;
}
}
}
}
if (allowExtraDigit)
{
pNumSec->value += chDigit;
return true;
}
}
return false;
}
bool CalcInput::TryAddDecimalPt()
{
// Already have a decimal pt or we're in the exponent
if (m_hasDecimal || m_hasExponent)
{
return false;
}
if (m_base.IsEmpty())
{
m_base.value += L"0"; // Add a leading zero
}
m_decPtIndex = m_base.value.size();
m_base.value += m_decSymbol;
m_hasDecimal = true;
return true;
}
bool CalcInput::HasDecimalPt()
{
return m_hasDecimal;
}
bool CalcInput::TryBeginExponent()
{
// For compatibility, add a trailing dec point to base num if it doesn't have one
TryAddDecimalPt();
if (m_hasExponent) // Already entering exponent
{
return false;
}
m_hasExponent = true; // Entering exponent
return true;
}
void CalcInput::Backspace()
{
if (m_hasExponent)
{
if (!m_exponent.IsEmpty())
{
m_exponent.value.pop_back();
if (m_exponent.IsEmpty())
{
m_exponent.Clear();
}
}
else
{
m_hasExponent = false;
}
}
else
{
if (!m_base.IsEmpty())
{
m_base.value.pop_back();
}
if (m_base.value.size() <= m_decPtIndex)
{
// Backed up over decimal point
m_hasDecimal = false;
m_decPtIndex = 0;
}
if (m_base.IsEmpty())
{
m_base.Clear();
}
}
}
void CalcInput::SetDecimalSymbol(wchar_t decSymbol)
{
if (m_decSymbol != decSymbol)
{
m_decSymbol = decSymbol;
if (m_hasDecimal)
{
// Change to new decimal pt
m_base.value[m_decPtIndex] = m_decSymbol;
}
}
}
wstring CalcInput::ToString(uint32_t radix, bool isIntegerMode)
{
// In theory both the base and exponent could be C_NUM_MAX_DIGITS long.
wstringstream resStream;
if ((m_base.value.size() > MAX_STRLEN) || (m_hasExponent && m_exponent.value.size() > MAX_STRLEN))
{
return wstring();
}
if (m_base.IsNegative())
{
resStream << L'-';
}
resStream << (m_base.IsEmpty() ? L"0" : m_base.value);
if (m_hasExponent)
{
// Add a decimal point if it is not already there
if (!m_hasDecimal)
{
resStream << m_decSymbol;
}
resStream << ((radix == 10) ? L'e' : L'^');
resStream << (m_exponent.IsNegative() ? L'-' : L'+');
resStream << (m_exponent.IsEmpty() ? L"0" : m_exponent.value);
}
auto result = resStream.str();
// Base and Exp can each be up to C_NUM_MAX_DIGITS in length, plus 4 characters for sign, dec, exp, and expSign.
if (result.size() > C_NUM_MAX_DIGITS * 2 + 4)
{
return wstring();
}
return result;
}
Rational CalcInput::ToRational(uint32_t radix, int32_t precision)
{
PRAT rat = StringToRat(m_base.IsNegative(), m_base.value, m_exponent.IsNegative(), m_exponent.value, radix, precision);
if (rat == nullptr)
{
return 0;
}
Rational result{ rat };
destroyrat(rat);
return result;
}