// Copyright 2026 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.
///|
/// Trait for types that can be randomly generated
pub(open) trait Arbitrary {
// `arbitrary` function takes a random number generator and a number that
// determines the size of the generated value as arguments, and returns a
// randomly generated value of certain type.
fn arbitrary(Int, @splitmix.RandomState) -> Self
}
///|
pub impl Arbitrary for Unit with fn arbitrary(_, _) {
()
}
///|
pub impl Arbitrary for Bool with fn arbitrary(_, rs) {
rs.next_double() < 0.5
}
///|
pub impl Arbitrary for Int with fn arbitrary(size, rs) {
if size == 0 {
0
} else {
rs.next_int() % size
}
}
///|
pub impl Arbitrary for UInt with fn arbitrary(size, rs) {
if size == 0 {
0
} else {
rs.next_uint() % size.reinterpret_as_uint()
}
}
///|
pub impl Arbitrary for Byte with fn arbitrary(_, rs) {
rs.next_uint().to_byte()
}
///|
pub impl Arbitrary for Bytes with fn arbitrary(size, rs) {
if size == 0 {
Bytes::new(0)
} else {
let sz = rs.next_positive_int() % size
Bytes::makei(sz, _ => Arbitrary::arbitrary(size, rs))
}
}
///|
pub impl Arbitrary for Int64 with fn arbitrary(size, rs) {
if size == 0 {
0
} else {
rs.next_int64() % size.to_int64()
}
}
///|
pub impl Arbitrary for UInt64 with fn arbitrary(size, rs) {
if size == 0 {
0
} else {
rs.next_uint64() % size.to_uint64()
}
}
///|
pub impl Arbitrary for Float with fn arbitrary(_, rs) {
rs.next_float()
}
///|
pub impl Arbitrary for Double with fn arbitrary(_, rs) {
rs.next_double()
}
///|
pub impl Arbitrary for Char with fn arbitrary(_, rs) {
// Bias toward ASCII, but regularly produce arbitrary Unicode scalar
// values so consumers also see multi-byte and non-BMP characters.
if rs.next_uint() % 4 < 3 {
(rs.next_uint() % 128).reinterpret_as_int().to_char().unwrap()
} else {
// Unicode scalar values: [0x0, 0xD7FF] and [0xE000, 0x10FFFF].
let index = (rs.next_uint() % (0x110000 - 0x800)).reinterpret_as_int()
let code = if index < 0xD800 { index } else { index + 0x800 }
code.to_char().unwrap()
}
}
///|
pub impl Arbitrary for String with fn arbitrary(size, rs) {
let len = if size == 0 { 0 } else { rs.next_positive_int() % size }
for i in 0.. {
guard i < len else { None }
let elem = X::arbitrary(i, rs)
i += 1
Some(elem)
})
}
///|
/// Generates a single random value, optionally with a given size and random state.
pub fn[T : Arbitrary] gen(size? : Int, state? : @splitmix.RandomState) -> T {
let size = size.unwrap_or(0)
let state = state.unwrap_or_else(() => {
(Default::default() : @splitmix.RandomState)
})
Arbitrary::arbitrary(size, state)
}
///|
pub impl[X : Arbitrary] Arbitrary for X? with fn arbitrary(i, rs) {
if rs.next_double() < 0.3 {
None
} else {
Some(Arbitrary::arbitrary(i, rs))
}
}
///|
pub impl[T : Arbitrary, E : Arbitrary] Arbitrary for Result[T, E] with fn arbitrary(
size,
rs,
) {
if Arbitrary::arbitrary(size, rs) {
Ok(T::arbitrary(size, rs))
} else {
Err(E::arbitrary(size, rs))
}
}
///|
pub impl[X : Arbitrary] Arbitrary for FixedArray[X] with fn arbitrary(size, rs) {
let len = if size == 0 { 0 } else { rs.next_positive_int() % size }
FixedArray::makei(len, i => X::arbitrary(i, rs))
}
///|
pub impl[A : Arbitrary] Arbitrary for ArrayView[A] with fn arbitrary(size, rs) {
let arr : Array[A] = Arbitrary::arbitrary(size, rs)
// Note can not use Array::arbitrary
arr
}
///|
pub impl[X : Arbitrary] Arbitrary for Array[X] with fn arbitrary(size, rs) {
let len = if size == 0 { 0 } else { rs.next_positive_int() % size }
Array::makei(len, x => X::arbitrary(x, rs))
}
///|
pub impl Arbitrary for @bigint.BigInt with fn arbitrary(size, rs) {
if size == 0 {
0
} else {
rs.next_int64() |> @bigint.BigInt::from_int64
}
}
///|
pub impl[X : Arbitrary] Arbitrary for @ref.Ref[X] with fn arbitrary(size, rs) {
@ref.new(X::arbitrary(size, rs))
}
///|
pub impl[A : Arbitrary, B : Arbitrary] Arbitrary for (A, B) with fn arbitrary(
size,
r0,
) {
let r1 = r0.split()
(Arbitrary::arbitrary(size, r0), Arbitrary::arbitrary(size, r1))
}
///|
pub impl[A : Arbitrary, B : Arbitrary, C : Arbitrary] Arbitrary for (A, B, C) with fn arbitrary(
size,
r0,
) {
let r1 = r0.split()
let (v1, v2) = Arbitrary::arbitrary(size, r1)
(Arbitrary::arbitrary(size, r0), v1, v2)
}
///|
pub impl[A : Arbitrary, B : Arbitrary, C : Arbitrary, D : Arbitrary] Arbitrary for (
A,
B,
C,
D,
) with fn arbitrary(size, r0) {
let r1 = r0.split()
let (v1, v2, v3) = Arbitrary::arbitrary(size, r1)
(Arbitrary::arbitrary(size, r0), v1, v2, v3)
}
///|
pub impl[
A : Arbitrary,
B : Arbitrary,
C : Arbitrary,
D : Arbitrary,
E : Arbitrary,
] Arbitrary for (A, B, C, D, E) with fn arbitrary(size, r0) {
let r1 = r0.split()
let (v1, v2, v3, v4) = Arbitrary::arbitrary(size, r1)
(Arbitrary::arbitrary(size, r0), v1, v2, v3, v4)
}
///|
pub impl[
A : Arbitrary,
B : Arbitrary,
C : Arbitrary,
D : Arbitrary,
E : Arbitrary,
F : Arbitrary,
] Arbitrary for (A, B, C, D, E, F) with fn arbitrary(size, r0) {
let r1 = r0.split()
let (v1, v2, v3, v4, v5) = Arbitrary::arbitrary(size, r1)
(Arbitrary::arbitrary(size, r0), v1, v2, v3, v4, v5)
}
///|
pub impl[
A : Arbitrary,
B : Arbitrary,
C : Arbitrary,
D : Arbitrary,
E : Arbitrary,
F : Arbitrary,
G : Arbitrary,
] Arbitrary for (A, B, C, D, E, F, G) with fn arbitrary(size, r0) {
let r1 = r0.split()
let (v1, v2, v3, v4, v5, v6) = Arbitrary::arbitrary(size, r1)
(Arbitrary::arbitrary(size, r0), v1, v2, v3, v4, v5, v6)
}