I found Project Euler some time ago and I thought I'd enjoy trying those problems.
I'm using C++11 (Visual Studio 2012). I thought I'd program a Manager to test each problem and the time it takes my solution to run. I programmed this:
#include "ProblemsManager.h"
#include <algorithm>
#include <iostream>
#include <cstdio>
#include <string>
#include <sstream>
#include <ctime>
ProblemsManager::ProblemsManager() {
}
ProblemsManager::~ProblemsManager() {
}
ProblemsManager & ProblemsManager::getInstance() {
static ProblemsManager instance;
return instance;
}
void ProblemsManager::registerProblem(int n, std::function<PSolution()> solution) {
if (registeredProblems.find(n) != registeredProblems.end()) {
throw std::exception("That problem is already registered!");
} else if (testProblems.find(n) != testProblems.end()) {
throw std::exception("That problem is already registerd as a test problem!");
}
registeredProblems[n] = solution;
}
void ProblemsManager::registerTestProblem(int n, std::function<PSolution()> testSolution) {
if (registeredProblems.find(n) != registeredProblems.end()) {
throw std::exception("That problem already has a valid solution!");
} else if (testProblems.find(n) != testProblems.end()) {
throw std::exception("That problem is already registered!");
}
testProblems[n] = testSolution;
}
void ProblemsManager::run() {
printHeaders();
if (testProblems.size() > 0) {
for (const auto & entry : testProblems) {
log<<"Testing problem "<<entry.first<<": \n";
auto start = std::chrono::steady_clock::now();
PSolution sol = entry.second();
auto end = std::chrono::steady_clock::now();
log<<"Solution reported: "<<sol<<". ("<<timeDifference(start,end).c_str()<<")\n";
std::cout<<"Press any key to continue.\n";
getchar();
}
} else {
for (const auto & entry : registeredProblems) {
// Redirect std::cout (debug msg from the solution to every problem
std::streambuf * coutBuff = std::cout.rdbuf();
std::stringstream debug;
std::cout.rdbuf(debug.rdbuf());
auto start = std::chrono::steady_clock::now();
PSolution sol = entry.second();
auto end = std::chrono::steady_clock::now();
// Restore std::cout to screen
std::cout.rdbuf(coutBuff);
// Reusing the debug stream for the problem to show the information
debug.str(std::string()); // clear stream
debug<<"Problem "<<entry.first;
for (int i= debug.str().size(); i< TIME_MARGIN; i++) {
debug<<" ";
}
debug<<"Time: "<<timeDifference(start,end).c_str();
auto timeInSeconds = std::chrono::duration_cast<std::chrono::seconds>(end - start).count();
if (timeInSeconds > MAX_TIME_SECONDS) {
debug<<" (too much)";
}
for (int i= debug.str().size(); i< ANSWER_MARGIN; i++) {
debug<<" ";
}
debug<<"Solution: "<<sol;
log<<debug.str();
log.endl();
}
}
}
void ProblemsManager::printHeaders() {
/// Get datetime
time_t now = time(NULL);
#pragma warning(push) // Disabling warnings on VS
#pragma warning(disable: 4996) // 4996 for _CRT_SECURE_NO_WARNINGS equivalent
struct tm * ptm = localtime(&now);
#pragma warning(pop)
char buffer[32];
strftime (buffer, 32, "%d/%m/%Y %H:%M:%S", ptm);
log<<"Report generated on: "<<buffer;
log.endl();
log.endl();
// From http://www.informit.com/articles/article.aspx?p=1881386&seqNum=2
log<<"Clock data:";
log.endl();
log<<"\tPrecision: "<<
static_cast<double>(std::ratio_multiply<std::chrono::steady_clock::period,std::kilo>::type::num)
/
static_cast<double>(std::ratio_multiply<std::chrono::steady_clock::period,std::kilo>::type::den)
<<" milliseconds.";
log.endl();
log<<'\t'<<(std::chrono::steady_clock::is_steady?"Steady clock.":"Warning! Non steady clock. Measurements may be inaccurate");
log.endl();
log.endl();
log<<"-----------------------------";
log.endl();
log.endl();
}
std::string ProblemsManager::timeDifference(std::chrono::steady_clock::time_point t1, std::chrono::steady_clock::time_point t2) {
auto msTotalTime = std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count();
if (msTotalTime >= 0) {
std::string retValue = "";
if (msTotalTime >= 1000) {
retValue += std::to_string(msTotalTime / 1000);
retValue += "s";
}
retValue += std::to_string(msTotalTime % 1000);
retValue += "ms";
return retValue;
} else {
std::string retValue = "-";
retValue += timeDifference(t2,t1);
return retValue;
}
}
const std::string ProblemsManager::logger::LOG_PATH = "Resources/report.log";
ProblemsManager::logger::logger() : fileStream() {}
ProblemsManager::logger::~logger() {}
void ProblemsManager::logger::endl() {
fileStream<<std::endl;
std::cout<<std::endl;
}
#pragma once
#include <functional>
#include <map>
#include <chrono>
#include <fstream>
#include "PSolution.h"
class ProblemsManager {
public:
static ProblemsManager & getInstance();
void registerProblem(int n, std::function<PSolution()> solution);
void registerTestProblem(int n, std::function<PSolution()> testSolution);
void run();
void printHeaders();
private:
ProblemsManager();
~ProblemsManager();
ProblemsManager(const ProblemsManager &);
void operator=(const ProblemsManager &);
std::string static timeDifference(std::chrono::steady_clock::time_point start, std::chrono::steady_clock::time_point end);
std::map<int, std::function<PSolution()>> registeredProblems;
std::map<int, std::function<PSolution()>> testProblems;
static const int TIME_MARGIN = 12;
static const int ANSWER_MARGIN = 38;
static const int MAX_TIME_SECONDS = 60;
private:
class logger {
private:
std::ofstream fileStream;
static const std::string LOG_PATH;
public:
logger();
~logger();
void endl();
template<class T>
friend logger & operator<<(logger &log, const T & bytes) {
if (!log.fileStream.is_open()) {
log.fileStream.open(LOG_PATH);
if (log.fileStream.fail()) {
perror("Problem opening log file");
}
}
log.fileStream<<bytes;
std::cout<<bytes;
return log;
}
};
logger log;
};
Then, I can just use Problems.h:
#pragma once
#include "ProblemsManager.h"
#include "PSolution.h"
#include <functional>
#include <iostream>
class Problem {
public:
Problem(int n, std::function<PSolution()> f) {
ProblemsManager::getInstance().registerProblem(n,f);
}
~Problem() {
}
};
class TestProblem {
public:
TestProblem(int n, std::function<PSolution()> f) {
ProblemsManager::getInstance().registerTestProblem(n,f);
}
~TestProblem() {
}
};
And add a .cpp file (Problem1.cpp) with the solution for a problem like this:
#include "../ProblemsManager/Problems.h"
#include <iostream>
using namespace std;
static Problem p(1,
[] () -> PSolution {
// Code for the problem...
return solution;
}
);
The problem I have is: when I run all the problems, some of them run slowly. For example, I get something like this (pastebin ID: WYjbqgc0):
Note: Problem 52
Time: 133s171ms
Solution: 142857
But, if I change Problem
to TestProblem
in Problem52.cpp, so that only this problem can be executed (pastebin ID: t803wLvE):
Note: Solution
Reported: 142857 (2s156ms)
I mainly have two questions:
- Why do the performances differ?
- How can I improve my
ProblemManager
?
Also, I have noticed it take a long time for the program to terminate (after the return, in main()
). Is that because of all the static
variables? If so, how can I fix that?