Line data Source code
1 : // Copyright (c) 2023 Made to Order Software Corp. All Rights Reserved
2 : //
3 : // https://snapwebsites.org/project/versiontheca
4 : // contact@m2osw.com
5 : //
6 : // This program is free software: you can redistribute it and/or modify
7 : // it under the terms of the GNU General Public License as published by
8 : // the Free Software Foundation, either version 3 of the License, or
9 : // (at your option) any later version.
10 : //
11 : // This program is distributed in the hope that it will be useful,
12 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : // GNU General Public License for more details.
15 : //
16 : // You should have received a copy of the GNU General Public License
17 : // along with this program. If not, see <https://www.gnu.org/licenses/>.
18 :
19 : /** \file
20 : * \brief Parse and compare decimal based versions.
21 : *
22 : * A decimal based version is a version which is formed with two numbers
23 : * separated by a decimal period (.) character. It can then be retrieved
24 : * as a floating point number.
25 : *
26 : * \note
27 : * It is possible that the result will not be exactly correct when retrieved
28 : * as a floating point. Internally, though, the version is kept as two
29 : * separate integers which are always perfectly defined.
30 : */
31 :
32 : // self
33 : //
34 : #include <versiontheca/decimal.h>
35 :
36 :
37 :
38 : // C++
39 : //
40 : #include <cmath>
41 : #include <iomanip>
42 : #include <iostream>
43 : #include <sstream>
44 :
45 :
46 : // last include
47 : //
48 : #include <snapdev/poison.h>
49 :
50 :
51 :
52 : namespace versiontheca
53 : {
54 :
55 :
56 :
57 10039 : bool decimal::parse(std::string const & v)
58 : {
59 10039 : if(!trait::parse(v))
60 : {
61 22 : return false;
62 : }
63 :
64 : // decimal version must be:
65 : //
66 : // 1. one or two parts
67 : // 2. separated by a period
68 : // 3. integers
69 : //
70 : // note: since we limit characters to only digits, clearly the parts
71 : // should already be integers or an error occurred earlier
72 : //
73 15033 : return (size() == 1 || size() == 2)
74 10015 : && at(0).is_integer()
75 30047 : && (size() == 1
76 15031 : || (at(1).get_separator() == '.' && at(1).is_integer()));
77 : }
78 :
79 :
80 21 : bool decimal::is_valid_character(char32_t c) const
81 : {
82 21 : return c >= '0' && c <= '9';
83 : }
84 :
85 :
86 10030 : std::string decimal::to_string() const
87 : {
88 : // ignore all .0 at the end except for the minor version
89 : // (i.e. "1.0" keep that zero)
90 : //
91 10030 : if(empty())
92 : {
93 3 : f_last_error = "no parts to output.";
94 3 : return std::string();
95 : }
96 :
97 :
98 10027 : part_integer_t fraction(0);
99 10027 : int width(1);
100 10027 : if(size() == 2)
101 : {
102 5025 : fraction = at(1).get_integer();
103 5025 : width = std::max(static_cast<std::uint8_t>(1), at(1).get_width());
104 : }
105 :
106 20054 : std::stringstream ss;
107 10027 : ss << at(0).get_integer()
108 : << '.'
109 : << std::setfill('0')
110 10027 : << std::setw(width)
111 10027 : << fraction;
112 :
113 10027 : return ss.str();
114 : }
115 :
116 :
117 : /** \brief Get the version as a floating point.
118 : *
119 : * This function converts the version of one or two parts in a floating
120 : * pointer number.
121 : *
122 : * If the version is considered invalid, this function returns a NaN.
123 : * You can verify such using the std::isnan() function.
124 : *
125 : * \note
126 : * Keep in mind that decimal numbers are not always properly represented
127 : * by floating point numbers. In most cases, for numbers with 2 or 3 digits
128 : * after the decimal point, it is likely to work as expected. Just don't
129 : * use the `==` to compare such numbers. Instead, look at our nearly_equal()
130 : * template function in snapcatch2/snapcatch2.hpp for an example of properly
131 : * comparing two floating point numbers of any precision.
132 : *
133 : * \return The version as a floating point double.
134 : */
135 10017 : double decimal::get_decimal_version() const
136 : {
137 10017 : if(size() > 0)
138 : {
139 10015 : double version(at(0).get_integer());
140 10015 : if(size() == 2)
141 : {
142 5014 : double const width(at(1).get_width());
143 5014 : double const fraction(at(1).get_integer());
144 : //double digits(1 + significant_zeroes);
145 : //if(fraction > 0)
146 : //{
147 : // digits += static_cast<int>(log(fraction));
148 : //}
149 5014 : version += fraction * pow(10, -width);
150 : }
151 10015 : return version;
152 : }
153 :
154 2 : return std::numeric_limits<double>::quiet_NaN();
155 : }
156 :
157 :
158 :
159 6 : }
160 : // namespace versiontheca
161 : // vim: ts=4 sw=4 et
|