O-RAN E Maintenance Release contribution for ODULOW
[o-du/phy.git] / fhi_lib / test / common / common.hpp
1 /*******************************************************************************
2  *
3  * Copyright (c) 2020 Intel.
4  * 
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  * 
9  * http://www.apache.org/licenses/LICENSE-2.0
10  * 
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  * 
17  *
18  *******************************************************************************/
19
20 /* This is the new utility file for all tests, all new common functionality has to go here.
21    When contributing to the common.hpp please focus on readability and maintainability rather than
22    execution time. */
23 #ifndef XRANLIB_COMMON_HPP
24 #define XRANLIB_COMMON_HPP
25
26 /* Disable warnings generated by JSON parser */
27 #pragma warning(disable : 191)
28 #pragma warning(disable : 186)
29 #pragma warning(disable : 192)
30
31 #include <exception>
32 #include <random>
33 #include <string>
34 #include <utility>
35 #include <vector>
36
37 #include <immintrin.h>
38 #include <malloc.h>
39
40 #define _BBLIB_DPDK_
41
42 #ifdef _BBLIB_DPDK_
43 #include <rte_config.h>
44 #include <rte_malloc.h>
45 #endif
46
47 #include "gtest/gtest.h"
48
49 #include "common_typedef_xran.h"
50
51 #include "json.hpp"
52
53 using json = nlohmann::json;
54
55 #define ASSERT_ARRAY_NEAR(reference, actual, size, precision) \
56         assert_array_near(reference, actual, size, precision)
57
58 #define ASSERT_ARRAY_EQ(reference, actual, size) \
59         assert_array_eq(reference, actual, size)
60
61 #define ASSERT_AVG_GREATER_COMPLEX(reference, actual, size, precision) \
62         assert_avg_greater_complex(reference, actual, size, precision)
63
64 struct BenchmarkParameters
65 {
66     static long repetition;
67     static long loop;
68     static unsigned cpu_id;
69 };
70
71 struct missing_config_file_exception : public std::exception
72 {
73     const char * what () const throw () override {
74         return "JSON file cannot be opened!";
75     }
76 };
77
78 struct reading_input_file_exception : public std::exception
79 {
80     const char * what () const throw () override {
81         return "Input file cannot be read!";
82     }
83 };
84
85 /*!
86     \brief Attach current process to the selected core.
87     \param [in] cpu Core number.
88     \return 0 on success, -1 otherwise.
89 */
90 int bind_to_cpu(const unsigned cpu);
91
92 /*!
93     \brief Calculate the mean and variance from the result of the run_benchmark.
94     \param [in] values Vector with result values.
95     \return std::pair where the first element is mean and the second one is standard deviation.
96     \note It's not a general mean/stddev function it only works properly when feed with data from
97           the benchmark function.
98 */
99 std::pair<double, double> calculate_statistics(const std::vector<long> values);
100
101 /*!
102     \brief For a given number return sequence of number from 0 to number - 1.
103     \param [in] number Positive integer value.
104     \return Vector with the sorted integer numbers between 0 and number - 1.
105 */
106 std::vector<unsigned> get_sequence(const unsigned number);
107
108 /*!
109     \brief Read JSON from the given file.
110     \param [in] filename name of the .json file.
111     \return JSON object with data.
112     \throws missing_config_file_exception when file cannot be opened.
113 */
114 json read_json_from_file(const std::string &filename);
115
116 /*!
117     \brief Read binary data from the file.
118     \param [in] filename name of the binary file.
119     \return Pointer to the allocated memory with data from the file.
120     \throws std::runtime_error when memory cannot be allocated.
121 */
122 char* read_data_to_aligned_array(const std::string &filename);
123
124 /*!
125     \brief Measure the TSC on the machine
126     \return Number of ticks per us
127 */
128 unsigned long tsc_recovery();
129
130 /*!
131     \brief Return the current value of the TSC
132     \return Current TSC value
133 */
134 unsigned long tsc_tick();
135
136 /*!
137     \class KernelTests
138
139     Each test class has to inherit from KernelTests class as it provides GTest support and does a lot
140     of setup (including JSON) an provides useful methods to operate on loaded JSON file.
141     Unfortunately GTest is limited in the way that all TEST_P within the class are called for all
142     cases/parameters, but we usually want two different data sets for functional and performance
143     tests (or maybe other types of tests). Because of that to use different data sets we need to
144     create separate classes, hence performance and functional test are in separate classes. it adds
145     an extra overhead, but adds much more flexibility. init_test(...) is used to select data set from
146     the JSON file.
147
148     Important note on the JSON file structure. Top JSON object can have as many section (JSON
149     objects) as needed, but each have to have a distinct name that is used by init_test. Then
150     each section must contain an array of objects (test cases) where each object has a name,
151     parameters and references. Everything inside parameters and references can be completely custom
152     as it's loaded by get_input/reference_parameter function. JSON values can be either literal
153     values, e.g. 1, 0.001, 5e-05, etc. or filename. Depends on the get type test framework can either
154     read the value or load data from the file - and it happens automatically (*pff* MAGIC!).
155 */
156 class KernelTests : public testing::TestWithParam<unsigned>
157 {
158 public:
159     static json conf;
160     static std::string test_type;
161
162     static void SetUpTestCase()
163     {
164         test_type = "None";
165
166         try
167         {
168             conf = read_json_from_file("conf.json");
169         }
170         catch(missing_config_file_exception &e)
171         {
172             std::cout << "[----------] SetUpTestCase failed: " << e.what() << std::endl;
173             exit(-1);
174         }
175
176         tsc = tsc_recovery();
177
178         if(!tsc)
179         {
180             std::cout << "[----------] SetUpTestCase failed: TSC recovery failed" << std::endl;
181             exit(-1);
182         }
183     }
184
185     static void TearDownTestCase()
186     {
187         /* Free resources - nothing to free at the moment */
188     }
189
190     static unsigned get_number_of_cases(const std::string &type)
191     {
192         try
193         {
194             json json_data = read_json_from_file("conf.json");
195
196             return json_data[type].size();
197         }
198         catch(missing_config_file_exception &e)
199         {
200             std::cout << "[----------] get_number_of_cases failed: " << e.what() << std::endl;
201
202             exit(-1);
203         }
204         catch(std::domain_error &e)
205         {
206             std::cout << "[----------] get_number_of_cases failed: " << e.what() << std::endl;
207             std::cout << "[----------] Use a default value: 0" << std::endl;
208
209             return 0;
210         }
211     }
212
213 protected:
214     double division_factor = 1.0;
215     std::string result_units = "None";
216     int parallelization_factor = 1;
217
218     /*!
219         \brief Set division factor
220         \param [in] factor Division factor that divides mean and standard deviation.
221     */
222     void set_division_factor(const double factor)
223     {
224         division_factor = factor;
225     }
226
227     /*!
228         \brief Set reults units
229         \param [in] units Units that are displayed in the report.
230     */
231     void set_results_units(const std::string &units)
232     {
233         result_units = units;
234     }
235
236     /*!
237         \brief Set size of processed data
238         \param [in] size Size of processed data used to calculate module throughput.
239     */
240     void set_parallelization_factor(const int factor)
241     {
242         parallelization_factor = factor;
243     }
244
245     /*!
246         \brief Run performance test case for a given function.
247         \param [in] isa Used Instruction Set.
248         \param [in] module_name name of the tested kernel.
249         \param [in] function function to be tested.
250         \param [in] args function's arguments.
251     */
252     template <typename F, typename ... Args>
253     void performance(const std::string &isa, const std::string &module_name, F function,
254                      Args ... args) {
255         ASSERT_EQ(0, bind_to_cpu(BenchmarkParameters::cpu_id)) << "Failed to bind to cpu!";
256
257         const auto result = run_benchmark(function, args ...);
258         const auto scaled_mean = result.first / division_factor / tsc;
259         const auto scaled_stddev = result.second / division_factor / tsc;
260
261         print_and_store_results(isa, get_case_name(), module_name, get_case_name(), result_units,
262                                 parallelization_factor, scaled_mean, scaled_stddev);
263     }
264
265     /*!
266         \brief Print unique test description to the results xml file
267         \param [in] isa Used Instruction Set.
268         \param [in] module_name name of the tested kernel.
269         \param [in] function function to be tested.
270     */
271     void print_test_description(const std::string &isa, const std::string &module_name) {
272         print_and_store_results(isa, get_case_name(), module_name, get_case_name(), result_units,
273                                 parallelization_factor, 0, 0);
274     }
275
276     //! @{
277     /*!
278         \brief Load selected data from a JSON object.
279          get_input_parameter loads data from parameters section of the test case in JSON file and
280          get_reference_parameter does the same thing for references section.
281
282          Get parameter function uses template type to figure out how to load parameters. If type
283          is NOT a pointer it'll load value directly from the JSON. Otherwise path to the test
284          vector is expected and function will allocate memory, load data from the binary file to
285          this memory location and return pointer to it. For example in here we request to load
286          pointer to float so llrs filed is expected to be
287          a path to the binary file.
288     */
289     template <typename T>
290     T get_input_parameter(const std::string &parameter_name)
291     {
292         try
293         {
294             return get_parameter<T>("parameters", parameter_name);
295         }
296         catch (std::domain_error &e)
297         {
298             std::cout << "[----------] get_input_parameter (" << parameter_name
299                       << ") failed: " << e.what()
300                       << ". Did you mispell the parameter name?" << std::endl;
301             throw;
302         }
303         catch(reading_input_file_exception &e)
304         {
305             std::cout << "[----------] get_input_parameter (" << parameter_name
306                       << ") failed: " << e.what() << std::endl;
307             throw;
308         }
309     }
310
311     template <typename T>
312     T get_input_parameter(const std::string &subsection_name, const std::string &parameter_name)
313     {
314         try
315         {
316             return get_parameter<T>("parameters", subsection_name, parameter_name);
317         }
318         catch (std::domain_error &e)
319         {
320             std::cout << "[----------] get_input_parameter (" << subsection_name << "." << parameter_name
321                       << ") failed: " << e.what()
322                       << ". Did you mispell the parameter name?" << std::endl;
323             throw;
324         }
325         catch(reading_input_file_exception &e)
326         {
327             std::cout << "[----------] get_input_parameter (" << subsection_name << "." << parameter_name
328                       << ") failed: " << e.what() << std::endl;
329             throw;
330         }
331     }
332     
333     template <typename T>
334     T get_input_parameter(const std::string &subsection_name, const int index, const std::string &parameter_name)
335     {
336         try
337         {
338             return get_parameter<T>("parameters", subsection_name, index, parameter_name);
339         }
340         catch (std::domain_error &e)
341         {
342             std::cout << "[----------] get_input_parameter (" << subsection_name << "[" << index << "]." << parameter_name
343                       << ") failed: " << e.what()
344                       << ". Did you mispell the parameter name?" << std::endl;
345             throw;
346         }
347         catch(reading_input_file_exception &e)
348         {
349             std::cout << "[----------] get_input_parameter (" << subsection_name << "[" << index << "]." << parameter_name
350                       << ") failed: " << e.what() << std::endl;
351             throw;
352         }
353     }
354     int get_input_parameter_size(const std::string &subsection_name, const std::string &parameter_name)
355     {
356         try
357         {
358             auto array_size = conf[test_type][GetParam()]["parameters"][subsection_name][parameter_name].size();
359             return (array_size);
360         }
361         catch (std::domain_error &e)
362         {
363             std::cout << "[----------] get_input_parameter_size (" << subsection_name << "." << parameter_name
364                       << ") failed: " << e.what()
365                       << ". Did you mispell the parameter name?" << std::endl;
366             return (-1);
367         }
368         catch(reading_input_file_exception &e)
369         {
370             std::cout << "[----------] get_input_parameter_size (" << subsection_name << "." << parameter_name
371                       << ") failed: " << e.what() << std::endl;
372             throw;
373         }
374     }
375     int get_input_subsection_size(const std::string &subsection_name)
376     {
377         try
378         {
379             auto array_size = conf[test_type][GetParam()]["parameters"][subsection_name].size();
380             return (array_size);
381         }
382         catch (std::domain_error &e)
383         {
384             std::cout << "[----------] get_input_subsection_size (" << subsection_name 
385                       << ") failed: " << e.what()
386                       << ". Did you mispell the subsection name?" << std::endl;
387             return (-1);
388         }
389         catch(reading_input_file_exception &e)
390         {
391             std::cout << "[----------] get_input_subsection_size (" << subsection_name
392                       << ") failed: " << e.what() << std::endl;
393             throw;
394         }
395     }
396
397     template <typename T>
398     T get_reference_parameter(const std::string &parameter_name)
399     {
400         try
401         {
402             return get_parameter<T>("references", parameter_name);
403         }
404         catch (std::domain_error &e)
405         {
406             std::cout << "[----------] get_reference_parameter (" << parameter_name
407                       << ") failed: " << e.what()
408                       << ". Did you mispell the parameter name?" << std::endl;
409             throw;
410         }
411         catch(reading_input_file_exception &e)
412         {
413             std::cout << "[----------] get_reference_parameter (" << parameter_name
414                       << ") failed: " << e.what() << std::endl;
415             throw;
416         }
417     }
418     //! @}
419
420     /*!
421         \brief Get name of the test case from JSON file.
422         \return Test'ss case name or a default name if name field is missing.
423     */
424     const std::string get_case_name()
425     {
426         try
427         {
428             return conf[test_type][GetParam()]["name"];
429         }
430         catch (std::domain_error &e)
431         {
432             std::cout << "[----------] get_case_name failed: " << e.what()
433                       << ". Did you specify a test name in JSON?" << std::endl;
434             std::cout << "[----------] Using a default name instead" << std::endl;
435
436             return "Default test name";
437         }
438     }
439
440     /*!
441         \brief Defines section in the conf.json that is used to load parameters from.
442         \param [in] type Name of the section in the JSON file.
443     */
444     void init_test(const std::string &type)
445     {
446         test_type = type;
447         const std::string name = get_case_name();
448         std::cout << "[----------] Test case: " << name << std::endl;
449     }
450
451 private:
452     static unsigned long tsc;
453
454     template<typename T>
455     struct data_reader {
456         static T read_parameter(const int index, const std::string &type,
457                                 const std::string &parameter_name)
458         {
459             return conf[test_type][index][type][parameter_name];
460         }
461     };
462
463     template<typename T>
464     struct data_reader<std::vector<T>> {
465         static std::vector<T> read_parameter(const int index, const std::string &type,
466                                              const std::string &parameter_name)
467         {
468             auto array_size = conf[test_type][index][type][parameter_name].size();
469
470             std::vector<T> result(array_size);
471
472             for(unsigned number = 0; number < array_size; number++)
473                 result.at(number) = conf[test_type][index][type][parameter_name][number];
474
475             return result;
476         }
477     };
478
479     template<typename T>
480     struct data_reader<T*> {
481         static T* read_parameter(const int index, const std::string &type,
482                                  const std::string &parameter_name)
483         {
484             return (T*) read_data_to_aligned_array(conf[test_type][index][type][parameter_name]);
485         }
486     };
487
488     template <typename T>
489     T get_parameter(const std::string &type, const std::string &parameter_name)
490     {
491         return data_reader<T>::read_parameter(GetParam(), type, parameter_name);
492     }
493
494     template<typename T>
495     struct data_reader2 {
496         static T read_parameter(const int index, const std::string &type,
497                                 const std::string &subsection_name,
498                                 const std::string &parameter_name)
499         {
500             return conf[test_type][index][type][subsection_name][parameter_name];
501         }
502     };
503
504     template<typename T>
505     struct data_reader2<std::vector<T>> {
506         static std::vector<T> read_parameter(const int index, const std::string &type,
507                                              const std::string &subsection_name,
508                                              const std::string &parameter_name)
509         {
510             auto array_size = conf[test_type][index][type][subsection_name][parameter_name].size();
511
512             std::vector<T> result(array_size);
513
514             for(unsigned number = 0; number < array_size; number++)
515                 result.at(number) = conf[test_type][index][type][subsection_name][parameter_name][number];
516
517             return result;
518         }
519     };
520
521     template<typename T>
522     struct data_reader2<T*> {
523         static T* read_parameter(const int index, const std::string &type,
524                                  const std::string &subsection_name,
525                                  const std::string &parameter_name)
526         {
527             return (T*) read_data_to_aligned_array(conf[test_type][index][type][subsection_name][parameter_name]);
528         }
529     };
530     template <typename T>
531     T get_parameter(const std::string &type, const std::string &subsection_name, const std::string &parameter_name)
532     {
533         return data_reader2<T>::read_parameter(GetParam(), type, subsection_name, parameter_name);
534     }
535
536     template<typename T>
537     struct data_reader3 {
538         static T read_parameter(const int index, const std::string &type,
539                                 const std::string &subsection_name,
540                                 const int subindex,
541                                 const std::string &parameter_name)
542         {
543             return conf[test_type][index][type][subsection_name][subindex][parameter_name];
544         }
545     };
546
547     template<typename T>
548     struct data_reader3<std::vector<T>> {
549         static std::vector<T> read_parameter(const int index, const std::string &type,
550                                              const std::string &subsection_name,
551                                              const int subindex,
552                                              const std::string &parameter_name)
553         {
554             auto array_size = conf[test_type][index][type][subsection_name][subindex][parameter_name].size();
555
556             std::vector<T> result(array_size);
557
558             for(unsigned number = 0; number < array_size; number++)
559                 result.at(number) = conf[test_type][index][type][subsection_name][subindex][parameter_name][number];
560
561             return result;
562         }
563     };
564
565     template<typename T>
566     struct data_reader3<T*> {
567         static T* read_parameter(const int index, const std::string &type,
568                                  const std::string &subsection_name,
569                                  const int subindex,
570                                  const std::string &parameter_name)
571         {
572             return (T*) read_data_to_aligned_array(conf[test_type][index][type][subsection_name][subindex][parameter_name]);
573         }
574     };
575     template <typename T>
576     T get_parameter(const std::string &type, const std::string &subsection_name, const int subindex, const std::string &parameter_name)
577     {
578         return data_reader3<T>::read_parameter(GetParam(), type, subsection_name, subindex, parameter_name);
579     }
580
581     void print_and_store_results(const std::string &isa,
582                                  const std::string &parameters,
583                                  const std::string &module_name,
584                                  const std::string &test_name,
585                                  const std::string &unit,
586                                  const int para_factor,
587                                  const double mean,
588                                  const double stddev);
589 };
590
591 /*!
592     \brief Run the given function and return the mean run time and stddev.
593     \param [in] function Function to benchmark.
594     \param [in] args Function's arguments.
595     \return std::pair where the first element is mean and the second one is standard deviation.
596 */
597 template <typename F, typename ... Args>
598 std::pair<double, double> run_benchmark(F function, Args ... args)
599 {
600     std::vector<long> results((unsigned long) BenchmarkParameters::repetition);
601
602     for(unsigned int outer_loop = 0; outer_loop < BenchmarkParameters::repetition; outer_loop++) {
603         const auto start_time =  __rdtsc();
604         for (unsigned int inner_loop = 0; inner_loop < BenchmarkParameters::loop; inner_loop++) {
605                 function(args ...);
606         }
607         const auto end_time = __rdtsc();
608         results.push_back(end_time - start_time);
609     }
610
611     return calculate_statistics(results);
612 };
613
614 /*!
615     \brief Assert elements of two arrays. It calls ASSERT_EQ for each element of the array.
616     \param [in] reference Array with reference values.
617     \param [in] actual Array with the actual output.
618     \param [in] size Size of the array.
619 */
620 template <typename T>
621 void assert_array_eq(const T* reference, const T* actual, const int size)
622 {
623     for(int index = 0; index < size ; index++)
624     {
625         ASSERT_EQ(reference[index], actual[index])
626                           <<"The wrong number is index: "<< index;
627     }
628 }
629
630 /*!
631     \brief Assert elements of two arrays. It calls ASSERT_NEAR for each element of the array.
632     \param [in] reference Array with reference values.
633     \param [in] actual Array with the actual output.
634     \param [in] size Size of the array.
635     \param [in] precision Precision fo the comparision used by ASSERT_NEAR.
636 */
637 template <typename T>
638 void assert_array_near(const T* reference, const T* actual, const int size, const double precision)
639 {
640     for(int index = 0; index < size ; index++)
641     {
642         ASSERT_NEAR(reference[index], actual[index], precision)
643                                 <<"The wrong number is index: "<< index;
644     }
645 }
646
647 template <>
648 void assert_array_near<complex_float>(const complex_float* reference, const complex_float* actual, const int size, const double precision)
649 {
650     for(int index = 0; index < size ; index++)
651     {
652         ASSERT_NEAR(reference[index].re, actual[index].re, precision)
653                              <<"The wrong number is RE, index: "<< index;
654         ASSERT_NEAR(reference[index].im, actual[index].im, precision)
655                              <<"The wrong number is IM, index: "<< index;
656     }
657 }
658
659 /*!
660     \brief Assert average diff of two arrays. It calls ASSERT_GT to check the average.
661     \param [in] reference Array with reference values, interleaved IQ inputs.
662     \param [in] actual Array with the actual output, interleaved IQ inputs.
663     \param [in] size Size of the array, based on complex inputs.
664     \param [in] precision Precision for the comparison used by ASSERT_GT.
665 */
666 template<typename T>
667 void assert_avg_greater_complex(const T* reference, const T* actual, const int size, const double precision)
668 {
669     float mseDB, MSE;
670     double avgMSEDB = 0.0;
671     for (int index = 0; index < size; index++) {
672         T refReal = reference[2*index];
673         T refImag = reference[(2*index)+1];
674         T resReal = actual[2*index];
675         T resImag = actual[(2*index)+1];
676
677         T errReal = resReal - refReal;
678         T errIm = resImag - refImag;
679
680         /* For some unit tests, e.g. PUCCH deomdulation, the expected output is 0. To avoid a
681            divide by zero error, check the reference results to determine if the expected result
682            is 0 and, if so, add a 1 to the division. */
683         if (refReal == 0 && refImag == 0)
684             MSE = (float)(errReal*errReal + errIm*errIm)/(float)(refReal*refReal + refImag*refImag + 1);
685         else
686             MSE = (float)(errReal*errReal + errIm*errIm)/(float)(refReal*refReal + refImag*refImag);
687
688         if(MSE == 0)
689             mseDB = (float)(-100.0);
690         else
691             mseDB = (float)(10.0) * (float)log10(MSE);
692
693         avgMSEDB += (double)mseDB;
694         }
695
696         avgMSEDB /= size;
697
698         ASSERT_GT(precision, avgMSEDB);
699 }
700
701 /*!
702     \brief Allocates memory of the given size.
703
704     aligned_malloc is wrapper to functions that allocate memory:
705     'rte_malloc' from DPDK if hugepages are defined, 'memalign' otherwise.
706     Size is defined as a number of variables of given type e.g. floats, rather than bytes.
707     It hides sizeof(T) multiplication and cast hence makes things cleaner.
708
709     \param [in] size Size of the memory to allocate.
710     \param [in] alignment Bytes alignment of the allocated memory. If 0, the return is a pointer
711                 that is suitably aligned for any kind of variable (in the same manner as malloc()).
712                 Otherwise, the return is a pointer that is a multiple of align. In this case,
713                 it must be a power of two. (Minimum alignment is the cacheline size, i.e. 64-bytes)
714     \return Pointer to the allocated memory.
715 */
716 template <typename T>
717 T* aligned_malloc(const int size, const unsigned alignment)
718 {
719 #ifdef _BBLIB_DPDK_
720     return (T*) rte_malloc(NULL, sizeof(T) * size, alignment);
721 #else
722 #ifndef _WIN64
723     return (T*) memalign(alignment, sizeof(T) * size);
724 #else
725     return (T*)_aligned_malloc(sizeof(T)*size, alignment);
726 #endif
727 #endif
728 }
729
730 /*!
731     \brief Frees memory pointed by the given pointer.
732
733     aligned_free is a wrapper for functions that free memory allocated by
734     aligned_malloc: 'rte_free' from DPDK if hugepages are defined and 'free' otherwise.
735
736     \param [in] ptr Pointer to the allocated memory.
737 */
738 template <typename T>
739 void aligned_free(T* ptr)
740 {
741 #ifdef _BBLIB_DPDK_
742     rte_free((void*)ptr);
743 #else
744
745 #ifndef _WIN64
746     free((void*)ptr);
747 #else
748     _aligned_free((void *)ptr);
749 #endif
750 #endif
751 }
752
753 /*!
754     \brief generate random numbers.
755
756     It allocates memory and populate it with random numbers using C++11 default engine and
757     uniform real / int distribution (where lo_range <= x <up_range). Don't forget to free
758     allocated memory!
759
760     \param [in] size Size of the memory to be filled with random data.
761     \param [in] alignment Bytes alignment of the memory.
762     \param [in] distribution Distribuiton for random generator.
763     \return Pointer to the allocated memory with random data.
764 */
765 template <typename T, typename U>
766 T* generate_random_numbers(const long size, const unsigned alignment, U& distribution)
767 {
768     auto array = (T*) aligned_malloc<char>(size * sizeof(T), alignment);
769
770     std::random_device random_device;
771     std::default_random_engine generator(random_device());
772
773     for(long i = 0; i < size; i++)
774         array[i] = (T)distribution(generator);
775
776     return array;
777 }
778
779 /*!
780     \brief generate random data.
781
782     It allocates memory and populate it with random data using C++11 default engine and
783     uniform integer distribution (bytes not floats are uniformly distributed). Don't forget
784     to free allocated memory!
785
786     \param [in] size Size of the memory to be filled with random data.
787     \param [in] alignment Bytes alignment of the memory.
788     \return Pointer to the allocated memory with random data.
789 */
790 template <typename T>
791 T* generate_random_data(const long size, const unsigned alignment)
792 {
793     std::uniform_int_distribution<> random(0, 255);
794
795     return (T*)generate_random_numbers<char, std::uniform_int_distribution<>>(size * sizeof(T), alignment, random);
796 }
797
798 /*!
799     \brief generate integer random numbers.
800
801     It allocates memory and populate it with random numbers using C++11 default engine and
802     uniform integer distribution (where lo_range <= x < up_range). Don't forget
803     to free allocated memory! The result type generated by the generator should be one of
804     int types.
805
806     \param [in] size Size of the memory to be filled with random data.
807     \param [in] alignment Bytes alignment of the memory.
808     \param [in] lo_range Lower bound of range of values returned by random generator.
809     \param [in] up_range Upper bound of range of values returned by random generator.
810     \return Pointer to the allocated memory with random data.
811 */
812 template <typename T>
813 T* generate_random_int_numbers(const long size, const unsigned alignment, const T lo_range,
814                                const T up_range)
815 {
816     std::uniform_int_distribution<T> random(lo_range, up_range);
817
818     return generate_random_numbers<T, std::uniform_int_distribution<T>>(size, alignment, random);
819 }
820
821 /*!
822     \brief generate real random numbers.
823
824     It allocates memory and populate it with random numbers using C++11 default engine and
825     uniform real distribution (where lo_range <= x <up_range). Don't forget to free
826     allocated memory! The result type generated by the generator should be one of
827     real types: float, double or long double.
828
829     \param [in] size Size of the memory to be filled with random data.
830     \param [in] alignment Bytes alignment of the memory.
831     \param [in] lo_range Lower bound of range of values returned by random generator.
832     \param [in] up_range Upper bound of range of values returned by random generator.
833     \return Pointer to the allocated memory with random data.
834 */
835 template <typename T>
836 T* generate_random_real_numbers(const long size, const unsigned alignment, const T lo_range,
837                                 const T up_range)
838 {
839     std::uniform_real_distribution<T> distribution(lo_range, up_range);
840
841     return generate_random_numbers<T, std::uniform_real_distribution<T>>(size, alignment, distribution);
842 }
843
844 #endif //XRANLIB_COMMON_HPP