Commit c6195f58 by François Dumont

re PR libstdc++/29988 (More stl_tree.h enhancements: improving operator=)

2014-09-24  François Dumont  <fdumont@gcc.gnu.org>

	PR libstdc++/29988
	* include/bits/stl_tree.h (_Rb_tree_reuse_or_alloc_node<>): New.
	(_Rb_tree_alloc_node<>): New.
	(_Rb_tree<>::operator=(_Rb_tree<>&&)): New.
	(_Rb_tree<>::_M_assign_unique): New.
	(_Rb_tree<>::_M_assign_equal): New.
	(_Rb_tree<>): Adapt to reuse allocated nodes as much as possible.
	* include/bits/stl_map.h
	(std::map<>::operator=(std::map<>&&)): Default implementation.
	(std::map<>::operator=(initializer_list<>)): Adapt to use
	_Rb_tree::_M_assign_unique.
	* include/bits/stl_multimap.h
	(std::multimap<>::operator=(std::multimap<>&&)): Default implementation.
	(std::multimap<>::operator=(initializer_list<>)): Adapt to use
	_Rb_tree::_M_assign_equal.
	* include/bits/stl_set.h
	(std::set<>::operator=(std::set<>&&)): Default implementation.
	(std::set<>::operator=(initializer_list<>)): Adapt to use
	_Rb_tree::_M_assign_unique.
	* include/bits/stl_multiset.h
	(std::multiset<>::operator=(std::multiset<>&&)): Default implementation.
	(std::multiset<>::operator=(initializer_list<>)): Adapt to use
	_Rb_tree::_M_assign_equal.
	* testsuite/23_containers/map/allocator/copy_assign.cc (test03): New.
	* testsuite/23_containers/map/allocator/init-list.cc: New.
	* testsuite/23_containers/map/allocator/move_assign.cc (test03): New.
	* testsuite/23_containers/multimap/allocator/copy_assign.cc
	(test03): New.
	* testsuite/23_containers/multimap/allocator/init-list.cc: New.
	* testsuite/23_containers/multimap/allocator/move_assign.cc
	(test03): New.
	* testsuite/23_containers/multiset/allocator/copy_assign.cc
	(test03): New.
	* testsuite/23_containers/multiset/allocator/init-list.cc: New.
	* testsuite/23_containers/multiset/allocator/move_assign.cc
	(test03): New.
	* testsuite/23_containers/set/allocator/copy_assign.cc (test03): New.
	* testsuite/23_containers/set/allocator/init-list.cc: New.
	* testsuite/23_containers/set/allocator/move_assign.cc (test03): New.

