#include <omp.h>
#include <stdlib.h>

struct A { A (); ~A (); A (const A &); static int cnt1, cnt2, cnt3; int a; };
int A::cnt1;
int A::cnt2;
int A::cnt3;
A::A () : a (0)
{
  #pragma omp atomic
  cnt1++;
}
A::A (const A &x) : a (x.a)
{
  #pragma omp atomic
  cnt2++;
}
A::~A ()
{
  #pragma omp atomic
  cnt3++;
}
#pragma omp declare reduction (+: A: omp_out.a += omp_in.a)

void
foo (int x)
{
  A a, b[2];
  int d = 1;
  long int e[2] = { 1L, 1L };
  int c = 0;
  #pragma omp parallel
  {
    if (x && omp_get_thread_num () == 0)
      {
	for (int i = 0; i < 10000000; ++i)
	  asm volatile ("");
	c = 1;
	#pragma omp cancel parallel
      }
    #pragma omp for reduction (task, +: a, b) reduction (task, *: d, e)
    for (int i = 0; i < 64; i++)
      #pragma omp task in_reduction (+: a, b) in_reduction (*: d, e)
      {
	a.a++;
	b[0].a += 2;
	b[1].a += 3;
	d *= ((i & 7) == 0) + 1;
	e[0] *= ((i & 7) == 3) + 1;
	e[1] *= ((i & 3) == 2) + 1;
      }
    if (x && omp_get_cancellation ())
      abort ();
  }
  if (!c)
    {
      if (a.a != 64 || b[0].a != 128 || b[1].a != 192)
	abort ();
      if (d != 256 || e[0] != 256L || e[1] != 65536L)
	abort ();
    }
}

int
main ()
{
  int c1 = A::cnt1, c2 = A::cnt2, c3 = A::cnt3;
  volatile int zero = 0;
  foo (zero);
  if (A::cnt1 + A::cnt2 - c1 - c2 != A::cnt3 - c3)
    abort ();
}
