char rcsid_table[] = "$Id$";

#include "b.h"
#include <string.h>
#include <stdio.h>

static void growIndex_Map ARGS((Index_Map *));
static Relevant newRelevant ARGS((void));
static Dimension newDimension ARGS((Operator, int));
static void GT_1 ARGS((Table));
static void GT_2_0 ARGS((Table));
static void GT_2_1 ARGS((Table));
static void growTransition ARGS((Table, int));
static Item_Set restrict ARGS((Dimension, Item_Set));
static void addHP_1 ARGS((Table, Item_Set));
static void addHP_2_0 ARGS((Table, Item_Set));
static void addHP_2_1 ARGS((Table, Item_Set));
static void addHyperPlane ARGS((Table, int, Item_Set));

static void
growIndex_Map(r) Index_Map *r;
{
  Index_Map new;

  new.max_size = r->max_size + STATES_INCR;
  new.class = (Item_Set*) zalloc(new.max_size * sizeof(Item_Set));
  assert(new.class);
  memcpy(new.class, r->class, r->max_size * sizeof(Item_Set));
  zfree(r->class);
  *r = new;
}

static Relevant
newRelevant()
{
  Relevant r = (Relevant) zalloc(max_nonterminal * sizeof(*r));
  return r;
}

void
addRelevant(r, nt) Relevant r; NonTerminalNum nt;
{
  int i;

  for (i = 0; r[i]; i++) {
    if (r[i] == nt) {
      break;
    }
  }
  if (!r[i]) {
    r[i] = nt;
  }
}

static Dimension
newDimension(op, index) Operator op; ArityNum index;
{
  Dimension d;
  List pl;
  Relevant r;

  assert(op);
  assert(index >= 0 && index < op->arity);
  d = (Dimension) zalloc(sizeof(struct dimension));
  assert(d);

  r = d->relevant = newRelevant();
  for (pl = rules; pl; pl = pl->next) {
    Rule pr = (Rule) pl->x;
    if (pr->pat->op == op) {
      addRelevant(r, pr->pat->children[index]->num);
    }
  }

  d->index_map.max_size = STATES_INCR;
  d->index_map.class = (Item_Set*)
      zalloc(d->index_map.max_size * sizeof(Item_Set));
  d->map = newMapping(DIM_MAP_SIZE);
  d->max_size = TABLE_INCR;

  return d;
}

Table
newTable(op) Operator op;
{
  Table t;
  int i, size;

  assert(op);

  t = (Table) zalloc(sizeof(struct table));
  assert(t);

  t->op = op;

  for (i = 0; i < op->arity; i++) {
    t->dimen[i] = newDimension(op, i);
  }

  size = 1;
  for (i = 0; i < op->arity; i++) {
    size *= t->dimen[i]->max_size;
  }
  t->transition = (Item_Set*) zalloc(size * sizeof(Item_Set));
  t->relevant = newRelevant();
  assert(t->transition);

  return t;
}

static void
GT_1(t) Table t;
{
  Item_Set  *ts;
  ItemSetNum  oldsize = t->dimen[0]->max_size;
  ItemSetNum  newsize = t->dimen[0]->max_size + TABLE_INCR;

  t->dimen[0]->max_size = newsize;

  ts = (Item_Set*) zalloc(newsize * sizeof(Item_Set));
  assert(ts);
  memcpy(ts, t->transition, oldsize * sizeof(Item_Set));
  zfree(t->transition);
  t->transition = ts;
}

static void
GT_2_0(t) Table t;
{
  Item_Set  *ts;
  ItemSetNum  oldsize = t->dimen[0]->max_size;
  ItemSetNum  newsize = t->dimen[0]->max_size + TABLE_INCR;
  int   size;

  t->dimen[0]->max_size = newsize;

  size = newsize * t->dimen[1]->max_size;

  ts = (Item_Set*) zalloc(size * sizeof(Item_Set));
  assert(ts);
  memcpy(ts, t->transition, oldsize*t->dimen[1]->max_size * sizeof(Item_Set));
  zfree(t->transition);
  t->transition = ts;
}

static void
GT_2_1(t) Table t;
{
  Item_Set  *ts;
  ItemSetNum  oldsize = t->dimen[1]->max_size;
  ItemSetNum  newsize = t->dimen[1]->max_size + TABLE_INCR;
  int   size;
  Item_Set  *from;
  Item_Set  *to;
  int     i1, i2;

  t->dimen[1]->max_size = newsize;

  size = newsize * t->dimen[0]->max_size;

  ts = (Item_Set*) zalloc(size * sizeof(Item_Set));
  assert(ts);

  from = t->transition;
  to = ts;
  for (i1 = 0; i1 < t->dimen[0]->max_size; i1++) {
    for (i2 = 0; i2 < oldsize; i2++) {
      to[i2] = from[i2];
    }
    to += newsize;
    from += oldsize;
  }
  zfree(t->transition);
  t->transition = ts;
}

