File: ARAP_parameterizer_3.h

package info (click to toggle)
cgal 6.1.1-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 144,952 kB
  • sloc: cpp: 811,597; ansic: 208,576; sh: 493; python: 411; makefile: 286; javascript: 174
file content (1480 lines) | stat: -rw-r--r-- 56,758 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
// Copyright (c) 2016  GeometryFactory (France).
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org).
//
// $URL: https://github.com/CGAL/cgal/blob/v6.1.1/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/ARAP_parameterizer_3.h $
// $Id: include/CGAL/Surface_mesh_parameterization/ARAP_parameterizer_3.h 08b27d3db14 $
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
//
// Author(s)     : Mael Rouxel-Labbé

#ifndef CGAL_SURFACE_MESH_PARAMETERIZATION_ARAP_PARAMETERIZER_3_H
#define CGAL_SURFACE_MESH_PARAMETERIZATION_ARAP_PARAMETERIZER_3_H

#include <CGAL/disable_warnings.h>

#include <CGAL/license/Surface_mesh_parameterization.h>

#include <CGAL/Surface_mesh_parameterization/internal/Bool_property_map.h>
#include <CGAL/Surface_mesh_parameterization/internal/Containers_filler.h>
#include <CGAL/Surface_mesh_parameterization/internal/kernel_traits.h>
#include <CGAL/Surface_mesh_parameterization/internal/validity.h>
#include <CGAL/Surface_mesh_parameterization/IO/File_off.h>

#include <CGAL/Surface_mesh_parameterization/Mean_value_coordinates_parameterizer_3.h>
#include <CGAL/Surface_mesh_parameterization/LSCM_parameterizer_3.h>
#include <CGAL/Surface_mesh_parameterization/MVC_post_processor_3.h>
#include <CGAL/Surface_mesh_parameterization/Two_vertices_parameterizer_3.h>
#include <CGAL/Surface_mesh_parameterization/parameterize.h>
#include <CGAL/Weights/utils.h>

#include <CGAL/Polygon_mesh_processing/connected_components.h>

#if defined(CGAL_EIGEN3_ENABLED)
#include <CGAL/Eigen_solver_traits.h>
#ifdef CGAL_SMP_USE_SUITESPARSE_SOLVERS
#include <Eigen/UmfPackSupport>
#endif
#endif

#include <CGAL/assertions.h>
#include <CGAL/basic.h>
#include <CGAL/circulator.h>
#include <CGAL/Default.h>
#include <CGAL/number_utils.h>

// Below are two macros that can be used to improve the accuracy of optimal Lt
// matrices.
// Note that at least one of these macros should be defined. If:
//
// - CGAL_SMP_SOLVE_CUBIC_EQUATION is defined: a cubic equation is solved instead of the
//   complete bivariate system. Although less accurate, it is usually sufficient.
// - CGAL_SMP_SOLVE_CUBIC_EQUATION and CGAL_SMP_SOLVE_EQUATIONS_WITH_GMP are defined:
//   the same cubic is solved but using GMP and CGAL's algebraic kernel.
// - CGAL_SMP_SOLVE_EQUATIONS_WITH_GMP is defined: a bivariate system is solved,
//   using GMP and CGAL's algebraic kernel.
//
// Using CGAL_SMP_SOLVE_EQUATIONS_WITH_GMP requires GMP, MPFI, and linking CGAL
// with Core and MPFI. This can be simply done in 'CMakeLists.txt' by using:
// 'find_package(CGAL QUIET COMPONENTS Core MPFI)'

// -----------------------------------------------------------------------------
#define CGAL_SMP_SOLVE_CUBIC_EQUATION
//#define CGAL_SMP_SOLVE_EQUATIONS_WITH_GMP
// -----------------------------------------------------------------------------

#if !defined(CGAL_SMP_SOLVE_CUBIC_EQUATION) && !defined(CGAL_SMP_SOLVE_EQUATIONS_WITH_GMP)
#error "Either 'CGAL_SMP_SOLVE_CUBIC_EQUATION' or 'CGAL_SMP_SOLVE_EQUATIONS_WITH_GMP' must be defined."
#endif

#if defined(CGAL_SMP_SOLVE_EQUATIONS_WITH_GMP)
#if !defined(CGAL_USE_GMP) || !defined(CGAL_USE_MPFI)
#error "'CGAL_SMP_SOLVE_EQUATIONS_WITH_GMP' cannot be defined if GMP or MPFI is not present."
#endif
#endif

#ifdef CGAL_SMP_SOLVE_EQUATIONS_WITH_GMP
#include <CGAL/GMP_arithmetic_kernel.h>
#include <CGAL/Algebraic_kernel_d_2.h>
#elif defined(CGAL_SMP_SOLVE_CUBIC_EQUATION)
#include <CGAL/Kernel/Conic_misc.h> // used to solve conic equations
#endif

#include <boost/iterator/function_output_iterator.hpp>
#include <boost/functional/hash.hpp>

#include <unordered_set>
#include <iostream>
#include <fstream>
#include <limits>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include <type_traits>

/// \file ARAP_parameterizer_3.h

// @todo Handle the case cot = 0 with a local parameterization aligned with the axes
//       (this produces C2=0 which is problematic to compute a & b)
// @todo Add distortion measures
// @todo Parallelize the local phase?

namespace CGAL {

namespace Surface_mesh_parameterization {

// ------------------------------------------------------------------------------------
// Declaration
// ------------------------------------------------------------------------------------

/// \ingroup PkgSurfaceMeshParameterizationMethods
///
/// The class `ARAP_parameterizer_3` implements the
/// *Local/Global Approach to Mesh Parameterization* \cgalCite{liu2008local}.
///
/// This parameterization allows the user to prioritize angle preservation,
/// shape preservation, or a balance of both.
/// A parameter \f$\lambda\f$ controls whether the priority is given to angle
/// or to shape preservation: when \f$\lambda=0\f$, the parameterization is
/// as-similar-as-possible (ASAP) and is equivalent to the (conforming) LSCM parameterization.
/// As \f$\lambda\f$ grows, the shape preservation becomes more and more important,
/// yielding, when \f$\lambda\f$ goes to infinity, a parameterization that is as-rigid-as-possible (ARAP).
///
/// This is a free border parameterization. There is no need to map the border of the surface
/// onto a convex polygon.
/// When \f$\lambda=0\f$, only two pinned vertices are needed to ensure a unique solution.
/// When \f$\lambda\f$ is non-null, the border does not need to be parameterized and
/// a random vertex is pinned.
///
/// If flips are present in the initial parameterization, a postprocessing step
/// is applied using `CGAL::Surface_mesh_parameterization::MVC_post_processor_3<TriangleMesh_, SolverTraits_>`
/// to attempt to obtain a valid final embedding.
///
/// A one-to-one mapping is *not* guaranteed.
///
/// \cgalModels{Parameterizer_3}
///
/// \tparam TriangleMesh_ must be a model of `FaceGraph`.
///
/// \tparam BorderParameterizer_ is a strategy to parameterize the surface border
///         and must be a model of `Parameterizer_3`.<br>
///         <b>%Default:</b>
/// \code
///   Two_vertices_parameterizer_3<TriangleMesh_>
/// \endcode
///
/// \tparam SolverTraits_ must be a model of `SparseLinearAlgebraTraits_d`.<br>
///         Note that the system is *not* symmetric.<br>
///         <b>%Default:</b> If \ref thirdpartyEigen "Eigen" 3.1 (or greater) is available
///         and `CGAL_EIGEN3_ENABLED` is defined, then an overload of `Eigen_solver_traits`
///         is provided as default parameter:
/// \code
///   CGAL::Eigen_solver_traits<
///           Eigen::SparseLU<Eigen_sparse_matrix<double>::EigenType> >
/// \endcode
///         Moreover, if SuiteSparse solvers are available, which is greatly preferable for speed,
///         then the default parameter is:
/// \code
///   CGAL::Eigen_solver_traits<
///           Eigen::UmfPackLU<Eigen_sparse_matrix<double>::EigenType> >
/// \endcode
///
/// \sa `CGAL::Surface_mesh_parameterization::Fixed_border_parameterizer_3<TriangleMesh, BorderParameterizer, SolverTraits>`
/// \sa `CGAL::Surface_mesh_parameterization::Iterative_authalic_parameterizer_3<TriangleMesh, BorderParameterizer, SolverTraits>`
///
template < class TriangleMesh_,
           class BorderParameterizer_ = Default,
           class SolverTraits_ = Default>
class ARAP_parameterizer_3
{
public:
#ifndef DOXYGEN_RUNNING
  typedef typename Default::Get<
    BorderParameterizer_,
    Two_vertices_parameterizer_3<TriangleMesh_> >::type       Border_parameterizer;

