@@ -9,6 +9,27 @@ type ComplexNumber struct {
9
9
A0 , A1 * big.Int
10
10
}
11
11
12
+ // ──────────────────────────────────────────────────────────────────────────────
13
+ // helpers – hex-lattice geometry & symmetric rounding
14
+ // ──────────────────────────────────────────────────────────────────────────────
15
+
16
+ // six axial directions of the hexagonal lattice
17
+ var neighbours = [][2 ]int64 {
18
+ {1 , 0 }, {0 , 1 }, {- 1 , 1 }, {- 1 , 0 }, {0 , - 1 }, {1 , - 1 },
19
+ }
20
+
21
+ // roundNearest returns ⌊(z + d/2) / d⌋ for *any* sign of z, d>0
22
+ func roundNearest (z , d * big.Int ) * big.Int {
23
+ half := new (big.Int ).Rsh (d , 1 ) // d / 2
24
+ if z .Sign () >= 0 {
25
+ return new (big.Int ).Div (new (big.Int ).Add (z , half ), d )
26
+ }
27
+ tmp := new (big.Int ).Neg (z )
28
+ tmp .Add (tmp , half )
29
+ tmp .Div (tmp , d )
30
+ return tmp .Neg (tmp )
31
+ }
32
+
12
33
func (z * ComplexNumber ) init () {
13
34
if z .A0 == nil {
14
35
z .A0 = new (big.Int )
@@ -124,19 +145,55 @@ func (z *ComplexNumber) Norm() *big.Int {
124
145
return norm
125
146
}
126
147
127
- // QuoRem sets z to the quotient of x and y, r to the remainder, and returns z and r.
148
+ // QuoRem sets z to the Euclidean quotient of x / y, r to the remainder,
149
+ // and guarantees ‖r‖ < ‖y‖ (true Euclidean division in ℤ[ω]).
128
150
func (z * ComplexNumber ) QuoRem (x , y , r * ComplexNumber ) (* ComplexNumber , * ComplexNumber ) {
129
- norm := y .Norm ()
130
- if norm .Cmp (big .NewInt (0 )) == 0 {
151
+
152
+ norm := y .Norm () // > 0 (Eisenstein norm is always non-neg)
153
+ if norm .Sign () == 0 {
131
154
panic ("division by zero" )
132
155
}
133
- z .Conjugate (y )
134
- z .Mul (x , z )
135
- z .A0 .Div (z .A0 , norm )
136
- z .A1 .Div (z .A1 , norm )
156
+
157
+ // num = x * ȳ (ȳ computed in a fresh variable → y unchanged)
158
+ var yConj , num ComplexNumber
159
+ yConj .Conjugate (y )
160
+ num .Mul (x , & yConj )
161
+
162
+ // first guess by *symmetric* rounding of both coordinates
163
+ q0 := roundNearest (num .A0 , norm )
164
+ q1 := roundNearest (num .A1 , norm )
165
+ z .A0 , z .A1 = q0 , q1
166
+
167
+ // r = x – q*y
137
168
r .Mul (y , z )
138
169
r .Sub (x , r )
139
170
171
+ // If Euclidean inequality already holds we're done.
172
+ // Otherwise walk ≤2 unit steps in the hex lattice until N(r) < N(y).
173
+ if r .Norm ().Cmp (norm ) >= 0 {
174
+ bestQ0 , bestQ1 := new (big.Int ).Set (z .A0 ), new (big.Int ).Set (z .A1 )
175
+ bestR := new (ComplexNumber ).Set (r )
176
+ bestN2 := bestR .Norm ()
177
+
178
+ for _ , dir := range neighbours {
179
+ candQ0 := new (big.Int ).Add (z .A0 , big .NewInt (dir [0 ]))
180
+ candQ1 := new (big.Int ).Add (z .A1 , big .NewInt (dir [1 ]))
181
+ var candQ ComplexNumber
182
+ candQ .A0 , candQ .A1 = candQ0 , candQ1
183
+
184
+ var candR ComplexNumber
185
+ candR .Mul (y , & candQ )
186
+ candR .Sub (x , & candR )
187
+
188
+ if candR .Norm ().Cmp (bestN2 ) < 0 {
189
+ bestQ0 , bestQ1 = candQ0 , candQ1
190
+ bestR .Set (& candR )
191
+ bestN2 = bestR .Norm ()
192
+ }
193
+ }
194
+ z .A0 , z .A1 = bestQ0 , bestQ1
195
+ r .Set (bestR ) // update remainder and retry; Euclidean property ⇒ ≤ 2 loops
196
+ }
140
197
return z , r
141
198
}
142
199
0 commit comments