static void
growTransition(t, dim) Table t; ArityNum dim;
{

  assert(t);
  assert(t->op);
  assert(dim < t->op->arity);

  switch (t->op->arity) {
  default:
    assert(0);
    break;
  case 1:
    GT_1(t);
    return;
  case 2:
    switch (dim) {
    default:
      assert(0);
      break;
    case 0:
      GT_2_0(t);
      return;
    case 1:
      GT_2_1(t);
      return;
    }
  }
}

static Item_Set
restrict(d, ts) Dimension d; Item_Set ts;
{
  DeltaCost base;
  Item_Set  r;
  int found;
  register Relevant r_ptr = d->relevant;
  register Item *ts_current = ts->closed;
  register Item *r_current;
  register int i;
  register int nt;

  ZEROCOST(base);
  found = 0;
  r = newItem_Set(d->relevant);
  r_current = r->virgin;
  for (i = 0; (nt = r_ptr[i]) != 0; i++) {
    if (ts_current[nt].rule) {
      r_current[nt].rule = &stub_rule;
      if (!found) {
        found = 1;
        ASSIGNCOST(base, ts_current[nt].delta);
      } else {
        if (LESSCOST(ts_current[nt].delta, base)) {
          ASSIGNCOST(base, ts_current[nt].delta);
        }
      }
    }
  }

  /* zero align */
  for (i = 0; (nt = r_ptr[i]) != 0; i++) {
    if (r_current[nt].rule) {
      ASSIGNCOST(r_current[nt].delta, ts_current[nt].delta);
      MINUSCOST(r_current[nt].delta, base);
    }
  }
  assert(!r->closed);
  r->representative = ts;
  return r;
}

static void
addHP_1(t, ts) Table t; Item_Set ts;
{
  List pl;
  Item_Set e;
  Item_Set tmp;
  int new;

  e = newItem_Set(t->relevant);
  assert(e);
  e->kids[0] = ts->representative;
  for (pl = t->rules; pl; pl = pl->next) {
    Rule p = (Rule) pl->x;
    if (t->op == p->pat->op && ts->virgin[p->pat->children[0]->num].rule) {
      DeltaCost dc;
      ASSIGNCOST(dc, ts->virgin[p->pat->children[0]->num].delta);
      ADDCOST(dc, p->delta);
      if (!e->virgin[p->lhs->num].rule || LESSCOST(dc, e->virgin[p->lhs->num].delta)) {
        e->virgin[p->lhs->num].rule = p;
        ASSIGNCOST(e->virgin[p->lhs->num].delta, dc);
        e->op = t->op;
      }
    }
  }
  trim(e);
  zero(e);
  tmp = encode(globalMap, e, &new);
  assert(ts->num < t->dimen[0]->map->max_size);
  t->transition[ts->num] = tmp;
  if (new) {
    closure(e);
    addQ(globalQ, tmp);
  } else {
    freeItem_Set(e);
  }
}

static void
addHP_2_0(t, ts) Table t; Item_Set ts;
{
  List pl;
  register Item_Set e;
  Item_Set tmp;
  int new;
  int i2;

  assert(t->dimen[1]->map->count <= t->dimen[1]->map->max_size);
  for (i2 = 0; i2 < t->dimen[1]->map->count; i2++) {
    e = newItem_Set(t->relevant);
    assert(e);
    e->kids[0] = ts->representative;
    e->kids[1] = t->dimen[1]->map->set[i2]->representative;
    for (pl = t->rules; pl; pl = pl->next) {
      register Rule p = (Rule) pl->x;

      if (t->op == p->pat->op
          && ts->virgin[p->pat->children[0]->num].rule
          && t->dimen[1]->map->set[i2]->virgin[p->pat->children[1]->num].rule){
        DeltaCost dc;
        ASSIGNCOST(dc, p->delta);
        ADDCOST(dc, ts->virgin[p->pat->children[0]->num].delta);
        ADDCOST(dc, t->dimen[1]->map->set[i2]->virgin[p->pat->children[1]->num].delta);

        if (!e->virgin[p->lhs->num].rule || LESSCOST(dc, e->virgin[p->lhs->num].delta)) {
          e->virgin[p->lhs->num].rule = p;
          ASSIGNCOST(e->virgin[p->lhs->num].delta, dc);
          e->op = t->op;
        }
      }
    }
    trim(e);
    zero(e);
    tmp = encode(globalMap, e, &new);
    assert(ts->num < t->dimen[0]->map->max_size);
    t->transition[ts->num * t->dimen[1]->max_size + i2] = tmp;
    if (new) {
      closure(e);
      addQ(globalQ, tmp);
    } else {
      freeItem_Set(e);
    }
  }
}

