Author Topic: Student Type Problems ... Compare example solutions coded in Python 3, C++ and C  (Read 28794 times)

Offline David

  • Hero Member
  • *****
  • Posts: 647
    • View Profile
These space is reserved for example solutions to common Student Type programming problems ...

Here, the goal is to see how one might code student type solutions in C++ code, compared to C code, compared to Python 3 code ...

(and to compare the processing speeds where time would be significant.)

Let's start with a very common C++ Student Grades program where the data for each program is input from a file, we will call ...

grades.txt

structured like this:
Code: [Select]
// example of file structure ...
/*
4 7
Sam : Smith-Jones : 10 : 52 : 99 : 70 : 90  : 88  : 77
Julie  :  Anders Johnston : 80 : 52 : 33 : 70 : 90 : 44 : 99
Zorman Storman : Davies : 0 : 52
Omar Gomar : Davies : 100 : 100 : 100 : 100 : 100 : 100 : 100
*/

Here is a first go ... using a struct that seems a suitable match to the file structure ...

i.e. each struct holds each line of data, First Name, Last Name and up to 7 integer scores, each out of 100 max

BUT NOTE: the first line in the data file holds two integers separated by 'white space'.  The first integer lets the program know how many lines of student data follow.  The 2nd integer lets the program know the max number of scores to be stored in memory for each student.

Also note: we are using the STL vector to hold our student data struct's, and a library sort to sort by names and average grade.

A slightly advanced feature here would be using static variables in the student struct ...

1. to store (one time only) the max number of student scores to be handled for each student record
2. to store (one time only) the max length of all the student names, to 'make pretty the alignment' of the output

... and a vector of integers inside each struct, to hold the grades for each student data struct

Also using functions (methods) in the struct that pertain to processing the data stored in each student struct

Also, note our coded compare functions passed to the library sort function, to specify how to do each desired sort, here by descending average mark and also by ascending student last name, first name.

Code: [Select]
// gradesCpp.cpp // 2013-02-03 //

#include <string>
#include <fstream>
#include <sstream> // re. istringstream obj's to parse line
#include <iostream>
#include <iomanip>
#include <vector>
#include <algorithm> // re. sort ...

using namespace std;

const string FNAME = "grades.txt";
// example of file structure ...
/*
4 7
Sam : Smith-Jones : 10 : 52 : 99 : 70 : 90  : 88  : 77
Julie  :  Anders Johnston : 80 : 52 : 33 : 70 : 90 : 44 : 99
Zorman Storman : Davies : 0 : 52
Omar Gomar : Davies : 100 : 100 : 100 : 100 : 100 : 100 : 100
*/


struct Student
{
    string fname, lname;
    static int max_len;
    static int num_tests;
    double avg;
    vector< int > grades;
   
    Student() // default constructor ...
    {
        avg = 0;
        grades.reserve( num_tests );
    }
   
    void show()
    {
        cout << left << setw(max_len+5) << setfill('.')
             << (lname + ", " + fname) << " ";
       
        cout << setfill(' ') << right;
        for( unsigned i = 0; i < grades.size(); ++ i )
        {
            cout << setw(3) << grades[i] << ' ';
        }
        cout << " Average = " << setprecision(1) << fixed << setw(5)
             << avg << endl;
    }
   
    void cal_set_avg()
    {
        //avg = 0; // set by (default) constructor above //
        unsigned i;
        for( i = 0; i < grades.size(); ++ i )
        {
            avg += grades[i];
        }
        if( i ) avg /= num_tests;
    }
} ;

// initial static variables used in above struct here ...
int Student::max_len = 0;
int Student::num_tests = 0;


// these next compare functions are used by sort algorithm
bool cmpAvg( const Student& a, const Student& b )
{
    return a.avg > b.avg;
}
bool cmpName( const Student& a, const Student& b )
{
    if( a.lname == b.lname ) return a.fname < b.fname;
    return a.lname < b.lname;
}


void rstrip( string& s )
{
    int len = s.size();
    while( len && s[--len] == ' ' );
    s.erase(len+1);
}
void lstrip( string& s )
{
    int len = s.size(), count = 0;
    while( len && s[count] == ' ' ) --len, ++count;
    s.erase( 0, count );
}
void strip( string& s )
{
    int len = s.size(), count = 0;
    while( len && s[--len] == ' ' );
    s.erase(len+1);
    while( len && s[count] == ' ' ) --len, ++count;
    s.erase( 0, count );
}

void fillFromFile( ifstream& fin, vector< Student >& v )
{
    cout << "File dump ..." << endl;

    int numStuds;
    fin >> numStuds >> Student::num_tests;
    cout << numStuds << ' ' << Student::num_tests << endl;

    string line;
    getline( fin, line ); // get to end of line ...

    v.reserve( numStuds ); // reserve dynamic memory for numStuds Students
    while( getline( fin, line ) )
    {
        cout << line << endl;
       
        // get fname and lname ('stripped' of end spaces) ...
        istringstream iss( line );
        Student s;
        getline( iss, s.fname, ':' );
        rstrip( s.fname );
        getline( iss, s.lname, ':' );
        strip( s.lname );

        int lenl = s.lname.size();
        int lenf = s.fname.size();
        if( lenl+lenf > Student::max_len )
            Student::max_len = lenl+lenf; // update max_len

        // get all grades for this student record ...
        string tmp;
        while( getline( iss, tmp, ':' ) )
        {
            // convert string tmp to int g ...
            istringstream issg( tmp );
            int g;
            issg >> g;
            s.grades.push_back( g ); // append this 'g' to this Student's grades
        }
        s.cal_set_avg(); // calculate and set this student average

        // ok ... append this Student record ...
        v.push_back( s );
    }
}




int main()
{
    vector< Student > studs; // create empty vector to hold 'Student' objects
   
    ifstream fin( FNAME.c_str() ); // recall must use 'C type' of string here
    if( fin )
    {
        fillFromFile( fin, studs );
        cout << "\nVector dump ..." << endl;
        unsigned i;
        for( i = 0; i < studs.size(); ++i ) studs[i].show();

        sort( studs.begin(), studs.end(), cmpName );
       
        cout << "\nSorted by name... " << endl;
        for( i = 0; i < studs.size(); ++i ) studs[i].show();
       
        sort( studs.begin(), studs.end(), cmpAvg );
       
        cout << "\nSorted by avg... " << endl;
        for( i = 0; i < studs.size(); ++i ) studs[i].show();
       
        fin.close();
    }
   
    cout << "\nPress 'Enter' to continue/exit ... " << flush;
    string line;
    getline( cin, line );
}
« Last Edit: February 10, 2013, 01:57:04 PM by David »

Offline David

  • Hero Member
  • *****
  • Posts: 647
    • View Profile
Now the above ... but as a class ...

Code: [Select]
// gradesClass.cpp // 2013-02-10 //

#include <string>
#include <fstream>
#include <sstream> // re. istringstream obj's to parse line
#include <iostream>
#include <iomanip>
#include <vector>
#include <algorithm> // re. sort ...

using namespace std;

const string FNAME = "grades.txt";
// example of file structure ...
/*
4 7
Sam : Smith-Jones : 10 : 52 : 99 : 70 : 90  : 88  : 77
Julie  :  Anders Johnston : 80 : 52 : 33 : 70 : 90 : 44 : 99
Zorman Storman : Davies : 0 : 52
Omar Gomar : Davies : 100 : 100 : 100 : 100 : 100 : 100 : 100
*/


