142 lines
4.0 KiB
Plaintext
142 lines
4.0 KiB
Plaintext
module pyth::i64 {
|
|
use pyth::error;
|
|
|
|
const MAX_POSITIVE_MAGNITUDE: u64 = (1 << 63) - 1;
|
|
const MAX_NEGATIVE_MAGNITUDE: u64 = (1 << 63);
|
|
|
|
/// As Move does not support negative numbers natively, we use our own internal
|
|
/// representation.
|
|
///
|
|
/// To consume these values, first call `get_is_negative()` to determine if the I64
|
|
/// represents a negative or positive value. Then call `get_magnitude_if_positive()` or
|
|
/// `get_magnitude_if_negative()` to get the magnitude of the number in unsigned u64 format.
|
|
/// This API forces consumers to handle positive and negative numbers safely.
|
|
struct I64 has copy, drop, store {
|
|
negative: bool,
|
|
magnitude: u64,
|
|
}
|
|
|
|
public fun new(magnitude: u64, negative: bool): I64 {
|
|
let max_magnitude = MAX_POSITIVE_MAGNITUDE;
|
|
if (negative) {
|
|
max_magnitude = MAX_NEGATIVE_MAGNITUDE;
|
|
};
|
|
assert!(magnitude <= max_magnitude, error::magnitude_too_large());
|
|
|
|
|
|
// Ensure we have a single zero representation: (0, false).
|
|
// (0, true) is invalid.
|
|
if (magnitude == 0) {
|
|
negative = false;
|
|
};
|
|
|
|
I64 {
|
|
magnitude: magnitude,
|
|
negative: negative,
|
|
}
|
|
}
|
|
|
|
public fun get_is_negative(i: &I64): bool {
|
|
i.negative
|
|
}
|
|
|
|
public fun get_magnitude_if_positive(in: &I64): u64 {
|
|
assert!(!in.negative, error::negative_value());
|
|
in.magnitude
|
|
}
|
|
|
|
public fun get_magnitude_if_negative(in: &I64): u64 {
|
|
assert!(in.negative, error::positive_value());
|
|
in.magnitude
|
|
}
|
|
|
|
public fun from_u64(from: u64): I64 {
|
|
// Use the MSB to determine whether the number is negative or not.
|
|
let negative = (from >> 63) == 1;
|
|
let magnitude = parse_magnitude(from, negative);
|
|
|
|
new(magnitude, negative)
|
|
}
|
|
|
|
fun parse_magnitude(from: u64, negative: bool): u64 {
|
|
// If positive, then return the input verbatamin
|
|
if (!negative) {
|
|
return from
|
|
};
|
|
|
|
// Otherwise convert from two's complement by inverting and adding 1
|
|
let inverted = from ^ 0xFFFFFFFFFFFFFFFF;
|
|
inverted + 1
|
|
}
|
|
|
|
#[test]
|
|
fun test_max_positive_magnitude() {
|
|
new(0x7FFFFFFFFFFFFFFF, false);
|
|
assert!(&new(1<<63 - 1, false) == &from_u64(1<<63 - 1), 1);
|
|
}
|
|
|
|
#[test]
|
|
#[expected_failure(abort_code = 65557)]
|
|
fun test_magnitude_too_large_positive() {
|
|
new(0x8000000000000000, false);
|
|
}
|
|
|
|
#[test]
|
|
fun test_max_negative_magnitude() {
|
|
new(0x8000000000000000, true);
|
|
assert!(&new(1<<63, true) == &from_u64(1<<63), 1);
|
|
}
|
|
|
|
#[test]
|
|
#[expected_failure(abort_code = 65557)]
|
|
fun test_magnitude_too_large_negative() {
|
|
new(0x8000000000000001, true);
|
|
}
|
|
|
|
#[test]
|
|
fun test_from_u64_positive() {
|
|
assert!(from_u64(0x64673) == new(0x64673, false), 1);
|
|
}
|
|
|
|
#[test]
|
|
fun test_from_u64_negative() {
|
|
assert!(from_u64(0xFFFFFFFFFFFEDC73) == new(0x1238D, true), 1);
|
|
}
|
|
|
|
#[test]
|
|
fun test_get_is_negative() {
|
|
assert!(get_is_negative(&new(234, true)) == true, 1);
|
|
assert!(get_is_negative(&new(767, false)) == false, 1);
|
|
}
|
|
|
|
#[test]
|
|
fun test_get_magnitude_if_positive_positive() {
|
|
assert!(get_magnitude_if_positive(&new(7686, false)) == 7686, 1);
|
|
}
|
|
|
|
#[test]
|
|
#[expected_failure(abort_code = 196609)]
|
|
fun test_get_magnitude_if_positive_negative() {
|
|
assert!(get_magnitude_if_positive(&new(7686, true)) == 7686, 1);
|
|
}
|
|
|
|
#[test]
|
|
fun test_get_magnitude_if_negative_negative() {
|
|
assert!(get_magnitude_if_negative(&new(7686, true)) == 7686, 1);
|
|
}
|
|
|
|
#[test]
|
|
#[expected_failure(abort_code = 196627)]
|
|
fun test_get_magnitude_if_negative_positive() {
|
|
assert!(get_magnitude_if_negative(&new(7686, false)) == 7686, 1);
|
|
}
|
|
|
|
#[test]
|
|
fun test_single_zero_representation() {
|
|
assert!(&new(0, true) == &new(0, false), 1);
|
|
assert!(&new(0, true) == &from_u64(0), 1);
|
|
assert!(&new(0, false) == &from_u64(0), 1);
|
|
}
|
|
|
|
}
|