Return to Snippet

Revision: 66124
at March 18, 2014 08:48 by omarelsaid


Initial Code
//PRODUCT.HPP FILE
#pragma once
#include <iostream>
#include <vector>

#include <boost/fusion/include/vector.hpp>

typedef boost::fusion::vector4<std::string, unsigned int, unsigned int, double> bfVector4;

class Product
{
public:
	Product(const bfVector4& line);

	void findMinAndMaxOriginYears();
	void addData(const bfVector4& currentBFVector);
	const std::string getProductName() const { return m_productName; }
	const unsigned int getMinOriginYear() const { return m_minOriginYear; }
	void setMinOriginYear(unsigned int minOriginYear) { m_minOriginYear = minOriginYear; }
	void setMaxOriginYear(unsigned int maxOriginYear) { m_maxOriginYear = maxOriginYear; }
	const unsigned int getMaxOriginYear() const { return m_maxOriginYear; }
	const std::vector<bfVector4> getData() const { return m_data; }
private:
	std::string m_productName;
	unsigned int m_minOriginYear;
	unsigned int m_maxOriginYear;
	std::vector<bfVector4> m_data;
};


//END PRODUCT.HPP FILE

//PRODUCT.CPP FILE
#include "Product.hpp"

#include <boost/fusion/sequence.hpp>
#include <boost/foreach.hpp>

Product::Product(const bfVector4& line)
{
	m_productName = boost::fusion::at_c<0>(line);
	m_data.push_back(line);
	m_minOriginYear = std::numeric_limits<unsigned int>::max();
	m_maxOriginYear = std::numeric_limits<unsigned int>::min();
}

void Product::findMinAndMaxOriginYears()
{
	BOOST_FOREACH(const bfVector4& line, m_data)
	{
		unsigned int currentOriginYear = boost::fusion::at_c<1>(line);

		if (currentOriginYear < m_minOriginYear)
		{
			m_minOriginYear = currentOriginYear;
			continue;
		}
		if (currentOriginYear > m_maxOriginYear)
		{
			m_maxOriginYear = currentOriginYear;
			continue;
		}
	}
}

void Product::addData(const bfVector4& currentBFVector)
{
	m_data.push_back(currentBFVector);
}
//END PRODUCT.CPP FILE

//MAIN.CPP FILE
#define _SCL_SECURE_NO_WARNINGS

#include <iostream>
#include <fstream>
#include <vector>
#include <map>
#include <set>

#include <boost/filesystem.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/fusion/sequence.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/foreach.hpp>

#include "Product.hpp"

typedef boost::fusion::vector3<std::string, unsigned int, unsigned int> bfVector3;