  #if !defined(CGAL_EIGEN3_ENABLED)
  static_assert(!(std::is_same<SolverTraits_, Default>::value),
                            "Error: You must either provide 'SolverTraits_' or link CGAL with the Eigen library");
  #endif

  typedef typename Default::Get<
    SolverTraits_,
  #if defined(CGAL_EIGEN3_ENABLED)
    #ifdef CGAL_SMP_USE_SUITESPARSE_SOLVERS
      CGAL::Eigen_solver_traits<
        Eigen::UmfPackLU<Eigen_sparse_matrix<double>::EigenType> >
    #else
      CGAL::Eigen_solver_traits<
        Eigen::SparseLU<Eigen_sparse_matrix<double>::EigenType> >
    #endif
  #else
    SolverTraits_ // no parameter provided, and Eigen is not enabled: so don't compile!
  #endif
  >::type                                                     Solver_traits;
#else
  /// Border parameterizer type
  typedef Border_parameterizer_                               Border_parameterizer;

  /// Solver traits type
  typedef SolverTraits_                                       Solver_traits;

  /// Number type, deduced from the internal vertex point map of `Triangle_mesh`
  typedef unspecified_type                                    NT;
#endif

  /// Triangle mesh type
  typedef TriangleMesh_                                       Triangle_mesh;

  typedef TriangleMesh_                                       TriangleMesh;

  /// Mesh halfedge type
  typedef typename boost::graph_traits<Triangle_mesh>::halfedge_descriptor  halfedge_descriptor;

// Private types
private:
  typedef typename boost::graph_traits<Triangle_mesh>::vertex_descriptor    vertex_descriptor;
  typedef typename boost::graph_traits<Triangle_mesh>::face_descriptor      face_descriptor;
  typedef typename boost::graph_traits<Triangle_mesh>::face_iterator        face_iterator;
  typedef typename boost::graph_traits<Triangle_mesh>::vertex_iterator      vertex_iterator;

  typedef CGAL::Halfedge_around_target_circulator<Triangle_mesh>    halfedge_around_target_circulator;
  typedef CGAL::Halfedge_around_face_circulator<Triangle_mesh>      halfedge_around_face_circulator;

  typedef std::unordered_set<vertex_descriptor>                     Vertex_set;
  typedef std::vector<face_descriptor>                              Faces_vector;

  // Traits subtypes:
  typedef typename internal::Kernel_traits<Triangle_mesh>::Kernel   Kernel;
  typedef typename Kernel::FT                                       NT;
  typedef typename Kernel::Point_2                                  Point_2;
  typedef typename Kernel::Point_3                                  Point_3;
  typedef typename Kernel::Vector_2                                 Vector_2;
  typedef typename Kernel::Vector_3                                 Vector_3;

  // Solver traits subtypes:
  typedef typename Solver_traits::Vector                            Vector;
  typedef typename Solver_traits::Matrix                            Matrix;

  // Memory maps
    // Each triangle is associated a linear transformation matrix
  typedef std::pair<NT, NT>                                         Lt_matrix;
  typedef CGAL::Unique_hash_map<face_descriptor,
                                Lt_matrix,
                                boost::hash<face_descriptor> >      Lt_hash_map;
  typedef boost::associative_property_map<Lt_hash_map>              Lt_map;

    // Each angle (uniquely determined by the opposite half edge) has a cotangent
  typedef CGAL::Unique_hash_map<halfedge_descriptor, NT,
                                boost::hash<halfedge_descriptor> >  Cot_hm;
  typedef boost::associative_property_map<Cot_hm>                   Cot_map;

    // Each face has a local 2D isometric parameterization
  typedef std::pair<int, int>                                       Local_indices;
  typedef CGAL::Unique_hash_map<halfedge_descriptor,
                                Local_indices,
                                boost::hash<halfedge_descriptor> >  Lp_hm;
  typedef boost::associative_property_map<Lp_hm>                    Lp_map;
  typedef std::vector<Point_2>                                      Local_points;

// Private fields
private:
  // %Object that maps (at least two) border vertices onto a 2D space
  Border_parameterizer m_borderParameterizer;

  // Traits object to solve a sparse linear system
  Solver_traits m_linearAlgebra;

  // Controlling parameters
  const NT m_lambda;
  const NT m_lambda_tolerance; // used to determine when we switch to full ARAP

  // Energy minimization parameters
  const unsigned int m_iterations;
  const NT m_tolerance;

// Private accessors
private:
  // Get the object that maps the surface's border onto a 2D space.
  Border_parameterizer& get_border_parameterizer() { return m_borderParameterizer; }

  // Get the sparse linear algebra (traits object to access the linear system).
  Solver_traits& get_linear_algebra_traits() { return m_linearAlgebra; }

// Private utilities
private:
  // Print the parameterized mesh.
  template <typename VertexUVMap,
            typename VertexIndexMap>
  void output_uvmap(const std::string filename,
                    const Triangle_mesh& mesh,
                    const Vertex_set& vertices,
                    const Faces_vector& faces,
                    const VertexUVMap uvmap,
                    const VertexIndexMap vimap) const
  {
    std::ofstream out(filename.c_str());
    IO::output_uvmap_to_off(mesh, vertices, faces, uvmap, vimap, out);
  }

  // Print the parameterized mesh.
  template <typename VertexUVMap,
            typename VertexIndexMap>
  void output_uvmap(const std::string filename,
                    const unsigned int iter,
                    const Triangle_mesh& mesh,
                    const Vertex_set& vertices,
                    const Faces_vector& faces,
                    const VertexUVMap uvmap,
                    const VertexIndexMap vimap) const
  {
    std::ostringstream out_ss;
    out_ss << filename << iter << ".off" << std::ends;
    std::ofstream out(out_ss.str().c_str());
    IO::output_uvmap_to_off(mesh, vertices, faces, uvmap, vimap, out);
  }

  // Copy the data from two vectors to the UVmap.
  template <typename VertexUVMap,
            typename VertexIndexMap,
            typename VertexParameterizedMap>
  void assign_solution(const Vector& Xu,
                       const Vector& Xv,
                       const Vertex_set& vertices,
                       VertexUVMap uvmap,
                       const VertexIndexMap vimap,
                       const VertexParameterizedMap vpmap)
  {
    for(vertex_descriptor vd : vertices) {
      // The solver might not have managed to exactly constrain the vertex that was marked
      // as constrained; simply don't update its position.
      if(get(vpmap, vd))
        continue;

      int index = get(vimap, vd);
      NT u = Xu(index);
      NT v = Xv(index);
      put(uvmap, vd, Point_2(u, v));
    }
  }

// Private operations
private:
  // Store the vertices and faces of the mesh in memory.
  Error_code initialize_containers(const Triangle_mesh& mesh,
                                   halfedge_descriptor bhd,
                                   Vertex_set& vertices,
                                   Faces_vector& faces) const
  {
    internal::Containers_filler<Triangle_mesh> fc(mesh, vertices, &faces);
    Polygon_mesh_processing::connected_component(
                                      face(opposite(bhd, mesh), mesh),
                                      mesh,
                                      boost::make_function_output_iterator(fc));

    if (vertices.empty() || faces.empty())
      return ERROR_EMPTY_MESH;

    return OK;
  }

