// Example 126: Const Generics
// Rust's const generics allow compile-time parameterization by values
use std::ops::{Add, Mul};
// Approach 1: Fixed-size array wrapper with const generic
#[derive(Debug, Clone)]
struct FixedArray<T, const N: usize> {
data: [T; N],
}
impl<T: Default + Copy, const N: usize> FixedArray<T, N> {
fn new(default: T) -> Self {
FixedArray { data: [default; N] }
}
fn len(&self) -> usize {
N
}
fn get(&self, i: usize) -> Option<&T> {
self.data.get(i)
}
fn set(&mut self, i: usize, val: T) {
if i < N {
self.data[i] = val;
}
}
fn map<U: Default + Copy, F: Fn(&T) -> U>(&self, f: F) -> FixedArray<U, N> {
let mut result = FixedArray::<U, N>::new(U::default());
for i in 0..N {
result.data[i] = f(&self.data[i]);
}
result
}
}
impl<const N: usize> FixedArray<f64, N> {
fn dot(&self, other: &Self) -> f64 {
self.data.iter().zip(other.data.iter()).map(|(a, b)| a * b).sum()
}
}
// Approach 2: Matrix with const generics for rows and cols
#[derive(Debug, Clone)]
struct Matrix<T, const ROWS: usize, const COLS: usize> {
data: [[T; COLS]; ROWS],
}
impl<T: Default + Copy, const ROWS: usize, const COLS: usize> Matrix<T, ROWS, COLS> {
fn new(default: T) -> Self {
Matrix {
data: [[default; COLS]; ROWS],
}
}
fn get(&self, r: usize, c: usize) -> Option<&T> {
self.data.get(r).and_then(|row| row.get(c))
}
fn set(&mut self, r: usize, c: usize, val: T) {
if r < ROWS && c < COLS {
self.data[r][c] = val;
}
}
fn rows(&self) -> usize { ROWS }
fn cols(&self) -> usize { COLS }
}
// Matrix multiplication with compile-time dimension checking!
impl<T, const M: usize, const N: usize> Matrix<T, M, N>
where
T: Default + Copy + Add<Output = T> + Mul<Output = T>,
{
fn mul<const P: usize>(&self, other: &Matrix<T, N, P>) -> Matrix<T, M, P> {
let mut result = Matrix::<T, M, P>::new(T::default());
for i in 0..M {
for j in 0..P {
let mut sum = T::default();
for k in 0..N {
sum = sum + self.data[i][k] * other.data[k][j];
}
result.data[i][j] = sum;
}
}
result
}
}
// Approach 3: Compile-time array operations
fn array_sum<const N: usize>(arr: &[f64; N]) -> f64 {
arr.iter().sum()
}
fn array_zip_with<T: Copy, U: Copy, V, const N: usize>(
a: &[T; N],
b: &[U; N],
f: impl Fn(T, U) -> V,
) -> Vec<V> {
a.iter().zip(b.iter()).map(|(&x, &y)| f(x, y)).collect()
}
fn main() {
// Const generic vectors
let mut v: FixedArray<f64, 3> = FixedArray::new(0.0);
v.set(0, 1.0);
v.set(1, 2.0);
v.set(2, 3.0);
println!("Vec3: {:?}, len={}", v, v.len());
let a = FixedArray { data: [1.0, 2.0, 3.0] };
let b = FixedArray { data: [4.0, 5.0, 6.0] };
println!("Dot product: {}", a.dot(&b));
// Matrix multiplication with dimension safety
let mut m1: Matrix<f64, 2, 3> = Matrix::new(0.0);
m1.data = [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]];
let mut m2: Matrix<f64, 3, 2> = Matrix::new(0.0);
m2.data = [[7.0, 8.0], [9.0, 10.0], [11.0, 12.0]];
let m3: Matrix<f64, 2, 2> = m1.mul(&m2);
println!("Matrix product [0][0]: {}", m3.data[0][0]);
// Compile-time sized operations
let arr = [1.0, 2.0, 3.0, 4.0, 5.0];
println!("Sum of [1..5]: {}", array_sum(&arr));
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_fixed_array_basic() {
let mut v: FixedArray<i32, 4> = FixedArray::new(0);
assert_eq!(v.len(), 4);
v.set(2, 42);
assert_eq!(v.get(2), Some(&42));
assert_eq!(v.get(4), None);
}
#[test]
fn test_dot_product() {
let a = FixedArray { data: [1.0, 2.0, 3.0] };
let b = FixedArray { data: [4.0, 5.0, 6.0] };
assert_eq!(a.dot(&b), 32.0);
}
#[test]
fn test_matrix_dimensions() {
let m: Matrix<f64, 2, 3> = Matrix::new(0.0);
assert_eq!(m.rows(), 2);
assert_eq!(m.cols(), 3);
}
#[test]
fn test_matrix_multiply() {
let m1 = Matrix { data: [[1.0, 2.0], [3.0, 4.0]] };
let m2 = Matrix { data: [[5.0, 6.0], [7.0, 8.0]] };
let result: Matrix<f64, 2, 2> = m1.mul(&m2);
assert_eq!(result.data[0][0], 19.0);
assert_eq!(result.data[0][1], 22.0);
assert_eq!(result.data[1][0], 43.0);
assert_eq!(result.data[1][1], 50.0);
}
#[test]
fn test_map() {
let v = FixedArray { data: [1, 2, 3] };
let doubled = v.map(|x| x * 2);
assert_eq!(doubled.data, [2, 4, 6]);
}
#[test]
fn test_array_sum() {
assert_eq!(array_sum(&[1.0, 2.0, 3.0]), 6.0);
assert_eq!(array_sum(&[10.0, 20.0]), 30.0);
}
}
(* Example 126: Const Generics *)
(* OCaml doesn't have const generics โ we simulate fixed-size arrays with modules *)
(* Approach 1: Functorized fixed-size array *)
module type SIZE = sig
val n : int
end
module FixedArray (S : SIZE) = struct
type 'a t = 'a array
let create default = Array.make S.n default
let length _ = S.n
let get arr i =
if i < 0 || i >= S.n then failwith "index out of bounds"
else arr.(i)
let set arr i v =
if i < 0 || i >= S.n then failwith "index out of bounds"
else arr.(i) <- v
let map f arr = Array.map f arr
let dot a b =
let sum = ref 0.0 in
for i = 0 to S.n - 1 do
sum := !sum +. a.(i) *. b.(i)
done;
!sum
end
module Size3 = struct let n = 3 end
module Vec3 = FixedArray(Size3)
(* Approach 2: Matrix with size encoding *)
module type MATRIX_SIZE = sig
val rows : int
val cols : int
end
module Matrix (S : MATRIX_SIZE) = struct
type t = float array array
let create default =
Array.init S.rows (fun _ -> Array.make S.cols default)
let get m r c = m.(r).(c)
let set m r c v = m.(r).(c) <- v
let rows = S.rows
let cols = S.cols
end
module Mat2x3 = Matrix(struct let rows = 2 let cols = 3 end)
(* Approach 3: Simple tuple-based fixed vectors *)
type vec2 = { x: float; y: float }
type vec3 = { x: float; y: float; z: float }
let vec2_add a b = { x = a.x +. b.x; y = a.y +. b.y }
let vec3_add a b = { x = a.x +. b.x; y = a.y +. b.y; z = a.z +. b.z }
let vec3_dot a b = a.x *. b.x +. a.y *. b.y +. a.z *. b.z
(* Tests *)
let () =
(* Test functorized array *)
let v = Vec3.create 0.0 in
Vec3.set v 0 1.0;
Vec3.set v 1 2.0;
Vec3.set v 2 3.0;
assert (Vec3.length v = 3);
assert (Vec3.get v 1 = 2.0);
let a = [| 1.0; 2.0; 3.0 |] in
let b = [| 4.0; 5.0; 6.0 |] in
assert (Vec3.dot a b = 32.0);
(* Test matrix *)
let m = Mat2x3.create 0.0 in
Mat2x3.set m 0 0 1.0;
Mat2x3.set m 1 2 5.0;
assert (Mat2x3.get m 0 0 = 1.0);
assert (Mat2x3.get m 1 2 = 5.0);
(* Test record-based vectors *)
let v1 = { x = 1.0; y = 2.0; z = 3.0 } in
let v2 = { x = 4.0; y = 5.0; z = 6.0 } in
let sum = vec3_add v1 v2 in
assert (sum.x = 5.0);
assert (vec3_dot v1 v2 = 32.0);
Printf.printf "โ All tests passed\n"