use crate::executor::VirtualMachine;
use shape_runtime::context::ExecutionContext;
use shape_runtime::intrinsics::matrix_kernels;
use shape_value::aligned_vec::AlignedVec;
use shape_value::heap_value::{HeapKind, MatrixData};
use shape_value::{KindedSlot, NativeKind, VMError};
use std::sync::Arc;
#[inline]
fn type_error(msg: impl Into<String>) -> VMError {
VMError::RuntimeError(msg.into())
}
#[inline]
fn as_matrix(slot: &KindedSlot) -> Result<Arc<MatrixData>, VMError> {
if !matches!(slot.kind, NativeKind::Ptr(HeapKind::Matrix)) {
return Err(type_error(format!(
"Matrix method receiver must be a Matrix (got kind {:?})",
slot.kind
)));
}
let bits = slot.slot.raw();
if bits == 0 {
return Err(type_error("Matrix method receiver: slot bits null"));
}
let arc = unsafe { Arc::<MatrixData>::from_raw(bits as *const MatrixData) };
let cloned = Arc::clone(&arc);
let _ = Arc::into_raw(arc);
Ok(cloned)
}
#[inline]
fn matrix_slot(m: MatrixData) -> KindedSlot {
KindedSlot::from_matrix(Arc::new(m))
}
fn float_array_slot(data: AlignedVec<f64>) -> Result<KindedSlot, VMError> {
let _ = data;
Err(VMError::NotImplemented(
"matrix: float_array_slot SURFACE — V3-S5 ckpt-5 consumer-cascade \
tier 3. The deleted typed-array-data F64 constructor + outer \
`HeapValue::TypedArray` arm DELETED at ckpt-1..ckpt-4 per W12-typed-array-\
data-deletion audit §3.5 + §3.6. Rebuild lands at ckpt-6 STRICT \
close per v2-raw `TypedArray<f64>` direct-access. REFUSED ON \
SIGHT: TypedArrayData resurrection under any rename (Refusal #1)."
.to_string(),
))
}
fn int_array_slot(data: Vec<i64>) -> Result<KindedSlot, VMError> {
let _ = data;
Err(VMError::NotImplemented(
"matrix: int_array_slot SURFACE — V3-S5 ckpt-5 consumer-cascade \
tier 3. The deleted typed-array-data I64 constructor DELETED at ckpt-1..\
ckpt-4 per W12-typed-array-data-deletion audit §3.5. Rebuild \
lands at ckpt-6 STRICT close per v2-raw `TypedArray<i64>` \
direct-access. REFUSED ON SIGHT (Refusal #1)."
.to_string(),
))
}
#[inline]
fn arg_as_index(slot: &KindedSlot, name: &str) -> Result<i64, VMError> {
match slot.kind {
NativeKind::Int64 => Ok(slot.slot.raw() as i64),
NativeKind::Float64 => {
let f = f64::from_bits(slot.slot.raw());
if f.is_finite() && f.trunc() == f {
Ok(f as i64)
} else {
Err(type_error(format!(
"Matrix.{}: expected integer index, got non-integral number {}",
name, f
)))
}
}
other => Err(type_error(format!(
"Matrix.{}: expected integer index, got kind {:?}",
name, other
))),
}
}
pub fn v2_transpose(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.is_empty() {
return Err(type_error("transpose: missing receiver"));
}
let m = as_matrix(&args[0])?;
let out = matrix_kernels::matrix_transpose(&m);
Ok(matrix_slot(out))
}
pub fn v2_inverse(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.is_empty() {
return Err(type_error("inverse: missing receiver"));
}
let m = as_matrix(&args[0])?;
let out = matrix_kernels::matrix_inverse(&m).map_err(VMError::RuntimeError)?;
Ok(matrix_slot(out))
}
pub fn v2_determinant(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.is_empty() {
return Err(type_error("det: missing receiver"));
}
let m = as_matrix(&args[0])?;
let det = matrix_kernels::matrix_determinant(&m).map_err(VMError::RuntimeError)?;
Ok(KindedSlot::from_number(det))
}
pub fn v2_trace(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.is_empty() {
return Err(type_error("trace: missing receiver"));
}
let m = as_matrix(&args[0])?;
let tr = matrix_kernels::matrix_trace(&m).map_err(VMError::RuntimeError)?;
Ok(KindedSlot::from_number(tr))
}
pub fn v2_shape(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.is_empty() {
return Err(type_error("shape: missing receiver"));
}
let m = as_matrix(&args[0])?;
int_array_slot(vec![m.rows as i64, m.cols as i64])
}
pub fn v2_reshape(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() < 3 {
return Err(type_error(
"reshape: expected (matrix, rows, cols)".to_string(),
));
}
let m = as_matrix(&args[0])?;
let rows = arg_as_index(&args[1], "reshape")?;
let cols = arg_as_index(&args[2], "reshape")?;
if rows < 0 || cols < 0 {
return Err(type_error(format!(
"reshape: dimensions must be non-negative (got rows={}, cols={})",
rows, cols
)));
}
let total = (rows as usize) * (cols as usize);
if total != m.data.len() {
return Err(type_error(format!(
"reshape: rows*cols ({}) must equal element count ({})",
total,
m.data.len()
)));
}
let mut data = AlignedVec::<f64>::with_capacity(total);
for v in m.data.as_slice().iter() {
data.push(*v);
}
let out = MatrixData::from_flat(data, rows as u32, cols as u32);
Ok(matrix_slot(out))
}
pub fn v2_row(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() < 2 {
return Err(type_error("row: expected (matrix, index)".to_string()));
}
let m = as_matrix(&args[0])?;
let idx = arg_as_index(&args[1], "row")?;
if idx < 0 || (idx as u32) >= m.rows {
return Err(type_error(format!(
"row: index {} out of bounds (rows={})",
idx, m.rows
)));
}
let cols = m.cols as usize;
let mut data = AlignedVec::<f64>::with_capacity(cols);
for v in m.row_slice(idx as u32).iter() {
data.push(*v);
}
float_array_slot(data)
}
pub fn v2_col(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() < 2 {
return Err(type_error("col: expected (matrix, index)".to_string()));
}
let m = as_matrix(&args[0])?;
let idx = arg_as_index(&args[1], "col")?;
if idx < 0 || (idx as u32) >= m.cols {
return Err(type_error(format!(
"col: index {} out of bounds (cols={})",
idx, m.cols
)));
}
let cols = m.cols as usize;
let rows = m.rows as usize;
let mut data = AlignedVec::<f64>::with_capacity(rows);
for r in 0..rows {
data.push(m.data.as_slice()[r * cols + idx as usize]);
}
float_array_slot(data)
}
pub fn v2_diag(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.is_empty() {
return Err(type_error("diag: missing receiver"));
}
let m = as_matrix(&args[0])?;
let n = (m.rows as usize).min(m.cols as usize);
let cols = m.cols as usize;
let mut data = AlignedVec::<f64>::with_capacity(n);
for i in 0..n {
data.push(m.data.as_slice()[i * cols + i]);
}
float_array_slot(data)
}
pub fn v2_flatten(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.is_empty() {
return Err(type_error("flatten: missing receiver"));
}
let m = as_matrix(&args[0])?;
let mut data = AlignedVec::<f64>::with_capacity(m.data.len());
for v in m.data.as_slice().iter() {
data.push(*v);
}
float_array_slot(data)
}
pub fn v2_sum(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.is_empty() {
return Err(type_error("sum: missing receiver"));
}
let m = as_matrix(&args[0])?;
let s: f64 = m.data.as_slice().iter().sum();
Ok(KindedSlot::from_number(s))
}
pub fn v2_min(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.is_empty() {
return Err(type_error("min: missing receiver"));
}
let m = as_matrix(&args[0])?;
if m.data.is_empty() {
return Err(type_error("min: empty matrix"));
}
let mn = m
.data
.as_slice()
.iter()
.copied()
.fold(f64::INFINITY, f64::min);
Ok(KindedSlot::from_number(mn))
}
pub fn v2_max(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.is_empty() {
return Err(type_error("max: missing receiver"));
}
let m = as_matrix(&args[0])?;
if m.data.is_empty() {
return Err(type_error("max: empty matrix"));
}
let mx = m
.data
.as_slice()
.iter()
.copied()
.fold(f64::NEG_INFINITY, f64::max);
Ok(KindedSlot::from_number(mx))
}
pub fn v2_mean(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.is_empty() {
return Err(type_error("mean: missing receiver"));
}
let m = as_matrix(&args[0])?;
if m.data.is_empty() {
return Err(type_error("mean: empty matrix"));
}
let s: f64 = m.data.as_slice().iter().sum();
Ok(KindedSlot::from_number(s / (m.data.len() as f64)))
}
pub fn v2_row_sum(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.is_empty() {
return Err(type_error("rowSum: missing receiver"));
}
let m = as_matrix(&args[0])?;
let rows = m.rows as usize;
let cols = m.cols as usize;
let mut data = AlignedVec::<f64>::with_capacity(rows);
for r in 0..rows {
let s: f64 = m.data.as_slice()[r * cols..r * cols + cols].iter().sum();
data.push(s);
}
float_array_slot(data)
}
pub fn v2_col_sum(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.is_empty() {
return Err(type_error("colSum: missing receiver"));
}
let m = as_matrix(&args[0])?;
let rows = m.rows as usize;
let cols = m.cols as usize;
let mut sums = vec![0.0_f64; cols];
let slice = m.data.as_slice();
for r in 0..rows {
for c in 0..cols {
sums[c] += slice[r * cols + c];
}
}
let mut data = AlignedVec::<f64>::with_capacity(cols);
for s in sums.into_iter() {
data.push(s);
}
float_array_slot(data)
}
pub(crate) fn handle_map(
vm: &mut VirtualMachine,
args: &[KindedSlot],
mut ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
if args.len() < 2 {
return Err(type_error("map: expected (matrix, closure)".to_string()));
}
if args[1].kind != NativeKind::Ptr(HeapKind::Closure) {
return Err(type_error(format!(
"map: second argument must be a closure, got kind {:?}",
args[1].kind
)));
}
let m = as_matrix(&args[0])?;
let total = m.data.len();
let closure = &args[1];
let mut out = AlignedVec::<f64>::with_capacity(total);
for i in 0..total {
let elem = KindedSlot::from_number(m.data.as_slice()[i]);
let result = vm.call_value_immediate_nb(closure, &[elem], ctx.as_deref_mut())?;
let v = match result.kind {
NativeKind::Float64 => f64::from_bits(result.slot.raw()),
NativeKind::Int64 => (result.slot.raw() as i64) as f64,
NativeKind::Bool => {
if result.slot.raw() != 0 {
1.0
} else {
0.0
}
}
other => {
return Err(type_error(format!(
"map: closure must return a numeric kind, got {:?}",
other
)));
}
};
out.push(v);
}
let new_matrix = MatrixData::from_flat(out, m.rows, m.cols);
Ok(matrix_slot(new_matrix))
}
#[cfg(test)]
mod tests {
use super::*;
use shape_value::aligned_vec::AlignedVec;
fn aligned_from(vals: &[f64]) -> AlignedVec<f64> {
let mut a = AlignedVec::with_capacity(vals.len());
for v in vals {
a.push(*v);
}
a
}
#[test]
fn matrix_from_flat_round_trips_dimensions_and_data() {
let data = aligned_from(&[1.0, 2.0, 3.0, 4.0]);
let m = MatrixData::from_flat(data, 2, 2);
assert_eq!(m.rows, 2);
assert_eq!(m.cols, 2);
assert_eq!(m.data.len(), 4);
assert_eq!(m.get(0, 0), 1.0);
assert_eq!(m.get(0, 1), 2.0);
assert_eq!(m.get(1, 0), 3.0);
assert_eq!(m.get(1, 1), 4.0);
}
#[test]
fn matrix_transpose_reorders_row_major_layout() {
let data = aligned_from(&[1.0, 2.0, 3.0, 4.0]);
let m = MatrixData::from_flat(data, 2, 2);
let t = matrix_kernels::matrix_transpose(&m);
assert_eq!(t.rows, 2);
assert_eq!(t.cols, 2);
assert_eq!(t.get(0, 0), 1.0); assert_eq!(t.get(0, 1), 3.0);
assert_eq!(t.get(1, 0), 2.0);
assert_eq!(t.get(1, 1), 4.0);
}
#[test]
fn matrix_transpose_non_square_swaps_dimensions() {
let data = aligned_from(&[1.0, 2.0, 3.0, 4.0, 5.0, 6.0]);
let m = MatrixData::from_flat(data, 2, 3);
let t = matrix_kernels::matrix_transpose(&m);
assert_eq!(t.rows, 3);
assert_eq!(t.cols, 2);
assert_eq!(t.get(0, 0), 1.0);
assert_eq!(t.get(0, 1), 4.0);
assert_eq!(t.get(1, 0), 2.0);
assert_eq!(t.get(1, 1), 5.0);
assert_eq!(t.get(2, 0), 3.0);
assert_eq!(t.get(2, 1), 6.0);
}
#[test]
fn matrix_determinant_2x2_matches_classic_formula() {
let data = aligned_from(&[1.0, 2.0, 3.0, 4.0]);
let m = MatrixData::from_flat(data, 2, 2);
let d = matrix_kernels::matrix_determinant(&m).expect("det");
assert!((d - (-2.0)).abs() < 1e-12);
}
#[test]
fn matrix_trace_sums_diagonal() {
let data = aligned_from(&[1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]);
let m = MatrixData::from_flat(data, 3, 3);
let t = matrix_kernels::matrix_trace(&m).expect("trace");
assert!((t - 15.0).abs() < 1e-12);
}
#[test]
fn matrix_data_clone_shares_arc_payload() {
let data = aligned_from(&[1.0, 2.0, 3.0, 4.0]);
let m = Arc::new(MatrixData::from_flat(data, 2, 2));
let cloned = Arc::clone(&m);
assert!(Arc::ptr_eq(&m, &cloned));
assert_eq!(Arc::strong_count(&m), 2);
}
}