  // Initialize the UV values with a first parameterization of the input.
  template <typename VertexUVMap,
            typename VertexIndexMap>
  Error_code compute_initial_uv_map(Triangle_mesh& mesh,
                                    halfedge_descriptor bhd,
                                    VertexUVMap uvmap,
                                    VertexIndexMap vimap) const
  {
    Error_code status;

    unsigned int number_of_borders =
                         CGAL::Polygon_mesh_processing::number_of_borders(mesh);
    if(number_of_borders == 0) {
      status = ERROR_BORDER_TOO_SHORT;
      return status;
    }

    // temporary vpmap since we do not need it in the future
    std::unordered_set<vertex_descriptor> vs;
    internal::Bool_property_map<std::unordered_set<vertex_descriptor> > vpmap(vs);

    // According to the paper, MVC is better for single border and LSCM is better
    // when there are multiple borders
    if(number_of_borders == 1) {
      typedef Mean_value_coordinates_parameterizer_3<Triangle_mesh>     MVC_parameterizer;
      MVC_parameterizer mvc_parameterizer;
      status = mvc_parameterizer.parameterize(mesh, bhd, uvmap, vimap, vpmap);
    } else {
      typedef LSCM_parameterizer_3<Triangle_mesh, Border_parameterizer> LSCM_parameterizer;
      LSCM_parameterizer lscm_parameterizer;
      status = lscm_parameterizer.parameterize(mesh, bhd, uvmap, vimap, vpmap);
    }

    return status;
  }

  // Parameterize the border. The number of fixed vertices depends on the value
  // of the parameter lambda.
  template <typename VertexUVMap,
            typename VertexIndexMap,
            typename VertexParameterizedMap>
  Error_code parameterize_border(const Triangle_mesh& mesh,
                                 const Vertex_set& vertices,
                                 halfedge_descriptor bhd,
                                 VertexIndexMap vimap,
                                 VertexParameterizedMap vpmap)
  {
    Error_code status = OK;
    CGAL_precondition(!vertices.empty());

    if(m_lambda != 0.) {
      // Fix a random vertex, the value in uvmap is already set
      vertex_descriptor vd = *(vertices.begin());
      put(vpmap, vd, true);
    } else {
      // This fixes two vertices that are far in the original geometry

      // A local uvmap (that is then discarded) is passed to avoid changing
      // the values of the real uvmap. Since we can't get VertexUVMap::C,
      // we build a map with the same key and value types
      typedef std::unordered_map<typename VertexUVMap::key_type,
                                   typename VertexUVMap::value_type> Useless_map;
      typedef boost::associative_property_map<Useless_map>           Useless_pmap;

      Useless_map useless_map;
      Useless_pmap useless_uvpmap(useless_map);
      status = get_border_parameterizer().parameterize(mesh, bhd, useless_uvpmap,
                                                       vimap, vpmap);
    }

    return status;
  }

  // Compute the cotangent of the angle between the vectors ij and ik.
  void compute_cotangent_angle(const Triangle_mesh& mesh,
                               halfedge_descriptor hd,
                               vertex_descriptor vi,
                               vertex_descriptor vj,
                               vertex_descriptor vk,
                               Cot_map ctmap) const
  {
    typedef typename boost::property_map<Triangle_mesh,
                                         boost::vertex_point_t>::const_type PPmap;
    const PPmap ppmap = get(vertex_point, mesh);

    const Point_3& position_vi = get(ppmap, vi);
    const Point_3& position_vj = get(ppmap, vj);
    const Point_3& position_vk = get(ppmap, vk);

    const NT cot = CGAL::Weights::cotangent(position_vi, position_vj, position_vk);
    put(ctmap, hd, cot);
  }

  // Fill the map 'ctmap' with the cotangents of the angles of the faces of 'mesh'.
  Error_code compute_cotangent_angles(const Triangle_mesh& mesh,
                                      const Faces_vector& faces,
                                      Cot_map ctmap) const
  {
    // Since we loop faces, we are implicitly defining the weight of border halfedges as 0...
    for(face_descriptor fd : faces) {
      halfedge_descriptor hd = halfedge(fd, mesh), hdb = hd;

      vertex_descriptor vi = target(hd, mesh);
      hd = next(hd, mesh);
      vertex_descriptor vj = target(hd, mesh);
      hd = next(hd, mesh);
      vertex_descriptor vk = target(hd, mesh);
      hd = next(hd, mesh);

      if(hd != hdb) { // make sure that it is a triangular face
        return ERROR_NON_TRIANGULAR_MESH;
      }

      compute_cotangent_angle(mesh, hd, vk, vj, vi , ctmap); // angle at v_j
      compute_cotangent_angle(mesh, next(hd, mesh), vi, vk, vj , ctmap); // angle at v_k
      compute_cotangent_angle(mesh, prev(hd, mesh), vj, vi, vk , ctmap); // angle at v_i
    }

    return OK;
  }

  // computes `w_ij`, the `(i,j)`-coefficient of matrix `A` for `j` neighbor vertex of `i`.
  NT compute_w_ij(const Triangle_mesh& mesh,
                  halfedge_descriptor hd,
                  const Cot_map ctmap) const
  {
    // Note that BGL halfedge and vertex circulators move clockwise!!

    // coefficient corresponding to the angle at vk if vk is the vertex before vj
    // while circulating around vi
    const NT c_k = get(ctmap, opposite(hd, mesh));

    // coefficient corresponding to the angle at vl if vl is the vertex after vj
    // while circulating around vi
    const NT c_l = get(ctmap, hd);

    const NT weight = c_k + c_l;
    return weight;
  }

  // Compute the line i of matrix A
  // - call compute_w_ij() to compute the A coefficient w_ij for each neighbor v_j.
  // - compute w_ii = - sum of w_ijs.
  //
  // \pre Vertices must be indexed.
  // \pre Vertex i mustn't be already parameterized.
  // \pre Line i of A must contain only zeros.
  template <typename VertexIndexMap>
  Error_code fill_linear_system_matrix(Matrix& A,
                                       const Triangle_mesh& mesh,
                                       vertex_descriptor vertex,
                                       const Cot_map ct_map,
                                       VertexIndexMap vimap) const
  {
    int i = get(vimap, vertex);

    // Circulate over the vertices around 'vertex' to compute w_ii and w_ijs
    NT w_ii = 0;
    int vertexIndex = 0;

    halfedge_around_target_circulator hc(vertex, mesh), end = hc;
    CGAL_For_all(hc, end) {
      halfedge_descriptor hd = *hc;
      CGAL_assertion(target(hd, mesh) == vertex);

      NT w_ij = -1.0 * compute_w_ij(mesh, hd, ct_map);

      // w_ii = - sum of w_ijs
      w_ii -= w_ij;

      // Get j index
      vertex_descriptor v_j = source(hd, mesh);
      int j = get(vimap, v_j);

      // Set w_ij in matrix
      A.set_coef(i, j, w_ij, true /*new*/);
      vertexIndex++;
    }

    if (vertexIndex < 2)
      return ERROR_NON_TRIANGULAR_MESH;

    // Set w_ii in matrix
    A.set_coef(i, i, w_ii, true /*new*/);
    return OK;
  }

