Lab 7. ADT

Lab 7. ADT

Note that there was a bug in the second test for the function assign. This program has this test fixed.

/*
    Author:
    Description:
*/

#include <iostream>
using namespace std;

// The employees at a certain software development company
enum Programmer {Alice=1, Bob=2, Carol=3, Dave=4, Eve=5, Frank=6, Grace=7, UNASSIGNED = 0};

// Constants
const int N = 24;

const int NOT_FOUND = -1;

/*
      The Scheduler Class
      -------------------
      >>> The CEO wants this functionality to be implemented today!
      The scheduler helps assigning the employees to do their work.

    - It implements a schedule for one working day.

    - A day is divided into 24 one-hour long time slots.

    - Only one employee can be assigned to work per time slot.

    - According to the local labor laws, an employee cannot be scheduled
      to work for more than 12 hours in total per day (for example, two
      disjoint intervals of 7 hours are prohibited, because 7+7 > 12).
*/

class Scheduler {
    Programmer timetable[N];

public:
      // Sets the schedule to UNASSIGNED for all time slots
    void init();

      // Returns who is assigned to work in the time slot t.
      // If t is out of bounds [0,N-1], return UNASSIGNED
    Programmer get(int t);

      // Assigns the programmer to the time interval if the entire interval is vacant.
      // The interval must have positive duration (duration > 0).
      // Returns true if succeeds, otherwise returns false.
    bool assign(Programmer name, int start, int duration);

      // Cancel all time assignments for the Programmer `name`.
    void cancel(Programmer name);

      // Searches for a vacant time interval of length `duration`,
      // returns the starting time if it finds such an interval,
      // otherwise returns NOT_FOUND.
    int find_interval(int duration);

};


/* Unit tests */
bool test_init();
bool test_get();
bool test_assign();
bool test_cancel();
bool test_find();


int main() {

    // Testing
    cout << endl;

    bool b = true;
    b = test_init() && b;
    b = test_get() && b;
    b = test_assign() && b;
    b = test_cancel() && b;
    b = test_find() && b;

    cout << endl;

    if (b)
        cout << "PASSED";
    else
        cout << "FAILED";

    cout << endl << endl;
}


/***************** Scheduler Implementation ******************/

void Scheduler::init() {
    for (int i = 0; i < N; i++) {
        timetable[i] = UNASSIGNED;
    }
}

Programmer Scheduler::get(int t) {
    if (t >= N || t < 0) return UNASSIGNED;
    return timetable[t];
}

bool Scheduler::assign (Programmer name, int start, int dur) {

    if (start < 0 || start + dur > N || dur <= 0) return false;

    // check that the interval is vacant
    for(int i = start; i < start + dur; i++) {
        if (timetable[i] != UNASSIGNED) return false;
    }

    // check that the programmer would not work > 12 hours
    int count = 0;
    for(int i = 0; i < N; i++) {
        if (timetable[i] == name) count ++;
    }

    if (count + dur > 12) return false;

    // the interval is valid
    for(int i = start; i < start + dur; i++) {
        timetable[i] = name;
    }

    return true;
}

void Scheduler::cancel (Programmer name) {
    for(int i = 0; i < N; i++) {
        if (timetable[i] == name)
            timetable[i] = UNASSIGNED;
    }
}

int Scheduler::find_interval (int dur) {
    for (int start = 0; start < N - dur; start++) {
        bool b = true;

        for(int i = start; i < start + dur; i++) {
            if (timetable[i] != UNASSIGNED) b = false;
        }

        if (b)
            return start;
    }
    return NOT_FOUND;
}


/***************** Unit Tests ******************/

bool test_init () {
    cout << "Testing init:\t";

    bool passed = true;

    // Test: check that all time slots become UNASSIGNED
    {
        bool b = true;
        Scheduler s;
        s.init();
        for(int i = 0; i < N; i++) {
            b = b && (UNASSIGNED == s.get(i));
        }

        if (b) { cout << "pass "; } else { cout << "fail "; }
        passed = passed && b;
    }

    cout << endl;
    return passed;
}


bool test_get () {
    cout << "Testing get:\t";

    bool passed = true;

    // Test: test that get out of bounds return UNASSIGNED
    {
        bool b = true;
        Scheduler s;
        s.init();

        // when i < 0
        for(int i = -1; i > -N * 100; i--)
            b = b && (UNASSIGNED == s.get(i));

        // when i >= N
        for(int i = N; i < N * 100; i++)
            b = b && (UNASSIGNED == s.get(i));

        if (b) { cout << "pass "; } else { cout << "fail "; }
        passed = passed && b;
    }

    cout << endl;
    return passed;
}


