// Copyright 2025 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
///|
fn add_prefix_zero(s : String, len : Int) -> String {
let buf = StringBuilder::new(size_hint=len)
for i = s.length(); i < len; i = i + 1 {
buf.write_char('0')
}
buf.write_string(s)
buf.to_string()
}
///|
test "add_prefix_zero" {
inspect(add_prefix_zero("", 2), content="00")
inspect(add_prefix_zero("1", 0), content="1")
inspect(add_prefix_zero("1", 1), content="1")
inspect(add_prefix_zero("1", 2), content="01")
inspect(add_prefix_zero("1", 3), content="001")
}
///|
fn remove_suffix_zero(s : StringView) -> StringView {
for state = s {
match state {
[_] as x => break x // keep one zero if all are zeros
[.. head, '0'] => continue head
x => break x
}
}
}
///|
///
/// Formats a nanosecond value as a fractional seconds string for ISO 8601 output.
///
/// Pads the nanosecond value to 9 digits, trims trailing zeros, and prepends a dot.
/// For example:
/// format_nanos(0L) => ".0"
/// format_nanos(1000100L) => ".0010001"
/// format_nanos(100_000_000L) => ".1"
/// format_nanos(110_000_000L) => ".11"
///
/// This is used for formatting fractional seconds in time and duration string outputs.
fn format_nanos(nanos : Int64) -> String {
let nanos_str = nanos.to_string().pad_start(9, '0')
StringView::add(".", remove_suffix_zero(nanos_str)).to_owned()
}
///|
test "remove_suffix_zero && format_nanos" {
inspect(remove_suffix_zero(""), content="")
inspect(remove_suffix_zero("1000100"), content="10001")
inspect(remove_suffix_zero("000"), content="0")
inspect(format_nanos(0L), content=".0")
inspect(format_nanos(1000100L), content=".0010001")
inspect(format_nanos(100_000_000L), content=".1")
inspect(format_nanos(110_000_000L), content=".11")
}
///|
fn add_suffix_zero(s : StringView, len : Int) -> String {
let buf = StringBuilder::new(size_hint=len)
buf.write_stringview(s)
for i = s.length(); i < len; i = i + 1 {
buf.write_char('0')
}
buf.to_string()
}
///|
test "add_suffix_zero" {
inspect(add_suffix_zero("1", 0), content="1")
inspect(add_suffix_zero("1", 1), content="1")
inspect(add_suffix_zero("1", 2), content="10")
inspect(add_suffix_zero("1", 3), content="100")
}
///|
fn[T] int_overflow_err() -> T raise Error {
raise Failure::Failure("Int overflow")
}
///|
fn[T] int64_overflow_err() -> T raise Error {
raise Failure::Failure("Int64 overflow")
}
///|
/// FIXME: use checked_add method of Int
fn checked_add_int(x : Int, y : Int) -> Int raise Error {
let r = x.to_int64() + y.to_int64()
if r < @int.MIN_VALUE.to_int64() || r > @int.MAX_VALUE.to_int64() {
int_overflow_err()
} else {
r.to_int()
}
}
///|
/// FIXME: use checked_mul method of Int
fn checked_mul_int(x : Int, y : Int) -> Int raise Error {
let r = x.to_int64() * y.to_int64()
if r < @int.MIN_VALUE.to_int64() || r > @int.MAX_VALUE.to_int64() {
int_overflow_err()
} else {
r.to_int()
}
}
///|
/// FIXME: use checked_add method of Int64
fn checked_add_int64(x : Int64, y : Int64) -> Int64 raise Error {
let r = x + y
// Overflow iff both arguments have the opposite sign of the result
if ((x ^ r) & (y ^ r)) < 0L {
int64_overflow_err()
} else {
r
}
}
///|
/// FIXME: use checked_mul method of Int64
fn checked_mul_int64(x : Int64, y : Int64) -> Int64 raise Error {
let r = x * y
let abs_x = x.abs()
let abs_y = y.abs()
if ((abs_x ^ abs_y).reinterpret_as_uint64() >> 31).reinterpret_as_int64() !=
0L {
// bits that greater than 2^31 might cause overflow
if (y != 0L && r / y != x) || (x == @int64.MIN_VALUE && y == -1L) {
int64_overflow_err()
}
}
r
}
///|
test "checked_add_int" {
debug_inspect(
try checked_add_int(1, 1) catch {
e => Err(e)
} noraise {
value => Ok(value)
},
content="Ok(2)",
)
debug_inspect(
try checked_add_int(1, -1) catch {
e => Err(e)
} noraise {
value => Ok(value)
},
content="Ok(0)",
)
debug_inspect(
try checked_add_int(-1, -1) catch {
e => Err(e)
} noraise {
value => Ok(value)
},
content="Ok(-2)",
)
debug_inspect(
try checked_add_int(@int.MAX_VALUE, 1) catch {
e => Err(e)
} noraise {
value => Ok(value)
},
content=(
#|Err(Failure("Int overflow"))
),
)
debug_inspect(
try checked_add_int(@int.MIN_VALUE, -1) catch {
e => Err(e)
} noraise {
value => Ok(value)
},
content=(
#|Err(Failure("Int overflow"))
),
)
}
///|
test "checked_mul_int" {
debug_inspect(
try checked_mul_int(2, 3) catch {
e => Err(e)
} noraise {
value => Ok(value)
},
content="Ok(6)",
)
debug_inspect(
try checked_mul_int(2, -3) catch {
e => Err(e)
} noraise {
value => Ok(value)
},
content="Ok(-6)",
)
debug_inspect(
try checked_mul_int(-2, -3) catch {
e => Err(e)
} noraise {
value => Ok(value)
},
content="Ok(6)",
)
debug_inspect(
try checked_mul_int(@int.MAX_VALUE, 2) catch {
e => Err(e)
} noraise {
value => Ok(value)
},
content=(
#|Err(Failure("Int overflow"))
),
)
debug_inspect(
try checked_mul_int(@int.MIN_VALUE, 2) catch {
e => Err(e)
} noraise {
value => Ok(value)
},
content=(
#|Err(Failure("Int overflow"))
),
)
}
///|
test "checked_add_int64" {
debug_inspect(
try checked_add_int64(1L, 1L) catch {
e => Err(e)
} noraise {
value => Ok(value)
},
content="Ok(2)",
)
debug_inspect(
try checked_add_int64(1L, -1L) catch {
e => Err(e)
} noraise {
value => Ok(value)
},
content="Ok(0)",
)
debug_inspect(
try checked_add_int64(-1L, -1L) catch {
e => Err(e)
} noraise {
value => Ok(value)
},
content="Ok(-2)",
)
debug_inspect(
try checked_add_int64(1L, @int64.MAX_VALUE) catch {
e => Err(e)
} noraise {
value => Ok(value)
},
content=(
#|Err(Failure("Int64 overflow"))
),
)
debug_inspect(
try checked_add_int64(-1L, @int64.MIN_VALUE) catch {
e => Err(e)
} noraise {
value => Ok(value)
},
content=(
#|Err(Failure("Int64 overflow"))
),
)
}
///|
test "checked_mul_int64" {
debug_inspect(
try checked_mul_int64(2L, 3L) catch {
e => Err(e)
} noraise {
value => Ok(value)
},
content="Ok(6)",
)
debug_inspect(
try checked_mul_int64(2L, -3L) catch {
e => Err(e)
} noraise {
value => Ok(value)
},
content="Ok(-6)",
)
debug_inspect(
try checked_mul_int64(-2L, -3L) catch {
e => Err(e)
} noraise {
value => Ok(value)
},
content="Ok(6)",
)
debug_inspect(
try checked_mul_int64(@int64.MAX_VALUE, 2L) catch {
e => Err(e)
} noraise {
value => Ok(value)
},
content=(
#|Err(Failure("Int64 overflow"))
),
)
debug_inspect(
try checked_mul_int64(@int64.MIN_VALUE, 2L) catch {
e => Err(e)
} noraise {
value => Ok(value)
},
content=(
#|Err(Failure("Int64 overflow"))
),
)
}
///|
fn floor(x : Double) -> Double {
if x.is_pos_inf() || x.is_neg_inf() || x.is_nan() {
return x
}
let n = x.to_int64()
let d = n.to_double()
if x >= 0.0 || x == d {
d
} else {
d - 1.0
}
}
///|
fn floor_div_int(x : Int, y : Int) -> Int raise Error {
if y == 0 {
raise Failure::Failure("division by zero")
}
let r = x.to_double() / y.to_double()
floor(r).to_int()
}
///|
test "floor_div_int" {
debug_inspect(
try floor_div_int(1, 10) catch {
e => Err(e)
} noraise {
value => Ok(value)
},
content="Ok(0)",
)
debug_inspect(
try floor_div_int(-1, 10) catch {
e => Err(e)
} noraise {
value => Ok(value)
},
content="Ok(-1)",
)
debug_inspect(
try floor_div_int(9, 10) catch {
e => Err(e)
} noraise {
value => Ok(value)
},
content="Ok(0)",
)
debug_inspect(
try floor_div_int(-9, 10) catch {
e => Err(e)
} noraise {
value => Ok(value)
},
content="Ok(-1)",
)
debug_inspect(
try floor_div_int(0, 10) catch {
e => Err(e)
} noraise {
value => Ok(value)
},
content="Ok(0)",
)
debug_inspect(
try floor_div_int(10, 0) catch {
e => Err(e)
} noraise {
value => Ok(value)
},
content=(
#|Err(Failure("division by zero"))
),
)
}
///|
fn floor_div_int64(x : Int64, y : Int64) -> Int64 raise Error {
if y == 0L {
fail("division by zero")
}
let r = x.to_double() / y.to_double()
floor(r).to_int64()
}
///|
test "floor_div_int64" {
debug_inspect(
try floor_div_int64(1L, 10L) catch {
e => Err(e)
} noraise {
value => Ok(value)
},
content="Ok(0)",
)
debug_inspect(
try floor_div_int64(-1L, 10L) catch {
e => Err(e)
} noraise {
value => Ok(value)
},
content="Ok(-1)",
)
debug_inspect(
try floor_div_int64(9L, 10L) catch {
e => Err(e)
} noraise {
value => Ok(value)
},
content="Ok(0)",
)
debug_inspect(
try floor_div_int64(-9L, 10L) catch {
e => Err(e)
} noraise {
value => Ok(value)
},
content="Ok(-1)",
)
debug_inspect(
try floor_div_int64(0L, 10L) catch {
e => Err(e)
} noraise {
value => Ok(value)
},
content="Ok(0)",
)
assert_true(
try floor_div_int64(10L, 0L) catch {
_ => true
} noraise {
_ => false
},
)
}
///|
fn floor_mod_int(x : Int, y : Int) -> Int raise Error {
x - floor_div_int(x, y) * y
}
///|
test "floor_mod_int" {
debug_inspect(
try floor_mod_int(1, 10) catch {
e => Err(e)
} noraise {
value => Ok(value)
},
content="Ok(1)",
)
debug_inspect(
try floor_mod_int(-1, 10) catch {
e => Err(e)
} noraise {
value => Ok(value)
},
content="Ok(9)",
)
debug_inspect(
try floor_mod_int(11, 10) catch {
e => Err(e)
} noraise {
value => Ok(value)
},
content="Ok(1)",
)
debug_inspect(
try floor_mod_int(-11, 10) catch {
e => Err(e)
} noraise {
value => Ok(value)
},
content="Ok(9)",
)
debug_inspect(
try floor_mod_int(0, 10) catch {
e => Err(e)
} noraise {
value => Ok(value)
},
content="Ok(0)",
)
debug_inspect(
try floor_mod_int(10, 0) catch {
e => Err(e)
} noraise {
value => Ok(value)
},
content=(
#|Err(Failure("division by zero"))
),
)
}
///|
fn floor_mod_int64(x : Int64, y : Int64) -> Int64 raise Error {
x - floor_div_int64(x, y) * y
}
///|
test "floor_mod_int64" {
debug_inspect(
try floor_mod_int64(1L, 10L) catch {
e => Err(e)
} noraise {
value => Ok(value)
},
content="Ok(1)",
)
debug_inspect(
try floor_mod_int64(-1L, 10L) catch {
e => Err(e)
} noraise {
value => Ok(value)
},
content="Ok(9)",
)
debug_inspect(
try floor_mod_int64(11L, 10L) catch {
e => Err(e)
} noraise {
value => Ok(value)
},
content="Ok(1)",
)
debug_inspect(
try floor_mod_int64(-11L, 10L) catch {
e => Err(e)
} noraise {
value => Ok(value)
},
content="Ok(9)",
)
debug_inspect(
try floor_mod_int64(0L, 10L) catch {
e => Err(e)
} noraise {
value => Ok(value)
},
content="Ok(0)",
)
assert_true(
try floor_mod_int64(10L, 0L) catch {
_ => true
} noraise {
_ => false
},
)
}