"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
const native_loader_1 = require("../ts/native_loader");
const array_1 = require("../ts/array");
const fs = __importStar(require("fs"));
const { Conv1d, BatchNorm1d, Sequential, Flatten, Linear, ReLU, Dropout } = native_loader_1.nn;
// --- Data Generation ---
// Generate synthetic sine wave data
function generateSyntheticTimeSeries(numSamples, seqLen, offset) {
    const inputs = [];
    const targets = [];
    const totalPoints = numSamples + seqLen;
    const rawData = new Float32Array(totalPoints);
    for (let k = 0; k < totalPoints; k++) {
        const t = offset + k;
        // Sine wave with period ~63 + noise
        let val = Math.sin(t * 0.1) * 10.0;
        const noise = ((k * 12345 + 6789) % 100) / 20.0 - 2.5;
        val += noise;
        rawData[k] = val;
    }
    const center = 0.0;
    const scale = 12.5;
    for (let i = 0; i < numSamples; i++) {
        const window = [];
        for (let j = 0; j < seqLen; j++) {
            window.push((rawData[i + j] - center) / scale);
        }
        const targetVal = (rawData[i + seqLen] - center) / scale;
        inputs.push(window);
        targets.push([targetVal]);
    }
    return { inputs, targets };
}
async function main() {
    console.log("═══════════════════════════════════════════════════════════");
    console.log("  📈  NumRs: Time-Series CNN Forecasting (Node.js)");
    console.log("═══════════════════════════════════════════════════════════\n");
    const SEQ_LEN = 128;
    const BATCH_SIZE = 32;
    const EPOCHS = 50;
    // 1. Data Generation
    console.log("📊 PASO 1: Generando dataset (Noisy Sine Wave)\n");
    const trainData = generateSyntheticTimeSeries(5000, SEQ_LEN, 0);
    const testData = generateSyntheticTimeSeries(100, SEQ_LEN, 200);
    const trainDataset = new native_loader_1.Dataset(trainData.inputs, trainData.targets, BATCH_SIZE);
    const testDataset = new native_loader_1.Dataset(testData.inputs, testData.targets, BATCH_SIZE);
    console.log(`  Training batches: ${trainDataset.numBatches}`);
    // 2. Define Architecture
    console.log("\n🧠 PASO 2: Definiendo arquitectura CNN\n");
    // Model: Unsqueeze(1) -> Conv -> BN -> ReLU -> Dropout -> ...
    const model = new Sequential();
    // IMPORTANT: Custom reshape layer we added to bindings!
    // Input [N, L] -> [N, 1, L]
    model.addUnsqueeze(1);
    // Block 1: 1 -> 16
    model.addConv1D(new Conv1d(1, 16, 3, 1, 0));
    model.addBatchNorm1D(new BatchNorm1d(16));
    model.addRelu(new ReLU());
    model.addDropout(new Dropout(0.1));
    // Block 2: 16 -> 32
    model.addConv1D(new Conv1d(16, 32, 3, 1, 0));
    model.addBatchNorm1D(new BatchNorm1d(32));
    model.addRelu(new ReLU());
    model.addDropout(new Dropout(0.1));
    // Head calculation:
    // L_in = 128
    // L1 = 128 - 3 + 1 = 126
    // L2 = 126 - 3 + 1 = 124
    // Output channels = 32
    // Flat features = 32 * 124 = 3968
    const flatFeatures = 32 * 124;
    model.addFlatten(new Flatten(1, 2)); // Flatten C and L
    model.addLinear(new Linear(flatFeatures, 64));
    model.addRelu(new ReLU());
    model.addLinear(new Linear(64, 1));
    console.log("  Arquitectura configurada.");
    // 3. Training
    console.log("\n🎯 PASO 3: Iniciando entrenamiento\n");
    const builder = new native_loader_1.TrainerBuilder(model);
    builder.learningRate(0.001);
    const trainer = builder.build("adam", "mse");
    const history = trainer.fit(trainDataset, testDataset, EPOCHS, true);
    const last = history[history.length - 1];
    console.log(`\n  ✓ Entrenamiento completado. Loss final: ${last.trainLoss.toFixed(4)}`);
    // 4. Visualization
    console.log("\n🔍 PASO 4: Validación Visual (Primer batch de test)\n");
    // Manual forward on random sample
    // We can't easily get batch from Dataset in JS (no getter exposed in NativeDataset yet).
    // So we reconstruct a tensor from testData manually.
    const sampleInputRaw = testData.inputs[0];
    const sampleTargetRaw = testData.targets[0][0];
    const f32Input = new Float32Array(sampleInputRaw);
    // Create [1, 128]
    const inputTensor = new native_loader_1.Tensor(new array_1.Array(f32Input, [1, SEQ_LEN]), [1, SEQ_LEN], false);
    // Forward
    const predTensor = model.forward(inputTensor);
    const predVal = predTensor.data.data[0];
    // Denormalize
    const scale = 12.5;
    const center = 0.0;
    const realPred = predVal * scale + center;
    const realTarget = sampleTargetRaw * scale + center;
    console.log(`  Sample 0:`);
    console.log(`    Predicted (Norm): ${predVal.toFixed(4)}`);
    console.log(`    Target    (Norm): ${sampleTargetRaw.toFixed(4)}`);
    console.log(`    Predicted (Real): ${realPred.toFixed(4)}`);
    console.log(`    Target    (Real): ${realTarget.toFixed(4)}`);
    console.log(`    Diff:             ${Math.abs(realPred - realTarget).toFixed(4)}`);
    // 5. Export
    console.log("\n📦 PASO 5: Exportando a ONNX\n");
    try {
        model.saveOnnx(inputTensor, "timeseries_cnn.onnx");
        console.log("  ✅ Modelo exportado a timeseries_cnn.onnx");
        if (fs.existsSync("timeseries_cnn.onnx")) {
            fs.unlinkSync("timeseries_cnn.onnx");
        }
    }
    catch (e) {
        console.error("  Error exporting:", e);
    }
}
main().catch(console.error);
//# sourceMappingURL=time_series.js.map