Face_handle test_point_location(const Triangulation &t, const Point &query, const Triangulation::Locate_type <_in) { Triangulation::Locate_type lt, lt2; int li, li2; Face_handle fh; CGAL::Bounded_side bs; CGAL::Oriented_side os; fh = t.locate(query, lt, li); CGAL_assertion(lt == lt_in); if (lt_in == Triangulation::EMPTY) { CGAL_assertion(fh == Face_handle()); return fh; } bs = t.side_of_face(query, fh, lt2, li2); os = t.oriented_side(fh, query); CGAL_USE(bs); CGAL_USE(os); CGAL_assertion(lt2 == lt_in); switch (lt_in) { case Triangulation::VERTEX: case Triangulation::EDGE: { CGAL_assertion(fh != Face_handle()); CGAL_assertion(bs == CGAL::ON_BOUNDARY); CGAL_assertion(os == CGAL::ON_ORIENTED_BOUNDARY); CGAL_assertion(li == li2); break; } case Triangulation::FACE: { CGAL_assertion(fh != Face_handle()); CGAL_assertion(bs == CGAL::ON_BOUNDED_SIDE); CGAL_assertion(os == CGAL::ON_POSITIVE_SIDE); break; } case Triangulation::EMPTY: { // Handled above CGAL_assertion(false); break; } case Triangulation::OUTSIDE_CONVEX_HULL: CGAL_error(); case Triangulation::OUTSIDE_AFFINE_HULL: CGAL_error(); } return fh; }
void test_isolated_vertex() { Graph G; typedef boost::graph_traits< Graph > Traits; typedef typename Traits::vertex_descriptor vertex_descriptor; typedef typename Traits::halfedge_descriptor halfedge_descriptor; vertex_descriptor v = add_vertex(G); // the connectivity of v may be anything set_halfedge(v, Traits::null_halfedge(), G); halfedge_descriptor h = halfedge(v,G); CGAL_USE(h); }
int main() { Triangulation tr; int a, b, d; for (a=0;a!=4;a++) for (b=0;b!=4;b++) for (d=0;d!=4;d++) tr.insert(Point((a*b-d*a)*10 +a ,(a-b+d +5*b)*100, a*a-d*d-b)); Triangulation::Finite_cells_iterator cit=tr.finite_cells_begin(); for(; cit != tr.finite_cells_end(); ++cit) { Point circum = tr.dual(cit); CGAL_USE(circum); } return 0; }
void test_comparison_operators() { typedef CGAL::MP_Float RT; typedef CGAL::Quotient<RT> QT; RT r(1); QT q(1); q+q; q+r; r+q; q+1; 1+q; q-q; q-r; r-q; q-1; 1-q; q*q; q*r; r*q; q*1; 1*q; q/q; q/r; r/q; q/1; 1/q; -q; bool b; // avoid clang warning: equality comparison result unused [-Wunused-comparison] b = q<q; b = q<r; b = r<q; b = q<1; b = 1<q; b = q>q; b = q>r; b = r>q; b = q>1; b = 1>q; b = q<=q; b = q<=r; b = r<=q; b = q<=1; b = 1<=q; b = q>=q; b = q>=r; b = r>=q; b = q>=1; b = 1>=q; b = q==q; b = q==r; b = r==q; b = q==1; b = 1==q; b = q!=q; b = q!=r; b = r!=q; b = q!=1; b = 1!=q; CGAL_USE(b); }
int main() { Triangulation t; Face_handle fh; // Check the empty triangulation fh = test_point_location(t, Point(0.5, 0.5), Triangulation::EMPTY); CGAL_assertion(fh == Face_handle()); // Insert the first point Point p0(0.5, 0.5); Vertex_handle vh0 = t.insert(p0); CGAL_assertion(t.is_valid(true)); CGAL_USE(vh0); fh = test_point_location(t, p0, Triangulation::VERTEX); CGAL_assertion(fh->has_vertex(vh0)); fh = test_point_location(t, p0 + Vector(0.1, 0.1), Triangulation::EDGE); CGAL_assertion(fh->has_vertex(vh0)); fh = test_point_location(t, p0 + Vector(-0.1, -0.1), Triangulation::EDGE); CGAL_assertion(fh->has_vertex(vh0)); fh = test_point_location(t, p0 + Vector(-0.2, -0.3), Triangulation::FACE); CGAL_assertion(fh->has_vertex(vh0)); CGAL_assertion(t.is_valid(true)); // Insert the second point on an edge Point p1(0.7, 0.7); Vertex_handle vh1 = t.insert(p1); CGAL_USE(vh1); CGAL_assertion(t.is_valid(true)); fh = test_point_location(t, p0, Triangulation::VERTEX); CGAL_assertion(fh->has_vertex(vh0)); fh = test_point_location(t, p1, Triangulation::VERTEX); CGAL_assertion(fh->has_vertex(vh1)); fh = test_point_location(t, p0 + Vector(0.1, 0.1), Triangulation::EDGE); CGAL_assertion(fh->has_vertex(vh0)); CGAL_assertion(fh->has_vertex(vh1)); fh = test_point_location(t, p0 + Vector(-0.1, -0.1), Triangulation::EDGE); CGAL_assertion(fh->has_vertex(vh0)); CGAL_assertion(!fh->has_vertex(vh1)); fh = test_point_location(t, p1 + Vector(0.1, 0.1), Triangulation::EDGE); CGAL_assertion(!fh->has_vertex(vh0)); CGAL_assertion(fh->has_vertex(vh1)); fh = test_point_location(t, p0 + Vector(-0.02, -0.03), Triangulation::FACE); CGAL_assertion(fh->has_vertex(vh0)); fh = test_point_location(t, p1 + Vector(-0.02, -0.03), Triangulation::FACE); CGAL_assertion(fh->has_vertex(vh1)); CGAL_assertion(t.is_valid(true)); // Insert the third point in a face Point p2(0.8, 0.6); Vertex_handle vh2 = t.insert(p2); CGAL_USE(vh2); CGAL_assertion(t.is_valid(true)); fh = test_point_location(t, p0, Triangulation::VERTEX); CGAL_assertion(fh->has_vertex(vh0)); fh = test_point_location(t, p1, Triangulation::VERTEX); CGAL_assertion(fh->has_vertex(vh1)); fh = test_point_location(t, p2, Triangulation::VERTEX); CGAL_assertion(fh->has_vertex(vh2)); fh = test_point_location(t, Point(0.6, 0.6), Triangulation::EDGE); CGAL_assertion(fh->has_vertex(vh0)); CGAL_assertion(fh->has_vertex(vh1)); test_point_location(t, Point(0.7, 0.6), Triangulation::FACE); test_point_location(t, p0 + Vector(-0.02, -0.03), Triangulation::FACE); test_point_location(t, p0 + Vector(0.02, -0.03), Triangulation::FACE); test_point_location(t, p0 + Vector(-0.02, 0.03), Triangulation::FACE); test_point_location(t, p0 + Vector(0.02, 0.03), Triangulation::FACE); return 0; }
int main(int,char*[]) { Triangulation t; t.insert(Point(0.1,0,1)); t.insert(Point(1,0,1)); t.insert(Point(0.2,0.2, 2)); t.insert(Point(0,1,2)); t.insert(Point(0,2,3)); vertex_iterator vit, ve; // Associate indices to the vertices int index = 0; // boost::tie assigns the first and second element of the std::pair // returned by boost::vertices to the variables vit and ve for(boost::tie(vit,ve) = vertices(t); vit!=ve; ++vit ){ vertex_descriptor vd = *vit; if(! t.is_infinite(vd)){ vertex_id_map[vd]= index++; } } std::cerr << index << " vertices" << std::endl; index = 0; face_iterator fit,fe; for(boost::tie(fit,fe) = faces(t); fit!= fe; ++fit){ face_descriptor fd = *fit; halfedge_descriptor hd = halfedge(fd,t); halfedge_descriptor n = next(hd,t); halfedge_descriptor nn = next(n,t); if(next(nn,t) != hd){ std::cerr << "the face is not a triangle" << std::endl; } ++index; } std::cerr << index << " faces" << std::endl; index = 0; edge_iterator eit,ee; for(boost::tie(eit,ee) = edges(t); eit!= ee; ++eit){ edge_descriptor ed = *eit; vertex_descriptor vd = source(ed,t); CGAL_USE(vd); ++index; } std::cerr << index << " edges" << std::endl; index = 0; halfedge_iterator hit,he; for(boost::tie(hit,he) = halfedges(t); hit!= he; ++hit){ halfedge_descriptor hd = *hit; vertex_descriptor vd = source(hd,t); CGAL_USE(vd); ++index; } std::cerr << index << " halfedges" << std::endl; std::cerr << num_vertices(t) << " " << num_edges(t) << " " << num_halfedges(t) << " " << num_faces(t) << std::endl; typedef boost::property_map<Triangulation, boost::vertex_point_t>::type Ppmap; Ppmap ppmap = get(boost::vertex_point, t); for(vertex_descriptor vd : vertices_around_target(*vertices(t).first, t)){ std::cout << ppmap[vd] << std::endl; } ppmap[*(++vertices(t).first)] = Point(78,1,2); std::cout << " changed point of vertex " << ppmap[*(++vertices(t).first)] << std::endl; return 0; }
void test_edge_collapse() { std::cerr << "test_edge_collapse" << std::endl; PointMassList points; //use the stair example for testing load_xy_file<PointMassList, Point>("data/stair-noise00.xy", points); Point_property_map point_pmap; Mass_property_map mass_pmap; CGAL::Optimal_transportation_reconstruction_2<K, Point_property_map, Mass_property_map> otr2(points, point_pmap, mass_pmap); const Rt_2& rt2 = otr2.tds(); FT min_priority = 1000; R_edge_2 contract_edge; for (Finite_edges_iterator ei = rt2.finite_edges_begin(); ei != rt2.finite_edges_end(); ++ei) { if (rt2.is_pinned(*ei) || rt2.is_target_cyclic(*ei)) continue; R_edge_2 cur_r_edge; if(!otr2.create_pedge(*ei, cur_r_edge)) continue; print_edge(cur_r_edge); if (cur_r_edge.priority() < min_priority && cur_r_edge.priority() > 0) { min_priority = cur_r_edge.priority(); contract_edge = cur_r_edge; } } R_edge_2 pedge; otr2.pick_edge(0, pedge); std::cout << "--------" << std::endl; print_edge(contract_edge); print_edge(pedge); std::cout << "--------" << std::endl; //test that the right edge was picked for collapsing // N.B.: it can be two different edges if several edges have the same // priority value. assert(CGAL::abs(pedge.priority() - contract_edge.priority()) < pedge.priority()*1e-13); otr2.do_collapse(contract_edge.edge()); bool found = false; for (Finite_edges_iterator ei = rt2.finite_edges_begin(); ei != rt2.finite_edges_end(); ++ei) { if (*ei == contract_edge.edge()) { found = true; break; } } //test that the edge was collapsed assert(!found); CGAL_USE(found); }
void test(const Cont &) { // Testing if all types are provided. typename Cont::value_type t0; typename Cont::reference t1 = t0; CGAL_USE(t1); typename Cont::const_reference t2 = t0; CGAL_USE(t2); typename Cont::pointer t3 = &t0; typename Cont::const_pointer t4 = &t0; CGAL_USE(t4); typename Cont::size_type t5 = 0; CGAL_USE(t5); typename Cont::difference_type t6 = t3-t3; CGAL_USE(t6); typename Cont::iterator t7; CGAL_USE(t7); typename Cont::const_iterator t8; CGAL_USE(t8); typename Cont::reverse_iterator t9; CGAL_USE(t9); typename Cont::const_reverse_iterator t10; CGAL_USE(t10); typename Cont::allocator_type t15; std::cout << "Testing empty containers." << std::endl; Cont c0, c1; Cont c2(t15); Cont c3(c2); Cont c4; c4 = c2; typedef std::vector<typename Cont::value_type> Vect; Vect v0; const Cont c5(v0.begin(), v0.end()); Cont c6(c5.begin(), c5.end()); typename Cont::allocator_type Al; Cont c7(c0.begin(), c0.end(), Al); Cont c8; c8.insert(c0.rbegin(), c0.rend()); // test conversion iterator-> const_iterator. typename Cont::const_iterator t16 = c5.begin(); CGAL_USE(t16); assert(t16 == c5.begin()); assert(c0 == c1); assert(! (c0 < c1)); assert(check_empty(c0)); assert(check_empty(c1)); assert(check_empty(c2)); assert(check_empty(c3)); assert(check_empty(c4)); assert(check_empty(c5)); assert(check_empty(c6)); assert(check_empty(c7)); assert(check_empty(c8)); c1.swap(c0); assert(check_empty(c0)); assert(check_empty(c1)); c1.merge(c0); assert(check_empty(c0)); assert(check_empty(c1)); typename Cont::allocator_type t20 = c0.get_allocator(); std::cout << "Now filling some containers" << std::endl; Vect v1(10000); Cont c9(v1.begin(), v1.end()); assert(c9.size() == v1.size()); assert(c9.max_size() >= v1.size()); assert(c9.capacity() >= c9.size()); Cont c10 = c9; assert(c10 == c9); assert(c10.size() == v1.size()); assert(c10.max_size() >= v1.size()); assert(c10.capacity() >= c10.size()); c9.clear(); assert(check_empty(c9)); assert(c9.capacity() >= c9.size()); assert(c0 == c9); c9.merge(c10); c10.swap(c9); assert(check_empty(c9)); assert(c9.capacity() >= c9.size()); assert(c10.size() == v1.size()); assert(c10.max_size() >= v1.size()); assert(c10.capacity() >= c10.size()); std::cout << "Testing insertion methods" << std::endl; c9.assign(c10.begin(), c10.end()); assert(c9 == c10); c10.assign(c9.begin(), c9.end()); assert(c9 == c10); c9.insert(c10.begin(), c10.end()); assert(c9.size() == 2*v1.size()); c9.clear(); assert(c9 != c10); c9.insert(c10.begin(), c10.end()); assert(c9.size() == v1.size()); assert(c9 == c10); typename Cont::iterator it = c9.iterator_to(*c9.begin()); assert(it == c9.begin()); typename Cont::const_iterator cit = c9.iterator_to(const_cast<typename Cont::const_reference>(*c9.begin())); assert(cit == c9.begin()); typename Cont::iterator s_it = Cont::s_iterator_to(*c9.begin()); assert(s_it == c9.begin()); typename Cont::const_iterator s_cit = Cont::s_iterator_to(const_cast<typename Cont::const_reference>(*c9.begin())); assert(s_cit == c9.begin()); c10 = Cont(); assert(check_empty(c10)); for(typename Vect::const_iterator it = v1.begin(); it != v1.end(); ++it) c10.insert(*it); assert(c10.size() == v1.size()); assert(c9 == c10); c9.erase(c9.begin()); c9.erase(c9.begin()); assert(c9.size() == v1.size() - 2); // test reserve /*Cont c11; c11.reserve(v1.size()); for(typename Vect::const_iterator it = v1.begin(); it != v1.end(); ++it) c11.insert(*it); assert(c11.size() == v1.size()); assert(c10 == c11);*/ // owns() and owns_dereferencable(). for(typename Cont::const_iterator it = c9.begin(), end = c9.end(); it != end; ++it) { assert(c9.owns(it)); assert(c9.owns_dereferencable(it)); assert(! c10.owns(it)); assert(! c10.owns_dereferencable(it)); } assert(c9.owns(c9.end())); assert(! c9.owns_dereferencable(c9.end())); c9.erase(c9.begin(), c9.end()); assert(check_empty(c9)); std::cout << "Testing parallel insertion" << std::endl; { Cont c11; Vect v11(1000000); std::vector<typename Cont::iterator> iterators(v11.size()); tbb::parallel_for( tbb::blocked_range<size_t>( 0, v11.size() ), Insert_in_CCC_functor<Vect, Cont>(v11, c11, iterators) ); assert(c11.size() == v11.size()); std::cout << "Testing parallel erasure" << std::endl; tbb::parallel_for( tbb::blocked_range<size_t>( 0, v11.size() ), Erase_in_CCC_functor<Cont>(c11, iterators) ); assert(c11.empty()); } std::cout << "Testing parallel insertion AND erasure" << std::endl; { Cont c12; Vect v12(1000000); std::vector<tbb::atomic<bool> > free_elements(v12.size()); for(typename std::vector<tbb::atomic<bool> >::iterator it = free_elements.begin(), end = free_elements.end(); it != end; ++it) { *it = true; } tbb::atomic<unsigned int> num_erasures; num_erasures = 0; std::vector<typename Cont::iterator> iterators(v12.size()); tbb::parallel_for( tbb::blocked_range<size_t>( 0, v12.size() ), Insert_and_erase_in_CCC_functor<Vect, Cont>( v12, c12, iterators, free_elements, num_erasures) ); assert(c12.size() == v12.size() - num_erasures); } }
void test_boundary_mesh() { typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; typedef CGAL::Polyhedron_3<Kernel, CGAL::Polyhedron_items_with_id_3> Polyhedron_3; typedef CGAL::Surface_mesh_shortest_path_traits<Kernel, Polyhedron_3> Traits; typedef Traits::Barycentric_coordinate Barycentric_coordinate; typedef Traits::FT FT; typedef Traits::Point_3 Point_3; typedef Traits::Triangle_3 Triangle_3; typedef boost::graph_traits<Polyhedron_3> Graph_traits; typedef Graph_traits::vertex_descriptor vertex_descriptor; typedef Graph_traits::vertex_iterator vertex_iterator; typedef Graph_traits::face_descriptor face_descriptor; typedef Graph_traits::face_iterator face_iterator; typedef CGAL::Surface_mesh_shortest_path<Traits> Surface_mesh_shortest_path; typedef boost::property_map<Polyhedron_3, CGAL::vertex_point_t>::type VPM; Traits traits; Traits::Construct_triangle_3_to_triangle_2_projection project_triangle_3_to_triangle_2(traits.construct_triangle_3_to_triangle_2_projection_object()); CGAL_USE(project_triangle_3_to_triangle_2); Traits::Compute_squared_distance_3 compute_squared_distance_3(traits.compute_squared_distance_3_object()); Traits::Construct_barycenter_3 construct_barycenter_3(traits.construct_barycenter_3_object()); Traits::Construct_triangle_3_along_segment_2_flattening flatten_triangle_3_along_segment_2(traits.construct_triangle_3_along_segment_2_flattening_object()); CGAL_USE(flatten_triangle_3_along_segment_2); Traits::Construct_barycentric_coordinate construct_barycentric_coordinate(traits.construct_barycentric_coordinate_object()); struct Construct_barycenter_in_triangle_3 { Traits::Construct_barycenter_3 m_cb3; Construct_barycenter_in_triangle_3(Traits::Construct_barycenter_3 cb3) : m_cb3(cb3) { } Point_3 operator() (const Triangle_3& t, const Barycentric_coordinate& b) { return m_cb3(t[0], b[0], t[1], b[1], t[2], b[2]); } } construct_barycenter_in_triangle_3(construct_barycenter_3); std::ifstream inFile("data/boundary_mesh.off"); Polyhedron_3 P; inFile >> P; inFile.close(); CGAL::set_halfedgeds_items_id(P); face_iterator startFace; face_iterator endFace; boost::tie(startFace, endFace) = CGAL::faces(P); vertex_iterator currentVertex; vertex_iterator endVertex; VPM vpm = CGAL::get(CGAL::vertex_point, P); vertex_descriptor vertexHandles[10]; face_descriptor faceHandles[8]; Point_3 vertexLocations[10]; size_t currentVertexIndex = 0; for (boost::tie(currentVertex, endVertex) = CGAL::vertices(P); currentVertex != endVertex; ++currentVertex) { vertexHandles[currentVertexIndex] = *currentVertex; vertexLocations[currentVertexIndex] = vpm[*currentVertex]; ++currentVertexIndex; } size_t currentFaceIndex = 0; for (face_iterator currentFace = startFace; currentFace != endFace; ++currentFace) { faceHandles[currentFaceIndex] = *currentFace; ++currentFaceIndex; } Barycentric_coordinate startLocation = construct_barycentric_coordinate(FT(0.1), FT(0.8), FT(0.1)); typedef boost::property_map<Polyhedron_3, CGAL::face_external_index_t>::type FaceIndexMap; FaceIndexMap faceIndexMap(CGAL::get(CGAL::face_external_index, P)); Surface_mesh_shortest_path shortestPaths(P, traits); //shortestPaths.m_debugOutput = true; shortestPaths.add_source_point(*startFace, startLocation); shortestPaths.build_sequence_tree(); Triangle_3 firstTriangle(vertexLocations[1], vertexLocations[0], vertexLocations[2]); Point_3 locationInTriangle(construct_barycenter_in_triangle_3(firstTriangle, startLocation)); FT dist0 = shortestPaths.shortest_distance_to_source_points(vertexHandles[0]).first; CHECK_CLOSE(dist0, CGAL::sqrt(compute_squared_distance_3(locationInTriangle, vertexLocations[0])), FT(0.000001)); FT dist1 = shortestPaths.shortest_distance_to_source_points(vertexHandles[1]).first; CHECK_CLOSE(dist1, CGAL::sqrt(compute_squared_distance_3(locationInTriangle, vertexLocations[1])), FT(0.000001)); FT dist2 = shortestPaths.shortest_distance_to_source_points(vertexHandles[2]).first; CHECK_CLOSE(dist2, CGAL::sqrt(compute_squared_distance_3(locationInTriangle, vertexLocations[2])), FT(0.000001)); FT dist3 = shortestPaths.shortest_distance_to_source_points(vertexHandles[3]).first; CHECK_CLOSE(dist3, CGAL::sqrt(compute_squared_distance_3(locationInTriangle, vertexLocations[3])), FT(0.000001)); FT dist4 = shortestPaths.shortest_distance_to_source_points(vertexHandles[4]).first; CHECK_CLOSE(dist4, CGAL::sqrt(compute_squared_distance_3(locationInTriangle, vertexLocations[1])) + CGAL::sqrt(compute_squared_distance_3(vertexLocations[1], vertexLocations[4])), FT(0.000001)); FT dist5 = shortestPaths.shortest_distance_to_source_points(vertexHandles[5]).first; CHECK_CLOSE(dist5, CGAL::sqrt(compute_squared_distance_3(locationInTriangle, vertexLocations[3])) + CGAL::sqrt(compute_squared_distance_3(vertexLocations[3], vertexLocations[5])), FT(0.000001)); Barycentric_coordinate somewhereElseInFirstTriangle = construct_barycentric_coordinate(0.8, 0.05, 0.15); FT distT0 = shortestPaths.shortest_distance_to_source_points(faceHandles[0], somewhereElseInFirstTriangle).first; CHECK_CLOSE(distT0, CGAL::sqrt(compute_squared_distance_3(locationInTriangle, construct_barycenter_in_triangle_3(firstTriangle, somewhereElseInFirstTriangle))), FT(0.000001)); Triangle_3 oneStepTriangle(vertexLocations[4], vertexLocations[1], vertexLocations[3]); Barycentric_coordinate locationInOneStepTriangle = construct_barycentric_coordinate(0.1, 0.8, 0.1); CGAL::test::Edge_sequence_collector<Traits> collector(P); shortestPaths.shortest_path_sequence_to_source_points(faceHandles[2], locationInOneStepTriangle, collector); FT distT2 = shortestPaths.shortest_distance_to_source_points(faceHandles[2], locationInOneStepTriangle).first; CHECK_CLOSE(distT2, dist1 + CGAL::sqrt(compute_squared_distance_3(vertexLocations[1], construct_barycenter_in_triangle_3(oneStepTriangle, locationInOneStepTriangle))), FT(0.00001)); Triangle_3 twoStepTriangle(vertexLocations[6], vertexLocations[5], vertexLocations[7]); Barycentric_coordinate locationInTwoStepTriangle = construct_barycentric_coordinate(0.8, 0.1, 0.1); FT distT5 = shortestPaths.shortest_distance_to_source_points(faceHandles[5], locationInTwoStepTriangle).first; CHECK_CLOSE(distT5, dist3 + CGAL::sqrt(compute_squared_distance_3(vertexLocations[3], construct_barycenter_in_triangle_3(twoStepTriangle, locationInTwoStepTriangle))), FT(0.00001)); Triangle_3 threeStepTriangle(vertexLocations[7], vertexLocations[5], vertexLocations[8]); Barycentric_coordinate locationInThreeStepTriangle = construct_barycentric_coordinate(0.2, 0.6, 0.2); FT distT6 = shortestPaths.shortest_distance_to_source_points(faceHandles[6], locationInThreeStepTriangle).first; CHECK_CLOSE(distT6, dist5 + CGAL::sqrt(compute_squared_distance_3(vertexLocations[5], construct_barycenter_in_triangle_3(threeStepTriangle, locationInThreeStepTriangle))), FT(0.00001)); }