mirror of
https://github.com/zenorogue/hyperrogue.git
synced 2024-12-27 02:20:36 +00:00
331 lines
11 KiB
C++
331 lines
11 KiB
C++
namespace reps {
|
|
|
|
TD struct mvector {
|
|
array<typename D::Number, D::Dim> values;
|
|
typename D::Number& operator [] (int i) { return values[i]; }
|
|
const typename D::Number& operator [] (int i) const { return values[i]; }
|
|
mvector operator + (const mvector& M) const {
|
|
mvector result;
|
|
for(int i=0; i<D::Dim; i++) result[i] = self[i] + M[i];
|
|
return result;
|
|
}
|
|
mvector operator - (const mvector& M) const {
|
|
mvector result;
|
|
for(int i=0; i<D::Dim; i++) result[i] = self[i] - M[i];
|
|
return result;
|
|
}
|
|
mvector operator * (const typename D::Number& x) const {
|
|
mvector result;
|
|
for(int i=0; i<D::Dim; i++) result[i] = self[i] * x;
|
|
return result;
|
|
}
|
|
mvector operator / (const typename D::Number& x) const {
|
|
mvector result;
|
|
for(int i=0; i<D::Dim; i++) result[i] = self[i] / x;
|
|
return result;
|
|
}
|
|
mvector operator * (int x) const {
|
|
mvector result;
|
|
for(int i=0; i<D::Dim; i++) result[i] = self[i] * x;
|
|
return result;
|
|
}
|
|
mvector operator / (int x) const {
|
|
mvector result;
|
|
for(int i=0; i<D::Dim; i++) result[i] = self[i] / x;
|
|
return result;
|
|
}
|
|
};
|
|
|
|
TD struct matrix {
|
|
array<array<typename D::Number, D::Dim>, D::Dim> values;
|
|
array<typename D::Number, D::Dim>& operator [] (int i) { return values[i]; }
|
|
const array<typename D::Number, D::Dim>& operator [] (int i) const { return values[i]; }
|
|
matrix operator * (const matrix& M) const {
|
|
matrix result;
|
|
for(int i=0; i<D::Dim; i++)
|
|
for(int k=0; k<D::Dim; k++) {
|
|
result[i][k] = typename D::Number(0);
|
|
for(int j=0; j<D::Dim; j++) result[i][k] += self[i][j] * M[j][k];
|
|
}
|
|
return result;
|
|
}
|
|
mvector<D> operator * (const mvector<D>& V) const {
|
|
mvector<D> result;
|
|
for(int i=0; i<D::Dim; i++) {
|
|
result[i] = typename D::Number(0);
|
|
for(int j=0; j<D::Dim; j++) result[i] += self[i][j] * V[j];
|
|
}
|
|
return result;
|
|
}
|
|
matrix operator * (const typename D::Number& x) const {
|
|
matrix result;
|
|
for(int i=0; i<D::Dim; i++) for(int j=0; j<D::Dim; j++) result[i][j] = self[i][j] * x;
|
|
return result;
|
|
}
|
|
matrix operator / (const typename D::Number& x) const {
|
|
matrix result;
|
|
for(int i=0; i<D::Dim; i++) for(int j=0; j<D::Dim; j++) result[i][j] = self[i][j] / x;
|
|
return result;
|
|
}
|
|
matrix operator * (int x) const {
|
|
matrix result;
|
|
for(int i=0; i<D::Dim; i++) for(int j=0; j<D::Dim; j++) result[i][j] = self[i][j] * x;
|
|
return result;
|
|
}
|
|
matrix operator / (int x) const {
|
|
matrix result;
|
|
for(int i=0; i<D::Dim; i++) for(int j=0; j<D::Dim; j++) result[i][j] = self[i][j] / x;
|
|
return result;
|
|
}
|
|
};
|
|
|
|
TD constexpr mvector<D> zero_vector() {
|
|
mvector<D> result;
|
|
for(int i=0; i<D::Dim; i++) result[i] = typename D::Number(0);
|
|
return result;
|
|
}
|
|
|
|
TD constexpr mvector<D> unit_vector(int id) {
|
|
mvector<D> result;
|
|
for(int i=0; i<D::Dim; i++) result[i] = typename D::Number(0);
|
|
result[id] = typename D::Number(1);
|
|
return result;
|
|
}
|
|
|
|
TD struct multivector_data {
|
|
using Number = typename D::Number;
|
|
static constexpr int Dim = 1<<D::Dim;
|
|
static constexpr int Flipped = -1;
|
|
};
|
|
|
|
TD using multivector = mvector<multivector_data<D>>;
|
|
|
|
TD std::string nz(const multivector<D>& a) {
|
|
constexpr int mdim = 1<<D::Dim;
|
|
using Number = typename D::Number;
|
|
hr::shstream str;
|
|
for(int i=0; i<mdim; i++) if(abs(a[i]) > Number(1e-10)) {
|
|
if(str.s != "") print(str, " ");
|
|
if(a[i] > Number(0)) print(str, "+");
|
|
print(str, a[i]);
|
|
for(int u=0; u<D::Dim; u++) if(i & (1<<u)) print(str, hr::s0 + char('A'+u));
|
|
}
|
|
if(str.s == "") return "0";
|
|
return str.s;
|
|
}
|
|
|
|
TD constexpr multivector<D> unit(const typename D::Number& a) {
|
|
auto res = zero_vector<multivector_data<D>>();
|
|
res[0] = a;
|
|
return res;
|
|
}
|
|
|
|
TD constexpr multivector<D> embed(const mvector<D>& a) {
|
|
auto res = zero_vector<multivector_data<D>>();
|
|
for(int i=0; i<D::Dim; i++) res[1<<i] = a[i];
|
|
return res;
|
|
}
|
|
|
|
TD constexpr mvector<D> unembed(const multivector<D>& a) {
|
|
mvector<D> res;
|
|
for(int i=0; i<D::Dim; i++) res[i] = a[1<<i];
|
|
return res;
|
|
}
|
|
|
|
/* for clarity */
|
|
using mvindex = int;
|
|
using signtype = int;
|
|
/* mvindex decimal 10 (binary 1010) corresponds to unit_vector(1) * unit_vector(3) */
|
|
|
|
TD constexpr signtype conj_sign(mvindex mvid) {
|
|
int b = __builtin_popcount(mvid);
|
|
b = b * (b+1) / 2;
|
|
return (b&1) ? -1 : 1;
|
|
}
|
|
|
|
TD constexpr signtype tra_sign(mvindex mvid) {
|
|
int b = __builtin_popcount(mvid);
|
|
b = b * (b-1) / 2;
|
|
return (b&1) ? -1 : 1;
|
|
}
|
|
|
|
TD constexpr signtype mul_sign(mvindex a, mvindex b) {
|
|
int flips = 0;
|
|
for(int i=0; i<D::Dim; i++) if(b & (1<<i)) {
|
|
// we will need to swap it with that many 1-bits of a
|
|
flips += __builtin_popcount(a & ((1<<i)-1));
|
|
if((i == D::Flipped) && (a & (1<<i))) flips++;
|
|
}
|
|
return (flips&1) ? -1 : 1;
|
|
}
|
|
|
|
TD struct all {
|
|
static constexpr bool check(mvindex a) { return true; }
|
|
static constexpr bool isflat(mvindex a) { return false; }
|
|
};
|
|
|
|
TD struct even {
|
|
static constexpr bool check(mvindex a) { return __builtin_popcount(a) % 2 == 0; }
|
|
static constexpr bool isflat(mvindex a) { return false; }
|
|
};
|
|
|
|
TD struct flat_even {
|
|
static constexpr bool check(mvindex a) { return __builtin_popcount(a) % 2 == 0; }
|
|
static constexpr bool isflat(mvindex a) { return nm == nmFlatten && a == 0; }
|
|
};
|
|
|
|
TD struct odd {
|
|
static constexpr bool check(mvindex a) { return __builtin_popcount(a) % 2 == 1; }
|
|
static constexpr bool isflat(mvindex a) { return false; }
|
|
};
|
|
|
|
TD struct units {
|
|
static constexpr bool check(mvindex a) { return a == 0; }
|
|
static constexpr bool isflat(mvindex a) { return false; }
|
|
};
|
|
|
|
TD struct rotational {
|
|
static constexpr bool check(mvindex a) { return __builtin_popcount(a) % 2 == 0 && a < (1<<(D::Dim-1)); }
|
|
static constexpr bool isflat(mvindex a) { return false; }
|
|
};
|
|
|
|
TD struct underling {
|
|
static constexpr bool check(mvindex a) { return __builtin_popcount(a) == 1; }
|
|
static constexpr bool isflat(mvindex a) { return false; }
|
|
};
|
|
|
|
TD struct flat_underling {
|
|
static constexpr bool check(mvindex a) { return __builtin_popcount(a) == 1; }
|
|
static constexpr bool isflat(mvindex a) { return nm == nmFlatten && a == 1<<(D::Dim-1); }
|
|
};
|
|
|
|
TD struct poincare {
|
|
static constexpr bool check(mvindex a) { return __builtin_popcount(a ^ (1<<(D::Dim-1))) == 1; }
|
|
static constexpr bool isflat(mvindex a) { return false; }
|
|
};
|
|
|
|
TD multivector<D> multimul(const multivector<D>& a, const multivector<D>& b) {
|
|
constexpr int mdim = 1<<D::Dim;
|
|
auto res = zero_vector<multivector_data<D>>();
|
|
for(mvindex i=0; i<mdim; i++)
|
|
for(mvindex j=0; j<mdim; j++) {
|
|
res[i^j] += a[i] * b[j] * mul_sign<D>(i, j);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
template<class A, class B, class C, class D>
|
|
multivector<D> chkmul(const multivector<D>& a, const multivector<D>& b) {
|
|
constexpr int mdim = 1<<D::Dim;
|
|
auto res = zero_vector<multivector_data<D>>();
|
|
|
|
/* we initialize with 0s and then add stuff, so one add per component is not necessary */
|
|
for(mvindex i=0; i<mdim; i++) if(C::check(i)) cbc[cbcAdd]--;
|
|
|
|
for(mvindex i=0; i<mdim; i++) if(A::check(i))
|
|
for(mvindex j=0; j<mdim; j++) if(B::check(j) && C::check(i^j)) {
|
|
if(A::isflat(i))
|
|
res[i^j] += b[j] * mul_sign<D>(i, j);
|
|
else if(B::isflat(j))
|
|
res[i^j] += a[i] * mul_sign<D>(i, j);
|
|
else
|
|
res[i^j] += a[i] * b[j] * mul_sign<D>(i, j);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
TD multivector<D> conjugate(const multivector<D>& a) {
|
|
constexpr int mdim = 1<<D::Dim;
|
|
auto res = a;
|
|
for(int i=0; i<mdim; i++) res[i] *= conj_sign<D>(i);
|
|
return res;
|
|
}
|
|
|
|
TD multivector<D> transpose(const multivector<D>& a) {
|
|
constexpr int mdim = 1<<D::Dim;
|
|
auto res = a;
|
|
for(int i=0; i<mdim; i++) res[i] *= tra_sign<D>(i);
|
|
return res;
|
|
}
|
|
|
|
template<class C, class D> multivector<D> apply_nm(multivector<D> a);
|
|
|
|
TD using poincare_rotation = std::pair<multivector<D>, multivector<D>>;
|
|
|
|
/** decompose o into the poincare part and the rotational component */
|
|
TD poincare_rotation<D> despin2(const multivector<D>& a) {
|
|
auto p = a;
|
|
for(int i=(1<<(D::Dim-1)); i<(1<<(D::Dim)); i++) p[i] = typename D::Number(0);
|
|
p = p * pow(chkmul<rotational<D>,rotational<D>,units<D>>(p, conjugate(p))[0], -0.5);
|
|
auto p1 = chkmul<even<D>,rotational<D>,poincare<D>>(a, conjugate(p));
|
|
return {apply_nm<poincare<D>, D>(p1), p};
|
|
}
|
|
|
|
/** remove the rotational component of a, leaving only the poincare part */
|
|
TD multivector<D> despin(const multivector<D>& a) {
|
|
auto p = a;
|
|
for(int i=(1<<(D::Dim-1)); i<(1<<(D::Dim)); i++) p[i] = typename D::Number(0);
|
|
auto p1 = chkmul<even<D>,rotational<D>,poincare<D>>(a, conjugate(p));
|
|
if(nm == nmInvariant) return p1 * pow(chkmul<rotational<D>,rotational<D>,units<D>>(p, conjugate(p))[0], -0.5);
|
|
return apply_nm<poincare<D>, D>(p1);
|
|
}
|
|
|
|
TD std::string nzv(const mvector<D>& a) { return "vector(" + nz(embed(a)) + ")"; }
|
|
TD std::string nzv(const matrix<D>& a) { return "<matrix>"; }
|
|
|
|
template<class C, class D>
|
|
typename D::Number sqnorm(multivector<D> a) {
|
|
using N = typename D::Number;
|
|
auto res = chkmul<C, C, units<D>>(a, conjugate(a))[0];
|
|
if(res <= N(0) || isinf(res) || isnan(res)) res = N(1);
|
|
return res;
|
|
}
|
|
|
|
TD typename D::Number sqnorm(mvector<D> a) {
|
|
using N = typename D::Number;
|
|
N res(0);
|
|
for(int i=0; i<D::Dim; i++) res += a[i] * a[i] * (i == D::Flipped ? -1:1);
|
|
if(D::Flipped != -1) res = -res;
|
|
if(nm ==nmWeak && (res <= N(0) || isinf(res) || isnan(res))) res = N(1);
|
|
return res;
|
|
}
|
|
|
|
/** if nm is set to nmFlatten or nmForced or nmBinary, apply the requested operation */
|
|
|
|
template<class C, class D> multivector<D> flatten(multivector<D> a) {
|
|
using N = typename D::Number;
|
|
auto divby = a[0]; a[0] = N(1);
|
|
for(int i=1; i<(1<<D::Dim); i++) if(C::check(i)) a[i] /= divby;
|
|
return a;
|
|
}
|
|
|
|
template<class C, class D>
|
|
multivector<D> apply_nm(multivector<D> a) {
|
|
if(nm == nmFlatten) return flatten<C>(a);
|
|
if(nm == nmForced || nm == nmWeak) return a * pow(sqnorm<C,D>(a), -0.5);
|
|
if(nm == nmBinary) { while(a[0] >= 2) { a = a / 2; } while(a[0] > 0 && a[0] < 0.5) { a = a * 2; } }
|
|
return a;
|
|
}
|
|
|
|
TD mvector<D> apply_nm(mvector<D> a) {
|
|
if(nm == nmFlatten) { cbc[cbcDiv]--; return a / a[D::Dim-1]; }
|
|
if(nm == nmForced || nm == nmWeak) return a * pow(sqnorm<D>(a), -0.5);
|
|
if(nm == nmBinary) { while(a[D::Dim-1] >= 2) { a = a / 2; } while(a[D::Dim-1] > 0 && a[D::Dim-1] < 0.5) { a = a * 2; } }
|
|
return a;
|
|
}
|
|
|
|
/** get b which is a coordinate of a, but in normalized form. That is, if a is normalized simply return b, otherwise, multiply b appropriately */
|
|
|
|
template<class C, class D, class E> E get_normalized(multivector<D> a, E b) {
|
|
if(nm != nmInvariant && nm != nmForced) return b * pow(sqnorm<C,D>(a), -0.5);
|
|
return b;
|
|
}
|
|
|
|
template<class D, class E> E get_normalized(mvector<D> a, E b) {
|
|
if(nm != nmInvariant && nm != nmForced) return b * pow(sqnorm<D>(a), -0.5);
|
|
return b;
|
|
}
|
|
|
|
}
|
|
|