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
74 changes: 74 additions & 0 deletions src/decimojo/bigint/arithmetics.mojo
Original file line number Diff line number Diff line change
Expand Up @@ -187,3 +187,77 @@ fn absolute(x: BigInt) -> BigInt:
return -x
else:
return x


fn multiply(x1: BigInt, x2: BigInt) raises -> BigInt:
"""Returns the product of two BigInt numbers.

Args:
x1: The first BigInt operand (multiplicand).
x2: The second BigInt operand (multiplier).

Returns:
The product of the two BigInt numbers.
"""
# CASE: One of the operands is zero
if x1.is_zero() or x2.is_zero():
return BigInt.from_raw_words(UInt32(0), sign=x1.sign != x2.sign)

# CASE: One of the operands is one or negative one
if x1.is_one_or_minus_one():
var result = x2
result.sign = x1.sign != x2.sign
return result^

if x2.is_one_or_minus_one():
var result = x1
result.sign = x1.sign != x2.sign
return result^

# The maximum number of words in the result is the sum of the words in the operands
var max_result_len = len(x1.words) + len(x2.words)
var result = BigInt(empty=True, capacity=max_result_len)
result.sign = x1.sign != x2.sign

# Initialize result words with zeros
for _ in range(max_result_len):
result.words.append(0)

# Perform the multiplication word by word (from least significant to most significant)
# x1 = x1[0] + x1[1] * 10^9
# x2 = x2[0] + x2[1] * 10^9
# x1 * x2 = x1[0] * x2[0] + (x1[0] * x2[1] + x1[1] * x2[0]) * 10^9 + x1[1] * x2[1] * 10^18
var carry: UInt64 = 0
for i in range(len(x1.words)):
# Skip if the word is zero
if x1.words[i] == 0:
continue

carry = UInt64(0)

for j in range(len(x2.words)):
# Skip if the word is zero
if x2.words[j] == 0:
continue

# Calculate the product of the current words
# plus the carry from the previous multiplication
# plus the value already at this position in the result
var product = UInt64(x1.words[i]) * UInt64(
x2.words[j]
) + carry + UInt64(result.words[i + j])

# The lower 9 digits (base 10^9) go into the current word
# The upper digits become the carry for the next position
result.words[i + j] = UInt32(product % 1_000_000_000)
carry = product // 1_000_000_000

# If there is a carry left, add it to the next position
if carry > 0:
result.words[i + len(x2.words)] += UInt32(carry)

# Remove trailing zeros
while len(result.words) > 1 and result.words[len(result.words) - 1] == 0:
result.words.resize(len(result.words) - 1)

return result^
35 changes: 27 additions & 8 deletions src/decimojo/bigint/bigint.mojo
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,13 @@ struct BigInt(Absable, IntableRaising, Writable):
which can be of arbitrary length stored in little-endian order.
Each UInt32 word represents digits ranging from 0 to 10^9 - 1.
- A Bool value for the sign.
"""

# Internal representation fields
alias _words_type = List[UInt32, hint_trivial_type=True]
The value of the BigInt is calculated as follows:

x = x[0] * 10^0 + x[1] * 10^9 + x[2] * 10^18 + ... x[n] * 10^(9n)
"""

var words: Self._words_type
var words: List[UInt32]
"""A list of UInt32 words representing the coefficient."""
var sign: Bool
"""Sign information."""
Expand All @@ -76,7 +77,7 @@ struct BigInt(Absable, IntableRaising, Writable):

fn __init__(out self):
"""Initializes a BigInt with value 0."""
self.words = Self._words_type(UInt32(0))
self.words = List[UInt32](UInt32(0))
self.sign = False

fn __init__(out self, empty: Bool):
Expand All @@ -87,7 +88,7 @@ struct BigInt(Absable, IntableRaising, Writable):
If True, the BigInt is empty.
If False, the BigInt is intialized with value 0.
"""
self.words = Self._words_type()
self.words = List[UInt32]()
self.sign = False
if not empty:
self.words.append(UInt32(0))
Expand All @@ -101,7 +102,7 @@ struct BigInt(Absable, IntableRaising, Writable):
If False, the BigInt is intialized with value 0.
capacity: The capacity of the BigInt.
"""
self.words = Self._words_type(capacity=capacity)
self.words = List[UInt32](capacity=capacity)
self.sign = False
if not empty:
self.words.append(UInt32(0))
Expand All @@ -127,7 +128,7 @@ struct BigInt(Absable, IntableRaising, Writable):

End of examples.
"""
self.words = Self._words_type(capacity=len(words))
self.words = List[UInt32](capacity=len(words))
self.sign = sign

# Check if the words are valid
Expand Down Expand Up @@ -470,6 +471,10 @@ struct BigInt(Absable, IntableRaising, Writable):
fn __sub__(self, other: Self) raises -> Self:
return decimojo.bigint.arithmetics.subtract(self, other)

@always_inline
fn __mul__(self, other: Self) raises -> Self:
return decimojo.bigint.arithmetics.multiply(self, other)

# ===------------------------------------------------------------------=== #
# Basic binary augmented arithmetic assignments dunders
# These methods are called to implement the binary augmented arithmetic
Expand All @@ -485,6 +490,10 @@ struct BigInt(Absable, IntableRaising, Writable):
fn __isub__(mut self, other: Self) raises:
self = decimojo.bigint.arithmetics.subtract(self, other)

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

# ===------------------------------------------------------------------=== #
# Other methods
# ===------------------------------------------------------------------=== #
Expand All @@ -494,6 +503,16 @@ struct BigInt(Absable, IntableRaising, Writable):
"""Returns True if this BigInt represents zero."""
return len(self.words) == 1 and self.words[0] == 0

@always_inline
fn is_one_or_minus_one(self) -> Bool:
"""Returns True if this BigInt represents one or negative one."""
return len(self.words) == 1 and self.words[0] == 1

@always_inline
fn is_negative(self) -> Bool:
"""Returns True if this BigInt is negative."""
return self.sign

# ===------------------------------------------------------------------=== #
# Internal methods
# ===------------------------------------------------------------------=== #
Expand Down
Loading