Compare commits

..

14 Commits

Author SHA1 Message Date
suter_a 5bbcb37370 PMusrCanvas: replace TObjArray/TObjString with PStringUtils
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 15:45:24 +02:00
suter_a 66aa847b58 PFitter: replace TObjArray/TObjString with PStringUtils
Replace ROOT's TString::Tokenize() + TObjArray/TObjString token
handling with the dependency-free PStringUtils::Split() across all
command/theory parsing in PFitter (GetPhaseParams, GetParFromFun,
CheckCommands and the Execute* helpers). Split() mirrors Tokenize()
semantics (delimiter-set, skips empty tokens), so token counts and
indices are unchanged. Each token is still copied into a TString,
so the downstream Atoi/Atof/IsFloat/IsDigit/Contains/CompareTo logic
stays as-is.

Using a std::vector<std::string> removes the manual TObjArray
cleanup and incidentally fixes three pre-existing leaks: the
tokens array in ExecuteFitRange was never freed, and the early
return paths in ExecutePrintLevel and the SECTOR check in
CheckCommands skipped the cleanup.

Build of libPMusr and musrfit is clean; full ctest suite passes
(85/85).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 15:38:15 +02:00
suter_a 76070f2098 msr2msr: replace TObjArray/TObjString with PStringUtils
Replace ROOT's TString::Tokenize() + TObjArray/TObjString token
handling with the dependency-free PStringUtils::Split() across
msr2msr_run, msr2msr_param and msr2msr_theory. Split() mirrors
Tokenize() semantics (skips empty tokens), so token counts and
indices are unchanged.

Using a std::vector<std::string> removes the manual TObjArray
cleanup and incidentally fixes a pre-existing leak in
msr2msr_theory, which tokenized in every branch but never deleted
the TObjArray.

Since msr2msr links only against ROOT (not PMusr, where
PStringUtils lives) and PStringUtils is pure C++17, compile
classes/PStringUtils.cpp directly into the msr2msr executable
rather than pulling in the whole PMusr library.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 15:16:50 +02:00
suter_a e7af0a781e libCuba: guard memcpy in SobolIni against negative length
GCC's -Wstringop-overflow flagged the memcpy in SobolIni() with a bound
of (size_t)(-4): on the (in practice unreachable) path where the Sobol
generator polynomial 'powers' is 0, the bit-count loop leaves inibits at
its initial -1, so inibits*sizeof underflows. The generator-polynomial
table always has a non-zero first column, so this never happens at run
time, but the compiler cannot prove it.

Guard the copy with 'if (inibits > 0)', which silences the false-positive
warning and hardens the edge case without changing behaviour for valid
input.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 15:02:17 +02:00
suter_a 8cdbb24929 PRun*: use reduction(+:mllh) instead of deprecated reduction(-:mllh)
OpenMP 5.2 deprecates the '-' reduction operator (-Wdeprecated-openmp)
because it is functionally identical to '+': the private reduction copy
is initialised to 0 and partial results are combined by addition in both
cases. All affected loops accumulate with 'mllh += ...', so switching to
reduction(+:mllh) is results-identical and silences the warning.

Fixes the pragma in PRunMuMinus.cpp and the two in PRunSingleHisto.cpp,
and corrects the now-inaccurate "for subtraction" doc comment.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 14:50:12 +02:00
suter_a b2db41194f musredit: capture 'this' explicitly to silence C++20 [-Wdeprecated]
The QProcess::finished lambdas used [=], which implicitly captures 'this'
to call member functions (exitStatusMusrWiz, fileReload,
exitStatusMusrSetSteps). Implicit 'this' capture via [=] is deprecated in
C++20; name it explicitly with [=, this]. Applied to both the qt6 and qt5
copies.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 14:43:11 +02:00
suter_a e3e84a6e56 PMsrHandler: escape '?' to avoid trigraph warning in date placeholder
The fallback date placeholder string contained the sequence '??-', which
the compiler interprets as a trigraph for '~' (-Wtrigraphs warning).
Escape the question marks (\?) so the literal string is unchanged while
the warning is silenced.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 14:37:35 +02:00
suter_a 1a85444763 PStartupHandler: replace TObjArray/TObjString with PStringUtils
Replace the ROOT TString::Tokenize()/TObjArray/TObjString machinery used
for parsing the RGB color code in OnCharacters() with the C++17
PStringUtils helpers (Split/IsInt/ToInt). Drops the manual heap cleanup
and the <TObjArray.h>/<TObjString.h> includes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 14:32:17 +02:00
suter_a 07f9c744b3 PStringUtils::IsInt: accept an optional leading sign
IsInt now recognises (possibly signed) integers such as "-5" or "+42",
making it slightly more permissive than TString::IsDigit(). A lone sign,
a double sign, or a sign following a digit are still rejected. strToNum
test expectations updated accordingly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 14:14:11 +02:00
suter_a aa5cdf8d6a add strToNum test driver for PStringUtils
Stand-alone test under src/tests/strToNum exercising every PStringUtils
method (Split / IsInt / IsFloat / ToInt / ToDouble / IsEqualNoCase /
ContainsNoCase / BeginsWithNoCase). Three modes: built-in pass/fail
suite, ad-hoc inspection of command-line strings, and an interactive
prompt. Adds -h/--help and -i/--interactive options. Pure C++17, no
ROOT dependency.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 14:08:25 +02:00
suter_a bb32426005 PStringUtils::ToDouble: signal conversion errors like ToInt
ToDouble() had the same shortcoming as ToInt(): wrapping strtod() with a
nullptr endptr made a failed conversion indistinguishable from a
legitimate 0.0. Add an optional `bool *ok` out-parameter that reports
success. strtod() (with endptr + errno) is kept instead of
std::from_chars so the accepted input set stays identical to
TString::Atof() (leading whitespace skipped, leading '+' honoured,
trailing characters ignored); ok is set false on a non-numeric string or
an ERANGE overflow. The parameter defaults to nullptr, so existing call
sites keep compiling unchanged.

Convert the IsFloat-guarded ToDouble call sites in PMsrHandler to the
single-parse ToDouble(token, &ok) form (replacing the IsFloat() guard +
separate ToDouble() that parsed every token twice). All downstream
>=0 / <=0 / range checks are preserved, and push_back sites only append
on success so no spurious 0.0 is stored on error. Number-vs-keyword
discriminators (pos.error/boundary "none", rrf_phase/fourier-phase parX)
are restructured so the keyword branch is taken when ok is false.

As a side effect this fixes a latent gap in the GLOBAL rrf_freq handler,
where a non-numeric frequency previously slipped through with a stale
value instead of raising an error.

The IsFloat-guarded ToInt fit-range offsets (fgb/lgb) are intentionally
left untouched, as there the guard type differs from the conversion.

All 85 integration tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 12:08:01 +02:00
suter_a 4319b4ad69 PStringUtils::ToInt: signal conversion errors via from_chars
ToInt() previously wrapped strtol() with a nullptr endptr, so a failed
conversion was indistinguishable from a legitimate 0 (matching the old
TString::Atoi() behaviour). Switch the implementation to std::from_chars
and add an optional `bool *ok` out-parameter that reports success: it is
set to false on a non-numeric string or an out-of-range value, true
otherwise. Leading whitespace is skipped and trailing characters are
ignored, preserving the Atoi-like prefix semantics. The parameter
defaults to nullptr, so existing call sites keep compiling unchanged.

Convert the parse-validation call sites in PMsrHandler to the single
-parse ToInt(token, &ok) form, replacing the IsInt() guard + separate
ToInt() (which parsed every token twice). All downstream >0 / >=0 /
range / enum checks are preserved.

Left untouched the call sites where IsInt() acts as a structural
discriminator rather than a numeric validator (write path, xy-data
index-vs-label, fParamInUse usage scans) and the IsFloat-guarded ToInt
offsets, where switching to ToInt(&ok) would change parsing semantics.

All 85 integration tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 11:54:33 +02:00
suter_a b072a481ba PMsrHandler: replace ROOT tokenizer machinery with C++17 PStringUtils
Reduce the ROOT footprint of the MSR parser by removing the pervasive
TString::Tokenize / TObjArray / TObjString / dynamic_cast pattern (28
tokenize sites, 14 TObjArray, 106 TObjString) used to split lines into
tokens, together with the manual `delete tokens` cleanup.

Add a new dependency-free C++17 utility class PStringUtils (Split, IsInt,
IsFloat, ToInt, ToDouble, IsEqualNoCase, ContainsNoCase, BeginsWithNoCase)
that replicates the relevant TString semantics exactly, so it can be reused
elsewhere in the suite. IsInt/IsFloat tolerate surrounding whitespace to
match TString::IsDigit/IsFloat (needed for tokens split on ',' / ';' only).

