pub fn generate(
a: f32,
b: f32,
c: f32,
d: f32,
initial: (f32, f32),
n: usize,
) -> Vec<(f32, f32)> {
let mut path = Vec::with_capacity(n);
let (mut x, mut y) = initial;
for _ in 0..n {
path.push((x, y));
let x_next = (a * y).sin() + c * (a * x).cos();
let y_next = (b * x).sin() + d * (b * y).cos();
x = x_next;
y = y_next;
}
path
}
pub fn clifford_x(a: f32, b: f32, c: f32, d: f32, initial: (f32, f32), n: usize) -> Vec<f32> {
generate(a, b, c, d, initial, n)
.into_iter()
.map(|(x, _)| x)
.collect()
}
pub fn clifford_y(a: f32, b: f32, c: f32, d: f32, initial: (f32, f32), n: usize) -> Vec<f32> {
generate(a, b, c, d, initial, n)
.into_iter()
.map(|(_, y)| y)
.collect()
}
pub fn clifford_flow(n: usize) -> Vec<(f32, f32)> {
let total_steps = n + 10;
let full_path = generate(-1.4, 1.6, 1.0, 0.7, (0.0, 0.0), total_steps);
full_path.into_iter().skip(10).collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_clifford_attractor_length() {
let path = generate(-1.4, 1.6, 1.0, 0.7, (0.0, 0.0), 100);
assert_eq!(path.len(), 100);
}
#[test]
fn test_clifford_flow() {
let path = clifford_flow(50);
assert_eq!(path.len(), 50);
}
#[test]
fn test_clifford_first_point_is_initial() {
let path = generate(-1.4, 1.6, 1.0, 0.7, (0.5, 0.5), 10);
let (x0, y0) = path[0];
assert_eq!(x0, 0.5);
assert_eq!(y0, 0.5);
}
#[test]
fn test_clifford_stays_bounded() {
let path = generate(-1.4, 1.6, 1.0, 0.7, (0.0, 0.0), 1000);
for (x, y) in path {
assert!(
x.abs() < 3.0,
"X should stay bounded, got {}",
x
);
assert!(
y.abs() < 3.0,
"Y should stay bounded, got {}",
y
);
}
}
#[test]
fn test_clifford_deterministic() {
let path1 = generate(-1.4, 1.6, 1.0, 0.7, (0.0, 0.0), 100);
let path2 = generate(-1.4, 1.6, 1.0, 0.7, (0.0, 0.0), 100);
for i in 0..100 {
let (x1, y1) = path1[i];
let (x2, y2) = path2[i];
assert!((x1 - x2).abs() < 1e-6);
assert!((y1 - y2).abs() < 1e-6);
}
}
#[test]
fn test_clifford_different_initial_conditions() {
let path1 = generate(-1.4, 1.6, 1.0, 0.7, (0.0, 0.0), 50);
let path2 = generate(-1.4, 1.6, 1.0, 0.7, (0.5, 0.5), 50);
let (x1, y1) = path1[0];
let (x2, y2) = path2[0];
assert!((x1 - x2).abs() > 0.1 || (y1 - y2).abs() > 0.1);
}
#[test]
fn test_clifford_evolution() {
let path = generate(-1.4, 1.6, 1.0, 0.7, (0.0, 0.0), 5);
let (x1, y1) = path[1];
assert!((x1 - 1.0).abs() < 0.001, "x1 should be 1.0, got {}", x1);
assert!((y1 - 0.7).abs() < 0.001, "y1 should be 0.7, got {}", y1);
}
#[test]
fn test_clifford_different_parameters() {
let path1 = generate(-1.4, 1.6, 1.0, 0.7, (0.0, 0.0), 100);
let path2 = generate(1.5, -1.8, 1.6, 0.9, (0.0, 0.0), 100);
let (x1, y1) = path1[50];
let (x2, y2) = path2[50];
let dist = ((x2 - x1).powi(2) + (y2 - y1).powi(2)).sqrt();
assert!(dist > 0.1, "Different parameters should produce different trajectories");
}
#[test]
fn test_clifford_x_convenience() {
let x_only = clifford_x(-1.4, 1.6, 1.0, 0.7, (0.0, 0.0), 32);
let full_path = generate(-1.4, 1.6, 1.0, 0.7, (0.0, 0.0), 32);
let x_full: Vec<f32> = full_path.into_iter().map(|(x, _)| x).collect();
assert_eq!(x_only, x_full);
}
#[test]
fn test_clifford_y_convenience() {
let y_only = clifford_y(-1.4, 1.6, 1.0, 0.7, (0.0, 0.0), 32);
let full_path = generate(-1.4, 1.6, 1.0, 0.7, (0.0, 0.0), 32);
let y_full: Vec<f32> = full_path.into_iter().map(|(_, y)| y).collect();
assert_eq!(y_only, y_full);
}
#[test]
fn test_clifford_single_iteration() {
let path = generate(-1.4, 1.6, 1.0, 0.7, (1.0, 1.0), 1);
assert_eq!(path.len(), 1);
let (x0, y0) = path[0];
assert_eq!(x0, 1.0);
assert_eq!(y0, 1.0);
}
#[test]
fn test_clifford_coordinates_evolve() {
let path = generate(-1.4, 1.6, 1.0, 0.7, (0.0, 0.0), 200);
let (x0, y0) = path[0];
let mut x_changed = false;
let mut y_changed = false;
for i in 1..200 {
let (x, y) = path[i];
if (x - x0).abs() > 0.1 {
x_changed = true;
}
if (y - y0).abs() > 0.1 {
y_changed = true;
}
}
assert!(x_changed, "X coordinate should evolve over time");
assert!(y_changed, "Y coordinate should evolve over time");
}
#[test]
fn test_clifford_produces_finite_values() {
let path = generate(-1.4, 1.6, 1.0, 0.7, (0.0, 0.0), 1000);
for (x, y) in path {
assert!(x.is_finite(), "X should be finite");
assert!(y.is_finite(), "Y should be finite");
}
}
#[test]
fn test_clifford_symmetric_parameters() {
let path = generate(1.7, 1.7, 0.6, 1.2, (0.0, 0.0), 100);
for (x, y) in path {
assert!(x.is_finite() && y.is_finite());
assert!(x.abs() < 3.0 && y.abs() < 3.0);
}
}
}
pub fn classic() -> Vec<(f32, f32)> {
clifford_flow(500)
}
pub fn variant1() -> Vec<(f32, f32)> {
let full = generate(1.5, -1.8, 1.6, 0.9, (0.0, 0.0), 510);
full.into_iter().skip(10).collect()
}
pub fn variant2() -> Vec<(f32, f32)> {
let full = generate(-1.7, 1.8, -0.9, -0.4, (0.0, 0.0), 510);
full.into_iter().skip(10).collect()
}
pub fn extended() -> Vec<(f32, f32)> {
clifford_flow(1000)
}