// Copyright 2006 The RE2 Authors.  All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Test character class manipulations.

#include <stdio.h>

#include "util/test.h"
#include "util/utf.h"
#include "re2/regexp.h"

namespace re2 {

struct CCTest {
  struct {
    Rune lo;
    Rune hi;
  } add[10];
  int remove;
  struct {
    Rune lo;
    Rune hi;
  } final[10];
};

static CCTest tests[] = {
  { { { 10, 20 }, {-1} }, -1,
    { { 10, 20 }, {-1} } },

  { { { 10, 20 }, { 20, 30 }, {-1} }, -1,
    { { 10, 30 }, {-1} } },

  { { { 10, 20 }, { 30, 40 }, { 20, 30 }, {-1} }, -1,
    { { 10, 40 }, {-1} } },

  { { { 0, 50 }, { 20, 30 }, {-1} }, -1,
    { { 0, 50 }, {-1} } },

  { { { 10, 11 }, { 13, 14 }, { 16, 17 }, { 19, 20 }, { 22, 23 }, {-1} }, -1,
    { { 10, 11 }, { 13, 14 }, { 16, 17 }, { 19, 20 }, { 22, 23 }, {-1} } },

  { { { 13, 14 }, { 10, 11 }, { 22, 23 }, { 19, 20 }, { 16, 17 }, {-1} }, -1,
    { { 10, 11 }, { 13, 14 }, { 16, 17 }, { 19, 20 }, { 22, 23 }, {-1} } },

  { { { 13, 14 }, { 10, 11 }, { 22, 23 }, { 19, 20 }, { 16, 17 }, {-1} }, -1,
    { { 10, 11 }, { 13, 14 }, { 16, 17 }, { 19, 20 }, { 22, 23 }, {-1} } },

  { { { 13, 14 }, { 10, 11 }, { 22, 23 }, { 19, 20 }, { 16, 17 }, { 5, 25 }, {-1} }, -1,
    { { 5, 25 }, {-1} } },

  { { { 13, 14 }, { 10, 11 }, { 22, 23 }, { 19, 20 }, { 16, 17 }, { 12, 21 }, {-1} }, -1,
    { { 10, 23 }, {-1} } },

  // These check boundary cases during negation.
  { { { 0, Runemax }, {-1} }, -1,
    { { 0, Runemax }, {-1} } },

  { { { 0, 50 }, {-1} }, -1,
    { { 0, 50 }, {-1} } },

  { { { 50, Runemax }, {-1} }, -1,
    { { 50, Runemax }, {-1} } },

  // Check RemoveAbove.
  { { { 50, Runemax }, {-1} }, 255,
    { { 50, 255 }, {-1} } },

  { { { 50, Runemax }, {-1} }, 65535,
    { { 50, 65535 }, {-1} } },

  { { { 50, Runemax }, {-1} }, Runemax,
    { { 50, Runemax }, {-1} } },

  { { { 50, 60 }, { 250, 260 }, { 350, 360 }, {-1} }, 255,
    { { 50, 60 }, { 250, 255 }, {-1} } },

  { { { 50, 60 }, {-1} }, 255,
    { { 50, 60 }, {-1} } },

  { { { 350, 360 }, {-1} }, 255,
    { {-1} } },

  { { {-1} }, 255,
    { {-1} } },
};

template <typename CharClass>
static void Broke(const char *desc, const CCTest* t, CharClass* cc) {
  if (t == NULL) {
    printf("\t%s:", desc);
  } else {
    printf("\n");
    printf("CharClass added: [%s]", desc);
    for (int k = 0; t->add[k].lo >= 0; k++)
      printf(" %d-%d", t->add[k].lo, t->add[k].hi);
    printf("\n");
    if (t->remove >= 0)
      printf("Removed > %d\n", t->remove);
    printf("\twant:");
    for (int k = 0; t->final[k].lo >= 0; k++)
      printf(" %d-%d", t->final[k].lo, t->final[k].hi);
    printf("\n");
    printf("\thave:");
  }

  for (typename CharClass::iterator it = cc->begin(); it != cc->end(); ++it)
    printf(" %d-%d", it->lo, it->hi);
  printf("\n");
}

bool ShouldContain(CCTest *t, int x) {
  for (int j = 0; t->final[j].lo >= 0; j++)
    if (t->final[j].lo <= x && x <= t->final[j].hi)
      return true;
  return false;
}

// Helpers to make templated CorrectCC work with both CharClass and CharClassBuilder.

CharClass* Negate(CharClass *cc) {
  return cc->Negate();
}

void Delete(CharClass* cc) {
  cc->Delete();
}

CharClassBuilder* Negate(CharClassBuilder* cc) {
  CharClassBuilder* ncc = cc->Copy();
  ncc->Negate();
  return ncc;
}

void Delete(CharClassBuilder* cc) {
  delete cc;
}

template <typename CharClass>
bool CorrectCC(CharClass *cc, CCTest *t, const char *desc) {
  typename CharClass::iterator it = cc->begin();
  int size = 0;
  for (int j = 0; t->final[j].lo >= 0; j++, ++it) {
    if (it == cc->end() ||
        it->lo != t->final[j].lo ||
        it->hi != t->final[j].hi) {
      Broke(desc, t, cc);
      return false;
    }
    size += it->hi - it->lo + 1;
  }
  if (it != cc->end()) {
    Broke(desc, t, cc);
    return false;
  }
  if (cc->size() != size) {
    Broke(desc, t, cc);
    printf("wrong size: want %d have %d\n", size, cc->size());
    return false;
  }

  for (int j = 0; j < 101; j++) {
    if (j == 100)
      j = Runemax;
    if (ShouldContain(t, j) != cc->Contains(j)) {
      Broke(desc, t, cc);
      printf("want contains(%d)=%d, got %d\n",
             j, ShouldContain(t, j), cc->Contains(j));
      return false;
    }
  }

  CharClass* ncc = Negate(cc);
  for (int j = 0; j < 101; j++) {
    if (j == 100)
      j = Runemax;
    if (ShouldContain(t, j) == ncc->Contains(j)) {
      Broke(desc, t, cc);
      Broke("ncc", NULL, ncc);
      printf("want ncc contains(%d)!=%d, got %d\n",
             j, ShouldContain(t, j), ncc->Contains(j));
      Delete(ncc);
      return false;
    }
    if (ncc->size() != Runemax+1 - cc->size()) {
      Broke(desc, t, cc);
      Broke("ncc", NULL, ncc);
      printf("ncc size should be %d is %d\n",
             Runemax+1 - cc->size(), ncc->size());
      Delete(ncc);
      return false;
    }
  }
  Delete(ncc);
  return true;
}

TEST(TestCharClassBuilder, Adds) {
  int nfail = 0;
  for (size_t i = 0; i < arraysize(tests); i++) {
    CharClassBuilder ccb;
    CCTest* t = &tests[i];
    for (int j = 0; t->add[j].lo >= 0; j++)
      ccb.AddRange(t->add[j].lo, t->add[j].hi);
    if (t->remove >= 0)
      ccb.RemoveAbove(t->remove);
    if (!CorrectCC(&ccb, t, "before copy (CharClassBuilder)"))
      nfail++;
    CharClass* cc = ccb.GetCharClass();
    if (!CorrectCC(cc, t, "before copy (CharClass)"))
      nfail++;
    cc->Delete();

    CharClassBuilder *ccb1 = ccb.Copy();
    if (!CorrectCC(ccb1, t, "after copy (CharClassBuilder)"))
      nfail++;
    cc = ccb.GetCharClass();
    if (!CorrectCC(cc, t, "after copy (CharClass)"))
      nfail++;
    cc->Delete();
    delete ccb1;
  }
  EXPECT_EQ(nfail, 0);
}

}  // namespace re2
