mirror of
				https://github.com/zenorogue/hyperrogue.git
				synced 2025-10-30 13:32:59 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			356 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			356 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| 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<data>;
 | |
|   using isometry = matrix<data>;
 | |
|   using N = typename D::Number;
 | |
| 
 | |
|   static constexpr isometry id() { 
 | |
|     matrix<D> result;
 | |
|     for(int i=0; i<D::Dim; i++)
 | |
|     for(int j=0; j<D::Dim; j++)
 | |
|       result[i][j] = N(i == j);
 | |
|     return result;
 | |
|     };
 | |
| 
 | |
|   static constexpr isometry cspin(int i, int j, typename D::Number angle) {
 | |
|     auto res = id();
 | |
|     auto ca = cos(angle), sa = sin(angle);
 | |
|     res[i][i] = ca;
 | |
|     res[j][j] = ca;
 | |
|     res[i][j] = sa;
 | |
|     res[j][i] = -sa;
 | |
|     return res;
 | |
|     };
 | |
| 
 | |
|   static constexpr isometry cspin90(int i, int j) {
 | |
|     auto res = id();
 | |
|     res[i][i] = 0;
 | |
|     res[j][j] = 0;
 | |
|     res[i][j] = 1;
 | |
|     res[j][i] = -1;
 | |
|     return res;
 | |
|     };
 | |
| 
 | |
|   static constexpr isometry lorentz(int i, int j, typename D::Number angle) {
 | |
|     auto res = id();
 | |
|     auto ca = cosh(angle), sa = sinh(angle);
 | |
|     res[i][i] = ca;
 | |
|     res[j][j] = ca;
 | |
|     res[i][j] = sa;
 | |
|     res[j][i] = sa;
 | |
|     return res;
 | |
|     }
 | |
| 
 | |
|   static constexpr point center() { return unit_vector<data>(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<D> (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<D::Dim; i++)
 | |
|     for(int j=0; j<i; j++) std::swap(T[i][j], T[j][i]);
 | |
|     if constexpr(D::Flipped != -1) {
 | |
|       for(int i=0; i<D::Dim-1; i++) T[i][D::Dim-1] = -T[i][D::Dim-1];
 | |
|       for(int i=0; i<D::Dim-1; i++) T[D::Dim-1][i] = -T[D::Dim-1][i];
 | |
|       }
 | |
|     return T;
 | |
|     }
 | |
|   static isometry push(const point& p) {
 | |
|     auto res = id();
 | |
|     // to do: for spherical!
 | |
|     N fac = N(1)/(p[D::Dim-1]+N(1));
 | |
|     for(int i=0; i<D::Dim-1; i++)
 | |
|     for(int j=0; j<D::Dim-1; j++)
 | |
|       res[i][j] += p[i] * p[j] * fac;
 | |
|       
 | |
|     for(int d=0; d<D::Dim-1; d++)
 | |
|       res[d][D::Dim-1] = p[d],
 | |
|       res[D::Dim-1][d] = p[d];
 | |
|     res[D::Dim-1][D::Dim-1] = p[D::Dim-1];
 | |
|     
 | |
|     return res;
 | |
|     }
 | |
| 
 | |
|   static std::string print(point p) { return nzv(p); }
 | |
|   static std::string print(isometry p) { return nzv(p); }
 | |
|   };
 | |
| 
 | |
| TD mvector<D> get_column(matrix<D> a, int id) {
 | |
|   mvector<D> tmp;
 | |
|   for(int i=0; i<D::Dim; i++) tmp[i] = a[i][id];
 | |
|   return tmp;
 | |
|   }
 | |
| 
 | |
| TD typename D::Number inner(mvector<D> a, mvector<D> b) {
 | |
|   using N = typename D::Number;
 | |
|   N res(0);
 | |
|   for(int i=0; i<D::Dim; i++) res += a[i] * b[i] * (i==D::Flipped?-1:1);
 | |
|   if(isnan(res) || isinf(res)) return N(0);
 | |
|   return res;
 | |
|   }
 | |
| 
 | |