From-SVN: r215568
parent 00de328a
2014-09-24 François Dumont <fdumont@gcc.gnu.org>
PR libstdc++/29988
* include/bits/stl_tree.h (_Rb_tree_reuse_or_alloc_node<>): New.
(_Rb_tree_alloc_node<>): New.
(_Rb_tree<>::operator=(_Rb_tree<>&&)): New.
(_Rb_tree<>::_M_assign_unique): New.
(_Rb_tree<>::_M_assign_equal): New.
(_Rb_tree<>): Adapt to reuse allocated nodes as much as possible.
* include/bits/stl_map.h
(std::map<>::operator=(std::map<>&&)): Default implementation.
(std::map<>::operator=(initializer_list<>)): Adapt to use
_Rb_tree::_M_assign_unique.
* include/bits/stl_multimap.h
(std::multimap<>::operator=(std::multimap<>&&)): Default implementation.
(std::multimap<>::operator=(initializer_list<>)): Adapt to use
_Rb_tree::_M_assign_equal.
* include/bits/stl_set.h
(std::set<>::operator=(std::set<>&&)): Default implementation.
(std::set<>::operator=(initializer_list<>)): Adapt to use
_Rb_tree::_M_assign_unique.
* include/bits/stl_multiset.h
(std::multiset<>::operator=(std::multiset<>&&)): Default implementation.
(std::multiset<>::operator=(initializer_list<>)): Adapt to use
_Rb_tree::_M_assign_equal.
* testsuite/23_containers/map/allocator/copy_assign.cc (test03): New.
* testsuite/23_containers/map/allocator/init-list.cc: New.
* testsuite/23_containers/map/allocator/move_assign.cc (test03): New.
* testsuite/23_containers/multimap/allocator/copy_assign.cc
(test03): New.
* testsuite/23_containers/multimap/allocator/init-list.cc: New.
* testsuite/23_containers/multimap/allocator/move_assign.cc
(test03): New.
* testsuite/23_containers/multiset/allocator/copy_assign.cc
(test03): New.
* testsuite/23_containers/multiset/allocator/init-list.cc: New.
* testsuite/23_containers/multiset/allocator/move_assign.cc
(test03): New.
* testsuite/23_containers/set/allocator/copy_assign.cc (test03): New.
* testsuite/23_containers/set/allocator/init-list.cc: New.
* testsuite/23_containers/set/allocator/move_assign.cc (test03): New.
2014-09-24 Jonathan Wakely <jwakely@redhat.com>
PR libstdc++/63353
......
......@@ -297,28 +297,9 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
}
#if __cplusplus >= 201103L
/**
* @brief %Map move assignment operator.
* @param __x A %map of identical element and allocator types.
*
* The contents of @a __x are moved into this map (without copying
* if the allocators compare equal or get moved on assignment).
* Afterwards @a __x is in a valid, but unspecified state.
*/
/// Move assignment operator.
map&
operator=(map&& __x) noexcept(_Alloc_traits::_S_nothrow_move())
{
if (!_M_t._M_move_assign(__x._M_t))
{
// The rvalue's allocator cannot be moved and is not equal,
// so we need to individually move each element.
clear();
insert(std::__make_move_if_noexcept_iterator(__x.begin()),
std::__make_move_if_noexcept_iterator(__x.end()));
__x.clear();
}
return *this;
}
operator=(map&&) = default;
/**
* @brief %Map list assignment operator.
......@@ -334,8 +315,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
map&
operator=(initializer_list<value_type> __l)
{
this->clear();
this->insert(__l.begin(), __l.end());
_M_t._M_assign_unique(__l.begin(), __l.end());
return *this;
}
#endif
......
......@@ -292,28 +292,9 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
}
#if __cplusplus >= 201103L
/**
* @brief %Multimap move assignment operator.
* @param __x A %multimap of identical element and allocator types.
*
* The contents of @a __x are moved into this multimap (without copying
* if the allocators compare equal or get moved on assignment).
* Afterwards @a __x is in a valid, but unspecified state.
*/
/// Move assignment operator.
multimap&
operator=(multimap&& __x) noexcept(_Alloc_traits::_S_nothrow_move())
{
if (!_M_t._M_move_assign(__x._M_t))
{
// The rvalue's allocator cannot be moved and is not equal,
// so we need to individually move each element.
clear();
insert(std::__make_move_if_noexcept_iterator(__x.begin()),
std::__make_move_if_noexcept_iterator(__x.end()));
__x.clear();
}
return *this;
}
operator=(multimap&&) = default;
/**
* @brief %Multimap list assignment operator.
......@@ -329,8 +310,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
multimap&
operator=(initializer_list<value_type> __l)
{
this->clear();
this->insert(__l.begin(), __l.end());
_M_t._M_assign_equal(__l.begin(), __l.end());
return *this;
}
#endif
......
......@@ -263,28 +263,9 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
}
#if __cplusplus >= 201103L
/**
* @brief %Multiset move assignment operator.
* @param __x A %multiset of identical element and allocator types.
*
* The contents of @a __x are moved into this %multiset (without
* copying if the allocators compare equal or get moved on assignment).
* Afterwards @a __x is in a valid, but unspecified state.
*/
/// Move assignment operator.
multiset&
operator=(multiset&& __x) noexcept(_Alloc_traits::_S_nothrow_move())
{
if (!_M_t._M_move_assign(__x._M_t))
{
// The rvalue's allocator cannot be moved and is not equal,
// so we need to individually move each element.
clear();
insert(std::__make_move_if_noexcept_iterator(__x._M_t.begin()),
std::__make_move_if_noexcept_iterator(__x._M_t.end()));
__x.clear();
}
return *this;
}
operator=(multiset&&) = default;
/**
* @brief %Multiset list assignment operator.
......@@ -300,8 +281,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
multiset&
operator=(initializer_list<value_type> __l)
{
this->clear();
this->insert(__l.begin(), __l.end());
_M_t._M_assign_equal(__l.begin(), __l.end());
return *this;
}
#endif
......
......@@ -267,28 +267,9 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
}
#if __cplusplus >= 201103L
/**
* @brief %Set move assignment operator.
* @param __x A %set of identical element and allocator types.
*
* The contents of @a __x are moved into this %set (without copying
* if the allocators compare equal or get moved on assignment).
* Afterwards @a __x is in a valid, but unspecified state.
*/
/// Move assignment operator.
set&
operator=(set&& __x) noexcept(_Alloc_traits::_S_nothrow_move())
{
if (!_M_t._M_move_assign(__x._M_t))
{
// The rvalue's allocator cannot be moved and is not equal,
// so we need to individually move each element.
clear();
insert(std::__make_move_if_noexcept_iterator(__x._M_t.begin()),
std::__make_move_if_noexcept_iterator(__x._M_t.end()));
__x.clear();
}
return *this;
}
operator=(set&&) = default;
/**
* @brief %Set list assignment operator.
......@@ -304,8 +285,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
set&
operator=(initializer_list<value_type> __l)
{
this->clear();
this->insert(__l.begin(), __l.end());
_M_t._M_assign_unique(__l.begin(), __l.end());
return *this;
}
#endif
......
......@@ -59,9 +59,33 @@ void test02()
VERIFY(1 == v2.get_allocator().get_personality());
}
void test03()
{
bool test __attribute__((unused)) = true;
using namespace __gnu_test;
typedef tracker_allocator<std::pair<const int, int>> alloc_type;
typedef std::map<int, int, std::less<int>, alloc_type> test_type;
tracker_allocator_counter::reset();
test_type v1 = { { 0, 0 }, { 1, 1 } };
test_type v2 = { { 2, 2 }, { 3, 3 } };
auto allocs = tracker_allocator_counter::get_allocation_count();
auto constructs = tracker_allocator_counter::get_construct_count();
v1 = v2;
VERIFY( tracker_allocator_counter::get_allocation_count() == allocs );
VERIFY( tracker_allocator_counter::get_construct_count() == constructs + 2 );
}
int main()
{
test01();
test02();
test03();
return 0;
}
// Copyright (C) 2014 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this library; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.
//
// { dg-options "-std=gnu++11" }
#include <map>
#include <testsuite_hooks.h>
#include <testsuite_allocator.h>
void test01()
{
bool test __attribute__((unused)) = true;
using namespace __gnu_test;
typedef tracker_allocator<std::pair<const int, int>> alloc_type;
typedef std::map<int, int, std::less<int>, alloc_type> test_type;
tracker_allocator_counter::reset();
test_type v1;
v1 = { { 0, 0 }, { 1, 1 } };
auto allocs = tracker_allocator_counter::get_allocation_count();
auto constructs = tracker_allocator_counter::get_construct_count();
VERIFY( allocs != 0 );
VERIFY( constructs != 0 );
// Check no allocation on list initialization.
v1 = { { 4, 4 }, { 5, 5 } };
VERIFY( tracker_allocator_counter::get_allocation_count() == allocs );
VERIFY( tracker_allocator_counter::get_construct_count() == constructs + 2 );
}
int main()
{
test01();
}
......@@ -43,8 +43,8 @@ void test01()
v2 = { test_type::value_type{} };
v2 = std::move(v1);
VERIFY(1 == v1.get_allocator().get_personality());
VERIFY(2 == v2.get_allocator().get_personality());
VERIFY( 1 == v1.get_allocator().get_personality() );
VERIFY( 2 == v2.get_allocator().get_personality() );
}
void test02()
......@@ -60,14 +60,47 @@ void test02()
v2 = { test_type::value_type{} };
v2 = std::move(v1);
VERIFY(0 == v1.get_allocator().get_personality());
VERIFY(1 == v2.get_allocator().get_personality());
VERIFY( 0 == v1.get_allocator().get_personality() );
VERIFY( 1 == v2.get_allocator().get_personality() );
VERIFY( it == v2.begin() );
}
void test03()
{
bool test __attribute__((unused)) = true;
using namespace __gnu_test;
typedef propagating_allocator<std::pair<const int, int>, false,
tracker_allocator<std::pair<const int, int>>>
alloc_type;
typedef std::map<int, int, std::less<int>, alloc_type> test_type;
tracker_allocator_counter::reset();
test_type v1(alloc_type(1));
v1 = { { 0, 0 }, { 1, 1 } };
test_type v2(alloc_type(2));
v2 = { { 2, 2 }, { 3, 3 } };
auto allocs = tracker_allocator_counter::get_allocation_count();
auto constructs = tracker_allocator_counter::get_construct_count();
// Check no allocation on move assignment with non propagating allocators.
v1 = std::move(v2);
VERIFY( 1 == v1.get_allocator().get_personality() );
VERIFY( 2 == v2.get_allocator().get_personality() );
VERIFY( tracker_allocator_counter::get_allocation_count() == allocs );
VERIFY( tracker_allocator_counter::get_construct_count() == constructs + 2 );
}
int main()
{
test01();
test02();
test03();
return 0;
}
......@@ -59,9 +59,33 @@ void test02()
VERIFY(1 == v2.get_allocator().get_personality());
}
void test03()
{
bool test __attribute__((unused)) = true;
using namespace __gnu_test;
typedef tracker_allocator<std::pair<const int, int>> alloc_type;
typedef std::multimap<int, int, std::less<int>, alloc_type> test_type;
tracker_allocator_counter::reset();
test_type v1 = { { 1, 1 }, { 1, 1 } };
test_type v2 = { { 2, 2 }, { 2, 2 } };
auto allocs = tracker_allocator_counter::get_allocation_count();
auto constructs = tracker_allocator_counter::get_construct_count();
v1 = v2;
VERIFY( tracker_allocator_counter::get_allocation_count() == allocs );
VERIFY( tracker_allocator_counter::get_construct_count() == constructs + 2 );
}
int main()
{
test01();
test02();
test03();
return 0;
}
// Copyright (C) 2014 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this library; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.
//
// { dg-options "-std=gnu++11" }
#include <map>
#include <testsuite_hooks.h>
#include <testsuite_allocator.h>
void test01()
{
bool test __attribute__((unused)) = true;
using namespace __gnu_test;
typedef tracker_allocator<std::pair<const int, int>> alloc_type;
typedef std::multimap<int, int, std::less<int>, alloc_type> test_type;
tracker_allocator_counter::reset();
test_type v1;
v1 = { { 0, 0 }, { 0, 0 } };
auto allocs = tracker_allocator_counter::get_allocation_count();
auto constructs = tracker_allocator_counter::get_construct_count();
VERIFY( allocs != 0 );
VERIFY( constructs != 0 );
// Check no allocation on list initialization.
v1 = { { 1, 1 }, { 1, 1 } };
VERIFY( tracker_allocator_counter::get_allocation_count() == allocs );
VERIFY( tracker_allocator_counter::get_construct_count() == constructs + 2 );
}
int main()
{
test01();
}
......@@ -61,9 +61,42 @@ void test02()
VERIFY( it == v2.begin() );
}
void test03()
{
bool test __attribute__((unused)) = true;
using namespace __gnu_test;
typedef propagating_allocator<std::pair<const int, int>, false,
tracker_allocator<std::pair<const int, int>>>
alloc_type;
typedef std::multimap<int, int, std::less<int>, alloc_type> test_type;
tracker_allocator_counter::reset();
test_type v1(alloc_type(1));
v1 = { { 1, 1 }, { 1, 1 } };
test_type v2(alloc_type(2));
v2 = { { 2, 2 }, { 2, 2 } };
auto allocs = tracker_allocator_counter::get_allocation_count();
auto constructs = tracker_allocator_counter::get_construct_count();
// Check no allocation on move assignment with non propagating allocators.
v1 = std::move(v2);
VERIFY( 1 == v1.get_allocator().get_personality() );
VERIFY( 2 == v2.get_allocator().get_personality() );
VERIFY( tracker_allocator_counter::get_allocation_count() == allocs );
VERIFY( tracker_allocator_counter::get_construct_count() == constructs + 2 );
}
int main()
{
test01();
test02();
test03();
return 0;
}
......@@ -57,9 +57,33 @@ void test02()
VERIFY(1 == v2.get_allocator().get_personality());
}
void test03()
{
bool test __attribute__((unused)) = true;
using namespace __gnu_test;
typedef tracker_allocator<int> alloc_type;
typedef std::multiset<int, std::less<int>, alloc_type> test_type;
tracker_allocator_counter::reset();
test_type v1 = { 0, 0 };
test_type v2 = { 1, 1 };
auto allocs = tracker_allocator_counter::get_allocation_count();
auto constructs = tracker_allocator_counter::get_construct_count();
v1 = v2;
VERIFY( tracker_allocator_counter::get_allocation_count() == allocs );
VERIFY( tracker_allocator_counter::get_construct_count() == constructs + 2 );
}
int main()
{
test01();
test02();
test03();
return 0;
}
// Copyright (C) 2014 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this library; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.
//
// { dg-options "-std=gnu++11" }
#include <set>
#include <testsuite_hooks.h>
#include <testsuite_allocator.h>
void test01()
{
bool test __attribute__((unused)) = true;
using namespace __gnu_test;
typedef tracker_allocator<int> alloc_type;
typedef std::multiset<int, std::less<int>, alloc_type> test_type;
tracker_allocator_counter::reset();
test_type v1;
v1 = { 0, 0 };
auto allocs = tracker_allocator_counter::get_allocation_count();
auto constructs = tracker_allocator_counter::get_construct_count();
VERIFY( allocs != 0 );
VERIFY( constructs != 0 );
// Check no allocation on list initialization.
v1 = { 1, 1 };
VERIFY( tracker_allocator_counter::get_allocation_count() == allocs );
VERIFY( tracker_allocator_counter::get_construct_count() == constructs + 2 );
}
int main()
{
test01();
}
......@@ -59,9 +59,40 @@ void test02()
VERIFY( it == v2.begin() );
}
void test03()
{
bool test __attribute__((unused)) = true;
using namespace __gnu_test;
typedef propagating_allocator<int, false, tracker_allocator<int>> alloc_type;
typedef std::multiset<int, std::less<int>, alloc_type> test_type;
tracker_allocator_counter::reset();
test_type v1(alloc_type(1));
v1 = { 0, 0 };
test_type v2(alloc_type(2));
v2 = { 2, 2 };
auto allocs = tracker_allocator_counter::get_allocation_count();
auto constructs = tracker_allocator_counter::get_construct_count();
// Check no allocation on move assignment with non propagating allocators.
v1 = std::move(v2);
VERIFY( 1 == v1.get_allocator().get_personality() );
VERIFY( 2 == v2.get_allocator().get_personality() );
VERIFY( tracker_allocator_counter::get_allocation_count() == allocs );
VERIFY( tracker_allocator_counter::get_construct_count() == constructs + 2 );
}
int main()
{
test01();
test02();
test03();
return 0;
}
......@@ -57,9 +57,33 @@ void test02()
VERIFY(1 == v2.get_allocator().get_personality());
}
void test03()
{
bool test __attribute__((unused)) = true;
using namespace __gnu_test;
typedef tracker_allocator<int> alloc_type;
typedef std::set<int, std::less<int>, alloc_type> test_type;
tracker_allocator_counter::reset();
test_type v1 = { 0, 1 };
test_type v2 = { 2, 3 };
auto allocs = tracker_allocator_counter::get_allocation_count();
auto constructs = tracker_allocator_counter::get_construct_count();
v1 = v2;
VERIFY( tracker_allocator_counter::get_allocation_count() == allocs );
VERIFY( tracker_allocator_counter::get_construct_count() == constructs + 2 );
}
int main()
{
test01();
test02();
test03();
return 0;
}
// Copyright (C) 2014 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this library; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.
//
// { dg-options "-std=gnu++11" }
#include <set>
#include <testsuite_hooks.h>
#include <testsuite_allocator.h>
void test01()
{
bool test __attribute__((unused)) = true;
using namespace __gnu_test;
typedef tracker_allocator<int> alloc_type;
typedef std::set<int, std::less<int>, alloc_type> test_type;
tracker_allocator_counter::reset();
test_type v1;
v1 = { 0, 1 };
auto allocs = tracker_allocator_counter::get_allocation_count();
auto constructs = tracker_allocator_counter::get_construct_count();
VERIFY( allocs != 0 );
VERIFY( constructs != 0 );
// Check no allocation on list initialization.
v1 = { 4, 5 };
VERIFY( tracker_allocator_counter::get_allocation_count() == allocs );
VERIFY( tracker_allocator_counter::get_construct_count() == constructs + 2 );
}
int main()
{
test01();
}
......@@ -59,9 +59,40 @@ void test02()
VERIFY( it == v2.begin() );
}
void test03()
{
bool test __attribute__((unused)) = true;
using namespace __gnu_test;
typedef propagating_allocator<int, false, tracker_allocator<int>> alloc_type;
typedef std::set<int, std::less<int>, alloc_type> test_type;
tracker_allocator_counter::reset();
test_type v1(alloc_type(1));
v1 = { 0, 1 };
test_type v2(alloc_type(2));
v2 = { 2, 3 };
auto allocs = tracker_allocator_counter::get_allocation_count();
auto constructs = tracker_allocator_counter::get_construct_count();
// Check no allocation on move assignment with non propagating allocators.
v1 = std::move(v2);
VERIFY( 1 == v1.get_allocator().get_personality() );
VERIFY( 2 == v2.get_allocator().get_personality() );
VERIFY( tracker_allocator_counter::get_allocation_count() == allocs );
VERIFY( tracker_allocator_counter::get_construct_count() == constructs + 2 );
}
int main()
{
test01();
test02();
test03();
return 0;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment