diff --git a/Exponential.sln b/Exponential.sln
new file mode 100644
index 0000000..781745c
--- /dev/null
+++ b/Exponential.sln
@@ -0,0 +1,31 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.3.32929.385
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Exponential", "Exponential\Exponential.vcxproj", "{74C04891-9509-4EA4-BC52-6D86492B203A}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {74C04891-9509-4EA4-BC52-6D86492B203A}.Debug|x64.ActiveCfg = Debug|x64
+ {74C04891-9509-4EA4-BC52-6D86492B203A}.Debug|x64.Build.0 = Debug|x64
+ {74C04891-9509-4EA4-BC52-6D86492B203A}.Debug|x86.ActiveCfg = Debug|Win32
+ {74C04891-9509-4EA4-BC52-6D86492B203A}.Debug|x86.Build.0 = Debug|Win32
+ {74C04891-9509-4EA4-BC52-6D86492B203A}.Release|x64.ActiveCfg = Release|x64
+ {74C04891-9509-4EA4-BC52-6D86492B203A}.Release|x64.Build.0 = Release|x64
+ {74C04891-9509-4EA4-BC52-6D86492B203A}.Release|x86.ActiveCfg = Release|Win32
+ {74C04891-9509-4EA4-BC52-6D86492B203A}.Release|x86.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {C33279A4-898B-46DE-9C0E-87EE72B702AF}
+ EndGlobalSection
+EndGlobal
diff --git a/Exponential/Exponential.vcxproj b/Exponential/Exponential.vcxproj
new file mode 100644
index 0000000..07d448b
--- /dev/null
+++ b/Exponential/Exponential.vcxproj
@@ -0,0 +1,147 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 16.0
+ Win32Proj
+ {74c04891-9509-4ea4-bc52-6d86492b203a}
+ Exponential
+ 10.0
+
+
+
+ Application
+ true
+ v143
+ Unicode
+
+
+ Application
+ false
+ v143
+ true
+ Unicode
+
+
+ Application
+ true
+ v143
+ Unicode
+
+
+ Application
+ false
+ v143
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Level3
+ true
+ WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+
+
+ Console
+ true
+
+
+
+
+ Level3
+ true
+ true
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+
+
+ Console
+ true
+ true
+ true
+
+
+
+
+ Level3
+ true
+ _DEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+ stdcpp20
+ false
+ $(SolutionDir)
+ NoListing
+
+
+ Console
+ true
+
+
+
+
+ Level3
+ true
+ true
+ true
+ NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+ stdcpp20
+ false
+ $(SolutionDir)
+ NoListing
+
+
+ Console
+ true
+ true
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Exponential/Exponential.vcxproj.filters b/Exponential/Exponential.vcxproj.filters
new file mode 100644
index 0000000..b9ec802
--- /dev/null
+++ b/Exponential/Exponential.vcxproj.filters
@@ -0,0 +1,30 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+
+
+
+ Source Files
+
+
+ Source Files
+
+
+
+
+ Source Files
+
+
+
\ No newline at end of file
diff --git a/Exponential/FunctionsTemplate.h b/Exponential/FunctionsTemplate.h
new file mode 100644
index 0000000..49fcb7b
--- /dev/null
+++ b/Exponential/FunctionsTemplate.h
@@ -0,0 +1,475 @@
+#pragma once
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace MATH
+{
+ constexpr double GA_DEFAULT_MIN_RANGE = -100;
+ constexpr double GA_DEFAULT_MAX_RANGE = 100;
+ constexpr int GA_DEFAULT_NUM_OF_GENERATIONS = 100;
+ constexpr int GA_DEFAULT_SAMPLE_SIZE = 1000;
+ constexpr int GA_DEFAULT_DATA_SIZE = 100000;
+ constexpr double GA_DEFAULT_MUTATION_PERCENTAGE = 0.01;
+
+ template
+ [[nodiscard("MATH::ABS(T) returns a value of type T")]] T ABS(const T& n) noexcept
+ {
+ return n < 0 ? n * -1 : n;
+ }
+
+ template
+ [[nodiscard("MATH::NEGATE(T) returns a value of type T")]] T NEGATE(const T& n) noexcept
+ {
+ return n * -1;
+ }
+
+ template
+ [[nodiscard("MATH::POW(T, int) returns a value of type T")]] T POW(const T& n, const int& exp) noexcept
+ {
+ if (exp == 0)
+ return 1;
+
+ T res = n;
+ for (int i = 1; i < exp; i++)
+ {
+ res *= n;
+ }
+
+ return res;
+ }
+
+ template
+ [[nodiscard]] T SUM(const std::vector& vec) noexcept
+ {
+ T res{};
+ for (auto& val : vec)
+ res += val;
+ return res;
+ }
+
+ template
+ [[nodiscard]] T MEDIAN(std::vector vec) noexcept
+ {
+ std::sort(
+ vec.begin(),
+ vec.end(),
+ [](const auto& lhs, const auto& rhs){
+ return lhs < rhs;
+ });
+
+ return vec[vec.size() / 2];
+ }
+
+ template
+ [[nodiscard]] double MEAN(const std::vector& vec) noexcept
+ {
+ return SUM(vec) / vec.size();
+ }
+
+ template
+ void SortASC(std::vector& vec)
+ {
+ std::sort(
+ vec.begin(), vec.end(),
+ [](const auto& lhs, const auto& rhs) {
+ return lhs < rhs;
+ });
+ }
+
+ template
+ void SortDESC(std::vector& vec)
+ {
+ std::sort(
+ vec.begin(), vec.end(),
+ [](const auto& lhs, const auto& rhs) {
+ return lhs > rhs;
+ });
+ }
+
+ class Coordinate2D
+ {
+ private:
+ double X, Y;
+
+ public:
+ Coordinate2D() : X(0), Y(0) {}
+ Coordinate2D(double x) : X(x), Y(x) {}
+ Coordinate2D(double x, double y) : X(x), Y(y) {}
+ virtual ~Coordinate2D() = default;
+
+ inline void set_x(const double val) noexcept { X = val; }
+ inline void set_y(const double val) noexcept { Y = val; }
+
+ [[nodiscard]] inline double get_x() const noexcept { return X; }
+ [[nodiscard]] inline double get_y() const noexcept { return Y; }
+
+ friend std::ostream& operator<<(std::ostream& os, const Coordinate2D& coord)
+ {
+ os << '(' << coord.X << ", " << coord.Y << ") ";
+ return os;
+ }
+ };
+
+ namespace INTERNAL
+ {
+ template // Genetic Algorithm helper struct
+ struct GA_Solution
+ {
+ double rank, x;
+
+ GA_Solution(double Rank, double x_val) : rank(Rank), x(x_val) {}
+ virtual ~GA_Solution() = default;
+
+ void fitness(const std::vector& constants)
+ {
+ std::vector exceptions;
+ for (int i : constants)
+ exceptions.push_back(i != 0);
+
+ double ans = 0;
+ for (int i = lrgst_exp; i >= 0; i--)
+ {
+ if (exceptions[i])
+ {
+ ans += constants[i] * POW(x, (lrgst_exp - i));
+ }
+ }
+ rank = (ans == 0) ? DBL_MAX : ABS(1 / ans);
+ }
+ };
+ }
+
+ namespace EXP
+ {
+ template
+ class Function
+ {
+ private:
+ std::vector constants;
+
+ public:
+ Function(const std::vector& constnts);
+ Function(std::vector&& constnts);
+ Function(const Function& other) = default;
+ Function(Function&& other) noexcept = default;
+ virtual ~Function();
+
+ Function& operator=(const Function& other) = default;
+ Function& operator=(Function&& other) noexcept = default;
+
+ [[nodiscard("MATH::EXP::Function::differential() returns the differential, the calling object is not changed")]]
+ Function differential() const; // This function returns the differential (dy/dx) of the Function object
+
+ // Operator function to display function object in a human readable format
+ friend std::ostream& operator<<(std::ostream& os, const Function func)
+ {
+ if (lrgst_exp == 0)
+ {
+ os << func.constants[0];
+ return os;
+ }
+
+ if (func.constants[0] == 1)
+ os << "x";
+ else if (func.constants[0] == -1)
+ os << "-x";
+ else
+ os << func.constants[0] << "x";
+
+ if (lrgst_exp != 1)
+ os << "^" << lrgst_exp;
+
+ for (int i = lrgst_exp - 1; i > 0; i--)
+ {
+ int n = func.constants[lrgst_exp - i];
+ if (n == 0) continue;
+
+ auto s = n > 0 ? " + " : " - ";
+
+ if (n != 1)
+ os << s << ABS(n) << "x";
+ else
+ os << s << "x";
+
+ if (i != 1)
+ os << "^" << i;
+ }
+
+ int n = func.constants[lrgst_exp];
+ if (n == 0) return os;
+
+ auto s = n > 0 ? " + " : " - ";
+ os << s;
+
+ os << ABS(n);
+
+ return os;
+ }
+
+ // Speicialty function to get the real roots of a Quadratic Function without relying on a Genetic Algorithm to approximate
+ friend std::vector QuadraticSolve(const Function<2>& f);
+
+ template
+ friend Function operator+(const Function& f1, const Function& f2); // Operator to add two functions
+ template
+ friend Function operator-(const Function& f1, const Function& f2); // Operator to subtract two functions
+
+ // Operators to multiply a function by a constant (Scaling it)
+ friend Function operator*(const Function& f, const int& c)
+ {
+ if (c == 1) return f;
+ if (c == 0) throw std::logic_error("Cannot multiply a function by 0");
+
+ std::vector res;
+ for (auto& val : f.constants)
+ res.push_back(c * val);
+
+ return Function(res);
+ }
+ Function& operator*=(const int& c)
+ {
+ for (auto& val : this->constants)
+ val *= c;
+
+ return *this;
+ }
+
+ // Function that uses a genetic algorithm to find the approximate roots of the function
+ [[nodiscard]] std::vector get_real_roots_ga(
+ const double& min_range = GA_DEFAULT_MIN_RANGE,
+ const double& max_range = GA_DEFAULT_MAX_RANGE,
+ const int& num_of_generations = GA_DEFAULT_NUM_OF_GENERATIONS,
+ const int& sample_size = GA_DEFAULT_SAMPLE_SIZE,
+ const int& data_size = GA_DEFAULT_DATA_SIZE,
+ const double& mutation_percentage = GA_DEFAULT_MUTATION_PERCENTAGE) const;
+
+ // Function that returns the y-intercept of the function i.e. where x = 0
+ [[nodiscard]] inline Coordinate2D get_y_intrcpt() const noexcept { return Coordinate2D{ 0, (double)constants[lrgst_exp] }; }
+
+ };
+
+ template
+ Function::Function(const std::vector& constnts)
+ {
+ if (lrgst_exp < 0)
+ throw std::logic_error("Function template argument must not be less than 0");
+
+ if (constnts.size() != lrgst_exp + 1)
+ throw std::logic_error("Function must be created with (n+1) integers in vector object");
+
+ if (constnts[0] == 0)
+ throw std::logic_error("First value should not be 0");
+
+ constants = constnts;
+ }
+
+ template
+ Function::Function(std::vector&& constnts)
+ {
+ if (lrgst_exp < 0)
+ throw std::logic_error("Function template argument must not be less than 0");
+
+ if (constnts.size() != lrgst_exp + 1)
+ throw std::logic_error("Function must be created with (n+1) integers in vector object");
+
+ if (constnts[0] == 0)
+ throw std::logic_error("First value should not be 0");
+
+ constants = std::move(constnts);
+ }
+
+ template
+ Function::~Function()
+ {
+ constants.clear();
+ }
+
+ template
+ Function Function::differential() const
+ {
+ if (lrgst_exp == 0)
+ throw std::logic_error("Cannot differentiate a number (Function<0>)");
+
+ std::vector result;
+ for (int i = 0; i < lrgst_exp; i++)
+ {
+ result.push_back(constants[i] * (lrgst_exp - i));
+ }
+
+ return Function{result};
+ }
+
+ template
+ std::vector Function::get_real_roots_ga(
+ const double& min_range, const double& max_range,
+ const int& num_of_generations,
+ const int& sample_size, const int& data_size,
+ const double& mutation_percentage) const
+ {
+ // Create initial random solutions
+ std::random_device device;
+ std::uniform_real_distribution unif(static_cast(min_range), static_cast(max_range));
+ std::vector> solutions;
+
+ for (int i = 0; i < sample_size; i++)
+ solutions.push_back(INTERNAL::GA_Solution{0, unif(device)});
+
+ for(int count = 0; count < num_of_generations; count++)
+ {
+ for (int i = sample_size; i < data_size; i++)
+ solutions.push_back(INTERNAL::GA_Solution{0, unif(device)});
+
+ // Run our fitness function
+ for (auto& s : solutions) { s.fitness(constants); }
+
+ // Sort our solutions by rank
+ std::sort(solutions.begin(), solutions.end(),
+ [](const auto& lhs, const auto& rhs) {
+ return lhs.rank > rhs.rank;
+ });
+
+ // Take top solutions
+ std::vector> sample;
+ std::copy(
+ solutions.begin(),
+ solutions.begin() + sample_size,
+ std::back_inserter(sample)
+ );
+ solutions.clear();
+
+ if (count + 1 == num_of_generations)
+ {
+ std::copy(
+ sample.begin(),
+ sample.end(),
+ std::back_inserter(solutions)
+ );
+ sample.clear();
+ break;
+ }
+
+ // Mutate the top solutions by %
+ std::uniform_real_distribution m((1 - mutation_percentage), (1 + mutation_percentage));
+ std::for_each(sample.begin(), sample.end(), [&m, &device](auto& s) {
+ s.x *= m(device);
+ });
+
+ // Cross over not needed as it's only one value
+
+ std::copy(
+ sample.begin(),
+ sample.end(),
+ std::back_inserter(solutions)
+ );
+ sample.clear();
+ }
+
+ std::sort(solutions.begin(), solutions.end(),
+ [](const auto& lhs, const auto& rhs) {
+ return lhs.x < rhs.x;
+ });
+
+ std::vector ans;
+ for (auto& s : solutions)
+ {
+ ans.push_back(s.x);
+ }
+ return ans;
+ }
+
+ std::vector QuadraticSolve(const Function<2>& f)
+ {
+ std::vector res;
+
+ const int& a = f.constants[0];
+ const int& b = f.constants[1];
+ const int& c = f.constants[2];
+
+ const double sqr_val = static_cast(POW(b, 2) - (4 * a * c));
+
+ if (sqr_val < 0)
+ {
+ return res;
+ }
+
+ const double root1 = (NEGATE(b) + sqrt(sqr_val)) / 2 * a;
+ const double root2 = (NEGATE(b) - sqrt(sqr_val)) / 2 * a;
+
+ res.push_back(root1);
+ res.push_back(root2);
+ return res;
+ }
+
+ template e2 ? e1 : e2)>
+ Function operator+(const Function& f1, const Function& f2)
+ {
+ std::vector res;
+ if (e1 > e2)
+ {
+ for (auto& val : f1.constants)
+ res.push_back(val);
+
+ int i = e1 - e2;
+ for (auto& val : f2.constants)
+ {
+ res[i] += val;
+ i++;
+ }
+ }
+ else
+ {
+ for (auto& val : f2.constants)
+ res.push_back(val);
+
+ int i = e2 - e1;
+ for (auto& val : f1.constants)
+ {
+ res[i] += val;
+ i++;
+ }
+ }
+
+ return Function{res};
+ }
+
+ template e2 ? e1 : e2)>
+ Function operator-(const Function& f1, const Function& f2)
+ {
+ std::vector res;
+ if (e1 > e2)
+ {
+ for (auto& val : f1.constants)
+ res.push_back(val);
+
+ int i = e1 - e2;
+ for (auto& val : f2.constants)
+ {
+ res[i] -= val;
+ i++;
+ }
+ }
+ else
+ {
+ for (auto& val : f2.constants)
+ res.push_back(val);
+
+ int i = e2 - e1;
+
+ for (int j = 0; j < i; j++)
+ res[j] *= -1;
+
+ for (auto& val : f1.constants)
+ {
+ res[i] = val - res[i];
+ i++;
+ }
+ }
+
+ return Function{res};
+ }
+ }
+}
\ No newline at end of file
diff --git a/Exponential/Source.cpp b/Exponential/Source.cpp
new file mode 100644
index 0000000..a161957
--- /dev/null
+++ b/Exponential/Source.cpp
@@ -0,0 +1,45 @@
+#include
+#include
+#include "FunctionsTemplate.h"
+#include "Timer.h"
+
+template
+using Function = MATH::EXP::Function;
+
+typedef TIMER::Timer timer;
+
+int main()
+{
+ std::vector vec{ 1, 5, 4 };
+ Function<2> f{ vec };
+ Function<3> g{ { 1, -6, 11, -6 } };
+
+ auto res = f * 2;
+ std::cout << res << '\n';
+
+ timer t;
+
+ for (int i = 11; i < 2; i++)
+ {
+ t.Reset();
+ auto gr = g.get_real_roots_ga(-100, 100, i, 1000, 100000, 0.005);
+ t.SetEnd();
+
+ std::cout << "Time took: " << t.GetTimeInS() << "s\n";
+
+
+ std::for_each(gr.begin(), gr.end(),
+ [](const auto& val) {
+ std::cout << "x:" << val << '\n';
+ });
+ }
+ //std::cout << "Median: " << MATH::MEDIAN(gr) << '\n';
+ //std::cout << "Mean: " << MATH::MEAN(gr) << '\n';
+
+ //std::cout << g << '\n';
+ //std::cout << fr[0] << ", " << fr[1] << '\n';
+ //std::cout << f.get_y_intrcpt() << '\n';
+ //std::cout << f.differential() << '\n';
+
+ return 0;
+}
\ No newline at end of file
diff --git a/Exponential/Timer.h b/Exponential/Timer.h
new file mode 100644
index 0000000..1ec849e
--- /dev/null
+++ b/Exponential/Timer.h
@@ -0,0 +1,42 @@
+#pragma once
+#include
+#include
+
+namespace TIMER{
+ struct Timer
+ {
+ std::chrono::time_point start, end;
+ std::chrono::duration duration;
+
+ Timer()
+ {
+ Reset();
+ }
+
+ ~Timer()
+ {
+
+ }
+
+ inline void Reset() noexcept
+ {
+ start = std::chrono::high_resolution_clock::now();
+ }
+
+ void SetEnd() noexcept
+ {
+ end = std::chrono::high_resolution_clock::now();
+ duration = end - start;
+ }
+
+ inline float GetTimeInMS() const noexcept
+ {
+ return float(duration.count() * 1000.f);
+ }
+
+ inline float GetTimeInS() const noexcept
+ {
+ return float(duration.count());
+ }
+ };
+}
\ No newline at end of file