// Batch image preprocessing on four local CPU workers.
// Synthetic grayscale images; requires at least four admitted CPU slots.
// Run: aner run examples/parallel/cpu_image_blur_intro.aner --max-steps 500000000
import aner.parallel
// Batch preprocessing: one independent grayscale camera image per task.
// The fixture is synthetic: a lighting gradient, a bright central object and
// reproducible sensor texture. Each image varies exposure; no file I/O is timed.
import aner.math

fn process_image(parameters: List, iteration: Int64, seed: Int64) -> List {
    let width = cast<Int64>(parameters.get(0))
    let height = cast<Int64>(parameters.get(1))
    let passes = cast<Int64>(parameters.get(2))
    let pixels = width * height
    var source = Array<Float64>(pixels, 0.0)
    var destination = Array<Float64>(pixels, 0.0)
    let exposure = 0.85 + Float64(iteration % 15) * 0.02

    // Both buffers start with the same boundary pixels. The blur updates only
    // interior pixels, so every pass preserves the original one-pixel border.
    var y = 0
    while y < height {
        var x = 0
        while x < width {
            let texture = Float64((x * 17 + y * 13 + iteration * 3) % 23) - 11.0
            var intensity = 20.0 + 140.0 * Float64(x) / Float64(width - 1) + texture
            if x >= width / 4 && x < 3 * width / 4 &&
               y >= height / 4 && y < 3 * height / 4 {
                intensity = intensity + 65.0
            }
            intensity = intensity * exposure
            if intensity < 0.0 { intensity = 0.0 }
            if intensity > 255.0 { intensity = 255.0 }
            let index = y * width + x
            source.set(index, intensity)
            destination.set(index, intensity)
            x = x + 1
        }
        y = y + 1
    }

    var pass = 0
    while pass < passes {
        y = 1
        while y < height - 1 {
            var x = 1
            while x < width - 1 {
                let index = y * width + x
                let neighborhood = source.get(index - width - 1) + source.get(index - width) +
                    source.get(index - width + 1) + source.get(index - 1) + source.get(index) +
                    source.get(index + 1) + source.get(index + width - 1) +
                    source.get(index + width) + source.get(index + width + 1)
                destination.set(index, neighborhood / 9.0)
                x = x + 1
            }
            y = y + 1
        }
        // Read the old image throughout one pass; swap after the full pass.
        let previous = source
        source = destination
        destination = previous
        pass = pass + 1
    }

    var pixel_sum = 0.0
    var square_sum = 0.0
    var index = 0
    while index < pixels {
        let value = source.get(index)
        pixel_sum = pixel_sum + value
        square_sum = square_sum + value * value
        index = index + 1
    }
    // Deterministic output columns: mean gray level, RMS gray level, center pixel.
    return List(pixel_sum / Float64(pixels), math.sqrt(square_sum / Float64(pixels)),
        source.get((height / 2) * width + width / 2))
}

// Each image gets its own buffers.
let parameters = List(64, 64, 3)
let results = parallel.repeat(
    iterations: 64,
    task: process_image,
    parameters: parameters,
    execution: parallel.local(workers: 4),
    seed: 2026)

print(results.rows())
print(results.mean(column: 0))
