1 /*
2         This program is free software; you can redistribute it and/or
3         modify it under the terms of the GNU General Public License
4         as published by the Free Software Foundation; either version 2
5         of the License, or (at your option) any later version.
6 
7         This program is distributed in the hope that it will be useful,
8         but WITHOUT ANY WARRANTY; without even the implied warranty of
9         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10         GNU General Public License for more details.
11 
12         You should have received a copy of the GNU General Public License
13         along with this program; if not, write to the Free Software
14         Foundation, Inc., 51 Franklin Street, Fifth Floor,
15         Boston, MA  02110-1301, USA.
16 
17         ---
18         Copyright (C) 2011 - 2015, Simon Hampe <simon.hampe@googlemail.com>
19 
20         ---
21         Copyright (c) 2016-2021
22         Ewgenij Gawrilow, Michael Joswig, and the polymake team
23         Technische Universität Berlin, Germany
24         https://polymake.org
25 
26         Contains functions to compute intersection products in tropical surfaces.
27         */
28 
29 #include "polymake/client.h"
30 #include "polymake/Rational.h"
31 #include "polymake/Matrix.h"
32 #include "polymake/Vector.h"
33 #include "polymake/linalg.h"
34 #include "polymake/PowerSet.h"
35 #include "polymake/IncidenceMatrix.h"
36 #include "polymake/common/lattice_tools.h"
37 #include "polymake/tropical/thomog.h"
38 #include "polymake/tropical/linear_algebra_tools.h"
39 #include "polymake/tropical/lattice.h"
40 #include "polymake/tropical/specialcycles.h"
41 #include "polymake/tropical/convex_hull_tools.h"
42 #include "polymake/tropical/refine.h"
43 #include "polymake/tropical/morphism_thomog.h"
44 
45 namespace polymake { namespace tropical {
46 
47 /*
48  * @brief Takes a list of rays which add up to 0, whose span dimension is one less than their number and whose maximal minors are 1. Then it also
49  * takes a list of vectors lying in that span. It will then compute for each vector the unique representation as a positive linear combination of the rays.
50  * @param Matrix<Rational> rank_one_flats, as PRIMITIVE INTEGER
51  * @param Matrix<Rational> curve_rays, as PRIMITIVE INTEGER
52  * @return Matrix<Integer> Each row corresponds to the representation of the same row in curve_rays. Each column corresponds to a row of surface_rays and contains
53  * the corresponding positive coefficient.
54  * */
positive_decomposition(const Matrix<Rational> & rank_one_flats,const Matrix<Rational> & curve_rays)55 Matrix<Integer> positive_decomposition(const Matrix<Rational> &rank_one_flats, const Matrix<Rational> &curve_rays)
56 {
57   Matrix<Integer> result(curve_rays.rows(), rank_one_flats.rows());
58   // Iterate curve rays
59   for (Int cr = 0; cr < curve_rays.rows(); ++cr) {
60     // Compute a linear representation of the vector in the rays
61     Vector<Rational> linRep = linearRepresentation(curve_rays.row(cr), rank_one_flats);
62     //vGo through its entries. For each negative entry, add the absolute value to all other entries and set this one to 0.
63     for (Int entry = 0; entry < linRep.dim(); ++entry) {
64       if (linRep[entry] < 0)
65         linRep = linRep - (linRep[entry] * ones_vector<Rational>(linRep.dim()));
66     }
67     result.row(cr) = linRep;
68   }
69   return result;
70 } //END positive_decomposition
71 
72 /*
73  * @brief Takes a list of rays of a curve given by their positive linear representations wrt to a rank-1-flat-vectors matrix and a list of weights of those rays. It then computes the degree of that curve.
74  * @param Matrix<Rational> curve_decompositions
75  * @param Vector<Integer> curve_weights
76  * @return Integer
77  */
degree_via_decomposition(const Matrix<Integer> & curve_decompositions,const Vector<Integer> & curve_weights)78 Integer degree_via_decomposition(const Matrix<Integer> &curve_decompositions, const Vector<Integer> &curve_weights)
79 {
80   Integer deg = 0;
81   for (Int i = 0; i < curve_decompositions.rows(); ++i) {
82     deg += curve_decompositions(i,0) * curve_weights[i];
83   }
84   return deg;
85 }//END degree_in_uniform
86 
87 /*
88  * @brief Computes the intersection multiplicity of two fan curves in a surface that is GLnZ-isomorphic
89  * to a uniform surface
90  * @param Matrix<Integer> rank_one_flats An integer matrix, whose rows are rays in a surface corresponding to the rank one flats of a matroid realization of that surface.
91  * @param Matrix<Rational> curve_a_rays The rays of the first curve (not homog, no leading coord)
92  * @param Vector<Integer> curve_a_weights The weights of the rays of the first curve, in the same order as the rays
93  * @param Matrix<Rational> curve_b_rays The rays of the second curve (not homog, no leading coord)
94  * @param Vector<Integer> curve_b_weights The weights of the rays of the second curve, in the same order as the rays.
95  */
intersection_multiplicity_via_flats(Matrix<Rational> & rank_one_flats,Matrix<Rational> & curve_a_rays,const Vector<Integer> & curve_a_weights,Matrix<Rational> & curve_b_rays,const Vector<Integer> & curve_b_weights)96 Integer intersection_multiplicity_via_flats(Matrix<Rational>& rank_one_flats,
97                                             Matrix<Rational>& curve_a_rays,
98                                             const Vector<Integer>& curve_a_weights,
99                                             Matrix<Rational>& curve_b_rays,
100                                             const Vector<Integer>& curve_b_weights)
101 {
102   // Make everything integer
103   rank_one_flats = Matrix<Rational>(common::primitive(rank_one_flats));
104   curve_a_rays = Matrix<Rational>(common::primitive(curve_a_rays));
105   curve_b_rays = Matrix<Rational>(common::primitive(curve_b_rays));
106 
107   Matrix<Integer> curve_a_decompositions = positive_decomposition( rank_one_flats, curve_a_rays);
108   Matrix<Integer> curve_b_decompositions = positive_decomposition( rank_one_flats, curve_b_rays);
109 
110   Integer result = degree_via_decomposition(curve_a_decompositions, curve_a_weights) *
111     degree_via_decomposition( curve_b_decompositions, curve_b_weights);
112 
113   // We iterate pairs of rays of the two curves
114   for (Int aray = 0; aray < curve_a_rays.rows(); ++aray) {
115     Vector<Integer> amap = curve_a_decompositions.row(aray);
116     for (Int bray = 0; bray < curve_b_rays.rows(); ++bray) {
117       Vector<Integer> bmap = curve_b_decompositions.row(bray);
118       Integer correction = 0;
119       // Iterate pairs of rank one flats
120       for (auto pair = entire(all_subsets_of_k( sequence(0,rank_one_flats.rows()), 2)); !pair.at_end(); ++pair) {
121         Vector<Int> pair_as_vector(*pair);
122         correction = std::max( correction,
123                                std::min(
124                                         amap[pair_as_vector[0]] * bmap[pair_as_vector[1]],
125                                         amap[pair_as_vector[1]] * bmap[pair_as_vector[0]]
126                                         )*curve_a_weights[aray]*curve_b_weights[bray]
127                                );
128       } // END iterate pairs of rank one flats
129       result -= correction;
130     } // END iterate curve B rays
131   } // END iterate curve A rays
132 
133   return result;
134 } // END intersection_multiplicity_in_uniform
135 
136 /*
137  *      @brief Takes a smooth surface fan and finds vectors corresponding to the rank one flats in some matroidal
138  *      realization
139  * @param BigObject surface A tropical surface fan.
140  *      @return Matrix<Rational> The rank one rays in non-tropically homogeneous coordinates.
141  */
142 template <typename Addition>
find_rank_one_vectors(BigObject surface)143 Matrix<Rational> find_rank_one_vectors(BigObject surface)
144 {
145   BigObject matroid, map;
146   bool is_smooth;
147   call_function("is_smooth", surface) >> is_smooth >> matroid >> map;
148   //Sanity check
149   if (!is_smooth) throw std::runtime_error("Finding rank one vectors: Surface is not smooth.");
150 
151   BigObject face_lattice = matroid.give("LATTICE_OF_FLATS");
152 
153   //Extract rank one flats
154   Int N = matroid.give("N_ELEMENTS");
155   NodeMap<Directed, Set<Int>> all_faces = face_lattice.give("FACES");
156   Set<Int> rank_one_flats = face_lattice.call_method("nodes_of_rank", 1);
157 
158   Matrix<Rational> map_matrix = map.give("MATRIX");
159   map_matrix = inv(map_matrix.minor(range_from(1), range_from(1)));
160   map_matrix = thomog_morphism(map_matrix, zero_vector<Rational>(map_matrix.cols())).first;
161 
162   Matrix<Rational> converted_vectors(0, map_matrix.cols());
163 
164   for (auto j = entire(rank_one_flats); !j.at_end(); ++j) {
165     Vector<Rational> fvector(N);
166     const Set<Int>& flat = all_faces[*j];
167     fvector.slice(flat) = Addition::orientation() * ones_vector<Rational>(flat.size());
168     converted_vectors /= (map_matrix * fvector);
169   }
170 
171   return tdehomog(converted_vectors, 0, 0);
172 } // END find_rank_one_vectors
173 
174 /*
175  * @brief Finds all the maximal cones of a surface containing a given point
176  * @param Vector<Rational> point, in tropical homogeneous coordinates with leading coordinate.
177  * @param Matrix<Rational> facet_normals The [[FACET_NORMALS]]
178  * @param Matrix<Rational> affine_normals The [[AFFINE_HULL_NORMALS]]
179  * @param SparseMatrix<Int> maximal_facets The [[MAXIMAL_POLYTOPES_FACETS]]
180  * @param IncidenceMatrix maximal_affine The [[MAXIMAL_POLYTOPES_AFFINE_HULL_NORMALS]]
181  * @param Matrix<Rational> vertices The [[VERTICES]]
182  * @param IncidenceMatrix<> cones The [[MAXIMAL_POLYTOPES]]
183  * @param Matrix<Rational> lineality The [[LINEALITY_SPACE]]
184  * @return BigObject The star of the surface as a Cycle
185  */
186 template <typename Addition>
compute_surface_star(const Vector<Rational> & point,const Matrix<Rational> & facet_normals,const Matrix<Rational> & affine_normals,const SparseMatrix<Int> & maximal_facets,const IncidenceMatrix<> & maximal_affine,const Matrix<Rational> & vertices,const Matrix<Rational> & lineality,const IncidenceMatrix<> & cones)187 BigObject compute_surface_star(const Vector<Rational>& point,
188                                   const Matrix<Rational>& facet_normals,
189                                   const Matrix<Rational>& affine_normals,
190                                   const SparseMatrix<Int>& maximal_facets,
191                                   const IncidenceMatrix<>& maximal_affine,
192                                   const Matrix<Rational>& vertices,
193                                   const Matrix<Rational>& lineality,
194                                   const IncidenceMatrix<>& cones)
195 {
196   // First of all find the set of all maximal cones containing the point.
197   Set<Int> containing_cones;
198   for (Int mc = 0; mc < cones.rows(); ++mc) {
199     if (!is_zero(affine_normals.minor( maximal_affine.row(mc), All) * point)) {
200       continue;
201     }
202     bool found_non_facet = false;
203     for (Int col = 0; col < maximal_facets.cols() && !found_non_facet; ++col) {
204       if (maximal_facets(mc,col) != 0) {
205         if (facet_normals.row(col) * maximal_facets(mc,col) * point < 0) {
206           found_non_facet = true;
207           continue;
208         }
209       }
210     }
211     if (found_non_facet) continue;
212     // Arriving here, it must contain the point
213     containing_cones += mc;
214   } // END iterate maximal cones
215 
216   // Now construct the star itself
217   Matrix<Rational> star_rays = vertices;
218   Matrix<Rational> star_lineality = lineality;
219   IncidenceMatrix<> star_cones = cones;
220 
221   std::pair<Set<Int>, Set<Int>> f_and_nf = far_and_nonfar_vertices(vertices);
222   Set<Int> cone_intersection = accumulate( rows( cones.minor(containing_cones,All)), operations::mul());
223   Set<Int> unused_rays = sequence(0, vertices.rows()) -
224     accumulate( rows(cones.minor(containing_cones,All)),operations::add());
225 
226   // Start by constructing the lineality space
227   Vector<Int> intersect_nonfar( cone_intersection * f_and_nf.second);
228   Set<Int> intersect_far = cone_intersection * f_and_nf.first;
229   Set<Int> rays_to_remove = intersect_far + unused_rays;
230 
231   for (Int inf = 1; inf < intersect_nonfar.dim(); ++inf) {
232     star_lineality /= (vertices.row(intersect_nonfar[inf]) - star_rays.row(intersect_nonfar[0]));
233     rays_to_remove += intersect_nonfar[inf];
234   }
235   star_lineality /= vertices.minor( intersect_far,All);
236   star_lineality = star_lineality.minor( basis_rows(star_lineality),All);
237 
238   // Replace nonfar vertices in adjacent cones by differences
239   Set<Int> nonfar_remaining = f_and_nf.second - rays_to_remove - intersect_nonfar[0];
240   for (auto nfr = entire(nonfar_remaining); !nfr.at_end(); ++nfr) {
241     star_rays.row(*nfr) = star_rays.row(*nfr) - star_rays.row(intersect_nonfar[0]);
242   }
243 
244   // Make the reference vertex the origin
245   star_rays.row( intersect_nonfar[0]) = unit_vector<Rational>(star_rays.cols(), 0);
246 
247   return BigObject("Cycle", mlist<Addition>(),
248                    "VERTICES", star_rays.minor(~rays_to_remove, All),
249                    "MAXIMAL_POLYTOPES", star_cones.minor(containing_cones, ~rays_to_remove),
250                    "LINEALITY_SPACE", star_lineality,
251                    "WEIGHTS", ones_vector<Integer>(containing_cones.size()));
252 
253 } // END findSurfaceStarCones
254 
255 
256 /*
257  * @brief Computes the rays of the star of a curve at a point
258  * @param Matrix<Rational> curve_vertices The curve's vertices (non-homog. with leading coord)
259  * @param IncidenceMatrix<> curve_cones The cuve's cones
260  * @param IncidenceMatrix<> curve_containers The indices of all the maximal cones containing the point.
261  * @param Matrix<Rationa> lineality If the curve consists only of a lineality space, this is its generator.
262  * @return Matrix<Rational> The star rays, non-homog and without(!) leading coordinate.
263  */
264 std::pair<Matrix<Rational>, Vector<Integer>>
compute_curve_star_rays(const Matrix<Rational> & curve_vertices,const IncidenceMatrix<> & curve_cones,const Vector<Integer> & curve_weights,const Set<Int> & curve_containers,const Matrix<Rational> & lineality)265 compute_curve_star_rays(
266                         const Matrix<Rational>& curve_vertices,
267                         const IncidenceMatrix<>& curve_cones,
268                         const Vector<Integer>& curve_weights,
269                         const Set<Int>& curve_containers,
270                         const Matrix<Rational>& lineality )
271 {
272   // Split up the rays in far and nonfar
273   std::pair<Set<Int>, Set<Int>> f_and_nf = far_and_nonfar_vertices(curve_vertices);
274 
275   Matrix<Rational> result(0,curve_vertices.cols()-1);
276   Vector<Integer> star_weights;
277 
278   // If the curve is only a lineality space:
279   if (lineality.rows() > 0) {
280     result /= lineality.minor(All, range_from(1));
281     result /= -lineality.minor(All, range_from(1));
282     star_weights |= curve_weights[0];
283     star_weights |= curve_weights[0];
284     return std::make_pair(result, star_weights);
285   }
286 
287   // If there is only one maximal cone, we compute its direction vector
288   if (curve_containers.size() == 1) {
289     Set<Int> single_cone = curve_cones.row( *(curve_containers.begin()));
290     Set<Int> far_in_container = f_and_nf.first * single_cone;
291     Vector<Rational> direction;
292     if (far_in_container.size() == 0) {
293       // It's a bounded edge
294       Vector<Int> both_rays(single_cone);
295       direction = curve_vertices.row( both_rays[0]) - curve_vertices.row(both_rays[1]);
296     } else {
297       direction = curve_vertices.row( *(far_in_container.begin()));
298     }
299 
300     direction = direction.slice(range_from(1));
301     result /= direction;
302     result /= -direction;
303     star_weights = curve_weights[ *(curve_containers.begin())] * ones_vector<Integer>(2);
304     return std::make_pair(result, star_weights);
305   } else {
306     // If there are several maximal cones, then every one of them must have point as a vertex
307     const Int points_index = accumulate(rows(curve_cones.minor(curve_containers,All)), operations::mul()).front();
308     for (auto adjRays = entire(curve_containers); !adjRays.at_end(); ++adjRays) {
309       Vector<Rational> direction;
310       const Int ray_index = (curve_cones.row(*adjRays) - points_index).front();
311       if (curve_vertices(ray_index,0) == 0) {
312         direction = curve_vertices.row(ray_index);
313       } else {
314         direction = curve_vertices.row(ray_index) - curve_vertices.row(points_index);
315       }
316       direction = direction.slice(range_from(1));
317       result /= direction;
318       star_weights |= curve_weights[*adjRays];
319     }
320   }
321   return std::make_pair(result, star_weights);
322 } // END compute_curve_star_rays
323 
324 /*
325  * @brief For a curve and a point, computes the indices of all maximal cones containing the point
326  * @param Vector<Rational> point the point, non-homog, with leading coord
327  * @param Matrix<Rational> curve_rays The curve vertices, non-homog, with leading coord
328  * @param IncidenceMatrix curve_cones The curve cones
329  * @param Int some_container An index of one maximal cone containing the point
330  * @return Set<Int> All indices of cones containing the point
331  */
compute_containing_cones(const Vector<Rational> & point,const Matrix<Rational> & curve_rays,const IncidenceMatrix<> & curve_cones,Int some_container)332 Set<Int> compute_containing_cones(const Vector<Rational>& point,
333                                   const Matrix<Rational>& curve_rays,
334                                   const IncidenceMatrix<>& curve_cones,
335                                   Int some_container)
336 {
337   // Go through the rays of the container - if point is equal to one of them, return all
338   // maximal cones using the vertex
339   Set<Int> container_set = curve_cones.row(some_container);
340   for (auto cray = entire(container_set); !cray.at_end(); ++cray) {
341     if (point == curve_rays.row(*cray)) {
342       return T(curve_cones).row(*cray);
343     }
344   }
345 
346   // If it isn't, it's just the one cone
347   return scalar2set(some_container);
348 }
349 
350 
351 template <typename Addition>
intersect_in_smooth_surface(BigObject surface,BigObject cycle_a,BigObject cycle_b)352 BigObject intersect_in_smooth_surface(BigObject surface, BigObject cycle_a, BigObject cycle_b)
353 {
354   // Extract data
355   Int dim_a = cycle_a.give("PROJECTIVE_DIM");
356   Int dim_b = cycle_b.give("PROJECTIVE_DIM");
357   Int ambient_dim = surface.give("PROJECTIVE_AMBIENT_DIM");
358 
359   // Basic sanity checks
360   if (dim_a + dim_b <= 1)
361     return empty_cycle<Addition>(ambient_dim);
362   if (dim_a > 2 || dim_b > 2)
363     throw std::runtime_error("intersect_in_smooth_surface: Cycles dimension too large.");
364 
365   // If one is full-dimensional, return the other one with multiplied weights
366   const Vector<Integer> weights_a = cycle_a.give("WEIGHTS");
367   const Vector<Integer> weights_b = cycle_b.give("WEIGHTS");
368   if (dim_a == 2) {
369     return cycle_b.call_method("multiply_weights", weights_a[0]);
370   }
371   if (dim_b == 2) {
372     return cycle_a.call_method("multiply_weights", weights_b[1]);
373   }
374 
375   // From here on we know we have two curves.
376 
377   // Refine both curves along the surface
378   RefinementResult refined_cycle_a = refinement(cycle_a, surface, false, false, false, true, false);
379   RefinementResult refined_cycle_b = refinement(cycle_b, surface, false, false, false, true, false);
380   Matrix<Rational> rays_a = refined_cycle_a.complex.give("VERTICES");
381   rays_a = tdehomog(rays_a);
382   Matrix<Rational> rays_b = refined_cycle_b.complex.give("VERTICES");
383   rays_b = tdehomog(rays_b);
384   Matrix<Rational> lin_a = refined_cycle_a.complex.give("LINEALITY_SPACE");
385   lin_a = tdehomog(lin_a);
386   Matrix<Rational> lin_b = refined_cycle_b.complex.give("LINEALITY_SPACE");
387   lin_b = tdehomog(lin_b);
388   IncidenceMatrix<> cones_a = refined_cycle_a.complex.give("MAXIMAL_POLYTOPES");
389   IncidenceMatrix<> cones_b = refined_cycle_b.complex.give("MAXIMAL_POLYTOPES");
390   const Vector<Integer> refweights_a = refined_cycle_a.complex.give("WEIGHTS");
391   const Vector<Integer> refweights_b = refined_cycle_b.complex.give("WEIGHTS");
392 
393   // Now intersect the refined versions.
394   fan_intersection_result ab_intersect = fan_intersection(rays_a, lin_a, cones_a, rays_b, lin_b, cones_b);
395 
396   // The potential intersection points are the nonfar vertices of this
397   Matrix<Rational> ab_vertices = ab_intersect.rays;
398   Matrix<Rational> ab_vertices_homog = thomog(ab_vertices);
399   IncidenceMatrix<> ab_vertices_by_cones = T(ab_intersect.cones);
400   Vector<Integer> ab_weights;
401   Set<Int> nonfar_and_nonzero;
402 
403   Matrix<Rational> surface_rays = surface.give("VERTICES");
404   IncidenceMatrix<> surface_cones = surface.give("MAXIMAL_POLYTOPES");
405   Matrix<Rational> surface_lineality = surface.give("LINEALITY_SPACE");
406   Matrix<Rational> facet_normals = surface.give("FACET_NORMALS");
407   Matrix<Rational> affine_hull = surface.give("AFFINE_HULL");
408   SparseMatrix<Int> maximal_facets = surface.give("MAXIMAL_POLYTOPES_FACETS");
409   IncidenceMatrix<> maximal_affine = surface.give("MAXIMAL_POLYTOPES_AFFINE_HULL_NORMALS");
410 
411   for (Int point = 0; point < ab_vertices.rows(); ++point) {
412     // If it's a ray, ignore it.
413     if (ab_vertices(point,0) == 0)
414       continue;
415 
416     // If it's a vertex, we need to compute the star of X at that point.
417     BigObject surface_star = compute_surface_star<Addition>(
418                                                 ab_vertices_homog.row(point),
419                                                 facet_normals,
420                                                 affine_hull,
421                                                 maximal_facets,
422                                                 maximal_affine,
423                                                 surface_rays,
424                                                 surface_lineality,
425                                                 surface_cones);
426     Matrix<Rational> rank_one_vectors = find_rank_one_vectors<Addition>(surface_star);
427 
428     // Now compute the star at each curve
429     Int cone_of_point = ab_vertices_by_cones.row(point).front();
430     Int cone_of_a = ab_intersect.xcontainers.row(cone_of_point).front();
431     Int cone_of_b = ab_intersect.ycontainers.row(cone_of_point).front();
432     std::pair<Matrix<Rational>, Vector<Integer>> a_star =
433        compute_curve_star_rays(rays_a, cones_a, refweights_a,
434                                compute_containing_cones(ab_vertices.row(point), rays_a, cones_a, cone_of_a), lin_a);
435     std::pair<Matrix<Rational>, Vector<Integer>> b_star =
436        compute_curve_star_rays(rays_b, cones_b, refweights_b,
437                                compute_containing_cones(ab_vertices.row(point), rays_b, cones_b, cone_of_b), lin_b);
438 
439 
440     // Finally, we can compute the multiplicity
441 
442     Integer mult = intersection_multiplicity_via_flats(
443        rank_one_vectors, a_star.first, a_star.second, b_star.first, b_star.second);
444 
445     if (mult != 0) {
446       nonfar_and_nonzero += point;
447       ab_weights |= mult;
448     }
449   } // END iterate intersection rays
450 
451   // If no points remain, return the empty_cycle
452   if (nonfar_and_nonzero.size() == 0)
453     return empty_cycle<Addition>(ambient_dim);
454   ab_vertices = ab_vertices.minor(nonfar_and_nonzero, range_from(1));
455 
456   return point_collection<Addition>(thomog(ab_vertices,0,0), ab_weights);
457 } // END intersect_in_smooth_surface
458 
459 
460 // --------------------- PERL WRAPPERS -----------------------------
461 
462 
463 UserFunctionTemplate4perl("# @category Intersection theory"
464                           "# Computes the intersection product of two cycles in a smooth surface"
465                           "# @param Cycle<Addition> surface A smooth surface"
466                           "# @param Cycle<Addition> A any cycle in the surface"
467                           "# @param Cycle<Addition> B any cycle in the surface"
468                           "# @return Cycle<Addition> The intersection product of A and B in the surface",
469                           "intersect_in_smooth_surface<Addition>(Cycle<Addition>,Cycle<Addition>, Cycle<Addition>)");
470 
471 FunctionTemplate4perl("compute_surface_star<Addition>(Vector, Matrix,Matrix,SparseMatrix<Int>, IncidenceMatrix, Matrix, Matrix,IncidenceMatrix)");
472 
473 } }
474