ComPPare 1.0.0
Loading...
Searching...
No Matches
policy.hpp
Go to the documentation of this file.
1/*
2
3Copyright 2025 | Leong Fan FUNG | funglf | stanleyfunglf@gmail.com
4
5Permission is hereby granted, free of charge, to any person obtaining a copy
6of this software and associated documentation files (the “Software”), to deal
7in the Software without restriction, including without limitation the rights
8to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9copies of the Software, and to permit persons to whom the Software is
10furnished to do so, subject to the following conditions:
11
12The above copyright notice and this permission notice shall be included in
13all copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21SOFTWARE.
22
23*/
24
33#pragma once
34#include <concepts>
35#include <ranges>
36#include <string>
37#include <string_view>
38#include <type_traits>
39#include <utility>
40#include <limits>
41#include <ostream>
42#include <sstream>
43#include <variant>
44#include <stdexcept>
45
48
50{
51
73 template <comppare::internal::concepts::Streamable T>
75 {
77 bool is_fail_{false};
78
79 bool valid_{true};
80 std::string_view err_msg_;
81
82 public:
83 MetricValue(T v) : value_(v), err_msg_(""), valid_(true), is_fail_(false) {}
84 MetricValue(T v, bool is_fail) : value_(v), is_fail_(is_fail), valid_(true), err_msg_("") {}
85 MetricValue(T v, bool is_fail, bool valid, std::string_view msg) : value_(v), is_fail_(is_fail), valid_(valid), err_msg_(msg) {}
86
99 friend std::ostream &
100 operator<<(std::ostream &os, MetricValue<T> const &mv)
101 {
102 std::ios saved(nullptr);
103 saved.copyfmt(os);
104
105 std::ostringstream tmp;
106 tmp.copyfmt(os);
107 if (mv.valid_ && !mv.is_fail_)
108 tmp << mv.value_;
109 else if (mv.valid_ && mv.is_fail_)
110 tmp << comppare::internal::ansi::RED(mv.value_);
111 else
112 tmp << comppare::internal::ansi::RED(mv.err_msg_);
113 std::string body = std::move(tmp).str();
114
115 os.width(0);
116 os << body;
117
118 os.copyfmt(saved);
119 return os;
120 }
121 };
122
126 template <typename>
127 struct is_metric_value : std::false_type
128 {
129 };
130
134 template <typename U>
135 struct is_metric_value<MetricValue<U>> : std::true_type
136 {
137 };
138
148 template <typename M>
150
156 template <typename M>
157 concept IsMetricValue = is_metric_value_v<M>;
158
159 /*
160 Concept for a valid Error Policy
161
162 It requires:
163 - metric_count() to return the number of metrics
164 - metric_name(std::size_t i) to return the name of the metric at index i
165 - compute_error(const Val &a, const Val &b, double tol) to compute the error
166 between two values a and b with a given tolerance tol -- or not
167 - metric(std::size_t) to return the value of the metric as MetricValue<T>
168 - is_fail() to return true if the error exceeds the tolerance
169 */
170
191 template <typename Val, typename EP>
192 concept ErrorPolicy = requires
193 // static members
194 {
195 { EP::metric_count() } -> std::convertible_to<std::size_t>;
196 { EP::metric_name(std::size_t{}) } -> std::convertible_to<std::string_view>;
197 } &&
198 // compute error -- either with or without tolerance
199 (requires(EP ep, const Val &a, const Val &b) { ep.compute_error(a, b); }) &&
200 // metric() returns value of the metric
201 (requires(EP ep, std::size_t i) {
202 { ep.metric(i) } -> IsMetricValue; } || requires(EP ep, std::size_t i) {
203 { ep.metric(i) } -> std::convertible_to<double>; } || requires(EP ep, std::size_t i) {
204 { ep.metric(i) } -> std::same_as<std::string>; }) &&
205 // is_fail() -- either with or without tolerance
206 (requires(EP ep) {
207 { ep.is_fail() } -> std::convertible_to<bool>; });
208
218 template <class EP, class V>
219 inline void compute_error(EP &ep, const V &a, const V &b)
220 {
221 ep.compute_error(a, b);
222 }
223
231 template <class EP>
232 inline bool is_fail(const EP &ep)
233 {
234 return ep.is_fail();
235 }
236
241 namespace autopolicy
242 {
253 template <typename T>
258
259 /*
260 Error Policy for scalar/numbers
261 */
262
271 template <typename T>
274 {
275 T error_ = T(0);
276 std::string err_msg_;
277 bool fail_{false};
278 bool valid_{true};
280
281 static constexpr std::array names{"Total|err|"};
282
283 public:
285 {
286 if constexpr (!std::is_floating_point_v<T>)
287 {
288 // cast tolerance to T for integral types
289 tolerance_ = static_cast<T>(comppare::config::fp_tolerance<double>());
290 }
291 else
292 {
293 // use tolerance as is for floating-point types
294 tolerance_ = comppare::config::fp_tolerance<T>();
295 }
296 }
297
299
300 static constexpr std::size_t metric_count() { return 1; }
301 static constexpr std::string_view metric_name(std::size_t) { return names[0]; }
302
303 MetricValue<T> metric(std::size_t) const
304 {
306 }
307
308 bool is_fail() const { return fail_; }
309
310 void compute_error(const T &a, const T &b)
311 {
312 if constexpr (std::is_floating_point_v<T>)
313 {
314 if (!std::isfinite(a) || !std::isfinite(b))
315 {
316 error_ = std::numeric_limits<T>::quiet_NaN();
317 err_msg_ = "NAN/INF";
318 valid_ = false;
319 return;
320 }
321 }
322
323 T e = std::abs(a - b);
324
325 if (e > tolerance_)
326 fail_ = true;
327
328 error_ = e;
329 }
330 };
331
337 {
338 bool eq_{true};
339
340 static constexpr std::array names{"Equal?"};
341
342 public:
343 StringEqualPolicy() = default;
345
346 static constexpr std::size_t metric_count() { return 1; }
347 static constexpr std::string_view metric_name(std::size_t) { return names[0]; }
348
350 {
351 return MetricValue<std::string>(eq_ ? "true" : "false", is_fail());
352 }
353
354 bool is_fail() const { return !eq_; }
355
356 void compute_error(const std::string &a, const std::string &b) { eq_ = (a == b); }
357 };
358
366 template <typename R>
369 {
370 using T = std::remove_cvref_t<std::ranges::range_value_t<R>>;
371
374 std::size_t elem_cnt_ = 0;
375
376 bool fail_{false};
377 bool valid_{true};
378 std::string err_msg_;
379
381
382 static constexpr std::array names{"Max|err|", "Mean|err|", "Total|err|"};
383
388
390 {
391 if (elem_cnt_ && valid_)
392 return MetricValue<T>(total_error_ / static_cast<T>(elem_cnt_), is_fail(), valid_, err_msg_);
393 else
394 return MetricValue<T>(T(0), is_fail(), valid_, err_msg_);
395 }
396
401
402 public:
404 {
405 if constexpr (!std::is_floating_point_v<T>)
406 {
407 // cast tolerance to T for integral types
408 tolerance_ = static_cast<T>(comppare::config::fp_tolerance<double>());
409 }
410 else
411 {
412 // use tolerance as is for floating-point types
413 tolerance_ = comppare::config::fp_tolerance<T>();
414 }
415 }
416
417 ~RangeErrorPolicy() = default;
418
419 static constexpr std::size_t metric_count() { return names.size(); }
420 static constexpr std::string_view metric_name(std::size_t i) { return names[i]; }
421
422 MetricValue<T> metric(std::size_t i) const
423 {
424 switch (i)
425 {
426 case 0:
427 return get_max();
428 case 1:
429 return get_mean();
430 case 2:
431 return get_total();
432 default:
433 throw std::out_of_range("Invalid metric index");
434 }
435 }
436
443 bool is_fail() const
444 {
445 if (valid_)
446 return fail_;
447 else
448 return false;
449 }
450
451 void compute_error(const R &a, const R &b)
452 {
453 if (std::ranges::size(a) != std::ranges::size(b))
454 {
455 // invalid if sizes are different
456 valid_ = false;
457 err_msg_ = "Size mismatch";
458 elem_cnt_ = 0;
459 return;
460 }
461
462 auto ia = std::ranges::begin(a);
463 auto ib = std::ranges::begin(b);
464 for (; ia != std::ranges::end(a) && ib != std::ranges::end(b); ++ia, ++ib)
465 {
466 if constexpr (std::is_floating_point_v<T>)
467 {
468 if (!std::isfinite(*ia) || !std::isfinite(*ib))
469 {
470 max_error_ = std::numeric_limits<T>::quiet_NaN();
471 total_error_ = std::numeric_limits<T>::quiet_NaN();
472 elem_cnt_ = 0;
473
474 // invalid if any element is NAN/INF
475 valid_ = false;
476 err_msg_ = "NAN/INF";
477 return;
478 }
479 }
480
481 T e = std::abs(*ia - *ib);
482
483 if (e > tolerance_)
484 {
485 fail_ = true;
486 elem_cnt_++;
487 }
488
489 total_error_ += e;
490 max_error_ = std::max(max_error_, e);
491 }
492 }
493 };
494
495
500 template <typename T>
502
508 template <typename T>
514
520 template <typename T>
522 struct AutoPolicy<T>
523 {
525 };
526
532 template <typename T>
534 struct AutoPolicy<T>
535 {
537 };
538
544 template <typename T>
546 }
547}
This file contains utilities for applying ANSI styles and colors to console output.
Wrapper for a value that can be streamed to an output stream.
Definition policy.hpp:75
T value_
Definition policy.hpp:76
MetricValue(T v, bool is_fail, bool valid, std::string_view msg)
Definition policy.hpp:85
MetricValue(T v)
Definition policy.hpp:83
bool is_fail_
Definition policy.hpp:77
bool valid_
Definition policy.hpp:79
MetricValue(T v, bool is_fail)
Definition policy.hpp:84
friend std::ostream & operator<<(std::ostream &os, MetricValue< T > const &mv)
Overloaded operator<< to stream the value or error message.
Definition policy.hpp:100
std::string_view err_msg_
Definition policy.hpp:80
Concept for types supported by automatic error policy selection.
Definition policy.hpp:274
MetricValue< T > metric(std::size_t) const
Definition policy.hpp:303
static constexpr std::size_t metric_count()
Definition policy.hpp:300
void compute_error(const T &a, const T &b)
Definition policy.hpp:310
static constexpr std::array names
Definition policy.hpp:281
static constexpr std::string_view metric_name(std::size_t)
Definition policy.hpp:301
Error policy for ranges of arithmetic types.
Definition policy.hpp:369
std::size_t elem_cnt_
Definition policy.hpp:374
std::remove_cvref_t< std::ranges::range_value_t< R > > T
Definition policy.hpp:370
MetricValue< T > metric(std::size_t i) const
Definition policy.hpp:422
static constexpr std::size_t metric_count()
Definition policy.hpp:419
MetricValue< T > get_mean() const
Definition policy.hpp:389
bool valid_
Indicates if the current error state is a failure.
Definition policy.hpp:377
MetricValue< T > get_max() const
Definition policy.hpp:384
std::string err_msg_
Indicates if the current error state is valid.
Definition policy.hpp:378
void compute_error(const R &a, const R &b)
Definition policy.hpp:451
MetricValue< T > get_total() const
Definition policy.hpp:397
bool is_fail() const
Checks if the current error state is a failure.
Definition policy.hpp:443
static constexpr std::string_view metric_name(std::size_t i)
Definition policy.hpp:420
static constexpr std::array names
Definition policy.hpp:382
Error policy for std::string. Compares two strings for equality.
Definition policy.hpp:337
static constexpr std::size_t metric_count()
Definition policy.hpp:346
void compute_error(const std::string &a, const std::string &b)
Definition policy.hpp:356
static constexpr std::string_view metric_name(std::size_t)
Definition policy.hpp:347
static constexpr std::array names
Definition policy.hpp:340
bool is_fail() const
Definition policy.hpp:354
MetricValue< std::string > metric(std::size_t) const
Definition policy.hpp:349
Concept for an arithmetic type (either floating-point or integral).
Definition concepts.hpp:78
Concept for a range of arithmetic types, excluding strings.
Definition concepts.hpp:103
Concept for a string type.
Definition concepts.hpp:87
Concept for a valid Error Policy.
Definition policy.hpp:192
Concept for a MetricValue.
Definition policy.hpp:157
Concept for types supported by automatic error policy selection.
Definition policy.hpp:254
This file contains commonly used concepts internally within the ComPPare library.
typename AutoPolicy< T >::type AutoPolicy_t
Helper alias to get the automatic error policy type for a given type T.
Definition policy.hpp:545
Definition policy.hpp:50
bool is_fail(const EP &ep)
Wrapper function to check if the error policy indicates a failure.
Definition policy.hpp:232
constexpr bool is_metric_value_v
Checks if a type is a MetricValue.
Definition policy.hpp:149
void compute_error(EP &ep, const V &a, const V &b)
Wrapper function to compute the error using the given instance of the error policy.
Definition policy.hpp:219
AutoPolicy is a empty struct with alias type to deduce the appropriate error policy.
Definition policy.hpp:501
Partial specialisation for not a MetricValue.
Definition policy.hpp:128