class Student
{
    string fname, lname;
    double avg;
    vector< int > grades;
   
    // these next compare functions are used by sort algorithm
    friend bool cmpAvg( const Student& a, const Student& b );
    friend bool cmpName( const Student& a, const Student& b );

public:
    static int max_len;
    static int num_tests;

    Student() // default constructor ...
    {
        avg = 0;
        grades.reserve( num_tests );
    }
   
    void show()
    {
        cout << left << setw(max_len+5) << setfill('.')
             << (lname + ", " + fname) << " ";
       
        cout << setfill(' ') << right;
        for( unsigned i = 0; i < grades.size(); ++ i )
        {
            cout << setw(3) << grades[i] << ' ';
        }
        cout << " Average = " << setprecision(1) << fixed << setw(5)
             << avg << endl;
    }
   
    void cal_set_avg()
    {
        //avg = 0; // set by (default) constructor above //
        unsigned i;
        for( i = 0; i < grades.size(); ++ i )
        {
            avg += grades[i];
        }
        if( i ) avg /= num_tests;
    }

    void set_fname( string s ) { fname = s; }
    void set_lname( string s ) { lname = s; }
    void set_avg( double a ) { avg = a; }
    void push_back_grade( int g ) { grades.push_back( g ); }
} ;

// initial static variables used in above struct here ...
int Student::max_len = 0;
int Student::num_tests = 0;

bool cmpAvg( const Student& a, const Student& b )
{
    return a.avg > b.avg;
}
bool cmpName( const Student& a, const Student& b )
{
    if( a.lname == b.lname ) return a.fname < b.fname;
    return a.lname < b.lname;
}


void rstrip( string& s )
{
    int len = s.size();
    while( len && s[--len] == ' ' );
    s.erase(len+1);
}
void lstrip( string& s )
{
    int len = s.size(), count = 0;
    while( len && s[count] == ' ' ) --len, ++count;
    s.erase( 0, count );
}
void strip( string& s )
{
    int len = s.size(), count = 0;
    while( len && s[--len] == ' ' );
    s.erase(len+1);
    while( len && s[count] == ' ' ) --len, ++count;
    s.erase( 0, count );
}

void fillFromFile( ifstream& fin, vector< Student >& v )
{
    cout << "File dump ..." << endl;

    int numStuds;
    fin >> numStuds >> Student::num_tests;
    cout << numStuds << ' ' << Student::num_tests << endl;

    string line;
    getline( fin, line ); // get to end of line ...

    v.reserve( numStuds ); // reserve dynamic memory for numStuds Students
    while( getline( fin, line ) )
    {
        cout << line << endl;
       
        // get fname and lname ('stripped' of end spaces) ...
        istringstream iss( line );
        Student s;

        string tmp;
        getline( iss, tmp, ':' );
        rstrip( tmp );
        int lenf = tmp.size();
        s.set_fname( tmp );

        getline( iss, tmp, ':' );
        strip( tmp );
        int lenl = tmp.size();
        s.set_lname( tmp );
       
        if( lenl+lenf > Student::max_len )
            Student::max_len = lenl+lenf; // update max_len

        // get all grades for this student record ...
        while( getline( iss, tmp, ':' ) )
        {
            // convert string tmp to int g ...
            istringstream issg( tmp );
            int g;
            issg >> g;
            s.push_back_grade( g ); // append this 'g' to this Student's grades
        }
        s.cal_set_avg(); // calculate and set this student average

        // ok ... append this Student record ...
        v.push_back( s );
    }
}




int main()
{
    vector< Student > studs; // create empty vector to hold 'Student' objects
   
    ifstream fin( FNAME.c_str() ); // recall must use 'C type' of string here
    if( fin )
    {
        fillFromFile( fin, studs );
        cout << "\nVector dump ..." << endl;
        unsigned i;
        for( i = 0; i < studs.size(); ++i ) studs[i].show();

        sort( studs.begin(), studs.end(), cmpName );
       
        cout << "\nSorted by name... " << endl;
        for( i = 0; i < studs.size(); ++i ) studs[i].show();
       
        sort( studs.begin(), studs.end(), cmpAvg );
       
        cout << "\nSorted by avg... " << endl;
        for( i = 0; i < studs.size(); ++i ) studs[i].show();
       
        fin.close();
    }
   
    cout << "\nPress 'Enter' to continue/exit ... " << flush;
    string line;
    getline( cin, line );
}

Offline David

  • Hero Member
  • *****
  • Posts: 647
    • View Profile
Now ... just to show how it could be done, using dynamic memory, and getting new memory as we need it, instead of using the STL vector container.  But see all the extra code we need to write here, and the extra care to delete our dynamic memory, when done with it, to avoid memory leak.

Note in particular, because we are using dynamic memory in our struct, we need to code for a destructor, a copy constructor and an overloaded assignment operator ...

We are just using a struct here, to start ... (later, we will show this using a class)

Code: [Select]
// gradesAry.cpp // 2013-02-02 //

// using dynamic memory and arrays //

#include <string>
#include <fstream>
#include <sstream> // re. istringstream obj's to parse line
#include <iostream>
#include <iomanip>
#include <algorithm> // re. sort ...

using namespace std;

const string FNAME = "grades.txt";
// example of file structure ...
/*
4 7
Sam : Smith-Jones : 10 : 52 : 99 : 70 : 90  : 88  : 77
Julie  :  Anders Johnston : 80 : 52 : 33 : 70 : 90 : 44 : 99
Zorman Storman : Davies : 0 : 52
Omar Gomar : Davies : 100 : 100 : 100 : 100 : 100 : 100 : 100
*/


struct Student
{
    string fname, lname;
    static int max_len;
    static int max_num_tests;
    int num_tests;
    int* grades;
    double avg;
   
    Student() // default constructor ...
    {
        num_tests = 0;
        grades = new int[ max_num_tests ];
        avg = 0;
    }
    ~Student() // destructor
    {
        delete [] grades;
    }
    Student( const Student& s) // copy constructor
    {
            fname = s.fname;
            lname = s.lname;
            num_tests = s.num_tests;
            grades = new int[ num_tests ];
            for( int i = 0; i < num_tests; ++i ) grades[i] = s.grades[i];
            avg = s.avg;
    }
    Student& operator = ( const Student& s) // overloaded =
    {
        if( this != &s )
        {
            fname = s.fname;
            lname = s.lname;
            num_tests = s.num_tests;
            delete [] grades;
            grades = new int[ num_tests ];
            for( int i = 0; i < num_tests; ++i ) grades[i] = s.grades[i];
            avg = s.avg;
        }
        return *this;
    }
   
    void show()
    {
        cout << left << setw(max_len+5) << setfill('.')
             << (lname + ", " + fname) << " ";
       
        cout << setfill(' ') << right;
        for( int i = 0; i < num_tests; ++ i )
        {
            cout << setw(3) << grades[i] << ' ';
        }
        cout << " Average = " << setprecision(1) << fixed << setw(5)
             << avg << endl;
    }
   
    void cal_set_avg()
    {
        //avg = 0; // set by (default) constructor above //
        int i;
        for( i = 0; i < num_tests; ++ i )
        {
            avg += grades[i];
        }
        if( i ) avg /= max_num_tests;
    }
} ;

