// Weighted dice: serial setup, 1000 parallel paired rolls, serial analysis.
// Requires the updated native Aner engine and at least four admitted CPU slots.
import aner.parallel

// The supplied fixtures have finite, positive Float64 weights.
// Select faces 1..weights.len() by their cumulative relative weights.
fn dice(weights: List, seed: Int64, draw: Int64) -> Int64 {
    var total = 0.0
    var index = 0
    while index < weights.len() {
        total = total + cast<Float64>(weights.get(index))
        index = index + 1
    }
    let threshold = parallel.random(seed: seed, draw: draw) * total
    var cumulative = 0.0
    index = 0
    while index < weights.len() {
        cumulative = cumulative + cast<Float64>(weights.get(index))
        if threshold < cumulative { return index + 1 }
        index = index + 1
    }
    return weights.len()
}

// Each invocation owns its parameter copies; two draws produce one paired row.
fn roll_pair(parameters: List, iteration: Int64, seed: Int64) -> List {
    let balanced = cast<List>(parameters.get(0))
    let unbalanced = cast<List>(parameters.get(1))
    return List(dice(balanced, seed, draw: 0), dice(unbalanced, seed, draw: 1))
}

// Serial histogram over the retained Float64 face values. No numeric narrowing.
fn face_counts(rolls: List) -> Array<Int64> {
    let counts = Array<Int64>(6, 0)
    var iteration = 0
    while iteration < rolls.len() {
        let roll = cast<Float64>(rolls.get(iteration))
        var face = 0
        while face < 6 {
            if roll == Float64(face + 1) {
                counts.set(face, counts.get(face) + 1)
            }
            face = face + 1
        }
        iteration = iteration + 1
    }
    return counts
}

// 1. Serial setup: the same initial weights are supplied to every repetition.
let dice_balanced = List(1.0, 1.0, 1.0, 1.0, 1.0, 1.0)
let dice_unbalanced = List(50.0, 10.0, 10.0, 10.0, 10.0, 10.0)
let initial_parameters = List(dice_balanced, dice_unbalanced)

// BEGIN PARALLEL
// 2. Four native CPU workers independently roll both dice 1000 times.
let results = parallel.repeat(iterations: 1000, task: roll_pair,
    parameters: initial_parameters, execution: parallel.local(workers: 4), seed: 42)
// END PARALLEL

// 3. Serial analysis after every paired roll is collected in iteration order.
let dice_balanced_rolls = results.column(column: 0)
let dice_unbalanced_rolls = results.column(column: 1)
print("Balanced average: " + String(results.mean(column: 0)))
print("Unbalanced average: " + String(results.mean(column: 1)))
let balanced_counts = face_counts(dice_balanced_rolls)
let unbalanced_counts = face_counts(dice_unbalanced_rolls)
print("Face | Balanced | Unbalanced")
var face = 0
while face < 6 {
    print(String(face + 1) + " | " + String(balanced_counts.get(face)) +
        " | " + String(unbalanced_counts.get(face)))
    face = face + 1
}
// No files are created. To save all paired outcomes, choose a new CSV path:
// results.save_csv("dice-results.csv")
