Skip to content

Commit d1e2c6a

Browse files
committed
[MERGE #5792 @duongnhn] BigInt: implement add, subtract
Merge pull request #5792 from duongnhn:user/duongn/bigint_add_sub - implement add, subtract - throw type error if type mismatch - assign with add, subtract
2 parents 96c3c33 + a84a29e commit d1e2c6a

File tree

12 files changed

+528
-21
lines changed

12 files changed

+528
-21
lines changed

lib/Runtime/Language/JavascriptConversion.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,7 @@ using namespace Js;
326326
case TypeIds_Number:
327327
case TypeIds_String:
328328
case TypeIds_Symbol:
329+
case TypeIds_BigInt:
329330
return aValue;
330331

331332
case TypeIds_VariantDate:

lib/Runtime/Library/JavascriptBigInt.cpp

Lines changed: 192 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,18 @@ namespace Js
1111
return RecyclerNew(scriptContext->GetRecycler(), JavascriptBigInt, content, cchUseLength, isNegative, scriptContext->GetLibrary()->GetBigIntTypeStatic());
1212
}
1313

14+
JavascriptBigInt * JavascriptBigInt::CreateZero(ScriptContext * scriptContext)
15+
{
16+
JavascriptBigInt * bigintNew = RecyclerNew(scriptContext->GetRecycler(), JavascriptBigInt, scriptContext->GetLibrary()->GetBigIntTypeStatic());
17+
bigintNew->m_length = 1;
18+
bigintNew->m_isNegative = false;
19+
bigintNew->m_maxLength = 1;
20+
bigintNew->m_digits = RecyclerNewArrayLeaf(scriptContext->GetRecycler(), digit_t, bigintNew->m_length);
21+
bigintNew->m_digits[0] = 0;
22+
23+
return bigintNew;
24+
}
25+
1426
JavascriptBigInt * JavascriptBigInt::New(JavascriptBigInt * pbi, ScriptContext * scriptContext)
1527
{
1628
JavascriptBigInt * bigintNew = RecyclerNew(scriptContext->GetRecycler(), JavascriptBigInt, scriptContext->GetLibrary()->GetBigIntTypeStatic());
@@ -88,25 +100,25 @@ namespace Js
88100
return true;
89101
}
90102

91-
bool JavascriptBigInt::Resize(digit_t length)
103+
void JavascriptBigInt::Resize(digit_t length)
92104
{
93105
digit_t *digits;
94106

95107
if (length <= m_maxLength)
96108
{
97-
return true;
109+
return;
98110
}
99111

100112
length += length;// double size
101113
if (SIZE_MAX / sizeof(digit_t) < length) // overflow
102114
{
103-
return false;
115+
JavascriptError::ThrowRangeError(this->GetScriptContext(), VBSERR_TypeMismatch, _u("Resize BigInt"));
104116
}
105117

106118
digits = RecyclerNewArrayLeaf(this->GetScriptContext()->GetRecycler(), digit_t, length);
107119
if (NULL == digits)
108120
{
109-
return false;
121+
JavascriptError::ThrowRangeError(this->GetScriptContext(), VBSERR_TypeMismatch, _u("Resize BigInt"));
110122
}
111123

112124
if (0 < m_length) // in this case, we need to copy old data over
@@ -116,8 +128,6 @@ namespace Js
116128

117129
m_digits = digits;
118130
m_maxLength = length;
119-
120-
return true;
121131
}
122132

123133
template <typename EncodedChar>
@@ -200,9 +210,9 @@ namespace Js
200210
}
201211
if (carry > 0) //increase length
202212
{
203-
if (result->m_length >= result->m_maxLength && !result->Resize(result->m_length + 1))
213+
if (result->m_length >= result->m_maxLength)
204214
{
205-
AssertOrFailFastMsg(false, "AbsoluteIncrement overflow");
215+
result->Resize(result->m_length + 1);
206216
}
207217
result->m_digits[result->m_length++] = carry;
208218
}
@@ -328,9 +338,9 @@ namespace Js
328338
}
329339
if (0 < digitAdd) // length increase by 1
330340
{
331-
if (m_length >= m_maxLength && !Resize(m_length + 1))
341+
if (m_length >= m_maxLength)
332342
{
333-
return false;
343+
Resize(m_length + 1);
334344
}
335345
m_digits[m_length++] = digitAdd;
336346
}
@@ -351,16 +361,21 @@ namespace Js
351361
}
352362
}
353363

354-
digit_t index;
355364
int sign = m_isNegative ? -1 : 1;
365+
return sign * JavascriptBigInt::CompareAbsolute(pbi);
366+
}
367+
368+
int JavascriptBigInt::CompareAbsolute(JavascriptBigInt *pbi)
369+
{
370+
digit_t index;
356371

357372
if (m_length > pbi->m_length)
358373
{
359-
return 1 * sign;
374+
return 1;
360375
}
361376
if (m_length < pbi->m_length)
362377
{
363-
return -1 * sign;
378+
return -1;
364379
}
365380
if (0 == m_length)
366381
{
@@ -375,7 +390,7 @@ namespace Js
375390
}
376391
Assert(m_digits[index] != pbi->m_digits[index]);
377392

378-
return sign*((m_digits[index] > pbi->m_digits[index]) ? 1 : -1);
393+
return (m_digits[index] > pbi->m_digits[index]) ? 1 : -1;
379394
}
380395

