Time due: 11:00 PM Wednesday, April 30
The capricious King of Heard Island has changed his mind about how tariffs will be imposed on guano imports. His new scheme has the duty imposed being a percentage of the value of the shipment, with an individualized percentage for each country or territory of origin that might change over time depending on the status of negotiations with that country, the effect on the economy, or whether that country's leader said something about the king that he considers nice or nasty.
Since none of the king's subjects know how to program, you've been hired as part of a team to help with this. The percentages for various countries are encoded in a string along with indications whether each country's percentage is expected to go up or down in the near future. You will need to process that string. We will describe the format of the string and what you must do to process it.
First, we define some terms. A letter is one of the 52 letter charactersA through
Z and a through z.
A country code is one of the following 250 two-letter codes, with
each letter being in either upper or lower case (so US Us uS and us are all
country codes):
AD AE AF AG AI AL AM AO AQ AR AS AT AU AW AX AZ BA BB BD BE BF BG BH BI BJ BL
BM BN BO BQ BR BS BT BV BW BY BZ CA CC CD CF CG CH CI CK CL CM CN CO CR CU CV
CW CX CY CZ DE DK DJ DM DO DZ EC EE EG EH ER ES ET FI FJ FK FM FO FR GA GB GD
GE GF GG GH GI GL GN GM GP GQ GR GS GT GU GW GY HK HM HN HR HT HU ID IE IL IM
IN IO IQ IR IS IT JE JM JO JP KE KG KH KI KM KN KP KR KW KY KZ LA LB LC LI LK
LR LS LT LU LV LY MA MC MD ME MF MG MH MK ML MM MN MO MP MQ MR MS MT MU MV MW
MX MY MZ NA NC NE NF NG NI NL NO NP NR NU NZ OM PA PE PF PG PH PK PL PM PN PR
PS PT PW PY QA RE RO RS RU RW SA SB SC SD SE SG SH SI SJ SK SL SM SN SO SR SS
ST SV SX SY SZ TC TD TF TG TH TJ TK TL TM TN TO TR TT TV TW TZ UA UG UK UM US
UY UZ VA VC VE VG VI VN VU WF WS YE YT ZA ZM ZW
A digit is one of the ten digit characters 0 through 9.
An expectation is one of these four letter characters: U u D dYour assignment is essentially to take a tariff data string and compute some summary data about the tariffs represented in that string. For example, for the tariff data string FR7UjP15dNZ9Uin00U, the mean tariff percentage is 7.75% (the mean of 7, 15, 9, and 0), the number of tariff percentages expected to go up in the near future is 3, and the number expected to go down is 1.
For this project, you will implement the following two functions, using the exact function names, parameter types, and return types shown in this specification. (The parameter names may be different if you wish.)
bool isCorrectlyFormed(string tariffData)This function returns true if its parameter is a tariff data string (i.e., it meets the definition above), or false otherwise.
int summarizeData(string tariffData, double& meanPercentage, int& numUp, int& numDown)
If the parameter tariffData is not a tariff data string (i.e.,
it does not meet the definition above), this function returns 1. If
tariffData is a tariff data string, but a country record
indicates a percentage of 99% and an expectation that the percentage will go
up or it indicates a percentage of zero and an expectation that it will go
down, this function returns 2. If the function returns 1 or 2,
meanPercentage, numUp, and numDown
must have the same values they had just before any statements in the function
were executed. If none of the conditions for which the function returns 1 or
2 hold, then the function returns 0 after setting meanPercentage
to the mean of the percentages indicated in the country records in
tariffData (or 0 if there are no country records in
tariffData), numUp to the number of country records
indicating an expectation of up, and numDown to the number of
country records indicating an expectation of down.
These are the only two functions you are required to write. (Hint:
summarizeData may well call isCorrectlyFormed.)
Your solution may use functions that you write in addition to these two if
you wish. While we won't test those additional functions separately, using
them may help you structure your program more readably.
Of course, to test the functions you write, you'll want to write a main routine that calls your functions. During the course of developing your solution, you might change that main routine many times. As long as your main routine compiles correctly when you turn in your solution, it doesn't matter what it does, since we will rename it to something harmless and never call it (because we will supply our own main routine to thoroughly test your functions).
There is a situation that would be important to deal with properly
in the real world that we won't make you deal with to keep things simpler:
If a tariff data string contains two or more country records for the same
country, summarizeData does not have to work correctly. We
guarantee we will not test that function with tariff data strings like
FR7UjP15dfR26D (with FR and fR) or NZ9UNZ9U (with NZ and NZ).
The functions that you write must not use any global variables whose values may be changed during execution. Global constants are allowed.
When you turn in your solution, neither of the two required functions, nor
any functions you write that they call, may read any input from
cin or write any output to cout. (Of course,
during development, you may have them write whatever you like to help you
debug.) If you want to print things out for debugging purposes, write to
cerr instead of cout. cerr is the
standard error destination; items written to it by default go to the
screen. When we test your program, we will cause everything written to
cerr to be discarded instead — we will never see that
output, so you may leave those debugging output statements that write to
cerr in your program if you wish.
The correctness of your program must not depend on undefined program behavior.
Your program must never access out of range positions in a string. Your
program must not, for example, assume anything about n's
value at the point indicated, or even whether or not the program crashes:
int main()
{
string s = "Hello";
int n; // n is uninitialized
s.at(5*n/n) = '!'; // undefined behavior!
…
Be sure that your program builds successfully, and try to ensure that your functions do something reasonable for at least a few test cases under both g31 and either Visual C++ or clang++. That way, you can get some partial credit for a solution that does not meet the entire specification.
If you wish, you may use this isValidUppercaseCountryCode.txt function as part of your solution. (We can't imagine why you would not want to use it, since it does some of the work of validating a supposed country code.)
You do not need to know anything about arrays to write this program. You may use arrays if you wish, but the most straightforward solutions to this project actually don't use arrays.
Your program must not use library facilities for matching regular expressions. (These usually involve "regex" somewhere in the name.) If you don't know what this paragraph is talking about, then there's nothing for you to worry about.
There are a number of ways you might write your main routine to test your functions. One way is to interactively accept test strings:
int main()
{
for (;;)
{
cout << "Enter supposed tariff data string: ";
string tds;
getline(cin, tds);
if (tds == "quit")
break;
cout << "isCorrectlyFormed returns ";
if (isCorrectlyFormed(tds))
cout << "true";
else
cout << "false";
cout << endl;
}
}
While this is flexible, you run the risk of not being able to reproduce all your test cases if you make a change to your code and want to test that you didn't break anything that used to work.
Another way is to hard-code various tests and report which ones the program passes:
#include <cmath>
bool isNear(double a, double b)
{
return abs(a - b) < 1e-7;
}
int main()
{
if (isCorrectlyFormed("FR7UjP15dNZ9Uin00U"))
cout << "Passed test 1: isCorrectlyFormed(\"FR7UjP15dNZ9Uin00U\")" << endl;
if (!isCorrectlyFormed("ZZ7UjP15dNZ9Uin00U"))
cout << "Passed test 2: !isCorrectlyFormed(\"ZZ7UjP15dNZ9Uin00U\")" << endl;
double mean;
int nUp;
int nDown;
mean = -999; nUp = -999; nDown = -999; // so we can detect if summarizeData sets these
if (summarizeData("FR7UjP15dNZ9Uin00U", mean, nUp, nDown) == 0 &&
isNear(mean, 7.75) && nUp == 3 && nDown == 1)
cout << "Passed test 3: summarizeData(\"FR7UjP15dNZ9Uin00U\", mean, nUp, nDown)" << endl;
mean = -999; nUp = -999; nDown = -999; // so we can detect if summarizeData sets these
if (summarizeData("ZZ7UjP15dNZ9Uin00U", mean, nUp, nDSown) == 1 &&
mean == -999 && nUp == -999 && nDown == -999)
cout << "Passed test 4: summarizeData(\"ZZ7UjP15dNZ9Uin00U\", mean, nUp, nDown)" << endl;
…
This can get rather tedious. Fortunately, the library has a facility to make
this easier: assert. If you include the header
<cassert>, you can call assert in the following
manner:
assert(some boolean expression);
During execution, if the expression is true, nothing happens and execution
continues normally; if it is false, a diagnostic message is written to
cerr telling you the text and location of the failed assertion,
and the program is terminated. Using assert, we can write the
tests above more easily (This is a very incomplete set of test cases):
#include <iostream>
#include <string>
#include <cmath>
#include <cassert>
using namespace std;
bool isCorrectlyFormed(string tariffData)
{
… Your code goes here …
}
int summarizeData(string tariffData, char party, int& seatCount)
{
… Your code goes here …
}
bool isNear(double a, double b)
{
return abs(a - b) < 1e-7;
}
int main()
{
assert(isCorrectlyFormed("FR7UjP15dNZ9Uin00U"));
assert(!isCorrectlyFormed("ZZ7UjP15dNZ9Uin00U"));
double mean;
int nUp;
int nDown;
mean = -999; nUp = -999; nDown = -999; // so we can detect if summarizeData sets these
assert(summarizeData("FR7UjP15dNZ9Uin00U", mean, nUp, nDown) == 0 &&
isNear(mean, 7.75) && nUp == 3 && nDown == 1);
mean = -999; nUp = -999; nDown = -999; // so we can detect if summarizeData sets these
assert(summarizeData("ZZ7UjP15dNZ9Uin00U", mean, nUp, nDown) == 1 &&
mean == -999 && nUp == -999 && nDown == -999);
…
cerr << "All tests succeeded" << endl;
}
The reason for writing one line of output at the end is to ensure that you can distinguish the situation of all tests succeeding from the case where one function you're testing silently crashes the program.
What you will turn in for this assignment is a zip file containing these two files and nothing more:
A text file named tariff.cpp that contains the source code for your C++ program. Your source code should have helpful comments that tell the purpose of the major program segments and explain any tricky code. The file must be a complete C++ program that can be built and run, so it must contain appropriate #include lines, a main routine, and any additional functions you may have chosen to write.
assert style
above for writing your test code, you can copy those asserts,
along with a very brief comment about what each is testing for. Notice that
most of this portion of your report can be written just after reading the
requirements in this specification, before you even start designing your
program.
By April 29, there will be a link on the class webpage that will enable you to turn in your zip file electronically. Turn in the file by the due time above. Give yourself enough time to be sure you can turn something in. There's a lot to be said for turning in a preliminary version of your program and report early (You can always overwrite it with a later submission). That way you have something submitted in case there's a problem later.