// initial static variables used in above struct here ...
int Student::max_len = 0;
int Student::max_num_tests = 0;


// these next compare functions are used by sort algorithm
bool cmpAvg( const Student& a, const Student& b )
{
    return a.avg > b.avg;
}
bool cmpName( const Student& a, const Student& b )
{
    if( a.lname == b.lname ) return a.fname < b.fname;
    return a.lname < b.lname;
}


void rstrip( string& s )
{
    int len = s.size();
    while( len && s[--len] == ' ' );
    s.erase(len+1);
}
void lstrip( string& s )
{
    int len = s.size(), count = 0;
    while( len && s[count] == ' ' ) --len, ++count;
    s.erase( 0, count );
}
void strip( string& s )
{
    int len = s.size(), count = 0;
    while( len && s[--len] == ' ' );
    s.erase(len+1);
    while( len && s[count] == ' ' ) --len, ++count;
    s.erase( 0, count );
}

int fillFromFile( ifstream& fin, Student*& v )
{
    cout << "File dump ..." << endl;

    int numStuds, count = 0;
    fin >> numStuds >> Student::max_num_tests;
    cout << numStuds << ' ' << Student::max_num_tests << endl;

    string line;
    getline( fin, line ); // get to end of line ...

    v = new Student[numStuds]; // get dynamic memory for numStuds Students

    while( count < numStuds && getline( fin, line ) )
    {
        cout << line << endl;
       
        // get fname and lname ('stripped' of end spaces) ...
        istringstream iss( line );
        Student s;
        getline( iss, s.fname, ':' );
        rstrip( s.fname );
        getline( iss, s.lname, ':' );
        strip( s.lname );

        int lenl = s.lname.size();
        int lenf = s.fname.size();
        if( lenl+lenf > Student::max_len )
            Student::max_len = lenl+lenf; // update max_len

        // get all grades for this student record ...
        string tmp;
        int testNum = 0;
        while( testNum < Student::max_num_tests && getline( iss, tmp, ':' ) )
        {
            // convert string tmp to int g ...
            istringstream issg( tmp );
            int g;
            issg >> g;
            s.grades[testNum] = g ; // update this 'g' to this Student's grades
            ++testNum;
        }
       
        s.num_tests = testNum; // update for this student

        s.cal_set_avg(); // calculate and set this student average

        // ok ... update with this Student record ...
        v[count] = s;
        ++count;
    }
    return count;
}




int main()
{
    Student* studs; // create pointer to 'Student' objects
   
    ifstream fin( FNAME.c_str() ); // recall must use 'C type' of string here
    if( fin )
    {
        int size = fillFromFile( fin, studs );

        cout << "\nVector dump ..." << endl;
        int i;
        for( i = 0; i < size; ++i ) studs[i].show();

        sort( studs, studs+size, cmpName );
       
        cout << "\nSorted by name... " << endl;
        for( i = 0; i < size; ++i ) studs[i].show();
       
        sort( studs, studs+size, cmpAvg );
       
        cout << "\nSorted by avg... " << endl;
        for( i = 0; i < size; ++i ) studs[i].show();
       
        fin.close();
       
        delete [] studs;
    }
   
    cout << "\nPress 'Enter' to continue/exit ... " << flush;
    string line;
    getline( cin, line );
}
« Last Edit: February 10, 2013, 10:30:25 AM by David »

Offline David

  • Hero Member
  • *****
  • Posts: 647
    • View Profile
Now a little more efficient version of the above ...

Code: [Select]
// gradesAry2.cpp // 2013-02-02 //

// using dynamic memory and arrays //

#include <string>
#include <fstream>
#include <sstream> // re. istringstream obj's to parse line
#include <iostream>
#include <iomanip>
#include <algorithm> // re. sort ...

using namespace std;

const string FNAME = "grades.txt";
// example of file structure ...
/*
4 7
Sam : Smith-Jones : 10 : 52 : 99 : 70 : 90  : 88  : 77
Julie  :  Anders Johnston : 80 : 52 : 33 : 70 : 90 : 44 : 99
Zorman Storman : Davies : 0 : 52
Omar Gomar : Davies : 100 : 100 : 100 : 100 : 100 : 100 : 100
*/


struct Student
{
    string fname, lname;
    static int max_len;
    static int max_num_tests;
    int num_tests;
    int* grades;
    double avg;
   
    Student() // default constructor ...
    {
        num_tests = 0;
        grades = new int[ max_num_tests ];
        avg = 0;
    }
    ~Student() // destructor
    {
        delete [] grades;
    }
    Student( const Student& s) // copy constructor
    {
            fname = s.fname;
            lname = s.lname;
            num_tests = s.num_tests;
            grades = new int[ num_tests ];
            for( int i = 0; i < num_tests; ++i ) grades[i] = s.grades[i];
            avg = s.avg;
    }
    Student& operator = ( const Student& s) // overloaded =
    {
        if( this != &s )
        {
            fname = s.fname;
            lname = s.lname;
            num_tests = s.num_tests;
            delete [] grades;
            grades = new int[ num_tests ];
            for( int i = 0; i < num_tests; ++i ) grades[i] = s.grades[i];
            avg = s.avg;
        }
        return *this;
    }

    void show()
    {
        cout << left << setw(max_len+5) << setfill('.')
             << (lname + ", " + fname) << " ";
       
        cout << setfill(' ') << right;
        for( int i = 0; i < num_tests; ++ i )
        {
            cout << setw(3) << grades[i] << ' ';
        }
        cout << " Average = " << setprecision(1) << fixed << setw(5)
             << avg << endl;
    }
   
    void cal_set_avg()
    {
        //avg = 0; // set by (default) constructor above //
        int i;
        for( i = 0; i < num_tests; ++ i )
        {
            avg += grades[i];
        }
        if( i ) avg /= max_num_tests;
    }
} ;

// initial static variables used in above struct here ...
int Student::max_len = 0;
int Student::max_num_tests = 0;


// these next compare functions are used by sort algorithm
bool cmpAvg( const Student& a, const Student& b )
{
    return a.avg > b.avg;
}
bool cmpName( const Student& a, const Student& b )
{
    if( a.lname == b.lname ) return a.fname < b.fname;
    return a.lname < b.lname;
}


void rstrip( string& s )
{
    int len = s.size();
    while( len && s[--len] == ' ' );
    s.erase(len+1);
}
void lstrip( string& s )
{
    int len = s.size(), count = 0;
    while( len && s[count] == ' ' ) --len, ++count;
    s.erase( 0, count );
}
void strip( string& s )
{
    int len = s.size(), count = 0;
    while( len && s[--len] == ' ' );
    s.erase(len+1);
    while( len && s[count] == ' ' ) --len, ++count;
    s.erase( 0, count );
}

