Skip to content

Commit 41366c1

Browse files
authored
[integer][tests] Add operation overload between BigInt and Int + refactor some tests (#88)
1. Add operation overload between `BigInt` and `Int`. Allow syntax like `BInt(100) + 1`. 2. Refactor the test scripts for `BigUInt`.
1 parent 8ed3dab commit 41366c1

File tree

9 files changed

+357
-499
lines changed

9 files changed

+357
-499
lines changed

src/decimojo/bigdecimal/bigdecimal.mojo

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,20 @@ struct BigDecimal(
108108
# from_string(value: String) -> Self
109109
# ===------------------------------------------------------------------=== #
110110

111+
@staticmethod
112+
fn from_raw_components(
113+
coefficient: BigUInt, scale: Int = 0, sign: Bool = False
114+
) -> Self:
115+
"""Creates a BigDecimal from its raw components."""
116+
return Self(coefficient, scale, sign)
117+
118+
@staticmethod
119+
fn from_raw_components(
120+
coefficient: UInt32, scale: Int = 0, sign: Bool = False
121+
) -> Self:
122+
"""Creates a BigDecimal from its raw components."""
123+
return Self(BigUInt(List[UInt32](coefficient)), scale, sign)
124+
111125
@staticmethod
112126
fn from_int(value: Int) raises -> Self:
113127
"""Creates a BigDecimal from an integer."""
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# ===----------------------------------------------------------------------=== #
2+
# Copyright 2025 Yuhao Zhu
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
# ===----------------------------------------------------------------------=== #
16+
"""
17+
# Implements functions for calculating common constants.
18+
"""
19+
20+
import math as builtin_math
21+
22+
from decimojo.bigdecimal.bigdecimal import BigDecimal
23+
from decimojo.rounding_mode import RoundingMode

src/decimojo/bigint/bigint.mojo

Lines changed: 115 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ struct BigInt(Absable, IntableRaising, Representable, Stringable, Writable):
199199
return Self(BigUInt(list_of_words^), sign)
200200

201201
@staticmethod
202-
fn from_int(value: Int) raises -> Self:
202+
fn from_int(value: Int) -> Self:
203203
"""Creates a BigInt from an integer."""
204204
if value == 0:
205205
return Self()
@@ -421,22 +421,46 @@ struct BigInt(Absable, IntableRaising, Representable, Stringable, Writable):
421421
fn __add__(self, other: Self) raises -> Self:
422422
return decimojo.bigint.arithmetics.add(self, other)
423423

424+
@always_inline
425+
fn __add__(self, other: Int) raises -> Self:
426+
return decimojo.bigint.arithmetics.add(self, Self.from_int(other))
427+
424428
@always_inline
425429
fn __sub__(self, other: Self) raises -> Self:
426430
return decimojo.bigint.arithmetics.subtract(self, other)
427431

432+
@always_inline
433+
fn __sub__(self, other: Int) raises -> Self:
434+
return decimojo.bigint.arithmetics.subtract(self, Self.from_int(other))
435+
428436
@always_inline
429437
fn __mul__(self, other: Self) raises -> Self:
430438
return decimojo.bigint.arithmetics.multiply(self, other)
431439

440+
@always_inline
441+
fn __mul__(self, other: Int) raises -> Self:
442+
return decimojo.bigint.arithmetics.multiply(self, Self.from_int(other))
443+
432444
@always_inline
433445
fn __floordiv__(self, other: Self) raises -> Self:
434446
return decimojo.bigint.arithmetics.floor_divide(self, other)
435447

448+
@always_inline
449+
fn __floordiv__(self, other: Int) raises -> Self:
450+
return decimojo.bigint.arithmetics.floor_divide(
451+
self, Self.from_int(other)
452+
)
453+
436454
@always_inline
437455
fn __mod__(self, other: Self) raises -> Self:
438456
return decimojo.bigint.arithmetics.floor_modulo(self, other)
439457

458+
@always_inline
459+
fn __mod__(self, other: Int) raises -> Self:
460+
return decimojo.bigint.arithmetics.floor_modulo(
461+
self, Self.from_int(other)
462+
)
463+
440464
@always_inline
441465
fn __pow__(self, exponent: Self) raises -> Self:
442466
return self.power(exponent)
@@ -445,6 +469,40 @@ struct BigInt(Absable, IntableRaising, Representable, Stringable, Writable):
445469
fn __pow__(self, exponent: Int) raises -> Self:
446470
return self.power(exponent)
447471

472+
# ===------------------------------------------------------------------=== #
473+
# Basic binary right-side arithmetic operation dunders
474+
# These methods are called to implement the binary arithmetic operations
475+
# (+, -, *, @, /, //, %, divmod(), pow(), **, <<, >>, &, ^, |)
476+
# ===------------------------------------------------------------------=== #
477+
478+
@always_inline
479+
fn __radd__(self, other: Int) raises -> Self:
480+
return decimojo.bigint.arithmetics.add(self, Self.from_int(other))
481+
482+
@always_inline
483+
fn __rsub__(self, other: Int) raises -> Self:
484+
return decimojo.bigint.arithmetics.subtract(Self.from_int(other), self)
485+
486+
@always_inline
487+
fn __rmul__(self, other: Int) raises -> Self:
488+
return decimojo.bigint.arithmetics.multiply(self, Self.from_int(other))
489+
490+
@always_inline
491+
fn __rfloordiv__(self, other: Int) raises -> Self:
492+
return decimojo.bigint.arithmetics.floor_divide(
493+
Self.from_int(other), self
494+
)
495+
496+
@always_inline
497+
fn __rmod__(self, other: Int) raises -> Self:
498+
return decimojo.bigint.arithmetics.floor_modulo(
499+
Self.from_int(other), self
500+
)
501+
502+
@always_inline
503+
fn __rpow__(self, base: Int) raises -> Self:
504+
return Self(base).power(self)
505+
448506
# ===------------------------------------------------------------------=== #
449507
# Basic binary augmented arithmetic assignments dunders
450508
# These methods are called to implement the binary augmented arithmetic
@@ -456,22 +514,46 @@ struct BigInt(Absable, IntableRaising, Representable, Stringable, Writable):
456514
fn __iadd__(mut self, other: Self) raises:
457515
self = decimojo.bigint.arithmetics.add(self, other)
458516

517+
@always_inline
518+
fn __iadd__(mut self, other: Int) raises:
519+
self = decimojo.bigint.arithmetics.add(self, Self.from_int(other))
520+
459521
@always_inline
460522
fn __isub__(mut self, other: Self) raises:
461523
self = decimojo.bigint.arithmetics.subtract(self, other)
462524

525+
@always_inline
526+
fn __isub__(mut self, other: Int) raises:
527+
self = decimojo.bigint.arithmetics.subtract(self, Self.from_int(other))
528+
463529
@always_inline
464530
fn __imul__(mut self, other: Self) raises:
465531
self = decimojo.bigint.arithmetics.multiply(self, other)
466532

533+
@always_inline
534+
fn __imul__(mut self, other: Int) raises:
535+
self = decimojo.bigint.arithmetics.multiply(self, Self.from_int(other))
536+
467537
@always_inline
468538
fn __ifloordiv__(mut self, other: Self) raises:
469539
self = decimojo.bigint.arithmetics.floor_divide(self, other)
470540

541+
@always_inline
542+
fn __ifloordiv__(mut self, other: Int) raises:
543+
self = decimojo.bigint.arithmetics.floor_divide(
544+
self, Self.from_int(other)
545+
)
546+
471547
@always_inline
472548
fn __imod__(mut self, other: Self) raises:
473549
self = decimojo.bigint.arithmetics.floor_modulo(self, other)
474550

551+
@always_inline
552+
fn __imod__(mut self, other: Int) raises:
553+
self = decimojo.bigint.arithmetics.floor_modulo(
554+
self, Self.from_int(other)
555+
)
556+
475557
# ===------------------------------------------------------------------=== #
476558
# Basic binary comparison operation dunders
477559
# __gt__, __ge__, __lt__, __le__, __eq__, __ne__
@@ -482,31 +564,63 @@ struct BigInt(Absable, IntableRaising, Representable, Stringable, Writable):
482564
"""Returns True if self > other."""
483565
return decimojo.bigint.comparison.greater(self, other)
484566

567+
@always_inline
568+
fn __gt__(self, other: Int) -> Bool:
569+
"""Returns True if self > other."""
570+
return decimojo.bigint.comparison.greater(self, Self.from_int(other))
571+
485572
@always_inline
486573
fn __ge__(self, other: Self) -> Bool:
487574
"""Returns True if self >= other."""
488575
return decimojo.bigint.comparison.greater_equal(self, other)
489576

577+
@always_inline
578+
fn __ge__(self, other: Int) -> Bool:
579+
"""Returns True if self >= other."""
580+
return decimojo.bigint.comparison.greater_equal(
581+
self, Self.from_int(other)
582+
)
583+
490584
@always_inline
491585
fn __lt__(self, other: Self) -> Bool:
492586
"""Returns True if self < other."""
493587
return decimojo.bigint.comparison.less(self, other)
494588

589+
@always_inline
590+
fn __lt__(self, other: Int) -> Bool:
591+
"""Returns True if self < other."""
592+
return decimojo.bigint.comparison.less(self, Self.from_int(other))
593+
495594
@always_inline
496595
fn __le__(self, other: Self) -> Bool:
497596
"""Returns True if self <= other."""
498597
return decimojo.bigint.comparison.less_equal(self, other)
499598

599+
@always_inline
600+
fn __le__(self, other: Int) -> Bool:
601+
"""Returns True if self <= other."""
602+
return decimojo.bigint.comparison.less_equal(self, Self.from_int(other))
603+
500604
@always_inline
501605
fn __eq__(self, other: Self) -> Bool:
502606
"""Returns True if self == other."""
503607
return decimojo.bigint.comparison.equal(self, other)
504608

609+
@always_inline
610+
fn __eq__(self, other: Int) -> Bool:
611+
"""Returns True if self == other."""
612+
return decimojo.bigint.comparison.equal(self, Self.from_int(other))
613+
505614
@always_inline
506615
fn __ne__(self, other: Self) -> Bool:
507616
"""Returns True if self != other."""
508617
return decimojo.bigint.comparison.not_equal(self, other)
509618

619+
@always_inline
620+
fn __ne__(self, other: Int) -> Bool:
621+
"""Returns True if self != other."""
622+
return decimojo.bigint.comparison.not_equal(self, Self.from_int(other))
623+
510624
# ===------------------------------------------------------------------=== #
511625
# Mathematical methods that do not implement a trait (not a dunder)
512626
# ===------------------------------------------------------------------=== #

src/decimojo/biguint/biguint.mojo

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,11 @@ struct BigUInt(Absable, IntableRaising, Stringable, Writable):
8080
# Constructors and life time dunder methods
8181
#
8282
# __init__(out self)
83-
# __init__(out self, empty: Bool)
84-
# __init__(out self, empty: Bool, capacity: Int)
85-
# __init__(out self, *words: UInt32) raises
83+
# __init__(out self, owned words: List[UInt32])
84+
# __init__(out self, owned *words: UInt32)
8685
# __init__(out self, value: Int) raises
87-
# __init__(out self, value: String) raises
86+
# __init__(out self, value: Scalar) raises
87+
# __init__(out self, value: String, ignore_sign: Bool = False) raises
8888
# ===------------------------------------------------------------------=== #
8989

9090
fn __init__(out self):
@@ -93,7 +93,7 @@ struct BigUInt(Absable, IntableRaising, Stringable, Writable):
9393

9494
fn __init__(out self, owned words: List[UInt32]):
9595
"""Initializes a BigUInt from a list of UInt32 words.
96-
It does not check whether the list is empty or the words are invalid.
96+
It does not verify whether the list is empty or the words are invalid.
9797
See `from_list()` for safer initialization.
9898
9999
Args:
@@ -163,6 +163,7 @@ struct BigUInt(Absable, IntableRaising, Stringable, Writable):
163163
fn from_list(owned words: List[UInt32]) raises -> Self:
164164
"""Initializes a BigUInt from a list of UInt32 words safely.
165165
If the list is empty, the BigUInt is initialized with value 0.
166+
The words are validated to ensure they are smaller than `999_999_999`.
166167
167168
Args:
168169
words: A list of UInt32 words representing the coefficient.

src/decimojo/tests.mojo

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@
1818
Implement structs and functions for tests.
1919
"""
2020

21+
import tomlmojo
2122

22-
struct TestCase(Copyable, Movable):
23+
24+
struct TestCase(Copyable, Movable, Stringable, Writable):
2325
"""Structure to hold test case data.
2426
2527
Attributes:
@@ -40,7 +42,9 @@ struct TestCase(Copyable, Movable):
4042
self.a = a
4143
self.b = b
4244
self.expected = expected
43-
self.description = description + "\nx1 = " + self.a + "\nx2 = " + self.b
45+
self.description = (
46+
description + " (a = " + self.a + ", b = " + self.b + ")"
47+
)
4448

4549
fn __copyinit__(out self, other: Self):
4650
self.a = other.a
@@ -53,3 +57,71 @@ struct TestCase(Copyable, Movable):
5357
self.b = other.b^
5458
self.expected = other.expected^
5559
self.description = other.description^
60+
61+
fn __str__(self) -> String:
62+
return (
63+
"TestCase(a: "
64+
+ self.a
65+
+ ", b: "
66+
+ self.b
67+
+ ", expected: "
68+
+ self.expected
69+
+ ", description: "
70+
+ self.description
71+
+ ")"
72+
)
73+
74+
fn write_to[T: Writer](self, mut writer: T):
75+
writer.write("TestCase:\n")
76+
writer.write(" a: " + self.a + "\n")
77+
writer.write(" b: " + self.b + "\n")
78+
writer.write(" expected: " + self.expected + "\n")
79+
writer.write(" description: " + self.description + "\n")
80+
81+
82+
fn load_test_cases(
83+
file_path: String, table_name: String, has_expected_value: Bool = True
84+
) raises -> List[TestCase]:
85+
"""Load test cases from a TOML file for a specific table."""
86+
var toml = tomlmojo.parse_file(file_path)
87+
var test_cases = List[TestCase]()
88+
89+
# Get array of test cases
90+
var cases_array = toml.get_array_of_tables(table_name)
91+
92+
for i in range(len(cases_array)):
93+
var case_table = cases_array[i]
94+
var expected_value = case_table[
95+
"expected"
96+
].as_string() if has_expected_value else String("")
97+
test_cases.append(
98+
TestCase(
99+
case_table["a"].as_string(),
100+
case_table["b"].as_string(),
101+
expected_value,
102+
case_table["description"].as_string(),
103+
)
104+
)
105+
106+
return test_cases
107+
108+
109+
fn load_test_cases(
110+
toml: tomlmojo.parser.TOMLDocument, table_name: String
111+
) raises -> List[TestCase]:
112+
"""Load test cases from a TOMLDocument."""
113+
# Get array of test cases
114+
var cases_array = toml.get_array_of_tables(table_name)
115+
116+
var test_cases = List[TestCase]()
117+
for case_table in cases_array:
118+
test_cases.append(
119+
TestCase(
120+
case_table["a"].as_string(),
121+
case_table["b"].as_string(),
122+
case_table["expected"].as_string(),
123+
case_table["description"].as_string(),
124+
)
125+
)
126+
127+
return test_cases

0 commit comments

Comments
 (0)