diff --git a/README.md b/README.md index bc5d4bc6..76187445 100644 --- a/README.md +++ b/README.md @@ -184,14 +184,16 @@ the source code, which is well-documented with NatSpec comments. ### Adjacent Value Types -PRBMath provides adjacent value types that serve as abstractions over other vanilla types such as `int64`. The types currently available are: +PRBMath provides adjacent value types that serve as abstractions over other vanilla types: | Value Type | Underlying Type | | ---------- | --------------- | | `SD1x18` | int64 | +| `SD21x18` | int128 | | `UD2x18` | uint64 | +| `UD21x18` | uint128 | -These are useful if you want to save gas by using a lower bit width integer, e.g. in a struct. +These are useful if you want to save gas by using a lower bit width integer, e.g., in a struct. Note that these types don't have any mathematical functionality. To do math with them, you will have to unwrap them into a simple integer and then to the core types `SD59x18` and `UD60x18`. diff --git a/src/Common.sol b/src/Common.sol index c19cebb1..91502ae8 100644 --- a/src/Common.sol +++ b/src/Common.sol @@ -32,6 +32,9 @@ uint128 constant MAX_UINT128 = type(uint128).max; /// @dev The maximum value a uint40 number can have. uint40 constant MAX_UINT40 = type(uint40).max; +/// @dev The maximum value a uint64 number can have. +uint64 constant MAX_UINT64 = type(uint64).max; + /// @dev The unit number, which the decimal precision of the fixed-point types. uint256 constant UNIT = 1e18; diff --git a/src/SD21x18.sol b/src/SD21x18.sol new file mode 100644 index 00000000..b488863c --- /dev/null +++ b/src/SD21x18.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.19; + +/* + +██████╗ ██████╗ ██████╗ ███╗ ███╗ █████╗ ████████╗██╗ ██╗ +██╔══██╗██╔══██╗██╔══██╗████╗ ████║██╔══██╗╚══██╔══╝██║ ██║ +██████╔╝██████╔╝██████╔╝██╔████╔██║███████║ ██║ ███████║ +██╔═══╝ ██╔══██╗██╔══██╗██║╚██╔╝██║██╔══██║ ██║ ██╔══██║ +██║ ██║ ██║██████╔╝██║ ╚═╝ ██║██║ ██║ ██║ ██║ ██║ +╚═╝ ╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ + +███████╗██████╗ ██████╗ ██╗██╗ ██╗ ██╗ █████╗ +██╔════╝██╔══██╗╚════██╗███║╚██╗██╔╝███║██╔══██╗ +███████╗██║ ██║ █████╔╝╚██║ ╚███╔╝ ╚██║╚█████╔╝ +╚════██║██║ ██║██╔═══╝ ██║ ██╔██╗ ██║██╔══██╗ +███████║██████╔╝███████╗ ██║██╔╝ ██╗ ██║╚█████╔╝ +╚══════╝╚═════╝ ╚══════╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚════╝ + +*/ + +import "./sd21x18/Casting.sol"; +import "./sd21x18/Constants.sol"; +import "./sd21x18/Errors.sol"; +import "./sd21x18/ValueType.sol"; diff --git a/src/UD21x18.sol b/src/UD21x18.sol new file mode 100644 index 00000000..535c1437 --- /dev/null +++ b/src/UD21x18.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.19; + +/* + +██████╗ ██████╗ ██████╗ ███╗ ███╗ █████╗ ████████╗██╗ ██╗ +██╔══██╗██╔══██╗██╔══██╗████╗ ████║██╔══██╗╚══██╔══╝██║ ██║ +██████╔╝██████╔╝██████╔╝██╔████╔██║███████║ ██║ ███████║ +██╔═══╝ ██╔══██╗██╔══██╗██║╚██╔╝██║██╔══██║ ██║ ██╔══██║ +██║ ██║ ██║██████╔╝██║ ╚═╝ ██║██║ ██║ ██║ ██║ ██║ +╚═╝ ╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ + +██╗ ██╗██████╗ ██████╗ ██╗██╗ ██╗ ██╗ █████╗ +██║ ██║██╔══██╗╚════██╗███║╚██╗██╔╝███║██╔══██╗ +██║ ██║██║ ██║ █████╔╝╚██║ ╚███╔╝ ╚██║╚█████╔╝ +██║ ██║██║ ██║██╔═══╝ ██║ ██╔██╗ ██║██╔══██╗ +╚██████╔╝██████╔╝███████╗ ██║██╔╝ ██╗ ██║╚█████╔╝ + ╚═════╝ ╚═════╝ ╚══════╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚════╝ + +*/ + +import "./ud21x18/Casting.sol"; +import "./ud21x18/Constants.sol"; +import "./ud21x18/Errors.sol"; +import "./ud21x18/ValueType.sol"; diff --git a/src/casting/Uint128.sol b/src/casting/Uint128.sol index e6dacb67..c2c3c180 100644 --- a/src/casting/Uint128.sol +++ b/src/casting/Uint128.sol @@ -3,14 +3,20 @@ pragma solidity >=0.8.19; import { uMAX_SD1x18 } from "../sd1x18/Constants.sol"; import { SD1x18 } from "../sd1x18/ValueType.sol"; +import { uMAX_SD21x18 } from "../sd21x18/Constants.sol"; +import { SD21x18 } from "../sd21x18/ValueType.sol"; import { SD59x18 } from "../sd59x18/ValueType.sol"; import { uMAX_UD2x18 } from "../ud2x18/Constants.sol"; import { UD2x18 } from "../ud2x18/ValueType.sol"; +import { UD21x18 } from "../ud21x18/ValueType.sol"; import { UD60x18 } from "../ud60x18/ValueType.sol"; /// @notice Thrown when trying to cast a uint128 that doesn't fit in SD1x18. error PRBMath_IntoSD1x18_Overflow(uint128 x); +/// @notice Thrown when trying to cast a uint128 that doesn't fit in SD21x18. +error PRBMath_IntoSD21x18_Overflow(uint128 x); + /// @notice Thrown when trying to cast a uint128 that doesn't fit in UD2x18. error PRBMath_IntoUD2x18_Overflow(uint128 x); @@ -19,7 +25,7 @@ error PRBMath_IntoUD2x18_Overflow(uint128 x); library PRBMathCastingUint128 { /// @notice Casts a uint128 number to SD1x18. /// @dev Requirements: - /// - x must be less than or equal to `MAX_SD1x18`. + /// - x must be less than or equal to `uMAX_SD1x18`. function intoSD1x18(uint128 x) internal pure returns (SD1x18 result) { if (x > uint256(int256(uMAX_SD1x18))) { revert PRBMath_IntoSD1x18_Overflow(x); @@ -27,6 +33,16 @@ library PRBMathCastingUint128 { result = SD1x18.wrap(int64(uint64(x))); } + /// @notice Casts a uint128 number to SD21x18. + /// @dev Requirements: + /// - x must be less than or equal to `uMAX_SD21x18`. + function intoSD21x18(uint128 x) internal pure returns (SD21x18 result) { + if (x > uint256(int256(uMAX_SD21x18))) { + revert PRBMath_IntoSD21x18_Overflow(x); + } + result = SD21x18.wrap(int128(x)); + } + /// @notice Casts a uint128 number to SD59x18. /// @dev There is no overflow check because the domain of uint128 is a subset of SD59x18. function intoSD59x18(uint128 x) internal pure returns (SD59x18 result) { @@ -35,7 +51,7 @@ library PRBMathCastingUint128 { /// @notice Casts a uint128 number to UD2x18. /// @dev Requirements: - /// - x must be less than or equal to `MAX_SD1x18`. + /// - x must be less than or equal to `uMAX_UD2x18`. function intoUD2x18(uint128 x) internal pure returns (UD2x18 result) { if (x > uint64(uMAX_UD2x18)) { revert PRBMath_IntoUD2x18_Overflow(x); @@ -43,9 +59,14 @@ library PRBMathCastingUint128 { result = UD2x18.wrap(uint64(x)); } + /// @notice Casts a uint128 number to UD21x18. + function intoUD21x18(uint128 x) internal pure returns (UD21x18 result) { + result = UD21x18.wrap(x); + } + /// @notice Casts a uint128 number to UD60x18. /// @dev There is no overflow check because the domain of uint128 is a subset of UD60x18. function intoUD60x18(uint128 x) internal pure returns (UD60x18 result) { - result = UD60x18.wrap(uint256(x)); + result = UD60x18.wrap(x); } } diff --git a/src/casting/Uint256.sol b/src/casting/Uint256.sol index 03aede6c..e98f207b 100644 --- a/src/casting/Uint256.sol +++ b/src/casting/Uint256.sol @@ -3,27 +3,37 @@ pragma solidity >=0.8.19; import { uMAX_SD1x18 } from "../sd1x18/Constants.sol"; import { SD1x18 } from "../sd1x18/ValueType.sol"; +import { uMAX_SD21x18 } from "../sd21x18/Constants.sol"; +import { SD21x18 } from "../sd21x18/ValueType.sol"; import { uMAX_SD59x18 } from "../sd59x18/Constants.sol"; import { SD59x18 } from "../sd59x18/ValueType.sol"; import { uMAX_UD2x18 } from "../ud2x18/Constants.sol"; import { UD2x18 } from "../ud2x18/ValueType.sol"; +import { uMAX_UD21x18 } from "../ud21x18/Constants.sol"; +import { UD21x18 } from "../ud21x18/ValueType.sol"; import { UD60x18 } from "../ud60x18/ValueType.sol"; /// @notice Thrown when trying to cast a uint256 that doesn't fit in SD1x18. error PRBMath_IntoSD1x18_Overflow(uint256 x); +/// @notice Thrown when trying to cast a uint256 that doesn't fit in SD21x18. +error PRBMath_IntoSD21x18_Overflow(uint256 x); + /// @notice Thrown when trying to cast a uint256 that doesn't fit in SD59x18. error PRBMath_IntoSD59x18_Overflow(uint256 x); /// @notice Thrown when trying to cast a uint256 that doesn't fit in UD2x18. error PRBMath_IntoUD2x18_Overflow(uint256 x); +/// @notice Thrown when trying to cast a uint256 that doesn't fit in UD21x18. +error PRBMath_IntoUD21x18_Overflow(uint256 x); + /// @title PRBMathCastingUint256 /// @notice Casting utilities for uint256. library PRBMathCastingUint256 { /// @notice Casts a uint256 number to SD1x18. /// @dev Requirements: - /// - x must be less than or equal to `MAX_SD1x18`. + /// - x must be less than or equal to `uMAX_SD1x18`. function intoSD1x18(uint256 x) internal pure returns (SD1x18 result) { if (x > uint256(int256(uMAX_SD1x18))) { revert PRBMath_IntoSD1x18_Overflow(x); @@ -31,9 +41,19 @@ library PRBMathCastingUint256 { result = SD1x18.wrap(int64(int256(x))); } + /// @notice Casts a uint256 number to SD21x18. + /// @dev Requirements: + /// - x must be less than or equal to `uMAX_SD21x18`. + function intoSD21x18(uint256 x) internal pure returns (SD21x18 result) { + if (x > uint256(int256(uMAX_SD21x18))) { + revert PRBMath_IntoSD21x18_Overflow(x); + } + result = SD21x18.wrap(int128(int256(x))); + } + /// @notice Casts a uint256 number to SD59x18. /// @dev Requirements: - /// - x must be less than or equal to `MAX_SD59x18`. + /// - x must be less than or equal to `uMAX_SD59x18`. function intoSD59x18(uint256 x) internal pure returns (SD59x18 result) { if (x > uint256(uMAX_SD59x18)) { revert PRBMath_IntoSD59x18_Overflow(x); @@ -42,6 +62,8 @@ library PRBMathCastingUint256 { } /// @notice Casts a uint256 number to UD2x18. + /// @dev Requirements: + /// - x must be less than or equal to `uMAX_UD2x18`. function intoUD2x18(uint256 x) internal pure returns (UD2x18 result) { if (x > uint256(uMAX_UD2x18)) { revert PRBMath_IntoUD2x18_Overflow(x); @@ -49,6 +71,16 @@ library PRBMathCastingUint256 { result = UD2x18.wrap(uint64(x)); } + /// @notice Casts a uint256 number to UD2x18. + /// @dev Requirements: + /// - x must be less than or equal to `uMAX_UD21x18`. + function intoUD21x18(uint256 x) internal pure returns (UD21x18 result) { + if (x > uint256(uMAX_UD21x18)) { + revert PRBMath_IntoUD21x18_Overflow(x); + } + result = UD21x18.wrap(uint128(x)); + } + /// @notice Casts a uint256 number to UD60x18. function intoUD60x18(uint256 x) internal pure returns (UD60x18 result) { result = UD60x18.wrap(x); diff --git a/src/casting/Uint40.sol b/src/casting/Uint40.sol index 13974e74..c93edb9a 100644 --- a/src/casting/Uint40.sol +++ b/src/casting/Uint40.sol @@ -2,8 +2,10 @@ pragma solidity >=0.8.19; import { SD1x18 } from "../sd1x18/ValueType.sol"; +import { SD21x18 } from "../sd21x18/ValueType.sol"; import { SD59x18 } from "../sd59x18/ValueType.sol"; import { UD2x18 } from "../ud2x18/ValueType.sol"; +import { UD21x18 } from "../ud21x18/ValueType.sol"; import { UD60x18 } from "../ud60x18/ValueType.sol"; /// @title PRBMathCastingUint40 @@ -15,6 +17,12 @@ library PRBMathCastingUint40 { result = SD1x18.wrap(int64(uint64(x))); } + /// @notice Casts a uint40 number into SD21x18. + /// @dev There is no overflow check because the domain of uint40 is a subset of SD21x18. + function intoSD21x18(uint40 x) internal pure returns (SD21x18 result) { + result = SD21x18.wrap(int128(uint128(x))); + } + /// @notice Casts a uint40 number into SD59x18. /// @dev There is no overflow check because the domain of uint40 is a subset of SD59x18. function intoSD59x18(uint40 x) internal pure returns (SD59x18 result) { @@ -24,12 +32,18 @@ library PRBMathCastingUint40 { /// @notice Casts a uint40 number into UD2x18. /// @dev There is no overflow check because the domain of uint40 is a subset of UD2x18. function intoUD2x18(uint40 x) internal pure returns (UD2x18 result) { - result = UD2x18.wrap(uint64(x)); + result = UD2x18.wrap(x); + } + + /// @notice Casts a uint40 number into UD21x18. + /// @dev There is no overflow check because the domain of uint40 is a subset of UD21x18. + function intoUD21x18(uint40 x) internal pure returns (UD21x18 result) { + result = UD21x18.wrap((x)); } /// @notice Casts a uint40 number into UD60x18. /// @dev There is no overflow check because the domain of uint40 is a subset of UD60x18. function intoUD60x18(uint40 x) internal pure returns (UD60x18 result) { - result = UD60x18.wrap(uint256(x)); + result = UD60x18.wrap(x); } } diff --git a/src/sd1x18/Casting.sol b/src/sd1x18/Casting.sol index a49b2a1c..a167103e 100644 --- a/src/sd1x18/Casting.sol +++ b/src/sd1x18/Casting.sol @@ -3,11 +3,19 @@ pragma solidity >=0.8.19; import "../Common.sol" as Common; import "./Errors.sol" as CastingErrors; +import { SD21x18 } from "../sd21x18/ValueType.sol"; import { SD59x18 } from "../sd59x18/ValueType.sol"; import { UD2x18 } from "../ud2x18/ValueType.sol"; +import { UD21x18 } from "../ud21x18/ValueType.sol"; import { UD60x18 } from "../ud60x18/ValueType.sol"; import { SD1x18 } from "./ValueType.sol"; +/// @notice Casts an SD1x18 number into SD21x18. +/// @dev There is no overflow check because the domain of SD1x18 is a subset of SD21x18. +function intoSD21x18(SD1x18 x) pure returns (SD21x18 result) { + result = SD21x18.wrap(int128(int256(SD1x18.unwrap(x)))); +} + /// @notice Casts an SD1x18 number into SD59x18. /// @dev There is no overflow check because the domain of SD1x18 is a subset of SD59x18. function intoSD59x18(SD1x18 x) pure returns (SD59x18 result) { @@ -24,6 +32,16 @@ function intoUD2x18(SD1x18 x) pure returns (UD2x18 result) { result = UD2x18.wrap(uint64(xInt)); } +/// @notice Casts an SD1x18 number into UD21x18. +/// - x must be positive. +function intoUD21x18(SD1x18 x) pure returns (UD21x18 result) { + int64 xInt = SD1x18.unwrap(x); + if (xInt < 0) { + revert CastingErrors.PRBMath_SD1x18_ToUD21x18_Underflow(x); + } + result = UD21x18.wrap(uint128(uint64(xInt))); +} + /// @notice Casts an SD1x18 number into UD60x18. /// @dev Requirements: /// - x must be positive. @@ -35,26 +53,26 @@ function intoUD60x18(SD1x18 x) pure returns (UD60x18 result) { result = UD60x18.wrap(uint64(xInt)); } -/// @notice Casts an SD1x18 number into uint256. +/// @notice Casts an SD1x18 number into uint128. /// @dev Requirements: /// - x must be positive. -function intoUint256(SD1x18 x) pure returns (uint256 result) { +function intoUint128(SD1x18 x) pure returns (uint128 result) { int64 xInt = SD1x18.unwrap(x); if (xInt < 0) { - revert CastingErrors.PRBMath_SD1x18_ToUint256_Underflow(x); + revert CastingErrors.PRBMath_SD1x18_ToUint128_Underflow(x); } - result = uint256(uint64(xInt)); + result = uint128(uint64(xInt)); } -/// @notice Casts an SD1x18 number into uint128. +/// @notice Casts an SD1x18 number into uint256. /// @dev Requirements: /// - x must be positive. -function intoUint128(SD1x18 x) pure returns (uint128 result) { +function intoUint256(SD1x18 x) pure returns (uint256 result) { int64 xInt = SD1x18.unwrap(x); if (xInt < 0) { - revert CastingErrors.PRBMath_SD1x18_ToUint128_Underflow(x); + revert CastingErrors.PRBMath_SD1x18_ToUint256_Underflow(x); } - result = uint128(uint64(xInt)); + result = uint256(uint64(xInt)); } /// @notice Casts an SD1x18 number into uint40. diff --git a/src/sd1x18/Constants.sol b/src/sd1x18/Constants.sol index 4c1ebc2b..5a762342 100644 --- a/src/sd1x18/Constants.sol +++ b/src/sd1x18/Constants.sol @@ -10,7 +10,7 @@ SD1x18 constant E = SD1x18.wrap(2_718281828459045235); int64 constant uMAX_SD1x18 = 9_223372036854775807; SD1x18 constant MAX_SD1x18 = SD1x18.wrap(uMAX_SD1x18); -/// @dev The maximum value an SD1x18 number can have. +/// @dev The minimum value an SD1x18 number can have. int64 constant uMIN_SD1x18 = -9_223372036854775808; SD1x18 constant MIN_SD1x18 = SD1x18.wrap(uMIN_SD1x18); diff --git a/src/sd1x18/Errors.sol b/src/sd1x18/Errors.sol index 1e65eafb..ee896939 100644 --- a/src/sd1x18/Errors.sol +++ b/src/sd1x18/Errors.sol @@ -3,20 +3,23 @@ pragma solidity >=0.8.19; import { SD1x18 } from "./ValueType.sol"; -/// @notice Thrown when trying to cast a SD1x18 number that doesn't fit in UD2x18. +/// @notice Thrown when trying to cast an SD1x18 number that doesn't fit in UD2x18. error PRBMath_SD1x18_ToUD2x18_Underflow(SD1x18 x); -/// @notice Thrown when trying to cast a SD1x18 number that doesn't fit in UD60x18. +/// @notice Thrown when trying to cast an SD1x18 number that doesn't fit in UD21x18. +error PRBMath_SD1x18_ToUD21x18_Underflow(SD1x18 x); + +/// @notice Thrown when trying to cast an SD1x18 number that doesn't fit in UD60x18. error PRBMath_SD1x18_ToUD60x18_Underflow(SD1x18 x); -/// @notice Thrown when trying to cast a SD1x18 number that doesn't fit in uint128. +/// @notice Thrown when trying to cast an SD1x18 number that doesn't fit in uint128. error PRBMath_SD1x18_ToUint128_Underflow(SD1x18 x); -/// @notice Thrown when trying to cast a SD1x18 number that doesn't fit in uint256. +/// @notice Thrown when trying to cast an SD1x18 number that doesn't fit in uint256. error PRBMath_SD1x18_ToUint256_Underflow(SD1x18 x); -/// @notice Thrown when trying to cast a SD1x18 number that doesn't fit in uint40. +/// @notice Thrown when trying to cast an SD1x18 number that doesn't fit in uint40. error PRBMath_SD1x18_ToUint40_Overflow(SD1x18 x); -/// @notice Thrown when trying to cast a SD1x18 number that doesn't fit in uint40. +/// @notice Thrown when trying to cast an SD1x18 number that doesn't fit in uint40. error PRBMath_SD1x18_ToUint40_Underflow(SD1x18 x); diff --git a/src/sd1x18/ValueType.sol b/src/sd1x18/ValueType.sol index be876097..e9ba0db5 100644 --- a/src/sd1x18/ValueType.sol +++ b/src/sd1x18/ValueType.sol @@ -14,11 +14,13 @@ type SD1x18 is int64; //////////////////////////////////////////////////////////////////////////*/ using { + Casting.intoSD21x18, Casting.intoSD59x18, Casting.intoUD2x18, + Casting.intoUD21x18, Casting.intoUD60x18, - Casting.intoUint256, Casting.intoUint128, + Casting.intoUint256, Casting.intoUint40, Casting.unwrap } for SD1x18 global; diff --git a/src/sd21x18/Casting.sol b/src/sd21x18/Casting.sol new file mode 100644 index 00000000..c3453c9b --- /dev/null +++ b/src/sd21x18/Casting.sol @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.19; + +import "../Common.sol" as Common; +import "./Errors.sol" as CastingErrors; +import { SD1x18 } from "../sd1x18/ValueType.sol"; +import { uMAX_SD1x18, uMIN_SD1x18 } from "../sd1x18/Constants.sol"; +import { SD59x18 } from "../sd59x18/ValueType.sol"; +import { uMAX_UD2x18 } from "../ud2x18/Constants.sol"; +import { UD2x18 } from "../ud2x18/ValueType.sol"; +import { UD21x18 } from "../ud21x18/ValueType.sol"; +import { UD60x18 } from "../ud60x18/ValueType.sol"; +import { SD21x18 } from "./ValueType.sol"; + +/// @notice Casts an SD21x18 number into SD1x18. +/// @dev Requirements: +/// - x must be greater than or equal to `uMIN_SD1x18`. +/// - x must be less than or equal to `uMAX_SD1x18`. +function intoSD1x18(SD21x18 x) pure returns (SD1x18 result) { + int128 xInt = SD21x18.unwrap(x); + if (xInt < uMIN_SD1x18) { + revert CastingErrors.PRBMath_SD21x18_IntoSD1x18_Underflow(x); + } + if (xInt > uMAX_SD1x18) { + revert CastingErrors.PRBMath_SD21x18_IntoSD1x18_Overflow(x); + } + result = SD1x18.wrap(int64(xInt)); +} + +/// @notice Casts an SD21x18 number into SD59x18. +/// @dev There is no overflow check because the domain of SD21x18 is a subset of SD59x18. +function intoSD59x18(SD21x18 x) pure returns (SD59x18 result) { + result = SD59x18.wrap(int256(SD21x18.unwrap(x))); +} + +/// @notice Casts an SD21x18 number into UD2x18. +/// - x must be positive. +/// - x must be less than or equal to `uMAX_UD2x18`. +function intoUD2x18(SD21x18 x) pure returns (UD2x18 result) { + int128 xInt = SD21x18.unwrap(x); + if (xInt < 0) { + revert CastingErrors.PRBMath_SD21x18_ToUD2x18_Underflow(x); + } + if (xInt > int128(uint128(uMAX_UD2x18))) { + revert CastingErrors.PRBMath_SD21x18_ToUD2x18_Overflow(x); + } + result = UD2x18.wrap(uint64(uint128(xInt))); +} + +/// @notice Casts an SD21x18 number into UD21x18. +/// - x must be positive. +function intoUD21x18(SD21x18 x) pure returns (UD21x18 result) { + int128 xInt = SD21x18.unwrap(x); + if (xInt < 0) { + revert CastingErrors.PRBMath_SD21x18_ToUD21x18_Underflow(x); + } + result = UD21x18.wrap(uint128(uint128(xInt))); +} + +/// @notice Casts an SD21x18 number into UD60x18. +/// @dev Requirements: +/// - x must be positive. +function intoUD60x18(SD21x18 x) pure returns (UD60x18 result) { + int128 xInt = SD21x18.unwrap(x); + if (xInt < 0) { + revert CastingErrors.PRBMath_SD21x18_ToUD60x18_Underflow(x); + } + result = UD60x18.wrap(uint128(xInt)); +} + +/// @notice Casts an SD21x18 number into uint128. +/// @dev Requirements: +/// - x must be positive. +function intoUint128(SD21x18 x) pure returns (uint128 result) { + int128 xInt = SD21x18.unwrap(x); + if (xInt < 0) { + revert CastingErrors.PRBMath_SD21x18_ToUint128_Underflow(x); + } + result = uint128(xInt); +} + +/// @notice Casts an SD21x18 number into uint256. +/// @dev Requirements: +/// - x must be positive. +function intoUint256(SD21x18 x) pure returns (uint256 result) { + int128 xInt = SD21x18.unwrap(x); + if (xInt < 0) { + revert CastingErrors.PRBMath_SD21x18_ToUint256_Underflow(x); + } + result = uint256(uint128(xInt)); +} + +/// @notice Casts an SD21x18 number into uint40. +/// @dev Requirements: +/// - x must be positive. +/// - x must be less than or equal to `MAX_UINT40`. +function intoUint40(SD21x18 x) pure returns (uint40 result) { + int128 xInt = SD21x18.unwrap(x); + if (xInt < 0) { + revert CastingErrors.PRBMath_SD21x18_ToUint40_Underflow(x); + } + if (xInt > int128(uint128(Common.MAX_UINT40))) { + revert CastingErrors.PRBMath_SD21x18_ToUint40_Overflow(x); + } + result = uint40(uint128(xInt)); +} + +/// @notice Casts an SD21x18 number into uint64. +/// @dev Requirements: +/// - x must be positive. +/// - x must be less than or equal to `MAX_UINT64`. +function intoUint64(SD21x18 x) pure returns (uint64 result) { + int128 xInt = SD21x18.unwrap(x); + if (xInt < 0) { + revert CastingErrors.PRBMath_SD21x18_ToUint64_Underflow(x); + } + if (xInt > int128(uint128(Common.MAX_UINT64))) { + revert CastingErrors.PRBMath_SD21x18_ToUint64_Overflow(x); + } + result = uint64(uint128(xInt)); +} + +/// @notice Alias for {wrap}. +function sd21x18(int128 x) pure returns (SD21x18 result) { + result = SD21x18.wrap(x); +} + +/// @notice Unwraps an SD21x18 number into int128. +function unwrap(SD21x18 x) pure returns (int128 result) { + result = SD21x18.unwrap(x); +} + +/// @notice Wraps an int128 number into SD21x18. +function wrap(int128 x) pure returns (SD21x18 result) { + result = SD21x18.wrap(x); +} diff --git a/src/sd21x18/Constants.sol b/src/sd21x18/Constants.sol new file mode 100644 index 00000000..4e67ce4d --- /dev/null +++ b/src/sd21x18/Constants.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.19; + +import { SD21x18 } from "./ValueType.sol"; + +/// @dev Euler's number as an SD21x18 number. +SD21x18 constant E = SD21x18.wrap(2_718281828459045235); + +/// @dev The maximum value an SD21x18 number can have. +int128 constant uMAX_SD21x18 = 170141183460469231731_687303715884105727; +SD21x18 constant MAX_SD21x18 = SD21x18.wrap(uMAX_SD21x18); + +/// @dev The minimum value an SD21x18 number can have. +int128 constant uMIN_SD21x18 = -170141183460469231731_687303715884105728; +SD21x18 constant MIN_SD21x18 = SD21x18.wrap(uMIN_SD21x18); + +/// @dev PI as an SD21x18 number. +SD21x18 constant PI = SD21x18.wrap(3_141592653589793238); + +/// @dev The unit number, which gives the decimal precision of SD21x18. +SD21x18 constant UNIT = SD21x18.wrap(1e18); +int128 constant uUNIT = 1e18; diff --git a/src/sd21x18/Errors.sol b/src/sd21x18/Errors.sol new file mode 100644 index 00000000..9a716970 --- /dev/null +++ b/src/sd21x18/Errors.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.19; + +import { SD21x18 } from "./ValueType.sol"; + +/// @notice Thrown when trying to cast an SD21x18 number that doesn't fit in SD1x18. +error PRBMath_SD21x18_IntoSD1x18_Overflow(SD21x18 x); + +/// @notice Thrown when trying to cast an SD21x18 number that doesn't fit in SD1x18. +error PRBMath_SD21x18_IntoSD1x18_Underflow(SD21x18 x); + +/// @notice Thrown when trying to cast an SD21x18 number that doesn't fit in UD2x18. +error PRBMath_SD21x18_ToUD2x18_Overflow(SD21x18 x); + +/// @notice Thrown when trying to cast an SD21x18 number that doesn't fit in UD2x18. +error PRBMath_SD21x18_ToUD2x18_Underflow(SD21x18 x); + +/// @notice Thrown when trying to cast an SD21x18 number that doesn't fit in UD21x18. +error PRBMath_SD21x18_ToUD21x18_Underflow(SD21x18 x); + +/// @notice Thrown when trying to cast an SD21x18 number that doesn't fit in UD60x18. +error PRBMath_SD21x18_ToUD60x18_Underflow(SD21x18 x); + +/// @notice Thrown when trying to cast an SD21x18 number that doesn't fit in uint256. +error PRBMath_SD21x18_ToUint256_Underflow(SD21x18 x); + +/// @notice Thrown when trying to cast an SD21x18 number that doesn't fit in uint128. +error PRBMath_SD21x18_ToUint128_Underflow(SD21x18 x); + +/// @notice Thrown when trying to cast an SD21x18 number that doesn't fit in uint64. +error PRBMath_SD21x18_ToUint64_Overflow(SD21x18 x); + +/// @notice Thrown when trying to cast an SD21x18 number that doesn't fit in uint64. +error PRBMath_SD21x18_ToUint64_Underflow(SD21x18 x); + +/// @notice Thrown when trying to cast an SD21x18 number that doesn't fit in uint40. +error PRBMath_SD21x18_ToUint40_Overflow(SD21x18 x); + +/// @notice Thrown when trying to cast an SD21x18 number that doesn't fit in uint40. +error PRBMath_SD21x18_ToUint40_Underflow(SD21x18 x); diff --git a/src/sd21x18/ValueType.sol b/src/sd21x18/ValueType.sol new file mode 100644 index 00000000..3ae00082 --- /dev/null +++ b/src/sd21x18/ValueType.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.19; + +import "./Casting.sol" as Casting; + +/// @notice The signed 21.18-decimal fixed-point number representation, which can have up to 21 digits and up to 18 +/// decimals. The values of this are bound by the minimum and the maximum values permitted by the underlying Solidity +/// type int128. This is useful when end users want to use int128 to save gas, e.g. with tight variable packing in contract +/// storage. +type SD21x18 is int128; + +/*////////////////////////////////////////////////////////////////////////// + CASTING +//////////////////////////////////////////////////////////////////////////*/ + +using { + Casting.intoSD1x18, + Casting.intoSD59x18, + Casting.intoUD2x18, + Casting.intoUD21x18, + Casting.intoUD60x18, + Casting.intoUint128, + Casting.intoUint256, + Casting.intoUint40, + Casting.unwrap +} for SD21x18 global; diff --git a/src/sd59x18/Casting.sol b/src/sd59x18/Casting.sol index d85b8b8c..9bee9fee 100644 --- a/src/sd59x18/Casting.sol +++ b/src/sd59x18/Casting.sol @@ -5,8 +5,12 @@ import "./Errors.sol" as CastingErrors; import { MAX_UINT128, MAX_UINT40 } from "../Common.sol"; import { uMAX_SD1x18, uMIN_SD1x18 } from "../sd1x18/Constants.sol"; import { SD1x18 } from "../sd1x18/ValueType.sol"; +import { uMAX_SD21x18, uMIN_SD21x18 } from "../sd21x18/Constants.sol"; +import { SD21x18 } from "../sd21x18/ValueType.sol"; import { uMAX_UD2x18 } from "../ud2x18/Constants.sol"; import { UD2x18 } from "../ud2x18/ValueType.sol"; +import { uMAX_UD21x18 } from "../ud21x18/Constants.sol"; +import { UD21x18 } from "../ud21x18/ValueType.sol"; import { UD60x18 } from "../ud60x18/ValueType.sol"; import { SD59x18 } from "./ValueType.sol"; @@ -31,6 +35,21 @@ function intoSD1x18(SD59x18 x) pure returns (SD1x18 result) { result = SD1x18.wrap(int64(xInt)); } +/// @notice Casts an SD59x18 number into SD21x18. +/// @dev Requirements: +/// - x must be greater than or equal to `uMIN_SD21x18`. +/// - x must be less than or equal to `uMAX_SD21x18`. +function intoSD21x18(SD59x18 x) pure returns (SD21x18 result) { + int256 xInt = SD59x18.unwrap(x); + if (xInt < uMIN_SD21x18) { + revert CastingErrors.PRBMath_SD59x18_IntoSD21x18_Underflow(x); + } + if (xInt > uMAX_SD21x18) { + revert CastingErrors.PRBMath_SD59x18_IntoSD21x18_Overflow(x); + } + result = SD21x18.wrap(int128(xInt)); +} + /// @notice Casts an SD59x18 number into UD2x18. /// @dev Requirements: /// - x must be positive. @@ -46,6 +65,21 @@ function intoUD2x18(SD59x18 x) pure returns (UD2x18 result) { result = UD2x18.wrap(uint64(uint256(xInt))); } +/// @notice Casts an SD59x18 number into UD21x18. +/// @dev Requirements: +/// - x must be positive. +/// - x must be less than or equal to `uMAX_UD21x18`. +function intoUD21x18(SD59x18 x) pure returns (UD21x18 result) { + int256 xInt = SD59x18.unwrap(x); + if (xInt < 0) { + revert CastingErrors.PRBMath_SD59x18_IntoUD21x18_Underflow(x); + } + if (xInt > int256(uint256(uMAX_UD21x18))) { + revert CastingErrors.PRBMath_SD59x18_IntoUD21x18_Overflow(x); + } + result = UD21x18.wrap(uint128(uint256(xInt))); +} + /// @notice Casts an SD59x18 number into UD60x18. /// @dev Requirements: /// - x must be positive. diff --git a/src/sd59x18/Errors.sol b/src/sd59x18/Errors.sol index c463f838..c70b2cf6 100644 --- a/src/sd59x18/Errors.sol +++ b/src/sd59x18/Errors.sol @@ -36,34 +36,46 @@ error PRBMath_SD59x18_Gm_NegativeProduct(SD59x18 x, SD59x18 y); /// @notice Thrown when taking the geometric mean of two numbers and multiplying them overflows SD59x18. error PRBMath_SD59x18_Gm_Overflow(SD59x18 x, SD59x18 y); -/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in SD1x18. +/// @notice Thrown when trying to cast an SD59x18 number that doesn't fit in SD1x18. error PRBMath_SD59x18_IntoSD1x18_Overflow(SD59x18 x); -/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in SD1x18. +/// @notice Thrown when trying to cast an SD59x18 number that doesn't fit in SD1x18. error PRBMath_SD59x18_IntoSD1x18_Underflow(SD59x18 x); -/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in UD2x18. +/// @notice Thrown when trying to cast an SD59x18 number that doesn't fit in SD21x18. +error PRBMath_SD59x18_IntoSD21x18_Overflow(SD59x18 x); + +/// @notice Thrown when trying to cast an SD59x18 number that doesn't fit in SD21x18. +error PRBMath_SD59x18_IntoSD21x18_Underflow(SD59x18 x); + +/// @notice Thrown when trying to cast an SD59x18 number that doesn't fit in UD2x18. error PRBMath_SD59x18_IntoUD2x18_Overflow(SD59x18 x); -/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in UD2x18. +/// @notice Thrown when trying to cast an SD59x18 number that doesn't fit in UD2x18. error PRBMath_SD59x18_IntoUD2x18_Underflow(SD59x18 x); -/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in UD60x18. +/// @notice Thrown when trying to cast an SD59x18 number that doesn't fit in UD21x18. +error PRBMath_SD59x18_IntoUD21x18_Overflow(SD59x18 x); + +/// @notice Thrown when trying to cast an SD59x18 number that doesn't fit in UD21x18. +error PRBMath_SD59x18_IntoUD21x18_Underflow(SD59x18 x); + +/// @notice Thrown when trying to cast an SD59x18 number that doesn't fit in UD60x18. error PRBMath_SD59x18_IntoUD60x18_Underflow(SD59x18 x); -/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in uint128. +/// @notice Thrown when trying to cast an SD59x18 number that doesn't fit in uint128. error PRBMath_SD59x18_IntoUint128_Overflow(SD59x18 x); -/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in uint128. +/// @notice Thrown when trying to cast an SD59x18 number that doesn't fit in uint128. error PRBMath_SD59x18_IntoUint128_Underflow(SD59x18 x); -/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in uint256. +/// @notice Thrown when trying to cast an SD59x18 number that doesn't fit in uint256. error PRBMath_SD59x18_IntoUint256_Underflow(SD59x18 x); -/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in uint40. +/// @notice Thrown when trying to cast an SD59x18 number that doesn't fit in uint40. error PRBMath_SD59x18_IntoUint40_Overflow(SD59x18 x); -/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in uint40. +/// @notice Thrown when trying to cast an SD59x18 number that doesn't fit in uint40. error PRBMath_SD59x18_IntoUint40_Underflow(SD59x18 x); /// @notice Thrown when taking the logarithm of a number less than or equal to zero. diff --git a/src/sd59x18/ValueType.sol b/src/sd59x18/ValueType.sol index f8203a3b..e0f13370 100644 --- a/src/sd59x18/ValueType.sol +++ b/src/sd59x18/ValueType.sol @@ -17,7 +17,9 @@ type SD59x18 is int256; using { Casting.intoInt256, Casting.intoSD1x18, + Casting.intoSD21x18, Casting.intoUD2x18, + Casting.intoUD21x18, Casting.intoUD60x18, Casting.intoUint256, Casting.intoUint128, diff --git a/src/ud21x18/Casting.sol b/src/ud21x18/Casting.sol new file mode 100644 index 00000000..52c4b205 --- /dev/null +++ b/src/ud21x18/Casting.sol @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.19; + +import "../Common.sol" as Common; +import "./Errors.sol" as Errors; +import { uMAX_SD1x18 } from "../sd1x18/Constants.sol"; +import { SD1x18 } from "../sd1x18/ValueType.sol"; +import { SD21x18 } from "../sd21x18/ValueType.sol"; +import { uMAX_SD21x18 } from "../sd21x18/Constants.sol"; +import { SD59x18 } from "../sd59x18/ValueType.sol"; +import { UD60x18 } from "../ud60x18/ValueType.sol"; +import { uMAX_UD2x18 } from "../ud2x18/Constants.sol"; +import { UD2x18 } from "../ud2x18/ValueType.sol"; +import { UD21x18 } from "./ValueType.sol"; + +/// @notice Casts a UD21x18 number into SD1x18. +/// @dev Requirements: +/// - x must be less than or equal to `uMAX_SD1x18`. +function intoSD1x18(UD21x18 x) pure returns (SD1x18 result) { + uint128 xUint = UD21x18.unwrap(x); + if (xUint > uint128(uint64(uMAX_SD1x18))) { + revert Errors.PRBMath_UD21x18_IntoSD1x18_Overflow(x); + } + result = SD1x18.wrap(int64(uint64(xUint))); +} + +/// @notice Casts a UD21x18 number into SD21x18. +/// @dev Requirements: +/// - x must be less than or equal to `uMAX_SD21x18`. +function intoSD21x18(UD21x18 x) pure returns (SD21x18 result) { + uint128 xUint = UD21x18.unwrap(x); + if (xUint > uint128(uMAX_SD21x18)) { + revert Errors.PRBMath_UD21x18_IntoSD21x18_Overflow(x); + } + result = SD21x18.wrap(int128(xUint)); +} + +/// @notice Casts a UD21x18 number into SD59x18. +/// @dev There is no overflow check because the domain of UD21x18 is a subset of SD59x18. +function intoSD59x18(UD21x18 x) pure returns (SD59x18 result) { + result = SD59x18.wrap(int256(uint256(UD21x18.unwrap(x)))); +} + +/// @notice Casts a UD21x18 number into UD2x18. +/// @dev Requirements: +/// - x must be less than or equal to `uMAX_UD2x18`. +function intoUD2x18(UD21x18 x) pure returns (UD2x18 result) { + uint128 xUint = UD21x18.unwrap(x); + if (xUint > uMAX_UD2x18) { + revert Errors.PRBMath_UD21x18_IntoUD2x18_Overflow(x); + } + result = UD2x18.wrap(uint64(UD21x18.unwrap(x))); +} + +/// @notice Casts a UD21x18 number into UD60x18. +/// @dev There is no overflow check because the domain of UD21x18 is a subset of UD60x18. +function intoUD60x18(UD21x18 x) pure returns (UD60x18 result) { + result = UD60x18.wrap(UD21x18.unwrap(x)); +} + +/// @notice Casts a UD21x18 number into uint256. +/// @dev There is no overflow check because the domain of UD21x18 is a subset of uint256. +function intoUint256(UD21x18 x) pure returns (uint256 result) { + result = uint256(UD21x18.unwrap(x)); +} + +/// @notice Casts a UD21x18 number into uint40. +/// @dev Requirements: +/// - x must be less than or equal to `MAX_UINT40`. +function intoUint40(UD21x18 x) pure returns (uint40 result) { + uint128 xUint = UD21x18.unwrap(x); + if (xUint > uint128(Common.MAX_UINT40)) { + revert Errors.PRBMath_UD21x18_IntoUint40_Overflow(x); + } + result = uint40(xUint); +} + +/// @notice Casts a UD21x18 number into uint64. +/// @dev Requirements: +/// - x must be less than or equal to `MAX_UINT64`. +function intoUint64(UD21x18 x) pure returns (uint64 result) { + uint128 xUint = UD21x18.unwrap(x); + if (xUint > uint128(Common.MAX_UINT64)) { + revert Errors.PRBMath_UD21x18_IntoUint64_Overflow(x); + } + result = uint64(xUint); +} + +/// @notice Alias for {wrap}. +function ud21x18(uint128 x) pure returns (UD21x18 result) { + result = UD21x18.wrap(x); +} + +/// @notice Unwrap a UD21x18 number into uint128. +function unwrap(UD21x18 x) pure returns (uint128 result) { + result = UD21x18.unwrap(x); +} + +/// @notice Wraps a uint128 number into UD21x18. +function wrap(uint128 x) pure returns (UD21x18 result) { + result = UD21x18.wrap(x); +} diff --git a/src/ud21x18/Constants.sol b/src/ud21x18/Constants.sol new file mode 100644 index 00000000..2c9c9fc2 --- /dev/null +++ b/src/ud21x18/Constants.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.19; + +import { UD21x18 } from "./ValueType.sol"; + +/// @dev Euler's number as a UD21x18 number. +UD21x18 constant E = UD21x18.wrap(2_718281828459045235); + +/// @dev The maximum value a UD21x18 number can have. +uint128 constant uMAX_UD21x18 = 340282366920938463463_374607431768211455; +UD21x18 constant MAX_UD21x18 = UD21x18.wrap(uMAX_UD21x18); + +/// @dev PI as a UD21x18 number. +UD21x18 constant PI = UD21x18.wrap(3_141592653589793238); + +/// @dev The unit number, which gives the decimal precision of UD21x18. +uint256 constant uUNIT = 1e18; +UD21x18 constant UNIT = UD21x18.wrap(1e18); diff --git a/src/ud21x18/Errors.sol b/src/ud21x18/Errors.sol new file mode 100644 index 00000000..9c3effb9 --- /dev/null +++ b/src/ud21x18/Errors.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.19; + +import { UD21x18 } from "./ValueType.sol"; + +/// @notice Thrown when trying to cast a UD21x18 number that doesn't fit in SD1x18. +error PRBMath_UD21x18_IntoSD1x18_Overflow(UD21x18 x); + +/// @notice Thrown when trying to cast a UD21x18 number that doesn't fit in SD21x18. +error PRBMath_UD21x18_IntoSD21x18_Overflow(UD21x18 x); + +/// @notice Thrown when trying to cast a UD21x18 number that doesn't fit in UD2x18. +error PRBMath_UD21x18_IntoUD2x18_Overflow(UD21x18 x); + +/// @notice Thrown when trying to cast a UD21x18 number that doesn't fit in uint40. +error PRBMath_UD21x18_IntoUint40_Overflow(UD21x18 x); + +/// @notice Thrown when trying to cast a UD21x18 number that doesn't fit in uint64. +error PRBMath_UD21x18_IntoUint64_Overflow(UD21x18 x); diff --git a/src/ud21x18/ValueType.sol b/src/ud21x18/ValueType.sol new file mode 100644 index 00000000..464d891b --- /dev/null +++ b/src/ud21x18/ValueType.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.19; + +import "./Casting.sol" as Casting; + +/// @notice The unsigned 21.18-decimal fixed-point number representation, which can have up to 21 digits and up to 18 +/// decimals. The values of this are bound by the minimum and the maximum values permitted by the underlying Solidity +/// type uint128. This is useful when end users want to use uint128 to save gas, e.g. with tight variable packing in contract +/// storage. +type UD21x18 is uint128; + +/*////////////////////////////////////////////////////////////////////////// + CASTING +//////////////////////////////////////////////////////////////////////////*/ + +using { + Casting.intoSD1x18, + Casting.intoSD21x18, + Casting.intoSD59x18, + Casting.intoUD2x18, + Casting.intoUD60x18, + Casting.intoUint256, + Casting.intoUint40, + Casting.intoUint64, + Casting.unwrap +} for UD21x18 global; diff --git a/src/ud2x18/Casting.sol b/src/ud2x18/Casting.sol index d3b40ec8..5b076c48 100644 --- a/src/ud2x18/Casting.sol +++ b/src/ud2x18/Casting.sol @@ -5,7 +5,9 @@ import "../Common.sol" as Common; import "./Errors.sol" as Errors; import { uMAX_SD1x18 } from "../sd1x18/Constants.sol"; import { SD1x18 } from "../sd1x18/ValueType.sol"; +import { SD21x18 } from "../sd21x18/ValueType.sol"; import { SD59x18 } from "../sd59x18/ValueType.sol"; +import { UD21x18 } from "../ud21x18/ValueType.sol"; import { UD60x18 } from "../ud60x18/ValueType.sol"; import { UD2x18 } from "./ValueType.sol"; @@ -19,12 +21,24 @@ function intoSD1x18(UD2x18 x) pure returns (SD1x18 result) { result = SD1x18.wrap(int64(xUint)); } +/// @notice Casts a UD2x18 number into SD21x18. +/// @dev There is no overflow check because the domain of UD2x18 is a subset of SD21x18. +function intoSD21x18(UD2x18 x) pure returns (SD21x18 result) { + result = SD21x18.wrap(int128(uint128(UD2x18.unwrap(x)))); +} + /// @notice Casts a UD2x18 number into SD59x18. /// @dev There is no overflow check because the domain of UD2x18 is a subset of SD59x18. function intoSD59x18(UD2x18 x) pure returns (SD59x18 result) { result = SD59x18.wrap(int256(uint256(UD2x18.unwrap(x)))); } +/// @notice Casts a UD2x18 number into UD21x18. +/// @dev There is no overflow check because the domain of UD2x18 is a subset of UD21x18. +function intoUD21x18(UD2x18 x) pure returns (UD21x18 result) { + result = UD21x18.wrap(UD2x18.unwrap(x)); +} + /// @notice Casts a UD2x18 number into UD60x18. /// @dev There is no overflow check because the domain of UD2x18 is a subset of UD60x18. function intoUD60x18(UD2x18 x) pure returns (UD60x18 result) { diff --git a/src/ud2x18/ValueType.sol b/src/ud2x18/ValueType.sol index 7aed31b0..8b5000d4 100644 --- a/src/ud2x18/ValueType.sol +++ b/src/ud2x18/ValueType.sol @@ -15,10 +15,12 @@ type UD2x18 is uint64; using { Casting.intoSD1x18, + Casting.intoSD21x18, Casting.intoSD59x18, + Casting.intoUD21x18, Casting.intoUD60x18, - Casting.intoUint256, Casting.intoUint128, + Casting.intoUint256, Casting.intoUint40, Casting.unwrap } for UD2x18 global; diff --git a/src/ud60x18/Casting.sol b/src/ud60x18/Casting.sol index 855d9192..7fadb45a 100644 --- a/src/ud60x18/Casting.sol +++ b/src/ud60x18/Casting.sol @@ -5,10 +5,14 @@ import "./Errors.sol" as CastingErrors; import { MAX_UINT128, MAX_UINT40 } from "../Common.sol"; import { uMAX_SD1x18 } from "../sd1x18/Constants.sol"; import { SD1x18 } from "../sd1x18/ValueType.sol"; +import { uMAX_SD21x18 } from "../sd21x18/Constants.sol"; +import { SD21x18 } from "../sd21x18/ValueType.sol"; import { uMAX_SD59x18 } from "../sd59x18/Constants.sol"; import { SD59x18 } from "../sd59x18/ValueType.sol"; import { uMAX_UD2x18 } from "../ud2x18/Constants.sol"; +import { uMAX_UD21x18 } from "../ud21x18/Constants.sol"; import { UD2x18 } from "../ud2x18/ValueType.sol"; +import { UD21x18 } from "../ud21x18/ValueType.sol"; import { UD60x18 } from "./ValueType.sol"; /// @notice Casts a UD60x18 number into SD1x18. @@ -22,6 +26,17 @@ function intoSD1x18(UD60x18 x) pure returns (SD1x18 result) { result = SD1x18.wrap(int64(uint64(xUint))); } +/// @notice Casts a UD60x18 number into SD21x18. +/// @dev Requirements: +/// - x must be less than or equal to `uMAX_SD21x18`. +function intoSD21x18(UD60x18 x) pure returns (SD21x18 result) { + uint256 xUint = UD60x18.unwrap(x); + if (xUint > uint256(int256(uMAX_SD21x18))) { + revert CastingErrors.PRBMath_UD60x18_IntoSD21x18_Overflow(x); + } + result = SD21x18.wrap(int128(uint128(xUint))); +} + /// @notice Casts a UD60x18 number into UD2x18. /// @dev Requirements: /// - x must be less than or equal to `uMAX_UD2x18`. @@ -33,6 +48,17 @@ function intoUD2x18(UD60x18 x) pure returns (UD2x18 result) { result = UD2x18.wrap(uint64(xUint)); } +/// @notice Casts a UD60x18 number into UD21x18. +/// @dev Requirements: +/// - x must be less than or equal to `uMAX_UD21x18`. +function intoUD21x18(UD60x18 x) pure returns (UD21x18 result) { + uint256 xUint = UD60x18.unwrap(x); + if (xUint > uMAX_UD21x18) { + revert CastingErrors.PRBMath_UD60x18_IntoUD21x18_Overflow(x); + } + result = UD21x18.wrap(uint128(xUint)); +} + /// @notice Casts a UD60x18 number into SD59x18. /// @dev Requirements: /// - x must be less than or equal to `uMAX_SD59x18`. diff --git a/src/ud60x18/Errors.sol b/src/ud60x18/Errors.sol index c47e28c1..fede85bb 100644 --- a/src/ud60x18/Errors.sol +++ b/src/ud60x18/Errors.sol @@ -21,12 +21,18 @@ error PRBMath_UD60x18_Gm_Overflow(UD60x18 x, UD60x18 y); /// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in SD1x18. error PRBMath_UD60x18_IntoSD1x18_Overflow(UD60x18 x); +/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in SD21x18. +error PRBMath_UD60x18_IntoSD21x18_Overflow(UD60x18 x); + /// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in SD59x18. error PRBMath_UD60x18_IntoSD59x18_Overflow(UD60x18 x); /// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in UD2x18. error PRBMath_UD60x18_IntoUD2x18_Overflow(UD60x18 x); +/// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in UD21x18. +error PRBMath_UD60x18_IntoUD21x18_Overflow(UD60x18 x); + /// @notice Thrown when trying to cast a UD60x18 number that doesn't fit in uint128. error PRBMath_UD60x18_IntoUint128_Overflow(UD60x18 x); diff --git a/src/ud60x18/ValueType.sol b/src/ud60x18/ValueType.sol index a5a8cc62..82a31f5f 100644 --- a/src/ud60x18/ValueType.sol +++ b/src/ud60x18/ValueType.sol @@ -16,8 +16,10 @@ type UD60x18 is uint256; using { Casting.intoSD1x18, - Casting.intoUD2x18, + Casting.intoSD21x18, Casting.intoSD59x18, + Casting.intoUD2x18, + Casting.intoUD21x18, Casting.intoUint128, Casting.intoUint256, Casting.intoUint40, diff --git a/test/Base.t.sol b/test/Base.t.sol index d44bb822..80489e26 100644 --- a/test/Base.t.sol +++ b/test/Base.t.sol @@ -37,6 +37,8 @@ abstract contract Base_Test is StdAssertions, StdCheats, PRBMathAssertions, PRBM uint128 internal constant MAX_UINT40 = type(uint40).max; + uint128 internal constant MAX_UINT64 = type(uint64).max; + int256 internal constant MIN_INT256 = type(int256).min; /*////////////////////////////////////////////////////////////////////////// diff --git a/test/fuzz/casting/CastingUint128.t.sol b/test/fuzz/casting/CastingUint128.t.sol index efe10ed9..052ff376 100644 --- a/test/fuzz/casting/CastingUint128.t.sol +++ b/test/fuzz/casting/CastingUint128.t.sol @@ -8,9 +8,12 @@ import { } from "src/casting/Uint128.sol"; import { uMAX_SD1x18 } from "src/sd1x18/Constants.sol"; import { SD1x18 } from "src/sd1x18/ValueType.sol"; +import { uMAX_SD21x18 } from "src/sd21x18/Constants.sol"; +import { SD21x18 } from "src/sd21x18/ValueType.sol"; import { SD59x18 } from "src/sd59x18/ValueType.sol"; import { uMAX_UD2x18 } from "src/ud2x18/Constants.sol"; import { UD2x18 } from "src/ud2x18/ValueType.sol"; +import { UD21x18 } from "src/ud21x18/ValueType.sol"; import { UD60x18 } from "src/ud60x18/ValueType.sol"; import { Base_Test } from "../../Base.t.sol"; @@ -32,6 +35,13 @@ contract CastingUint128_Test is Base_Test { assertEq(actual, expected, "uint128 intoSD1x18"); } + function testFuzz_intoSD21x18(uint128 x) external pure { + x = boundUint128(x, 0, uint128(int128(uMAX_SD21x18))); + SD21x18 actual = x.intoSD21x18(); + SD21x18 expected = SD21x18.wrap(int128(int128(x))); + assertEq(actual, expected, "uint128 intoSD21x18"); + } + function testFuzz_intoSD59x18(uint128 x) external pure { SD59x18 actual = x.intoSD59x18(); SD59x18 expected = SD59x18.wrap(int256(uint256(x))); @@ -51,6 +61,12 @@ contract CastingUint128_Test is Base_Test { assertEq(actual, expected, "uint128 intoUD2x18"); } + function testFuzz_intoUD21x18(uint128 x) external pure { + UD21x18 actual = x.intoUD21x18(); + UD21x18 expected = UD21x18.wrap(x); + assertEq(actual, expected, "uint128 intoUD21x18"); + } + function testFuzz_intoUD60x18(uint128 x) external pure { UD60x18 actual = x.intoUD60x18(); UD60x18 expected = UD60x18.wrap(uint256(x)); diff --git a/test/fuzz/casting/CastingUint256.t.sol b/test/fuzz/casting/CastingUint256.t.sol index ffcc2961..c0bf99bf 100644 --- a/test/fuzz/casting/CastingUint256.t.sol +++ b/test/fuzz/casting/CastingUint256.t.sol @@ -9,10 +9,14 @@ import { } from "src/casting/Uint256.sol"; import { uMAX_SD1x18 } from "src/sd1x18/Constants.sol"; import { SD1x18 } from "src/sd1x18/ValueType.sol"; +import { uMAX_SD21x18 } from "src/sd21x18/Constants.sol"; +import { SD21x18 } from "src/sd21x18/ValueType.sol"; import { uMAX_SD59x18 } from "src/sd59x18/Constants.sol"; import { SD59x18 } from "src/sd59x18/ValueType.sol"; import { uMAX_UD2x18 } from "src/ud2x18/Constants.sol"; +import { uMAX_UD21x18 } from "src/ud21x18/Constants.sol"; import { UD2x18 } from "src/ud2x18/ValueType.sol"; +import { UD21x18 } from "src/ud21x18/ValueType.sol"; import { UD60x18 } from "src/ud60x18/ValueType.sol"; import { Base_Test } from "../../Base.t.sol"; @@ -34,6 +38,13 @@ contract CastingUint256_Test is Base_Test { assertEq(actual, expected, "uint256 intoSD1x18"); } + function testFuzz_intoSD21x18(uint256 x) external pure { + x = _bound(x, 0, uint128(uMAX_SD21x18)); + SD21x18 actual = x.intoSD21x18(); + SD21x18 expected = SD21x18.wrap(int128(uint128(x))); + assertEq(actual, expected, "uint256 intoSD21x18"); + } + function testFuzz_RevertWhen_OverflowSD59x18(uint256 x) external { x = _bound(x, uint256(uMAX_SD59x18) + 1, type(uint256).max); vm.expectRevert(abi.encodeWithSelector(PRBMath_IntoSD59x18_Overflow.selector, x)); @@ -60,6 +71,13 @@ contract CastingUint256_Test is Base_Test { assertEq(actual, expected, "uint256 intoUD2x18"); } + function testFuzz_intoUD21x18(uint256 x) external pure { + x = _bound(x, 0, uint256(uMAX_UD21x18)); + UD21x18 actual = x.intoUD21x18(); + UD21x18 expected = UD21x18.wrap(uint128(x)); + assertEq(actual, expected, "uint256 intoUD21x18"); + } + function testFuzz_intoUD60x18(uint256 x) external pure { UD60x18 actual = x.intoUD60x18(); UD60x18 expected = UD60x18.wrap(x); diff --git a/test/fuzz/casting/CastingUint40.t.sol b/test/fuzz/casting/CastingUint40.t.sol index ee456676..fa973d1a 100644 --- a/test/fuzz/casting/CastingUint40.t.sol +++ b/test/fuzz/casting/CastingUint40.t.sol @@ -3,8 +3,10 @@ pragma solidity >=0.8.19 <0.9.0; import { PRBMathCastingUint40 as CastingUint40 } from "src/casting/Uint40.sol"; import { SD1x18 } from "src/sd1x18/ValueType.sol"; +import { SD21x18 } from "src/sd21x18/ValueType.sol"; import { SD59x18 } from "src/sd59x18/ValueType.sol"; import { UD2x18 } from "src/ud2x18/ValueType.sol"; +import { UD21x18 } from "src/ud21x18/ValueType.sol"; import { UD60x18 } from "src/ud60x18/ValueType.sol"; import { Base_Test } from "../../Base.t.sol"; @@ -19,6 +21,12 @@ contract CastingUint40_Test is Base_Test { assertEq(actual, expected, "uint40 intoSD1x18"); } + function testFuzz_intoSD21x18(uint40 x) external pure { + SD21x18 actual = x.intoSD21x18(); + SD21x18 expected = SD21x18.wrap(int128(uint128(x))); + assertEq(actual, expected, "uint40 intoSD21x18"); + } + function testFuzz_intoSD59x18(uint40 x) external pure { SD59x18 actual = x.intoSD59x18(); SD59x18 expected = SD59x18.wrap(int256(uint256(x))); @@ -31,6 +39,12 @@ contract CastingUint40_Test is Base_Test { assertEq(actual, expected, "uint40 intoUD2x18"); } + function testFuzz_intoUD21x18(uint40 x) external pure { + UD21x18 actual = x.intoUD21x18(); + UD21x18 expected = UD21x18.wrap(uint128(x)); + assertEq(actual, expected, "uint40 intoUD21x18"); + } + function testFuzz_intoUD60x18(uint40 x) external pure { UD60x18 actual = x.intoUD60x18(); UD60x18 expected = UD60x18.wrap(uint256(x)); diff --git a/test/fuzz/sd1x18/casting/Casting.t.sol b/test/fuzz/sd1x18/casting/Casting.t.sol index f72206e1..d3596545 100644 --- a/test/fuzz/sd1x18/casting/Casting.t.sol +++ b/test/fuzz/sd1x18/casting/Casting.t.sol @@ -3,10 +3,12 @@ pragma solidity >=0.8.19 <0.9.0; import { sd1x18, wrap } from "src/sd1x18/Casting.sol"; import { SD1x18 } from "src/sd1x18/ValueType.sol"; +import { SD21x18 } from "src/sd21x18/ValueType.sol"; import { SD59x18 } from "src/sd59x18/ValueType.sol"; import { MAX_SD1x18, MIN_SD1x18 } from "src/sd1x18/Constants.sol"; import { PRBMath_SD1x18_ToUD2x18_Underflow, + PRBMath_SD1x18_ToUD21x18_Underflow, PRBMath_SD1x18_ToUD60x18_Underflow, PRBMath_SD1x18_ToUint128_Underflow, PRBMath_SD1x18_ToUint256_Underflow, @@ -14,12 +16,19 @@ import { PRBMath_SD1x18_ToUint40_Underflow } from "src/sd1x18/Errors.sol"; import { UD2x18 } from "src/ud2x18/ValueType.sol"; +import { UD21x18 } from "src/ud21x18/ValueType.sol"; import { UD60x18 } from "src/ud60x18/ValueType.sol"; import { Base_Test } from "../../../Base.t.sol"; /// @dev Collection of tests for the casting functions available in SD1x18. contract Casting_Fuzz_Test is Base_Test { + function testFuzz_IntoSD21x18(SD1x18 x) external pure { + SD21x18 actual = x.intoSD21x18(); + SD21x18 expected = SD21x18.wrap(int128(x.unwrap())); + assertEq(actual, expected, "SD1x18 intoSD21x18"); + } + function testFuzz_IntoSD59x18(SD1x18 x) external pure { SD59x18 actual = x.intoSD59x18(); SD59x18 expected = SD59x18.wrap(int256(x.unwrap())); @@ -39,6 +48,19 @@ contract Casting_Fuzz_Test is Base_Test { assertEq(actual, expected, "SD1x18 intoUD2x18"); } + function testFuzz_RevertWhen_UnderflowUD21x18(SD1x18 x) external { + x = _bound(x, MIN_SD1x18, -1); + vm.expectRevert(abi.encodeWithSelector(PRBMath_SD1x18_ToUD21x18_Underflow.selector, x)); + x.intoUD21x18(); + } + + function testFuzz_IntoUD21x18(SD1x18 x) external pure { + x = _bound(x, 0, MAX_SD1x18); + UD21x18 actual = x.intoUD21x18(); + UD21x18 expected = UD21x18.wrap(uint64(x.unwrap())); + assertEq(actual, expected, "SD1x18 intoUD21x18"); + } + function testFuzz_RevertWhen_UnderflowUD60x18(SD1x18 x) external { x = _bound(x, MIN_SD1x18, -1); vm.expectRevert(abi.encodeWithSelector(PRBMath_SD1x18_ToUD60x18_Underflow.selector, x)); diff --git a/test/fuzz/sd21x18/casting/Casting.t.sol b/test/fuzz/sd21x18/casting/Casting.t.sol new file mode 100644 index 00000000..76268b9d --- /dev/null +++ b/test/fuzz/sd21x18/casting/Casting.t.sol @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.19 <0.9.0; + +import { SD1x18 } from "src/sd1x18/ValueType.sol"; +import { uMIN_SD1x18, uMAX_SD1x18 } from "src/sd1x18/Constants.sol"; +import { sd21x18, wrap } from "src/sd21x18/Casting.sol"; +import { SD21x18 } from "src/sd21x18/ValueType.sol"; +import { SD59x18 } from "src/sd59x18/ValueType.sol"; +import { MAX_SD21x18, MIN_SD21x18 } from "src/sd21x18/Constants.sol"; +import { + PRBMath_SD21x18_IntoSD1x18_Overflow, + PRBMath_SD21x18_IntoSD1x18_Underflow, + PRBMath_SD21x18_ToUD2x18_Underflow, + PRBMath_SD21x18_ToUD60x18_Underflow, + PRBMath_SD21x18_ToUint128_Underflow, + PRBMath_SD21x18_ToUint256_Underflow, + PRBMath_SD21x18_ToUint40_Overflow, + PRBMath_SD21x18_ToUint40_Underflow +} from "src/sd21x18/Errors.sol"; +import { uMAX_UD2x18 } from "src/ud2x18/Constants.sol"; +import { UD2x18 } from "src/ud2x18/ValueType.sol"; +import { UD60x18 } from "src/ud60x18/ValueType.sol"; + +import { Base_Test } from "../../../Base.t.sol"; + +/// @dev Collection of tests for the casting functions available in SD21x18. +contract Casting_Fuzz_Test is Base_Test { + function testFuzz_RevertWhen_OverflowSD1x18(SD21x18 x) external { + x = _bound(x, int128(uMAX_SD1x18) + 1, MAX_SD21x18); + vm.expectRevert(abi.encodeWithSelector(PRBMath_SD21x18_IntoSD1x18_Overflow.selector, x)); + x.intoSD1x18(); + } + + function testFuzz_RevertWhen_UnderflowSD1x18(SD21x18 x) external { + x = _bound(x, MIN_SD21x18, int128(uMIN_SD1x18) - 1); + vm.expectRevert(abi.encodeWithSelector(PRBMath_SD21x18_IntoSD1x18_Underflow.selector, x)); + x.intoSD1x18(); + } + + function testFuzz_IntoSD1x18(SD21x18 x) external pure { + x = _bound(x, int128(uMIN_SD1x18), uMAX_SD1x18); + SD1x18 actual = x.intoSD1x18(); + SD1x18 expected = SD1x18.wrap(int64(x.unwrap())); + assertEq(actual, expected, "SD59x18 intoSD1x18"); + } + + function testFuzz_IntoSD59x18(SD21x18 x) external pure { + SD59x18 actual = x.intoSD59x18(); + SD59x18 expected = SD59x18.wrap(int256(x.unwrap())); + assertEq(actual, expected, "SD21x18 intoSD59x18"); + } + + function testFuzz_RevertWhen_OverflowUD2x18(SD21x18 x) external { + x = _bound(x, int128(uint128(uMAX_UD2x18)) + 1, MAX_SD21x18); + vm.expectRevert(abi.encodeWithSelector(PRBMath_SD21x18_IntoSD1x18_Overflow.selector, x)); + x.intoSD1x18(); + } + + function testFuzz_RevertWhen_UnderflowUD2x18(SD21x18 x) external { + x = _bound(x, MIN_SD21x18, -1); + vm.expectRevert(abi.encodeWithSelector(PRBMath_SD21x18_ToUD2x18_Underflow.selector, x)); + x.intoUD2x18(); + } + + function testFuzz_IntoUD2x18(SD21x18 x) external pure { + x = _bound(x, 0, int128(uint128(uMAX_UD2x18))); + UD2x18 actual = x.intoUD2x18(); + UD2x18 expected = UD2x18.wrap(uint64(uint128(x.unwrap()))); + assertEq(actual, expected, "SD21x18 intoUD2x18"); + } + + function testFuzz_RevertWhen_UnderflowUD60x18(SD21x18 x) external { + x = _bound(x, MIN_SD21x18, -1); + vm.expectRevert(abi.encodeWithSelector(PRBMath_SD21x18_ToUD60x18_Underflow.selector, x)); + x.intoUD60x18(); + } + + function testFuzz_IntoUD60x18(SD21x18 x) external pure { + x = _bound(x, 0, MAX_SD21x18); + UD60x18 actual = x.intoUD60x18(); + UD60x18 expected = UD60x18.wrap(uint128(x.unwrap())); + assertEq(actual, expected, "SD21x18 intoUD60x18"); + } + + function testFuzz_RevertWhen_UnderflowUint256(SD21x18 x) external { + x = _bound(x, MIN_SD21x18, -1); + vm.expectRevert(abi.encodeWithSelector(PRBMath_SD21x18_ToUint256_Underflow.selector, x)); + x.intoUint256(); + } + + function testFuzz_IntoUint256(SD21x18 x) external pure { + x = _bound(x, 0, MAX_SD21x18); + uint256 actual = x.intoUint256(); + uint256 expected = uint128(x.unwrap()); + assertEq(actual, expected, "SD21x18 intoUint256"); + } + + function testFuzz_RevertWhen_UnderflowUint128(SD21x18 x) external { + x = _bound(x, MIN_SD21x18, -1); + vm.expectRevert(abi.encodeWithSelector(PRBMath_SD21x18_ToUint128_Underflow.selector, x)); + x.intoUint128(); + } + + function testFuzz_IntoUint128(SD21x18 x) external pure { + x = _bound(x, 0, MAX_SD21x18); + uint128 actual = x.intoUint128(); + uint128 expected = uint128(x.unwrap()); + assertEq(actual, expected, "SD21x18 intoUint128"); + } + + function testFuzz_RevertWhen_UnderflowUint40(SD21x18 x) external { + x = _bound(x, MIN_SD21x18, -1); + vm.expectRevert(abi.encodeWithSelector(PRBMath_SD21x18_ToUint40_Underflow.selector, x)); + x.intoUint40(); + } + + function testFuzz_RevertWhen_OverflowUint40(SD21x18 x) external { + x = _bound(x, int64(uint64(MAX_UINT40)) + 1, MAX_SD21x18); + vm.expectRevert(abi.encodeWithSelector(PRBMath_SD21x18_ToUint40_Overflow.selector, x)); + x.intoUint40(); + } + + function testFuzz_IntoUint40(SD21x18 x) external pure { + x = _bound(x, 0, int128(uint128(MAX_UINT40))); + uint40 actual = x.intoUint40(); + uint40 expected = uint40(uint128(x.unwrap())); + assertEq(actual, expected, "SD21x18 intoUint40"); + } + + function testFuzz_SD21x18(int128 x) external pure { + SD21x18 actual = sd21x18(x); + SD21x18 expected = SD21x18.wrap(x); + assertEq(actual, expected, "SD21x18"); + } + + function testFuzz_Unwrap(SD21x18 x) external pure { + int128 actual = x.unwrap(); + int128 expected = SD21x18.unwrap(x); + assertEq(actual, expected, "SD21x18 unwrap"); + } + + function testFuzz_Wrap(int128 x) external pure { + SD21x18 actual = wrap(x); + SD21x18 expected = SD21x18.wrap(x); + assertEq(actual, expected, "SD21x18 wrap"); + } +} diff --git a/test/fuzz/sd59x18/casting/Casting.t.sol b/test/fuzz/sd59x18/casting/Casting.t.sol index 402e3d79..f2fe4ccd 100644 --- a/test/fuzz/sd59x18/casting/Casting.t.sol +++ b/test/fuzz/sd59x18/casting/Casting.t.sol @@ -3,15 +3,21 @@ pragma solidity >=0.8.19 <0.9.0; import { uMAX_SD1x18, uMIN_SD1x18 } from "src/sd1x18/Constants.sol"; import { SD1x18 } from "src/sd1x18/ValueType.sol"; +import { uMAX_SD21x18, uMIN_SD21x18 } from "src/sd21x18/Constants.sol"; +import { SD21x18 } from "src/sd21x18/ValueType.sol"; import { sd, sd59x18, wrap } from "src/sd59x18/Casting.sol"; import { MAX_SD59x18, MIN_SD59x18 } from "src/sd59x18/Constants.sol"; import { PRBMath_SD59x18_IntoSD1x18_Overflow, PRBMath_SD59x18_IntoSD1x18_Underflow, + PRBMath_SD59x18_IntoSD21x18_Overflow, + PRBMath_SD59x18_IntoSD21x18_Underflow, PRBMath_SD59x18_IntoUD60x18_Underflow, PRBMath_SD59x18_IntoUint256_Underflow, PRBMath_SD59x18_IntoUD2x18_Overflow, PRBMath_SD59x18_IntoUD2x18_Underflow, + PRBMath_SD59x18_IntoUD21x18_Overflow, + PRBMath_SD59x18_IntoUD21x18_Underflow, PRBMath_SD59x18_IntoUint128_Overflow, PRBMath_SD59x18_IntoUint128_Underflow, PRBMath_SD59x18_IntoUint40_Overflow, @@ -20,6 +26,8 @@ import { import { SD59x18 } from "src/sd59x18/ValueType.sol"; import { uMAX_UD2x18 } from "src/ud2x18/Constants.sol"; import { UD2x18 } from "src/ud2x18/ValueType.sol"; +import { uMAX_UD21x18 } from "src/ud21x18/Constants.sol"; +import { UD21x18 } from "src/ud21x18/ValueType.sol"; import { UD60x18 } from "src/ud60x18/ValueType.sol"; import { Base_Test } from "../../../Base.t.sol"; @@ -51,6 +59,25 @@ contract SD59x18_Casting_Fuzz_Test is Base_Test { assertEq(actual, expected, "SD59x18 intoSD1x18"); } + function testFuzz_RevertWhen_UnderflowSD21x18(SD59x18 x) external { + x = _bound(x, MIN_SD59x18, int256(uMIN_SD21x18) - 1); + vm.expectRevert(abi.encodeWithSelector(PRBMath_SD59x18_IntoSD21x18_Underflow.selector, x)); + x.intoSD21x18(); + } + + function testFuzz_RevertWhen_OverflowSD21x18(SD59x18 x) external { + x = _bound(x, int256(uMAX_SD21x18) + 1, MAX_SD59x18); + vm.expectRevert(abi.encodeWithSelector(PRBMath_SD59x18_IntoSD21x18_Overflow.selector, x)); + x.intoSD21x18(); + } + + function testFuzz_IntoSD21x18(SD59x18 x) external pure { + x = _bound(x, uMIN_SD21x18, uMAX_SD21x18); + SD21x18 actual = x.intoSD21x18(); + SD21x18 expected = SD21x18.wrap(int128(x.unwrap())); + assertEq(actual, expected, "SD59x18 intoSD21x18"); + } + function testFuzz_RevertWhen_UnderflowUD2x18(SD59x18 x) external { x = _bound(x, MIN_SD59x18, -1); vm.expectRevert(abi.encodeWithSelector(PRBMath_SD59x18_IntoUD2x18_Underflow.selector, x)); @@ -70,6 +97,25 @@ contract SD59x18_Casting_Fuzz_Test is Base_Test { assertEq(actual, expected, "SD59x18 intoUD2x18"); } + function testFuzz_RevertWhen_UnderflowUD21x18(SD59x18 x) external { + x = _bound(x, MIN_SD59x18, -1); + vm.expectRevert(abi.encodeWithSelector(PRBMath_SD59x18_IntoUD21x18_Underflow.selector, x)); + x.intoUD21x18(); + } + + function testFuzz_RevertWhen_OverflowUD21x18(SD59x18 x) external { + x = _bound(x, int256(uint256(uMAX_UD21x18)) + 1, MAX_SD59x18); + vm.expectRevert(abi.encodeWithSelector(PRBMath_SD59x18_IntoUD21x18_Overflow.selector, x)); + x.intoUD21x18(); + } + + function testFuzz_IntoUD21x18(SD59x18 x) external pure { + x = _bound(x, 0, int256(uint256(uMAX_UD21x18))); + UD21x18 actual = x.intoUD21x18(); + UD21x18 expected = UD21x18.wrap(uint128(uint256(x.unwrap()))); + assertEq(actual, expected, "SD59x18 intoUD21x18"); + } + function testFuzz_RevertWhen_UnderflowUD60x18(SD59x18 x) external { x = _bound(x, MIN_SD59x18, -1); vm.expectRevert(abi.encodeWithSelector(PRBMath_SD59x18_IntoUD60x18_Underflow.selector, x)); diff --git a/test/fuzz/ud21x18/casting/Casting.t.sol b/test/fuzz/ud21x18/casting/Casting.t.sol new file mode 100644 index 00000000..70307e21 --- /dev/null +++ b/test/fuzz/ud21x18/casting/Casting.t.sol @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.19 <0.9.0; + +import { uMAX_SD1x18 } from "src/sd1x18/Constants.sol"; +import { SD1x18 } from "src/sd1x18/ValueType.sol"; +import { uMAX_SD21x18 } from "src/sd21x18/Constants.sol"; +import { SD21x18 } from "src/sd21x18/ValueType.sol"; +import { SD59x18 } from "src/sd59x18/ValueType.sol"; +import { ud21x18, wrap } from "src/ud21x18/Casting.sol"; +import { uMAX_UD21x18 } from "src/ud21x18/Constants.sol"; +import { + PRBMath_UD21x18_IntoSD1x18_Overflow, + PRBMath_UD21x18_IntoSD21x18_Overflow, + PRBMath_UD21x18_IntoUD2x18_Overflow, + PRBMath_UD21x18_IntoUint40_Overflow, + PRBMath_UD21x18_IntoUint64_Overflow +} from "src/ud21x18/Errors.sol"; +import { uMAX_UD2x18 } from "src/ud2x18/Constants.sol"; +import { UD2x18 } from "src/ud2x18/ValueType.sol"; +import { UD21x18 } from "src/ud21x18/ValueType.sol"; +import { UD60x18 } from "src/ud60x18/ValueType.sol"; +import { Base_Test } from "../../../Base.t.sol"; + +/// @dev Collection of tests for the casting functions available in UD21x18. +contract UD21x18_Casting_Fuzz_Test is Base_Test { + function testFuzz_RevertWhen_OverflowSD1x18(UD21x18 x) external { + x = _bound(x, uint128(uint64(uMAX_SD1x18)) + 1, uMAX_UD21x18); + vm.expectRevert(abi.encodeWithSelector(PRBMath_UD21x18_IntoSD1x18_Overflow.selector, x)); + x.intoSD1x18(); + } + + function testFuzz_IntoSD1x18(UD21x18 x) external pure { + x = _bound(x, 0, uint128(uint64(uMAX_SD1x18))); + SD1x18 actual = x.intoSD1x18(); + SD1x18 expected = SD1x18.wrap(int64(uint64(uint128(x.unwrap())))); + assertEq(actual, expected, "UD21x18 intoSD1x18"); + } + + function testFuzz_RevertWhen_OverflowSD21x18(UD21x18 x) external { + x = _bound(x, uint128(uMAX_SD21x18) + 1, uMAX_UD21x18); + vm.expectRevert(abi.encodeWithSelector(PRBMath_UD21x18_IntoSD21x18_Overflow.selector, x)); + x.intoSD21x18(); + } + + function testFuzz_IntoSD21x18(UD21x18 x) external pure { + x = _bound(x, 0, uint128(uMAX_SD21x18)); + SD21x18 actual = x.intoSD21x18(); + SD21x18 expected = SD21x18.wrap(int128(uint128(x.unwrap()))); + assertEq(actual, expected, "UD21x18 intoSD21x18"); + } + + function testFuzz_IntoSD59x18(UD21x18 x) external pure { + SD59x18 actual = x.intoSD59x18(); + SD59x18 expected = SD59x18.wrap(int256(uint256(x.unwrap()))); + assertEq(actual, expected, "UD21x18 intoSD59x18"); + } + + function testFuzz_RevertWhen_OverflowUD2x18(UD21x18 x) external { + x = _bound(x, uint128(uMAX_UD2x18) + 1, uMAX_UD21x18); + vm.expectRevert(abi.encodeWithSelector(PRBMath_UD21x18_IntoUD2x18_Overflow.selector, x)); + x.intoUD2x18(); + } + + function testFuzz_IntoUD2x18(UD21x18 x) external pure { + x = _bound(x, 0, uMAX_UD2x18); + UD2x18 actual = x.intoUD2x18(); + UD2x18 expected = UD2x18.wrap(uint64(uint128(x.unwrap()))); + assertEq(actual, expected, "UD21x18 intoUD60x18"); + } + + function testFuzz_IntoUD60x18(UD21x18 x) external pure { + UD60x18 actual = x.intoUD60x18(); + UD60x18 expected = UD60x18.wrap(uint256(x.unwrap())); + assertEq(actual, expected, "UD21x18 intoUD60x18"); + } + + function testFuzz_IntoUint256(UD21x18 x) external pure { + uint256 actual = x.intoUint256(); + uint256 expected = uint256(x.unwrap()); + assertEq(actual, expected, "UD21x18 intoUint256"); + } + + function testFuzz_RevertWhen_OverflowUint40(UD21x18 x) external { + x = _bound(x, uint128(MAX_UINT40) + 1, uMAX_UD21x18); + vm.expectRevert(abi.encodeWithSelector(PRBMath_UD21x18_IntoUint40_Overflow.selector, x)); + x.intoUint40(); + } + + function testFuzz_IntoUint40(UD21x18 x) external pure { + x = _bound(x, 0, uint128(MAX_UINT40)); + uint40 actual = x.intoUint40(); + uint40 expected = uint40(x.unwrap()); + assertEq(actual, expected, "UD21x18 intoUint40"); + } + + function testFuzz_RevertWhen_OverflowUint64(UD21x18 x) external { + x = _bound(x, uint128(MAX_UINT64) + 1, uMAX_UD21x18); + vm.expectRevert(abi.encodeWithSelector(PRBMath_UD21x18_IntoUint64_Overflow.selector, x)); + x.intoUint64(); + } + + function testFuzz_IntoUint64(UD21x18 x) external pure { + x = _bound(x, 0, uint128(MAX_UINT64)); + uint64 actual = x.intoUint64(); + uint64 expected = uint64(x.unwrap()); + assertEq(actual, expected, "UD21x18 intoUint64"); + } + + function testFuzz_ud21x18(uint128 x) external pure { + UD21x18 actual = ud21x18(x); + UD21x18 expected = UD21x18.wrap(x); + assertEq(actual, expected, "ud21x18"); + } + + function testFuzz_Unwrap(UD21x18 x) external pure { + uint128 actual = x.unwrap(); + uint128 expected = UD21x18.unwrap(x); + assertEq(actual, expected, "UD21x18 unwrap"); + } + + function testFuzz_Wrap(uint128 x) external pure { + UD21x18 actual = wrap(x); + UD21x18 expected = UD21x18.wrap(x); + assertEq(actual, expected, "UD21x18 wrap"); + } +} diff --git a/test/fuzz/ud2x18/casting/Casting.t.sol b/test/fuzz/ud2x18/casting/Casting.t.sol index 67f75930..fcae815d 100644 --- a/test/fuzz/ud2x18/casting/Casting.t.sol +++ b/test/fuzz/ud2x18/casting/Casting.t.sol @@ -3,11 +3,13 @@ pragma solidity >=0.8.19 <0.9.0; import { uMAX_SD1x18 } from "src/sd1x18/Constants.sol"; import { SD1x18 } from "src/sd1x18/ValueType.sol"; +import { SD21x18 } from "src/sd21x18/ValueType.sol"; import { SD59x18 } from "src/sd59x18/ValueType.sol"; import { ud2x18, wrap } from "src/ud2x18/Casting.sol"; import { uMAX_UD2x18 } from "src/ud2x18/Constants.sol"; import { PRBMath_UD2x18_IntoSD1x18_Overflow, PRBMath_UD2x18_IntoUint40_Overflow } from "src/ud2x18/Errors.sol"; import { UD2x18 } from "src/ud2x18/ValueType.sol"; +import { UD21x18 } from "src/ud21x18/ValueType.sol"; import { UD60x18 } from "src/ud60x18/ValueType.sol"; import { Base_Test } from "../../../Base.t.sol"; @@ -27,12 +29,24 @@ contract UD2x18_Casting_Fuzz_Test is Base_Test { assertEq(actual, expected, "UD2x18 intoSD1x18"); } + function testFuzz_IntoSD21x18(UD2x18 x) external pure { + SD21x18 actual = x.intoSD21x18(); + SD21x18 expected = SD21x18.wrap(int128(uint128(x.unwrap()))); + assertEq(actual, expected, "UD2x18 intoSD21x18"); + } + function testFuzz_IntoSD59x18(UD2x18 x) external pure { SD59x18 actual = x.intoSD59x18(); SD59x18 expected = SD59x18.wrap(int256(uint256(x.unwrap()))); assertEq(actual, expected, "UD2x18 intoSD59x18"); } + function testFuzz_IntoUD21x18(UD2x18 x) external pure { + UD21x18 actual = x.intoUD21x18(); + UD21x18 expected = UD21x18.wrap(uint128(x.unwrap())); + assertEq(actual, expected, "UD2x18 intoUD21x18"); + } + function testFuzz_IntoUD60x18(UD2x18 x) external pure { UD60x18 actual = x.intoUD60x18(); UD60x18 expected = UD60x18.wrap(uint256(x.unwrap())); diff --git a/test/fuzz/ud60x18/casting/Casting.t.sol b/test/fuzz/ud60x18/casting/Casting.t.sol index 2b2d7513..36399a7c 100644 --- a/test/fuzz/ud60x18/casting/Casting.t.sol +++ b/test/fuzz/ud60x18/casting/Casting.t.sol @@ -3,14 +3,19 @@ pragma solidity >=0.8.19 <0.9.0; import { uMAX_SD1x18 } from "src/sd1x18/Constants.sol"; import { SD1x18 } from "src/sd1x18/ValueType.sol"; +import { uMAX_SD21x18 } from "src/sd21x18/Constants.sol"; +import { SD21x18 } from "src/sd21x18/ValueType.sol"; import { uMAX_SD59x18 } from "src/sd59x18/Constants.sol"; import { SD59x18 } from "src/sd59x18/ValueType.sol"; import { uMAX_UD2x18 } from "src/ud2x18/Constants.sol"; +import { uMAX_UD21x18 } from "src/ud21x18/Constants.sol"; import { UD2x18 } from "src/ud2x18/ValueType.sol"; +import { UD21x18 } from "src/ud21x18/ValueType.sol"; import { ud, ud60x18, wrap } from "src/ud60x18/Casting.sol"; import { MAX_UD60x18 } from "src/ud60x18/Constants.sol"; import { PRBMath_UD60x18_IntoSD1x18_Overflow, + PRBMath_UD60x18_IntoSD21x18_Overflow, PRBMath_UD60x18_IntoSD59x18_Overflow, PRBMath_UD60x18_IntoUD2x18_Overflow, PRBMath_UD60x18_IntoUint128_Overflow, @@ -35,6 +40,19 @@ contract UD60x18_Casting_Fuzz_Test is Base_Test { assertEq(actual, expected, "UD60x18 intoSD1x18"); } + function testFuzz_RevertWhen_OverflowSD21x18(UD60x18 x) external { + x = _bound(x, ud(uint128(uMAX_SD21x18) + 1), MAX_UD60x18); + vm.expectRevert(abi.encodeWithSelector(PRBMath_UD60x18_IntoSD21x18_Overflow.selector, x)); + x.intoSD21x18(); + } + + function testFuzz_intoSD21x18(UD60x18 x) external pure { + x = _bound(x, 0, ud(uint128(uMAX_SD21x18))); + SD21x18 actual = x.intoSD21x18(); + SD21x18 expected = SD21x18.wrap(int128(uint128(x.unwrap()))); + assertEq(actual, expected, "UD60x18 intoSD21x18"); + } + function testFuzz_RevertWhen_OverflowSD59x18(UD60x18 x) external { x = _bound(x, ud(uint256(uMAX_SD59x18) + 1), MAX_UD60x18); vm.expectRevert(abi.encodeWithSelector(PRBMath_UD60x18_IntoSD59x18_Overflow.selector, x)); @@ -61,6 +79,13 @@ contract UD60x18_Casting_Fuzz_Test is Base_Test { assertEq(actual, expected, "UD60x18 intoUD2x18"); } + function testFuzz_intoUD21x18(UD60x18 x) external pure { + x = _bound(x, 0, ud(uint256(uMAX_UD21x18))); + UD21x18 actual = x.intoUD21x18(); + UD21x18 expected = UD21x18.wrap(uint128(x.unwrap())); + assertEq(actual, expected, "UD60x18 intoUD21x18"); + } + function testFuzz_RevertWhen_OverflowUint128(UD60x18 x) external { x = _bound(x, ud(uint256(MAX_UINT128) + 1), MAX_UD60x18); vm.expectRevert(abi.encodeWithSelector(PRBMath_UD60x18_IntoUint128_Overflow.selector, x)); diff --git a/test/utils/Assertions.sol b/test/utils/Assertions.sol index 6ff7c660..515a4e16 100644 --- a/test/utils/Assertions.sol +++ b/test/utils/Assertions.sol @@ -4,8 +4,10 @@ pragma solidity >=0.8.19; import { StdAssertions } from "forge-std/src/StdAssertions.sol"; import { SD1x18 } from "../../src/sd1x18/ValueType.sol"; +import { SD21x18 } from "../../src/sd21x18/ValueType.sol"; import { SD59x18 } from "../../src/sd59x18/ValueType.sol"; import { UD2x18 } from "../../src/ud2x18/ValueType.sol"; +import { UD21x18 } from "../../src/ud21x18/ValueType.sol"; import { UD60x18 } from "../../src/ud60x18/ValueType.sol"; contract PRBMathAssertions is StdAssertions { @@ -97,6 +99,94 @@ contract PRBMathAssertions is StdAssertions { assertEq(castedA, castedB, err); } + /*////////////////////////////////////////////////////////////////////////// + SD21X18 + //////////////////////////////////////////////////////////////////////////*/ + + function assertEq(SD21x18 a, SD21x18 b) internal pure { + assertEq(SD21x18.unwrap(a), SD21x18.unwrap(b)); + } + + function assertEq(SD21x18 a, SD21x18 b, string memory err) internal pure { + assertEq(SD21x18.unwrap(a), SD21x18.unwrap(b), err); + } + + function assertEq(SD21x18 a, int64 b) internal pure { + assertEq(SD21x18.unwrap(a), b); + } + + function assertEq(SD21x18 a, int64 b, string memory err) internal pure { + assertEq(SD21x18.unwrap(a), b, err); + } + + function assertEq(int64 a, SD21x18 b) internal pure { + assertEq(a, SD21x18.unwrap(b)); + } + + function assertEq(int64 a, SD21x18 b, string memory err) internal pure { + assertEq(a, SD21x18.unwrap(b), err); + } + + function assertEq(SD21x18[] memory a, SD21x18[] memory b) internal pure { + int256[] memory castedA; + int256[] memory castedB; + assembly { + castedA := a + castedB := b + } + assertEq(castedA, castedB); + } + + function assertEq(SD21x18[] memory a, SD21x18[] memory b, string memory err) internal pure { + int256[] memory castedA; + int256[] memory castedB; + assembly { + castedA := a + castedB := b + } + assertEq(castedA, castedB, err); + } + + function assertEq(SD21x18[] memory a, int64[] memory b) internal pure { + int256[] memory castedA; + int256[] memory castedB; + assembly { + castedA := a + castedB := b + } + assertEq(castedA, castedB); + } + + function assertEq(SD21x18[] memory a, int64[] memory b, string memory err) internal pure { + int256[] memory castedA; + int256[] memory castedB; + assembly { + castedA := a + castedB := b + } + assertEq(castedA, castedB, err); + } + + function assertEq(int64[] memory a, SD21x18[] memory b) internal pure { + int256[] memory castedA; + int256[] memory castedB; + assembly { + castedA := a + castedB := b + } + assertEq(castedA, castedB); + } + + function assertEq(int64[] memory a, SD21x18[] memory b, string memory err) internal pure { + int256[] memory castedA; + int256[] memory castedB; + assembly { + castedA := a + castedB := b + } + assertEq(castedA, castedB, err); + } + /*////////////////////////////////////////////////////////////////////////// SD59X18 //////////////////////////////////////////////////////////////////////////*/ @@ -161,7 +251,7 @@ contract PRBMathAssertions is StdAssertions { assertEq(castedA, b, err); } - function assertEq(int256[] memory a, SD59x18[] memory b) internal { + function assertEq(int256[] memory a, SD59x18[] memory b) internal pure { int256[] memory castedB; assembly { castedB := b @@ -169,7 +259,7 @@ contract PRBMathAssertions is StdAssertions { assertEq(a, b); } - function assertEq(int256[] memory a, SD59x18[] memory b, string memory err) internal { + function assertEq(int256[] memory a, SD59x18[] memory b, string memory err) internal pure { int256[] memory castedB; assembly { castedB := b @@ -265,6 +355,94 @@ contract PRBMathAssertions is StdAssertions { assertEq(castedA, castedB, err); } + /*////////////////////////////////////////////////////////////////////////// + UD21X18 + //////////////////////////////////////////////////////////////////////////*/ + + function assertEq(UD21x18 a, UD21x18 b) internal pure { + assertEq(UD21x18.unwrap(a), UD21x18.unwrap(b)); + } + + function assertEq(UD21x18 a, UD21x18 b, string memory err) internal pure { + assertEq(UD21x18.unwrap(a), UD21x18.unwrap(b), err); + } + + function assertEq(UD21x18 a, uint128 b) internal pure { + assertEq(UD21x18.unwrap(a), uint256(b)); + } + + function assertEq(UD21x18 a, uint128 b, string memory err) internal pure { + assertEq(UD21x18.unwrap(a), uint256(b), err); + } + + function assertEq(uint128 a, UD21x18 b) internal pure { + assertEq(uint256(a), UD21x18.unwrap(b)); + } + + function assertEq(uint128 a, UD21x18 b, string memory err) internal pure { + assertEq(uint256(a), UD21x18.unwrap(b), err); + } + + function assertEq(UD21x18[] memory a, UD21x18[] memory b) internal pure { + uint256[] memory castedA; + uint256[] memory castedB; + assembly { + castedA := a + castedB := b + } + assertEq(castedA, castedB); + } + + function assertEq(UD21x18[] memory a, UD21x18[] memory b, string memory err) internal pure { + uint256[] memory castedA; + uint256[] memory castedB; + assembly { + castedA := a + castedB := b + } + assertEq(castedA, castedB, err); + } + + function assertEq(UD21x18[] memory a, uint128[] memory b) internal pure { + uint256[] memory castedA; + uint256[] memory castedB; + assembly { + castedA := a + castedB := b + } + assertEq(castedA, castedB); + } + + function assertEq(UD21x18[] memory a, uint128[] memory b, string memory err) internal pure { + uint256[] memory castedA; + uint256[] memory castedB; + assembly { + castedA := a + castedB := b + } + assertEq(castedA, castedB, err); + } + + function assertEq(uint128[] memory a, UD21x18[] memory b) internal pure { + uint256[] memory castedA; + uint256[] memory castedB; + assembly { + castedA := a + castedB := b + } + assertEq(castedA, castedB); + } + + function assertEq(uint128[] memory a, UD21x18[] memory b, string memory err) internal pure { + uint256[] memory castedA; + uint256[] memory castedB; + assembly { + castedA := a + castedB := b + } + assertEq(castedA, castedB, err); + } + /*////////////////////////////////////////////////////////////////////////// UD60X18 //////////////////////////////////////////////////////////////////////////*/ @@ -329,7 +507,7 @@ contract PRBMathAssertions is StdAssertions { assertEq(castedA, b, err); } - function assertEq(uint256[] memory a, SD59x18[] memory b) internal { + function assertEq(uint256[] memory a, SD59x18[] memory b) internal pure { uint256[] memory castedB; assembly { castedB := b @@ -337,7 +515,7 @@ contract PRBMathAssertions is StdAssertions { assertEq(a, b); } - function assertEq(uint256[] memory a, SD59x18[] memory b, string memory err) internal { + function assertEq(uint256[] memory a, SD59x18[] memory b, string memory err) internal pure { uint256[] memory castedB; assembly { castedB := b diff --git a/test/utils/Utils.sol b/test/utils/Utils.sol index 1d8baf0b..bc2ee615 100644 --- a/test/utils/Utils.sol +++ b/test/utils/Utils.sol @@ -4,8 +4,10 @@ pragma solidity >=0.8.19; import { StdUtils } from "forge-std/src/StdUtils.sol"; import { SD1x18 } from "../../src/sd1x18/ValueType.sol"; +import { SD21x18 } from "../../src/sd21x18/ValueType.sol"; import { SD59x18 } from "../../src/sd59x18/ValueType.sol"; import { UD2x18 } from "../../src/ud2x18/ValueType.sol"; +import { UD21x18 } from "../../src/ud21x18/ValueType.sol"; import { UD60x18 } from "../../src/ud60x18/ValueType.sol"; contract PRBMathUtils is StdUtils { @@ -13,86 +15,130 @@ contract PRBMathUtils is StdUtils { SD1x18 //////////////////////////////////////////////////////////////////////////*/ - /// @dev Helper function to bound an SD1x18 number, which console logs the bounded result. + /// @dev Helper function to bound an SD1x18 number, which console logs the result. function bound(SD1x18 x, SD1x18 min, SD1x18 max) internal pure returns (SD1x18) { return SD1x18.wrap(int64(bound(int256(x.unwrap()), int256(min.unwrap()), int256(max.unwrap())))); } - /// @dev Helper function to bound an SD1x18 number. + /// @dev Helper function to bound an SD1x18 number, which does NOT console log the result. function _bound(SD1x18 x, SD1x18 min, SD1x18 max) internal pure returns (SD1x18) { return SD1x18.wrap(int64(_bound(int256(x.unwrap()), int256(min.unwrap()), int256(max.unwrap())))); } - /// @dev Helper function to bound an SD1x18 number, which console logs the bounded result. + /// @dev Helper function to bound an SD1x18 number, which console logs the result. function bound(SD1x18 x, int64 min, SD1x18 max) internal pure returns (SD1x18) { return SD1x18.wrap(int64(bound(int256(x.unwrap()), int256(min), int256(max.unwrap())))); } - /// @dev Helper function to bound an SD1x18 number. + /// @dev Helper function to bound an SD1x18 number, which does NOT console log the result. function _bound(SD1x18 x, int64 min, SD1x18 max) internal pure returns (SD1x18) { return SD1x18.wrap(int64(_bound(int256(x.unwrap()), int256(min), int256(max.unwrap())))); } - /// @dev Helper function to bound an SD1x18 number, which console logs the bounded result. + /// @dev Helper function to bound an SD1x18 number, which console logs the result. function bound(SD1x18 x, SD1x18 min, int64 max) internal pure returns (SD1x18) { return SD1x18.wrap(int64(bound(int256(x.unwrap()), int256(min.unwrap()), int256(max)))); } - /// @dev Helper function to bound an SD1x18 number. + /// @dev Helper function to bound an SD1x18 number, which does NOT console log the result. function _bound(SD1x18 x, SD1x18 min, int64 max) internal pure returns (SD1x18) { return SD1x18.wrap(int64(_bound(int256(x.unwrap()), int256(min.unwrap()), int256(max)))); } - /// @dev Helper function to bound an SD1x18 number, which console logs the bounded result. + /// @dev Helper function to bound an SD1x18 number, which console logs the result. function bound(SD1x18 x, int64 min, int64 max) internal pure returns (SD1x18) { return SD1x18.wrap(int64(bound(int256(x.unwrap()), int256(min), int256(max)))); } - /// @dev Helper function to bound an SD1x18 number. + /// @dev Helper function to bound an SD1x18 number, which does NOT console log the result. function _bound(SD1x18 x, int64 min, int64 max) internal pure returns (SD1x18) { return SD1x18.wrap(int64(_bound(int256(x.unwrap()), int256(min), int256(max)))); } + /*////////////////////////////////////////////////////////////////////////// + SD21x18 + //////////////////////////////////////////////////////////////////////////*/ + + /// @dev Helper function to bound an SD21x18 number, which console logs the result. + function bound(SD21x18 x, SD21x18 min, SD21x18 max) internal pure returns (SD21x18) { + return SD21x18.wrap(int128(bound(int256(x.unwrap()), int256(min.unwrap()), int256(max.unwrap())))); + } + + /// @dev Helper function to bound an SD21x18 number, which does NOT console log the result. + function _bound(SD21x18 x, SD21x18 min, SD21x18 max) internal pure returns (SD21x18) { + return SD21x18.wrap(int128(_bound(x.unwrap(), int256(min.unwrap()), int256(max.unwrap())))); + } + + /// @dev Helper function to bound an SD21x18 number, which console logs the result. + function bound(SD21x18 x, int128 min, SD21x18 max) internal pure returns (SD21x18) { + return SD21x18.wrap(int128(bound(int256(x.unwrap()), int256(min), int256(max.unwrap())))); + } + + /// @dev Helper function to bound an SD21x18 number, which does NOT console log the result. + function _bound(SD21x18 x, int128 min, SD21x18 max) internal pure returns (SD21x18) { + return SD21x18.wrap(int128(_bound(x.unwrap(), min, max.unwrap()))); + } + + /// @dev Helper function to bound an SD21x18 number, which console logs the result. + function bound(SD21x18 x, SD21x18 min, int128 max) internal pure returns (SD21x18) { + return SD21x18.wrap(int128(bound(int256(x.unwrap()), int256(min.unwrap()), int256(max)))); + } + + /// @dev Helper function to bound an SD21x18 number, which does NOT console log the result. + function _bound(SD21x18 x, SD21x18 min, int128 max) internal pure returns (SD21x18) { + return SD21x18.wrap(int128(_bound(int256(x.unwrap()), int256(min.unwrap()), int256(max)))); + } + + /// @dev Helper function to bound an SD21x18 number, which console logs the result. + function bound(SD21x18 x, int128 min, int128 max) internal pure returns (SD21x18) { + return SD21x18.wrap(int128(bound(int256(x.unwrap()), int256(min), int256(max)))); + } + + /// @dev Helper function to bound an SD21x18 number, which does NOT console log the result. + function _bound(SD21x18 x, int128 min, int128 max) internal pure returns (SD21x18) { + return SD21x18.wrap(int128(_bound(int256(x.unwrap()), int256(min), int256(max)))); + } + /*////////////////////////////////////////////////////////////////////////// SD59X18 //////////////////////////////////////////////////////////////////////////*/ - /// @dev Helper function to bound an SD59x18 number, which console logs the bounded result. + /// @dev Helper function to bound an SD59x18 number, which console logs the result. function bound(SD59x18 x, SD59x18 min, SD59x18 max) internal pure returns (SD59x18) { return SD59x18.wrap(bound(x.unwrap(), min.unwrap(), max.unwrap())); } - /// @dev Helper function to bound an SD59x18 number. + /// @dev Helper function to bound an SD59x18 number, which does NOT console log the result. function _bound(SD59x18 x, SD59x18 min, SD59x18 max) internal pure returns (SD59x18) { return SD59x18.wrap(_bound(x.unwrap(), min.unwrap(), max.unwrap())); } - /// @dev Helper function to bound an SD59x18 number, which console logs the bounded result. + /// @dev Helper function to bound an SD59x18 number, which console logs the result. function bound(SD59x18 x, int256 min, SD59x18 max) internal pure returns (SD59x18) { return SD59x18.wrap(bound(x.unwrap(), min, max.unwrap())); } - /// @dev Helper function to bound an SD59x18 number. + /// @dev Helper function to bound an SD59x18 number, which does NOT console log the result. function _bound(SD59x18 x, int256 min, SD59x18 max) internal pure returns (SD59x18) { return SD59x18.wrap(_bound(x.unwrap(), min, max.unwrap())); } - /// @dev Helper function to bound an SD59x18 number, which console logs the bounded result. + /// @dev Helper function to bound an SD59x18 number, which console logs the result. function bound(SD59x18 x, SD59x18 min, int256 max) internal pure returns (SD59x18) { return SD59x18.wrap(bound(x.unwrap(), min.unwrap(), max)); } - /// @dev Helper function to bound an SD59x18 number. + /// @dev Helper function to bound an SD59x18 number, which does NOT console log the result. function _bound(SD59x18 x, SD59x18 min, int256 max) internal pure returns (SD59x18) { return SD59x18.wrap(_bound(x.unwrap(), min.unwrap(), max)); } - /// @dev Helper function to bound an SD59x18 number, which console logs the bounded result. + /// @dev Helper function to bound an SD59x18 number, which console logs the result. function bound(SD59x18 x, int256 min, int256 max) internal pure returns (SD59x18) { return SD59x18.wrap(bound(x.unwrap(), min, max)); } - /// @dev Helper function to bound an SD59x18 number. + /// @dev Helper function to bound an SD59x18 number, which does NOT console log the result. function _bound(SD59x18 x, int256 min, int256 max) internal pure returns (SD59x18) { return SD59x18.wrap(_bound(x.unwrap(), min, max)); } @@ -101,86 +147,130 @@ contract PRBMathUtils is StdUtils { UD2x18 //////////////////////////////////////////////////////////////////////////*/ - /// @dev Helper function to bound a UD2x18 number, which console logs the bounded result. + /// @dev Helper function to bound a UD2x18 number, which console logs the result. function bound(UD2x18 x, UD2x18 min, UD2x18 max) internal pure returns (UD2x18) { return UD2x18.wrap(uint64(bound(uint256(x.unwrap()), uint256(min.unwrap()), uint256(max.unwrap())))); } - /// @dev Helper function to bound a UD2x18 number. + /// @dev Helper function to bound a UD2x18 number, which does NOT console log the result. function _bound(UD2x18 x, UD2x18 min, UD2x18 max) internal pure returns (UD2x18) { return UD2x18.wrap(uint64(_bound(uint256(x.unwrap()), uint256(min.unwrap()), uint256(max.unwrap())))); } - /// @dev Helper function to bound a UD2x18 number, which console logs the bounded result. + /// @dev Helper function to bound a UD2x18 number, which console logs the result. function bound(UD2x18 x, uint64 min, UD2x18 max) internal pure returns (UD2x18) { return UD2x18.wrap(uint64(bound(uint256(x.unwrap()), uint256(min), uint256(max.unwrap())))); } - /// @dev Helper function to bound a UD2x18 number. + /// @dev Helper function to bound a UD2x18 number, which does NOT console log the result. function _bound(UD2x18 x, uint64 min, UD2x18 max) internal pure returns (UD2x18) { return UD2x18.wrap(uint64(_bound(uint256(x.unwrap()), uint256(min), uint256(max.unwrap())))); } - /// @dev Helper function to bound a UD2x18 number, which console logs the bounded result. + /// @dev Helper function to bound a UD2x18 number, which console logs the result. function bound(UD2x18 x, UD2x18 min, uint64 max) internal pure returns (UD2x18) { return UD2x18.wrap(uint64(bound(uint256(x.unwrap()), uint256(min.unwrap()), uint256(max)))); } - /// @dev Helper function to bound a UD2x18 number. + /// @dev Helper function to bound a UD2x18 number, which does NOT console log the result. function _bound(UD2x18 x, UD2x18 min, uint64 max) internal pure returns (UD2x18) { return UD2x18.wrap(uint64(_bound(uint256(x.unwrap()), uint256(min.unwrap()), uint256(max)))); } - /// @dev Helper function to bound a UD2x18 number, which console logs the bounded result. + /// @dev Helper function to bound a UD2x18 number, which console logs the result. function bound(UD2x18 x, uint64 min, uint64 max) internal pure returns (UD2x18) { return UD2x18.wrap(uint64(bound(uint256(x.unwrap()), uint256(min), uint256(max)))); } - /// @dev Helper function to bound a UD2x18 number. + /// @dev Helper function to bound a UD2x18 number, which does NOT console log the result. function _bound(UD2x18 x, uint64 min, uint64 max) internal pure returns (UD2x18) { return UD2x18.wrap(uint64(_bound(uint256(x.unwrap()), uint256(min), uint256(max)))); } + /*////////////////////////////////////////////////////////////////////////// + UD21x18 + //////////////////////////////////////////////////////////////////////////*/ + + /// @dev Helper function to bound a UD21x18 number, which console logs the result. + function bound(UD21x18 x, UD21x18 min, UD21x18 max) internal pure returns (UD21x18) { + return UD21x18.wrap(uint128(bound(uint256(x.unwrap()), uint256(min.unwrap()), uint256(max.unwrap())))); + } + + /// @dev Helper function to bound a UD21x18 number, which does NOT console log the result. + function _bound(UD21x18 x, UD21x18 min, UD21x18 max) internal pure returns (UD21x18) { + return UD21x18.wrap(uint128(_bound(uint256(x.unwrap()), uint256(min.unwrap()), uint256(max.unwrap())))); + } + + /// @dev Helper function to bound a UD21x18 number, which console logs the result. + function bound(UD21x18 x, uint128 min, UD21x18 max) internal pure returns (UD21x18) { + return UD21x18.wrap(uint128(bound(uint256(x.unwrap()), uint256(min), uint256(max.unwrap())))); + } + + /// @dev Helper function to bound a UD21x18 number, which does NOT console log the result. + function _bound(UD21x18 x, uint128 min, UD21x18 max) internal pure returns (UD21x18) { + return UD21x18.wrap(uint128(_bound(uint256(x.unwrap()), uint256(min), uint256(max.unwrap())))); + } + + /// @dev Helper function to bound a UD21x18 number, which console logs the result. + function bound(UD21x18 x, UD21x18 min, uint128 max) internal pure returns (UD21x18) { + return UD21x18.wrap(uint128(bound(uint256(x.unwrap()), uint256(min.unwrap()), uint256(max)))); + } + + /// @dev Helper function to bound a UD21x18 number, which does NOT console log the result. + function _bound(UD21x18 x, UD21x18 min, uint128 max) internal pure returns (UD21x18) { + return UD21x18.wrap(uint128(_bound(uint256(x.unwrap()), uint256(min.unwrap()), uint256(max)))); + } + + /// @dev Helper function to bound a UD21x18 number, which console logs the result. + function bound(UD21x18 x, uint128 min, uint128 max) internal pure returns (UD21x18) { + return UD21x18.wrap(uint128(bound(uint256(x.unwrap()), uint256(min), uint256(max)))); + } + + /// @dev Helper function to bound a UD21x18 number, which does NOT console log the result. + function _bound(UD21x18 x, uint128 min, uint128 max) internal pure returns (UD21x18) { + return UD21x18.wrap(uint128(_bound(uint256(x.unwrap()), uint256(min), uint256(max)))); + } + /*////////////////////////////////////////////////////////////////////////// UD60X18 //////////////////////////////////////////////////////////////////////////*/ - /// @dev Helper function to bound a UD60x18 number, which console logs the bounded result. + /// @dev Helper function to bound a UD60x18 number, which console logs the result. function bound(UD60x18 x, UD60x18 min, UD60x18 max) internal pure returns (UD60x18) { return UD60x18.wrap(bound(x.unwrap(), min.unwrap(), max.unwrap())); } - /// @dev Helper function to bound a UD60x18 number. + /// @dev Helper function to bound a UD60x18 number, which does NOT console log the result. function _bound(UD60x18 x, UD60x18 min, UD60x18 max) internal pure returns (UD60x18) { return UD60x18.wrap(_bound(x.unwrap(), min.unwrap(), max.unwrap())); } - /// @dev Helper function to bound a UD60x18 number, which console logs the bounded result. + /// @dev Helper function to bound a UD60x18 number, which console logs the result. function bound(UD60x18 x, uint256 min, UD60x18 max) internal pure returns (UD60x18) { return UD60x18.wrap(bound(x.unwrap(), min, max.unwrap())); } - /// @dev Helper function to bound a UD60x18 number. + /// @dev Helper function to bound a UD60x18 number, which does NOT console log the result. function _bound(UD60x18 x, uint256 min, UD60x18 max) internal pure returns (UD60x18) { return UD60x18.wrap(_bound(x.unwrap(), min, max.unwrap())); } - /// @dev Helper function to bound a UD60x18 number, which console logs the bounded result. + /// @dev Helper function to bound a UD60x18 number, which console logs the result. function bound(UD60x18 x, UD60x18 min, uint256 max) internal pure returns (UD60x18) { return UD60x18.wrap(bound(x.unwrap(), min.unwrap(), max)); } - /// @dev Helper function to bound a UD60x18 number. + /// @dev Helper function to bound a UD60x18 number, which does NOT console log the result. function _bound(UD60x18 x, UD60x18 min, uint256 max) internal pure returns (UD60x18) { return UD60x18.wrap(_bound(x.unwrap(), min.unwrap(), max)); } - /// @dev Helper function to bound a UD60x18 number, which console logs the bounded result. + /// @dev Helper function to bound a UD60x18 number, which console logs the result. function bound(UD60x18 x, uint256 min, uint256 max) internal pure returns (UD60x18) { return UD60x18.wrap(bound(x.unwrap(), min, max)); } - /// @dev Helper function to bound a UD60x18 number. + /// @dev Helper function to bound a UD60x18 number, which does NOT console log the result. function _bound(UD60x18 x, uint256 min, uint256 max) internal pure returns (UD60x18) { return UD60x18.wrap(_bound(x.unwrap(), min, max)); }