int fillFromFile( ifstream& fin, Student*& v )
{
    cout << "File dump ..." << endl;

    int numStuds,
        count = 0;
    fin >> numStuds >> Student::max_num_tests;
    cout << numStuds << ' ' << Student::max_num_tests << endl;

    string line;
    getline( fin, line ); // get to end of line ...

    v = new Student[numStuds]; // get dynamic memory for numStuds Students

    while( count < numStuds && getline( fin, line ) )
    {
        cout << line << endl;
       
        // get fname and lname ('stripped' of end spaces) ...
        istringstream iss( line );
        getline( iss, v[count].fname, ':' );
        rstrip( v[count].fname );
        getline( iss, v[count].lname, ':' );
        strip( v[count].lname );

        int lenl = v[count].lname.size();
        int lenf = v[count].fname.size();
        if( lenl+lenf > Student::max_len )
            Student::max_len = lenl+lenf; // update max_len

        // get all grades for this student record ...
        string tmp;
        int testNum = 0;
        while( testNum < Student::max_num_tests && getline( iss, tmp, ':' ) )
        {
            // convert string tmp to int g ...
            istringstream issg( tmp );
            int g;
            issg >> g;
            v[count].grades[testNum] = g ; // update this 'g' to this Student's grades
            ++testNum;
        }
       
        v[count].num_tests = testNum; // update for this student

        v[count].cal_set_avg(); // calculate and set this student average

        ++count;
    }
    return count;
}



int main()
{
    Student* studs; // create pointer to 'Student' objects
   
    ifstream fin( FNAME.c_str() ); // recall must use 'C type' of string here
    if( fin )
    {
        int size = fillFromFile( fin, studs );

        cout << "\nVector dump ..." << endl;
        int i;
        for( i = 0; i < size; ++i ) studs[i].show();

        sort( studs, studs+size, cmpName );

        cout << "\nSorted by name... " << endl;
        for( i = 0; i < size; ++i ) studs[i].show();

        sort( studs, studs+size, cmpAvg );
       
        cout << "\nSorted by avg... " << endl;
        for( i = 0; i < size; ++i ) studs[i].show();
       
        fin.close();
       
        delete [] studs;
    }
   
    cout << "\nPress 'Enter' to continue/exit ... " << flush;
    string line;
    getline( cin, line );
}

Offline David

  • Hero Member
  • *****
  • Posts: 647
    • View Profile
Now ... the above as a class (instead of a struct) ...

Code: [Select]
// gradesClassAry2.cpp // 2013-02-02 //

// using dynamic memory and arrays //

#include <string>
#include <fstream>
#include <sstream> // re. istringstream obj's to parse line
#include <iostream>
#include <iomanip>
#include <algorithm> // re. sort ...

using namespace std;

const string FNAME = "grades.txt";
// example of file structure ...
/*
4 7
Sam : Smith-Jones : 10 : 52 : 99 : 70 : 90  : 88  : 77
Julie  :  Anders Johnston : 80 : 52 : 33 : 70 : 90 : 44 : 99
Zorman Storman : Davies : 0 : 52
Omar Gomar : Davies : 100 : 100 : 100 : 100 : 100 : 100 : 100
*/


class Student
{
    string fname, lname;
    int num_tests;
    int* grades;
    double avg;
   
    // these next compare functions are used by sort algorithm
    friend bool cmpAvg( const Student& a, const Student& b );
    friend bool cmpName( const Student& a, const Student& b );

public:
    static int max_len_names;
    static int max_num_tests;

    Student() // default constructor ...
    {
        num_tests = 0;
        grades = new int[ max_num_tests ];
        avg = 0;
    }
    ~Student() // destructor
    {
        delete [] grades;
    }
    Student( const Student& s) // copy constructor
    {
            fname = s.fname;
            lname = s.lname;
            num_tests = s.num_tests;
            grades = new int[ num_tests ];
            for( int i = 0; i < num_tests; ++i ) grades[i] = s.grades[i];
            avg = s.avg;
    }
    Student& operator = ( const Student& s) // overloaded =
    {
        if( this != &s )
        {
            fname = s.fname;
            lname = s.lname;
            num_tests = s.num_tests;
            delete [] grades;
            grades = new int[ num_tests ];
            for( int i = 0; i < num_tests; ++i ) grades[i] = s.grades[i];
            avg = s.avg;
        }
        return *this;
    }

    void show()
    {
        cout << left << setw(max_len_names+5) << setfill('.')
             << (lname + ", " + fname) << " ";
       
        cout << setfill(' ') << right;
        for( int i = 0; i < num_tests; ++ i )
        {
            cout << setw(3) << grades[i] << ' ';
        }
        cout << " Average = " << setprecision(1) << fixed << setw(5)
             << avg << endl;
    }
   
    void cal_set_avg()
    {
        //avg = 0; // set by (default) constructor above //
        int i;
        for( i = 0; i < num_tests; ++ i )
        {
            avg += grades[i];
        }
        if( i ) avg /= max_num_tests;
    }
   
    void set_fname( string s ) { fname = s; }
    void set_lname( string s ) { lname = s; }
    void set_num_tests( int n ) { num_tests = n; }
    void set_avg( double a ) { avg = a; }
    void set_grade( int g, int i ) { grades[i] = g; }
} ;

// initial static variables used in above struct here ...
int Student::max_len_names = 0;
int Student::max_num_tests = 0;

bool cmpAvg( const Student& a, const Student& b )
{
    return a.avg > b.avg;
}
bool cmpName( const Student& a, const Student& b )
{
    if( a.lname == b.lname ) return a.fname < b.fname;
    return a.lname < b.lname;
}


void rstrip( string& s )
{
    int len = s.size();
    while( len && s[--len] == ' ' );
    s.erase(len+1);
}
void lstrip( string& s )
{
    int len = s.size(), count = 0;
    while( len && s[count] == ' ' ) --len, ++count;
    s.erase( 0, count );
}
void strip( string& s )
{
    int len = s.size(), count = 0;
    while( len && s[--len] == ' ' );
    s.erase(len+1);
    while( len && s[count] == ' ' ) --len, ++count;
    s.erase( 0, count );
}

int fillFromFile( ifstream& fin, Student*& v )
{
    cout << "File dump ..." << endl;

    int numStuds,
        count = 0;
    fin >> numStuds >> Student::max_num_tests;
    cout << numStuds << ' ' << Student::max_num_tests << endl;

    string line;
    getline( fin, line ); // get to end of line ...

    v = new Student[numStuds]; // get dynamic memory for numStuds Students

    while( count < numStuds && getline( fin, line ) )
    {
        cout << line << endl;
       
        // get fname and lname ('stripped' of end spaces) ...
        istringstream iss( line );
       
        string tmp;
        getline( iss, tmp, ':' );
        rstrip( tmp );
        int lenf = tmp.size();
        v[count].set_fname( tmp );

        getline( iss, tmp, ':' );
        strip( tmp );
        int lenl = tmp.size();
        v[count].set_lname( tmp );
       
        if( lenl+lenf > Student::max_len_names )
            Student::max_len_names = lenl+lenf; // update max_len_names

        // get all grades for this student record ...
        int testNum = 0;
        while( testNum < Student::max_num_tests && getline( iss, tmp, ':' ) )
        {
            // convert string tmp to int g ...
            istringstream issg( tmp );
            int g;
            issg >> g;
            v[count].set_grade( g, testNum ); // update this Student's grades
            ++testNum;
        }
       
        v[count].set_num_tests( testNum ); // update for this student

        v[count].cal_set_avg(); // calculate and set this student average

        ++count;
    }
    return count;
}



int main()
{
    Student* studs; // create pointer to 'Student' objects
   
    ifstream fin( FNAME.c_str() ); // recall must use 'C type' of string here
    if( fin )
    {
        int size = fillFromFile( fin, studs );

        cout << "\nVector dump ..." << endl;
        int i;
        for( i = 0; i < size; ++i ) studs[i].show();

        sort( studs, studs+size, cmpName );

        cout << "\nSorted by name... " << endl;
        for( i = 0; i < size; ++i ) studs[i].show();

        sort( studs, studs+size, cmpAvg );
       
        cout << "\nSorted by avg... " << endl;
        for( i = 0; i < size; ++i ) studs[i].show();
       
        fin.close();
       
        delete [] studs;
    }
   
    cout << "\nPress 'Enter' to continue/exit ... " << flush;
    string line;
    getline( cin, line );
}

