namespace reps { TD typename D::Number acos_auto(typename D::Number x) { using N = typename D::Number; if(hyperbolic) { if(x < N(1)) return N(0); return acosh(x); } if(sphere) { if(x > N(1)) return N(0); return acos(x); } throw hr::hr_exception("error"); } /* use the linear representation, as in HyperRogue, but DO NOT apply nm, for comparison */ TD struct rep_linear_nn { using data = D; using point = mvector; using isometry = matrix; using N = typename D::Number; static constexpr isometry id() { matrix result; for(int i=0; i(D::Dim-1); } static point apply(const isometry& T, const point& x) { return T * x; }; static isometry apply(const isometry& T, const isometry& U) { return T * U; }; static typename D::Number dist0(point x) { return acos_auto (x[D::Dim-1]); } static typename D::Number angle(const point& x) { return atan2(x[1], x[0]); } static typename D::Number get_coord(point x, int i) { return x[i]; } static isometry inverse(isometry T) { for(int i=0; i get_column(matrix a, int id) { mvector tmp; for(int i=0; i a, mvector b) { using N = typename D::Number; N res(0); for(int i=0; i& a, int id, mvector v) { for(int i=0; i a) { return sqnorm(get_column(a, D::Dim-1)); } bool fix_matrices; TD matrix apply_nm(matrix a) { using N = typename D::Number; // normalize first auto& lead = a[D::Dim-1][D::Dim-1]; if(nm == nmFlatten) a = a / lead, cbc[cbcDiv]--; if(nm == nmForced || nm == nmWeak) a = a * pow(sqnorm(a), -0.5); if(nm == nmBinary) { while(lead >= 2 && !isinf(lead)) { a = a / 2; } while(lead > 0 && lead < 0.5) { a = a * 2; } } // fixmatrix later if(!fix_matrices) return a; auto divby = (nm == nmBinary || nm == nmWeak || nm == nmCareless || nm == nmFlatten) ? sqnorm(a) : N(1); for(int i=D::Dim-2; i>=0; i--) { auto ro = get_column(a, i); auto last = get_column(a, D::Dim-1); ro = ro + last * inner(ro, last) / divby; for(int j=i+1; j N(0)) ro = ro * (pow(in*in, -.5) * divby); set_column(a, i, ro); } return a; } /* use the linear representation, as in HyperRogue */ TD struct rep_linear { using data = D; using point = mvector; using isometry = matrix; using N = typename D::Number; static constexpr isometry cspin(int i, int j, typename D::Number angle) { return apply_nm( rep_linear_nn::cspin(i, j, angle) ); } static constexpr isometry cspin90(int i, int j) { return rep_linear_nn::cspin90(i, j); } static constexpr isometry lorentz(int i, int j, typename D::Number angle) { return apply_nm( rep_linear_nn::lorentz(i, j, angle) ); } static isometry id() { return rep_linear_nn::id(); }; static constexpr point center() { return unit_vector(D::Dim-1); } static point apply(const isometry& T, const point& x) { return apply_nm(T * x); }; static isometry apply(const isometry& T, const isometry& U) { return apply_nm(T * U); }; static typename D::Number dist0(point x) { return acos_auto (get_normalized(x, x[D::Dim-1])); } static typename D::Number angle(const point& x) { return atan2(x[1], x[0]); } static typename D::Number get_coord(point x, int i) { return get_normalized(x, x[i]); } static isometry inverse(isometry T) { return rep_linear_nn::inverse(T); } static isometry push(const point& p) { return apply_nm( rep_linear_nn::push(get_normalized(p, p)) ); } static std::string print(point p) { return nzv(p); } static std::string print(isometry p) { return nzv(p); } }; /* use the linear representation of points and the multivector representation of isometries */ TD struct rep_mixed { using data = D; using N = typename D::Number; using point = mvector; using isometry = multivector; static isometry cspin(int i, int j, typename data::Number alpha, bool noflat = false) { /* auto u = unit_vector> (0); auto ui = unit_vector (i); auto uj = unit_vector (j); return u * cos(alpha/2) + multimul(embed(ui), embed(uj)) * sin(alpha/2); */ auto res = zero_vector> (); if(nm == nmFlatten && !noflat) { res[0] = N(1); res[(1< j ? 1 : -1); return res; } res[0] = cos(alpha/2); res[(1< j ? 1 : -1); return res; } static isometry cspin90(int i, int j, bool noflat = false) { auto res = zero_vector> (); if(nm == nmFlatten && !noflat) { res[0] = N(1); res[(1< j ? 1 : -1); return res; } res[0] = sqrt(N(.5)); res[(1< j ? 1 : -1); return res; } static isometry lorentz(int i, int j, typename data::Number alpha) { /* // j must be time coordinate auto u = unit_vector> (0); auto ui = unit_vector (i); auto uj = unit_vector (j); return u * cosh(alpha/2) + multimul(embed(uj), embed(ui)) * sinh(alpha/2); */ auto res = zero_vector> (); if(nm == nmFlatten) { res[0] = N(1); res[(1<> (0); }; static constexpr point center() { return unit_vector(D::Dim-1); } static point apply(const isometry& T, const point& x) { // return unembed(multimul(multimul(T, embed(x)), conjugate(T))); return apply_nm(unembed(chkmul,flat_even,underling>(chkmul,flat_underling,odd>(T, embed(x)), conjugate(T)))); }; static isometry apply(const isometry& T, const isometry& U) { auto res = apply_nm, D>(chkmul,flat_even,even>(T, U)); return res; } static isometry inverse(isometry T) { return conjugate(T); } static isometry push(const point& p) { auto pm = get_normalized(p, p); pm[D::Dim-1] = pm[D::Dim-1] + N(1); // since p was normalized, sqnorm of pm is 2 * pm[D::Dim-1] pm = pm * pow(2 * pm[D::Dim-1], -0.5); multivector v1 = embed(pm); multivector v2 = unit_vector>(1<<(D::Dim-1)); multivector v3 = chkmul,underling,poincare>(v1, v2); v3 = apply_nm, D>(v3); return v3; } static typename D::Number dist0(point x) { return acos_auto (get_normalized(x, x[D::Dim-1])); } static typename D::Number angle(const point& x) { return atan2(x[1], x[0]); } static typename D::Number get_coord(point x, int i) { return get_normalized(x, x[i]); } static std::string print(point p) { return nzv(p); } static std::string print(isometry p) { return nz(p); } }; /* use the hyperboloid-Poincare representation of points and the multivector representation of isometries */ TD struct rep_clifford { using data = D; using N = typename D::Number; using point = array< multivector, 1>; using isometry = multivector; static isometry cspin(int i, int j, typename data::Number alpha) { return rep_mixed::cspin(i, j, alpha); } static isometry cspin90(int i, int j) { return rep_mixed::cspin90(i, j); } // j must be the neg coordinate! static isometry lorentz(int i, int j, N alpha) { return rep_mixed::lorentz(i, j, alpha); } static isometry id() { return rep_mixed::id(); } static constexpr point center() { return point{{ id() }}; } static point apply(const isometry& T, const point& x) { return point{{ despin(chkmul,poincare,even>(T, x[0])) }}; } static isometry apply(const isometry& T, const isometry& U) { return apply_nm,D>( chkmul,even,even>(T, U) ); } static isometry inverse(isometry T) { return conjugate(T); } static isometry push(const point& p) { return p[0]; } static typename D::Number dist0(const point& ax) { return acos_auto(get_normalized, D, N>(ax[0], ax[0][0]))*2; } static constexpr int mvlast = 1<<(D::Dim-1); static typename D::Number angle(const point& x) { return atan2(x[0][2 | mvlast], x[0][1 | mvlast]); } static typename D::Number get_coord(const point& x, int i) { auto x1 = multimul(multimul(x[0], unit_vector> (mvlast)), conjugate(x[0])); auto x2 = unembed(x1); return get_normalized(x2, x2[i]); } static std::string print(point p) { return nz(p[0]); } static std::string print(isometry p) { return nz(p); } }; /* split isometries into the poincare and rotational part */ TD struct rep_gyro { using data = D; using N = typename D::Number; using point = multivector; using isometry = poincare_rotation; static isometry cspin(int i, int j, typename data::Number alpha) { return { rep_mixed::id(), rep_mixed::cspin(i, j, alpha, true) }; } static isometry cspin90(int i, int j, typename data::Number alpha) { return { rep_mixed::id(), rep_mixed::cspin90(i, j, alpha, true) }; } static isometry lorentz(int i, int j, typename data::Number alpha) { return {rep_mixed::lorentz(i, j, alpha), rep_mixed::id() }; } static isometry id() { return { rep_mixed::id(), rep_mixed::id() }; } static constexpr point center() { return rep_mixed::id(); } static point apply(const isometry& T, const point& x) { return despin(chkmul,poincare,even>(T.first, chkmul,poincare,poincare>(T.second, x))); } static isometry apply(const isometry& T, const isometry& U) { auto R1 = apply_nm, poincare, poincare> (T.second, U.first); auto R2 = apply_nm, poincare, even> (T.first, R1); auto R3 = despin2(R2); return { R3.first, apply_nm, rotational, rotational> (R3.second, U.second) }; } static isometry inverse(isometry T) { return { conjugate(T.first), conjugate(T.second) }; } static isometry push(const point& p) { return { p, rep_mixed::id() }; } static typename D::Number dist0(const point& ax) { return acos_auto(get_normalized, D, N>(ax, ax[0]))*2; } static constexpr int mvlast = 1<<(D::Dim-1); static typename D::Number angle(const point& x) { return atan2(x[0][2 | mvlast], x[0][1 | mvlast]); } static typename D::Number get_coord(const point& x, int i) { auto x1 = multimul(multimul(x[0], unit_vector> (mvlast)), conjugate(x[0])); auto x2 = unembed(x1); return get_normalized(x2, x2[i]); } static std::string print(point p) { return nz(p[0]); } static std::string print(isometry p) { return "["+nz(p.first)+","+nz(p.second)+"]"; } }; }