'use strict';

const { Worker, isMainThread, parentPort, workerData } = require('worker_threads');

const ARRAY_SIZE = 15000576;
const NUM_THREADS = 8; // Reduced from 12 to minimize overhead; adjust to your core count (e.g., 4-16)

function getTime() {
    const ns = process.hrtime.bigint();
    return Number(ns) / 1e9; // Returns seconds
}

if (isMainThread) {
    (async () => {
        let start, end;

        // Time memory allocation
        start = getTime();
        const sabA = new SharedArrayBuffer(ARRAY_SIZE * 4);
        const sabB = new SharedArrayBuffer(ARRAY_SIZE * 4);
        const sabC = new SharedArrayBuffer(ARRAY_SIZE * 4);
        const a = new Float32Array(sabA);
        const b = new Float32Array(sabB);
        const c = new Float32Array(sabC);
        end = getTime();
        const allocTime = end - start;

        // Time array initialization (single-threaded, like original C)
        start = getTime();
        for (let i = 0; i < ARRAY_SIZE; i++) {
            a[i] = i;
            b[i] = i * 2;
        }
        end = getTime();
        const initTime = end - start;

        // Create workers for parallel computation
        const chunkSize = Math.floor(ARRAY_SIZE / NUM_THREADS);
        const workerPromises = [];

        // Time worker creation and execution (only for computation)
        start = getTime();
        for (let t = 0; t < NUM_THREADS; t++) {
            const workerStart = t * chunkSize;
            const workerEnd = (t === NUM_THREADS - 1) ? ARRAY_SIZE : (t + 1) * chunkSize;
            const worker = new Worker(__filename, {
                workerData: {
                    start: workerStart,
                    end: workerEnd,
                    sabA,
                    sabB,
                    sabC
                }
            });
            workerPromises.push(new Promise((resolve, reject) => {
                worker.on('exit', resolve);
                worker.on('error', reject);
            }));
        }

        // Wait for workers to finish
        await Promise.all(workerPromises);
        end = getTime();
        const threadTime = end - start;

        // Print timing results (matching original structure)
        console.log(`Memory allocation time: ${(allocTime * 1000).toFixed(6)} ms`);
        console.log(`Array initialization time: ${(initTime * 1000).toFixed(6)} ms`);
        console.log(`Thread execution time: ${(threadTime * 1000).toFixed(6)} ms`);
        console.log(`Total time: ${((allocTime + initTime + threadTime) * 1000).toFixed(6)} ms`);

        // Print a sample of the result
        for (let i = 0; i < 10; i++) {
            console.log(`c[${i}] = ${c[i]}`);
        }

        // No explicit cleanup (garbage collected)
    })();
} else {
    const { start, end, sabA, sabB, sabC } = workerData;
    const a = new Float32Array(sabA);
    const b = new Float32Array(sabB);
    const c = new Float32Array(sabC);

    // Parallel computation only
    for (let i = start; i < end; i++) {
        c[i] = a[i] + b[i];
    }

    // Tip: For extra speed, you could combine init into this loop (if moved from main thread),
    // but that would parallelize init too: a[i] = i; b[i] = i * 2; c[i] = a[i] + b[i];

    // Worker exits automatically
}