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
// the 720° mystery is one bit of winding
//
// spin-1/2 is taught as quantum weirdness: rotate an electron 360° and its
// state picks up −1, rotate 720° and it returns. the weirdness dissolves once
// the angle is stored instead of projected:
//
// - a spinor turns at HALF the physical rate, so a 2π physical rotation is a
// π spinor rotation — grade 2, the −1 position. 4π physical is 2π spinor —
// blade 4, grade 0, home. the −1 is a place on the winding line
// - observables read grade (blade mod 4), so the physical apparatus returns
// at 2π while the stored angle differs by blade 2 — the SU(2) → SO(3)
// double cover is exactly the bit of winding the projection forgets
// - neutron interferometry (rauch 1975) measured that bit: a 2π-rotated arm
// interferes destructively with an unrotated one. the experiment reads the
// angle the projection drops
// - the half-angle parameter spinors are built on, tan(θ/2), is the t geonum
// stores. the sandwich product RvR† exists to double the half-angle back
// into a rotation; cos_sin's rational formulas ARE that doubling, so
// rotation is one angle addition, no sandwich
//
// run: cargo test --test spinor_test -- --show-output
use geonum::*;
#[test]
fn it_lands_minus_one_at_2pi_because_spin_halves_the_angle() {
// the spinor turns at half rate: physical α → spinor α/2
let physical_2pi = Angle::new(2.0, 1.0); // blade 4
let physical_4pi = Angle::new(4.0, 1.0); // blade 8
let spinor_at_2pi = physical_2pi / 2.0; // π — blade 2
let spinor_at_4pi = physical_4pi / 2.0; // 2π — blade 4
assert_eq!(
spinor_at_2pi.grade(),
2,
"one full physical turn lands the spinor at −1 — grade 2, a position"
);
assert_eq!(
spinor_at_4pi.grade(),
0,
"two full turns bring it home — grade 0"
);
// the physical observable is blind to the difference: a vector rotated 2π
// returns to its base angle, so every measurement of the apparatus reads
// identity while the spinor sits at −1
let apparatus = Geonum::new(1.0, 1.0, 5.0);
let turned = apparatus.rotate(physical_2pi);
assert_eq!(
turned.angle.base_angle(),
apparatus.angle.base_angle(),
"the apparatus returns at 2π — the projection reads identity"
);
assert_eq!(
turned.angle.blade(),
apparatus.angle.blade() + 4,
"while the stored angle carries the turn the projection dropped"
);
}
#[test]
fn it_cancels_the_interferometer_at_2pi_physical_rotation() {
// rauch 1975: split a neutron beam, rotate one arm's spin through 2π with
// a magnetic field, recombine. the beams cancel — the fringe shift proves
// the 4π period. in geonum the rotated arm sits a π spinor rotation away
// and the recombination is one addition
let arm_a = Geonum::new(1.0, 1.0, 8.0); // reference arm
let arm_b_2pi = arm_a.rotate(Angle::new(2.0, 1.0) / 2.0); // 2π physical = π spinor
let arm_b_4pi = arm_a.rotate(Angle::new(4.0, 1.0) / 2.0); // 4π physical = 2π spinor
assert!(
(arm_a + arm_b_2pi).near_mag(0.0),
"2π rotation: the arms interfere destructively — the measured minimum"
);
assert!(
(arm_a + arm_b_4pi).near_mag(2.0),
"4π rotation: full constructive recovery — the measured period"
);
}
#[test]
fn it_stores_the_spinor_half_angle_as_t() {
// the spinor parametrization of a rotation α is built on tan(α/2) — the
// cayley parameter. geonum stores exactly that ratio as t, so the spinor's
// coordinate is the struct's native field, not a change of variables
for (p, d) in [(1.0, 5.0), (1.0, 7.0), (2.0, 5.0), (3.0, 7.0)] {
let alpha = Angle::new(p, d); // rotations within the first quadrant
let half_tangent = (alpha.grade_angle() / 2.0).tan();
assert!(
(alpha.t() - half_tangent).abs() < 1e-15,
"t IS tan(α/2) — the spinor coordinate, stored"
);
// the sandwich RvR† exists to double the half-angle back into the
// rotation. cos_sin's rational formulas are that doubling — degree 2
// in t — so the full rotation reads out with no sandwich
let t = alpha.t();
let (cos_a, sin_a) = alpha.cos_sin();
assert!(
(cos_a - (1.0 - t * t) / (1.0 + t * t)).abs() < 1e-15,
"cos α = (1−t²)/(1+t²) — the sandwich's double angle, rational in t"
);
assert!(
(sin_a - 2.0 * t / (1.0 + t * t)).abs() < 1e-15,
"sin α = 2t/(1+t²) — same doubling, same readout"
);
}
}
#[test]
fn it_double_covers_by_collapsing_pi_into_a_turn() {
// SU(2) → SO(3) is 2-to-1: a spinor s and its negative s + π drive the
// same physical rotation. doubling maps the π gap to a 2π gap — a full
// turn, invisible to grade — so two distinct spinors, one rotation
let s = Angle::new(1.0, 5.0);
let minus_s = s + Angle::new(1.0, 1.0); // the ± partner, π away
assert_ne!(s, minus_s, "two distinct spinor states");
assert_eq!(
(s * 2.0).base_angle(),
(minus_s * 2.0).base_angle(),
"doubled, they land the same physical rotation — the cover is 2-to-1"
);
assert_eq!(
(minus_s * 2.0).blade() - (s * 2.0).blade(),
4,
"the two preimages differ by exactly one full turn of winding"
);
}
#[test]
fn it_untangles_two_twists_but_not_one() {
// the belt trick: a 2π twist in a belt cannot be undone without rotating
// the ends; a 4π twist can. π₁(SO(3)) = ℤ/2 read as blade parity: each
// full physical twist is a π spinor rotation — blade 2 — and the
// obstruction class is the grade the accumulated blades land on
let twist = Angle::new(2.0, 1.0) / 2.0; // one full twist = π spinor
let mut belt = Angle::new(0.0, 1.0);
let expected_class = [2usize, 0, 2, 0]; // odd twists obstructed, even free
for (n, expected) in expected_class.iter().enumerate() {
belt = belt + twist;
assert_eq!(
belt.grade(),
*expected,
"{} twist(s): class {} — parity is the only invariant",
n + 1,
expected
);
}
}