/ Published in: C++
                    
                                        
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
                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
                            
                                Expand |
                                Embed | Plain Text
                            
                        
                        Copy this code and paste it in your HTML
//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
Comments
 Subscribe to comments
                    Subscribe to comments
                
                