• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

rlalik / HelloFitty / 16521874479

25 Jul 2025 12:23PM UTC coverage: 85.056% (-0.3%) from 85.39%
16521874479

push

github

rlalik
Add fit_result struct

19 of 29 new or added lines in 3 files covered. (65.52%)

4 existing lines in 1 file now uncovered.

535 of 629 relevant lines covered (85.06%)

20.52 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

72.22
/source/fitter.cpp
1
/*
2
    HelloFitty - a versatile histogram fitting tool for ROOT-based projects
3
    Copyright (C) 2015-2023  Rafał Lalik <rafallalik@gmail.com>
4

5
    This program is free software: you can redistribute it and/or modify
6
    it under the terms of the GNU General Public License as published by
7
    the Free Software Foundation, either version 3 of the License, or
8
    (at your option) any later version.
9

10
    This program is distributed in the hope that it will be useful,
11
    but WITHOUT ANY WARRANTY; without even the implied warranty of
12
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
    GNU General Public License for more details.
14

15
    You should have received a copy of the GNU General Public License
16
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
*/
18

19
#include <fmt/core.h>
20
#include <fmt/ranges.h>
21

22
#include "hellofitty.hpp"
23

24
#include "details.hpp"
25
#include "parser.hpp"
26

27
#include <TGraph.h>
28
#include <TH1.h>
29
#include <TList.h>
30

31
#include <fstream>
32

33
#if __cplusplus >= 201703L
34
#    include <filesystem>
35
#else
36
#    include <sys/stat.h>
37
#endif
38

39
bool hf::detail::fitter_impl::verbose_flag = true;
40

41
namespace
42
{
43
enum class source
44
{
45
    none,
46
    only_reference,
47
    only_auxiliary,
48
    reference,
49
    auxiliary
50
};
51

52
auto select_source(const char* filename, const char* auxname = nullptr) -> source
5✔
53
{
54
#if __cplusplus >= 201703L
55
    const auto s_ref = std::filesystem::exists(filename);
5✔
56
    const auto s_aux = std::filesystem::exists(auxname);
5✔
57

58
    if (!s_ref and !s_aux) { return source::none; }
5✔
59
    if (s_ref and !s_aux) { return source::only_reference; }
5✔
60
    if (!s_ref and s_aux) { return source::only_auxiliary; }
×
61

62
    const std::filesystem::file_time_type mod_ref = std::filesystem::last_write_time(filename);
×
63
    const std::filesystem::file_time_type mod_aux = std::filesystem::last_write_time(auxname);
×
64
#else
65
    struct stat st_ref;
66
    struct stat st_aux;
67

68
    const auto s_ref = stat(filename, &st_ref) == 0;
69
    const auto s_aux = stat(auxname, &st_aux) == 0;
70

71
    if (!s_ref and !s_aux) { return source::none; }
72
    if (s_ref and !s_aux) { return source::only_reference; }
73
    if (!s_ref and s_aux) { return source::only_auxiliary; }
74

75
    const auto mod_ref = (long long)st_ref.st_mtim.tv_sec;
76
    const auto mod_aux = (long long)st_aux.st_mtim.tv_sec;
77
#endif
78

79
    return mod_aux > mod_ref ? source::auxiliary : source::reference;
×
80
}
81

82
} // namespace
83

84
template<>
85
struct fmt::formatter<hf::entry>
86
{
87
    // Presentation format: 'f' - fixed, 'e' - exponential, 'g' - either.
88
    char presentation = 'g';
89

90
    // Parses format specifications of the form ['f' | 'e' | 'g'].
91
    CONSTEXPR auto parse(format_parse_context& ctx) -> format_parse_context::iterator
×
92
    {
93
        // Parse the presentation format and store it in the formatter:
94
        auto it = ctx.begin(), end = ctx.end();
×
95
        if (it != end && *it != '}') { FMT_THROW(format_error("invalid format")); }
×
96

97
        // Return an iterator past the end of the parsed range:
98
        return it;
×
99
    }
100

101
    auto format(const hf::entry& /*fitentry*/, format_context& ctx) const -> format_context::iterator
×
102
    {
103
        return ctx.out();
×
104
    }
105
};
106

