1 //
2 //  Copyright (c) 2018-2019, Cem Bassoy, [email protected]
3 //
4 //  Distributed under the Boost Software License, Version 1.0. (See
5 //  accompanying file LICENSE_1_0.txt or copy at
6 //  http://www.boost.org/LICENSE_1_0.txt)
7 //
8 //  The authors gratefully acknowledge the support of
9 //  Fraunhofer IOSB, Ettlingen, Germany
10 //
11 
12 #ifndef BOOST_UBLAS_TENSOR_MULTI_INDEX_UTILITY_HPP
13 #define BOOST_UBLAS_TENSOR_MULTI_INDEX_UTILITY_HPP
14 
15 
16 #include <tuple>
17 #include <type_traits>
18 
19 
20 namespace boost   {
21 namespace numeric {
22 namespace ublas   {
23 namespace detail  {
24 
25 
26 template<class ... index_types>
27 struct has_index_impl;
28 
29 template<class itype_left, class itype_right>
30 struct has_index_impl<itype_left, itype_right>
31 {
32 	static constexpr bool value = itype_left::value == itype_right::value;
33 };
34 
35 template<class itype_left>
36 struct has_index_impl <itype_left, std::tuple<> >
37 {
38 	static constexpr bool value = false;
39 };
40 
41 template<class itype_left, class itype_right>
42 struct has_index_impl <itype_left, std::tuple<itype_right> >
43 {
44 	static constexpr bool value = has_index_impl<itype_left,itype_right>::value;
45 };
46 
47 template<class itype_left, class itype_right, class ... index_types>
48 struct has_index_impl <itype_left, std::tuple<itype_right, index_types...> >
49 {
50 	using next_type = has_index_impl<itype_left, std::tuple<index_types...>>;
51 	static constexpr bool value = has_index_impl<itype_left,itype_right>::value || next_type::value;
52 };
53 } // namespace detail
54 
55 
56 
57 /** @brief has_index is true if index occurs once or more in a multi-index
58  *
59  * @note a multi-index represents as tuple of single indexes of type boost::numeric::ublas::index::index_type
60  *
61  * @code auto has_index_value = has_index<index_type<1>, std::tuple<index_type<2>,index_type<1>> >::value; @endcode
62  *
63  * @tparam index_type type of index
64  * @tparam tuple_type type of std::tuple representing a multi-index
65 */
66 template<class index_type, class tuple_type>
67 struct has_index
68 {
69 	static constexpr bool value = detail::has_index_impl<std::decay_t<index_type>,std::decay_t<tuple_type>>::value;
70 };
71 
72 } // namespace ublas
73 } // namespace numeric
74 } // namespace boost
75 
76 ////////////////////////////////////////////////
77 ////////////////////////////////////////////////
78 
79 namespace boost   {
80 namespace numeric {
81 namespace ublas   {
82 namespace detail  {
83 
84 
85 template<class ... index_types>
86 struct valid_multi_index_impl;
87 
88 template<>
89 struct valid_multi_index_impl<std::tuple<>>
90 {
91 	static constexpr bool value = true;
92 };
93 
94 template<class itype>
95 struct valid_multi_index_impl<std::tuple<itype>>
96 {
97 	static constexpr bool value = true;
98 };
99 
100 
101 template<class itype, class ... index_types>
102 struct valid_multi_index_impl<std::tuple<itype,index_types...>>
103 {
104 	using ttype     = std::tuple<index_types...>;
105 	using has_index_type = has_index<itype, ttype>;
106 
107 	static constexpr bool is_index_zero   = itype::value==0ul;
108 	static constexpr bool has_index_value = has_index_type::value && !is_index_zero;
109 	static constexpr bool value = !has_index_value && valid_multi_index_impl<ttype>::value;
110 };
111 } // namespace detail
112 
113 /** @brief valid_multi_index is true if indexes occur only once in a multi-index
114  *
115  * @note a multi-index represents as tuple of single indexes of type boost::numeric::ublas::index::index_type
116  *
117  * @code auto valid = valid_multi_index<  std::tuple<index_type<2>,index_type<1>>  >::value;
118  * @endcode
119  *
120  * @tparam tuple_type type of std::tuple representing a multi-index
121 */
122 template<class tupe_type>
123 struct valid_multi_index
124 {
125 	static constexpr bool value = detail::valid_multi_index_impl<std::decay_t<tupe_type>>::value;
126 };
127 
128 } // namespace ublas
129 } // namespace numeric
130 } // namespace boost
131 
132 ////////////////////////////////////////////////
133 ////////////////////////////////////////////////
134 
135 namespace boost   {
136 namespace numeric {
137 namespace ublas   {
138 namespace detail  {
139 
140 template<class ... index_types >
141 struct number_equal_indexes_impl;
142 
143 template<class ... itypes_right >
144 struct number_equal_indexes_impl < std::tuple<>, std::tuple<itypes_right...>>
145 {
146 	static constexpr unsigned value  = 0;
147 };
148 
149 template<class itype, class ... itypes_left, class ... itypes_right>
150 struct number_equal_indexes_impl < std::tuple<itype,itypes_left...>, std::tuple<itypes_right...>>
151 {
152 	using tuple_right = std::tuple<itypes_right...>;
153 	using has_index_type = has_index<itype, tuple_right>;
154 
155 	static constexpr bool is_index_zero   = itype::value==0ul;
156 	static constexpr bool has_index_value = has_index_type::value && !is_index_zero;
157 
158 	using next_type = number_equal_indexes_impl< std::tuple<itypes_left...>, tuple_right >;
159 	static constexpr unsigned v = has_index_value ? 1 : 0;
160 	static constexpr unsigned value  = v + next_type::value;
161 };
162 } // namespace detail
163 
164 
165 /** @brief number_equal_indexes contains the number of equal indexes of two multi-indexes
166  *
167  * @note a multi-index represents as tuple of single indexes of type boost::numeric::ublas::index::index_type
168  *
169  *
170  * @code auto num = number_equal_indexes<
171  *                        std::tuple<index_type<2>,index_type<1>>,
172  *                        std::tuple<index_type<1>,index_type<3>>  >::value;
173  * @endcode
174  *
175  * @tparam tuple_type_left  type of left std::tuple representing a multi-index
176  * @tparam tuple_type_right type of right std::tuple representing a multi-index
177 */
178 template<class tuple_left, class tuple_right>
179 struct number_equal_indexes
180 {
181 	static constexpr unsigned value  =
182 	    detail::number_equal_indexes_impl< std::decay_t<tuple_left>, std::decay_t<tuple_right>>::value;
183 };
184 
185 } // namespace ublas
186 } // namespace numeric
187 } // namespace boost
188 
189 
190 ////////////////////////////////////////////////
191 ////////////////////////////////////////////////
192 
193 namespace boost   {
194 namespace numeric {
195 namespace ublas   {
196 namespace detail  {
197 
198 
199 template<std::size_t r, std::size_t m, class itype, class ttype>
200 struct index_position_impl
201 {
202 	static constexpr auto is_same = std::is_same< std::decay_t<itype>, std::decay_t<std::tuple_element_t<r,ttype>> >::value;
203 	static constexpr auto value   = is_same ? r : index_position_impl<r+1,m,itype,ttype>::value;
204 };
205 
206 
207 
208 template<std::size_t m, class itype, class ttype>
209 struct index_position_impl < m, m, itype, ttype>
210 {
211 	static constexpr auto value = std::tuple_size<ttype>::value;
212 };
213 
214 } // namespace detail
215 
216 
217 
218 /** @brief index_position contains the zero-based index position of an index type within a multi-index
219  *
220  * @note a multi-index represents as tuple of single indexes of type boost::numeric::ublas::index::index_type
221  *
222  * @code auto num = index_position<
223  *                       index_type<1>,
224  *                       std::tuple<index_type<2>,index_type<1>>  >::value;
225  * @endcode
226  *
227  * @returns value returns 0 and N-1 if index_type is found, N otherwise where N is tuple_size_v<tuple_type>.
228  *
229  * @tparam index_type type of index
230  * @tparam tuple_type type of std::tuple that is searched for index
231 */
232 template<class index_type, class tuple_type>
233 struct index_position
234 {
235 	static constexpr auto value  = detail::index_position_impl<0ul,std::tuple_size<tuple_type>::value,std::decay_t<index_type>,std::decay_t<tuple_type>>::value;
236 };
237 
238 } // namespace ublas
239 } // namespace numeric
240 } // namespace boost
241 
242 ////////////////////////////////////////////////
243 ////////////////////////////////////////////////
244 
245 
246 namespace boost   {
247 namespace numeric {
248 namespace ublas   {
249 namespace detail  {
250 
251 template<std::size_t r, std::size_t m>
252 struct index_position_pairs_impl
253 {
254 	template<class array_type, class tuple_left, class tuple_right>
runboost::numeric::ublas::detail::index_position_pairs_impl255 	static constexpr void run(array_type& out, tuple_left const& lhs, tuple_right const& rhs, std::size_t p)
256 	{
257 		using index_type     = std::tuple_element_t<r-1,tuple_left>;
258 		using has_index_type = has_index<index_type, tuple_right>;
259 		using get_index_type = index_position<index_type,tuple_right>;
260 		using next_type      = index_position_pairs_impl<r+1,m>;
261 		if constexpr ( has_index_type::value && index_type::value != 0)
262 		    out[p++] = std::make_pair(r-1,get_index_type::value);
263 		next_type::run( out, lhs, rhs, p );
264 	}
265 };
266 
267 template<std::size_t m>
268 struct index_position_pairs_impl<m,m>
269 {
270 	template<class array_type, class tuple_left, class tuple_right>
runboost::numeric::ublas::detail::index_position_pairs_impl271 	static constexpr void run(array_type& out, tuple_left const& , tuple_right const& , std::size_t p)
272 	{
273 		using index_type     = std::tuple_element_t<m-1,tuple_left>;
274 		using has_index_type = has_index<index_type, tuple_right>;
275 		using get_index_type = index_position<index_type, tuple_right>;
276 		if constexpr ( has_index_type::value && index_type::value != 0 )
277 		    out[p] = std::make_pair(m-1,get_index_type::value);
278 	}
279 };
280 
281 template<std::size_t r>
282 struct index_position_pairs_impl<r,0>
283 {
284 	template<class array_type, class tuple_left, class tuple_right>
runboost::numeric::ublas::detail::index_position_pairs_impl285 	static constexpr void run(array_type&, tuple_left const& , tuple_right const& , std::size_t)
286 	{}
287 };
288 
289 
290 } // namespace detail
291 
292 
293 /** @brief index_position_pairs returns zero-based index positions of matching indexes of two multi-indexes
294  *
295  * @note a multi-index represents as tuple of single indexes of type boost::numeric::ublas::index::index_type
296  *
297  * @code auto pairs = index_position_pairs(std::make_tuple(_a,_b), std::make_tuple(_b,_c));
298  * @endcode
299  *
300  * @returns a std::array instance containing index position pairs of type std::pair<std::size_t, std::size_t>.
301  *
302  * @param lhs left std::tuple instance representing a multi-index
303  * @param rhs right std::tuple instance representing a multi-index
304 */
305 template<class tuple_left, class tuple_right>
index_position_pairs(tuple_left const & lhs,tuple_right const & rhs)306 auto index_position_pairs(tuple_left const& lhs, tuple_right const& rhs)
307 {
308 	using pair_type = std::pair<std::size_t,std::size_t>;
309 	constexpr auto m = std::tuple_size<tuple_left >::value;
310 	constexpr auto p = number_equal_indexes<tuple_left, tuple_right>::value;
311 	auto array = std::array<pair_type,p>{};
312 	detail::index_position_pairs_impl<1,m>::run(array, lhs, rhs,0);
313 	return array;
314 }
315 
316 } // namespace ublas
317 } // namespace numeric
318 } // namespace boost
319 
320 ////////////////////////////
321 ////////////////////////////
322 ////////////////////////////
323 ////////////////////////////
324 
325 
326 namespace boost   {
327 namespace numeric {
328 namespace ublas   {
329 namespace detail  {
330 
331 template<class array_type, std::size_t ... R>
array_to_vector_impl(array_type const & array,std::index_sequence<R...>)332 constexpr auto array_to_vector_impl( array_type const& array, std::index_sequence<R...> )
333 {
334 	return std::make_pair(
335 	      std::vector<std::size_t>{std::get<0>( std::get<R>(array) )+1 ...} ,
336 	      std::vector<std::size_t>{std::get<1>( std::get<R>(array) )+1 ...} );
337 }
338 
339 } // namespace detail
340 
341 
342 /** @brief array_to_vector converts a std::array of zero-based index position pairs into two std::vector of one-based index positions
343  *
344  * @code auto two_vectors = array_to_vector(std::make_array ( std::make_pair(1,2), std::make_pair(3,4) ) ) ;
345  * @endcode
346  *
347  * @returns two std::vector of one-based index positions
348  *
349  * @param array std::array of zero-based index position pairs
350 */
351 template<class pair_type, std::size_t N>
array_to_vector(std::array<pair_type,N> const & array)352 constexpr auto array_to_vector( std::array<pair_type,N> const& array)
353 {
354 	constexpr auto sequence = std::make_index_sequence<N>{};
355 	return detail::array_to_vector_impl( array, sequence );
356 }
357 
358 
359 } // namespace ublas
360 } // namespace numeric
361 } // namespace boost
362 
363 
364 #endif // _BOOST_UBLAS_TENSOR_MULTI_INDEX_UTILITY_HPP_
365