// lab3.cpp sample solution for CSCI 159, fall 2026
// Author: D. Wessels
// This program estimates how many marbles, ping pong balls, and tennis balls
// could fit into a container whose name and size is specified by the user.

// iostream/manip needed for input/output and formatting,
// string needed for holding container names
#include <iostream>
#include <iomanip>
#include <string>
using std::cin;
using std::cout;
using std::cerr;
using std::endl;
using std::setw;
using std::fixed;
using std::setprecision;
using std::string;

// constants estimating how many of item X fit in a one litre container
const float MarblesPerL = 78.0f;
const float PingPongPerL = 20.5f;
const float TennisPerL = 3.7f;

// constant fraction for the expected variation in how many of an item fit
const float Variability = 0.1f;  // i.e +/- 10%

// assumed maximum length of a line of input
const int LineSize = 96;

// welcome() gives the user an introduction to our program
void welcome();

// getCName() prompts the user to enter a container name, reads and returns it
string getCName();

// getCSize(containerName) prompts the user to enter the size of the specified
//     container type in litres, then reads and returns the value
float getCSize(string containerName);

// displayFit(containerName, containerSize)
//     for the named container and its size, calculates and displays
//     the range of how many marbles, ping pong balls, and tennis balls
//     will fit in the container with the results columnized as shown
//     below (1 digit after the decimal point, width 6 for the column)
// example for displayFit("carton", 1.5):
// A carton of size 1.5 litres holds
//  105.3 to  128.7 small marbles
//   27.7 to   33.8 ping pong balls
//    5.0 to    6.1 tennis balls
void displayFit(string containerName, float containerSize);

int main()
{
   // storage for the name and size of the container
   string cname;
   float csize;

   // display overall program instructions
   welcome();

   // get the container name and size, then process the container
   cname = getCName();
   csize = getCSize(cname);
   displayFit(cname, csize);

   cout << "\nThanks for using HowManyFit!" << endl << endl;
   return 0;
}

void welcome()
{
   // display basic program overview
   cout << "\nWelcome to HowManyFit!\n\n";
   cout << "We estimate how many (small) marbles, ping pong balls, and tennis balls\n";
   cout << "fit in a container whose size you specify.\n" << endl;
}

string getCName()
{
   // ask for and return a one-word container name
   string contName;
   cout << "\nPlease enter a name for the container (as a single word): ";
   cin >> contName;
   return contName;
}

float getCSize(string containerName)
{
   // get a potential container size from the user
   float contSize = 0;
   cout << "Please enter the size of a(n) " << containerName << " in litres (e.g. 4.6): ";
   cin >> contSize;

   // if they entered something non-numeric
   //    we need to clear cin's fail state, remove the bad input,
   //    and call the function again to retry
   if (cin.fail()) {
      cin.clear();
      cin.ignore(LineSize, '\n');
      cerr << "Sorry, that was not a valid number, please try again" << endl;
      contSize = getCSize(containerName);
   }

   // if they gave a negative size then we need to call the function again to retry
   else if (contSize <= 0) {
      cerr << "Sorry, the size must be a positive number, please try again" << endl;
      contSize = getCSize(containerName);
   }

   // return the final (valid) size
   return contSize;
}


void displayFit(string containerName, float containerSize)
{
   // determine the range of marbles that will fit
   float minMarbles, maxMarbles;
   minMarbles = (1-Variability) * (containerSize*MarblesPerL);
   maxMarbles = (1+Variability) * (containerSize*MarblesPerL);

   // determine the range of ping pong balls that will fit
   float minPingPong, maxPingPong;
   minPingPong = (1-Variability) * (containerSize*PingPongPerL);
   maxPingPong = (1+Variability) * (containerSize*PingPongPerL);

   // determine the range of tennis balls that will fit
   float minTennis, maxTennis;
   minTennis = (1-Variability) * (containerSize*TennisPerL);
   maxTennis = (1+Variability) * (containerSize*TennisPerL);

   // display the results, formatted to a fixed column width and precision after decimal point
   const int colWidth = 6;
   const int displayDecimals = 1;
   cout << "\nA " << containerName << " of size " << containerSize << " litres holds" << endl;
   cout << fixed << setprecision(displayDecimals);
   cout << setw(colWidth) << minMarbles << " to " << setw(colWidth) << maxMarbles
        << " small marbles" << endl;
   cout << setw(colWidth) << minPingPong << " to " << setw(colWidth) << maxPingPong
        << " ping pong balls" << endl;
   cout << setw(colWidth) << minTennis << " to " << setw(colWidth) << maxTennis
        << " tennis balls" << endl;
}

