cppkh-interface 0.1.3__py3-none-any.whl → 0.2.1__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 +29 -25
- cppkh_interface/__main__.py +4 -4
- cppkh_interface/data/src/main.cpp +622 -522
- cppkh_interface/main.py +661 -567
- cppkh_interface/py.typed +1 -1
- {cppkh_interface-0.1.3.dist-info → cppkh_interface-0.2.1.dist-info}/METADATA +19 -2
- cppkh_interface-0.2.1.dist-info/RECORD +9 -0
- cppkh_interface-0.1.3.dist-info/RECORD +0 -9
- {cppkh_interface-0.1.3.dist-info → cppkh_interface-0.2.1.dist-info}/WHEEL +0 -0
- {cppkh_interface-0.1.3.dist-info → cppkh_interface-0.2.1.dist-info}/entry_points.txt +0 -0
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#include <algorithm>
|
|
2
2
|
#include <array>
|
|
3
3
|
#include <cctype>
|
|
4
|
-
#include <chrono>
|
|
5
|
-
#include <cstdint>
|
|
6
|
-
#include <cstdlib>
|
|
7
|
-
#include <cstring>
|
|
8
|
-
#include <dirent.h>
|
|
4
|
+
#include <chrono>
|
|
5
|
+
#include <cstdint>
|
|
6
|
+
#include <cstdlib>
|
|
7
|
+
#include <cstring>
|
|
8
|
+
#include <dirent.h>
|
|
9
9
|
#include <fstream>
|
|
10
10
|
#include <functional>
|
|
11
11
|
#include <iostream>
|
|
@@ -298,14 +298,14 @@ static BigInt coeffProduct(const BigInt& a, const BigInt& b) {
|
|
|
298
298
|
return a * b;
|
|
299
299
|
}
|
|
300
300
|
|
|
301
|
-
struct Options {
|
|
302
|
-
int matrixThreads = 1;
|
|
303
|
-
bool reorderCrossings = true;
|
|
304
|
-
bool progress = true;
|
|
305
|
-
bool simplifyR1 = true;
|
|
306
|
-
bool simplifyNugatory = true;
|
|
307
|
-
bool profile = false;
|
|
308
|
-
};
|
|
301
|
+
struct Options {
|
|
302
|
+
int matrixThreads = 1;
|
|
303
|
+
bool reorderCrossings = true;
|
|
304
|
+
bool progress = true;
|
|
305
|
+
bool simplifyR1 = true;
|
|
306
|
+
bool simplifyNugatory = true;
|
|
307
|
+
bool profile = false;
|
|
308
|
+
};
|
|
309
309
|
|
|
310
310
|
static Options g_options;
|
|
311
311
|
typedef std::vector<std::vector<int> > PDCode;
|
|
@@ -682,8 +682,8 @@ static bool operator==(const ArenaAllocator<T>&, const ArenaAllocator<U>&) { ret
|
|
|
682
682
|
template <typename T, typename U>
|
|
683
683
|
static bool operator!=(const ArenaAllocator<T>&, const ArenaAllocator<U>&) { return false; }
|
|
684
684
|
|
|
685
|
-
typedef std::vector<Small, ArenaAllocator<Small> > SmallVec;
|
|
686
|
-
typedef std::vector<int, ArenaAllocator<int> > ArenaIntVec;
|
|
685
|
+
typedef std::vector<Small, ArenaAllocator<Small> > SmallVec;
|
|
686
|
+
typedef std::vector<int, ArenaAllocator<int> > ArenaIntVec;
|
|
687
687
|
|
|
688
688
|
template <size_t InlineN>
|
|
689
689
|
struct IntBuffer {
|
|
@@ -875,9 +875,9 @@ struct Cobordism {
|
|
|
875
875
|
mutable std::string keyCache;
|
|
876
876
|
mutable bool hashReady = false;
|
|
877
877
|
mutable uint64_t hashCache = 0;
|
|
878
|
-
mutable ArenaIntVec boundaryOffset;
|
|
879
|
-
mutable SmallVec boundaryItems;
|
|
880
|
-
mutable ArenaIntVec edgeOffset;
|
|
878
|
+
mutable ArenaIntVec boundaryOffset;
|
|
879
|
+
mutable SmallVec boundaryItems;
|
|
880
|
+
mutable ArenaIntVec edgeOffset;
|
|
881
881
|
mutable SmallVec edgeItems;
|
|
882
882
|
|
|
883
883
|
Cobordism() = default;
|
|
@@ -1003,10 +1003,10 @@ struct Cobordism {
|
|
|
1003
1003
|
CobPtr composeHorizontal(int start, const CobPtr& cc, int cstart, int nc) const;
|
|
1004
1004
|
};
|
|
1005
1005
|
|
|
1006
|
-
template <typename... Args>
|
|
1007
|
-
static CobPtr makeCob(Args&&... args) {
|
|
1008
|
-
return std::make_shared<Cobordism>(std::forward<Args>(args)...);
|
|
1009
|
-
}
|
|
1006
|
+
template <typename... Args>
|
|
1007
|
+
static CobPtr makeCob(Args&&... args) {
|
|
1008
|
+
return std::make_shared<Cobordism>(std::forward<Args>(args)...);
|
|
1009
|
+
}
|
|
1010
1010
|
|
|
1011
1011
|
static SimpleMutex g_cobMutex;
|
|
1012
1012
|
static std::unordered_map<uint64_t, std::vector<CobPtr> > g_cobCache;
|
|
@@ -1495,11 +1495,11 @@ CobPtr Cobordism::composeHorizontal(int start, const CobPtr& cc, int cstart, int
|
|
|
1495
1495
|
return cacheCob(ret);
|
|
1496
1496
|
}
|
|
1497
1497
|
|
|
1498
|
-
struct TermList {
|
|
1499
|
-
typedef std::pair<CobPtr, BigInt> Term;
|
|
1500
|
-
size_t n = 0;
|
|
1501
|
-
Term first;
|
|
1502
|
-
std::vector<Term, ArenaAllocator<Term> > rest;
|
|
1498
|
+
struct TermList {
|
|
1499
|
+
typedef std::pair<CobPtr, BigInt> Term;
|
|
1500
|
+
size_t n = 0;
|
|
1501
|
+
Term first;
|
|
1502
|
+
std::vector<Term, ArenaAllocator<Term> > rest;
|
|
1503
1503
|
|
|
1504
1504
|
bool empty() const { return n == 0; }
|
|
1505
1505
|
size_t size() const { return n; }
|
|
@@ -1617,24 +1617,24 @@ struct LCCC {
|
|
|
1617
1617
|
return ret;
|
|
1618
1618
|
}
|
|
1619
1619
|
|
|
1620
|
-
LCCC multiplied(const BigInt& c) const {
|
|
1621
|
-
LCCC r;
|
|
1622
|
-
if (c.isZero()) return r;
|
|
1623
|
-
r.terms.reserve(terms.size());
|
|
1624
|
-
for (size_t i = 0; i < terms.size(); ++i) r.terms.push_back(std::make_pair(terms[i].first, coeffProduct(terms[i].second, c)));
|
|
1625
|
-
r.alreadyReduced = alreadyReduced;
|
|
1626
|
-
return r;
|
|
1627
|
-
}
|
|
1628
|
-
|
|
1629
|
-
void multiplyInPlace(const BigInt& c) {
|
|
1630
|
-
if (c == BI_ONE) return;
|
|
1631
|
-
alreadyReduced = false;
|
|
1632
|
-
if (c == BI_MINUS_ONE) {
|
|
1633
|
-
for (size_t i = 0; i < terms.size(); ++i) terms[i].second = -terms[i].second;
|
|
1634
|
-
} else {
|
|
1635
|
-
for (size_t i = 0; i < terms.size(); ++i) terms[i].second = coeffProduct(terms[i].second, c);
|
|
1636
|
-
}
|
|
1637
|
-
}
|
|
1620
|
+
LCCC multiplied(const BigInt& c) const {
|
|
1621
|
+
LCCC r;
|
|
1622
|
+
if (c.isZero()) return r;
|
|
1623
|
+
r.terms.reserve(terms.size());
|
|
1624
|
+
for (size_t i = 0; i < terms.size(); ++i) r.terms.push_back(std::make_pair(terms[i].first, coeffProduct(terms[i].second, c)));
|
|
1625
|
+
r.alreadyReduced = alreadyReduced;
|
|
1626
|
+
return r;
|
|
1627
|
+
}
|
|
1628
|
+
|
|
1629
|
+
void multiplyInPlace(const BigInt& c) {
|
|
1630
|
+
if (c == BI_ONE) return;
|
|
1631
|
+
alreadyReduced = false;
|
|
1632
|
+
if (c == BI_MINUS_ONE) {
|
|
1633
|
+
for (size_t i = 0; i < terms.size(); ++i) terms[i].second = -terms[i].second;
|
|
1634
|
+
} else {
|
|
1635
|
+
for (size_t i = 0; i < terms.size(); ++i) terms[i].second = coeffProduct(terms[i].second, c);
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
1638
|
|
|
1639
1639
|
LCCC composeVertical(const LCCC& other) const {
|
|
1640
1640
|
KH_PROFILE(lcccComposeVertical);
|
|
@@ -1678,25 +1678,25 @@ struct LCCC {
|
|
|
1678
1678
|
if (isZero()) return LCCC();
|
|
1679
1679
|
if (alreadyReduced) return *this;
|
|
1680
1680
|
LCCC ret;
|
|
1681
|
-
for (size_t ti = 0; ti < terms.size(); ++ti) {
|
|
1682
|
-
CobPtr cc = terms[ti].first;
|
|
1683
|
-
BigInt num = terms[ti].second;
|
|
1684
|
-
bool canonical = cc->hpower == 0 && cc->ncc == cc->nbc;
|
|
1685
|
-
if (canonical) {
|
|
1686
|
-
for (int i = 0; i < cc->nbc; ++i) {
|
|
1687
|
-
if (cc->connectedComponent[i] != i || cc->genus[i] != 0 || cc->dots[i] > 1) {
|
|
1688
|
-
canonical = false;
|
|
1689
|
-
break;
|
|
1690
|
-
}
|
|
1691
|
-
}
|
|
1692
|
-
if (canonical) {
|
|
1693
|
-
if (ret.isZero()) ret.terms.push_back(std::make_pair(cc, num));
|
|
1694
|
-
else ret.addTerm(cc, num);
|
|
1695
|
-
continue;
|
|
1696
|
-
}
|
|
1697
|
-
}
|
|
1698
|
-
cc->reverseMaps();
|
|
1699
|
-
SmallVec dots(cc->nbc, 0);
|
|
1681
|
+
for (size_t ti = 0; ti < terms.size(); ++ti) {
|
|
1682
|
+
CobPtr cc = terms[ti].first;
|
|
1683
|
+
BigInt num = terms[ti].second;
|
|
1684
|
+
bool canonical = cc->hpower == 0 && cc->ncc == cc->nbc;
|
|
1685
|
+
if (canonical) {
|
|
1686
|
+
for (int i = 0; i < cc->nbc; ++i) {
|
|
1687
|
+
if (cc->connectedComponent[i] != i || cc->genus[i] != 0 || cc->dots[i] > 1) {
|
|
1688
|
+
canonical = false;
|
|
1689
|
+
break;
|
|
1690
|
+
}
|
|
1691
|
+
}
|
|
1692
|
+
if (canonical) {
|
|
1693
|
+
if (ret.isZero()) ret.terms.push_back(std::make_pair(cc, num));
|
|
1694
|
+
else ret.addTerm(cc, num);
|
|
1695
|
+
continue;
|
|
1696
|
+
}
|
|
1697
|
+
}
|
|
1698
|
+
cc->reverseMaps();
|
|
1699
|
+
SmallVec dots(cc->nbc, 0);
|
|
1700
1700
|
SmallVec genus(cc->nbc, 0);
|
|
1701
1701
|
WorkInts moreWork(cc->ncc);
|
|
1702
1702
|
int moreWorkCount = 0;
|
|
@@ -1733,12 +1733,12 @@ struct LCCC {
|
|
|
1733
1733
|
else ret.addTerm(cached, num);
|
|
1734
1734
|
continue;
|
|
1735
1735
|
}
|
|
1736
|
-
std::vector<SmallVec, ArenaAllocator<SmallVec> > neckCutting;
|
|
1737
|
-
neckCutting.push_back(dots);
|
|
1738
|
-
for (int wi = 0; wi < moreWorkCount; ++wi) {
|
|
1739
|
-
int concomp = moreWork[wi];
|
|
1740
|
-
int nbc = cc->boundaryCount(concomp);
|
|
1741
|
-
std::vector<SmallVec, ArenaAllocator<SmallVec> > next;
|
|
1736
|
+
std::vector<SmallVec, ArenaAllocator<SmallVec> > neckCutting;
|
|
1737
|
+
neckCutting.push_back(dots);
|
|
1738
|
+
for (int wi = 0; wi < moreWorkCount; ++wi) {
|
|
1739
|
+
int concomp = moreWork[wi];
|
|
1740
|
+
int nbc = cc->boundaryCount(concomp);
|
|
1741
|
+
std::vector<SmallVec, ArenaAllocator<SmallVec> > next;
|
|
1742
1742
|
next.reserve(neckCutting.size() * static_cast<size_t>(nbc));
|
|
1743
1743
|
for (size_t j = 0; j < neckCutting.size(); ++j) {
|
|
1744
1744
|
SmallVec base = neckCutting[j];
|
|
@@ -1777,8 +1777,8 @@ struct SmoothingColumn {
|
|
|
1777
1777
|
}
|
|
1778
1778
|
};
|
|
1779
1779
|
|
|
1780
|
-
typedef std::pair<int, LCCC> MatrixEntry;
|
|
1781
|
-
typedef std::vector<MatrixEntry, ArenaAllocator<MatrixEntry> > MatrixRow;
|
|
1780
|
+
typedef std::pair<int, LCCC> MatrixEntry;
|
|
1781
|
+
typedef std::vector<MatrixEntry, ArenaAllocator<MatrixEntry> > MatrixRow;
|
|
1782
1782
|
|
|
1783
1783
|
static MatrixRow::iterator findRowEntry(MatrixRow& row, int column) {
|
|
1784
1784
|
return std::lower_bound(row.begin(), row.end(), column,
|
|
@@ -1976,88 +1976,88 @@ struct CobMatrix {
|
|
|
1976
1976
|
return result;
|
|
1977
1977
|
}
|
|
1978
1978
|
|
|
1979
|
-
CobMatrix extractColumns(const std::vector<int>& columns) {
|
|
1980
|
-
int oldSourceN = source.n;
|
|
1981
|
-
std::vector<int> selectedIndex(oldSourceN, -1);
|
|
1982
|
-
std::vector<char> selected(oldSourceN, 0);
|
|
1983
|
-
for (size_t i = 0; i < columns.size(); ++i) {
|
|
1984
|
-
selectedIndex[columns[i]] = static_cast<int>(i);
|
|
1985
|
-
selected[columns[i]] = 1;
|
|
1986
|
-
}
|
|
1987
|
-
std::vector<int> removedBefore(oldSourceN + 1, 0);
|
|
1988
|
-
for (int c = 0; c < oldSourceN; ++c) removedBefore[c + 1] = removedBefore[c] + (selected[c] ? 1 : 0);
|
|
1989
|
-
SmoothingColumn sc(static_cast<int>(columns.size()));
|
|
1990
|
-
for (size_t i = 0; i < columns.size(); ++i) {
|
|
1991
|
-
sc.smoothings[i] = source.smoothings[columns[i]];
|
|
1992
|
-
sc.numbers[i] = source.numbers[columns[i]];
|
|
1993
|
-
}
|
|
1994
|
-
std::vector<CapPtr> newSmoothings;
|
|
1995
|
-
std::vector<int> newNumbers;
|
|
1996
|
-
newSmoothings.reserve(oldSourceN - columns.size());
|
|
1997
|
-
newNumbers.reserve(oldSourceN - columns.size());
|
|
1998
|
-
for (int c = 0; c < oldSourceN; ++c) {
|
|
1999
|
-
if (!selected[c]) {
|
|
2000
|
-
newSmoothings.push_back(source.smoothings[c]);
|
|
2001
|
-
newNumbers.push_back(source.numbers[c]);
|
|
2002
|
-
}
|
|
2003
|
-
}
|
|
2004
|
-
source.smoothings.swap(newSmoothings);
|
|
2005
|
-
source.numbers.swap(newNumbers);
|
|
2006
|
-
source.n -= static_cast<int>(columns.size());
|
|
2007
|
-
CobMatrix result(sc, target);
|
|
2008
|
-
for (size_t r = 0; r < entries.size(); ++r) {
|
|
2009
|
-
MatrixRow remaining;
|
|
2010
|
-
remaining.reserve(entries[r].size());
|
|
2011
|
-
for (size_t e = 0; e < entries[r].size(); ++e) {
|
|
2012
|
-
int oldColumn = entries[r][e].first;
|
|
2013
|
-
if (selected[oldColumn]) {
|
|
2014
|
-
putRowEntry(result.entries[r], selectedIndex[oldColumn], entries[r][e].second);
|
|
2015
|
-
} else {
|
|
2016
|
-
entries[r][e].first = oldColumn - removedBefore[oldColumn];
|
|
2017
|
-
remaining.push_back(std::move(entries[r][e]));
|
|
2018
|
-
}
|
|
2019
|
-
}
|
|
2020
|
-
entries[r].swap(remaining);
|
|
2021
|
-
}
|
|
2022
|
-
return result;
|
|
2023
|
-
}
|
|
2024
|
-
|
|
2025
|
-
CobMatrix extractRows(const std::vector<int>& rows) {
|
|
2026
|
-
int oldTargetN = target.n;
|
|
2027
|
-
std::vector<int> selectedIndex(oldTargetN, -1);
|
|
2028
|
-
std::vector<char> selected(oldTargetN, 0);
|
|
2029
|
-
for (size_t i = 0; i < rows.size(); ++i) {
|
|
2030
|
-
selectedIndex[rows[i]] = static_cast<int>(i);
|
|
2031
|
-
selected[rows[i]] = 1;
|
|
2032
|
-
}
|
|
2033
|
-
SmoothingColumn sc(static_cast<int>(rows.size()));
|
|
2034
|
-
for (size_t i = 0; i < rows.size(); ++i) {
|
|
2035
|
-
sc.smoothings[i] = target.smoothings[rows[i]];
|
|
2036
|
-
sc.numbers[i] = target.numbers[rows[i]];
|
|
2037
|
-
}
|
|
2038
|
-
std::vector<CapPtr> newSmoothings;
|
|
2039
|
-
std::vector<int> newNumbers;
|
|
2040
|
-
newSmoothings.reserve(oldTargetN - rows.size());
|
|
2041
|
-
newNumbers.reserve(oldTargetN - rows.size());
|
|
2042
|
-
for (int r = 0; r < oldTargetN; ++r) {
|
|
2043
|
-
if (!selected[r]) {
|
|
2044
|
-
newSmoothings.push_back(target.smoothings[r]);
|
|
2045
|
-
newNumbers.push_back(target.numbers[r]);
|
|
2046
|
-
}
|
|
2047
|
-
}
|
|
2048
|
-
target.smoothings.swap(newSmoothings);
|
|
2049
|
-
target.numbers.swap(newNumbers);
|
|
2050
|
-
target.n -= static_cast<int>(rows.size());
|
|
2051
|
-
CobMatrix result(source, sc);
|
|
2052
|
-
std::vector<MatrixRow> remaining;
|
|
2053
|
-
remaining.reserve(oldTargetN - rows.size());
|
|
2054
|
-
for (int r = 0; r < oldTargetN; ++r) {
|
|
2055
|
-
if (selected[r]) result.entries[selectedIndex[r]] = std::move(entries[r]);
|
|
2056
|
-
else remaining.push_back(std::move(entries[r]));
|
|
2057
|
-
}
|
|
2058
|
-
entries.swap(remaining);
|
|
2059
|
-
return result;
|
|
2060
|
-
}
|
|
1979
|
+
CobMatrix extractColumns(const std::vector<int>& columns) {
|
|
1980
|
+
int oldSourceN = source.n;
|
|
1981
|
+
std::vector<int> selectedIndex(oldSourceN, -1);
|
|
1982
|
+
std::vector<char> selected(oldSourceN, 0);
|
|
1983
|
+
for (size_t i = 0; i < columns.size(); ++i) {
|
|
1984
|
+
selectedIndex[columns[i]] = static_cast<int>(i);
|
|
1985
|
+
selected[columns[i]] = 1;
|
|
1986
|
+
}
|
|
1987
|
+
std::vector<int> removedBefore(oldSourceN + 1, 0);
|
|
1988
|
+
for (int c = 0; c < oldSourceN; ++c) removedBefore[c + 1] = removedBefore[c] + (selected[c] ? 1 : 0);
|
|
1989
|
+
SmoothingColumn sc(static_cast<int>(columns.size()));
|
|
1990
|
+
for (size_t i = 0; i < columns.size(); ++i) {
|
|
1991
|
+
sc.smoothings[i] = source.smoothings[columns[i]];
|
|
1992
|
+
sc.numbers[i] = source.numbers[columns[i]];
|
|
1993
|
+
}
|
|
1994
|
+
std::vector<CapPtr> newSmoothings;
|
|
1995
|
+
std::vector<int> newNumbers;
|
|
1996
|
+
newSmoothings.reserve(oldSourceN - columns.size());
|
|
1997
|
+
newNumbers.reserve(oldSourceN - columns.size());
|
|
1998
|
+
for (int c = 0; c < oldSourceN; ++c) {
|
|
1999
|
+
if (!selected[c]) {
|
|
2000
|
+
newSmoothings.push_back(source.smoothings[c]);
|
|
2001
|
+
newNumbers.push_back(source.numbers[c]);
|
|
2002
|
+
}
|
|
2003
|
+
}
|
|
2004
|
+
source.smoothings.swap(newSmoothings);
|
|
2005
|
+
source.numbers.swap(newNumbers);
|
|
2006
|
+
source.n -= static_cast<int>(columns.size());
|
|
2007
|
+
CobMatrix result(sc, target);
|
|
2008
|
+
for (size_t r = 0; r < entries.size(); ++r) {
|
|
2009
|
+
MatrixRow remaining;
|
|
2010
|
+
remaining.reserve(entries[r].size());
|
|
2011
|
+
for (size_t e = 0; e < entries[r].size(); ++e) {
|
|
2012
|
+
int oldColumn = entries[r][e].first;
|
|
2013
|
+
if (selected[oldColumn]) {
|
|
2014
|
+
putRowEntry(result.entries[r], selectedIndex[oldColumn], entries[r][e].second);
|
|
2015
|
+
} else {
|
|
2016
|
+
entries[r][e].first = oldColumn - removedBefore[oldColumn];
|
|
2017
|
+
remaining.push_back(std::move(entries[r][e]));
|
|
2018
|
+
}
|
|
2019
|
+
}
|
|
2020
|
+
entries[r].swap(remaining);
|
|
2021
|
+
}
|
|
2022
|
+
return result;
|
|
2023
|
+
}
|
|
2024
|
+
|
|
2025
|
+
CobMatrix extractRows(const std::vector<int>& rows) {
|
|
2026
|
+
int oldTargetN = target.n;
|
|
2027
|
+
std::vector<int> selectedIndex(oldTargetN, -1);
|
|
2028
|
+
std::vector<char> selected(oldTargetN, 0);
|
|
2029
|
+
for (size_t i = 0; i < rows.size(); ++i) {
|
|
2030
|
+
selectedIndex[rows[i]] = static_cast<int>(i);
|
|
2031
|
+
selected[rows[i]] = 1;
|
|
2032
|
+
}
|
|
2033
|
+
SmoothingColumn sc(static_cast<int>(rows.size()));
|
|
2034
|
+
for (size_t i = 0; i < rows.size(); ++i) {
|
|
2035
|
+
sc.smoothings[i] = target.smoothings[rows[i]];
|
|
2036
|
+
sc.numbers[i] = target.numbers[rows[i]];
|
|
2037
|
+
}
|
|
2038
|
+
std::vector<CapPtr> newSmoothings;
|
|
2039
|
+
std::vector<int> newNumbers;
|
|
2040
|
+
newSmoothings.reserve(oldTargetN - rows.size());
|
|
2041
|
+
newNumbers.reserve(oldTargetN - rows.size());
|
|
2042
|
+
for (int r = 0; r < oldTargetN; ++r) {
|
|
2043
|
+
if (!selected[r]) {
|
|
2044
|
+
newSmoothings.push_back(target.smoothings[r]);
|
|
2045
|
+
newNumbers.push_back(target.numbers[r]);
|
|
2046
|
+
}
|
|
2047
|
+
}
|
|
2048
|
+
target.smoothings.swap(newSmoothings);
|
|
2049
|
+
target.numbers.swap(newNumbers);
|
|
2050
|
+
target.n -= static_cast<int>(rows.size());
|
|
2051
|
+
CobMatrix result(source, sc);
|
|
2052
|
+
std::vector<MatrixRow> remaining;
|
|
2053
|
+
remaining.reserve(oldTargetN - rows.size());
|
|
2054
|
+
for (int r = 0; r < oldTargetN; ++r) {
|
|
2055
|
+
if (selected[r]) result.entries[selectedIndex[r]] = std::move(entries[r]);
|
|
2056
|
+
else remaining.push_back(std::move(entries[r]));
|
|
2057
|
+
}
|
|
2058
|
+
entries.swap(remaining);
|
|
2059
|
+
return result;
|
|
2060
|
+
}
|
|
2061
2061
|
};
|
|
2062
2062
|
|
|
2063
2063
|
struct IntMatrix {
|
|
@@ -2480,19 +2480,19 @@ bool Komplex::reductionLemma(int i) {
|
|
|
2480
2480
|
}
|
|
2481
2481
|
|
|
2482
2482
|
void Komplex::reductionLemma(int i, int j, int k, const BigInt& n) {
|
|
2483
|
-
CapPtr isoSource = columns[i].smoothings[k];
|
|
2484
|
-
CapPtr isoTarget = columns[i + 1].smoothings[j];
|
|
2485
|
-
CobMatrix m = std::move(matrices[i]);
|
|
2486
|
-
CobMatrix delta = m.extractRow(j);
|
|
2483
|
+
CapPtr isoSource = columns[i].smoothings[k];
|
|
2484
|
+
CapPtr isoTarget = columns[i + 1].smoothings[j];
|
|
2485
|
+
CobMatrix m = std::move(matrices[i]);
|
|
2486
|
+
CobMatrix delta = m.extractRow(j);
|
|
2487
2487
|
delta.extractColumn(k);
|
|
2488
2488
|
CobMatrix gamma = m.extractColumn(k);
|
|
2489
|
-
BigInt coeff = (n == BI_ONE ? BI_MINUS_ONE : BI_ONE);
|
|
2490
|
-
for (size_t row = 0; row < gamma.entries.size(); ++row) {
|
|
2491
|
-
for (size_t e = 0; e < gamma.entries[row].size(); ++e) {
|
|
2492
|
-
if (gamma.entries[row][e].first == 0) gamma.entries[row][e].second.multiplyInPlace(coeff);
|
|
2493
|
-
}
|
|
2494
|
-
}
|
|
2495
|
-
CobMatrix gpd = gamma.compose(delta);
|
|
2489
|
+
BigInt coeff = (n == BI_ONE ? BI_MINUS_ONE : BI_ONE);
|
|
2490
|
+
for (size_t row = 0; row < gamma.entries.size(); ++row) {
|
|
2491
|
+
for (size_t e = 0; e < gamma.entries[row].size(); ++e) {
|
|
2492
|
+
if (gamma.entries[row][e].first == 0) gamma.entries[row][e].second.multiplyInPlace(coeff);
|
|
2493
|
+
}
|
|
2494
|
+
}
|
|
2495
|
+
CobMatrix gpd = gamma.compose(delta);
|
|
2496
2496
|
gpd.add(m);
|
|
2497
2497
|
matrices[i] = gpd;
|
|
2498
2498
|
columns[i] = delta.source;
|
|
@@ -2505,30 +2505,30 @@ void Komplex::reductionLemma(int i, int j, int k, const BigInt& n) {
|
|
|
2505
2505
|
}
|
|
2506
2506
|
}
|
|
2507
2507
|
|
|
2508
|
-
std::vector<Komplex::Isomorphism> Komplex::findBlock(const CobMatrix& m) const {
|
|
2509
|
-
std::vector<Isomorphism> isos;
|
|
2510
|
-
std::vector<char> disallowedColumns(m.source.n, 0);
|
|
2511
|
-
std::vector<char> isomorphismColumns(m.source.n, 0);
|
|
2512
|
-
std::vector<int> potentialDisallowedColumns;
|
|
2513
|
-
for (int j = 0; j < static_cast<int>(m.entries.size()); ++j) {
|
|
2514
|
-
bool foundIsomorphismOnRow = false;
|
|
2515
|
-
bool rowForbidden = false;
|
|
2516
|
-
bool hasRowCandidate = false;
|
|
2517
|
-
Isomorphism rowCandidate;
|
|
2518
|
-
potentialDisallowedColumns.clear();
|
|
2519
|
-
potentialDisallowedColumns.reserve(m.entries[j].size());
|
|
2520
|
-
for (const auto& kv : m.entries[j]) {
|
|
2521
|
-
int k = kv.first;
|
|
2522
|
-
const LCCC& lc = kv.second;
|
|
2523
|
-
if (isomorphismColumns[k]) rowForbidden = true;
|
|
2524
|
-
if (disallowedColumns[k]) continue;
|
|
2525
|
-
if (foundIsomorphismOnRow) {
|
|
2526
|
-
disallowedColumns[k] = 1;
|
|
2527
|
-
} else {
|
|
2528
|
-
potentialDisallowedColumns.push_back(k);
|
|
2529
|
-
if (!hasRowCandidate && lc.numberOfTerms() == 1) {
|
|
2530
|
-
CobPtr cc = lc.firstTerm();
|
|
2531
|
-
BigInt n = lc.firstCoefficient();
|
|
2508
|
+
std::vector<Komplex::Isomorphism> Komplex::findBlock(const CobMatrix& m) const {
|
|
2509
|
+
std::vector<Isomorphism> isos;
|
|
2510
|
+
std::vector<char> disallowedColumns(m.source.n, 0);
|
|
2511
|
+
std::vector<char> isomorphismColumns(m.source.n, 0);
|
|
2512
|
+
std::vector<int> potentialDisallowedColumns;
|
|
2513
|
+
for (int j = 0; j < static_cast<int>(m.entries.size()); ++j) {
|
|
2514
|
+
bool foundIsomorphismOnRow = false;
|
|
2515
|
+
bool rowForbidden = false;
|
|
2516
|
+
bool hasRowCandidate = false;
|
|
2517
|
+
Isomorphism rowCandidate;
|
|
2518
|
+
potentialDisallowedColumns.clear();
|
|
2519
|
+
potentialDisallowedColumns.reserve(m.entries[j].size());
|
|
2520
|
+
for (const auto& kv : m.entries[j]) {
|
|
2521
|
+
int k = kv.first;
|
|
2522
|
+
const LCCC& lc = kv.second;
|
|
2523
|
+
if (isomorphismColumns[k]) rowForbidden = true;
|
|
2524
|
+
if (disallowedColumns[k]) continue;
|
|
2525
|
+
if (foundIsomorphismOnRow) {
|
|
2526
|
+
disallowedColumns[k] = 1;
|
|
2527
|
+
} else {
|
|
2528
|
+
potentialDisallowedColumns.push_back(k);
|
|
2529
|
+
if (!hasRowCandidate && lc.numberOfTerms() == 1) {
|
|
2530
|
+
CobPtr cc = lc.firstTerm();
|
|
2531
|
+
BigInt n = lc.firstCoefficient();
|
|
2532
2532
|
if (!(n == BI_ONE || n == BI_MINUS_ONE)) continue;
|
|
2533
2533
|
if (!cc->isIsomorphism()) continue;
|
|
2534
2534
|
rowCandidate = Isomorphism(j, k, n);
|
|
@@ -2536,17 +2536,17 @@ std::vector<Komplex::Isomorphism> Komplex::findBlock(const CobMatrix& m) const {
|
|
|
2536
2536
|
foundIsomorphismOnRow = true;
|
|
2537
2537
|
}
|
|
2538
2538
|
}
|
|
2539
|
-
}
|
|
2540
|
-
if (!rowForbidden && hasRowCandidate) {
|
|
2541
|
-
isos.push_back(rowCandidate);
|
|
2542
|
-
isomorphismColumns[rowCandidate.column] = 1;
|
|
2543
|
-
for (size_t p = 0; p < potentialDisallowedColumns.size(); ++p) {
|
|
2544
|
-
disallowedColumns[potentialDisallowedColumns[p]] = 1;
|
|
2545
|
-
}
|
|
2546
|
-
}
|
|
2547
|
-
}
|
|
2548
|
-
return isos;
|
|
2549
|
-
}
|
|
2539
|
+
}
|
|
2540
|
+
if (!rowForbidden && hasRowCandidate) {
|
|
2541
|
+
isos.push_back(rowCandidate);
|
|
2542
|
+
isomorphismColumns[rowCandidate.column] = 1;
|
|
2543
|
+
for (size_t p = 0; p < potentialDisallowedColumns.size(); ++p) {
|
|
2544
|
+
disallowedColumns[potentialDisallowedColumns[p]] = 1;
|
|
2545
|
+
}
|
|
2546
|
+
}
|
|
2547
|
+
}
|
|
2548
|
+
return isos;
|
|
2549
|
+
}
|
|
2550
2550
|
|
|
2551
2551
|
void Komplex::blockReductionLemma(int i, const std::vector<Isomorphism>& block) {
|
|
2552
2552
|
KH_PROFILE(blockReductionApply);
|
|
@@ -2561,20 +2561,20 @@ void Komplex::blockReductionLemma(int i, const std::vector<Isomorphism>& block)
|
|
|
2561
2561
|
cols.push_back(iso.column);
|
|
2562
2562
|
coeffs.push_back(iso.coefficient);
|
|
2563
2563
|
}
|
|
2564
|
-
CobMatrix m = std::move(matrices[i]);
|
|
2564
|
+
CobMatrix m = std::move(matrices[i]);
|
|
2565
2565
|
CobMatrix delta = m.extractRows(rows);
|
|
2566
2566
|
delta.extractColumns(cols);
|
|
2567
2567
|
CobMatrix gamma = m.extractColumns(cols);
|
|
2568
|
-
for (size_t k = 0; k < block.size(); ++k) {
|
|
2569
|
-
BigInt coeff = (coeffs[k] == BI_ONE ? BI_MINUS_ONE : BI_ONE);
|
|
2570
|
-
for (size_t row = 0; row < gamma.entries.size(); ++row) {
|
|
2571
|
-
MatrixRow::iterator it = findRowEntry(gamma.entries[row], static_cast<int>(k));
|
|
2572
|
-
if (it != gamma.entries[row].end() && it->first == static_cast<int>(k)) {
|
|
2573
|
-
it->second.multiplyInPlace(coeff);
|
|
2574
|
-
}
|
|
2575
|
-
}
|
|
2576
|
-
}
|
|
2577
|
-
CobMatrix gpd = gamma.compose(delta);
|
|
2568
|
+
for (size_t k = 0; k < block.size(); ++k) {
|
|
2569
|
+
BigInt coeff = (coeffs[k] == BI_ONE ? BI_MINUS_ONE : BI_ONE);
|
|
2570
|
+
for (size_t row = 0; row < gamma.entries.size(); ++row) {
|
|
2571
|
+
MatrixRow::iterator it = findRowEntry(gamma.entries[row], static_cast<int>(k));
|
|
2572
|
+
if (it != gamma.entries[row].end() && it->first == static_cast<int>(k)) {
|
|
2573
|
+
it->second.multiplyInPlace(coeff);
|
|
2574
|
+
}
|
|
2575
|
+
}
|
|
2576
|
+
}
|
|
2577
|
+
CobMatrix gpd = gamma.compose(delta);
|
|
2578
2578
|
gpd.add(m);
|
|
2579
2579
|
matrices[i] = gpd;
|
|
2580
2580
|
columns[i] = delta.source;
|
|
@@ -2759,12 +2759,12 @@ std::string Komplex::KhForZ() {
|
|
|
2759
2759
|
return ret;
|
|
2760
2760
|
}
|
|
2761
2761
|
|
|
2762
|
-
static int chooseXingRecursive(const std::vector<int>& edges, const std::vector<std::vector<int> >& pd,
|
|
2763
|
-
std::vector<char>& in, std::vector<char>& done, int depth, std::vector<int>& retmax) {
|
|
2764
|
-
// Choose the next crossing for the growing tangle. The heuristic maximizes
|
|
2765
|
-
// already-attached boundary arcs, with a short lookahead to keep later
|
|
2766
|
-
// intermediate girth smaller. It affects runtime, not the homology result.
|
|
2767
|
-
int nedges = static_cast<int>(edges.size());
|
|
2762
|
+
static int chooseXingRecursive(const std::vector<int>& edges, const std::vector<std::vector<int> >& pd,
|
|
2763
|
+
std::vector<char>& in, std::vector<char>& done, int depth, std::vector<int>& retmax) {
|
|
2764
|
+
// Choose the next crossing for the growing tangle. The heuristic maximizes
|
|
2765
|
+
// already-attached boundary arcs, with a short lookahead to keep later
|
|
2766
|
+
// intermediate girth smaller. It affects runtime, not the homology result.
|
|
2767
|
+
int nedges = static_cast<int>(edges.size());
|
|
2768
2768
|
int best = -1, nconbest = -1;
|
|
2769
2769
|
std::vector<int> rbest(depth, 0);
|
|
2770
2770
|
for (size_t i = 0; i < pd.size(); ++i) if (!done[i]) {
|
|
@@ -2823,91 +2823,91 @@ static int takeNextCrossing(const std::vector<int>&, const std::vector<std::vect
|
|
|
2823
2823
|
throw std::runtime_error("no crossing left");
|
|
2824
2824
|
}
|
|
2825
2825
|
|
|
2826
|
-
static std::vector<int> getSigns(const std::vector<std::vector<int> >& pd) {
|
|
2827
|
-
const int nodeCount = static_cast<int>(pd.size() * 4);
|
|
2828
|
-
if (nodeCount == 0) return std::vector<int>();
|
|
2829
|
-
|
|
2830
|
-
int maxLabel = -1;
|
|
2831
|
-
for (size_t i = 0; i < pd.size(); ++i) {
|
|
2832
|
-
if (pd[i].size() != 4) throw std::runtime_error("PD crossing must have four entries");
|
|
2833
|
-
for (int label : pd[i]) {
|
|
2834
|
-
if (label < 0) throw std::runtime_error("PD arc labels must be positive");
|
|
2835
|
-
maxLabel = std::max(maxLabel, label);
|
|
2836
|
-
}
|
|
2837
|
-
}
|
|
2838
|
-
|
|
2839
|
-
std::vector<int> first(maxLabel + 1, -1), second(maxLabel + 1, -1);
|
|
2840
|
-
for (size_t i = 0; i < pd.size(); ++i) {
|
|
2841
|
-
for (int slot = 0; slot < 4; ++slot) {
|
|
2842
|
-
int label = pd[i][slot];
|
|
2843
|
-
int node = static_cast<int>(4 * i) + slot;
|
|
2844
|
-
if (first[label] == -1) first[label] = node;
|
|
2845
|
-
else if (second[label] == -1) second[label] = node;
|
|
2846
|
-
else throw std::runtime_error("PD arc label appears more than twice");
|
|
2847
|
-
}
|
|
2848
|
-
}
|
|
2849
|
-
|
|
2850
|
-
std::vector<int> other(nodeCount, -1);
|
|
2851
|
-
for (int label = 0; label <= maxLabel; ++label) {
|
|
2852
|
-
if (first[label] == -1) continue;
|
|
2853
|
-
if (second[label] == -1) throw std::runtime_error("PD arc label does not appear twice");
|
|
2854
|
-
other[first[label]] = second[label];
|
|
2855
|
-
other[second[label]] = first[label];
|
|
2856
|
-
}
|
|
2857
|
-
|
|
2858
|
-
// 1 means that this edge leaves the crossing at this incidence, and 0
|
|
2859
|
-
// means that it enters. Opposite incidences at a crossing and the two
|
|
2860
|
-
// incidences of one edge always have opposite directions.
|
|
2861
|
-
std::vector<signed char> outgoing(nodeCount, -1);
|
|
2862
|
-
std::vector<int> queue;
|
|
2863
|
-
queue.reserve(nodeCount);
|
|
2864
|
-
auto orientComponent = [&](int seed, signed char direction) {
|
|
2865
|
-
if (outgoing[seed] != -1) {
|
|
2866
|
-
if (outgoing[seed] != direction) throw std::runtime_error("inconsistent PD orientation");
|
|
2867
|
-
return;
|
|
2868
|
-
}
|
|
2869
|
-
queue.clear();
|
|
2870
|
-
outgoing[seed] = direction;
|
|
2871
|
-
queue.push_back(seed);
|
|
2872
|
-
for (size_t head = 0; head < queue.size(); ++head) {
|
|
2873
|
-
int node = queue[head];
|
|
2874
|
-
int crossing = node / 4;
|
|
2875
|
-
int slot = node % 4;
|
|
2876
|
-
int neighbors[2] = {4 * crossing + ((slot + 2) % 4), other[node]};
|
|
2877
|
-
for (int next : neighbors) {
|
|
2878
|
-
if (next < 0) throw std::runtime_error("broken PD edge incidence");
|
|
2879
|
-
signed char nextDirection = static_cast<signed char>(1 - outgoing[node]);
|
|
2880
|
-
if (outgoing[next] == -1) {
|
|
2881
|
-
outgoing[next] = nextDirection;
|
|
2882
|
-
queue.push_back(next);
|
|
2883
|
-
} else if (outgoing[next] != nextDirection) {
|
|
2884
|
-
throw std::runtime_error("inconsistent PD orientation");
|
|
2885
|
-
}
|
|
2886
|
-
}
|
|
2887
|
-
}
|
|
2888
|
-
};
|
|
2889
|
-
|
|
2890
|
-
// In SageMath's PD convention slot 0 is the incoming under-edge and slot
|
|
2891
|
-
// 2 is the outgoing under-edge. These seeds orient every component that
|
|
2892
|
-
// passes under at least once.
|
|
2893
|
-
for (size_t i = 0; i < pd.size(); ++i) orientComponent(static_cast<int>(4 * i + 2), 1);
|
|
2894
|
-
|
|
2895
|
-
// A component which is over at every crossing has no orientation encoded
|
|
2896
|
-
// by the PD tuples. Match SageMath's deterministic traversal by taking the
|
|
2897
|
-
// first occurrence of the smallest still-unoriented edge as a tail.
|
|
2898
|
-
for (int label = 0; label <= maxLabel; ++label) {
|
|
2899
|
-
if (first[label] != -1 && outgoing[first[label]] == -1) orientComponent(first[label], 1);
|
|
2900
|
-
}
|
|
2901
|
-
|
|
2902
|
-
std::vector<int> xsigns(pd.size());
|
|
2903
|
-
for (size_t i = 0; i < pd.size(); ++i) {
|
|
2904
|
-
const std::vector<int>& crossing = pd[i];
|
|
2905
|
-
if (crossing[0] == crossing[3] || crossing[2] == crossing[1]) xsigns[i] = -1;
|
|
2906
|
-
else if (crossing[3] == crossing[2] || crossing[0] == crossing[1]) xsigns[i] = 1;
|
|
2907
|
-
else xsigns[i] = outgoing[4 * i + 3] ? -1 : 1;
|
|
2908
|
-
}
|
|
2909
|
-
return xsigns;
|
|
2910
|
-
}
|
|
2826
|
+
static std::vector<int> getSigns(const std::vector<std::vector<int> >& pd) {
|
|
2827
|
+
const int nodeCount = static_cast<int>(pd.size() * 4);
|
|
2828
|
+
if (nodeCount == 0) return std::vector<int>();
|
|
2829
|
+
|
|
2830
|
+
int maxLabel = -1;
|
|
2831
|
+
for (size_t i = 0; i < pd.size(); ++i) {
|
|
2832
|
+
if (pd[i].size() != 4) throw std::runtime_error("PD crossing must have four entries");
|
|
2833
|
+
for (int label : pd[i]) {
|
|
2834
|
+
if (label < 0) throw std::runtime_error("PD arc labels must be positive");
|
|
2835
|
+
maxLabel = std::max(maxLabel, label);
|
|
2836
|
+
}
|
|
2837
|
+
}
|
|
2838
|
+
|
|
2839
|
+
std::vector<int> first(maxLabel + 1, -1), second(maxLabel + 1, -1);
|
|
2840
|
+
for (size_t i = 0; i < pd.size(); ++i) {
|
|
2841
|
+
for (int slot = 0; slot < 4; ++slot) {
|
|
2842
|
+
int label = pd[i][slot];
|
|
2843
|
+
int node = static_cast<int>(4 * i) + slot;
|
|
2844
|
+
if (first[label] == -1) first[label] = node;
|
|
2845
|
+
else if (second[label] == -1) second[label] = node;
|
|
2846
|
+
else throw std::runtime_error("PD arc label appears more than twice");
|
|
2847
|
+
}
|
|
2848
|
+
}
|
|
2849
|
+
|
|
2850
|
+
std::vector<int> other(nodeCount, -1);
|
|
2851
|
+
for (int label = 0; label <= maxLabel; ++label) {
|
|
2852
|
+
if (first[label] == -1) continue;
|
|
2853
|
+
if (second[label] == -1) throw std::runtime_error("PD arc label does not appear twice");
|
|
2854
|
+
other[first[label]] = second[label];
|
|
2855
|
+
other[second[label]] = first[label];
|
|
2856
|
+
}
|
|
2857
|
+
|
|
2858
|
+
// 1 means that this edge leaves the crossing at this incidence, and 0
|
|
2859
|
+
// means that it enters. Opposite incidences at a crossing and the two
|
|
2860
|
+
// incidences of one edge always have opposite directions.
|
|
2861
|
+
std::vector<signed char> outgoing(nodeCount, -1);
|
|
2862
|
+
std::vector<int> queue;
|
|
2863
|
+
queue.reserve(nodeCount);
|
|
2864
|
+
auto orientComponent = [&](int seed, signed char direction) {
|
|
2865
|
+
if (outgoing[seed] != -1) {
|
|
2866
|
+
if (outgoing[seed] != direction) throw std::runtime_error("inconsistent PD orientation");
|
|
2867
|
+
return;
|
|
2868
|
+
}
|
|
2869
|
+
queue.clear();
|
|
2870
|
+
outgoing[seed] = direction;
|
|
2871
|
+
queue.push_back(seed);
|
|
2872
|
+
for (size_t head = 0; head < queue.size(); ++head) {
|
|
2873
|
+
int node = queue[head];
|
|
2874
|
+
int crossing = node / 4;
|
|
2875
|
+
int slot = node % 4;
|
|
2876
|
+
int neighbors[2] = {4 * crossing + ((slot + 2) % 4), other[node]};
|
|
2877
|
+
for (int next : neighbors) {
|
|
2878
|
+
if (next < 0) throw std::runtime_error("broken PD edge incidence");
|
|
2879
|
+
signed char nextDirection = static_cast<signed char>(1 - outgoing[node]);
|
|
2880
|
+
if (outgoing[next] == -1) {
|
|
2881
|
+
outgoing[next] = nextDirection;
|
|
2882
|
+
queue.push_back(next);
|
|
2883
|
+
} else if (outgoing[next] != nextDirection) {
|
|
2884
|
+
throw std::runtime_error("inconsistent PD orientation");
|
|
2885
|
+
}
|
|
2886
|
+
}
|
|
2887
|
+
}
|
|
2888
|
+
};
|
|
2889
|
+
|
|
2890
|
+
// In SageMath's PD convention slot 0 is the incoming under-edge and slot
|
|
2891
|
+
// 2 is the outgoing under-edge. These seeds orient every component that
|
|
2892
|
+
// passes under at least once.
|
|
2893
|
+
for (size_t i = 0; i < pd.size(); ++i) orientComponent(static_cast<int>(4 * i + 2), 1);
|
|
2894
|
+
|
|
2895
|
+
// A component which is over at every crossing has no orientation encoded
|
|
2896
|
+
// by the PD tuples. Match SageMath's deterministic traversal by taking the
|
|
2897
|
+
// first occurrence of the smallest still-unoriented edge as a tail.
|
|
2898
|
+
for (int label = 0; label <= maxLabel; ++label) {
|
|
2899
|
+
if (first[label] != -1 && outgoing[first[label]] == -1) orientComponent(first[label], 1);
|
|
2900
|
+
}
|
|
2901
|
+
|
|
2902
|
+
std::vector<int> xsigns(pd.size());
|
|
2903
|
+
for (size_t i = 0; i < pd.size(); ++i) {
|
|
2904
|
+
const std::vector<int>& crossing = pd[i];
|
|
2905
|
+
if (crossing[0] == crossing[3] || crossing[2] == crossing[1]) xsigns[i] = -1;
|
|
2906
|
+
else if (crossing[3] == crossing[2] || crossing[0] == crossing[1]) xsigns[i] = 1;
|
|
2907
|
+
else xsigns[i] = outgoing[4 * i + 3] ? -1 : 1;
|
|
2908
|
+
}
|
|
2909
|
+
return xsigns;
|
|
2910
|
+
}
|
|
2911
2911
|
|
|
2912
2912
|
static bool sanityPD(const PDCode& pd) {
|
|
2913
2913
|
std::map<int, int> counts;
|
|
@@ -2990,17 +2990,17 @@ static PDCode renumberR1Order(PDCode pd) {
|
|
|
2990
2990
|
return pd;
|
|
2991
2991
|
}
|
|
2992
2992
|
|
|
2993
|
-
static PDCode eraseR1(PDCode pd, std::vector<int>* signs) {
|
|
2994
|
-
if (!sanityPD(pd)) throw std::runtime_error("invalid PD code: every arc label must appear exactly twice");
|
|
2995
|
-
if (signs && signs->size() != pd.size()) throw std::runtime_error("crossing sign count does not match PD code");
|
|
2996
|
-
bool hasR1 = true;
|
|
2993
|
+
static PDCode eraseR1(PDCode pd, std::vector<int>* signs) {
|
|
2994
|
+
if (!sanityPD(pd)) throw std::runtime_error("invalid PD code: every arc label must appear exactly twice");
|
|
2995
|
+
if (signs && signs->size() != pd.size()) throw std::runtime_error("crossing sign count does not match PD code");
|
|
2996
|
+
bool hasR1 = true;
|
|
2997
2997
|
while (hasR1) {
|
|
2998
2998
|
hasR1 = false;
|
|
2999
2999
|
for (size_t i = 0; i < pd.size(); ++i) {
|
|
3000
3000
|
if (uniqueCount(pd[i]) <= 3) {
|
|
3001
|
-
std::vector<int> crossing = pd[i];
|
|
3002
|
-
pd.erase(pd.begin() + static_cast<std::ptrdiff_t>(i));
|
|
3003
|
-
if (signs) signs->erase(signs->begin() + static_cast<std::ptrdiff_t>(i));
|
|
3001
|
+
std::vector<int> crossing = pd[i];
|
|
3002
|
+
pd.erase(pd.begin() + static_cast<std::ptrdiff_t>(i));
|
|
3003
|
+
if (signs) signs->erase(signs->begin() + static_cast<std::ptrdiff_t>(i));
|
|
3004
3004
|
std::vector<int> singles;
|
|
3005
3005
|
for (int v : crossing) {
|
|
3006
3006
|
if (std::count(crossing.begin(), crossing.end(), v) == 1) singles.push_back(v);
|
|
@@ -3155,9 +3155,9 @@ static PDCode renumberFullDfs(PDCode pd) {
|
|
|
3155
3155
|
return pd;
|
|
3156
3156
|
}
|
|
3157
3157
|
|
|
3158
|
-
static PDCode eraseOneNugatory(PDCode pd, size_t index, std::vector<int>* signs) {
|
|
3159
|
-
if (uniqueCount(pd[index]) != 4) throw std::runtime_error("nugatory erase requires R1-free PD code");
|
|
3160
|
-
if (signs && signs->size() != pd.size()) throw std::runtime_error("crossing sign count does not match PD code");
|
|
3158
|
+
static PDCode eraseOneNugatory(PDCode pd, size_t index, std::vector<int>* signs) {
|
|
3159
|
+
if (uniqueCount(pd[index]) != 4) throw std::runtime_error("nugatory erase requires R1-free PD code");
|
|
3160
|
+
if (signs && signs->size() != pd.size()) throw std::runtime_error("crossing sign count does not match PD code");
|
|
3161
3161
|
int ax = pd[index][0];
|
|
3162
3162
|
int bx = pd[index][1];
|
|
3163
3163
|
int cx = pd[index][2];
|
|
@@ -3179,28 +3179,28 @@ static PDCode eraseOneNugatory(PDCode pd, size_t index, std::vector<int>* signs)
|
|
|
3179
3179
|
if (!loopSet.count(ax) || !loopSet.count(bx) || !loopSet.count(cx) || !loopSet.count(dx)) {
|
|
3180
3180
|
throw std::runtime_error("nugatory crossing arcs are not in one component");
|
|
3181
3181
|
}
|
|
3182
|
-
PDCode bad = pd;
|
|
3183
|
-
bad.erase(bad.begin() + static_cast<std::ptrdiff_t>(index));
|
|
3184
|
-
if (signs) signs->erase(signs->begin() + static_cast<std::ptrdiff_t>(index));
|
|
3182
|
+
PDCode bad = pd;
|
|
3183
|
+
bad.erase(bad.begin() + static_cast<std::ptrdiff_t>(index));
|
|
3184
|
+
if (signs) signs->erase(signs->begin() + static_cast<std::ptrdiff_t>(index));
|
|
3185
3185
|
bad = replaceArcValue(bad, ax, cx);
|
|
3186
3186
|
bad = replaceArcValue(bad, dx, bx);
|
|
3187
3187
|
return renumberFullDfs(bad);
|
|
3188
3188
|
}
|
|
3189
3189
|
|
|
3190
|
-
static PDCode simplifyPDCode(PDCode pd, std::vector<int>* signs,
|
|
3191
|
-
bool simplifyR1, bool simplifyNugatory) {
|
|
3192
|
-
// The nugatory-crossing operation historically used by the Python API also
|
|
3193
|
-
// removes R1 crossings, so nugatory-only mode still starts with R1 cleanup.
|
|
3194
|
-
if (simplifyR1 || simplifyNugatory) pd = eraseR1(pd, signs);
|
|
3195
|
-
if (simplifyNugatory) {
|
|
3196
|
-
while (true) {
|
|
3197
|
-
int index = findNugatory(pd);
|
|
3198
|
-
if (index < 0) break;
|
|
3199
|
-
pd = eraseOneNugatory(pd, static_cast<size_t>(index), signs);
|
|
3200
|
-
}
|
|
3201
|
-
}
|
|
3202
|
-
return pd;
|
|
3203
|
-
}
|
|
3190
|
+
static PDCode simplifyPDCode(PDCode pd, std::vector<int>* signs,
|
|
3191
|
+
bool simplifyR1, bool simplifyNugatory) {
|
|
3192
|
+
// The nugatory-crossing operation historically used by the Python API also
|
|
3193
|
+
// removes R1 crossings, so nugatory-only mode still starts with R1 cleanup.
|
|
3194
|
+
if (simplifyR1 || simplifyNugatory) pd = eraseR1(pd, signs);
|
|
3195
|
+
if (simplifyNugatory) {
|
|
3196
|
+
while (true) {
|
|
3197
|
+
int index = findNugatory(pd);
|
|
3198
|
+
if (index < 0) break;
|
|
3199
|
+
pd = eraseOneNugatory(pd, static_cast<size_t>(index), signs);
|
|
3200
|
+
}
|
|
3201
|
+
}
|
|
3202
|
+
return pd;
|
|
3203
|
+
}
|
|
3204
3204
|
|
|
3205
3205
|
static int komplexSize(const Komplex& k) {
|
|
3206
3206
|
int total = 0;
|
|
@@ -3208,8 +3208,8 @@ static int komplexSize(const Komplex& k) {
|
|
|
3208
3208
|
return total;
|
|
3209
3209
|
}
|
|
3210
3210
|
|
|
3211
|
-
static Komplex generateFast(const std::vector<std::vector<int> >& pd, const std::vector<int>& xsigns) {
|
|
3212
|
-
KH_PROFILE(generateFast);
|
|
3211
|
+
static Komplex generateFast(const std::vector<std::vector<int> >& pd, const std::vector<int>& xsigns) {
|
|
3212
|
+
KH_PROFILE(generateFast);
|
|
3213
3213
|
if (pd.empty()) {
|
|
3214
3214
|
Komplex kom(1);
|
|
3215
3215
|
kom.columns[0] = SmoothingColumn(1);
|
|
@@ -3217,13 +3217,13 @@ static Komplex generateFast(const std::vector<std::vector<int> >& pd, const std:
|
|
|
3217
3217
|
kom.reduce();
|
|
3218
3218
|
return kom;
|
|
3219
3219
|
}
|
|
3220
|
-
std::vector<char> in(pd.size() * 4, 0), done(pd.size(), 0);
|
|
3221
|
-
std::vector<std::vector<int> > pd1(1, std::vector<int>{0,1,2,3});
|
|
3222
|
-
Komplex kplus(pd1, std::vector<int>{1}, 4);
|
|
3223
|
-
Komplex kminus(pd1, std::vector<int>{-1}, 4);
|
|
3224
|
-
// Build the full complex by composing one crossing complex at a time and
|
|
3225
|
-
// reducing after every composition, following JavaKh's tangle-growth path.
|
|
3226
|
-
std::vector<int> edges;
|
|
3220
|
+
std::vector<char> in(pd.size() * 4, 0), done(pd.size(), 0);
|
|
3221
|
+
std::vector<std::vector<int> > pd1(1, std::vector<int>{0,1,2,3});
|
|
3222
|
+
Komplex kplus(pd1, std::vector<int>{1}, 4);
|
|
3223
|
+
Komplex kminus(pd1, std::vector<int>{-1}, 4);
|
|
3224
|
+
// Build the full complex by composing one crossing complex at a time and
|
|
3225
|
+
// reducing after every composition, following JavaKh's tangle-growth path.
|
|
3226
|
+
std::vector<int> edges;
|
|
3227
3227
|
int firstdepth = pd.size() > 4 ? 3 : static_cast<int>(pd.size()) - 1;
|
|
3228
3228
|
std::vector<int> firstdummy(firstdepth + 1, 0);
|
|
3229
3229
|
int first = g_options.reorderCrossings ? chooseXingRecursive(edges, pd, in, done, firstdepth, firstdummy)
|
|
@@ -3403,17 +3403,83 @@ static std::vector<std::string> listInputFiles(const std::string& dir) {
|
|
|
3403
3403
|
return files;
|
|
3404
3404
|
}
|
|
3405
3405
|
|
|
3406
|
-
static std::string computePD(const std::vector<std::vector<int> >& pd) {
|
|
3407
|
-
flushCobCache();
|
|
3408
|
-
g_smallArena.reset();
|
|
3409
|
-
std::vector<int> signs = getSigns(pd);
|
|
3410
|
-
PDCode working = simplifyPDCode(pd, &signs, g_options.simplifyR1, g_options.simplifyNugatory);
|
|
3411
|
-
Komplex k = generateFast(working, signs);
|
|
3406
|
+
static std::string computePD(const std::vector<std::vector<int> >& pd) {
|
|
3407
|
+
flushCobCache();
|
|
3408
|
+
g_smallArena.reset();
|
|
3409
|
+
std::vector<int> signs = getSigns(pd);
|
|
3410
|
+
PDCode working = simplifyPDCode(pd, &signs, g_options.simplifyR1, g_options.simplifyNugatory);
|
|
3411
|
+
Komplex k = generateFast(working, signs);
|
|
3412
3412
|
ProfileScope khScope(g_profile.kh);
|
|
3413
3413
|
return k.KhForZ();
|
|
3414
3414
|
}
|
|
3415
3415
|
|
|
3416
|
-
static std::string
|
|
3416
|
+
static std::string computePDWithSigns(const std::vector<std::vector<int> >& pd,
|
|
3417
|
+
const std::vector<int>& xsigns) {
|
|
3418
|
+
if (xsigns.size() != pd.size()) throw std::runtime_error("sign count does not match PD crossing count");
|
|
3419
|
+
for (int sign : xsigns) {
|
|
3420
|
+
if (sign != 1 && sign != -1) throw std::runtime_error("crossing signs must be +1 or -1");
|
|
3421
|
+
}
|
|
3422
|
+
flushCobCache();
|
|
3423
|
+
g_smallArena.reset();
|
|
3424
|
+
Komplex k = generateFast(pd, xsigns);
|
|
3425
|
+
ProfileScope khScope(g_profile.kh);
|
|
3426
|
+
return k.KhForZ();
|
|
3427
|
+
}
|
|
3428
|
+
|
|
3429
|
+
static std::string trimAscii(const std::string& s) {
|
|
3430
|
+
size_t first = 0;
|
|
3431
|
+
while (first < s.size() && std::isspace(static_cast<unsigned char>(s[first]))) ++first;
|
|
3432
|
+
size_t last = s.size();
|
|
3433
|
+
while (last > first && std::isspace(static_cast<unsigned char>(s[last - 1]))) --last;
|
|
3434
|
+
return s.substr(first, last - first);
|
|
3435
|
+
}
|
|
3436
|
+
|
|
3437
|
+
static std::vector<int> parseSignsLine(const std::string& line) {
|
|
3438
|
+
std::vector<int> signs;
|
|
3439
|
+
size_t pos = 0;
|
|
3440
|
+
while (pos < line.size()) {
|
|
3441
|
+
char ch = line[pos];
|
|
3442
|
+
if (std::isspace(static_cast<unsigned char>(ch)) || ch == ',' ||
|
|
3443
|
+
ch == '[' || ch == ']' || ch == '(' || ch == ')' || ch == ';') {
|
|
3444
|
+
++pos;
|
|
3445
|
+
continue;
|
|
3446
|
+
}
|
|
3447
|
+
int sign = 1;
|
|
3448
|
+
bool sawExplicitSign = false;
|
|
3449
|
+
if (ch == '+' || ch == '-') {
|
|
3450
|
+
sign = (ch == '-') ? -1 : 1;
|
|
3451
|
+
sawExplicitSign = true;
|
|
3452
|
+
++pos;
|
|
3453
|
+
}
|
|
3454
|
+
int value = 0;
|
|
3455
|
+
bool sawDigit = false;
|
|
3456
|
+
while (pos < line.size() && std::isdigit(static_cast<unsigned char>(line[pos]))) {
|
|
3457
|
+
sawDigit = true;
|
|
3458
|
+
value = value * 10 + (line[pos++] - '0');
|
|
3459
|
+
}
|
|
3460
|
+
if (!sawExplicitSign && !sawDigit) throw std::runtime_error("invalid crossing sign token");
|
|
3461
|
+
if (sawDigit && value != 1) throw std::runtime_error("crossing signs must be +1 or -1");
|
|
3462
|
+
signs.push_back(sign);
|
|
3463
|
+
}
|
|
3464
|
+
return signs;
|
|
3465
|
+
}
|
|
3466
|
+
|
|
3467
|
+
static std::vector<std::vector<int> > parseSignsDocument(const std::string& text) {
|
|
3468
|
+
std::vector<std::vector<int> > result;
|
|
3469
|
+
if (trimAscii(text).empty()) {
|
|
3470
|
+
result.push_back(std::vector<int>());
|
|
3471
|
+
return result;
|
|
3472
|
+
}
|
|
3473
|
+
std::istringstream lines(text);
|
|
3474
|
+
std::string line;
|
|
3475
|
+
while (std::getline(lines, line)) {
|
|
3476
|
+
if (!trimAscii(line).empty()) result.push_back(parseSignsLine(line));
|
|
3477
|
+
}
|
|
3478
|
+
if (result.empty()) result.push_back(std::vector<int>());
|
|
3479
|
+
return result;
|
|
3480
|
+
}
|
|
3481
|
+
|
|
3482
|
+
static std::string formatPDCode(const PDCode& pd) {
|
|
3417
3483
|
std::ostringstream out;
|
|
3418
3484
|
out << "PD[";
|
|
3419
3485
|
for (size_t i = 0; i < pd.size(); ++i) {
|
|
@@ -3426,164 +3492,198 @@ static std::string formatPDCode(const PDCode& pd) {
|
|
|
3426
3492
|
out << "]";
|
|
3427
3493
|
}
|
|
3428
3494
|
out << "]";
|
|
3429
|
-
return out.str();
|
|
3430
|
-
}
|
|
3431
|
-
|
|
3432
|
-
static std::string formatCrossingSigns(const PDCode& pd) {
|
|
3433
|
-
std::vector<int> signs = getSigns(pd);
|
|
3434
|
-
std::ostringstream out;
|
|
3435
|
-
out << "[";
|
|
3436
|
-
for (size_t i = 0; i < signs.size(); ++i) {
|
|
3437
|
-
if (i) out << ",";
|
|
3438
|
-
out << signs[i];
|
|
3439
|
-
}
|
|
3440
|
-
out << "]";
|
|
3441
|
-
return out.str();
|
|
3442
|
-
}
|
|
3443
|
-
|
|
3444
|
-
static std::string simplifiedPDString(const PDCode& pd) {
|
|
3445
|
-
getSigns(pd);
|
|
3446
|
-
PDCode working = simplifyPDCode(pd, nullptr, g_options.simplifyR1, g_options.simplifyNugatory);
|
|
3447
|
-
return formatPDCode(working);
|
|
3448
|
-
}
|
|
3449
|
-
|
|
3450
|
-
} // namespace kh
|
|
3451
|
-
|
|
3452
|
-
#if defined(CPPKH_SHARED_LIBRARY)
|
|
3453
|
-
#if defined(_WIN32)
|
|
3454
|
-
#define CPPKH_API extern "C" __declspec(dllexport)
|
|
3455
|
-
#else
|
|
3456
|
-
#define CPPKH_API extern "C" __attribute__((visibility("default")))
|
|
3457
|
-
#endif
|
|
3458
|
-
|
|
3459
|
-
static thread_local std::string g_cppkhLastError;
|
|
3460
|
-
|
|
3461
|
-
static char* cppkhDuplicateString(const std::string& value) {
|
|
3462
|
-
char* out = static_cast<char*>(std::malloc(value.size() + 1));
|
|
3463
|
-
if (!out) {
|
|
3464
|
-
g_cppkhLastError = "out of memory";
|
|
3465
|
-
return nullptr;
|
|
3466
|
-
}
|
|
3467
|
-
std::memcpy(out, value.c_str(), value.size() + 1);
|
|
3468
|
-
return out;
|
|
3469
|
-
}
|
|
3470
|
-
|
|
3471
|
-
struct CppkhOptionsGuard {
|
|
3472
|
-
kh::Options oldOptions;
|
|
3473
|
-
CppkhOptionsGuard() : oldOptions(kh::g_options) {}
|
|
3474
|
-
~CppkhOptionsGuard() { kh::g_options = oldOptions; }
|
|
3475
|
-
};
|
|
3476
|
-
|
|
3477
|
-
CPPKH_API const char* cppkh_version() {
|
|
3478
|
-
return "cppkh/1";
|
|
3479
|
-
}
|
|
3480
|
-
|
|
3481
|
-
CPPKH_API const char* cppkh_last_error() {
|
|
3482
|
-
return g_cppkhLastError.c_str();
|
|
3483
|
-
}
|
|
3484
|
-
|
|
3485
|
-
CPPKH_API void cppkh_free(char* value) {
|
|
3486
|
-
std::free(value);
|
|
3487
|
-
}
|
|
3488
|
-
|
|
3489
|
-
CPPKH_API char* cppkh_compute_pd_ex(const char* pd_code, int simplify_pd, int reorder_crossings) {
|
|
3490
|
-
try {
|
|
3491
|
-
g_cppkhLastError.clear();
|
|
3492
|
-
if (!pd_code) throw std::runtime_error("pd_code is null");
|
|
3493
|
-
std::vector<std::pair<std::string, kh::PDCode> > parsed = kh::parsePDDocument(pd_code, "ctypes");
|
|
3494
|
-
if (parsed.empty()) throw std::runtime_error("no PD code found");
|
|
3495
|
-
if (parsed.size() != 1) throw std::runtime_error("cppkh_compute_pd_ex expects exactly one PD code");
|
|
3496
|
-
|
|
3497
|
-
CppkhOptionsGuard guard;
|
|
3498
|
-
kh::g_options.progress = false;
|
|
3499
|
-
kh::g_options.profile = false;
|
|
3500
|
-
kh::g_options.simplifyR1 = simplify_pd != 0;
|
|
3501
|
-
kh::g_options.simplifyNugatory = simplify_pd != 0;
|
|
3502
|
-
kh::g_options.reorderCrossings = reorder_crossings != 0;
|
|
3503
|
-
std::string result = kh::computePD(parsed[0].second);
|
|
3504
|
-
return cppkhDuplicateString(result);
|
|
3505
|
-
} catch (const std::exception& e) {
|
|
3506
|
-
g_cppkhLastError = e.what();
|
|
3507
|
-
return nullptr;
|
|
3508
|
-
} catch (...) {
|
|
3509
|
-
g_cppkhLastError = "unknown error";
|
|
3510
|
-
return nullptr;
|
|
3511
|
-
}
|
|
3512
|
-
}
|
|
3513
|
-
|
|
3514
|
-
CPPKH_API char* cppkh_compute_pd(const char* pd_code) {
|
|
3515
|
-
return cppkh_compute_pd_ex(pd_code, 1, 1);
|
|
3516
|
-
}
|
|
3517
|
-
|
|
3518
|
-
CPPKH_API char* cppkh_compute_pd_batch_ex(const char* pd_codes, int simplify_pd, int reorder_crossings) {
|
|
3519
|
-
try {
|
|
3520
|
-
g_cppkhLastError.clear();
|
|
3521
|
-
if (!pd_codes) throw std::runtime_error("pd_codes is null");
|
|
3522
|
-
std::vector<std::pair<std::string, kh::PDCode> > parsed = kh::parsePDDocument(pd_codes, "ctypes-batch");
|
|
3523
|
-
if (parsed.empty()) throw std::runtime_error("no PD code found");
|
|
3524
|
-
|
|
3525
|
-
CppkhOptionsGuard guard;
|
|
3526
|
-
kh::g_options.progress = false;
|
|
3527
|
-
kh::g_options.profile = false;
|
|
3528
|
-
kh::g_options.simplifyR1 = simplify_pd != 0;
|
|
3529
|
-
kh::g_options.simplifyNugatory = simplify_pd != 0;
|
|
3530
|
-
kh::g_options.reorderCrossings = reorder_crossings != 0;
|
|
3531
|
-
|
|
3532
|
-
std::ostringstream out;
|
|
3533
|
-
for (size_t i = 0; i < parsed.size(); ++i) {
|
|
3534
|
-
if (i) out << "\n";
|
|
3535
|
-
out << kh::computePD(parsed[i].second);
|
|
3536
|
-
}
|
|
3537
|
-
return cppkhDuplicateString(out.str());
|
|
3538
|
-
} catch (const std::exception& e) {
|
|
3539
|
-
g_cppkhLastError = e.what();
|
|
3540
|
-
return nullptr;
|
|
3541
|
-
} catch (...) {
|
|
3542
|
-
g_cppkhLastError = "unknown error";
|
|
3543
|
-
return nullptr;
|
|
3544
|
-
}
|
|
3545
|
-
}
|
|
3546
|
-
|
|
3547
|
-
CPPKH_API char* cppkh_compute_pd_batch(const char* pd_codes) {
|
|
3548
|
-
return cppkh_compute_pd_batch_ex(pd_codes, 1, 1);
|
|
3549
|
-
}
|
|
3550
|
-
|
|
3551
|
-
CPPKH_API char*
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
if (!pd_code) throw std::runtime_error("pd_code is null");
|
|
3555
|
-
std::vector<std::pair<std::string, kh::PDCode> > parsed = kh::parsePDDocument(pd_code, "ctypes");
|
|
3556
|
-
if (parsed.empty()) throw std::runtime_error("no PD code found");
|
|
3557
|
-
if (parsed.size() != 1) throw std::runtime_error("cppkh_simplify_pd expects exactly one PD code");
|
|
3558
|
-
CppkhOptionsGuard guard;
|
|
3559
|
-
kh::g_options.simplifyR1 = true;
|
|
3560
|
-
kh::g_options.simplifyNugatory = true;
|
|
3561
|
-
std::string result = kh::simplifiedPDString(parsed[0].second);
|
|
3562
|
-
return cppkhDuplicateString(result);
|
|
3563
|
-
} catch (const std::exception& e) {
|
|
3564
|
-
g_cppkhLastError = e.what();
|
|
3565
|
-
return nullptr;
|
|
3566
|
-
} catch (...) {
|
|
3567
|
-
g_cppkhLastError = "unknown error";
|
|
3568
|
-
return nullptr;
|
|
3569
|
-
}
|
|
3570
|
-
}
|
|
3571
|
-
#endif
|
|
3572
|
-
|
|
3573
|
-
#ifndef CPPKH_SHARED_LIBRARY
|
|
3574
|
-
static void usage() {
|
|
3575
|
-
std::cout << "Usage: cppkh [--pd-file FILE] [--pd-dir DIR] [--pd-code CODE] [--ordered] [--threads N|auto] [--quiet] [--profile] [--no-simplify-pd] [--simplify-r1|--no-simplify-r1] [--simplify-nugatory|--no-simplify-nugatory] [--print-simplified-pd] [--print-crossing-signs]\n";
|
|
3576
|
-
std::cout << "Thread backend: " << KH_THREAD_BACKEND_NAME << "\n";
|
|
3577
|
-
std::cout << "Detected CPU threads: " << kh::detectHardwareThreads() << "\n";
|
|
3578
|
-
std::cout << "PD simplification: R1 removal then nugatory crossing removal is enabled by default.\n";
|
|
3579
|
-
}
|
|
3580
|
-
|
|
3581
|
-
int main(int argc, char** argv) {
|
|
3495
|
+
return out.str();
|
|
3496
|
+
}
|
|
3497
|
+
|
|
3498
|
+
static std::string formatCrossingSigns(const PDCode& pd) {
|
|
3499
|
+
std::vector<int> signs = getSigns(pd);
|
|
3500
|
+
std::ostringstream out;
|
|
3501
|
+
out << "[";
|
|
3502
|
+
for (size_t i = 0; i < signs.size(); ++i) {
|
|
3503
|
+
if (i) out << ",";
|
|
3504
|
+
out << signs[i];
|
|
3505
|
+
}
|
|
3506
|
+
out << "]";
|
|
3507
|
+
return out.str();
|
|
3508
|
+
}
|
|
3509
|
+
|
|
3510
|
+
static std::string simplifiedPDString(const PDCode& pd) {
|
|
3511
|
+
getSigns(pd);
|
|
3512
|
+
PDCode working = simplifyPDCode(pd, nullptr, g_options.simplifyR1, g_options.simplifyNugatory);
|
|
3513
|
+
return formatPDCode(working);
|
|
3514
|
+
}
|
|
3515
|
+
|
|
3516
|
+
} // namespace kh
|
|
3517
|
+
|
|
3518
|
+
#if defined(CPPKH_SHARED_LIBRARY)
|
|
3519
|
+
#if defined(_WIN32)
|
|
3520
|
+
#define CPPKH_API extern "C" __declspec(dllexport)
|
|
3521
|
+
#else
|
|
3522
|
+
#define CPPKH_API extern "C" __attribute__((visibility("default")))
|
|
3523
|
+
#endif
|
|
3524
|
+
|
|
3525
|
+
static thread_local std::string g_cppkhLastError;
|
|
3526
|
+
|
|
3527
|
+
static char* cppkhDuplicateString(const std::string& value) {
|
|
3528
|
+
char* out = static_cast<char*>(std::malloc(value.size() + 1));
|
|
3529
|
+
if (!out) {
|
|
3530
|
+
g_cppkhLastError = "out of memory";
|
|
3531
|
+
return nullptr;
|
|
3532
|
+
}
|
|
3533
|
+
std::memcpy(out, value.c_str(), value.size() + 1);
|
|
3534
|
+
return out;
|
|
3535
|
+
}
|
|
3536
|
+
|
|
3537
|
+
struct CppkhOptionsGuard {
|
|
3538
|
+
kh::Options oldOptions;
|
|
3539
|
+
CppkhOptionsGuard() : oldOptions(kh::g_options) {}
|
|
3540
|
+
~CppkhOptionsGuard() { kh::g_options = oldOptions; }
|
|
3541
|
+
};
|
|
3542
|
+
|
|
3543
|
+
CPPKH_API const char* cppkh_version() {
|
|
3544
|
+
return "cppkh/1";
|
|
3545
|
+
}
|
|
3546
|
+
|
|
3547
|
+
CPPKH_API const char* cppkh_last_error() {
|
|
3548
|
+
return g_cppkhLastError.c_str();
|
|
3549
|
+
}
|
|
3550
|
+
|
|
3551
|
+
CPPKH_API void cppkh_free(char* value) {
|
|
3552
|
+
std::free(value);
|
|
3553
|
+
}
|
|
3554
|
+
|
|
3555
|
+
CPPKH_API char* cppkh_compute_pd_ex(const char* pd_code, int simplify_pd, int reorder_crossings) {
|
|
3556
|
+
try {
|
|
3557
|
+
g_cppkhLastError.clear();
|
|
3558
|
+
if (!pd_code) throw std::runtime_error("pd_code is null");
|
|
3559
|
+
std::vector<std::pair<std::string, kh::PDCode> > parsed = kh::parsePDDocument(pd_code, "ctypes");
|
|
3560
|
+
if (parsed.empty()) throw std::runtime_error("no PD code found");
|
|
3561
|
+
if (parsed.size() != 1) throw std::runtime_error("cppkh_compute_pd_ex expects exactly one PD code");
|
|
3562
|
+
|
|
3563
|
+
CppkhOptionsGuard guard;
|
|
3564
|
+
kh::g_options.progress = false;
|
|
3565
|
+
kh::g_options.profile = false;
|
|
3566
|
+
kh::g_options.simplifyR1 = simplify_pd != 0;
|
|
3567
|
+
kh::g_options.simplifyNugatory = simplify_pd != 0;
|
|
3568
|
+
kh::g_options.reorderCrossings = reorder_crossings != 0;
|
|
3569
|
+
std::string result = kh::computePD(parsed[0].second);
|
|
3570
|
+
return cppkhDuplicateString(result);
|
|
3571
|
+
} catch (const std::exception& e) {
|
|
3572
|
+
g_cppkhLastError = e.what();
|
|
3573
|
+
return nullptr;
|
|
3574
|
+
} catch (...) {
|
|
3575
|
+
g_cppkhLastError = "unknown error";
|
|
3576
|
+
return nullptr;
|
|
3577
|
+
}
|
|
3578
|
+
}
|
|
3579
|
+
|
|
3580
|
+
CPPKH_API char* cppkh_compute_pd(const char* pd_code) {
|
|
3581
|
+
return cppkh_compute_pd_ex(pd_code, 1, 1);
|
|
3582
|
+
}
|
|
3583
|
+
|
|
3584
|
+
CPPKH_API char* cppkh_compute_pd_batch_ex(const char* pd_codes, int simplify_pd, int reorder_crossings) {
|
|
3585
|
+
try {
|
|
3586
|
+
g_cppkhLastError.clear();
|
|
3587
|
+
if (!pd_codes) throw std::runtime_error("pd_codes is null");
|
|
3588
|
+
std::vector<std::pair<std::string, kh::PDCode> > parsed = kh::parsePDDocument(pd_codes, "ctypes-batch");
|
|
3589
|
+
if (parsed.empty()) throw std::runtime_error("no PD code found");
|
|
3590
|
+
|
|
3591
|
+
CppkhOptionsGuard guard;
|
|
3592
|
+
kh::g_options.progress = false;
|
|
3593
|
+
kh::g_options.profile = false;
|
|
3594
|
+
kh::g_options.simplifyR1 = simplify_pd != 0;
|
|
3595
|
+
kh::g_options.simplifyNugatory = simplify_pd != 0;
|
|
3596
|
+
kh::g_options.reorderCrossings = reorder_crossings != 0;
|
|
3597
|
+
|
|
3598
|
+
std::ostringstream out;
|
|
3599
|
+
for (size_t i = 0; i < parsed.size(); ++i) {
|
|
3600
|
+
if (i) out << "\n";
|
|
3601
|
+
out << kh::computePD(parsed[i].second);
|
|
3602
|
+
}
|
|
3603
|
+
return cppkhDuplicateString(out.str());
|
|
3604
|
+
} catch (const std::exception& e) {
|
|
3605
|
+
g_cppkhLastError = e.what();
|
|
3606
|
+
return nullptr;
|
|
3607
|
+
} catch (...) {
|
|
3608
|
+
g_cppkhLastError = "unknown error";
|
|
3609
|
+
return nullptr;
|
|
3610
|
+
}
|
|
3611
|
+
}
|
|
3612
|
+
|
|
3613
|
+
CPPKH_API char* cppkh_compute_pd_batch(const char* pd_codes) {
|
|
3614
|
+
return cppkh_compute_pd_batch_ex(pd_codes, 1, 1);
|
|
3615
|
+
}
|
|
3616
|
+
|
|
3617
|
+
CPPKH_API char* cppkh_compute_pd_signed_variants_ex(const char* pd_code,
|
|
3618
|
+
const char* signs_text,
|
|
3619
|
+
int reorder_crossings) {
|
|
3582
3620
|
try {
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
|
|
3621
|
+
g_cppkhLastError.clear();
|
|
3622
|
+
if (!pd_code) throw std::runtime_error("pd_code is null");
|
|
3623
|
+
if (!signs_text) throw std::runtime_error("signs_text is null");
|
|
3624
|
+
std::vector<std::pair<std::string, kh::PDCode> > parsed =
|
|
3625
|
+
kh::parsePDDocument(pd_code, "ctypes-signed");
|
|
3626
|
+
if (parsed.size() != 1) {
|
|
3627
|
+
throw std::runtime_error("signed variants expect exactly one PD code");
|
|
3628
|
+
}
|
|
3629
|
+
std::vector<std::vector<int> > signs = kh::parseSignsDocument(signs_text);
|
|
3630
|
+
CppkhOptionsGuard guard;
|
|
3631
|
+
kh::g_options.progress = false;
|
|
3632
|
+
kh::g_options.profile = false;
|
|
3633
|
+
kh::g_options.simplifyR1 = false;
|
|
3634
|
+
kh::g_options.simplifyNugatory = false;
|
|
3635
|
+
kh::g_options.reorderCrossings = reorder_crossings != 0;
|
|
3636
|
+
std::ostringstream out;
|
|
3637
|
+
for (size_t i = 0; i < signs.size(); ++i) {
|
|
3638
|
+
if (i) out << "\n";
|
|
3639
|
+
out << kh::computePDWithSigns(parsed[0].second, signs[i]);
|
|
3640
|
+
}
|
|
3641
|
+
return cppkhDuplicateString(out.str());
|
|
3642
|
+
} catch (const std::exception& e) {
|
|
3643
|
+
g_cppkhLastError = e.what();
|
|
3644
|
+
return nullptr;
|
|
3645
|
+
} catch (...) {
|
|
3646
|
+
g_cppkhLastError = "unknown error";
|
|
3647
|
+
return nullptr;
|
|
3648
|
+
}
|
|
3649
|
+
}
|
|
3650
|
+
|
|
3651
|
+
CPPKH_API char* cppkh_simplify_pd(const char* pd_code) {
|
|
3652
|
+
try {
|
|
3653
|
+
g_cppkhLastError.clear();
|
|
3654
|
+
if (!pd_code) throw std::runtime_error("pd_code is null");
|
|
3655
|
+
std::vector<std::pair<std::string, kh::PDCode> > parsed = kh::parsePDDocument(pd_code, "ctypes");
|
|
3656
|
+
if (parsed.empty()) throw std::runtime_error("no PD code found");
|
|
3657
|
+
if (parsed.size() != 1) throw std::runtime_error("cppkh_simplify_pd expects exactly one PD code");
|
|
3658
|
+
CppkhOptionsGuard guard;
|
|
3659
|
+
kh::g_options.simplifyR1 = true;
|
|
3660
|
+
kh::g_options.simplifyNugatory = true;
|
|
3661
|
+
std::string result = kh::simplifiedPDString(parsed[0].second);
|
|
3662
|
+
return cppkhDuplicateString(result);
|
|
3663
|
+
} catch (const std::exception& e) {
|
|
3664
|
+
g_cppkhLastError = e.what();
|
|
3665
|
+
return nullptr;
|
|
3666
|
+
} catch (...) {
|
|
3667
|
+
g_cppkhLastError = "unknown error";
|
|
3668
|
+
return nullptr;
|
|
3669
|
+
}
|
|
3670
|
+
}
|
|
3671
|
+
#endif
|
|
3672
|
+
|
|
3673
|
+
#ifndef CPPKH_SHARED_LIBRARY
|
|
3674
|
+
static void usage() {
|
|
3675
|
+
std::cout << "Usage: cppkh [--pd-file FILE] [--pd-dir DIR] [--pd-code CODE] [--ordered] [--threads N|auto] [--quiet] [--profile] [--no-simplify-pd] [--simplify-r1|--no-simplify-r1] [--simplify-nugatory|--no-simplify-nugatory] [--print-simplified-pd] [--print-crossing-signs]\n";
|
|
3676
|
+
std::cout << "Thread backend: " << KH_THREAD_BACKEND_NAME << "\n";
|
|
3677
|
+
std::cout << "Detected CPU threads: " << kh::detectHardwareThreads() << "\n";
|
|
3678
|
+
std::cout << "PD simplification: R1 removal then nugatory crossing removal is enabled by default.\n";
|
|
3679
|
+
}
|
|
3680
|
+
|
|
3681
|
+
int main(int argc, char** argv) {
|
|
3682
|
+
try {
|
|
3683
|
+
std::vector<std::string> files;
|
|
3684
|
+
std::vector<std::pair<std::string, std::vector<std::vector<int> > > > jobs;
|
|
3685
|
+
bool printSimplifiedPD = false;
|
|
3686
|
+
bool printCrossingSigns = false;
|
|
3587
3687
|
for (int i = 1; i < argc; ++i) {
|
|
3588
3688
|
std::string a = argv[i];
|
|
3589
3689
|
if (a == "--help" || a == "-h") { usage(); return 0; }
|
|
@@ -3601,18 +3701,18 @@ int main(int argc, char** argv) {
|
|
|
3601
3701
|
} else if (a == "--ordered" || a == "-O") kh::g_options.reorderCrossings = false;
|
|
3602
3702
|
else if (a == "--quiet" || a == "-q") kh::g_options.progress = false;
|
|
3603
3703
|
else if (a == "--profile") kh::g_options.profile = true;
|
|
3604
|
-
else if (a == "--no-simplify-pd" || a == "--raw-pd") {
|
|
3605
|
-
kh::g_options.simplifyR1 = false;
|
|
3606
|
-
kh::g_options.simplifyNugatory = false;
|
|
3607
|
-
} else if (a == "--simplify-pd") {
|
|
3608
|
-
kh::g_options.simplifyR1 = true;
|
|
3609
|
-
kh::g_options.simplifyNugatory = true;
|
|
3610
|
-
} else if (a == "--no-simplify-r1") kh::g_options.simplifyR1 = false;
|
|
3611
|
-
else if (a == "--simplify-r1") kh::g_options.simplifyR1 = true;
|
|
3612
|
-
else if (a == "--no-simplify-nugatory") kh::g_options.simplifyNugatory = false;
|
|
3613
|
-
else if (a == "--simplify-nugatory") kh::g_options.simplifyNugatory = true;
|
|
3614
|
-
else if (a == "--print-simplified-pd") printSimplifiedPD = true;
|
|
3615
|
-
else if (a == "--print-crossing-signs") printCrossingSigns = true;
|
|
3704
|
+
else if (a == "--no-simplify-pd" || a == "--raw-pd") {
|
|
3705
|
+
kh::g_options.simplifyR1 = false;
|
|
3706
|
+
kh::g_options.simplifyNugatory = false;
|
|
3707
|
+
} else if (a == "--simplify-pd") {
|
|
3708
|
+
kh::g_options.simplifyR1 = true;
|
|
3709
|
+
kh::g_options.simplifyNugatory = true;
|
|
3710
|
+
} else if (a == "--no-simplify-r1") kh::g_options.simplifyR1 = false;
|
|
3711
|
+
else if (a == "--simplify-r1") kh::g_options.simplifyR1 = true;
|
|
3712
|
+
else if (a == "--no-simplify-nugatory") kh::g_options.simplifyNugatory = false;
|
|
3713
|
+
else if (a == "--simplify-nugatory") kh::g_options.simplifyNugatory = true;
|
|
3714
|
+
else if (a == "--print-simplified-pd") printSimplifiedPD = true;
|
|
3715
|
+
else if (a == "--print-crossing-signs") printCrossingSigns = true;
|
|
3616
3716
|
else if (a == "--threads" || a == "-j") {
|
|
3617
3717
|
if (++i >= argc) throw std::runtime_error("--threads needs a number");
|
|
3618
3718
|
std::string v = argv[i];
|
|
@@ -3639,10 +3739,10 @@ int main(int argc, char** argv) {
|
|
|
3639
3739
|
kh::g_profile.reset();
|
|
3640
3740
|
profileStart = kh::profileNowNs();
|
|
3641
3741
|
}
|
|
3642
|
-
if (label) std::cout << jobs[i].first << "\t";
|
|
3643
|
-
if (printSimplifiedPD) std::cout << kh::simplifiedPDString(jobs[i].second) << "\n";
|
|
3644
|
-
else if (printCrossingSigns) std::cout << kh::formatCrossingSigns(jobs[i].second) << "\n";
|
|
3645
|
-
else std::cout << "\"" << kh::computePD(jobs[i].second) << "\"\n";
|
|
3742
|
+
if (label) std::cout << jobs[i].first << "\t";
|
|
3743
|
+
if (printSimplifiedPD) std::cout << kh::simplifiedPDString(jobs[i].second) << "\n";
|
|
3744
|
+
else if (printCrossingSigns) std::cout << kh::formatCrossingSigns(jobs[i].second) << "\n";
|
|
3745
|
+
else std::cout << "\"" << kh::computePD(jobs[i].second) << "\"\n";
|
|
3646
3746
|
if (kh::g_options.profile) {
|
|
3647
3747
|
kh::g_profile.totalNs = kh::profileNowNs() - profileStart;
|
|
3648
3748
|
kh::printProfile();
|
|
@@ -3651,7 +3751,7 @@ int main(int argc, char** argv) {
|
|
|
3651
3751
|
return 0;
|
|
3652
3752
|
} catch (const std::exception& e) {
|
|
3653
3753
|
std::cerr << "error: " << e.what() << "\n";
|
|
3654
|
-
return 1;
|
|
3655
|
-
}
|
|
3656
|
-
}
|
|
3657
|
-
#endif
|
|
3754
|
+
return 1;
|
|
3755
|
+
}
|
|
3756
|
+
}
|
|
3757
|
+
#endif
|