Offline David

  • Hero Member
  • *****
  • Posts: 647
    • View Profile
Now the C version ...

Note: you will need to have the 3 files,

readLine.h,

found here ...
http://developers-heaven.net/forum/index.php/topic,2580.msg2864.html#msg2864

and ...

Clist.h,

found here ...
http://developers-heaven.net/forum/index.php?topic=2582.msg2877#msg2877

and ...

Clist_func's.h

found here ...
http://developers-heaven.net/forum/index.php?topic=2582.msg2883#msg2883


Code: [Select]
/* gradesC.c */ /* 2013-02-04 */

/* #include <time.h> */
#include "readLine.h"

#define FNAME "grades.txt"

int MAX_NUM_TESTS = 0;
int MAX_NAME_LEN = 0;

typedef struct myList       /*   need these   */
{
    char* fname;
    char* lname;
    int num_tests;
    int* grades;
    double avg;
    struct myList* next;
} List;

typedef List* pList;

void clearLrec( pList p )
{
    free( p->grades );
    free( p->lname );
    free( p->fname );
}                         /*   need these   */

/* now can include ... */
#include "Clist.h"
#include "Clist_func's.h"

double getAvg( int ary[], int size )
{
    double avg = 0.0;
    int i;
    for( i = 0; i < size; ++i ) avg += ary[i];
    if( i && MAX_NUM_TESTS )  avg = avg/MAX_NUM_TESTS;
    return avg;
}

int readFile( Clist* myLst )
{
    FILE* f = fopen( FNAME, "r" );
    int numStuds = 0;
    if( f )
    {
        List lst;
        int count = 0;
        char* line;
        char* p;
        while( (line = readLine( f )) != NULL )
        {
            printf( "%s\n", line );
           
            if( count == 0 )
            {
                p = strtok( line, " " );
                numStuds = atoi( p );
                p = strtok( NULL, " " );
                MAX_NUM_TESTS = atoi( p );
            }
            else
            {
                int num = 0, lenf, lenl;
                lst.grades = malloc( sizeof(int)*MAX_NUM_TESTS );
                myAssert((lst.grades!=NULL),"Error! malloc failed in readFile");
               
                p = strtok( line, ":" );
                while( p != NULL )
                {
                    if( num == 0 ) lst.fname = newCopy( p );
                    else if( num == 1 ) lst.lname = newCopy( p );
                    else lst.grades[num-2] = atoi( p );
                   
                    p = strtok( NULL, ":" );
                    ++ num;
                   
                    if( num == MAX_NUM_TESTS+2 ) break;
                }
               
                lst.num_tests = num-2;
                lst.avg = getAvg( lst.grades, lst.num_tests );
               
                rtrim( lst.fname );
                trim( lst.lname );
                push_backClist( myLst, &lst );

                lenf = strlen(lst.fname);
                lenl = strlen(lst.lname);
                if( lenf + lenl > MAX_NAME_LEN ) MAX_NAME_LEN = lenf+lenl;
            }
           
            free( line );
            ++count;
        }
       
        fclose( f );
    }
    return numStuds;
}

void printDots( int num )
{
    while( num-- ) putchar( '.' );
}

void showLrec( pList p )
{
    int i, lenl = strlen(p->lname), lenf = strlen(p->fname);
    printf( "%s, %s ", p->lname, p->fname );
    printDots( MAX_NAME_LEN + 3 - lenl - lenf );
   
    fputs( " Scores: ", stdout );
    for( i = 0; i < p->num_tests; ++ i ) printf( "%3d ", p->grades[i] );
    printf( " Avg: %5.1f\n", p->avg );
}

void showClist( Clist* lst )
{
    if( lst->size )
    {
pList p = lst->start;
for( ; p != NULL; p = p->next ) showLrec( p );
    }
    else puts( "The list is empty ... " );
}

int cmpAvg( const pList a, const pList b )
{
    return b->avg - a->avg;
}

int cmpName( const pList a, const pList b )
{
    int tmp = strcmp(a->lname, b->lname);
    if(  tmp != 0 ) return tmp;
    return strcmp(a->fname, b->fname);
}




int main() /* *************************************************************** */
{
    Clist studs;
    int numStuds;

    /* double t1 = clock(); */


    initClist( &studs );
   
    numStuds = readFile( &studs );
    /* printf( "Elapsed time = %.4f", (clock()-t1)/CLOCKS_PER_SEC ); */

    if( numStuds && numStuds == studs.size )
    {

        putchar( '\n' );
        showClist( &studs );

        puts( "\nSorted by names..." );
        msortClist( &studs, cmpName );
        /* printf( "Elapsed time = %.4f", (clock()-t1)/CLOCKS_PER_SEC ); */
        showClist( &studs );

        puts( "\nSorted by avg..." );
        msortClist( &studs, cmpAvg );
        /* printf( "Elapsed time = %.4f", (clock()-t1)/CLOCKS_PER_SEC ); */
        showClist( &studs );


        clearClist( &studs );
    }
    else printf( "\nThere was some error reading file \"%s\"\n"
                 "%d lines were read from file.\n"
                 "%d lines were supposed to be read from file.\n" ,
                 FNAME, studs.size, numStuds );
                 
    /* printf( "Elapsed time = %.4f", (clock()-t1)/CLOCKS_PER_SEC ); */
                 
    fputs( "\nPress 'Enter' to continue/exit ... ", stdout); fflush( stdout );
    getchar(); /* keep 'Window' open until 'Enter' key is pressed ... */
    return 0;
   
} /* ************************************************************************ */

« Last Edit: February 10, 2013, 12:19:11 PM by David »

Offline David

  • Hero Member
  • *****
  • Posts: 647
    • View Profile
And finally ... a quick Python version that uses a Python list as a struct ...

and a list of lists ...

to hold all the structs

Code: [Select]
# grades.py # 2013-02-17 #


FNAME = 'grades.txt'
# example file structure  #
'''
4 7
Sam : Smith-Jones : 10 : 52 : 99 : 70 : 90  : 88  : 77
Julie  :  Anders Johnston : 80 : 52 : 33 : 70 : 90 : 44 : 99
Zorman Storman : Davies : 0 : 52
Omar Gomar : Davies : 100 : 100 : 100 : 100 : 100 : 100 : 100
'''   

f = 0
try:
    f = open( FNAME, 'r' )
except:
    print( "Error trying to open file '" + FNAME + "'" )
   