  // Initialize the (constant) matrix A in the linear system "A*X = B",
  // after (at least two) border vertices parameterization.
  template <typename VertexIndexMap,
            typename VertexParameterizedMap>
  Error_code initialize_matrix_A(const Triangle_mesh& mesh,
                                 const Vertex_set& vertices,
                                 const Cot_map ctmap,
                                 VertexIndexMap vimap,
                                 VertexParameterizedMap vpmap,
                                 Matrix& A) const
  {
    Error_code status = OK;

    // compute A
    for(vertex_descriptor vd : vertices) {
      if(!get(vpmap, vd)) { // not yet parameterized
        // Compute the line i of the matrix A
        status = fill_linear_system_matrix(A, mesh, vd, ctmap, vimap);
        if(status != OK)
          return status;
      } else { // fixed vertices
        int index = get(vimap, vd);
        A.set_coef(index, index, 1, true /*new*/);
      }
    }
    return status;
  }

  // Solves the cubic equation a3 x^3 + a2 x^2 + a1 x + a0 = 0.
  std::size_t solve_cubic_equation(const NT a3, const NT a2, const NT a1, const NT a0,
                                   std::vector<NT>& roots) const
  {
    CGAL_precondition(roots.empty());
    NT r1 = 0, r2 = 0, r3 = 0; // roots of the cubic equation

    int root_n = CGAL::solve_cubic(a3, a2, a1, a0, r1, r2, r3);
    CGAL_postcondition(root_n > 0);

    roots.push_back(r1);
    if(root_n > 1)
      roots.push_back(r2);
    if(root_n > 2)
      roots.push_back(r3);

    return roots.size();
  }

#if defined(CGAL_SMP_SOLVE_CUBIC_EQUATION) && defined(CGAL_SMP_SOLVE_EQUATIONS_WITH_GMP)
  // Solves the equation a3 x^3 + a2 x^2 + a1 x + a0 = 0, using CGAL's algebraic kernel.
  std::size_t solve_cubic_equation_with_AK(const NT a3, const NT a2,
                                           const NT a1, const NT a0,
                                           std::vector<NT>& roots) const
  {
    CGAL_precondition(roots.empty());

    typedef CGAL::Gmpq                                        GMP_NT;
    typedef CGAL::Algebraic_kernel_d_1<GMP_NT>                Algebraic_kernel_d_1;
    typedef typename Algebraic_kernel_d_1::Polynomial_1       Polynomial_1;
    typedef typename Algebraic_kernel_d_1::Algebraic_real_1   Algebraic_real_1;
    typedef typename Algebraic_kernel_d_1::Coefficient        Coefficient;
    typedef typename Algebraic_kernel_d_1::Multiplicity_type  Multiplicity_type;

    typedef CGAL::Polynomial_traits_d<Polynomial_1>           Polynomial_traits_1;

    typedef Algebraic_kernel_d_1::Solve_1                     Solve_1;

    Algebraic_kernel_d_1 ak_1;
    const Solve_1 solve_1 = ak_1.solve_1_object();

    GMP_NT a3q(a3);
    GMP_NT a2q(a2);
    GMP_NT a1q(a1);
    GMP_NT a0q(a0);

    typename Polynomial_traits_1::Construct_polynomial construct_polynomial_1;
    std::pair<CGAL::Exponent_vector, Coefficient> coeffs_x[1]
      = {std::make_pair(CGAL::Exponent_vector(1,0), Coefficient(1))};
    Polynomial_1 x=construct_polynomial_1(coeffs_x, coeffs_x+1);
    Polynomial_1 pol = a3q*CGAL::ipower(x,3) + a2q*CGAL::ipower(x,2) + a1q*x + a0q;

    std::vector<std::pair<Algebraic_real_1, Multiplicity_type> > ak_roots;
    solve_1(pol, std::back_inserter(ak_roots));

    for(std::size_t i=0; i<ak_roots.size(); ++i)
      roots.push_back(CGAL::to_double(ak_roots[i].first));

    return roots.size();
  }
#endif // defined(CGAL_SMP_SOLVE_CUBIC_EQUATION) && defined(CGAL_SMP_SOLVE_EQUATIONS_WITH_GMP)

#if !defined(CGAL_SMP_SOLVE_CUBIC_EQUATION) && defined(CGAL_SMP_SOLVE_EQUATIONS_WITH_GMP)
  // Solve the bivariate system
  // { C1 * a + 2 * lambda * a ( a^2 + b^2 - 1 ) = C2
  // { C1 * b + 2 * lambda * b ( a^2 + b^2 - 1 ) = C3
  // using CGAL's algebraic kernel.
  std::size_t solve_bivariate_system(const NT C1, const NT C2, const NT C3,
                                     std::vector<NT>& a_roots, std::vector<NT>& b_roots) const
  {
    typedef CGAL::Gmpq                                        GMP_NT;
    typedef CGAL::Algebraic_kernel_d_2<GMP_NT>                Algebraic_kernel_d_2;
    typedef typename Algebraic_kernel_d_2::Polynomial_2       Polynomial_2;
    typedef typename Algebraic_kernel_d_2::Algebraic_real_2   Algebraic_real_2;
    typedef typename Algebraic_kernel_d_2::Multiplicity_type  Multiplicity_type;
    typedef typename Algebraic_kernel_d_2::Coefficient        Coefficient;

    typedef CGAL::Polynomial_traits_d<Polynomial_2>           Polynomial_traits_2;

    typedef Algebraic_kernel_d_2::Solve_2             Solve_2;
    typedef Algebraic_kernel_d_2::Is_coprime_2        Is_coprime_2;
    typedef Algebraic_kernel_d_2::Make_coprime_2      Make_coprime_2;

    Algebraic_kernel_d_2 ak_2;
    const Is_coprime_2 is_coprime_2 = ak_2.is_coprime_2_object();
    const Solve_2 solve_2 = ak_2.solve_2_object();
    const Make_coprime_2 make_coprime_2 = ak_2.make_coprime_2_object();

    typename Polynomial_traits_2::Construct_polynomial construct_polynomial_2;
    std::pair<CGAL::Exponent_vector, Coefficient> coeffs_x[1]
      = {std::make_pair(CGAL::Exponent_vector(1,0), Coefficient(1))};
    Polynomial_2 x=construct_polynomial_2(coeffs_x,coeffs_x+1);
    std::pair<CGAL::Exponent_vector, Coefficient> coeffs_y[1]
      = {std::make_pair(CGAL::Exponent_vector(0,1), Coefficient(1))};
    Polynomial_2 y=construct_polynomial_2(coeffs_y,coeffs_y+1);

    GMP_NT C1q(C1);
    GMP_NT C2q(C2);
    GMP_NT C3q(C3);

    Polynomial_2 pol1 = C1q * x + 2 * m_lambda * x * ( x*x + y*y - 1) - C2q;
    Polynomial_2 pol2 = C1q * y + 2 * m_lambda * y * ( x*x + y*y - 1) - C3q;

    CGAL_precondition_code(typedef Algebraic_kernel_d_2::Is_square_free_2    Is_square_free_2;)
    CGAL_precondition_code(const Is_square_free_2 is_square_free_2 = ak_2.is_square_free_2_object();)
    CGAL_precondition(is_square_free_2(pol1));
    CGAL_precondition(is_square_free_2(pol2));
    if(!is_coprime_2(pol1, pol2)) {
      CGAL_assertion(false); // @todo handle that case

      Polynomial_2 g, q1, q2;
      make_coprime_2(pol1, pol2, g, q1, q2);
      pol1 = q1;
      pol2 = q2;
    }

    std::vector<std::pair<Algebraic_real_2, Multiplicity_type> > roots;
    solve_2(pol1, pol2, std::back_inserter(roots));

    for(std::size_t i=0; i<roots.size(); i++)
    {
      a_roots.push_back(roots[i].first.to_double().first);
      b_roots.push_back(roots[i].first.to_double().second);
    }

    return a_roots.size();
  }
#endif // !defined(CGAL_SMP_SOLVE_CUBIC_EQUATION) && defined(CGAL_SMP_SOLVE_EQUATIONS_WITH_GMP)

