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-2026 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
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) = static_cast<char>(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 constexpr auto getOp = [](const ArithmeticMethod method) constexpr
94 {
95 switch (method)
96 {
97 case ArithmeticMethod::addition:
98 return '+';
99 case ArithmeticMethod::subtraction:
100 return '-';
101 case ArithmeticMethod::multiplication:
102 return '*';
103 case ArithmeticMethod::division:
104 return '/';
105 default:
106 return '?';
107 }
108 };
109 switch (method)
110 {
111 using numeric::arithmetic::Arithmetic;
112 case ArithmeticMethod::addition:
113 result = Arithmetic::addition(augend: a, addend: b);
114 break;
115 case ArithmeticMethod::subtraction:
116 result = Arithmetic::subtraction(minuend: a, subtrahend: b);
117 break;
118 case ArithmeticMethod::multiplication:
119 result = Arithmetic::multiplication(multiplier: a, multiplicand: b);
120 break;
121 case ArithmeticMethod::division:
122 result = Arithmetic::division(dividend: a, divisor: b);
123 break;
124 default:
125 return;
126 }
127 display(method, result, a, b, op: getOp(method));
128}
129catch (const std::exception& err)
130{
131 LOG_WRN_P("Exception in %s (%s): %s", __func__, customTitle(method).c_str(), err.what());
132}
133} // namespace arithmetic
134//! @brief To apply arithmetic-related methods that are mapped to choices.
135//! @param candidates - container for the candidate target choices
136void applyingArithmetic(const std::vector<std::string>& candidates)
137{
138 constexpr auto category = Category::arithmetic;
139 const auto& spec = categoryOpts<category>();
140 if (MACRO_IMPLIES(spec.any(), spec.size() != candidates.size()))
141 {
142 return;
143 }
144
145 const std::string_view title = numeric::arithmetic::description();
146 APP_NUM_PRINT_TASK_TITLE_SCOPE_BEGIN(title);
147
148 auto& pooling = configure::task::resourcePool();
149 auto* const allocatedJob = pooling.newEntry(args: spec.count());
150 using arithmetic::InputBuilder, arithmetic::input::operandA, arithmetic::input::operandB;
151 const auto inputData = std::make_shared<InputBuilder>(args: operandA, args: operandB);
152 const auto taskNamer = utility::currying::curry(curried: curriedTaskName(), args: categoryAlias<category>());
153 const auto addTask =
154 [allocatedJob, &inputData, &taskNamer](const std::string_view subtask, const ArithmeticMethod method)
155 {
156 allocatedJob->enqueue(
157 name: taskNamer(subtask),
158 func&: arithmetic::calculation,
159 args: method,
160 args: inputData->getOperands().first,
161 args: inputData->getOperands().second);
162 };
163 MACRO_DEFER(utility::common::wrapClosure([&]() { pooling.deleteEntry(allocatedJob); }));
164
165 for (const auto index :
166 std::views::iota(0U, spec.size()) | std::views::filter([&spec](const auto i) { return spec.test(position: i); }))
167 {
168 const auto& choice = candidates.at(n: index);
169 switch (utility::common::bkdrHash(str: choice.c_str()))
170 {
171 case abbrLitHash(method: ArithmeticMethod::addition):
172 addTask(choice, ArithmeticMethod::addition);
173 break;
174 case abbrLitHash(method: ArithmeticMethod::subtraction):
175 addTask(choice, ArithmeticMethod::subtraction);
176 break;
177 case abbrLitHash(method: ArithmeticMethod::multiplication):
178 addTask(choice, ArithmeticMethod::multiplication);
179 break;
180 case abbrLitHash(method: ArithmeticMethod::division):
181 addTask(choice, ArithmeticMethod::division);
182 break;
183 default:
184 throw std::runtime_error{"Unknown " + std::string{toString(cat: category)} + " choice: " + choice + '.'};
185 }
186 }
187
188 APP_NUM_PRINT_TASK_TITLE_SCOPE_END(title);
189}
190
191namespace divisor
192{
193//! @brief Show the contents of the divisor result.
194//! @param method - used divisor method
195//! @param result - divisor result
196//! @param interval - time interval
197static void display(const DivisorMethod method, const std::set<std::int32_t>& result, const double interval)
198{
199 const std::uint32_t bufferSize = result.size() * maxAlignOfPrint;
200 std::vector<char> fmtBuffer(bufferSize + 1);
201 std::printf(
202 format: "\n==> %-9s Method <==\n%s\nrun time: %8.5f ms\n",
203 customTitle(method).c_str(),
204 InputBuilder::spliceAllIntegers(container: result, fmtBuffer: fmtBuffer.data(), bufferSize: bufferSize + 1),
205 interval);
206}
207
208//! @brief Calculation of divisor.
209//! @param method - used divisor method
210//! @param a - first integer
211//! @param b - second integer
212static void calculation(const DivisorMethod method, const std::int32_t a, const std::int32_t b)
213try
214{
215 std::set<std::int32_t> result{};
216 const utility::time::Stopwatch timing{};
217 switch (method)
218 {
219 using numeric::divisor::Divisor;
220 case DivisorMethod::euclidean:
221 result = Divisor::euclidean(a, b);
222 break;
223 case DivisorMethod::stein:
224 result = Divisor::stein(a, b);
225 break;
226 default:
227 return;
228 }
229 display(method, result, interval: timing.elapsedTime());
230}
231catch (const std::exception& err)
232{
233 LOG_WRN_P("Exception in %s (%s): %s", __func__, customTitle(method).c_str(), err.what());
234}
235} // namespace divisor
236//! @brief To apply divisor-related methods that are mapped to choices.
237//! @param candidates - container for the candidate target choices
238void applyingDivisor(const std::vector<std::string>& candidates)
239{
240 constexpr auto category = Category::divisor;
241 const auto& spec = categoryOpts<category>();
242 if (MACRO_IMPLIES(spec.any(), spec.size() != candidates.size()))
243 {
244 return;
245 }
246
247 const std::string_view title = numeric::divisor::description();
248 APP_NUM_PRINT_TASK_TITLE_SCOPE_BEGIN(title);
249
250 auto& pooling = configure::task::resourcePool();
251 auto* const allocatedJob = pooling.newEntry(args: spec.count());
252 using divisor::InputBuilder, divisor::input::numberA, divisor::input::numberB;
253 const auto inputData = std::make_shared<InputBuilder>(args: numberA, args: numberB);
254 const auto taskNamer = utility::currying::curry(curried: curriedTaskName(), args: categoryAlias<category>());
255 const auto addTask =
256 [allocatedJob, &inputData, &taskNamer](const std::string_view subtask, const DivisorMethod method)
257 {
258 allocatedJob->enqueue(
259 name: taskNamer(subtask),
260 func&: divisor::calculation,
261 args: method,
262 args: inputData->getNumbers().first,
263 args: inputData->getNumbers().second);
264 };
265 MACRO_DEFER(utility::common::wrapClosure([&]() { pooling.deleteEntry(allocatedJob); }));
266
267 for (const auto index :
268 std::views::iota(0U, spec.size()) | std::views::filter([&spec](const auto i) { return spec.test(position: i); }))
269 {
270 const auto& choice = candidates.at(n: index);
271 switch (utility::common::bkdrHash(str: choice.c_str()))
272 {
273 case abbrLitHash(method: DivisorMethod::euclidean):
274 addTask(choice, DivisorMethod::euclidean);
275 break;
276 case abbrLitHash(method: DivisorMethod::stein):
277 addTask(choice, DivisorMethod::stein);
278 break;
279 default:
280 throw std::runtime_error{"Unknown " + std::string{toString(cat: category)} + " choice: " + choice + '.'};
281 }
282 }
283
284 APP_NUM_PRINT_TASK_TITLE_SCOPE_END(title);
285}
286
287namespace integral
288{
289//! @brief Show the contents of the integral result.
290//! @param method - used integral method
291//! @param result - integral result
292//! @param interval - time interval
293static void display(const IntegralMethod method, const double result, const double interval)
294{
295 std::printf(
296 format: "\n==> %-11s Method <==\nI(def)=%+.5f, run time: %8.5f ms\n", customTitle(method).c_str(), result, interval);
297}
298
299//! @brief Calculation of integral.
300//! @param method - used integral method
301//! @param expr - target expression
302//! @param lower - lower endpoint
303//! @param upper - upper endpoint
304static void calculation(const IntegralMethod method, const Expression& expr, const double lower, const double upper)
305try
306{
307 using namespace numeric::integral; // NOLINT(google-build-using-namespace)
308 Result result = 0.0;
309 const utility::time::Stopwatch timing{};
310 switch (method)
311 {
312 case IntegralMethod::trapezoidal:
313 result = Trapezoidal(expr)(lower, upper, epsilon);
314 break;
315 case IntegralMethod::simpson:
316 result = Simpson(expr)(lower, upper, epsilon);
317 break;
318 case IntegralMethod::romberg:
319 result = Romberg(expr)(lower, upper, epsilon);
320 break;
321 case IntegralMethod::gauss:
322 result = Gauss(expr)(lower, upper, epsilon);
323 break;
324 case IntegralMethod::monteCarlo:
325 result = MonteCarlo(expr)(lower, upper, epsilon);
326 break;
327 default:
328 return;
329 }
330 display(method, result, interval: timing.elapsedTime());
331}
332catch (const std::exception& err)
333{
334 LOG_WRN_P("Exception in %s (%s): %s", __func__, customTitle(method).c_str(), err.what());
335}
336} // namespace integral
337//! @brief To apply integral-related methods that are mapped to choices.
338//! @param candidates - container for the candidate target choices
339void applyingIntegral(const std::vector<std::string>& candidates)
340{
341 constexpr auto category = Category::integral;
342 const auto& spec = categoryOpts<category>();
343 if (MACRO_IMPLIES(spec.any(), spec.size() != candidates.size()))
344 {
345 return;
346 }
347
348 const std::string_view title = numeric::integral::description();
349 APP_NUM_PRINT_TASK_TITLE_SCOPE_BEGIN(title);
350
351 auto& pooling = configure::task::resourcePool();
352 auto* const allocatedJob = pooling.newEntry(args: spec.count());
353 using integral::InputBuilder, integral::input::CylindricalBessel, integral::Expression;
354 static_assert(numeric::integral::epsilon >= std::numeric_limits<double>::epsilon());
355 const auto inputData = std::make_shared<InputBuilder>(
356 args: CylindricalBessel{}, args: CylindricalBessel::range1, args: CylindricalBessel::range2, args: CylindricalBessel::exprDescr);
357 const auto taskNamer = utility::currying::curry(curried: curriedTaskName(), args: categoryAlias<category>());
358 const auto addTask =
359 [allocatedJob, &inputData, &taskNamer](const std::string_view subtask, const IntegralMethod method)
360 {
361 allocatedJob->enqueue(
362 name: taskNamer(subtask),
363 func&: integral::calculation,
364 args: method,
365 args: inputData->getExpression(),
366 args: inputData->getRanges().first,
367 args: inputData->getRanges().second);
368 };
369 MACRO_DEFER(utility::common::wrapClosure([&]() { pooling.deleteEntry(allocatedJob); }));
370
371 for (const auto index :
372 std::views::iota(0U, spec.size()) | std::views::filter([&spec](const auto i) { return spec.test(position: i); }))
373 {
374 const auto& choice = candidates.at(n: index);
375 switch (utility::common::bkdrHash(str: choice.c_str()))
376 {
377 case abbrLitHash(method: IntegralMethod::trapezoidal):
378 addTask(choice, IntegralMethod::trapezoidal);
379 break;
380 case abbrLitHash(method: IntegralMethod::simpson):
381 addTask(choice, IntegralMethod::simpson);
382 break;
383 case abbrLitHash(method: IntegralMethod::romberg):
384 addTask(choice, IntegralMethod::romberg);
385 break;
386 case abbrLitHash(method: IntegralMethod::gauss):
387 addTask(choice, IntegralMethod::gauss);
388 break;
389 case abbrLitHash(method: IntegralMethod::monteCarlo):
390 addTask(choice, IntegralMethod::monteCarlo);
391 break;
392 default:
393 throw std::runtime_error{"Unknown " + std::string{toString(cat: category)} + " choice: " + choice + '.'};
394 }
395 }
396
397 APP_NUM_PRINT_TASK_TITLE_SCOPE_END(title);
398}
399
400namespace prime
401{
402//! @brief Show the contents of the prime result.
403//! @param method - used prime method
404//! @param result - prime result
405//! @param interval - time interval
406static void display(const PrimeMethod method, const std::vector<std::uint32_t>& result, const double interval)
407{
408 const std::uint32_t bufferSize = result.size() * maxAlignOfPrint;
409 std::vector<char> fmtBuffer(bufferSize + 1);
410 std::printf(
411 format: "\n==> %-9s Method <==\n%s\nrun time: %8.5f ms\n",
412 customTitle(method).c_str(),
413 InputBuilder::spliceAllIntegers(container: result, fmtBuffer: fmtBuffer.data(), bufferSize: bufferSize + 1),
414 interval);
415}
416
417//! @brief Calculation of prime.
418//! @param method - used prime method
419//! @param limit - maximum positive integer
420static void calculation(const PrimeMethod method, const std::uint32_t limit)
421try
422{
423 std::vector<std::uint32_t> result{};
424 const utility::time::Stopwatch timing{};
425 switch (method)
426 {
427 using numeric::prime::Prime;
428 case PrimeMethod::eratosthenes:
429 result = Prime::eratosthenes(limit);
430 break;
431 case PrimeMethod::euler:
432 result = Prime::euler(limit);
433 break;
434 default:
435 return;
436 }
437 display(method, result, interval: timing.elapsedTime());
438}
439catch (const std::exception& err)
440{
441 LOG_WRN_P("Exception in %s (%s): %s", __func__, customTitle(method).c_str(), err.what());
442}
443} // namespace prime
444//! @brief To apply prime-related methods that are mapped to choices.
445//! @param candidates - container for the candidate target choices
446void applyingPrime(const std::vector<std::string>& candidates)
447{
448 constexpr auto category = Category::prime;
449 const auto& spec = categoryOpts<category>();
450 if (MACRO_IMPLIES(spec.any(), spec.size() != candidates.size()))
451 {
452 return;
453 }
454
455 const std::string_view title = numeric::prime::description();
456 APP_NUM_PRINT_TASK_TITLE_SCOPE_BEGIN(title);
457
458 auto& pooling = configure::task::resourcePool();
459 auto* const allocatedJob = pooling.newEntry(args: spec.count());
460 using prime::InputBuilder, prime::input::upperBound;
461 const auto inputData = std::make_shared<InputBuilder>(args: upperBound);
462 const auto taskNamer = utility::currying::curry(curried: curriedTaskName(), args: categoryAlias<category>());
463 const auto addTask =
464 [allocatedJob, &inputData, &taskNamer](const std::string_view subtask, const PrimeMethod method)
465 { allocatedJob->enqueue(name: taskNamer(subtask), func&: prime::calculation, args: method, args: inputData->getUpperBound()); };
466 MACRO_DEFER(utility::common::wrapClosure([&]() { pooling.deleteEntry(allocatedJob); }));
467
468 for (const auto index :
469 std::views::iota(0U, spec.size()) | std::views::filter([&spec](const auto i) { return spec.test(position: i); }))
470 {
471 const auto& choice = candidates.at(n: index);
472 switch (utility::common::bkdrHash(str: choice.c_str()))
473 {
474 case abbrLitHash(method: PrimeMethod::eratosthenes):
475 addTask(choice, PrimeMethod::eratosthenes);
476 break;
477 case abbrLitHash(method: PrimeMethod::euler):
478 addTask(choice, PrimeMethod::euler);
479 break;
480 default:
481 throw std::runtime_error{"Unknown " + std::string{toString(cat: category)} + " choice: " + choice + '.'};
482 }
483 }
484
485 APP_NUM_PRINT_TASK_TITLE_SCOPE_END(title);
486}
487} // namespace application::app_num
488
489#undef APP_NUM_PRINT_TASK_TITLE_SCOPE_BEGIN
490#undef APP_NUM_PRINT_TASK_TITLE_SCOPE_END
491