void readFile(const std::string& inputFile, std::set<std::string>& productNames, std::vector<Product>& products)
{
	std::ifstream inputFileStream(inputFile);
	std::string currentLine;
	std::map<std::string, unsigned int> headerPositions;
	//the order I want the headers to be in
	headerPositions.insert(std::make_pair("Product", 0));
	headerPositions.insert(std::make_pair("OriginYear", 1));
	headerPositions.insert(std::make_pair("DevelopmentYear", 2));
	headerPositions.insert(std::make_pair("IncrementalValue", 3));

	//process first line first - the headers line, to determine what column goes where; reordering the columns in memory to my liking
	std::map<unsigned int, int> correctHeaderPositions;
	std::getline(inputFileStream, currentLine);
	currentLine.erase(std::remove_if(currentLine.begin(), currentLine.end(), isspace), currentLine.end());
	std::vector<std::string> headers;
	boost::split(headers, currentLine, boost::is_any_of(","));

	if (headers.size() != 4)
		std::cout << "There should only be 4 headers in your text file. Any extra incorrect columns will be ignored." << std::endl;

	for (int i = 0; i < (int)headers.size(); ++i)
	{
		if (!headerPositions.count(headers[i]))
		{
			correctHeaderPositions.insert(std::make_pair(i, -(i + 1)));
			std::cout << "The header \'" << headers[i] << "\' is incorrect. This column will be ignored." << std::endl;
			continue;
		}

		correctHeaderPositions.insert(std::make_pair(i, headerPositions[headers[i]]));
	}
	
	//now read all the other lines
	while (std::getline(inputFileStream, currentLine))
	{
		bool skip = false;
		std::vector<std::string> currentData;
		bfVector4 currentBFVector;
		currentLine.erase(std::remove_if(currentLine.begin(), currentLine.end(), isspace), currentLine.end());
		boost::split(currentData, currentLine, boost::is_any_of(","));
		for (unsigned int i = 0; i < currentData.size(); ++i)
		{
			if (correctHeaderPositions[i] < 0 || skip)
				continue;

			unsigned int realPosition = correctHeaderPositions[i];
			int currentOriginYear = 0;
			int currentDevelopmentYear = 0;
			double currentIncrementalValue = 0.0;
			switch (realPosition)
			{
			case 0:
				try
				{
					boost::fusion::at_c<0>(currentBFVector) = boost::lexical_cast<std::string>(currentData[correctHeaderPositions[0]]);
				}
				catch (boost::bad_lexical_cast)
				{
					std::cout << "The Product name of the line: " << std::endl << currentLine << std::endl << "is not a valid string. Discarding line."
							  << std::endl;
					skip = true;
					break;
				}
				break;

			case 1:
				try
				{
					currentOriginYear = boost::lexical_cast<int>(currentData[correctHeaderPositions[1]]);
				}
				catch (boost::bad_lexical_cast)
				{
					std::cout << "The Origin Year of the line:" << std::endl << currentLine << std::endl << "is not a valid integer. Discarding line."
							  << std::endl;
					skip = true;
					break;
				}
				//check if the current origin year is positive and non-zero
				if (currentOriginYear <= 0)
				{
					std::cout << "The Origin Year of the line:" << std::endl << currentLine << std::endl << "is zero or negative. Discarding line."
							  << std::endl;
					skip = true;
					break;
				}
				boost::fusion::at_c<1>(currentBFVector) = currentOriginYear;
				break;

			case 2:
				try
				{
					currentDevelopmentYear = boost::lexical_cast<unsigned int>(currentData[correctHeaderPositions[2]]);
				}
				catch (boost::bad_lexical_cast)
				{
					std::cout << "The Development Year of the line:" << std::endl << currentLine << std::endl << "is not a valid integer. Discarding line."
							  << std::endl;
					skip = true;
					break;
				}
				//check if the current development year is positive and non-zero
				if (currentDevelopmentYear <= 0)
				{
					std::cout << "The Development Year of the line:" << std::endl << currentLine << std::endl << "is zero or negative. Discarding line."
							  << std::endl;
					skip = true;
					break;
				}
				//check if the current development year is less than it's respective origin year
				if (currentDevelopmentYear < (int)boost::fusion::at_c<1>(currentBFVector))
				{
					std::cout << "The Development Year of the line:" << std::endl << currentLine << std::endl << "is less than it's corresponding origin year. "
							  << "Discarding line." << std::endl;
					skip = true;
					break;
				}
				boost::fusion::at_c<2>(currentBFVector) = currentDevelopmentYear;
				break;

			case 3:
				try
				{
					currentIncrementalValue = boost::lexical_cast<double>(currentData[correctHeaderPositions[3]]);
				}
				catch (boost::bad_lexical_cast)
				{
					std::cout << "The Incremental Value of the line:" << std::endl << currentLine << std::endl << "is not a valid real number. "
							  << "Discarding line." << std::endl;
					skip = true;
					break;
				}
				//check if the current incremental value is positive and non-zero
				if (currentIncrementalValue <= 0.0)
				{
					std::cout << "The Incremental Value of the line:" << std::endl << currentLine << std::endl << "zero or negative. Discarding line."
							  << std::endl;
					skip = true;
					break;
				}
				boost::fusion::at_c<3>(currentBFVector) = currentIncrementalValue;
				break;
			}				
		}
		//if this is a new product, add it to the products vector
		if (!productNames.count(boost::fusion::at_c<0>(currentBFVector)))
		{
			products.push_back(Product(currentBFVector));
			productNames.insert(boost::fusion::at_c<0>(currentBFVector));
			continue;
		}

		//if this is an existing product, then add the data to the relevant product
		for (unsigned int i = 0; i < products.size(); ++i)
			if (products[i].getProductName() == boost::fusion::at_c<0>(currentBFVector))
				products[i].addData(currentBFVector);
	}
}

