1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
//! Kinematics with compile-time dimensional analysis.
//!
//! Demonstrates: free fall, projectile motion, work-energy theorem.
//! Uses expr! for formulas, DiffWrt for typed calculus.
//!
//! Run with: cargo run --example units_kinematics
use symplex::prelude::*;
use symplex::units::constants;
use symplex::units::*;
fn main() {
println!("═══════════════════════════════════════════════════════════════");
println!(" Symplex: Kinematics with Compile-Time Dimensional Analysis");
println!("═══════════════════════════════════════════════════════════════\n");
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Section 1: Free Fall
// x(t) = ½gt², v(t) = dx/dt, a(t) = dv/dt
// Uses expr! for the formula, DiffWrt for typed derivatives.
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
println!("── Free Fall ──");
// Declare raw Ex variables for use inside expr!
let ctx = Context::new();
symplex::syms!(ctx; g, t);
// Typed variables for DiffWrt — the compiler tracks dimensions
let t_var = Time::symbol(&ctx, "t");
// Position: x(t) = ½gt² — built ergonomically with expr!
// from_ex wraps the raw expression in the Length type
let x_t = Length::from_ex(expr!(ctx, 1 / 2 * g * t ^ 2));
println!(" x(t) = {}", x_t);
// Velocity: v(t) = dx/dt — typed differentiation!
// d(Length)/d(Time) → Velocity, verified at compile time
let v_t: Velocity = x_t.diff_wrt(&t_var);
println!(" v(t) = dx/dt = {}", v_t);
// Acceleration: a(t) = dv/dt
// d(Velocity)/d(Time) → Acceleration, verified at compile time
let a_t: Acceleration = v_t.diff_wrt(&t_var);
println!(" a(t) = dv/dt = {}", a_t);
// Verify: acceleration should equal g (constant gravitational field)
println!(" ✓ a(t) = {} (should be g)", a_t.inner());
// Numerical evaluation: x at t=3s with g=9.81 m/s²
let x_num = x_t
.clone()
.subs(&g, &ctx.rational(981, 100))
.subs(&t, &ctx.int(3))
.eval();
println!(" x(t=3, g=9.81) = {} (≈44.145 m)", x_num);
// Also demonstrate eval_f64_with for quick numeric answers
let x_f64 = x_t.eval_f64_with(&[(&g, 10), (&t, 3)]).unwrap();
println!(" x(t=3, g=10) = {:.2} m (f64)", x_f64);
// Using the physical constant for g:
let g_const = constants::standard_gravity(&ctx);
println!(" g₀ = {} (physical constant, exact)", g_const);
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Section 2: Projectile Motion
// x(t) = v₀·cos(θ)·t, y(t) = v₀·sin(θ)·t - ½gt²
// vx = dx/dt, vy = dy/dt
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
println!("\n── Projectile Motion ──");
// Raw variables for expr!
symplex::syms!(ctx; v0, theta);
// g and t already declared above in the same context
// Horizontal position: x(t) = v₀·cos(θ)·t
let x_proj = Length::from_ex(expr!(ctx, v0 * cos(theta) * t));
println!(" x(t) = {}", x_proj);
// Vertical position: y(t) = v₀·sin(θ)·t - ½gt²
let y_proj = Length::from_ex(expr!(ctx, v0 * sin(theta) * t - 1 / 2 * g * t ^ 2));
println!(" y(t) = {}", y_proj);
// Horizontal velocity: vx = dx/dt (use raw diff + from_ex)
let vx = Velocity::from_ex(x_proj.diff(&t));
println!(" vx(t) = dx/dt = {}", vx);
// Vertical velocity: vy = dy/dt
let vy = Velocity::from_ex(y_proj.diff(&t));
println!(" vy(t) = dy/dt = {}", vy);
// Vertical acceleration: ay = dvy/dt — should be -g
let ay = Acceleration::from_ex(vy.diff(&t));
println!(" ay(t) = dvy/dt = {}", ay);
println!(" ✓ vertical acceleration = {} (should be -g)", ay.inner());
// Substitute v0=20 m/s, θ=π/4, g=9.81 m/s² and evaluate at several times
println!("\n Trajectory (v₀=20 m/s, θ=π/4, g=9.81 m/s²):");
let pi_over_4 = &ctx.pi() / 4;
let g_val = ctx.rational(981, 100);
for t_val in [0, 1, 2, 3] {
let x_val = x_proj
.clone()
.subs(&v0, &ctx.int(20))
.subs(&theta, &pi_over_4)
.subs(&g, &g_val)
.subs(&t, &ctx.int(t_val))
.eval();
let y_val = y_proj
.clone()
.subs(&v0, &ctx.int(20))
.subs(&theta, &pi_over_4)
.subs(&g, &g_val)
.subs(&t, &ctx.int(t_val))
.eval();
println!(" t={t_val}s: x = {}, y = {}", x_val, y_val);
}
// Quick f64 check at t=1
let y_f64 = y_proj
.subs(&v0, &ctx.int(20))
.subs(&theta, &pi_over_4)
.subs(&g, &g_val)
.subs(&t, &ctx.int(1))
.eval_f64()
.unwrap();
println!(" y(t=1) ≈ {:.4} m (f64)", y_f64);
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Section 3: Work-Energy Theorem
// F = ma (named arithmetic, compile-time checked)
// W = ∫F dx → Energy
// KE = ½mv² using expr!
// Conceptually: W = ΔKE
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
println!("\n── Work-Energy Theorem ──");
// Named typed variables — compile-time dimension checking
let mass = Mass::symbol(&ctx, "m");
let accel = Acceleration::symbol(&ctx, "a");
// F = ma — Mass × Acceleration → Force (compile-time verified!)
let force = symplex::dim!(ctx, Force: mass * accel);
println!(" F = m·a = {}", force);
// Work: W = ∫F dx → Energy (typed integration!)
let x_var = Length::symbol(&ctx, "x");
let work: Energy = force.integrate_wrt(&x_var);
println!(" W = ∫F dx = {}", work);
// Kinetic energy: KE = ½mv² using expr!
symplex::syms!(ctx; m, v);
let ke = Energy::from_ex(expr!(ctx, 1 / 2 * m * v ^ 2));
println!(" KE = ½mv² = {}", ke);
// Differentiate KE w.r.t. velocity → Momentum (p = mv)
let v_var = Velocity::symbol(&ctx, "v");
let momentum: Momentum = ke.diff_wrt(&v_var);
println!(" dKE/dv = p = {}", momentum);
// Differentiate momentum w.r.t. time → Force (Newton's 2nd law)
let t_typed = Time::symbol(&ctx, "t");
let force_from_p: Force = momentum.diff_wrt(&t_typed);
println!(" dp/dt = F = {}", force_from_p);
// Numerical: m=2kg moving at v=5m/s → KE = 25 J
let ke_num = ke.subs(&m, &ctx.int(2)).subs(&v, &ctx.int(5)).eval();
println!("\n KE(m=2, v=5) = {} (should be 25 J)", ke_num);
// Work = F·d = ma·d. With m=2, a=3, d=10 → W = 60 J
let work_num = work
.subs(&mass, &ctx.int(2))
.subs(&accel, &ctx.int(3))
.subs(&x_var, &ctx.int(10))
.eval();
println!(" W(m=2, a=3, x=10) = {} (should be 60 J)", work_num);
// Conceptual demonstration: W = ΔKE
// If a 2kg object accelerates from 0 to v under force F=ma,
// after distance d: v² = 2·a·d → KE = ½·m·2·a·d = m·a·d = W ✓
println!(" ✓ Work-Energy Theorem: W = ΔKE (dimensions verified!)");
println!("\n✓ All done!");
}