static void
addHP_2_1(t, ts) Table t; Item_Set ts;
{
  List pl;
  register Item_Set e;
  Item_Set tmp;
  int new;
  int i1;

  assert(t->dimen[0]->map->count <= t->dimen[0]->map->max_size);
  for (i1 = 0; i1 < t->dimen[0]->map->count; i1++) {
    e = newItem_Set(t->relevant);
    assert(e);
    e->kids[0] = t->dimen[0]->map->set[i1]->representative;
    e->kids[1] = ts->representative;
    for (pl = t->rules; pl; pl = pl->next) {
      register Rule p = (Rule) pl->x;

      if (t->op == p->pat->op
          && ts->virgin[p->pat->children[1]->num].rule
          && t->dimen[0]->map->set[i1]->virgin[p->pat->children[0]->num].rule){
        DeltaCost dc;
        ASSIGNCOST(dc, p->delta );
        ADDCOST(dc, ts->virgin[p->pat->children[1]->num].delta);
        ADDCOST(dc, t->dimen[0]->map->set[i1]->virgin[p->pat->children[0]->num].delta);
        if (!e->virgin[p->lhs->num].rule || LESSCOST(dc, e->virgin[p->lhs->num].delta)) {
          e->virgin[p->lhs->num].rule = p;
          ASSIGNCOST(e->virgin[p->lhs->num].delta, dc);
          e->op = t->op;
        }
      }
    }
    trim(e);
    zero(e);
    tmp = encode(globalMap, e, &new);
    assert(ts->num < t->dimen[1]->map->max_size);
    t->transition[i1 * t->dimen[1]->max_size + ts->num] = tmp;
    if (new) {
      closure(e);
      addQ(globalQ, tmp);
    } else {
      freeItem_Set(e);
    }
  }
}

static void
addHyperPlane(t, i, ts) Table t; ArityNum i; Item_Set ts;
{
  switch (t->op->arity) {
  default:
    assert(0);
    break;
  case 1:
    addHP_1(t, ts);
    return;
  case 2:
    switch (i) {
    default:
      assert(0);
      break;
    case 0:
      addHP_2_0(t, ts);
      return;
    case 1:
      addHP_2_1(t, ts);
      return;
    }
  }
}

void
addToTable(t, ts) Table t; Item_Set ts;
{
  ArityNum i;

  assert(t);
  assert(ts);
  assert(t->op);

  for (i = 0; i < t->op->arity; i++) {
    Item_Set r;
    Item_Set tmp;
    int new;

    r = restrict(t->dimen[i], ts);
    tmp = encode(t->dimen[i]->map, r, &new);
    if (t->dimen[i]->index_map.max_size <= ts->num) {
      growIndex_Map(&t->dimen[i]->index_map);
    }
    assert(ts->num < t->dimen[i]->index_map.max_size);
    t->dimen[i]->index_map.class[ts->num] = tmp;
    if (new) {
      if (t->dimen[i]->max_size <= r->num) {
        growTransition(t, i);
      }
      addHyperPlane(t, i, r);
    } else {
      freeItem_Set(r);
    }
  }
}

Item_Set *
transLval(t, row, col) Table t; int row; int col;
{
  switch (t->op->arity) {
  case 0:
    assert(row == 0);
    assert(col == 0);
    return t->transition;
  case 1:
    assert(col == 0);
    return t->transition + row;
  case 2:
    return t->transition + row * t->dimen[1]->max_size + col;
  default:
    assert(0);
  }
  return 0;
}

void
dumpRelevant(r) Relevant r;
{
  for (; *r; r++) {
    printf("%4d", *r);
  }
}

void
dumpIndex_Map(r) Index_Map *r;
{
  int i;

  printf("BEGIN Index_Map: MaxSize (%d)\n", r->max_size);
  for (i = 0; i < globalMap->count; i++) {
    printf("\t#%d: -> %d\n", i, r->class[i]->num);
  }
  printf("END Index_Map:\n");
}

void
dumpDimension(d) Dimension d;
{
  printf("BEGIN Dimension:\n");
  printf("Relevant: ");
  dumpRelevant(d->relevant);
  printf("\n");
  dumpIndex_Map(&d->index_map);
  dumpMapping(d->map);
  printf("MaxSize of dimension = %d\n", d->max_size);
  printf("END Dimension\n");
}

void
dumpTable(t, full) Table t; int full;
{
  int i;

  if (!t) {
    printf("NO Table yet.\n");
    return;
  }
  printf("BEGIN Table:\n");
  if (full) {
    dumpOperator(t->op, 0);
  }
  for (i = 0; i < t->op->arity; i++) {
    printf("BEGIN dimension(%d)\n", i);
    dumpDimension(t->dimen[i]);
    printf("END dimension(%d)\n", i);
  }
  dumpTransition(t);
  printf("END Table:\n");
}

void
dumpTransition(t) Table t;
{
  int i,j;

  switch (t->op->arity) {
  case 0:
    printf("{ %d }", t->transition[0]->num);
    break;
  case 1:
    printf("{");
    for (i = 0; i < t->dimen[0]->map->count; i++) {
      if (i > 0) {
        printf(",");
      }
      printf("%5d", t->transition[i]->num);
    }
    printf("}");
    break;
  case 2:
    printf("{");
    for (i = 0; i < t->dimen[0]->map->count; i++) {
      if (i > 0) {
        printf(",");
      }
      printf("\n");
      printf("{");
      for (j = 0; j < t->dimen[1]->map->count; j++) {
        Item_Set *ts = transLval(t, i, j);
        if (j > 0) {
          printf(",");
        }
        printf("%5d", (*ts)->num);
      }
      printf("}");
    }
    printf("\n}\n");
    break;
  default:
    assert(0);
  }
}
