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
6 changes: 4 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

## v0.24.0 - unreleased

- `string.slice` is now tail recursive and will no longer blog the stack on
large inputs when running on JavaScript.
- `string.slice` is now tail recursive and will no longer exceed the stack size
on large inputs on target JavaScript.
- Added `int.remainder` and `int.modulo` functions which allow safe remainder
and modulo operations the way common languages support them.
- Added `int.floor_divide` to complement the truncated `int.divide`.

## v0.23.0 - 2022-09-15
Expand Down
104 changes: 100 additions & 4 deletions src/gleam/int.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -502,7 +502,10 @@ pub fn random(boundary_a: Int, boundary_b: Int) -> Int {
|> float.round()
}

/// Returns division of the inputs as a `Result`.
/// Performs a truncated integer division.
///
/// Returns division of the inputs as a `Result`: If the given divisor equals
/// `0`, this function returns an `Error`.
///
/// ## Examples
///
Expand All @@ -512,12 +515,105 @@ pub fn random(boundary_a: Int, boundary_b: Int) -> Int {
///
/// > divide(1, 0)
/// Error(Nil)
///
/// > divide(5, 2)
/// Ok(2)
///
/// > divide(-99, 2)
/// Ok(-49)
/// ```
///
pub fn divide(a: Int, by b: Int) -> Result(Int, Nil) {
case b {
pub fn divide(dividend: Int, by divisor: Int) -> Result(Int, Nil) {
case divisor {
0 -> Error(Nil)
b -> Ok(a / b)
divisor -> Ok(dividend / divisor)
}
}

/// Computes the remainder of an integer division of inputs as a `Result`.
///
/// This functions mimicks modulo operation of following languages:
/// C, C#, C++, Go, Java, JavaScript, Kotlin, Nim, PHP, Rust,
/// Scala, Swift, Crystal as well as Erlang's and Elixir's rem operator.
///
/// Returns division of the inputs as a `Result`: If the given divisor equals
/// `0`, this function returns an `Error`.
///
/// ## Examples
///
/// ```gleam
/// > int.remainder(3, 2)
/// Ok(1)
///
/// > int.remainder(1, 0)
/// Error(Nil)
///
/// > int.remainder(10, -1)
/// Ok(0)
///
/// > int.remainder(13, by: 3)
/// Ok(1)
///
/// > int.remainder(-13, by: 3)
/// Ok(-1)
///
/// > int.remainder(13, by: -3)
/// Ok(1)
///
/// > int.remainder(-13, by: -3)
/// Ok(-1)
/// ```
///
pub fn remainder(dividend: Int, by divisor: Int) -> Result(Int, Nil) {
case divisor {
0 -> Error(Nil)
divisor -> Ok(dividend % divisor)
}
}

/// Computes the modulo of an integer division of inputs as a `Result`.
///
/// This functions mimicks modulo operation on following languages:
/// Haskell, Lua, Python, Ruby, as well as Elixir's Integer.mod().
///
/// Returns division of the inputs as a `Result`: If the given divisor equals
/// `0`, this function returns an `Error`.
///
/// ## Examples
///
/// ```gleam
/// > int.modulo(3, 2)
/// Ok(1)
///
/// > int.modulo(1, 0)
/// Error(Nil)
///
/// > int.modulo(10, -1)
/// Ok(0)
///
/// > int.modulo(13, by: 3)
/// Ok(1)
///
/// > int.modulo(-13, by: 3)
/// Ok(2)
///
/// > int.modulo(13, by: -3)
/// Ok(-2)
///
/// > int.modulo(-13, by: -3)
/// Ok(-1)
/// ```
///
pub fn modulo(dividend: Int, by divisor: Int) -> Result(Int, Nil) {
case divisor {
0 -> Error(Nil)
_ -> {
let remainder = dividend % divisor
case remainder * divisor < 0 {
True -> Ok(remainder + divisor)
False -> Ok(remainder)
}
}
}
}

Expand Down
54 changes: 54 additions & 0 deletions test/gleam/int_test.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -415,13 +415,67 @@ pub fn random_test() {
pub fn divide_test() {
int.divide(1, 1)
|> should.equal(Ok(1))

int.divide(1, 0)
|> should.equal(Error(Nil))

int.divide(0, by: 1)
|> should.equal(Ok(0))

int.divide(1, by: 0)
|> should.equal(Error(Nil))

int.divide(5, by: 2)
|> should.equal(Ok(2))

int.divide(-99, by: 2)
|> should.equal(Ok(-49))
}

pub fn remainder_test() {
int.remainder(3, 2)
|> should.equal(Ok(1))

int.remainder(1, 0)
|> should.equal(Error(Nil))

int.remainder(10, -1)
|> should.equal(Ok(0))

int.remainder(13, by: 3)
|> should.equal(Ok(1))

int.remainder(-13, by: 3)
|> should.equal(Ok(-1))

int.remainder(13, by: -3)
|> should.equal(Ok(1))

int.remainder(-13, by: -3)
|> should.equal(Ok(-1))
}

pub fn modulo_test() {
int.modulo(3, 2)
|> should.equal(Ok(1))

int.modulo(1, 0)
|> should.equal(Error(Nil))

int.modulo(10, -1)
|> should.equal(Ok(0))

int.modulo(13, by: 3)
|> should.equal(Ok(1))

int.modulo(-13, by: 3)
|> should.equal(Ok(2))

int.modulo(13, by: -3)
|> should.equal(Ok(-2))

int.modulo(-13, by: -3)
|> should.equal(Ok(-1))
}

pub fn floor_divide_test() {
Expand Down