bool test_assign() {
    cout << "Testing assign:\t";

    bool passed = true;

    // Test 1: assigning a valid interval
    {
        bool b = true;
        Scheduler s;
        s.init();

        bool result = s.assign(Alice, 5, 2) && s.assign(Alice, 7, 4);

        b = b && (true == result) && (UNASSIGNED == s.get(4)) && (UNASSIGNED == s.get(11));

        for(int i = 5; i < 5+6; i++)
            b = b && (Alice == s.get(i));

        if (b) { cout << "pass "; } else { cout << "fail "; }
        passed = passed && b;
    }

    // Test 2: assigning an out of bound or negative or zero-length interval
    {
        bool b = true;
        Scheduler s;
        s.init();

        bool result =
            s.assign(Bob,  -1,   5) || 
            s.assign(Bob, -10,   1) || 
            s.assign(Bob, -10,   0) || 
            s.assign(Bob,   2,   0) || 
            s.assign(Bob,   5,  -1) || 
            s.assign(Bob,  10, -10) || 
            s.assign(Bob, N-2,   3) || 
            s.assign(Bob, N-3,  10) ;

        b = b && (false == result);

        for(int i = 0; i < N; i++)
            b = b && (UNASSIGNED == s.get(i));

        if (b) { cout << "pass "; } else { cout << "fail "; }
        passed = passed && b;
    }

    // Test 3: assigning to a non-vacant interval
    {
        bool b = true;
        Scheduler s;
        s.init();

        int result1 = s.assign(Carol, 3, 1) && s.assign(Dave, 7,1);
        b = b && (true == result1);

        int result2 = s.assign(Eve, 2, 7);
        b = b && (false == result2);

        b = b &&
            (
                UNASSIGNED == s.get(2) &&
                Carol      == s.get(3) &&
                UNASSIGNED == s.get(4) &&
                UNASSIGNED == s.get(5) &&
                UNASSIGNED == s.get(6) &&
                Dave       == s.get(7) &&
                UNASSIGNED == s.get(8)
            );

        if (b) { cout << "pass "; } else { cout << "fail "; }
        passed = passed && b;
    }

    // Test 4: assigning to a non-vacant interval - overlaping with the same Programmer
    {
        bool b = true;
        Scheduler s;
        s.init();

        int result1 =
            s.assign(Frank, 10, 4);

        b = b && (true == result1);

        int result2 =
            s.assign(Frank, 8, 3) ||
            s.assign(Frank, 7, 9) ||
            s.assign(Frank, 11, 1) ||
            s.assign(Frank, 12, 10);

        b = b && (false == result2);

        b = b &&
            (
                UNASSIGNED == s.get(9)  &&
                Frank      == s.get(10) &&
                Frank      == s.get(11) &&
                Frank      == s.get(12) &&
                Frank      == s.get(13) &&
                UNASSIGNED == s.get(14)
            );

        if (b) { cout << "pass "; } else { cout << "fail "; }
        passed = passed && b;
    }

    // Test 5: assigning in total more than 12 hours to a single programmer
    {
        bool b = true;
        Scheduler s;
        s.init();

        int result1 =
            s.assign(Grace, 1, 1) &&  // 1
            s.assign(Grace, 4, 2) &&  // 4,5
            s.assign(Grace, 9, 2) &&  // 9,10
            s.assign(Grace, 6, 2) &&  // 6,7
            s.assign(Grace, 8, 1) &&  // 8
            s.assign(Grace, 12, 3) && // 12,13,14
            s.assign(Grace, 17, 1)    // 17
            ;

        int result2 = s.assign(Grace, 15, 1);

        b = (true == result1) && (false == result2);

        if (b) { cout << "pass "; } else { cout << "fail "; }
        passed = passed && b;
    }

    cout << endl;
    return passed;

}


bool test_cancel() {
    cout << "Testing cancel:\t";

    bool passed = true;

    // Test: you can cancel job assignments
    {
        bool b = true;
        Scheduler s;
        s.init();

        int result1 = s.assign(Frank, 5, 2);
        int result2 = s.assign(Grace, 10, 3);
        int result3 = s.assign(Frank, 18, 1);
        s.cancel(Frank);

        b = b &&
            (true == result1) && (true == result2) && (true == result3) &&
            (UNASSIGNED == s.get(5) ) &&
            (UNASSIGNED == s.get(6) ) &&
            (Grace      == s.get(10)) &&
            (Grace      == s.get(11)) &&
            (Grace      == s.get(12)) &&
            (UNASSIGNED == s.get(18));

        if (b) { cout << "pass "; } else { cout << "fail "; }
        passed = passed && b;
    }

    cout << endl;
    return passed;
}


bool test_find() {
    cout << "Testing find:\t";

    bool passed = true;

    // Test: finding an existing interval
    {
        bool b = true;
        Scheduler s;
        s.init();

        s.assign(Alice, 4, 1);
        s.assign(Bob, 15, 1);
        s.assign(Alice, 20, 1);

        int start = s.find_interval(10);
        b = b && (NOT_FOUND != start) && (start >= 0) && (start + 10 < N);

        if (b) {
            for(int i = start; i < start + 10; i++) {
                b = b && (UNASSIGNED == s.get(i));
            }
        }

        if (b) { cout << "pass "; } else { cout << "fail "; }
        passed = passed && b;
    }

    // Test: when such ah interval does not exist
    {
        bool b = true;
        Scheduler s;
        s.init();

        s.assign(Alice, 4, 1);
        s.assign(Bob, 15, 1);
        s.assign(Alice, 20, 1);

        int start = s.find_interval(11);
        b = b && (NOT_FOUND == start);

        if (b) { cout << "pass "; } else { cout << "fail "; }
        passed = passed && b;
    }

    cout << endl;
    return passed;
}


Testing init:   pass 
Testing get:    pass 
Testing assign: pass pass pass pass pass 
Testing cancel: pass 
Testing find:   pass pass 

PASSED