/*
 *
 * Test whether the copy constructor gets called correctly
 *
 */

#include <iostream>
#include "omp.h"

#define NTHREADS 4

class myclass {
    int tid;
    int *_n;

public:
    myclass(int n) { 
        _n = (int *) malloc(sizeof(int));
        *_n = n;
        // std::cout << "Init value constructor\n" << std::endl;
    }

    myclass() { 
        _n = (int *) malloc(sizeof(int));
        *_n = 0;
        // std::cout << "Default constructor\n" << std::endl;
    }

    myclass(const myclass & other)
    { 
        _n = (int *) malloc(sizeof(int));
        *_n = *other._n;
        std::cout << "Copy constructor\n" << std::endl;
    }

    ~myclass() { 
#pragma omp critical
        std::cout << "Calling destructor for thread " << tid << std::endl;
        free(_n);
    }

    void print() { 
        std::cout << omp_get_thread_num() << ": " << *_n << "\n"; 
    }

    void add(int t) { 
        *_n += t; 
    }

    void setId(int id) { 
        tid = id;
    }

    int get() {
        return *_n;
    }
};

myclass a = myclass(10);
myclass x = myclass(a);
#pragma omp threadprivate(x)

int main(int argc, char* argv[])
{
    int arr[NTHREADS];
    omp_set_num_threads(NTHREADS);
    omp_set_dynamic(false);

    for(int i = 0; i < NTHREADS; i++)
        arr[i] = -1;

#pragma omp parallel
    {
        // #pragma omp threadprivate (x)
        int myid = omp_get_thread_num();
        x.add(myid);
        x.setId(myid);
    }

#pragma omp parallel
    {
        int myid = omp_get_thread_num();
        arr[myid] = x.get();
    }

    for(int i = 0; i < NTHREADS; i++) {
        if(arr[i] == i + 10)
            std::cout << "PASS" << std::endl;
        else
            std::cout << "FAIL" << std::endl;
    }

    return 0;
}
