quaternion_hurwitz.hpp
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <utility> | ||
| 4 | #include <array> | ||
| 5 | #include <tuple> | ||
| 6 | #include <iostream> | ||
| 7 | |||
| 8 | template<typename num = int> | ||
| 9 | struct hurwitz_quaternion { | ||
| 10 | // we store the doubled quaternion | ||
| 11 | num s,x,y,z; | ||
| 12 | ✗ | hurwitz_quaternion() : s(0), x(0), y(0), z(0) {} | |
| 13 | ✗ | hurwitz_quaternion(num v) : s(2*v), x(0), y(0), z(0) {} | |
| 14 | ✗ | hurwitz_quaternion(num s_, num x_, num y_, num z_) : s(2*s_), x(2*x_), y(2*y_), z(2*z_) {} | |
| 15 | struct doubled_coords_tag {}; | ||
| 16 | ✗ | hurwitz_quaternion(doubled_coords_tag, num s_, num x_, num y_, num z_) : s(s_), x(x_), y(y_), z(z_) { | |
| 17 | ✗ | assert((s & 1) == (x & 1) && (s & 1) == (y & 1) && (s & 1) == (z & 1)); | |
| 18 | } | ||
| 19 | ✗ | friend std::ostream& operator << (std::ostream& o, const hurwitz_quaternion& q) { | |
| 20 | ✗ | o << double(q.s)/2; | |
| 21 | { | ||
| 22 | ✗ | std::ios_base::fmtflags f(o.flags()); | |
| 23 | ✗ | o << std::showpos << double(q.x)/2 << "i" << double(q.y)/2 << "j" << double(q.z)/2 << "k"; | |
| 24 | ✗ | o.flags(f); | |
| 25 | } | ||
| 26 | ✗ | return o; | |
| 27 | } | ||
| 28 | |||
| 29 | ✗ | explicit operator bool() const { | |
| 30 | ✗ | return s || x || y || z; | |
| 31 | } | ||
| 32 | |||
| 33 | ✗ | friend bool operator == (const hurwitz_quaternion& a, const hurwitz_quaternion& b) { | |
| 34 | ✗ | return std::tie(a.s,a.x,a.y,a.z) == std::tie(b.s,b.x,b.y,b.z); | |
| 35 | } | ||
| 36 | ✗ | friend bool operator != (const hurwitz_quaternion& a, const hurwitz_quaternion& b) { return !(a == b); } | |
| 37 | |||
| 38 | ✗ | num real_doubled() const { | |
| 39 | ✗ | return s; | |
| 40 | } | ||
| 41 | ✗ | num real() const { | |
| 42 | ✗ | assert(!(s & 1)); | |
| 43 | ✗ | return s >> 1; | |
| 44 | } | ||
| 45 | ✗ | std::array<num, 3> imag_doubled() const { | |
| 46 | ✗ | return {x, y, z}; | |
| 47 | } | ||
| 48 | ✗ | std::array<num, 3> imag() const { | |
| 49 | ✗ | assert(!(s & 1)); | |
| 50 | ✗ | return {x>>1, y>>1, z>>1}; | |
| 51 | } | ||
| 52 | ✗ | std::array<num, 4> coords_doubled() const { | |
| 53 | ✗ | return {s, x, y, z}; | |
| 54 | } | ||
| 55 | ✗ | std::array<num, 4> coords() const { | |
| 56 | ✗ | assert(!(s & 1)); | |
| 57 | ✗ | return {s>>1, x>>1, y>>1, z>>1}; | |
| 58 | } | ||
| 59 | |||
| 60 | ✗ | friend num norm(const hurwitz_quaternion& q) { | |
| 61 | ✗ | return (q.s * q.s + q.x * q.x + q.y * q.y + q.z * q.z) >> 2; | |
| 62 | } | ||
| 63 | ✗ | friend hurwitz_quaternion conj(const hurwitz_quaternion& q) { | |
| 64 | ✗ | return hurwitz_quaternion(doubled_coords_tag{}, q.s, -q.x, -q.y, -q.z); | |
| 65 | } | ||
| 66 | |||
| 67 | ✗ | friend hurwitz_quaternion operator + (const hurwitz_quaternion& q) { | |
| 68 | ✗ | return hurwitz_quaternion(doubled_coords_tag{}, +q.s, +q.x, +q.y, +q.z); | |
| 69 | } | ||
| 70 | ✗ | friend hurwitz_quaternion operator - (const hurwitz_quaternion& q) { | |
| 71 | ✗ | return hurwitz_quaternion(doubled_coords_tag{}, -q.s, -q.x, -q.y, -q.z); | |
| 72 | } | ||
| 73 | |||
| 74 | ✗ | hurwitz_quaternion& operator += (const hurwitz_quaternion& o) { | |
| 75 | ✗ | s += o.s; | |
| 76 | ✗ | x += o.x; | |
| 77 | ✗ | y += o.y; | |
| 78 | ✗ | z += o.z; | |
| 79 | ✗ | return *this; | |
| 80 | } | ||
| 81 | ✗ | friend hurwitz_quaternion operator + (const hurwitz_quaternion& a, const hurwitz_quaternion& b) { | |
| 82 | ✗ | return hurwitz_quaternion(doubled_coords_tag{}, a.s + b.s, a.x + b.x, a.y + b.y, a.z + b.z); | |
| 83 | } | ||
| 84 | ✗ | hurwitz_quaternion& operator -= (const hurwitz_quaternion& o) { | |
| 85 | ✗ | s -= o.s; | |
| 86 | ✗ | x -= o.x; | |
| 87 | ✗ | y -= o.y; | |
| 88 | ✗ | z -= o.z; | |
| 89 | ✗ | return *this; | |
| 90 | } | ||
| 91 | ✗ | friend hurwitz_quaternion operator - (const hurwitz_quaternion& a, const hurwitz_quaternion& b) { | |
| 92 | ✗ | return hurwitz_quaternion(doubled_coords_tag{}, a.s - b.s, a.x - b.x, a.y - b.y, a.z - b.z); | |
| 93 | } | ||
| 94 | |||
| 95 | ✗ | friend hurwitz_quaternion operator * (const num& a, const hurwitz_quaternion& q) { | |
| 96 | ✗ | return hurwitz_quaternion(doubled_coords_tag{}, a*q.s, a*q.x, a*q.y, a*q.z); | |
| 97 | } | ||
| 98 | ✗ | friend hurwitz_quaternion operator * (const hurwitz_quaternion& q, const num& a) { | |
| 99 | ✗ | return hurwitz_quaternion(doubled_coords_tag{}, q.s*a, q.x*a, q.y*a, q.z*a); | |
| 100 | } | ||
| 101 | ✗ | hurwitz_quaternion& operator *= (const num& a) { | |
| 102 | ✗ | s *= a; | |
| 103 | ✗ | x *= a; | |
| 104 | ✗ | y *= a; | |
| 105 | ✗ | z *= a; | |
| 106 | ✗ | return *this; | |
| 107 | } | ||
| 108 | |||
| 109 | ✗ | friend hurwitz_quaternion operator * (const hurwitz_quaternion& a, const hurwitz_quaternion& b) { | |
| 110 | ✗ | return hurwitz_quaternion( | |
| 111 | doubled_coords_tag{}, | ||
| 112 | (a.s * b.s - a.x * b.x - a.y * b.y - a.z * b.z) >> 1, | ||
| 113 | (a.s * b.x + a.x * b.s + a.y * b.z - a.z * b.y) >> 1, | ||
| 114 | (a.s * b.y + a.y * b.s + a.z * b.x - a.x * b.z) >> 1, | ||
| 115 | (a.s * b.z + a.z * b.s + a.x * b.y - a.y * b.x) >> 1 | ||
| 116 | ); | ||
| 117 | } | ||
| 118 | ✗ | hurwitz_quaternion& operator *= (const hurwitz_quaternion& o) { | |
| 119 | ✗ | return *this = *this * o; | |
| 120 | } | ||
| 121 | |||
| 122 | struct div_t { | ||
| 123 | hurwitz_quaternion quot, rem; | ||
| 124 | }; | ||
| 125 | // a = b * quot + rem | ||
| 126 | ✗ | friend div_t right_div(const hurwitz_quaternion& a, const hurwitz_quaternion& b) { | |
| 127 | ✗ | hurwitz_quaternion numer = conj(b) * a; | |
| 128 | ✗ | num denom = norm(b); | |
| 129 | |||
| 130 | ✗ | auto floor_div = [](num u, num v) -> num { | |
| 131 | ✗ | if ((u^v) >= 0) { | |
| 132 | ✗ | return u/v; | |
| 133 | } else { | ||
| 134 | ✗ | auto res = std::div(u, v); | |
| 135 | ✗ | return res.quot - bool(res.rem); | |
| 136 | } | ||
| 137 | }; | ||
| 138 | ✗ | num s = floor_div(numer.s, denom); | |
| 139 | ✗ | num x = floor_div(numer.x, denom); | |
| 140 | ✗ | num y = floor_div(numer.y, denom); | |
| 141 | ✗ | num z = floor_div(numer.z, denom); | |
| 142 | |||
| 143 | ✗ | hurwitz_quaternion q_odd(doubled_coords_tag{}, s | 1, x | 1, y | 1, z | 1); | |
| 144 | ✗ | hurwitz_quaternion r_odd = a - b * q_odd; | |
| 145 | ✗ | hurwitz_quaternion q_even(doubled_coords_tag{}, (s+1)&~num(1), (x+1)&~num(1), (y+1)&~num(1), (z+1)&~num(1)); | |
| 146 | ✗ | hurwitz_quaternion r_even = a - b * q_even; | |
| 147 | ✗ | div_t res = norm(r_odd) < norm(r_even) ? div_t{q_odd, r_odd} : div_t{q_even, r_even}; | |
| 148 | ✗ | assert(norm(res.rem) < norm(b)); | |
| 149 | ✗ | return res; | |
| 150 | } | ||
| 151 | |||
| 152 | // a = ga', b = gb' | ||
| 153 | ✗ | friend hurwitz_quaternion right_gcd(hurwitz_quaternion a, hurwitz_quaternion b) { | |
| 154 | ✗ | while (a) { | |
| 155 | ✗ | b = right_div(b, a).rem; | |
| 156 | ✗ | std::swap(a, b); | |
| 157 | } | ||
| 158 | ✗ | return b; | |
| 159 | } | ||
| 160 | }; | ||
| 161 |