Skip to content

Commit 64b810d

Browse files
authored
Add int.{remainder, modulo} (#342)
1 parent fe62710 commit 64b810d

File tree

3 files changed

+158
-6
lines changed

3 files changed

+158
-6
lines changed

CHANGELOG.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22

33
## v0.24.0 - unreleased
44

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

911
## v0.23.0 - 2022-09-15

src/gleam/int.gleam

Lines changed: 100 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -502,7 +502,10 @@ pub fn random(boundary_a: Int, boundary_b: Int) -> Int {
502502
|> float.round()
503503
}
504504

505-
/// Returns division of the inputs as a `Result`.
505+
/// Performs a truncated integer division.
506+
///
507+
/// Returns division of the inputs as a `Result`: If the given divisor equals
508+
/// `0`, this function returns an `Error`.
506509
///
507510
/// ## Examples
508511
///
@@ -512,12 +515,105 @@ pub fn random(boundary_a: Int, boundary_b: Int) -> Int {
512515
///
513516
/// > divide(1, 0)
514517
/// Error(Nil)
518+
///
519+
/// > divide(5, 2)
520+
/// Ok(2)
521+
///
522+
/// > divide(-99, 2)
523+
/// Ok(-49)
515524
/// ```
516525
///
517-
pub fn divide(a: Int, by b: Int) -> Result(Int, Nil) {
518-
case b {
526+
pub fn divide(dividend: Int, by divisor: Int) -> Result(Int, Nil) {
527+
case divisor {
519528
0 -> Error(Nil)
520-
b -> Ok(a / b)
529+
divisor -> Ok(dividend / divisor)
530+
}
531+
}
532+
533+
/// Computes the remainder of an integer division of inputs as a `Result`.
534+
///
535+
/// This functions mimicks modulo operation of following languages:
536+
/// C, C#, C++, Go, Java, JavaScript, Kotlin, Nim, PHP, Rust,
537+
/// Scala, Swift, Crystal as well as Erlang's and Elixir's rem operator.
538+
///
539+
/// Returns division of the inputs as a `Result`: If the given divisor equals
540+
/// `0`, this function returns an `Error`.
541+
///
542+
/// ## Examples
543+
///
544+
/// ```gleam
545+
/// > int.remainder(3, 2)
546+
/// Ok(1)
547+
///
548+
/// > int.remainder(1, 0)
549+
/// Error(Nil)
550+
///
551+
/// > int.remainder(10, -1)
552+
/// Ok(0)
553+
///
554+
/// > int.remainder(13, by: 3)
555+
/// Ok(1)
556+
///
557+
/// > int.remainder(-13, by: 3)
558+
/// Ok(-1)
559+
///
560+
/// > int.remainder(13, by: -3)
561+
/// Ok(1)
562+
///
563+
/// > int.remainder(-13, by: -3)
564+
/// Ok(-1)
565+
/// ```
566+
///
567+
pub fn remainder(dividend: Int, by divisor: Int) -> Result(Int, Nil) {
568+
case divisor {
569+
0 -> Error(Nil)
570+
divisor -> Ok(dividend % divisor)
571+
}
572+
}
573+
574+
/// Computes the modulo of an integer division of inputs as a `Result`.
575+
///
576+
/// This functions mimicks modulo operation on following languages:
577+
/// Haskell, Lua, Python, Ruby, as well as Elixir's Integer.mod().
578+
///
579+
/// Returns division of the inputs as a `Result`: If the given divisor equals
580+
/// `0`, this function returns an `Error`.
581+
///
582+
/// ## Examples
583+
///
584+
/// ```gleam
585+
/// > int.modulo(3, 2)
586+
/// Ok(1)
587+
///
588+
/// > int.modulo(1, 0)
589+
/// Error(Nil)
590+
///
591+
/// > int.modulo(10, -1)
592+
/// Ok(0)
593+
///
594+
/// > int.modulo(13, by: 3)
595+
/// Ok(1)
596+
///
597+
/// > int.modulo(-13, by: 3)
598+
/// Ok(2)
599+
///
600+
/// > int.modulo(13, by: -3)
601+
/// Ok(-2)
602+
///
603+
/// > int.modulo(-13, by: -3)
604+
/// Ok(-1)
605+
/// ```
606+
///
607+
pub fn modulo(dividend: Int, by divisor: Int) -> Result(Int, Nil) {
608+
case divisor {
609+
0 -> Error(Nil)
610+
_ -> {
611+
let remainder = dividend % divisor
612+
case remainder * divisor < 0 {
613+
True -> Ok(remainder + divisor)
614+
False -> Ok(remainder)
615+
}
616+
}
521617
}
522618
}
523619

test/gleam/int_test.gleam

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,13 +415,67 @@ pub fn random_test() {
415415
pub fn divide_test() {
416416
int.divide(1, 1)
417417
|> should.equal(Ok(1))
418+
418419
int.divide(1, 0)
419420
|> should.equal(Error(Nil))
420421

421422
int.divide(0, by: 1)
422423
|> should.equal(Ok(0))
424+
423425
int.divide(1, by: 0)
424426
|> should.equal(Error(Nil))
427+
428+
int.divide(5, by: 2)
429+
|> should.equal(Ok(2))
430+
431+
int.divide(-99, by: 2)
432+
|> should.equal(Ok(-49))
433+
}
434+
435+
pub fn remainder_test() {
436+
int.remainder(3, 2)
437+
|> should.equal(Ok(1))
438+
439+
int.remainder(1, 0)
440+
|> should.equal(Error(Nil))
441+
442+
int.remainder(10, -1)
443+
|> should.equal(Ok(0))
444+
445+
int.remainder(13, by: 3)
446+
|> should.equal(Ok(1))
447+
448+
int.remainder(-13, by: 3)
449+
|> should.equal(Ok(-1))
450+
451+
int.remainder(13, by: -3)
452+
|> should.equal(Ok(1))
453+
454+
int.remainder(-13, by: -3)
455+
|> should.equal(Ok(-1))
456+
}
457+
458+
pub fn modulo_test() {
459+
int.modulo(3, 2)
460+
|> should.equal(Ok(1))
461+
462+
int.modulo(1, 0)
463+
|> should.equal(Error(Nil))
464+
465+
int.modulo(10, -1)
466+
|> should.equal(Ok(0))
467+
468+
int.modulo(13, by: 3)
469+
|> should.equal(Ok(1))
470+
471+
int.modulo(-13, by: 3)
472+
|> should.equal(Ok(2))
473+
474+
int.modulo(13, by: -3)
475+
|> should.equal(Ok(-2))
476+
477+
int.modulo(-13, by: -3)
478+
|> should.equal(Ok(-1))
425479
}
426480

427481
pub fn floor_divide_test() {

0 commit comments

Comments
 (0)