bool findGlobalMinAndMaxOriginYears(std::vector<Product>& products, unsigned int& minOriginYear, unsigned int& maxOriginYear)
{
	BOOST_FOREACH(Product& product, products)
	{
		product.findMinAndMaxOriginYears();
		unsigned int currentMinOriginYear = product.getMinOriginYear();
		unsigned int currentMaxOriginYear = product.getMaxOriginYear();

		if (currentMinOriginYear < minOriginYear)
			minOriginYear = currentMinOriginYear;
		if (currentMaxOriginYear > maxOriginYear)
			maxOriginYear = currentMaxOriginYear;
	}

	if (minOriginYear < std::numeric_limits<unsigned int>::max() && maxOriginYear > std::numeric_limits<unsigned int>::min())
		return true;

	return false;
}

void fillInBlanks(std::vector<Product>& products, const unsigned int minOriginYear, const unsigned int maxOriginYear)
{
	BOOST_FOREACH(Product& product, products)
	{
		unsigned int productMinOriginYear = product.getMinOriginYear();
		unsigned int productMaxOriginYear = product.getMaxOriginYear();
		std::string productName = product.getProductName();
		//fill in blanks in between min and max product origin years
		for (unsigned int i = productMinOriginYear; i <= productMaxOriginYear; ++i)
		{
			for (unsigned int j = i; j <= productMaxOriginYear; ++j)
			{
				bool needsInserting = true;
				BOOST_FOREACH(const bfVector4& line, product.getData())
				{
					if (bfVector3(boost::fusion::at_c<0>(line), boost::fusion::at_c<1>(line), boost::fusion::at_c<2>(line))
						== bfVector3(productName, i, j))
					{
						needsInserting = false;
						break;
					}
				}

				if (needsInserting)
					product.addData(bfVector4(productName, i, j, 0.0));
			}
		}
		//fill in missing origin years before the current product min origin year
		if (minOriginYear < productMinOriginYear)
		{
			for (unsigned int i = minOriginYear; i < productMinOriginYear; ++i)
				for (unsigned int j = i; j <= maxOriginYear; ++j)
					product.addData(bfVector4(productName, i, j, 0.0));

			product.setMinOriginYear(minOriginYear);
		}
		//fill in missing origin years after the current product max origin year
		if (maxOriginYear > productMaxOriginYear)
		{
			for (unsigned int i = productMaxOriginYear + 1; i <= maxOriginYear; ++i)
				for (unsigned int j = i; j <= maxOriginYear; ++j)
					product.addData(bfVector4(productName, i, j, 0.0));

			product.setMaxOriginYear(maxOriginYear);
		}
	}
}