if f != 0 :

    stud = []
    count = 0
    max_name_len = 0

    def getAvg( lst, num ):
        summ = 0.0
        for item in lst:
            summ += item
        if num:
            return summ/num
        return summ

    def printstud( s ):
        name = s[0] + ', ' + s[1] + ' '
        name = name + '.'*(max_name_len + 6 - len(name))
        print( name, end = '  Scores: ' )
        for item in s[3]:
            print( '{:3d}'.format( item ), end = ' '  )
        print( '  Avg: {:5.1f}'.format(s[2]) )


    for line in f:
        line = line.rstrip()
        print( line )
       
        if count == 0:
            stuff = line.split()
            numStuds = int( stuff[0] )
            numTests = int( stuff[1] )
        else:
            stuff = line.split( ':' )
            scores = []
           
            for i, item in enumerate(stuff):
                if i == 0:
                    fname = item.rstrip()
                elif i == 1:
                    lname = item.strip()
                else:
                    scores.append( int(item) )
                   
            avg = getAvg( scores, numTests )
            s = [ lname, fname, avg, scores ]   
            stud.append( s )

            if len(lname) + len(fname) > max_name_len:
                max_name_len = len(lname) + len(fname)
           
        count += 1

    f.close()
   
    print()
    for s in stud: printstud( s )

    from operator import itemgetter


    print( '\nSorted by name ...')
    stud.sort( key = itemgetter(1,0) ) # firstly sort by fname
    #stud.sort() # then sort by lname
    for s in stud: printstud( s )

   
    print( '\nSorted by average ...')
    stud.sort( key = itemgetter(2), reverse = True ) # sort by avg
    #stud.reverse()
    for s in stud: printstud( s )


input( "\nPress 'Enter' to continue/exit ... " )
« Last Edit: February 17, 2013, 09:25:48 AM by David »

Offline David

  • Hero Member
  • *****
  • Posts: 647
    • View Profile
Now ... using a Python class ...

Code: [Select]
# gradesClass.py # 2013-02-17 #


FNAME = 'grades.txt'
# example file structure  #
'''
4 7
Sam : Smith-Jones : 10 : 52 : 99 : 70 : 90  : 88  : 77
Julie  :  Anders Johnston : 80 : 52 : 33 : 70 : 90 : 44 : 99
Zorman Storman : Davies : 0 : 52
Omar Gomar : Davies : 100 : 100 : 100 : 100 : 100 : 100 : 100
'''

max_name_len = 0
max_num_scores = 0

class Grades( object ):
    def __init__( self, fname ='', lname ='', scores =[] ):
        self.fname = fname
        self.lname = lname
        self.scores = scores
    def getName( self ):
        return self.lname + ', ' + self.fname
    def getAvg( self ):
        summ = 0.0
        for item in self.scores:
            summ += item
        if max_num_scores:
            return summ/max_num_scores
        return summ
    def getStr( self ):
        name = self.getName()
        gstr = name + ' ' + '.'*(max_name_len + 5 - len(name))
        gstr = gstr + '  Scores: '
        for item in self.scores:
            gstr += ' {:3d}'.format( item )
        return gstr + '  Avg: {:5.1f}'.format( self.getAvg() )
       

f = 0
try:
    f = open( FNAME, 'r' )
except:
    print( "Error trying to open file '" + FNAME + "'" )
   
if f:

    studs = []
    count = 0
   
    for line in f:
        line = line.rstrip()
        print( line )
       
        if count == 0:
            stuff = line.split()
            num_studs = int( stuff[0] ) # not used in this program #
            max_num_scores = int( stuff[1] )
        else:
            stuff = line.split( ':' )
            scores = []

            i = 0
            for item in stuff:
                if i == 0:
                    fname = item.rstrip()
                elif i == 1:
                    lname = item.strip()
                else:
                    scores.append( int(item) )
                i += 1
                if i == max_num_scores:
                    break
                       
            studs.append( Grades( lname, fname, scores ) )

            if len(lname) + len(fname) > max_name_len:
                max_name_len = len(lname) + len(fname)
           
        count += 1

    f.close()
   
    print()
    for s in studs: print( s.getStr() )

    print( '\nSorted by name ...')
    studs.sort( key = Grades.getName )
    for s in studs: print( s.getStr() )

   
    print( '\nSorted by average ...')
    studs.sort( key = Grades.getAvg, reverse = True )
    for s in studs: print( s.getStr() )


input( "\nPress 'Enter' to continue/exit ... " )

Offline David

  • Hero Member
  • *****
  • Posts: 647
    • View Profile
And while we are 'at it' ... i.e. Python Classes ...

here are two more related little 'demo programs' that illustrate overloading (in Python 3) ...

Code: [Select]
# testClassIter.py # 2013-03-01 #

class Student( object ):
    count = 0
    def __init__(self, idNum =0, name ='', subjectMarks =()):
        self.idNum = idNum
        self.name = name
        self.subjectMarks = subjectMarks
        Student.count += 1
    def __str__(self):
        return 'ID: {:>2d}  NAME: {:<16s}  AVERAGE: {:>5.1f} \n{}'\
               .format(self.idNum,self.name,self.getAvg(),self.subjectMarks)
    def __iter__(self):
        self.index = 0
        return self
    def __next__(self):
        self.index += 1
        if self.index == 1: return self.idNum
        if self.index == 2: return self.name
        if self.index == 3: return self.subjectMarks
        raise StopIteration
    def __getitem__(self, index):
        if index == 0: return self.idNum
        if index == 1: return self.name
        if index == 2: return self.subjectMarks
       
    def __del__(self):
        Student.count -= 1
        print( '\n   del was called ...' )
       
    def getAvg(self):
        if self.subjectMarks:
            return sum(x[1] for x in \
                        self.subjectMarks)/len(self.subjectMarks)
        else:
            return 0
                           
       
s1 = Student( 1, 'Sam', (('Math',99),('History',77),('English',69)) )

studs = [s1]

print( 'Student.count =', Student.count )
print( s1 )

for i, item in enumerate(s1):
    if i < 2:
        print( item )
    else:
        for element in item:
            print( element[0], '=', element[1] );
print( 's1.getAvg() = {:.1f}'.format(s1.getAvg()) )

def addStud():
    s2 = Student( 2, 'Sally', (('Math',100),('History',100),('English',100)) )

    print( '\nStudent.count =', Student.count )
    print( s2 )

    for i, item in enumerate(s2):
        if i < 2:
            print( item )
        else:
            for element in item:
                print( element[0], '=', element[1] )
    print( 's2.getAvg() = {:.1f}'.format(s2.getAvg()) )
    #studs.append(s2)


addStud()


print( '\nStudent.count =', Student.count )

def add2():
    global studs
   
    studs.append( \
        Student(3, 'Ally', (('Linear Algebra',100), \
                            ('Calculus',100), ('Physics',100))) )
    studs.append( \
        Student(4, 'Ally', (('Linear Algebra',99), \
                            ('Calculus',98), ('Physics',88), ('Chem 201', 93))) )

    print( "\nSorting by avg's using "\
           "studs.sort( key = Student.getAvg, reverse = True )")
    studs.sort( key = Student.getAvg, reverse = True )
    for s in studs:
        print( s )

    print( "\nSorting by names using "\
           "studs.sort( key = itemgetter(1,0), reverse = True )")
    from operator import itemgetter
    studs.sort( key = itemgetter(1,0), reverse = True )
    print( '\nStudent.count =', Student.count )

    for s in studs:
        print( s )

    studs = studs[0:1]

add2()

print( '\nStudent.count =', Student.count )

input( "\nPress 'Enter' to continue/exit ... ")


Now ... using a Python dictionary ...

Code: [Select]
# testClassIterDict.py # 2013-03-01 #

