1//! @file apply_numeric.cpp
2//! @author ryftchen
3//! @brief The definitions (apply_numeric) in the application module.
4//! @version 0.1.0
5//! @copyright Copyright (c) 2022-2025 ryftchen. All rights reserved.
6
7#include "apply_numeric.hpp"
8#include "register_numeric.hpp"
9
10#ifndef _PRECOMPILED_HEADER
11#include <iomanip>
12#include <ranges>
13#else
14#include "application/pch/precompiled_header.hpp"
15#endif // _PRECOMPILED_HEADER
16
17#include "application/core/include/log.hpp"
18#include "utility/include/currying.hpp"
19#include "utility/include/time.hpp"
20
21//! @brief Title of printing when numeric tasks are beginning.
22#define APP_NUM_PRINT_TASK_TITLE_SCOPE_BEGIN(title) \
23 std::osyncstream(std::cout) << "\nAPPLY NUMERIC: " << std::setiosflags(std::ios_base::left) << std::setfill('.') \
24 << std::setw(50) << (title) << "BEGIN" << std::resetiosflags(std::ios_base::left) \
25 << std::setfill(' ') << std::endl; \
26 {
27//! @brief Title of printing when numeric tasks are ending.
28#define APP_NUM_PRINT_TASK_TITLE_SCOPE_END(title) \
29 } \
30 std::osyncstream(std::cout) << "\nAPPLY NUMERIC: " << std::setiosflags(std::ios_base::left) << std::setfill('.') \
31 << std::setw(50) << (title) << "END" << std::resetiosflags(std::ios_base::left) \
32 << std::setfill(' ') << '\n' \
33 << std::endl;
34
35namespace application::app_num
36{
37using namespace reg_num; // NOLINT(google-build-using-namespace)
38
39//! @brief Make the title of a particular method in numeric choices.
40//! @tparam Meth - type of target method
41//! @param method - target method
42//! @return initial capitalized title
43template <typename Meth>
44static std::string customTitle(const Meth method)
45{
46 std::string title(TypeInfo<Meth>::fields.nameOfValue(method));
47 title.at(n: 0) = std::toupper(c: title.at(n: 0));
48 return title;
49}
50
51//! @brief Get the curried task name.
52//! @return curried task name
53static const auto& curriedTaskName()
54{
55 static const auto curried = utility::currying::curry(func: configure::task::presetName, args: TypeInfo<ApplyNumeric>::name);
56 return curried;
57}
58
59//! @brief Get the alias of the category in numeric choices.
60//! @tparam Cat - target category
61//! @return alias of the category name
62template <Category Cat>
63static consteval std::string_view categoryAlias()
64{
65 constexpr auto attr =
66 TypeInfo<ApplyNumeric>::fields.find(REFLECTION_STR(toString(Cat))).attrs.find(REFLECTION_STR("alias"));
67 static_assert(attr.hasValue);
68 return attr.value;
69}
70
71namespace arithmetic
72{
73//! @brief Show the contents of the arithmetic result.
74//! @param method - used arithmetic method
75//! @param result - arithmetic result
76//! @param a - first integer for elementary arithmetic
77//! @param b - second integer for elementary arithmetic
78//! @param op - operator of arithmetic
79static void display(
80 const ArithmeticMethod method, const std::int32_t result, const std::int32_t a, const std::int32_t b, const char op)
81{
82 std::printf(format: "\n==> %-14s Method <==\n(%d) %c (%d) = %d\n", customTitle(method).c_str(), a, op, b, result);
83}
84
85//! @brief Calculation of arithmetic.
86//! @param method - used arithmetic method
87//! @param a - first integer for elementary arithmetic
88//! @param b - second integer for elementary arithmetic
89static void calculation(const ArithmeticMethod method, const std::int32_t a, const std::int32_t b)
90try
91{
92 std::int32_t result = 0;
93 char op = ' ';
94 switch (method)
95 {
96 using numeric::arithmetic::Arithmetic;
97 case ArithmeticMethod::addition:
98 result = Arithmetic().addition(augend: a, addend: b);
99 op = '+';
100 break;
101 case ArithmeticMethod::subtraction:
102 result = Arithmetic().subtraction(minuend: a, subtrahend: b);
103 op = '-';
104 break;
105 case ArithmeticMethod::multiplication:
106 result = Arithmetic().multiplication(multiplier: a, multiplicand: b);
107 op = '*';
108 break;
109 case ArithmeticMethod::division:
110 result = Arithmetic().division(dividend: a, divisor: b);
111 op = '/';
112 break;
113 default:
114 return;
115 }
116 display(method, result, a, b, op);
117}
118catch (const std::exception& err)
119{
120 LOG_WRN_P("Exception in %s (%s): %s", __func__, customTitle(method).c_str(), err.what());
121}
122} // namespace arithmetic
123//! @brief To apply arithmetic-related methods that are mapped to choices.
124//! @param candidates - container for the candidate target choices
125void applyingArithmetic(const std::vector<std::string>& candidates)
126{
127 constexpr auto category = Category::arithmetic;
128 const auto& spec = categoryOpts<category>();
129 if (spec.none())
130 {
131 return;
132 }
133 MACRO_ASSERT(spec.size() == candidates.size());
134
135 const std::string_view title = numeric::arithmetic::description();
136 APP_NUM_PRINT_TASK_TITLE_SCOPE_BEGIN(title);
137
138 auto& pooling = configure::task::resourcePool();
139 auto* const allocatedJob = pooling.newEntry(args: spec.count());
140 using arithmetic::InputBuilder, arithmetic::input::operandA, arithmetic::input::operandB;
141 const auto inputData = std::make_shared<InputBuilder>(args: operandA, args: operandB);
142 const auto taskNamer = utility::currying::curry(curried: curriedTaskName(), args: categoryAlias<category>());
143 const auto addTask =
144 [allocatedJob, &inputData, &taskNamer](const std::string_view subTask, const ArithmeticMethod method)
145 {
146 allocatedJob->enqueue(
147 name: taskNamer(subTask),
148 func&: arithmetic::calculation,
149 args: method,
150 args: inputData->getOperands().first,
151 args: inputData->getOperands().second);
152 };
153 MACRO_DEFER(utility::common::wrapClosure([&]() { pooling.deleteEntry(allocatedJob); }));
154
155 for (const auto index :
156 std::views::iota(0U, spec.size()) | std::views::filter([&spec](const auto i) { return spec.test(position: i); }))
157 {
158 const auto& choice = candidates.at(n: index);
159 switch (utility::common::bkdrHash(str: choice.c_str()))
160 {
161 case abbrLitHash(method: ArithmeticMethod::addition):
162 addTask(choice, ArithmeticMethod::addition);
163 break;
164 case abbrLitHash(method: ArithmeticMethod::subtraction):
165 addTask(choice, ArithmeticMethod::subtraction);
166 break;
167 case abbrLitHash(method: ArithmeticMethod::multiplication):
168 addTask(choice, ArithmeticMethod::multiplication);
169 break;
170 case abbrLitHash(method: ArithmeticMethod::division):
171 addTask(choice, ArithmeticMethod::division);
172 break;
173 default:
174 throw std::runtime_error{"Unknown " + std::string{toString(cat: category)} + " choice: " + choice + '.'};
175 }
176 }
177
178 APP_NUM_PRINT_TASK_TITLE_SCOPE_END(title);
179}
180
181namespace divisor
182{
183//! @brief Show the contents of the divisor result.
184//! @param method - used divisor method
185//! @param result - divisor result
186//! @param interval - time interval
187static void display(const DivisorMethod method, const std::set<std::int32_t>& result, const double interval)
188{
189 const std::uint32_t bufferSize = result.size() * maxAlignOfPrint;
190 std::vector<char> fmtBuffer(bufferSize + 1);
191 std::printf(
192 format: "\n==> %-9s Method <==\n%s\nrun time: %8.5f ms\n",
193 customTitle(method).c_str(),
194 InputBuilder::spliceAllIntegers(container: result, fmtBuffer: fmtBuffer.data(), bufferSize: bufferSize + 1),
195 interval);
196}
197
198//! @brief Calculation of divisor.
199//! @param method - used divisor method
200//! @param a - first integer
201//! @param b - second integer
202static void calculation(const DivisorMethod method, const std::int32_t a, const std::int32_t b)
203try
204{
205 std::set<std::int32_t> result{};
206 const utility::time::Stopwatch timing{};
207 switch (method)
208 {
209 using numeric::divisor::Divisor;
210 case DivisorMethod::euclidean:
211 result = Divisor().euclidean(a, b);
212 break;
213 case DivisorMethod::stein:
214 result = Divisor().stein(a, b);
215 break;
216 default:
217 return;
218 }
219 display(method, result, interval: timing.elapsedTime());
220}
221catch (const std::exception& err)
222{
223 LOG_WRN_P("Exception in %s (%s): %s", __func__, customTitle(method).c_str(), err.what());
224}
225} // namespace divisor
226//! @brief To apply divisor-related methods that are mapped to choices.
227//! @param candidates - container for the candidate target choices
228void applyingDivisor(const std::vector<std::string>& candidates)
229{
230 constexpr auto category = Category::divisor;
231 const auto& spec = categoryOpts<category>();
232 if (spec.none())
233 {
234 return;
235 }
236 MACRO_ASSERT(spec.size() == candidates.size());
237
238 const std::string_view title = numeric::divisor::description();
239 APP_NUM_PRINT_TASK_TITLE_SCOPE_BEGIN(title);
240
241 auto& pooling = configure::task::resourcePool();
242 auto* const allocatedJob = pooling.newEntry(args: spec.count());
243 using divisor::InputBuilder, divisor::input::numberA, divisor::input::numberB;
244 const auto inputData = std::make_shared<InputBuilder>(args: numberA, args: numberB);
245 const auto taskNamer = utility::currying::curry(curried: curriedTaskName(), args: categoryAlias<category>());
246 const auto addTask =
247 [allocatedJob, &inputData, &taskNamer](const std::string_view subTask, const DivisorMethod method)
248 {
249 allocatedJob->enqueue(
250 name: taskNamer(subTask),
251 func&: divisor::calculation,
252 args: method,
253 args: inputData->getNumbers().first,
254 args: inputData->getNumbers().second);
255 };
256 MACRO_DEFER(utility::common::wrapClosure([&]() { pooling.deleteEntry(allocatedJob); }));
257
258 for (const auto index :
259 std::views::iota(0U, spec.size()) | std::views::filter([&spec](const auto i) { return spec.test(position: i); }))
260 {
261 const auto& choice = candidates.at(n: index);
262 switch (utility::common::bkdrHash(str: choice.c_str()))
263 {
264 case abbrLitHash(method: DivisorMethod::euclidean):
265 addTask(choice, DivisorMethod::euclidean);
266 break;
267 case abbrLitHash(method: DivisorMethod::stein):
268 addTask(choice, DivisorMethod::stein);
269 break;
270 default:
271 throw std::runtime_error{"Unknown " + std::string{toString(cat: category)} + " choice: " + choice + '.'};
272 }
273 }
274
275 APP_NUM_PRINT_TASK_TITLE_SCOPE_END(title);
276}
277
278namespace integral
279{
280//! @brief Show the contents of the integral result.
281//! @param method - used integral method
282//! @param result - integral result
283//! @param interval - time interval
284static void display(const IntegralMethod method, const double result, const double interval)
285{
286 std::printf(
287 format: "\n==> %-11s Method <==\nI(def)=%+.5f, run time: %8.5f ms\n", customTitle(method).c_str(), result, interval);
288}
289
290//! @brief Calculation of integral.
291//! @param method - used integral method
292//! @param expr - target expression
293//! @param lower - lower endpoint
294//! @param upper - upper endpoint
295static void calculation(const IntegralMethod method, const Expression& expr, const double lower, const double upper)
296try
297{
298 double result = 0.0;
299 const utility::time::Stopwatch timing{};
300 switch (method)
301 {
302 using namespace numeric::integral; // NOLINT(google-build-using-namespace)
303 case IntegralMethod::trapezoidal:
304 result = Trapezoidal(expr)(lower, upper, epsilon);
305 break;
306 case IntegralMethod::simpson:
307 result = Simpson(expr)(lower, upper, epsilon);
308 break;
309 case IntegralMethod::romberg:
310 result = Romberg(expr)(lower, upper, epsilon);
311 break;
312 case IntegralMethod::gauss:
313 result = Gauss(expr)(lower, upper, epsilon);
314 break;
315 case IntegralMethod::monteCarlo:
316 result = MonteCarlo(expr)(lower, upper, epsilon);
317 break;
318 default:
319 return;
320 }
321 display(method, result, interval: timing.elapsedTime());
322}
323catch (const std::exception& err)
324{
325 LOG_WRN_P("Exception in %s (%s): %s", __func__, customTitle(method).c_str(), err.what());
326}
327} // namespace integral
328//! @brief To apply integral-related methods that are mapped to choices.
329//! @param candidates - container for the candidate target choices
330void applyingIntegral(const std::vector<std::string>& candidates)
331{
332 constexpr auto category = Category::integral;
333 const auto& spec = categoryOpts<category>();
334 if (spec.none())
335 {
336 return;
337 }
338 MACRO_ASSERT(spec.size() == candidates.size());
339
340 const std::string_view title = numeric::integral::description();
341 APP_NUM_PRINT_TASK_TITLE_SCOPE_BEGIN(title);
342
343 auto& pooling = configure::task::resourcePool();
344 auto* const allocatedJob = pooling.newEntry(args: spec.count());
345 using integral::InputBuilder, integral::input::CylindricalBessel, integral::Expression;
346 static_assert(numeric::integral::epsilon >= std::numeric_limits<double>::epsilon());
347 const auto inputData = std::make_shared<InputBuilder>(
348 args: CylindricalBessel{}, args: CylindricalBessel::range1, args: CylindricalBessel::range2, args: CylindricalBessel::exprDescr);
349 const auto taskNamer = utility::currying::curry(curried: curriedTaskName(), args: categoryAlias<category>());
350 const auto addTask =
351 [allocatedJob, &inputData, &taskNamer](const std::string_view subTask, const IntegralMethod method)
352 {
353 allocatedJob->enqueue(
354 name: taskNamer(subTask),
355 func&: integral::calculation,
356 args: method,
357 args: inputData->getExpression(),
358 args: inputData->getRanges().first,
359 args: inputData->getRanges().second);
360 };
361 MACRO_DEFER(utility::common::wrapClosure([&]() { pooling.deleteEntry(allocatedJob); }));
362
363 for (const auto index :
364 std::views::iota(0U, spec.size()) | std::views::filter([&spec](const auto i) { return spec.test(position: i); }))
365 {
366 const auto& choice = candidates.at(n: index);
367 switch (utility::common::bkdrHash(str: choice.c_str()))
368 {
369 case abbrLitHash(method: IntegralMethod::trapezoidal):
370 addTask(choice, IntegralMethod::trapezoidal);
371 break;
372 case abbrLitHash(method: IntegralMethod::simpson):
373 addTask(choice, IntegralMethod::simpson);
374 break;
375 case abbrLitHash(method: IntegralMethod::romberg):
376 addTask(choice, IntegralMethod::romberg);
377 break;
378 case abbrLitHash(method: IntegralMethod::gauss):
379 addTask(choice, IntegralMethod::gauss);
380 break;
381 case abbrLitHash(method: IntegralMethod::monteCarlo):
382 addTask(choice, IntegralMethod::monteCarlo);
383 break;
384 default:
385 throw std::runtime_error{"Unknown " + std::string{toString(cat: category)} + " choice: " + choice + '.'};
386 }
387 }
388
389 APP_NUM_PRINT_TASK_TITLE_SCOPE_END(title);
390}
391
392namespace prime
393{
394//! @brief Show the contents of the prime result.
395//! @param method - used prime method
396//! @param result - prime result
397//! @param interval - time interval
398static void display(const PrimeMethod method, const std::vector<std::uint32_t>& result, const double interval)
399{
400 const std::uint32_t bufferSize = result.size() * maxAlignOfPrint;
401 std::vector<char> fmtBuffer(bufferSize + 1);
402 std::printf(
403 format: "\n==> %-9s Method <==\n%s\nrun time: %8.5f ms\n",
404 customTitle(method).c_str(),
405 InputBuilder::spliceAllIntegers(container: result, fmtBuffer: fmtBuffer.data(), bufferSize: bufferSize + 1),
406 interval);
407}
408
409//! @brief Calculation of prime.
410//! @param method - used prime method
411//! @param limit - maximum positive integer
412static void calculation(const PrimeMethod method, const std::uint32_t limit)
413try
414{
415 std::vector<std::uint32_t> result{};
416 const utility::time::Stopwatch timing{};
417 switch (method)
418 {
419 using numeric::prime::Prime;
420 case PrimeMethod::eratosthenes:
421 result = Prime().eratosthenes(limit);
422 break;
423 case PrimeMethod::euler:
424 result = Prime().euler(limit);
425 break;
426 default:
427 return;
428 }
429 display(method, result, interval: timing.elapsedTime());
430}
431catch (const std::exception& err)
432{
433 LOG_WRN_P("Exception in %s (%s): %s", __func__, customTitle(method).c_str(), err.what());
434}
435} // namespace prime
436//! @brief To apply prime-related methods that are mapped to choices.
437//! @param candidates - container for the candidate target choices
438void applyingPrime(const std::vector<std::string>& candidates)
439{
440 constexpr auto category = Category::prime;
441 const auto& spec = categoryOpts<category>();
442 if (spec.none())
443 {
444 return;
445 }
446 MACRO_ASSERT(spec.size() == candidates.size());
447
448 const std::string_view title = numeric::prime::description();
449 APP_NUM_PRINT_TASK_TITLE_SCOPE_BEGIN(title);
450
451 auto& pooling = configure::task::resourcePool();
452 auto* const allocatedJob = pooling.newEntry(args: spec.count());
453 using prime::InputBuilder, prime::input::upperBound;
454 const auto inputData = std::make_shared<InputBuilder>(args: upperBound);
455 const auto taskNamer = utility::currying::curry(curried: curriedTaskName(), args: categoryAlias<category>());
456 const auto addTask =
457 [allocatedJob, &inputData, &taskNamer](const std::string_view subTask, const PrimeMethod method)
458 { allocatedJob->enqueue(name: taskNamer(subTask), func&: prime::calculation, args: method, args: inputData->getUpperBound()); };
459 MACRO_DEFER(utility::common::wrapClosure([&]() { pooling.deleteEntry(allocatedJob); }));
460
461 for (const auto index :
462 std::views::iota(0U, spec.size()) | std::views::filter([&spec](const auto i) { return spec.test(position: i); }))
463 {
464 const auto& choice = candidates.at(n: index);
465 switch (utility::common::bkdrHash(str: choice.c_str()))
466 {
467 case abbrLitHash(method: PrimeMethod::eratosthenes):
468 addTask(choice, PrimeMethod::eratosthenes);
469 break;
470 case abbrLitHash(method: PrimeMethod::euler):
471 addTask(choice, PrimeMethod::euler);
472 break;
473 default:
474 throw std::runtime_error{"Unknown " + std::string{toString(cat: category)} + " choice: " + choice + '.'};
475 }
476 }
477
478 APP_NUM_PRINT_TASK_TITLE_SCOPE_END(title);
479}
480} // namespace application::app_num
481
482#undef APP_NUM_PRINT_TASK_TITLE_SCOPE_BEGIN
483#undef APP_NUM_PRINT_TASK_TITLE_SCOPE_END
484