381396
bool JavascriptBigInt::LessThan(Var aLeft, Var aRight)
@@ -397,4 +412,167 @@ namespace Js
397412
return (leftBigInt->Compare(rightBigInt) == 0);
398413
}
399414

415+
// pbi1 += pbi2 assume pbi1 has length no less than pbi2
416+
void JavascriptBigInt::AddAbsolute(JavascriptBigInt * pbi1, JavascriptBigInt * pbi2)
417+
{
418+
Assert(pbi1->m_length >= pbi2->m_length);
419+
digit_t carryDigit = 0;
420+
digit_t *pDigit1 = pbi1->m_digits;
421+
digit_t *pDigit2 = pbi2->m_digits;
422+
digit_t i = 0;
423+
424+
for (; i < pbi2->m_length; i++)
425+
{
426+
digit_t tempCarryDigit = 0;
427+
pDigit1[i] = JavascriptBigInt::AddDigit(pDigit1[i], pDigit2[i], &tempCarryDigit);
428+
pDigit1[i] = JavascriptBigInt::AddDigit(pDigit1[i], carryDigit, &tempCarryDigit);
429+
carryDigit = tempCarryDigit;
430+
}
431+
432+
for (; i < pbi1->m_length && carryDigit > 0; i++)
433+
{
434+
digit_t tempCarryDigit = 0;
435+
pDigit1[i] = JavascriptBigInt::AddDigit(pDigit1[i], carryDigit, &tempCarryDigit);
436+
carryDigit = tempCarryDigit;
437+
}
438+
439+
if (0 < carryDigit) // length increase by 1
440+
{
441+
if (pbi1->m_length >= pbi1->m_maxLength)
442+
{
443+
pbi1->Resize(pbi1->m_length + 1);
444+
}
445+
pbi1->m_digits[pbi1->m_length++] = carryDigit;
446+
}
447+
}
448+
449+
// pbi1 -= pbi2 assume |pbi1| > |pbi2|
450+
void JavascriptBigInt::SubAbsolute(JavascriptBigInt * pbi1, JavascriptBigInt * pbi2)
451+
{
452+
Assert(pbi1->CompareAbsolute(pbi2) == 1);
453+
digit_t carryDigit = 0;
454+
digit_t *pDigit1 = pbi1->m_digits;
455+
digit_t *pDigit2 = pbi2->m_digits;
456+
digit_t i = 0;
457+
458+
for (; i < pbi2->m_length; i++)
459+
{
460+
digit_t tempCarryDigit = 0;
461+
pDigit1[i] = JavascriptBigInt::SubDigit(pDigit1[i], pDigit2[i], &tempCarryDigit);
462+
pDigit1[i] = JavascriptBigInt::SubDigit(pDigit1[i], carryDigit, &tempCarryDigit);
463+
carryDigit = tempCarryDigit;
464+
}
465+
466+
for (; i < pbi1->m_length && carryDigit > 0; i++)
467+
{
468+
digit_t tempCarryDigit = 0;
469+
pDigit1[i] = JavascriptBigInt::SubDigit(pDigit1[i], carryDigit, &tempCarryDigit);
470+
carryDigit = tempCarryDigit;
471+
}
472+
// adjust length
473+
while ((pbi1->m_length>0) && (pbi1->m_digits[pbi1->m_length-1] == 0))
474+
{
475+
pbi1->m_length--;
476+
}
477+
Assert(pbi1->m_length > 0);
478+
}
479+
480+
JavascriptBigInt * JavascriptBigInt::Sub(JavascriptBigInt * pbi1, JavascriptBigInt * pbi2)
481+
{
482+
if (JavascriptBigInt::IsZero(pbi1))
483+
{
484+
pbi2->m_isNegative = !pbi2->m_isNegative;
485+
return pbi2;
486+
}
487+
if (JavascriptBigInt::IsZero(pbi2))
488+
{
489+
return pbi1;
490+
}
491+
492+
if (pbi2->m_isNegative)
493+
{
494+
pbi2->m_isNegative = false;
495+
return JavascriptBigInt::Add(pbi1, pbi2);// a-(-b)=a+b
496+
}
497+
498+
if (pbi1->m_isNegative) // -a-b=-(a+b)
499+
{
500+
if (pbi1->m_length >= pbi2->m_length)
501+
{
502+
JavascriptBigInt::AddAbsolute(pbi1, pbi2);
503+
return pbi1;
504+
}
505+
JavascriptBigInt::AddAbsolute(pbi2, pbi1);
506+
return pbi2;
507+
}
508+
else // both positive
509+
{
510+
switch (pbi1->CompareAbsolute(pbi2))
511+
{
512+
case 0: // a -a = 0
513+
return JavascriptBigInt::CreateZero(pbi1->GetScriptContext());
514+
case 1:
515+
JavascriptBigInt::SubAbsolute(pbi1, pbi2); // a - b > 0
516+
return pbi1;
517+
default:
518+
pbi2->m_isNegative = true;
519+
JavascriptBigInt::SubAbsolute(pbi2, pbi1); // a - b = - (b-a) < 0
520+
return pbi2;
521+
}
522+
}
523+
}
524+
525+
JavascriptBigInt * JavascriptBigInt::Add(JavascriptBigInt * pbi1, JavascriptBigInt * pbi2)
526+
{
527+
if (JavascriptBigInt::IsZero(pbi1))
528+
{
529+
return pbi2;
530+
}
531+
if (JavascriptBigInt::IsZero(pbi2))
532+
{
533+
return pbi1;
534+
}
535+
536+
if (pbi1->m_isNegative == pbi2->m_isNegative) // (-a)+(-b) = -(a+b)
537+
{
538+
if (pbi1->m_length >= pbi2->m_length)
539+
{
540+
JavascriptBigInt::AddAbsolute(pbi1, pbi2);
541+
return pbi1;
542+
}
543+
JavascriptBigInt::AddAbsolute(pbi2, pbi1);
544+
return pbi2;
545+
}
546+
else
547+
{
548+
switch (pbi1->CompareAbsolute(pbi2))
549+
{
550+
case 0:
551+
return JavascriptBigInt::CreateZero(pbi1->GetScriptContext()); // a + (-a) = -a + a = 0
552+
case 1:
553+
JavascriptBigInt::SubAbsolute(pbi1, pbi2); // a + (-b) = a - b or (-a) + b = -(a-b)
554+
return pbi1;
555+
default:
556+
JavascriptBigInt::SubAbsolute(pbi2, pbi1); // -a + b = b - a or a + (-b) = -(b-a)
557+
return pbi2;
558+
}
559+
}
560+
}
561+
562+
Var JavascriptBigInt::Add(Var aLeft, Var aRight)
563+
{
564+
JavascriptBigInt *leftBigInt = VarTo<JavascriptBigInt>(aLeft);
565+
JavascriptBigInt *rightBigInt = VarTo<JavascriptBigInt>(aRight);
566+
return JavascriptBigInt::Add(JavascriptBigInt::New(leftBigInt, leftBigInt->GetScriptContext()), JavascriptBigInt::New(rightBigInt, rightBigInt->GetScriptContext()));
567+
// TODO: Consider deferring creation of new instances until we need them
568+
}
569+
570+
Var JavascriptBigInt::Sub(Var aLeft, Var aRight)
571+
{
572+
JavascriptBigInt *leftBigInt = VarTo<JavascriptBigInt>(aLeft);
573+
JavascriptBigInt *rightBigInt = VarTo<JavascriptBigInt>(aRight);
574+
return JavascriptBigInt::Sub(JavascriptBigInt::New(leftBigInt, leftBigInt->GetScriptContext()), JavascriptBigInt::New(rightBigInt, rightBigInt->GetScriptContext()));
575+
// TODO: Consider deferring creation of new instances until we need them
576+
}
577+
400578
} // namespace Js

