/* This test needs deterministic allocation with enough spacing
   between the allocations.  Otherwise, if by coincidence the
   allocated vararg memory object is directly before another valid
   memory object, KLEE will be able to resolve the out-of-bounds
   access with another object and not detect the false access.  This
   will fail this test case. */

// RUN: %clang %s -emit-llvm %O0opt -c -g -o %t1.bc
// RUN: rm -rf %t.klee-out
// RUN: %klee --output-dir=%t.klee-out --allocate-determ=true --allocate-determ-start-address=0x0 %t1.bc | FileCheck %s
// RUN: test -f %t.klee-out/test000001.ptr.err

#include <stdarg.h>
#include <assert.h>
#include <stdio.h>

struct triple {
  int first, second, third;
};

void test1(int x, ...) {
  va_list ap;
  va_start(ap, x);
  int i32 = va_arg(ap, int);
  long long i64 = va_arg(ap, long long);
  double d = va_arg(ap, double);
  struct triple p = va_arg(ap, struct triple);
  printf("types: (%d, %lld, %.2f, (%d,%d,%d))\n", i32, i64, d, p.first, p.second, p.third);
  va_end(ap);
}

int sum(int N, ...) {
  int i, res = 0;
  va_list ap,ap2;
  
  va_start(ap, N);
  for (i=0; i<N; i++) {
    if (i==1)
      va_copy(ap2, ap);
    res += va_arg(ap, int);
  }
  for (i=0; i<N-1; i++)
    res += va_arg(ap2, int);
  va_end(ap);

  return res;
}

// test using aps in an array (no multiple resolution
// testing, though)
int va_array(int N, ...) {
  va_list aps[2];
  unsigned i;
  unsigned sum1 = 0, sum2 = 0;

  for (i=0; i<2; i++)
    va_start(aps[i], N);
  
  for (i=0; i<N; i++) {
    unsigned cmd = va_arg(aps[0], int);

    if (cmd==0) {
      sum1 += va_arg(aps[0],int);
    } else if (cmd==1) {
      sum2 += va_arg(aps[1],int);
    } else if (cmd==2) {
      va_copy(aps[1], aps[0]);
    }
  }

  for (i=0; i<2; i++)
    va_end(aps[i]);

  return 3*sum1 + 5*sum2;
}

int main() {
  struct triple p = { 9, 12, 15 };
  test1(-1, 52, 37ll, 2.0, p);
  // CHECK: types: (52, 37, 2.00, (9,12,15))

  assert(sum(2, 3, 4) == 11);
  assert(sum(0) == 0);
  assert(va_array(5, 0, 5, 1, 1, 2, 1)==45); // 15 + 30

  // should give memory error
  test1(-1, 52, 2.0, p);

  return 0;
}
