import TestLib;

StartTest('Queue');

from collections.queue(T=int) access
    Queue_T as Queue_int,
    makeNaiveQueue,
    makeArrayQueue,
    makeLinkedQueue;


struct ActionEnum {
  static restricted int numActions = 0;
  static private int next() {
    return ++numActions - 1;
  }
  static restricted int PUSH = next();
  static restricted int POP = next();
}

from collections.zip(T=int) access zip;

string differences(Queue_int a, Queue_int b) {
  if (a.size() != b.size()) {
    return 'Different sizes: ' + string(a.size()) + ' vs ' + string(b.size());
  }
  if (a.size() != 0) {
    if (a.peek() != b.peek()) {
      return 'Different peek: ' + string(a.peek()) + ' vs ' + string(b.peek());
    }
  }
  int[] aArray, bArray;
  for (int ia : a) {
    aArray.push(ia);
  }
  for (int ib : b) {
    bArray.push(ib);
  }
  if (!all(aArray == bArray)) {
    write(zip(aArray, bArray));
    return 'Different contents';
  }
  return '';
}

string string(int[] a) {
  string result = '[';
  for (int i = 0; i < a.length; ++i) {
    if (i > 0) {
      result += ', ';
    }
    result += string(a[i]);
  }
  result += ']';
  return result;
}

typedef void Action(...Queue_int[]);

Action[] actions = new Action[ActionEnum.numActions];
actions[ActionEnum.PUSH] = new void(...Queue_int[] qs) {
  int toPush = rand();
  for (Queue_int q : qs) {
    q.push(toPush);
  }
};
actions[ActionEnum.POP] = new void(...Queue_int[] qs) {
  int[] results = new int[];
  for (Queue_int q : qs) {
    if (q.size() > 0) {
      results.push(q.pop());
    }
  }
  if (results.length > 0) {
    int expected = results[0];
    for (int r : results) {
      assert(r == expected, 'Different results: ' + string(results));
    }
  }
};

real[] increasingProbs = new real[ActionEnum.numActions];
increasingProbs[ActionEnum.PUSH] = 0.7;
increasingProbs[ActionEnum.POP] = 0.3;

real[] decreasingProbs = new real[ActionEnum.numActions];
decreasingProbs[ActionEnum.PUSH] = 0.3;
decreasingProbs[ActionEnum.POP] = 0.7;

Queue_int naive = makeNaiveQueue(new int[]);
Queue_int array = makeArrayQueue(new int[]);
Queue_int linked = makeLinkedQueue(new int[]);
Queue_int defaultQueue = makeQueue(new int[]);

for (int i = 0; i < 2000; ++i) {
  // if (i % 100 == 0) {
  //   write('Step ' + string(i));
  //   write('Naive: ' + string(naive.toArray()));
  //   write('Array: ' + string(array.toArray()));
  //   write('Linked: ' + string(linked.toArray()));
  //   write('Default: ' + string(defaultQueue.toArray()));
  // }
  real[] probs = i < 800 ? increasingProbs : decreasingProbs;
  int choice = (unitrand() < probs[ActionEnum.PUSH]
                ? ActionEnum.PUSH
                : ActionEnum.POP);
  actions[choice](naive, array, linked, defaultQueue);
  string diffs = differences(naive, array);
  assert(diffs == '', 'Naive vs array: \n' + diffs);
  diffs = differences(naive, linked);
  assert(diffs == '', 'Naive vs linked: \n' + diffs);
  diffs = differences(naive, defaultQueue);
  assert(diffs == '', 'Naive vs default: \n' + diffs);
}

EndTest();
