Skip to content
Snippets Groups Projects
Commit 4bf4b89a authored by Mark OLESEN's avatar Mark OLESEN
Browse files

ENH: support scalar predicates and lists of scalar predicates (#1056)

parent 1883a872
Branches
Tags
No related merge requests found
Test-scalarPredicates.C
EXE = $(FOAM_USER_APPBIN)/Test-scalarPredicates
/**/
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2018 OpenCFD Ltd.
\\/ M anipulation |
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM 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 3 of the License, or
(at your option) any later version.
OpenFOAM 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 OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
Application
Test-scalarPredicates
Description
Simple tests using predicates for scalars
\*---------------------------------------------------------------------------*/
#include "IOstreams.H"
#include "labelList.H"
#include "scalarList.H"
#include "scalarPredicates.H"
#include "FlatOutput.H"
#include "Tuple2.H"
#include "StringStream.H"
using namespace Foam;
void doTest(const scalarList& values, const predicates::scalars& accept)
{
// Also tests that output is suppressed
Info<<"Have: " << accept.size() << " predicates" << accept << endl;
Info<<"values: " << flatOutput(values) << endl;
for (const scalar& value : values)
{
if (accept.match(value))
{
Info<< "matched: " << value << " at "
<< flatOutput(accept.matching(value)) << nl;
}
}
labelList matches = accept.matching(values);
Info<< "values matched at positions: " << flatOutput(matches) << nl;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
// Main program:
int main(int argc, char *argv[])
{
scalarList values
({
-10,
10,
0,
3.145,
1000.56,
1e5,
});
Info<< nl << "From a mixed list of entries" << nl;
{
predicates::scalars
accept
({
predicates::scalars::lessOp(10),
predicates::scalars::greaterOp(100),
predicates::scalars::orOp
(
predicates::scalars::lessOp(-5),
predicates::scalars::greaterOp(100)
),
predicates::scalars::orOp
(
[](const scalar& val){ return val < 5; },
predicates::scalars::greaterOp(100)
),
[](const scalar& val){ return val < -8; },
// Rather wordy, word normally not be called manually
predicates::scalars::operation("le", -9),
});
doTest(values, accept);
}
Info<< nl << "Construct from list input" << nl;
{
List<Tuple2<word, scalar>> entries
({
{"less", 10},
{"greater", 100},
// Not possible > ((less -5) or (greater 100))
{"less", -8},
{"le", -9},
});
predicates::scalars accept(entries);
doTest(values, accept);
}
Info<< nl << "Construct from initializer_list input" << nl;
{
predicates::scalars accept
({
{"less", 10},
{"greater", 100},
// Not possible > ((less -5) or (greater 100))
{"less", -8},
{"le", -9},
});
doTest(values, accept);
}
Info<< nl << "Construct from Istream" << nl;
{
IStringStream is("((less 10) (greater 100) (less -8) (le -9))");
predicates::scalars accept(is);
doTest(values, accept);
// change some location
accept[0] = predicates::scalars::greaterOp(1000);
Info<< nl << "Reset some values" << nl;
doTest(values, accept);
}
Info<< "\nEnd\n" << endl;
return 0;
}
// ************************************************************************* //
......@@ -123,6 +123,8 @@ $(strings)/parsing/parsing.C
ops = primitives/ops
$(ops)/flipOp.C
primitives/predicates/scalar/scalarPredicates.C
primitives/hashes/Hasher/Hasher.C
sha1 = primitives/hashes/SHA1
......
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2018 OpenCFD Ltd.
\\/ M anipulation |
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM 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 3 of the License, or
(at your option) any later version.
OpenFOAM 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 OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
\*---------------------------------------------------------------------------*/
#include "scalarPredicates.H"
#include "HashSet.H"
#include "FlatOutput.H"
#include "Tuple2.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
const Foam::Enum
<
Foam::predicates::scalars::opType
>
Foam::predicates::scalars::opNames
({
{ opType::EQUAL, "eq" },
{ opType::EQUAL, "equal" },
{ opType::NOT_EQUAL, "neq" },
{ opType::NOT_EQUAL, "notEqual" },
{ opType::LESS, "lt" },
{ opType::LESS, "less" },
{ opType::LESS_EQ, "le" },
{ opType::LESS_EQ, "lessEq" },
{ opType::GREATER, "gt" },
{ opType::GREATER, "greater" },
{ opType::GREATER_EQ, "ge" },
{ opType::GREATER_EQ, "greaterEq" },
});
// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
std::function<bool(Foam::scalar)> Foam::predicates::scalars::operation
(
const enum predicates::scalars::opType op,
const Foam::scalar opVal,
const Foam::scalar tol
)
{
switch (op)
{
case opType::EQUAL:
return equalOp(opVal, tol);
break;
case opType::NOT_EQUAL:
return notEqualOp(opVal, tol);
break;
case opType::LESS:
return lessOp(opVal);
break;
case opType::LESS_EQ:
return lessEqOp(opVal);
break;
case opType::GREATER:
return greaterOp(opVal);
break;
case opType::GREATER_EQ:
return greaterEqOp(opVal);
break;
case opType::ALWAYS:
return trueOp();
break;
default:
break;
}
return falseOp();
}
// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
namespace Foam
{
// Check for bad/unknown operations
template<class Container, class Get0>
static bool hasBadEntries
(
const Container& entries,
const Get0& get0
)
{
for (const auto& entry : entries)
{
if (!Foam::predicates::scalars::opNames.found(get0(entry)))
{
return true;
}
}
return false;
}
// Print bad/unknown operations
template<class Error, class Container, class Get0, class Get1>
static Error& printBadEntries
(
Error& err,
const Container& entries,
const Get0& get0,
const Get1& get1
)
{
labelHashSet badIndices;
label idx = 0;
for (const auto& entry : entries)
{
if (!Foam::predicates::scalars::opNames.found(get0(entry)))
{
badIndices.insert(idx);
}
++idx;
}
err
<< "Entries with unknown operations:" << nl
<< idx << nl
<< '(' << nl;
idx = 0;
for (const auto& entry : entries)
{
const bool bad = badIndices.found(idx);
++idx;
if (bad)
{
err << ">>> ";
}
else
{
err << " ";
}
err << '(' << get0(entry) << ' ' << get1(entry) << ')';
if (bad)
{
err << " <<<";
}
err << nl;
}
err << ')' << nl;
return err;
}
} // End namespace Foam
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::predicates::scalars::scalars
(
std::initializer_list<std::pair<word, scalar>> entries
)
:
List<unary>(entries.size())
{
// Access
const auto get0 =
[](const std::pair<word,scalar>& entry) { return entry.first; };
const auto get1 =
[](const std::pair<word,scalar>& entry) { return entry.second; };
// Check for bad/unknown operations
if (hasBadEntries(entries, get0))
{
printBadEntries(FatalErrorInFunction, entries, get0, get1)
<< exit(FatalError);
}
// Appears to be good
auto& list = *this;
label idx = 0;
for (const auto& entry : entries)
{
list[idx] = predicates::scalars::operation(entry);
++idx;
}
}
Foam::predicates::scalars::scalars
(
const UList<Tuple2<word, scalar>>& entries
)
:
List<unary>(entries.size())
{
// Access
const auto get0 =
[](const Tuple2<word,scalar>& entry) { return entry.first(); };
const auto get1 =
[](const Tuple2<word,scalar>& entry) { return entry.second(); };
// Check for bad/unknown operations
if (hasBadEntries(entries, get0))
{
printBadEntries(FatalErrorInFunction, entries, get0, get1)
<< exit(FatalError);
}
// Appears to be good
auto& list = *this;
label idx = 0;
for (const auto& entry : entries)
{
list[idx] = predicates::scalars::operation(entry);
++idx;
}
}
Foam::predicates::scalars::scalars(Istream& is)
:
List<unary>()
{
is >> *this;
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
Foam::label Foam::predicates::scalars::find
(
const scalar& value,
const label start
) const
{
const label len = this->size();
if (start >= 0 && len)
{
// auto iter = this->cbegin();
for (label i = start; i < len; ++i)
{
if ((*this)[i](value))
{
return i;
}
}
}
return -1;
}
Foam::label Foam::predicates::scalars::rfind
(
const scalar& value,
const label pos
) const
{
const label len1 = (this->size()-1);
// pos == -1 has same meaning as std::string::npos - search from end
for (label i = ((pos >= 0 && pos < len1) ? pos : len1); i >= 0; --i)
{
if ((*this)[i](value))
{
return i;
}
}
return -1;
}
// * * * * * * * * * * * * * * * IOstream Operators * * * * * * * * * * * * //
Foam::Istream& Foam::operator>>(Istream& is, Foam::predicates::scalars& list)
{
// Read tuples
List<Tuple2<word, scalar>> entries(is);
// Access
const auto get0 =
[](const Tuple2<word,scalar>& entry) { return entry.first(); };
const auto get1 =
[](const Tuple2<word,scalar>& entry) { return entry.second(); };
// Check for bad/unknown operations
if (hasBadEntries(entries, get0))
{
printBadEntries(FatalIOErrorInFunction(is), entries, get0, get1)
<< exit(FatalIOError);
}
// Appears to be good
list.resize(entries.size());
label idx = 0;
for (const Tuple2<word, scalar>& entry : entries)
{
list[idx] = predicates::scalars::operation(entry);
++idx;
}
return is;
}
// ************************************************************************* //
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2018 OpenCFD Ltd.
\\/ M anipulation |
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM 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 3 of the License, or
(at your option) any later version.
OpenFOAM 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 OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
Class
Foam::predicates::scalars
Description
A list of unary predicates (tests) on scalars.
Includes a number of standard comparison predicates
(eg, "less", "greater", ...)
Note
This class is still subject to larger changes (2018-11) as it matures.
SourceFiles
scalarPredicates.C
scalarPredicatesI.H
\*---------------------------------------------------------------------------*/
#ifndef scalarPredicates_H
#define scalarPredicates_H
#include "scalar.H"
#include "predicates.H"
#include "Enum.H"
#include "List.H"
#include "Tuple2.H"
#include <functional>
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
namespace predicates
{
/*---------------------------------------------------------------------------*\
Class scalars Declaration
\*---------------------------------------------------------------------------*/
class scalars
:
public List<std::function<bool(Foam::scalar)>>
{
public:
// Public types
//- The unary function type for testing a scalar
typedef std::function<bool(Foam::scalar)> unary;
//- Enumerations for some standard comparison predicates
enum opType
{
EQUAL, //!< "eq", "equal"
NOT_EQUAL, //!< "neq", "notEqual"
LESS, //!< "lt", "less"
LESS_EQ, //!< "le", "lessEq"
GREATER, //!< "gt", "greater"
GREATER_EQ, //!< "ge", "greaterEq"
ALWAYS, //!< Always matches
NEVER, //!< Never matches
};
//- Names for the opType enumeration.
static const Enum<opType> opNames;
// Standard comparison methods
//- Compare for equality, with specified tolerance (non-negative)
static unary equalOp
(
const scalar opVal,
const scalar tol = VSMALL
)
{
return [=](const scalar val)
{
return (Foam::mag(opVal - val) <= tol);
};
}
//- Compare for inequality, with specified tolerance (non-negative)
static unary notEqualOp
(
const scalar opVal,
const scalar tol = VSMALL
)
{
return [=](const scalar val)
{
return (Foam::mag(opVal - val) > tol);
};
}
//- Test if value is 'less' than prescribed
static unary lessOp(const scalar opVal)
{
return std::bind
(
std::less<scalar>(), std::placeholders::_1, opVal
);
}
//- Test if value is 'less_equal' to prescribed
static unary lessEqOp(const scalar opVal)
{
return std::bind
(
std::less_equal<scalar>(), std::placeholders::_1, opVal
);
}
//- Test if value is 'greater' than prescribed
//- Compare scalar values for inequality
static unary greaterOp(const scalar opVal)
{
return std::bind
(
std::greater<scalar>(), std::placeholders::_1, opVal
);
}
//- Test if value is 'greater_equal' to prescribed
static unary greaterEqOp(const scalar opVal)
{
return std::bind
(
std::greater_equal<scalar>(), std::placeholders::_1, opVal
);
}
// Special purpose comparison methods
//- Predicate that always returns true
static unary trueOp()
{
return [](const scalar) { return true; };
}
//- Predicate that always returns false
static unary falseOp()
{
return [](const scalar) { return false; };
}
// Combining operations
//- Combine unary tests as an AND operation
static unary andOp(const unary& test1, const unary& test2)
{
return [=](const scalar value)
{
return (test1(value) && test2(value));
};
}
//- Combine unary tests as an OR operation
static unary orOp(const unary& test1, const unary& test2)
{
return [=](const scalar value)
{
return (test1(value) || test2(value));
};
}
// Factory for standard comparison methods
//- Standard comparison method by type
static unary operation
(
const opType op,
const scalar opVal,
const scalar tol = VSMALL
);
//- Standard comparison method by name
inline static unary operation
(
const word& opName,
const scalar opVal,
const scalar tol = VSMALL
);
//- Standard comparison method by (name, value)
inline static unary operation
(
const Tuple2<word, scalar>& op,
const scalar tol = VSMALL
);
//- Standard comparison method by (name, value)
inline static unary operation
(
const std::pair<word, scalar>& op,
const scalar tol = VSMALL
);
// Constructors
//- Inherit constructors from List of unary functions
using List<unary>::List;
//- Construct from an initializer list of (opName opValue) tuples
explicit scalars(std::initializer_list<std::pair<word, scalar>> entries);
//- Copy construct from a list of (opName opValue) tuples
explicit scalars(const UList<Tuple2<word, scalar>>& entries);
//- Construct from Istream, from list of (opName opValue) tuples
explicit scalars(Istream& is);
//- Destructor
~scalars() = default;
// Member Functions
// Search
//- Index of the first match for the value.
// When start is specified, any occurrences before start are ignored.
// Linear search.
// \return position in list or -1 if not found.
label find(const scalar& value, const label start=0) const;
//- Index of the last match for the value.
// When pos is specified, any occurrences after pos are ignored.
// Linear search.
// \return position in list or -1 if not found.
label rfind(const scalar& value, const label pos=-1) const;
//- True if the value matches any in the list.
// When start is specified, any occurences before start are ignored.
// Linear search.
// \return true if found.
inline bool found(const scalar& value, const label start=0) const;
//- Match any condition in the list.
//
// \return True if the value matches any condition in the list.
inline bool match(const scalar& value) const;
//- Match any condition in the list.
//
// \return True if the value matches any condition in the list.
inline bool matchAny(const scalar& value) const;
//- Match all conditions in the list.
//
// \return True if the value matches all conditions in the list.
inline bool matchAll(const scalar& value) const;
//- Extract list indices for all matches.
//
// \return The locations (indices) in the input list where match()
// is true
inline labelList matching(const scalar& value) const;
//- Extract list indices for all matches.
//
// \param input A list of scalar inputs to test
// \param invert invert the matching logic
// \return The locations (indices) in the input list where match()
// is true
inline labelList matching
(
const UList<scalar>& input,
const bool invert=false
) const;
// Member Operators
//- Identical to found(), match(), for use as a predicate.
inline bool operator()(const scalar& value) const;
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace predicates
// IOstream Operators
//- Suppressed write
Ostream& operator<<(Ostream& os, const predicates::scalars& list)
{
return os;
}
//- Read list of (opName opValue) tuple
Istream& operator>>(Istream& is, predicates::scalars& list);
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#include "scalarPredicatesI.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2018 OpenCFD Ltd.
\\/ M anipulation |
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM 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 3 of the License, or
(at your option) any later version.
OpenFOAM 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 OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
\*---------------------------------------------------------------------------*/
// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
inline Foam::predicates::scalars::unary Foam::predicates::scalars::operation
(
const word& opName,
const scalar opVal,
const scalar tol
)
{
return operation(opNames[opName], opVal, tol);
}
inline Foam::predicates::scalars::unary Foam::predicates::scalars::operation
(
const Tuple2<word, scalar>& op,
const scalar tol
)
{
return operation(opNames[op.first()], op.second(), tol);
}
inline Foam::predicates::scalars::unary Foam::predicates::scalars::operation
(
const std::pair<word, scalar>& op,
const scalar tol
)
{
return operation(opNames[op.first], op.second, tol);
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
inline bool Foam::predicates::scalars::found
(
const scalar& value,
const label start
) const
{
return (this->find(value, start) >= 0);
}
inline bool Foam::predicates::scalars::match(const scalar& value) const
{
return this->matchAny(value);
}
inline bool Foam::predicates::scalars::matchAny(const scalar& value) const
{
for (const unary& test : *this)
{
if (test(value))
{
return true;
}
}
return false;
}
inline bool Foam::predicates::scalars::matchAll(const scalar& value) const
{
for (const unary& test : *this)
{
if (!test(value))
{
return false;
}
}
return (!this->empty());
}
inline Foam::labelList Foam::predicates::scalars::matching
(
const scalar& value
) const
{
labelList indices(this->size());
label i = 0, count = 0;
for (const unary& test : *this)
{
if (test(value))
{
indices[count] = i;
++count;
}
++i;
}
indices.resize(count);
return indices;
}
inline Foam::labelList Foam::predicates::scalars::matching
(
const UList<scalar>& input,
const bool invert
) const
{
const label len = input.size();
labelList indices(len);
label count = 0;
for (label i=0; i < len; ++i)
{
if (match(input[i]) ? !invert : invert)
{
indices[count] = i;
++count;
}
}
indices.resize(count);
return indices;
}
// * * * * * * * * * * * * * * * Member Operators * * * * * * * * * * * * * //
inline bool Foam::predicates::scalars::operator()(const scalar& value) const
{
return this->found(value);
}
// ************************************************************************* //
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment