1 /* Copyright 2017-2021 PaGMO development team
2 
3 This file is part of the PaGMO library.
4 
5 The PaGMO library is free software; you can redistribute it and/or modify
6 it under the terms of either:
7 
8   * the GNU Lesser General Public License as published by the Free
9     Software Foundation; either version 3 of the License, or (at your
10     option) any later version.
11 
12 or
13 
14   * the GNU General Public License as published by the Free Software
15     Foundation; either version 3 of the License, or (at your option) any
16     later version.
17 
18 or both in parallel, as here.
19 
20 The PaGMO library is distributed in the hope that it will be useful, but
21 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
22 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
23 for more details.
24 
25 You should have received copies of the GNU General Public License and the
26 GNU Lesser General Public License along with the PaGMO library.  If not,
27 see https://www.gnu.org/licenses/. */
28 
29 #if defined(_MSC_VER)
30 
31 // Disable warnings from MSVC.
32 #pragma warning(disable : 4822)
33 
34 #endif
35 
36 #define BOOST_TEST_MODULE s_policy_test
37 #define BOOST_TEST_DYN_LINK
38 #include <boost/test/unit_test.hpp>
39 
40 #include <initializer_list>
41 #include <iostream>
42 #include <limits>
43 #include <memory>
44 #include <sstream>
45 #include <stdexcept>
46 #include <string>
47 #include <type_traits>
48 #include <typeindex>
49 #include <typeinfo>
50 #include <utility>
51 
52 #include <boost/algorithm/string/predicate.hpp>
53 #include <boost/lexical_cast.hpp>
54 
55 #include <pagmo/detail/type_name.hpp>
56 #include <pagmo/s11n.hpp>
57 #include <pagmo/s_policies/select_best.hpp>
58 #include <pagmo/s_policy.hpp>
59 #include <pagmo/types.hpp>
60 
61 using namespace pagmo;
62 
BOOST_AUTO_TEST_CASE(type_traits_tests)63 BOOST_AUTO_TEST_CASE(type_traits_tests)
64 {
65     BOOST_CHECK(!is_udsp<void>::value);
66     BOOST_CHECK(!is_udsp<int>::value);
67     BOOST_CHECK(!is_udsp<double>::value);
68 
69     struct udsp00 {
70         individuals_group_t select(const individuals_group_t &, const vector_double::size_type &,
71                                    const vector_double::size_type &, const vector_double::size_type &,
72                                    const vector_double::size_type &, const vector_double::size_type &,
73                                    const vector_double &) const;
74     };
75 
76     BOOST_CHECK(is_udsp<udsp00>::value);
77     BOOST_CHECK(!is_udsp<const udsp00>::value);
78     BOOST_CHECK(!is_udsp<const udsp00 &>::value);
79     BOOST_CHECK(!is_udsp<udsp00 &>::value);
80 
81     struct no_udsp00 {
82         void select(const individuals_group_t &, const vector_double::size_type &, const vector_double::size_type &,
83                     const vector_double::size_type &, const vector_double::size_type &,
84                     const vector_double::size_type &, const vector_double &) const;
85     };
86 
87     BOOST_CHECK(!is_udsp<no_udsp00>::value);
88 
89     struct no_udsp01 {
90         individuals_group_t select(const individuals_group_t &, const vector_double::size_type &,
91                                    const vector_double::size_type &, const vector_double::size_type &,
92                                    const vector_double::size_type &, const vector_double::size_type &,
93                                    const vector_double &);
94     };
95 
96     BOOST_CHECK(!is_udsp<no_udsp01>::value);
97 
98     struct no_udsp02 {
99         no_udsp02() = delete;
100         individuals_group_t select(const individuals_group_t &, const vector_double::size_type &,
101                                    const vector_double::size_type &, const vector_double::size_type &,
102                                    const vector_double::size_type &, const vector_double::size_type &,
103                                    const vector_double &) const;
104     };
105 
106     BOOST_CHECK(!is_udsp<no_udsp02>::value);
107 }
108 
109 struct udsp1 {
selectudsp1110     individuals_group_t select(const individuals_group_t &inds, const vector_double::size_type &,
111                                const vector_double::size_type &, const vector_double::size_type &,
112                                const vector_double::size_type &, const vector_double::size_type &,
113                                const vector_double &) const
114     {
115         return inds;
116     }
117     std::string foo = "hello world";
118 };
119 
120 struct udsp2 {
121     udsp2() = default;
udsp2udsp2122     udsp2(const udsp2 &other) : foo{new std::string{*other.foo}} {}
123     udsp2(udsp2 &&) = default;
selectudsp2124     individuals_group_t select(const individuals_group_t &inds, const vector_double::size_type &,
125                                const vector_double::size_type &, const vector_double::size_type &,
126                                const vector_double::size_type &, const vector_double::size_type &,
127                                const vector_double &) const
128     {
129         return inds;
130     }
get_nameudsp2131     std::string get_name() const
132     {
133         return "frobniz";
134     }
135     std::unique_ptr<std::string> foo = std::unique_ptr<std::string>{new std::string{"hello world"}};
136 };
137 
BOOST_AUTO_TEST_CASE(basic_tests)138 BOOST_AUTO_TEST_CASE(basic_tests)
139 {
140     s_policy r;
141 
142     BOOST_CHECK(r.is<select_best>());
143     BOOST_CHECK(!r.is<udsp1>());
144 
145     BOOST_CHECK(r.extract<select_best>() != nullptr);
146     BOOST_CHECK(r.extract<udsp1>() == nullptr);
147 
148     BOOST_CHECK(static_cast<const s_policy &>(r).extract<select_best>() != nullptr);
149     BOOST_CHECK(static_cast<const s_policy &>(r).extract<udsp1>() == nullptr);
150 
151     BOOST_CHECK(r.get_name() == "Select best");
152     BOOST_CHECK(!r.get_extra_info().empty());
153 
154     BOOST_CHECK(s_policy(udsp1{}).get_extra_info().empty());
155     BOOST_CHECK(s_policy(udsp1{}).get_name() == detail::type_name<udsp1>());
156 
157     // Constructors, assignments.
158     // Generic constructor with copy.
159     udsp1 r1;
160     s_policy s_pol1{r1};
161     BOOST_CHECK(r1.foo == "hello world");
162     BOOST_CHECK(s_pol1.extract<udsp1>()->foo == "hello world");
163     // Generic constructor with move.
164     udsp2 r2;
165     s_policy s_pol2{std::move(r2)};
166     BOOST_CHECK(r2.foo.get() == nullptr);
167     BOOST_CHECK(s_pol2.extract<udsp2>()->foo.get() != nullptr);
168     BOOST_CHECK(*s_pol2.extract<udsp2>()->foo == "hello world");
169     // Copy constructor.
170     udsp2 r3;
171     s_policy s_pol3{r3}, s_pol4{s_pol3};
172     BOOST_CHECK(*s_pol4.extract<udsp2>()->foo == "hello world");
173     BOOST_CHECK(s_pol4.extract<udsp2>()->foo.get() != s_pol3.extract<udsp2>()->foo.get());
174     BOOST_CHECK(s_pol4.get_name() == "frobniz");
175     // Move constructor.
176     s_policy s_pol5{std::move(s_pol4)};
177     BOOST_CHECK(*s_pol5.extract<udsp2>()->foo == "hello world");
178     BOOST_CHECK(s_pol5.get_name() == "frobniz");
179     // Revive s_pol4 via copy assignment.
180     s_pol4 = s_pol5;
181     BOOST_CHECK(*s_pol4.extract<udsp2>()->foo == "hello world");
182     BOOST_CHECK(s_pol4.get_name() == "frobniz");
183     // Revive s_pol4 via move assignment.
184     s_policy s_pol6{std::move(s_pol4)};
185     s_pol4 = std::move(s_pol5);
186     BOOST_CHECK(*s_pol4.extract<udsp2>()->foo == "hello world");
187     BOOST_CHECK(s_pol4.get_name() == "frobniz");
188     // Self move-assignment.
189     s_pol4 = std::move(*&s_pol4);
190     BOOST_CHECK(*s_pol4.extract<udsp2>()->foo == "hello world");
191     BOOST_CHECK(s_pol4.get_name() == "frobniz");
192 
193     // Minimal iostream test.
194     {
195         std::ostringstream oss;
196         oss << r;
197         BOOST_CHECK(!oss.str().empty());
198     }
199 
200     // Minimal serialization test.
201     {
202         std::string before;
203         std::stringstream ss;
204         {
205             before = boost::lexical_cast<std::string>(r);
206             boost::archive::binary_oarchive oarchive(ss);
207             oarchive << r;
208         }
209         r = s_policy{udsp1{}};
210         BOOST_CHECK(r.is<udsp1>());
211         BOOST_CHECK(before != boost::lexical_cast<std::string>(r));
212         {
213             boost::archive::binary_iarchive iarchive(ss);
214             iarchive >> r;
215         }
216         BOOST_CHECK(before == boost::lexical_cast<std::string>(r));
217         BOOST_CHECK(r.is<select_best>());
218     }
219 
220     std::cout << s_policy{} << '\n';
221 }
222 
BOOST_AUTO_TEST_CASE(optional_tests)223 BOOST_AUTO_TEST_CASE(optional_tests)
224 {
225     // get_name().
226     struct udsp_00 {
227         individuals_group_t select(const individuals_group_t &inds, const vector_double::size_type &,
228                                    const vector_double::size_type &, const vector_double::size_type &,
229                                    const vector_double::size_type &, const vector_double::size_type &,
230                                    const vector_double &) const
231         {
232             return inds;
233         }
234         std::string get_name() const
235         {
236             return "frobniz";
237         }
238     };
239     BOOST_CHECK_EQUAL(s_policy{udsp_00{}}.get_name(), "frobniz");
240     struct udsp_01 {
241         individuals_group_t select(const individuals_group_t &inds, const vector_double::size_type &,
242                                    const vector_double::size_type &, const vector_double::size_type &,
243                                    const vector_double::size_type &, const vector_double::size_type &,
244                                    const vector_double &) const
245         {
246             return inds;
247         }
248         // Missing const.
249         std::string get_name()
250         {
251             return "frobniz";
252         }
253     };
254     BOOST_CHECK(s_policy{udsp_01{}}.get_name() != "frobniz");
255 
256     // get_extra_info().
257     struct udsp_02 {
258         individuals_group_t select(const individuals_group_t &inds, const vector_double::size_type &,
259                                    const vector_double::size_type &, const vector_double::size_type &,
260                                    const vector_double::size_type &, const vector_double::size_type &,
261                                    const vector_double &) const
262         {
263             return inds;
264         }
265         std::string get_extra_info() const
266         {
267             return "frobniz";
268         }
269     };
270     BOOST_CHECK_EQUAL(s_policy{udsp_02{}}.get_extra_info(), "frobniz");
271     struct udsp_03 {
272         individuals_group_t select(const individuals_group_t &inds, const vector_double::size_type &,
273                                    const vector_double::size_type &, const vector_double::size_type &,
274                                    const vector_double::size_type &, const vector_double::size_type &,
275                                    const vector_double &) const
276         {
277             return inds;
278         }
279         // Missing const.
280         std::string get_extra_info()
281         {
282             return "frobniz";
283         }
284     };
285     BOOST_CHECK(s_policy{udsp_03{}}.get_extra_info().empty());
286 }
287 
BOOST_AUTO_TEST_CASE(stream_operator)288 BOOST_AUTO_TEST_CASE(stream_operator)
289 {
290     struct udsp_00 {
291         individuals_group_t select(const individuals_group_t &inds, const vector_double::size_type &,
292                                    const vector_double::size_type &, const vector_double::size_type &,
293                                    const vector_double::size_type &, const vector_double::size_type &,
294                                    const vector_double &) const
295         {
296             return inds;
297         }
298     };
299     {
300         std::ostringstream oss;
301         oss << s_policy{udsp_00{}};
302         BOOST_CHECK(!oss.str().empty());
303     }
304     struct udsp_01 {
305         individuals_group_t select(const individuals_group_t &inds, const vector_double::size_type &,
306                                    const vector_double::size_type &, const vector_double::size_type &,
307                                    const vector_double::size_type &, const vector_double::size_type &,
308                                    const vector_double &) const
309         {
310             return inds;
311         }
312         std::string get_extra_info() const
313         {
314             return "bartoppo";
315         }
316     };
317     {
318         std::ostringstream oss;
319         oss << s_policy{udsp_01{}};
320         const auto st = oss.str();
321         BOOST_CHECK(boost::contains(st, "bartoppo"));
322         BOOST_CHECK(boost::contains(st, "Extra info:"));
323     }
324 }
325 
BOOST_AUTO_TEST_CASE(selection)326 BOOST_AUTO_TEST_CASE(selection)
327 {
328     s_policy r0;
329 
330     BOOST_CHECK_EXCEPTION(r0.select(individuals_group_t{{0}, {}, {}}, 0, 0, 0, 0, 0, {}), std::invalid_argument,
331                           [](const std::invalid_argument &ia) {
332                               return boost::contains(
333                                   ia.what(),
334                                   "an invalid group of individuals was passed to a selection policy of type 'Select "
335                                   "best': the sets of individuals IDs, decision vectors and fitness vectors "
336                                   "must all have the same sizes, but instead their sizes are 1, 0 and 0");
337                           });
338 
339     BOOST_CHECK_EXCEPTION(r0.select(individuals_group_t{{0}, {{1.}}, {{1.}}}, 0, 0, 0, 0, 0, {}), std::invalid_argument,
340                           [](const std::invalid_argument &ia) {
341                               return boost::contains(
342                                   ia.what(),
343                                   "a problem dimension of zero was passed to a selection policy of type 'Select best'");
344                           });
345 
346     BOOST_CHECK_EXCEPTION(r0.select(individuals_group_t{{0}, {{1.}}, {{1.}}}, 1, 2, 0, 0, 0, {}), std::invalid_argument,
347                           [](const std::invalid_argument &ia) {
348                               return boost::contains(ia.what(),
349                                                      "the integer dimension (2) passed to a selection policy of type "
350                                                      "'Select best' is larger than the supplied problem dimension (1)");
351                           });
352 
353     BOOST_CHECK_EXCEPTION(
354         r0.select(individuals_group_t{{0}, {{1.}}, {{1.}}}, 1, 0, 0, 0, 0, {}), std::invalid_argument,
355         [](const std::invalid_argument &ia) {
356             return boost::contains(
357                 ia.what(),
358                 "an invalid number of objectives (0) was passed to a selection policy of type 'Select best'");
359         });
360 
361     BOOST_CHECK_EXCEPTION(r0.select(individuals_group_t{{0}, {{1.}}, {{1.}}}, 1, 0,
362                                     std::numeric_limits<vector_double::size_type>::max(), 0, 0, {}),
363                           std::invalid_argument, [](const std::invalid_argument &ia) {
364                               return boost::contains(
365                                   ia.what(), "the number of objectives ("
366                                                  + std::to_string(std::numeric_limits<vector_double::size_type>::max())
367                                                  + ") passed to a selection policy of type 'Select best' is too large");
368                           });
369 
370     BOOST_CHECK_EXCEPTION(r0.select(individuals_group_t{{0}, {{1.}}, {{1.}}}, 1, 0, 1,
371                                     std::numeric_limits<vector_double::size_type>::max(), 0, {}),
372                           std::invalid_argument, [](const std::invalid_argument &ia) {
373                               return boost::contains(
374                                   ia.what(), "the number of equality constraints ("
375                                                  + std::to_string(std::numeric_limits<vector_double::size_type>::max())
376                                                  + ") passed to a selection policy of type 'Select best' is too large");
377                           });
378 
379     BOOST_CHECK_EXCEPTION(r0.select(individuals_group_t{{0}, {{1.}}, {{1.}}}, 1, 0, 1, 0,
380                                     std::numeric_limits<vector_double::size_type>::max(), {}),
381                           std::invalid_argument, [](const std::invalid_argument &ia) {
382                               return boost::contains(
383                                   ia.what(), "the number of inequality constraints ("
384                                                  + std::to_string(std::numeric_limits<vector_double::size_type>::max())
385                                                  + ") passed to a selection policy of type 'Select best' is too large");
386                           });
387 
388     BOOST_CHECK_EXCEPTION(r0.select(individuals_group_t{{0}, {{1.}}, {{1.}}}, 1, 0, 1, 1, 1, {}), std::invalid_argument,
389                           [](const std::invalid_argument &ia) {
390                               return boost::contains(
391                                   ia.what(),
392                                   "the vector of tolerances passed to a selection policy of type 'Select best' has "
393                                   "a dimension (0) which is inconsistent with the total number of constraints (2)");
394                           });
395 
396     BOOST_CHECK_EXCEPTION(r0.select(individuals_group_t{{0, 1}, {{1.}, {}}, {{1.}, {1.}}}, 1, 0, 1, 0, 0, {}),
397                           std::invalid_argument, [](const std::invalid_argument &ia) {
398                               return boost::contains(
399                                   ia.what(), "not all the individuals passed to a selection policy of type 'Select "
400                                              "best' have the expected dimension (1)");
401                           });
402 
403     BOOST_CHECK_EXCEPTION(r0.select(individuals_group_t{{0, 1}, {{1.}, {1.}}, {{1.}, {}}}, 1, 0, 1, 0, 0, {}),
404                           std::invalid_argument, [](const std::invalid_argument &ia) {
405                               return boost::contains(
406                                   ia.what(), "not all the individuals passed to a selection policy of type 'Select "
407                                              "best' have the expected fitness dimension (1)");
408                           });
409 
410     struct fail_0 {
411         individuals_group_t select(const individuals_group_t &, const vector_double::size_type &,
412                                    const vector_double::size_type &, const vector_double::size_type &,
413                                    const vector_double::size_type &, const vector_double::size_type &,
414                                    const vector_double &) const
415         {
416             return individuals_group_t{{0}, {}, {}};
417         }
418         std::string get_name() const
419         {
420             return "fail_0";
421         }
422     };
423 
424     BOOST_CHECK_EXCEPTION(
425         s_policy{fail_0{}}.select(individuals_group_t{{0, 1}, {{1.}, {1.}}, {{1.}, {1.}}}, 1, 0, 1, 0, 0, {}),
426         std::invalid_argument, [](const std::invalid_argument &ia) {
427             return boost::contains(ia.what(),
428                                    "an invalid group of individuals was returned by a selection policy of type "
429                                    "'fail_0': the sets of individuals IDs, decision vectors and fitness vectors "
430                                    "must all have the same sizes, but instead their sizes are 1, 0 and 0");
431         });
432 
433     struct fail_1 {
434         individuals_group_t select(const individuals_group_t &, const vector_double::size_type &,
435                                    const vector_double::size_type &, const vector_double::size_type &,
436                                    const vector_double::size_type &, const vector_double::size_type &,
437                                    const vector_double &) const
438         {
439             return individuals_group_t{{0, 1}, {{1}, {}}, {{1}, {1}}};
440         }
441         std::string get_name() const
442         {
443             return "fail_1";
444         }
445     };
446 
447     BOOST_CHECK_EXCEPTION(
448         s_policy{fail_1{}}.select(individuals_group_t{{0, 1}, {{1.}, {1.}}, {{1.}, {1.}}}, 1, 0, 1, 0, 0, {}),
449         std::invalid_argument, [](const std::invalid_argument &ia) {
450             return boost::contains(ia.what(), "not all the individuals returned by a selection "
451                                               "policy of type 'fail_1' have the expected dimension (1)");
452         });
453 
454     struct fail_2 {
455         individuals_group_t select(const individuals_group_t &, const vector_double::size_type &,
456                                    const vector_double::size_type &, const vector_double::size_type &,
457                                    const vector_double::size_type &, const vector_double::size_type &,
458                                    const vector_double &) const
459         {
460             return individuals_group_t{{0, 1}, {{1}, {1}}, {{1}, {}}};
461         }
462         std::string get_name() const
463         {
464             return "fail_2";
465         }
466     };
467 
468     BOOST_CHECK_EXCEPTION(
469         s_policy{fail_2{}}.select(individuals_group_t{{0, 1}, {{1.}, {1.}}, {{1.}, {1.}}}, 1, 0, 1, 0, 0, {}),
470         std::invalid_argument, [](const std::invalid_argument &ia) {
471             return boost::contains(ia.what(), "not all the individuals returned by a selection policy of type "
472                                               "'fail_2' have the expected fitness dimension (1)");
473         });
474 }
475 
476 struct udsp_a {
selectudsp_a477     individuals_group_t select(const individuals_group_t &inds, const vector_double::size_type &,
478                                const vector_double::size_type &, const vector_double::size_type &,
479                                const vector_double::size_type &, const vector_double::size_type &,
480                                const vector_double &) const
481     {
482         return inds;
483     }
get_nameudsp_a484     std::string get_name() const
485     {
486         return "abba";
487     }
get_extra_infoudsp_a488     std::string get_extra_info() const
489     {
490         return "dabba";
491     }
492     template <typename Archive>
serializeudsp_a493     void serialize(Archive &ar, unsigned)
494     {
495         ar &state;
496     }
497     int state = 42;
498 };
499 
500 PAGMO_S11N_S_POLICY_EXPORT(udsp_a)
501 
502 // Serialization tests.
BOOST_AUTO_TEST_CASE(s11n)503 BOOST_AUTO_TEST_CASE(s11n)
504 {
505     s_policy s_pol0{udsp_a{}};
506     BOOST_CHECK(s_pol0.extract<udsp_a>()->state == 42);
507     s_pol0.extract<udsp_a>()->state = -42;
508     // Store the string representation.
509     std::stringstream ss;
510     auto before = boost::lexical_cast<std::string>(s_pol0);
511     // Now serialize, deserialize and compare the result.
512     {
513         boost::archive::binary_oarchive oarchive(ss);
514         oarchive << s_pol0;
515     }
516     // Change the content of p before deserializing.
517     s_pol0 = s_policy{};
518     {
519         boost::archive::binary_iarchive iarchive(ss);
520         iarchive >> s_pol0;
521     }
522     auto after = boost::lexical_cast<std::string>(s_pol0);
523     BOOST_CHECK_EQUAL(before, after);
524     BOOST_CHECK(s_pol0.is<udsp_a>());
525     BOOST_CHECK(s_pol0.extract<udsp_a>()->state = -42);
526 }
527 
BOOST_AUTO_TEST_CASE(is_valid)528 BOOST_AUTO_TEST_CASE(is_valid)
529 {
530     s_policy p0;
531     BOOST_CHECK(p0.is_valid());
532     s_policy p1(std::move(p0));
533     BOOST_CHECK(!p0.is_valid());
534     p0 = s_policy{udsp_a{}};
535     BOOST_CHECK(p0.is_valid());
536     p1 = std::move(p0);
537     BOOST_CHECK(!p0.is_valid());
538     p0 = s_policy{udsp_a{}};
539     BOOST_CHECK(p0.is_valid());
540 }
541 
BOOST_AUTO_TEST_CASE(generic_assignment)542 BOOST_AUTO_TEST_CASE(generic_assignment)
543 {
544     s_policy p0;
545     BOOST_CHECK(p0.is<select_best>());
546     BOOST_CHECK(&(p0 = udsp_a{}) == &p0);
547     BOOST_CHECK(p0.is_valid());
548     BOOST_CHECK(p0.is<udsp_a>());
549     p0 = udsp1{};
550     BOOST_CHECK(p0.is<udsp1>());
551     BOOST_CHECK((!std::is_assignable<s_policy, void>::value));
552     BOOST_CHECK((!std::is_assignable<s_policy, int &>::value));
553     BOOST_CHECK((!std::is_assignable<s_policy, const int &>::value));
554     BOOST_CHECK((!std::is_assignable<s_policy, int &&>::value));
555 }
556 
BOOST_AUTO_TEST_CASE(type_index)557 BOOST_AUTO_TEST_CASE(type_index)
558 {
559     s_policy p0;
560     BOOST_CHECK(p0.get_type_index() == std::type_index(typeid(select_best)));
561     p0 = s_policy{udsp1{}};
562     BOOST_CHECK(p0.get_type_index() == std::type_index(typeid(udsp1)));
563 }
564 
BOOST_AUTO_TEST_CASE(get_ptr)565 BOOST_AUTO_TEST_CASE(get_ptr)
566 {
567     s_policy p0;
568     BOOST_CHECK(p0.get_ptr() == p0.extract<select_best>());
569     BOOST_CHECK(static_cast<const s_policy &>(p0).get_ptr()
570                 == static_cast<const s_policy &>(p0).extract<select_best>());
571     p0 = s_policy{udsp1{}};
572     BOOST_CHECK(p0.get_ptr() == p0.extract<udsp1>());
573     BOOST_CHECK(static_cast<const s_policy &>(p0).get_ptr() == static_cast<const s_policy &>(p0).extract<udsp1>());
574 }
575