#include <iostream>
#include <vector>
#include <algorithm>


using namespace std;

#define check( s, a, b ) { \
    if( (a) != (b) ) { \
        cout << "## FAIL (" << (s) << "): " << (a) << " != " << (b) << endl; \
        exit(1); \
    } \
    cout << "## PASS (" << (s) << "): " <<  (a) << endl; \
    }

class Dept;
class Subject;

 class Student {
     string lname, fname;
     int year;
     vector<string> cls;
     vector<Subject> subjects;
     int sid; //ex2
     Dept &dept;
     
 public:
     Student(string lastname, string firstname, Dept& dep);
     string get_name() const { return lname + " " + fname; }
     int get_year() { return year; }
     void set_year( int yr ) { year = yr; }
     void promote() { year = (!year || year >= 4) ? 0 : year+1; }
     bool graduated() { return !year; }
     void enroll( string cname ) { cls.push_back( cname ); }
     void enroll( Subject &s ); // { subjects.push_back(s); }
     string get_class(int i) { return cls[i]; }
     //ex2 {
     int get_sid() { return sid; }
     //}
     //ex3 {
     void drop( string cname );
     int cls_cnt( ) { return (int)cls.size(); }
     //}
     //ex4 {
     Student &operator++() { year++; return *this; }
     Student operator++(int) { Student t = *this; year++; return t; }
     //ex5 {
     friend bool operator<(const Student s1, const Student s2);
     //}
     //ex6 {
     friend ostream &operator<<(ostream &out, const Student &t);
     //}
     Dept &get_dept() { return dept; }
 };

class Dept {
    string name;
    vector<string> profs;
    vector<Student> students;
public:
    Dept( string name ) { this->name = name; students.clear(); profs.clear(); }
    void add_student( Student &s ) { students.push_back( s ); }
    Student &get_student_at(int i) { return students[i]; }
    string get_name() { return name; }
};

class Subject {
    string name;
    string prof;
    vector<Student> students;
public:
    Subject( string name, string prof ) : name(name), prof(prof) {}
    void reg(Student &s) { students.push_back(s); }
    Student &get_student_at( int i ) { return students[i]; }
};


Student::Student(string lastname, string firstname, Dept& dep) : dept(dep)
{
    lname = lastname;
    fname = firstname;
    year=1;
    cls.clear();
    dept.add_student(*this);

    srand( (unsigned int) time(0) );
    sid = rand();
}

void Student::enroll(Subject &s) {
    subjects.push_back(s);
    s.reg(*this);
}

//ex3
void Student::drop(string cname) {
    vector<string>::iterator it = find( cls.begin(), cls.end(), cname );
    if( it != cls.end() )
        cls.erase(it);
    else
        cout << "Not found" << endl;
}

//ex5
bool operator<(Student s1, Student s2) {
    return s1.year < s2.year;
}

//ex6
ostream &operator<<(ostream &out, const Student &t)
{
    out << "Student { name: " << t.get_name() << ", year: " << t.year;
    return out;
}

int main(int argc, const char * argv[]) {
    
    Dept ce("Computer Engineering");
    
    Student s1( "Kim", "Soochul", ce );
    
    check( "dept student", ce.get_student_at(0).get_name(), "Kim Soochul");
    check( "student dept", s1.get_dept().get_name(), "Computer Engineering");
    
    check( "get_year()", s1.get_year(), 1 );
    check( "get_name()", s1.get_name(), "Kim Soochul" );
    s1.promote();
    check( "get_year()", s1.get_year(), 2 );
    check( "graduated()", s1.graduated(), false );
    
    Subject cpp("C++", "Minho Shin");
    Subject java("Java", "Java King");
    
    s1.enroll(cpp);
    s1.enroll(java);
    
    check( "subj student", cpp.get_student_at(0).get_name(), "Kim Soochul");
    
    s1.enroll( "C++" );
    s1.enroll( "Java" );
    check( "get_class()", s1.get_class(0), "C++" );
    check( "get_class()", s1.get_class(1), "Java" );
    s1.promote(); s1.promote(); s1.promote();
    check( "graduated()", s1.graduated(), true );
    
    //ex2
    check( "get_sid()", s1.get_sid(), s1.get_sid());
    
    //ex3
    s1.drop("C++");
    check( "get_class()", s1.get_class(0), "Java" );
    check( "cls_size()", s1.cls_cnt(), 1 );
    
    //ex4
    s1.set_year(1);
    ++s1;
    check( "prefix++: get_year()", s1.get_year(), 2)
    s1++;
    check( "postfix++: get_year()", s1.get_year(), 3)
    
    //ex5
    Student s2("Lee", "June", ce);
    s2 < s1;
    check( "<", s2 < s1, true );
    
    cout << s1 << endl;
    cout << s2 << endl;
    system( "read" );
    return 0;
}