  // Compute the root that gives the lowest face energy.
  template <typename VertexUVMap>
  std::size_t compute_root_with_lowest_energy(const Triangle_mesh& mesh,
                                              face_descriptor fd,
                                              const Cot_map ctmap,
                                              const Local_points& lp,
                                              const Lp_map lpmap,
                                              const VertexUVMap uvmap,
                                              const NT C2_denom, const NT C3,
                                              const std::vector<NT>& roots) const
  {
    CGAL_precondition(!roots.empty());
    NT E_min = (std::numeric_limits<NT>::max)();
    std::size_t index_arg = 0;
    for(std::size_t i=0; i<roots.size(); ++i)
    {
      const NT a = roots[i];
      const NT b = C3 * C2_denom * a;
      NT Ef = compute_current_face_energy(mesh, fd, ctmap, lp, lpmap, uvmap, a, b);
      if(Ef < E_min) {
        E_min = Ef;
        index_arg = i;
      }
    }
    return index_arg;
  }

  // Compute the root that gives the lowest face energy.
  template <typename VertexUVMap>
  std::size_t compute_root_with_lowest_energy(const Triangle_mesh& mesh,
                                              face_descriptor fd,
                                              const Cot_map ctmap,
                                              const Local_points& lp,
                                              const Lp_map lpmap,
                                              const VertexUVMap uvmap,
                                              const std::vector<NT>& a_roots,
                                              const std::vector<NT>& b_roots) const
  {
    CGAL_precondition(!a_roots.empty() && a_roots.size() == b_roots.size());
    NT E_min = (std::numeric_limits<NT>::max)();
    std::size_t index_arg = -1;
    for(std::size_t i=0; i<a_roots.size(); ++i)
    {
      NT Ef = compute_current_face_energy(mesh, fd, ctmap, lp, lpmap, uvmap,
                                          a_roots[i], b_roots[i]);
      if(Ef < E_min) {
        E_min = Ef;
        index_arg = i;
      }
    }
    return index_arg;
  }

  // Compute the optimal values of the linear transformation matrices Lt.
  template <typename VertexUVMap>
  Error_code compute_optimal_Lt_matrices(const Triangle_mesh& mesh,
                                         const Faces_vector& faces,
                                         const Cot_map ctmap,
                                         const Local_points& lp,
                                         const Lp_map lpmap,
                                         const VertexUVMap uvmap,
                                         Lt_map ltmap) const
  {
    Error_code status = OK;

    for(face_descriptor fd : faces) {
      // Compute the coefficients C1, C2, C3
      NT C1 = 0., C2 = 0., C3 = 0.;

      halfedge_around_face_circulator hc(halfedge(fd, mesh), mesh), end(hc);
      CGAL_For_all(hc, end) {
        halfedge_descriptor hd = *hc;
        NT c = get(ctmap, hd);

        // UV positions
        const Point_2& uvpi = get(uvmap, source(hd, mesh));
        const Point_2& uvpj = get(uvmap, target(hd, mesh));
        NT diff_x = uvpi.x() - uvpj.x();
        NT diff_y = uvpi.y() - uvpj.y();
//        CGAL_warning(diff_x == 0. && diff_y == 0.);

        // local positions (in the isometric 2D param)
        const Local_indices& li = get(lpmap, hd);
        const Point_2& ppi = lp[ li.first ];
        const Point_2& ppj = lp[ li.second ];
        NT p_diff_x = ppi.x() - ppj.x();
        NT p_diff_y = ppi.y() - ppj.y();
        CGAL_precondition(p_diff_x != 0. || p_diff_y != 0.);

        C1 += c * ( p_diff_x*p_diff_x + p_diff_y*p_diff_y );
        C2 += c * ( diff_x*p_diff_x + diff_y*p_diff_y );
        C3 += c * ( diff_x*p_diff_y - diff_y*p_diff_x );
      }

      // Compute a and b
      NT a = 0., b = 0.;

      if(m_lambda == 0.) { // ASAP
        CGAL_precondition(C1 != 0.);
        a = C2 / C1;
        b = C3 / C1;
      }
      else if( std::abs(C1) < m_lambda_tolerance * m_lambda &&
               std::abs(C2) < m_lambda_tolerance * m_lambda ) { // ARAP
        // If lambda is large compared to C1 and C2, the cubic equation that
        // determines a and b can be simplified to a simple quadric equation

        CGAL_precondition(C2*C2 + C3*C3 != 0.);
        NT denom = 1. / CGAL::sqrt(C2*C2 + C3*C3);
        a = C2 * denom;
        b = C3 * denom;
      }
      else { // general case
#ifdef CGAL_SMP_SOLVE_CUBIC_EQUATION
        CGAL_precondition(C2 != 0.);
        NT C2_denom = 1. / C2;
        NT a3_coeff = 2. * m_lambda * (C2 * C2 + C3 * C3) * C2_denom * C2_denom;

        std::vector<NT> roots;
#ifdef CGAL_SMP_SOLVE_EQUATIONS_WITH_GMP
        solve_cubic_equation_with_AK(a3_coeff, 0., (C1 - 2. * m_lambda), -C2, roots);
#else // !CGAL_SMP_SOLVE_EQUATIONS_WITH_GMP
        solve_cubic_equation(a3_coeff, 0., (C1 - 2. * m_lambda), -C2, roots);
#endif
        std::size_t ind = compute_root_with_lowest_energy(mesh, fd,
                                                          ctmap, lp, lpmap, uvmap,
                                                          C2_denom, C3, roots);

        a = roots[ind];
        b = C3 * C2_denom * a;
#else // !CGAL_SMP_SOLVE_CUBIC_EQUATION, solve the bivariate system
        std::vector<NT> a_roots;
        std::vector<NT> b_roots;
        solve_bivariate_system(C1, C2, C3, a_roots, b_roots);

        std::size_t ind = compute_root_with_lowest_energy(mesh, fd,
                                                          ctmap, lp, lpmap, uvmap,
                                                          a_roots, b_roots);
        a = a_roots[ind];
        b = b_roots[ind];
#endif
      }

      // Update the map faces --> optimal Lt matrices
      Lt_matrix ltm = std::make_pair(a, b);
      put(ltmap, fd, ltm);
    }

    return status;
  }

  // Computes the coordinates of the vertices p0, p1, p2
  // in a local 2D orthonormal basis of the triangle's plane.
  void project_triangle(const Point_3& p0, const Point_3& p1, const Point_3& p2,  // in
                        Point_2& z0, Point_2& z1, Point_2& z2) const              // out
  {
    Vector_3 X = p1 - p0;
    NT X_norm = CGAL::sqrt(X * X);
    if(X_norm != 0.0)
      X = X / X_norm;

    Vector_3 Z = CGAL::cross_product(X, p2 - p0);
    NT Z_norm = CGAL::sqrt(Z * Z);
    if(Z_norm != 0.0)
      Z = Z / Z_norm;

    Vector_3 Y = CGAL::cross_product(Z, X);

    NT x0 = 0;
    NT y0 = 0;
    NT x1 = CGAL::sqrt( (p1 - p0)*(p1 - p0) );
    NT y1 = 0;
    NT x2 = (p2 - p0) * X;
    NT y2 = (p2 - p0) * Y;

    z0 = Point_2(x0, y0);
    z1 = Point_2(x1, y1);
    z2 = Point_2(x2, y2);
  }

