1 /* Copyright (c) 1997-2021
2    Ewgenij Gawrilow, Michael Joswig, and the polymake team
3    Technische Universität Berlin, Germany
4    https://polymake.org
5 
6    This program is free software; you can redistribute it and/or modify it
7    under the terms of the GNU General Public License as published by the
8    Free Software Foundation; either version 2, or (at your option) any
9    later version: http://www.gnu.org/licenses/gpl.txt.
10 
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15 --------------------------------------------------------------------------------
16 */
17 
18 #include "polymake/client.h"
19 #include "polymake/Graph.h"
20 #include "polymake/Set.h"
21 #include "polymake/Vector.h"
22 #include "polymake/Matrix.h"
23 #include "polymake/graph/DoublyConnectedEdgeList.h"
24 #include "polymake/Polynomial.h"
25 #include "polymake/hash_map"
26 
27 namespace polymake { namespace topaz {
28 
29 using DoublyConnectedEdgeList = graph::dcel::DoublyConnectedEdgeList;
30 using HalfEdge = graph::dcel::HalfEdge;
31 
32 using halfedge_variables = Array<Polynomial<Rational, Int>>;
33 using flip_sequence = std::list<Int>;
34 
35 // return the outitude polynomial for edge of index edge_id (half edges: 2*edge_id, 2*edge_id+1)
getOutitudePolynomial(const Matrix<Int> & dcel_data,Int edge_id)36 Polynomial<Rational, Int> getOutitudePolynomial(const Matrix<Int>& dcel_data, Int edge_id)
37 {
38    const auto var = Polynomial<Rational, Int>::monomial;
39    DoublyConnectedEdgeList dcel(dcel_data);
40    Int dim = 4*dcel.getNumHalfEdges()/3;
41    Int e = 2*edge_id;
42    const HalfEdge* halfEdge = dcel.getHalfEdge(e);
43    Int a = dcel.getHalfEdgeId( halfEdge->getNext() );
44    Int b = dcel.getHalfEdgeId( halfEdge->getPrev()->getTwin() );
45    Int c = dcel.getHalfEdgeId( halfEdge->getTwin()->getNext() );
46    Int d = dcel.getHalfEdgeId( halfEdge->getTwin()->getPrev()->getTwin() );
47    Int f = dcel.getHalfEdgeId( halfEdge->getTwin() );
48    auto triangle_map = dcel.triangleMap();
49    //auto triangle_map = triangleMap(dcel_data);
50    return var(triangle_map[f],dim)*( var(e,dim)*var(a,dim) + var(f,dim)*var(b,dim) - var(e,dim)*var(f,dim) ) +
51           var(triangle_map[e],dim)*( var(e,dim)*var(d,dim) + var(f,dim)*var(c,dim) - var(e,dim)*var(f,dim) );
52 }
53 
54 // given dcel data, return all outitudes, one for each edge
outitudePolynomials(const Matrix<Int> & dcel_data)55 Array<Polynomial<Rational,Int>> outitudePolynomials(const Matrix<Int>& dcel_data)
56 {
57    DoublyConnectedEdgeList dcel(dcel_data);
58    auto outs = Array<Polynomial<Rational, Int>>(dcel.getNumEdges());
59    for (Int i = 0; i < dcel.getNumEdges(); ++i) {
60       outs[i] = getOutitudePolynomial(dcel_data,i);
61    }
62    return outs;
63 }
64 
65 UserFunction4perl("# @category Other"
66                   "# Given a triangulation of a punctured surface this calculates all the outitude polynomials.\n"
67                   "# The first e = #{oriented edges} monomials correspond to A-coordinates of the oriented edges, labeled as in the input.\n"
68                   "# The last t = #{triangles} monomials correspond to A-coordinates of the triangles."
69                   "# @param Matrix<Int> dcel_data the data for the doubly connected edge list representing the triangulation."
70                   "# @return Array<Polynomial<Rational,Int>> an array containing the outitudes in order of the input."
71                   "# @example We may calculate the outitude polynomials of a thrice punctured sphere."
72                   "# Here the first six monomials x_0, ... , x_5 are associated to the six oriented edges, x_6 and x_7 are associated to the triangles enclosed by the oriented edges 0,2,4 and 1,3,5 respectively."
73                   "# > $S3 = new Matrix<Int>([[1,0,2,5,0,1],[2,1,4,1,0,1],[0,2,0,3,0,1]]);;"
74                   "# > print outitudePolynomials($S3);"
75                   "# | - x_0*x_1*x_6 - x_0*x_1*x_7 + x_0*x_2*x_6 + x_0*x_2*x_7 + x_1*x_5*x_6 + x_1*x_5*x_7 x_1*x_3*x_6 + x_1*x_3*x_7 - x_2*x_3*x_6 - x_2*x_3*x_7 + x_2*x_4*x_6 + x_2*x_4*x_7 x_0*x_4*x_6 + x_0*x_4*x_7 + x_3*x_5*x_6 + x_3*x_5*x_7 - x_4*x_5*x_6 - x_4*x_5*x_7",
76                   &outitudePolynomials,"outitudePolynomials( Matrix<Int> )");
77 
78 
79 
80 // return all outitudes, one for each edge
81 /*Array<Polynomial<Rational,Int>> outitudePolynomialsFromString(const std::string& surface)
82 {
83    const char s(surface[0]);
84    Int n;
85    std::istringstream is (surface.substr(1));
86    is >> n;
87    switch(s){
88       case 'S':
89       switch(n){
90          case 3:
91             return outitudePolynomials( {{1,0,2,5,0,1},{2,1,4,1,0,1},{0,2,0,3,0,1}} );
92          //case 4:
93          //   return outitudePolynomials( {{1,0,2,6},{2,1,4,9},{0,2,0,11},{3,0,8,5},{1,3,1,10},{2,3,3,7}} );
94          default:
95             throw std::runtime_error("Sorry, so far we cannot handle spheres with more than three punctures.");
96       }
97 
98       case 'T':
99       switch(n){
100          case 1:
101             return outitudePolynomials( {{0,0,2,3,0,1},{0,0,4,5,0,1},{0,0,0,1,0,1}} );
102          case 2:
103             return outitudePolynomials( {{0,0,6,5},{0,0,1,10},{0,0,8,2},{1,0,11,4},{1,0,7,3},{1,0,9,0}} );
104          case 3:
105             return outitudePolynomials( {{1,0,2,17},{2,1,4,14},{0,2,0,6},{1,2,8,16},{0,1,5,10},{2,1,12,1},{0,2,9,3},{0,1,13,7},{0,2,15,11}} );
106 
107          default:
108             throw std::runtime_error("Sorry, so far we cannot handle a torus with more than one puncture.");
109       }
110 
111       case 'D':
112       switch(n){
113          case 1:
114             return outitudePolynomials( {{0,0,2,6},{0,0,4,8},{0,0,0,1},{0,0,5,3},{0,0,7,10},{0,0,16,12},{0,0,14,15},{0,0,11,17},{0,0,9,13}} );
115          default:
116             throw std::runtime_error("Sorry, so far we cannot handle a double torus with more than one puncture. Soon we will.");
117       }
118       default:
119          throw std::runtime_error("Did not recognize your surface.");
120    }
121 
122 }
123 UserFunction4perl("# @category Other"
124                   "# Given a punctured surface by a string from the list below, this calculates all the outitude polynomials.\n"
125                   "# Choose among: S3, S4 (punctured spheres) and T1, T2, T3 (punctured tori) and D1 (punctured double torus).\n"
126                   "# A triangulation of the surface will be chosen for you.\n"
127                   "# The first e = #{oriented edges} monomials correspond to A-coordinates of the oriented edges.\n"
128                   "# The last t = #{triangles} monomials correspond to A-coordinates of the triangles."
129                   "# @param String surface the surface name."
130                   "# @return Array<Polynomial<Rational,Int>> an array containing the outitudes." ,
131                   &outitudePolynomialsFromString,"outitudePolynomialsFromString( String )");
132 */
133 
134 
135 // return the dual outitude polynomial for edge of index edge_id (half edges: 2*edge_id, 2*edge_id+1)
getDualOutitudePolynomial(const Matrix<Int> & dcel_data,Int edge_id)136 Polynomial<Rational,Int> getDualOutitudePolynomial(const Matrix<Int>& dcel_data, Int edge_id)
137 {
138    const auto var = Polynomial<Rational, Int>::monomial;
139 
140    DoublyConnectedEdgeList dcel(dcel_data);
141    Int dim = 4*dcel.getNumHalfEdges()/3;
142    Int e = 2*edge_id;
143    const HalfEdge* halfEdge = dcel.getHalfEdge(e);
144    Int a = dcel.getHalfEdgeId( halfEdge->getNext() );
145    Int aa = dcel.getHalfEdgeId( halfEdge->getNext()->getTwin() );
146    Int b = dcel.getHalfEdgeId( halfEdge->getPrev()->getTwin() );
147    Int bb = dcel.getHalfEdgeId( halfEdge->getPrev() );
148    Int c = dcel.getHalfEdgeId( halfEdge->getTwin()->getNext() );
149    Int cc = dcel.getHalfEdgeId( halfEdge->getTwin()->getNext()->getTwin() );
150    Int d = dcel.getHalfEdgeId( halfEdge->getTwin()->getPrev()->getTwin() );
151    Int dd = dcel.getHalfEdgeId( halfEdge->getTwin()->getPrev() );
152    Int f = dcel.getHalfEdgeId( halfEdge->getTwin() );
153 
154    auto triangle_map = dcel.triangleMap();
155    Int A = triangle_map[e];
156    Int B = triangle_map[f];
157 
158    return var(A,dim)*( var(e,dim)*var(cc,dim)*var(d,dim) + var(f,dim)*var(c,dim)*var(dd,dim) )*( var(f,dim)*var(aa,dim) + var(e,dim)*var(bb,dim) - var(e,dim)*var(f,dim) ) +
159           var(B,dim)*( var(e,dim)*var(a,dim)*var(bb,dim) + var(f,dim)*var(aa,dim)*var(b,dim) )*( var(f,dim)*var(dd,dim) + var(e,dim)*var(cc,dim) - var(e,dim)*var(f,dim) );
160 }
161 //Function4perl(&getDualOutitudePolynomial,"getDualOutitudePolynomial( $$ )");
162 
163 
164 
165 // return all  dual outitudes, one for each edge
dualOutitudePolynomials(const Matrix<Int> & dcel_data)166 Array<Polynomial<Rational,Int>> dualOutitudePolynomials(const Matrix<Int>& dcel_data)
167 {
168    DoublyConnectedEdgeList dcel(dcel_data);
169    auto outs = Array<Polynomial<Rational, Int>>(dcel.getNumEdges());
170    for (Int i = 0; i < dcel.getNumEdges(); i++){
171       outs[i] = getDualOutitudePolynomial(dcel_data,i);
172    }
173    return outs;
174 }
175 UserFunction4perl("# @category Other"
176                   "# Given a triangulation of a punctured surface this calculates all the outitude polynomials of the dual structure.\n"
177                   "# The first e = #{oriented edges} monomials correspond to A-coordinates of the oriented edges of the primal structure , labeled as in the input.\n"
178                   "# The last t = #{triangles} monomials correspond to A-coordinates of the triangles of the primal structure."
179                   "# @param Matrix<Int> dcel_data the data for the doubly connected edge list representing the triangulation."
180                   "# @return Array<Polynomial<Rational,Int>> an array containing the dual outitudes in order of the input.",
181                   &dualOutitudePolynomials,"dualOutitudePolynomials( $ )");
182 
183 
184 
185 
outitudes_from_dcel(const DoublyConnectedEdgeList & dcel)186 Vector<Rational> outitudes_from_dcel(const DoublyConnectedEdgeList& dcel)
187 {
188    Vector<Rational> out_vec(dcel.getNumEdges());
189    for (Int i = 0; i < dcel.getNumEdges(); ++i) {
190       const HalfEdge* e_edge = dcel.getHalfEdge(2*i);
191       const HalfEdge* e_twin = e_edge->getTwin();
192       const Rational& e_plus = e_edge->getLength();
193       const Rational& e_minus = e_twin->getLength();
194       const Rational& a = e_edge->getNext()->getLength();
195       const Rational& b = e_edge->getPrev()->getTwin()->getLength();
196       const Rational& c = e_twin->getNext()->getLength();
197       const Rational& d = e_twin->getPrev()->getTwin()->getLength();
198       const Rational& A = e_twin->getFace()->getDetCoord();
199       const Rational& B = e_edge->getFace()->getDetCoord();
200       out_vec[i] = A*(e_plus*a+e_minus*b-e_plus*e_minus) + B*(e_plus*d+e_minus*c-e_plus*e_minus);
201    }
202    return out_vec;
203 }
204 
205 
is_canonical(const DoublyConnectedEdgeList & dcel)206 std::pair<Set<Int>, Set<Int>> is_canonical(const DoublyConnectedEdgeList& dcel)
207 {
208    Set<Int> concave_edges;
209    Set<Int> flat_edges;
210 
211    Vector<Rational> out_vec = outitudes_from_dcel(dcel);
212 
213    for (Int i = 0; i < out_vec.size(); ++i) {
214       Rational out_i = out_vec[i];
215       if (out_i < 0)
216          concave_edges += i;
217       else if (out_i == 0)
218          flat_edges += i;
219    }
220    return { concave_edges, flat_edges };
221 }
222 
223 
224 
flip_coords(DoublyConnectedEdgeList & dcel,Vector<Rational> coords,Int edge_id)225 Vector<Rational> flip_coords(DoublyConnectedEdgeList& dcel, Vector<Rational> coords, Int edge_id)
226 {
227    Vector<Rational> new_coords(coords);
228    Int e_plus_id = 2*edge_id;
229    Int e_minus_id = 2*edge_id+1;
230    const HalfEdge* halfEdge = dcel.getHalfEdge(e_plus_id);
231    Int A_id = dcel.getFaceId(halfEdge->getFace());
232    Int B_id = dcel.getFaceId(halfEdge->getTwin()->getFace());
233    Int a_plus_id = dcel.getHalfEdgeId(halfEdge->getNext());
234    Int a_minus_id = dcel.getHalfEdgeId(halfEdge->getNext()->getTwin());
235    Int b_plus_id = dcel.getHalfEdgeId(halfEdge->getNext()->getNext());
236    Int b_minus_id = dcel.getHalfEdgeId(halfEdge->getNext()->getNext()->getTwin());
237    Int c_minus_id = dcel.getHalfEdgeId(halfEdge->getTwin()->getNext());
238    Int c_plus_id = dcel.getHalfEdgeId(halfEdge->getTwin()->getNext()->getTwin());
239    Int d_minus_id = dcel.getHalfEdgeId(halfEdge->getTwin()->getNext()->getNext());
240    Int d_plus_id = dcel.getHalfEdgeId(halfEdge->getTwin()->getNext()->getNext()->getTwin());
241 
242    Rational C = (coords[A_id]*coords[c_minus_id] + coords[B_id]*coords[b_minus_id])/coords[e_plus_id];
243    Rational D = (coords[A_id]*coords[d_plus_id] + coords[B_id]*coords[a_plus_id])/coords[e_minus_id];
244    Rational f_plus = (C*coords[d_minus_id] + D*coords[c_plus_id])/coords[B_id];
245    Rational f_minus = (C*coords[a_minus_id] + D*coords[b_plus_id])/coords[A_id];
246 
247    new_coords[e_plus_id] = f_plus;
248    new_coords[e_minus_id] = f_minus;
249    new_coords[A_id] = C;
250    new_coords[B_id] = D;
251 
252 	//halfEdge->setLength(f_plus);
253 	//dcel.getHalfEdge(e_minus_id).setLength(f_minus);
254 	//dcel.getFaces()[A_id].setDetCoord(C);
255 	//dcel.getFaces()[B_id].setDetCoord(D);
256 	return new_coords;
257 }
258 
259 
260 
flips_to_canonical_triangulation(const Matrix<Int> & dcel_data,Vector<Rational> & a_coords)261 std::pair<flip_sequence, Set<Int>> flips_to_canonical_triangulation(const Matrix<Int>& dcel_data, Vector<Rational>& a_coords)
262 {
263    DoublyConnectedEdgeList dcel(dcel_data,a_coords);
264    Vector<Rational> curr_a_coords(a_coords);
265 
266    flip_sequence flips;
267    auto stuff = is_canonical(dcel);
268    auto concave_edges = stuff.first;
269    auto flat_edges = stuff.second;
270    while (!concave_edges.empty()) {
271       Int bad_edge = concave_edges.front();
272       Vector<Rational> new_coords = flip_coords(dcel,curr_a_coords,bad_edge);
273       dcel.flipEdgeWithFaces(bad_edge);
274       flips.push_back(bad_edge);
275       stuff = is_canonical(dcel);
276       concave_edges = stuff.first;
277       flat_edges = stuff.second;
278    }
279    return { flips, flat_edges };
280 }
281 
282 UserFunction4perl("# @category Other\n"
283                   "# Computes a flip sequence to a canonical triangulation (first list)."
284                   "# The second output is a list of flat edges in the canonical triangulation."
285                   "# @param Matrix<Int> DCEL_data"
286                   "# @param Vector<Rational> A_coords"
287                   "# @return Pair<List<Int>,Set<Int>>"
288                   "# @example In the following example only edge 2 has negative outitude, so the flip sequence should start with 2. After performing this flip, the triangulation thus obtained is canonical."
289                   "# > $T1 = new Matrix<Int>([[0,0,2,3,0,1],[0,0,4,5,0,1],[0,0,0,1,0,1]]);"
290                   "# > print flips_to_canonical_triangulation($T1,[1,2,3,4,5,6,1,2]);"
291                   "# | {2} {}",
292                   &flips_to_canonical_triangulation,"flips_to_canonical_triangulation($$)");
293 
294 
out(Matrix<Int> dcel_data,Vector<Rational> a_coords,Int edge_id)295 Rational out(Matrix<Int> dcel_data, Vector<Rational> a_coords, Int edge_id)
296 {
297    DoublyConnectedEdgeList dcel(dcel_data, a_coords);
298    const HalfEdge* e_edge = dcel.getHalfEdge(2*edge_id);
299    const HalfEdge* e_twin = e_edge->getTwin();
300    Rational e_plus = e_edge->getLength();
301    Rational e_minus = e_twin->getLength();
302    Rational a = e_edge->getNext()->getLength();
303    Rational b = e_edge->getPrev()->getTwin()->getLength();
304    Rational c = e_twin->getNext()->getLength();
305    Rational d = e_twin->getPrev()->getTwin()->getLength();
306    Rational A = e_twin->getFace()->getDetCoord();
307    Rational B = e_edge->getFace()->getDetCoord();
308    return A*(e_plus*a+e_minus*b-e_plus*e_minus) + B*(e_plus*d+e_minus*c-e_plus*e_minus);
309 }
310 
outitudes(Matrix<Int> dcel_data,Vector<Rational> a_coords)311 Vector<Rational> outitudes(Matrix<Int> dcel_data, Vector<Rational> a_coords)
312 {
313    Vector<Rational> out_vec(dcel_data.rows());
314    for (Int i = 0; i < dcel_data.rows(); ++i) {
315       out_vec[i] = out(dcel_data,a_coords, i);
316    }
317    return out_vec;
318 }
319 
320 UserFunction4perl("# @category Producing other objects\n"
321                   "# Computes the outitudes along edges."
322                   "# @param Matrix<Int> DCEL_data"
323                   "# @param Vector<Rational> A_coords"
324                   "# @return Vector<Rational>"
325                   "# @example In the following example only edge 2 has negative outitude."
326                   "# > $T1 = new Matrix<Int>([[0,0,2,3,0,1],[0,0,4,5,0,1],[0,0,0,1,0,1]]);"
327                   "# > print outitudes($T1,[1,2,3,4,5,6,1,2]);"
328                   "# | 37 37 -5",
329 &outitudes, "outitudes($ $)");
330 
331 
332 
333 } //end topaz namespace
334 } //end polymake namespace
335 
336 // Local Variables:
337 // mode:C++
338 // c-basic-offset:3
339 // indent-tabs-mode:nil
340 // End:
341