| TD void set_column(matrix<D>& a, int id, mvector<D> v) {
 | |
|   for(int i=0; i<D::Dim; i++) a[i][id] = v[i];
 | |
|   }
 | |
| 
 | |
| TD typename D::Number sqnorm(matrix<D> a) { return sqnorm<D>(get_column<D>(a, D::Dim-1)); }
 | |
| 
 | |
| bool fix_matrices;
 | |
| 
 | |
| TD matrix<D> apply_nm(matrix<D> 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<D>(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<D>(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<D::Dim-1; j++) {
 | |
|       auto next = get_column(a, j);
 | |
|       ro = ro - next * inner(ro, next) / divby;
 | |
|       }
 | |
|     auto in = inner(ro, ro);
 | |
|     if(in > 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<data>;
 | |
|   using isometry = matrix<data>;
 | |
|   using N = typename D::Number;
 | |
| 
 | |
|   static constexpr isometry cspin(int i, int j, typename D::Number angle) {
 | |
|     return apply_nm<D>( rep_linear_nn<D>::cspin(i, j, angle) );
 | |
|     }
 | |
|   static constexpr isometry cspin90(int i, int j) {
 | |
|     return rep_linear_nn<D>::cspin90(i, j);
 | |
|     }
 | |
|   static constexpr isometry lorentz(int i, int j, typename D::Number angle) {
 | |
|     return apply_nm<D>( rep_linear_nn<D>::lorentz(i, j, angle) );
 | |
|     }
 | |
|   static isometry id() { return rep_linear_nn<D>::id(); };
 | |
|   static constexpr point center() { return unit_vector<data>(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<D> (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<D>::inverse(T);
 | |
|     }
 | |
|   static isometry push(const point& p) {
 | |
|     return apply_nm( rep_linear_nn<D>::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<data>;
 | |
|   using isometry = multivector<data>;
 | |
| 
 | |
|   static isometry cspin(int i, int j, typename data::Number alpha, bool noflat = false) {
 | |
| /*    auto u = unit_vector<multivector_data<data>> (0);
 | |
|     auto ui = unit_vector<data> (i);
 | |
|     auto uj = unit_vector<data> (j);
 | |
|     return u * cos(alpha/2) + multimul(embed(ui), embed(uj)) * sin(alpha/2); */
 | |
|     auto res = zero_vector<multivector_data<data>> ();
 | |
|     if(nm == nmFlatten && !noflat) {
 | |
|       res[0] = N(1);
 | |
|       res[(1<<i) | (1<<j)] = tan(alpha/2) * (i > j ? 1 : -1); 
 | |
|       return res;
 | |
|       }
 | |
|     res[0] = cos(alpha/2);
 | |
|     res[(1<<i) | (1<<j)] = sin(alpha/2) * (i > j ? 1 : -1); 
 | |
|     return res;
 | |
|     }
 | |
| 
 | |
|   static isometry cspin90(int i, int j, bool noflat = false) {
 | |
|     auto res = zero_vector<multivector_data<data>> ();
 | |
|     if(nm == nmFlatten && !noflat) {
 | |
|       res[0] = N(1);
 | |
|       res[(1<<i) | (1<<j)] = N(i > j ? 1 : -1); 
 | |
|       return res;
 | |
|       }
 | |
|     res[0] = sqrt(N(.5));
 | |
|     res[(1<<i) | (1<<j)] = sqrt(N(.5)) * (i > 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<multivector_data<data>> (0);
 | |
|     auto ui = unit_vector<data> (i);
 | |
|     auto uj = unit_vector<data> (j);
 | |
|     return u * cosh(alpha/2) + multimul(embed(uj), embed(ui)) * sinh(alpha/2); */
 | |
|     auto res = zero_vector<multivector_data<data>> ();
 | |
|     if(nm == nmFlatten) {
 | |
|       res[0] = N(1);
 | |
|       res[(1<<i) | (1<<j)] = tanh(alpha/2);
 | |
|       return res; 
 | |
|       }
 | |
|     res[0] = cosh(alpha/2);
 | |
|     res[(1<<i) | (1<<j)] = sinh(alpha/2);
 | |
|     return res; 
 | |
|     }
 | |
|   static isometry id() { return unit_vector<multivector_data<data>> (0); };
 | |
|   static constexpr point center() { return unit_vector<data>(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<odd<D>,flat_even<D>,underling<D>>(chkmul<flat_even<D>,flat_underling<D>,odd<D>>(T, embed(x)), conjugate(T))));
 | |
|     };
 | |
|   static isometry apply(const isometry& T, const isometry& U) { 
 | |
|     auto res = apply_nm<even<D>, D>(chkmul<flat_even<D>,flat_even<D>,even<D>>(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<data> v1 = embed(pm);
 | |
|     multivector<data> v2 = unit_vector<multivector_data<data>>(1<<(D::Dim-1));
 | |
|     multivector<data> v3 = chkmul<underling<D>,underling<D>,poincare<D>>(v1, v2);
 | |
|     v3 = apply_nm<poincare<D>, D>(v3);
 | |
|     return v3;
 | |
|     }
 | |
| 
 | |
|   static typename D::Number dist0(point x) { return acos_auto<D> (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<data>, 1>;
 | |
|   using isometry = multivector<data>;
 | |
| 
 | |
|   static isometry cspin(int i, int j, typename data::Number alpha) { return rep_mixed<D>::cspin(i, j, alpha); }
 | |
|   static isometry cspin90(int i, int j) { return rep_mixed<D>::cspin90(i, j); }
 | |
|   // j must be the neg coordinate!
 | |
|   static isometry lorentz(int i, int j, N alpha) { return rep_mixed<D>::lorentz(i, j, alpha); }
 | |
|   static isometry id() { return rep_mixed<D>::id(); }
 | |
|   static constexpr point center() { return point{{ id() }}; }
 | |
|   static point apply(const isometry& T, const point& x) { return point{{ despin(chkmul<even<D>,poincare<D>,even<D>>(T, x[0])) }}; }
 | |
|   static isometry apply(const isometry& T, const isometry& U) { return apply_nm<even<D>,D>( chkmul<even<D>,even<D>,even<D>>(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<D>(get_normalized<poincare<D>, 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<multivector_data<data>> (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<data>;
 | |
|   using isometry = poincare_rotation<data>;
 | |
| 
 | |
|   static isometry cspin(int i, int j, typename data::Number alpha) { return { rep_mixed<D>::id(), rep_mixed<D>::cspin(i, j, alpha, true) }; }
 | |
|   static isometry cspin90(int i, int j, typename data::Number alpha) { return { rep_mixed<D>::id(), rep_mixed<D>::cspin90(i, j, alpha, true) }; }
 | |
|   static isometry lorentz(int i, int j, typename data::Number alpha) { return {rep_mixed<D>::lorentz(i, j, alpha), rep_mixed<D>::id() }; }
 | |
|   static isometry id() { return { rep_mixed<D>::id(), rep_mixed<D>::id() }; }
 | |
|   static constexpr point center() { return rep_mixed<D>::id(); }
 | |
|   static point apply(const isometry& T, const point& x) { return despin(chkmul<poincare<D>,poincare<D>,even<D>>(T.first, chkmul<rotational<D>,poincare<D>,poincare<D>>(T.second, x))); }
 | |
|   static isometry apply(const isometry& T, const isometry& U) {
 | |
|     auto R1 = apply_nm<rotational<D>, poincare<D>, poincare<D>> (T.second, U.first);
 | |
|     auto R2 = apply_nm<poincare<D>, poincare<D>, even<D>> (T.first, R1);
 | |
|     auto R3 = despin2(R2);
 | |
|     return { R3.first, apply_nm<rotational<D>, rotational<D>, rotational<D>> (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<D>::id() }; }
 | |
| 
 | |
|   static typename D::Number dist0(const point& ax) { 
 | |
|     return acos_auto<D>(get_normalized<poincare<D>, 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<multivector_data<data>> (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)+"]"; }
 | |
|   };
 | |
| 
 | |
| }
 | 
