cppkh-interface 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cppkh_interface/__init__.py +19 -0
- cppkh_interface/__main__.py +4 -0
- cppkh_interface/data/src/main.cpp +3506 -0
- cppkh_interface/main.py +352 -0
- cppkh_interface/py.typed +1 -0
- cppkh_interface-0.1.0.dist-info/METADATA +76 -0
- cppkh_interface-0.1.0.dist-info/RECORD +9 -0
- cppkh_interface-0.1.0.dist-info/WHEEL +4 -0
- cppkh_interface-0.1.0.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,3506 @@
|
|
|
1
|
+
#include <algorithm>
|
|
2
|
+
#include <array>
|
|
3
|
+
#include <cctype>
|
|
4
|
+
#include <chrono>
|
|
5
|
+
#include <cstdint>
|
|
6
|
+
#include <cstdlib>
|
|
7
|
+
#include <cstring>
|
|
8
|
+
#include <dirent.h>
|
|
9
|
+
#include <fstream>
|
|
10
|
+
#include <functional>
|
|
11
|
+
#include <iostream>
|
|
12
|
+
#include <map>
|
|
13
|
+
#include <memory>
|
|
14
|
+
#include <new>
|
|
15
|
+
#include <set>
|
|
16
|
+
#include <sstream>
|
|
17
|
+
#include <stdexcept>
|
|
18
|
+
#include <string>
|
|
19
|
+
#include <sys/stat.h>
|
|
20
|
+
#include <unordered_map>
|
|
21
|
+
#include <utility>
|
|
22
|
+
#include <vector>
|
|
23
|
+
|
|
24
|
+
#if defined(KH_THREAD_BACKEND_BOOST)
|
|
25
|
+
#include <boost/thread.hpp>
|
|
26
|
+
#define KH_THREAD_BACKEND_NAME "boost"
|
|
27
|
+
#elif defined(KH_THREAD_BACKEND_STD)
|
|
28
|
+
#include <mutex>
|
|
29
|
+
#include <thread>
|
|
30
|
+
#define KH_THREAD_BACKEND_NAME "std"
|
|
31
|
+
#elif defined(KH_THREAD_BACKEND_PTHREAD)
|
|
32
|
+
#include <pthread.h>
|
|
33
|
+
#define KH_THREAD_BACKEND_NAME "pthread"
|
|
34
|
+
#elif defined(KH_THREAD_BACKEND_SINGLE)
|
|
35
|
+
#define KH_THREAD_BACKEND_NAME "single"
|
|
36
|
+
#elif defined(KH_THREAD_BACKEND_WIN32)
|
|
37
|
+
#define KH_THREAD_BACKEND_NAME "win32"
|
|
38
|
+
#elif defined(_WIN32)
|
|
39
|
+
#define KH_THREAD_BACKEND_WIN32
|
|
40
|
+
#define KH_THREAD_BACKEND_NAME "win32"
|
|
41
|
+
#else
|
|
42
|
+
#define KH_THREAD_BACKEND_PTHREAD
|
|
43
|
+
#include <pthread.h>
|
|
44
|
+
#define KH_THREAD_BACKEND_NAME "pthread"
|
|
45
|
+
#endif
|
|
46
|
+
|
|
47
|
+
#ifdef _WIN32
|
|
48
|
+
#ifndef NOMINMAX
|
|
49
|
+
#define NOMINMAX
|
|
50
|
+
#endif
|
|
51
|
+
#include <windows.h>
|
|
52
|
+
#else
|
|
53
|
+
#include <unistd.h>
|
|
54
|
+
#endif
|
|
55
|
+
|
|
56
|
+
namespace kh {
|
|
57
|
+
|
|
58
|
+
class BigInt {
|
|
59
|
+
static const uint32_t BASE = 1000000000u;
|
|
60
|
+
int sign_ = 0;
|
|
61
|
+
std::vector<uint32_t> d_;
|
|
62
|
+
|
|
63
|
+
void trim() {
|
|
64
|
+
while (!d_.empty() && d_.back() == 0) d_.pop_back();
|
|
65
|
+
if (d_.empty()) sign_ = 0;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
static int cmp_abs(const BigInt& a, const BigInt& b) {
|
|
69
|
+
if (a.d_.size() != b.d_.size()) return a.d_.size() < b.d_.size() ? -1 : 1;
|
|
70
|
+
for (int i = static_cast<int>(a.d_.size()) - 1; i >= 0; --i) {
|
|
71
|
+
if (a.d_[i] != b.d_[i]) return a.d_[i] < b.d_[i] ? -1 : 1;
|
|
72
|
+
}
|
|
73
|
+
return 0;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
static BigInt add_abs(const BigInt& a, const BigInt& b) {
|
|
77
|
+
BigInt r;
|
|
78
|
+
r.sign_ = 1;
|
|
79
|
+
uint64_t carry = 0;
|
|
80
|
+
size_t n = std::max(a.d_.size(), b.d_.size());
|
|
81
|
+
r.d_.resize(n);
|
|
82
|
+
for (size_t i = 0; i < n; ++i) {
|
|
83
|
+
uint64_t cur = carry;
|
|
84
|
+
if (i < a.d_.size()) cur += a.d_[i];
|
|
85
|
+
if (i < b.d_.size()) cur += b.d_[i];
|
|
86
|
+
r.d_[i] = static_cast<uint32_t>(cur % BASE);
|
|
87
|
+
carry = cur / BASE;
|
|
88
|
+
}
|
|
89
|
+
if (carry) r.d_.push_back(static_cast<uint32_t>(carry));
|
|
90
|
+
r.trim();
|
|
91
|
+
return r;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
static BigInt sub_abs(const BigInt& a, const BigInt& b) {
|
|
95
|
+
BigInt r;
|
|
96
|
+
r.sign_ = 1;
|
|
97
|
+
r.d_.resize(a.d_.size());
|
|
98
|
+
int64_t carry = 0;
|
|
99
|
+
for (size_t i = 0; i < a.d_.size(); ++i) {
|
|
100
|
+
int64_t cur = static_cast<int64_t>(a.d_[i]) - carry - (i < b.d_.size() ? b.d_[i] : 0);
|
|
101
|
+
if (cur < 0) {
|
|
102
|
+
cur += BASE;
|
|
103
|
+
carry = 1;
|
|
104
|
+
} else {
|
|
105
|
+
carry = 0;
|
|
106
|
+
}
|
|
107
|
+
r.d_[i] = static_cast<uint32_t>(cur);
|
|
108
|
+
}
|
|
109
|
+
r.trim();
|
|
110
|
+
return r;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
void shift_base_add(uint32_t x) {
|
|
114
|
+
if (sign_ == 0 && x == 0) return;
|
|
115
|
+
d_.insert(d_.begin(), x);
|
|
116
|
+
sign_ = 1;
|
|
117
|
+
trim();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
public:
|
|
121
|
+
BigInt() = default;
|
|
122
|
+
BigInt(long long v) { *this = v; }
|
|
123
|
+
|
|
124
|
+
BigInt& operator=(long long v) {
|
|
125
|
+
d_.clear();
|
|
126
|
+
if (v == 0) {
|
|
127
|
+
sign_ = 0;
|
|
128
|
+
return *this;
|
|
129
|
+
}
|
|
130
|
+
sign_ = v < 0 ? -1 : 1;
|
|
131
|
+
unsigned long long x = v < 0 ? static_cast<unsigned long long>(-v) : static_cast<unsigned long long>(v);
|
|
132
|
+
while (x) {
|
|
133
|
+
d_.push_back(static_cast<uint32_t>(x % BASE));
|
|
134
|
+
x /= BASE;
|
|
135
|
+
}
|
|
136
|
+
return *this;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
bool isZero() const { return sign_ == 0; }
|
|
140
|
+
int sign() const { return sign_; }
|
|
141
|
+
BigInt abs() const { BigInt r = *this; if (r.sign_ < 0) r.sign_ = 1; return r; }
|
|
142
|
+
bool isOneAbs() const { return d_.size() == 1 && d_[0] == 1; }
|
|
143
|
+
|
|
144
|
+
std::string str() const {
|
|
145
|
+
if (sign_ == 0) return "0";
|
|
146
|
+
std::ostringstream out;
|
|
147
|
+
if (sign_ < 0) out << '-';
|
|
148
|
+
out << d_.back();
|
|
149
|
+
for (int i = static_cast<int>(d_.size()) - 2; i >= 0; --i) {
|
|
150
|
+
std::string s = std::to_string(d_[i]);
|
|
151
|
+
out << std::string(9 - s.size(), '0') << s;
|
|
152
|
+
}
|
|
153
|
+
return out.str();
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
friend bool operator==(const BigInt& a, const BigInt& b) {
|
|
157
|
+
return a.sign_ == b.sign_ && a.d_ == b.d_;
|
|
158
|
+
}
|
|
159
|
+
friend bool operator!=(const BigInt& a, const BigInt& b) { return !(a == b); }
|
|
160
|
+
friend bool operator<(const BigInt& a, const BigInt& b) {
|
|
161
|
+
if (a.sign_ != b.sign_) return a.sign_ < b.sign_;
|
|
162
|
+
if (a.sign_ == 0) return false;
|
|
163
|
+
int c = cmp_abs(a, b);
|
|
164
|
+
return a.sign_ > 0 ? c < 0 : c > 0;
|
|
165
|
+
}
|
|
166
|
+
friend bool operator>(const BigInt& a, const BigInt& b) { return b < a; }
|
|
167
|
+
friend bool operator<=(const BigInt& a, const BigInt& b) { return !(b < a); }
|
|
168
|
+
friend bool operator>=(const BigInt& a, const BigInt& b) { return !(a < b); }
|
|
169
|
+
|
|
170
|
+
BigInt operator-() const {
|
|
171
|
+
BigInt r = *this;
|
|
172
|
+
r.sign_ = -r.sign_;
|
|
173
|
+
return r;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
friend BigInt operator+(const BigInt& a, const BigInt& b) {
|
|
177
|
+
if (a.sign_ == 0) return b;
|
|
178
|
+
if (b.sign_ == 0) return a;
|
|
179
|
+
if (a.sign_ == b.sign_) {
|
|
180
|
+
BigInt r = add_abs(a, b);
|
|
181
|
+
r.sign_ = a.sign_;
|
|
182
|
+
return r;
|
|
183
|
+
}
|
|
184
|
+
int c = cmp_abs(a, b);
|
|
185
|
+
if (c == 0) return BigInt(0);
|
|
186
|
+
BigInt r = c > 0 ? sub_abs(a, b) : sub_abs(b, a);
|
|
187
|
+
r.sign_ = c > 0 ? a.sign_ : b.sign_;
|
|
188
|
+
return r;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
friend BigInt operator-(const BigInt& a, const BigInt& b) { return a + (-b); }
|
|
192
|
+
|
|
193
|
+
friend BigInt operator*(const BigInt& a, const BigInt& b) {
|
|
194
|
+
if (a.sign_ == 0 || b.sign_ == 0) return BigInt(0);
|
|
195
|
+
BigInt r;
|
|
196
|
+
r.sign_ = a.sign_ * b.sign_;
|
|
197
|
+
r.d_.assign(a.d_.size() + b.d_.size(), 0);
|
|
198
|
+
for (size_t i = 0; i < a.d_.size(); ++i) {
|
|
199
|
+
uint64_t carry = 0;
|
|
200
|
+
for (size_t j = 0; j < b.d_.size() || carry; ++j) {
|
|
201
|
+
uint64_t cur = r.d_[i + j] + carry;
|
|
202
|
+
if (j < b.d_.size()) cur += static_cast<uint64_t>(a.d_[i]) * b.d_[j];
|
|
203
|
+
r.d_[i + j] = static_cast<uint32_t>(cur % BASE);
|
|
204
|
+
carry = cur / BASE;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
r.trim();
|
|
208
|
+
return r;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
BigInt mul_small(uint32_t m) const {
|
|
212
|
+
if (sign_ == 0 || m == 0) return BigInt(0);
|
|
213
|
+
BigInt r;
|
|
214
|
+
r.sign_ = sign_;
|
|
215
|
+
r.d_.resize(d_.size());
|
|
216
|
+
uint64_t carry = 0;
|
|
217
|
+
for (size_t i = 0; i < d_.size(); ++i) {
|
|
218
|
+
uint64_t cur = carry + static_cast<uint64_t>(d_[i]) * m;
|
|
219
|
+
r.d_[i] = static_cast<uint32_t>(cur % BASE);
|
|
220
|
+
carry = cur / BASE;
|
|
221
|
+
}
|
|
222
|
+
if (carry) r.d_.push_back(static_cast<uint32_t>(carry));
|
|
223
|
+
return r;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
int mod_small(uint32_t m) const {
|
|
227
|
+
uint64_t rem = 0;
|
|
228
|
+
for (int i = static_cast<int>(d_.size()) - 1; i >= 0; --i) {
|
|
229
|
+
rem = (rem * BASE + d_[i]) % m;
|
|
230
|
+
}
|
|
231
|
+
return sign_ < 0 && rem ? static_cast<int>(m - rem) : static_cast<int>(rem);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
BigInt div_small(uint32_t m, uint32_t* remainder = nullptr) const {
|
|
235
|
+
if (m == 0) throw std::runtime_error("division by zero");
|
|
236
|
+
if (sign_ == 0) {
|
|
237
|
+
if (remainder) *remainder = 0;
|
|
238
|
+
return BigInt(0);
|
|
239
|
+
}
|
|
240
|
+
BigInt r;
|
|
241
|
+
r.sign_ = sign_;
|
|
242
|
+
r.d_.assign(d_.size(), 0);
|
|
243
|
+
uint64_t rem = 0;
|
|
244
|
+
for (int i = static_cast<int>(d_.size()) - 1; i >= 0; --i) {
|
|
245
|
+
uint64_t cur = d_[i] + rem * BASE;
|
|
246
|
+
r.d_[i] = static_cast<uint32_t>(cur / m);
|
|
247
|
+
rem = cur % m;
|
|
248
|
+
}
|
|
249
|
+
r.trim();
|
|
250
|
+
if (remainder) *remainder = static_cast<uint32_t>(rem);
|
|
251
|
+
return r;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
static std::pair<BigInt, BigInt> divmod(const BigInt& aa, const BigInt& bb) {
|
|
255
|
+
if (bb.sign_ == 0) throw std::runtime_error("division by zero");
|
|
256
|
+
if (aa.sign_ == 0) return std::make_pair(BigInt(0), BigInt(0));
|
|
257
|
+
BigInt a = aa.abs(), b = bb.abs();
|
|
258
|
+
if (cmp_abs(a, b) < 0) return std::make_pair(BigInt(0), aa);
|
|
259
|
+
BigInt q, r;
|
|
260
|
+
q.sign_ = 1;
|
|
261
|
+
q.d_.assign(a.d_.size(), 0);
|
|
262
|
+
for (int i = static_cast<int>(a.d_.size()) - 1; i >= 0; --i) {
|
|
263
|
+
r.shift_base_add(a.d_[i]);
|
|
264
|
+
uint32_t lo = 0, hi = BASE - 1, x = 0;
|
|
265
|
+
while (lo <= hi) {
|
|
266
|
+
uint32_t mid = lo + (hi - lo) / 2;
|
|
267
|
+
BigInt prod = b.mul_small(mid);
|
|
268
|
+
if (prod <= r) {
|
|
269
|
+
x = mid;
|
|
270
|
+
lo = mid + 1;
|
|
271
|
+
} else {
|
|
272
|
+
if (mid == 0) break;
|
|
273
|
+
hi = mid - 1;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
q.d_[i] = x;
|
|
277
|
+
if (x) r = r - b.mul_small(x);
|
|
278
|
+
}
|
|
279
|
+
q.sign_ = aa.sign_ * bb.sign_;
|
|
280
|
+
q.trim();
|
|
281
|
+
r.sign_ = aa.sign_;
|
|
282
|
+
r.trim();
|
|
283
|
+
return std::make_pair(q, r);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
friend BigInt operator/(const BigInt& a, const BigInt& b) { return divmod(a, b).first; }
|
|
287
|
+
friend BigInt operator%(const BigInt& a, const BigInt& b) { return divmod(a, b).second; }
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
static const BigInt BI_ZERO(0), BI_ONE(1), BI_MINUS_ONE(-1);
|
|
291
|
+
|
|
292
|
+
static BigInt coeffProduct(const BigInt& a, const BigInt& b) {
|
|
293
|
+
if (a.isZero() || b.isZero()) return BI_ZERO;
|
|
294
|
+
if (a == BI_ONE) return b;
|
|
295
|
+
if (b == BI_ONE) return a;
|
|
296
|
+
if (a == BI_MINUS_ONE) return -b;
|
|
297
|
+
if (b == BI_MINUS_ONE) return -a;
|
|
298
|
+
return a * b;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
struct Options {
|
|
302
|
+
int matrixThreads = 1;
|
|
303
|
+
bool reorderCrossings = true;
|
|
304
|
+
bool progress = true;
|
|
305
|
+
bool simplifyPD = true;
|
|
306
|
+
bool profile = false;
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
static Options g_options;
|
|
310
|
+
typedef std::vector<std::vector<int> > PDCode;
|
|
311
|
+
|
|
312
|
+
struct ProfileCounter {
|
|
313
|
+
uint64_t ns = 0;
|
|
314
|
+
uint64_t calls = 0;
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
struct ProfileStats {
|
|
318
|
+
uint64_t totalNs = 0;
|
|
319
|
+
ProfileCounter kh;
|
|
320
|
+
ProfileCounter generateFast;
|
|
321
|
+
ProfileCounter complexCompose;
|
|
322
|
+
ProfileCounter complexReduce;
|
|
323
|
+
ProfileCounter deLoop;
|
|
324
|
+
ProfileCounter deLoopSetup;
|
|
325
|
+
ProfileCounter deLoopPrev;
|
|
326
|
+
ProfileCounter deLoopNext;
|
|
327
|
+
ProfileCounter deLoopPrevTerms;
|
|
328
|
+
ProfileCounter deLoopNextTerms;
|
|
329
|
+
ProfileCounter matrixReduce;
|
|
330
|
+
ProfileCounter matrixCompose;
|
|
331
|
+
ProfileCounter blockReduction;
|
|
332
|
+
ProfileCounter blockReductionApply;
|
|
333
|
+
ProfileCounter lcccReduce;
|
|
334
|
+
ProfileCounter lcccComposeVertical;
|
|
335
|
+
ProfileCounter lcccComposeHorizontal;
|
|
336
|
+
ProfileCounter cobComposeVertical;
|
|
337
|
+
ProfileCounter cobComposeHorizontal;
|
|
338
|
+
|
|
339
|
+
void reset() {
|
|
340
|
+
*this = ProfileStats();
|
|
341
|
+
}
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
static ProfileStats g_profile;
|
|
345
|
+
|
|
346
|
+
static uint64_t profileNowNs() {
|
|
347
|
+
#ifdef _WIN32
|
|
348
|
+
static LARGE_INTEGER freq = []() {
|
|
349
|
+
LARGE_INTEGER f;
|
|
350
|
+
QueryPerformanceFrequency(&f);
|
|
351
|
+
return f;
|
|
352
|
+
}();
|
|
353
|
+
LARGE_INTEGER counter;
|
|
354
|
+
QueryPerformanceCounter(&counter);
|
|
355
|
+
return static_cast<uint64_t>((static_cast<long double>(counter.QuadPart) * 1000000000.0L) /
|
|
356
|
+
static_cast<long double>(freq.QuadPart));
|
|
357
|
+
#else
|
|
358
|
+
return static_cast<uint64_t>(
|
|
359
|
+
std::chrono::duration_cast<std::chrono::nanoseconds>(
|
|
360
|
+
std::chrono::steady_clock::now().time_since_epoch()).count());
|
|
361
|
+
#endif
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
struct ProfileScope {
|
|
365
|
+
ProfileCounter* counter;
|
|
366
|
+
uint64_t start;
|
|
367
|
+
|
|
368
|
+
explicit ProfileScope(ProfileCounter& c) : counter(g_options.profile ? &c : nullptr), start(0) {
|
|
369
|
+
if (counter) {
|
|
370
|
+
counter->calls++;
|
|
371
|
+
start = profileNowNs();
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
~ProfileScope() {
|
|
376
|
+
if (counter) counter->ns += profileNowNs() - start;
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
|
|
380
|
+
#define KH_JOIN2(a, b) a##b
|
|
381
|
+
#define KH_JOIN(a, b) KH_JOIN2(a, b)
|
|
382
|
+
#define KH_PROFILE(counterName) ProfileScope KH_JOIN(khProfileScope_, __LINE__)(g_profile.counterName)
|
|
383
|
+
|
|
384
|
+
static std::string profileMs(uint64_t ns) {
|
|
385
|
+
std::ostringstream out;
|
|
386
|
+
out.setf(std::ios::fixed);
|
|
387
|
+
out.precision(3);
|
|
388
|
+
out << (static_cast<double>(ns) / 1000000.0);
|
|
389
|
+
return out.str();
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
static void appendProfileCounter(std::ostream& out, const char* name, const ProfileCounter& c) {
|
|
393
|
+
out << ' ' << name << "Ms=" << profileMs(c.ns)
|
|
394
|
+
<< ' ' << name << "Calls=" << c.calls;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
static void printProfile() {
|
|
398
|
+
if (!g_options.profile) return;
|
|
399
|
+
std::cerr << "CPPKH_PROFILE totalMs=" << profileMs(g_profile.totalNs)
|
|
400
|
+
<< " khMs=" << profileMs(g_profile.kh.ns);
|
|
401
|
+
appendProfileCounter(std::cerr, "generateFast", g_profile.generateFast);
|
|
402
|
+
appendProfileCounter(std::cerr, "complexCompose", g_profile.complexCompose);
|
|
403
|
+
appendProfileCounter(std::cerr, "complexReduce", g_profile.complexReduce);
|
|
404
|
+
appendProfileCounter(std::cerr, "deLoop", g_profile.deLoop);
|
|
405
|
+
appendProfileCounter(std::cerr, "deLoopSetup", g_profile.deLoopSetup);
|
|
406
|
+
appendProfileCounter(std::cerr, "deLoopPrev", g_profile.deLoopPrev);
|
|
407
|
+
appendProfileCounter(std::cerr, "deLoopNext", g_profile.deLoopNext);
|
|
408
|
+
std::cerr << " deLoopPrevTermCount=" << g_profile.deLoopPrevTerms.ns
|
|
409
|
+
<< " deLoopPrevTermCalls=" << g_profile.deLoopPrevTerms.calls
|
|
410
|
+
<< " deLoopNextTermCount=" << g_profile.deLoopNextTerms.ns
|
|
411
|
+
<< " deLoopNextTermCalls=" << g_profile.deLoopNextTerms.calls;
|
|
412
|
+
appendProfileCounter(std::cerr, "matrixReduce", g_profile.matrixReduce);
|
|
413
|
+
appendProfileCounter(std::cerr, "matrixCompose", g_profile.matrixCompose);
|
|
414
|
+
appendProfileCounter(std::cerr, "blockReduction", g_profile.blockReduction);
|
|
415
|
+
appendProfileCounter(std::cerr, "blockReductionApply", g_profile.blockReductionApply);
|
|
416
|
+
appendProfileCounter(std::cerr, "lcccReduce", g_profile.lcccReduce);
|
|
417
|
+
appendProfileCounter(std::cerr, "lcccComposeVertical", g_profile.lcccComposeVertical);
|
|
418
|
+
appendProfileCounter(std::cerr, "lcccComposeHorizontal", g_profile.lcccComposeHorizontal);
|
|
419
|
+
appendProfileCounter(std::cerr, "cobComposeVertical", g_profile.cobComposeVertical);
|
|
420
|
+
appendProfileCounter(std::cerr, "cobComposeHorizontal", g_profile.cobComposeHorizontal);
|
|
421
|
+
std::cerr << "\n";
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
static int detectHardwareThreads() {
|
|
425
|
+
#ifdef _WIN32
|
|
426
|
+
SYSTEM_INFO info;
|
|
427
|
+
GetSystemInfo(&info);
|
|
428
|
+
return std::max(1, static_cast<int>(info.dwNumberOfProcessors));
|
|
429
|
+
#elif defined(KH_THREAD_BACKEND_STD)
|
|
430
|
+
unsigned int n = std::thread::hardware_concurrency();
|
|
431
|
+
return n == 0 ? 1 : static_cast<int>(n);
|
|
432
|
+
#elif defined(KH_THREAD_BACKEND_BOOST)
|
|
433
|
+
unsigned int n = boost::thread::hardware_concurrency();
|
|
434
|
+
return n == 0 ? 1 : static_cast<int>(n);
|
|
435
|
+
#elif !defined(_WIN32)
|
|
436
|
+
long n = sysconf(_SC_NPROCESSORS_ONLN);
|
|
437
|
+
return n > 0 ? static_cast<int>(n) : 1;
|
|
438
|
+
#else
|
|
439
|
+
return 1;
|
|
440
|
+
#endif
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
static int requestedMatrixThreads() {
|
|
444
|
+
// JavaKh's own -P path is experimental and slower on the benchmark set.
|
|
445
|
+
// The attempted row-parallel matrix path in this port also loses to the
|
|
446
|
+
// single-threaded algorithm and can stress shared caches, so the core
|
|
447
|
+
// computation intentionally stays serial for correctness and speed.
|
|
448
|
+
return 1;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
class SimpleMutex {
|
|
452
|
+
#if defined(KH_THREAD_BACKEND_BOOST)
|
|
453
|
+
boost::mutex m_;
|
|
454
|
+
public:
|
|
455
|
+
void lock() { m_.lock(); }
|
|
456
|
+
void unlock() { m_.unlock(); }
|
|
457
|
+
#elif defined(KH_THREAD_BACKEND_STD)
|
|
458
|
+
std::mutex m_;
|
|
459
|
+
public:
|
|
460
|
+
void lock() { m_.lock(); }
|
|
461
|
+
void unlock() { m_.unlock(); }
|
|
462
|
+
#elif defined(KH_THREAD_BACKEND_PTHREAD)
|
|
463
|
+
pthread_mutex_t m_;
|
|
464
|
+
public:
|
|
465
|
+
SimpleMutex() { pthread_mutex_init(&m_, NULL); }
|
|
466
|
+
~SimpleMutex() { pthread_mutex_destroy(&m_); }
|
|
467
|
+
void lock() { pthread_mutex_lock(&m_); }
|
|
468
|
+
void unlock() { pthread_mutex_unlock(&m_); }
|
|
469
|
+
#elif defined(KH_THREAD_BACKEND_WIN32)
|
|
470
|
+
CRITICAL_SECTION cs_;
|
|
471
|
+
public:
|
|
472
|
+
SimpleMutex() { InitializeCriticalSection(&cs_); }
|
|
473
|
+
~SimpleMutex() { DeleteCriticalSection(&cs_); }
|
|
474
|
+
void lock() { EnterCriticalSection(&cs_); }
|
|
475
|
+
void unlock() { LeaveCriticalSection(&cs_); }
|
|
476
|
+
#else
|
|
477
|
+
public:
|
|
478
|
+
void lock() {}
|
|
479
|
+
void unlock() {}
|
|
480
|
+
#endif
|
|
481
|
+
};
|
|
482
|
+
|
|
483
|
+
class SimpleLock {
|
|
484
|
+
SimpleMutex& m_;
|
|
485
|
+
public:
|
|
486
|
+
explicit SimpleLock(SimpleMutex& m) : m_(m) { m_.lock(); }
|
|
487
|
+
~SimpleLock() { m_.unlock(); }
|
|
488
|
+
};
|
|
489
|
+
|
|
490
|
+
#if !defined(KH_THREAD_BACKEND_SINGLE)
|
|
491
|
+
struct ParallelJob {
|
|
492
|
+
int next = 0;
|
|
493
|
+
int n = 0;
|
|
494
|
+
int chunk = 1;
|
|
495
|
+
SimpleMutex lock;
|
|
496
|
+
std::function<void(int,int)> fn;
|
|
497
|
+
};
|
|
498
|
+
|
|
499
|
+
static void parallelWorkerLoop(ParallelJob* job) {
|
|
500
|
+
while (true) {
|
|
501
|
+
int begin;
|
|
502
|
+
{
|
|
503
|
+
SimpleLock guard(job->lock);
|
|
504
|
+
begin = job->next;
|
|
505
|
+
job->next += job->chunk;
|
|
506
|
+
}
|
|
507
|
+
if (begin >= job->n) break;
|
|
508
|
+
job->fn(begin, std::min(job->n, begin + job->chunk));
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
#if defined(KH_THREAD_BACKEND_PTHREAD)
|
|
513
|
+
static void* pthreadParallelWorker(void* ptr) {
|
|
514
|
+
parallelWorkerLoop(static_cast<ParallelJob*>(ptr));
|
|
515
|
+
return NULL;
|
|
516
|
+
}
|
|
517
|
+
#elif defined(KH_THREAD_BACKEND_WIN32)
|
|
518
|
+
static DWORD WINAPI win32ParallelWorker(LPVOID ptr) {
|
|
519
|
+
parallelWorkerLoop(static_cast<ParallelJob*>(ptr));
|
|
520
|
+
return 0;
|
|
521
|
+
}
|
|
522
|
+
#endif
|
|
523
|
+
#endif
|
|
524
|
+
|
|
525
|
+
static void parallelForWithThreads(int n, int threads, int minParallelRows, const std::function<void(int,int)>& fn) {
|
|
526
|
+
threads = std::min(std::max(1, threads), std::max(1, n));
|
|
527
|
+
if (threads <= 1 || n < minParallelRows) {
|
|
528
|
+
fn(0, n);
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
#if defined(KH_THREAD_BACKEND_BOOST)
|
|
532
|
+
ParallelJob job;
|
|
533
|
+
job.n = n;
|
|
534
|
+
job.chunk = std::max(1, (n + threads * 4 - 1) / (threads * 4));
|
|
535
|
+
job.fn = fn;
|
|
536
|
+
std::vector<std::unique_ptr<boost::thread> > handles;
|
|
537
|
+
handles.reserve(threads);
|
|
538
|
+
for (int i = 0; i < threads; ++i) {
|
|
539
|
+
handles.push_back(std::unique_ptr<boost::thread>(new boost::thread([&job]() { parallelWorkerLoop(&job); })));
|
|
540
|
+
}
|
|
541
|
+
for (size_t i = 0; i < handles.size(); ++i) handles[i]->join();
|
|
542
|
+
#elif defined(KH_THREAD_BACKEND_STD)
|
|
543
|
+
ParallelJob job;
|
|
544
|
+
job.n = n;
|
|
545
|
+
job.chunk = std::max(1, (n + threads * 4 - 1) / (threads * 4));
|
|
546
|
+
job.fn = fn;
|
|
547
|
+
std::vector<std::thread> handles;
|
|
548
|
+
handles.reserve(threads);
|
|
549
|
+
for (int i = 0; i < threads; ++i) handles.push_back(std::thread([&job]() { parallelWorkerLoop(&job); }));
|
|
550
|
+
for (size_t i = 0; i < handles.size(); ++i) handles[i].join();
|
|
551
|
+
#elif defined(KH_THREAD_BACKEND_PTHREAD)
|
|
552
|
+
ParallelJob job;
|
|
553
|
+
job.n = n;
|
|
554
|
+
job.chunk = std::max(1, (n + threads * 4 - 1) / (threads * 4));
|
|
555
|
+
job.fn = fn;
|
|
556
|
+
std::vector<pthread_t> handles(threads);
|
|
557
|
+
int created = 0;
|
|
558
|
+
for (int i = 0; i < threads; ++i) {
|
|
559
|
+
if (pthread_create(&handles[i], NULL, pthreadParallelWorker, &job) == 0) ++created;
|
|
560
|
+
else break;
|
|
561
|
+
}
|
|
562
|
+
if (created == 0) {
|
|
563
|
+
fn(0, n);
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
566
|
+
for (int i = 0; i < created; ++i) pthread_join(handles[i], NULL);
|
|
567
|
+
#elif defined(KH_THREAD_BACKEND_WIN32)
|
|
568
|
+
threads = std::min(threads, 64);
|
|
569
|
+
ParallelJob job;
|
|
570
|
+
job.n = n;
|
|
571
|
+
job.chunk = std::max(1, (n + threads * 4 - 1) / (threads * 4));
|
|
572
|
+
job.fn = fn;
|
|
573
|
+
std::vector<HANDLE> handles;
|
|
574
|
+
handles.reserve(threads);
|
|
575
|
+
for (int i = 0; i < threads; ++i) {
|
|
576
|
+
HANDLE h = CreateThread(NULL, 0, win32ParallelWorker, &job, 0, NULL);
|
|
577
|
+
if (h) handles.push_back(h);
|
|
578
|
+
}
|
|
579
|
+
if (handles.empty()) {
|
|
580
|
+
fn(0, n);
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
WaitForMultipleObjects(static_cast<DWORD>(handles.size()), handles.data(), TRUE, INFINITE);
|
|
584
|
+
for (HANDLE h : handles) CloseHandle(h);
|
|
585
|
+
#else
|
|
586
|
+
fn(0, n);
|
|
587
|
+
#endif
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
static void parallelFor(int n, int minParallelRows, const std::function<void(int,int)>& fn) {
|
|
591
|
+
parallelForWithThreads(n, requestedMatrixThreads(), minParallelRows, fn);
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
static uint64_t hashMix(uint64_t h, uint64_t v) {
|
|
595
|
+
v += 0x9e3779b97f4a7c15ULL;
|
|
596
|
+
v = (v ^ (v >> 30)) * 0xbf58476d1ce4e5b9ULL;
|
|
597
|
+
v = (v ^ (v >> 27)) * 0x94d049bb133111ebULL;
|
|
598
|
+
v ^= (v >> 31);
|
|
599
|
+
h ^= v + 0x9e3779b97f4a7c15ULL + (h << 6) + (h >> 2);
|
|
600
|
+
return h;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
static uint64_t hashInt(uint64_t h, int v) {
|
|
604
|
+
return hashMix(h, static_cast<uint64_t>(static_cast<int64_t>(v)));
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
template <typename T, typename Alloc>
|
|
608
|
+
static uint64_t hashIntVector(uint64_t h, const std::vector<T, Alloc>& values) {
|
|
609
|
+
h = hashMix(h, values.size());
|
|
610
|
+
for (T v : values) h = hashInt(h, static_cast<int>(v));
|
|
611
|
+
return h;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
typedef int8_t Small;
|
|
615
|
+
|
|
616
|
+
class SmallArena {
|
|
617
|
+
struct Chunk {
|
|
618
|
+
std::unique_ptr<unsigned char[]> data;
|
|
619
|
+
size_t capacity = 0;
|
|
620
|
+
explicit Chunk(size_t n) : data(new unsigned char[n]), capacity(n) {}
|
|
621
|
+
};
|
|
622
|
+
|
|
623
|
+
static const size_t defaultChunkSize = 4u * 1024u * 1024u;
|
|
624
|
+
std::vector<Chunk> chunks;
|
|
625
|
+
size_t chunkIndex = 0;
|
|
626
|
+
size_t offset = 0;
|
|
627
|
+
|
|
628
|
+
public:
|
|
629
|
+
void reset() {
|
|
630
|
+
chunkIndex = 0;
|
|
631
|
+
offset = 0;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
void* allocate(size_t bytes, size_t alignment) {
|
|
635
|
+
if (bytes == 0) bytes = 1;
|
|
636
|
+
if (alignment == 0) alignment = 1;
|
|
637
|
+
while (true) {
|
|
638
|
+
if (chunkIndex >= chunks.size()) {
|
|
639
|
+
size_t capacity = std::max(defaultChunkSize, bytes + alignment);
|
|
640
|
+
chunks.push_back(Chunk(capacity));
|
|
641
|
+
offset = 0;
|
|
642
|
+
}
|
|
643
|
+
Chunk& chunk = chunks[chunkIndex];
|
|
644
|
+
uintptr_t base = reinterpret_cast<uintptr_t>(chunk.data.get());
|
|
645
|
+
uintptr_t ptr = base + offset;
|
|
646
|
+
uintptr_t aligned = (ptr + alignment - 1) & ~(static_cast<uintptr_t>(alignment) - 1);
|
|
647
|
+
size_t alignedOffset = static_cast<size_t>(aligned - base);
|
|
648
|
+
if (alignedOffset + bytes <= chunk.capacity) {
|
|
649
|
+
offset = alignedOffset + bytes;
|
|
650
|
+
return reinterpret_cast<void*>(aligned);
|
|
651
|
+
}
|
|
652
|
+
++chunkIndex;
|
|
653
|
+
offset = 0;
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
};
|
|
657
|
+
|
|
658
|
+
static SmallArena g_smallArena;
|
|
659
|
+
|
|
660
|
+
template <typename T>
|
|
661
|
+
struct ArenaAllocator {
|
|
662
|
+
typedef T value_type;
|
|
663
|
+
|
|
664
|
+
ArenaAllocator() noexcept {}
|
|
665
|
+
template <typename U> ArenaAllocator(const ArenaAllocator<U>&) noexcept {}
|
|
666
|
+
|
|
667
|
+
T* allocate(std::size_t n) {
|
|
668
|
+
if (n > static_cast<std::size_t>(-1) / sizeof(T)) throw std::bad_alloc();
|
|
669
|
+
return static_cast<T*>(g_smallArena.allocate(n * sizeof(T), alignof(T)));
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
void deallocate(T*, std::size_t) noexcept {}
|
|
673
|
+
|
|
674
|
+
template <typename U>
|
|
675
|
+
struct rebind { typedef ArenaAllocator<U> other; };
|
|
676
|
+
};
|
|
677
|
+
|
|
678
|
+
template <typename T, typename U>
|
|
679
|
+
static bool operator==(const ArenaAllocator<T>&, const ArenaAllocator<U>&) { return true; }
|
|
680
|
+
|
|
681
|
+
template <typename T, typename U>
|
|
682
|
+
static bool operator!=(const ArenaAllocator<T>&, const ArenaAllocator<U>&) { return false; }
|
|
683
|
+
|
|
684
|
+
typedef std::vector<Small, ArenaAllocator<Small> > SmallVec;
|
|
685
|
+
typedef std::vector<int, ArenaAllocator<int> > ArenaIntVec;
|
|
686
|
+
|
|
687
|
+
template <size_t InlineN>
|
|
688
|
+
struct IntBuffer {
|
|
689
|
+
size_t n = 0;
|
|
690
|
+
std::array<int, InlineN> inlineData;
|
|
691
|
+
std::vector<int> heapData;
|
|
692
|
+
|
|
693
|
+
explicit IntBuffer(size_t count = 0, int value = 0) : n(count) {
|
|
694
|
+
if (n <= InlineN) std::fill(inlineData.begin(), inlineData.begin() + static_cast<std::ptrdiff_t>(n), value);
|
|
695
|
+
else heapData.assign(n, value);
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
size_t size() const { return n; }
|
|
699
|
+
|
|
700
|
+
int& operator[](size_t i) {
|
|
701
|
+
return heapData.empty() ? inlineData[i] : heapData[i];
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
const int& operator[](size_t i) const {
|
|
705
|
+
return heapData.empty() ? inlineData[i] : heapData[i];
|
|
706
|
+
}
|
|
707
|
+
};
|
|
708
|
+
|
|
709
|
+
typedef IntBuffer<256> WorkInts;
|
|
710
|
+
|
|
711
|
+
struct Cap;
|
|
712
|
+
typedef std::shared_ptr<Cap> CapPtr;
|
|
713
|
+
|
|
714
|
+
struct Cap {
|
|
715
|
+
int n = 0;
|
|
716
|
+
int ncycles = 0;
|
|
717
|
+
std::vector<int> pairings;
|
|
718
|
+
mutable std::string keyCache;
|
|
719
|
+
mutable bool hashReady = false;
|
|
720
|
+
mutable uint64_t hashCache = 0;
|
|
721
|
+
|
|
722
|
+
Cap() = default;
|
|
723
|
+
Cap(int nn, int cycles) : n(nn), ncycles(cycles), pairings(nn, 0) {}
|
|
724
|
+
|
|
725
|
+
const std::string& key() const {
|
|
726
|
+
if (!keyCache.empty()) return keyCache;
|
|
727
|
+
std::ostringstream out;
|
|
728
|
+
out << n << ':' << ncycles << ':';
|
|
729
|
+
for (size_t i = 0; i < pairings.size(); ++i) out << pairings[i] << ',';
|
|
730
|
+
keyCache = out.str();
|
|
731
|
+
return keyCache;
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
bool equals(const Cap& c) const {
|
|
735
|
+
return n == c.n && ncycles == c.ncycles && pairings == c.pairings;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
uint64_t hash() const {
|
|
739
|
+
if (hashReady) return hashCache;
|
|
740
|
+
uint64_t h = 1469598103934665603ULL;
|
|
741
|
+
h = hashInt(h, n);
|
|
742
|
+
h = hashInt(h, ncycles);
|
|
743
|
+
h = hashIntVector(h, pairings);
|
|
744
|
+
hashCache = h;
|
|
745
|
+
hashReady = true;
|
|
746
|
+
return hashCache;
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
CapPtr compose(int start, const CapPtr& c, int cstart, int nc, std::vector<int>* joins = nullptr) const;
|
|
750
|
+
};
|
|
751
|
+
|
|
752
|
+
static SimpleMutex g_capMutex;
|
|
753
|
+
static std::unordered_map<uint64_t, std::vector<CapPtr> > g_capCache;
|
|
754
|
+
|
|
755
|
+
static CapPtr cacheCapNoLock(const Cap& cap) {
|
|
756
|
+
uint64_t h = cap.hash();
|
|
757
|
+
std::vector<CapPtr>& bucket = g_capCache[h];
|
|
758
|
+
for (size_t i = 0; i < bucket.size(); ++i) {
|
|
759
|
+
if (bucket[i]->equals(cap)) return bucket[i];
|
|
760
|
+
}
|
|
761
|
+
CapPtr p = std::make_shared<Cap>(cap);
|
|
762
|
+
bucket.push_back(p);
|
|
763
|
+
return p;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
static CapPtr cacheCap(const Cap& cap) {
|
|
767
|
+
if (requestedMatrixThreads() <= 1) {
|
|
768
|
+
return cacheCapNoLock(cap);
|
|
769
|
+
} else {
|
|
770
|
+
SimpleLock lock(g_capMutex);
|
|
771
|
+
return cacheCapNoLock(cap);
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
static CapPtr compose2Cap(const Cap& a, int start, const Cap& c, int cstart, int nc, std::vector<int>& joins) {
|
|
776
|
+
Cap ret(a.n + c.n - 2 * nc, a.ncycles + c.ncycles);
|
|
777
|
+
for (int i = 0; i < a.n - nc; i++) {
|
|
778
|
+
int ii = (i + start + nc) % a.n;
|
|
779
|
+
ret.pairings[i] = (a.pairings[ii] - start - nc + 2 * a.n) % a.n;
|
|
780
|
+
if (ret.pairings[i] >= a.n - nc) ret.pairings[i] = -1 - (ret.pairings[i] - a.n + nc);
|
|
781
|
+
}
|
|
782
|
+
std::vector<int> thisjoins(nc);
|
|
783
|
+
for (int i = 0; i < nc; i++) {
|
|
784
|
+
int ii = (i + start) % a.n;
|
|
785
|
+
thisjoins[i] = (a.pairings[ii] - start - nc + 2 * a.n) % a.n;
|
|
786
|
+
if (thisjoins[i] >= a.n - nc) thisjoins[i] = -1 - (thisjoins[i] - a.n + nc);
|
|
787
|
+
}
|
|
788
|
+
for (int i = 0; i < c.n - nc; i++) {
|
|
789
|
+
int ii = (i + cstart + nc) % c.n;
|
|
790
|
+
int j = i + a.n - nc;
|
|
791
|
+
ret.pairings[j] = (c.pairings[ii] - cstart - nc + 2 * c.n) % c.n;
|
|
792
|
+
if (ret.pairings[j] >= c.n - nc) ret.pairings[j] = -1 - (c.n - ret.pairings[j] - 1);
|
|
793
|
+
else ret.pairings[j] += a.n - nc;
|
|
794
|
+
}
|
|
795
|
+
std::vector<int> cjoins(nc);
|
|
796
|
+
for (int i = 0; i < nc; i++) {
|
|
797
|
+
int ii = (cstart + nc - 1 - i + c.n) % c.n;
|
|
798
|
+
cjoins[i] = (c.pairings[ii] - cstart - nc + 2 * c.n) % c.n;
|
|
799
|
+
if (cjoins[i] >= c.n - nc) cjoins[i] = -1 - (c.n - cjoins[i] - 1);
|
|
800
|
+
else cjoins[i] += a.n - nc;
|
|
801
|
+
}
|
|
802
|
+
std::vector<char> joinsdone(nc, 0);
|
|
803
|
+
for (int i = 0; i < a.n - nc; i++) {
|
|
804
|
+
if (ret.pairings[i] < 0) {
|
|
805
|
+
while (ret.pairings[i] < 0) {
|
|
806
|
+
int j = -1 - ret.pairings[i];
|
|
807
|
+
joinsdone[j] = 1;
|
|
808
|
+
ret.pairings[i] = cjoins[j];
|
|
809
|
+
if (ret.pairings[i] < 0) {
|
|
810
|
+
j = -1 - ret.pairings[i];
|
|
811
|
+
joinsdone[j] = 1;
|
|
812
|
+
ret.pairings[i] = thisjoins[j];
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
ret.pairings[ret.pairings[i]] = i;
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
for (int i = a.n - nc; i < ret.n; i++) {
|
|
819
|
+
if (ret.pairings[i] < 0) {
|
|
820
|
+
while (ret.pairings[i] < 0) {
|
|
821
|
+
int j = -1 - ret.pairings[i];
|
|
822
|
+
joinsdone[j] = 1;
|
|
823
|
+
ret.pairings[i] = thisjoins[j];
|
|
824
|
+
if (ret.pairings[i] < 0) {
|
|
825
|
+
j = -1 - ret.pairings[i];
|
|
826
|
+
joinsdone[j] = 1;
|
|
827
|
+
ret.pairings[i] = cjoins[j];
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
ret.pairings[ret.pairings[i]] = i;
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
std::fill(joins.begin(), joins.end(), 0);
|
|
834
|
+
for (int i = 0; i < nc; i++) if (joinsdone[i]) joins[i] = -1;
|
|
835
|
+
int nnew = 0;
|
|
836
|
+
for (int i = 0; i < nc; i++) if (!joinsdone[i]) {
|
|
837
|
+
int j = i;
|
|
838
|
+
do {
|
|
839
|
+
joinsdone[j] = 1;
|
|
840
|
+
joins[j] = nnew;
|
|
841
|
+
j = -1 - thisjoins[j];
|
|
842
|
+
joinsdone[j] = 1;
|
|
843
|
+
joins[j] = nnew;
|
|
844
|
+
j = -1 - cjoins[j];
|
|
845
|
+
} while (j != i);
|
|
846
|
+
ret.ncycles++;
|
|
847
|
+
nnew++;
|
|
848
|
+
}
|
|
849
|
+
return cacheCap(ret);
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
CapPtr Cap::compose(int start, const CapPtr& c, int cstart, int nc, std::vector<int>* joins) const {
|
|
853
|
+
std::vector<int> local(nc);
|
|
854
|
+
CapPtr ret = compose2Cap(*this, start, *c, cstart, nc, local);
|
|
855
|
+
if (joins) *joins = local;
|
|
856
|
+
return ret;
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
struct Cobordism;
|
|
860
|
+
typedef std::shared_ptr<Cobordism> CobPtr;
|
|
861
|
+
|
|
862
|
+
struct Cobordism {
|
|
863
|
+
int n = 0;
|
|
864
|
+
int hpower = 0;
|
|
865
|
+
CapPtr top, bottom;
|
|
866
|
+
int nbc = 0;
|
|
867
|
+
int offtop = 0, offbot = 0;
|
|
868
|
+
SmallVec component;
|
|
869
|
+
int ncc = 0;
|
|
870
|
+
SmallVec connectedComponent;
|
|
871
|
+
SmallVec dots;
|
|
872
|
+
SmallVec genus;
|
|
873
|
+
mutable bool mapsReady = false;
|
|
874
|
+
mutable std::string keyCache;
|
|
875
|
+
mutable bool hashReady = false;
|
|
876
|
+
mutable uint64_t hashCache = 0;
|
|
877
|
+
mutable ArenaIntVec boundaryOffset;
|
|
878
|
+
mutable SmallVec boundaryItems;
|
|
879
|
+
mutable ArenaIntVec edgeOffset;
|
|
880
|
+
mutable SmallVec edgeItems;
|
|
881
|
+
|
|
882
|
+
Cobordism() = default;
|
|
883
|
+
Cobordism(const CapPtr& t, const CapPtr& b) : top(t), bottom(b) {
|
|
884
|
+
if (b->n != t->n) throw std::runtime_error("cobordism cap size mismatch");
|
|
885
|
+
n = t->n;
|
|
886
|
+
component.assign(n, static_cast<Small>(-1));
|
|
887
|
+
nbc = 0;
|
|
888
|
+
for (int i = 0; i < n; ++i) if (component[i] == -1) {
|
|
889
|
+
int j = i;
|
|
890
|
+
do {
|
|
891
|
+
component[j] = static_cast<Small>(nbc);
|
|
892
|
+
j = top->pairings[j];
|
|
893
|
+
component[j] = static_cast<Small>(nbc);
|
|
894
|
+
j = bottom->pairings[j];
|
|
895
|
+
} while (j != i);
|
|
896
|
+
++nbc;
|
|
897
|
+
}
|
|
898
|
+
offtop = nbc;
|
|
899
|
+
nbc += top->ncycles;
|
|
900
|
+
offbot = nbc;
|
|
901
|
+
nbc += bottom->ncycles;
|
|
902
|
+
connectedComponent.assign(nbc, 0);
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
const std::string& key() const {
|
|
906
|
+
if (!keyCache.empty()) return keyCache;
|
|
907
|
+
std::ostringstream out;
|
|
908
|
+
out << top->key() << '|' << bottom->key() << '|' << n << '|' << hpower << '|';
|
|
909
|
+
for (size_t i = 0; i < component.size(); ++i) out << static_cast<int>(component[i]) << ',';
|
|
910
|
+
out << '|';
|
|
911
|
+
for (size_t i = 0; i < connectedComponent.size(); ++i) out << static_cast<int>(connectedComponent[i]) << ',';
|
|
912
|
+
out << '|';
|
|
913
|
+
for (size_t i = 0; i < dots.size(); ++i) out << static_cast<int>(dots[i]) << ',';
|
|
914
|
+
out << '|';
|
|
915
|
+
for (size_t i = 0; i < genus.size(); ++i) out << static_cast<int>(genus[i]) << ',';
|
|
916
|
+
keyCache = out.str();
|
|
917
|
+
return keyCache;
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
void reverseMaps() const {
|
|
921
|
+
if (mapsReady) return;
|
|
922
|
+
boundaryOffset.assign(ncc + 1, 0);
|
|
923
|
+
for (int i = 0; i < nbc; ++i) ++boundaryOffset[connectedComponent[i] + 1];
|
|
924
|
+
for (int i = 1; i <= ncc; ++i) boundaryOffset[i] += boundaryOffset[i - 1];
|
|
925
|
+
boundaryItems.resize(nbc);
|
|
926
|
+
WorkInts boundaryCursor(ncc);
|
|
927
|
+
for (int i = 0; i < ncc; ++i) boundaryCursor[i] = boundaryOffset[i];
|
|
928
|
+
for (int i = 0; i < nbc; ++i) {
|
|
929
|
+
int cc = connectedComponent[i];
|
|
930
|
+
boundaryItems[boundaryCursor[cc]++] = static_cast<Small>(i);
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
edgeOffset.assign(offtop + 1, 0);
|
|
934
|
+
for (int i = 0; i < n; ++i) ++edgeOffset[component[i] + 1];
|
|
935
|
+
for (int i = 1; i <= offtop; ++i) edgeOffset[i] += edgeOffset[i - 1];
|
|
936
|
+
edgeItems.resize(n);
|
|
937
|
+
WorkInts edgeCursor(offtop);
|
|
938
|
+
for (int i = 0; i < offtop; ++i) edgeCursor[i] = edgeOffset[i];
|
|
939
|
+
for (int i = 0; i < n; ++i) {
|
|
940
|
+
int c = component[i];
|
|
941
|
+
edgeItems[edgeCursor[c]++] = static_cast<Small>(i);
|
|
942
|
+
}
|
|
943
|
+
mapsReady = true;
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
int boundaryBegin(int i) const { return boundaryOffset[i]; }
|
|
947
|
+
int boundaryEnd(int i) const { return boundaryOffset[i + 1]; }
|
|
948
|
+
int boundaryCount(int i) const { return boundaryOffset[i + 1] - boundaryOffset[i]; }
|
|
949
|
+
int boundaryAt(int pos) const { return boundaryItems[pos]; }
|
|
950
|
+
int edgeBegin(int i) const { return edgeOffset[i]; }
|
|
951
|
+
int edgeEnd(int i) const { return edgeOffset[i + 1]; }
|
|
952
|
+
int edgeCount(int i) const { return edgeOffset[i + 1] - edgeOffset[i]; }
|
|
953
|
+
int edgeAt(int pos) const { return edgeItems[pos]; }
|
|
954
|
+
|
|
955
|
+
bool isIsomorphism() const {
|
|
956
|
+
if (!top->equals(*bottom)) return false;
|
|
957
|
+
if (top->ncycles != 0) throw std::runtime_error("isomorphism check before delooping");
|
|
958
|
+
if (nbc != ncc || hpower != 0) return false;
|
|
959
|
+
for (int i = 0; i < nbc; ++i) {
|
|
960
|
+
if (connectedComponent[i] != i || dots[i] != 0 || genus[i] != 0) return false;
|
|
961
|
+
}
|
|
962
|
+
return true;
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
bool equals(const Cobordism& c) const {
|
|
966
|
+
return n == c.n
|
|
967
|
+
&& hpower == c.hpower
|
|
968
|
+
&& (top.get() == c.top.get() || top->equals(*c.top))
|
|
969
|
+
&& (bottom.get() == c.bottom.get() || bottom->equals(*c.bottom))
|
|
970
|
+
&& nbc == c.nbc
|
|
971
|
+
&& offtop == c.offtop
|
|
972
|
+
&& offbot == c.offbot
|
|
973
|
+
&& component == c.component
|
|
974
|
+
&& ncc == c.ncc
|
|
975
|
+
&& connectedComponent == c.connectedComponent
|
|
976
|
+
&& dots == c.dots
|
|
977
|
+
&& genus == c.genus;
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
uint64_t hash() const {
|
|
981
|
+
if (hashReady) return hashCache;
|
|
982
|
+
uint64_t h = 1469598103934665603ULL;
|
|
983
|
+
h = hashMix(h, top->hash());
|
|
984
|
+
h = hashMix(h, bottom->hash());
|
|
985
|
+
h = hashInt(h, n);
|
|
986
|
+
h = hashInt(h, hpower);
|
|
987
|
+
h = hashInt(h, nbc);
|
|
988
|
+
h = hashInt(h, offtop);
|
|
989
|
+
h = hashInt(h, offbot);
|
|
990
|
+
h = hashIntVector(h, component);
|
|
991
|
+
h = hashInt(h, ncc);
|
|
992
|
+
h = hashIntVector(h, connectedComponent);
|
|
993
|
+
h = hashIntVector(h, dots);
|
|
994
|
+
h = hashIntVector(h, genus);
|
|
995
|
+
hashCache = h;
|
|
996
|
+
hashReady = true;
|
|
997
|
+
return hashCache;
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
CobPtr composeVertical(const CobPtr& cc) const { return composeVerticalPtr(cc.get()); }
|
|
1001
|
+
CobPtr composeVerticalPtr(const Cobordism* cc) const;
|
|
1002
|
+
CobPtr composeHorizontal(int start, const CobPtr& cc, int cstart, int nc) const;
|
|
1003
|
+
};
|
|
1004
|
+
|
|
1005
|
+
template <typename... Args>
|
|
1006
|
+
static CobPtr makeCob(Args&&... args) {
|
|
1007
|
+
return std::make_shared<Cobordism>(std::forward<Args>(args)...);
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
static SimpleMutex g_cobMutex;
|
|
1011
|
+
static std::unordered_map<uint64_t, std::vector<CobPtr> > g_cobCache;
|
|
1012
|
+
|
|
1013
|
+
static CobPtr cacheCobNoLock(const CobPtr& cob) {
|
|
1014
|
+
#ifdef KH_DISABLE_COB_CACHE
|
|
1015
|
+
return cob;
|
|
1016
|
+
#else
|
|
1017
|
+
uint64_t h = cob->hash();
|
|
1018
|
+
std::vector<CobPtr>& bucket = g_cobCache[h];
|
|
1019
|
+
for (size_t i = 0; i < bucket.size(); ++i) {
|
|
1020
|
+
if (bucket[i]->equals(*cob)) return bucket[i];
|
|
1021
|
+
}
|
|
1022
|
+
bucket.push_back(cob);
|
|
1023
|
+
return cob;
|
|
1024
|
+
#endif
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
static CobPtr cacheCob(const CobPtr& cob) {
|
|
1028
|
+
if (requestedMatrixThreads() <= 1) {
|
|
1029
|
+
return cacheCobNoLock(cob);
|
|
1030
|
+
} else {
|
|
1031
|
+
SimpleLock lock(g_cobMutex);
|
|
1032
|
+
return cacheCobNoLock(cob);
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
static void flushCobCache() {
|
|
1037
|
+
#ifndef KH_DISABLE_COB_CACHE
|
|
1038
|
+
g_cobCache.clear();
|
|
1039
|
+
#endif
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
static std::vector<int> counting(int n) {
|
|
1043
|
+
std::vector<int> r(n);
|
|
1044
|
+
for (int i = 0; i < n; ++i) r[i] = i;
|
|
1045
|
+
return r;
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
static SmallVec countingSmall(int n) {
|
|
1049
|
+
SmallVec r(n);
|
|
1050
|
+
for (int i = 0; i < n; ++i) r[i] = static_cast<Small>(i);
|
|
1051
|
+
return r;
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
static CobPtr isomorphism(const CapPtr& c) {
|
|
1055
|
+
if (c->ncycles != 0) throw std::runtime_error("cycles in cap not supported by isomorphism");
|
|
1056
|
+
CobPtr ret = makeCob(c, c);
|
|
1057
|
+
ret->ncc = ret->nbc;
|
|
1058
|
+
ret->connectedComponent = countingSmall(ret->nbc);
|
|
1059
|
+
ret->dots.assign(ret->ncc, 0);
|
|
1060
|
+
ret->genus.assign(ret->ncc, 0);
|
|
1061
|
+
return cacheCob(ret);
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
template <typename MidBuffer, typename DotBuffer>
|
|
1065
|
+
static void mergeRet(SmallVec& retcc, MidBuffer& mid, DotBuffer& rdots, int from, int to) {
|
|
1066
|
+
if (from == to) return;
|
|
1067
|
+
for (size_t i = 0; i < retcc.size(); ++i) if (retcc[i] == from) retcc[i] = static_cast<Small>(to);
|
|
1068
|
+
for (size_t i = 0; i < mid.size(); ++i) if (mid[i] == from) mid[i] = to;
|
|
1069
|
+
if (from >= 0 && to >= 0) rdots[to] += rdots[from];
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
CobPtr Cobordism::composeVerticalPtr(const Cobordism* cc) const {
|
|
1073
|
+
KH_PROFILE(cobComposeVertical);
|
|
1074
|
+
if (!top->equals(*cc->bottom)) throw std::runtime_error("vertical cobordism source mismatch");
|
|
1075
|
+
reverseMaps();
|
|
1076
|
+
cc->reverseMaps();
|
|
1077
|
+
CobPtr ret = makeCob(cc->top, bottom);
|
|
1078
|
+
ret->hpower = hpower + cc->hpower;
|
|
1079
|
+
ret->connectedComponent = countingSmall(ret->nbc);
|
|
1080
|
+
WorkInts midConComp(top->ncycles);
|
|
1081
|
+
for (int i = 0; i < top->ncycles; ++i) midConComp[i] = -2 - i;
|
|
1082
|
+
WorkInts rdots(ncc + cc->ncc + ret->nbc + top->ncycles + 4, 0);
|
|
1083
|
+
WorkInts mdots(ncc + cc->ncc + ret->nbc + top->ncycles + 4, 0);
|
|
1084
|
+
WorkInts udots(ncc + cc->ncc + ret->nbc + 4, 0);
|
|
1085
|
+
WorkInts ugenus(ncc + cc->ncc + ret->nbc + 4, 0);
|
|
1086
|
+
int unconnected = 0;
|
|
1087
|
+
|
|
1088
|
+
for (int i = 0; i < ncc; ++i) {
|
|
1089
|
+
int reti = -1;
|
|
1090
|
+
for (int jj = boundaryBegin(i); jj < boundaryEnd(i); ++jj) {
|
|
1091
|
+
int k = boundaryAt(jj);
|
|
1092
|
+
if (k < offtop) {
|
|
1093
|
+
for (int l = edgeBegin(k); l < edgeEnd(k); ++l) {
|
|
1094
|
+
reti = ret->connectedComponent[ret->component[edgeAt(l)]];
|
|
1095
|
+
if (reti >= 0) break;
|
|
1096
|
+
}
|
|
1097
|
+
} else if (k < offbot) {
|
|
1098
|
+
reti = midConComp[k - offtop];
|
|
1099
|
+
} else {
|
|
1100
|
+
reti = ret->connectedComponent[k - offbot + ret->offbot];
|
|
1101
|
+
}
|
|
1102
|
+
if (reti >= 0) break;
|
|
1103
|
+
}
|
|
1104
|
+
if (reti == -1) {
|
|
1105
|
+
ugenus[unconnected] = genus[i];
|
|
1106
|
+
udots[unconnected++] = dots[i];
|
|
1107
|
+
continue;
|
|
1108
|
+
} else if (reti >= 0) rdots[reti] += dots[i];
|
|
1109
|
+
else mdots[-2 - reti] += dots[i];
|
|
1110
|
+
|
|
1111
|
+
for (int jj = boundaryBegin(i); jj < boundaryEnd(i); ++jj) {
|
|
1112
|
+
int bc = boundaryAt(jj);
|
|
1113
|
+
if (bc < offtop) {
|
|
1114
|
+
for (int kk = edgeBegin(bc); kk < edgeEnd(bc); ++kk) {
|
|
1115
|
+
int test = ret->connectedComponent[ret->component[edgeAt(kk)]];
|
|
1116
|
+
if (test != reti) mergeRet(ret->connectedComponent, midConComp, rdots, test, reti);
|
|
1117
|
+
}
|
|
1118
|
+
} else if (bc < offbot) {
|
|
1119
|
+
int mtest = midConComp[bc - offtop];
|
|
1120
|
+
if (mtest != reti) {
|
|
1121
|
+
if (mtest >= 0) mergeRet(ret->connectedComponent, midConComp, rdots, mtest, reti);
|
|
1122
|
+
else for (size_t kk = 0; kk < midConComp.size(); ++kk) if (midConComp[kk] == mtest) midConComp[kk] = reti;
|
|
1123
|
+
if (reti >= 0) rdots[reti] += (mtest >= 0 ? 0 : mdots[-2 - mtest]);
|
|
1124
|
+
else if (mtest >= 0) mdots[-2 - reti] += rdots[mtest];
|
|
1125
|
+
else mdots[-2 - reti] += mdots[-2 - mtest];
|
|
1126
|
+
}
|
|
1127
|
+
} else {
|
|
1128
|
+
int rtest = ret->connectedComponent[bc - offbot + ret->offbot];
|
|
1129
|
+
if (rtest != reti) mergeRet(ret->connectedComponent, midConComp, rdots, rtest, reti);
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
for (int i = 0; i < cc->ncc; ++i) {
|
|
1135
|
+
int reti = -1;
|
|
1136
|
+
for (int jj = cc->boundaryBegin(i); jj < cc->boundaryEnd(i); ++jj) {
|
|
1137
|
+
int k = cc->boundaryAt(jj);
|
|
1138
|
+
if (k < cc->offtop) {
|
|
1139
|
+
for (int l = cc->edgeBegin(k); l < cc->edgeEnd(k); ++l) {
|
|
1140
|
+
reti = ret->connectedComponent[ret->component[cc->edgeAt(l)]];
|
|
1141
|
+
if (reti >= 0) break;
|
|
1142
|
+
}
|
|
1143
|
+
} else if (k < cc->offbot) {
|
|
1144
|
+
reti = ret->connectedComponent[k - cc->offtop + ret->offtop];
|
|
1145
|
+
} else {
|
|
1146
|
+
reti = midConComp[k - cc->offbot];
|
|
1147
|
+
}
|
|
1148
|
+
if (reti >= 0) break;
|
|
1149
|
+
}
|
|
1150
|
+
if (reti == -1) {
|
|
1151
|
+
ugenus[unconnected] = cc->genus[i];
|
|
1152
|
+
udots[unconnected++] = cc->dots[i];
|
|
1153
|
+
continue;
|
|
1154
|
+
} else if (reti >= 0) rdots[reti] += cc->dots[i];
|
|
1155
|
+
else mdots[-2 - reti] += cc->dots[i];
|
|
1156
|
+
|
|
1157
|
+
for (int jj = cc->boundaryBegin(i); jj < cc->boundaryEnd(i); ++jj) {
|
|
1158
|
+
int bc = cc->boundaryAt(jj);
|
|
1159
|
+
if (bc < cc->offtop) {
|
|
1160
|
+
for (int kk = cc->edgeBegin(bc); kk < cc->edgeEnd(bc); ++kk) {
|
|
1161
|
+
int test = ret->connectedComponent[ret->component[cc->edgeAt(kk)]];
|
|
1162
|
+
if (test != reti) mergeRet(ret->connectedComponent, midConComp, rdots, test, reti);
|
|
1163
|
+
}
|
|
1164
|
+
} else if (bc < cc->offbot) {
|
|
1165
|
+
int rtest = ret->connectedComponent[bc - cc->offtop + ret->offtop];
|
|
1166
|
+
if (rtest != reti) mergeRet(ret->connectedComponent, midConComp, rdots, rtest, reti);
|
|
1167
|
+
} else {
|
|
1168
|
+
int mtest = midConComp[bc - cc->offbot];
|
|
1169
|
+
if (mtest != reti) {
|
|
1170
|
+
if (mtest >= 0) mergeRet(ret->connectedComponent, midConComp, rdots, mtest, reti);
|
|
1171
|
+
else for (size_t kk = 0; kk < midConComp.size(); ++kk) if (midConComp[kk] == mtest) midConComp[kk] = reti;
|
|
1172
|
+
if (reti >= 0) rdots[reti] += (mtest >= 0 ? 0 : mdots[-2 - mtest]);
|
|
1173
|
+
else if (mtest >= 0) mdots[-2 - reti] += rdots[mtest];
|
|
1174
|
+
else mdots[-2 - reti] += mdots[-2 - mtest];
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
ret->ncc = 0;
|
|
1181
|
+
for (int i = 0; i < ret->nbc; ++i) {
|
|
1182
|
+
if (ret->connectedComponent[i] > ret->ncc) {
|
|
1183
|
+
int old = ret->connectedComponent[i];
|
|
1184
|
+
for (int k = i; k < ret->nbc; ++k) {
|
|
1185
|
+
if (ret->connectedComponent[k] == old) ret->connectedComponent[k] = ret->ncc;
|
|
1186
|
+
else if (ret->connectedComponent[k] == ret->ncc) ret->connectedComponent[k] = old;
|
|
1187
|
+
}
|
|
1188
|
+
for (size_t k = 0; k < midConComp.size(); ++k) {
|
|
1189
|
+
if (midConComp[k] == old) midConComp[k] = ret->ncc;
|
|
1190
|
+
else if (midConComp[k] == ret->ncc) midConComp[k] = old;
|
|
1191
|
+
}
|
|
1192
|
+
std::swap(rdots[ret->ncc], rdots[old]);
|
|
1193
|
+
ret->ncc++;
|
|
1194
|
+
} else if (ret->connectedComponent[i] == ret->ncc) {
|
|
1195
|
+
ret->ncc++;
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
ret->reverseMaps();
|
|
1200
|
+
WorkInts rgenus(ret->ncc, 0);
|
|
1201
|
+
for (int i = 0; i < ret->ncc; ++i) {
|
|
1202
|
+
int b = ret->boundaryCount(i);
|
|
1203
|
+
int x = 0;
|
|
1204
|
+
for (int j = 0; j < ncc; ++j) {
|
|
1205
|
+
bool used = false;
|
|
1206
|
+
for (int kk = boundaryBegin(j); kk < boundaryEnd(j) && !used; ++kk) {
|
|
1207
|
+
int bc = boundaryAt(kk);
|
|
1208
|
+
if (bc < offtop) {
|
|
1209
|
+
for (int l = edgeBegin(bc); l < edgeEnd(bc); ++l)
|
|
1210
|
+
if (ret->connectedComponent[ret->component[edgeAt(l)]] == i) { used = true; break; }
|
|
1211
|
+
} else if (bc < offbot) used = midConComp[bc - offtop] == i;
|
|
1212
|
+
else used = ret->connectedComponent[bc - offbot + ret->offbot] == i;
|
|
1213
|
+
}
|
|
1214
|
+
if (used) {
|
|
1215
|
+
x += 2 - 2 * genus[j] - boundaryCount(j);
|
|
1216
|
+
int njoins = 0;
|
|
1217
|
+
for (int l = boundaryBegin(j); l < boundaryEnd(j); ++l) {
|
|
1218
|
+
int bc = boundaryAt(l);
|
|
1219
|
+
if (bc < offtop) njoins += edgeCount(bc);
|
|
1220
|
+
else break;
|
|
1221
|
+
}
|
|
1222
|
+
x -= njoins / 2;
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
for (int j = 0; j < cc->ncc; ++j) {
|
|
1226
|
+
bool used = false;
|
|
1227
|
+
for (int kk = cc->boundaryBegin(j); kk < cc->boundaryEnd(j) && !used; ++kk) {
|
|
1228
|
+
int bc = cc->boundaryAt(kk);
|
|
1229
|
+
if (bc < cc->offtop) {
|
|
1230
|
+
for (int l = cc->edgeBegin(bc); l < cc->edgeEnd(bc); ++l)
|
|
1231
|
+
if (ret->connectedComponent[ret->component[cc->edgeAt(l)]] == i) { used = true; break; }
|
|
1232
|
+
} else if (bc < cc->offbot) used = ret->connectedComponent[bc - cc->offtop + ret->offtop] == i;
|
|
1233
|
+
else used = midConComp[bc - cc->offbot] == i;
|
|
1234
|
+
}
|
|
1235
|
+
if (used) x += 2 - 2 * cc->genus[j] - cc->boundaryCount(j);
|
|
1236
|
+
}
|
|
1237
|
+
int g = 2 - b - x;
|
|
1238
|
+
if (g % 2 != 0 || g < 0) throw std::runtime_error("invalid vertical genus");
|
|
1239
|
+
rgenus[i] = g / 2;
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
for (int i = 0; i < static_cast<int>(midConComp.size()); ++i) {
|
|
1243
|
+
bool found = false;
|
|
1244
|
+
int g = 1;
|
|
1245
|
+
for (size_t j = 0; j < midConComp.size(); ++j) if (midConComp[j] == -2 - i) { found = true; g++; }
|
|
1246
|
+
if (found) {
|
|
1247
|
+
for (int j = 0; j < ncc; ++j)
|
|
1248
|
+
for (int k = offtop; k < offbot; ++k)
|
|
1249
|
+
if (midConComp[k - offtop] == -2 - i && connectedComponent[k] == j) { g += genus[j] - 1; break; }
|
|
1250
|
+
for (int j = 0; j < cc->ncc; ++j)
|
|
1251
|
+
for (int k = cc->offbot; k < cc->nbc; ++k)
|
|
1252
|
+
if (midConComp[k - cc->offbot] == -2 - i && cc->connectedComponent[k] == j) { g += cc->genus[j] - 1; break; }
|
|
1253
|
+
ugenus[unconnected] = g;
|
|
1254
|
+
udots[unconnected++] = mdots[i];
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
int rncc = ret->ncc;
|
|
1259
|
+
ret->ncc += unconnected;
|
|
1260
|
+
ret->dots.assign(ret->ncc, 0);
|
|
1261
|
+
ret->genus.assign(ret->ncc, 0);
|
|
1262
|
+
for (int i = 0; i < rncc; ++i) {
|
|
1263
|
+
ret->dots[i] = rdots[i];
|
|
1264
|
+
ret->genus[i] = rgenus[i];
|
|
1265
|
+
}
|
|
1266
|
+
for (int i = 0; i < unconnected; ++i) {
|
|
1267
|
+
ret->dots[rncc + i] = udots[i];
|
|
1268
|
+
ret->genus[rncc + i] = ugenus[i];
|
|
1269
|
+
}
|
|
1270
|
+
if (unconnected > 0) ret->mapsReady = false;
|
|
1271
|
+
return cacheCob(ret);
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
CobPtr Cobordism::composeHorizontal(int start, const CobPtr& cc, int cstart, int nc) const {
|
|
1275
|
+
KH_PROFILE(cobComposeHorizontal);
|
|
1276
|
+
std::vector<int> tjoins(nc), bjoins(nc);
|
|
1277
|
+
CapPtr rtop = top->compose(start, cc->top, cstart, nc, &tjoins);
|
|
1278
|
+
CapPtr rbot = bottom->compose(start, cc->bottom, cstart, nc, &bjoins);
|
|
1279
|
+
CobPtr ret = makeCob(rtop, rbot);
|
|
1280
|
+
ret->hpower = hpower + cc->hpower;
|
|
1281
|
+
ret->connectedComponent = countingSmall(ret->nbc);
|
|
1282
|
+
WorkInts midConComp(nc);
|
|
1283
|
+
for (int i = 0; i < nc; ++i) midConComp[i] = -2 - i;
|
|
1284
|
+
WorkInts rdots(ret->nbc + nc + 2, 0), mdots(nc, 0), udots(ncc + cc->ncc + 2, 0), ugenus(ncc + cc->ncc + 2, 0);
|
|
1285
|
+
int unconnected = 0;
|
|
1286
|
+
|
|
1287
|
+
for (int i = 0; i < ncc; ++i) {
|
|
1288
|
+
int reti = -1;
|
|
1289
|
+
for (int j = offtop; j < nbc; ++j) if (connectedComponent[j] == i) {
|
|
1290
|
+
int retj = j < offbot ? j - offtop + ret->offtop : j - offbot + ret->offbot;
|
|
1291
|
+
reti = ret->connectedComponent[retj];
|
|
1292
|
+
break;
|
|
1293
|
+
}
|
|
1294
|
+
if (reti == -1) {
|
|
1295
|
+
for (int j = 0; j < n; ++j) {
|
|
1296
|
+
int thisj = (j + start + nc) % n;
|
|
1297
|
+
if (connectedComponent[component[thisj]] == i) {
|
|
1298
|
+
reti = j < n - nc ? ret->connectedComponent[ret->component[j]] : midConComp[j - n + nc];
|
|
1299
|
+
if (reti >= 0) break;
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
if (reti >= 0) rdots[reti] += dots[i];
|
|
1304
|
+
else if (reti < -1) mdots[-2 - reti] += dots[i];
|
|
1305
|
+
if (reti != -1) {
|
|
1306
|
+
for (int j = 0; j < offtop; ++j) if (connectedComponent[j] == i) {
|
|
1307
|
+
for (int k = 0; k < n; ++k) if (component[k] == j) {
|
|
1308
|
+
int retk = (k - start - nc + 2 * n) % n;
|
|
1309
|
+
int tmp = retk < n - nc ? ret->connectedComponent[ret->component[retk]] : midConComp[retk - n + nc];
|
|
1310
|
+
if (tmp != reti) {
|
|
1311
|
+
if (tmp >= 0) mergeRet(ret->connectedComponent, midConComp, rdots, tmp, reti);
|
|
1312
|
+
else for (int l = 0; l < nc; ++l) if (midConComp[l] == tmp) midConComp[l] = reti;
|
|
1313
|
+
if (tmp >= 0) {
|
|
1314
|
+
if (reti < 0) mdots[-2 - reti] += rdots[tmp];
|
|
1315
|
+
} else if (reti >= 0) rdots[reti] += mdots[-2 - tmp];
|
|
1316
|
+
else mdots[-2 - reti] += mdots[-2 - tmp];
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
if (reti >= 0) {
|
|
1322
|
+
for (int j = offtop; j < nbc; ++j) if (connectedComponent[j] == i) {
|
|
1323
|
+
int retj = j < offbot ? j - offtop + ret->offtop : j - offbot + ret->offbot;
|
|
1324
|
+
if (ret->connectedComponent[retj] != reti) {
|
|
1325
|
+
int tmp = ret->connectedComponent[retj];
|
|
1326
|
+
mergeRet(ret->connectedComponent, midConComp, rdots, tmp, reti);
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
if (reti == -1) {
|
|
1331
|
+
ugenus[unconnected] = genus[i];
|
|
1332
|
+
udots[unconnected++] = dots[i];
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
for (int i = 0; i < cc->ncc; ++i) {
|
|
1337
|
+
int reti = -1;
|
|
1338
|
+
for (int j = cc->offtop; j < cc->nbc; ++j) if (cc->connectedComponent[j] == i) {
|
|
1339
|
+
int retj = j < cc->offbot ? j - cc->offtop + offbot - offtop + ret->offtop : j - cc->offbot + nbc - offbot + ret->offbot;
|
|
1340
|
+
reti = ret->connectedComponent[retj];
|
|
1341
|
+
break;
|
|
1342
|
+
}
|
|
1343
|
+
if (reti == -1) {
|
|
1344
|
+
for (int j = 0; j < cc->n; ++j) {
|
|
1345
|
+
int thisj = (j + cstart + nc) % cc->n;
|
|
1346
|
+
if (cc->connectedComponent[cc->component[thisj]] == i) {
|
|
1347
|
+
reti = j < cc->n - nc ? ret->connectedComponent[ret->component[j + n - nc]] : midConComp[cc->n - j - 1];
|
|
1348
|
+
if (reti >= 0) break;
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
if (reti >= 0) rdots[reti] += cc->dots[i];
|
|
1353
|
+
else if (reti < -1) mdots[-2 - reti] += cc->dots[i];
|
|
1354
|
+
if (reti != -1) {
|
|
1355
|
+
for (int j = 0; j < cc->offtop; ++j) if (cc->connectedComponent[j] == i) {
|
|
1356
|
+
for (int k = 0; k < cc->n; ++k) if (cc->component[k] == j) {
|
|
1357
|
+
int retk = (k - cstart - nc + 2 * cc->n) % cc->n;
|
|
1358
|
+
int tmp = retk < cc->n - nc ? ret->connectedComponent[ret->component[retk + n - nc]] : midConComp[cc->n - retk - 1];
|
|
1359
|
+
if (tmp != reti) {
|
|
1360
|
+
if (tmp >= 0) mergeRet(ret->connectedComponent, midConComp, rdots, tmp, reti);
|
|
1361
|
+
else for (int l = 0; l < nc; ++l) if (midConComp[l] == tmp) midConComp[l] = reti;
|
|
1362
|
+
if (tmp >= 0) {
|
|
1363
|
+
if (reti < 0) mdots[-2 - reti] += rdots[tmp];
|
|
1364
|
+
} else if (reti >= 0) rdots[reti] += mdots[-2 - tmp];
|
|
1365
|
+
else mdots[-2 - reti] += mdots[-2 - tmp];
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
if (reti >= 0) {
|
|
1371
|
+
for (int j = cc->offtop; j < cc->nbc; ++j) if (cc->connectedComponent[j] == i) {
|
|
1372
|
+
int retj = j < cc->offbot ? j - cc->offtop + offbot - offtop + ret->offtop : j - cc->offbot + (nbc - offbot) + ret->offbot;
|
|
1373
|
+
if (ret->connectedComponent[retj] != reti) {
|
|
1374
|
+
int tmp = ret->connectedComponent[retj];
|
|
1375
|
+
mergeRet(ret->connectedComponent, midConComp, rdots, tmp, reti);
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
if (reti == -1) {
|
|
1380
|
+
ugenus[unconnected] = cc->genus[i];
|
|
1381
|
+
udots[unconnected++] = cc->dots[i];
|
|
1382
|
+
}
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
for (int i = 0; i < nc; ++i) {
|
|
1386
|
+
bool found = false;
|
|
1387
|
+
for (int j = 0; j < nc; ++j) if (tjoins[j] == i) {
|
|
1388
|
+
int reti = ret->offbot - 1 - i;
|
|
1389
|
+
if (midConComp[j] >= 0) ret->connectedComponent[reti] = midConComp[j];
|
|
1390
|
+
else {
|
|
1391
|
+
ret->connectedComponent[reti] = ret->nbc - midConComp[j];
|
|
1392
|
+
rdots[ret->nbc - midConComp[j]] = mdots[-2 - midConComp[j]];
|
|
1393
|
+
midConComp[j] = ret->nbc - midConComp[j];
|
|
1394
|
+
}
|
|
1395
|
+
found = true;
|
|
1396
|
+
break;
|
|
1397
|
+
}
|
|
1398
|
+
if (!found) break;
|
|
1399
|
+
}
|
|
1400
|
+
for (int i = 0; i < nc; ++i) {
|
|
1401
|
+
bool found = false;
|
|
1402
|
+
for (int j = 0; j < nc; ++j) if (bjoins[j] == i) {
|
|
1403
|
+
int reti = ret->nbc - 1 - i;
|
|
1404
|
+
if (midConComp[j] >= 0) ret->connectedComponent[reti] = midConComp[j];
|
|
1405
|
+
else {
|
|
1406
|
+
ret->connectedComponent[reti] = ret->nbc - midConComp[j];
|
|
1407
|
+
rdots[ret->nbc - midConComp[j]] = mdots[-2 - midConComp[j]];
|
|
1408
|
+
midConComp[j] = ret->nbc - midConComp[j];
|
|
1409
|
+
}
|
|
1410
|
+
found = true;
|
|
1411
|
+
break;
|
|
1412
|
+
}
|
|
1413
|
+
if (!found) break;
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
WorkInts rgenus(ret->nbc + nc + 2, 0);
|
|
1417
|
+
for (int i = 0; i < static_cast<int>(rgenus.size()); ++i) {
|
|
1418
|
+
int b = 0;
|
|
1419
|
+
for (int j = 0; j < ret->nbc; ++j) if (ret->connectedComponent[j] == i) ++b;
|
|
1420
|
+
if (b == 0) continue;
|
|
1421
|
+
int x = 0;
|
|
1422
|
+
for (int j = 0; j < ncc; ++j) {
|
|
1423
|
+
bool used = false;
|
|
1424
|
+
for (int k = 0; k < nbc && !used; ++k) if (connectedComponent[k] == j) {
|
|
1425
|
+
if (k < offtop) {
|
|
1426
|
+
for (int l = 0; l < n; ++l) if (component[l] == k) {
|
|
1427
|
+
int retl = (l - start - nc + 2 * n) % n;
|
|
1428
|
+
if (retl < n - nc) used = ret->connectedComponent[ret->component[retl]] == i;
|
|
1429
|
+
else used = midConComp[retl - n + nc] == i;
|
|
1430
|
+
if (used) break;
|
|
1431
|
+
}
|
|
1432
|
+
} else if (k < offbot) used = ret->connectedComponent[k - offtop + ret->offtop] == i;
|
|
1433
|
+
else used = ret->connectedComponent[k - offbot + ret->offbot] == i;
|
|
1434
|
+
}
|
|
1435
|
+
if (used) {
|
|
1436
|
+
x += 2 - 2 * genus[j];
|
|
1437
|
+
for (int l = 0; l < nbc; ++l) if (connectedComponent[l] == j) --x;
|
|
1438
|
+
int njoins = 0;
|
|
1439
|
+
for (int l = 0; l < nc; ++l) if (connectedComponent[component[(l + start) % n]] == j) ++njoins;
|
|
1440
|
+
x -= njoins;
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
for (int j = 0; j < cc->ncc; ++j) {
|
|
1444
|
+
bool used = false;
|
|
1445
|
+
for (int k = 0; k < cc->nbc && !used; ++k) if (cc->connectedComponent[k] == j) {
|
|
1446
|
+
if (k < cc->offtop) {
|
|
1447
|
+
for (int l = 0; l < cc->n; ++l) if (cc->component[l] == k) {
|
|
1448
|
+
int retl = (l - cstart - nc + 2 * cc->n) % cc->n;
|
|
1449
|
+
if (retl < cc->n - nc) used = ret->connectedComponent[ret->component[retl + n - nc]] == i;
|
|
1450
|
+
else used = midConComp[cc->n - retl - 1] == i;
|
|
1451
|
+
if (used) break;
|
|
1452
|
+
}
|
|
1453
|
+
} else if (k < cc->offbot) used = ret->connectedComponent[k - cc->offtop + offbot - offtop + ret->offtop] == i;
|
|
1454
|
+
else used = ret->connectedComponent[k - cc->offbot + nbc - offbot + ret->offbot] == i;
|
|
1455
|
+
}
|
|
1456
|
+
if (used) {
|
|
1457
|
+
x += 2 - 2 * cc->genus[j];
|
|
1458
|
+
for (int l = 0; l < cc->nbc; ++l) if (cc->connectedComponent[l] == j) --x;
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1461
|
+
int g = 2 - b - x;
|
|
1462
|
+
if (g % 2 != 0 || g < 0) throw std::runtime_error("invalid horizontal genus");
|
|
1463
|
+
rgenus[i] = g / 2;
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
ret->ncc = 0;
|
|
1467
|
+
for (int i = 0; i < ret->nbc; ++i) {
|
|
1468
|
+
if (ret->connectedComponent[i] > ret->ncc) {
|
|
1469
|
+
int old = ret->connectedComponent[i];
|
|
1470
|
+
for (int k = i; k < ret->nbc; ++k) {
|
|
1471
|
+
if (ret->connectedComponent[k] == old) ret->connectedComponent[k] = ret->ncc;
|
|
1472
|
+
else if (ret->connectedComponent[k] == ret->ncc) ret->connectedComponent[k] = old;
|
|
1473
|
+
}
|
|
1474
|
+
std::swap(rdots[ret->ncc], rdots[old]);
|
|
1475
|
+
std::swap(rgenus[ret->ncc], rgenus[old]);
|
|
1476
|
+
ret->ncc++;
|
|
1477
|
+
} else if (ret->connectedComponent[i] == ret->ncc) ret->ncc++;
|
|
1478
|
+
}
|
|
1479
|
+
std::vector<std::pair<int,int> > sortarr;
|
|
1480
|
+
for (int i = 0; i < unconnected; ++i) sortarr.push_back(std::make_pair(ugenus[i], udots[i]));
|
|
1481
|
+
std::sort(sortarr.begin(), sortarr.end());
|
|
1482
|
+
int rncc = ret->ncc;
|
|
1483
|
+
ret->ncc += unconnected;
|
|
1484
|
+
ret->dots.assign(ret->ncc, 0);
|
|
1485
|
+
ret->genus.assign(ret->ncc, 0);
|
|
1486
|
+
for (int i = 0; i < rncc; ++i) {
|
|
1487
|
+
ret->dots[i] = rdots[i];
|
|
1488
|
+
ret->genus[i] = rgenus[i];
|
|
1489
|
+
}
|
|
1490
|
+
for (int i = 0; i < unconnected; ++i) {
|
|
1491
|
+
ret->genus[rncc + i] = sortarr[i].first;
|
|
1492
|
+
ret->dots[rncc + i] = sortarr[i].second;
|
|
1493
|
+
}
|
|
1494
|
+
return cacheCob(ret);
|
|
1495
|
+
}
|
|
1496
|
+
|
|
1497
|
+
struct TermList {
|
|
1498
|
+
typedef std::pair<CobPtr, BigInt> Term;
|
|
1499
|
+
size_t n = 0;
|
|
1500
|
+
Term first;
|
|
1501
|
+
std::vector<Term, ArenaAllocator<Term> > rest;
|
|
1502
|
+
|
|
1503
|
+
bool empty() const { return n == 0; }
|
|
1504
|
+
size_t size() const { return n; }
|
|
1505
|
+
Term& front() { return first; }
|
|
1506
|
+
const Term& front() const { return first; }
|
|
1507
|
+
|
|
1508
|
+
Term& operator[](size_t i) {
|
|
1509
|
+
return i == 0 ? first : rest[i - 1];
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1512
|
+
const Term& operator[](size_t i) const {
|
|
1513
|
+
return i == 0 ? first : rest[i - 1];
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
void reserve(size_t count) {
|
|
1517
|
+
if (count > 1) rest.reserve(count - 1);
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
void push_back(const Term& term) {
|
|
1521
|
+
if (n == 0) first = term;
|
|
1522
|
+
else rest.push_back(term);
|
|
1523
|
+
++n;
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1526
|
+
void push_back(Term&& term) {
|
|
1527
|
+
if (n == 0) first = std::move(term);
|
|
1528
|
+
else rest.push_back(std::move(term));
|
|
1529
|
+
++n;
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1532
|
+
void eraseIndex(size_t i) {
|
|
1533
|
+
if (i >= n) return;
|
|
1534
|
+
if (n == 1) {
|
|
1535
|
+
first = Term();
|
|
1536
|
+
n = 0;
|
|
1537
|
+
rest.clear();
|
|
1538
|
+
return;
|
|
1539
|
+
}
|
|
1540
|
+
if (i == 0) {
|
|
1541
|
+
first = std::move(rest.front());
|
|
1542
|
+
rest.erase(rest.begin());
|
|
1543
|
+
} else {
|
|
1544
|
+
rest.erase(rest.begin() + static_cast<std::ptrdiff_t>(i - 1));
|
|
1545
|
+
}
|
|
1546
|
+
--n;
|
|
1547
|
+
}
|
|
1548
|
+
};
|
|
1549
|
+
|
|
1550
|
+
struct LCCC {
|
|
1551
|
+
TermList terms;
|
|
1552
|
+
bool alreadyReduced = false;
|
|
1553
|
+
|
|
1554
|
+
LCCC() = default;
|
|
1555
|
+
LCCC(const CobPtr& cc, const BigInt& c) { addTerm(cc, c); }
|
|
1556
|
+
bool isZero() const { return terms.empty(); }
|
|
1557
|
+
int numberOfTerms() const { return static_cast<int>(terms.size()); }
|
|
1558
|
+
CobPtr firstTerm() const { return terms.front().first; }
|
|
1559
|
+
BigInt firstCoefficient() const { return terms.front().second; }
|
|
1560
|
+
CapPtr source() const { return firstTerm()->top; }
|
|
1561
|
+
CapPtr target() const { return firstTerm()->bottom; }
|
|
1562
|
+
|
|
1563
|
+
void addTerm(const CobPtr& cc, const BigInt& c) {
|
|
1564
|
+
alreadyReduced = false;
|
|
1565
|
+
if (c.isZero()) return;
|
|
1566
|
+
for (size_t i = 0; i < terms.size(); ++i) {
|
|
1567
|
+
if (terms[i].first.get() == cc.get()) {
|
|
1568
|
+
BigInt n = terms[i].second + c;
|
|
1569
|
+
if (n.isZero()) terms.eraseIndex(i);
|
|
1570
|
+
else terms[i].second = n;
|
|
1571
|
+
return;
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1574
|
+
uint64_t hash = cc->hash();
|
|
1575
|
+
for (size_t i = 0; i < terms.size(); ++i) {
|
|
1576
|
+
if (terms[i].first->hash() == hash && terms[i].first->equals(*cc)) {
|
|
1577
|
+
BigInt n = terms[i].second + c;
|
|
1578
|
+
if (n.isZero()) terms.eraseIndex(i);
|
|
1579
|
+
else terms[i].second = n;
|
|
1580
|
+
return;
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1583
|
+
terms.push_back(std::make_pair(cc, c));
|
|
1584
|
+
}
|
|
1585
|
+
|
|
1586
|
+
void add(const LCCC& other) {
|
|
1587
|
+
alreadyReduced = false;
|
|
1588
|
+
for (size_t i = 0; i < other.terms.size(); ++i) addTerm(other.terms[i].first, other.terms[i].second);
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1591
|
+
static LCCC composeCobLeft(const Cobordism& left, const LCCC& other) {
|
|
1592
|
+
LCCC ret;
|
|
1593
|
+
if (other.isZero()) return ret;
|
|
1594
|
+
if (other.terms.size() == 1) {
|
|
1595
|
+
ret.terms.push_back(std::make_pair(left.composeVerticalPtr(other.terms[0].first.get()), other.terms[0].second));
|
|
1596
|
+
return ret;
|
|
1597
|
+
}
|
|
1598
|
+
ret.terms.reserve(other.terms.size());
|
|
1599
|
+
for (size_t i = 0; i < other.terms.size(); ++i) {
|
|
1600
|
+
ret.addTerm(left.composeVerticalPtr(other.terms[i].first.get()), other.terms[i].second);
|
|
1601
|
+
}
|
|
1602
|
+
return ret;
|
|
1603
|
+
}
|
|
1604
|
+
|
|
1605
|
+
static LCCC composeCobRight(const LCCC& left, const Cobordism& right) {
|
|
1606
|
+
LCCC ret;
|
|
1607
|
+
if (left.isZero()) return ret;
|
|
1608
|
+
if (left.terms.size() == 1) {
|
|
1609
|
+
ret.terms.push_back(std::make_pair(left.terms[0].first->composeVerticalPtr(&right), left.terms[0].second));
|
|
1610
|
+
return ret;
|
|
1611
|
+
}
|
|
1612
|
+
ret.terms.reserve(left.terms.size());
|
|
1613
|
+
for (size_t i = 0; i < left.terms.size(); ++i) {
|
|
1614
|
+
ret.addTerm(left.terms[i].first->composeVerticalPtr(&right), left.terms[i].second);
|
|
1615
|
+
}
|
|
1616
|
+
return ret;
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1619
|
+
LCCC multiplied(const BigInt& c) const {
|
|
1620
|
+
LCCC r;
|
|
1621
|
+
if (c.isZero()) return r;
|
|
1622
|
+
r.terms.reserve(terms.size());
|
|
1623
|
+
for (size_t i = 0; i < terms.size(); ++i) r.terms.push_back(std::make_pair(terms[i].first, coeffProduct(terms[i].second, c)));
|
|
1624
|
+
r.alreadyReduced = alreadyReduced;
|
|
1625
|
+
return r;
|
|
1626
|
+
}
|
|
1627
|
+
|
|
1628
|
+
void multiplyInPlace(const BigInt& c) {
|
|
1629
|
+
if (c == BI_ONE) return;
|
|
1630
|
+
alreadyReduced = false;
|
|
1631
|
+
if (c == BI_MINUS_ONE) {
|
|
1632
|
+
for (size_t i = 0; i < terms.size(); ++i) terms[i].second = -terms[i].second;
|
|
1633
|
+
} else {
|
|
1634
|
+
for (size_t i = 0; i < terms.size(); ++i) terms[i].second = coeffProduct(terms[i].second, c);
|
|
1635
|
+
}
|
|
1636
|
+
}
|
|
1637
|
+
|
|
1638
|
+
LCCC composeVertical(const LCCC& other) const {
|
|
1639
|
+
KH_PROFILE(lcccComposeVertical);
|
|
1640
|
+
LCCC ret;
|
|
1641
|
+
if (isZero() || other.isZero()) return ret;
|
|
1642
|
+
if (terms.size() == 1 && other.terms.size() == 1) {
|
|
1643
|
+
BigInt coeff = coeffProduct(terms[0].second, other.terms[0].second);
|
|
1644
|
+
if (!coeff.isZero()) {
|
|
1645
|
+
ret.terms.push_back(std::make_pair(terms[0].first->composeVerticalPtr(other.terms[0].first.get()), coeff));
|
|
1646
|
+
}
|
|
1647
|
+
return ret;
|
|
1648
|
+
}
|
|
1649
|
+
ret.terms.reserve(terms.size() * other.terms.size());
|
|
1650
|
+
for (size_t i = 0; i < terms.size(); ++i) {
|
|
1651
|
+
for (size_t j = 0; j < other.terms.size(); ++j) {
|
|
1652
|
+
ret.addTerm(terms[i].first->composeVerticalPtr(other.terms[j].first.get()), coeffProduct(terms[i].second, other.terms[j].second));
|
|
1653
|
+
}
|
|
1654
|
+
}
|
|
1655
|
+
return ret;
|
|
1656
|
+
}
|
|
1657
|
+
|
|
1658
|
+
LCCC composeHorizontal(int start, const CobPtr& cc, int cstart, int nc, bool reverse) const {
|
|
1659
|
+
KH_PROFILE(lcccComposeHorizontal);
|
|
1660
|
+
LCCC ret;
|
|
1661
|
+
if (terms.size() == 1) {
|
|
1662
|
+
CobPtr composition = reverse ? cc->composeHorizontal(cstart, terms[0].first, start, nc)
|
|
1663
|
+
: terms[0].first->composeHorizontal(start, cc, cstart, nc);
|
|
1664
|
+
ret.terms.push_back(std::make_pair(composition, terms[0].second));
|
|
1665
|
+
return ret;
|
|
1666
|
+
}
|
|
1667
|
+
for (size_t i = 0; i < terms.size(); ++i) {
|
|
1668
|
+
CobPtr composition = reverse ? cc->composeHorizontal(cstart, terms[i].first, start, nc)
|
|
1669
|
+
: terms[i].first->composeHorizontal(start, cc, cstart, nc);
|
|
1670
|
+
ret.addTerm(composition, terms[i].second);
|
|
1671
|
+
}
|
|
1672
|
+
return ret;
|
|
1673
|
+
}
|
|
1674
|
+
|
|
1675
|
+
LCCC reduce() const {
|
|
1676
|
+
KH_PROFILE(lcccReduce);
|
|
1677
|
+
if (isZero()) return LCCC();
|
|
1678
|
+
if (alreadyReduced) return *this;
|
|
1679
|
+
LCCC ret;
|
|
1680
|
+
for (size_t ti = 0; ti < terms.size(); ++ti) {
|
|
1681
|
+
CobPtr cc = terms[ti].first;
|
|
1682
|
+
BigInt num = terms[ti].second;
|
|
1683
|
+
bool canonical = cc->hpower == 0 && cc->ncc == cc->nbc;
|
|
1684
|
+
if (canonical) {
|
|
1685
|
+
for (int i = 0; i < cc->nbc; ++i) {
|
|
1686
|
+
if (cc->connectedComponent[i] != i || cc->genus[i] != 0 || cc->dots[i] > 1) {
|
|
1687
|
+
canonical = false;
|
|
1688
|
+
break;
|
|
1689
|
+
}
|
|
1690
|
+
}
|
|
1691
|
+
if (canonical) {
|
|
1692
|
+
if (ret.isZero()) ret.terms.push_back(std::make_pair(cc, num));
|
|
1693
|
+
else ret.addTerm(cc, num);
|
|
1694
|
+
continue;
|
|
1695
|
+
}
|
|
1696
|
+
}
|
|
1697
|
+
cc->reverseMaps();
|
|
1698
|
+
SmallVec dots(cc->nbc, 0);
|
|
1699
|
+
SmallVec genus(cc->nbc, 0);
|
|
1700
|
+
WorkInts moreWork(cc->ncc);
|
|
1701
|
+
int moreWorkCount = 0;
|
|
1702
|
+
bool kill = false;
|
|
1703
|
+
for (int i = 0; i < cc->ncc; ++i) {
|
|
1704
|
+
int bcCount = cc->boundaryCount(i);
|
|
1705
|
+
if (cc->genus[i] + cc->dots[i] > 1) kill = true;
|
|
1706
|
+
else if (bcCount == 0) {
|
|
1707
|
+
if (cc->genus[i] == 1) num = num.mul_small(2);
|
|
1708
|
+
else if (cc->dots[i] == 0) kill = true;
|
|
1709
|
+
} else if (bcCount == 1) {
|
|
1710
|
+
dots[cc->boundaryAt(cc->boundaryBegin(i))] = static_cast<Small>(cc->dots[i] + cc->genus[i]);
|
|
1711
|
+
if (cc->genus[i] == 1) num = num.mul_small(2);
|
|
1712
|
+
} else {
|
|
1713
|
+
if (cc->genus[i] + cc->dots[i] == 1) {
|
|
1714
|
+
if (cc->genus[i] == 1) num = num.mul_small(2);
|
|
1715
|
+
for (int p = cc->boundaryBegin(i); p < cc->boundaryEnd(i); ++p) dots[cc->boundaryAt(p)] = 1;
|
|
1716
|
+
} else {
|
|
1717
|
+
moreWork[moreWorkCount++] = i;
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
if (kill) break;
|
|
1721
|
+
}
|
|
1722
|
+
if (kill) continue;
|
|
1723
|
+
SmallVec connected = countingSmall(cc->nbc);
|
|
1724
|
+
if (moreWorkCount == 0) {
|
|
1725
|
+
CobPtr newcc = makeCob(cc->top, cc->bottom);
|
|
1726
|
+
newcc->connectedComponent = connected;
|
|
1727
|
+
newcc->ncc = newcc->nbc;
|
|
1728
|
+
newcc->genus = genus;
|
|
1729
|
+
newcc->dots = dots;
|
|
1730
|
+
CobPtr cached = cacheCob(newcc);
|
|
1731
|
+
if (ret.isZero()) ret.terms.push_back(std::make_pair(cached, num));
|
|
1732
|
+
else ret.addTerm(cached, num);
|
|
1733
|
+
continue;
|
|
1734
|
+
}
|
|
1735
|
+
std::vector<SmallVec, ArenaAllocator<SmallVec> > neckCutting;
|
|
1736
|
+
neckCutting.push_back(dots);
|
|
1737
|
+
for (int wi = 0; wi < moreWorkCount; ++wi) {
|
|
1738
|
+
int concomp = moreWork[wi];
|
|
1739
|
+
int nbc = cc->boundaryCount(concomp);
|
|
1740
|
+
std::vector<SmallVec, ArenaAllocator<SmallVec> > next;
|
|
1741
|
+
next.reserve(neckCutting.size() * static_cast<size_t>(nbc));
|
|
1742
|
+
for (size_t j = 0; j < neckCutting.size(); ++j) {
|
|
1743
|
+
SmallVec base = neckCutting[j];
|
|
1744
|
+
for (int p = cc->boundaryBegin(concomp); p < cc->boundaryEnd(concomp); ++p) base[cc->boundaryAt(p)] = 1;
|
|
1745
|
+
for (int k = 0; k < nbc; ++k) {
|
|
1746
|
+
SmallVec variant = base;
|
|
1747
|
+
variant[cc->boundaryAt(cc->boundaryBegin(concomp) + k)] = 0;
|
|
1748
|
+
next.push_back(std::move(variant));
|
|
1749
|
+
}
|
|
1750
|
+
}
|
|
1751
|
+
neckCutting.swap(next);
|
|
1752
|
+
}
|
|
1753
|
+
for (size_t i = 0; i < neckCutting.size(); ++i) {
|
|
1754
|
+
CobPtr newcc = makeCob(cc->top, cc->bottom);
|
|
1755
|
+
newcc->connectedComponent = connected;
|
|
1756
|
+
newcc->ncc = newcc->nbc;
|
|
1757
|
+
newcc->genus = genus;
|
|
1758
|
+
newcc->dots = neckCutting[i];
|
|
1759
|
+
ret.addTerm(cacheCob(newcc), num);
|
|
1760
|
+
}
|
|
1761
|
+
}
|
|
1762
|
+
ret.alreadyReduced = true;
|
|
1763
|
+
return ret;
|
|
1764
|
+
}
|
|
1765
|
+
};
|
|
1766
|
+
|
|
1767
|
+
struct SmoothingColumn {
|
|
1768
|
+
int n = 0;
|
|
1769
|
+
std::vector<CapPtr> smoothings;
|
|
1770
|
+
std::vector<int> numbers;
|
|
1771
|
+
SmoothingColumn() = default;
|
|
1772
|
+
explicit SmoothingColumn(int nn) : n(nn), smoothings(nn), numbers(nn, 0) {}
|
|
1773
|
+
bool nonNull() const {
|
|
1774
|
+
for (size_t i = 0; i < smoothings.size(); ++i) if (!smoothings[i]) return false;
|
|
1775
|
+
return true;
|
|
1776
|
+
}
|
|
1777
|
+
};
|
|
1778
|
+
|
|
1779
|
+
typedef std::pair<int, LCCC> MatrixEntry;
|
|
1780
|
+
typedef std::vector<MatrixEntry, ArenaAllocator<MatrixEntry> > MatrixRow;
|
|
1781
|
+
|
|
1782
|
+
static MatrixRow::iterator findRowEntry(MatrixRow& row, int column) {
|
|
1783
|
+
return std::lower_bound(row.begin(), row.end(), column,
|
|
1784
|
+
[](const std::pair<int, LCCC>& entry, int key) { return entry.first < key; });
|
|
1785
|
+
}
|
|
1786
|
+
|
|
1787
|
+
static MatrixRow::const_iterator findRowEntry(const MatrixRow& row, int column) {
|
|
1788
|
+
return std::lower_bound(row.begin(), row.end(), column,
|
|
1789
|
+
[](const std::pair<int, LCCC>& entry, int key) { return entry.first < key; });
|
|
1790
|
+
}
|
|
1791
|
+
|
|
1792
|
+
static void putRowEntry(MatrixRow& row, int column, const LCCC& lc) {
|
|
1793
|
+
if (lc.isZero()) return;
|
|
1794
|
+
MatrixRow::iterator it = findRowEntry(row, column);
|
|
1795
|
+
if (it != row.end() && it->first == column) it->second = lc;
|
|
1796
|
+
else row.insert(it, std::make_pair(column, lc));
|
|
1797
|
+
}
|
|
1798
|
+
|
|
1799
|
+
static void addRowEntry(MatrixRow& row, int column, const LCCC& lc) {
|
|
1800
|
+
if (lc.isZero()) return;
|
|
1801
|
+
MatrixRow::iterator it = findRowEntry(row, column);
|
|
1802
|
+
if (it == row.end() || it->first != column) {
|
|
1803
|
+
row.insert(it, std::make_pair(column, lc));
|
|
1804
|
+
} else {
|
|
1805
|
+
it->second.add(lc);
|
|
1806
|
+
if (it->second.isZero()) row.erase(it);
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1810
|
+
static void appendSortedRowEntry(MatrixRow& row, int column, const LCCC& lc) {
|
|
1811
|
+
if (lc.isZero()) return;
|
|
1812
|
+
if (row.empty() || row.back().first < column) row.push_back(std::make_pair(column, lc));
|
|
1813
|
+
else addRowEntry(row, column, lc);
|
|
1814
|
+
}
|
|
1815
|
+
|
|
1816
|
+
static void appendSortedRowEntry(MatrixRow& row, int column, LCCC&& lc) {
|
|
1817
|
+
if (lc.isZero()) return;
|
|
1818
|
+
if (row.empty() || row.back().first < column) row.push_back(std::make_pair(column, std::move(lc)));
|
|
1819
|
+
else addRowEntry(row, column, lc);
|
|
1820
|
+
}
|
|
1821
|
+
|
|
1822
|
+
static void addRowEntry(MatrixRow& row, int column, LCCC&& lc) {
|
|
1823
|
+
if (lc.isZero()) return;
|
|
1824
|
+
MatrixRow::iterator it = findRowEntry(row, column);
|
|
1825
|
+
if (it == row.end() || it->first != column) {
|
|
1826
|
+
row.insert(it, std::make_pair(column, std::move(lc)));
|
|
1827
|
+
} else {
|
|
1828
|
+
it->second.add(lc);
|
|
1829
|
+
if (it->second.isZero()) row.erase(it);
|
|
1830
|
+
}
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1833
|
+
static void addSortedRow(MatrixRow& row, const MatrixRow& other) {
|
|
1834
|
+
if (other.empty()) return;
|
|
1835
|
+
if (row.empty()) {
|
|
1836
|
+
row = other;
|
|
1837
|
+
return;
|
|
1838
|
+
}
|
|
1839
|
+
MatrixRow merged;
|
|
1840
|
+
merged.reserve(row.size() + other.size());
|
|
1841
|
+
size_t i = 0, j = 0;
|
|
1842
|
+
while (i < row.size() || j < other.size()) {
|
|
1843
|
+
if (j == other.size() || (i < row.size() && row[i].first < other[j].first)) {
|
|
1844
|
+
merged.push_back(row[i++]);
|
|
1845
|
+
} else if (i == row.size() || other[j].first < row[i].first) {
|
|
1846
|
+
merged.push_back(other[j++]);
|
|
1847
|
+
} else {
|
|
1848
|
+
LCCC sum = row[i].second;
|
|
1849
|
+
sum.add(other[j].second);
|
|
1850
|
+
if (!sum.isZero()) merged.push_back(std::make_pair(row[i].first, sum));
|
|
1851
|
+
++i;
|
|
1852
|
+
++j;
|
|
1853
|
+
}
|
|
1854
|
+
}
|
|
1855
|
+
row.swap(merged);
|
|
1856
|
+
}
|
|
1857
|
+
|
|
1858
|
+
static void decrementIndexesAbove(MatrixRow& row, int column) {
|
|
1859
|
+
for (size_t i = 0; i < row.size(); ++i) {
|
|
1860
|
+
if (row[i].first > column) --row[i].first;
|
|
1861
|
+
}
|
|
1862
|
+
}
|
|
1863
|
+
|
|
1864
|
+
struct CobMatrix {
|
|
1865
|
+
SmoothingColumn source, target;
|
|
1866
|
+
std::vector<MatrixRow> entries;
|
|
1867
|
+
|
|
1868
|
+
CobMatrix() = default;
|
|
1869
|
+
CobMatrix(const SmoothingColumn& s, const SmoothingColumn& t) : source(s), target(t), entries(t.n) {}
|
|
1870
|
+
|
|
1871
|
+
void putEntry(int i, int j, const LCCC& lc) {
|
|
1872
|
+
putRowEntry(entries[i], j, lc);
|
|
1873
|
+
}
|
|
1874
|
+
|
|
1875
|
+
void addEntry(int i, int j, const LCCC& lc) {
|
|
1876
|
+
addRowEntry(entries[i], j, lc);
|
|
1877
|
+
}
|
|
1878
|
+
|
|
1879
|
+
std::vector<LCCC> unpackRow(int i) const {
|
|
1880
|
+
std::vector<LCCC> row(source.n);
|
|
1881
|
+
for (auto& kv : entries[i]) row[kv.first] = kv.second;
|
|
1882
|
+
return row;
|
|
1883
|
+
}
|
|
1884
|
+
|
|
1885
|
+
CobMatrix compose(const CobMatrix& cm) const {
|
|
1886
|
+
KH_PROFILE(matrixCompose);
|
|
1887
|
+
CobMatrix ret(cm.source, target);
|
|
1888
|
+
auto work = [&](int begin, int end) {
|
|
1889
|
+
for (int i = begin; i < end; ++i) {
|
|
1890
|
+
MatrixRow result;
|
|
1891
|
+
for (auto& ij : entries[i]) {
|
|
1892
|
+
int j = ij.first;
|
|
1893
|
+
auto& cmrow = cm.entries[j];
|
|
1894
|
+
for (auto& jk : cmrow) {
|
|
1895
|
+
LCCC lc = ij.second.composeVertical(jk.second);
|
|
1896
|
+
if (!lc.isZero()) addRowEntry(result, jk.first, std::move(lc));
|
|
1897
|
+
}
|
|
1898
|
+
}
|
|
1899
|
+
ret.entries[i].swap(result);
|
|
1900
|
+
}
|
|
1901
|
+
};
|
|
1902
|
+
parallelFor(target.n, 8, work);
|
|
1903
|
+
return ret;
|
|
1904
|
+
}
|
|
1905
|
+
|
|
1906
|
+
void add(const CobMatrix& that) {
|
|
1907
|
+
for (size_t i = 0; i < entries.size(); ++i) {
|
|
1908
|
+
addSortedRow(entries[i], that.entries[i]);
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1911
|
+
|
|
1912
|
+
void reduce() {
|
|
1913
|
+
KH_PROFILE(matrixReduce);
|
|
1914
|
+
auto work = [&](int begin, int end) {
|
|
1915
|
+
for (int j = begin; j < end; ++j) {
|
|
1916
|
+
MatrixRow& row = entries[j];
|
|
1917
|
+
bool allReduced = true;
|
|
1918
|
+
for (size_t idx = 0; idx < row.size(); ++idx) {
|
|
1919
|
+
if (!row[idx].second.alreadyReduced) {
|
|
1920
|
+
allReduced = false;
|
|
1921
|
+
break;
|
|
1922
|
+
}
|
|
1923
|
+
}
|
|
1924
|
+
if (allReduced) continue;
|
|
1925
|
+
MatrixRow reduced;
|
|
1926
|
+
reduced.reserve(row.size());
|
|
1927
|
+
for (size_t idx = 0; idx < row.size(); ++idx) {
|
|
1928
|
+
if (row[idx].second.alreadyReduced) {
|
|
1929
|
+
reduced.push_back(std::move(row[idx]));
|
|
1930
|
+
continue;
|
|
1931
|
+
}
|
|
1932
|
+
LCCC rlc = row[idx].second.reduce();
|
|
1933
|
+
if (!rlc.isZero()) reduced.push_back(std::make_pair(row[idx].first, std::move(rlc)));
|
|
1934
|
+
}
|
|
1935
|
+
row.swap(reduced);
|
|
1936
|
+
}
|
|
1937
|
+
};
|
|
1938
|
+
parallelFor(static_cast<int>(entries.size()), 16, work);
|
|
1939
|
+
}
|
|
1940
|
+
|
|
1941
|
+
bool isZero() const {
|
|
1942
|
+
for (auto& row : entries) if (!row.empty()) return false;
|
|
1943
|
+
return true;
|
|
1944
|
+
}
|
|
1945
|
+
|
|
1946
|
+
CobMatrix extractColumn(int column) {
|
|
1947
|
+
SmoothingColumn sc(1);
|
|
1948
|
+
sc.smoothings[0] = source.smoothings[column];
|
|
1949
|
+
sc.numbers[0] = source.numbers[column];
|
|
1950
|
+
source.smoothings.erase(source.smoothings.begin() + column);
|
|
1951
|
+
source.numbers.erase(source.numbers.begin() + column);
|
|
1952
|
+
source.n--;
|
|
1953
|
+
CobMatrix result(sc, target);
|
|
1954
|
+
for (size_t a = 0; a < entries.size(); ++a) {
|
|
1955
|
+
MatrixRow::iterator it = findRowEntry(entries[a], column);
|
|
1956
|
+
if (it != entries[a].end() && it->first == column) {
|
|
1957
|
+
putRowEntry(result.entries[a], 0, it->second);
|
|
1958
|
+
entries[a].erase(it);
|
|
1959
|
+
}
|
|
1960
|
+
decrementIndexesAbove(entries[a], column);
|
|
1961
|
+
}
|
|
1962
|
+
return result;
|
|
1963
|
+
}
|
|
1964
|
+
|
|
1965
|
+
CobMatrix extractRow(int row) {
|
|
1966
|
+
SmoothingColumn sc(1);
|
|
1967
|
+
sc.smoothings[0] = target.smoothings[row];
|
|
1968
|
+
sc.numbers[0] = target.numbers[row];
|
|
1969
|
+
target.smoothings.erase(target.smoothings.begin() + row);
|
|
1970
|
+
target.numbers.erase(target.numbers.begin() + row);
|
|
1971
|
+
target.n--;
|
|
1972
|
+
CobMatrix result(source, sc);
|
|
1973
|
+
result.entries[0] = entries[row];
|
|
1974
|
+
entries.erase(entries.begin() + row);
|
|
1975
|
+
return result;
|
|
1976
|
+
}
|
|
1977
|
+
|
|
1978
|
+
CobMatrix extractColumns(const std::vector<int>& columns) {
|
|
1979
|
+
int oldSourceN = source.n;
|
|
1980
|
+
std::vector<int> selectedIndex(oldSourceN, -1);
|
|
1981
|
+
std::vector<char> selected(oldSourceN, 0);
|
|
1982
|
+
for (size_t i = 0; i < columns.size(); ++i) {
|
|
1983
|
+
selectedIndex[columns[i]] = static_cast<int>(i);
|
|
1984
|
+
selected[columns[i]] = 1;
|
|
1985
|
+
}
|
|
1986
|
+
std::vector<int> removedBefore(oldSourceN + 1, 0);
|
|
1987
|
+
for (int c = 0; c < oldSourceN; ++c) removedBefore[c + 1] = removedBefore[c] + (selected[c] ? 1 : 0);
|
|
1988
|
+
SmoothingColumn sc(static_cast<int>(columns.size()));
|
|
1989
|
+
for (size_t i = 0; i < columns.size(); ++i) {
|
|
1990
|
+
sc.smoothings[i] = source.smoothings[columns[i]];
|
|
1991
|
+
sc.numbers[i] = source.numbers[columns[i]];
|
|
1992
|
+
}
|
|
1993
|
+
std::vector<CapPtr> newSmoothings;
|
|
1994
|
+
std::vector<int> newNumbers;
|
|
1995
|
+
newSmoothings.reserve(oldSourceN - columns.size());
|
|
1996
|
+
newNumbers.reserve(oldSourceN - columns.size());
|
|
1997
|
+
for (int c = 0; c < oldSourceN; ++c) {
|
|
1998
|
+
if (!selected[c]) {
|
|
1999
|
+
newSmoothings.push_back(source.smoothings[c]);
|
|
2000
|
+
newNumbers.push_back(source.numbers[c]);
|
|
2001
|
+
}
|
|
2002
|
+
}
|
|
2003
|
+
source.smoothings.swap(newSmoothings);
|
|
2004
|
+
source.numbers.swap(newNumbers);
|
|
2005
|
+
source.n -= static_cast<int>(columns.size());
|
|
2006
|
+
CobMatrix result(sc, target);
|
|
2007
|
+
for (size_t r = 0; r < entries.size(); ++r) {
|
|
2008
|
+
MatrixRow remaining;
|
|
2009
|
+
remaining.reserve(entries[r].size());
|
|
2010
|
+
for (size_t e = 0; e < entries[r].size(); ++e) {
|
|
2011
|
+
int oldColumn = entries[r][e].first;
|
|
2012
|
+
if (selected[oldColumn]) {
|
|
2013
|
+
putRowEntry(result.entries[r], selectedIndex[oldColumn], entries[r][e].second);
|
|
2014
|
+
} else {
|
|
2015
|
+
entries[r][e].first = oldColumn - removedBefore[oldColumn];
|
|
2016
|
+
remaining.push_back(std::move(entries[r][e]));
|
|
2017
|
+
}
|
|
2018
|
+
}
|
|
2019
|
+
entries[r].swap(remaining);
|
|
2020
|
+
}
|
|
2021
|
+
return result;
|
|
2022
|
+
}
|
|
2023
|
+
|
|
2024
|
+
CobMatrix extractRows(const std::vector<int>& rows) {
|
|
2025
|
+
int oldTargetN = target.n;
|
|
2026
|
+
std::vector<int> selectedIndex(oldTargetN, -1);
|
|
2027
|
+
std::vector<char> selected(oldTargetN, 0);
|
|
2028
|
+
for (size_t i = 0; i < rows.size(); ++i) {
|
|
2029
|
+
selectedIndex[rows[i]] = static_cast<int>(i);
|
|
2030
|
+
selected[rows[i]] = 1;
|
|
2031
|
+
}
|
|
2032
|
+
SmoothingColumn sc(static_cast<int>(rows.size()));
|
|
2033
|
+
for (size_t i = 0; i < rows.size(); ++i) {
|
|
2034
|
+
sc.smoothings[i] = target.smoothings[rows[i]];
|
|
2035
|
+
sc.numbers[i] = target.numbers[rows[i]];
|
|
2036
|
+
}
|
|
2037
|
+
std::vector<CapPtr> newSmoothings;
|
|
2038
|
+
std::vector<int> newNumbers;
|
|
2039
|
+
newSmoothings.reserve(oldTargetN - rows.size());
|
|
2040
|
+
newNumbers.reserve(oldTargetN - rows.size());
|
|
2041
|
+
for (int r = 0; r < oldTargetN; ++r) {
|
|
2042
|
+
if (!selected[r]) {
|
|
2043
|
+
newSmoothings.push_back(target.smoothings[r]);
|
|
2044
|
+
newNumbers.push_back(target.numbers[r]);
|
|
2045
|
+
}
|
|
2046
|
+
}
|
|
2047
|
+
target.smoothings.swap(newSmoothings);
|
|
2048
|
+
target.numbers.swap(newNumbers);
|
|
2049
|
+
target.n -= static_cast<int>(rows.size());
|
|
2050
|
+
CobMatrix result(source, sc);
|
|
2051
|
+
std::vector<MatrixRow> remaining;
|
|
2052
|
+
remaining.reserve(oldTargetN - rows.size());
|
|
2053
|
+
for (int r = 0; r < oldTargetN; ++r) {
|
|
2054
|
+
if (selected[r]) result.entries[selectedIndex[r]] = std::move(entries[r]);
|
|
2055
|
+
else remaining.push_back(std::move(entries[r]));
|
|
2056
|
+
}
|
|
2057
|
+
entries.swap(remaining);
|
|
2058
|
+
return result;
|
|
2059
|
+
}
|
|
2060
|
+
};
|
|
2061
|
+
|
|
2062
|
+
struct IntMatrix {
|
|
2063
|
+
int rows = 0, columns = 0;
|
|
2064
|
+
std::vector<std::vector<BigInt> > matrix;
|
|
2065
|
+
IntMatrix* prev = nullptr;
|
|
2066
|
+
IntMatrix* next = nullptr;
|
|
2067
|
+
std::vector<int>* source = nullptr;
|
|
2068
|
+
std::vector<int>* target = nullptr;
|
|
2069
|
+
|
|
2070
|
+
IntMatrix() = default;
|
|
2071
|
+
IntMatrix(int r, int c) : rows(r), columns(c), matrix(r, std::vector<BigInt>(c, BI_ZERO)) {}
|
|
2072
|
+
explicit IntMatrix(const CobMatrix& cm) : rows(cm.target.n), columns(cm.source.n), matrix(rows, std::vector<BigInt>(columns, BI_ZERO)) {
|
|
2073
|
+
for (int i = 0; i < rows; ++i) {
|
|
2074
|
+
for (auto& kv : cm.entries[i]) {
|
|
2075
|
+
if (kv.second.numberOfTerms() == 1) matrix[i][kv.first] = kv.second.firstCoefficient();
|
|
2076
|
+
}
|
|
2077
|
+
}
|
|
2078
|
+
}
|
|
2079
|
+
|
|
2080
|
+
bool isDiagonal() const {
|
|
2081
|
+
for (int i = 0; i < rows; ++i) for (int j = 0; j < columns; ++j)
|
|
2082
|
+
if (!matrix[i][j].isZero() && i != j) return false;
|
|
2083
|
+
return true;
|
|
2084
|
+
}
|
|
2085
|
+
int rowNonZeros(int i) const {
|
|
2086
|
+
int r = 0; for (int j = 0; j < columns; ++j) if (!matrix[i][j].isZero()) ++r; return r;
|
|
2087
|
+
}
|
|
2088
|
+
int columnNonZeros(int i) const {
|
|
2089
|
+
int r = 0; for (int j = 0; j < rows; ++j) if (!matrix[j][i].isZero()) ++r; return r;
|
|
2090
|
+
}
|
|
2091
|
+
void swapRows2(int a, int b) { std::swap(matrix[a], matrix[b]); }
|
|
2092
|
+
void swapColumns2(int a, int b) { for (int i = 0; i < rows; ++i) std::swap(matrix[i][a], matrix[i][b]); }
|
|
2093
|
+
void swapRows(int a, int b) {
|
|
2094
|
+
swapRows2(a,b);
|
|
2095
|
+
if (next) next->swapColumns2(a,b);
|
|
2096
|
+
if (target) std::swap((*target)[a], (*target)[b]);
|
|
2097
|
+
}
|
|
2098
|
+
void swapColumns(int a, int b) {
|
|
2099
|
+
swapColumns2(a,b);
|
|
2100
|
+
if (prev) prev->swapRows2(a,b);
|
|
2101
|
+
if (source) std::swap((*source)[a], (*source)[b]);
|
|
2102
|
+
}
|
|
2103
|
+
void addRow(int a, int b, const BigInt& n) { for (int i = 0; i < columns; ++i) matrix[a][i] = matrix[a][i] + matrix[b][i] * n; }
|
|
2104
|
+
void addColumn(int a, int b, const BigInt& n) { for (int i = 0; i < rows; ++i) matrix[i][a] = matrix[i][a] + matrix[i][b] * n; }
|
|
2105
|
+
void multRow(int a, const BigInt& n) { for (int i = 0; i < columns; ++i) matrix[a][i] = matrix[a][i] * n; }
|
|
2106
|
+
void multColumn(int a, const BigInt& n) { for (int i = 0; i < rows; ++i) matrix[i][a] = matrix[i][a] * n; }
|
|
2107
|
+
int zeroRowsToEnd() {
|
|
2108
|
+
int nz = rows;
|
|
2109
|
+
for (int i = 0; i < nz; ++i) while (i < nz && rowNonZeros(i) == 0) swapRows(i, --nz);
|
|
2110
|
+
return nz;
|
|
2111
|
+
}
|
|
2112
|
+
int zeroColumnsToEnd() {
|
|
2113
|
+
int nz = columns;
|
|
2114
|
+
for (int i = 0; i < nz; ++i) while (i < nz && columnNonZeros(i) == 0) swapColumns(i, --nz);
|
|
2115
|
+
return nz;
|
|
2116
|
+
}
|
|
2117
|
+
void toSmithForm() {
|
|
2118
|
+
for (int row = 0, col = 0; row < rows && col < columns; ++row, ++col) {
|
|
2119
|
+
while (row < rows && rowNonZeros(row) == 0) row++;
|
|
2120
|
+
while (col < columns && columnNonZeros(col) == 0) col++;
|
|
2121
|
+
if (row >= rows || col >= columns) break;
|
|
2122
|
+
if (row > col) { swapRows(row, col); row = col; }
|
|
2123
|
+
else if (col > row) { swapColumns(row, col); col = row; }
|
|
2124
|
+
while (rowNonZeros(row) != 1 || columnNonZeros(col) != 1 || matrix[row][col] <= BI_ZERO) {
|
|
2125
|
+
for (int j = row; j < rows; ++j) if (matrix[j][col] < BI_ZERO) multRow(j, BI_MINUS_ONE);
|
|
2126
|
+
while (columnNonZeros(col) != 1 || matrix[row][col].isZero()) {
|
|
2127
|
+
BigInt min(-1); int idxmin = -1;
|
|
2128
|
+
for (int j = row; j < rows; ++j) if ((matrix[j][col] < min || min == BI_MINUS_ONE) && matrix[j][col] > BI_ZERO) { min = matrix[j][col]; idxmin = j; }
|
|
2129
|
+
if (idxmin != row) swapRows(row, idxmin);
|
|
2130
|
+
for (int j = row + 1; j < rows; ++j) if (!matrix[j][col].isZero()) addRow(j, row, -(matrix[j][col] / min));
|
|
2131
|
+
}
|
|
2132
|
+
for (int j = col + 1; j < columns; ++j) if (matrix[row][j] < BI_ZERO) multColumn(j, BI_MINUS_ONE);
|
|
2133
|
+
while (rowNonZeros(row) != 1 || matrix[row][col].isZero()) {
|
|
2134
|
+
BigInt min(-1); int idxmin = -1;
|
|
2135
|
+
for (int j = col; j < columns; ++j) if ((matrix[row][j] < min || min == BI_MINUS_ONE) && matrix[row][j] > BI_ZERO) { min = matrix[row][j]; idxmin = j; }
|
|
2136
|
+
if (idxmin != col) swapColumns(col, idxmin);
|
|
2137
|
+
for (int j = col + 1; j < columns; ++j) if (!matrix[row][j].isZero()) addColumn(j, col, -(matrix[row][j] / min));
|
|
2138
|
+
}
|
|
2139
|
+
}
|
|
2140
|
+
}
|
|
2141
|
+
zeroRowsToEnd();
|
|
2142
|
+
zeroColumnsToEnd();
|
|
2143
|
+
}
|
|
2144
|
+
};
|
|
2145
|
+
|
|
2146
|
+
struct Komplex {
|
|
2147
|
+
int ncolumns = 0;
|
|
2148
|
+
std::vector<SmoothingColumn> columns;
|
|
2149
|
+
std::vector<CobMatrix> matrices;
|
|
2150
|
+
int startnum = 0;
|
|
2151
|
+
|
|
2152
|
+
Komplex() = default;
|
|
2153
|
+
explicit Komplex(int n) : ncolumns(n), columns(n) {}
|
|
2154
|
+
Komplex(const std::vector<std::vector<int> >& pd, const std::vector<int>& xsigns, int nfixed);
|
|
2155
|
+
|
|
2156
|
+
void reduce();
|
|
2157
|
+
void deLoop(int colnum);
|
|
2158
|
+
bool reductionLemma(int i);
|
|
2159
|
+
void reductionLemma(int i, int j, int k, const BigInt& n);
|
|
2160
|
+
struct Isomorphism {
|
|
2161
|
+
int row = 0;
|
|
2162
|
+
int column = 0;
|
|
2163
|
+
BigInt coefficient;
|
|
2164
|
+
Isomorphism() = default;
|
|
2165
|
+
Isomorphism(int r, int c, const BigInt& n) : row(r), column(c), coefficient(n) {}
|
|
2166
|
+
};
|
|
2167
|
+
std::vector<Isomorphism> findBlock(const CobMatrix& m) const;
|
|
2168
|
+
void blockReductionLemma(int i);
|
|
2169
|
+
void blockReductionLemma(int i, const std::vector<Isomorphism>& block);
|
|
2170
|
+
Komplex compose(int start, const Komplex& kom, int kstart, int nc) const;
|
|
2171
|
+
std::string KhForZ();
|
|
2172
|
+
};
|
|
2173
|
+
|
|
2174
|
+
static std::vector<std::vector<int> > pascalTriangle;
|
|
2175
|
+
static void fillPascal(int n) {
|
|
2176
|
+
if (static_cast<int>(pascalTriangle.size()) > n) return;
|
|
2177
|
+
pascalTriangle.assign(n + 1, std::vector<int>());
|
|
2178
|
+
for (int i = 0; i <= n; ++i) {
|
|
2179
|
+
pascalTriangle[i].assign(i + 1, 1);
|
|
2180
|
+
for (int j = 1; j < i; ++j) pascalTriangle[i][j] = pascalTriangle[i - 1][j - 1] + pascalTriangle[i - 1][j];
|
|
2181
|
+
}
|
|
2182
|
+
}
|
|
2183
|
+
|
|
2184
|
+
Komplex::Komplex(const std::vector<std::vector<int> >& pd, const std::vector<int>& xsigns, int nfixed) {
|
|
2185
|
+
ncolumns = static_cast<int>(pd.size()) + 1;
|
|
2186
|
+
columns.assign(ncolumns, SmoothingColumn());
|
|
2187
|
+
startnum = 0;
|
|
2188
|
+
for (size_t i = 0; i < pd.size(); ++i) if (xsigns[i] == -1) startnum--;
|
|
2189
|
+
fillPascal(static_cast<int>(pd.size()));
|
|
2190
|
+
for (int i = 0; i < ncolumns; ++i) columns[i] = SmoothingColumn(pascalTriangle[pd.size()][i]);
|
|
2191
|
+
for (int i = 0; i < ncolumns - 1; ++i) matrices.push_back(CobMatrix(columns[i], columns[i + 1]));
|
|
2192
|
+
|
|
2193
|
+
int total = 1 << pd.size();
|
|
2194
|
+
std::vector<int> numsmoothings(ncolumns, 0);
|
|
2195
|
+
std::vector<std::vector<std::vector<int> > > crossing2cycles(total, std::vector<std::vector<int> >(pd.size(), std::vector<int>(2, -1)));
|
|
2196
|
+
std::vector<CapPtr> smoothings(total);
|
|
2197
|
+
std::vector<int> whichColumn(total), whichRow(total);
|
|
2198
|
+
for (int i = 0; i < total; ++i) {
|
|
2199
|
+
std::vector<std::vector<int> > smoothing(pd.size() * 2, std::vector<int>(2, 0));
|
|
2200
|
+
int num1 = 0;
|
|
2201
|
+
for (size_t j = 0; j < pd.size(); ++j) {
|
|
2202
|
+
if (((i >> j) & 1) == 0) {
|
|
2203
|
+
smoothing[2*j][0] = pd[j][0]; smoothing[2*j][1] = pd[j][1];
|
|
2204
|
+
smoothing[2*j+1][0] = pd[j][2]; smoothing[2*j+1][1] = pd[j][3];
|
|
2205
|
+
} else {
|
|
2206
|
+
smoothing[2*j][0] = pd[j][0]; smoothing[2*j][1] = pd[j][3];
|
|
2207
|
+
smoothing[2*j+1][0] = pd[j][1]; smoothing[2*j+1][1] = pd[j][2];
|
|
2208
|
+
num1++;
|
|
2209
|
+
}
|
|
2210
|
+
}
|
|
2211
|
+
std::vector<std::vector<int> > rsmoothing(pd.size() * 4, std::vector<int>(2, -1));
|
|
2212
|
+
for (size_t j = 0; j < smoothing.size(); ++j) {
|
|
2213
|
+
if (rsmoothing[smoothing[j][0]][0] == -1) rsmoothing[smoothing[j][0]][0] = static_cast<int>(j);
|
|
2214
|
+
else rsmoothing[smoothing[j][0]][1] = static_cast<int>(j);
|
|
2215
|
+
if (rsmoothing[smoothing[j][1]][0] == -1) rsmoothing[smoothing[j][1]][0] = static_cast<int>(j);
|
|
2216
|
+
else rsmoothing[smoothing[j][1]][1] = static_cast<int>(j);
|
|
2217
|
+
}
|
|
2218
|
+
std::vector<char> done(4 * pd.size(), 0);
|
|
2219
|
+
int ncycles = 0;
|
|
2220
|
+
std::vector<int> pairings(nfixed, 0);
|
|
2221
|
+
for (int j = 0; j < static_cast<int>(rsmoothing.size()) && rsmoothing[j][0] != -1; ++j) {
|
|
2222
|
+
if (done[j]) continue;
|
|
2223
|
+
int dst = j;
|
|
2224
|
+
do {
|
|
2225
|
+
int a;
|
|
2226
|
+
if (dst < nfixed) a = 0;
|
|
2227
|
+
else {
|
|
2228
|
+
int b0 = smoothing[rsmoothing[dst][0]][0] == dst ? 1 : 0;
|
|
2229
|
+
int b1 = smoothing[rsmoothing[dst][1]][0] == dst ? 1 : 0;
|
|
2230
|
+
if (done[smoothing[rsmoothing[dst][0]][b0]]) {
|
|
2231
|
+
if (done[smoothing[rsmoothing[dst][1]][b1]]) a = smoothing[rsmoothing[dst][0]][b0] == j ? 0 : 1;
|
|
2232
|
+
else a = 1;
|
|
2233
|
+
} else a = 0;
|
|
2234
|
+
crossing2cycles[i][rsmoothing[dst][1] / 2][rsmoothing[dst][1] % 2] = j;
|
|
2235
|
+
}
|
|
2236
|
+
crossing2cycles[i][rsmoothing[dst][0] / 2][rsmoothing[dst][0] % 2] = j;
|
|
2237
|
+
done[dst] = 1;
|
|
2238
|
+
dst = smoothing[rsmoothing[dst][a]][0] == dst ? smoothing[rsmoothing[dst][a]][1] : smoothing[rsmoothing[dst][a]][0];
|
|
2239
|
+
} while (dst >= nfixed && dst != j);
|
|
2240
|
+
if (dst == j) ncycles++;
|
|
2241
|
+
else { pairings[j] = dst; pairings[dst] = j; }
|
|
2242
|
+
done[dst] = 1;
|
|
2243
|
+
}
|
|
2244
|
+
std::vector<int> remap(rsmoothing.size(), -1);
|
|
2245
|
+
for (size_t j = 0, k = 0; j < pd.size(); ++j) for (int l = 0; l < 2; ++l) {
|
|
2246
|
+
int& v = crossing2cycles[i][j][l];
|
|
2247
|
+
if (v < nfixed) v = -1 - v;
|
|
2248
|
+
else if (remap[v] != -1) v = remap[v];
|
|
2249
|
+
else { remap[v] = static_cast<int>(k); v = static_cast<int>(k++); }
|
|
2250
|
+
}
|
|
2251
|
+
Cap c(nfixed, ncycles);
|
|
2252
|
+
c.pairings = pairings;
|
|
2253
|
+
CapPtr cp = cacheCap(c);
|
|
2254
|
+
whichColumn[i] = num1;
|
|
2255
|
+
whichRow[i] = numsmoothings[num1];
|
|
2256
|
+
columns[num1].smoothings[numsmoothings[num1]] = cp;
|
|
2257
|
+
for (size_t j = 0; j < pd.size(); ++j) {
|
|
2258
|
+
int& q = columns[num1].numbers[numsmoothings[num1]];
|
|
2259
|
+
if ((i & (1 << j)) == 0) q += xsigns[j] == 1 ? 1 : -2;
|
|
2260
|
+
else q += xsigns[j] == 1 ? 2 : -1;
|
|
2261
|
+
}
|
|
2262
|
+
numsmoothings[num1]++;
|
|
2263
|
+
smoothings[i] = cp;
|
|
2264
|
+
if (i != 0) {
|
|
2265
|
+
for (size_t j = 0; j < pd.size(); ++j) if ((i & (1 << j)) != 0) {
|
|
2266
|
+
int k = i ^ (1 << j);
|
|
2267
|
+
CobPtr cc = makeCob(smoothings[k], cp);
|
|
2268
|
+
std::fill(cc->connectedComponent.begin(), cc->connectedComponent.end(), -1);
|
|
2269
|
+
cc->ncc = 0;
|
|
2270
|
+
for (size_t l = 0; l < pd.size(); ++l) {
|
|
2271
|
+
if (l != j) {
|
|
2272
|
+
for (int m = 0; m < 2; ++m) {
|
|
2273
|
+
int x = crossing2cycles[i][l][m];
|
|
2274
|
+
if (x < 0) x = cc->component[-1 - x]; else x += cc->offbot;
|
|
2275
|
+
int y = crossing2cycles[k][l][m];
|
|
2276
|
+
if (y < 0) y = cc->component[-1 - y]; else y += cc->offtop;
|
|
2277
|
+
int cci;
|
|
2278
|
+
if (cc->connectedComponent[y] != -1) cci = cc->connectedComponent[y];
|
|
2279
|
+
else if (cc->connectedComponent[x] != -1) cci = cc->connectedComponent[x];
|
|
2280
|
+
else cci = cc->ncc++;
|
|
2281
|
+
cc->connectedComponent[y] = cci;
|
|
2282
|
+
cc->connectedComponent[x] = cci;
|
|
2283
|
+
}
|
|
2284
|
+
} else {
|
|
2285
|
+
int num[4] = { crossing2cycles[i][l][0], crossing2cycles[i][l][1], crossing2cycles[k][l][0], crossing2cycles[k][l][1] };
|
|
2286
|
+
for (int x = 0; x < 4; ++x) {
|
|
2287
|
+
if (num[x] >= 0) num[x] += x < 2 ? cc->offbot : cc->offtop;
|
|
2288
|
+
else num[x] = cc->component[-1 - num[x]];
|
|
2289
|
+
}
|
|
2290
|
+
int cci = -1;
|
|
2291
|
+
for (int x = 0; x < 4; ++x) if (cc->connectedComponent[num[x]] != -1) { cci = cc->connectedComponent[num[x]]; break; }
|
|
2292
|
+
if (cci == -1) cci = cc->ncc++;
|
|
2293
|
+
for (int x = 0; x < 4; ++x) if (cc->connectedComponent[num[x]] != cci) {
|
|
2294
|
+
int y = cc->connectedComponent[num[x]];
|
|
2295
|
+
if (y != -1) for (int z = 0; z < cc->nbc; ++z) if (cc->connectedComponent[z] == y) cc->connectedComponent[z] = cci;
|
|
2296
|
+
cc->connectedComponent[num[x]] = cci;
|
|
2297
|
+
}
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
2300
|
+
cc->ncc = 0;
|
|
2301
|
+
for (int l = 0; l < cc->nbc; ++l) {
|
|
2302
|
+
if (cc->connectedComponent[l] > cc->ncc) {
|
|
2303
|
+
int x = cc->connectedComponent[l];
|
|
2304
|
+
for (int m = l; m < cc->nbc; ++m) {
|
|
2305
|
+
if (cc->connectedComponent[m] == x) cc->connectedComponent[m] = cc->ncc;
|
|
2306
|
+
else if (cc->connectedComponent[m] == cc->ncc) cc->connectedComponent[m] = x;
|
|
2307
|
+
}
|
|
2308
|
+
cc->ncc++;
|
|
2309
|
+
} else if (cc->connectedComponent[l] == cc->ncc) cc->ncc++;
|
|
2310
|
+
}
|
|
2311
|
+
cc->dots.assign(cc->ncc, 0);
|
|
2312
|
+
cc->genus.assign(cc->ncc, 0);
|
|
2313
|
+
int coeff = 1;
|
|
2314
|
+
for (size_t l = j + 1; l < pd.size(); ++l) if ((i & (1 << l)) != 0) coeff = -coeff;
|
|
2315
|
+
LCCC lc(cacheCob(cc), BigInt(coeff));
|
|
2316
|
+
matrices[num1 - 1].putEntry(whichRow[i], whichRow[k], lc);
|
|
2317
|
+
}
|
|
2318
|
+
}
|
|
2319
|
+
}
|
|
2320
|
+
}
|
|
2321
|
+
|
|
2322
|
+
void Komplex::deLoop(int colnum) {
|
|
2323
|
+
KH_PROFILE(deLoop);
|
|
2324
|
+
uint64_t deLoopSetupStart = g_options.profile ? profileNowNs() : 0;
|
|
2325
|
+
CobMatrix* prevMatrix = colnum != 0 ? &matrices[colnum - 1] : nullptr;
|
|
2326
|
+
CobMatrix* nextMatrix = colnum != ncolumns - 1 ? &matrices[colnum] : nullptr;
|
|
2327
|
+
int size = 0;
|
|
2328
|
+
for (int i = 0; i < columns[colnum].n; ++i) size += 1 << columns[colnum].smoothings[i]->ncycles;
|
|
2329
|
+
SmoothingColumn newsc(size);
|
|
2330
|
+
CobMatrix prev, next;
|
|
2331
|
+
if (prevMatrix) prev = CobMatrix(columns[colnum - 1], newsc);
|
|
2332
|
+
if (nextMatrix) next = CobMatrix(newsc, columns[colnum + 1]);
|
|
2333
|
+
std::vector<int> nextOffset;
|
|
2334
|
+
std::vector<std::pair<int, const LCCC*> > nextColumnEntries;
|
|
2335
|
+
if (nextMatrix) {
|
|
2336
|
+
nextOffset.assign(columns[colnum].n + 1, 0);
|
|
2337
|
+
for (size_t row = 0; row < nextMatrix->entries.size(); ++row) {
|
|
2338
|
+
for (size_t e = 0; e < nextMatrix->entries[row].size(); ++e) {
|
|
2339
|
+
int col = nextMatrix->entries[row][e].first;
|
|
2340
|
+
if (col >= 0 && col < columns[colnum].n) {
|
|
2341
|
+
nextOffset[col + 1]++;
|
|
2342
|
+
}
|
|
2343
|
+
}
|
|
2344
|
+
}
|
|
2345
|
+
for (size_t col = 1; col < nextOffset.size(); ++col) nextOffset[col] += nextOffset[col - 1];
|
|
2346
|
+
nextColumnEntries.resize(nextOffset.back());
|
|
2347
|
+
std::vector<int> cursor = nextOffset;
|
|
2348
|
+
for (size_t row = 0; row < nextMatrix->entries.size(); ++row) {
|
|
2349
|
+
for (size_t e = 0; e < nextMatrix->entries[row].size(); ++e) {
|
|
2350
|
+
int col = nextMatrix->entries[row][e].first;
|
|
2351
|
+
if (col >= 0 && col < columns[colnum].n) {
|
|
2352
|
+
int pos = cursor[col]++;
|
|
2353
|
+
nextColumnEntries[pos] = std::make_pair(static_cast<int>(row), &nextMatrix->entries[row][e].second);
|
|
2354
|
+
}
|
|
2355
|
+
}
|
|
2356
|
+
}
|
|
2357
|
+
}
|
|
2358
|
+
if (g_options.profile) {
|
|
2359
|
+
g_profile.deLoopSetup.calls++;
|
|
2360
|
+
g_profile.deLoopSetup.ns += profileNowNs() - deLoopSetupStart;
|
|
2361
|
+
}
|
|
2362
|
+
int newn = 0;
|
|
2363
|
+
for (int i = 0; i < columns[colnum].n; ++i) {
|
|
2364
|
+
CapPtr oldsm = columns[colnum].smoothings[i];
|
|
2365
|
+
Cap newsmValue(oldsm->n, 0);
|
|
2366
|
+
newsmValue.pairings = oldsm->pairings;
|
|
2367
|
+
CapPtr newsm = cacheCap(newsmValue);
|
|
2368
|
+
Cobordism prevccBase(oldsm, newsm);
|
|
2369
|
+
prevccBase.ncc = prevccBase.nbc;
|
|
2370
|
+
prevccBase.connectedComponent = countingSmall(prevccBase.nbc);
|
|
2371
|
+
prevccBase.dots.assign(prevccBase.ncc, 0);
|
|
2372
|
+
prevccBase.genus.assign(prevccBase.ncc, 0);
|
|
2373
|
+
Cobordism nextccBase(newsm, oldsm);
|
|
2374
|
+
nextccBase.ncc = nextccBase.nbc;
|
|
2375
|
+
nextccBase.connectedComponent = countingSmall(nextccBase.nbc);
|
|
2376
|
+
nextccBase.dots.assign(nextccBase.ncc, 0);
|
|
2377
|
+
nextccBase.genus.assign(nextccBase.ncc, 0);
|
|
2378
|
+
Cobordism prevcc = prevccBase;
|
|
2379
|
+
Cobordism nextcc = nextccBase;
|
|
2380
|
+
for (int j = 0; j < (1 << oldsm->ncycles); ++j) {
|
|
2381
|
+
int nmod = 0;
|
|
2382
|
+
for (int k = 0; k < oldsm->ncycles; ++k) {
|
|
2383
|
+
if ((j & (1 << k)) == 0) {
|
|
2384
|
+
nmod++;
|
|
2385
|
+
prevcc.dots[prevcc.offtop + k] = 1;
|
|
2386
|
+
nextcc.dots[nextcc.offbot + k] = 0;
|
|
2387
|
+
} else {
|
|
2388
|
+
nmod--;
|
|
2389
|
+
prevcc.dots[prevcc.offtop + k] = 0;
|
|
2390
|
+
nextcc.dots[nextcc.offbot + k] = 1;
|
|
2391
|
+
}
|
|
2392
|
+
}
|
|
2393
|
+
newsc.smoothings[newn] = newsm;
|
|
2394
|
+
newsc.numbers[newn] = columns[colnum].numbers[i] + nmod;
|
|
2395
|
+
if (prevMatrix) {
|
|
2396
|
+
uint64_t partStart = g_options.profile ? profileNowNs() : 0;
|
|
2397
|
+
if (oldsm->ncycles != 0) {
|
|
2398
|
+
MatrixRow row;
|
|
2399
|
+
for (auto& kv : prevMatrix->entries[i]) {
|
|
2400
|
+
if (g_options.profile) {
|
|
2401
|
+
g_profile.deLoopPrevTerms.calls++;
|
|
2402
|
+
g_profile.deLoopPrevTerms.ns += kv.second.terms.size();
|
|
2403
|
+
}
|
|
2404
|
+
LCCC composition = LCCC::composeCobLeft(prevcc, kv.second);
|
|
2405
|
+
appendSortedRowEntry(row, kv.first, std::move(composition));
|
|
2406
|
+
}
|
|
2407
|
+
prev.entries[newn] = row;
|
|
2408
|
+
} else {
|
|
2409
|
+
prev.entries[newn] = prevMatrix->entries[i];
|
|
2410
|
+
}
|
|
2411
|
+
if (g_options.profile) {
|
|
2412
|
+
g_profile.deLoopPrev.calls++;
|
|
2413
|
+
g_profile.deLoopPrev.ns += profileNowNs() - partStart;
|
|
2414
|
+
}
|
|
2415
|
+
}
|
|
2416
|
+
if (nextMatrix) {
|
|
2417
|
+
uint64_t partStart = g_options.profile ? profileNowNs() : 0;
|
|
2418
|
+
if (oldsm->ncycles != 0) {
|
|
2419
|
+
for (int idx = nextOffset[i]; idx < nextOffset[i + 1]; ++idx) {
|
|
2420
|
+
int k = nextColumnEntries[idx].first;
|
|
2421
|
+
if (g_options.profile) {
|
|
2422
|
+
g_profile.deLoopNextTerms.calls++;
|
|
2423
|
+
g_profile.deLoopNextTerms.ns += nextColumnEntries[idx].second->terms.size();
|
|
2424
|
+
}
|
|
2425
|
+
LCCC composition = LCCC::composeCobRight(*nextColumnEntries[idx].second, nextcc);
|
|
2426
|
+
appendSortedRowEntry(next.entries[k], newn, std::move(composition));
|
|
2427
|
+
}
|
|
2428
|
+
} else {
|
|
2429
|
+
for (int idx = nextOffset[i]; idx < nextOffset[i + 1]; ++idx) {
|
|
2430
|
+
int k = nextColumnEntries[idx].first;
|
|
2431
|
+
appendSortedRowEntry(next.entries[k], newn, *nextColumnEntries[idx].second);
|
|
2432
|
+
}
|
|
2433
|
+
}
|
|
2434
|
+
if (g_options.profile) {
|
|
2435
|
+
g_profile.deLoopNext.calls++;
|
|
2436
|
+
g_profile.deLoopNext.ns += profileNowNs() - partStart;
|
|
2437
|
+
}
|
|
2438
|
+
}
|
|
2439
|
+
newn++;
|
|
2440
|
+
}
|
|
2441
|
+
}
|
|
2442
|
+
columns[colnum] = newsc;
|
|
2443
|
+
if (prevMatrix) { prev.target = newsc; matrices[colnum - 1] = prev; }
|
|
2444
|
+
if (nextMatrix) { next.source = newsc; matrices[colnum] = next; }
|
|
2445
|
+
}
|
|
2446
|
+
|
|
2447
|
+
bool Komplex::reductionLemma(int i) {
|
|
2448
|
+
bool found, found2 = false, ret = false;
|
|
2449
|
+
int count = 0;
|
|
2450
|
+
do {
|
|
2451
|
+
found = false;
|
|
2452
|
+
CobMatrix& m = matrices[i];
|
|
2453
|
+
for (int j = 0; j < static_cast<int>(m.entries.size()) && !found; ++j) {
|
|
2454
|
+
for (auto& kv : m.entries[j]) {
|
|
2455
|
+
int k = kv.first;
|
|
2456
|
+
LCCC& lc = kv.second;
|
|
2457
|
+
if (lc.numberOfTerms() == 1) {
|
|
2458
|
+
if (!columns[i].smoothings[k]->equals(*columns[i + 1].smoothings[j])) continue;
|
|
2459
|
+
CobPtr cc = lc.firstTerm();
|
|
2460
|
+
BigInt n = lc.firstCoefficient();
|
|
2461
|
+
if (!(n == BI_ONE || n == BI_MINUS_ONE)) continue;
|
|
2462
|
+
if (!cc->isIsomorphism()) continue;
|
|
2463
|
+
found2 = found = true;
|
|
2464
|
+
++count;
|
|
2465
|
+
reductionLemma(i, j, k, n);
|
|
2466
|
+
break;
|
|
2467
|
+
}
|
|
2468
|
+
}
|
|
2469
|
+
}
|
|
2470
|
+
if (found) ret = true;
|
|
2471
|
+
if (!found && found2) {
|
|
2472
|
+
matrices[i].reduce();
|
|
2473
|
+
found = true;
|
|
2474
|
+
found2 = false;
|
|
2475
|
+
}
|
|
2476
|
+
} while (found);
|
|
2477
|
+
(void)count;
|
|
2478
|
+
return ret;
|
|
2479
|
+
}
|
|
2480
|
+
|
|
2481
|
+
void Komplex::reductionLemma(int i, int j, int k, const BigInt& n) {
|
|
2482
|
+
CapPtr isoSource = columns[i].smoothings[k];
|
|
2483
|
+
CapPtr isoTarget = columns[i + 1].smoothings[j];
|
|
2484
|
+
CobMatrix m = std::move(matrices[i]);
|
|
2485
|
+
CobMatrix delta = m.extractRow(j);
|
|
2486
|
+
delta.extractColumn(k);
|
|
2487
|
+
CobMatrix gamma = m.extractColumn(k);
|
|
2488
|
+
BigInt coeff = (n == BI_ONE ? BI_MINUS_ONE : BI_ONE);
|
|
2489
|
+
for (size_t row = 0; row < gamma.entries.size(); ++row) {
|
|
2490
|
+
for (size_t e = 0; e < gamma.entries[row].size(); ++e) {
|
|
2491
|
+
if (gamma.entries[row][e].first == 0) gamma.entries[row][e].second.multiplyInPlace(coeff);
|
|
2492
|
+
}
|
|
2493
|
+
}
|
|
2494
|
+
CobMatrix gpd = gamma.compose(delta);
|
|
2495
|
+
gpd.add(m);
|
|
2496
|
+
matrices[i] = gpd;
|
|
2497
|
+
columns[i] = delta.source;
|
|
2498
|
+
columns[i + 1] = gamma.target;
|
|
2499
|
+
if (i != 0) {
|
|
2500
|
+
matrices[i - 1].extractRow(k);
|
|
2501
|
+
}
|
|
2502
|
+
if (i != ncolumns - 2) {
|
|
2503
|
+
matrices[i + 1].extractColumn(j);
|
|
2504
|
+
}
|
|
2505
|
+
}
|
|
2506
|
+
|
|
2507
|
+
std::vector<Komplex::Isomorphism> Komplex::findBlock(const CobMatrix& m) const {
|
|
2508
|
+
std::vector<Isomorphism> isos;
|
|
2509
|
+
std::vector<char> disallowedColumns(m.source.n, 0);
|
|
2510
|
+
std::vector<char> isomorphismColumns(m.source.n, 0);
|
|
2511
|
+
std::vector<int> potentialDisallowedColumns;
|
|
2512
|
+
for (int j = 0; j < static_cast<int>(m.entries.size()); ++j) {
|
|
2513
|
+
bool foundIsomorphismOnRow = false;
|
|
2514
|
+
bool rowForbidden = false;
|
|
2515
|
+
bool hasRowCandidate = false;
|
|
2516
|
+
Isomorphism rowCandidate;
|
|
2517
|
+
potentialDisallowedColumns.clear();
|
|
2518
|
+
potentialDisallowedColumns.reserve(m.entries[j].size());
|
|
2519
|
+
for (const auto& kv : m.entries[j]) {
|
|
2520
|
+
int k = kv.first;
|
|
2521
|
+
const LCCC& lc = kv.second;
|
|
2522
|
+
if (isomorphismColumns[k]) rowForbidden = true;
|
|
2523
|
+
if (disallowedColumns[k]) continue;
|
|
2524
|
+
if (foundIsomorphismOnRow) {
|
|
2525
|
+
disallowedColumns[k] = 1;
|
|
2526
|
+
} else {
|
|
2527
|
+
potentialDisallowedColumns.push_back(k);
|
|
2528
|
+
if (!hasRowCandidate && lc.numberOfTerms() == 1) {
|
|
2529
|
+
CobPtr cc = lc.firstTerm();
|
|
2530
|
+
BigInt n = lc.firstCoefficient();
|
|
2531
|
+
if (!(n == BI_ONE || n == BI_MINUS_ONE)) continue;
|
|
2532
|
+
if (!cc->isIsomorphism()) continue;
|
|
2533
|
+
rowCandidate = Isomorphism(j, k, n);
|
|
2534
|
+
hasRowCandidate = true;
|
|
2535
|
+
foundIsomorphismOnRow = true;
|
|
2536
|
+
}
|
|
2537
|
+
}
|
|
2538
|
+
}
|
|
2539
|
+
if (!rowForbidden && hasRowCandidate) {
|
|
2540
|
+
isos.push_back(rowCandidate);
|
|
2541
|
+
isomorphismColumns[rowCandidate.column] = 1;
|
|
2542
|
+
for (size_t p = 0; p < potentialDisallowedColumns.size(); ++p) {
|
|
2543
|
+
disallowedColumns[potentialDisallowedColumns[p]] = 1;
|
|
2544
|
+
}
|
|
2545
|
+
}
|
|
2546
|
+
}
|
|
2547
|
+
return isos;
|
|
2548
|
+
}
|
|
2549
|
+
|
|
2550
|
+
void Komplex::blockReductionLemma(int i, const std::vector<Isomorphism>& block) {
|
|
2551
|
+
KH_PROFILE(blockReductionApply);
|
|
2552
|
+
if (block.empty()) return;
|
|
2553
|
+
std::vector<int> rows, cols;
|
|
2554
|
+
std::vector<BigInt> coeffs;
|
|
2555
|
+
rows.reserve(block.size());
|
|
2556
|
+
cols.reserve(block.size());
|
|
2557
|
+
coeffs.reserve(block.size());
|
|
2558
|
+
for (const Isomorphism& iso : block) {
|
|
2559
|
+
rows.push_back(iso.row);
|
|
2560
|
+
cols.push_back(iso.column);
|
|
2561
|
+
coeffs.push_back(iso.coefficient);
|
|
2562
|
+
}
|
|
2563
|
+
CobMatrix m = std::move(matrices[i]);
|
|
2564
|
+
CobMatrix delta = m.extractRows(rows);
|
|
2565
|
+
delta.extractColumns(cols);
|
|
2566
|
+
CobMatrix gamma = m.extractColumns(cols);
|
|
2567
|
+
for (size_t k = 0; k < block.size(); ++k) {
|
|
2568
|
+
BigInt coeff = (coeffs[k] == BI_ONE ? BI_MINUS_ONE : BI_ONE);
|
|
2569
|
+
for (size_t row = 0; row < gamma.entries.size(); ++row) {
|
|
2570
|
+
MatrixRow::iterator it = findRowEntry(gamma.entries[row], static_cast<int>(k));
|
|
2571
|
+
if (it != gamma.entries[row].end() && it->first == static_cast<int>(k)) {
|
|
2572
|
+
it->second.multiplyInPlace(coeff);
|
|
2573
|
+
}
|
|
2574
|
+
}
|
|
2575
|
+
}
|
|
2576
|
+
CobMatrix gpd = gamma.compose(delta);
|
|
2577
|
+
gpd.add(m);
|
|
2578
|
+
matrices[i] = gpd;
|
|
2579
|
+
columns[i] = delta.source;
|
|
2580
|
+
columns[i + 1] = gamma.target;
|
|
2581
|
+
if (i != 0) matrices[i - 1].extractRows(cols);
|
|
2582
|
+
if (i != ncolumns - 2) matrices[i + 1].extractColumns(rows);
|
|
2583
|
+
}
|
|
2584
|
+
|
|
2585
|
+
void Komplex::blockReductionLemma(int i) {
|
|
2586
|
+
KH_PROFILE(blockReduction);
|
|
2587
|
+
while (true) {
|
|
2588
|
+
std::vector<Isomorphism> block = findBlock(matrices[i]);
|
|
2589
|
+
if (block.empty()) break;
|
|
2590
|
+
blockReductionLemma(i, block);
|
|
2591
|
+
matrices[i].reduce();
|
|
2592
|
+
}
|
|
2593
|
+
}
|
|
2594
|
+
|
|
2595
|
+
void Komplex::reduce() {
|
|
2596
|
+
KH_PROFILE(complexReduce);
|
|
2597
|
+
for (int i = 0; i < ncolumns; ++i) {
|
|
2598
|
+
deLoop(i);
|
|
2599
|
+
if (i > 0) {
|
|
2600
|
+
matrices[i - 1].reduce();
|
|
2601
|
+
blockReductionLemma(i - 1);
|
|
2602
|
+
}
|
|
2603
|
+
}
|
|
2604
|
+
}
|
|
2605
|
+
|
|
2606
|
+
Komplex Komplex::compose(int start, const Komplex& kom, int kstart, int nc) const {
|
|
2607
|
+
KH_PROFILE(complexCompose);
|
|
2608
|
+
Komplex ret(ncolumns + kom.ncolumns - 1);
|
|
2609
|
+
ret.startnum = startnum + kom.startnum;
|
|
2610
|
+
std::vector<int> colsizes(ret.ncolumns, 0);
|
|
2611
|
+
for (int i = 0; i < ret.ncolumns; ++i)
|
|
2612
|
+
for (int j = 0; j <= i && j < ncolumns; ++j)
|
|
2613
|
+
if (i - j < kom.ncolumns) colsizes[i] += columns[j].n * kom.columns[i - j].n;
|
|
2614
|
+
for (int i = 0; i < ret.ncolumns; ++i) ret.columns[i] = SmoothingColumn(colsizes[i]);
|
|
2615
|
+
std::vector<std::vector<int> > startnum2(ncolumns, std::vector<int>(kom.ncolumns, 0));
|
|
2616
|
+
for (int i = 0; i < ret.ncolumns; ++i) {
|
|
2617
|
+
int sn = 0;
|
|
2618
|
+
for (int j = 0; j <= i && j < ncolumns; ++j) if (i - j < kom.ncolumns) {
|
|
2619
|
+
int kk = i - j;
|
|
2620
|
+
startnum2[j][kk] = sn;
|
|
2621
|
+
for (int l = 0; l < columns[j].n; ++l) for (int m = 0; m < kom.columns[kk].n; ++m) {
|
|
2622
|
+
ret.columns[i].smoothings[sn] = columns[j].smoothings[l]->compose(start, kom.columns[kk].smoothings[m], kstart, nc);
|
|
2623
|
+
ret.columns[i].numbers[sn] = columns[j].numbers[l] + kom.columns[kk].numbers[m];
|
|
2624
|
+
sn++;
|
|
2625
|
+
}
|
|
2626
|
+
}
|
|
2627
|
+
}
|
|
2628
|
+
for (int i = 0; i < ret.ncolumns - 1; ++i) {
|
|
2629
|
+
CobMatrix newMatrix(ret.columns[i], ret.columns[i + 1]);
|
|
2630
|
+
for (int j = 0; j <= i && j < ncolumns; ++j) if (i - j < kom.ncolumns) {
|
|
2631
|
+
int kk = i - j;
|
|
2632
|
+
if (j < ncolumns - 1) {
|
|
2633
|
+
const CobMatrix& matrixJ = matrices[j];
|
|
2634
|
+
for (int m = 0; m < kom.columns[kk].n; ++m) {
|
|
2635
|
+
CobPtr komcc = isomorphism(kom.columns[kk].smoothings[m]);
|
|
2636
|
+
for (int nrow = 0; nrow < columns[j + 1].n; ++nrow) {
|
|
2637
|
+
for (auto& kv : matrixJ.entries[nrow]) {
|
|
2638
|
+
LCCC composition = kv.second.composeHorizontal(start, komcc, kstart, nc, false);
|
|
2639
|
+
if (!composition.isZero()) {
|
|
2640
|
+
if (kk % 2 == 0) composition = composition.multiplied(BI_MINUS_ONE);
|
|
2641
|
+
newMatrix.putEntry(startnum2[j + 1][kk] + nrow * kom.columns[kk].n + m,
|
|
2642
|
+
startnum2[j][kk] + kv.first * kom.columns[kk].n + m,
|
|
2643
|
+
composition);
|
|
2644
|
+
}
|
|
2645
|
+
}
|
|
2646
|
+
}
|
|
2647
|
+
}
|
|
2648
|
+
}
|
|
2649
|
+
if (kk < kom.ncolumns - 1) {
|
|
2650
|
+
const CobMatrix& komMatrixK = kom.matrices[kk];
|
|
2651
|
+
for (int l = 0; l < columns[j].n; ++l) {
|
|
2652
|
+
CobPtr thiscc = isomorphism(columns[j].smoothings[l]);
|
|
2653
|
+
for (int nrow = 0; nrow < kom.columns[kk + 1].n; ++nrow) {
|
|
2654
|
+
for (auto& kv : komMatrixK.entries[nrow]) {
|
|
2655
|
+
LCCC composition = kv.second.composeHorizontal(kstart, thiscc, start, nc, true);
|
|
2656
|
+
if (!composition.isZero()) {
|
|
2657
|
+
newMatrix.putEntry(startnum2[j][kk + 1] + l * kom.columns[kk + 1].n + nrow,
|
|
2658
|
+
startnum2[j][kk] + l * kom.columns[kk].n + kv.first,
|
|
2659
|
+
composition);
|
|
2660
|
+
}
|
|
2661
|
+
}
|
|
2662
|
+
}
|
|
2663
|
+
}
|
|
2664
|
+
}
|
|
2665
|
+
}
|
|
2666
|
+
ret.matrices.push_back(newMatrix);
|
|
2667
|
+
}
|
|
2668
|
+
return ret;
|
|
2669
|
+
}
|
|
2670
|
+
|
|
2671
|
+
std::string Komplex::KhForZ() {
|
|
2672
|
+
std::vector<IntMatrix> mats;
|
|
2673
|
+
mats.reserve(matrices.size());
|
|
2674
|
+
std::vector<std::vector<int> > colcopy(ncolumns);
|
|
2675
|
+
for (int i = 0; i < ncolumns; ++i) colcopy[i] = columns[i].numbers;
|
|
2676
|
+
for (size_t i = 0; i < matrices.size(); ++i) {
|
|
2677
|
+
mats.push_back(IntMatrix(matrices[i]));
|
|
2678
|
+
}
|
|
2679
|
+
for (size_t i = 0; i < mats.size(); ++i) {
|
|
2680
|
+
mats[i].source = &colcopy[i];
|
|
2681
|
+
mats[i].target = &colcopy[i + 1];
|
|
2682
|
+
if (i > 0) mats[i - 1].next = &mats[i];
|
|
2683
|
+
}
|
|
2684
|
+
std::string ret;
|
|
2685
|
+
for (int i = 0; i < ncolumns; ++i) {
|
|
2686
|
+
std::vector<int> degrees;
|
|
2687
|
+
std::vector<int> nentries;
|
|
2688
|
+
std::vector<std::vector<int> > retvals;
|
|
2689
|
+
int last = INT32_MIN;
|
|
2690
|
+
int n = 0;
|
|
2691
|
+
if (i != 0) n = std::min(mats[i - 1].rows, mats[i - 1].columns);
|
|
2692
|
+
while (true) {
|
|
2693
|
+
int minv = INT32_MAX;
|
|
2694
|
+
for (int j = 0; j < columns[i].n; ++j)
|
|
2695
|
+
if (colcopy[i][j] > last && colcopy[i][j] < minv) minv = colcopy[i][j];
|
|
2696
|
+
if (minv == INT32_MAX) break;
|
|
2697
|
+
degrees.push_back(minv);
|
|
2698
|
+
retvals.push_back(std::vector<int>());
|
|
2699
|
+
std::vector<BigInt> nums(n);
|
|
2700
|
+
int nnum = 0;
|
|
2701
|
+
for (int j = 0; j < n; ++j)
|
|
2702
|
+
if (colcopy[i][j] == minv && !mats[i - 1].matrix[j][j].isZero()) nums[nnum++] = mats[i - 1].matrix[j][j].abs();
|
|
2703
|
+
nentries.push_back(nnum);
|
|
2704
|
+
if (nnum != 0) {
|
|
2705
|
+
bool done = false;
|
|
2706
|
+
int pk = 1;
|
|
2707
|
+
while (!done) {
|
|
2708
|
+
int p = 0;
|
|
2709
|
+
pk++;
|
|
2710
|
+
for (;; pk++) {
|
|
2711
|
+
for (int j = 2; j <= pk; ++j) if (pk % j == 0) { p = j; break; }
|
|
2712
|
+
int tmp = pk;
|
|
2713
|
+
while (tmp > 1) {
|
|
2714
|
+
if (tmp % p == 0) tmp /= p;
|
|
2715
|
+
else break;
|
|
2716
|
+
}
|
|
2717
|
+
if (tmp == 1) break;
|
|
2718
|
+
}
|
|
2719
|
+
for (int j = 0; j < nnum; ++j) {
|
|
2720
|
+
uint32_t rem = 0;
|
|
2721
|
+
BigInt div = nums[j].div_small(pk, &rem);
|
|
2722
|
+
if (rem == 0 && nums[j].mod_small(pk * p) != 0) {
|
|
2723
|
+
retvals.back().push_back(pk);
|
|
2724
|
+
nums[j] = div;
|
|
2725
|
+
}
|
|
2726
|
+
}
|
|
2727
|
+
done = true;
|
|
2728
|
+
for (int j = 0; j < nnum; ++j) if (!(nums[j] == BI_ONE)) { done = false; break; }
|
|
2729
|
+
}
|
|
2730
|
+
}
|
|
2731
|
+
last = minv;
|
|
2732
|
+
}
|
|
2733
|
+
if (i != ncolumns - 1) {
|
|
2734
|
+
mats[i].toSmithForm();
|
|
2735
|
+
if (!mats[i].isDiagonal()) throw std::runtime_error("matrix is not diagonal");
|
|
2736
|
+
}
|
|
2737
|
+
for (size_t j = 0; j < degrees.size(); ++j) {
|
|
2738
|
+
int nzeros = 0;
|
|
2739
|
+
if (i != ncolumns - 1) {
|
|
2740
|
+
for (int k = 0; k < columns[i].n; ++k)
|
|
2741
|
+
if (colcopy[i][k] == degrees[j] && (k >= mats[i].rows || k >= mats[i].columns || mats[i].matrix[k][k].isZero())) nzeros++;
|
|
2742
|
+
} else {
|
|
2743
|
+
for (int k = 0; k < columns[i].n; ++k) if (colcopy[i][k] == degrees[j]) nzeros++;
|
|
2744
|
+
}
|
|
2745
|
+
nzeros -= nentries[j];
|
|
2746
|
+
for (int k = 0; k < nzeros; ++k) retvals[j].push_back(0);
|
|
2747
|
+
if (!retvals[j].empty()) {
|
|
2748
|
+
std::sort(retvals[j].begin(), retvals[j].end());
|
|
2749
|
+
if (!ret.empty()) ret += " + ";
|
|
2750
|
+
ret += "q^" + std::to_string(degrees[j]) + "*t^" + std::to_string(i + startnum) + "*Z[";
|
|
2751
|
+
for (size_t k = 0; k < retvals[j].size(); ++k) {
|
|
2752
|
+
ret += std::to_string(retvals[j][k]);
|
|
2753
|
+
ret += (k + 1 == retvals[j].size()) ? "]" : ",";
|
|
2754
|
+
}
|
|
2755
|
+
}
|
|
2756
|
+
}
|
|
2757
|
+
}
|
|
2758
|
+
return ret;
|
|
2759
|
+
}
|
|
2760
|
+
|
|
2761
|
+
static int chooseXingRecursive(const std::vector<int>& edges, const std::vector<std::vector<int> >& pd,
|
|
2762
|
+
std::vector<char>& in, std::vector<char>& done, int depth, std::vector<int>& retmax) {
|
|
2763
|
+
int nedges = static_cast<int>(edges.size());
|
|
2764
|
+
int best = -1, nconbest = -1;
|
|
2765
|
+
std::vector<int> rbest(depth, 0);
|
|
2766
|
+
for (size_t i = 0; i < pd.size(); ++i) if (!done[i]) {
|
|
2767
|
+
int ncon = 0;
|
|
2768
|
+
for (int j = 0; j < 4; ++j) if (in[pd[i][j]]) ncon++;
|
|
2769
|
+
if (ncon == 0 && nedges != 0) continue;
|
|
2770
|
+
if (ncon < nconbest) continue;
|
|
2771
|
+
int start;
|
|
2772
|
+
for (start = 0; start < nedges; ++start) {
|
|
2773
|
+
bool found = false;
|
|
2774
|
+
for (int k = 0; k < 4; ++k) if (pd[i][k] == edges[start]) { found = true; break; }
|
|
2775
|
+
if (!found) break;
|
|
2776
|
+
}
|
|
2777
|
+
if (start == nedges) start = 0;
|
|
2778
|
+
for (int k = 0; k < nedges; ++k) {
|
|
2779
|
+
bool found = false;
|
|
2780
|
+
for (int l = 0; l < 4; ++l) if (pd[i][l] == edges[(start + k) % nedges]) { found = true; start = (start + k) % nedges; break; }
|
|
2781
|
+
if (found) break;
|
|
2782
|
+
}
|
|
2783
|
+
int kstart = 0;
|
|
2784
|
+
if (nedges != 0) for (kstart = 0; kstart < 4; ++kstart) if (pd[i][kstart] == edges[start]) break;
|
|
2785
|
+
bool good = true;
|
|
2786
|
+
for (int k = 0; k < ncon; ++k) if (pd[i][(kstart + 4 - k) % 4] != edges[(start + k) % nedges]) { good = false; break; }
|
|
2787
|
+
if (!good) continue;
|
|
2788
|
+
std::vector<int> getn(depth, 0);
|
|
2789
|
+
if (depth != 0) {
|
|
2790
|
+
kstart = (kstart + 4 - ncon + 1) % 4;
|
|
2791
|
+
std::vector<int> newedges(nedges + 4 - 2 * ncon);
|
|
2792
|
+
int j = 0;
|
|
2793
|
+
for (; j < nedges - ncon; ++j) newedges[j] = edges[(start + ncon + j) % nedges];
|
|
2794
|
+
for (int k = 0; k < 4 - ncon; ++k, ++j) newedges[j] = pd[i][(kstart + ncon + k) % 4];
|
|
2795
|
+
done[i] = 1;
|
|
2796
|
+
std::vector<char> previn(4);
|
|
2797
|
+
for (int k = 0; k < 4; ++k) { previn[k] = in[pd[i][k]]; in[pd[i][k]] = 1; }
|
|
2798
|
+
chooseXingRecursive(newedges, pd, in, done, depth - 1, getn);
|
|
2799
|
+
done[i] = 0;
|
|
2800
|
+
for (int k = 0; k < 4; ++k) in[pd[i][k]] = previn[k];
|
|
2801
|
+
}
|
|
2802
|
+
bool better = false;
|
|
2803
|
+
if (ncon > nconbest) better = true;
|
|
2804
|
+
else if (ncon == nconbest) for (int j = 0; j < depth; ++j) {
|
|
2805
|
+
if (getn[j] > rbest[j]) { better = true; break; }
|
|
2806
|
+
if (getn[j] < rbest[j]) break;
|
|
2807
|
+
}
|
|
2808
|
+
if (better) { nconbest = ncon; rbest = getn; best = static_cast<int>(i); }
|
|
2809
|
+
}
|
|
2810
|
+
if (best == -1) throw std::runtime_error("could not choose crossing");
|
|
2811
|
+
retmax[0] = nconbest;
|
|
2812
|
+
for (int i = 0; i < depth; ++i) retmax[i + 1] = rbest[i];
|
|
2813
|
+
return best;
|
|
2814
|
+
}
|
|
2815
|
+
|
|
2816
|
+
static int takeNextCrossing(const std::vector<int>&, const std::vector<std::vector<int> >& pd,
|
|
2817
|
+
std::vector<char>&, std::vector<char>& done, int, std::vector<int>&) {
|
|
2818
|
+
for (size_t i = 0; i < pd.size(); ++i) if (!done[i]) return static_cast<int>(i);
|
|
2819
|
+
throw std::runtime_error("no crossing left");
|
|
2820
|
+
}
|
|
2821
|
+
|
|
2822
|
+
static std::vector<int> getSigns(const std::vector<std::vector<int> >& pd) {
|
|
2823
|
+
std::vector<int> xsigns(pd.size());
|
|
2824
|
+
for (size_t i = 0; i < pd.size(); ++i) {
|
|
2825
|
+
if (pd[i][1] - pd[i][3] == 1 || pd[i][3] - pd[i][1] > 1) xsigns[i] = 1;
|
|
2826
|
+
else if (pd[i][3] - pd[i][1] == 1 || pd[i][1] - pd[i][3] > 1) xsigns[i] = -1;
|
|
2827
|
+
else throw std::runtime_error("error finding crossing signs");
|
|
2828
|
+
}
|
|
2829
|
+
return xsigns;
|
|
2830
|
+
}
|
|
2831
|
+
|
|
2832
|
+
static bool sanityPD(const PDCode& pd) {
|
|
2833
|
+
std::map<int, int> counts;
|
|
2834
|
+
for (size_t i = 0; i < pd.size(); ++i) {
|
|
2835
|
+
if (pd[i].size() != 4) return false;
|
|
2836
|
+
for (int v : pd[i]) counts[v]++;
|
|
2837
|
+
}
|
|
2838
|
+
for (std::map<int, int>::const_iterator it = counts.begin(); it != counts.end(); ++it) {
|
|
2839
|
+
if (it->second != 2) return false;
|
|
2840
|
+
}
|
|
2841
|
+
return true;
|
|
2842
|
+
}
|
|
2843
|
+
|
|
2844
|
+
static int uniqueCount(const std::vector<int>& crossing) {
|
|
2845
|
+
return static_cast<int>(std::set<int>(crossing.begin(), crossing.end()).size());
|
|
2846
|
+
}
|
|
2847
|
+
|
|
2848
|
+
static std::vector<int> valueSet(const PDCode& pd) {
|
|
2849
|
+
std::set<int> values;
|
|
2850
|
+
for (size_t i = 0; i < pd.size(); ++i) {
|
|
2851
|
+
for (int v : pd[i]) values.insert(v);
|
|
2852
|
+
}
|
|
2853
|
+
return std::vector<int>(values.begin(), values.end());
|
|
2854
|
+
}
|
|
2855
|
+
|
|
2856
|
+
static bool containsValue(const std::vector<int>& xs, int v) {
|
|
2857
|
+
return std::find(xs.begin(), xs.end(), v) != xs.end();
|
|
2858
|
+
}
|
|
2859
|
+
|
|
2860
|
+
static void addUndirectedVectorEdge(std::map<int, std::vector<int> >& graph, int a, int b) {
|
|
2861
|
+
std::vector<int>& ga = graph[a];
|
|
2862
|
+
if (std::find(ga.begin(), ga.end(), b) == ga.end()) ga.push_back(b);
|
|
2863
|
+
std::vector<int>& gb = graph[b];
|
|
2864
|
+
if (std::find(gb.begin(), gb.end(), a) == gb.end()) gb.push_back(a);
|
|
2865
|
+
}
|
|
2866
|
+
|
|
2867
|
+
static void addUndirectedSetEdge(std::map<int, std::set<int> >& graph, int a, int b) {
|
|
2868
|
+
graph[a].insert(b);
|
|
2869
|
+
graph[b].insert(a);
|
|
2870
|
+
}
|
|
2871
|
+
|
|
2872
|
+
static std::map<int, std::vector<int> > pdAdjacencyVector(const PDCode& pd) {
|
|
2873
|
+
std::map<int, std::vector<int> > graph;
|
|
2874
|
+
for (size_t i = 0; i < pd.size(); ++i) {
|
|
2875
|
+
addUndirectedVectorEdge(graph, pd[i][0], pd[i][2]);
|
|
2876
|
+
addUndirectedVectorEdge(graph, pd[i][1], pd[i][3]);
|
|
2877
|
+
}
|
|
2878
|
+
return graph;
|
|
2879
|
+
}
|
|
2880
|
+
|
|
2881
|
+
static PDCode renumberR1Order(PDCode pd) {
|
|
2882
|
+
if (pd.empty()) return pd;
|
|
2883
|
+
std::vector<int> values = valueSet(pd);
|
|
2884
|
+
std::map<int, std::vector<int> > graph = pdAdjacencyVector(pd);
|
|
2885
|
+
std::vector<int> visitOrder;
|
|
2886
|
+
for (int value : values) {
|
|
2887
|
+
if (containsValue(visitOrder, value)) continue;
|
|
2888
|
+
if (graph.find(value) == graph.end()) throw std::runtime_error("invalid PD graph during R1 renumbering");
|
|
2889
|
+
visitOrder.push_back(value);
|
|
2890
|
+
while (true) {
|
|
2891
|
+
int top = visitOrder.back();
|
|
2892
|
+
std::vector<int> neighbors = graph[top];
|
|
2893
|
+
std::sort(neighbors.begin(), neighbors.end());
|
|
2894
|
+
bool advanced = false;
|
|
2895
|
+
for (int nxt : neighbors) {
|
|
2896
|
+
if (!containsValue(visitOrder, nxt)) {
|
|
2897
|
+
visitOrder.push_back(nxt);
|
|
2898
|
+
advanced = true;
|
|
2899
|
+
break;
|
|
2900
|
+
}
|
|
2901
|
+
}
|
|
2902
|
+
if (!advanced) break;
|
|
2903
|
+
}
|
|
2904
|
+
}
|
|
2905
|
+
std::map<int, int> newId;
|
|
2906
|
+
for (size_t i = 0; i < visitOrder.size(); ++i) newId[visitOrder[i]] = static_cast<int>(i);
|
|
2907
|
+
for (size_t i = 0; i < pd.size(); ++i) {
|
|
2908
|
+
for (int& v : pd[i]) v = newId[v];
|
|
2909
|
+
}
|
|
2910
|
+
return pd;
|
|
2911
|
+
}
|
|
2912
|
+
|
|
2913
|
+
static PDCode eraseR1(PDCode pd) {
|
|
2914
|
+
if (!sanityPD(pd)) throw std::runtime_error("invalid PD code: every arc label must appear exactly twice");
|
|
2915
|
+
bool hasR1 = true;
|
|
2916
|
+
while (hasR1) {
|
|
2917
|
+
hasR1 = false;
|
|
2918
|
+
for (size_t i = 0; i < pd.size(); ++i) {
|
|
2919
|
+
if (uniqueCount(pd[i]) <= 3) {
|
|
2920
|
+
std::vector<int> crossing = pd[i];
|
|
2921
|
+
pd.erase(pd.begin() + static_cast<std::ptrdiff_t>(i));
|
|
2922
|
+
std::vector<int> singles;
|
|
2923
|
+
for (int v : crossing) {
|
|
2924
|
+
if (std::count(crossing.begin(), crossing.end(), v) == 1) singles.push_back(v);
|
|
2925
|
+
}
|
|
2926
|
+
if (singles.size() == 2) {
|
|
2927
|
+
for (size_t r = 0; r < pd.size(); ++r) {
|
|
2928
|
+
for (int& v : pd[r]) {
|
|
2929
|
+
if (v == singles[0]) v = singles[1];
|
|
2930
|
+
}
|
|
2931
|
+
}
|
|
2932
|
+
}
|
|
2933
|
+
hasR1 = true;
|
|
2934
|
+
break;
|
|
2935
|
+
}
|
|
2936
|
+
}
|
|
2937
|
+
}
|
|
2938
|
+
return renumberR1Order(pd);
|
|
2939
|
+
}
|
|
2940
|
+
|
|
2941
|
+
static std::map<int, std::set<int> > baseGraph(const PDCode& pd) {
|
|
2942
|
+
std::map<int, std::set<int> > graph;
|
|
2943
|
+
for (size_t i = 0; i < pd.size(); ++i) {
|
|
2944
|
+
int crossingNode = -static_cast<int>(i) - 1;
|
|
2945
|
+
for (int v : pd[i]) addUndirectedSetEdge(graph, v, crossingNode);
|
|
2946
|
+
}
|
|
2947
|
+
return graph;
|
|
2948
|
+
}
|
|
2949
|
+
|
|
2950
|
+
static int graphComponentCount(const PDCode& pd) {
|
|
2951
|
+
std::map<int, std::set<int> > graph = baseGraph(pd);
|
|
2952
|
+
std::set<int> visited;
|
|
2953
|
+
int count = 0;
|
|
2954
|
+
for (std::map<int, std::set<int> >::const_iterator it = graph.begin(); it != graph.end(); ++it) {
|
|
2955
|
+
int start = it->first;
|
|
2956
|
+
if (visited.count(start)) continue;
|
|
2957
|
+
++count;
|
|
2958
|
+
std::vector<int> stack(1, start);
|
|
2959
|
+
visited.insert(start);
|
|
2960
|
+
while (!stack.empty()) {
|
|
2961
|
+
int node = stack.back();
|
|
2962
|
+
stack.pop_back();
|
|
2963
|
+
const std::set<int>& neighbors = graph[node];
|
|
2964
|
+
for (std::set<int>::const_iterator jt = neighbors.begin(); jt != neighbors.end(); ++jt) {
|
|
2965
|
+
if (!visited.count(*jt)) {
|
|
2966
|
+
visited.insert(*jt);
|
|
2967
|
+
stack.push_back(*jt);
|
|
2968
|
+
}
|
|
2969
|
+
}
|
|
2970
|
+
}
|
|
2971
|
+
}
|
|
2972
|
+
return count;
|
|
2973
|
+
}
|
|
2974
|
+
|
|
2975
|
+
static bool isNugatory(const PDCode& pd, size_t index) {
|
|
2976
|
+
if (uniqueCount(pd[index]) != 4) throw std::runtime_error("nugatory check requires R1-free PD code");
|
|
2977
|
+
PDCode bad = pd;
|
|
2978
|
+
bad.erase(bad.begin() + static_cast<std::ptrdiff_t>(index));
|
|
2979
|
+
return graphComponentCount(bad) > graphComponentCount(pd);
|
|
2980
|
+
}
|
|
2981
|
+
|
|
2982
|
+
static int findNugatory(const PDCode& pd) {
|
|
2983
|
+
for (size_t i = 0; i < pd.size(); ++i) {
|
|
2984
|
+
if (isNugatory(pd, i)) return static_cast<int>(i);
|
|
2985
|
+
}
|
|
2986
|
+
return -1;
|
|
2987
|
+
}
|
|
2988
|
+
|
|
2989
|
+
static int rawArcValue(int v) {
|
|
2990
|
+
return v;
|
|
2991
|
+
}
|
|
2992
|
+
|
|
2993
|
+
static void addPreNextEdge(std::map<int, int>& pre, std::map<int, int>& nxt, int v1, int v2) {
|
|
2994
|
+
int raw1 = rawArcValue(v1);
|
|
2995
|
+
int raw2 = rawArcValue(v2);
|
|
2996
|
+
int preVal;
|
|
2997
|
+
int nxtVal;
|
|
2998
|
+
if (std::abs(raw1 - raw2) == 1) {
|
|
2999
|
+
preVal = raw1 < raw2 ? v1 : v2;
|
|
3000
|
+
nxtVal = raw1 < raw2 ? v2 : v1;
|
|
3001
|
+
} else {
|
|
3002
|
+
preVal = raw1 < raw2 ? v2 : v1;
|
|
3003
|
+
nxtVal = raw1 < raw2 ? v1 : v2;
|
|
3004
|
+
}
|
|
3005
|
+
pre[nxtVal] = preVal;
|
|
3006
|
+
nxt[preVal] = nxtVal;
|
|
3007
|
+
}
|
|
3008
|
+
|
|
3009
|
+
static std::pair<std::map<int, int>, std::map<int, int> > getPreNext(const PDCode& pd) {
|
|
3010
|
+
if (!sanityPD(pd)) throw std::runtime_error("invalid PD code while computing pre/next maps");
|
|
3011
|
+
std::map<int, int> pre;
|
|
3012
|
+
std::map<int, int> nxt;
|
|
3013
|
+
for (size_t i = 0; i < pd.size(); ++i) {
|
|
3014
|
+
if (uniqueCount(pd[i]) > 2) {
|
|
3015
|
+
addPreNextEdge(pre, nxt, pd[i][0], pd[i][2]);
|
|
3016
|
+
addPreNextEdge(pre, nxt, pd[i][1], pd[i][3]);
|
|
3017
|
+
} else {
|
|
3018
|
+
std::vector<int> values(pd[i].begin(), pd[i].end());
|
|
3019
|
+
std::sort(values.begin(), values.end());
|
|
3020
|
+
values.erase(std::unique(values.begin(), values.end()), values.end());
|
|
3021
|
+
if (values.size() != 2) throw std::runtime_error("invalid two-value crossing in pre/next maps");
|
|
3022
|
+
pre[values[0]] = values[1];
|
|
3023
|
+
nxt[values[0]] = values[1];
|
|
3024
|
+
pre[values[1]] = values[0];
|
|
3025
|
+
nxt[values[1]] = values[0];
|
|
3026
|
+
}
|
|
3027
|
+
}
|
|
3028
|
+
std::vector<int> nums = valueSet(pd);
|
|
3029
|
+
for (int num : nums) {
|
|
3030
|
+
if (!pre.count(num)) {
|
|
3031
|
+
if (!nxt.count(num)) throw std::runtime_error("broken PD pre/next map");
|
|
3032
|
+
pre[num] = nxt[num];
|
|
3033
|
+
}
|
|
3034
|
+
if (!nxt.count(num)) nxt[num] = pre[num];
|
|
3035
|
+
}
|
|
3036
|
+
return std::make_pair(pre, nxt);
|
|
3037
|
+
}
|
|
3038
|
+
|
|
3039
|
+
static PDCode replaceArcValue(PDCode pd, int from, int to) {
|
|
3040
|
+
for (size_t i = 0; i < pd.size(); ++i) {
|
|
3041
|
+
for (int& v : pd[i]) {
|
|
3042
|
+
if (v == from) v = to;
|
|
3043
|
+
}
|
|
3044
|
+
}
|
|
3045
|
+
return pd;
|
|
3046
|
+
}
|
|
3047
|
+
|
|
3048
|
+
static PDCode renumberFullDfs(PDCode pd) {
|
|
3049
|
+
if (pd.empty()) return pd;
|
|
3050
|
+
std::vector<int> nums = valueSet(pd);
|
|
3051
|
+
std::map<int, std::set<int> > graph;
|
|
3052
|
+
for (size_t i = 0; i < pd.size(); ++i) {
|
|
3053
|
+
addUndirectedSetEdge(graph, pd[i][0], pd[i][2]);
|
|
3054
|
+
addUndirectedSetEdge(graph, pd[i][1], pd[i][3]);
|
|
3055
|
+
}
|
|
3056
|
+
std::set<int> visited;
|
|
3057
|
+
std::map<int, int> newNum;
|
|
3058
|
+
std::function<void(int)> dfs = [&](int num) {
|
|
3059
|
+
if (visited.count(num)) return;
|
|
3060
|
+
int id = static_cast<int>(visited.size());
|
|
3061
|
+
visited.insert(num);
|
|
3062
|
+
newNum[num] = id;
|
|
3063
|
+
if (!graph.count(num)) throw std::runtime_error("invalid PD graph during renumbering");
|
|
3064
|
+
for (std::set<int>::const_iterator it = graph[num].begin(); it != graph[num].end(); ++it) dfs(*it);
|
|
3065
|
+
};
|
|
3066
|
+
for (int num : nums) {
|
|
3067
|
+
if (!visited.count(num)) dfs(num);
|
|
3068
|
+
}
|
|
3069
|
+
if (newNum.size() != nums.size()) throw std::runtime_error("PD renumbering failed");
|
|
3070
|
+
for (size_t i = 0; i < pd.size(); ++i) {
|
|
3071
|
+
for (int& v : pd[i]) v = newNum[v];
|
|
3072
|
+
}
|
|
3073
|
+
return pd;
|
|
3074
|
+
}
|
|
3075
|
+
|
|
3076
|
+
static PDCode eraseOneNugatory(PDCode pd, size_t index) {
|
|
3077
|
+
if (uniqueCount(pd[index]) != 4) throw std::runtime_error("nugatory erase requires R1-free PD code");
|
|
3078
|
+
int ax = pd[index][0];
|
|
3079
|
+
int bx = pd[index][1];
|
|
3080
|
+
int cx = pd[index][2];
|
|
3081
|
+
int dx = pd[index][3];
|
|
3082
|
+
std::map<int, int> nxt = getPreNext(pd).second;
|
|
3083
|
+
std::vector<int> loop(1, ax);
|
|
3084
|
+
size_t guard = valueSet(pd).size() + 1;
|
|
3085
|
+
while (true) {
|
|
3086
|
+
if (!nxt.count(loop.back())) throw std::runtime_error("broken loop while erasing nugatory crossing");
|
|
3087
|
+
int next = nxt[loop.back()];
|
|
3088
|
+
loop.push_back(next);
|
|
3089
|
+
if (next == ax) {
|
|
3090
|
+
loop.pop_back();
|
|
3091
|
+
break;
|
|
3092
|
+
}
|
|
3093
|
+
if (loop.size() > guard) throw std::runtime_error("failed to close PD loop while erasing nugatory crossing");
|
|
3094
|
+
}
|
|
3095
|
+
std::set<int> loopSet(loop.begin(), loop.end());
|
|
3096
|
+
if (!loopSet.count(ax) || !loopSet.count(bx) || !loopSet.count(cx) || !loopSet.count(dx)) {
|
|
3097
|
+
throw std::runtime_error("nugatory crossing arcs are not in one component");
|
|
3098
|
+
}
|
|
3099
|
+
PDCode bad = pd;
|
|
3100
|
+
bad.erase(bad.begin() + static_cast<std::ptrdiff_t>(index));
|
|
3101
|
+
bad = replaceArcValue(bad, ax, cx);
|
|
3102
|
+
bad = replaceArcValue(bad, dx, bx);
|
|
3103
|
+
return renumberFullDfs(bad);
|
|
3104
|
+
}
|
|
3105
|
+
|
|
3106
|
+
static PDCode simplifyPDCode(PDCode pd) {
|
|
3107
|
+
pd = eraseR1(pd);
|
|
3108
|
+
while (true) {
|
|
3109
|
+
int index = findNugatory(pd);
|
|
3110
|
+
if (index < 0) break;
|
|
3111
|
+
pd = eraseOneNugatory(pd, static_cast<size_t>(index));
|
|
3112
|
+
}
|
|
3113
|
+
return pd;
|
|
3114
|
+
}
|
|
3115
|
+
|
|
3116
|
+
static int komplexSize(const Komplex& k) {
|
|
3117
|
+
int total = 0;
|
|
3118
|
+
for (auto& c : k.columns) total += c.n;
|
|
3119
|
+
return total;
|
|
3120
|
+
}
|
|
3121
|
+
|
|
3122
|
+
static Komplex generateFast(const std::vector<std::vector<int> >& pd, const std::vector<int>& xsigns) {
|
|
3123
|
+
KH_PROFILE(generateFast);
|
|
3124
|
+
if (pd.empty()) {
|
|
3125
|
+
Komplex kom(1);
|
|
3126
|
+
kom.columns[0] = SmoothingColumn(1);
|
|
3127
|
+
kom.columns[0].smoothings[0] = cacheCap(Cap(0, 1));
|
|
3128
|
+
kom.reduce();
|
|
3129
|
+
return kom;
|
|
3130
|
+
}
|
|
3131
|
+
std::vector<char> in(pd.size() * 4, 0), done(pd.size(), 0);
|
|
3132
|
+
std::vector<std::vector<int> > pd1(1, std::vector<int>{0,1,2,3});
|
|
3133
|
+
Komplex kplus(pd1, std::vector<int>{1}, 4);
|
|
3134
|
+
Komplex kminus(pd1, std::vector<int>{-1}, 4);
|
|
3135
|
+
std::vector<int> edges;
|
|
3136
|
+
int firstdepth = pd.size() > 4 ? 3 : static_cast<int>(pd.size()) - 1;
|
|
3137
|
+
std::vector<int> firstdummy(firstdepth + 1, 0);
|
|
3138
|
+
int first = g_options.reorderCrossings ? chooseXingRecursive(edges, pd, in, done, firstdepth, firstdummy)
|
|
3139
|
+
: takeNextCrossing(edges, pd, in, done, firstdepth, firstdummy);
|
|
3140
|
+
Komplex kom = xsigns[first] == 1 ? kplus : kminus;
|
|
3141
|
+
edges = pd[first];
|
|
3142
|
+
int nedges = 4;
|
|
3143
|
+
done[first] = 1;
|
|
3144
|
+
for (int i = 0; i < 4; ++i) in[pd[first][i]] = 1;
|
|
3145
|
+
for (size_t i = 1; i < pd.size(); ++i) {
|
|
3146
|
+
int depth = static_cast<int>(pd.size() - i - 1);
|
|
3147
|
+
if (depth > 3) depth = 3;
|
|
3148
|
+
std::vector<int> dummy(depth + 1, 0);
|
|
3149
|
+
int best = g_options.reorderCrossings ? chooseXingRecursive(edges, pd, in, done, depth, dummy)
|
|
3150
|
+
: takeNextCrossing(edges, pd, in, done, depth, dummy);
|
|
3151
|
+
int nbest = 0;
|
|
3152
|
+
for (int j = 0; j < 4; ++j) if (in[pd[best][j]]) nbest++;
|
|
3153
|
+
int start;
|
|
3154
|
+
for (start = 0; start < nedges; ++start) {
|
|
3155
|
+
bool found = false;
|
|
3156
|
+
for (int j = 0; j < 4; ++j) if (pd[best][j] == edges[start]) { found = true; break; }
|
|
3157
|
+
if (!found) break;
|
|
3158
|
+
}
|
|
3159
|
+
if (start == nedges) start = 0;
|
|
3160
|
+
for (int j = 0; j < nedges; ++j) {
|
|
3161
|
+
bool found = false;
|
|
3162
|
+
for (int k = 0; k < 4; ++k) if (pd[best][k] == edges[(start + j) % nedges]) { found = true; start = (start + j) % nedges; break; }
|
|
3163
|
+
if (found) break;
|
|
3164
|
+
}
|
|
3165
|
+
int kstart = -1;
|
|
3166
|
+
for (int j = 0; j < 4; ++j) if (pd[best][j] == edges[start]) { kstart = j; break; }
|
|
3167
|
+
for (int j = 0; j < nbest; ++j) if (pd[best][(-j + kstart + 4) % 4] != edges[(start + j) % nedges])
|
|
3168
|
+
throw std::runtime_error("ordered crossing prefix is not a simply connected tangle");
|
|
3169
|
+
kstart = (kstart + 4 - nbest + 1) % 4;
|
|
3170
|
+
if (g_options.progress) std::cerr << "add crossing " << (i + 1) << "/" << pd.size() << " girth: " << nedges << "\n";
|
|
3171
|
+
kom = kom.compose(start, xsigns[best] == 1 ? kplus : kminus, kstart, nbest);
|
|
3172
|
+
flushCobCache();
|
|
3173
|
+
kom.reduce();
|
|
3174
|
+
if (g_options.progress) std::cerr << "size: " << komplexSize(kom) << "\n";
|
|
3175
|
+
std::vector<int> newedges(nedges + 4 - 2 * nbest);
|
|
3176
|
+
int n = 0;
|
|
3177
|
+
for (; n < nedges - nbest; ++n) newedges[n] = edges[(start + nbest + n) % nedges];
|
|
3178
|
+
for (int j = 0; j < 4 - nbest; ++j, ++n) newedges[n] = pd[best][(kstart + nbest + j) % 4];
|
|
3179
|
+
edges.swap(newedges);
|
|
3180
|
+
nedges = static_cast<int>(edges.size());
|
|
3181
|
+
done[best] = 1;
|
|
3182
|
+
for (int j = 0; j < 4; ++j) in[pd[best][j]] = 1;
|
|
3183
|
+
flushCobCache();
|
|
3184
|
+
}
|
|
3185
|
+
return kom;
|
|
3186
|
+
}
|
|
3187
|
+
|
|
3188
|
+
static std::vector<std::vector<int> > parsePDLine(const std::string& line) {
|
|
3189
|
+
std::vector<std::vector<int> > pd;
|
|
3190
|
+
size_t pos = 0;
|
|
3191
|
+
while (true) {
|
|
3192
|
+
pos = line.find("X[", pos);
|
|
3193
|
+
if (pos == std::string::npos) break;
|
|
3194
|
+
pos += 2;
|
|
3195
|
+
std::vector<int> nums;
|
|
3196
|
+
while (pos < line.size() && line[pos] != ']') {
|
|
3197
|
+
while (pos < line.size() && !(line[pos] == '-' || std::isdigit(static_cast<unsigned char>(line[pos])))) ++pos;
|
|
3198
|
+
if (pos >= line.size() || line[pos] == ']') break;
|
|
3199
|
+
int sign = 1;
|
|
3200
|
+
if (line[pos] == '-') { sign = -1; ++pos; }
|
|
3201
|
+
int v = 0;
|
|
3202
|
+
while (pos < line.size() && std::isdigit(static_cast<unsigned char>(line[pos]))) v = v * 10 + (line[pos++] - '0');
|
|
3203
|
+
nums.push_back(sign * v - 1);
|
|
3204
|
+
}
|
|
3205
|
+
if (nums.size() == 4) pd.push_back(nums);
|
|
3206
|
+
}
|
|
3207
|
+
if (!pd.empty()) return pd;
|
|
3208
|
+
pos = 0;
|
|
3209
|
+
while (true) {
|
|
3210
|
+
pos = line.find('[', pos);
|
|
3211
|
+
if (pos == std::string::npos) break;
|
|
3212
|
+
++pos;
|
|
3213
|
+
std::vector<int> nums;
|
|
3214
|
+
while (pos < line.size() && line[pos] != ']') {
|
|
3215
|
+
while (pos < line.size() && !(line[pos] == '-' || std::isdigit(static_cast<unsigned char>(line[pos])) || line[pos] == ']')) ++pos;
|
|
3216
|
+
if (pos >= line.size() || line[pos] == ']') break;
|
|
3217
|
+
int sign = 1;
|
|
3218
|
+
if (line[pos] == '-') { sign = -1; ++pos; }
|
|
3219
|
+
int v = 0;
|
|
3220
|
+
while (pos < line.size() && std::isdigit(static_cast<unsigned char>(line[pos]))) v = v * 10 + (line[pos++] - '0');
|
|
3221
|
+
nums.push_back(sign * v - 1);
|
|
3222
|
+
}
|
|
3223
|
+
if (nums.size() == 4) pd.push_back(nums);
|
|
3224
|
+
}
|
|
3225
|
+
return pd;
|
|
3226
|
+
}
|
|
3227
|
+
|
|
3228
|
+
static std::vector<std::pair<std::string, std::vector<std::vector<int> > > > parsePDDocument(const std::string& text, const std::string& labelPrefix) {
|
|
3229
|
+
std::vector<std::pair<std::string, std::vector<std::vector<int> > > > result;
|
|
3230
|
+
size_t pos = 0;
|
|
3231
|
+
int index = 0;
|
|
3232
|
+
while (true) {
|
|
3233
|
+
size_t start = text.find("PD[", pos);
|
|
3234
|
+
if (start == std::string::npos) break;
|
|
3235
|
+
int depth = 0;
|
|
3236
|
+
size_t end = std::string::npos;
|
|
3237
|
+
for (size_t i = start + 2; i < text.size(); ++i) {
|
|
3238
|
+
if (text[i] == '[') ++depth;
|
|
3239
|
+
else if (text[i] == ']') {
|
|
3240
|
+
--depth;
|
|
3241
|
+
if (depth == 0) {
|
|
3242
|
+
end = i;
|
|
3243
|
+
break;
|
|
3244
|
+
}
|
|
3245
|
+
}
|
|
3246
|
+
}
|
|
3247
|
+
if (end == std::string::npos) throw std::runtime_error("unterminated PD[...] block in " + labelPrefix);
|
|
3248
|
+
std::string block = text.substr(start, end - start + 1);
|
|
3249
|
+
std::string label = labelPrefix;
|
|
3250
|
+
if (index > 0) label += "#" + std::to_string(index + 1);
|
|
3251
|
+
result.push_back(std::make_pair(label, parsePDLine(block)));
|
|
3252
|
+
++index;
|
|
3253
|
+
pos = end + 1;
|
|
3254
|
+
}
|
|
3255
|
+
if (result.empty()) {
|
|
3256
|
+
std::istringstream lines(text);
|
|
3257
|
+
std::string line;
|
|
3258
|
+
while (std::getline(lines, line)) {
|
|
3259
|
+
std::vector<std::vector<int> > pd = parsePDLine(line);
|
|
3260
|
+
if (pd.empty()) continue;
|
|
3261
|
+
std::string label = labelPrefix;
|
|
3262
|
+
size_t colon = line.find(':');
|
|
3263
|
+
if (colon != std::string::npos) {
|
|
3264
|
+
std::string lineLabel = line.substr(0, colon);
|
|
3265
|
+
size_t first = lineLabel.find_first_not_of(" \t\r\n");
|
|
3266
|
+
size_t last = lineLabel.find_last_not_of(" \t\r\n");
|
|
3267
|
+
if (first != std::string::npos && last != std::string::npos) label += ":" + lineLabel.substr(first, last - first + 1);
|
|
3268
|
+
} else if (!result.empty()) {
|
|
3269
|
+
label += "#" + std::to_string(result.size() + 1);
|
|
3270
|
+
}
|
|
3271
|
+
result.push_back(std::make_pair(label, pd));
|
|
3272
|
+
}
|
|
3273
|
+
}
|
|
3274
|
+
return result;
|
|
3275
|
+
}
|
|
3276
|
+
|
|
3277
|
+
static std::vector<std::pair<std::string, std::vector<std::vector<int> > > > readPDFile(const std::string& path) {
|
|
3278
|
+
std::ifstream in(path.c_str(), std::ios::in | std::ios::binary);
|
|
3279
|
+
if (!in) throw std::runtime_error("cannot open " + path);
|
|
3280
|
+
std::ostringstream buffer;
|
|
3281
|
+
buffer << in.rdbuf();
|
|
3282
|
+
std::vector<std::pair<std::string, std::vector<std::vector<int> > > > result = parsePDDocument(buffer.str(), path);
|
|
3283
|
+
if (result.size() == 1) result[0].first = path;
|
|
3284
|
+
return result;
|
|
3285
|
+
}
|
|
3286
|
+
|
|
3287
|
+
static bool isDirectory(const std::string& path) {
|
|
3288
|
+
struct stat st;
|
|
3289
|
+
return stat(path.c_str(), &st) == 0 && (st.st_mode & S_IFDIR);
|
|
3290
|
+
}
|
|
3291
|
+
|
|
3292
|
+
static bool hasPdExtension(const std::string& name) {
|
|
3293
|
+
std::string lower = name;
|
|
3294
|
+
std::transform(lower.begin(), lower.end(), lower.begin(), [](char c){ return static_cast<char>(std::tolower(static_cast<unsigned char>(c))); });
|
|
3295
|
+
return lower.size() >= 4 && (lower.substr(lower.size() - 4) == ".txt" || lower.substr(lower.size() - 3) == ".pd");
|
|
3296
|
+
}
|
|
3297
|
+
|
|
3298
|
+
static std::vector<std::string> listInputFiles(const std::string& dir) {
|
|
3299
|
+
std::vector<std::string> files;
|
|
3300
|
+
DIR* d = opendir(dir.c_str());
|
|
3301
|
+
if (!d) throw std::runtime_error("cannot open directory " + dir);
|
|
3302
|
+
while (dirent* ent = readdir(d)) {
|
|
3303
|
+
std::string name = ent->d_name;
|
|
3304
|
+
if (name == "." || name == ".." || !hasPdExtension(name)) continue;
|
|
3305
|
+
std::string path = dir;
|
|
3306
|
+
if (!path.empty() && path[path.size() - 1] != '/' && path[path.size() - 1] != '\\') path += "/";
|
|
3307
|
+
path += name;
|
|
3308
|
+
if (!isDirectory(path)) files.push_back(path);
|
|
3309
|
+
}
|
|
3310
|
+
closedir(d);
|
|
3311
|
+
std::sort(files.begin(), files.end());
|
|
3312
|
+
return files;
|
|
3313
|
+
}
|
|
3314
|
+
|
|
3315
|
+
static std::string computePD(const std::vector<std::vector<int> >& pd) {
|
|
3316
|
+
flushCobCache();
|
|
3317
|
+
g_smallArena.reset();
|
|
3318
|
+
PDCode working = g_options.simplifyPD ? simplifyPDCode(pd) : pd;
|
|
3319
|
+
Komplex k = generateFast(working, getSigns(working));
|
|
3320
|
+
ProfileScope khScope(g_profile.kh);
|
|
3321
|
+
return k.KhForZ();
|
|
3322
|
+
}
|
|
3323
|
+
|
|
3324
|
+
static std::string formatPDCode(const PDCode& pd) {
|
|
3325
|
+
std::ostringstream out;
|
|
3326
|
+
out << "PD[";
|
|
3327
|
+
for (size_t i = 0; i < pd.size(); ++i) {
|
|
3328
|
+
if (i) out << ",";
|
|
3329
|
+
out << "X[";
|
|
3330
|
+
for (int j = 0; j < 4; ++j) {
|
|
3331
|
+
if (j) out << ",";
|
|
3332
|
+
out << (pd[i][j] + 1);
|
|
3333
|
+
}
|
|
3334
|
+
out << "]";
|
|
3335
|
+
}
|
|
3336
|
+
out << "]";
|
|
3337
|
+
return out.str();
|
|
3338
|
+
}
|
|
3339
|
+
|
|
3340
|
+
static std::string simplifiedPDString(const PDCode& pd) {
|
|
3341
|
+
PDCode working = g_options.simplifyPD ? simplifyPDCode(pd) : pd;
|
|
3342
|
+
return formatPDCode(working);
|
|
3343
|
+
}
|
|
3344
|
+
|
|
3345
|
+
} // namespace kh
|
|
3346
|
+
|
|
3347
|
+
#if defined(CPPKH_SHARED_LIBRARY)
|
|
3348
|
+
#if defined(_WIN32)
|
|
3349
|
+
#define CPPKH_API extern "C" __declspec(dllexport)
|
|
3350
|
+
#else
|
|
3351
|
+
#define CPPKH_API extern "C" __attribute__((visibility("default")))
|
|
3352
|
+
#endif
|
|
3353
|
+
|
|
3354
|
+
static thread_local std::string g_cppkhLastError;
|
|
3355
|
+
|
|
3356
|
+
static char* cppkhDuplicateString(const std::string& value) {
|
|
3357
|
+
char* out = static_cast<char*>(std::malloc(value.size() + 1));
|
|
3358
|
+
if (!out) {
|
|
3359
|
+
g_cppkhLastError = "out of memory";
|
|
3360
|
+
return nullptr;
|
|
3361
|
+
}
|
|
3362
|
+
std::memcpy(out, value.c_str(), value.size() + 1);
|
|
3363
|
+
return out;
|
|
3364
|
+
}
|
|
3365
|
+
|
|
3366
|
+
struct CppkhOptionsGuard {
|
|
3367
|
+
kh::Options oldOptions;
|
|
3368
|
+
CppkhOptionsGuard() : oldOptions(kh::g_options) {}
|
|
3369
|
+
~CppkhOptionsGuard() { kh::g_options = oldOptions; }
|
|
3370
|
+
};
|
|
3371
|
+
|
|
3372
|
+
CPPKH_API const char* cppkh_version() {
|
|
3373
|
+
return "cppkh/1";
|
|
3374
|
+
}
|
|
3375
|
+
|
|
3376
|
+
CPPKH_API const char* cppkh_last_error() {
|
|
3377
|
+
return g_cppkhLastError.c_str();
|
|
3378
|
+
}
|
|
3379
|
+
|
|
3380
|
+
CPPKH_API void cppkh_free(char* value) {
|
|
3381
|
+
std::free(value);
|
|
3382
|
+
}
|
|
3383
|
+
|
|
3384
|
+
CPPKH_API char* cppkh_compute_pd_ex(const char* pd_code, int simplify_pd, int reorder_crossings) {
|
|
3385
|
+
try {
|
|
3386
|
+
g_cppkhLastError.clear();
|
|
3387
|
+
if (!pd_code) throw std::runtime_error("pd_code is null");
|
|
3388
|
+
std::vector<std::pair<std::string, kh::PDCode> > parsed = kh::parsePDDocument(pd_code, "ctypes");
|
|
3389
|
+
if (parsed.empty()) throw std::runtime_error("no PD code found");
|
|
3390
|
+
if (parsed.size() != 1) throw std::runtime_error("cppkh_compute_pd_ex expects exactly one PD code");
|
|
3391
|
+
|
|
3392
|
+
CppkhOptionsGuard guard;
|
|
3393
|
+
kh::g_options.progress = false;
|
|
3394
|
+
kh::g_options.profile = false;
|
|
3395
|
+
kh::g_options.simplifyPD = simplify_pd != 0;
|
|
3396
|
+
kh::g_options.reorderCrossings = reorder_crossings != 0;
|
|
3397
|
+
std::string result = kh::computePD(parsed[0].second);
|
|
3398
|
+
return cppkhDuplicateString(result);
|
|
3399
|
+
} catch (const std::exception& e) {
|
|
3400
|
+
g_cppkhLastError = e.what();
|
|
3401
|
+
return nullptr;
|
|
3402
|
+
} catch (...) {
|
|
3403
|
+
g_cppkhLastError = "unknown error";
|
|
3404
|
+
return nullptr;
|
|
3405
|
+
}
|
|
3406
|
+
}
|
|
3407
|
+
|
|
3408
|
+
CPPKH_API char* cppkh_compute_pd(const char* pd_code) {
|
|
3409
|
+
return cppkh_compute_pd_ex(pd_code, 1, 1);
|
|
3410
|
+
}
|
|
3411
|
+
|
|
3412
|
+
CPPKH_API char* cppkh_simplify_pd(const char* pd_code) {
|
|
3413
|
+
try {
|
|
3414
|
+
g_cppkhLastError.clear();
|
|
3415
|
+
if (!pd_code) throw std::runtime_error("pd_code is null");
|
|
3416
|
+
std::vector<std::pair<std::string, kh::PDCode> > parsed = kh::parsePDDocument(pd_code, "ctypes");
|
|
3417
|
+
if (parsed.empty()) throw std::runtime_error("no PD code found");
|
|
3418
|
+
if (parsed.size() != 1) throw std::runtime_error("cppkh_simplify_pd expects exactly one PD code");
|
|
3419
|
+
CppkhOptionsGuard guard;
|
|
3420
|
+
kh::g_options.simplifyPD = true;
|
|
3421
|
+
std::string result = kh::simplifiedPDString(parsed[0].second);
|
|
3422
|
+
return cppkhDuplicateString(result);
|
|
3423
|
+
} catch (const std::exception& e) {
|
|
3424
|
+
g_cppkhLastError = e.what();
|
|
3425
|
+
return nullptr;
|
|
3426
|
+
} catch (...) {
|
|
3427
|
+
g_cppkhLastError = "unknown error";
|
|
3428
|
+
return nullptr;
|
|
3429
|
+
}
|
|
3430
|
+
}
|
|
3431
|
+
#endif
|
|
3432
|
+
|
|
3433
|
+
#ifndef CPPKH_SHARED_LIBRARY
|
|
3434
|
+
static void usage() {
|
|
3435
|
+
std::cout << "Usage: javakh_cpp [--pd-file FILE] [--pd-dir DIR] [--pd-code CODE] [--ordered] [--threads N|auto] [--quiet] [--profile] [--no-simplify-pd] [--print-simplified-pd]\n";
|
|
3436
|
+
std::cout << "Thread backend: " << KH_THREAD_BACKEND_NAME << "\n";
|
|
3437
|
+
std::cout << "Detected CPU threads: " << kh::detectHardwareThreads() << "\n";
|
|
3438
|
+
std::cout << "PD simplification: R1 removal then nugatory crossing removal is enabled by default.\n";
|
|
3439
|
+
}
|
|
3440
|
+
|
|
3441
|
+
int main(int argc, char** argv) {
|
|
3442
|
+
try {
|
|
3443
|
+
std::vector<std::string> files;
|
|
3444
|
+
std::vector<std::pair<std::string, std::vector<std::vector<int> > > > jobs;
|
|
3445
|
+
bool printSimplifiedPD = false;
|
|
3446
|
+
for (int i = 1; i < argc; ++i) {
|
|
3447
|
+
std::string a = argv[i];
|
|
3448
|
+
if (a == "--help" || a == "-h") { usage(); return 0; }
|
|
3449
|
+
else if (a == "--pd-file" || a == "-f") {
|
|
3450
|
+
if (++i >= argc) throw std::runtime_error("--pd-file needs a path");
|
|
3451
|
+
files.push_back(argv[i]);
|
|
3452
|
+
} else if (a == "--pd-dir" || a == "-d") {
|
|
3453
|
+
if (++i >= argc) throw std::runtime_error("--pd-dir needs a path");
|
|
3454
|
+
std::vector<std::string> more = kh::listInputFiles(argv[i]);
|
|
3455
|
+
files.insert(files.end(), more.begin(), more.end());
|
|
3456
|
+
} else if (a == "--pd-code" || a == "-c") {
|
|
3457
|
+
if (++i >= argc) throw std::runtime_error("--pd-code needs a PD[...] string");
|
|
3458
|
+
std::vector<std::pair<std::string, std::vector<std::vector<int> > > > parsed = kh::parsePDDocument(argv[i], "command-line");
|
|
3459
|
+
jobs.insert(jobs.end(), parsed.begin(), parsed.end());
|
|
3460
|
+
} else if (a == "--ordered" || a == "-O") kh::g_options.reorderCrossings = false;
|
|
3461
|
+
else if (a == "--quiet" || a == "-q") kh::g_options.progress = false;
|
|
3462
|
+
else if (a == "--profile") kh::g_options.profile = true;
|
|
3463
|
+
else if (a == "--no-simplify-pd" || a == "--raw-pd") kh::g_options.simplifyPD = false;
|
|
3464
|
+
else if (a == "--simplify-pd") kh::g_options.simplifyPD = true;
|
|
3465
|
+
else if (a == "--print-simplified-pd") printSimplifiedPD = true;
|
|
3466
|
+
else if (a == "--threads" || a == "-j") {
|
|
3467
|
+
if (++i >= argc) throw std::runtime_error("--threads needs a number");
|
|
3468
|
+
std::string v = argv[i];
|
|
3469
|
+
if (v == "auto" || v == "AUTO" || v == "0") kh::g_options.matrixThreads = 0;
|
|
3470
|
+
else kh::g_options.matrixThreads = std::max(1, std::atoi(argv[i]));
|
|
3471
|
+
} else if (!a.empty() && a[0] != '-') {
|
|
3472
|
+
if (kh::isDirectory(a)) {
|
|
3473
|
+
std::vector<std::string> more = kh::listInputFiles(a);
|
|
3474
|
+
files.insert(files.end(), more.begin(), more.end());
|
|
3475
|
+
} else files.push_back(a);
|
|
3476
|
+
} else throw std::runtime_error("unknown option: " + a);
|
|
3477
|
+
}
|
|
3478
|
+
if (files.empty() && jobs.empty()) files.push_back("PD.txt");
|
|
3479
|
+
|
|
3480
|
+
for (size_t i = 0; i < files.size(); ++i) {
|
|
3481
|
+
auto parsed = kh::readPDFile(files[i]);
|
|
3482
|
+
jobs.insert(jobs.end(), parsed.begin(), parsed.end());
|
|
3483
|
+
}
|
|
3484
|
+
if (jobs.empty()) throw std::runtime_error("no PD code found");
|
|
3485
|
+
bool label = jobs.size() > 1;
|
|
3486
|
+
for (size_t i = 0; i < jobs.size(); ++i) {
|
|
3487
|
+
uint64_t profileStart = 0;
|
|
3488
|
+
if (kh::g_options.profile) {
|
|
3489
|
+
kh::g_profile.reset();
|
|
3490
|
+
profileStart = kh::profileNowNs();
|
|
3491
|
+
}
|
|
3492
|
+
if (label) std::cout << jobs[i].first << "\t";
|
|
3493
|
+
if (printSimplifiedPD) std::cout << kh::simplifiedPDString(jobs[i].second) << "\n";
|
|
3494
|
+
else std::cout << "\"" << kh::computePD(jobs[i].second) << "\"\n";
|
|
3495
|
+
if (kh::g_options.profile) {
|
|
3496
|
+
kh::g_profile.totalNs = kh::profileNowNs() - profileStart;
|
|
3497
|
+
kh::printProfile();
|
|
3498
|
+
}
|
|
3499
|
+
}
|
|
3500
|
+
return 0;
|
|
3501
|
+
} catch (const std::exception& e) {
|
|
3502
|
+
std::cerr << "error: " << e.what() << "\n";
|
|
3503
|
+
return 1;
|
|
3504
|
+
}
|
|
3505
|
+
}
|
|
3506
|
+
#endif
|