// Copyright (c) 2025 OPEN CASCADE SAS // // This file is part of Open CASCADE Technology software library. // // This library is free software; you can redistribute it and/or modify it under // the terms of the GNU Lesser General Public License version 2.1 as published // by the Free Software Foundation, with special exception defined in the file // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT // distribution for complete text of the license and disclaimer of any warranty. // // Alternatively, this file may be used under the terms of Open CASCADE // commercial license or contractual agreement. #ifndef _MathUtils_FunctorScalar_HeaderFile #define _MathUtils_FunctorScalar_HeaderFile #include "math_Vector.hxx" #include #include #include //! @file MathUtils_FunctorScalar.hxx //! @brief Non-virtual functor classes for scalar (1D) functions. //! //! Provides ready-to-use functor classes that work with the template-based //! math API (MathRoot, MathOpt, MathInteg) without virtual dispatch overhead. //! Core utilities for modern math solvers. namespace MathUtils { //! Lambda wrapper for scalar functions with value only. //! Wraps a lambda/callable into a functor with Value() method. //! //! Usage: //! @code //! auto aFunc = ScalarLambda([](double x, double& y) { //! y = x * x - 2.0; //! return true; //! }); //! auto aResult = MathRoot::Brent(aFunc, 0.0, 2.0); //! @endcode //! //! @tparam Lambda callable type with signature bool(double, double&) template class ScalarLambda { public: //! Constructor from lambda/callable. //! @param theLambda callable with signature bool(double theX, double& theY) explicit ScalarLambda(Lambda theLambda) : myLambda(std::move(theLambda)) { } //! Evaluates the function at theX. //! @param[in] theX input value //! @param[out] theY function value f(theX) //! @return true if evaluation succeeded bool Value(double theX, double& theY) const { return myLambda(theX, theY); } private: Lambda myLambda; }; //! Lambda wrapper for scalar functions with value and derivative. //! Wraps a lambda/callable into a functor with Values() method. //! //! Usage: //! @code //! auto aFunc = ScalarLambdaWithDerivative([](double x, double& y, double& dy) { //! y = x * x - 2.0; //! dy = 2.0 * x; //! return true; //! }); //! auto aResult = MathRoot::Newton(aFunc, 1.0); //! @endcode //! //! @tparam Lambda callable type with signature bool(double, double&, double&) template class ScalarLambdaWithDerivative { public: //! Constructor from lambda/callable. //! @param theLambda callable with signature bool(double theX, double& theY, double& theDY) explicit ScalarLambdaWithDerivative(Lambda theLambda) : myLambda(std::move(theLambda)) { } //! Evaluates function and derivative at theX. //! @param[in] theX input value //! @param[out] theY function value f(theX) //! @param[out] theDY derivative value f'(theX) //! @return true if evaluation succeeded bool Values(double theX, double& theY, double& theDY) const { return myLambda(theX, theY, theDY); } //! Evaluates only the function value (for algorithms that don't need derivative). //! @param[in] theX input value //! @param[out] theY function value f(theX) //! @return true if evaluation succeeded bool Value(double theX, double& theY) const { double aDummy = 0.0; return myLambda(theX, theY, aDummy); } private: Lambda myLambda; }; //! Helper function to create ScalarLambda with type deduction. //! @tparam Lambda callable type (auto-deduced) //! @param theLambda callable with signature bool(double, double&) //! @return ScalarLambda wrapper template ScalarLambda MakeScalar(Lambda theLambda) { return ScalarLambda(std::move(theLambda)); } //! Helper function to create ScalarLambdaWithDerivative with type deduction. //! @tparam Lambda callable type (auto-deduced) //! @param theLambda callable with signature bool(double, double&, double&) //! @return ScalarLambdaWithDerivative wrapper template ScalarLambdaWithDerivative MakeScalarWithDerivative(Lambda theLambda) { return ScalarLambdaWithDerivative(std::move(theLambda)); } //! Polynomial functor: f(x) = sum(a[i] * x^i). //! Coefficients are stored in order: a[0] + a[1]*x + a[2]*x^2 + ... //! //! Usage: //! @code //! // x^2 - 2 (find sqrt(2)) //! Polynomial aPoly({-2.0, 0.0, 1.0}); //! //! // x^3 - 6x^2 + 11x - 6 = (x-1)(x-2)(x-3) //! Polynomial aCubic({-6.0, 11.0, -6.0, 1.0}); //! @endcode class Polynomial { public: //! Constructor from initializer list. //! @param theCoeffs coefficients in ascending power order Polynomial(std::initializer_list theCoeffs) : myCoeffs(0, static_cast(theCoeffs.size()) - 1) { int anIdx = 0; for (double aCoeff : theCoeffs) { myCoeffs(anIdx++) = aCoeff; } } //! Constructor from math_Vector. //! @param theCoeffs coefficients in ascending power order explicit Polynomial(const math_Vector& theCoeffs) : myCoeffs(theCoeffs) { } //! Evaluates polynomial at theX using Horner's method. //! @param[in] theX input value //! @param[out] theY polynomial value p(theX) //! @return true (always succeeds for polynomials) bool Value(double theX, double& theY) const { if (myCoeffs.Length() <= 0) { theY = 0.0; return true; } // Horner's method: p(x) = a[0] + x*(a[1] + x*(a[2] + ...)) const int aLast = myCoeffs.Upper(); theY = myCoeffs(aLast); for (int i = aLast - 1; i >= myCoeffs.Lower(); --i) { theY = theY * theX + myCoeffs(i); } return true; } //! Evaluates polynomial and its derivative at theX. //! @param[in] theX input value //! @param[out] theY polynomial value p(theX) //! @param[out] theDY derivative value p'(theX) //! @return true (always succeeds for polynomials) bool Values(double theX, double& theY, double& theDY) const { if (myCoeffs.Length() <= 0) { theY = 0.0; theDY = 0.0; return true; } const int n = myCoeffs.Length(); if (n == 1) { theY = myCoeffs(myCoeffs.Lower()); theDY = 0.0; return true; } // Horner's method for value and derivative simultaneously const int aLower = myCoeffs.Lower(); const int aLast = myCoeffs.Upper(); theY = myCoeffs(aLast); theDY = 0.0; for (int i = aLast - 1; i >= aLower; --i) { theDY = theDY * theX + theY; theY = theY * theX + myCoeffs(i); } return true; } //! Returns the degree of the polynomial. //! @return polynomial degree (number of coefficients - 1) int Degree() const { return myCoeffs.Length() <= 0 ? 0 : myCoeffs.Length() - 1; } //! Returns coefficient by index. //! @param theIndex coefficient index (0 = constant term) //! @return coefficient value double Coefficient(int theIndex) const { const int aIdx = myCoeffs.Lower() + theIndex; return (aIdx >= myCoeffs.Lower() && aIdx <= myCoeffs.Upper()) ? myCoeffs(aIdx) : 0.0; } private: math_Vector myCoeffs; }; //! Rational function functor: f(x) = P(x) / Q(x). //! Both numerator P and denominator Q are polynomials. //! //! Usage: //! @code //! // (x + 1) / (x^2 + 1) //! Rational aRat({1.0, 1.0}, {1.0, 0.0, 1.0}); //! @endcode class Rational { public: //! Constructor from math_Vector. //! @param theNum numerator coefficients (ascending power order) //! @param theDenom denominator coefficients (ascending power order) Rational(const math_Vector& theNum, const math_Vector& theDenom) : myNum(theNum), myDenom(theDenom) { } //! Constructor from initializer lists. //! @param theNum numerator coefficients //! @param theDenom denominator coefficients Rational(std::initializer_list theNum, std::initializer_list theDenom) : myNum(0, static_cast(theNum.size()) - 1), myDenom(0, static_cast(theDenom.size()) - 1) { int anIdx = 0; for (double aCoeff : theNum) { myNum(anIdx++) = aCoeff; } anIdx = 0; for (double aCoeff : theDenom) { myDenom(anIdx++) = aCoeff; } } //! Evaluates rational function at theX. //! @param[in] theX input value //! @param[out] theY function value P(theX)/Q(theX) //! @return false if denominator is zero bool Value(double theX, double& theY) const { double aNum = 0.0; double aDenom = 0.0; // Evaluate numerator using Horner's method if (myNum.Length() > 0) { const int aLast = myNum.Upper(); aNum = myNum(aLast); for (int i = aLast - 1; i >= myNum.Lower(); --i) { aNum = aNum * theX + myNum(i); } } // Evaluate denominator using Horner's method if (myDenom.Length() > 0) { const int aLast = myDenom.Upper(); aDenom = myDenom(aLast); for (int i = aLast - 1; i >= myDenom.Lower(); --i) { aDenom = aDenom * theX + myDenom(i); } } // Check for division by zero if (std::abs(aDenom) < 1e-15) { return false; } theY = aNum / aDenom; return true; } private: math_Vector myNum; math_Vector myDenom; }; //! Composite functor: f(g(x)). //! Evaluates the outer function at the result of the inner function. //! //! Usage: //! @code //! Polynomial aInner({0.0, 1.0}); // g(x) = x //! Polynomial aOuter({-1.0, 0.0, 1.0}); // f(x) = x^2 - 1 //! Composite aComp(aOuter, aInner); // f(g(x)) = x^2 - 1 //! @endcode //! //! @tparam Outer outer function type (must have Value method) //! @tparam Inner inner function type (must have Value method) template class Composite { public: //! Constructor from outer and inner functions. //! @param theOuter outer function f //! @param theInner inner function g Composite(Outer theOuter, Inner theInner) : myOuter(std::move(theOuter)), myInner(std::move(theInner)) { } //! Evaluates composite function f(g(theX)). //! @param[in] theX input value //! @param[out] theY function value f(g(theX)) //! @return false if either evaluation fails bool Value(double theX, double& theY) const { double aInner = 0.0; if (!myInner.Value(theX, aInner)) { return false; } return myOuter.Value(aInner, theY); } private: Outer myOuter; Inner myInner; }; //! Helper function to create Composite with type deduction. //! @tparam Outer outer function type //! @tparam Inner inner function type //! @param theOuter outer function f //! @param theInner inner function g //! @return Composite functor representing f(g(x)) template Composite MakeComposite(Outer theOuter, Inner theInner) { return Composite(std::move(theOuter), std::move(theInner)); } //! Sum of functions functor: f(x) + g(x). //! //! Usage: //! @code //! Polynomial aF({1.0, 2.0}); // f(x) = 1 + 2x //! Polynomial aG({3.0, 0.0, 1.0}); // g(x) = 3 + x^2 //! Sum aSum(aF, aG); // h(x) = 4 + 2x + x^2 //! @endcode //! //! @tparam F first function type //! @tparam G second function type template class Sum { public: //! Constructor from two functions. //! @param theF first function //! @param theG second function Sum(F theF, G theG) : myF(std::move(theF)), myG(std::move(theG)) { } //! Evaluates sum f(theX) + g(theX). //! @param[in] theX input value //! @param[out] theY sum value //! @return false if either evaluation fails bool Value(double theX, double& theY) const { double aF = 0.0; double aG = 0.0; if (!myF.Value(theX, aF)) { return false; } if (!myG.Value(theX, aG)) { return false; } theY = aF + aG; return true; } private: F myF; G myG; }; //! Helper function to create Sum with type deduction. template Sum MakeSum(F theF, G theG) { return Sum(std::move(theF), std::move(theG)); } //! Difference of functions functor: f(x) - g(x). //! //! @tparam F first function type //! @tparam G second function type template class Difference { public: //! Constructor from two functions. //! @param theF first function (minuend) //! @param theG second function (subtrahend) Difference(F theF, G theG) : myF(std::move(theF)), myG(std::move(theG)) { } //! Evaluates difference f(theX) - g(theX). //! @param[in] theX input value //! @param[out] theY difference value //! @return false if either evaluation fails bool Value(double theX, double& theY) const { double aF = 0.0; double aG = 0.0; if (!myF.Value(theX, aF)) { return false; } if (!myG.Value(theX, aG)) { return false; } theY = aF - aG; return true; } private: F myF; G myG; }; //! Helper function to create Difference with type deduction. template Difference MakeDifference(F theF, G theG) { return Difference(std::move(theF), std::move(theG)); } //! Product of functions functor: f(x) * g(x). //! //! @tparam F first function type //! @tparam G second function type template class Product { public: //! Constructor from two functions. //! @param theF first function (multiplicand) //! @param theG second function (multiplier) Product(F theF, G theG) : myF(std::move(theF)), myG(std::move(theG)) { } //! Evaluates product f(theX) * g(theX). //! @param[in] theX input value //! @param[out] theY product value //! @return false if either evaluation fails bool Value(double theX, double& theY) const { double aF = 0.0; double aG = 0.0; if (!myF.Value(theX, aF)) { return false; } if (!myG.Value(theX, aG)) { return false; } theY = aF * aG; return true; } private: F myF; G myG; }; //! Helper function to create Product with type deduction. template Product MakeProduct(F theF, G theG) { return Product(std::move(theF), std::move(theG)); } //! Quotient of functions functor: f(x) / g(x). //! //! @tparam F numerator function type //! @tparam G denominator function type template class Quotient { public: //! Constructor from two functions. //! @param theF numerator function //! @param theG denominator function Quotient(F theF, G theG) : myF(std::move(theF)), myG(std::move(theG)) { } //! Evaluates quotient f(theX) / g(theX). //! @param[in] theX input value //! @param[out] theY quotient value //! @return false if evaluation fails or denominator is zero bool Value(double theX, double& theY) const { double aF = 0.0; double aG = 0.0; if (!myF.Value(theX, aF)) { return false; } if (!myG.Value(theX, aG)) { return false; } if (std::abs(aG) < 1e-15) { return false; } theY = aF / aG; return true; } private: F myF; G myG; }; //! Helper function to create Quotient with type deduction. template Quotient MakeQuotient(F theF, G theG) { return Quotient(std::move(theF), std::move(theG)); } //! Scaled function functor: c * f(x). //! //! @tparam F function type template class Scaled { public: //! Constructor from function and scale factor. //! @param theF function to scale //! @param theScale scale factor Scaled(F theF, double theScale) : myF(std::move(theF)), myScale(theScale) { } //! Evaluates scaled function c * f(theX). //! @param[in] theX input value //! @param[out] theY scaled value //! @return false if evaluation fails bool Value(double theX, double& theY) const { double aF = 0.0; if (!myF.Value(theX, aF)) { return false; } theY = myScale * aF; return true; } private: F myF; double myScale; }; //! Helper function to create Scaled with type deduction. template Scaled MakeScaled(F theF, double theScale) { return Scaled(std::move(theF), theScale); } //! Shifted function functor: f(x) + c. //! //! @tparam F function type template class Shifted { public: //! Constructor from function and shift value. //! @param theF function to shift //! @param theShift shift value (added to output) Shifted(F theF, double theShift) : myF(std::move(theF)), myShift(theShift) { } //! Evaluates shifted function f(theX) + c. //! @param[in] theX input value //! @param[out] theY shifted value //! @return false if evaluation fails bool Value(double theX, double& theY) const { double aF = 0.0; if (!myF.Value(theX, aF)) { return false; } theY = aF + myShift; return true; } private: F myF; double myShift; }; //! Helper function to create Shifted with type deduction. template Shifted MakeShifted(F theF, double theShift) { return Shifted(std::move(theF), theShift); } //! Negated function functor: -f(x). //! //! @tparam F function type template class Negated { public: //! Constructor from function. //! @param theF function to negate explicit Negated(F theF) : myF(std::move(theF)) { } //! Evaluates negated function -f(theX). //! @param[in] theX input value //! @param[out] theY negated value //! @return false if evaluation fails bool Value(double theX, double& theY) const { double aF = 0.0; if (!myF.Value(theX, aF)) { return false; } theY = -aF; return true; } private: F myF; }; //! Helper function to create Negated with type deduction. template Negated MakeNegated(F theF) { return Negated(std::move(theF)); } //! Constant function functor: f(x) = c. class Constant { public: //! Constructor from constant value. //! @param theValue constant value explicit Constant(double theValue) : myValue(theValue) { } //! Evaluates constant function. //! @param[in] theX input value (ignored) //! @param[out] theY constant value //! @return true (always succeeds) bool Value(double /*theX*/, double& theY) const { theY = myValue; return true; } //! Evaluates constant and derivative (derivative is always 0). //! @param[in] theX input value (ignored) //! @param[out] theY constant value //! @param[out] theDY derivative (always 0) //! @return true (always succeeds) bool Values(double /*theX*/, double& theY, double& theDY) const { theY = myValue; theDY = 0.0; return true; } private: double myValue; }; //! Linear function functor: f(x) = a*x + b. class Linear { public: //! Constructor from slope and intercept. //! @param theSlope coefficient a (slope) //! @param theIntercept coefficient b (y-intercept) Linear(double theSlope, double theIntercept) : mySlope(theSlope), myIntercept(theIntercept) { } //! Evaluates linear function a*x + b. //! @param[in] theX input value //! @param[out] theY function value //! @return true (always succeeds) bool Value(double theX, double& theY) const { theY = mySlope * theX + myIntercept; return true; } //! Evaluates linear function and derivative. //! @param[in] theX input value //! @param[out] theY function value //! @param[out] theDY derivative (= slope) //! @return true (always succeeds) bool Values(double theX, double& theY, double& theDY) const { theY = mySlope * theX + myIntercept; theDY = mySlope; return true; } private: double mySlope; double myIntercept; }; //! Sine function functor: f(x) = a * sin(b*x + c) + d. class Sine { public: //! Constructor with full parameters. //! @param theAmplitude amplitude a //! @param theFrequency angular frequency b //! @param thePhase phase shift c //! @param theOffset vertical offset d Sine(double theAmplitude = 1.0, double theFrequency = 1.0, double thePhase = 0.0, double theOffset = 0.0) : myAmplitude(theAmplitude), myFrequency(theFrequency), myPhase(thePhase), myOffset(theOffset) { } //! Evaluates sine function. //! @param[in] theX input value //! @param[out] theY function value //! @return true (always succeeds) bool Value(double theX, double& theY) const { theY = myAmplitude * std::sin(myFrequency * theX + myPhase) + myOffset; return true; } //! Evaluates sine function and derivative. //! @param[in] theX input value //! @param[out] theY function value //! @param[out] theDY derivative value //! @return true (always succeeds) bool Values(double theX, double& theY, double& theDY) const { const double anArg = myFrequency * theX + myPhase; theY = myAmplitude * std::sin(anArg) + myOffset; theDY = myAmplitude * myFrequency * std::cos(anArg); return true; } private: double myAmplitude; double myFrequency; double myPhase; double myOffset; }; //! Cosine function functor: f(x) = a * cos(b*x + c) + d. class Cosine { public: //! Constructor with full parameters. //! @param theAmplitude amplitude a //! @param theFrequency angular frequency b //! @param thePhase phase shift c //! @param theOffset vertical offset d Cosine(double theAmplitude = 1.0, double theFrequency = 1.0, double thePhase = 0.0, double theOffset = 0.0) : myAmplitude(theAmplitude), myFrequency(theFrequency), myPhase(thePhase), myOffset(theOffset) { } //! Evaluates cosine function. //! @param[in] theX input value //! @param[out] theY function value //! @return true (always succeeds) bool Value(double theX, double& theY) const { theY = myAmplitude * std::cos(myFrequency * theX + myPhase) + myOffset; return true; } //! Evaluates cosine function and derivative. //! @param[in] theX input value //! @param[out] theY function value //! @param[out] theDY derivative value //! @return true (always succeeds) bool Values(double theX, double& theY, double& theDY) const { const double anArg = myFrequency * theX + myPhase; theY = myAmplitude * std::cos(anArg) + myOffset; theDY = -myAmplitude * myFrequency * std::sin(anArg); return true; } private: double myAmplitude; double myFrequency; double myPhase; double myOffset; }; //! Exponential function functor: f(x) = a * exp(b*x) + c. class Exponential { public: //! Constructor with full parameters. //! @param theScale scale factor a //! @param theRate rate b //! @param theOffset vertical offset c Exponential(double theScale = 1.0, double theRate = 1.0, double theOffset = 0.0) : myScale(theScale), myRate(theRate), myOffset(theOffset) { } //! Evaluates exponential function. //! @param[in] theX input value //! @param[out] theY function value //! @return true (always succeeds) bool Value(double theX, double& theY) const { theY = myScale * std::exp(myRate * theX) + myOffset; return true; } //! Evaluates exponential function and derivative. //! @param[in] theX input value //! @param[out] theY function value //! @param[out] theDY derivative value //! @return true (always succeeds) bool Values(double theX, double& theY, double& theDY) const { const double anExp = std::exp(myRate * theX); theY = myScale * anExp + myOffset; theDY = myScale * myRate * anExp; return true; } private: double myScale; double myRate; double myOffset; }; //! Power function functor: f(x) = a * x^n + b. class Power { public: //! Constructor with full parameters. //! @param theExponent power n //! @param theScale scale factor a //! @param theOffset vertical offset b Power(double theExponent, double theScale = 1.0, double theOffset = 0.0) : myExponent(theExponent), myScale(theScale), myOffset(theOffset) { } //! Evaluates power function. //! @param[in] theX input value //! @param[out] theY function value //! @return false if x < 0 and exponent is non-integer bool Value(double theX, double& theY) const { if (theX < 0.0 && myExponent != std::floor(myExponent)) { return false; } theY = myScale * std::pow(theX, myExponent) + myOffset; return true; } //! Evaluates power function and derivative. //! @param[in] theX input value //! @param[out] theY function value //! @param[out] theDY derivative value //! @return false if x < 0 and exponent is non-integer bool Values(double theX, double& theY, double& theDY) const { if (theX < 0.0 && myExponent != std::floor(myExponent)) { return false; } const double aPow = std::pow(theX, myExponent); theY = myScale * aPow + myOffset; if (std::abs(theX) < 1e-15) { theDY = (myExponent == 1.0) ? myScale : 0.0; } else { theDY = myScale * myExponent * aPow / theX; } return true; } private: double myExponent; double myScale; double myOffset; }; //! Gaussian function functor: f(x) = a * exp(-((x-mu)^2)/(2*sigma^2)). class Gaussian { public: //! Constructor with full parameters. //! @param theAmplitude amplitude a (peak height) //! @param theMean mean mu (center) //! @param theSigma standard deviation sigma (width) Gaussian(double theAmplitude = 1.0, double theMean = 0.0, double theSigma = 1.0) : myAmplitude(theAmplitude), myMean(theMean), mySigma(theSigma) { } //! Evaluates Gaussian function. //! @param[in] theX input value //! @param[out] theY function value //! @return false if sigma is zero bool Value(double theX, double& theY) const { if (std::abs(mySigma) < 1e-15) { return false; } const double aZ = (theX - myMean) / mySigma; theY = myAmplitude * std::exp(-0.5 * aZ * aZ); return true; } //! Evaluates Gaussian function and derivative. //! @param[in] theX input value //! @param[out] theY function value //! @param[out] theDY derivative value //! @return false if sigma is zero bool Values(double theX, double& theY, double& theDY) const { if (std::abs(mySigma) < 1e-15) { return false; } const double aZ = (theX - myMean) / mySigma; const double aExp = std::exp(-0.5 * aZ * aZ); theY = myAmplitude * aExp; theDY = -myAmplitude * aZ * aExp / mySigma; return true; } private: double myAmplitude; double myMean; double mySigma; }; } // namespace MathUtils #endif // _MathUtils_FunctorScalar_HeaderFile