lib/Runtime/Library/JavascriptBigInt.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,13 @@ namespace Js
3737
static bool LessThan(Var aLeft, Var aRight);
3838
static bool Equals(Var aLeft, Var aRight);
3939
static Var Increment(Var aRight);
40+
static Var Add(Var aLeft, Var aRight);
41+
static Var Sub(Var aLeft, Var aRight);
4042
static Var Decrement(Var aRight);
4143

4244
inline BOOL isNegative() { return m_isNegative; }
4345

46+
static JavascriptBigInt * CreateZero(ScriptContext * scriptContext);
4447
static JavascriptBigInt * Create(const char16 * content, charcount_t cchUseLength, bool isNegative, ScriptContext * scriptContext);
4548
virtual RecyclableObject * CloneToScriptContext(ScriptContext* requestContext) override;
4649

@@ -70,9 +73,14 @@ namespace Js
7073
static void AbsoluteDecrement(JavascriptBigInt * pbi);
7174
static void Increment(JavascriptBigInt * aValue);
7275
static void Decrement(JavascriptBigInt * pbi);
76+
static JavascriptBigInt * Add(JavascriptBigInt * pbi1, JavascriptBigInt * pbi2);
77+
static void AddAbsolute(JavascriptBigInt * pbi1, JavascriptBigInt * pbi2);
78+
static JavascriptBigInt * Sub(JavascriptBigInt * pbi1, JavascriptBigInt * pbi2);
79+
static void SubAbsolute(JavascriptBigInt * pbi1, JavascriptBigInt * pbi2);
7380
int Compare(JavascriptBigInt * pbi);
81+
int CompareAbsolute(JavascriptBigInt * pbi);
7482
static BOOL Equals(JavascriptBigInt* left, Var right, BOOL* value, ScriptContext * requestContext);
75-
bool Resize(digit_t length);
83+
void Resize(digit_t length);
7684

7785
static JavascriptBigInt * New(JavascriptBigInt * pbi, ScriptContext * scriptContext);
7886
};

0 commit comments

Comments
 (0)