Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 6 additions & 8 deletions src/decimojo/bigint/arithmetics.mojo
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ fn add(x1: BigInt, x2: BigInt) raises -> BigInt:
"""

# If one of the numbers is zero, return the other number
if len(x1.words) == 1 and x1.words[0] == 0:
if x1.is_zero():
return x2
if len(x2.words) == 1 and x2.words[0] == 0:
if x2.is_zero():
return x1

# If signs are different, we use `subtract` instead
Expand Down Expand Up @@ -81,7 +81,7 @@ fn add(x1: BigInt, x2: BigInt) raises -> BigInt:
if carry > 0:
result.words.append(carry)

return result
return result^


fn subtract(x1: BigInt, x2: BigInt) raises -> BigInt:
Expand Down Expand Up @@ -157,7 +157,7 @@ fn subtract(x1: BigInt, x2: BigInt) raises -> BigInt:
while len(result.words) > 1 and result.words[len(result.words) - 1] == 0:
result.words.resize(len(result.words) - 1)

return result
return result^


fn negative(x: BigInt) -> BigInt:
Expand All @@ -171,7 +171,7 @@ fn negative(x: BigInt) -> BigInt:
"""
var result = x
result.sign = not result.sign
return result
return result^


fn absolute(x: BigInt) -> BigInt:
Expand All @@ -184,8 +184,6 @@ fn absolute(x: BigInt) -> BigInt:
A new BigInt containing the absolute value of x.
"""
if x.sign:
var result = x
result.sign = False
return result
return -x
else:
return x
59 changes: 40 additions & 19 deletions src/decimojo/bigint/bigint.mojo
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ mathematical methods that do not implement a trait.

from memory import UnsafePointer
import testing
import time

import decimojo.bigint.arithmetics
import decimojo.bigint.comparison
Expand Down Expand Up @@ -244,41 +245,40 @@ struct BigInt(Absable, IntableRaising, Writable):
@staticmethod
fn from_string(value: String) raises -> BigInt:
"""Initializes a BigInt from a string representation.
The string is normalized with `deciomojo.str.parse_string_of_number()`.
The string is normalized with `deciomojo.str.parse_numeric_string()`.

Args:
value: The string representation of the BigInt.