  // Compute the local parameterization (2D) of a face and store it in memory.
  void project_face(const Triangle_mesh& mesh,
                    vertex_descriptor vi,
                    vertex_descriptor vj,
                    vertex_descriptor vk,
                    Local_points& lp) const
  {
    typedef typename boost::property_map<Triangle_mesh,
                                         boost::vertex_point_t>::const_type PPmap;
    const PPmap ppmap = get(vertex_point, mesh);

    const Point_3& position_vi = get(ppmap, vi);
    const Point_3& position_vj = get(ppmap, vj);
    const Point_3& position_vk = get(ppmap, vk);

    Point_2 pvi, pvj, pvk;
    project_triangle(position_vi, position_vj, position_vk,
                     pvi, pvj, pvk);

    // Add the newly computed 2D points to the vector of local positions
    lp.push_back(pvi);
    lp.push_back(pvj);
    lp.push_back(pvk);
  }

  // Utility for fill_linear_system_rhs():
  // Compute the local isometric parameterization (2D) of the faces of the mesh.
  Error_code compute_local_parameterization(const Triangle_mesh& mesh,
                                            const Faces_vector& faces,
                                            Local_points& lp,
                                            Lp_map lpmap) const
  {
    int global_index = 0;

    for(face_descriptor fd : faces) {
      halfedge_descriptor hd = halfedge(fd, mesh), hdb = hd;

      vertex_descriptor vi = target(hd, mesh); // hd is k -- > i
      put(lpmap, hd, std::make_pair(global_index + 2, global_index));

      hd = next(hd, mesh);
      vertex_descriptor vj = target(hd, mesh); // hd is i -- > j
      put(lpmap, hd, std::make_pair(global_index, global_index + 1));

      hd = next(hd, mesh);
      vertex_descriptor vk = target(hd, mesh); // hd is j -- > k
      put(lpmap, hd, std::make_pair(global_index + 1, global_index + 2));

      hd = next(hd, mesh);
      if(hd != hdb) { // to make sure that it is a triangular face
        return ERROR_NON_TRIANGULAR_MESH;
      }

      project_face(mesh, vi, vj, vk, lp);
      global_index += 3;
    }

    if(lp.size() != 3 * faces.size())
      return ERROR_NON_TRIANGULAR_MESH;

    return OK;
  }

  // Compute the coefficient b_ij = (i,j) of the right hand side vector B,
  // for j neighbor vertex of i.
  void compute_b_ij(const Triangle_mesh& mesh,
                    halfedge_descriptor hd,
                    const Cot_map ctmap,
                    const Local_points& lp,
                    const Lp_map lpmap,
                    const Lt_map ltmap,
                    NT& x, NT& y) const // x for Bu and y for Bv
  {
    CGAL_precondition(x == 0.0 && y == 0.0);

    // Note that :
    // -- Circulators move in a clockwise manner
    // -- The halfedge hd points to vi

    // -- Face IJK with vk before vj while circulating around vi
    halfedge_descriptor hd_opp = opposite(hd, mesh); // hd_opp points to vj
    face_descriptor fd_k = face(hd_opp, mesh);

    // Get the matrix L_t corresponding to the face ijk
    const Lt_matrix& ltm_k = get(ltmap, fd_k);
    NT a_k = ltm_k.first;
    NT b_k = ltm_k.second;

    // Get the local parameterization in the triangle ijk
    const Local_indices& loc_indices_k = get(lpmap, hd_opp);
    const Point_2& pvi_k = lp[ loc_indices_k.first ];
    const Point_2& pvj_k = lp[ loc_indices_k.second ];

    // x_i - x_j in the local parameterization of the triangle ijk
    NT diff_k_x = pvi_k.x() - pvj_k.x();
    NT diff_k_y = pvi_k.y() - pvj_k.y();

    // get the cotangent angle at vk
    NT ct_k = get(ctmap, hd_opp);

    // cot_k * Lt_k * (xi - xj)_k
    x = ct_k * (  a_k * diff_k_x + b_k * diff_k_y );
    y = ct_k * ( -b_k * diff_k_x + a_k * diff_k_y );

    // -- Face IJL with vl after vj while circulating around vi
    face_descriptor fd_l = face(hd, mesh);

    // Get the matrix L_t corresponding to the face ijl
    const Lt_matrix& ltm_l = get(ltmap, fd_l);
    NT a_l = ltm_l.first;
    NT b_l = ltm_l.second;

    // Get the local parameterization in the triangle ijl
    const Local_indices& loc_indices_l = get(lpmap, hd);
    const Point_2& pvi_l = lp[ loc_indices_l.second ]; // because hd points to vi
    const Point_2& pvj_l = lp[ loc_indices_l.first ];

    // x_i - x_j in the local parameterization of the triangle ijl
    NT diff_l_x = pvi_l.x() - pvj_l.x();
    NT diff_l_y = pvi_l.y() - pvj_l.y();

    // get the cotangent angle at vl
    NT ct_l = get(ctmap, hd);

    // cot_l * Lt_l * (xi - xj)_l
    x += ct_l * (  a_l * diff_l_x + b_l * diff_l_y );
    y += ct_l * ( -b_l * diff_l_x + a_l * diff_l_y );
  }

  // Compute the line i of right hand side vectors Bu and Bv
  // - call compute_b_ij() for each neighbor v_j to compute the B coefficient b_i
  //
  // \pre Vertices must be indexed.
  // \pre Vertex i mustn't be already parameterized.
  // \pre Lines i of Bu and Bv must be zero.
  template <typename VertexIndexMap>
  Error_code fill_linear_system_rhs(const Triangle_mesh& mesh,
                                    vertex_descriptor vertex,
                                    const Cot_map ctmap,
                                    const Local_points& lp,
                                    const Lp_map lpmap,
                                    const Lt_map ltmap,
                                    VertexIndexMap vimap,
                                    Vector& Bu, Vector& Bv) const
  {
    int i = get(vimap, vertex);

    // Circulate over vertices around 'vertex' to compute w_ii and w_ijs
    NT bu_i = 0, bv_i = 0;
    int vertexIndex = 0;

    halfedge_around_target_circulator hc(vertex, mesh), end = hc;
    CGAL_For_all(hc, end) {
      halfedge_descriptor hd = *hc;
      CGAL_assertion(target(hd, mesh) == vertex);

      NT x = 0., y = 0.;
      compute_b_ij(mesh, hd, ctmap, lp, lpmap, ltmap, x, y);

      bu_i += x;
      bv_i += y;
      vertexIndex++;
    }

    // Set the entries for the right hand side vectors
    Bu.set(i, bu_i);
    Bv.set(i, bv_i);

    if (vertexIndex < 2)
      return ERROR_NON_TRIANGULAR_MESH;

    return OK;
  }

