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