void outputFile(std::ofstream& output, const std::vector<Product> products, const unsigned int minOriginYear, const unsigned int developmentYears)
{
	output << minOriginYear << ", " << developmentYears << std::endl;
	BOOST_FOREACH(const Product& product, products)
	{
		output << product.getProductName();
		std::vector<double> productOutput((developmentYears * (developmentYears + 1)) / 2, 0.0); //vector of incremental values to be summed
		//go through each origin year and workout where in the productOutput array the corresponding incremental value for each line should go
		for (unsigned int i = product.getMinOriginYear(); i <= product.getMaxOriginYear(); ++i)
		{
			BOOST_FOREACH(bfVector4 line, product.getData())
			{
				if (boost::fusion::at_c<1>(line) != i)
					continue;

				unsigned int currentOriginYearFromMin = i - product.getMinOriginYear();
				//the following index was worked out using the fact that the number of incremental values decreases by 1 each time each time
				//the origin year increases, and the difference between the development year and the origin year, amongst other things.
				//used pen and paper to work out this index
				int index = (currentOriginYearFromMin * ((2 * (developmentYears - 1)) + 3 - currentOriginYearFromMin)) / 2
							+ (boost::fusion::at_c<2>(line) - i);
				productOutput.at(index) = boost::fusion::at_c<3>(line);
			}
		}
		//now go through vector just constructed and sum relevant values
		for (unsigned int i = developmentYears; i > 0; --i)
		{
			for (unsigned int j = 0; j < i; ++j)
			{
				double sum = 0.0;
				for (int k = j; k >= 0; --k)
				{
					if (productOutput[k] != 0)
						sum += productOutput[k];
				}

				output << ", " << sum;
			}

			productOutput.erase(productOutput.begin(), productOutput.begin() + i);
		}

		output << std::endl;
	}

	output.close();
}

void main()
{
	boost::filesystem::path inputFile, outputFileDir;
	std::cout << "Please enter the path of the file you wish to examine: " << std::endl;
	std::cin >> inputFile;
	std::cout << "Note: The headers in your text file must be exactly the following, and nothing more:" << std::endl
			  << "Product" << std::endl << "Origin Year" << std::endl << "Development Year" << std::endl << "Incremental Value" << std::endl;

	//do some checks on the file specified first
	if (!boost::filesystem::exists(inputFile))
	{
		std::cout << "The file specified doesn't exist." << std::endl;
		return;
	}

	if (inputFile.extension() != ".txt")
	{
		std::cout << " The file specified is not a .txt file. Please specify a path to a .txt file." << std::endl;
		return;
	}

	std::cout << "Please enter the path of the directory that you wish the output file to be saved in: " << std::endl;
	std::cin >> outputFileDir;

	if (!boost::filesystem::exists(outputFileDir))
		boost::filesystem::create_directories(outputFileDir);

	//start reading file
	std::set<std::string> productNames;
	std::vector<Product> products;
	readFile(inputFile.string(), productNames, products);
	unsigned int minOriginYear = std::numeric_limits<unsigned int>::max();
	unsigned int maxOriginYear = std::numeric_limits<unsigned int>::min();
	unsigned int developmentYears = std::numeric_limits<unsigned int>::max();
	if (findGlobalMinAndMaxOriginYears(products, minOriginYear, maxOriginYear))
		developmentYears = maxOriginYear - minOriginYear + 1;

	fillInBlanks(products, minOriginYear, maxOriginYear);
	//output results
	std::ofstream output((outputFileDir / "TriangleOutput.txt").string().c_str());
	outputFile(output, products, minOriginYear, developmentYears);
}
END OF MAIN.CPP FILE

Initial URL


Initial Description
This code creates a program to read in incremental payment data from a comma-separated text file, to accumulate the data and to output the results to a different comma-separated text file.
EXAMPLE INPUT DATA
A short input file might contain the following:
Product, Origin Year, Development Year, Incremental Value
Comp, 1992, 1992, 110.0
Comp, 1992, 1993, 170.0
Comp, 1993, 1993, 200.0
Non-Comp, 1990, 1990, 45.2
Non-Comp, 1990, 1991, 64.8
Non-Comp, 1990, 1993, 37.0
Non-Comp, 1991, 1991, 50.0
Non-Comp, 1991, 1992, 75.0
Non-Comp, 1991, 1993, 25.0
Non-Comp, 1992, 1992, 55.0
Non-Comp, 1992, 1993, 85.0
Non-Comp, 1993, 1993, 100.0

Initial Title
Reading in a specific type of text file

Initial Tags


Initial Language
C++