// Random Math is a program to allow users to choose the range of
// numbers to randomly generate math problems. The problems will be
// displayed and an answer will be accepted. There will be different
// responses depending on if the answer is correct or incorrect. Random
// positive and negative responses should be used.
// Written by Janine Bouyssounouse 01/07

#include <iostream>
#include <stdlib.h>
#include <string>
#include <vector>
#include <cstdlib>
#include <ctime>
#include <algorithm>

using namespace std;

void welcome();
float askNumber(string question);
char askChoice(string question, const vector<char>& choices);
bool isLegalChoice(char response, const vector<char>& choices);
bool isLegalHighLow(float high, float low);
bool isLegalHowMany(float numHigh, float numLow, float howMany);
float sum(float num1, float num2);
float difference(float num1, float num2);
float product(float num1, float num2);
float quotient(float num1, float num2);
float average(float num1, float num2);
char askYesNo(string question);
void result(char pick, float num1, float num2);
float random(float high, float low);
void letterGrade(float numCorrect, float howMany);
float percentage(float numCorrect, float howMany);
void displayProblem(float num1, float num2, char pick);
float getAnswer(float num1, float num2, char pick, const vector<string>& positives, const vector<string>& negatives);
void displayPositive(const vector<string>& positives);
void displayNegative(const vector<string>& negatives);

int main()
{
    float high = 0;
	float low = 0;
	float howMany = 0;
	float numLow = 1;
	float numHigh = 10;
	float numCorrect = 0;
	char pick;
	char negative;

	vector<char> choices;
	choices.push_back('a');
	choices.push_back('s');
	choices.push_back('m');
	
    vector<string> positives;
	positives.push_back("Great!");
	positives.push_back("Good job!");
	positives.push_back("You are on the right track.");
	positives.push_back("Look how well you are doing.");
	positives.push_back("Fantastic!");
	positives.push_back("Wonderful!");
	positives.push_back("Feel good about a job well done.");

	vector<string> negatives;
	negatives.push_back("Keep trying.");
	negatives.push_back("Next time is the time you will get it. If not, keep trying.");
	negatives.push_back("Practice improves your chances of getting it right.");
	negatives.push_back("Improvement comes step by step.");
	negatives.push_back("The harder you try, the better you will get.");
	negatives.push_back("Giving up makes it harder to learn. Keep going.");
	negatives.push_back("I think I can. I think I can. I KNOW I can!");
	negatives.push_back("Failure is just another opportunity to try again.");

	welcome();
	cout << endl << endl;

    do
    {
    cout << endl;
    low = askNumber("What is the lowest number you would like to use?");
	high = askNumber("What is the highest number you would like to use?");

    while (!(isLegalHighLow(high, low)))
    {
    	cout << "\nPlease remember the high number must be greater than the low number.\n\n";
        low = askNumber("What is the lowest number you would like to use?");
        high = askNumber("What is the highest number you would like to use?");
    } // end while

    // ask for which option the user wants
    pick = askChoice("Would you like to add, subtract or multiply?", choices);
    
    if (pick == 's')
    {
       negative = askYesNo("\nDo you want negative numbers?");
       cout << endl;
    }
	// ask for how many problems in the set
	howMany = askNumber("How many problems would you like for this round?");

    while (!(isLegalHowMany(numHigh, numLow, howMany)))
    {
    	cout << "\nPlease choose a number from " << numLow << " to " << numHigh << ".\n\n";
        howMany = askNumber("How many problems would you like for this round?");
    } // end while
    
    srand(time(0));
    
    for (int i = 1; i <= howMany; ++i)
    {
       // get two numbers in range
       float num1 = random(high, low);
       float num2 = random(high, low);
       
       // check for subtraction
       if (negative == 'n')
       {
          // check for possible negative answers in subtraction problems
          if (num1 < num2)
          {
             // if negative answer, then switch numbers
             float temp = num1;
             num1 = num2;
             num2 = temp;
          }
       }
       
       // display problem with two numbers and operand
       displayProblem(num1, num2, pick);
       cout << endl;
       
       // get answer
       // decide if answer is correct
       // if correct on first try, increment numCorrect
       numCorrect = numCorrect + getAnswer(num1, num2, pick, positives, negatives);
       
       
    }; // end for loop
    
    // display results of problem set
    cout << "\nYou got " << numCorrect << " out of " << howMany << " correct.\n\n";
    cout << "Your percentage correct is " << percentage(numCorrect, howMany);
    cout << "%.\n\n";
    } while (askYesNo("\nWould you like another round?") == 'y');
	// (problemSet function - high, low, opperator, howMany)
	// take high, low, opperator and how many to make the random problems
	// have (display function - num1, num2, opperator) show the problems
	// check to see if the answer is correct (isCorrect function - opperator, num1, num2)
	// (feedback function - correct y/n) give random positive/negative feedback as needed
	// give three chances for user to get the problem correct
	// after three chances, tell the user the correct answer and move on
	// keep track of how many problems are done correctly the FIRST time
	// (results function - numCorrect, howMany)give overall results of how many problems done correctly
	// give percentage of correct in seperate (percentage function - numCorrrect, howMany)
	// (letterGrade function - numCorrect, howMany) give OPTIONAL feedback on percentage matching to letter grades
  
  system("PAUSE");	
  return 0;
} // end main
// Functions:
//------------------------------------------------------------------------------
void welcome() // intro text only displayed at start of program
{
	cout << "\tWelcome to Random Math.\n\n";
	cout << "You will be asked for a low and high number. These numbers\n";
	cout << "will allow you to choose the range of numbers used in the \n";
	cout << "random math problems. You will also be asked to choose which\n";
	cout << "type of math problems you would like to do. The problems\n";
	cout << "will use only numbers in the range you selected.\n";
} // end welcome function
// ---------------------------------------------------
char askYesNo(string question) // allows open question for y/n answers
{
	char response;
	do
	{
		cout << question << " (y/n): "; // writes question and y/n prompt
		cin >> response;
	} while (response != 'y' && response != 'n'); // keep going until y or n is entered

	return response;
} // end askYesNo function
// ---------------------------------------------------
float askNumber(string question) // allows open question for numbers
{
    // floats can be entered, so positive, negative, and decimal
    // numbers are allowed
	float number;

	cout << question << " ";
	cin >> number;
	cout << endl;

	return number;
} // end askNumber function
// ---------------------------------------------------
char askChoice(string question, const vector<char>& choices)
{
	char response;
	vector<char>::const_iterator iter; // this will point to the items in a vector

	do
	{
		cout << question;
		cout << "\nYour choices are: ";

		// this is using the constant iterator to walk through the vecor
		for (iter = choices.begin(); iter != choices.end(); ++iter)
		     cout << *iter << " "; // this refers to the item in the vector

		cout << endl;
		cin >> response;

	} while (!isLegalChoice(response, choices)); // keep going until it is a valid choice

	return response;
} // end askChoice function
//-----------------------------------------------------
bool isLegalChoice(char response, const vector<char>& choices)
{
	// is the choice one of the menu options?
	for (int i = 0; i < choices.size(); ++i) // walk through the menu items
	{
		if (choices[i] == response)
		    return true; // yes, it is part of the menu
	}

	return false; // not part of the menu
} // end isLegalChoice function
//-----------------------------------------------------
bool isLegalHighLow(float high, float low)
{
	return (high > low);
} // end isLegalHighLow function
//-----------------------------------------------------
bool isLegalHowMany(float numHigh, float numLow, float howMany)
{
	return (howMany >= numLow && howMany <= numHigh);
} // end isLegalHowMany function
//-----------------------------------------------------
float sum(float num1, float num2) // add
{
	return (num1 + num2);
} // end sum function
//-----------------------------------------------------
float difference(float num1, float num2) // subtract
{
	return (num1 - num2);
} // end difference function
//-----------------------------------------------------
float product(float num1, float num2) // multiply
{
	return (num1 * num2);
} // end product function
//-----------------------------------------------------
float quotient(float num1, float num2) // divide
{
	if (num2 != 0) // check for dividing by zero
	    return (num1 / num2);

	else
	{
		cout << "\nIt is illegal to divide by zero.\n";
		return 0;
	}
} // end quotient function
//-----------------------------------------------------
float average(float num1, float num2) // computes average of two numbers
{
	return ((num1 + num2) / 2);
} // end average function
//-----------------------------------------------------
void result(char pick, float num1, float num2)
{
	cout << "Your answer is:\n";

	// give a different result for each menu item
	switch (pick)
	{
		case 'a':
		        cout << num1 << " + " << num2 << " = " << sum(num1, num2);
		        break;

		case 's':
		        cout << num1 << " - " << num2 << " = " << difference(num1, num2);
		        break;

		case 'm':
		        cout << num1 << " * " << num2 << " = " << product(num1, num2);
		        break;

		case 'd':
		        if (num2 != 0)
		            cout << num1 << " / " << num2 << " = " << quotient(num1, num2);
		        break;

		case 'v':
		        cout << "The average of " << num1 << " and " << num2 << " is ";
		        cout << average(num1, num2);
		        break;

		default:
		        cout << "You made an illegal choice.\n"; // just here for syntax, it should never run
	} // end switch
} // end result function
//--------------------------------------------------------------------
float random(float high, float low) // chooses numbers to be used in problems
{
	vector<float>::const_iterator iter;
	
    // fill vector with possible number choices
    vector<float> numbers;
    for (int i = static_cast<int>(low); i <= static_cast<int>(high); ++i)
    {
       numbers.push_back(i);
    } // end for (filling vector with choices)

    random_shuffle(numbers.begin(), numbers.end());
    iter = numbers.begin();

	return *iter;
} // end random function
//--------------------------------------------------------------------
void letterGrade(float numCorrect, float howMany) // assigns letter grade to percentage score
{
	if ((numCorrect/howMany) >= .9)
	{
	   cout << percentage(numCorrect, howMany);
	   cout << " Gives you an A.";
	}

	else if ((numCorrect/howMany) >= .8)
	{
	   cout << percentage(numCorrect, howMany);
	   cout << " Gives you a B.";
	}

	else if ((numCorrect/howMany) >= .7)
	{
	   cout << percentage(numCorrect, howMany);
	   cout << " Gives you a C.";
	}

	else if ((numCorrect/howMany) >= .6)
	{
	   cout << percentage(numCorrect, howMany);
	   cout << " Gives you a D.";
	}

	else
	{
	   cout << percentage(numCorrect, howMany);
	   cout << " Gives you an F.";
	}
} // end letterGrade function
//--------------------------------------------------------------------
float percentage(float numCorrect, float howMany)
{
	return ((numCorrect/howMany) * 100);
} // end percentage function
//--------------------------------------------------------------------
void displayProblem(float num1, float num2, char pick) // show problem
{
   // give a different result for each menu item
	switch (pick)
	{
		case 'a':
		        cout << num1 << " + " << num2 << " = ";
		        break;

		case 's':
		        cout << num1 << " - " << num2 << " = ";
                break;

		case 'm':
		        cout << num1 << " x " << num2 << " = ";
		        break;

		case 'd':
		        if (num2 != 0)
		            cout << num1 << " / " << num2 << " = ";
		        break;

		case 'v':
		        cout << "The average of " << num1 << " and " << num2 << " is ";
		        break;

		default:
                // just here for syntax, it should never run
		        cout << "This operator is not supported.\n"; 
	} // end switch
} // end displayProblem function
//--------------------------------------------------------------------
float getAnswer(float num1, float num2, char pick, const vector<string>& positives, const vector<string>& negatives)
{
   float answer;
   cout << "\nEnter your answer: ";
   cin >> answer;
   
    // give a different result for each menu item
	switch (pick)
	{
		case 'a':
		        if (answer == sum(num1, num2))
                {
                   cout << "Yeah!";
                   // displayPositive(positives); // random positive comment
                   cout << endl;
                   return 1;
                }
                else
                {
                   cout << "Oops...";
                   // displayNegative(negatives); // random negative comment
                   cout << endl;
                   cout << num1 << " + " << num2 << " = " << sum(num1, num2);
                   cout << endl << endl;
                   return 0;
                };     
          
		        break;

		case 's':
		        if (answer == difference(num1, num2))
                {
                   cout << "Yeah!";
                   // displayPositive(positives);
                   cout << endl;
                   return 1;
                }
                else
                {
                   cout << "Oops...";
                   // displayNegative(negatives);
                   cout << endl;
                   cout << num1 << " - " << num2 << " = " << difference(num1, num2);
                   cout << endl << endl;
                   return 0;
                };     
          
		        break;

		case 'm':
		        if (answer == product(num1, num2))
                {
                   cout << "Yeah!";
                   // displayPositive(positives);
                   cout << endl;
                   return 1;
                }
                else
                {
                   cout << "Oops!";
                   // displayNegative(negatives);
                   cout << endl;
                   cout << num1 << " x " << num2 << " = " << product(num1, num2);
                   cout << endl << endl;
                   return 0;
                };     
          
		        break;

		case 'd':
		        if (num2 != 0)
		            cout << num1 << " / " << num2 << " = ";
		        break;

		case 'v':
		        cout << "The average of " << num1 << " and " << num2 << " is ";
		        break;

		default:
                // just here for syntax, it should never run
		        cout << "This operator is not supported.\n"; 
	} // end switch   

} // end getAnswer function
//--------------------------------------------------------------------
void displayPositive(const vector<string>& positives) // displays random positives
{
	int randNumber = rand();

	int number = (randNumber % positives.size()) + 1;

	cout << positives[number] << endl;
} // end displayPositive function
//-------------------------------------------------------------------
void displayNegative(const vector<string>& negatives) // displays random negatives
{
	int randNumber = rand();

	int number = (randNumber % negatives.size()) + 1;

	cout << negatives[number] << endl;
} // end displayNegative function
//-------------------------------------------------------------------