1//! @file apply_algorithm.hpp
2//! @author ryftchen
3//! @brief The declarations (apply_algorithm) in the application module.
4//! @version 0.1.0
5//! @copyright Copyright (c) 2022-2026 ryftchen. All rights reserved.
6
7#pragma once
8
9#ifndef _PRECOMPILED_HEADER
10#include <gsl/gsl_sf.h>
11#include <mpfr.h>
12#include <cstring>
13#include <iostream>
14#include <memory>
15#include <span>
16#else
17#include "application/pch/precompiled_header.hpp"
18#endif
19
20#include "algorithm/include/match.hpp"
21#include "algorithm/include/notation.hpp"
22#include "algorithm/include/optimal.hpp"
23#include "algorithm/include/search.hpp"
24#include "algorithm/include/sort.hpp"
25
26//! @brief The application module.
27namespace application // NOLINT(modernize-concat-nested-namespaces)
28{
29//! @brief Algorithm-applying-related functions in the application module.
30namespace app_algo
31{
32//! @brief Apply match.
33namespace match
34{
35//! @brief The version used to apply.
36const char* const version = algorithm::match::version();
37
38//! @brief Set input parameters.
39namespace input
40{
41//! @brief Single pattern for match methods.
42inline constexpr std::string_view patternString = "12345";
43} // namespace input
44
45//! @brief Maximum number per line of printing.
46constexpr std::uint32_t maxNumPerLineOfPrint = 50;
47
48//! @brief Builder for the input.
49class InputBuilder
50{
51public:
52 //! @brief Construct a new InputBuilder object.
53 //! @param pattern - single pattern
54 explicit InputBuilder(const std::string_view pattern) :
55 marchingText{std::make_unique<unsigned char[]>(num: maxDigit)},
56 textLen{maxDigit},
57 singlePattern{std::make_unique<unsigned char[]>(num: pattern.length())},
58 patternLen{static_cast<std::uint32_t>(pattern.length())}
59 {
60 std::memset(s: marchingText.get(), c: 0, n: textLen * sizeof(unsigned char));
61 std::memset(s: singlePattern.get(), c: 0, n: patternLen * sizeof(unsigned char));
62 createMatchingText(text: marchingText.get(), textLen: maxDigit);
63 std::memcpy(dest: singlePattern.get(), src: pattern.data(), n: pattern.length() * sizeof(unsigned char));
64 }
65 //! @brief Destroy the InputBuilder object.
66 virtual ~InputBuilder() { ::mpfr_free_cache(); }
67 //! @brief Construct a new InputBuilder object.
68 InputBuilder(const InputBuilder&) = delete;
69 //! @brief Construct a new InputBuilder object.
70 InputBuilder(InputBuilder&&) = delete;
71 //! @brief The operator (=) overloading of InputBuilder class.
72 //! @return reference of the InputBuilder object
73 InputBuilder& operator=(const InputBuilder&) = delete;
74 //! @brief The operator (=) overloading of InputBuilder class.
75 //! @return reference of the InputBuilder object
76 InputBuilder& operator=(InputBuilder&&) = delete;
77
78 //! @brief Maximum digit for the target text.
79 static constexpr std::uint32_t maxDigit{100'000};
80 //! @brief Get the matching text.
81 //! @return matching text
82 [[nodiscard]] const std::unique_ptr<unsigned char[]>& getMatchingText() const noexcept { return marchingText; }
83 //! @brief Get the length of the matching text.
84 //! @return length of the matching text
85 [[nodiscard]] std::uint32_t getTextLength() const noexcept { return textLen; }
86 //! @brief Get the single pattern.
87 //! @return single pattern
88 [[nodiscard]] const std::unique_ptr<unsigned char[]>& getSinglePattern() const noexcept { return singlePattern; }
89 //! @brief Get the length of the single pattern.
90 //! @return length of the single pattern
91 [[nodiscard]] std::uint32_t getPatternLength() const noexcept { return patternLen; }
92
93private:
94 //! @brief Matching text.
95 const std::unique_ptr<unsigned char[]> marchingText;
96 //! @brief Length of the matching text.
97 const std::uint32_t textLen{0};
98 //! @brief Single pattern.
99 const std::unique_ptr<unsigned char[]> singlePattern;
100 //! @brief Length of the single pattern.
101 const std::uint32_t patternLen{0};
102
103 //! @brief Base number for converting the digit to precision.
104 static constexpr int mpfrBase{10};
105 //! @brief Create the matching text.
106 //! @param text - target matching text
107 //! @param textLen - length of matching text
108 static void createMatchingText(unsigned char* const text, const std::uint32_t textLen)
109 {
110 if (!text || (textLen == 0))
111 {
112 return;
113 }
114
115 ::mpfr_t operand{};
116 ::mpfr_init2(operand, calculatePrecision(digit: textLen + 1));
117 ::mpfr_const_pi(operand, ::MPFR_RNDN);
118 ::mpfr_exp_t decimalLocation = 0;
119 char* const piText = ::mpfr_get_str(nullptr, &decimalLocation, mpfrBase, textLen + 1, operand, ::MPFR_RNDN);
120 if (std::strlen(s: piText) != 0)
121 {
122 piText[textLen] = '\0';
123 std::memcpy(dest: text, src: piText, n: textLen * sizeof(unsigned char));
124#ifdef _RUNTIME_PRINTING
125 std::string brief(text, text + textLen);
126 if (textLen > 1)
127 {
128 brief = std::string{brief.at(n: 0)} + '.' + brief.substr(pos: 1, n: brief.length());
129 }
130 std::cout << "\nπ " << textLen << " digits:\n"
131 << brief.substr(pos: 0, n: std::min(a: textLen, b: maxNumPerLineOfPrint)) << std::endl;
132 if (textLen > maxNumPerLineOfPrint)
133 {
134 std::cout << "...\n"
135 << ((textLen > (maxNumPerLineOfPrint * 2))
136 ? brief.substr(pos: brief.length() - maxNumPerLineOfPrint, n: brief.length())
137 : brief.substr(pos: maxNumPerLineOfPrint + 1, n: brief.length()))
138 << std::endl;
139 }
140#endif
141 }
142 ::mpfr_clear(operand);
143 ::mpfr_free_str(piText);
144 }
145 //! @brief Calculate precision by digit.
146 //! @param digit - digit for the target text
147 //! @return precision converted from digit
148 static int calculatePrecision(const std::uint32_t digit)
149 {
150 return 1 + static_cast<int>(std::ceil(x: static_cast<double>(digit) * std::log2(x: mpfrBase)));
151 }
152};
153} // namespace match
154extern void applyingMatch(const std::vector<std::string>& candidates);
155
156//! @brief Apply notation.
157namespace notation
158{
159//! @brief The version used to apply.
160const char* const version = algorithm::notation::version();
161
162//! @brief Set input parameters.
163namespace input
164{
165//! @brief Infix string for notation methods.
166inline constexpr std::string_view infixString = "a+b*(c^d-e)^(f+g*h)-i";
167} // namespace input
168
169//! @brief Builder for the input.
170class InputBuilder
171{
172public:
173 //! @brief Construct a new InputBuilder object.
174 //! @param infixNotation - infix notation
175 explicit InputBuilder(const std::string_view infixNotation) : infixNotation{infixNotation}
176 {
177#ifdef _RUNTIME_PRINTING
178 std::cout << "\nInfix notation:\n" << infixNotation << std::endl;
179#endif
180 }
181
182 //! @brief Get the infix notation.
183 //! @return infix notation
184 [[nodiscard]] std::string getInfixNotation() const noexcept { return infixNotation; }
185
186private:
187 //! @brief Infix notation.
188 const std::string infixNotation;
189};
190} // namespace notation
191extern void applyingNotation(const std::vector<std::string>& candidates);
192
193//! @brief Apply optimal.
194namespace optimal
195{
196//! @brief The version used to apply.
197const char* const version = algorithm::optimal::version();
198
199//! @brief Alias for the target function.
200using Function = std::function<double(const double)>;
201//! @brief Wrapper for the target function.
202class AsFunction
203{
204public:
205 //! @brief Destroy the AsFunction object.
206 virtual ~AsFunction() = default;
207
208 //! @brief The operator (()) overloading of AsFunction class.
209 //! @param x - independent variable
210 //! @return dependent variable
211 virtual double operator()(const double x) const = 0;
212 //! @brief The operator (Function) overloading of AsFunction class.
213 //! @return Function object
214 virtual explicit operator Function() const
215 {
216 return [this](const double x) { return operator()(x); };
217 }
218};
219
220//! @brief Set input parameters.
221namespace input
222{
223//! @brief Spherical Bessel.
224class SphericalBessel : public AsFunction
225{
226public:
227 //! @brief The operator (()) overloading of SphericalBessel class.
228 //! @param x - independent variable
229 //! @return dependent variable
230 double operator()(const double x) const override { return ::gsl_sf_bessel_j0(x); }
231
232 //! @brief Left endpoint.
233 static constexpr double range1{0.0};
234 //! @brief Right endpoint.
235 static constexpr double range2{20.0};
236 //! @brief Spherical Bessel function of the first kind.
237 static constexpr std::string_view funcDescr{"f(x)=j₀(x),x∈[0,20] (Spherical Bessel function of the first kind)"};
238};
239} // namespace input
240
241//! @brief Builder for the input.
242class InputBuilder
243{
244public:
245 //! @brief Construct a new Input Builder object.
246 //! @param function - target function
247 //! @param range1 - left endpoint
248 //! @param range2 - right endpoint
249 //! @param funcDescr - function description
250 InputBuilder(
251 Function function,
252 const double range1,
253 const double range2,
254 [[maybe_unused]] const std::string_view funcDescr) :
255 function{std::move(function)}, range1{range1}, range2{range2}
256 {
257#ifdef _RUNTIME_PRINTING
258 std::cout << "\nOptimal function:\n" << funcDescr << std::endl;
259#endif
260 }
261
262 //! @brief Get the target function.
263 //! @return target function
264 [[nodiscard]] Function getFunction() const noexcept { return function; }
265 //! @brief Get the pair of ranges.
266 //! @return pair of ranges
267 [[nodiscard]] std::pair<double, double> getRanges() const noexcept { return std::make_pair(x: range1, y: range2); }
268
269private:
270 //! @brief Target function.
271 const Function function;
272 //! @brief Left endpoint.
273 const double range1{0.0};
274 //! @brief Right endpoint.
275 const double range2{0.0};
276};
277} // namespace optimal
278extern void applyingOptimal(const std::vector<std::string>& candidates);
279
280//! @brief Apply search.
281namespace search
282{
283//! @brief The version used to apply.
284const char* const version = algorithm::search::version();
285
286//! @brief Set input parameters.
287namespace input
288{
289//! @brief Minimum of the array for search methods.
290inline constexpr float arrayRangeMin = -50.0;
291//! @brief Maximum of the array for search methods.
292inline constexpr float arrayRangeMax = 150.0;
293//! @brief Length of the array for search methods.
294inline constexpr std::uint32_t arrayLength = 53;
295} // namespace input
296
297//! @brief Maximum alignment length per element of printing.
298constexpr std::uint8_t maxAlignOfPrint = 16;
299//! @brief Maximum columns per row of printing.
300constexpr std::uint8_t maxColumnOfPrint = 5;
301
302//! @brief Builder for the input.
303//! @tparam Elem - type of builder for the target
304template <typename Elem>
305requires std::is_integral_v<Elem> || std::is_floating_point_v<Elem>
306class InputBuilder
307{
308public:
309 //! @brief Construct a new InputBuilder object.
310 //! @param length - length of array
311 //! @param left - left boundary of the array
312 //! @param right - right boundary of the array
313 InputBuilder(const std::uint32_t length, const Elem left, const Elem right) :
314 orderedArray{std::make_unique<Elem[]>(length)}, length{length}
315 {
316 std::fill_n(orderedArray.get(), length, static_cast<Elem>(0));
317 setOrderedArray(orderedArray.get(), length, left, right);
318 }
319
320 //! @brief Get the ordered array.
321 //! @return ordered array
322 const std::unique_ptr<Elem[]>& getOrderedArray() const noexcept { return orderedArray; }
323 //! @brief Get the length.
324 //! @return length
325 [[nodiscard]] std::uint32_t getLength() const noexcept { return length; }
326 //! @brief Get the search key.
327 //! @return search key
328 Elem getSearchKey() const noexcept { return orderedArray[length / 2]; }
329 //! @brief Splice from array for printing.
330 //! @param array - target array
331 //! @param length - length of array
332 //! @param fmtBuffer - buffer for printing
333 //! @param bufferSize - size of the buffer
334 //! @return buffer after splicing
335 static char* spliceAll(
336 const Elem* const array, const std::uint32_t length, char* const fmtBuffer, const std::uint32_t bufferSize)
337 {
338 if (!array || (length == 0) || !fmtBuffer || (bufferSize == 0))
339 {
340 return fmtBuffer;
341 }
342
343 std::uint32_t align = 0;
344 for (std::uint32_t i = 0; i < length; ++i)
345 {
346 align = std::max(a: static_cast<std::uint32_t>(std::to_string(*(array + i)).length()), b: align);
347 }
348
349 std::array<char, 16> spliceFmt{};
350 if constexpr (std::is_integral_v<Elem> && std::is_signed_v<Elem>)
351 {
352 std::snprintf(s: spliceFmt.data(), maxlen: spliceFmt.size(), format: "%%%dd%%c", align + 1);
353 }
354 else if constexpr (std::is_integral_v<Elem> && std::is_unsigned_v<Elem>)
355 {
356 std::snprintf(s: spliceFmt.data(), maxlen: spliceFmt.size(), format: "%%%du%%c", align + 1);
357 }
358 else if constexpr (std::is_floating_point_v<Elem>)
359 {
360 std::snprintf(s: spliceFmt.data(), maxlen: spliceFmt.size(), format: "%%%d.5f%%c", align + 1);
361 }
362 else
363 {
364 return fmtBuffer;
365 }
366
367 for (std::uint32_t i = 0, offset = 0; i < length; ++i)
368 {
369 const char sep = (((i + 1) % maxColumnOfPrint == 0) && ((i + 1) != length)) ? '\n' : ' ';
370 const int written =
371 std::snprintf(s: fmtBuffer + offset, maxlen: bufferSize - offset, format: spliceFmt.data(), *(array + i), sep);
372 if ((written < 0) || (written >= static_cast<int>(bufferSize - offset)))
373 {
374 break;
375 }
376 offset += written;
377 }
378 return fmtBuffer;
379 }
380
381private:
382 //! @brief Ordered array.
383 const std::unique_ptr<Elem[]> orderedArray{};
384 //! @brief Length of the ordered array.
385 const std::uint32_t length{0};
386
387 //! @brief Set the ordered array.
388 //! @param array - ordered array
389 //! @param length - length of the ordered array
390 //! @param left - left boundary of the ordered array
391 //! @param right - left right of the ordered array
392 static void setOrderedArray(Elem* const array, const std::uint32_t length, const Elem left, const Elem right)
393 requires std::is_integral_v<Elem>
394 {
395 if (!array || (length == 0) || (left > right))
396 {
397 return;
398 }
399
400 const std::span<Elem> sequence{array, length};
401 std::ranges::for_each(
402 sequence,
403 [engine = std::ranlux48(std::random_device{}()),
404 dist = std::uniform_int_distribution<Elem>(left, right)](auto& elem) mutable { elem = dist(engine); });
405 std::ranges::sort(sequence);
406#ifdef _RUNTIME_PRINTING
407 const std::uint32_t bufferSize = sequence.size() * maxAlignOfPrint;
408 std::vector<char> fmtBuffer(bufferSize + 1);
409 std::cout << "\nGenerate " << sequence.size() << " ordered integral numbers from " << left << " to " << right
410 << ":\n"
411 << spliceAll(array: sequence.data(), length: sequence.size(), fmtBuffer: fmtBuffer.data(), bufferSize: bufferSize + 1) << std::endl;
412#endif
413 }
414 //! @brief Set the ordered array.
415 //! @param array - ordered array
416 //! @param length - length of the ordered array
417 //! @param left - left boundary of the ordered array
418 //! @param right - left right of the ordered array
419 static void setOrderedArray(Elem* const array, const std::uint32_t length, const Elem left, const Elem right)
420 requires std::is_floating_point_v<Elem>
421 {
422 if (!array || (length == 0) || (left > right))
423 {
424 return;
425 }
426
427 const std::span<Elem> sequence{array, length};
428 std::ranges::for_each(
429 sequence,
430 [engine = std::ranlux48(std::random_device{}()),
431 dist = std::uniform_real_distribution<Elem>(left, right)](auto& elem) mutable { elem = dist(engine); });
432 std::ranges::sort(sequence);
433#ifdef _RUNTIME_PRINTING
434 const std::uint32_t bufferSize = sequence.size() * maxAlignOfPrint;
435 std::vector<char> fmtBuffer(bufferSize + 1);
436 std::cout << "\nGenerate " << sequence.size() << " ordered floating point numbers from " << left << " to "
437 << right << ":\n"
438 << spliceAll(array: sequence.data(), length: sequence.size(), fmtBuffer: fmtBuffer.data(), bufferSize: bufferSize + 1) << std::endl;
439#endif
440 }
441};
442} // namespace search
443extern void applyingSearch(const std::vector<std::string>& candidates);
444
445//! @brief Apply sort.
446namespace sort
447{
448//! @brief The version used to apply.
449const char* const version = algorithm::sort::version();
450
451//! @brief Set input parameters.
452namespace input
453{
454//! @brief Minimum of the array for sort methods.
455inline constexpr std::int32_t arrayRangeMin = -50;
456//! @brief Maximum of the array for sort methods.
457inline constexpr std::int32_t arrayRangeMax = 150;
458//! @brief Length of the array for sort methods.
459inline constexpr std::uint32_t arrayLength = 53;
460} // namespace input
461
462//! @brief Maximum alignment length per element of printing.
463constexpr std::uint8_t maxAlignOfPrint = 16;
464//! @brief Maximum columns per row of printing.
465constexpr std::uint8_t maxColumnOfPrint = 10;
466
467//! @brief Builder for the input.
468//! @tparam Elem - type of builder for the target
469template <typename Elem>
470requires std::is_integral_v<Elem> || std::is_floating_point_v<Elem>
471class InputBuilder
472{
473public:
474 //! @brief Construct a new InputBuilder object.
475 //! @param length - length of array
476 //! @param left - left boundary of the array
477 //! @param right - right boundary of the array
478 InputBuilder(const std::uint32_t length, const Elem left, const Elem right) :
479 randomArray{std::make_unique<Elem[]>(length)}, length{length}
480 {
481 std::fill_n(randomArray.get(), length, static_cast<Elem>(0));
482 setRandomArray(randomArray.get(), length, left, right);
483 }
484
485 //! @brief Get the random array.
486 //! @return random array
487 const std::unique_ptr<Elem[]>& getRandomArray() const noexcept { return randomArray; }
488 //! @brief Get the length.
489 //! @return length
490 [[nodiscard]] std::uint32_t getLength() const noexcept { return length; }
491 //! @brief Splice from array for printing.
492 //! @param array - target array
493 //! @param length - length of array
494 //! @param fmtBuffer - buffer for printing
495 //! @param bufferSize - size of the buffer
496 //! @return buffer after splicing
497 static char* spliceAll(
498 const Elem* const array, const std::uint32_t length, char* const fmtBuffer, const std::uint32_t bufferSize)
499 {
500 if (!array || (length == 0) || !fmtBuffer || (bufferSize == 0))
501 {
502 return fmtBuffer;
503 }
504
505 std::uint32_t align = 0;
506 for (std::uint32_t i = 0; i < length; ++i)
507 {
508 align = std::max(a: static_cast<std::uint32_t>(std::to_string(*(array + i)).length()), b: align);
509 }
510
511 std::array<char, 16> spliceFmt{};
512 if constexpr (std::is_integral_v<Elem> && std::is_signed_v<Elem>)
513 {
514 std::snprintf(s: spliceFmt.data(), maxlen: spliceFmt.size(), format: "%%%dd%%c", align + 1);
515 }
516 else if constexpr (std::is_integral_v<Elem> && std::is_unsigned_v<Elem>)
517 {
518 std::snprintf(s: spliceFmt.data(), maxlen: spliceFmt.size(), format: "%%%du%%c", align + 1);
519 }
520 else if constexpr (std::is_floating_point_v<Elem>)
521 {
522 std::snprintf(s: spliceFmt.data(), maxlen: spliceFmt.size(), format: "%%%d.5f%%c", align + 1);
523 }
524 else
525 {
526 return fmtBuffer;
527 }
528
529 for (std::uint32_t i = 0, offset = 0; i < length; ++i)
530 {
531 const char sep = (((i + 1) % maxColumnOfPrint == 0) && ((i + 1) != length)) ? '\n' : ' ';
532 const int written =
533 std::snprintf(s: fmtBuffer + offset, maxlen: bufferSize - offset, format: spliceFmt.data(), *(array + i), sep);
534 if ((written < 0) || (written >= static_cast<int>(bufferSize - offset)))
535 {
536 break;
537 }
538 offset += written;
539 }
540 return fmtBuffer;
541 }
542
543private:
544 //! @brief Random array.
545 const std::unique_ptr<Elem[]> randomArray{};
546 //! @brief Length of the random array.
547 const std::uint32_t length{0};
548
549 //! @brief Set the random array.
550 //! @param array - random array
551 //! @param length - length of the random array
552 //! @param left - left boundary of the random array
553 //! @param right - left right of the random array
554 static void setRandomArray(Elem* const array, const std::uint32_t length, const Elem left, const Elem right)
555 requires std::is_integral_v<Elem>
556 {
557 if (!array || (length == 0) || (left > right))
558 {
559 return;
560 }
561
562 const std::span<Elem> sequence{array, length};
563 std::ranges::for_each(
564 sequence,
565 [engine = std::ranlux48(std::random_device{}()),
566 dist = std::uniform_int_distribution<Elem>(left, right)](auto& elem) mutable { elem = dist(engine); });
567#ifdef _RUNTIME_PRINTING
568 const std::uint32_t bufferSize = sequence.size() * maxAlignOfPrint;
569 std::vector<char> fmtBuffer(bufferSize + 1);
570 std::cout << "\nGenerate " << sequence.size() << " random integral numbers from " << left << " to " << right
571 << ":\n"
572 << spliceAll(array: sequence.data(), length: sequence.size(), fmtBuffer: fmtBuffer.data(), bufferSize: bufferSize + 1) << std::endl;
573#endif
574 }
575 //! @brief Set the random array.
576 //! @param array - random array
577 //! @param length - length of the random array
578 //! @param left - left boundary of the random array
579 //! @param right - left right of the random array
580 static void setRandomArray(Elem* const array, const std::uint32_t length, const Elem left, const Elem right)
581 requires std::is_floating_point_v<Elem>
582 {
583 if (!array || (length == 0) || (left > right))
584 {
585 return;
586 }
587
588 const std::span<Elem> sequence{array, length};
589 std::ranges::for_each(
590 sequence,
591 [engine = std::ranlux48(std::random_device{}()),
592 dist = std::uniform_real_distribution<Elem>(left, right)](auto& elem) mutable { elem = dist(engine); });
593#ifdef _RUNTIME_PRINTING
594 const std::uint32_t bufferSize = sequence.size() * maxAlignOfPrint;
595 std::vector<char> fmtBuffer(bufferSize + 1);
596 std::cout << "\nGenerate " << sequence.size() << " random floating point numbers from " << left << " to "
597 << right << ":\n"
598 << spliceAll(array: sequence.data(), length: sequence.size(), fmtBuffer: fmtBuffer.data(), bufferSize: bufferSize + 1) << std::endl;
599#endif
600 }
601};
602} // namespace sort
603extern void applyingSort(const std::vector<std::string>& candidates);
604} // namespace app_algo
605} // namespace application
606