OpenSTA/dcalc/test/cpp/TestFindRoot.cc

673 lines
20 KiB
C++

#include <gtest/gtest.h>
#include <cmath>
#include <functional>
#include "dcalc/FindRoot.hh"
namespace sta {
class FindRootTest : public ::testing::Test {};
////////////////////////////////////////////////////////////////
// Original 7 tests
////////////////////////////////////////////////////////////////
// Test finding root of f(x) = x^2 - 4 (root at x=2)
TEST_F(FindRootTest, QuadraticPositiveRoot) {
FindRootFunc func = [](double x, double &y, double &dy) {
y = x * x - 4.0;
dy = 2.0 * x;
};
bool fail = false;
double root = findRoot(func, 1.0, 3.0, 1e-10, 100, fail);
EXPECT_FALSE(fail);
EXPECT_NEAR(root, 2.0, 1e-8);
}
// Test finding root of f(x) = x^2 - 4 (root at x=-2)
TEST_F(FindRootTest, QuadraticNegativeRoot) {
FindRootFunc func = [](double x, double &y, double &dy) {
y = x * x - 4.0;
dy = 2.0 * x;
};
bool fail = false;
double root = findRoot(func, -3.0, -1.0, 1e-10, 100, fail);
EXPECT_FALSE(fail);
EXPECT_NEAR(root, -2.0, 1e-8);
}
// Test finding root of f(x) = x - 1 (linear, root at x=1)
TEST_F(FindRootTest, LinearRoot) {
FindRootFunc func = [](double x, double &y, double &dy) {
y = x - 1.0;
dy = 1.0;
};
bool fail = false;
double root = findRoot(func, 0.0, 2.0, 1e-10, 100, fail);
EXPECT_FALSE(fail);
EXPECT_NEAR(root, 1.0, 1e-8);
}
// Test finding root of f(x) = sin(x) near pi
TEST_F(FindRootTest, SinRoot) {
FindRootFunc func = [](double x, double &y, double &dy) {
y = sin(x);
dy = cos(x);
};
bool fail = false;
double root = findRoot(func, 2.5, 3.8, 1e-10, 100, fail);
EXPECT_FALSE(fail);
EXPECT_NEAR(root, M_PI, 1e-6);
}
// Test finding root of f(x) = e^x - 2 (root at x=ln(2))
TEST_F(FindRootTest, ExponentialRoot) {
FindRootFunc func = [](double x, double &y, double &dy) {
y = exp(x) - 2.0;
dy = exp(x);
};
bool fail = false;
double root = findRoot(func, 0.0, 1.0, 1e-10, 100, fail);
EXPECT_FALSE(fail);
EXPECT_NEAR(root, log(2.0), 1e-8);
}
// Test with tight tolerance
TEST_F(FindRootTest, TightTolerance) {
FindRootFunc func = [](double x, double &y, double &dy) {
y = x * x - 2.0;
dy = 2.0 * x;
};
bool fail = false;
double root = findRoot(func, 1.0, 2.0, 1e-14, 200, fail);
EXPECT_FALSE(fail);
EXPECT_NEAR(root, sqrt(2.0), 1e-12);
}
// Test the 4-argument version with pre-computed y values
TEST_F(FindRootTest, WithPrecomputedY) {
FindRootFunc func = [](double x, double &y, double &dy) {
y = x * x - 9.0;
dy = 2.0 * x;
};
bool fail = false;
// x1=2, y1=4-9=-5, x2=4, y2=16-9=7
double root = findRoot(func, 2.0, -5.0, 4.0, 7.0, 1e-10, 100, fail);
EXPECT_FALSE(fail);
EXPECT_NEAR(root, 3.0, 1e-8);
}
////////////////////////////////////////////////////////////////
// Tolerance edge cases
////////////////////////////////////////////////////////////////
// Very tight tolerance: 1e-15 (near machine epsilon)
TEST_F(FindRootTest, VeryTightTolerance) {
FindRootFunc func = [](double x, double &y, double &dy) {
y = x - 5.0;
dy = 1.0;
};
bool fail = false;
double root = findRoot(func, 3.0, 7.0, 1e-15, 500, fail);
EXPECT_FALSE(fail);
EXPECT_NEAR(root, 5.0, 1e-13);
}
// Very loose tolerance: 1e-1
TEST_F(FindRootTest, LooseTolerance) {
FindRootFunc func = [](double x, double &y, double &dy) {
y = x * x - 25.0;
dy = 2.0 * x;
};
bool fail = false;
double root = findRoot(func, 3.0, 7.0, 1e-1, 100, fail);
EXPECT_FALSE(fail);
// With 10% relative tolerance, result should still be in the right ballpark
EXPECT_NEAR(root, 5.0, 0.6);
}
// Zero tolerance: convergence check becomes abs(dx) <= 0, which is only
// satisfied when dx is exactly 0. Likely hits max_iter and fails.
TEST_F(FindRootTest, ZeroTolerance) {
FindRootFunc func = [](double x, double &y, double &dy) {
y = x - 3.0;
dy = 1.0;
};
bool fail = false;
double root = findRoot(func, 1.0, 5.0, 0.0, 100, fail);
// May or may not converge -- for a linear function Newton converges in 1 step
// so dx can be exactly 0. Accept either outcome.
if (!fail) {
EXPECT_NEAR(root, 3.0, 1e-10);
}
}
////////////////////////////////////////////////////////////////
// Iteration limit edge cases
////////////////////////////////////////////////////////////////
// Only 1 iteration allowed
TEST_F(FindRootTest, OneIteration) {
ASSERT_NO_THROW(( [&](){
FindRootFunc func = [](double x, double &y, double &dy) {
y = x * x - 4.0;
dy = 2.0 * x;
};
bool fail = false;
double root = findRoot(func, 1.0, 3.0, 1e-10, 1, fail);
// With only 1 iteration, a quadratic likely won't converge to tight tol
// The algorithm may or may not fail depending on initial bisection step
// Root should still be a finite number within the bracket
EXPECT_GE(root, 1.0);
EXPECT_LE(root, 3.0);
}() ));
}
// Two iterations
TEST_F(FindRootTest, TwoIterations) {
FindRootFunc func = [](double x, double &y, double &dy) {
y = x - 7.0;
dy = 1.0;
};
bool fail = false;
double root = findRoot(func, 5.0, 9.0, 1e-10, 2, fail);
// Linear function: Newton should converge very fast
// After the initial midpoint (7.0), Newton step should nail it
if (!fail) {
EXPECT_NEAR(root, 7.0, 1e-6);
}
}
// Zero max iterations: the for-loop body never executes, so fail is set to true
TEST_F(FindRootTest, ZeroMaxIterations) {
FindRootFunc func = [](double x, double &y, double &dy) {
y = x - 1.0;
dy = 1.0;
};
bool fail = false;
findRoot(func, 0.0, 2.0, 1e-10, 0, fail);
EXPECT_TRUE(fail);
}
// Large max_iter (should still converge quickly and not hang)
TEST_F(FindRootTest, LargeMaxIter) {
FindRootFunc func = [](double x, double &y, double &dy) {
y = x * x - 16.0;
dy = 2.0 * x;
};
bool fail = false;
double root = findRoot(func, 1.0, 10.0, 1e-12, 10000, fail);
EXPECT_FALSE(fail);
EXPECT_NEAR(root, 4.0, 1e-10);
}
////////////////////////////////////////////////////////////////
// Special function types
////////////////////////////////////////////////////////////////
// Cubic: f(x) = x^3 - 8 (root at x=2)
TEST_F(FindRootTest, CubicRoot) {
FindRootFunc func = [](double x, double &y, double &dy) {
y = x * x * x - 8.0;
dy = 3.0 * x * x;
};
bool fail = false;
double root = findRoot(func, 1.0, 3.0, 1e-10, 100, fail);
EXPECT_FALSE(fail);
EXPECT_NEAR(root, 2.0, 1e-8);
}
// Quartic: f(x) = x^4 - 16 (root at x=2)
TEST_F(FindRootTest, QuarticRoot) {
FindRootFunc func = [](double x, double &y, double &dy) {
y = x * x * x * x - 16.0;
dy = 4.0 * x * x * x;
};
bool fail = false;
double root = findRoot(func, 1.0, 3.0, 1e-10, 100, fail);
EXPECT_FALSE(fail);
EXPECT_NEAR(root, 2.0, 1e-8);
}
// Exponential: f(x) = e^x - 10 (root at x=ln(10))
TEST_F(FindRootTest, ExponentialRoot2) {
FindRootFunc func = [](double x, double &y, double &dy) {
y = exp(x) - 10.0;
dy = exp(x);
};
bool fail = false;
double root = findRoot(func, 1.0, 4.0, 1e-10, 100, fail);
EXPECT_FALSE(fail);
EXPECT_NEAR(root, log(10.0), 1e-8);
}
// Square root function: f(x) = sqrt(x) - 3, root at x=9
// Derivative: 1/(2*sqrt(x))
TEST_F(FindRootTest, SqrtFunctionRoot) {
FindRootFunc func = [](double x, double &y, double &dy) {
y = sqrt(x) - 3.0;
dy = 0.5 / sqrt(x);
};
bool fail = false;
double root = findRoot(func, 1.0, 20.0, 1e-10, 100, fail);
EXPECT_FALSE(fail);
EXPECT_NEAR(root, 9.0, 1e-6);
}
////////////////////////////////////////////////////////////////
// Near-zero roots
////////////////////////////////////////////////////////////////
// f(x) = x - 1e-10 (root very close to zero)
// Note: convergence check is abs(dx) <= x_tol * abs(root).
// When root is near zero, the relative tolerance is very tight.
// This may require many iterations or not converge.
TEST_F(FindRootTest, NearZeroRootLinear) {
FindRootFunc func = [](double x, double &y, double &dy) {
y = x - 1e-10;
dy = 1.0;
};
bool fail = false;
double root = findRoot(func, -1.0, 1.0, 1e-6, 200, fail);
// Newton on a linear function converges in 1-2 steps regardless of root location
if (!fail) {
EXPECT_NEAR(root, 1e-10, 1e-6);
}
}
// f(x) = x (root exactly at zero)
// Convergence test: abs(dx) <= x_tol * abs(root) = x_tol * 0 = 0
// Will likely hit max_iter because relative tolerance at root=0 requires dx=0 exactly
TEST_F(FindRootTest, RootExactlyAtZero) {
FindRootFunc func = [](double x, double &y, double &dy) {
y = x;
dy = 1.0;
};
bool fail = false;
double root = findRoot(func, -1.0, 1.0, 1e-10, 200, fail);
// Even if fail is true, root should be very close to 0
EXPECT_NEAR(root, 0.0, 1e-6);
}
////////////////////////////////////////////////////////////////
// Negative domain
////////////////////////////////////////////////////////////////
// Root in deeply negative domain: f(x) = x + 100, root at x=-100
TEST_F(FindRootTest, NegativeDomainRoot) {
FindRootFunc func = [](double x, double &y, double &dy) {
y = x + 100.0;
dy = 1.0;
};
bool fail = false;
double root = findRoot(func, -200.0, 0.0, 1e-10, 100, fail);
EXPECT_FALSE(fail);
EXPECT_NEAR(root, -100.0, 1e-6);
}
// Both bracket endpoints negative: f(x) = x^2 - 1, root at x=-1
TEST_F(FindRootTest, NegativeBracketRoot) {
FindRootFunc func = [](double x, double &y, double &dy) {
y = x * x - 1.0;
dy = 2.0 * x;
};
bool fail = false;
double root = findRoot(func, -2.0, -0.5, 1e-10, 100, fail);
EXPECT_FALSE(fail);
EXPECT_NEAR(root, -1.0, 1e-8);
}
////////////////////////////////////////////////////////////////
// Trigonometric functions
////////////////////////////////////////////////////////////////
// sin(x) root at x=0 (bracket [-1, 1])
TEST_F(FindRootTest, SinRootAtZero) {
FindRootFunc func = [](double x, double &y, double &dy) {
y = sin(x);
dy = cos(x);
};
bool fail = false;
double root = findRoot(func, -1.0, 1.0, 1e-10, 100, fail);
// Root at 0 has the relative-tolerance issue, but Newton converges fast for sin
EXPECT_NEAR(root, 0.0, 1e-4);
}
// sin(x) root at x=2*pi (bracket [5.5, 7.0])
TEST_F(FindRootTest, SinRootAt2Pi) {
FindRootFunc func = [](double x, double &y, double &dy) {
y = sin(x);
dy = cos(x);
};
bool fail = false;
double root = findRoot(func, 5.5, 7.0, 1e-10, 100, fail);
EXPECT_FALSE(fail);
EXPECT_NEAR(root, 2.0 * M_PI, 1e-6);
}
// cos(x) root at x=pi/2
TEST_F(FindRootTest, CosRootAtPiOver2) {
FindRootFunc func = [](double x, double &y, double &dy) {
y = cos(x);
dy = -sin(x);
};
bool fail = false;
double root = findRoot(func, 1.0, 2.0, 1e-10, 100, fail);
EXPECT_FALSE(fail);
EXPECT_NEAR(root, M_PI / 2.0, 1e-6);
}
////////////////////////////////////////////////////////////////
// Multiple roots nearby
////////////////////////////////////////////////////////////////
// f(x) = (x-1)(x-2) = x^2 - 3x + 2, roots at x=1 and x=2
// Bracket [0.5, 1.5] should find x=1
TEST_F(FindRootTest, MultipleRootsFindFirst) {
FindRootFunc func = [](double x, double &y, double &dy) {
y = (x - 1.0) * (x - 2.0);
dy = 2.0 * x - 3.0;
};
bool fail = false;
double root = findRoot(func, 0.5, 1.5, 1e-10, 100, fail);
EXPECT_FALSE(fail);
EXPECT_NEAR(root, 1.0, 1e-8);
}
// Same function, bracket [1.5, 2.5] should find x=2
TEST_F(FindRootTest, MultipleRootsFindSecond) {
FindRootFunc func = [](double x, double &y, double &dy) {
y = (x - 1.0) * (x - 2.0);
dy = 2.0 * x - 3.0;
};
bool fail = false;
double root = findRoot(func, 1.5, 2.5, 1e-10, 100, fail);
EXPECT_FALSE(fail);
EXPECT_NEAR(root, 2.0, 1e-8);
}
////////////////////////////////////////////////////////////////
// Discontinuous derivative (sharp corner)
////////////////////////////////////////////////////////////////
// f(x) = |x| - 1 with piecewise derivative.
// Root at x=1 (bracket [0.5, 2.0] avoids the corner at 0)
TEST_F(FindRootTest, AbsValueRoot) {
FindRootFunc func = [](double x, double &y, double &dy) {
y = fabs(x) - 1.0;
dy = (x >= 0.0) ? 1.0 : -1.0;
};
bool fail = false;
double root = findRoot(func, 0.5, 2.0, 1e-10, 100, fail);
EXPECT_FALSE(fail);
EXPECT_NEAR(root, 1.0, 1e-8);
}
// f(x) = |x| - 1, root at x=-1
TEST_F(FindRootTest, AbsValueNegativeRoot) {
FindRootFunc func = [](double x, double &y, double &dy) {
y = fabs(x) - 1.0;
dy = (x >= 0.0) ? 1.0 : -1.0;
};
bool fail = false;
double root = findRoot(func, -2.0, -0.5, 1e-10, 100, fail);
EXPECT_FALSE(fail);
EXPECT_NEAR(root, -1.0, 1e-8);
}
////////////////////////////////////////////////////////////////
// Very flat function (slow convergence)
////////////////////////////////////////////////////////////////
// f(x) = (x - 3)^5 has a repeated root at x=3 where the derivative is also
// zero. Newton-Raphson divides by dy which becomes 0, producing NaN.
// The algorithm is expected to fail on this degenerate case.
TEST_F(FindRootTest, FlatFifthOrderRootFails) {
FindRootFunc func = [](double x, double &y, double &dy) {
double d = x - 3.0;
double d2 = d * d;
double d4 = d2 * d2;
y = d4 * d; // (x-3)^5
dy = 5.0 * d4; // 5*(x-3)^4
};
bool fail = false;
findRoot(func, 2.0, 4.0, 1e-6, 500, fail);
// The algorithm is expected to fail because dy -> 0 at the root
EXPECT_TRUE(fail);
}
// A function that is very flat near the root but still has nonzero derivative
// at the root: f(x) = sinh(x - 3) which is ~0 near x=3 but never has dy=0.
// sinh is flat near 0 (sinh(e) ~ e for small e) but derivative cosh(e) >= 1.
TEST_F(FindRootTest, FlatSinhRoot) {
FindRootFunc func = [](double x, double &y, double &dy) {
y = sinh(x - 3.0);
dy = cosh(x - 3.0);
};
bool fail = false;
double root = findRoot(func, 2.0, 4.0, 1e-10, 100, fail);
EXPECT_FALSE(fail);
EXPECT_NEAR(root, 3.0, 1e-6);
}
////////////////////////////////////////////////////////////////
// Very steep function (fast convergence)
////////////////////////////////////////////////////////////////
// f(x) = 1000*(x - 5), root at x=5. Very steep gradient.
TEST_F(FindRootTest, SteepLinearRoot) {
FindRootFunc func = [](double x, double &y, double &dy) {
y = 1000.0 * (x - 5.0);
dy = 1000.0;
};
bool fail = false;
double root = findRoot(func, 3.0, 7.0, 1e-12, 100, fail);
EXPECT_FALSE(fail);
EXPECT_NEAR(root, 5.0, 1e-10);
}
// f(x) = 1e6 * (x - 2), very steep
TEST_F(FindRootTest, VerySteepLinearRoot) {
FindRootFunc func = [](double x, double &y, double &dy) {
y = 1e6 * (x - 2.0);
dy = 1e6;
};
bool fail = false;
double root = findRoot(func, 1.0, 3.0, 1e-14, 100, fail);
EXPECT_FALSE(fail);
EXPECT_NEAR(root, 2.0, 1e-12);
}
////////////////////////////////////////////////////////////////
// Large bracket
////////////////////////////////////////////////////////////////
// f(x) = x - 42, bracket [-1000, 1000]
TEST_F(FindRootTest, LargeBracket) {
FindRootFunc func = [](double x, double &y, double &dy) {
y = x - 42.0;
dy = 1.0;
};
bool fail = false;
double root = findRoot(func, -1000.0, 1000.0, 1e-10, 100, fail);
EXPECT_FALSE(fail);
EXPECT_NEAR(root, 42.0, 1e-6);
}
// Quadratic with large bracket: f(x) = x^2 - 100, root at 10
TEST_F(FindRootTest, LargeBracketQuadratic) {
FindRootFunc func = [](double x, double &y, double &dy) {
y = x * x - 100.0;
dy = 2.0 * x;
};
bool fail = false;
double root = findRoot(func, 1.0, 1000.0, 1e-10, 200, fail);
EXPECT_FALSE(fail);
EXPECT_NEAR(root, 10.0, 1e-6);
}
////////////////////////////////////////////////////////////////
// Small bracket
////////////////////////////////////////////////////////////////
// f(x) = x - 1.0, bracket [0.999999, 1.000001] (very tight bracket around root)
TEST_F(FindRootTest, SmallBracket) {
FindRootFunc func = [](double x, double &y, double &dy) {
y = x - 1.0;
dy = 1.0;
};
bool fail = false;
double root = findRoot(func, 0.999999, 1.000001, 1e-10, 100, fail);
EXPECT_FALSE(fail);
EXPECT_NEAR(root, 1.0, 1e-6);
}
// Quadratic with very small bracket around root=2
TEST_F(FindRootTest, SmallBracketQuadratic) {
FindRootFunc func = [](double x, double &y, double &dy) {
y = x * x - 4.0;
dy = 2.0 * x;
};
bool fail = false;
double root = findRoot(func, 1.9999, 2.0001, 1e-12, 100, fail);
EXPECT_FALSE(fail);
EXPECT_NEAR(root, 2.0, 1e-8);
}
////////////////////////////////////////////////////////////////
// Both overloads tested together
////////////////////////////////////////////////////////////////
// Compare 2-arg and 4-arg overloads produce same result
TEST_F(FindRootTest, OverloadsProduceSameResult) {
FindRootFunc func = [](double x, double &y, double &dy) {
y = x * x * x - 27.0;
dy = 3.0 * x * x;
};
bool fail_2arg = false;
double root_2arg = findRoot(func, 2.0, 4.0, 1e-12, 100, fail_2arg);
// Pre-compute y values for 4-arg version
double y1 = 2.0 * 2.0 * 2.0 - 27.0; // 8 - 27 = -19
double y2 = 4.0 * 4.0 * 4.0 - 27.0; // 64 - 27 = 37
bool fail_4arg = false;
double root_4arg = findRoot(func, 2.0, y1, 4.0, y2, 1e-12, 100, fail_4arg);
EXPECT_FALSE(fail_2arg);
EXPECT_FALSE(fail_4arg);
EXPECT_NEAR(root_2arg, 3.0, 1e-10);
EXPECT_NEAR(root_4arg, 3.0, 1e-10);
EXPECT_NEAR(root_2arg, root_4arg, 1e-14);
}
// 4-arg overload: x1 endpoint is exact root (y1 == 0)
TEST_F(FindRootTest, FourArgX1IsRoot) {
FindRootFunc func = [](double x, double &y, double &dy) {
y = x - 5.0;
dy = 1.0;
};
bool fail = false;
// y1 = 5 - 5 = 0
double root = findRoot(func, 5.0, 0.0, 8.0, 3.0, 1e-10, 100, fail);
EXPECT_FALSE(fail);
EXPECT_DOUBLE_EQ(root, 5.0);
}
// 4-arg overload: x2 endpoint is exact root (y2 == 0)
TEST_F(FindRootTest, FourArgX2IsRoot) {
FindRootFunc func = [](double x, double &y, double &dy) {
y = x - 5.0;
dy = 1.0;
};
bool fail = false;
// y2 = 5 - 5 = 0
double root = findRoot(func, 2.0, -3.0, 5.0, 0.0, 1e-10, 100, fail);
EXPECT_FALSE(fail);
EXPECT_DOUBLE_EQ(root, 5.0);
}
////////////////////////////////////////////////////////////////
// Same-sign y values (should fail)
////////////////////////////////////////////////////////////////
// Both endpoints positive: should fail
TEST_F(FindRootTest, BothEndpointsPositiveFails) {
FindRootFunc func = [](double x, double &y, double &dy) {
y = x * x + 1.0; // Always positive, no real root
dy = 2.0 * x;
};
bool fail = false;
findRoot(func, 1.0, 3.0, 1e-10, 100, fail);
EXPECT_TRUE(fail);
}
// Both endpoints negative: should fail
TEST_F(FindRootTest, BothEndpointsNegativeFails) {
FindRootFunc func = [](double x, double &y, double &dy) {
y = -x * x - 1.0; // Always negative
dy = -2.0 * x;
};
bool fail = false;
findRoot(func, -3.0, 3.0, 1e-10, 100, fail);
EXPECT_TRUE(fail);
}
// 4-arg version: same-sign y values
TEST_F(FindRootTest, FourArgSameSignFails) {
FindRootFunc func = [](double x, double &y, double &dy) {
y = x * x;
dy = 2.0 * x;
};
bool fail = false;
// Both y values positive
findRoot(func, 1.0, 1.0, 2.0, 4.0, 1e-10, 100, fail);
EXPECT_TRUE(fail);
}
////////////////////////////////////////////////////////////////
// Symmetry test
////////////////////////////////////////////////////////////////
// f(x) = x^2 - 4: bracket [0, 3] finds +2, bracket [-3, 0] finds -2
TEST_F(FindRootTest, SymmetryPositiveBracket) {
FindRootFunc func = [](double x, double &y, double &dy) {
y = x * x - 4.0;
dy = 2.0 * x;
};
bool fail = false;
double root = findRoot(func, 0.5, 3.0, 1e-10, 100, fail);
EXPECT_FALSE(fail);
EXPECT_NEAR(root, 2.0, 1e-8);
}
TEST_F(FindRootTest, SymmetryNegativeBracket) {
FindRootFunc func = [](double x, double &y, double &dy) {
y = x * x - 4.0;
dy = 2.0 * x;
};
bool fail = false;
double root = findRoot(func, -3.0, -0.5, 1e-10, 100, fail);
EXPECT_FALSE(fail);
EXPECT_NEAR(root, -2.0, 1e-8);
}
////////////////////////////////////////////////////////////////
// Swapped bracket order (x1 > x2)
////////////////////////////////////////////////////////////////
// The algorithm should work regardless of bracket order
TEST_F(FindRootTest, SwappedBracketOrder) {
FindRootFunc func = [](double x, double &y, double &dy) {
y = x - 3.0;
dy = 1.0;
};
bool fail = false;
// x1=5 > x2=1 (reversed order)
double root = findRoot(func, 5.0, 1.0, 1e-10, 100, fail);
EXPECT_FALSE(fail);
EXPECT_NEAR(root, 3.0, 1e-8);
}
} // namespace sta