#include <stdio.h>
#include <math.h>

#ifdef _OPENMP
    #include <omp.h>
#endif

int main(int argc, char *argv[]) {
    #ifdef _OPENMP

        const int sum_bound = 10000000;
	int i;
        double sum;
        double start_time, end_time;

        start_time = omp_get_wtime();
        sum = 5;
        for(i = 0; i < sum_bound; i++)
            sum += sin(i);
        end_time = omp_get_wtime();
        printf("sum: %g\n", sum);
        printf("compute time usage serial for: %g\n\n", end_time - start_time);

/*

sum: 6.53534
compute time usage serial for: 0.226479

*/

        start_time = omp_get_wtime();
        sum = 5;
        #pragma omp parallel for reduction(+: sum)
        for(i = 0; i < sum_bound; i++)
            sum += sin(i);
        end_time = omp_get_wtime();
        printf("sum: %g\n", sum);
        printf("compute time usage parallel for with reduce: %g\n\n", end_time - start_time);

/*

sum: 6.53534
compute time usage parallel for with reduce: 0.121119

*/

        start_time = omp_get_wtime();
        sum = 5;
        #pragma omp parallel
	{
	    double local_sum = 0.;
	    #pragma omp for
	    for(i = 0; i < sum_bound; i++)
	        local_sum += sin(i);
	    #pragma omp atomic
	    sum += local_sum;
	}
	end_time = omp_get_wtime();
	printf("sum: %g\n", sum);
	printf("compute time usage parallel for with thread local sum: %g\n\n", end_time - start_time);

/*

sum: 6.53534
compute time usage parallel for with thread local sum: 0.121576

*/

        start_time = omp_get_wtime();
        sum = 5;
        #pragma omp parallel for
        for(i = 0; i < sum_bound; i++) {
            #pragma omp atomic
            sum += sin(i);
        }
        end_time = omp_get_wtime();
        printf("sum: %g\n", sum);
        printf("compute time usage parallel for with atomic: %g\n\n", end_time - start_time);

/*

sum: 6.53534
compute time usage parallel for with atomic: 0.697731

*/

        start_time = omp_get_wtime();
        sum = 5;
        #pragma omp parallel for
        for(i = 0; i < sum_bound; i++) {
            #pragma omp critical
            sum += sin(i);
        }
        end_time = omp_get_wtime();
        printf("sum: %g\n", sum);
        printf("compute time usage parallel for with critical: %g\n\n", end_time - start_time);

/*

sum: 6.53534
compute time usage parallel for with critical: 0.868812

*/

        start_time = omp_get_wtime();
        sum = 5;
        omp_lock_t lock;
        omp_init_lock(&lock);
        #pragma omp parallel for
        for(i = 0; i < sum_bound; i++) {
            omp_set_lock(&lock);
            sum += sin(i);
            omp_unset_lock(&lock);
        }
        omp_destroy_lock(&lock);
        end_time = omp_get_wtime();
        printf("sum: %g\n", sum);
        printf("compute time usage parallel for with set_lock/unset_lock: %g\n\n", end_time - start_time);

/*

sum: 6.53534
compute time usage parallel for with set_lock/unset_lock: 1.68612

*/

        start_time = omp_get_wtime();
        sum = 5;
        #pragma omp parallel for shared(sum)
        for(i = 0; i < sum_bound; i++) {
            sum += sin(i);
        }
        end_time = omp_get_wtime();
        printf("wrong sum: %g\n", sum);
        printf("compute time usage parallel for wrong sum: %g\n\n", end_time - start_time);

/*

wrong sum: 4.93475
compute time usage parallel for wrong sum: 0.119021

*/

    #else
        printf("No OpenMP support\n");
    #endif

    return 0;
}