/*
* Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
* This source file is part of the Cangjie project, licensed under Apache-2.0
* with Runtime Library Exception.
*
* See https://cangjie-lang.cn/pages/LICENSE for license information.
*/
// The Cangjie API is in Beta. For details on its capabilities and limitations, please refer to the README file.
package std.unittest
import std.random.Random
import std.collection.ArrayList
import std.unittest.prop_test.*
import std.math.*
@C
struct UInt8Entry {
UInt8Entry(let left!: UInt8 = 0, let right!: UInt8 = 0) {}
}
@C
struct UInt16Entry {
UInt16Entry(let left!: UInt16 = 0, let right!: UInt16 = 0) {}
}
@C
struct UInt32Entry {
UInt32Entry(let left!: UInt32 = 0, let right!: UInt32 = 0) {}
}
@C
struct UInt64Entry {
UInt64Entry(let left!: UInt64 = 0, let right!: UInt64 = 0) {}
}
@C
struct Float32Entry {
Float32Entry(let left!: Float32 = 0.0, let right!: Float32 = 0.0) {}
}
@C
struct Float64Entry {
Float64Entry(let left!: Float64 = 0.0, let right!: Float64 = 0.0) {}
}
foreign func CJ_MICROFUZZ_FLIP_BIT_FLOAT(value: Float32, bit: UInt8): Float32
foreign func CJ_MICROFUZZ_FLIP_BIT_DOUBLE(value: Float64, bit: UInt8): Float64
foreign func CJ_MICROFUZZ_RANDOM_RECENT_CMP_VALUE_UINT8(result: CPointer<UInt8Entry>): Unit
foreign func CJ_MICROFUZZ_RANDOM_RECENT_CMP_VALUE_UINT16(result: CPointer<UInt16Entry>): Unit
foreign func CJ_MICROFUZZ_RANDOM_RECENT_CMP_VALUE_UINT32(result: CPointer<UInt32Entry>): Unit
foreign func CJ_MICROFUZZ_RANDOM_RECENT_CMP_VALUE_UINT64(result: CPointer<UInt64Entry>): Unit
foreign func CJ_MICROFUZZ_RANDOM_RECENT_CMP_VALUE_FLOAT(result: CPointer<Float32Entry>): Unit
foreign func CJ_MICROFUZZ_RANDOM_RECENT_CMP_VALUE_DOUBLE(result: CPointer<Float64Entry>): Unit
foreign func CJ_MICROFUZZ_RANDOM_RECENT_CMP_VALUE_FOR_KEY_UINT8(key: UInt8, result: CPointer<UInt8Entry>): Unit
foreign func CJ_MICROFUZZ_RANDOM_RECENT_CMP_VALUE_FOR_KEY_UINT16(key: UInt16, result: CPointer<UInt16Entry>): Unit
foreign func CJ_MICROFUZZ_RANDOM_RECENT_CMP_VALUE_FOR_KEY_UINT32(key: UInt32, result: CPointer<UInt32Entry>): Unit
foreign func CJ_MICROFUZZ_RANDOM_RECENT_CMP_VALUE_FOR_KEY_UINT64(key: UInt64, result: CPointer<UInt64Entry>): Unit
foreign func CJ_MICROFUZZ_RANDOM_RECENT_CMP_VALUE_FOR_KEY_FLOAT(key: Float32, result: CPointer<Float32Entry>): Unit
foreign func CJ_MICROFUZZ_RANDOM_RECENT_CMP_VALUE_FOR_KEY_DOUBLE(key: Float64, result: CPointer<Float64Entry>): Unit
foreign func CJ_MICROFUZZ_ENABLE_TRACE(): Unit
foreign func CJ_MICROFUZZ_DISABLE_TRACE(): Unit
foreign func CJ_MICROFUZZ_RESET_LOCAL_COVERAGE_INFO(): Unit
foreign func CJ_MICROFUZZ_RESET_ALL_COVERAGE_INFO(): Unit
foreign func CJ_MICROFUZZ_HAS_NEW_COVERAGE(): Bool
foreign func CJ_MICROFUZZ_IS_INITIALIZED(): Bool
foreign func CJ_MICROFUZZ_GLOBAL_EDGE_COVERAGE_EST(): Float64
foreign func CJ_MICROFUZZ_SET_DBG_CALLBACK(callback: CFunc<(CString, CString, UInt64, UInt64) -> Unit>): Unit
@C
func printMessage(mes: CString, t: CString, v1: UInt64, v2: UInt64): Unit {
print(mes)
print(t)
println(" ${v1} ${v2}")
}
func currentCoverageEst() {
unsafe { CJ_MICROFUZZ_GLOBAL_EDGE_COVERAGE_EST() }
}
interface Torcable<T> {
static func randomRecentEntry(): ?(T, T)
static func randomRecentValueComparedTo(value: T): ?T
static func nextFrom(random: Random): T
static func suggestedFrom(random: Random): T
func isZero(): Bool
func bitFlip(at: UInt8): T
operator func+(_: UInt8): T
operator func-(_: UInt8): T
}
extend UInt8 <: Torcable<UInt8> {
public static func randomRecentEntry(): ?(UInt8, UInt8) {
var cEntry = UInt8Entry()
unsafe { CJ_MICROFUZZ_RANDOM_RECENT_CMP_VALUE_UINT8(inout cEntry) }
if (cEntry.left == 0 && cEntry.right == 0) { return None }
return (cEntry.left, cEntry.right)
}
public static func randomRecentValueComparedTo(value: UInt8): ?UInt8 {
var cEntry = UInt8Entry()
unsafe { CJ_MICROFUZZ_RANDOM_RECENT_CMP_VALUE_FOR_KEY_UINT8(value, inout cEntry) }
if (cEntry.left == 0 && cEntry.right == 0) { return None }
if (cEntry.left == value) { return cEntry.right }
if (cEntry.right == value) { return cEntry.left }
return None
}
public static func nextFrom(random: Random): UInt8 {
random.nextUInt8()
}
public static func suggestedFrom(random: Random): UInt8 {
random.suggestUInt8()
}
public func isZero() { this == 0 }
public func bitFlip(at: UInt8): UInt8 {
this ^ (1 << (at % 8))
}
}
extend UInt16 <: Torcable<UInt16> {
public static func randomRecentEntry(): ?(UInt16, UInt16) {
var cEntry = UInt16Entry()
unsafe { CJ_MICROFUZZ_RANDOM_RECENT_CMP_VALUE_UINT16(inout cEntry) }
if (cEntry.left == 0 && cEntry.right == 0) { return None }
return (cEntry.left, cEntry.right)
}
public static func randomRecentValueComparedTo(value: UInt16): ?UInt16 {
var cEntry = UInt16Entry()
unsafe { CJ_MICROFUZZ_RANDOM_RECENT_CMP_VALUE_FOR_KEY_UINT16(value, inout cEntry) }
if (cEntry.left == 0 && cEntry.right == 0) { return None }
if (cEntry.left == value) { return cEntry.right }
if (cEntry.right == value) { return cEntry.left }
return None
}
public static func nextFrom(random: Random): UInt16 {
random.nextUInt16()
}
public static func suggestedFrom(random: Random): UInt16 {
random.suggestUInt16()
}
public func isZero() { this == 0 }
public func bitFlip(at: UInt8): UInt16 {
this ^ (1 << (at % 16))
}
@OverflowWrapping
public operator func+(arg: UInt8): UInt16 { this + UInt16(arg) }
@OverflowWrapping
public operator func-(arg: UInt8): UInt16 { this - UInt16(arg) }
}
extend UInt32 <: Torcable<UInt32> {
public static func randomRecentEntry(): ?(UInt32, UInt32) {
var cEntry = UInt32Entry()
unsafe { CJ_MICROFUZZ_RANDOM_RECENT_CMP_VALUE_UINT32(inout cEntry) }
if (cEntry.left == 0 && cEntry.right == 0) { return None }
return (cEntry.left, cEntry.right)
}
public static func randomRecentValueComparedTo(value: UInt32): ?UInt32 {
var cEntry = UInt32Entry()
unsafe { CJ_MICROFUZZ_RANDOM_RECENT_CMP_VALUE_FOR_KEY_UINT32(value, inout cEntry) }
if (cEntry.left == 0 && cEntry.right == 0) { return None }
if (cEntry.left == value) { return cEntry.right }
if (cEntry.right == value) { return cEntry.left }
return None
}
public static func nextFrom(random: Random): UInt32 {
random.nextUInt32()
}
public static func suggestedFrom(random: Random): UInt32 {
random.suggestUInt32()
}
public func isZero() { this == 0 }
public func bitFlip(at: UInt8): UInt32 {
this ^ (1 << (at % 32))
}
@OverflowWrapping
public operator func+(arg: UInt8): UInt32 { this + UInt32(arg) }
@OverflowWrapping
public operator func-(arg: UInt8): UInt32 { this - UInt32(arg) }
}
extend UInt64 <: Torcable<UInt64> {
public static func randomRecentEntry(): ?(UInt64, UInt64) {
var cEntry = UInt64Entry()
unsafe { CJ_MICROFUZZ_RANDOM_RECENT_CMP_VALUE_UINT64(inout cEntry) }
if (cEntry.left == 0 && cEntry.right == 0) { return None }
return (cEntry.left, cEntry.right)
}
public static func randomRecentValueComparedTo(value: UInt64): ?UInt64 {
var cEntry = UInt64Entry()
unsafe { CJ_MICROFUZZ_RANDOM_RECENT_CMP_VALUE_FOR_KEY_UINT64(value, inout cEntry) }
if (cEntry.left == 0 && cEntry.right == 0) { return None }
if (cEntry.left == value) { return cEntry.right }
if (cEntry.right == value) { return cEntry.left }
return None
}
public static func nextFrom(random: Random): UInt64 { random.nextUInt64() }
public static func suggestedFrom(random: Random): UInt64 {
random.suggestUInt64()
}
public func isZero() { this == 0 }
public func bitFlip(at: UInt8): UInt64 {
this ^ (1 << (at % 64))
}
@OverflowWrapping
public operator func+(arg: UInt8): UInt64 { this + UInt64(arg) }
@OverflowWrapping
public operator func-(arg: UInt8): UInt64 { this - UInt64(arg) }
}
extend Float32 <: Torcable<Float32> {
public static func randomRecentEntry(): ?(Float32, Float32) {
var cEntry = Float32Entry()
unsafe { CJ_MICROFUZZ_RANDOM_RECENT_CMP_VALUE_FLOAT(inout cEntry) }
if (cEntry.left.isZero() && cEntry.right.isZero()) { return None }
return (cEntry.left, cEntry.right)
}
public static func randomRecentValueComparedTo(value: Float32): ?Float32 {
var cEntry = Float32Entry()
unsafe { CJ_MICROFUZZ_RANDOM_RECENT_CMP_VALUE_FOR_KEY_FLOAT(value, inout cEntry) }
if (cEntry.left == 0.0 && cEntry.right == 0.0) { return None }
if (cEntry.left == value) { return cEntry.right }
if (cEntry.right == value) { return cEntry.left }
return None
}
public static func nextFrom(random: Random): Float32 {
random.nextFloat32()
}
public static func suggestedFrom(random: Random): Float32 {
random.suggestFloat32()
}
public func isZero() { this == 0.0 }
public func bitFlip(at: UInt8): Float32 {
unsafe { CJ_MICROFUZZ_FLIP_BIT_FLOAT(this, at) }
}
@OverflowWrapping
public operator func+(arg: UInt8): Float32 { this + Float32(arg) }
@OverflowWrapping
public operator func-(arg: UInt8): Float32 { this - Float32(arg) }
}
extend Float64 <: Torcable<Float64> {
public static func randomRecentEntry(): ?(Float64, Float64) {
var cEntry = Float64Entry()
unsafe { CJ_MICROFUZZ_RANDOM_RECENT_CMP_VALUE_DOUBLE(inout cEntry) }
if (cEntry.left.isZero() && cEntry.right.isZero()) { return None }
return (cEntry.left, cEntry.right)
}
public static func randomRecentValueComparedTo(value: Float64): ?Float64 {
var cEntry = Float64Entry()
unsafe { CJ_MICROFUZZ_RANDOM_RECENT_CMP_VALUE_FOR_KEY_DOUBLE(value, inout cEntry) }
if (cEntry.left == 0.0 && cEntry.right == 0.0) { return None }
if (cEntry.left == value) { return cEntry.right }
if (cEntry.right == value) { return cEntry.left }
return None
}
public static func nextFrom(random: Random): Float64 {
random.nextFloat64()
}
public static func suggestedFrom(random: Random): Float64 {
random.suggestFloat64()
}
public func isZero() { this == 0.0 }
public func bitFlip(at: UInt8): Float64 {
unsafe { CJ_MICROFUZZ_FLIP_BIT_DOUBLE(this, at) }
}
@OverflowWrapping
public operator func+(arg: UInt8): Float64 { this + Float64(arg) }
@OverflowWrapping
public operator func-(arg: UInt8): Float64 { this - Float64(arg) }
}
enum Mutation<T> where T <: Torcable<T> {
| Drop
| Insert(T)
| ReplaceWith(T)
| BitFlip(UInt8)
| Add(UInt8)
| Sub(UInt8)
@OverflowWrapping
func apply(at: Int64, values: ArrayList<T>): Unit {
match (this) {
case Drop => values.remove(at: at)
case Insert(v) => values.add(v, at: at)
case ReplaceWith(v) =>
values[at] = v
case BitFlip(bits) =>
values[at] = values[at].bitFlip(bits)
case Add(v) =>
values[at] += v
case Sub(v) =>
values[at] -= v
}
}
}
class Mutator {
Mutator(let random!: Random = Random()) {}
func randomRecentValue<T>(): ?T where T <: Torcable<T> & ToString {
let entry = T.randomRecentEntry() ?? return None
if (random.nextBool()) {
return entry[0]
} else {
return entry[1]
}
}
func randomRecentCmp<T>(value: T): ?T where T <: Torcable<T> & ToString {
match (T.randomRecentValueComparedTo(value)) {
case Some(operand) => return operand
case _ => randomRecentValue<T>()
}
}
private func pickRandomMutation<T>(value: T): Mutation<T> where T <: Torcable<T> & ToString {
match (random.nextUInt8(12)) {
case 0 | 1 | 2 | 3 => // ~1/3
let rep = randomRecentCmp<T>(value) ?? return pickRandomMutation<T>(value)
ReplaceWith(rep)
case 4 | 5 => // ~1/6
ReplaceWith(T.suggestedFrom(random))
case 6 | 7 => // ~1/6
BitFlip(random.nextUInt8())
case 8 => // ~1/12
Add(random.nextUInt8(31) + 1)
case 9 => // ~1/12
Sub(random.nextUInt8(31) + 1)
case 10 => // ~1/12
Insert(value) // duplication
case 11 => // ~1/12
Drop
case _ => throw Exception("Logic error: pickRandomMutation")
}
}
func randomMutation<T>(temperature: UInt8, value: T): ?Mutation<T> where T <: Torcable<T> & ToString {
if (random.nextUInt8() >= temperature) { return None }
return pickRandomMutation<T>(value)
}
}
private class MutatingRandomValueStream<T> where T <: Torcable<T> & ToString & Hashable & Equatable<T> {
MutatingRandomValueStream(let values!: ArrayList<T> = ArrayList()) {}
var currentIndex = 0
func nextUniform(random: Random): T {
let index = currentIndex
currentIndex++
while (index >= values.size) {
values.add(T.nextFrom(random))
}
return values[index]
}
func next(random: Random, temperature: UInt8, mutator: Mutator): T {
let index = currentIndex
currentIndex++
while (currentIndex >= values.size) {
values.add(T.suggestedFrom(random))
}
mutator.randomMutation(temperature, values[index])?.apply(index, values)
while (currentIndex >= values.size) {
values.add(T.suggestedFrom(random))
}
return values[index]
}
func reset(): MutatingRandomValueStream<T> {
MutatingRandomValueStream(values: values.clone())
}
func freeze(): MutatingRandomValueStream<T> {
MutatingRandomValueStream(values: values)
}
}
class CovRandomSource <: RandomSource {
private let inner: Random
private CovRandomSource(
let seed!: UInt64,
let temperature!: UInt8,
private let valuesInt8!: MutatingRandomValueStream<UInt8>,
private let valuesInt16!: MutatingRandomValueStream<UInt16>,
private let valuesInt32!: MutatingRandomValueStream<UInt32>,
private let valuesInt64!: MutatingRandomValueStream<UInt64>,
private let valuesFloat32!: MutatingRandomValueStream<Float32>,
private let valuesFloat64!: MutatingRandomValueStream<Float64>,
private let mutator!: Mutator
) {
this.inner = Random(seed)
}
init(seed!: UInt64, temperature!: UInt8 = 0, mutator!: Mutator = Mutator(random: Random(seed))) {
this(
seed: seed, temperature: temperature,
valuesInt8: MutatingRandomValueStream(),
valuesInt16: MutatingRandomValueStream(),
valuesInt32: MutatingRandomValueStream(),
valuesInt64: MutatingRandomValueStream(),
valuesFloat32: MutatingRandomValueStream(),
valuesFloat64: MutatingRandomValueStream(),
mutator: mutator
)
}
func copy(seed!: UInt64 = this.seed, temperature!: UInt8 = this.temperature): CovRandomSource {
CovRandomSource(
seed: seed, temperature: temperature,
valuesInt8: valuesInt8.reset(), valuesInt16: valuesInt16.reset(),
valuesInt32: valuesInt32.reset(), valuesInt64: valuesInt64.reset(),
valuesFloat32: valuesFloat32.reset(), valuesFloat64: valuesFloat64.reset(),
mutator: mutator
)
}
func freeze(): CovRandomSource {
CovRandomSource(
seed: seed, temperature: 0,
valuesInt8: valuesInt8.freeze(), valuesInt16: valuesInt16.freeze(),
valuesInt32: valuesInt32.freeze(), valuesInt64: valuesInt64.freeze(),
valuesFloat32: valuesFloat32.freeze(), valuesFloat64: valuesFloat64.freeze(),
mutator: mutator
)
}
public override func suggestBool(): Bool {
return nextBool()
}
public override func suggestUInt8(): UInt8 {
let result = valuesInt8.next(inner, temperature, mutator)
return result
}
public override func suggestUInt16(): UInt16 {
return valuesInt16.next(inner, temperature, mutator)
}
public override func suggestUInt32(): UInt32 {
return valuesInt32.next(inner, temperature, mutator)
}
public override func suggestUInt64(): UInt64 {
return valuesInt64.next(inner, temperature, mutator)
}
@OverflowWrapping
public override func suggestInt8(): Int8 {
Int8(suggestUInt8())
}
@OverflowWrapping
public override func suggestInt16(): Int16 {
Int16(suggestUInt16())
}
@OverflowWrapping
public override func suggestInt32(): Int32 {
Int32(suggestUInt32())
}
@OverflowWrapping
public override func suggestInt64(): Int64 {
Int64(suggestUInt64())
}
public override func nextFloat16(): Float16 {
return Float16(valuesFloat32.nextUniform(inner))
}
public override func nextFloat32(): Float32 {
return valuesFloat32.nextUniform(inner)
}
public override func nextFloat64(): Float64 {
return valuesFloat64.nextUniform(inner)
}
@OverflowWrapping
public override func suggestFloat16(): Float16 {
return Float16(suggestFloat32())
}
public override func suggestFloat32(): Float32 {
return valuesFloat32.next(inner, temperature, mutator)
}
public override func suggestFloat64(): Float64 {
return valuesFloat64.next(inner, temperature, mutator)
}
private var gaussian = GaussianGenerator(0.0, 1.0)
public override func nextGaussianFloat64(mean!: Float64 = 0.0, sigma!: Float64 = 1.0): Float64 {
if (gaussian.mean != mean || gaussian.sigma != sigma) {
this.gaussian = GaussianGenerator(mean, sigma)
}
return gaussian.next(this)
}
public override func nextUInt8(): UInt8 {
valuesInt8.nextUniform(inner)
}
public override func nextUInt16(): UInt16 {
valuesInt16.nextUniform(inner)
}
public override func nextUInt32(): UInt32 {
valuesInt32.nextUniform(inner)
}
public override func nextUInt64(): UInt64 {
valuesInt64.nextUniform(inner)
}
}
class GaussianGenerator {
var cached: ?Float64 = Option.None
GaussianGenerator(let mean: Float64, let sigma: Float64) {}
func next(random: RandomSource): Float64 {
if (let Some(cached) <- this.cached) {
this.cached = None
return cached
}
var u1: Float64 = random.nextFloat64()
let u2 = random.nextFloat64()
let mag = sigma * sqrt(-2.0 * log(u1))
let z0 = mag * cos(2.0 * Float64.GetPI()) + mean
let z1 = mag * sin(2.0 * Float64.GetPI()) + mean
this.cached = z1
return z0
}
}
class CoverageInfo {
static func newCoverageDiscovered(): Bool {
unsafe { CJ_MICROFUZZ_HAS_NEW_COVERAGE() }
}
}