Skip to content

Commit baf2bc4

Browse files
authored
[errors] Improve error handling by using DeciMojoError type (#114)
This pull request introduces significant improvements and error handling enhancements for arithmetic operations in the `BigInt` and `BigUInt` types. Key changes include the removal of the `raises` keyword from function signatures, the addition of structured error handling using custom error types, and improvements to the implementation of arithmetic operations to ensure correctness and maintainability. ### Improved Error Handling * Introduced custom error types such as `DeciMojoError`, `OverflowError`, and `ZeroDivisionError` across the codebase for more structured and descriptive error reporting. For example, division and modulo operations now raise `ZeroDivisionError` when dividing by zero. * Replaced generic error messages with detailed exceptions that include contextual information such as the file, function, and previous error, improving debugging and traceability. ### Update TOML parser * Updated the TOML parser by allowing parsing CRLF TOML files. ### Enhancements to Arithmetic Operations * Removed the `raises` keyword from all arithmetic function signatures in `src/decimojo/bigint/arithmetics.mojo` and `src/decimojo/biguint/arithmetics.mojo`. This simplifies the function definitions while maintaining error handling through explicit exceptions. * Updated the `add_inplace`, `subtract`, and `multiply` functions in `src/decimojo/bigint/arithmetics.mojo` to use helper functions like `negative` and `subtract_inplace_no_check` for better clarity and modularity. ### Updates to BigInt Struct Methods * Updated operator overloading methods (e.g., `__add__`, `__sub__`, `__mul__`) in `src/decimojo/bigint/bigint.mojo` to remove the `raises` keyword and include structured error handling where applicable. * Enhanced in-place operators like `__iadd__` and `__isub__` to directly call the updated arithmetic functions, ensuring consistency with the new error handling approach. ### Codebase-Wide Improvements * Added `.gitattributes` configuration to enforce LF (Unix-style) line endings for consistency across the project. * Improved modularity by importing `DeciMojoError` in relevant files such as `src/decimojo/bigint/bigint.mojo` and `src/decimojo/biguint/arithmetics.mojo`. These changes collectively improve the robustness, readability, and maintainability of the codebase while introducing a more structured approach to error handling for arithmetic operations.
1 parent 8a17db2 commit baf2bc4

File tree

8 files changed

+835
-166
lines changed

8 files changed

+835
-166
lines changed

.gitattributes

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
# SCM syntax highlighting & preventing 3-way merges
22
pixi.lock merge=binary linguist-language=YAML linguist-generated=true
3-
magic.lock merge=binary linguist-language=YAML linguist-generated=true
3+
magic.lock merge=binary linguist-language=YAML linguist-generated=true
4+
5+
# Force LF (Unix-style) line endings
6+
* text=auto eol=lf

pixi.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ readme = "README.md"
99
version = "0.5.0"
1010

1111
[dependencies]
12-
max = ">25.4"
12+
mojo = "*"
1313

1414
[tasks]
1515
# format the code
@@ -41,4 +41,4 @@ buint_debug = "clear && pixi run package && cd benches/biguint && pixi run mojo
4141
bdec_debug = "clear && pixi run package && cd benches/bigdecimal && pixi run mojo run -I ../ -D ASSERT=all bench.mojo && cd ../.. && pixi run clean"
4242

4343
# doc
44-
doc = "clear && pixi run mojo doc -o docs/doc.json --diagnose-missing-doc-strings --validate-doc-strings src/decimojo"
44+
doc = "clear && pixi run mojo doc -o docs/doc.json --diagnose-missing-doc-strings --validate-doc-strings src/decimojo"

src/decimojo/bigint/arithmetics.mojo

Lines changed: 116 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,11 @@ Implements basic arithmetic functions for the BigInt type.
2020

2121
from decimojo.bigint.bigint import BigInt
2222
from decimojo.biguint.biguint import BigUInt
23+
from decimojo.errors import DeciMojoError
2324
from decimojo.rounding_mode import RoundingMode
2425

2526

26-
fn add(x1: BigInt, x2: BigInt) raises -> BigInt:
27+
fn add(x1: BigInt, x2: BigInt) -> BigInt:
2728
"""Returns the sum of two BigInts.
2829
2930
Args:
@@ -57,7 +58,7 @@ fn add(x1: BigInt, x2: BigInt) raises -> BigInt:
5758
return BigInt(magnitude^, sign=x1.sign)
5859

5960

60-
fn add_inplace(mut x1: BigInt, x2: BigInt) raises -> None:
61+
fn add_inplace(mut x1: BigInt, x2: BigInt) -> None:
6162
"""Increments a BigInt number by another BigInt number in place.
6263
6364
Args:
@@ -67,15 +68,17 @@ fn add_inplace(mut x1: BigInt, x2: BigInt) raises -> None:
6768

6869
# If signs are different, delegate to `subtract`
6970
if x1.sign != x2.sign:
70-
x1 = subtract(x1, -x2)
71+
x1 = subtract(x1, negative(x2))
7172
return
7273

7374
# Same sign: add magnitudes in place
7475
else:
75-
x1.magnitude += x2.magnitude
76+
decimojo.biguint.arithmetics.add_inplace(x1.magnitude, x2.magnitude)
7677

78+
return
7779

78-
fn subtract(x1: BigInt, x2: BigInt) raises -> BigInt:
80+
81+
fn subtract(x1: BigInt, x2: BigInt) -> BigInt:
7982
"""Returns the difference of two numbers.
8083
8184
Args:
@@ -102,7 +105,7 @@ fn subtract(x1: BigInt, x2: BigInt) raises -> BigInt:
102105

103106
# If signs are different, delegate to `add`
104107
if x1.sign != x2.sign:
105-
return x1 + (-x2)
108+
return add(x1, negative(x2))
106109

107110
# Same sign, compare magnitudes to determine result sign and operation
108111
var comparison_result = x1.magnitude.compare(x2.magnitude)
@@ -114,12 +117,18 @@ fn subtract(x1: BigInt, x2: BigInt) raises -> BigInt:
114117
var sign: Bool
115118
if comparison_result > 0: # |x1| > |x2|
116119
# Subtract smaller from larger
117-
magnitude = x1.magnitude - x2.magnitude
120+
magnitude = x1.magnitude
121+
decimojo.biguint.arithmetics.subtract_inplace_no_check(
122+
magnitude, x2.magnitude
123+
)
118124
sign = x1.sign
119125

120126
else: # |x1| < |x2|
121127
# Subtract larger from smaller and negate the result
122-
magnitude = x2.magnitude - x1.magnitude
128+
magnitude = x2.magnitude
129+
decimojo.biguint.arithmetics.subtract_inplace_no_check(
130+
magnitude, x1.magnitude
131+
)
123132
sign = not x1.sign
124133

125134
return BigInt(magnitude^, sign=sign)
@@ -165,7 +174,7 @@ fn absolute(x: BigInt) -> BigInt:
165174
return x
166175

167176

168-
fn multiply(x1: BigInt, x2: BigInt) raises -> BigInt:
177+
fn multiply(x1: BigInt, x2: BigInt) -> BigInt:
169178
"""Returns the product of two BigInt numbers.
170179
171180
Args:
@@ -197,25 +206,51 @@ fn floor_divide(x1: BigInt, x2: BigInt) raises -> BigInt:
197206
198207
Returns:
199208
The quotient of x1 / x2, rounded toward negative infinity.
200-
"""
201209
202-
if x2.is_zero():
203-
raise Error("Error in `floor_divide`: Division by zero")
204-
205-
if x1.is_zero():
206-
return BigInt()
210+
Raises:
211+
DeciMojoError: If `decimojo.biguint.arithmetics.floor_divide()` fails.
212+
DeciMojoError: If `decimojo.biguint.arithmetics.ceil_divide()` fails.
213+
"""
207214

208215
# For floor division, the sign rules are:
209216
# (1) Same signs: result is positive, use `floor_divide` on magnitudes
210217
# (1) Different signs: result is negative, use `ceil_divide` on magnitudes
211218

219+
var magnitude: BigUInt
220+
212221
if x1.sign == x2.sign:
213-
# Use floor (truncate) division between magnitudes
214-
return BigInt(x1.magnitude.floor_divide(x2.magnitude), sign=False)
222+
# Use floor division of the magnitudes
223+
try:
224+
magnitude = decimojo.biguint.arithmetics.floor_divide(
225+
x1.magnitude, x2.magnitude
226+
)
227+
except e:
228+
raise Error(
229+
DeciMojoError(
230+
file="src/decimojo/bigint/arithmetics",
231+
function="floor_divide()",
232+
message=None,
233+
previous_error=e,
234+
),
235+
)
236+
return BigInt(magnitude^, sign=False)
215237

216238
else:
217239
# Use ceil division of the magnitudes
218-
return BigInt(x1.magnitude.ceil_divide(x2.magnitude), sign=True)
240+
try:
241+
magnitude = decimojo.biguint.arithmetics.ceil_divide(
242+
x1.magnitude, x2.magnitude
243+
)
244+
except e:
245+
raise Error(
246+
DeciMojoError(
247+
file="src/decimojo/bigint/arithmetics",
248+
function="floor_divide()",
249+
message=None,
250+
previous_error=e,
251+
),
252+
)
253+
return BigInt(magnitude^, sign=True)
219254

220255

221256
fn truncate_divide(x1: BigInt, x2: BigInt) raises -> BigInt:
@@ -231,15 +266,22 @@ fn truncate_divide(x1: BigInt, x2: BigInt) raises -> BigInt:
231266
The quotient of x1 / x2, truncated toward zero.
232267
233268
Raises:
234-
ValueError: If the divisor is zero.
269+
DeciMojoError: If `decimojo.biguint.arithmetics.floor_divide()` fails.
235270
"""
236-
if x2.is_zero():
237-
raise Error("Error in `truncate_divide`: Division by zero")
238-
239-
if x1.is_zero():
240-
return BigInt() # Return zero
241-
242-
var magnitude = x1.magnitude.floor_divide(x2.magnitude)
271+
var magnitude: BigUInt
272+
try:
273+
magnitude = decimojo.biguint.arithmetics.floor_divide(
274+
x1.magnitude, x2.magnitude
275+
)
276+
except e:
277+
raise Error(
278+
DeciMojoError(
279+
file="src/decimojo/bigint/arithmetics",
280+
function="truncate_divide()",
281+
message=None,
282+
previous_error=e,
283+
),
284+
)
243285
return BigInt(magnitude^, sign=x1.sign != x2.sign)
244286

245287

@@ -254,21 +296,47 @@ fn floor_modulo(x1: BigInt, x2: BigInt) raises -> BigInt:
254296
255297
Returns:
256298
The remainder of x1 being divided by x2, with the same sign as x2.
257-
"""
258299
259-
if x2.is_zero():
260-
raise Error("Error in `floor_modulo`: Division by zero")
300+
Raises:
301+
DeciMojoError: If `decimojo.biguint.arithmetics.floor_modulo()` fails.
302+
DeciMojoError: If `decimojo.biguint.arithmetics.ceil_modulo()` fails.
303+
"""
261304

262-
if x1.is_zero():
263-
return BigInt() # Return zero
305+
var magnitude: BigUInt
264306

265307
if x1.sign == x2.sign:
266308
# Use floor (truncate) division between magnitudes
267-
return BigInt(x1.magnitude.floor_modulo(x2.magnitude), sign=x2.sign)
309+
try:
310+
magnitude = decimojo.biguint.arithmetics.floor_modulo(
311+
x1.magnitude, x2.magnitude
312+
)
313+
except e:
314+
raise Error(
315+
DeciMojoError(
316+
file="src/decimojo/bigint/arithmetics",
317+
function="floor_modulo()",
318+
message=None,
319+
previous_error=e,
320+
),
321+
)
322+
return BigInt(magnitude^, sign=x2.sign)
268323

269324
else:
270325
# Use ceil division of the magnitudes
271-
return BigInt(x1.magnitude.ceil_modulo(x2.magnitude), sign=x2.sign)
326+
try:
327+
magnitude = decimojo.biguint.arithmetics.ceil_modulo(
328+
x1.magnitude, x2.magnitude
329+
)
330+
except e:
331+
raise Error(
332+
DeciMojoError(
333+
file="src/decimojo/bigint/arithmetics",
334+
function="floor_modulo()",
335+
message=None,
336+
previous_error=e,
337+
),
338+
)
339+
return BigInt(magnitude^, sign=x2.sign)
272340

273341

274342
fn truncate_modulo(x1: BigInt, x2: BigInt) raises -> BigInt:
@@ -284,13 +352,20 @@ fn truncate_modulo(x1: BigInt, x2: BigInt) raises -> BigInt:
284352
The remainder of x1 being divided by x2, with the same sign as x1.
285353
286354
Raises:
287-
ValueError: If the divisor is zero.
355+
DeciMojoError: If `decimojo.biguint.arithmetics.floor_modulo()` fails.
288356
"""
289-
if x2.is_zero():
290-
raise Error("Error in `truncate_modulo`: Division by zero")
291-
292-
if x1.is_zero():
293-
return BigInt() # Return zero
294-
295-
var magnitude = x1.magnitude.floor_modulo(x2.magnitude)
357+
var magnitude: BigUInt
358+
try:
359+
magnitude = decimojo.biguint.arithmetics.floor_modulo(
360+
x1.magnitude, x2.magnitude
361+
)
362+
except e:
363+
raise Error(
364+
DeciMojoError(
365+
file="src/decimojo/bigint/arithmetics",
366+
function="truncate_modulo()",
367+
message=None,
368+
previous_error=e,
369+
),
370+
)
296371
return BigInt(magnitude^, sign=x1.sign)

src/decimojo/bigint/bigint.mojo

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import decimojo.bigint.arithmetics
2929
import decimojo.bigint.comparison
3030
from decimojo.bigdecimal.bigdecimal import BigDecimal
3131
from decimojo.biguint.biguint import BigUInt
32+
from decimojo.errors import DeciMojoError
3233
import decimojo.str
3334

3435
# Type aliases
@@ -463,24 +464,44 @@ struct BigInt(
463464
# ===------------------------------------------------------------------=== #
464465

465466
@always_inline
466-
fn __add__(self, other: Self) raises -> Self:
467+
fn __add__(self, other: Self) -> Self:
467468
return decimojo.bigint.arithmetics.add(self, other)
468469

469470
@always_inline
470-
fn __sub__(self, other: Self) raises -> Self:
471+
fn __sub__(self, other: Self) -> Self:
471472
return decimojo.bigint.arithmetics.subtract(self, other)
472473

473474
@always_inline
474-
fn __mul__(self, other: Self) raises -> Self:
475+
fn __mul__(self, other: Self) -> Self:
475476
return decimojo.bigint.arithmetics.multiply(self, other)
476477

477478
@always_inline
478479
fn __floordiv__(self, other: Self) raises -> Self:
479-
return decimojo.bigint.arithmetics.floor_divide(self, other)
480+
try:
481+
return decimojo.bigint.arithmetics.floor_divide(self, other)
482+
except e:
483+
raise Error(
484+
DeciMojoError(
485+
message=None,
486+
function="BigInt.__floordiv__()",
487+
file="src/decimojo/bigint/bigint.mojo",
488+
previous_error=e,
489+
)
490+
)
480491

481492
@always_inline
482493
fn __mod__(self, other: Self) raises -> Self:
483-
return decimojo.bigint.arithmetics.floor_modulo(self, other)
494+
try:
495+
return decimojo.bigint.arithmetics.floor_modulo(self, other)
496+
except e:
497+
raise Error(
498+
DeciMojoError(
499+
message=None,
500+
function="BigInt.__mod__()",
501+
file="src/decimojo/bigint/bigint.mojo",
502+
previous_error=e,
503+
)
504+
)
484505

485506
@always_inline
486507
fn __pow__(self, exponent: Self) raises -> Self:
@@ -493,15 +514,15 @@ struct BigInt(
493514
# ===------------------------------------------------------------------=== #
494515

495516
@always_inline
496-
fn __radd__(self, other: Self) raises -> Self:
517+
fn __radd__(self, other: Self) -> Self:
497518
return decimojo.bigint.arithmetics.add(self, other)
498519

499520
@always_inline
500-
fn __rsub__(self, other: Self) raises -> Self:
521+
fn __rsub__(self, other: Self) -> Self:
501522
return decimojo.bigint.arithmetics.subtract(other, self)
502523

503524
@always_inline
504-
fn __rmul__(self, other: Self) raises -> Self:
525+
fn __rmul__(self, other: Self) -> Self:
505526
return decimojo.bigint.arithmetics.multiply(self, other)
506527

507528
@always_inline
@@ -524,11 +545,11 @@ struct BigInt(
524545
# ===------------------------------------------------------------------=== #
525546

526547
@always_inline
527-
fn __iadd__(mut self, other: Self) raises:
548+
fn __iadd__(mut self, other: Self):
528549
decimojo.bigint.arithmetics.add_inplace(self, other)
529550

530551
@always_inline
531-
fn __iadd__(mut self, other: Int) raises:
552+
fn __iadd__(mut self, other: Int):
532553
# Optimize the case `i += 1`
533554
if (self >= 0) and (other >= 0) and (other <= 999_999_999):
534555
decimojo.biguint.arithmetics.add_inplace_by_uint32(
@@ -538,11 +559,11 @@ struct BigInt(
538559
decimojo.bigint.arithmetics.add_inplace(self, other)
539560

540561
@always_inline
541-
fn __isub__(mut self, other: Self) raises:
562+
fn __isub__(mut self, other: Self):
542563
self = decimojo.bigint.arithmetics.subtract(self, other)
543564

544565
@always_inline
545-
fn __imul__(mut self, other: Self) raises:
566+
fn __imul__(mut self, other: Self):
546567
self = decimojo.bigint.arithmetics.multiply(self, other)
547568

548569
@always_inline

0 commit comments

Comments
 (0)