The public API and the PMusr.h data structures keep TString unchanged; only
the internal tokenizing logic is rewritten. Net -451 lines in
PMsrHandler.cpp. All 85 integration tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 11:15:02 +02:00
suter_a dd604d4bf6 add mupp/Qt6 python option.
Build and Deploy Documentation / build-and-deploy (push) Successful in 31s
2026-05-28 09:30:52 +02:00
18 changed files with 1438 additions and 1388 deletions
+1 -1
View File
@@ -6,7 +6,7 @@ if (CMAKE_VERSION GREATER_EQUAL "3.3")
cmake_policy(SET CMP0167 NEW)
endif ()
project(musrfit VERSION 1.10.0 LANGUAGES C CXX)
project(musrfit VERSION 1.11.0 LANGUAGES C CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
+7
View File
@@ -12,6 +12,13 @@ or
https://bitbucket.org/muonspin/musrfit
Release of V1.11.0, 2026/05/28
==============================
add to the mupp/Qt6 version the option to handle variables not only via the
Spirit/X3 interface, but also via Python3. It requires that ROOT is compiled
with Python support. For details see the musrfit-manual section mupp.
Release of V1.10.0, 2026/02/21
==============================
+1 -1
View File
@@ -75,7 +75,7 @@ target_include_directories(msr2data
)
target_link_libraries(msr2data ${ROOT_LIBRARIES} ${MUSRFIT_LIBS})
add_executable(msr2msr msr2msr.cpp)
add_executable(msr2msr msr2msr.cpp classes/PStringUtils.cpp)
target_link_libraries(msr2msr ${ROOT_LIBRARIES})
add_executable(musrfit musrfit.cpp)
+2
View File
@@ -106,6 +106,7 @@ add_library(PMusr SHARED
PMsgBoxDict.cxx
PMsr2Data.cpp
PMsrHandler.cpp
PStringUtils.cpp
PMusrCanvas.cpp
PMusrCanvasDict.cxx
PMusr.cpp
@@ -270,6 +271,7 @@ install(
${MUSRFIT_INC}/PRunSingleHisto.h
${MUSRFIT_INC}/PRunSingleHistoRRF.h
${MUSRFIT_INC}/PStartupHandler.h
${MUSRFIT_INC}/PStringUtils.h
${MUSRFIT_INC}/PTheory.h
${MUSRFIT_INC}/PUserFcnBase.h
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
+61 -239
View File
@@ -64,10 +64,9 @@
#include <TFile.h>
#include <TDatime.h>
#include <TString.h>
#include <TObjArray.h>
#include <TObjString.h>
#include "PFitter.h"
#include "PStringUtils.h"
//+++ PSectorChisq class +++++++++++++++++++++++++++++++++++++++++++++++++++
@@ -397,8 +396,6 @@ void PFitter::GetPhaseParams()
// default functions:
// user functions cannot be checked!
PMsrLines *theo = fRunInfo->GetMsrTheory();
TObjArray *tok = nullptr;
TObjString *ostr = nullptr;
TString str;
int pos = -1;
for (unsigned int i=0; i<theo->size(); i++) {
@@ -423,18 +420,10 @@ void PFitter::GetPhaseParams()
continue;
// extract phase token
tok = line.Tokenize(" \t");
if (tok == nullptr) {
std::cerr << "PFitter::GetPhaseParams(): **ERROR** couldn't tokenize theory line string." << std::endl;
return;
std::vector<std::string> tok = PStringUtils::Split(line.Data(), " \t");
if (static_cast<int>(tok.size()) > pos) {
str = tok[pos].c_str();
}
if (tok->GetEntries() > pos) {
ostr = dynamic_cast<TObjString*>(tok->At(pos));
str = ostr->GetString();
}
// clean up
delete tok;
tok = nullptr;
// decode phase token. It can be funX, mapX, or a number
if (str.Contains("fun")) { // function
@@ -491,22 +480,15 @@ PIntVector PFitter::GetParFromFun(const TString funStr)
PIntVector parVec;
PMsrLines *funList = fRunInfo->GetMsrFunctions();
TObjArray *tok = nullptr;
TObjString *ostr = nullptr;
TString str;
for (int i=0; i<funList->size(); i++) {
if (funList->at(i).fLine.Contains(funStr)) {
// tokenize function string
tok = funList->at(i).fLine.Tokenize(" =+-*/");
if (tok == nullptr) {
std::cerr << "PFitter::GetParFromFun(): **ERROR** couldn't tokenize function string." << std::endl;
return parVec;
}
std::vector<std::string> tok = PStringUtils::Split(funList->at(i).fLine.Data(), " =+-*/");
for (int j=1; j<tok->GetEntries(); j++) {
ostr = dynamic_cast<TObjString*>(tok->At(j));
str = ostr->GetString();
for (int j=1; j<static_cast<int>(tok.size()); j++) {
str = tok[j].c_str();
// parse tok for parX
if (str.Contains("par")) {
// find start idx of par in token
@@ -533,10 +515,6 @@ PIntVector PFitter::GetParFromFun(const TString funStr)
}
}
}
// clean up
delete tok;
tok = nullptr;
}
}
@@ -996,16 +974,14 @@ Bool_t PFitter::CheckCommands()
cmd.second = cmdLineNo;
fCmdList.push_back(cmd);
// filter out possible parameters for scan
TObjArray *tokens = nullptr;
TObjString *ostr;
std::vector<std::string> tokens;
TString str;
UInt_t ival;
tokens = line.Tokenize(", \t");
tokens = PStringUtils::Split(line.Data(), ", \t");
for (Int_t i=0; i<tokens->GetEntries(); i++) {
ostr = dynamic_cast<TObjString*>(tokens->At(i));
str = ostr->GetString();
for (Int_t i=0; i<static_cast<int>(tokens.size()); i++) {
str = tokens[i].c_str();
if ((i==1) || (i==2)) { // parX / parY
// check that token is a UInt_t
@@ -1016,10 +992,6 @@ Bool_t PFitter::CheckCommands()
std::cerr << std::endl << ">> command syntax for CONTOURS is: CONTOURS parameter-X parameter-Y [# of points]";
std::cerr << std::endl;
fIsValid = false;
if (tokens) {
delete tokens;
tokens = nullptr;
}
break;
}
ival = str.Atoi();
@@ -1031,10 +1003,6 @@ Bool_t PFitter::CheckCommands()
std::cerr << std::endl << ">> command syntax for CONTOURS is: CONTOURS parameter-X parameter-Y [# of points]";
std::cerr << std::endl;
fIsValid = false;
if (tokens) {
delete tokens;
tokens = nullptr;
}
break;
}
// keep parameter
@@ -1051,10 +1019,6 @@ Bool_t PFitter::CheckCommands()
std::cerr << std::endl << ">> command syntax for CONTOURS is: CONTOURS parameter-X parameter-Y [# of points]";
std::cerr << std::endl;
fIsValid = false;
if (tokens) {
delete tokens;
tokens = nullptr;
}
break;
}
ival = str.Atoi();
@@ -1065,20 +1029,12 @@ Bool_t PFitter::CheckCommands()
std::cerr << std::endl << ">> command syntax for CONTOURS is: CONTOURS parameter-X parameter-Y [# of points]";
std::cerr << std::endl;
fIsValid = false;
if (tokens) {
delete tokens;
tokens = nullptr;
}
break;
}
fScanNoPoints = ival;
}
}
if (tokens) {
delete tokens;
tokens = nullptr;
}
} else if (line.Contains("EIGEN", TString::kIgnoreCase)) {
cmd.first = PMN_EIGEN;
cmd.second = cmdLineNo;
@@ -1090,15 +1046,13 @@ Bool_t PFitter::CheckCommands()
// (iii) FIT_RANGE start1 end1 start2 end2 ... startN endN
// (iv) FIT_RANGE fgb+n0 lgb-n1
// (v) FIT_RANGE fgb+n00 lgb-n01 fgb+n10 lgb-n11 ... fgb+nN0 lgb-nN1
TObjArray *tokens = nullptr;
TObjString *ostr;
std::vector<std::string> tokens;
TString str;
tokens = line.Tokenize(", \t");
tokens = PStringUtils::Split(line.Data(), ", \t");
if (tokens->GetEntries() == 2) { // should only be RESET
ostr = dynamic_cast<TObjString*>(tokens->At(1));
str = ostr->GetString();
if (static_cast<int>(tokens.size()) == 2) { // should only be RESET
str = tokens[1].c_str();
if (str.Contains("RESET", TString::kIgnoreCase)) {
cmd.first = PMN_FIT_RANGE;
cmd.second = cmdLineNo;
@@ -1110,32 +1064,23 @@ Bool_t PFitter::CheckCommands()
std::cerr << std::endl << ">> with N the number of runs in the msr-file." << std::endl;
std::cerr << std::endl << ">> Found " << str.Data() << ", instead of RESET" << std::endl;
fIsValid = false;
if (tokens) {
delete tokens;
tokens = nullptr;
}
break;
}
} else if ((tokens->GetEntries() > 1) && (static_cast<UInt_t>(tokens->GetEntries()) % 2) == 1) {
if ((tokens->GetEntries() > 3) && ((static_cast<UInt_t>(tokens->GetEntries())-1)) != 2*fRunInfo->GetMsrRunList()->size()) {
} else if ((static_cast<int>(tokens.size()) > 1) && (static_cast<UInt_t>(static_cast<int>(tokens.size())) % 2) == 1) {
if ((static_cast<int>(tokens.size()) > 3) && ((static_cast<UInt_t>(static_cast<int>(tokens.size()))-1)) != 2*fRunInfo->GetMsrRunList()->size()) {
std::cerr << std::endl << ">> PFitter::CheckCommands: **ERROR** in line " << it->fLineNo;
std::cerr << std::endl << ">> " << line.Data();
std::cerr << std::endl << ">> Syntax: FIT_RANGE RESET | FIT_RANGE <start> <end> | FIT_RANGE <s1> <e1> <s2> <e2> .. <sN> <eN> |";
std::cerr << std::endl << ">> FIT_RANGE fgb+<n0> lgb-<n1> | FIT_RANGE fgb+<n00> lgb-<n01> fgb+<n10> lgb-<n11> ... fgb+<nN0> lgb-<nN1>,";
std::cerr << std::endl << ">> with N the number of runs in the msr-file.";
std::cerr << std::endl << ">> Found N=" << (tokens->GetEntries()-1)/2 << ", # runs in msr-file=" << fRunInfo->GetMsrRunList()->size() << std::endl;
std::cerr << std::endl << ">> Found N=" << (static_cast<int>(tokens.size())-1)/2 << ", # runs in msr-file=" << fRunInfo->GetMsrRunList()->size() << std::endl;
fIsValid = false;
if (tokens) {
delete tokens;
tokens = nullptr;
}
break;
} else {
// check that all range entries are numbers or fgb+n0 / lgb-n1
Bool_t ok = true;
for (Int_t n=1; n<tokens->GetEntries(); n++) {
ostr = dynamic_cast<TObjString*>(tokens->At(n));
str = ostr->GetString();
for (Int_t n=1; n<static_cast<int>(tokens.size()); n++) {
str = tokens[n].c_str();
if (!str.IsFloat()) {
if ((n%2 == 1) && (!str.Contains("fgb", TString::kIgnoreCase)))
ok = false;
@@ -1158,10 +1103,6 @@ Bool_t PFitter::CheckCommands()
std::cerr << std::endl << ">> with N the number of runs in the msr-file.";
std::cerr << std::endl << ">> Found token '" << str.Data() << "', which is not a floating point number." << std::endl;
fIsValid = false;
if (tokens) {
delete tokens;
tokens = nullptr;
}
break;
}
}
@@ -1172,29 +1113,19 @@ Bool_t PFitter::CheckCommands()
std::cerr << std::endl << ">> FIT_RANGE fgb+<n0> lgb-<n1> | FIT_RANGE fgb+<n00> lgb-<n01> fgb+<n10> lgb-<n11> ... fgb+<nN0> lgb-<nN1>,";
std::cerr << std::endl << ">> with N the number of runs in the msr-file.";
fIsValid = false;
if (tokens) {
delete tokens;
tokens = nullptr;
}
break;
}
if (tokens) {
delete tokens;
tokens = nullptr;
}
} else if (line.Contains("FIX", TString::kIgnoreCase)) {
// check if the given set of parameters (number or names) is present
TObjArray *tokens = nullptr;
TObjString *ostr;
std::vector<std::string> tokens;
TString str;
UInt_t ival;
tokens = line.Tokenize(", \t");
tokens = PStringUtils::Split(line.Data(), ", \t");
for (Int_t i=1; i<tokens->GetEntries(); i++) {
ostr = dynamic_cast<TObjString*>(tokens->At(i));
str = ostr->GetString();
for (Int_t i=1; i<static_cast<int>(tokens.size()); i++) {
str = tokens[i].c_str();
if (str.IsDigit()) { // token might be a parameter number
ival = str.Atoi();
@@ -1205,10 +1136,6 @@ Bool_t PFitter::CheckCommands()
std::cerr << std::endl << ">> Parameter " << ival << " is out of the Parameter Range [1," << fParams.size() << "]";
std::cerr << std::endl;
fIsValid = false;
if (tokens) {
delete tokens;
tokens = nullptr;
}
break;
}
} else { // token might be a parameter name
@@ -1226,19 +1153,11 @@ Bool_t PFitter::CheckCommands()
std::cerr << std::endl << ">> Parameter '" << str.Data() << "' is NOT present as a parameter name";
std::cerr << std::endl;
fIsValid = false;
if (tokens) {
delete tokens;
tokens = nullptr;
}
break;
}
}
}
if (tokens) {
delete tokens;
tokens = nullptr;
}
// everything looks fine, feed the command list
cmd.first = PMN_FIX;
@@ -1278,16 +1197,14 @@ Bool_t PFitter::CheckCommands()
fCmdList.push_back(cmd);
} else if (line.Contains("RELEASE", TString::kIgnoreCase)) {
// check if the given set of parameters (number or names) is present
TObjArray *tokens = nullptr;
TObjString *ostr;
std::vector<std::string> tokens;
TString str;
UInt_t ival;
tokens = line.Tokenize(", \t");
tokens = PStringUtils::Split(line.Data(), ", \t");
for (Int_t i=1; i<tokens->GetEntries(); i++) {
ostr = dynamic_cast<TObjString*>(tokens->At(i));
str = ostr->GetString();
for (Int_t i=1; i<static_cast<int>(tokens.size()); i++) {
str = tokens[i].c_str();
if (str.IsDigit()) { // token might be a parameter number
ival = str.Atoi();
@@ -1298,10 +1215,6 @@ Bool_t PFitter::CheckCommands()
std::cerr << std::endl << ">> Parameter " << ival << " is out of the Parameter Range [1," << fParams.size() << "]";
std::cerr << std::endl;
fIsValid = false;
if (tokens) {
delete tokens;
tokens = nullptr;
}
break;
}
} else { // token might be a parameter name
@@ -1319,19 +1232,11 @@ Bool_t PFitter::CheckCommands()
std::cerr << std::endl << ">> Parameter '" << str.Data() << "' is NOT present as a parameter name";
std::cerr << std::endl;
fIsValid = false;
if (tokens) {
delete tokens;
tokens = nullptr;
}
break;
}
}
}
if (tokens) {
delete tokens;
tokens = nullptr;
}
cmd.first = PMN_RELEASE;
cmd.second = cmdLineNo;
fCmdList.push_back(cmd);
@@ -1348,16 +1253,14 @@ Bool_t PFitter::CheckCommands()
cmd.second = cmdLineNo;
fCmdList.push_back(cmd);
// filter out possible parameters for scan
TObjArray *tokens = nullptr;
TObjString *ostr;
std::vector<std::string> tokens;
TString str;
UInt_t ival;
tokens = line.Tokenize(", \t");
tokens = PStringUtils::Split(line.Data(), ", \t");
for (Int_t i=0; i<tokens->GetEntries(); i++) {
ostr = dynamic_cast<TObjString*>(tokens->At(i));
str = ostr->GetString();
for (Int_t i=0; i<static_cast<int>(tokens.size()); i++) {
str = tokens[i].c_str();
if (i==1) { // get parameter number
// check that token is a UInt_t
if (!str.IsDigit()) {
@@ -1367,10 +1270,6 @@ Bool_t PFitter::CheckCommands()
std::cerr << std::endl << ">> command syntax for SCAN is: SCAN [parameter no [# of points [low high]]]";
std::cerr << std::endl;
fIsValid = false;
if (tokens) {
delete tokens;
tokens = nullptr;
}
break;
}
ival = str.Atoi();
@@ -1382,10 +1281,6 @@ Bool_t PFitter::CheckCommands()
std::cerr << std::endl << ">> command syntax for SCAN is: SCAN [parameter no [# of points [low high]]]";
std::cerr << std::endl;
fIsValid = false;
if (tokens) {
delete tokens;
tokens = nullptr;
}
break;
}
// keep parameter
@@ -1402,10 +1297,6 @@ Bool_t PFitter::CheckCommands()
std::cerr << std::endl << ">> command syntax for SCAN is: SCAN [parameter no [# of points [low high]]]";
std::cerr << std::endl;
fIsValid = false;
if (tokens) {
delete tokens;
tokens = nullptr;
}
break;
}
ival = str.Atoi();
@@ -1416,10 +1307,6 @@ Bool_t PFitter::CheckCommands()
std::cerr << std::endl << ">> command syntax for SCAN is: SCAN [parameter no [# of points [low high]]]";
std::cerr << std::endl;
fIsValid = false;
if (tokens) {
delete tokens;
tokens = nullptr;
}
break;
}
fScanNoPoints = ival;
@@ -1434,10 +1321,6 @@ Bool_t PFitter::CheckCommands()
std::cerr << std::endl << ">> command syntax for SCAN is: SCAN [parameter no [# of points [low high]]]";
std::cerr << std::endl;
fIsValid = false;
if (tokens) {
delete tokens;
tokens = nullptr;
}
break;
}
fScanLow = str.Atof();
@@ -1452,33 +1335,23 @@ Bool_t PFitter::CheckCommands()
std::cerr << std::endl << ">> command syntax for SCAN is: SCAN [parameter no [# of points [low high]]]";
std::cerr << std::endl;
fIsValid = false;
if (tokens) {
delete tokens;
tokens = nullptr;
}
break;
}
fScanHigh = str.Atof();
}
}
if (tokens) {
delete tokens;
tokens = nullptr;
}
} else if (line.Contains("SIMPLEX", TString::kIgnoreCase)) {
cmd.first = PMN_SIMPLEX;
cmd.second = cmdLineNo;
fCmdList.push_back(cmd);
} else if (line.Contains("STRATEGY", TString::kIgnoreCase)) {
TObjArray *tokens = nullptr;
TObjString *ostr;
std::vector<std::string> tokens;
TString str;
tokens = line.Tokenize(" \t");
if (tokens->GetEntries() == 2) {
ostr = dynamic_cast<TObjString*>(tokens->At(1));
str = ostr->GetString();
tokens = PStringUtils::Split(line.Data(), " \t");
if (static_cast<int>(tokens.size()) == 2) {
str = tokens[1].c_str();
if (str.CompareTo("0") == 0) { // low
fStrategy = 0;
} else if (str.CompareTo("1") == 0) { // default
@@ -1494,10 +1367,6 @@ Bool_t PFitter::CheckCommands()
}
}
if (tokens) {
delete tokens;
tokens = nullptr;
}
} else if (line.Contains("USER_COVARIANCE", TString::kIgnoreCase)) {
cmd.first = PMN_USER_COVARIANCE;
cmd.second = cmdLineNo;
@@ -1513,35 +1382,28 @@ Bool_t PFitter::CheckCommands()
fCmdList.push_back(cmd);
// check if the given sector arguments are valid time stamps, i.e. doubles and value < lgb time stamp
TObjArray *tokens = nullptr;
TObjString *ostr;
std::vector<std::string> tokens;
TString str;
tokens = line.Tokenize(" ,\t");
tokens = PStringUtils::Split(line.Data(), " ,\t");
if (tokens->GetEntries() == 1) { // no sector time stamps given -> issue an error
if (static_cast<int>(tokens.size()) == 1) { // no sector time stamps given -> issue an error
std::cerr << std::endl << ">> PFitter::CheckCommands(): **FATAL ERROR** in line " << it->fLineNo;
std::cerr << std::endl << ">> " << line.Data();
std::cerr << std::endl << ">> At least one sector time stamp is expected.";
std::cerr << std::endl << ">> Will stop ...";
std::cerr << std::endl;
// cleanup
if (tokens) {
delete tokens;
tokens = nullptr;
}
fIsValid = false;
fSectorFlag = false;
break;
}
Double_t dval;
for (Int_t i=1; i<tokens->GetEntries(); i++) {
for (Int_t i=1; i<static_cast<int>(tokens.size()); i++) {
// keep time range of sector
PSectorChisq sec(fRunInfo->GetNoOfRuns());
// get parse tokens
ostr = dynamic_cast<TObjString*>(tokens->At(i));
str = ostr->GetString();
str = tokens[i].c_str();
if (str.IsFloat()) {
dval = str.Atof();
// check that the sector time stamp is smaller than all lgb time stamps
@@ -1552,11 +1414,6 @@ Bool_t PFitter::CheckCommands()
std::cerr << std::endl << ">> The sector time stamp " << dval << " is > as the lgb time stamp (" << fOriginalFitRange[j].second << ") of run " << j << ".";
std::cerr << std::endl << ">> Will stop ...";
std::cerr << std::endl;
// cleanup
if (tokens) {
delete tokens;
tokens = nullptr;
}
fIsValid = false;
fSectorFlag = false;
return fIsValid;
@@ -1571,21 +1428,12 @@ Bool_t PFitter::CheckCommands()
std::cerr << std::endl << ">> The sector time stamp '" << str << "' is not a number.";
std::cerr << std::endl << ">> Will stop ...";
std::cerr << std::endl;
// cleanup
if (tokens) {
delete tokens;
tokens = nullptr;
}
fIsValid = false;
fSectorFlag = false;
break;
}
}
if (tokens) {
delete tokens;
tokens = nullptr;
}
} else { // unkown command
std::cerr << std::endl << ">> PFitter::CheckCommands(): **FATAL ERROR** in line " << it->fLineNo << " an unkown command is found:";
std::cerr << std::endl << ">> " << line.Data();
@@ -1729,27 +1577,24 @@ Bool_t PFitter::ExecuteFitRange(UInt_t lineNo)
return true;
}
TObjArray *tokens = nullptr;
TObjString *ostr;
std::vector<std::string> tokens;
TString str;
tokens = fCmdLines[lineNo].fLine.Tokenize(", \t");
tokens = PStringUtils::Split(fCmdLines[lineNo].fLine.Data(), ", \t");
PMsrRunList *runList = fRunInfo->GetMsrRunList();
// execute command, no error checking needed since this has been already carried out in CheckCommands()
if (tokens->GetEntries() == 2) { // reset command
if (static_cast<int>(tokens.size()) == 2) { // reset command
fRunListCollection->SetFitRange(fOriginalFitRange);
} else if (tokens->GetEntries() == 3) { // single fit range for all runs
} else if (static_cast<int>(tokens.size()) == 3) { // single fit range for all runs
Double_t start = 0.0, end = 0.0;
PDoublePair fitRange;
PDoublePairVector fitRangeVector;
ostr = dynamic_cast<TObjString*>(tokens->At(1));
str = ostr->GetString();
str = tokens[1].c_str();
start = str.Atof();
ostr = dynamic_cast<TObjString*>(tokens->At(2));
str = ostr->GetString();
str = tokens[2].c_str();
end = str.Atof();
fitRange.first = start;
@@ -1763,11 +1608,9 @@ Bool_t PFitter::ExecuteFitRange(UInt_t lineNo)
PDoublePairVector fitRangeVector;
for (UInt_t i=0; i<runList->size(); i++) {
ostr = dynamic_cast<TObjString*>(tokens->At(2*i+1));
str = ostr->GetString();
str = tokens[2*i+1].c_str();
start = str.Atof();
ostr = dynamic_cast<TObjString*>(tokens->At(2*i+2));
str = ostr->GetString();
str = tokens[2*i+2].c_str();
end = str.Atof();
fitRange.first = start;
@@ -1795,15 +1638,13 @@ Bool_t PFitter::ExecuteFix(UInt_t lineNo)
{
std::cout << ">> PFitter::ExecuteFix(): " << fCmdLines[lineNo].fLine.Data() << std::endl;
TObjArray *tokens = nullptr;
TObjString *ostr;
std::vector<std::string> tokens;
TString str;
tokens = fCmdLines[lineNo].fLine.Tokenize(", \t");
tokens = PStringUtils::Split(fCmdLines[lineNo].fLine.Data(), ", \t");
for (Int_t i=1; i<tokens->GetEntries(); i++) {
ostr = dynamic_cast<TObjString*>(tokens->At(i));
str = ostr->GetString();
for (Int_t i=1; i<static_cast<int>(tokens.size()); i++) {
str = tokens[i].c_str();
if (str.IsDigit()) { // token is a parameter number
fMnUserParams.Fix(static_cast<UInt_t>(str.Atoi())-1);
@@ -1812,11 +1653,6 @@ Bool_t PFitter::ExecuteFix(UInt_t lineNo)
}
}
// clean up
if (tokens) {
delete tokens;
tokens = nullptr;
}
return true;
}
@@ -2124,19 +1960,17 @@ Bool_t PFitter::ExecutePrintLevel(UInt_t lineNo)
{
std::cout << ">> PFitter::ExecutePrintLevel(): " << fCmdLines[lineNo].fLine.Data() << std::endl;
TObjArray *tokens = nullptr;
TObjString *ostr;
std::vector<std::string> tokens;
TString str;
tokens = fCmdLines[lineNo].fLine.Tokenize(", \t");
tokens = PStringUtils::Split(fCmdLines[lineNo].fLine.Data(), ", \t");
if (tokens->GetEntries() < 2) {
if (static_cast<int>(tokens.size()) < 2) {
std::cerr << std::endl << "**ERROR** from PFitter::ExecutePrintLevel(): SYNTAX: PRINT_LEVEL <N>, where <N>=0-3" << std::endl << std::endl;
return false;
}
ostr = (TObjString*)tokens->At(1);
str = ostr->GetString();
str = tokens[1].c_str();
Int_t ival;
if (str.IsDigit()) {
@@ -2159,11 +1993,6 @@ Bool_t PFitter::ExecutePrintLevel(UInt_t lineNo)
ROOT::Minuit2::MnPrint::SetLevel(fPrintLevel);
#endif
// clean up
if (tokens) {
delete tokens;
tokens = nullptr;
}
return true;
}
@@ -2180,17 +2009,15 @@ Bool_t PFitter::ExecutePrintLevel(UInt_t lineNo)
*/
Bool_t PFitter::ExecuteRelease(UInt_t lineNo)
{
TObjArray *tokens = nullptr;
TObjString *ostr;
std::vector<std::string> tokens;
TString str;
tokens = fCmdLines[lineNo].fLine.Tokenize(", \t");
tokens = PStringUtils::Split(fCmdLines[lineNo].fLine.Data(), ", \t");
std::cout << ">> PFitter::ExecuteRelease(): " << fCmdLines[lineNo].fLine.Data() << std::endl;
for (Int_t i=1; i<tokens->GetEntries(); i++) {
ostr = dynamic_cast<TObjString*>(tokens->At(i));
str = ostr->GetString();
for (Int_t i=1; i<static_cast<int>(tokens.size()); i++) {
str = tokens[i].c_str();
if (str.IsDigit()) { // token is a parameter number
fMnUserParams.Release(static_cast<UInt_t>(str.Atoi())-1);
@@ -2203,11 +2030,6 @@ Bool_t PFitter::ExecuteRelease(UInt_t lineNo)
}
}
// clean up
if (tokens) {
delete tokens;
tokens = nullptr;
}
return true;
}
File diff suppressed because it is too large Load Diff
+4 -12
View File
@@ -34,11 +34,11 @@
#include <TColor.h>
#include <TRandom.h>
#include <TROOT.h>
#include <TObjString.h>
#include <TGFileDialog.h>
#include "PMusrCanvas.h"
#include "PFourier.h"
#include "PStringUtils.h"
static const char *gFiletypes[] = { "Data files", "*.dat",
"All files", "*",
@@ -6408,23 +6408,15 @@ Bool_t PMusrCanvas::IsScaleN0AndBkg()
PMsrLines *cmd = fMsrHandler->GetMsrCommands();
for (UInt_t i=0; i<cmd->size(); i++) {
if (cmd->at(i).fLine.Contains("SCALE_N0_BKG", TString::kIgnoreCase)) {
TObjArray *tokens = nullptr;
TObjString *ostr = nullptr;
TString str;
tokens = cmd->at(i).fLine.Tokenize(" \t");
if (tokens->GetEntries() != 2) {
std::vector<std::string> tokens = PStringUtils::Split(cmd->at(i).fLine.Data(), " \t");
if (tokens.size() != 2) {
std::cerr << std::endl << ">> PRunSingleHisto::IsScaleN0AndBkg(): **WARNING** Found uncorrect 'SCALE_N0_BKG' command, will ignore it.";
std::cerr << std::endl << ">> Allowed commands: SCALE_N0_BKG TRUE | FALSE" << std::endl;
return willScale;
}
ostr = dynamic_cast<TObjString*>(tokens->At(1));
str = ostr->GetString();
if (!str.CompareTo("FALSE", TString::kIgnoreCase)) {
if (PStringUtils::IsEqualNoCase(tokens[1], "FALSE")) {
willScale = false;
}
// clean up
if (tokens)
delete tokens;
}
}
+1 -1
View File
@@ -383,7 +383,7 @@ Double_t PRunMuMinus::CalcMaxLikelihood(const std::vector<Double_t>& par)
Int_t chunk = (fEndTimeBin - fStartTimeBin)/omp_get_num_procs();
if (chunk < 10)
chunk = 10;
#pragma omp parallel for default(shared) private(i,time,theo,data) schedule(dynamic,chunk) reduction(-:mllh)
#pragma omp parallel for default(shared) private(i,time,theo,data) schedule(dynamic,chunk) reduction(+:mllh)
#endif
for (i=fStartTimeBin; i < fEndTimeBin; ++i) {
time = fData.GetDataTimeStart() + static_cast<Double_t>(i)*fData.GetDataTimeStep();
+3 -3
View File
@@ -439,7 +439,7 @@ Double_t PRunSingleHisto::CalcChiSquareExpected(const std::vector<Double_t>& par
* <b>OpenMP Parallelization:</b>
* - Dynamic scheduling with chunk size = (N_bins / N_processors), minimum 10
* - Private variables per thread: i, time, theo, data
* - Reduction performed on mllh sum (note: reduction(-:mllh) for subtraction)
* - Reduction performed on mllh sum (reduction(+:mllh))
*
* <b>When to Use Maximum Likelihood vs. χ²:</b>
* - <b>Use likelihood:</b> Low count rates (< 100 counts/bin), asymmetric errors
@@ -515,7 +515,7 @@ Double_t PRunSingleHisto::CalcMaxLikelihood(const std::vector<Double_t>& par)
Int_t chunk = (fEndTimeBin - fStartTimeBin)/omp_get_num_procs();
if (chunk < 10)
chunk = 10;
#pragma omp parallel for default(shared) private(i,time,theo,data) schedule(dynamic,chunk) reduction(-:mllh)
#pragma omp parallel for default(shared) private(i,time,theo,data) schedule(dynamic,chunk) reduction(+:mllh)
#endif
for (i=fStartTimeBin; i<fEndTimeBin; ++i) {
time = fData.GetDataTimeStart() + static_cast<Double_t>(i)*fData.GetDataTimeStep();
@@ -650,7 +650,7 @@ Double_t PRunSingleHisto::CalcMaxLikelihoodExpected(const std::vector<Double_t>&
Int_t chunk = (fEndTimeBin - fStartTimeBin)/omp_get_num_procs();
if (chunk < 10)
chunk = 10;
#pragma omp parallel for default(shared) private(i,time,theo,data) schedule(dynamic,chunk) reduction(-:mllh)
#pragma omp parallel for default(shared) private(i,time,theo,data) schedule(dynamic,chunk) reduction(+:mllh)
#endif
for (i=fStartTimeBin; i<fEndTimeBin; ++i) {
time = fData.GetDataTimeStart() + static_cast<Double_t>(i)*fData.GetDataTimeStep();
+38 -57
View File
@@ -34,13 +34,12 @@
#include <iostream>
#include <fstream>
#include <TObjArray.h>
#include <TObjString.h>
#include <TColor.h>
#include <TList.h>
#include <TXMLAttr.h>
#include "PStartupHandler.h"
#include "PStringUtils.h"
ClassImpQ(PStartupHandler)
@@ -451,8 +450,6 @@ void PStartupHandler::OnEndElement(const Char_t *str)
*/
void PStartupHandler::OnCharacters(const Char_t *str)
{
TObjArray *tokens;
TObjString *ostr;
TString tstr;
Int_t color, r, g, b, ival;
@@ -481,60 +478,44 @@ void PStartupHandler::OnCharacters(const Char_t *str)
}
break;
case eColor:
// check that str is a rbg code
tstr = TString(str);
tokens = tstr.Tokenize(",");
// check that there any tokens
if (!tokens) {
std::cerr << std::endl << "PStartupHandler **WARNING** '" << str << "' is not a rbg code, will ignore it";
std::cerr << std::endl;
return;
{
// check that str is a rbg code
std::vector<std::string> rgb = PStringUtils::Split(str, ",");
// check there is the right number of tokens
if (rgb.size() != 3) {
std::cerr << std::endl << "PStartupHandler **WARNING** '" << str << "' is not a rbg code, will ignore it";
std::cerr << std::endl;
return;
}
// get r
if (PStringUtils::IsInt(rgb[0])) {
r = PStringUtils::ToInt(rgb[0]);
} else {
std::cerr << std::endl << "PStartupHandler **WARNING** r within the rgb code is not a number, will ignore it";
std::cerr << std::endl;
return;
}
// get g
if (PStringUtils::IsInt(rgb[1])) {
g = PStringUtils::ToInt(rgb[1]);
} else {
std::cerr << std::endl << "PStartupHandler **WARNING** g within the rgb code is not a number, will ignore it";
std::cerr << std::endl;
return;
}
// get b
if (PStringUtils::IsInt(rgb[2])) {
b = PStringUtils::ToInt(rgb[2]);
} else {
std::cerr << std::endl << "PStartupHandler **WARNING** b within the rgb code is not a number, will ignore it";
std::cerr << std::endl;
return;
}
// generate the ROOT color code based on str
color = TColor::GetColor(r,g,b);
// add the color code to the color list
fColorList.push_back(color);
}
// check there is the right number of tokens
if (tokens->GetEntries() != 3) {
std::cerr << std::endl << "PStartupHandler **WARNING** '" << str << "' is not a rbg code, will ignore it";
std::cerr << std::endl;
return;
}
// get r
ostr = dynamic_cast<TObjString*>(tokens->At(0));
tstr = ostr->GetString();
if (tstr.IsDigit()) {
r = tstr.Atoi();
} else {
std::cerr << std::endl << "PStartupHandler **WARNING** r within the rgb code is not a number, will ignore it";
std::cerr << std::endl;
return;
}
// get g
ostr = dynamic_cast<TObjString*>(tokens->At(1));
tstr = ostr->GetString();
if (tstr.IsDigit()) {
g = tstr.Atoi();
} else {
std::cerr << std::endl << "PStartupHandler **WARNING** g within the rgb code is not a number, will ignore it";
std::cerr << std::endl;
return;
}
// get b
ostr = dynamic_cast<TObjString*>(tokens->At(2));
tstr = ostr->GetString();
if (tstr.IsDigit()) {
b = tstr.Atoi();
} else {
std::cerr << std::endl << "PStartupHandler **WARNING** b within the rgb code is not a number, will ignore it";
std::cerr << std::endl;
return;
}
// clean up tokens
if (tokens) {
delete tokens;
tokens = nullptr;
}
// generate the ROOT color code based on str
color = TColor::GetColor(r,g,b);
// add the color code to the color list
fColorList.push_back(color);
break;
case eUnits:
tstr = TString(str);
+275
View File
@@ -0,0 +1,275 @@
/***************************************************************************
PStringUtils.cpp
Author: Andreas Suter
e-mail: andreas.suter@psi.ch
***************************************************************************/
/***************************************************************************
* Copyright (C) 2007-2026 by Andreas Suter *
* andreas.suter@psi.ch *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include <cctype>
#include <cerrno>
#include <charconv>
#include <cstdlib>
#include "PStringUtils.h"
//--------------------------------------------------------------------------
// Split (static)
//--------------------------------------------------------------------------
/**
* <p>Splits a string into tokens on any character contained in delimiters,
* skipping empty tokens. Mirrors the semantics of TString::Tokenize().
*
* \param str input string to be tokenized
* \param delimiters set of delimiter characters
* \return vector of tokens (without the delimiters)
*/
std::vector<std::string> PStringUtils::Split(const std::string &str, const std::string &delimiters)
{
std::vector<std::string> tokens;
std::string::size_type start = str.find_first_not_of(delimiters);
while (start != std::string::npos) {
std::string::size_type end = str.find_first_of(delimiters, start);
if (end == std::string::npos) {
tokens.push_back(str.substr(start));
break;
}
tokens.push_back(str.substr(start, end - start));
start = str.find_first_not_of(delimiters, end);
}
return tokens;
}
//--------------------------------------------------------------------------
// IsInt (static)
//--------------------------------------------------------------------------
/**
* <p>Returns true if the string is an integer literal, i.e. a non-empty
* sequence of decimal digits with an optional single leading sign (+/-).
* This is slightly more permissive than TString::IsDigit(), which rejects a
* sign, so that negative/positive integers such as "-5" or "+42" are also
* recognised.
*
* \param str string to be checked
* \return true if str is a (possibly signed) integer
*/
bool PStringUtils::IsInt(const std::string &str)
{
// all characters must be digits or whitespace, with an optional single
// leading sign (+/-) preceding the digits, and there must be at least one
// digit (surrounding/embedded whitespace is tolerated, e.g. for tokens
// split on ',' or ';' only).
bool hasDigit = false;
bool hasSign = false;
for (char c : str) {
if (std::isdigit(static_cast<unsigned char>(c))) {
hasDigit = true;
} else if (c == '+' || c == '-') {
// a sign is only valid before any digit and may appear at most once
if (hasDigit || hasSign)
return false;
hasSign = true;
} else if (!std::isspace(static_cast<unsigned char>(c))) {
return false;
}
}
return hasDigit;
}
//--------------------------------------------------------------------------
// IsFloat (static)
//--------------------------------------------------------------------------
/**
* <p>Returns true if the string is a complete integer or floating point
* literal (optionally signed, with decimal point and/or exponent). Mirrors
* the semantics of TString::IsFloat() for the relevant cases.
*
* \param str string to be checked
* \return true if str is a valid number
*/
bool PStringUtils::IsFloat(const std::string &str)
{
// mirror TString::IsFloat(): surrounding whitespace is ignored (e.g. for
// tokens split on ',' or ';' only), then a complete number is required.
const std::string ws(" \t\n\r\f\v");
std::string::size_type b = str.find_first_not_of(ws);
if (b == std::string::npos)
return false;
std::string::size_type e = str.find_last_not_of(ws);
const std::string t = str.substr(b, e - b + 1);
std::string::size_type i = 0;
if (t[i] == '+' || t[i] == '-')
++i;
// reject things like "inf"/"nan" which strtod would otherwise accept
if (i >= t.size() || !(std::isdigit(static_cast<unsigned char>(t[i])) || t[i] == '.'))
return false;
const char *begin = t.c_str();
char *end = nullptr;
std::strtod(begin, &end);
return end == begin + t.size();
}
//--------------------------------------------------------------------------
// ToInt (static)
//--------------------------------------------------------------------------
/**
* <p>Converts the leading part of the string to an int (base 10), mirroring
* TString::Atoi(). Returns 0 if no conversion is possible.
*
* <p>Uses std::from_chars so that conversion errors can be reported through
* the optional \a ok out-parameter: it is set to true when a valid integer
* was parsed (leading whitespace is skipped) and to false otherwise (no
* digits present, or the value is out of int range). Trailing non-numeric
* characters are ignored, as with TString::Atoi(). A null \a ok preserves the
* historic fire-and-forget behaviour.
*
* \param str string to be converted
* \param ok optional out-parameter signalling conversion success
* \return converted integer value (0 on error)
*/
int PStringUtils::ToInt(const std::string &str, bool *ok)
{
// mirror TString::Atoi(): skip leading whitespace, then parse the leading
// integer (a possible sign followed by decimal digits).
const char *begin = str.c_str();
const char *end = begin + str.size();
while (begin != end && std::isspace(static_cast<unsigned char>(*begin)))
++begin;
int value = 0;
const std::from_chars_result res = std::from_chars(begin, end, value);
if (ok != nullptr)
*ok = (res.ec == std::errc{});
return value;
}
//--------------------------------------------------------------------------
// ToDouble (static)
//--------------------------------------------------------------------------
/**
* <p>Converts the leading part of the string to a double, mirroring
* TString::Atof(). Returns 0.0 if no conversion is possible.
*
* <p>Like ToInt(), conversion errors can be reported through the optional
* \a ok out-parameter: it is set to true when a value was parsed and to
* false otherwise (no number present, or the value is out of range). This
* lets callers distinguish a legitimate 0.0 from a failed conversion.
* strtod() (rather than std::from_chars) is used so that the accepted input
* set stays identical to TString::Atof() (leading whitespace is skipped, a
* leading '+' is honoured, trailing characters are ignored). A null \a ok
* preserves the historic fire-and-forget behaviour.
*
* \param str string to be converted
* \param ok optional out-parameter signalling conversion success
* \return converted double value (0.0 on error)
*/
double PStringUtils::ToDouble(const std::string &str, bool *ok)
{
const char *begin = str.c_str();
char *end = nullptr;
errno = 0;
const double value = std::strtod(begin, &end);
if (ok != nullptr)
*ok = (end != begin) && (errno != ERANGE);
return value;
}
//--------------------------------------------------------------------------
// IsEqualNoCase (static)
//--------------------------------------------------------------------------
/**
* <p>Case-insensitive full-string equality, mirroring
* TString::CompareTo(..., TString::kIgnoreCase) == 0.
*
* \param a first string
* \param b second string
* \return true if a and b are equal ignoring case
*/
bool PStringUtils::IsEqualNoCase(const std::string &a, const std::string &b)
{
if (a.size() != b.size())
return false;
for (std::string::size_type i = 0; i < a.size(); ++i) {
if (std::tolower(static_cast<unsigned char>(a[i])) !=
std::tolower(static_cast<unsigned char>(b[i])))
return false;
}
return true;
}
//--------------------------------------------------------------------------
// ContainsNoCase (static)
//--------------------------------------------------------------------------
/**
* <p>Case-insensitive substring search, mirroring
* TString::Contains(..., TString::kIgnoreCase).
*
* \param haystack string to be searched in
* \param needle substring to be searched for
* \return true if needle is contained in haystack ignoring case
*/
bool PStringUtils::ContainsNoCase(const std::string &haystack, const std::string &needle)
{
if (needle.empty())
return true;
if (needle.size() > haystack.size())
return false;
auto toLower = [](unsigned char c) { return std::tolower(c); };
for (std::string::size_type i = 0; i + needle.size() <= haystack.size(); ++i) {
std::string::size_type j = 0;
for (; j < needle.size(); ++j) {
if (toLower(haystack[i+j]) != toLower(needle[j]))
break;
}
if (j == needle.size())
return true;
}
return false;
}
//--------------------------------------------------------------------------
// BeginsWithNoCase (static)
//--------------------------------------------------------------------------
/**
* <p>Case-insensitive prefix test, mirroring
* TString::BeginsWith(..., TString::kIgnoreCase).
*
* \param str string to be tested
* \param prefix prefix to be searched for
* \return true if str starts with prefix ignoring case
*/
bool PStringUtils::BeginsWithNoCase(const std::string &str, const std::string &prefix)
{
if (prefix.size() > str.size())
return false;
for (std::string::size_type i = 0; i < prefix.size(); ++i) {
if (std::tolower(static_cast<unsigned char>(str[i])) !=
std::tolower(static_cast<unsigned char>(prefix[i])))
return false;
}
return true;
}
+2 -1
View File
@@ -102,7 +102,8 @@ static inline void SobolIni(This *t)
int inibits = -1, bit;
for( j = powers; j; j >>= 1 ) ++inibits;
memcpy(pv, pini, inibits*sizeof *pini);
if( inibits > 0 )
memcpy(pv, pini, inibits*sizeof *pini);
pini += 8;
for( bit = inibits; bit <= nbits; ++bit ) {
+150
View File
@@ -0,0 +1,150 @@
/***************************************************************************
PStringUtils.h
Author: Andreas Suter
e-mail: andreas.suter@psi.ch
***************************************************************************/
/***************************************************************************
* Copyright (C) 2007-2026 by Andreas Suter *
* andreas.suter@psi.ch *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#ifndef _PSTRINGUTILS_H_
#define _PSTRINGUTILS_H_
#include <string>
#include <vector>
//-------------------------------------------------------------
/**
* \brief Lightweight, dependency-free string utilities (pure C++17).
*
* PStringUtils collects small string helpers used throughout the musrfit
* suite, in particular for tokenizing and parsing the plain-text MSR file
* format. The implementation deliberately relies only on the C++ standard
* library (no ROOT) so that it can be reused freely.
*
* The provided helpers replicate the semantics of the corresponding
* ROOT TString methods that were previously used:
* - Split replaces TString::Tokenize() (+ TObjArray/TObjString)
* - IsInt replaces TString::IsDigit()
* - IsFloat replaces TString::IsFloat()
* - ToInt replaces TString::Atoi()
* - ToDouble replaces TString::Atof()
* - IsEqualNoCase replaces TString::CompareTo(..., TString::kIgnoreCase)
*
* All methods are static; the class is a pure namespace-like utility.
*/
class PStringUtils
{
public:
/**
* <p>Splits a string into tokens on any character contained in
* delimiters, skipping empty tokens. Mirrors TString::Tokenize().
*
* @param str input string to be tokenized
* @param delimiters set of delimiter characters
* @return vector of tokens (without the delimiters)
*/
static std::vector<std::string> Split(const std::string &str, const std::string &delimiters);
/**
* <p>Returns true if the string is a (possibly signed) integer literal,
* i.e. a non-empty sequence of decimal digits with an optional single
* leading sign (+/-). Slightly more permissive than TString::IsDigit(),
* which rejects a sign, so that e.g. "-5" is also recognised.
*
* @param str string to be checked
* @return true if str is a (possibly signed) integer
*/
static bool IsInt(const std::string &str);
/**
* <p>Returns true if the string is a complete integer or floating point
* literal (optionally signed, with decimal point and/or exponent).
* Mirrors TString::IsFloat() for the relevant cases.
*
* @param str string to be checked
* @return true if str is a valid number
*/
static bool IsFloat(const std::string &str);
/**
* <p>Converts the leading part of the string to an int (base 10).
* Mirrors TString::Atoi(). Returns 0 if no conversion is possible.
*
* <p>If \a ok is non-null it is set to true when a valid integer was
* parsed and to false on error (no digits, or value out of int range).
* This allows callers to distinguish a legitimate 0 from a failed
* conversion, which the bare return value cannot express.
*
* @param str string to be converted
* @param ok optional out-parameter signalling conversion success
* @return converted integer value (0 on error)
*/
static int ToInt(const std::string &str, bool *ok = nullptr);
/**
* <p>Converts the leading part of the string to a double.
* Mirrors TString::Atof(). Returns 0.0 if no conversion is possible.
*
* <p>If \a ok is non-null it is set to true when a value was parsed and
* to false on error (no number, or value out of range), allowing callers
* to distinguish a legitimate 0.0 from a failed conversion.
*
* @param str string to be converted
* @param ok optional out-parameter signalling conversion success
* @return converted double value (0.0 on error)
*/
static double ToDouble(const std::string &str, bool *ok = nullptr);
/**
* <p>Case-insensitive full-string equality.
* Mirrors TString::CompareTo(..., TString::kIgnoreCase) == 0.
*
* @param a first string
* @param b second string
* @return true if a and b are equal ignoring case
*/
static bool IsEqualNoCase(const std::string &a, const std::string &b);
/**
* <p>Case-insensitive substring search.
* Mirrors TString::Contains(..., TString::kIgnoreCase).
*
* @param haystack string to be searched in
* @param needle substring to be searched for
* @return true if needle is contained in haystack ignoring case
*/
static bool ContainsNoCase(const std::string &haystack, const std::string &needle);
/**
* <p>Case-insensitive prefix test.
* Mirrors TString::BeginsWith(..., TString::kIgnoreCase).
*
* @param str string to be tested
* @param prefix prefix to be searched for
* @return true if str starts with prefix ignoring case
*/
static bool BeginsWithNoCase(const std::string &str, const std::string &prefix);
};
#endif // _PSTRINGUTILS_H_
+52 -86
View File
@@ -35,8 +35,8 @@
#include <cstdlib>
#include <TString.h>
#include <TObjArray.h>
#include <TObjString.h>
#include "PStringUtils.h"
//-------------------------------------------------------------
// msr block header tags
@@ -88,8 +88,6 @@ bool msr2msr_run(char *str, const std::size_t size)
TString run(str);
TString line(str);
TObjArray *tokens;
TObjString *ostr[2];
// for filtering
run.ToUpper();
@@ -100,8 +98,8 @@ bool msr2msr_run(char *str, const std::size_t size)
line.Remove(idx);
// tokenize run
tokens = line.Tokenize(" \t");
if (tokens->GetEntries() < 4) {
std::vector<std::string> tokens = PStringUtils::Split(line.Data(), " \t");
if (tokens.size() < 4) {
std::cout << std::endl << "**ERROR**: Something is wrong with the RUN block header:";
std::cout << std::endl << " >> " << str;
std::cout << std::endl << " >> no <msr-file-out> is created";
@@ -109,35 +107,22 @@ bool msr2msr_run(char *str, const std::size_t size)
return false;
}
if (tokens->GetEntries() == 5) { // already a new msr file, do only add the proper run comment
if (tokens.size() == 5) { // already a new msr file, do only add the proper run comment
snprintf(str, size, "%s (name beamline institute data-file-format)", line.Data());
return true;
}
if (run.Contains("NEMU")) {
ostr[0] = dynamic_cast<TObjString*>(tokens->At(1)); // file name
snprintf(str, size, "RUN %s MUE4 PSI WKM (name beamline institute data-file-format)", ostr[0]->GetString().Data());
snprintf(str, size, "RUN %s MUE4 PSI WKM (name beamline institute data-file-format)", tokens[1].c_str());
} else if (run.Contains("PSI")) {
ostr[0] = dynamic_cast<TObjString*>(tokens->At(1)); // file name
ostr[1] = dynamic_cast<TObjString*>(tokens->At(2)); // beamline
snprintf(str, size, "RUN %s %s PSI PSI-BIN (name beamline institute data-file-format)",
ostr[0]->GetString().Data(), ostr[1]->GetString().Data());
tokens[1].c_str(), tokens[2].c_str());
} else if (run.Contains("TRIUMF")) {
ostr[0] = dynamic_cast<TObjString*>(tokens->At(1)); // file name
ostr[1] = dynamic_cast<TObjString*>(tokens->At(2)); // beamline
snprintf(str, size, "RUN %s %s TRIUMF MUD (name beamline institute data-file-format)",
ostr[0]->GetString().Data(), ostr[1]->GetString().Data());
tokens[1].c_str(), tokens[2].c_str());
} else if (run.Contains("RAL")) {
ostr[0] = dynamic_cast<TObjString*>(tokens->At(1)); // file name
ostr[1] = dynamic_cast<TObjString*>(tokens->At(2)); // beamline
snprintf(str, size, "RUN %s %s RAL NEXUS (name beamline institute data-file-format)",
ostr[0]->GetString().Data(), ostr[1]->GetString().Data());
}
// clean up
if (tokens) {
delete tokens;
tokens = nullptr;
tokens[1].c_str(), tokens[2].c_str());
}
return true;
@@ -165,38 +150,34 @@ bool msr2msr_param(char *str)
// handle parameter line
TString line(str);
TObjArray *tokens;
TObjString *ostr[6];
char sstr[256];
char spaces[256];
tokens = line.Tokenize(" \t");
Int_t noTokens = tokens->GetEntries();
std::vector<std::string> tokens = PStringUtils::Split(line.Data(), " \t");
std::size_t noTokens = tokens.size();
if (noTokens == 4) {
for (unsigned int i=0; i<4; i++)
ostr[i] = dynamic_cast<TObjString*>(tokens->At(i));
// number
snprintf(sstr, sizeof(sstr), "%10s", ostr[0]->GetString().Data());
snprintf(sstr, sizeof(sstr), "%10s", tokens[0].c_str());
// name
strcat(sstr, " ");
strcat(sstr, ostr[1]->GetString().Data());
strcat(sstr, tokens[1].c_str());
memset(spaces, 0, sizeof(spaces));
memset(spaces, ' ', 12-strlen(ostr[1]->GetString().Data()));
memset(spaces, ' ', 12-strlen(tokens[1].c_str()));
strcat(sstr, spaces);
// value
strcat(sstr, ostr[2]->GetString().Data());
if (strlen(ostr[2]->GetString().Data()) < 10) {
strcat(sstr, tokens[2].c_str());
if (strlen(tokens[2].c_str()) < 10) {
memset(spaces, 0, sizeof(spaces));
memset(spaces, ' ', 10-strlen(ostr[2]->GetString().Data()));
memset(spaces, ' ', 10-strlen(tokens[2].c_str()));
strcat(sstr, spaces);
} else {
strcat(sstr, " ");
}
// step
strcat(sstr, ostr[3]->GetString().Data());
if (strlen(ostr[3]->GetString().Data()) < 12) {
strcat(sstr, tokens[3].c_str());
if (strlen(tokens[3].c_str()) < 12) {
memset(spaces, 0, sizeof(spaces));
memset(spaces, ' ', 12-strlen(ostr[3]->GetString().Data()));
memset(spaces, ' ', 12-strlen(tokens[3].c_str()));
strcat(sstr, spaces);
} else {
strcat(sstr, " ");
@@ -204,30 +185,28 @@ bool msr2msr_param(char *str)
strcat(sstr, "none");
strcpy(str, sstr);
} else if (noTokens == 6) {
for (unsigned int i=0; i<6; i++)
ostr[i] = dynamic_cast<TObjString*>(tokens->At(i));
// number
snprintf(sstr, sizeof(sstr), "%10s", ostr[0]->GetString().Data());
snprintf(sstr, sizeof(sstr), "%10s", tokens[0].c_str());
// name
strcat(sstr, " ");
strcat(sstr, ostr[1]->GetString().Data());
strcat(sstr, tokens[1].c_str());
memset(spaces, 0, sizeof(spaces));
memset(spaces, ' ', 12-strlen(ostr[1]->GetString().Data()));
memset(spaces, ' ', 12-strlen(tokens[1].c_str()));
strcat(sstr, spaces);
// value
strcat(sstr, ostr[2]->GetString().Data());
if (strlen(ostr[2]->GetString().Data()) < 10) {
strcat(sstr, tokens[2].c_str());
if (strlen(tokens[2].c_str()) < 10) {
memset(spaces, 0, sizeof(spaces));
memset(spaces, ' ', 10-strlen(ostr[2]->GetString().Data()));
memset(spaces, ' ', 10-strlen(tokens[2].c_str()));
strcat(sstr, spaces);
} else {
strcat(sstr, " ");
}
// step
strcat(sstr, ostr[3]->GetString().Data());
if (strlen(ostr[3]->GetString().Data()) < 12) {
strcat(sstr, tokens[3].c_str());
if (strlen(tokens[3].c_str()) < 12) {
memset(spaces, 0, sizeof(spaces));
memset(spaces, ' ', 12-strlen(ostr[3]->GetString().Data()));
memset(spaces, ' ', 12-strlen(tokens[3].c_str()));
strcat(sstr, spaces);
} else {
strcat(sstr, " ");
@@ -235,25 +214,19 @@ bool msr2msr_param(char *str)
// pos. error
strcat(sstr, "none ");
// lower boundary
strcat(sstr, ostr[4]->GetString().Data());
if (strlen(ostr[4]->GetString().Data()) < 8) {
strcat(sstr, tokens[4].c_str());
if (strlen(tokens[4].c_str()) < 8) {
memset(spaces, 0, sizeof(spaces));
memset(spaces, ' ', 8-strlen(ostr[4]->GetString().Data()));
memset(spaces, ' ', 8-strlen(tokens[4].c_str()));
strcat(sstr, spaces);
} else {
strcat(sstr, " ");
}
// upper boundary
strcat(sstr, ostr[5]->GetString().Data());
strcat(sstr, tokens[5].c_str());
strcpy(str, sstr);
}
// clean up
if (tokens) {
delete tokens;
tokens = nullptr;
}
return true;
}
@@ -274,8 +247,7 @@ bool msr2msr_theory(char *str, int &tag, int &noOfAddionalParams)
{
// handle theory line
TString line(str);
TObjArray *tokens;
TObjString *ostr;
std::vector<std::string> tokens;
char sstr[256];
if ((line.Contains("sktt") || line.Contains("statKTTab")) && line.Contains("glf")) { // static Gauss KT LF table
@@ -283,8 +255,8 @@ bool msr2msr_theory(char *str, int &tag, int &noOfAddionalParams)
strcpy(sstr, "statGssKTLF ");
// tokenize the rest and extract the first two parameters
tokens = line.Tokenize(" \t");
Int_t noTokens = tokens->GetEntries();
tokens = PStringUtils::Split(line.Data(), " \t");
std::size_t noTokens = tokens.size();
if (noTokens < 3) {
std::cout << std::endl << "**ERROR** in THEORY block";
std::cout << std::endl << " Line: '" << str << "' is not a valid statKTTab statement.";
@@ -293,8 +265,7 @@ bool msr2msr_theory(char *str, int &tag, int &noOfAddionalParams)
}
for (Int_t i=1; i<3; i++) {
strcat(sstr, " ");
ostr = dynamic_cast<TObjString*>(tokens->At(i));
strcat(sstr, ostr->GetString().Data());
strcat(sstr, tokens[i].c_str());
}
strcat(sstr, " (frequency damping)");
strcpy(str, sstr);
@@ -303,8 +274,8 @@ bool msr2msr_theory(char *str, int &tag, int &noOfAddionalParams)
strcpy(sstr, "statExpKTLF ");
// tokenize the rest and extract the first two parameters
tokens = line.Tokenize(" \t");
Int_t noTokens = tokens->GetEntries();
tokens = PStringUtils::Split(line.Data(), " \t");
std::size_t noTokens = tokens.size();
if (noTokens < 3) {
std::cout << std::endl << "**ERROR** in THEORY block";
std::cout << std::endl << " Line: '" << str << "' is not a valid statKTTab statement.";
@@ -313,8 +284,7 @@ bool msr2msr_theory(char *str, int &tag, int &noOfAddionalParams)
}
for (Int_t i=1; i<3; i++) {
strcat(sstr, " ");
ostr = dynamic_cast<TObjString*>(tokens->At(i));
strcat(sstr, ostr->GetString().Data());
strcat(sstr, tokens[i].c_str());
}
strcat(sstr, " (frequency damping)");
strcpy(str, sstr);
@@ -323,8 +293,8 @@ bool msr2msr_theory(char *str, int &tag, int &noOfAddionalParams)
strcpy(sstr, "dynGssKTLF ");
// tokenize the rest and extract the first three parameters
tokens = line.Tokenize(" \t");
Int_t noTokens = tokens->GetEntries();
tokens = PStringUtils::Split(line.Data(), " \t");
std::size_t noTokens = tokens.size();
if (noTokens < 4) {
std::cout << std::endl << "**ERROR** in THEORY block";
std::cout << std::endl << " Line: '" << str << "' is not a valid dynmKTTab statement.";
@@ -333,8 +303,7 @@ bool msr2msr_theory(char *str, int &tag, int &noOfAddionalParams)
}
for (Int_t i=1; i<4; i++) {
strcat(sstr, " ");
ostr = dynamic_cast<TObjString*>(tokens->At(i));
strcat(sstr, ostr->GetString().Data());
strcat(sstr, tokens[i].c_str());
}
strcat(sstr, " (frequency damping hopping-rate)");
strcpy(str, sstr);
@@ -343,8 +312,8 @@ bool msr2msr_theory(char *str, int &tag, int &noOfAddionalParams)
strcpy(sstr, "dynExpKTLF ");
// tokenize the rest and extract the first three parameters
tokens = line.Tokenize(" \t");
Int_t noTokens = tokens->GetEntries();
tokens = PStringUtils::Split(line.Data(), " \t");
std::size_t noTokens = tokens.size();
if (noTokens < 4) {
std::cout << std::endl << "**ERROR** in THEORY block";
std::cout << std::endl << " Line: '" << str << "' is not a valid dynmKTTab statement.";
@@ -353,8 +322,7 @@ bool msr2msr_theory(char *str, int &tag, int &noOfAddionalParams)
}
for (Int_t i=1; i<4; i++) {
strcat(sstr, " ");
ostr = dynamic_cast<TObjString*>(tokens->At(i));
strcat(sstr, ostr->GetString().Data());
strcat(sstr, tokens[i].c_str());
}
strcat(sstr, " (frequency damping hopping-rate)");
strcpy(str, sstr);
@@ -366,8 +334,8 @@ bool msr2msr_theory(char *str, int &tag, int &noOfAddionalParams)
strcpy(sstr, "internFld ");
// tokenize the rest and extract the first three parameters
tokens = line.Tokenize(" \t");
Int_t noTokens = tokens->GetEntries();
tokens = PStringUtils::Split(line.Data(), " \t");
std::size_t noTokens = tokens.size();
if (noTokens < 4) {
std::cout << std::endl << "**ERROR** in THEORY block";
std::cout << std::endl << " Line: '" << str << "' is not a valid internFld statement.";
@@ -377,8 +345,7 @@ bool msr2msr_theory(char *str, int &tag, int &noOfAddionalParams)
strcat(sstr, " _x_");
for (Int_t i=1; i<4; i++) {
strcat(sstr, " ");
ostr = dynamic_cast<TObjString*>(tokens->At(i));
strcat(sstr, ostr->GetString().Data());
strcat(sstr, tokens[i].c_str());
}
strcat(sstr, " (fraction phase frequency Trate Lrate)");
strcpy(str, sstr);
@@ -390,8 +357,8 @@ bool msr2msr_theory(char *str, int &tag, int &noOfAddionalParams)
strcpy(sstr, "internBsl ");
// tokenize the rest and extract the first three parameters
tokens = line.Tokenize(" \t");
Int_t noTokens = tokens->GetEntries();
tokens = PStringUtils::Split(line.Data(), " \t");
std::size_t noTokens = tokens.size();
if (noTokens < 4) {
std::cout << std::endl << "**ERROR** in THEORY block";
std::cout << std::endl << " Line: '" << str << "' is not a valid internBsl statement.";
@@ -401,8 +368,7 @@ bool msr2msr_theory(char *str, int &tag, int &noOfAddionalParams)
strcat(sstr, " _x_");
for (Int_t i=1; i<4; i++) {
strcat(sstr, " ");
ostr = dynamic_cast<TObjString*>(tokens->At(i));
strcat(sstr, ostr->GetString().Data());
strcat(sstr, tokens[i].c_str());
}
strcat(sstr, " (fraction phase frequency Trate Lrate)");
strcpy(str, sstr);
+3 -3
View File
@@ -2046,7 +2046,7 @@ void PTextEdit::musrWiz()
// handle return status of musrWiz
connect(proc, static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
[=](int exitCode, QProcess::ExitStatus exitStatus){ exitStatusMusrWiz(exitCode, exitStatus); });
[=, this](int exitCode, QProcess::ExitStatus exitStatus){ exitStatusMusrWiz(exitCode, exitStatus); });
// make sure that the system environment variables are properly set
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
@@ -2761,7 +2761,7 @@ void PTextEdit::musrT0()
proc->setWorkingDirectory(workDir);
connect(proc, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
[=](int exitCode, QProcess::ExitStatus exitStatus){ fileReload(); });
[=, this](int exitCode, QProcess::ExitStatus exitStatus){ fileReload(); });
proc->start(cmd, arg);
if (!proc->waitForStarted()) {
@@ -2921,7 +2921,7 @@ void PTextEdit::musrSetSteps()
// handle return status of musrStep
connect(proc, static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
[=](int exitCode, QProcess::ExitStatus exitStatus){ exitStatusMusrSetSteps(exitCode, exitStatus); });
[=, this](int exitCode, QProcess::ExitStatus exitStatus){ exitStatusMusrSetSteps(exitCode, exitStatus); });
// make sure that the system environment variables are properly set
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
+3 -3
View File
@@ -2160,7 +2160,7 @@ void PTextEdit::musrWiz()
// handle return status of musrWiz
connect(proc, static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
[=](int exitCode, QProcess::ExitStatus exitStatus){ exitStatusMusrWiz(exitCode, exitStatus); });
[=, this](int exitCode, QProcess::ExitStatus exitStatus){ exitStatusMusrWiz(exitCode, exitStatus); });
// make sure that the system environment variables are properly set
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
@@ -2868,7 +2868,7 @@ void PTextEdit::musrT0()
proc->setWorkingDirectory(workDir);
connect(proc, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
[=](int exitCode, QProcess::ExitStatus exitStatus){ fileReload(); });
[=, this](int exitCode, QProcess::ExitStatus exitStatus){ fileReload(); });
proc->start(cmd, arg);
if (!proc->waitForStarted()) {
@@ -3022,7 +3022,7 @@ void PTextEdit::musrSetSteps()
// handle return status of musrStep
connect(proc, static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
[=](int exitCode, QProcess::ExitStatus exitStatus){ exitStatusMusrSetSteps(exitCode, exitStatus); });
[=, this](int exitCode, QProcess::ExitStatus exitStatus){ exitStatusMusrSetSteps(exitCode, exitStatus); });
// make sure that the system environment variables are properly set
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
+37
View File
@@ -0,0 +1,37 @@
#------------------------------------------------------
# CMakeLists.txt for strToNum
#
# little stand-alone test driver for the dependency-free
# PStringUtils class (pure C++17, no ROOT needed).
#
# build (stand-alone):
# cmake -S . -B build
# cmake --build build
# ./build/strToNum
#
# Andreas Suter, 2026/06/06
#------------------------------------------------------
cmake_minimum_required(VERSION 3.9)
project(strToNum VERSION 0.1 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
if (NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Debug)
endif (NOT CMAKE_BUILD_TYPE)
#--- the class under test lives in src/classes, its header in src/include -----
set(MUSRFIT_SRC ${CMAKE_CURRENT_SOURCE_DIR}/../../classes)
set(MUSRFIT_INC ${CMAKE_CURRENT_SOURCE_DIR}/../../include)
add_executable(strToNum
strToNum.cpp
${MUSRFIT_SRC}/PStringUtils.cpp
)
target_include_directories(strToNum
PRIVATE ${MUSRFIT_INC}
)
+304
View File
@@ -0,0 +1,304 @@
/***************************************************************************
strToNum.cpp
Author: Andreas Suter
e-mail: andreas.suter@psi.ch
Little stand-alone test driver for the PStringUtils class. It exercises
Split / IsInt / IsFloat / ToInt / ToDouble / IsEqualNoCase /
ContainsNoCase / BeginsWithNoCase and reports a pass/fail summary.
Usage:
strToNum -> run the built-in test suite
strToNum <string> -> show what PStringUtils makes of the given
string(s) on the command line (ad-hoc checks)
strToNum -i -> interactive mode: type a string at the prompt
and see the result of every PStringUtils method
(empty line or 'quit' to leave)
***************************************************************************/
/***************************************************************************
* Copyright (C) 2007-2026 by Andreas Suter *
* andreas.suter@psi.ch *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include <cmath>
#include <iostream>
#include <string>
#include <vector>
#include "PStringUtils.h"
//--------------------------------------------------------------------------
// tiny test bookkeeping
//--------------------------------------------------------------------------
static int gPassed = 0;
static int gFailed = 0;
static void check(const std::string &what, bool ok)
{
if (ok) {
++gPassed;
std::cout << " [ ok ] " << what << std::endl;
} else {
++gFailed;
std::cout << " [FAIL] " << what << std::endl;
}
}
//--------------------------------------------------------------------------
static std::string vecToStr(const std::vector<std::string> &v)
{
std::string s = "{";
for (std::vector<std::string>::size_type i = 0; i < v.size(); ++i) {
s += "'" + v[i] + "'";
if (i + 1 < v.size())
s += ", ";
}
s += "}";
return s;
}
//--------------------------------------------------------------------------
static void testSplit()
{
std::cout << "Split:" << std::endl;
std::vector<std::string> r = PStringUtils::Split("alpha beta gamma", " ");
std::cout << " input: 'alpha beta gamma', delim: ' '" << std::endl;
check("3 tokens on single space delimiter",
r.size() == 3 && r[0] == "alpha" && r[1] == "beta" && r[2] == "gamma");
std::cout << "----" << std::endl;
r = PStringUtils::Split(" , 1 , 2 ,, 3 ,", " ,");
std::cout << " input: ' , 1 , 2 ,, 3 ,', delim: ' ,'" << std::endl;
std::cout << " -> ";
for (auto i: r)
std::cout << i << ", ";
std::cout << std::endl;
check("mixed/repeated delimiters, empty tokens skipped",
r.size() == 3 && r[0] == "1" && r[1] == "2" && r[2] == "3");
std::cout << "----" << std::endl;
r = PStringUtils::Split("", " ");
check("empty input -> no tokens", r.empty());
r = PStringUtils::Split(" ", " ");
check("delimiters only -> no tokens", r.empty());
r = PStringUtils::Split("nodelim", ",;");
check("no delimiter present -> single token",
r.size() == 1 && r[0] == "nodelim");
}
//--------------------------------------------------------------------------
static void testIsInt()
{
std::cout << "IsInt:" << std::endl;
check("'12345' is int", PStringUtils::IsInt("12345"));
check("' 42 ' (surrounding ws) is int", PStringUtils::IsInt(" 42 "));
check("'-5' is int (negative)", PStringUtils::IsInt("-5"));
check("'+42' is int (positive sign)", PStringUtils::IsInt("+42"));
check("'' is not int", !PStringUtils::IsInt(""));
check("' ' (ws only) is not int", !PStringUtils::IsInt(" "));
check("'-' (sign only) is not int", !PStringUtils::IsInt("-"));
check("'+-5' (double sign) is not int", !PStringUtils::IsInt("+-5"));
check("'5-3' (sign after digit) is not int", !PStringUtils::IsInt("5-3"));
check("'3.14' is not int", !PStringUtils::IsInt("3.14"));
check("'12a' is not int", !PStringUtils::IsInt("12a"));
}
//--------------------------------------------------------------------------
static void testIsFloat()
{
std::cout << "IsFloat:" << std::endl;
check("'3.14' is float", PStringUtils::IsFloat("3.14"));
check("'-1.2e-3' is float", PStringUtils::IsFloat("-1.2e-3"));
check("'+42' is float", PStringUtils::IsFloat("+42"));
check("'.5' is float", PStringUtils::IsFloat(".5"));
check("' 6.022e23 ' (surrounding ws) is float",
PStringUtils::IsFloat(" 6.022e23 "));
check("'' is not float", !PStringUtils::IsFloat(""));
check("'nan' is not float", !PStringUtils::IsFloat("nan"));
check("'inf' is not float", !PStringUtils::IsFloat("inf"));
check("'1.2.3' is not float", !PStringUtils::IsFloat("1.2.3"));
check("'12abc' is not float", !PStringUtils::IsFloat("12abc"));
}
//--------------------------------------------------------------------------
static void testToInt()
{
std::cout << "ToInt:" << std::endl;
bool ok = false;
check("'42' -> 42, ok", PStringUtils::ToInt("42", &ok) == 42 && ok);
check("' -7' -> -7, ok", PStringUtils::ToInt(" -7", &ok) == -7 && ok);
check("'123abc' -> 123, ok (trailing ignored)",
PStringUtils::ToInt("123abc", &ok) == 123 && ok);
check("'abc' -> 0, !ok", PStringUtils::ToInt("abc", &ok) == 0 && !ok);
check("'' -> 0, !ok", PStringUtils::ToInt("", &ok) == 0 && !ok);
check("'99999999999999999999' -> out of range, !ok",
(PStringUtils::ToInt("99999999999999999999", &ok), !ok));
check("null ok pointer is tolerated", PStringUtils::ToInt("17") == 17);
}
//--------------------------------------------------------------------------
static void testToDouble()
{
std::cout << "ToDouble:" << std::endl;
bool ok = false;
check("'3.14' -> 3.14, ok",
std::fabs(PStringUtils::ToDouble("3.14", &ok) - 3.14) < 1e-12 && ok);
check("' +1.5e2' -> 150, ok",
std::fabs(PStringUtils::ToDouble(" +1.5e2", &ok) - 150.0) < 1e-9 && ok);
check("'2.5xyz' -> 2.5, ok (trailing ignored)",
std::fabs(PStringUtils::ToDouble("2.5xyz", &ok) - 2.5) < 1e-12 && ok);
check("'abc' -> 0.0, !ok", PStringUtils::ToDouble("abc", &ok) == 0.0 && !ok);
check("'' -> 0.0, !ok", PStringUtils::ToDouble("", &ok) == 0.0 && !ok);
check("'1e400' -> out of range, !ok",
(PStringUtils::ToDouble("1e400", &ok), !ok));
check("null ok pointer is tolerated",
std::fabs(PStringUtils::ToDouble("0.25") - 0.25) < 1e-12);
}
//--------------------------------------------------------------------------
static void testCaseHelpers()
{
std::cout << "IsEqualNoCase / ContainsNoCase / BeginsWithNoCase:" << std::endl;
check("'Fit' == 'fIT' (no case)", PStringUtils::IsEqualNoCase("Fit", "fIT"));
check("'Fit' != 'Fits'", !PStringUtils::IsEqualNoCase("Fit", "Fits"));
check("'' == ''", PStringUtils::IsEqualNoCase("", ""));
check("'Hello World' contains 'LO WO' (no case)",
PStringUtils::ContainsNoCase("Hello World", "LO WO"));
check("empty needle is contained",
PStringUtils::ContainsNoCase("abc", ""));
check("needle longer than haystack is not contained",
!PStringUtils::ContainsNoCase("ab", "abc"));
check("'THEORY' begins with 'the' (no case)",
PStringUtils::BeginsWithNoCase("THEORY", "the"));
check("'THEORY' does not begin with 'ory'",
!PStringUtils::BeginsWithNoCase("THEORY", "ory"));
}
//--------------------------------------------------------------------------
static void inspect(const std::string &str)
{
std::cout << "Inspecting '" << str << "':" << std::endl;
std::cout << " Split(' \\t,;') = "
<< vecToStr(PStringUtils::Split(str, " \t,;")) << std::endl;
std::cout << " IsInt = " << (PStringUtils::IsInt(str) ? "true" : "false") << std::endl;
std::cout << " IsFloat = " << (PStringUtils::IsFloat(str) ? "true" : "false") << std::endl;
bool ok = false;
int i = PStringUtils::ToInt(str, &ok);
std::cout << " ToInt = " << i << " (ok=" << (ok ? "true" : "false") << ")" << std::endl;
double d = PStringUtils::ToDouble(str, &ok);
std::cout << " ToDouble = " << d << " (ok=" << (ok ? "true" : "false") << ")" << std::endl;
}
//--------------------------------------------------------------------------
static void usage(const char *prog)
{
std::cout
<< "usage: " << prog << " [options] [string ...]\n"
<< "\n"
<< "Little stand-alone test driver for the PStringUtils class. It exercises\n"
<< "Split / IsInt / IsFloat / ToInt / ToDouble / IsEqualNoCase /\n"
<< "ContainsNoCase / BeginsWithNoCase.\n"
<< "\n"
<< "options:\n"
<< " -h, --help show this help and exit\n"
<< " -i, --interactive interactive mode: type a string at the prompt and\n"
<< " see the result of every PStringUtils method\n"
<< " (empty line, 'quit'/'exit' or Ctrl-D leaves)\n"
<< "\n"
<< "arguments:\n"
<< " string ... one or more strings to inspect; the result of every\n"
<< " PStringUtils method is printed for each of them\n"
<< "\n"
<< "with no options and no arguments the built-in test suite is run; the exit\n"
<< "code is 0 if all checks pass and 1 otherwise.\n"
<< "\n"
<< "examples:\n"
<< " " << prog << " run the built-in test suite\n"
<< " " << prog << " \" -42xyz\" 1.5e-3 inspect the given strings\n"
<< " " << prog << " -i interactive mode\n";
}
//--------------------------------------------------------------------------
static void interactive()
{
std::cout << "==== PStringUtils interactive mode ====" << std::endl;
std::cout << "Enter a string to test the methods on it." << std::endl;
std::cout << "An empty line or 'quit' leaves." << std::endl << std::endl;
std::string line;
while (true) {
std::cout << "strToNum> " << std::flush;
if (!std::getline(std::cin, line)) // EOF (e.g. Ctrl-D)
break;
if (line.empty() || line == "quit" || line == "exit")
break;
inspect(line);
std::cout << std::endl;
}
}
//--------------------------------------------------------------------------
int main(int argc, char *argv[])
{
if (argc > 1) {
const std::string arg1 = argv[1];
if (arg1 == "-h" || arg1 == "--help") {
usage(argv[0]);
return 0;
}
if (arg1 == "-i" || arg1 == "--interactive") {
interactive();
return 0;
}
// ad-hoc mode: inspect the strings given on the command line
for (int i = 1; i < argc; ++i) {
inspect(argv[i]);
std::cout << std::endl;
}
return 0;
}
std::cout << "==== PStringUtils test suite ====" << std::endl << std::endl;
testSplit();
testIsInt();
testIsFloat();
testToInt();
testToDouble();
testCaseHelpers();
std::cout << std::endl
<< "==== summary: " << gPassed << " passed, "
<< gFailed << " failed ====" << std::endl;
return (gFailed == 0) ? 0 : 1;
}