151c0b2f7Stbbdev /*
2*b15aabb3Stbbdev     Copyright (c) 2005-2021 Intel Corporation
351c0b2f7Stbbdev 
451c0b2f7Stbbdev     Licensed under the Apache License, Version 2.0 (the "License");
551c0b2f7Stbbdev     you may not use this file except in compliance with the License.
651c0b2f7Stbbdev     You may obtain a copy of the License at
751c0b2f7Stbbdev 
851c0b2f7Stbbdev         http://www.apache.org/licenses/LICENSE-2.0
951c0b2f7Stbbdev 
1051c0b2f7Stbbdev     Unless required by applicable law or agreed to in writing, software
1151c0b2f7Stbbdev     distributed under the License is distributed on an "AS IS" BASIS,
1251c0b2f7Stbbdev     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1351c0b2f7Stbbdev     See the License for the specific language governing permissions and
1451c0b2f7Stbbdev     limitations under the License.
1551c0b2f7Stbbdev */
1651c0b2f7Stbbdev 
1751c0b2f7Stbbdev #ifndef __TBB_test_common_concurrent_priority_queue_common_H
1851c0b2f7Stbbdev #define __TBB_test_common_concurrent_priority_queue_common_H
1951c0b2f7Stbbdev 
2051c0b2f7Stbbdev // We need to skip allocator_traits::is_always_equal tests for C++11 and C++14
2151c0b2f7Stbbdev #define __TBB_TEST_SKIP_IS_ALWAYS_EQUAL_CHECK (__cplusplus < 201703L)
2251c0b2f7Stbbdev #include <common/test.h>
2351c0b2f7Stbbdev #include <common/utils.h>
2449e08aacStbbdev #include <oneapi/tbb/concurrent_priority_queue.h>
2549e08aacStbbdev #include <oneapi/tbb/blocked_range.h>
2651c0b2f7Stbbdev #include <vector>
2751c0b2f7Stbbdev 
2851c0b2f7Stbbdev namespace equality_comparison_helpers {
2951c0b2f7Stbbdev 
3051c0b2f7Stbbdev template <typename ElementType, typename Compare, typename Allocator>
toVector(const tbb::concurrent_priority_queue<ElementType,Compare,Allocator> & source)3151c0b2f7Stbbdev std::vector<ElementType> toVector( const tbb::concurrent_priority_queue<ElementType, Compare, Allocator>& source ) {
3251c0b2f7Stbbdev     auto cpq = source;
3351c0b2f7Stbbdev     std::vector<ElementType> v;
3451c0b2f7Stbbdev     v.reserve(cpq.size());
3551c0b2f7Stbbdev 
3651c0b2f7Stbbdev     ElementType element;
3751c0b2f7Stbbdev     while(cpq.try_pop(element)) {
3851c0b2f7Stbbdev         v.emplace_back(element);
3951c0b2f7Stbbdev     }
4051c0b2f7Stbbdev     std::reverse(v.begin(), v.end());
4151c0b2f7Stbbdev     return v;
4251c0b2f7Stbbdev }
4351c0b2f7Stbbdev 
4451c0b2f7Stbbdev }; // namespace equality_comparison_helpers
4551c0b2f7Stbbdev 
4651c0b2f7Stbbdev template <bool HasCopyCtor>
4751c0b2f7Stbbdev struct QueuePushHelper {
4851c0b2f7Stbbdev     template <typename Q, typename T>
pushQueuePushHelper4951c0b2f7Stbbdev     static void push(Q& q, T&& t) {
5051c0b2f7Stbbdev         q.push(std::forward<T>(t));
5151c0b2f7Stbbdev     }
5251c0b2f7Stbbdev };
5351c0b2f7Stbbdev 
5451c0b2f7Stbbdev template<>
5551c0b2f7Stbbdev template <typename Q, typename T>
push(Q & q,T && t)5651c0b2f7Stbbdev void QueuePushHelper<false>::push( Q& q, T&& t ) {
5751c0b2f7Stbbdev     q.push(std::move(t));
5851c0b2f7Stbbdev }
5951c0b2f7Stbbdev 
6051c0b2f7Stbbdev template <bool HasCopyCtor, typename QueueType>
examine(QueueType & q1,QueueType & q2,const std::vector<typename QueueType::value_type> & vec_sorted)6151c0b2f7Stbbdev void examine( QueueType& q1, QueueType& q2, const std::vector<typename QueueType::value_type>& vec_sorted ) {
6251c0b2f7Stbbdev     using value_type = typename QueueType::value_type;
6351c0b2f7Stbbdev 
6451c0b2f7Stbbdev     REQUIRE((!q1.empty() && q1.size() == vec_sorted.size()));
6551c0b2f7Stbbdev     value_type elem;
6651c0b2f7Stbbdev 
6751c0b2f7Stbbdev     q2.clear();
6851c0b2f7Stbbdev     REQUIRE((q2.empty() && !q2.size() && !q2.try_pop(elem)));
6951c0b2f7Stbbdev 
7051c0b2f7Stbbdev     typename std::vector<value_type>::const_reverse_iterator it1;
7151c0b2f7Stbbdev     for (it1 = vec_sorted.rbegin(); q1.try_pop(elem); ++it1) {
7251c0b2f7Stbbdev         REQUIRE(utils::IsEqual{}(elem, *it1));
7351c0b2f7Stbbdev         if (std::distance(vec_sorted.rbegin(), it1) % 2) {
7451c0b2f7Stbbdev             QueuePushHelper<HasCopyCtor>::push(q2, elem);
7551c0b2f7Stbbdev         } else {
7651c0b2f7Stbbdev             QueuePushHelper<HasCopyCtor>::push(q2, std::move(elem));
7751c0b2f7Stbbdev         }
7851c0b2f7Stbbdev     }
7951c0b2f7Stbbdev     REQUIRE(it1 == vec_sorted.rend());
8051c0b2f7Stbbdev     REQUIRE((q1.empty() && !q1.size()));
8151c0b2f7Stbbdev     REQUIRE((!q2.empty() && q2.size() == vec_sorted.size()));
8251c0b2f7Stbbdev 
8351c0b2f7Stbbdev     q1.swap(q2);
8451c0b2f7Stbbdev     REQUIRE((q2.empty() && !q2.size()));
8551c0b2f7Stbbdev     REQUIRE((!q1.empty() && q1.size() == vec_sorted.size()));
8651c0b2f7Stbbdev     for (it1 = vec_sorted.rbegin(); q1.try_pop(elem); ++it1)
8751c0b2f7Stbbdev         REQUIRE(utils::IsEqual{}(elem, *it1));
8851c0b2f7Stbbdev     REQUIRE(it1 == vec_sorted.rend());
8951c0b2f7Stbbdev };
9051c0b2f7Stbbdev 
9151c0b2f7Stbbdev template <typename QueueType>
examine(const QueueType & q,const std::vector<typename QueueType::value_type> & vec_sorted)9251c0b2f7Stbbdev void examine( const QueueType& q, const std::vector<typename QueueType::value_type>& vec_sorted ) {
9351c0b2f7Stbbdev     QueueType q1(q), q2(q);
9451c0b2f7Stbbdev     examine</*HasCopyCtor=*/true>(q1, q2, vec_sorted);
9551c0b2f7Stbbdev }
9651c0b2f7Stbbdev 
9751c0b2f7Stbbdev // TODO: consider wrapping each constructor test into separate TEST_CASE
9851c0b2f7Stbbdev template <typename ValueType, typename Compare>
type_tester(const std::vector<ValueType> & vec,Compare comp)9951c0b2f7Stbbdev void type_tester( const std::vector<ValueType>& vec, Compare comp ) {
10051c0b2f7Stbbdev     using queue_type = tbb::concurrent_priority_queue<ValueType, Compare>;
10151c0b2f7Stbbdev     REQUIRE_MESSAGE(vec.size() >= 5, "Array should have at least 5 elements");
10251c0b2f7Stbbdev 
10351c0b2f7Stbbdev     std::vector<ValueType> vec_sorted(vec);
10451c0b2f7Stbbdev     std::sort(vec_sorted.begin(), vec_sorted.end(), comp);
10551c0b2f7Stbbdev 
10651c0b2f7Stbbdev     // Construct an empty queue
10751c0b2f7Stbbdev     queue_type q1;
10851c0b2f7Stbbdev     q1.assign(vec.begin(), vec.end());
10951c0b2f7Stbbdev     examine(q1, vec_sorted);
11051c0b2f7Stbbdev 
11151c0b2f7Stbbdev     // Constructor from std::initializer_list
11251c0b2f7Stbbdev     queue_type q2({vec[0], vec[1], vec[2]});
11351c0b2f7Stbbdev     for (auto it = vec.begin() + 3; it != vec.end(); ++it)
11451c0b2f7Stbbdev         q2.push(*it);
11551c0b2f7Stbbdev     examine(q2, vec_sorted);
11651c0b2f7Stbbdev 
11751c0b2f7Stbbdev     // Assignment operator with std::initializer_list
11851c0b2f7Stbbdev     queue_type q3;
11951c0b2f7Stbbdev     q3 = {vec[0], vec[1], vec[2]};
12051c0b2f7Stbbdev     for (auto it = vec.begin() + 3; it != vec.end(); ++it)
12151c0b2f7Stbbdev         q3.push(*it);
12251c0b2f7Stbbdev     examine(q3, vec_sorted);
12351c0b2f7Stbbdev 
12451c0b2f7Stbbdev     // Copy ctor
12551c0b2f7Stbbdev     queue_type q4(q1);
12651c0b2f7Stbbdev     examine(q4, vec_sorted);
12751c0b2f7Stbbdev 
12851c0b2f7Stbbdev     // Copy ctor with allocator
12951c0b2f7Stbbdev     auto alloc = q1.get_allocator();
13051c0b2f7Stbbdev     queue_type q4_alloc(q1, alloc);
13151c0b2f7Stbbdev     examine(q4_alloc, vec_sorted);
13251c0b2f7Stbbdev 
13351c0b2f7Stbbdev     // Constructor from the half-open interval
13451c0b2f7Stbbdev     queue_type q5(vec.begin(), vec.end());
13551c0b2f7Stbbdev     examine(q5, vec_sorted);
13651c0b2f7Stbbdev 
13751c0b2f7Stbbdev     // Constructor from the allocator object
13851c0b2f7Stbbdev     queue_type q6(alloc);
13951c0b2f7Stbbdev     q6.assign(vec.begin(), vec.end());
14051c0b2f7Stbbdev     examine(q6, vec_sorted);
14151c0b2f7Stbbdev 
14251c0b2f7Stbbdev     // Constructor from the comparator and allocator object
14351c0b2f7Stbbdev     queue_type q7(comp);
14451c0b2f7Stbbdev     q7.assign(vec.begin(), vec.end());
14551c0b2f7Stbbdev     examine(q7, vec_sorted);
14651c0b2f7Stbbdev 
14751c0b2f7Stbbdev     queue_type q8(comp, alloc);
14851c0b2f7Stbbdev     q8.assign(vec.begin(), vec.end());
14951c0b2f7Stbbdev     examine(q8, vec_sorted);
15051c0b2f7Stbbdev 
15151c0b2f7Stbbdev     // Constructor from the initial capacity, comparator and allocator
15251c0b2f7Stbbdev     queue_type q9(100);
15351c0b2f7Stbbdev     q9.assign(vec.begin(), vec.end());
15451c0b2f7Stbbdev     examine(q9, vec_sorted);
15551c0b2f7Stbbdev 
15651c0b2f7Stbbdev     queue_type q10(100, comp);
15751c0b2f7Stbbdev     q10.assign(vec.begin(), vec.end());
15851c0b2f7Stbbdev     examine(q10, vec_sorted);
15951c0b2f7Stbbdev 
16051c0b2f7Stbbdev     queue_type q11(100, alloc);
16151c0b2f7Stbbdev     q11.assign(vec.begin(), vec.end());
16251c0b2f7Stbbdev     examine(q11, vec_sorted);
16351c0b2f7Stbbdev 
16451c0b2f7Stbbdev     // Constructor from the half-open interval, compare and allocator object
16551c0b2f7Stbbdev     queue_type q12(vec.begin(), vec.end(), comp);
16651c0b2f7Stbbdev     examine(q12, vec_sorted);
16751c0b2f7Stbbdev 
16851c0b2f7Stbbdev     queue_type q13(vec.begin(), vec.end(), alloc);
16951c0b2f7Stbbdev     examine(q13, vec_sorted);
17051c0b2f7Stbbdev 
17151c0b2f7Stbbdev     // Constructor from the std::initializer_list from the half-open interval, compare and allocator object
17251c0b2f7Stbbdev     queue_type q14({vec[0], vec[1], vec[2]}, comp);
17351c0b2f7Stbbdev     for (auto it = vec.begin() + 3; it != vec.end(); ++it)
17451c0b2f7Stbbdev         q14.push(*it);
17551c0b2f7Stbbdev     examine(q14, vec_sorted);
17651c0b2f7Stbbdev 
17751c0b2f7Stbbdev     queue_type q15({vec[0], vec[1], vec[2]}, alloc);
17851c0b2f7Stbbdev     for (auto it = vec.begin() + 3; it != vec.end(); ++it)
17951c0b2f7Stbbdev         q15.push(*it);
18051c0b2f7Stbbdev     examine(q15, vec_sorted);
18151c0b2f7Stbbdev }
18251c0b2f7Stbbdev 
18351c0b2f7Stbbdev template <typename ValueType>
type_tester(const std::vector<ValueType> & vec)18451c0b2f7Stbbdev void type_tester( const std::vector<ValueType>& vec ) {
18551c0b2f7Stbbdev     type_tester(vec, std::less<ValueType>{});
18651c0b2f7Stbbdev }
18751c0b2f7Stbbdev 
18851c0b2f7Stbbdev struct LessForSmartPointers {
18951c0b2f7Stbbdev     template <typename T>
operatorLessForSmartPointers19051c0b2f7Stbbdev     bool operator()( const T& t1, const T& t2 ) {
19151c0b2f7Stbbdev         return *t1 < *t2;
19251c0b2f7Stbbdev     }
19351c0b2f7Stbbdev 
19451c0b2f7Stbbdev     template <typename T>
operatorLessForSmartPointers19551c0b2f7Stbbdev     bool operator()( const std::weak_ptr<T>& t1, const std::weak_ptr<T>& t2 ) {
19651c0b2f7Stbbdev         return *t1.lock().get() < *t2.lock().get();
19751c0b2f7Stbbdev     }
19851c0b2f7Stbbdev }; // struct LessForSmartPointers
19951c0b2f7Stbbdev 
20051c0b2f7Stbbdev template <typename T>
type_tester_unique_ptr(const std::vector<T> & vec)20151c0b2f7Stbbdev void type_tester_unique_ptr( const std::vector<T>& vec ) {
20251c0b2f7Stbbdev     REQUIRE_MESSAGE(vec.size() >= 5, "Array should have at least 5 elements");
20351c0b2f7Stbbdev 
20451c0b2f7Stbbdev     using value_type = std::unique_ptr<T>;
20551c0b2f7Stbbdev     using queue_type = tbb::concurrent_priority_queue<value_type, LessForSmartPointers>;
20651c0b2f7Stbbdev 
20751c0b2f7Stbbdev     std::vector<value_type> vec_sorted;
20851c0b2f7Stbbdev     for (auto& item : vec) {
20951c0b2f7Stbbdev         vec_sorted.push_back(value_type(new T(item)));
21051c0b2f7Stbbdev     }
21151c0b2f7Stbbdev     std::sort(vec_sorted.begin(), vec_sorted.end(), LessForSmartPointers{});
21251c0b2f7Stbbdev 
21351c0b2f7Stbbdev     queue_type q1, q1_copy;
21451c0b2f7Stbbdev     for (auto& item : vec) {
21551c0b2f7Stbbdev         q1.push(value_type(new T(item)));
21651c0b2f7Stbbdev         q1_copy.push(value_type(new T(item)));
21751c0b2f7Stbbdev     }
21851c0b2f7Stbbdev     examine</*HasCopyCtor=*/false>(q1, q1_copy, vec_sorted);
21951c0b2f7Stbbdev 
22051c0b2f7Stbbdev     queue_type q3_copy;
22151c0b2f7Stbbdev     q1.clear();
22251c0b2f7Stbbdev 
22351c0b2f7Stbbdev     for (auto& item : vec) {
22451c0b2f7Stbbdev         q1.emplace(new T(item));
22551c0b2f7Stbbdev     }
22651c0b2f7Stbbdev 
22751c0b2f7Stbbdev     queue_type q3(std::move(q1));
22851c0b2f7Stbbdev     examine</*HasCopyCtor=*/false>(q3, q3_copy, vec_sorted);
22951c0b2f7Stbbdev }
23051c0b2f7Stbbdev 
23151c0b2f7Stbbdev const std::size_t MAX_ITER = 10000;
23251c0b2f7Stbbdev const std::size_t push_selector_variants = 3; // push, push rvalue and emplace
23351c0b2f7Stbbdev 
23451c0b2f7Stbbdev template <typename Q, typename E>
push_selector(Q & q,E e,std::size_t i)23551c0b2f7Stbbdev void push_selector(Q& q, E e, std::size_t i) {
23651c0b2f7Stbbdev     switch(i % push_selector_variants) {
23751c0b2f7Stbbdev     case 0: q.push(e); break;
23851c0b2f7Stbbdev     case 1: q.push(std::move(e)); break;
23951c0b2f7Stbbdev     case 2: q.emplace(e); break;
24051c0b2f7Stbbdev     }
24151c0b2f7Stbbdev }
24251c0b2f7Stbbdev 
24351c0b2f7Stbbdev static std::atomic<std::size_t> counter;
24451c0b2f7Stbbdev 
24551c0b2f7Stbbdev template <typename T, typename C>
24651c0b2f7Stbbdev class FillBody {
24751c0b2f7Stbbdev     std::size_t n_thread;
24851c0b2f7Stbbdev     T my_min, my_max;
24951c0b2f7Stbbdev     tbb::concurrent_priority_queue<T, C>* q;
25051c0b2f7Stbbdev public:
25151c0b2f7Stbbdev     FillBody( const FillBody& ) = delete;
25251c0b2f7Stbbdev     FillBody& operator=( const FillBody& ) = delete;
25351c0b2f7Stbbdev 
FillBody(std::size_t n,T max,T min,tbb::concurrent_priority_queue<T,C> * cpq)25451c0b2f7Stbbdev     FillBody( std::size_t n, T max, T min, tbb::concurrent_priority_queue<T, C>* cpq )
25551c0b2f7Stbbdev         : n_thread(n), my_min(min), my_max(max), q(cpq) {}
25651c0b2f7Stbbdev 
operator()25751c0b2f7Stbbdev     void operator()( const std::size_t thread_id ) const {
25851c0b2f7Stbbdev         T elem = my_min + T(int(thread_id));
25951c0b2f7Stbbdev         for (std::size_t i = 0; i < MAX_ITER; ++i) {
26051c0b2f7Stbbdev             // do some pushes
26151c0b2f7Stbbdev             push_selector(*q, elem, i);
26251c0b2f7Stbbdev             if (elem == my_max) elem = my_min;
26351c0b2f7Stbbdev             elem = elem + T(int(n_thread));
26451c0b2f7Stbbdev         }
26551c0b2f7Stbbdev     }
26651c0b2f7Stbbdev }; // class FillBody
26751c0b2f7Stbbdev 
26851c0b2f7Stbbdev template <typename T, typename C>
26951c0b2f7Stbbdev struct EmptyBody {
27051c0b2f7Stbbdev     T my_max;
27151c0b2f7Stbbdev     tbb::concurrent_priority_queue<T, C>* q;
27251c0b2f7Stbbdev     C less_than;
27351c0b2f7Stbbdev public:
27451c0b2f7Stbbdev     EmptyBody( const EmptyBody& ) = delete;
27551c0b2f7Stbbdev     EmptyBody& operator=( const EmptyBody& ) = delete;
27651c0b2f7Stbbdev 
EmptyBodyEmptyBody27751c0b2f7Stbbdev     EmptyBody( T max, tbb::concurrent_priority_queue<T, C>* cpq )
27851c0b2f7Stbbdev         : my_max(max), q(cpq) {}
27951c0b2f7Stbbdev 
operatorEmptyBody28051c0b2f7Stbbdev     void operator()( const std::size_t ) const {
28151c0b2f7Stbbdev         T elem(my_max), last;
28251c0b2f7Stbbdev         if (q->try_pop(last)) {
28351c0b2f7Stbbdev             ++counter;
28451c0b2f7Stbbdev             while(q->try_pop(elem)) {
28551c0b2f7Stbbdev                 REQUIRE_MESSAGE(!less_than(last, elem), "Failed pop/priority test in EmptyBody");
28651c0b2f7Stbbdev                 last = elem;
28751c0b2f7Stbbdev                 elem = my_max;
28851c0b2f7Stbbdev                 ++counter;
28951c0b2f7Stbbdev             }
29051c0b2f7Stbbdev         }
29151c0b2f7Stbbdev     }
29251c0b2f7Stbbdev }; // struct EmptyBody
29351c0b2f7Stbbdev 
29451c0b2f7Stbbdev template <typename T, typename C>
29551c0b2f7Stbbdev class FloggerBody {
29651c0b2f7Stbbdev     tbb::concurrent_priority_queue<T, C>* q;
29751c0b2f7Stbbdev public:
29851c0b2f7Stbbdev     FloggerBody( const FloggerBody& ) = delete;
29951c0b2f7Stbbdev     FloggerBody& operator=( const FloggerBody& ) = delete;
30051c0b2f7Stbbdev 
FloggerBody(tbb::concurrent_priority_queue<T,C> * cpq)30151c0b2f7Stbbdev     FloggerBody( tbb::concurrent_priority_queue<T, C>* cpq )
30251c0b2f7Stbbdev         : q(cpq) {}
30351c0b2f7Stbbdev 
operator()30451c0b2f7Stbbdev     void operator()( const std::size_t thread_id ) const {
30551c0b2f7Stbbdev         T elem = T(int(thread_id + 1));
30651c0b2f7Stbbdev         for (std::size_t i = 0; i < MAX_ITER; ++i) {
30751c0b2f7Stbbdev             push_selector(*q, elem, i);
30851c0b2f7Stbbdev             q->try_pop(elem);
30951c0b2f7Stbbdev         }
31051c0b2f7Stbbdev     }
31151c0b2f7Stbbdev }; // class FloggerBody
31251c0b2f7Stbbdev 
31351c0b2f7Stbbdev template <typename C, typename T>
test_parallel_push_pop(std::size_t n,T t_max,T t_min)31451c0b2f7Stbbdev void test_parallel_push_pop( std::size_t n, T t_max, T t_min ) {
31551c0b2f7Stbbdev     std::size_t qsize;
31651c0b2f7Stbbdev 
31751c0b2f7Stbbdev     tbb::concurrent_priority_queue<T, C> q(0);
31851c0b2f7Stbbdev     FillBody<T, C> filler(n, t_max, t_min, &q);
31951c0b2f7Stbbdev     EmptyBody<T, C> emptier(t_max, &q);
32051c0b2f7Stbbdev 
32151c0b2f7Stbbdev     counter = 0;
32251c0b2f7Stbbdev     utils::NativeParallelFor(n, filler);
32351c0b2f7Stbbdev 
32451c0b2f7Stbbdev     qsize = q.size();
32551c0b2f7Stbbdev     REQUIRE_MESSAGE(q.size() == n * MAX_ITER, "Failed concurrent push size test");
32651c0b2f7Stbbdev     REQUIRE_MESSAGE(!q.empty(), "Failed concurrent push empty test");
32751c0b2f7Stbbdev 
32851c0b2f7Stbbdev     utils::NativeParallelFor(n, emptier);
32951c0b2f7Stbbdev     REQUIRE_MESSAGE(counter == qsize, "Failed pop size test");
33051c0b2f7Stbbdev     REQUIRE_MESSAGE(q.size() == 0, "Failed pop empty test");
33151c0b2f7Stbbdev }
33251c0b2f7Stbbdev 
33351c0b2f7Stbbdev template <typename C, typename T>
test_flogger(std::size_t n)33451c0b2f7Stbbdev void test_flogger( std::size_t n ) {
33551c0b2f7Stbbdev     tbb::concurrent_priority_queue<T, C> q(0);
33651c0b2f7Stbbdev     utils::NativeParallelFor(n, FloggerBody<T, C>{&q});
33751c0b2f7Stbbdev     REQUIRE_MESSAGE(q.empty(), "Failed flogger empty test");
33851c0b2f7Stbbdev     REQUIRE_MESSAGE(!q.size(), "Failed flogger size test");
33951c0b2f7Stbbdev }
34051c0b2f7Stbbdev 
34151c0b2f7Stbbdev #endif // __TBB_test_common_concurrent_priority_queue_common_H
342