From 331f20e03a695503096f33c2df798d7da1400e70 Mon Sep 17 00:00:00 2001 From: Mikko Ahlroth Date: Sun, 18 Feb 2024 20:13:50 +0200 Subject: [PATCH] Fix power to result type, add tests, adjust docs --- CHANGELOG.txt | 4 + README.md | 30 +- gleam.toml | 2 +- manifest.toml | 4 +- src/ranged_int/builtin/generic.gleam | 22 +- src/ranged_int/builtin/int128.gleam | 14 +- src/ranged_int/builtin/int16.gleam | 7 +- src/ranged_int/builtin/int32.gleam | 7 +- src/ranged_int/builtin/int64.gleam | 14 +- src/ranged_int/builtin/int8.gleam | 7 +- src/ranged_int/builtin/uint.gleam | 6 +- src/ranged_int/builtin/uint128.gleam | 14 +- src/ranged_int/builtin/uint16.gleam | 7 +- src/ranged_int/builtin/uint32.gleam | 7 +- src/ranged_int/builtin/uint64.gleam | 14 +- src/ranged_int/builtin/uint8.gleam | 7 +- src/ranged_int/interface.gleam | 20 +- src/ranged_int/utils.gleam | 20 +- test/builtin/builtin_test.gleam | 326 ------------------ test/builtin/generic_test.gleam | 124 ++++++- test/builtin/int128_test.gleam | 110 +++++- test/builtin/int16_test.gleam | 95 ++++++ test/builtin/int32_test.gleam | 95 ++++++ test/builtin/int64_test.gleam | 104 ++++++ test/builtin/int8_test.gleam | 95 ++++++ test/builtin/uint128_test.gleam | 114 +++++++ test/builtin/uint16_test.gleam | 88 ++++- test/builtin/uint32_test.gleam | 90 +++++ test/builtin/uint64_test.gleam | 108 ++++++ test/builtin/uint8_test.gleam | 93 ++++- test/overflow_test.gleam | 484 +++++++++++++++++++++++++++ test/readme/mvp2.gleam | 4 + test/readme/mvp2_test.gleam | 7 +- test/readme/mvp_test.gleam | 2 +- test/testgen/main.cc | 36 ++ 35 files changed, 1734 insertions(+), 447 deletions(-) create mode 100644 CHANGELOG.txt delete mode 100644 test/builtin/builtin_test.gleam create mode 100644 test/builtin/int16_test.gleam create mode 100644 test/builtin/int32_test.gleam create mode 100644 test/builtin/int64_test.gleam create mode 100644 test/builtin/int8_test.gleam create mode 100644 test/builtin/uint128_test.gleam create mode 100644 test/builtin/uint32_test.gleam create mode 100644 test/builtin/uint64_test.gleam create mode 100644 test/overflow_test.gleam create mode 100644 test/testgen/main.cc diff --git a/CHANGELOG.txt b/CHANGELOG.txt new file mode 100644 index 0000000..8b8e815 --- /dev/null +++ b/CHANGELOG.txt @@ -0,0 +1,4 @@ +1.0.0 +----- + +* Initial release. diff --git a/README.md b/README.md index 5af6742..28ef31e 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ latter will return an unranged big integer. An example with `ranged_int/builtin/ where these functions are wrapped: ```gleam -let assert Ok(a) = uint8.from_bigint(bigi.from_int(120)) +let assert Ok(a) = uint8.from_int(120) let b = bigi.from_int(160) uint8.overflow(uint8.add(a, b)) // Uint8(24) uint8.eject(uint8.add(a, b)) // BigInt(280) @@ -51,12 +51,21 @@ uint8.eject(uint8.add(a, b)) // BigInt(280) The overflow algorithm is based on how unsigned integer overflow is done in the C language. In C it is not defined for signed integers, but this library defines it for all -ranged integers. In a nutshell: +ranged integers, using 2's complement for the builtin signed integers. The formula for +calculating the overflowed result is -1. Shift the range into a positive range, starting from 0. -1. Shift the value to overflow the same amount. -1. Do unsigned integer overflow or underflow. -1. Shift the answer back to the original range. +``` +(value - min) % amount_of_values + min +``` + +where `min` is the minimum allowed value in the range, `amount_of_values` is the total +amount of allowed values, and `%` is the modulo (not remainder) operation. + +In effect, for types that represent unsigned integers or 2's complement signed integers, +this is equivalent to truncating the value to the amount of bits that defines the range. + +It's left up to the user to decide when overflowing makes sense, and to call `overflow` +when appropriate. ## The builtin types @@ -173,7 +182,7 @@ pub fn mvp_add_test() { let assert Ok(a) = interface.from_bigint(bigi.from_int(2007), mvp.iface) let b = bigi.from_int(9) let c = interface.math_op(a, b, mvp.iface, bigi.add) - should.equal(c, Error(utils.DidOverflow(bigi.from_int(1)))) + should.equal(c, Error(utils.WouldOverflow(bigi.from_int(1)))) } ``` @@ -229,7 +238,12 @@ pub fn mvp2_add_test() { let assert Ok(a) = mvp2.from_bigint(bigi.from_int(2007)) let b = bigi.from_int(9) let c = mvp2.add(a, b) - should.equal(c, Error(utils.DidOverflow(bigi.from_int(1)))) + should.equal(c, Error(utils.WouldOverflow(bigi.from_int(1)))) + should.equal( + mvp2.overflow(c) + |> mvp2.to_bigint(), + bigi.from_int(2007), + ) } ``` diff --git a/gleam.toml b/gleam.toml index 8a4422b..1759752 100644 --- a/gleam.toml +++ b/gleam.toml @@ -14,7 +14,7 @@ repository = { type = "gitlab", user = "Nicd", repo = "ranged_int" } [dependencies] gleam_stdlib = "~> 0.34 or ~> 1.0" -bigi = "~> 1.0" +bigi = "~> 2.0" [dev-dependencies] gleeunit = "~> 1.0" diff --git a/manifest.toml b/manifest.toml index 8c2b769..6d91ac7 100644 --- a/manifest.toml +++ b/manifest.toml @@ -2,12 +2,12 @@ # You typically do not need to edit this file packages = [ - { name = "bigi", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "bigi", source = "hex", outer_checksum = "CB2766365C3DA3C651AD55F6A6389A586FA6B2D66B80BE0DA168EEBD0EE1A43D" }, + { name = "bigi", version = "2.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "bigi", source = "hex", outer_checksum = "E7841470F475D20B2EC2D4CB1167A8893AE943B6541809DCAB9D95FB471FDCDB" }, { name = "gleam_stdlib", version = "0.34.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "1FB8454D2991E9B4C0C804544D8A9AD0F6184725E20D63C3155F0AEB4230B016" }, { name = "gleeunit", version = "1.0.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "D364C87AFEB26BDB4FB8A5ABDE67D635DC9FA52D6AB68416044C35B096C6882D" }, ] [requirements] -bigi = { version = "~> 1.0"} +bigi = { version = "~> 2.0" } gleam_stdlib = { version = "~> 0.34 or ~> 1.0" } gleeunit = { version = "~> 1.0" } diff --git a/src/ranged_int/builtin/generic.gleam b/src/ranged_int/builtin/generic.gleam index 9a1c15c..19c424b 100644 --- a/src/ranged_int/builtin/generic.gleam +++ b/src/ranged_int/builtin/generic.gleam @@ -4,12 +4,10 @@ //// compile time. Generic ranged integers are less type safe and have lower //// performance. It's always suggested to use one of the builtin types or to //// create your own type when you can. -//// -//// Any two-operand math operations will use the range of the first operand as -//// the range of the output value. import bigi.{type BigInt} import ranged_int/interface.{type Interface, type Overflowable, Interface} +import ranged_int/builtin/uint /// The interface of a generic ranged integer. pub opaque type GenericInterface(overflow_mode) { @@ -79,16 +77,30 @@ pub fn divide(a: RangedInt(overflow_mode), b: BigInt) { interface.math_op(a, b, a.interface(), bigi.divide) } +pub fn divide_no_zero(a: RangedInt(overflow_mode), b: BigInt) { + interface.fallible_op(a, b, a.interface(), bigi.divide_no_zero) +} + pub fn modulo(a: RangedInt(overflow_mode), b: BigInt) { interface.math_op(a, b, a.interface(), bigi.modulo) } +pub fn modulo_no_zero(a: RangedInt(overflow_mode), b: BigInt) { + interface.fallible_op(a, b, a.interface(), bigi.modulo_no_zero) +} + pub fn remainder(a: RangedInt(overflow_mode), b: BigInt) { interface.math_op(a, b, a.interface(), bigi.remainder) } -pub fn power(a: RangedInt(overflow_mode), b: BigInt) { - interface.math_op(a, b, a.interface(), bigi.power) +pub fn remainder_no_zero(a: RangedInt(overflow_mode), b: BigInt) { + interface.fallible_op(a, b, a.interface(), bigi.remainder_no_zero) +} + +pub fn power(a: RangedInt(overflow_mode), b: uint.Uint) { + let assert Ok(result) = + interface.fallible_op(a, uint.to_bigint(b), a.interface(), bigi.power) + result } pub fn overflow( diff --git a/src/ranged_int/builtin/int128.gleam b/src/ranged_int/builtin/int128.gleam index ac90d8c..c8f723a 100644 --- a/src/ranged_int/builtin/int128.gleam +++ b/src/ranged_int/builtin/int128.gleam @@ -1,5 +1,6 @@ import bigi.{type BigInt} import ranged_int/interface.{type Interface, Interface} +import ranged_int/builtin/uint pub opaque type Int128 { Int128(data: BigInt) @@ -63,8 +64,10 @@ pub fn remainder_no_zero(a: Int128, b: BigInt) { interface.fallible_op(a, b, iface, bigi.remainder_no_zero) } -pub fn power(a: Int128, b: BigInt) { - interface.math_op(a, b, iface, bigi.power) +pub fn power(a: Int128, b: uint.Uint) { + let assert Ok(result) = + interface.fallible_op(a, uint.to_bigint(b), iface, bigi.power) + result } pub fn overflow(op: interface.OpResult(Int128)) { @@ -76,11 +79,8 @@ pub fn eject(op: interface.OpResult(Int128)) { } fn limits() { - let min = - bigi.multiply( - bigi.power(bigi.from_int(2), bigi.from_int(127)), - bigi.from_int(-1), - ) + let assert Ok(b127) = bigi.power(bigi.from_int(2), bigi.from_int(127)) + let min = bigi.multiply(b127, bigi.from_int(-1)) let max = bigi.subtract(bigi.absolute(min), bigi.from_int(1)) interface.overflowable_limits(min, max) diff --git a/src/ranged_int/builtin/int16.gleam b/src/ranged_int/builtin/int16.gleam index 1ad41d4..d3f445f 100644 --- a/src/ranged_int/builtin/int16.gleam +++ b/src/ranged_int/builtin/int16.gleam @@ -1,5 +1,6 @@ import bigi.{type BigInt} import ranged_int/interface.{type Interface, Interface} +import ranged_int/builtin/uint const max_limit = 32_767 @@ -79,8 +80,10 @@ pub fn remainder_no_zero(a: Int16, b: BigInt) { interface.fallible_op(a, b, iface, bigi.remainder_no_zero) } -pub fn power(a: Int16, b: BigInt) { - interface.math_op(a, b, iface, bigi.power) +pub fn power(a: Int16, b: uint.Uint) { + let assert Ok(result) = + interface.fallible_op(a, uint.to_bigint(b), iface, bigi.power) + result } pub fn overflow(op: interface.OpResult(Int16)) { diff --git a/src/ranged_int/builtin/int32.gleam b/src/ranged_int/builtin/int32.gleam index 7817033..2c12edc 100644 --- a/src/ranged_int/builtin/int32.gleam +++ b/src/ranged_int/builtin/int32.gleam @@ -1,5 +1,6 @@ import bigi.{type BigInt} import ranged_int/interface.{type Interface, Interface} +import ranged_int/builtin/uint const max_limit = 2_147_483_647 @@ -79,8 +80,10 @@ pub fn remainder_no_zero(a: Int32, b: BigInt) { interface.fallible_op(a, b, iface, bigi.remainder_no_zero) } -pub fn power(a: Int32, b: BigInt) { - interface.math_op(a, b, iface, bigi.power) +pub fn power(a: Int32, b: uint.Uint) { + let assert Ok(result) = + interface.fallible_op(a, uint.to_bigint(b), iface, bigi.power) + result } pub fn overflow(op: interface.OpResult(Int32)) { diff --git a/src/ranged_int/builtin/int64.gleam b/src/ranged_int/builtin/int64.gleam index 4de6bec..01027e5 100644 --- a/src/ranged_int/builtin/int64.gleam +++ b/src/ranged_int/builtin/int64.gleam @@ -1,5 +1,6 @@ import bigi.{type BigInt} import ranged_int/interface.{type Interface, Interface} +import ranged_int/builtin/uint pub opaque type Int64 { Int64(data: BigInt) @@ -63,8 +64,10 @@ pub fn remainder_no_zero(a: Int64, b: BigInt) { interface.fallible_op(a, b, iface, bigi.remainder_no_zero) } -pub fn power(a: Int64, b: BigInt) { - interface.math_op(a, b, iface, bigi.power) +pub fn power(a: Int64, b: uint.Uint) { + let assert Ok(result) = + interface.fallible_op(a, uint.to_bigint(b), iface, bigi.power) + result } pub fn overflow(op: interface.OpResult(Int64)) { @@ -76,11 +79,8 @@ pub fn eject(op: interface.OpResult(Int64)) { } fn limits() { - let min = - bigi.multiply( - bigi.power(bigi.from_int(2), bigi.from_int(63)), - bigi.from_int(-1), - ) + let assert Ok(b63) = bigi.power(bigi.from_int(2), bigi.from_int(63)) + let min = bigi.multiply(b63, bigi.from_int(-1)) let max = bigi.subtract(bigi.absolute(min), bigi.from_int(1)) interface.overflowable_limits(min, max) diff --git a/src/ranged_int/builtin/int8.gleam b/src/ranged_int/builtin/int8.gleam index b477431..d73ae63 100644 --- a/src/ranged_int/builtin/int8.gleam +++ b/src/ranged_int/builtin/int8.gleam @@ -1,5 +1,6 @@ import bigi.{type BigInt} import ranged_int/interface.{type Interface, Interface} +import ranged_int/builtin/uint const max_limit = 127 @@ -79,8 +80,10 @@ pub fn remainder_no_zero(a: Int8, b: BigInt) { interface.fallible_op(a, b, iface, bigi.remainder_no_zero) } -pub fn power(a: Int8, b: BigInt) { - interface.math_op(a, b, iface, bigi.power) +pub fn power(a: Int8, b: uint.Uint) { + let assert Ok(result) = + interface.fallible_op(a, uint.to_bigint(b), iface, bigi.power) + result } pub fn overflow(op: interface.OpResult(Int8)) { diff --git a/src/ranged_int/builtin/uint.gleam b/src/ranged_int/builtin/uint.gleam index b466189..1dc61e5 100644 --- a/src/ranged_int/builtin/uint.gleam +++ b/src/ranged_int/builtin/uint.gleam @@ -51,8 +51,10 @@ pub fn remainder(a: Uint, b: BigInt) { interface.math_op(a, b, iface, bigi.remainder) } -pub fn power(a: Uint, b: BigInt) { - interface.math_op(a, b, iface, bigi.power) +pub fn power(a: Uint, b: Uint) { + let assert Ok(result) = + interface.fallible_op(a, to_bigint(b), iface, bigi.power) + result } pub fn eject(op: interface.OpResult(Uint)) { diff --git a/src/ranged_int/builtin/uint128.gleam b/src/ranged_int/builtin/uint128.gleam index 8cc7e8f..baa0162 100644 --- a/src/ranged_int/builtin/uint128.gleam +++ b/src/ranged_int/builtin/uint128.gleam @@ -1,5 +1,6 @@ import bigi.{type BigInt} import ranged_int/interface.{type Interface, Interface} +import ranged_int/builtin/uint pub opaque type Uint128 { Uint128(data: BigInt) @@ -59,8 +60,10 @@ pub fn remainder_no_zero(a: Uint128, b: BigInt) { interface.fallible_op(a, b, iface, bigi.remainder_no_zero) } -pub fn power(a: Uint128, b: BigInt) { - interface.math_op(a, b, iface, bigi.power) +pub fn power(a: Uint128, b: uint.Uint) { + let assert Ok(result) = + interface.fallible_op(a, uint.to_bigint(b), iface, bigi.power) + result } pub fn overflow(op: interface.OpResult(Uint128)) { @@ -73,11 +76,8 @@ pub fn eject(op: interface.OpResult(Uint128)) { fn limits() { let min = bigi.zero() - let max = - bigi.subtract( - bigi.power(bigi.from_int(2), bigi.from_int(128)), - bigi.from_int(1), - ) + let assert Ok(b128) = bigi.power(bigi.from_int(2), bigi.from_int(128)) + let max = bigi.subtract(b128, bigi.from_int(1)) interface.overflowable_limits(min, max) } diff --git a/src/ranged_int/builtin/uint16.gleam b/src/ranged_int/builtin/uint16.gleam index ba05aae..a43b546 100644 --- a/src/ranged_int/builtin/uint16.gleam +++ b/src/ranged_int/builtin/uint16.gleam @@ -1,5 +1,6 @@ import bigi.{type BigInt} import ranged_int/interface.{type Interface, Interface} +import ranged_int/builtin/uint const max_limit = 65_535 @@ -75,8 +76,10 @@ pub fn remainder_no_zero(a: Uint16, b: BigInt) { interface.fallible_op(a, b, iface, bigi.remainder_no_zero) } -pub fn power(a: Uint16, b: BigInt) { - interface.math_op(a, b, iface, bigi.power) +pub fn power(a: Uint16, b: uint.Uint) { + let assert Ok(result) = + interface.fallible_op(a, uint.to_bigint(b), iface, bigi.power) + result } pub fn overflow(op: interface.OpResult(Uint16)) { diff --git a/src/ranged_int/builtin/uint32.gleam b/src/ranged_int/builtin/uint32.gleam index c5bd9b1..fb6b6bb 100644 --- a/src/ranged_int/builtin/uint32.gleam +++ b/src/ranged_int/builtin/uint32.gleam @@ -1,5 +1,6 @@ import bigi.{type BigInt} import ranged_int/interface.{type Interface, Interface} +import ranged_int/builtin/uint pub const max_limit = 4_294_967_295 @@ -75,8 +76,10 @@ pub fn remainder_no_zero(a: Uint32, b: BigInt) { interface.fallible_op(a, b, iface, bigi.remainder_no_zero) } -pub fn power(a: Uint32, b: BigInt) { - interface.math_op(a, b, iface, bigi.power) +pub fn power(a: Uint32, b: uint.Uint) { + let assert Ok(result) = + interface.fallible_op(a, uint.to_bigint(b), iface, bigi.power) + result } pub fn overflow(op: interface.OpResult(Uint32)) { diff --git a/src/ranged_int/builtin/uint64.gleam b/src/ranged_int/builtin/uint64.gleam index e568786..f428ed8 100644 --- a/src/ranged_int/builtin/uint64.gleam +++ b/src/ranged_int/builtin/uint64.gleam @@ -1,5 +1,6 @@ import bigi.{type BigInt} import ranged_int/interface.{type Interface, Interface} +import ranged_int/builtin/uint pub opaque type Uint64 { Uint64(data: BigInt) @@ -59,8 +60,10 @@ pub fn remainder_no_zero(a: Uint64, b: BigInt) { interface.fallible_op(a, b, iface, bigi.remainder_no_zero) } -pub fn power(a: Uint64, b: BigInt) { - interface.math_op(a, b, iface, bigi.power) +pub fn power(a: Uint64, b: uint.Uint) { + let assert Ok(result) = + interface.fallible_op(a, uint.to_bigint(b), iface, bigi.power) + result } pub fn overflow(op: interface.OpResult(Uint64)) { @@ -73,11 +76,8 @@ pub fn eject(op: interface.OpResult(Uint64)) { fn limits() { let min = bigi.zero() - let max = - bigi.subtract( - bigi.power(bigi.from_int(2), bigi.from_int(64)), - bigi.from_int(1), - ) + let assert Ok(b64) = bigi.power(bigi.from_int(2), bigi.from_int(64)) + let max = bigi.subtract(b64, bigi.from_int(1)) interface.overflowable_limits(min, max) } diff --git a/src/ranged_int/builtin/uint8.gleam b/src/ranged_int/builtin/uint8.gleam index 543df86..46b604f 100644 --- a/src/ranged_int/builtin/uint8.gleam +++ b/src/ranged_int/builtin/uint8.gleam @@ -1,5 +1,6 @@ import bigi.{type BigInt} import ranged_int/interface.{type Interface, Interface} +import ranged_int/builtin/uint const max_limit = 255 @@ -75,8 +76,10 @@ pub fn remainder_no_zero(a: Uint8, b: BigInt) { interface.fallible_op(a, b, iface, bigi.remainder_no_zero) } -pub fn power(a: Uint8, b: BigInt) { - interface.math_op(a, b, iface, bigi.power) +pub fn power(a: Uint8, b: uint.Uint) { + let assert Ok(result) = + interface.fallible_op(a, uint.to_bigint(b), iface, bigi.power) + result } pub fn overflow(op: interface.OpResult(Uint8)) { diff --git a/src/ranged_int/interface.gleam b/src/ranged_int/interface.gleam index 3744fb2..4898103 100644 --- a/src/ranged_int/interface.gleam +++ b/src/ranged_int/interface.gleam @@ -85,8 +85,8 @@ pub fn from_bigint( let limits = interface.limits() case limit.check_limits(value, min: limits.min, max: limits.max) { NoOverflow -> Ok(interface.from_bigint_unsafe(value)) - Overflow(amount) -> Error(utils.DidOverflow(amount)) - Underflow(amount) -> Error(utils.DidUnderflow(amount)) + Overflow(amount) -> Error(utils.WouldOverflow(amount)) + Underflow(amount) -> Error(utils.WouldUnderflow(amount)) } } @@ -111,8 +111,8 @@ pub fn math_op( let result = op(int1, int2) case limit.check_limits(result, min: limits.min, max: limits.max) { NoOverflow -> Ok(interface.from_bigint_unsafe(result)) - Overflow(amount) -> Error(utils.DidOverflow(amount)) - Underflow(amount) -> Error(utils.DidUnderflow(amount)) + Overflow(amount) -> Error(utils.WouldOverflow(amount)) + Underflow(amount) -> Error(utils.WouldUnderflow(amount)) } } @@ -127,8 +127,8 @@ pub fn math_op_unary( let result = op(int) case limit.check_limits(result, min: limits.min, max: limits.max) { NoOverflow -> Ok(interface.from_bigint_unsafe(result)) - Overflow(amount) -> Error(utils.DidOverflow(amount)) - Underflow(amount) -> Error(utils.DidUnderflow(amount)) + Overflow(amount) -> Error(utils.WouldOverflow(amount)) + Underflow(amount) -> Error(utils.WouldUnderflow(amount)) } } @@ -148,8 +148,8 @@ pub fn fallible_op( use result <- result.try(op(int1, int2)) Ok(case limit.check_limits(result, min: limits.min, max: limits.max) { NoOverflow -> Ok(interface.from_bigint_unsafe(result)) - Overflow(amount) -> Error(utils.DidOverflow(amount)) - Underflow(amount) -> Error(utils.DidUnderflow(amount)) + Overflow(amount) -> Error(utils.WouldOverflow(amount)) + Underflow(amount) -> Error(utils.WouldUnderflow(amount)) }) } @@ -178,9 +178,9 @@ pub fn eject(value: OpResult(a), interface: Interface(a, overflow_mode)) { Ok(new_value) -> interface.to_bigint(new_value) Error(overflow) -> { case overflow, interface.limits() { - utils.DidOverflow(amount), Limits(max: limit.Bounded(max), ..) -> + utils.WouldOverflow(amount), Limits(max: limit.Bounded(max), ..) -> bigi.add(max, amount) - utils.DidUnderflow(amount), Limits(min: limit.Bounded(min), ..) -> + utils.WouldUnderflow(amount), Limits(min: limit.Bounded(min), ..) -> bigi.subtract(min, amount) _, _ -> panic as "Overflowed but there was no corresponding limit (o_O)" } diff --git a/src/ranged_int/utils.gleam b/src/ranged_int/utils.gleam index 005f013..b5f9f1b 100644 --- a/src/ranged_int/utils.gleam +++ b/src/ranged_int/utils.gleam @@ -4,9 +4,9 @@ import bigi.{type BigInt} /// range. pub type Overflow { /// The result was this much higher than the maximum allowed value. - DidOverflow(BigInt) + WouldOverflow(BigInt) /// The result was this much lower than the minimum allowed value. - DidUnderflow(BigInt) + WouldUnderflow(BigInt) } /// Calculate the overflowed value given an overflow result and the limits. @@ -15,15 +15,13 @@ pub fn overflow(overflow: Overflow, min min: BigInt, max max: BigInt) -> BigInt bigi.subtract(max, min) |> bigi.add(bigi.from_int(1)) - let min_adjustment_factor = bigi.multiply(min, bigi.from_int(-1)) - let adjusted_max = bigi.add(max, min_adjustment_factor) - let adjusted_min = bigi.zero() - - let overflowed_value = case overflow { - DidOverflow(amount) -> bigi.add(adjusted_max, amount) - DidUnderflow(amount) -> bigi.subtract(adjusted_min, amount) + let actual = case overflow { + WouldOverflow(amount) -> bigi.add(max, amount) + WouldUnderflow(amount) -> bigi.subtract(min, amount) } - let overflow_result = bigi.modulo(overflowed_value, total_values) - bigi.subtract(overflow_result, min_adjustment_factor) + actual + |> bigi.subtract(min) + |> bigi.modulo(total_values) + |> bigi.add(min) } diff --git a/test/builtin/builtin_test.gleam b/test/builtin/builtin_test.gleam deleted file mode 100644 index 2d350b8..0000000 --- a/test/builtin/builtin_test.gleam +++ /dev/null @@ -1,326 +0,0 @@ -import gleam/function -import gleeunit/should -import bigi.{type BigInt} -import ranged_int/interface -import ranged_int/builtin/uint8 -import ranged_int/builtin/uint16 -import ranged_int/builtin/uint32 -import ranged_int/builtin/uint64 -import ranged_int/builtin/uint128 -import ranged_int/builtin/int8 -import ranged_int/builtin/int16 -import ranged_int/builtin/int32 -import ranged_int/builtin/int64 -import ranged_int/builtin/int128 - -pub fn absolute_test() { - let input = bigi.from_int(-19) - let expected = bigi.from_int(19) - - unary(int8.from_bigint, int8.absolute)(input, expected) - unary(int16.from_bigint, int16.absolute)(input, expected) - unary(int32.from_bigint, int32.absolute)(input, expected) - unary(int64.from_bigint, int64.absolute)(input, expected) - unary(int128.from_bigint, int128.absolute)(input, expected) -} - -pub fn add_test() { - let input1 = bigi.from_int(1) - let input2 = bigi.from_int(12) - let expected = bigi.from_int(13) - - binary(int8.from_bigint, int8.add)( - input1, - input2, - should.equal(_, int8.from_bigint(expected)), - ) - binary(int16.from_bigint, int16.add)( - input1, - input2, - should.equal(_, int16.from_bigint(expected)), - ) - binary(int32.from_bigint, int32.add)( - input1, - input2, - should.equal(_, int32.from_bigint(expected)), - ) - binary(int64.from_bigint, int64.add)( - input1, - input2, - should.equal(_, int64.from_bigint(expected)), - ) - binary(int128.from_bigint, int128.add)( - input1, - input2, - should.equal(_, int128.from_bigint(expected)), - ) - binary(uint8.from_bigint, uint8.add)( - input1, - input2, - should.equal(_, uint8.from_bigint(expected)), - ) - binary(uint16.from_bigint, uint16.add)( - input1, - input2, - should.equal(_, uint16.from_bigint(expected)), - ) - binary(uint32.from_bigint, uint32.add)( - input1, - input2, - should.equal(_, uint32.from_bigint(expected)), - ) - binary(uint64.from_bigint, uint64.add)( - input1, - input2, - should.equal(_, uint64.from_bigint(expected)), - ) - binary(uint128.from_bigint, uint128.add)( - input1, - input2, - should.equal(_, uint128.from_bigint(expected)), - ) -} - -pub fn add_overflow_test() { - let input1 = bigi.zero() - let input2 = bigi.power(bigi.from_int(2), bigi.from_int(1024)) - - binary(int8.from_bigint, int8.add)(input1, input2, should.be_error) - binary(int16.from_bigint, int16.add)(input1, input2, should.be_error) - binary(int32.from_bigint, int32.add)(input1, input2, should.be_error) - binary(int64.from_bigint, int64.add)(input1, input2, should.be_error) - binary(int128.from_bigint, int128.add)(input1, input2, should.be_error) - binary(uint8.from_bigint, uint8.add)(input1, input2, should.be_error) - binary(uint16.from_bigint, uint16.add)(input1, input2, should.be_error) - binary(uint32.from_bigint, uint32.add)(input1, input2, should.be_error) - binary(uint64.from_bigint, uint64.add)(input1, input2, should.be_error) - binary(uint128.from_bigint, uint128.add)(input1, input2, should.be_error) -} - -pub fn add_overflow_eject_test() { - binary(int8.from_bigint, int8.add)( - bigi.from_int(-127), - bigi.from_int(1000), - function.compose(int8.eject, should.equal(_, bigi.from_int(873))), - ) - - binary(int16.from_bigint, int16.add)( - bigi.from_int(32_767), - bigi.from_int(1), - function.compose(int16.eject, should.equal(_, bigi.from_int(32_768))), - ) - - binary(int32.from_bigint, int32.add)( - bigi.from_int(2_000_000_000), - bigi.from_int(3_000_000_000), - function.compose(int32.eject, should.equal(_, bigi.from_int(5_000_000_000))), - ) - - binary(int64.from_bigint, int64.add)( - bigi.subtract( - bigi.power(bigi.from_int(2), bigi.from_int(63)), - bigi.from_int(1), - ), - bigi.from_int(1), - function.compose(int64.eject, should.equal(_, bigi.power( - bigi.from_int(2), - bigi.from_int(63), - ))), - ) - - binary(int128.from_bigint, int128.add)( - bigi.subtract( - bigi.power(bigi.from_int(2), bigi.from_int(127)), - bigi.from_int(1), - ), - bigi.from_int(1), - function.compose(int128.eject, should.equal(_, bigi.power( - bigi.from_int(2), - bigi.from_int(127), - ))), - ) - - binary(uint8.from_bigint, uint8.add)( - bigi.from_int(0), - bigi.from_int(1000), - function.compose(uint8.eject, should.equal(_, bigi.from_int(1000))), - ) - - binary(uint16.from_bigint, uint16.add)( - bigi.from_int(32_768), - bigi.from_int(32_768), - function.compose(uint16.eject, should.equal(_, bigi.from_int(65_536))), - ) - - binary(uint32.from_bigint, uint32.add)( - bigi.from_int(2_000_000_000), - bigi.from_int(3_000_000_000), - function.compose(uint32.eject, should.equal(_, bigi.from_int(5_000_000_000))), - ) - - binary(uint64.from_bigint, uint64.add)( - bigi.subtract( - bigi.power(bigi.from_int(2), bigi.from_int(64)), - bigi.from_int(1), - ), - bigi.from_int(1), - function.compose(uint64.eject, should.equal(_, bigi.power( - bigi.from_int(2), - bigi.from_int(64), - ))), - ) - - binary(uint128.from_bigint, uint128.add)( - bigi.subtract( - bigi.power(bigi.from_int(2), bigi.from_int(128)), - bigi.from_int(1), - ), - bigi.from_int(1), - function.compose(uint128.eject, should.equal(_, bigi.power( - bigi.from_int(2), - bigi.from_int(128), - ))), - ) -} - -pub fn add_overflow_wrap_test() { - overflow_wrap( - bigi.from_int(127), - bigi.from_int(1), - bigi.from_int(-128), - int8.from_bigint, - int8.add, - int8.overflow, - ) - - overflow_wrap( - bigi.from_int(-32_768), - bigi.from_int(-1), - bigi.from_int(32_767), - int16.from_bigint, - int16.add, - int16.overflow, - ) - - overflow_wrap( - bigi.from_int(2_000_000_000), - bigi.from_int(3_000_000_000), - bigi.from_int(705_032_704), - int32.from_bigint, - int32.add, - int32.overflow, - ) - - overflow_wrap( - bigi.subtract( - bigi.power(bigi.from_int(2), bigi.from_int(63)), - bigi.from_int(255), - ), - bigi.from_int(255), - bigi.multiply( - bigi.power(bigi.from_int(2), bigi.from_int(63)), - bigi.from_int(-1), - ), - int64.from_bigint, - int64.add, - int64.overflow, - ) - - overflow_wrap( - bigi.multiply( - bigi.power(bigi.from_int(2), bigi.from_int(127)), - bigi.from_int(-1), - ), - bigi.power(bigi.from_int(2), bigi.from_int(128)), - bigi.multiply( - bigi.power(bigi.from_int(2), bigi.from_int(127)), - bigi.from_int(-1), - ), - int128.from_bigint, - int128.add, - int128.overflow, - ) - - overflow_wrap( - bigi.from_int(255), - bigi.from_int(1), - bigi.zero(), - uint8.from_bigint, - uint8.add, - uint8.overflow, - ) - - overflow_wrap( - bigi.from_int(65_535), - bigi.from_int(1), - bigi.zero(), - uint16.from_bigint, - uint16.add, - uint16.overflow, - ) - - overflow_wrap( - bigi.from_int(2_000_000_000), - bigi.from_int(3_000_000_000), - bigi.from_int(705_032_704), - uint32.from_bigint, - uint32.add, - uint32.overflow, - ) - - overflow_wrap( - bigi.subtract( - bigi.power(bigi.from_int(2), bigi.from_int(64)), - bigi.from_int(1), - ), - bigi.from_int(1), - bigi.zero(), - uint64.from_bigint, - uint64.add, - uint64.overflow, - ) - - overflow_wrap( - bigi.power(bigi.from_int(2), bigi.from_int(127)), - bigi.power(bigi.from_int(2), bigi.from_int(127)), - bigi.zero(), - uint128.from_bigint, - uint128.add, - uint128.overflow, - ) -} - -fn overflow_wrap( - int1: BigInt, - int2: BigInt, - expected: BigInt, - from_bigint: fn(BigInt) -> Result(a, b), - fun: fn(a, BigInt) -> interface.OpResult(a), - overflow: fn(interface.OpResult(a)) -> a, -) { - let assert Ok(expected) = from_bigint(expected) - should.equal(binary(from_bigint, fun)(int1, int2, overflow), expected) -} - -fn unary( - from_bigint: fn(BigInt) -> Result(a, b), - fun: fn(a) -> interface.OpResult(a), -) { - fn(val: BigInt, expected: BigInt) { - let assert Ok(val) = from_bigint(val) - let assert Ok(expected) = from_bigint(expected) - let res = fun(val) - should.equal(res, Ok(expected)) - } -} - -fn binary( - from_bigint: fn(BigInt) -> Result(a, b), - fun: fn(a, BigInt) -> interface.OpResult(a), -) { - fn(val1: BigInt, val2: BigInt, check: fn(interface.OpResult(a)) -> any) { - let assert Ok(val1) = from_bigint(val1) - let res = fun(val1, val2) - check(res) - } -} diff --git a/test/builtin/generic_test.gleam b/test/builtin/generic_test.gleam index 9a7e67b..4d4fab6 100644 --- a/test/builtin/generic_test.gleam +++ b/test/builtin/generic_test.gleam @@ -1,24 +1,116 @@ +import gleam/int +import gleam/float import bigi import gleeunit/should import ranged_int/builtin/generic +import ranged_int/builtin/uint + +const min = -31_495 + +const max = -13_413 + +pub fn absolute_test() { + let int = from_int(min) + should.equal( + generic.to_bigint(generic.overflow( + generic.absolute(int), + generic.get_interface(int), + )), + bigi.from_int(-22_754), + ) +} pub fn add_test() { - let assert Ok(a) = generic.from_bigint_min(bigi.zero(), min: bigi.zero()) - let b = bigi.from_int(12) - let iface = generic.get_interface(a) - let c = generic.eject(generic.add(a, b), iface) - should.equal(c, bigi.from_int(12)) + let int1 = from_int(min) + let int2 = bigi.from_int(1) + let assert Ok(result) = generic.add(int1, int2) + should.equal(generic.to_bigint(result), bigi.from_int(min + 1)) } -pub fn overflow_test() { - let assert Ok(a) = - generic.from_bigint_overflowable( - bigi.from_int(-15), - min: bigi.from_int(-284), - max: bigi.from_int(-7), - ) - let b = bigi.from_int(12) - let iface = generic.get_interface(a) - let c = generic.overflow(generic.add(a, b), iface) - should.equal(generic.to_bigint(c), bigi.from_int(-281)) +pub fn subtract_test() { + let int1 = from_int(max) + let int2 = bigi.from_int(1) + let assert Ok(result) = generic.subtract(int1, int2) + should.equal(generic.to_bigint(result), bigi.from_int(max - 1)) +} + +pub fn multiply_test() { + let int1 = from_int(max) + let int2 = bigi.from_int(2) + let assert Ok(result) = generic.multiply(int1, int2) + should.equal(generic.to_bigint(result), bigi.from_int(max * 2)) +} + +pub fn divide_test() { + let int1 = from_int(min) + let int2 = bigi.from_int(2) + let assert Ok(result) = generic.divide(int1, int2) + should.equal(generic.to_bigint(result), bigi.from_int(min / 2)) +} + +pub fn divide_no_zero_test() { + let int1 = from_int(min) + should.be_error(generic.divide_no_zero(int1, bigi.zero())) + let assert Ok(Ok(result)) = generic.divide_no_zero(int1, bigi.from_int(2)) + should.equal(generic.to_bigint(result), bigi.from_int(min / 2)) +} + +pub fn modulo_test() { + let int1 = from_int(min) + let int2 = bigi.from_int(2) + should.equal( + generic.eject(generic.modulo(int1, int2), generic.get_interface(int1)), + bigi.from_int(1), + ) +} + +pub fn modulo_no_zero_test() { + let int1 = from_int(min) + should.be_error(generic.modulo_no_zero(int1, bigi.zero())) + let assert Ok(rem) = generic.modulo_no_zero(int1, bigi.from_int(2)) + should.equal( + generic.eject(rem, generic.get_interface(int1)), + bigi.from_int(1), + ) +} + +pub fn remainder_test() { + let int1 = from_int(min) + let int2 = bigi.from_int(2) + should.equal( + generic.eject(generic.remainder(int1, int2), generic.get_interface(int1)), + bigi.from_int(-1), + ) +} + +pub fn remainder_no_zero_test() { + let int1 = from_int(min) + should.be_error(generic.remainder_no_zero(int1, bigi.zero())) + let assert Ok(rem) = generic.remainder_no_zero(int1, bigi.from_int(2)) + should.equal( + generic.eject(rem, generic.get_interface(int1)), + bigi.from_int(-1), + ) +} + +pub fn power_test() { + let int1 = from_int(max) + let assert Ok(int2) = uint.from_bigint(bigi.from_int(2)) + let assert Ok(expected) = int.power(max, 2.0) + should.equal( + generic.eject(generic.power(int1, int2), generic.get_interface(int1)), + bigi.from_int(float.truncate(expected)), + ) +} + +fn from_int(int: Int) { + let min = bigi.from_int(min) + let max = bigi.from_int(max) + + let assert Ok(int) = + int + |> bigi.from_int() + |> generic.from_bigint_overflowable(min: min, max: max) + + int } diff --git a/test/builtin/int128_test.gleam b/test/builtin/int128_test.gleam index 611b60e..4d621ba 100644 --- a/test/builtin/int128_test.gleam +++ b/test/builtin/int128_test.gleam @@ -1,9 +1,113 @@ import bigi import gleeunit/should import ranged_int/builtin/int128 +import ranged_int/builtin/uint + +pub fn absolute_test() { + let assert Ok(int) = int128.from_bigint(bigi.from_int(-1)) + should.equal(int128.absolute(int), int128.from_bigint(bigi.from_int(1))) +} pub fn add_test() { - let assert Ok(a) = int128.from_bigint(bigi.from_int(-3_190_309_415)) - let b = int128.absolute(a) - should.equal(b, int128.from_bigint(bigi.from_int(3_190_309_415))) + let assert Ok(int1) = int128.from_bigint(bigi.from_int(-1)) + let assert int2 = bigi.from_int(1) + should.equal(int128.add(int1, int2), int128.from_bigint(bigi.from_int(0))) +} + +pub fn subtract_test() { + let assert Ok(int1) = int128.from_bigint(bigi.from_int(-1)) + let assert int2 = bigi.from_int(1) + should.equal( + int128.subtract(int1, int2), + int128.from_bigint(bigi.from_int(-2)), + ) +} + +pub fn multiply_test() { + let assert Ok(int1) = int128.from_bigint(bigi.from_int(2)) + let assert int2 = bigi.from_int(2) + should.equal( + int128.multiply(int1, int2), + int128.from_bigint(bigi.from_int(4)), + ) +} + +pub fn divide_test() { + let assert Ok(int1) = int128.from_bigint(bigi.from_int(-2)) + let assert int2 = bigi.from_int(-2) + should.equal(int128.divide(int1, int2), int128.from_bigint(bigi.from_int(1))) +} + +pub fn divide_no_zero_test() { + let assert Ok(int1) = int128.from_bigint(bigi.from_int(2)) + should.be_error(int128.divide_no_zero(int1, bigi.zero())) + should.equal( + int128.divide_no_zero(int1, bigi.from_int(2)), + Ok(int128.from_bigint(bigi.from_int(1))), + ) +} + +pub fn modulo_test() { + let assert Ok(int1) = int128.from_bigint(bigi.from_int(-2)) + let assert int2 = bigi.from_int(-2) + should.equal(int128.modulo(int1, int2), int128.from_bigint(bigi.from_int(0))) +} + +pub fn modulo_no_zero_test() { + let assert Ok(int1) = int128.from_bigint(bigi.from_int(2)) + should.be_error(int128.modulo_no_zero(int1, bigi.zero())) + should.equal( + int128.modulo_no_zero(int1, bigi.from_int(2)), + Ok(int128.from_bigint(bigi.from_int(0))), + ) +} + +pub fn remainder_test() { + let assert Ok(int1) = int128.from_bigint(bigi.from_int(-2)) + let assert int2 = bigi.from_int(-2) + should.equal( + int128.remainder(int1, int2), + int128.from_bigint(bigi.from_int(0)), + ) +} + +pub fn remainder_no_zero_test() { + let assert Ok(int1) = int128.from_bigint(bigi.from_int(2)) + should.be_error(int128.remainder_no_zero(int1, bigi.zero())) + should.equal( + int128.remainder_no_zero(int1, bigi.from_int(2)), + Ok(int128.from_bigint(bigi.from_int(0))), + ) +} + +pub fn power_test() { + let assert Ok(int1) = int128.from_bigint(bigi.from_int(-2)) + let assert Ok(int2) = uint.from_bigint(bigi.from_int(2)) + should.equal(int128.power(int1, int2), int128.from_bigint(bigi.from_int(4))) +} + +pub fn overflow_test() { + let #(min, max) = minmax() + let assert Ok(int1) = int128.from_bigint(max) + let int2 = bigi.from_int(1) + let assert Ok(expected) = int128.from_bigint(min) + should.equal(int128.overflow(int128.add(int1, int2)), expected) +} + +pub fn eject_test() { + let #(_min, max) = minmax() + let assert Ok(int1) = int128.from_bigint(max) + let int2 = bigi.from_int(1) + should.equal( + int128.eject(int128.add(int1, int2)), + bigi.add(max, bigi.from_int(1)), + ) +} + +fn minmax() { + let assert Ok(b127) = bigi.power(bigi.from_int(2), bigi.from_int(127)) + let min = bigi.multiply(b127, bigi.from_int(-1)) + let max = bigi.subtract(bigi.absolute(min), bigi.from_int(1)) + + #(min, max) } diff --git a/test/builtin/int16_test.gleam b/test/builtin/int16_test.gleam new file mode 100644 index 0000000..d539700 --- /dev/null +++ b/test/builtin/int16_test.gleam @@ -0,0 +1,95 @@ +import bigi +import gleeunit/should +import ranged_int/builtin/int16 +import ranged_int/builtin/uint + +const min = -32_768 + +const max = 32_767 + +pub fn absolute_test() { + let assert Ok(int) = int16.from_int(-1) + should.equal(int16.absolute(int), int16.from_int(1)) +} + +pub fn add_test() { + let assert Ok(int1) = int16.from_int(-1) + let assert int2 = bigi.from_int(1) + should.equal(int16.add(int1, int2), int16.from_int(0)) +} + +pub fn subtract_test() { + let assert Ok(int1) = int16.from_int(-1) + let assert int2 = bigi.from_int(1) + should.equal(int16.subtract(int1, int2), int16.from_int(-2)) +} + +pub fn multiply_test() { + let assert Ok(int1) = int16.from_int(2) + let assert int2 = bigi.from_int(2) + should.equal(int16.multiply(int1, int2), int16.from_int(4)) +} + +pub fn divide_test() { + let assert Ok(int1) = int16.from_int(-2) + let assert int2 = bigi.from_int(-2) + should.equal(int16.divide(int1, int2), int16.from_int(1)) +} + +pub fn divide_no_zero_test() { + let assert Ok(int1) = int16.from_int(2) + should.be_error(int16.divide_no_zero(int1, bigi.zero())) + should.equal( + int16.divide_no_zero(int1, bigi.from_int(2)), + Ok(int16.from_int(1)), + ) +} + +pub fn modulo_test() { + let assert Ok(int1) = int16.from_int(-2) + let assert int2 = bigi.from_int(-2) + should.equal(int16.modulo(int1, int2), int16.from_int(0)) +} + +pub fn modulo_no_zero_test() { + let assert Ok(int1) = int16.from_int(2) + should.be_error(int16.modulo_no_zero(int1, bigi.zero())) + should.equal( + int16.modulo_no_zero(int1, bigi.from_int(2)), + Ok(int16.from_int(0)), + ) +} + +pub fn remainder_test() { + let assert Ok(int1) = int16.from_int(-2) + let assert int2 = bigi.from_int(-2) + should.equal(int16.remainder(int1, int2), int16.from_int(0)) +} + +pub fn remainder_no_zero_test() { + let assert Ok(int1) = int16.from_int(2) + should.be_error(int16.remainder_no_zero(int1, bigi.zero())) + should.equal( + int16.remainder_no_zero(int1, bigi.from_int(2)), + Ok(int16.from_int(0)), + ) +} + +pub fn power_test() { + let assert Ok(int1) = int16.from_int(-2) + let assert Ok(int2) = uint.from_bigint(bigi.from_int(2)) + should.equal(int16.power(int1, int2), int16.from_int(4)) +} + +pub fn overflow_test() { + let assert Ok(int1) = int16.from_int(max) + let int2 = bigi.from_int(1) + let assert Ok(expected) = int16.from_int(min) + should.equal(int16.overflow(int16.add(int1, int2)), expected) +} + +pub fn eject_test() { + let assert Ok(int1) = int16.from_int(max) + let int2 = bigi.from_int(1) + should.equal(int16.eject(int16.add(int1, int2)), bigi.from_int(max + 1)) +} diff --git a/test/builtin/int32_test.gleam b/test/builtin/int32_test.gleam new file mode 100644 index 0000000..582b219 --- /dev/null +++ b/test/builtin/int32_test.gleam @@ -0,0 +1,95 @@ +import bigi +import gleeunit/should +import ranged_int/builtin/int32 +import ranged_int/builtin/uint + +const min = -2_147_483_648 + +const max = 2_147_483_647 + +pub fn absolute_test() { + let assert Ok(int) = int32.from_int(-1) + should.equal(int32.absolute(int), int32.from_int(1)) +} + +pub fn add_test() { + let assert Ok(int1) = int32.from_int(-1) + let assert int2 = bigi.from_int(1) + should.equal(int32.add(int1, int2), int32.from_int(0)) +} + +pub fn subtract_test() { + let assert Ok(int1) = int32.from_int(-1) + let assert int2 = bigi.from_int(1) + should.equal(int32.subtract(int1, int2), int32.from_int(-2)) +} + +pub fn multiply_test() { + let assert Ok(int1) = int32.from_int(2) + let assert int2 = bigi.from_int(2) + should.equal(int32.multiply(int1, int2), int32.from_int(4)) +} + +pub fn divide_test() { + let assert Ok(int1) = int32.from_int(-2) + let assert int2 = bigi.from_int(-2) + should.equal(int32.divide(int1, int2), int32.from_int(1)) +} + +pub fn divide_no_zero_test() { + let assert Ok(int1) = int32.from_int(2) + should.be_error(int32.divide_no_zero(int1, bigi.zero())) + should.equal( + int32.divide_no_zero(int1, bigi.from_int(2)), + Ok(int32.from_int(1)), + ) +} + +pub fn modulo_test() { + let assert Ok(int1) = int32.from_int(-2) + let assert int2 = bigi.from_int(-2) + should.equal(int32.modulo(int1, int2), int32.from_int(0)) +} + +pub fn modulo_no_zero_test() { + let assert Ok(int1) = int32.from_int(2) + should.be_error(int32.modulo_no_zero(int1, bigi.zero())) + should.equal( + int32.modulo_no_zero(int1, bigi.from_int(2)), + Ok(int32.from_int(0)), + ) +} + +pub fn remainder_test() { + let assert Ok(int1) = int32.from_int(-2) + let assert int2 = bigi.from_int(-2) + should.equal(int32.remainder(int1, int2), int32.from_int(0)) +} + +pub fn remainder_no_zero_test() { + let assert Ok(int1) = int32.from_int(2) + should.be_error(int32.remainder_no_zero(int1, bigi.zero())) + should.equal( + int32.remainder_no_zero(int1, bigi.from_int(2)), + Ok(int32.from_int(0)), + ) +} + +pub fn power_test() { + let assert Ok(int1) = int32.from_int(-2) + let assert Ok(int2) = uint.from_bigint(bigi.from_int(2)) + should.equal(int32.power(int1, int2), int32.from_int(4)) +} + +pub fn overflow_test() { + let assert Ok(int1) = int32.from_int(max) + let int2 = bigi.from_int(1) + let assert Ok(expected) = int32.from_int(min) + should.equal(int32.overflow(int32.add(int1, int2)), expected) +} + +pub fn eject_test() { + let assert Ok(int1) = int32.from_int(max) + let int2 = bigi.from_int(1) + should.equal(int32.eject(int32.add(int1, int2)), bigi.from_int(max + 1)) +} diff --git a/test/builtin/int64_test.gleam b/test/builtin/int64_test.gleam new file mode 100644 index 0000000..b66967b --- /dev/null +++ b/test/builtin/int64_test.gleam @@ -0,0 +1,104 @@ +import bigi +import gleeunit/should +import ranged_int/builtin/int64 +import ranged_int/builtin/uint + +pub fn absolute_test() { + let assert Ok(int) = int64.from_bigint(bigi.from_int(-1)) + should.equal(int64.absolute(int), int64.from_bigint(bigi.from_int(1))) +} + +pub fn add_test() { + let assert Ok(int1) = int64.from_bigint(bigi.from_int(-1)) + let assert int2 = bigi.from_int(1) + should.equal(int64.add(int1, int2), int64.from_bigint(bigi.from_int(0))) +} + +pub fn subtract_test() { + let assert Ok(int1) = int64.from_bigint(bigi.from_int(-1)) + let assert int2 = bigi.from_int(1) + should.equal(int64.subtract(int1, int2), int64.from_bigint(bigi.from_int(-2))) +} + +pub fn multiply_test() { + let assert Ok(int1) = int64.from_bigint(bigi.from_int(2)) + let assert int2 = bigi.from_int(2) + should.equal(int64.multiply(int1, int2), int64.from_bigint(bigi.from_int(4))) +} + +pub fn divide_test() { + let assert Ok(int1) = int64.from_bigint(bigi.from_int(-2)) + let assert int2 = bigi.from_int(-2) + should.equal(int64.divide(int1, int2), int64.from_bigint(bigi.from_int(1))) +} + +pub fn divide_no_zero_test() { + let assert Ok(int1) = int64.from_bigint(bigi.from_int(2)) + should.be_error(int64.divide_no_zero(int1, bigi.zero())) + should.equal( + int64.divide_no_zero(int1, bigi.from_int(2)), + Ok(int64.from_bigint(bigi.from_int(1))), + ) +} + +pub fn modulo_test() { + let assert Ok(int1) = int64.from_bigint(bigi.from_int(-2)) + let assert int2 = bigi.from_int(-2) + should.equal(int64.modulo(int1, int2), int64.from_bigint(bigi.from_int(0))) +} + +pub fn modulo_no_zero_test() { + let assert Ok(int1) = int64.from_bigint(bigi.from_int(2)) + should.be_error(int64.modulo_no_zero(int1, bigi.zero())) + should.equal( + int64.modulo_no_zero(int1, bigi.from_int(2)), + Ok(int64.from_bigint(bigi.from_int(0))), + ) +} + +pub fn remainder_test() { + let assert Ok(int1) = int64.from_bigint(bigi.from_int(-2)) + let assert int2 = bigi.from_int(-2) + should.equal(int64.remainder(int1, int2), int64.from_bigint(bigi.from_int(0))) +} + +pub fn remainder_no_zero_test() { + let assert Ok(int1) = int64.from_bigint(bigi.from_int(2)) + should.be_error(int64.remainder_no_zero(int1, bigi.zero())) + should.equal( + int64.remainder_no_zero(int1, bigi.from_int(2)), + Ok(int64.from_bigint(bigi.from_int(0))), + ) +} + +pub fn power_test() { + let assert Ok(int1) = int64.from_bigint(bigi.from_int(-2)) + let assert Ok(int2) = uint.from_bigint(bigi.from_int(2)) + should.equal(int64.power(int1, int2), int64.from_bigint(bigi.from_int(4))) +} + +pub fn overflow_test() { + let #(min, max) = minmax() + let assert Ok(int1) = int64.from_bigint(max) + let int2 = bigi.from_int(1) + let assert Ok(expected) = int64.from_bigint(min) + should.equal(int64.overflow(int64.add(int1, int2)), expected) +} + +pub fn eject_test() { + let #(_min, max) = minmax() + let assert Ok(int1) = int64.from_bigint(max) + let int2 = bigi.from_int(1) + should.equal( + int64.eject(int64.add(int1, int2)), + bigi.add(max, bigi.from_int(1)), + ) +} + +fn minmax() { + let assert Ok(b63) = bigi.power(bigi.from_int(2), bigi.from_int(63)) + let min = bigi.multiply(b63, bigi.from_int(-1)) + let max = bigi.subtract(bigi.absolute(min), bigi.from_int(1)) + + #(min, max) +} diff --git a/test/builtin/int8_test.gleam b/test/builtin/int8_test.gleam new file mode 100644 index 0000000..d8607ee --- /dev/null +++ b/test/builtin/int8_test.gleam @@ -0,0 +1,95 @@ +import bigi +import gleeunit/should +import ranged_int/builtin/int8 +import ranged_int/builtin/uint + +const min = -128 + +const max = 127 + +pub fn absolute_test() { + let assert Ok(int) = int8.from_int(-1) + should.equal(int8.absolute(int), int8.from_int(1)) +} + +pub fn add_test() { + let assert Ok(int1) = int8.from_int(-1) + let assert int2 = bigi.from_int(1) + should.equal(int8.add(int1, int2), int8.from_int(0)) +} + +pub fn subtract_test() { + let assert Ok(int1) = int8.from_int(-1) + let assert int2 = bigi.from_int(1) + should.equal(int8.subtract(int1, int2), int8.from_int(-2)) +} + +pub fn multiply_test() { + let assert Ok(int1) = int8.from_int(2) + let assert int2 = bigi.from_int(2) + should.equal(int8.multiply(int1, int2), int8.from_int(4)) +} + +pub fn divide_test() { + let assert Ok(int1) = int8.from_int(-2) + let assert int2 = bigi.from_int(-2) + should.equal(int8.divide(int1, int2), int8.from_int(1)) +} + +pub fn divide_no_zero_test() { + let assert Ok(int1) = int8.from_int(2) + should.be_error(int8.divide_no_zero(int1, bigi.zero())) + should.equal( + int8.divide_no_zero(int1, bigi.from_int(2)), + Ok(int8.from_int(1)), + ) +} + +pub fn modulo_test() { + let assert Ok(int1) = int8.from_int(-2) + let assert int2 = bigi.from_int(-2) + should.equal(int8.modulo(int1, int2), int8.from_int(0)) +} + +pub fn modulo_no_zero_test() { + let assert Ok(int1) = int8.from_int(2) + should.be_error(int8.modulo_no_zero(int1, bigi.zero())) + should.equal( + int8.modulo_no_zero(int1, bigi.from_int(2)), + Ok(int8.from_int(0)), + ) +} + +pub fn remainder_test() { + let assert Ok(int1) = int8.from_int(-2) + let assert int2 = bigi.from_int(-2) + should.equal(int8.remainder(int1, int2), int8.from_int(0)) +} + +pub fn remainder_no_zero_test() { + let assert Ok(int1) = int8.from_int(2) + should.be_error(int8.remainder_no_zero(int1, bigi.zero())) + should.equal( + int8.remainder_no_zero(int1, bigi.from_int(2)), + Ok(int8.from_int(0)), + ) +} + +pub fn power_test() { + let assert Ok(int1) = int8.from_int(-2) + let assert Ok(int2) = uint.from_bigint(bigi.from_int(2)) + should.equal(int8.power(int1, int2), int8.from_int(4)) +} + +pub fn overflow_test() { + let assert Ok(int1) = int8.from_int(max) + let int2 = bigi.from_int(1) + let assert Ok(expected) = int8.from_int(min) + should.equal(int8.overflow(int8.add(int1, int2)), expected) +} + +pub fn eject_test() { + let assert Ok(int1) = int8.from_int(max) + let int2 = bigi.from_int(1) + should.equal(int8.eject(int8.add(int1, int2)), bigi.from_int(max + 1)) +} diff --git a/test/builtin/uint128_test.gleam b/test/builtin/uint128_test.gleam new file mode 100644 index 0000000..5d566ab --- /dev/null +++ b/test/builtin/uint128_test.gleam @@ -0,0 +1,114 @@ +import bigi +import gleeunit/should +import ranged_int/builtin/uint128 +import ranged_int/builtin/uint + +pub fn add_test() { + let assert Ok(int1) = uint128.from_bigint(bigi.from_int(0)) + let assert int2 = bigi.from_int(1) + should.equal(uint128.add(int1, int2), uint128.from_bigint(bigi.from_int(1))) +} + +pub fn subtract_test() { + let assert Ok(int1) = uint128.from_bigint(bigi.from_int(1)) + let assert int2 = bigi.from_int(1) + should.equal( + uint128.subtract(int1, int2), + uint128.from_bigint(bigi.from_int(0)), + ) +} + +pub fn multiply_test() { + let assert Ok(int1) = uint128.from_bigint(bigi.from_int(2)) + let assert int2 = bigi.from_int(2) + should.equal( + uint128.multiply(int1, int2), + uint128.from_bigint(bigi.from_int(4)), + ) +} + +pub fn divide_test() { + let assert Ok(int1) = uint128.from_bigint(bigi.from_int(2)) + let assert int2 = bigi.from_int(2) + should.equal( + uint128.divide(int1, int2), + uint128.from_bigint(bigi.from_int(1)), + ) +} + +pub fn divide_no_zero_test() { + let assert Ok(int1) = uint128.from_bigint(bigi.from_int(2)) + should.be_error(uint128.divide_no_zero(int1, bigi.zero())) + should.equal( + uint128.divide_no_zero(int1, bigi.from_int(2)), + Ok(uint128.from_bigint(bigi.from_int(1))), + ) +} + +pub fn modulo_test() { + let assert Ok(int1) = uint128.from_bigint(bigi.from_int(2)) + let assert int2 = bigi.from_int(2) + should.equal( + uint128.modulo(int1, int2), + uint128.from_bigint(bigi.from_int(0)), + ) +} + +pub fn modulo_no_zero_test() { + let assert Ok(int1) = uint128.from_bigint(bigi.from_int(2)) + should.be_error(uint128.modulo_no_zero(int1, bigi.zero())) + should.equal( + uint128.modulo_no_zero(int1, bigi.from_int(2)), + Ok(uint128.from_bigint(bigi.from_int(0))), + ) +} + +pub fn remainder_test() { + let assert Ok(int1) = uint128.from_bigint(bigi.from_int(2)) + let assert int2 = bigi.from_int(2) + should.equal( + uint128.remainder(int1, int2), + uint128.from_bigint(bigi.from_int(0)), + ) +} + +pub fn remainder_no_zero_test() { + let assert Ok(int1) = uint128.from_bigint(bigi.from_int(2)) + should.be_error(uint128.remainder_no_zero(int1, bigi.zero())) + should.equal( + uint128.remainder_no_zero(int1, bigi.from_int(2)), + Ok(uint128.from_bigint(bigi.from_int(0))), + ) +} + +pub fn power_test() { + let assert Ok(int1) = uint128.from_bigint(bigi.from_int(2)) + let assert Ok(int2) = uint.from_bigint(bigi.from_int(2)) + should.equal(uint128.power(int1, int2), uint128.from_bigint(bigi.from_int(4))) +} + +pub fn overflow_test() { + let #(min, max) = minmax() + let assert Ok(int1) = uint128.from_bigint(max) + let int2 = bigi.from_int(1) + let assert Ok(expected) = uint128.from_bigint(min) + should.equal(uint128.overflow(uint128.add(int1, int2)), expected) +} + +pub fn eject_test() { + let #(_min, max) = minmax() + let assert Ok(int1) = uint128.from_bigint(max) + let int2 = bigi.from_int(1) + should.equal( + uint128.eject(uint128.add(int1, int2)), + bigi.add(max, bigi.from_int(1)), + ) +} + +fn minmax() { + let assert Ok(b128) = bigi.power(bigi.from_int(2), bigi.from_int(128)) + let min = bigi.zero() + let max = bigi.subtract(b128, bigi.from_int(1)) + + #(min, max) +} diff --git a/test/builtin/uint16_test.gleam b/test/builtin/uint16_test.gleam index f643605..935011e 100644 --- a/test/builtin/uint16_test.gleam +++ b/test/builtin/uint16_test.gleam @@ -1,10 +1,90 @@ import bigi import gleeunit/should import ranged_int/builtin/uint16 +import ranged_int/builtin/uint + +const min = 0 + +const max = 65_535 pub fn add_test() { - let assert Ok(a) = uint16.from_bigint(bigi.zero()) - let b = bigi.from_int(12) - let c = uint16.eject(uint16.add(a, b)) - should.equal(c, bigi.from_int(12)) + let assert Ok(int1) = uint16.from_int(0) + let assert int2 = bigi.from_int(1) + should.equal(uint16.add(int1, int2), uint16.from_int(1)) +} + +pub fn subtract_test() { + let assert Ok(int1) = uint16.from_int(1) + let assert int2 = bigi.from_int(1) + should.equal(uint16.subtract(int1, int2), uint16.from_int(0)) +} + +pub fn multiply_test() { + let assert Ok(int1) = uint16.from_int(2) + let assert int2 = bigi.from_int(2) + should.equal(uint16.multiply(int1, int2), uint16.from_int(4)) +} + +pub fn divide_test() { + let assert Ok(int1) = uint16.from_int(2) + let assert int2 = bigi.from_int(2) + should.equal(uint16.divide(int1, int2), uint16.from_int(1)) +} + +pub fn divide_no_zero_test() { + let assert Ok(int1) = uint16.from_int(2) + should.be_error(uint16.divide_no_zero(int1, bigi.zero())) + should.equal( + uint16.divide_no_zero(int1, bigi.from_int(2)), + Ok(uint16.from_int(1)), + ) +} + +pub fn modulo_test() { + let assert Ok(int1) = uint16.from_int(2) + let assert int2 = bigi.from_int(2) + should.equal(uint16.modulo(int1, int2), uint16.from_int(0)) +} + +pub fn modulo_no_zero_test() { + let assert Ok(int1) = uint16.from_int(2) + should.be_error(uint16.modulo_no_zero(int1, bigi.zero())) + should.equal( + uint16.modulo_no_zero(int1, bigi.from_int(2)), + Ok(uint16.from_int(0)), + ) +} + +pub fn remainder_test() { + let assert Ok(int1) = uint16.from_int(2) + let assert int2 = bigi.from_int(2) + should.equal(uint16.remainder(int1, int2), uint16.from_int(0)) +} + +pub fn remainder_no_zero_test() { + let assert Ok(int1) = uint16.from_int(2) + should.be_error(uint16.remainder_no_zero(int1, bigi.zero())) + should.equal( + uint16.remainder_no_zero(int1, bigi.from_int(2)), + Ok(uint16.from_int(0)), + ) +} + +pub fn power_test() { + let assert Ok(int1) = uint16.from_int(2) + let assert Ok(int2) = uint.from_bigint(bigi.from_int(2)) + should.equal(uint16.power(int1, int2), uint16.from_int(4)) +} + +pub fn overflow_test() { + let assert Ok(int1) = uint16.from_int(max) + let int2 = bigi.from_int(1) + let assert Ok(expected) = uint16.from_int(min) + should.equal(uint16.overflow(uint16.add(int1, int2)), expected) +} + +pub fn eject_test() { + let assert Ok(int1) = uint16.from_int(max) + let int2 = bigi.from_int(1) + should.equal(uint16.eject(uint16.add(int1, int2)), bigi.from_int(max + 1)) } diff --git a/test/builtin/uint32_test.gleam b/test/builtin/uint32_test.gleam new file mode 100644 index 0000000..6d17ec6 --- /dev/null +++ b/test/builtin/uint32_test.gleam @@ -0,0 +1,90 @@ +import bigi +import gleeunit/should +import ranged_int/builtin/uint32 +import ranged_int/builtin/uint + +const min = 0 + +const max = 4_294_967_295 + +pub fn add_test() { + let assert Ok(int1) = uint32.from_int(0) + let assert int2 = bigi.from_int(1) + should.equal(uint32.add(int1, int2), uint32.from_int(1)) +} + +pub fn subtract_test() { + let assert Ok(int1) = uint32.from_int(1) + let assert int2 = bigi.from_int(1) + should.equal(uint32.subtract(int1, int2), uint32.from_int(0)) +} + +pub fn multiply_test() { + let assert Ok(int1) = uint32.from_int(2) + let assert int2 = bigi.from_int(2) + should.equal(uint32.multiply(int1, int2), uint32.from_int(4)) +} + +pub fn divide_test() { + let assert Ok(int1) = uint32.from_int(2) + let assert int2 = bigi.from_int(2) + should.equal(uint32.divide(int1, int2), uint32.from_int(1)) +} + +pub fn divide_no_zero_test() { + let assert Ok(int1) = uint32.from_int(2) + should.be_error(uint32.divide_no_zero(int1, bigi.zero())) + should.equal( + uint32.divide_no_zero(int1, bigi.from_int(2)), + Ok(uint32.from_int(1)), + ) +} + +pub fn modulo_test() { + let assert Ok(int1) = uint32.from_int(2) + let assert int2 = bigi.from_int(2) + should.equal(uint32.modulo(int1, int2), uint32.from_int(0)) +} + +pub fn modulo_no_zero_test() { + let assert Ok(int1) = uint32.from_int(2) + should.be_error(uint32.modulo_no_zero(int1, bigi.zero())) + should.equal( + uint32.modulo_no_zero(int1, bigi.from_int(2)), + Ok(uint32.from_int(0)), + ) +} + +pub fn remainder_test() { + let assert Ok(int1) = uint32.from_int(2) + let assert int2 = bigi.from_int(2) + should.equal(uint32.remainder(int1, int2), uint32.from_int(0)) +} + +pub fn remainder_no_zero_test() { + let assert Ok(int1) = uint32.from_int(2) + should.be_error(uint32.remainder_no_zero(int1, bigi.zero())) + should.equal( + uint32.remainder_no_zero(int1, bigi.from_int(2)), + Ok(uint32.from_int(0)), + ) +} + +pub fn power_test() { + let assert Ok(int1) = uint32.from_int(2) + let assert Ok(int2) = uint.from_bigint(bigi.from_int(2)) + should.equal(uint32.power(int1, int2), uint32.from_int(4)) +} + +pub fn overflow_test() { + let assert Ok(int1) = uint32.from_int(max) + let int2 = bigi.from_int(1) + let assert Ok(expected) = uint32.from_int(min) + should.equal(uint32.overflow(uint32.add(int1, int2)), expected) +} + +pub fn eject_test() { + let assert Ok(int1) = uint32.from_int(max) + let int2 = bigi.from_int(1) + should.equal(uint32.eject(uint32.add(int1, int2)), bigi.from_int(max + 1)) +} diff --git a/test/builtin/uint64_test.gleam b/test/builtin/uint64_test.gleam new file mode 100644 index 0000000..ad05ce1 --- /dev/null +++ b/test/builtin/uint64_test.gleam @@ -0,0 +1,108 @@ +import bigi +import gleeunit/should +import ranged_int/builtin/uint64 +import ranged_int/builtin/uint + +pub fn add_test() { + let assert Ok(int1) = uint64.from_bigint(bigi.from_int(0)) + let assert int2 = bigi.from_int(1) + should.equal(uint64.add(int1, int2), uint64.from_bigint(bigi.from_int(1))) +} + +pub fn subtract_test() { + let assert Ok(int1) = uint64.from_bigint(bigi.from_int(1)) + let assert int2 = bigi.from_int(1) + should.equal( + uint64.subtract(int1, int2), + uint64.from_bigint(bigi.from_int(0)), + ) +} + +pub fn multiply_test() { + let assert Ok(int1) = uint64.from_bigint(bigi.from_int(2)) + let assert int2 = bigi.from_int(2) + should.equal( + uint64.multiply(int1, int2), + uint64.from_bigint(bigi.from_int(4)), + ) +} + +pub fn divide_test() { + let assert Ok(int1) = uint64.from_bigint(bigi.from_int(2)) + let assert int2 = bigi.from_int(2) + should.equal(uint64.divide(int1, int2), uint64.from_bigint(bigi.from_int(1))) +} + +pub fn divide_no_zero_test() { + let assert Ok(int1) = uint64.from_bigint(bigi.from_int(2)) + should.be_error(uint64.divide_no_zero(int1, bigi.zero())) + should.equal( + uint64.divide_no_zero(int1, bigi.from_int(2)), + Ok(uint64.from_bigint(bigi.from_int(1))), + ) +} + +pub fn modulo_test() { + let assert Ok(int1) = uint64.from_bigint(bigi.from_int(2)) + let assert int2 = bigi.from_int(2) + should.equal(uint64.modulo(int1, int2), uint64.from_bigint(bigi.from_int(0))) +} + +pub fn modulo_no_zero_test() { + let assert Ok(int1) = uint64.from_bigint(bigi.from_int(2)) + should.be_error(uint64.modulo_no_zero(int1, bigi.zero())) + should.equal( + uint64.modulo_no_zero(int1, bigi.from_int(2)), + Ok(uint64.from_bigint(bigi.from_int(0))), + ) +} + +pub fn remainder_test() { + let assert Ok(int1) = uint64.from_bigint(bigi.from_int(2)) + let assert int2 = bigi.from_int(2) + should.equal( + uint64.remainder(int1, int2), + uint64.from_bigint(bigi.from_int(0)), + ) +} + +pub fn remainder_no_zero_test() { + let assert Ok(int1) = uint64.from_bigint(bigi.from_int(2)) + should.be_error(uint64.remainder_no_zero(int1, bigi.zero())) + should.equal( + uint64.remainder_no_zero(int1, bigi.from_int(2)), + Ok(uint64.from_bigint(bigi.from_int(0))), + ) +} + +pub fn power_test() { + let assert Ok(int1) = uint64.from_bigint(bigi.from_int(2)) + let assert Ok(int2) = uint.from_bigint(bigi.from_int(2)) + should.equal(uint64.power(int1, int2), uint64.from_bigint(bigi.from_int(4))) +} + +pub fn overflow_test() { + let #(min, max) = minmax() + let assert Ok(int1) = uint64.from_bigint(max) + let int2 = bigi.from_int(1) + let assert Ok(expected) = uint64.from_bigint(min) + should.equal(uint64.overflow(uint64.add(int1, int2)), expected) +} + +pub fn eject_test() { + let #(_min, max) = minmax() + let assert Ok(int1) = uint64.from_bigint(max) + let int2 = bigi.from_int(1) + should.equal( + uint64.eject(uint64.add(int1, int2)), + bigi.add(max, bigi.from_int(1)), + ) +} + +fn minmax() { + let assert Ok(b64) = bigi.power(bigi.from_int(2), bigi.from_int(64)) + let min = bigi.zero() + let max = bigi.subtract(b64, bigi.from_int(1)) + + #(min, max) +} diff --git a/test/builtin/uint8_test.gleam b/test/builtin/uint8_test.gleam index 08d409a..7f857b3 100644 --- a/test/builtin/uint8_test.gleam +++ b/test/builtin/uint8_test.gleam @@ -1,31 +1,90 @@ import bigi import gleeunit/should import ranged_int/builtin/uint8 +import ranged_int/builtin/uint + +const min = 0 + +const max = 255 pub fn add_test() { - let assert Ok(a) = uint8.from_bigint(bigi.zero()) - let b = bigi.from_int(12) - let c = uint8.eject(uint8.add(a, b)) - should.equal(c, bigi.from_int(12)) + let assert Ok(int1) = uint8.from_int(0) + let assert int2 = bigi.from_int(1) + should.equal(uint8.add(int1, int2), uint8.from_int(1)) +} + +pub fn subtract_test() { + let assert Ok(int1) = uint8.from_int(1) + let assert int2 = bigi.from_int(1) + should.equal(uint8.subtract(int1, int2), uint8.from_int(0)) +} + +pub fn multiply_test() { + let assert Ok(int1) = uint8.from_int(2) + let assert int2 = bigi.from_int(2) + should.equal(uint8.multiply(int1, int2), uint8.from_int(4)) } pub fn divide_test() { - let assert Ok(a) = uint8.from_int(120) - let b = bigi.from_int(12) - let assert Ok(c) = uint8.divide_no_zero(a, b) - should.equal(uint8.eject(c), bigi.from_int(10)) + let assert Ok(int1) = uint8.from_int(2) + let assert int2 = bigi.from_int(2) + should.equal(uint8.divide(int1, int2), uint8.from_int(1)) +} + +pub fn divide_no_zero_test() { + let assert Ok(int1) = uint8.from_int(2) + should.be_error(uint8.divide_no_zero(int1, bigi.zero())) + should.equal( + uint8.divide_no_zero(int1, bigi.from_int(2)), + Ok(uint8.from_int(1)), + ) +} + +pub fn modulo_test() { + let assert Ok(int1) = uint8.from_int(2) + let assert int2 = bigi.from_int(2) + should.equal(uint8.modulo(int1, int2), uint8.from_int(0)) +} + +pub fn modulo_no_zero_test() { + let assert Ok(int1) = uint8.from_int(2) + should.be_error(uint8.modulo_no_zero(int1, bigi.zero())) + should.equal( + uint8.modulo_no_zero(int1, bigi.from_int(2)), + Ok(uint8.from_int(0)), + ) +} + +pub fn remainder_test() { + let assert Ok(int1) = uint8.from_int(2) + let assert int2 = bigi.from_int(2) + should.equal(uint8.remainder(int1, int2), uint8.from_int(0)) +} + +pub fn remainder_no_zero_test() { + let assert Ok(int1) = uint8.from_int(2) + should.be_error(uint8.remainder_no_zero(int1, bigi.zero())) + should.equal( + uint8.remainder_no_zero(int1, bigi.from_int(2)), + Ok(uint8.from_int(0)), + ) +} + +pub fn power_test() { + let assert Ok(int1) = uint8.from_int(2) + let assert Ok(int2) = uint.from_bigint(bigi.from_int(2)) + should.equal(uint8.power(int1, int2), uint8.from_int(4)) } pub fn overflow_test() { - let assert Ok(a) = uint8.from_int(120) - let b = bigi.from_int(160) - let c = uint8.overflow(uint8.add(a, b)) - should.equal(uint8.to_int(c), 24) + let assert Ok(int1) = uint8.from_int(max) + let int2 = bigi.from_int(1) + let assert Ok(expected) = uint8.from_int(min) + should.equal(uint8.overflow(uint8.add(int1, int2)), expected) } -pub fn underflow_test() { - let assert Ok(a) = uint8.from_bigint(bigi.from_int(120)) - let b = bigi.from_int(160) - let c = uint8.overflow(uint8.subtract(a, b)) - should.equal(uint8.to_bigint(c), bigi.from_int(216)) +pub fn eject_test() { + let assert Ok(int1) = uint8.from_int(max) + let int2 = bigi.from_int(1) + should.equal(uint8.eject(uint8.add(int1, int2)), bigi.from_int(max + 1)) } diff --git a/test/overflow_test.gleam b/test/overflow_test.gleam new file mode 100644 index 0000000..d276459 --- /dev/null +++ b/test/overflow_test.gleam @@ -0,0 +1,484 @@ +import gleam/list +import gleeunit/should +import bigi +import ranged_int/utils + +const uint8_testset = [ + #(112, 225, 81), + #(228, 195, 167), + #(179, 142, 65), + #(232, 68, 44), + #(220, 94, 58), + #(110, 147, 1), + #(103, 196, 43), + #(204, 212, 160), + #(109, 211, 64), + #(239, 57, 40), + #(137, 146, 27), + #(213, 62, 19), + #(110, 205, 59), + #(172, 131, 47), + #(97, 190, 31), + #(137, 203, 84), + #(249, 209, 202), + #(202, 107, 53), + #(158, 159, 61), + #(204, 253, 201), + #(184, 176, 104), + #(166, 221, 131), + #(209, 226, 179), + #(231, 117, 92), + #(171, 157, 72), + #(147, 229, 120), + #(239, 104, 87), + #(221, 112, 77), + #(240, 116, 100), + #(166, 247, 157), + #(198, 98, 40), + #(192, 229, 165), + #(200, 60, 4), + #(180, 85, 9), + #(171, 222, 137), + #(210, 57, 11), + #(86, 208, 38), + #(74, 236, 54), + #(206, 241, 191), + #(164, 100, 8), + #(244, 246, 234), + #(143, 246, 133), + #(131, 142, 17), + #(118, 194, 56), + #(31, 229, 4), + #(185, 145, 74), + #(121, 181, 46), + #(195, 190, 129), + #(248, 103, 95), + #(195, 125, 64), + #(143, 228, 115), + #(99, 182, 25), + #(171, 103, 18), + #(212, 107, 63), + #(48, 252, 44), + #(135, 157, 36), + #(104, 199, 47), + #(252, 136, 132), + #(219, 194, 157), + #(182, 95, 21), + #(110, 171, 25), + #(135, 244, 123), + #(56, 213, 13), + #(7, 251, 2), + #(53, 227, 24), + #(226, 194, 164), + #(150, 197, 91), + #(116, 181, 41), + #(89, 199, 32), + #(97, 234, 75), + #(114, 212, 70), + #(219, 197, 160), + #(232, 211, 187), + #(215, 236, 195), + #(153, 143, 40), + #(144, 225, 113), + #(134, 214, 92), + #(181, 179, 104), + #(105, 187, 36), + #(12, 247, 3), + #(250, 23, 17), + #(237, 89, 70), + #(233, 224, 201), + #(210, 236, 190), + #(233, 208, 185), + #(132, 172, 48), + #(197, 244, 185), + #(194, 143, 81), + #(232, 59, 35), + #(61, 214, 19), + #(224, 105, 73), + #(208, 62, 14), + #(165, 191, 100), + #(253, 245, 242), + #(142, 201, 87), + #(220, 55, 19), + #(249, 195, 188), + #(57, 230, 31), + #(192, 250, 186), + #(248, 132, 124), +] + +const uint16_testset = [ + #(53_507, 23_855, 11_826), + #(46_924, 25_977, 7365), + #(65_063, 53_609, 53_136), + #(57_356, 40_806, 32_626), + #(57_039, 42_996, 34_499), + #(52_094, 44_757, 31_315), + #(51_826, 58_047, 44_337), + #(34_558, 36_127, 5149), + #(46_715, 50_409, 31_588), + #(47_464, 37_618, 19_546), + #(5454, 61_708, 1626), + #(58_546, 38_113, 31_123), + #(42_084, 32_688, 9236), + #(41_435, 25_026, 925), + #(30_086, 54_929, 19_479), + #(23_497, 52_484, 10_445), + #(53_057, 27_358, 14_879), + #(39_282, 27_525, 1271), + #(14_626, 63_958, 13_048), + #(22_728, 54_372, 11_564), + #(45_820, 22_584, 2868), + #(43_481, 47_301, 25_246), + #(60_280, 43_875, 38_619), + #(46_998, 64_467, 45_929), + #(64_531, 3171, 2166), + #(22_327, 45_778, 2569), + #(44_104, 57_573, 36_141), + #(53_933, 21_452, 9849), + #(28_246, 64_724, 27_434), + #(63_087, 61_533, 59_084), + #(62_571, 23_304, 20_339), + #(51_536, 21_086, 7086), + #(45_994, 54_043, 34_501), + #(20_775, 64_334, 19_573), + #(41_124, 42_995, 18_583), + #(65_170, 11_754, 11_388), + #(9262, 62_579, 6305), + #(50_708, 26_924, 12_096), + #(48_199, 55_200, 37_863), + #(60_936, 43_822, 39_222), + #(60_862, 47_890, 43_216), + #(50_526, 36_306, 21_296), + #(24_522, 48_247, 7233), + #(45_165, 28_277, 7906), + #(48_854, 42_715, 26_033), + #(18_201, 47_541, 206), + #(48_568, 25_429, 8461), + #(57_765, 30_968, 23_197), + #(56_158, 38_031, 28_653), + #(22_481, 60_589, 17_534), + #(60_561, 35_159, 30_184), + #(60_390, 46_931, 41_785), + #(42_584, 51_757, 28_805), + #(50_536, 16_272, 1272), + #(47_834, 27_980, 10_278), + #(54_982, 15_731, 5177), + #(30_296, 42_947, 7707), + #(36_689, 63_208, 34_361), + #(32_499, 62_076, 29_039), + #(65_165, 31_455, 31_084), + #(58_090, 20_060, 12_614), + #(53_436, 29_679, 17_579), + #(41_189, 44_383, 20_036), + #(36_593, 52_979, 24_036), + #(39_633, 61_750, 35_847), + #(35_551, 33_517, 3532), + #(20_920, 63_223, 18_607), + #(58_628, 40_720, 33_812), + #(18_784, 60_975, 14_223), + #(52_625, 21_870, 8959), + #(28_227, 63_187, 25_878), + #(30_310, 50_085, 14_859), + #(49_863, 42_716, 27_043), + #(62_185, 54_112, 50_761), + #(43_652, 33_395, 11_511), + #(56_274, 22_812, 13_550), + #(36_147, 49_560, 20_171), + #(57_354, 14_989, 6807), + #(54_674, 24_884, 14_022), + #(53_699, 50_088, 38_251), + #(38_902, 37_979, 11_345), + #(62_742, 64_319, 61_525), + #(54_841, 12_958, 2263), + #(7886, 63_011, 5361), + #(58_935, 21_019, 14_418), + #(47_831, 26_851, 9146), + #(45_962, 39_335, 19_761), + #(53_975, 11_981, 420), + #(26_477, 58_093, 19_034), + #(44_588, 53_937, 32_989), + #(61_810, 49_637, 45_911), + #(19_980, 62_256, 16_700), + #(64_110, 17_762, 16_336), + #(4497, 61_449, 410), + #(40_318, 38_443, 13_225), + #(62_311, 39_529, 36_304), + #(18_039, 64_797, 17_300), + #(18_179, 52_010, 4653), + #(52_719, 26_144, 13_327), + #(48_750, 32_082, 15_296), +] + +const int8_multiply_testset = [ + #(87, 66, 110), + #(47, 34, 62), + #(-12, -81, -52), + #(76, 75, 68), + #(-16, -74, -96), + #(-66, -59, 54), + #(46, 48, -96), + #(-73, -118, -90), + #(120, 8, -64), + #(68, 120, -32), + #(92, 87, 68), + #(-47, -85, -101), + #(74, 72, -48), + #(-127, -43, 85), + #(97, 108, -20), + #(37, 12, -68), + #(120, 67, 104), + #(97, 19, 51), + #(-62, -96, 64), + #(-99, -84, 124), + #(-14, -103, -94), + #(106, 66, 84), + #(-21, -49, 5), + #(40, 73, 104), + #(61, 91, -81), + #(-5, -81, -107), + #(-69, -22, -18), + #(11, 35, -127), + #(-112, -14, 32), + #(-15, -90, 70), + #(53, 3, -97), + #(126, 88, 80), + #(38, 32, -64), + #(-114, -65, -14), + #(31, 71, -103), + #(8, 35, 24), + #(34, 96, -64), + #(23, 103, 65), + #(-32, -102, -64), + #(112, 87, 16), + #(-45, -15, -93), + #(75, 15, 101), + #(-32, -106, 64), + #(11, 109, -81), + #(99, 30, -102), + #(88, 51, -120), + #(-38, -112, -96), + #(78, 107, -102), + #(52, 55, 44), + #(96, 103, -96), + #(-55, -110, -94), + #(42, 42, -28), + #(-109, -83, 87), + #(117, 124, -84), + #(49, 108, -84), + #(17, 93, 45), + #(-75, -81, -69), + #(122, 115, -50), + #(-91, -33, -69), + #(113, 74, -86), + #(-99, -44, 4), + #(-10, -73, -38), + #(-75, -104, 120), + #(18, 59, 38), + #(-77, -18, 106), + #(-95, -6, 58), + #(-125, -37, 17), + #(-99, -42, 62), + #(118, 110, -76), + #(-22, -53, -114), + #(-89, -128, -128), + #(101, 57, 125), + #(100, 79, -36), + #(18, 32, 64), + #(-19, -12, -28), + #(90, 120, 48), + #(57, 20, 116), + #(-47, -85, -101), + #(-69, -102, 126), + #(62, 23, -110), + #(116, 34, 104), + #(105, 32, 32), + #(-22, -7, -102), + #(-70, -68, -104), + #(-10, -42, -92), + #(-5, -75, 119), + #(115, 42, -34), + #(-85, -9, -3), + #(-80, -11, 112), + #(76, 51, 36), + #(106, 5, 18), + #(117, 27, 87), + #(10, 113, 106), + #(-40, -116, 32), + #(-43, -6, 2), + #(97, 105, -55), + #(-12, -65, 12), + #(-55, -118, 90), + #(-46, -55, -30), + #(70, 93, 110), +] + +const int8_underflow_testset = [ + #(-57, 92, 107), + #(-123, 11, 122), + #(-115, 48, 93), + #(-128, 52, 76), + #(-125, 105, 26), + #(-76, 78, 102), + #(-76, 92, 88), + #(-46, 101, 109), + #(-110, 80, 66), + #(-62, 80, 114), + #(-75, 77, 104), + #(-127, 54, 75), + #(-121, 121, 14), + #(-127, 3, 126), + #(-72, 68, 116), + #(-93, 83, 80), + #(-110, 112, 34), + #(-83, 59, 114), + #(-124, 109, 23), + #(-121, 81, 54), + #(-72, 105, 79), + #(-106, 104, 46), + #(-122, 78, 56), + #(-99, 40, 117), + #(-88, 83, 85), + #(-68, 97, 91), + #(-106, 93, 57), + #(-94, 57, 105), + #(-78, 125, 53), + #(-122, 71, 63), + #(-80, 97, 79), + #(-118, 51, 87), + #(-83, 79, 94), + #(-122, 100, 34), + #(-102, 49, 105), + #(-84, 87, 85), + #(-56, 111, 89), + #(-82, 56, 118), + #(-111, 58, 87), + #(-117, 29, 110), + #(-112, 57, 87), + #(-115, 34, 107), + #(-92, 100, 64), + #(-107, 99, 50), + #(-49, 124, 83), + #(-125, 104, 27), + #(-116, 122, 18), + #(-116, 121, 19), + #(-128, 74, 54), + #(-34, 102, 120), + #(-126, 56, 74), + #(-63, 68, 125), + #(-13, 116, 127), + #(-82, 59, 115), + #(-99, 84, 73), + #(-65, 77, 114), + #(-128, 113, 15), + #(-100, 124, 32), + #(-63, 104, 89), + #(-120, 53, 83), + #(-80, 86, 90), + #(-36, 101, 119), + #(-78, 97, 81), + #(-80, 74, 102), + #(-52, 105, 99), + #(-76, 104, 76), + #(-121, 110, 25), + #(-102, 35, 119), + #(-35, 122, 99), + #(-118, 100, 38), + #(-82, 111, 63), + #(-18, 115, 123), + #(-81, 105, 70), + #(-113, 79, 64), + #(-49, 104, 103), + #(-44, 94, 118), + #(-46, 85, 125), + #(-51, 81, 124), + #(-50, 115, 91), + #(-50, 105, 101), + #(-49, 123, 84), + #(-97, 91, 68), + #(-89, 67, 100), + #(-93, 85, 78), + #(-91, 91, 74), + #(-102, 38, 116), + #(-116, 17, 123), + #(-23, 110, 123), + #(-45, 95, 116), + #(-65, 82, 109), + #(-126, 85, 45), + #(-33, 123, 100), + #(-68, 113, 75), + #(-80, 104, 72), + #(-127, 49, 80), + #(-108, 112, 36), + #(-123, 114, 19), + #(-76, 75, 105), + #(-82, 70, 104), + #(-114, 76, 66), +] + +pub fn uint8_overflow_test() { + list.each(uint8_testset, fn(data) { + let a = bigi.from_int(data.0) + let b = bigi.from_int(data.1) + let c = bigi.add(a, b) + let d = bigi.subtract(c, bigi.from_int(255)) + should.equal( + utils.overflow( + utils.WouldOverflow(d), + min: bigi.zero(), + max: bigi.from_int(255), + ), + bigi.from_int(data.2), + ) + }) +} + +pub fn uint16_overflow_test() { + list.each(uint16_testset, fn(data) { + let a = bigi.from_int(data.0) + let b = bigi.from_int(data.1) + let c = bigi.add(a, b) + let d = bigi.subtract(c, bigi.from_int(65_535)) + should.equal( + utils.overflow( + utils.WouldOverflow(d), + min: bigi.zero(), + max: bigi.from_int(65_535), + ), + bigi.from_int(data.2), + ) + }) +} + +pub fn int8_overflow_test() { + list.each(int8_multiply_testset, fn(data) { + let a = bigi.from_int(data.0) + let b = bigi.from_int(data.1) + let c = bigi.multiply(a, b) + let d = bigi.subtract(c, bigi.from_int(127)) + should.equal( + utils.overflow( + utils.WouldOverflow(d), + min: bigi.from_int(-128), + max: bigi.from_int(127), + ), + bigi.from_int(data.2), + ) + }) +} + +pub fn int8_underflow_test() { + list.each(int8_underflow_testset, fn(data) { + let a = bigi.from_int(data.0) + let b = bigi.from_int(data.1) + let c = bigi.subtract(a, b) + let d = bigi.absolute(bigi.subtract(c, bigi.from_int(-128))) + should.equal( + utils.overflow( + utils.WouldUnderflow(d), + min: bigi.from_int(-128), + max: bigi.from_int(127), + ), + bigi.from_int(data.2), + ) + }) +} diff --git a/test/readme/mvp2.gleam b/test/readme/mvp2.gleam index 2640fb1..c8873d6 100644 --- a/test/readme/mvp2.gleam +++ b/test/readme/mvp2.gleam @@ -41,3 +41,7 @@ pub fn from_bigint(value: BigInt) { pub fn compare(a: Katzen, b: Katzen) { interface.compare(a, b, iface) } + +pub fn overflow(res: interface.OpResult(Katzen)) { + interface.overflow(res, iface) +} diff --git a/test/readme/mvp2_test.gleam b/test/readme/mvp2_test.gleam index 14d7c01..678dbaa 100644 --- a/test/readme/mvp2_test.gleam +++ b/test/readme/mvp2_test.gleam @@ -15,5 +15,10 @@ pub fn mvp2_add_test() { let assert Ok(a) = mvp2.from_bigint(bigi.from_int(2007)) let b = bigi.from_int(9) let c = mvp2.add(a, b) - should.equal(c, Error(utils.DidOverflow(bigi.from_int(1)))) + should.equal(c, Error(utils.WouldOverflow(bigi.from_int(1)))) + should.equal( + mvp2.overflow(c) + |> mvp2.to_bigint(), + bigi.from_int(2007), + ) } diff --git a/test/readme/mvp_test.gleam b/test/readme/mvp_test.gleam index 56d9089..5c05b4f 100644 --- a/test/readme/mvp_test.gleam +++ b/test/readme/mvp_test.gleam @@ -16,5 +16,5 @@ pub fn mvp_add_test() { let assert Ok(a) = interface.from_bigint(bigi.from_int(2007), mvp.iface) let b = bigi.from_int(9) let c = interface.math_op(a, b, mvp.iface, bigi.add) - should.equal(c, Error(utils.DidOverflow(bigi.from_int(1)))) + should.equal(c, Error(utils.WouldOverflow(bigi.from_int(1)))) } diff --git a/test/testgen/main.cc b/test/testgen/main.cc new file mode 100644 index 0000000..701e856 --- /dev/null +++ b/test/testgen/main.cc @@ -0,0 +1,36 @@ +#include +#include +#include +#include + +const auto NUMS = 100; + +int8_t generateRandomNumber(unsigned int numBits) { + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution dist(-128, 127); + return dist(gen); +} + +int modulo(int a, int b) { + const int result = a % b; + return result >= 0 ? result : result + b; +} + +int main() { + auto numsSoFar = 0; + while (numsSoFar < NUMS) { + int8_t num1 = generateRandomNumber(8); + int8_t num2 = generateRandomNumber(8); + int32_t sum = num1 - num2; + + if (sum < -128) { + // Overflow occurred, adjust the sum + int8_t over = sum; + std::cout << "#(" << (int) num1 << ", " << (int) num2 << ", " << (int) over << ")," << std::endl; + ++numsSoFar; + } + } + + return 0; +}