  // Compute the entries of the right hand side of the ARAP linear system.
  //
  // \pre Vertices must be indexed.
  template <typename VertexUVMap,
            typename VertexIndexMap,
            typename VertexParameterizedMap>
  Error_code compute_rhs(const Triangle_mesh& mesh,
                         const Vertex_set& vertices,
                         const Cot_map ctmap,
                         const Local_points& lp,
                         const Lp_map lpmap,
                         const Lt_map ltmap,
                         VertexUVMap uvmap,
                         VertexIndexMap vimap,
                         VertexParameterizedMap vpmap,
                         Vector& Bu, Vector& Bv) const
  {
    // Initialize the right hand side B in the linear system "A*X = B"
    Error_code status = OK;

    for(vertex_descriptor vd : vertices) {
      if(!get(vpmap, vd)) { // not yet parameterized
        // Compute the lines i of the vectors Bu and Bv
        status = fill_linear_system_rhs(mesh, vd, ctmap, lp, lpmap,
                                                  ltmap, vimap, Bu, Bv);
        if(status != OK)
          return status;
      } else { // fixed vertices
        int index = get(vimap, vd);
        const Point_2& uv = get(uvmap, vd);
        Bu.set(index, uv.x());
        Bv.set(index, uv.y());
      }
    }
    return status;
  }

  // Compute the right hand side and solve the linear system to obtain the
  // new UV coordinates.
  template <typename VertexUVMap,
            typename VertexIndexMap,
            typename VertexParameterizedMap>
  Error_code update_solution(const Triangle_mesh& mesh,
                             const Vertex_set& vertices,
                             const Cot_map ctmap,
                             const Local_points& lp,
                             const Lp_map lpmap,
                             const Lt_map ltmap,
                             VertexUVMap uvmap,
                             VertexIndexMap vimap,
                             VertexParameterizedMap vpmap,
                             const Matrix& A)
  {
    Error_code status = OK;

    // Create two sparse linear systems "A*Xu = Bu" and "A*Xv = Bv"
    int nbVertices = static_cast<int>(vertices.size());
    Vector Xu(nbVertices), Xv(nbVertices), Bu(nbVertices), Bv(nbVertices);

    // Fill the right hand side vectors
    status = compute_rhs(mesh, vertices, ctmap, lp, lpmap, ltmap,
                                         uvmap, vimap, vpmap, Bu, Bv);
    if (status != OK)
      return status;

    // Solve "A*Xu = Bu". On success, the solution is (1/Du) * Xu.
    // Solve "A*Xv = Bv". On success, the solution is (1/Dv) * Xv.
    double Du, Dv;
    if(!get_linear_algebra_traits().linear_solver(A, Bu, Xu, Du) ||
       !get_linear_algebra_traits().linear_solver(A, Bv, Xv, Dv)) {
      std::cerr << "Could not solve linear system" << std::endl;
      status = ERROR_CANNOT_SOLVE_LINEAR_SYSTEM;
      return status;
    }

    // WARNING: this package does not support homogeneous coordinates!
    CGAL_assertion(Du == 1.0);
    CGAL_assertion(Dv == 1.0);

    CGAL_postcondition_code
    (
      // make sure that the constrained vertices have not been moved
      for(vertex_descriptor vd : vertices) {
        if(get(vpmap, vd)) {
          int index = get(vimap, vd);
          CGAL_warning(std::abs(Xu[index] - Bu[index] ) < 1e-7);
          CGAL_warning(std::abs(Xv[index] - Bv[index] ) < 1e-7);
        }
      }
    )

    assign_solution(Xu, Xv, vertices, uvmap, vimap, vpmap);
    return status;
  }


  // Compute the current energy of a face, given a linear transformation matrix.
  template <typename VertexUVMap>
  NT compute_current_face_energy(const Triangle_mesh& mesh,
                                 face_descriptor fd,
                                 const Cot_map ctmap,
                                 const Local_points& lp,
                                 const Lp_map lpmap,
                                 const VertexUVMap uvmap,
                                 const NT a, const NT b) const
  {
    NT Ef = 0.;

    halfedge_around_face_circulator hc(halfedge(fd, mesh), mesh), end(hc);
    CGAL_For_all(hc, end) {
      halfedge_descriptor hd = *hc;
      NT cot = get(ctmap, hd);
      NT nabla_x = 0., nabla_y = 0.;

      // UV positions
      const Point_2& pi = get(uvmap, source(hd, mesh));
      const Point_2& pj = get(uvmap, target(hd, mesh));
      NT diff_x = pi.x() - pj.x();
      NT diff_y = pi.y() - pj.y();

      // local positions (in the 2D param)
      const Local_indices& li = get(lpmap, hd);
      const Point_2& ppi = lp[ li.first ];
      const Point_2& ppj = lp[ li.second ];
      NT p_diff_x = ppi.x() - ppj.x();
      NT p_diff_y = ppi.y() - ppj.y();

      nabla_x = diff_x - (  a * p_diff_x + b * p_diff_y );
      nabla_y = diff_y - ( -b * p_diff_x + a * p_diff_y );

      NT sq_nabla_norm = nabla_x * nabla_x + nabla_y * nabla_y;
      Ef += cot * sq_nabla_norm;
    }

    NT s = a * a + b * b - 1;
    Ef += m_lambda * s * s;
    return Ef;
  }

  // Compute the current energy of a face.
  template <typename VertexUVMap>
  NT compute_current_face_energy(const Triangle_mesh& mesh,
                                 face_descriptor fd,
                                 const Cot_map ctmap,
                                 const Local_points& lp,
                                 const Lp_map lpmap,
                                 const Lt_map ltmap,
                                 const VertexUVMap uvmap) const
  {
    const Lt_matrix& ltm = get(ltmap, fd); // the (current) optimal linear transformation
    NT a = ltm.first;
    NT b = ltm.second;

    return compute_current_face_energy(mesh, fd, ctmap, lp, lpmap, uvmap, a, b);
  }

  // Compute the current energy of the parameterization.
  template <typename VertexUVMap>
  NT compute_current_energy(const Triangle_mesh& mesh,
                            const Faces_vector& faces,
                            const Cot_map ctmap,
                            const Local_points& lp,
                            const Lp_map lpmap,
                            const Lt_map ltmap,
                            const VertexUVMap uvmap) const
  {
    NT E = 0.;

    for(face_descriptor fd : faces) {
      NT Ef = compute_current_face_energy(mesh, fd, ctmap, lp, lpmap,
                                          ltmap, uvmap);
      E += Ef;
    }

    E *= 0.5;
    return E;
  }

// Post processing functions
  // Use the convex virtual boundary algorithm of Karni et al.[2005] to fix
  // the (hopefully few) flips in the result.
  template <typename VertexUVMap,
            typename VertexIndexMap>
  Error_code post_process(const Triangle_mesh& mesh,
                          const Vertex_set& vertices,
                          const Faces_vector& faces,
                          halfedge_descriptor bhd,
                          VertexUVMap uvmap,
                          const VertexIndexMap vimap) const
  {
    typedef MVC_post_processor_3<Triangle_mesh>     Post_processor;

    Post_processor p;
    Error_code status = p.parameterize(mesh, vertices, faces, bhd, uvmap, vimap);
    if(status != OK)
      return status;

#ifdef CGAL_SMP_ARAP_DEBUG
    output_uvmap("ARAP_final_post_processing.off", mesh, vertices, faces, uvmap, vimap);
#endif

    return OK;
  }

// Public operations
public:
  /// returns whether the 3D -> 2D mapping is one-to-one.
  template <typename FaceRange, typename VertexUVMap>
  bool is_one_to_one_mapping(const Triangle_mesh& mesh,
                             const FaceRange& faces,
                             const VertexUVMap uvmap) const
  {
    return internal::is_one_to_one_mapping(mesh, faces, uvmap);
  }

