From 4bf4b89af4ed0b7bebda904e149b3d7c5517b405 Mon Sep 17 00:00:00 2001
From: Mark Olesen <Mark.Olesen@esi-group.com>
Date: Tue, 13 Nov 2018 14:12:53 +0100
Subject: [PATCH] ENH: support scalar predicates and lists of scalar predicates
 (#1056)

---
 applications/test/scalarPredicates/Make/files |   3 +
 .../test/scalarPredicates/Make/options        |   1 +
 .../scalarPredicates/Test-scalarPredicates.C  | 158 ++++++++
 src/OpenFOAM/Make/files                       |   2 +
 .../predicates/scalar/scalarPredicates.C      | 332 +++++++++++++++++
 .../predicates/scalar/scalarPredicates.H      | 341 ++++++++++++++++++
 .../predicates/scalar/scalarPredicatesI.H     | 161 +++++++++
 7 files changed, 998 insertions(+)
 create mode 100644 applications/test/scalarPredicates/Make/files
 create mode 100644 applications/test/scalarPredicates/Make/options
 create mode 100644 applications/test/scalarPredicates/Test-scalarPredicates.C
 create mode 100644 src/OpenFOAM/primitives/predicates/scalar/scalarPredicates.C
 create mode 100644 src/OpenFOAM/primitives/predicates/scalar/scalarPredicates.H
 create mode 100644 src/OpenFOAM/primitives/predicates/scalar/scalarPredicatesI.H

diff --git a/applications/test/scalarPredicates/Make/files b/applications/test/scalarPredicates/Make/files
new file mode 100644
index 00000000000..4fa35a46879
--- /dev/null
+++ b/applications/test/scalarPredicates/Make/files
@@ -0,0 +1,3 @@
+Test-scalarPredicates.C
+
+EXE = $(FOAM_USER_APPBIN)/Test-scalarPredicates
diff --git a/applications/test/scalarPredicates/Make/options b/applications/test/scalarPredicates/Make/options
new file mode 100644
index 00000000000..9d6f459ad89
--- /dev/null
+++ b/applications/test/scalarPredicates/Make/options
@@ -0,0 +1 @@
+/**/
diff --git a/applications/test/scalarPredicates/Test-scalarPredicates.C b/applications/test/scalarPredicates/Test-scalarPredicates.C
new file mode 100644
index 00000000000..22338e91746
--- /dev/null
+++ b/applications/test/scalarPredicates/Test-scalarPredicates.C
@@ -0,0 +1,158 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  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;
+}
+
+
+// ************************************************************************* //
diff --git a/src/OpenFOAM/Make/files b/src/OpenFOAM/Make/files
index 7af81c16549..90ba8622095 100644
--- a/src/OpenFOAM/Make/files
+++ b/src/OpenFOAM/Make/files
@@ -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
diff --git a/src/OpenFOAM/primitives/predicates/scalar/scalarPredicates.C b/src/OpenFOAM/primitives/predicates/scalar/scalarPredicates.C
new file mode 100644
index 00000000000..921efb7509c
--- /dev/null
+++ b/src/OpenFOAM/primitives/predicates/scalar/scalarPredicates.C
@@ -0,0 +1,332 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  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;
+}
+
+
+// ************************************************************************* //
diff --git a/src/OpenFOAM/primitives/predicates/scalar/scalarPredicates.H b/src/OpenFOAM/primitives/predicates/scalar/scalarPredicates.H
new file mode 100644
index 00000000000..fbb57cc5593
--- /dev/null
+++ b/src/OpenFOAM/primitives/predicates/scalar/scalarPredicates.H
@@ -0,0 +1,341 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  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
+
+// ************************************************************************* //
diff --git a/src/OpenFOAM/primitives/predicates/scalar/scalarPredicatesI.H b/src/OpenFOAM/primitives/predicates/scalar/scalarPredicatesI.H
new file mode 100644
index 00000000000..9074fa2891d
--- /dev/null
+++ b/src/OpenFOAM/primitives/predicates/scalar/scalarPredicatesI.H
@@ -0,0 +1,161 @@
+/*---------------------------------------------------------------------------*\
+  =========                 |
+  \\      /  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);
+}
+
+
+// ************************************************************************* //
-- 
GitLab