class Student( object ):
    count = 0
    def __init__(self, idNum =0, name ='', subjectMarks ={}):
        self.idNum = idNum
        self.name = name
        self.subjectMarks = subjectMarks
        Student.count += 1
    def __str__(self):
        return 'ID: {:>2d}  NAME: {:<16s}  AVERAGE: {:>5.1f} \n{}'\
               .format(self.idNum,self.name,self.getAvg(),\
                       sorted(self.subjectMarks.items()))
    def __iter__(self):
        self.index = 0
        return self
    def __next__(self):
        self.index += 1
        if self.index == 1: return self.idNum
        if self.index == 2: return self.name
        if self.index == 3: return self.subjectMarks
        raise StopIteration
    def __getitem__(self, index):
        if index == 0: return self.idNum
        if index == 1: return self.name
        if index == 2: return self.subjectMarks
       
    def __del__(self):
        Student.count -= 1
        print( '\n   del was called ...' )
       
    def getAvg(self):
        if self.subjectMarks:
            return sum(v for v in self.subjectMarks.values()) \
                   /len(self.subjectMarks)
        else:
            return 0
                           
       
s1 = Student( 1, 'Sam', dict(Math=99, History=77, English=69) )

studs = [s1]

print( 'Student.count =', Student.count )
print( s1 )

for i, item in enumerate(s1):
    if i < 2:
        print( item )
    else:
        for k, v in sorted(item.items()):
            print( k, '=', v )
print( 's1.getAvg() = {:.1f}'.format(s1.getAvg()) )

def addStud():
    s2 = Student( 2, 'Sally', dict(Math =100, History =100, English =100) )
 
    print( '\nStudent.count =', Student.count )
    print( s2 )

    for i, item in enumerate(s2):
        if i < 2:
            print( item )
        else:
            for k, v in sorted(item.items()):
                print( k, '=', v )
    print( 's2.getAvg() = {:.1f}'.format(s2.getAvg()) )
    #studs.append(s2)


addStud()


print( '\nStudent.count =', Student.count )

studs.append( Student(3, 'Ally', dict(Linear_Algebra =100, Calculus =100, \
                                      Physics =100)) )
studs.append( Student(4, 'Ally', dict(Linear_Algebra =99, Calculus =98, \
                                      Physics =88, Chem_201 =93)) )

print( "\nSorting by avg's using "\
       "studs.sort( key = Student.getAvg, reverse = True )")
studs.sort( key = Student.getAvg, reverse = True )
for s in studs:
    print( s )

print( "\nSorting by names using "\
       "studs.sort( key = itemgetter(1,0), reverse = True )")
from operator import itemgetter
studs.sort( key = itemgetter(1,0), reverse = True )
for s in studs:
    print( s )

input( "\nPress 'Enter' to continue/exit ... ")

Offline David

  • Hero Member
  • *****
  • Posts: 647
    • View Profile
Ok ... here is an other classic student type problem ...

A Seat Sales System Emulation

Here, our first version for a solution to this very common problem ...

will be coded in C++

Code: [Select]
#include <iostream>
#include <iomanip>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>

using namespace std;

const size_t MAX_ROW_SIZE = 19;
const double MIN_SEAT_PRICE = 10.00;

const char* HEADER =
    "        ************************************\n"
    "        *                                  *\n"
    "        *   WELCOME TO SEAT SALES SYSTEMS  *\n"
    "        *                                  *\n"
    "        ************************************\n\n"

"Thank you for using our SEATING and PRICING system. \n"
    "Our tech support staff are ready to assist you 24/7.\n\n"

    "Would you like to start this program (0 to exit or 1 to continue) ?  ";


const char* MENU =
    "\n"
    "1. Purchase seat(s)\n"
    "2. Cancel seat(s)\n"
    "3. Show total number of seats sold\n"
    "4. Show total dollars of seats sold\n"
    "5. View seat status on a particular row\n"
    "6. View seat status of all seats\n"
    "7. Show total possible seat sales revenue $\n"
    "8. Show total possible number of seats to sell\n"
    "9. Show all stats \n"
    "0. Exit\n"
    "Your selection (0..9) ?  ";


// some utility functions from my library
template< typename T >
T takeInValue( const std::string& msg, const T& min, const std::string& err );
char getCharReply( const std::string& msg );
bool more();

// used here ...
size_t getMaxColSize( const vector< vector< bool > >& avail );

void showSeating( const vector< vector< bool > >& avail,
                  const vector< double >& prices, char c = ' ' );
void showRowSeats(  const vector< vector< bool > >& avail,
                    const vector< double >& prices );

void setRowPrices( vector< double >& prices, double minSeatPrice );
void setSeatSale( vector < vector < bool > >& seats, bool set = true );

size_t showTotalSeatsSold( const vector < vector < bool > >& seats );

double showTotalSeatsPossible( const vector < vector < bool > >& seats );
double showTotalSales( const vector < vector < bool > >& seats,
                       const vector < double >& prices );
double showTotalSalesPossible( const vector < vector < bool > >& seats,
                               const vector < double >& prices );

void showStats( const vector < vector < bool > >& seats,
                  const vector < double >& prices );



int main() /////////////////////////////////////////////////////////////////////
{
    if( takeInValue< int >
        ( HEADER, 0 , "\nOnly integers >= 0 are valid here.\n" ) != 1 )
        return 0;


    // if reach here ...
    size_t rows = takeInValue< size_t >( "\nHow many rows of seats: ", 1,
                               "\nOnly integers >= 1 are valid here ...\n" );

    vector< vector < bool > > seating;
    seating.reserve( rows );

    vector< double > prices( rows, 0 );

    cout << endl;

    for( size_t r = 0 ; r < rows ; )
    {
        // form message
        ostringstream oss;
        oss << "How many seats in row " << setw(2) << r+1 << ": ";
        size_t cols = takeInValue< size_t >( oss.str(), 1,
                                   "\nOnly integers >= 1 are valid here ...\n" );
        if( cols > MAX_ROW_SIZE )
        {
            cout << "\nOnly up to a max of " << MAX_ROW_SIZE
                 << " allowed here ... \n";
            continue;
        }

        vector< bool > row( cols, 0 );
        seating.push_back( row );
        ++ r;
    }


    showSeating( seating, prices ); // prices all 0 here //


    string msg  = "\nSet up seat prices (0 to exit or 1 to continue) ? ";
    if( takeInValue< int >( msg, 0,
        "\nOnly integers >= 0 are valid here ...\n" ) == 1 )
    {
        // form message ...
        ostringstream oss;
        oss << "\nDecimal values only >= " << MIN_SEAT_PRICE << " please ...\n";

        double minSeatPrice = takeInValue< double >
            ( "\nMinimum seat price: ", MIN_SEAT_PRICE, oss.str() );

        setRowPrices( prices, minSeatPrice );
        cout << "\nYour seating prices are updated.\n";
        showSeating( seating, prices );
        cout << endl;
    }
    else return 0;


    // if reach here ...

    bool doMainLoop = true;
    while( doMainLoop )
    {
        switch( takeInValue< int >( MENU, 0,
                                    "\nIntegers only >= 0 here please ...\n" ))
         {
             case 1:
                showSeating( seating, prices ); //don't show sold seat positions
                setSeatSale(seating, true );
                break;
             case 2:
                showSeating( seating, prices, '1' ); //show sold seat positions
                setSeatSale(seating, false );
                break;

             case 3: showTotalSales( seating, prices ); break;
             case 4: showTotalSeatsSold( seating ); break;
             
             case 5: showRowSeats( seating, prices ); break;
             case 6: showSeating( seating, prices, '1' ); break;
             case 7: showTotalSalesPossible( seating, prices ); break;
             case 8: showTotalSeatsPossible( seating ); break;
             case 9: showStats( seating, prices ); break;


             case 0: doMainLoop = false; break;
             default : cout << "\nNot implemented here yet...\n";
         }
    }



} //////////////////////////////////////////////////////////////////////////////