107
namespace hf
108
{
109

110
auto fitter::set_verbose(bool verbose) -> void { detail::fitter_impl::verbose_flag = verbose; }
4✔
111

112
fitter::fitter()
13✔
113
    : m_d {make_unique<detail::fitter_impl>()}
13✔
114
{
115
    m_d->mode = priority_mode::newer;
13✔
116
}
13✔
117

118
fitter::fitter(fitter&&) = default;
×
119

120
auto fitter::operator=(fitter&&) -> fitter& = default;
×
121

122
fitter::~fitter() = default;
13✔
123

124
auto fitter::init_from_file(std::string filename) -> bool
5✔
125
{
126
    m_d->par_ref = std::move(filename);
5✔
127

128
    if (!m_d->par_ref.c_str()) { fmt::print(stderr, "No reference input file given\n"); }
5✔
129
    if (!m_d->par_aux.c_str()) { fmt::print(stderr, "No output file given\n"); }
5✔
130

131
    auto selected = select_source(m_d->par_ref.c_str(), m_d->par_aux.c_str());
5✔
132

133
    fmt::print("Available source: [{:c}] REF  [{:c}] AUX\n",
5✔
134
               selected != source::only_auxiliary and selected != source::none ? 'x' : ' ',
5✔
135
               selected != source::only_reference and selected != source::none ? 'x' : ' ');
10✔
136
    fmt::print("Selected source : [{:c}] REF  [{:c}] AUX\n",
5✔
137
               (selected == source::reference or selected == source::only_reference) ? 'x' : ' ',
5✔
138
               (selected == source::auxiliary or selected == source::only_auxiliary) ? 'x' : ' ');
10✔
139

140
    if (selected == source::none) { return false; }
5✔
141

142
    if (m_d->mode == priority_mode::reference)
5✔
143
    {
144
        if (selected == source::only_auxiliary) { return false; }
1✔
145
        else { return import_parameters(m_d->par_ref); }
1✔
146
    }
147

148
    if (m_d->mode == priority_mode::auxiliary)
4✔
149
    {
150
        if (selected == source::only_reference) { return false; }
×
151
        else { return import_parameters(m_d->par_aux); }
×
152
    }
153

154
    if (m_d->mode == priority_mode::newer)
4✔
155
    {
156
        if (selected == source::auxiliary or selected == source::only_auxiliary)
4✔
157
        {
158
            return import_parameters(m_d->par_aux);
×
159
        }
160
        else if (selected == source::reference or selected == source::only_reference)
4✔
161
        {
162
            return import_parameters(m_d->par_ref);
4✔
163
        }
164
    }
165

166
    return false;
167
}
168

169
auto fitter::init_from_file(std::string filename, std::string auxname, priority_mode mode) -> bool
5✔
170
{
171
    m_d->mode = mode;
5✔
172
    m_d->par_aux = std::move(auxname);
5✔
173
    return init_from_file(std::move(filename));
5✔
174
}
175

176
auto fitter::export_to_file(bool update_reference) -> bool
5✔
177
{
178
    if (!update_reference) { return export_parameters(m_d->par_aux); }
5✔
179
    else { return export_parameters(m_d->par_ref); }
×
180
}
181

182
auto fitter::insert_parameter(std::pair<std::string, entry> hfp) -> entry*
10✔
183
{
184
    auto res = m_d->hfpmap.emplace(std::move(hfp));
10✔
185
    if (!res.second) { res.first->second = hfp.second; }
10✔
186

187
    return &res.first->second;
10✔
188
}
189

190
auto fitter::insert_parameter(std::string name, entry hfp) -> entry*
5✔
191
{
192
    return insert_parameter(std::make_pair(std::move(name), std::move(hfp)));
10✔
193
}
194

195
auto fitter::import_parameters(const std::string& filename) -> bool
5✔
196
{
197
    std::ifstream fparfile(filename.c_str());
5✔
198
    if (!fparfile.is_open())
5✔
199
    {
200
        fmt::print(stderr, "No file {:s} to open.\n", filename);
×
201
        return false;
×
202
    }
203

204
    m_d->hfpmap.clear();
5✔
205

206
    std::string line;
5✔
207
    while (std::getline(fparfile, line))
10✔
208
    {
209
        insert_parameter(tools::parse_line_entry(line, m_d->input_format_version));
10✔
210
    }
211

212
    return true;
5✔
213
}
5✔
214

215
auto fitter::export_parameters(const std::string& filename) -> bool
5✔
216
{
217
    std::ofstream fparfile(filename);
5✔
218
    if (!fparfile.is_open())
5✔
219
    {
220
        fmt::print(stderr, "Can't create output file {:s}. Skipping...\n", filename);
×
221
        return false;
×
222
    }
223
    else
224
    {
225
        fmt::print("Output file {:s} opened...  Exporting {:d} entries.\n", filename, m_d->hfpmap.size());
5✔
226
        for (auto it = m_d->hfpmap.begin(); it != m_d->hfpmap.end(); ++it)
11✔
227
        {
228
            fparfile << tools::format_line_entry(it->first, &it->second, m_d->output_format_version) << std::endl;
12✔
229
        }
230
    }
231
    return true;
5✔
232
}
5✔
233

234
auto fitter::find_fit(TH1* hist) const -> entry* { return find_fit(hist->GetName()); }
3✔
235

236
auto fitter::find_fit(const char* name) const -> entry*
17✔
237
{
238
    auto it = m_d->hfpmap.find(tools::format_name(name, m_d->name_decorator));
34✔
239
    if (it != m_d->hfpmap.end()) { return &it->second; }
17✔
240

241
    return nullptr;
242
}
243

244
auto fitter::find_or_make(TH1* hist, entry* generic) -> entry* { return find_or_make(hist->GetName(), generic); }
×
245

246
auto fitter::find_or_make(const char* name, entry* generic) -> entry*
10✔
247
{
248
    entry* hfp = find_fit(name);
10✔
249
    if (!hfp and generic)
10✔
250
    {
251
        if (detail::fitter_impl::verbose_flag)
4✔
252
        {
253
            fmt::print("HFP for histogram {:s} not found, trying from generic.\n", name);
4✔
254
        }
255

256
        if (!generic->get_functions_count()) { throw std::logic_error("Generic Fit Entry has no functions."); }
4✔
257

258
        hfp = insert_parameter(std::string(name), *generic);
3✔
259
        if (!hfp) { throw std::logic_error("Could not insert new parameter."); }
3✔
260

261
        if (detail::fitter_impl::verbose_flag) { fmt::print("HFP for histogram {:s} created from generic.\n", name); }
3✔
262
    }
263

264
    return hfp;
9✔
265
}
266

267
auto fitter::fit(TH1* hist, const char* pars, const char* gpars) -> fit_result
4✔
268
{
269
    entry* hfp = find_or_make(hist->GetName());
4✔
270
    if (!hfp) { return {fit_status::missing_entry, hfp}; }
4✔
271

272
    return fit(hfp, hist, pars, gpars);
3✔
273
}
1✔
274

275
auto fitter::fit(TH1* hist, entry* generic, const char* pars, const char* gpars) -> fit_result
5✔
276
{
277
    entry* hfp = find_or_make(hist->GetName(), generic);
5✔
278
    if (!hfp) { return {fit_status::missing_entry, hfp}; }
4✔
279

280
    return fit(hfp, hist, pars, gpars);
4✔
UNCOV
281
}
×
282

283
auto fitter::fit(entry* custom, TH1* hist, const char* pars, const char* gpars) -> fit_result
9✔
284
{
285
    custom->backup();
9✔
286

287
    Int_t bin_l = hist->FindBin(custom->get_fit_range_min());
9✔
288
    Int_t bin_u = hist->FindBin(custom->get_fit_range_max());
9✔
289

290
    if (custom->get_flag_rebin() != 0) { hist->Rebin(custom->get_flag_rebin()); }
9✔
291

292
    if (bin_u - bin_l == 0) { return {fit_status::empty_range, custom}; }
9✔
293
    // if (hist->Integral(bin_l, bin_u) == 0) return {false, hfp};
294

295
    auto fit_result = m_d->generic_fit(custom, custom->m_d.get(), hist->GetName(), hist, pars, gpars);
9✔
296
    if (fit_result.status != fit_status::ok) { custom->restore(); }
9✔
297

298
    return fit_result;
9✔
299
}
9✔
300

NEW
301
auto fitter::fit(const char* name, TGraph* graph, const char* pars, const char* gpars) -> fit_result
×
302
{
303
    entry* hfp = find_or_make(name);
×
NEW
304
    if (!hfp) { return {fit_status::missing_entry, hfp}; }
×
305

306
    return fit(hfp, name, graph, pars, gpars);
×
UNCOV
307
}
×
308

NEW
309
auto fitter::fit(const char* name, TGraph* graph, entry* generic, const char* pars, const char* gpars) -> fit_result
×
310
{
311
    entry* hfp = find_or_make(name, generic);
×
NEW
312
    if (!hfp) { return {fit_status::missing_entry, hfp, TFitResultPtr()}; }
×
313

314
    return fit(hfp, name, graph, pars, gpars);
×
UNCOV
315
}
×
316

NEW
317
auto fitter::fit(entry* custom, const char* name, TGraph* graph, const char* pars, const char* gpars) -> fit_result
×
318
{
319
    custom->backup();
×
320

NEW
321
    auto fit_result = m_d->generic_fit(custom, custom->m_d.get(), name, graph, pars, gpars);
×
NEW
322
    if (fit_result.status != fit_status::ok) { custom->restore(); }
×
323

NEW
324
    return fit_result;
×
UNCOV
325
}
×
326

327
auto fitter::set_name_decorator(std::string decorator) -> void { m_d->name_decorator = std::move(decorator); }
1✔
328

329
auto fitter::clear_name_decorator() -> void { m_d->name_decorator = "*"; }
×
330

331
auto fitter::set_function_decorator(std::string decorator) -> void { m_d->function_decorator = std::move(decorator); }
1✔
332

333
auto fitter::set_function_style(int function_index) -> draw_opts&
12✔
334
{
335
    auto res = m_d->partial_functions_styles.insert({function_index, draw_opts()});
24✔
336
    return res.first->second;
12✔
337
}
338

339
auto fitter::set_function_style() -> draw_opts& { return set_function_style(-1); }
4✔
340

341
auto fitter::set_qa_checker(fit_qa_checker checker) -> void { m_d->checker = std::move(checker); }
×
342

343
auto fitter::print() const -> void
4✔
344
{
345
    for (auto it = m_d->hfpmap.begin(); it != m_d->hfpmap.end(); ++it)
8✔
346
    {
347
        it->second.print(it->first);
4✔
348
    }
349
}
4✔
350

351
auto fitter::clear() -> void { m_d->hfpmap.clear(); }
5✔
352

353
} // namespace hf
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc