Template polynomial class with unit tests
This commit is contained in:
parent
8caa238c42
commit
f51d403b8e
4 changed files with 537 additions and 7 deletions
|
@ -1,7 +1,7 @@
|
|||
cmake_minimum_required(VERSION 3.13)
|
||||
project(snippets)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
add_executable(pM pietMondrian.cpp)
|
||||
|
||||
|
@ -29,9 +29,9 @@ else()
|
|||
message(FATAL_ERROR "GTest pas trouvé")
|
||||
endif()
|
||||
|
||||
add_executable(coincheTest gTestCoinche.cpp Coinche.cpp Coinche.h)
|
||||
|
||||
if(GTest_FOUND)
|
||||
add_executable(coincheTest gTestCoinche.cpp Coinche.cpp Coinche.h)
|
||||
target_link_libraries(coincheTest ${GTEST_BOTH_LIBRARIES})
|
||||
target_link_options(coincheTest PRIVATE -pthread)
|
||||
endif()
|
||||
|
@ -46,13 +46,24 @@ target_link_libraries(parseXML pugixml)
|
|||
target_include_directories(parseXML PRIVATE ${PugiXML_INCLUDE_DIR})
|
||||
target_link_directories(parseXML PRIVATE ${PugiXML_INCLUDE_DIR})
|
||||
|
||||
|
||||
if(GTest_FOUND)
|
||||
add_executable(xmlTest gTestXMLParser.cpp Circle.h xmlParser.h xmlParser.cpp)
|
||||
|
||||
target_include_directories(xmlTest PRIVATE ${PugiXML_INCLUDE_DIR})
|
||||
target_link_directories(xmlTest PRIVATE ${PugiXML_INCLUDE_DIR})
|
||||
target_link_libraries(xmlTest pugixml)
|
||||
|
||||
if(GTest_FOUND)
|
||||
target_link_libraries(xmlTest ${GTEST_BOTH_LIBRARIES})
|
||||
target_link_options(xmlTest PRIVATE -pthread)
|
||||
endif()
|
||||
|
||||
add_library(polynomial SHARED Polynomial.cpp Polynomial.tpp)
|
||||
#target_link_libraries(polynomial -static)
|
||||
|
||||
if(GTest_FOUND)
|
||||
add_executable(polynomialTest gTestPolynomial.cpp)
|
||||
target_link_libraries(polynomialTest ${GTEST_BOTH_LIBRARIES})
|
||||
target_link_options(polynomialTest PRIVATE -pthread)
|
||||
target_link_libraries(polynomialTest polynomial)
|
||||
endif()
|
5
snippets/Polynomial.cpp
Normal file
5
snippets/Polynomial.cpp
Normal file
|
@ -0,0 +1,5 @@
|
|||
//
|
||||
// Created by trotfunky on 09/05/19.
|
||||
//
|
||||
|
||||
#include "Polynomial.tpp"
|
318
snippets/Polynomial.tpp
Normal file
318
snippets/Polynomial.tpp
Normal file
|
@ -0,0 +1,318 @@
|
|||
//
|
||||
// Created by trotfunky on 09/05/19.
|
||||
//
|
||||
|
||||
#ifndef SNIPPETS_POLYNOMIAL_TPP
|
||||
#define SNIPPETS_POLYNOMIAL_TPP
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <iostream>
|
||||
#include <math.h>
|
||||
|
||||
template <typename T> class Polynomial;
|
||||
|
||||
/**
|
||||
* Class allowing creation, manipulation and calculation of polynomials
|
||||
* Factors are stored from the lowest power to the highest
|
||||
* The polynomial has at least one factor, which defaults to zero
|
||||
* @tparam T Has to be an arithmetic type (std::is_arithmetic<T>::value is true)
|
||||
*/
|
||||
template <typename T>
|
||||
class Polynomial {
|
||||
public:
|
||||
Polynomial();
|
||||
explicit Polynomial(const std::vector<T>&);
|
||||
explicit Polynomial(const std::map<int,T>&);
|
||||
|
||||
template <typename T1>
|
||||
explicit Polynomial(const Polynomial<T1>&);
|
||||
|
||||
int getDegree() const;
|
||||
Polynomial<T> getNthDerivative(int n) const;
|
||||
|
||||
template <typename T1>
|
||||
bool equals(const Polynomial<T1>&) const;
|
||||
|
||||
template <typename T1>
|
||||
auto add(const Polynomial<T1>& operand) const -> Polynomial<decltype(static_cast<T>(0) + operand[0])>;
|
||||
|
||||
template <typename T1>
|
||||
friend std::ostream& operator<<(std::ostream&, const Polynomial<T1>&);
|
||||
|
||||
template<typename T1>
|
||||
T1 operator()(const T1&) const;
|
||||
|
||||
T operator[](int) const;
|
||||
|
||||
private:
|
||||
std::vector<T> factors;
|
||||
double factorial(unsigned int n) const;
|
||||
|
||||
template <typename T2>
|
||||
friend class Polynomial;
|
||||
};
|
||||
|
||||
////////////////////
|
||||
/// ///
|
||||
/// Definitions ///
|
||||
/// ///
|
||||
////////////////////
|
||||
|
||||
template<typename T>
|
||||
Polynomial<T>::Polynomial()
|
||||
{
|
||||
static_assert(std::is_arithmetic<T>::value,"Polynomial must be of an arithmetic type!");
|
||||
factors = {0};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Polynomial<T>::Polynomial(const std::vector<T>& polynomialFactors) : Polynomial<T>()
|
||||
{
|
||||
if(polynomialFactors.size()>0)
|
||||
{
|
||||
factors = polynomialFactors;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Polynomial<T>::Polynomial(const std::map<int, T>& polynomialFactors) : Polynomial<T>()
|
||||
{
|
||||
int degree = polynomialFactors.rbegin()->first;
|
||||
if(degree >= 0)
|
||||
{
|
||||
factors.pop_back();
|
||||
}
|
||||
factors.reserve(degree);
|
||||
|
||||
for(int i = 0;i<=degree;i++)
|
||||
{
|
||||
auto nextFactor = polynomialFactors.find(i);
|
||||
if(nextFactor != polynomialFactors.end())
|
||||
{
|
||||
factors.push_back(nextFactor->second);
|
||||
}
|
||||
else
|
||||
{
|
||||
factors.push_back(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template <typename T1>
|
||||
Polynomial<T>::Polynomial(const Polynomial<T1>& copied)
|
||||
{
|
||||
static_assert(std::is_arithmetic<T>::value,"Polynomial must be of an arithmetic type!");
|
||||
|
||||
for(const T1& factor : copied.factors)
|
||||
{
|
||||
factors.push_back(static_cast<T>(factor));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the degree of the polynomial.
|
||||
* @tparam T Type of the polynomial factors
|
||||
* @return Degree of the polynomial, -1 if it is the null-polynomial
|
||||
*/
|
||||
template<typename T>
|
||||
int Polynomial<T>::getDegree() const
|
||||
{
|
||||
for(int i = factors.size()-1;i>=0;i--)
|
||||
{
|
||||
if(factors[i] != 0)
|
||||
{
|
||||
return(i);
|
||||
}
|
||||
}
|
||||
return(-1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the nth derivative of the polynomial
|
||||
* @tparam T Polyniaml factor type
|
||||
* @param n Order of the derivative
|
||||
* @return Derived polynomial, null-polynomial if the order is greater than the degree
|
||||
*/
|
||||
template<typename T>
|
||||
Polynomial<T> Polynomial<T>::getNthDerivative(int n) const
|
||||
{
|
||||
if(n<=0)
|
||||
{
|
||||
return(Polynomial<T>(*this));
|
||||
}
|
||||
|
||||
if(n>getDegree())
|
||||
{
|
||||
return(Polynomial<T>());
|
||||
}
|
||||
|
||||
std::vector<T> newFactors;
|
||||
|
||||
for(int i = n;i<factors.size();i++)
|
||||
{
|
||||
newFactors.push_back(factors[i]*factorial(i)/factorial(i-n));
|
||||
}
|
||||
|
||||
return(Polynomial<T>(newFactors));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
double Polynomial<T>::factorial(unsigned int n) const
|
||||
{
|
||||
double value = 1;
|
||||
for(unsigned int i = 2;i<=n;i++)
|
||||
{
|
||||
value *= i;
|
||||
}
|
||||
|
||||
return(value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
template<typename T1>
|
||||
bool Polynomial<T>::equals(const Polynomial<T1>& operand) const
|
||||
{
|
||||
if(getDegree() != operand.getDegree())
|
||||
{
|
||||
return(false);
|
||||
}
|
||||
if(getDegree() == -1)
|
||||
{
|
||||
return(true);
|
||||
}
|
||||
const std::vector<T1>& p2Factors = operand.factors;
|
||||
|
||||
for(int i = factors.size();i>=0;i--)
|
||||
{
|
||||
if(factors[i] != p2Factors[i])
|
||||
{
|
||||
return(false);
|
||||
}
|
||||
}
|
||||
|
||||
return(true);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
template<typename T1>
|
||||
auto Polynomial<T>::add(const Polynomial<T1>& operand) const -> Polynomial<decltype(static_cast<T>(0) + operand[0])>
|
||||
{
|
||||
bool isLargest = true;
|
||||
|
||||
int largestSize = factors.size();
|
||||
int smallestSize = operand.factors.size();
|
||||
|
||||
if(getDegree() < operand.getDegree())
|
||||
{
|
||||
isLargest = false;
|
||||
|
||||
smallestSize = factors.size();
|
||||
largestSize = operand.factors.size();
|
||||
}
|
||||
|
||||
std::vector<decltype(static_cast<T>(0)+operand[0])> resultPolynomial = {};
|
||||
resultPolynomial.reserve(largestSize);
|
||||
|
||||
for(int i = 0;i<smallestSize;i++)
|
||||
{
|
||||
resultPolynomial.push_back(factors[i]+operand[i]);
|
||||
}
|
||||
|
||||
for(int i = smallestSize;i<largestSize;i++)
|
||||
{
|
||||
resultPolynomial.push_back((isLargest ? factors[i] : operand[i]));
|
||||
}
|
||||
|
||||
return Polynomial<decltype(static_cast<T>(0)+operand[0])>(resultPolynomial);
|
||||
}
|
||||
|
||||
////////////////////
|
||||
/// ///
|
||||
/// Operators ///
|
||||
/// ///
|
||||
////////////////////
|
||||
|
||||
template<typename T1,typename T2>
|
||||
bool operator==(const Polynomial<T1>& p1, const Polynomial<T2>& p2)
|
||||
{
|
||||
return(p1.equals(p2));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Computes the sum of the two polynomials by adding factor by factor at first and pushing back
|
||||
* the leftover factors from the largest polynomial if their are different in degree.
|
||||
* @tparam T Type of the first polynomial
|
||||
* @tparam R Type of the second polynomial
|
||||
* @param p1 First polynomial
|
||||
* @param p2 Second polynomial
|
||||
* @return Sum of the two polynomials, null polynomial if both of them are null. The type is deduced from the addition
|
||||
* of the first factors of the two polynomials.
|
||||
*/
|
||||
template<typename T1, typename T2>
|
||||
auto operator+(const Polynomial<T1>& p1, const Polynomial<T2>& p2) -> Polynomial<decltype(p1[0] + p2[0])>
|
||||
{
|
||||
return(p1.add(p2));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::ostream& operator<<(std::ostream& ostream, const Polynomial<T>& polynomial)
|
||||
{
|
||||
if(polynomial.getDegree() < 0)
|
||||
{
|
||||
ostream << "0 ";
|
||||
return ostream;
|
||||
}
|
||||
|
||||
const std::vector<T>& factors = polynomial.factors;
|
||||
|
||||
if(factors[0] > 0)
|
||||
{
|
||||
ostream << factors[0] << " ";
|
||||
}
|
||||
else if(factors[0] < 0)
|
||||
{
|
||||
ostream << std::showpos << factors[0] << " ";
|
||||
}
|
||||
|
||||
for(int i = 1;i<factors.size();i++)
|
||||
{
|
||||
if(factors[i] != 0)
|
||||
{
|
||||
ostream << std::showpos << factors[i] << "*X^" << std::noshowpos << i << " ";
|
||||
}
|
||||
}
|
||||
|
||||
return(ostream);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
template<typename T1>
|
||||
T1 Polynomial<T>::operator()(const T1& input) const
|
||||
{
|
||||
static_assert(std::is_arithmetic<T1>::value,"Cannot evaluate polynomial at a non arithmetic value !");
|
||||
|
||||
if(getDegree() == -1)
|
||||
{
|
||||
return(0);
|
||||
}
|
||||
|
||||
T1 returnValue = 0;
|
||||
for(int i = 0;i<factors.size();i++)
|
||||
{
|
||||
returnValue += factors[i] * pow(input,i);
|
||||
}
|
||||
|
||||
return(returnValue);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T Polynomial<T>::operator[](const int index) const
|
||||
{
|
||||
return(factors.at(index));
|
||||
}
|
||||
|
||||
|
||||
#endif //SNIPPETS_POLYNOMIAL_TPP
|
196
snippets/gTestPolynomial.cpp
Normal file
196
snippets/gTestPolynomial.cpp
Normal file
|
@ -0,0 +1,196 @@
|
|||
//
|
||||
// Created by trotfunky on 09/05/19.
|
||||
//
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include "Polynomial.tpp"
|
||||
|
||||
class PolynomialOperationTest : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
void SetUp() override
|
||||
{
|
||||
pInt = Polynomial<int>({1,2,3});
|
||||
pFloat = Polynomial<float>({-1.0,0,2.0});
|
||||
pLarge = Polynomial<int>({{10,1},{0,5}});
|
||||
}
|
||||
|
||||
Polynomial<int> pInt;
|
||||
Polynomial<float> pFloat;
|
||||
Polynomial<int> pLarge;
|
||||
};
|
||||
|
||||
TEST(PolynomialTest,polynomialCreation)
|
||||
{
|
||||
Polynomial<int> pTestInt = Polynomial<int>({1,2,3});
|
||||
EXPECT_EQ(pTestInt[0],1);
|
||||
EXPECT_EQ(pTestInt[1],2);
|
||||
EXPECT_EQ(pTestInt[2],3);
|
||||
EXPECT_EQ(2,pTestInt.getDegree());
|
||||
|
||||
std::cout << "Int polynomial : " << pTestInt << std::endl;
|
||||
|
||||
Polynomial<float> pTestFloat = Polynomial<float>({0,0,1,0});
|
||||
EXPECT_EQ(pTestFloat[0],0);
|
||||
EXPECT_EQ(pTestFloat[1],0);
|
||||
EXPECT_EQ(pTestFloat[2],1);
|
||||
EXPECT_EQ(pTestFloat[3],0);
|
||||
EXPECT_EQ(2,pTestFloat.getDegree());
|
||||
|
||||
std::cout << "Float polynomial : " << pTestFloat << std::endl;
|
||||
|
||||
Polynomial<float> pTestCopy = Polynomial<float>(pTestInt);
|
||||
EXPECT_EQ(pTestCopy[0],1);
|
||||
EXPECT_EQ(pTestCopy[1],2);
|
||||
EXPECT_EQ(pTestCopy[2],3);
|
||||
|
||||
std::cout << pTestCopy << " is a copy of " << pTestInt << std::endl;
|
||||
|
||||
Polynomial<int> pTestMapInit = Polynomial<int>({{0,1},{2,3},{10,10}});
|
||||
EXPECT_EQ(pTestMapInit[0],1);
|
||||
EXPECT_EQ(pTestMapInit[2],3);
|
||||
EXPECT_EQ(pTestMapInit[10],10);
|
||||
|
||||
std::cout << "Polynomial created via a map : " << pTestMapInit << std::endl;
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
TEST_F(PolynomialOperationTest,polynomialSum)
|
||||
{
|
||||
Polynomial<int> summedIntP = pInt + pInt;
|
||||
EXPECT_EQ(summedIntP[0],2);
|
||||
EXPECT_EQ(summedIntP[1],4);
|
||||
EXPECT_EQ(summedIntP[2],6);
|
||||
|
||||
std::cout << "Summed int polynomial : " << summedIntP << std::endl;
|
||||
|
||||
Polynomial<float> summedFloatP = pFloat + pFloat;
|
||||
EXPECT_EQ(summedFloatP[0],-2.0f);
|
||||
EXPECT_EQ(summedFloatP[1],0);
|
||||
EXPECT_EQ(summedFloatP[2],4.0f);
|
||||
|
||||
std::cout << "Summed float polynomial : " << summedFloatP << std::endl;
|
||||
|
||||
auto summedAuto = pInt + pFloat;
|
||||
EXPECT_EQ(summedAuto[0],0);
|
||||
EXPECT_EQ(summedAuto[1],2);
|
||||
EXPECT_EQ(summedAuto[2],5);
|
||||
|
||||
summedAuto = pFloat + pInt;
|
||||
EXPECT_EQ(summedAuto[0],0);
|
||||
EXPECT_EQ(summedAuto[1],2);
|
||||
EXPECT_EQ(summedAuto[2],5);
|
||||
|
||||
std::cout << "Summed auto polynomial : " << summedAuto << std::endl;
|
||||
|
||||
Polynomial<int> summedLarge = pInt + pLarge;
|
||||
EXPECT_EQ(summedLarge[0],6);
|
||||
EXPECT_EQ(summedLarge[1],2);
|
||||
EXPECT_EQ(summedLarge[2],3);
|
||||
for(int i = 3;i<10;i++)
|
||||
EXPECT_EQ(summedLarge[i],0);
|
||||
EXPECT_EQ(summedLarge[10],1);
|
||||
|
||||
std::cout << "Summed small and large polynomials : " << summedLarge << std::endl;
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
TEST_F(PolynomialOperationTest,polynomialComparison)
|
||||
{
|
||||
EXPECT_TRUE(Polynomial<int>() == Polynomial<int>());
|
||||
EXPECT_TRUE(Polynomial<float>() == Polynomial<int>());
|
||||
|
||||
Polynomial<int> pCompInt = Polynomial<int>({1,2,3});
|
||||
EXPECT_TRUE(pCompInt == pInt);
|
||||
EXPECT_TRUE(pInt == pCompInt);
|
||||
|
||||
Polynomial<float> pCompFloat = Polynomial<float>({-1.0,0,2.0});
|
||||
EXPECT_TRUE(pCompFloat == pFloat);
|
||||
EXPECT_TRUE(pFloat == pCompFloat);
|
||||
|
||||
EXPECT_TRUE(Polynomial<float>(pFloat) == pFloat);
|
||||
EXPECT_TRUE(Polynomial<float>(pInt) == pInt);
|
||||
|
||||
EXPECT_FALSE(pInt == pFloat);
|
||||
EXPECT_FALSE(pInt == Polynomial<int>());
|
||||
EXPECT_FALSE(pInt == Polynomial<float>());
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
TEST_F(PolynomialOperationTest,polynomialDerivation)
|
||||
{
|
||||
EXPECT_TRUE(pInt.getNthDerivative(0) == pInt);
|
||||
EXPECT_TRUE(pFloat.getNthDerivative(0) == pFloat);
|
||||
|
||||
std::cout << "Testing with Polynomial<int> ..." << std::endl;
|
||||
|
||||
Polynomial<int> expectedIntDerivative = Polynomial<int>(std::vector<int>({2,6}));
|
||||
EXPECT_TRUE(expectedIntDerivative == pInt.getNthDerivative(1));
|
||||
|
||||
std::cout << "First order derivative : " << pInt.getNthDerivative(1) << std::endl;
|
||||
|
||||
expectedIntDerivative = Polynomial<int>({6});
|
||||
EXPECT_TRUE(expectedIntDerivative == pInt.getNthDerivative(2));
|
||||
|
||||
std::cout << "Second order derivative : " << pInt.getNthDerivative(2) << std::endl;
|
||||
|
||||
expectedIntDerivative = Polynomial<int>();
|
||||
EXPECT_TRUE(expectedIntDerivative == pInt.getNthDerivative(3));
|
||||
EXPECT_TRUE(expectedIntDerivative == pInt.getNthDerivative(10));
|
||||
|
||||
std::cout << "Third order derivative : " << pInt.getNthDerivative(3) << std::endl;
|
||||
std::cout << "Tenth order derivative : " << pInt.getNthDerivative(10) << std::endl;
|
||||
|
||||
|
||||
std::cout << std::endl;
|
||||
std::cout << "Testing with Polynomial<float> ..." << std::endl;
|
||||
|
||||
Polynomial<float> expectedFloatDerivative = Polynomial<float>(std::vector<float>({0,4}));
|
||||
EXPECT_TRUE(expectedFloatDerivative == pFloat.getNthDerivative(1));
|
||||
|
||||
std::cout << "First order derivative : " << pFloat.getNthDerivative(1) << std::endl;
|
||||
|
||||
expectedFloatDerivative = Polynomial<float>({4});
|
||||
EXPECT_TRUE(expectedFloatDerivative == pFloat.getNthDerivative(2));
|
||||
|
||||
std::cout << "Second order derivative : " << pFloat.getNthDerivative(2) << std::endl;
|
||||
|
||||
expectedFloatDerivative = Polynomial<float>();
|
||||
EXPECT_TRUE(expectedFloatDerivative == pFloat.getNthDerivative(3));
|
||||
EXPECT_TRUE(expectedFloatDerivative == pFloat.getNthDerivative(10));
|
||||
|
||||
std::cout << "Third order derivative : " << pFloat.getNthDerivative(3) << std::endl;
|
||||
std::cout << "Tenth order derivative : " << pFloat.getNthDerivative(10) << std::endl;
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
TEST_F(PolynomialOperationTest,polynomialAssertion)
|
||||
{
|
||||
EXPECT_EQ(pInt(0),1);
|
||||
EXPECT_EQ(pInt(0.0f),1);
|
||||
EXPECT_EQ(pInt(1),6);
|
||||
EXPECT_EQ(pInt(1.0),6);
|
||||
EXPECT_EQ(pInt(-1),2);
|
||||
|
||||
EXPECT_EQ(pFloat(0),-1);
|
||||
EXPECT_EQ(pFloat(0.0),-1);
|
||||
EXPECT_EQ(pFloat(1),1);
|
||||
EXPECT_EQ(pFloat(-1),1);
|
||||
|
||||
EXPECT_EQ(pLarge(0),5);
|
||||
EXPECT_EQ(pLarge(1),6);
|
||||
EXPECT_EQ(pLarge(2),1029);
|
||||
|
||||
EXPECT_EQ(Polynomial<bool>()(100),0);
|
||||
EXPECT_EQ(Polynomial<bool>()(0),0);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
::testing::InitGoogleTest(&argc,argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
Loading…
Add table
Reference in a new issue