template< typename T >
T takeInValue( const std::string& msg, const T& min, const std::string& err )
{
    T val;
    while( true )
    {
        std::cout << msg << std::flush;
        if( std::cin >> val && std::cin.get() == '\n' && val >= min )
            break;
        else
        {
            std::cout << err;
            std::cin.clear();
            std::cin.sync();
        }
    }
    return val;
}

char getCharReply( const std::string& msg )
{
    std::cout << msg << std::flush;
    std::string reply;
    getline( std::cin, reply );
    if( reply.size() ) return reply[0];
    // else ...
    return 0;
}

bool more()
{
    if( tolower( getCharReply( "\nMore (y/n) ? " )) == 'n' ) return false;
    // else ...
    return true;
}

size_t getMaxColSize( const vector< vector< bool > >& avail )
{
    size_t max = 0;
    for( size_t i = 0; i < avail.size(); ++ i )
        if( avail[i].size() > max )
            max = avail[i].size();
    return max;
}

void showSeating( const vector< vector< bool > >& avail,
                  const vector< double >& prices, char c = ' ' )
{
    size_t max_cols = getMaxColSize( avail );

    cout << "\nSeating... \n\n"
         << string( 10+max_cols*3, '=' )
         << "\nSEAT    : ";

    for( size_t i = 0; i < max_cols; ++ i )
        cout << setw(3) << i+1;

    cout << endl << string( 10+max_cols*3, '=' );

    cout << setprecision(2) << fixed;

    for( size_t i = 0; i < avail.size(); ++ i )
    {
        cout << "\nROW  " << setw(2) << i+1 << " : ";
        for( size_t j = 0; j < avail[i].size(); ++ j )
            cout << setw(3) << ( avail[i][j] ? c : '0' );
        cout << string( 3*(max_cols-avail[i].size()), ' ' );
        cout << " : $" << setfill( '0' ) << setw(7) << prices[i]
             << setfill( ' ');
    }

    cout << endl;
}

void showRowSeats(  const vector< vector< bool > >& avail,
                    const vector< double >& prices )
{
    size_t row = takeInValue< size_t >( "\nEnter row to show: ", 1,
                                        "\nIntegers <= 1 only here please ...\n" );
    if( row > avail.size() )
    {
        cout << "\nOnly " << avail.size() << " rows ...\n";
        return;
    }


    size_t max_cols = getMaxColSize( avail );

    cout << "\nSeating... \n\n"
         << string( 10+max_cols*3, '=' )
         << "\nSEAT    : ";

    for( size_t i = 0; i < max_cols; ++ i )
        cout << setw(3) << i+1;

    cout << endl << string( 10+max_cols*3, '=' );

    cout << setprecision(2) << fixed;

    cout << "\nROW  " << setw(2) << row << " : ";
    for( size_t j = 0; j < avail[row-1].size(); ++ j )
        cout << setw(3) << avail[row-1][j];
    cout << string( 3*(max_cols-avail[row-1].size()), ' ' );
    cout << " : $" << setfill( '0' ) << setw(7) << prices[row-1]
         << setfill( ' ');

    cout << endl;
}

void setRowPrices( vector< double >& prices, double minSeatPrice )
{
    cout << "Please enter a price for each row ...\n";
    for( size_t i = 0; i < prices.size(); ++ i )
    {
        // form messages ...
        ostringstream oss, oss2;
        oss << "Please enter seat price for row " << setw(2) << i+1 << ":  $";
        oss2 << "\nDecimal values only >= " << minSeatPrice << " please ...\n";
        prices[i] = takeInValue< double >
                                 ( oss.str(), minSeatPrice, oss2.str() );
    }
}

void setSeatSale( vector < vector < bool > >& seats, bool set = true )
{
    for( ; ; )
    {
        size_t row = takeInValue< size_t >( "Enter desired row: ", 1,
                                         "\nIntegers >= 1 only please ...\n" );
        if( row > seats.size() )
        {
            cout << "\nSorry ... Only " << seats.size() << " rows here ...\n";
            continue;
        }
        --row;

        size_t seat = takeInValue< size_t >( "Enter desired seat: ", 1,
                                         "\nIntegers >= 1 only please ...\n" );
        if( seat > seats[row].size() )
        {
            cout << "\nSorry ... Only " << seats[row].size() << " seats here ...\n";
            continue;
        }
        --seat;

        if( set )
        {
            if( !seats[row][seat] )
            {
                seats[row][seat] = true;
                cout << "\nOk ... seat " << ++seat << " in row " << ++row
                     << " is now reserved.\n";
            }
            else cout << "\nSeat " << ++seat << " in row " << ++row
                     << " is ALREADY taken.\n";
        }
        else
        {
            if( seats[row][seat] )
            {
                seats[row][seat] = false;
                cout << "\nOk ... seat " << ++seat << " and row " << ++row
                     << " is now free.\n";
            }
            else cout << "\nSeat " << ++seat << " and row " << ++row
                     << " is ALREADY free.\n";
        }

        if( !more() ) break;
    }
}

size_t showTotalSeatsSold( const vector < vector < bool > >& seats )
{
    size_t sum = 0;
    for( size_t i =0; i < seats.size(); ++i )
        for( size_t j =0; j < seats[i].size(); ++j )
            if( seats[i][j] ) ++sum;
    cout << "\nTotal number of seats sold: " << sum << endl;
    return sum;
}

double showTotalSeatsPossible( const vector < vector < bool > >& seats )
{
    size_t sum = 0;
    for( size_t i =0; i < seats.size(); ++i )
        sum += seats[i].size();

    cout << "\nTotal seats possible to sell: " << sum << endl;
    return sum;
}


double showTotalSales( const vector < vector < bool > >& seats,
                       const vector < double >& prices )
{
    double sum = 0;
    for( size_t i =0; i < seats.size(); ++i )
        for( size_t j =0; j < seats[i].size(); ++j )
            if( seats[i][j] ) sum += prices[i];

    cout << setprecision(2) << fixed;
    cout << "\nTotal sales: $" << sum << endl;
    return sum;
}

double showTotalSalesPossible( const vector < vector < bool > >& seats,
                               const vector < double >& prices )
{
    double sum = 0;
    for( size_t i =0; i < seats.size(); ++i )
        sum += prices[i] * seats[i].size();

    cout << setprecision(2) << fixed;
    cout << "\nMax sales possible: $" << sum << endl;
    return sum;
}

void showStats( const vector < vector < bool > >& seats,
                  const vector < double >& prices )
{
    size_t aseats = showTotalSeatsSold( seats ); // aseats => actual_seats_sold
    size_t tseats = showTotalSeatsPossible( seats );//tseats => total_seats_sold

    double asales = showTotalSales( seats, prices );
    double tsales = showTotalSalesPossible( seats, prices );

    if( aseats ) cout << "\nAverage sale was: " << asales / aseats << ",  ";

    cout << "% seats sold was: " << aseats * 100.00 /tseats
         << ",  % $$.$$ sold was: " << asales * 100.00 /tsales << endl;
}