@@ -9,6 +9,27 @@ type ComplexNumber struct {
99 A0 , A1 * big.Int
1010}
1111
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+
1233func (z * ComplexNumber ) init () {
1334 if z .A0 == nil {
1435 z .A0 = new (big.Int )
@@ -124,19 +145,55 @@ func (z *ComplexNumber) Norm() *big.Int {
124145 return norm
125146}
126147
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 ℤ[ω]).
128150func (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 {
131154 panic ("division by zero" )
132155 }
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
137168 r .Mul (y , z )
138169 r .Sub (x , r )
139170
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+ }
140197 return z , r
141198}
142199
0 commit comments