#include <stdio.h>
#include <time.h>
#include <sys/time.h>
#include <assert.h>

#include "oop.h"


//#define printf(fmt, ...)


void doit(void)
{
  oop s= new Set();

  static char *words[]= {
    "Peter", "Piper", "picked", "a", "peck", "of", "pickled", "peppers",
    "A", "peck", "of", "pickled", "peppers", "Peter", "Piper", "picked",
    "If", "Peter", "Piper", "picked", "a", "peck", "of", "pickled", "peppers",
    "Where's", "the", "peck", "of", "pickled", "peppers", "Peter", "Piper", "picked?",
    0
  };

  for (char **word= words;  *word;  ++word)
    s->add(new String(*word));

  printf("---- %d\n", s->array()->size());
  iterate(s->array(), iterator)
    printf("%p %s %d\n",
	   iterator.element(),
	   iterator.element() ? iterator.element()->cString() : "",
	   iterator.element() ? iterator.element()->hash() : 0);

  printf("---- %d\n", s->size());
  iterate(s, iterator)
    printf("%s\n", iterator.element()->cString());

  printf("pickled -> %d\n", s->includes(new String("pickled")));
  printf("pickuld -> %d\n", s->includes(new String("pickuld")));

  printf("---- %d\n", s->asArray()->size());
  iterate(s->asArray(), iterator)
    printf("%s\n", iterator.element()->cString());

  printf("=== %d\n", *(new String("hello")) == new String("hello"));
  printf("=== %d\n", *(new String("hello")) == new Symbol("hello"));
  printf("=== %d\n", *(new Symbol("hello")) == new String("hello"));
  printf("=== %d\n", *(new Symbol("hello")) == new Symbol("hello"));

  for (char **word= words;  *word;  ++word)
    {
      oop sym= Symbol::intern(new String(*word));
      printf("%p %s\n", sym, sym->cString());
      (void)sym;
    }

  oop o= new OrderedCollection();

  for (char **word= words;  *word;  ++word) o->addLast(new String(*word));
  printf("\n");
  while (!o->isEmpty()) printf("%s ", o->removeFirst()->cString());
  printf("\n");

  for (char **word= words;  *word;  ++word) o->addLast(new String(*word));
  printf("\n");
  while (!o->isEmpty()) printf("%s ", o->removeLast()->cString());
  printf("\n");

  for (char **word= words;  *word;  ++word) o->addFirst(new String(*word));
  printf("\n");
  while (!o->isEmpty()) printf("%s ", o->removeFirst()->cString());
  printf("\n");

  for (char **word= words;  *word;  ++word) o->addFirst(new String(*word));
  printf("\n");
  while (!o->isEmpty()) printf("%s ", o->removeLast()->cString());
  printf("\n");

  oop d= new Dictionary;
  oop i= new IdentityDictionary;
  for (char **word= words;  *word;  ++word)
    {
      oop s= new String(*word);
      d->atPut(s, s);
      i->atPut(s, s);
      i->atPut(s, s);
    }
  printf("---- %d\n", d->size());
  iterate(d->keys(), iterator)
    printf("%s -> %s\n", iterator.element()->cString(), d->at(iterator.element())->cString());
  printf("---- %d\n", i->size());
  iterate(i->keys(), iterator)
    printf("%s -> %s\n", iterator.element()->cString(), i->at(iterator.element())->cString());

  s= new String("Hello, world\n");
  iterate(s, iterator)
    printf("%c", iterator.element()->asciiValue());

#if 0

  iterate(i->keys()->asSortedCollection(), iterator)
    printf("%s\n", iterator.element()->cString());

#endif

  assert(*(new String("aaa")) <  (new String("bbb")));
  assert(*(new String("aaa")) <= (new String("bbb")));
  assert(*(new String("aaa")) <= (new String("aaa")));
  assert(*(new String("aaa")) == (new String("aaa")));
  assert(*(new String("aaa")) >= (new String("aaa")));
  assert(*(new String("bbb")) >= (new String("aaa")));
  assert(*(new String("bbb")) >  (new String("aaa")));

  assert(*(new String("aa"))  <  (new String("aaa")));
  assert(*(new String("aa"))  <= (new String("aaa")));
  assert(*(new String("aa"))  <= (new String("aa")));
  assert(*(new String("aa"))  == (new String("aa")));
  assert(*(new String("aa"))  >= (new String("aa")));
  assert(*(new String("aaa")) >= (new String("aa")));
  assert(*(new String("aaa")) >  (new String("aa")));

  assert( (new String("abracadabra"))->includes(Character::value('d')));
  assert(!(new String("abracadabra"))->includes(Character::value('e')));

  s= (new String)->writeStream();
  s->nextPutAll(new String("Hello"));
  s->nextPut(Character::value(','));
  s->space();
  s->nextPutAll(new String("world"));
  printf("%s\n", s->contents()->cString());
  printf("%s\n", s->printCString());
  printf("%s\n", s->contents()->printCString());
  s->format(" -- %d -- %s -- %P --\n", 42, "hello", s);
  printf("%s\n", s->contents()->printCString());

  s= new String("Hello");
  s= *s + new String(", ");
  s= *s + new String("world");
  printf("%s\n", s->cString());

  s= (new String("end of test\n"))->readStream();
  while (!s->atEnd())
    putchar(s->next()->asciiValue());
}


int benchmark(int n)
{
  // Handy bytecode-heavy benchmark.
  // (500000 // time to run) = approx bytecodes per second
  // 5000000 // (Time millisecondsToRun: [10 benchmark]) * 1000
  // 3059000 on a Mac 8100/100
  int size= 8190;
  int count= 0;
  for (int iter= 0;  iter < n;  iter= iter + 1)
    {
      count= 0;
      oop flags= new Array(size);
      flags->atAllPut((oop)1);
      for (int i= 2;  i < size;  i= i + 1)
	{
	  if (flags->at(i))
	    {
	      for (int k= i + i;  k < size;  k= k + i)
		flags->atPut(k, 0);
	      count= count + 1;
	    }
	}
    }
  return count;
}





int main(int argc, char **argv)
{
  GC_INIT();
  GC_set_max_heap_size(1024*1024);

  if (argc == 2)
    {
      // (500000 / time to run) == approx bytecodes per second
      struct timeval start, stop, elapsed;
      gettimeofday(&start, 0);
      benchmark(1000);
      gettimeofday(&stop, 0);
      timersub(&stop, &start, &elapsed);
      double t= (double)elapsed.tv_sec + (double)elapsed.tv_usec / 1000000.0;
      t /= 1000.0;
      printf("%.0f bytecodes per second\n", 500000.0 / t);
    }
  else
    do
      {
	doit();
	fprintf(stderr, "%lu\n", GC_get_heap_size());
      }
    while (argc > 2);

  return 0;
}

