/** representation based on the halfplane model; assumes Dim=3 */ namespace reps { template struct sl2 : public array { sl2(F a, F b, F c, F d) { self[0] = a; self[1] = b; self[2] = c; self[3] = d; } sl2 operator * (const sl2& sec) const { return sl2( self[0] * sec[0] + self[1] * sec[2], self[0] * sec[1] + self[1] * sec[3], self[2] * sec[0] + self[3] * sec[2], self[2] * sec[1] + self[3] * sec[3] ); } std::string print() { return hr::lalign(0, "[", self[0], ",", self[1], ";", self[2], ",", self[3], "]"); } }; TD sl2 split_quaternion_to_sl2(const multivector& h) { auto h3 = h[0], h2 = h[1 | 2], h1 = h[1 | 4], h0 = h[2 | 4]; return sl2(h3 - h1, h2 + h0, -h2 + h0, h3 + h1); } TD multivector sl2_to_split_quaternion(const sl2& e) { auto h0 = (e[1] + e[2]) / 2; auto h3 = (e[0] + e[3]) / 2; auto h1 = (e[3] - e[0]) / 2; auto h2 = (e[1] - e[2]) / 2; auto res = zero_vector>(); res[0] = h3; res[1 | 2] = h2; res[1 | 4] = h1; res[2 | 4] = h0; return res; } template using sl2c = sl2>; TD sl2c split_biquaternion_to_sl2c(const multivector& h) { using cn = std::complex; return sl2(cn(h[0]-h[9], h[15]-h[6]), cn(h[3]+h[10], -h[5]-h[12]), cn(h[10]-h[3], h[12]-h[5]), cn(h[0]+h[9], h[6]+h[15])); } TD multivector sl2c_to_split_biquaternion(const sl2c& e) { auto res = zero_vector>(); res[0] = +(real(e[0]) + real(e[3])) / 2; res[3] = +(real(e[1]) - real(e[2])) / 2; res[5] = -(imag(e[1]) + imag(e[2])) / 2; res[6] = +(imag(e[3]) - imag(e[0])) / 2; res[9] = +(real(e[3]) - real(e[0])) / 2; res[10] = +(real(e[1]) + real(e[2])) / 2; res[12] = +(imag(e[2]) - imag(e[1])) / 2; res[15] = +(imag(e[0]) + imag(e[3])) / 2; return res; } TD struct rep_halfplane { using data = D; using N = typename D::Number; using point = std::complex; using isometry = sl2; static isometry cspin(int i, int j, N alpha) { // return split_quaternion_to_sl2( rep_clifford::cspin(i, j, alpha) ); if(i>j) std::swap(i, j), alpha = -alpha; alpha /= 2; auto ca = cos(alpha), sa = sin(alpha); return isometry(ca, -sa, sa, ca); } static isometry cspin90(int i, int j, N alpha) { // return split_quaternion_to_sl2( rep_clifford::cspin(i, j, alpha) ); auto ca = sqrt(N(2)), sa = sqrt(N(2)); if(i>j) std::swap(i, j), sa = -sa; return isometry(ca, -sa, sa, ca); } static isometry lorentz(int i, int j, N alpha) { // return split_quaternion_to_sl2( rep_clifford::lorentz(i, j, alpha) ); if(i>j) std::swap(i, j); alpha /= 2; if(i == 0) return isometry(exp(-alpha), N(0), N(0), exp(alpha)); if(i == 1) { auto ca = cosh(alpha), sa = sinh(alpha); return isometry(ca, sa, sa, ca); } throw hr::hr_exception("bad lorentz"); } static isometry id() { return isometry(N(1),N(0),N(0),N(1)); }; static point center() { return point(N(0), N(1)); }; static point apply(const isometry& T, const point& x) { return (T[0] * x + T[1] * 1) / (T[2] * x + T[3] * 1); }; static isometry apply(const isometry& T, const isometry& U) { return T * U; }; static typename rep_clifford::point to_poincare(const point& x) { auto a = real(x), b = imag(x); auto tmp = isometry(sqrt(b), a/sqrt(b), N(0), N(1)/sqrt(b)); auto sq = sl2_to_split_quaternion(tmp); // sq[0] = (sqrt(b) + 1/sqrt(b)) / 2;; sq[1 | 2] = a/sqrt(b)/2; sq[1 | 4] = (1/sqrt(b) - sqrt(b)) / 2; sq[2 | 4] = a/sqrt(b)/2; sq = despin(sq); return typename rep_clifford::point({{sq}}); } static isometry inverse(isometry T) { return isometry(T[3], -T[1], -T[2], T[0]); } static isometry push(const point& p) { return split_quaternion_to_sl2(to_poincare(p)[0]); } static N dist0(const point& x) { return rep_clifford::dist0(to_poincare(x)); } static N angle(const point& x) { return rep_clifford::angle(to_poincare(x)); } static N get_coord(const point& x, int i) { return rep_clifford::get_coord(to_poincare(x), i); } // imag may be very small and still important, so do not use the default complex print static std::string print(const point& x) { return hr::lalign(0, "{real:", real(x), " imag:", imag(x), "}"); } static std::string print(const isometry& x) { return x.print(); } }; TD struct rep_halfspace { using data = D; using N = typename D::Number; struct point { std::complex xy; N z; }; using isometry = sl2c; static isometry cspin(int i, int j, N alpha) { return split_biquaternion_to_sl2c( rep_clifford::cspin(i, j, alpha) ); } static isometry cspin90(int i, int j) { return split_biquaternion_to_sl2c( rep_clifford::cspin90(i, j) ); } static isometry lorentz(int i, int j, N alpha) { return split_biquaternion_to_sl2c( rep_clifford::lorentz(i, j, alpha) ); } static isometry id() { return isometry(N(1),N(0),N(0),N(1)); } static point center() { return point{ .xy = N(0), .z = N(1) }; } static point apply(const isometry& T, const point& x) { auto nom = T[0] * x.xy + T[1] * N(1); auto nomz= T[0] * x.z; auto den = T[2] * x.xy + T[3] * N(1); auto denz= T[2] * x.z; // D = den + denz * j auto dnorm = std::norm(den) + std::norm(denz); using std::conj; // conj(D) = conj(den) - denz * j // N / D = (nom + nomz * j) / (den + denz * j) = // = (nom + nomz * j) * (conj(den) - denz * j) / dnorm // auto rxy = (nom * conj(den) - nomz * j * denz * j); // auto rz*j = (-nom * denz * j + nomz * j * conj(den)) // apply the formula: j * a = conj(a) * j auto rxy = (nom * conj(den) + nomz * conj(denz)); auto rz = (nomz * den - nom * denz); // todo only real part // println(hlog, "imag of rz = ", imag(rz)); return point { .xy = rxy / dnorm, .z = real(rz) / dnorm }; }; static isometry apply(const isometry& T, const isometry& U) { return T * U; }; static typename rep_clifford::point to_poincare(const point& x) { auto tmp = isometry(sqrt(x.z), x.xy/sqrt(x.z), N(0), N(1)/sqrt(x.z)); auto sq = sl2c_to_split_biquaternion(tmp); sq = despin(sq); return typename rep_clifford::point({{sq}}); } static isometry inverse(isometry T) { return isometry(T[3], -T[1], -T[2], T[0]); } static isometry push(const point& p) { return split_biquaternion_to_sl2c(to_poincare(p)[0]); } static N dist0(const point& x) { return rep_clifford::dist0(to_poincare(x)); } static N angle(const point& x) { return rep_clifford::angle(to_poincare(x)); } static N get_coord(const point& x, int i) { return rep_clifford::get_coord(to_poincare(x), i); } // imag may be very small and still important, so do not use the default complex print static std::string print(const point& x) { return hr::lalign(0, "{x:", real(x.xy), " y:", imag(x.xy), " z:", x.z, "}"); } static std::string print(const isometry& x) { return x.print(); } }; template using rep_half = typename std::conditional, rep_halfspace>::type; }