use ferric_core::{demo, matmul_cpu, max_abs_diff, Context};
use wasm_bindgen::prelude::*;
fn gen(m: u32, k: u32, n: u32) -> (Vec<f32>, Vec<f32>) {
let a: Vec<f32> = (0..(m * k) as usize).map(|i| ((i * 7 % 13) as f32 - 6.0) * 0.1).collect();
let b: Vec<f32> = (0..(k * n) as usize).map(|i| ((i * 5 % 11) as f32 - 5.0) * 0.1).collect();
(a, b)
}
#[wasm_bindgen]
pub async fn ferric_matmul_demo(m: u32, k: u32, n: u32) -> std::result::Result<String, JsValue> {
console_error_panic_hook::set_once();
let ctx = Context::new().await.map_err(|e| JsValue::from_str(&e))?;
let (a, b) = gen(m, k, n);
let gpu = ctx.matmul(&a, &b, m, k, n).await.map_err(|e| JsValue::from_str(&e))?;
let cpu = matmul_cpu(&a, &b, m as usize, k as usize, n as usize);
let diff = max_abs_diff(&gpu, &cpu);
Ok(format!("{:?}|{:.3e}|{:?}", ctx.backend, diff, &gpu[..6.min(gpu.len())]))
}
#[wasm_bindgen]
pub async fn ferric_lm_demo(prompt: String, steps: usize) -> std::result::Result<String, JsValue> {
console_error_panic_hook::set_once();
let ids: Vec<u32> = prompt.split(',').filter_map(|s| s.trim().parse().ok()).map(|v: u32| v % demo::VOCAB as u32).collect();
if ids.is_empty() {
return Err(JsValue::from_str("no valid token ids in prompt"));
}
let ctx = Context::new().await.map_err(|e| JsValue::from_str(&e))?;
let t0 = js_sys::Date::now();
let generated = demo::generate(&ctx, &ids, steps).await.map_err(|e| JsValue::from_str(&e))?;
let ms = js_sys::Date::now() - t0;
let gpu = demo::logits(&ctx, &ids).await.map_err(|e| JsValue::from_str(&e))?;
let diff = max_abs_diff(&gpu, &demo::logits_cpu(&ids));
let js = |v: &[u32]| v.iter().map(|x| x.to_string()).collect::<Vec<_>>().join(",");
Ok(format!(
"{{\"backend\":\"{:?}\",\"adapter\":\"{}\",\"prompt\":[{}],\"generated\":[{}],\"layers\":{},\"vocab\":{},\"logit_diff\":{:.3e},\"ms\":{:.1}}}",
ctx.backend, ctx.adapter_name, js(&ids), js(&generated), demo::N_LAYERS, demo::VOCAB, diff, ms
))
}