Returns:
The BigInt representation of the string.
"""

var coef_string: String
var coef: List[UInt8]
var scale: Int
var sign: Bool
coef_string, scale, sign = decimojo.str.parse_string_of_number(value)
coef, scale, sign = decimojo.str.parse_numeric_string(value)

# Check if the number is zero
if coef_string == "0":
return Self()
if len(coef) == 1 and coef[0] == UInt8(0):
return Self.from_raw_words(UInt32(0), sign=sign)

# Check whether the number is an integer
# If the fractional part is not zero, raise an error
# If the fractional part is zero, remove the fractional part
if scale > 0:
if scale >= len(coef_string):
if scale >= len(coef):
raise Error(
"Error in `from_string`: The number is not an integer."
)
for i in range(1, scale + 1):
if coef_string[-i] != "0":
if coef[-i] != 0:
raise Error(
"Error in `from_string`: The number is not an integer."
)
coef_string = coef_string[:-scale]
coef.resize(-scale)
scale = 0

var number_of_digits = len(coef_string) - scale
var number_of_digits = len(coef) - scale
var number_of_words = number_of_digits // 9
if number_of_digits % 9 != 0:
number_of_words += 1
Expand All @@ -288,23 +288,24 @@ struct BigInt(Absable, IntableRaising, Writable):

if scale == 0:
# This is a true integer
var number_of_digits = len(coef_string)
var number_of_digits = len(coef)
var number_of_words = number_of_digits // 9
if number_of_digits % 9 != 0:
number_of_words += 1

var result = Self(empty=True, capacity=number_of_words)
result.sign = sign

var end: Int = number_of_digits
var start: Int
while end >= 9:
start = end - 9
var word = UInt32(Int(coef_string[start:end]))
var word: UInt32 = 0
for digit in coef[start:end]:
word = word * 10 + UInt32(digit[])
result.words.append(word)
end = start
if end > 0:
var word = UInt32(Int(coef_string[0:end]))
var word: UInt32 = 0
for digit in coef[0:end]:
word = word * 10 + UInt32(digit[])
result.words.append(word)

return result
Expand All @@ -317,17 +318,22 @@ struct BigInt(Absable, IntableRaising, Writable):
for _ in range(number_of_trailing_zero_words):
result.words.append(UInt32(0))

coef_string += "0" * remaining_trailing_zero_digits
for _ in range(remaining_trailing_zero_digits):
coef.append(UInt8(0))

var end: Int = number_of_digits + scale + remaining_trailing_zero_digits
var start: Int
while end >= 9:
start = end - 9
var word = UInt32(Int(coef_string[start:end]))
var word: UInt32 = 0
for digit in coef[start:end]:
word = word * 10 + UInt32(digit[])
result.words.append(word)
end = start
if end > 0:
var word = UInt32(Int(coef_string[0:end]))
var word: UInt32 = 0
for digit in coef[0:end]:
word = word * 10 + UInt32(digit[])
result.words.append(word)

return result
Expand Down Expand Up @@ -464,6 +470,21 @@ struct BigInt(Absable, IntableRaising, Writable):
fn __sub__(self, other: Self) raises -> Self:
return decimojo.bigint.arithmetics.subtract(self, other)

# ===------------------------------------------------------------------=== #
# Basic binary augmented arithmetic assignments dunders
# These methods are called to implement the binary augmented arithmetic
# assignments
# (+=, -=, *=, @=, /=, //=, %=, **=, <<=, >>=, &=, ^=, |=)
# ===------------------------------------------------------------------=== #

@always_inline
fn __iadd__(mut self, other: Self) raises:
self = decimojo.bigint.arithmetics.add(self, other)

@always_inline
fn __isub__(mut self, other: Self) raises:
self = decimojo.bigint.arithmetics.subtract(self, other)

# ===------------------------------------------------------------------=== #
# Other methods
# ===------------------------------------------------------------------=== #
Expand Down
59 changes: 33 additions & 26 deletions src/decimojo/str.mojo
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,20 @@

"""String manipulation functions."""

import time

fn parse_string_of_number(value: String) raises -> Tuple[String, Int, Bool]:

fn parse_numeric_string(
value: String,
) raises -> Tuple[List[UInt8], Int, Bool]:
"""Parse the string of a number into normalized parts.

Args:
value: The string representation of a number.

Returns:
A tuple of:
- Normalized string which represents an integer.
- Normalized coefficient as List[UInt8] which represents an integer.
- Scale of the number.
- Sign of the number.

Expand All @@ -45,12 +49,13 @@ fn parse_string_of_number(value: String) raises -> Tuple[String, Int, Bool]:

Examples:
```console
parse_string("123") -> ("123", 0)
parse_string("123.456") -> ("123456", 3)
parse_string("123.456e3") -> ("123456", 0)
parse_string("123.456e-3") -> ("123456", 6)
parse_string("123.456e+10") -> ("123456", -7)
parse_string("0.00123456") -> ("123456", 8)
parse_string("123") -> (123, 0, False)
parse_string("123.456") -> (123456, 3, False)
parse_string("123.456e3") -> (123456, 0, False)
parse_string("123.456e-3") -> (123456, 6, False)
parse_string("123.456e+10") -> (123456, -7, False)
parse_string("0.00123456") -> (123456, 8, False)
parse_string("-123") -> (123, 0, True)
```
End of examples.
"""
Expand All @@ -60,7 +65,7 @@ fn parse_string_of_number(value: String) raises -> Tuple[String, Int, Bool]:
var value_bytes_len = len(value_bytes)

if value_bytes_len == 0:
return Tuple(String(""), 0, False)
raise Error("Error in `parse_numeric_string`: Empty string.")

if value_bytes_len != value_string_slice.char_length():
raise Error(
Expand All @@ -81,19 +86,20 @@ fn parse_string_of_number(value: String) raises -> Tuple[String, Int, Bool]:

var mantissa_sign: Bool = False # True if negative
var exponent_sign: Bool = False # True if negative
var coef_string: String = ""
var coef: List[UInt8] = List[UInt8](capacity=value_bytes_len)
var scale: Int = 0
var raw_exponent: Int = 0

for code in value_bytes:
for code_ptr in value_bytes:
var code = code_ptr[]
# If the char is " ", skip it
if code[] == 32:
if code == 32:
pass
# If the char is "," or "_", skip it
elif code[] == 44 or code[] == 95:
elif code == 44 or code == 95:
unexpected_end_char = True
# If the char is "-"
elif code[] == 45:
elif code == 45:
unexpected_end_char = True
if exponent_sign_read:
raise Error("Minus sign cannot appear twice in exponent.")
Expand All @@ -106,7 +112,7 @@ fn parse_string_of_number(value: String) raises -> Tuple[String, Int, Bool]:
mantissa_sign = True
mantissa_sign_read = True
# If the char is "+"
elif code[] == 43:
elif code == 43:
unexpected_end_char = True
if exponent_sign_read:
raise Error("Plus sign cannot appear twice in exponent.")
Expand All @@ -117,15 +123,15 @@ fn parse_string_of_number(value: String) raises -> Tuple[String, Int, Bool]:
else:
mantissa_sign_read = True
# If the char is "."
elif code[] == 46:
elif code == 46:
unexpected_end_char = False
if decimal_point_read:
raise Error("Decimal point can only appear once.")
else:
decimal_point_read = True
mantissa_sign_read = True
# If the char is "e" or "E"
elif code[] == 101 or code[] == 69:
elif code == 101 or code == 69:
unexpected_end_char = True
if exponent_notation_read:
raise Error("Exponential notation can only appear once.")
Expand All @@ -135,7 +141,7 @@ fn parse_string_of_number(value: String) raises -> Tuple[String, Int, Bool]:
exponent_notation_read = True

# If the char is a digit 0
elif code[] == 48:
elif code == 48:
unexpected_end_char = False

# Exponent part
Expand All @@ -150,41 +156,42 @@ fn parse_string_of_number(value: String) raises -> Tuple[String, Int, Bool]:
mantissa_start = True

if mantissa_significant_start:
coef_string += "0"
coef.append(0)

if decimal_point_read:
scale += 1

# If the char is a digit 1 - 9
elif code[] >= 49 and code[] <= 57:
elif code >= 49 and code <= 57:
unexpected_end_char = False

# Exponent part
if exponent_notation_read:
exponent_start = True
raw_exponent = raw_exponent * 10 + Int(code[] - 48)
raw_exponent = raw_exponent * 10 + Int(code - 48)

# Mantissa part
else:
mantissa_significant_start = True
mantissa_start = True
coef_string += String(code[] - 48)
coef.append(code - 48)
if decimal_point_read:
scale += 1

else:
raise Error(
"Invalid character in the string of the number: {}".format(
chr(Int(code[]))
chr(Int(code))
)
)

if unexpected_end_char:
raise Error("Unexpected end character in the string of the number.")

if len(coef_string) == 0:
if len(coef) == 0:
# For example, "0000."
if mantissa_start:
coef_string = "0"
coef.append(0)
else:
raise Error("No digits found in the string of the number.")

Expand All @@ -200,4 +207,4 @@ fn parse_string_of_number(value: String) raises -> Tuple[String, Int, Bool]:
# 1.234e8 -> 1234e5 -> 1234 and scale = -5
scale -= raw_exponent

return Tuple(coef_string, scale, mantissa_sign)
return Tuple(coef, scale, mantissa_sign)