  /// computes a mapping from a triangular 3D surface mesh to a piece of the 2D space.
  /// The mapping is piecewise linear (linear in each triangle).
  /// The result is the (u,v) pair image of each vertex of the 3D surface.
  ///
  /// \tparam VertexUVmap must be a model of `ReadWritePropertyMap` with
  ///         `boost::graph_traits<Triangle_mesh>::%vertex_descriptor` as key type and
  ///         %Point_2 (type deduced from `Triangle_mesh` using `Kernel_traits`)
  ///         as value type.
  /// \tparam VertexIndexMap must be a model of `ReadablePropertyMap` with
  ///         `boost::graph_traits<Triangle_mesh>::%vertex_descriptor` as key type and
  ///         a unique integer as value type.
  /// \tparam VertexParameterizedMap must be a model of `ReadWritePropertyMap` with
  ///         `boost::graph_traits<Triangle_mesh>::%vertex_descriptor` as key type and
  ///         a Boolean as value type.
  ///
  /// \param mesh a triangulated surface.
  /// \param bhd a halfedge descriptor on the boundary of `mesh`.
  /// \param uvmap an instantiation of the class `VertexUVmap`.
  /// \param vimap an instantiation of the class `VertexIndexMap`.
  /// \param vpmap an instantiation of the class `VertexParameterizedMap`.
  ///
  /// \pre `mesh` must be a triangular mesh.
  /// \pre The vertices must be indexed (vimap must be initialized).
  ///
  template <typename VertexUVMap,
            typename VertexIndexMap,
            typename VertexParameterizedMap>
  Error_code parameterize(Triangle_mesh& mesh,
                          halfedge_descriptor bhd,
                          VertexUVMap uvmap,
                          VertexIndexMap vimap,
                          VertexParameterizedMap vpmap)
  {
    CGAL_precondition(is_valid_polygon_mesh(mesh));
    CGAL_precondition(is_triangle_mesh(mesh));
    CGAL_precondition(bhd != boost::graph_traits<Triangle_mesh>::null_halfedge() && is_border(bhd, mesh));

    Error_code status = OK;

    // vertices and faces containers
    Faces_vector faces;
    Vertex_set vertices;
    status = initialize_containers(mesh, bhd, vertices, faces);
    if(status != OK)
      return status;

    // linear transformation matrices L_t
    // Only need to store 2 indices since the complete matrix is {{a,b},{-b,a}}
    Lt_hash_map lt_hm;
    Lt_map ltmap(lt_hm); // will be filled in 'compute_optimal_Lt_matrices()'

    // Compute the initial parameterization of the mesh
    status = compute_initial_uv_map(mesh, bhd, uvmap, vimap);

#ifdef CGAL_SMP_ARAP_DEBUG
    output_uvmap("ARAP_initial_param.off", mesh, vertices, faces, uvmap, vimap);
#endif

    if(status != OK)
      return status;

    // Handle the boundary condition depending on lambda
    status = parameterize_border<VertexUVMap>(mesh, vertices, bhd, vimap, vpmap);
    if(status != OK)
      return status;

    // Compute all cotangent angles
    Cot_hm cthm;
    Cot_map ctmap(cthm);
    status = compute_cotangent_angles(mesh, faces, ctmap);
    if(status != OK)
      return status;

    // Compute all local 2D parameterizations
    Lp_hm lphm;
    Lp_map lpmap(lphm);
    Local_points lp;
    status = compute_local_parameterization(mesh, faces, lp, lpmap);
    if(status != OK)
      return status;

    // The matrix A is constant and can be initialized outside of the loop
    int nbVertices = static_cast<int>(vertices.size());
    Matrix A(nbVertices, nbVertices); // the constant matrix using in the linear system A*X = B
    status = initialize_matrix_A(mesh, vertices, ctmap, vimap, vpmap, A);
    if(status != OK)
      return status;

    NT energy_this = compute_current_energy(mesh, faces, ctmap, lp, lpmap,
                                            ltmap, uvmap);
    NT energy_last;

#ifdef CGAL_PARAMETERIZATION_ARAP_VERBOSE
    std::cout << "Initial energy: " << energy_this << std::endl;
    std::cout << m_iterations << " max iterations" << std::endl;
#endif

    // main loop
    unsigned int ite = 1;
    for(;;)
    {
      compute_optimal_Lt_matrices(mesh, faces, ctmap, lp, lpmap, uvmap, ltmap);
      status = update_solution(mesh, vertices, ctmap, lp, lpmap, ltmap,
                                               uvmap, vimap, vpmap, A);

      // Output the current parameterization
#ifdef CGAL_SMP_ARAP_DEBUG
      output_uvmap("ARAP_iteration_", ite, mesh, vertices, faces, uvmap, vimap);
#endif

      if(status != OK)
        return status;

      // energy based termination
      if(m_tolerance > 0. && ite <= m_iterations) { // if tolerance <= 0, don't compute energy
        energy_last = energy_this;
        energy_this = compute_current_energy(mesh, faces, ctmap, lp, lpmap, ltmap, uvmap);

#ifdef CGAL_PARAMETERIZATION_ARAP_VERBOSE
        std::cout << "Energy at iteration " << ite << " : " << energy_this << std::endl;
#endif

        if(energy_this < 0) {
          // numerical issues can make it so you may get an energy of -1e-17,
          // but it shouldn't be too wrong
          CGAL_assertion(energy_this >= - std::numeric_limits<NT>::epsilon());
          break;
        }

        double energy_diff = std::abs((energy_last - energy_this) / energy_this);
        if(energy_diff < m_tolerance) {
          break;
        }
      }

      if(ite >= m_iterations)
        break;
      else
        ++ite;
    }

#ifdef CGAL_PARAMETERIZATION_ARAP_VERBOSE
    std::cout << "Minimization process ended after: " << ite << " iterations. " << std::endl;
#endif

#ifdef CGAL_SMP_ARAP_DEBUG
    output_uvmap("ARAP_final_pre_processing.off", mesh, vertices, faces, uvmap, vimap);
#endif

    if(!is_one_to_one_mapping(mesh, faces, uvmap)) {
     // Use post processing to handle flipped elements
#ifdef CGAL_PARAMETERIZATION_ARAP_VERBOSE
      std::cout << "Parameterization is not valid; calling post processor" << std::endl;
#endif
      status = post_process(mesh, vertices, faces, bhd, uvmap, vimap);
    }

    return status;
  }

public:
  /// Constructor taking only the parameter \f$\lambda\f$.
  ARAP_parameterizer_3(NT lambda)
    :
      m_borderParameterizer(Border_parameterizer()),
      m_linearAlgebra(Solver_traits()),
      m_lambda(lambda),
      m_lambda_tolerance(1e-10),
      m_iterations(50),
      m_tolerance(1e-6)
  { }

  /// %Default Constructor.
  ///
  /// \param border_param %Object that maps the surface's border to the 2D space.
  /// \param sparse_la %Traits object to access a sparse linear system.
  /// \param lambda Parameter to give importance to shape or angle preservation.
  /// \param iterations Maximal number of iterations in the energy minimization process.
  /// \param tolerance Minimal energy difference between two iterations for the minimization
  ///        process to continue.
  ///
  ARAP_parameterizer_3(Border_parameterizer border_param = Border_parameterizer(),
                       Solver_traits sparse_la = Solver_traits(),
                       NT lambda = 1000.,
                       unsigned int iterations = 50,
                       NT tolerance = 1e-6)
    :
      m_borderParameterizer(border_param),
      m_linearAlgebra(sparse_la),
      m_lambda(lambda),
      m_lambda_tolerance(1e-10),
      m_iterations(iterations),
      m_tolerance(tolerance)
  { }

  // Default copy constructor and operator=() are fine
};

} // namespace Surface_mesh_parameterization

} // namespace CGAL

#include <CGAL/enable_warnings.h>

#endif // CGAL_SURFACE_MESH_PARAMETERIZATION_ARAP_PARAMETERIZER_3_H