/** @file

  A set of Map templates.

  @section license License

  Licensed to the Apache Software Foundation (ASF) under one
  or more contributor license agreements.  See the NOTICE file
  distributed with this work for additional information
  regarding copyright ownership.  The ASF licenses this file
  to you under the Apache License, Version 2.0 (the
  "License"); you may not use this file except in compliance
  with the License.  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
*/

#ifndef _map_H_
#define _map_H_

#include <stdlib.h>
#include <string.h>
#include "ink_assert.h"
#include "List.h"
#include "Vec.h"

#define MAP_INTEGRAL_SIZE               (1 << (2))
#define MAP_INITIAL_SHIFT               ((2)+1)
#define MAP_INITIAL_SIZE                (1 << MAP_INITIAL_SHIFT)

typedef const char cchar;

template<class A>
static inline char *
_dupstr(cchar *s, cchar *e = 0) {
  int l = e ? e-s : strlen(s);
  char *ss = (char*)A::alloc(l+1);
  memcpy(ss, s, l);
  ss[l] = 0;
  return ss;
}

// Simple direct mapped Map (pointer hash table) and Environment

template <class K, class C> class MapElem {
 public:
  K     key;
  C     value;
  bool operator==(MapElem &e) { return e.key == key; }
  operator uintptr_t(void) { return (uintptr_t)(uintptr_t)key; }
  MapElem(uintptr_t x) { ink_assert(!x); key = 0; }
  MapElem(K akey, C avalue) : key(akey), value(avalue) {}
  MapElem(MapElem &e) : key(e.key), value(e.value) {}
  MapElem() : key(0) {}
};

template <class K, class C, class A = DefaultAlloc> class Map : public Vec<MapElem<K,C>, A> {
 public:
  typedef MapElem<K,C> ME;
  typedef Vec<ME,A> PType;
  using PType::n;
  using PType::i;
  using PType::v;
  ME *put(K akey, C avalue);
  ME *put(K akey);
  C get(K akey);
  C *getp(K akey);
  void get_keys(Vec<K> &keys);
  void get_keys_set(Vec<K> &keys);
  void get_values(Vec<C> &values);
  void map_union(Map<K,C> &m);
  int some_disjunction(Map<K,C> &m);
};

template <class C> class HashFns {
 public:
  static uintptr_t hash(C a);
  static int equal(C a, C b);
};

template <class K, class C> class HashSetFns {
 public:
  static uintptr_t hash(C a);
  static uintptr_t hash(K a);
  static int equal(C a, C b);
  static int equal(K a, C b);
};

template <class K, class AHashFns, class C, class A = DefaultAlloc> class HashMap : public Map<K,C,A> {
 public:
  using Map<K,C,A>::n;
  using Map<K,C,A>::i;
  using Map<K,C,A>::v;
  using Map<K,C,A>::e;
  MapElem<K,C> *get_internal(K akey);
  C get(K akey);
  MapElem<K,C> *put(K akey, C avalue);
  void get_keys(Vec<K> &keys);
  void get_values(Vec<C> &values);
};

#define form_Map(_c, _p, _v) if ((_v).n) for (_c *qq__##_p = (_c*)0, *_p = &(_v).v[0]; \
             ((intptr_t)(qq__##_p) < (_v).n) && ((_p = &(_v).v[(intptr_t)qq__##_p]) || 1); \
             qq__##_p = (_c*)(((intptr_t)qq__##_p) + 1)) \
          if ((_p)->key)


template <class K, class AHashFns, class C, class A = DefaultAlloc> class HashSet : public Vec<C,A> {
 public:
  typedef Vec<C,A> V;
  using V::n;
  using V::i;
  using V::v;
  using V::e;
  C get(K akey);
  C *put( C avalue);
};

class StringHashFns {
 public:
  static uintptr_t hash(cchar *s) { 
    uintptr_t h = 0; 
    // 31 changed to 27, to avoid prime2 in vec.cpp
    while (*s) h = h * 27 + (unsigned char)*s++;  
    return h;
  }
  static int equal(cchar *a, cchar *b) { return !strcmp(a, b); }
};

class CaseStringHashFns {
 public:
  static uintptr_t hash(cchar *s) { 
    uintptr_t h = 0; 
    // 31 changed to 27, to avoid prime2 in vec.cpp
    while (*s) h = h * 27 + (unsigned char)toupper(*s++);
    return h;
  }
  static int equal(cchar *a, cchar *b) { return !strcasecmp(a, b); }
};

class PointerHashFns {
 public:
  static uintptr_t hash(void *s) { return (uintptr_t)(uintptr_t)s; }
  static int equal(void *a, void *b) { return a == b; }
};

template <class C, class AHashFns, class A = DefaultAlloc> class ChainHash : public Map<uintptr_t, List<C,A>,A> {
 public:
  using Map<uintptr_t, List<C,A>,A>::n;
  using Map<uintptr_t, List<C,A>,A>::v;
  typedef ConsCell<C, A> ChainCons;
  C put(C c);
  C get(C c);
  C put_bag(C c);
  int get_bag(C c,Vec<C> &v);
  int del(C avalue);
  void get_elements(Vec<C> &elements);
};

template <class K, class AHashFns, class C, class A = DefaultAlloc> class ChainHashMap : 
  public Map<uintptr_t, List<MapElem<K,C>,A>,A> {
 public:
  using Map<uintptr_t, List<MapElem<K,C>,A>,A>::n;
  using Map<uintptr_t, List<MapElem<K,C>,A>,A>::v;
  MapElem<K,C> *put(K akey, C avalue);
  C get(K akey);
  int del(K akey);
  MapElem<K,C> *put_bag(K akey, C c);
  int get_bag(K akey, Vec<C> &v);
  void get_keys(Vec<K> &keys);
  void get_values(Vec<C> &values);
};

template<class F = StringHashFns, class A = DefaultAlloc>
class StringChainHash : public ChainHash<cchar *, F, A> {
 public:
  cchar *canonicalize(cchar *s, cchar *e);
  cchar *canonicalize(cchar *s) { return canonicalize(s, s + strlen(s)); }
};

template <class C, class AHashFns, int N, class A = DefaultAlloc> class NBlockHash {
 public:
  int n;
  int i;
  C *v;
  C e[N];

  C* end() { return last(); }
  int length() { return N * n; }
  C *first();
  C *last();
  C put(C c);
  C get(C c);
  C* assoc_put(C *c);
  C* assoc_get(C *c);
  int del(C c);
  void clear();
  void reset();
  int count();
  void size(int p2);
  void copy(const NBlockHash<C,AHashFns,N,A> &hh);
  void move(NBlockHash<C,AHashFns,N,A> &hh);
  NBlockHash();
  NBlockHash(NBlockHash<C,AHashFns,N,A> &hh) { v = e; copy(hh); }
};

/* use forv_Vec on BlockHashes */

#define DEFAULT_BLOCK_HASH_SIZE 4
template <class C, class ABlockHashFns> class BlockHash : 
  public NBlockHash<C, ABlockHashFns, DEFAULT_BLOCK_HASH_SIZE> {};
typedef BlockHash<cchar *, StringHashFns> StringBlockHash;

template <class K, class C, class A = DefaultAlloc> class Env {
 public:
  typedef ConsCell<C, A> EnvCons;
  void put(K akey, C avalue);
  C get(K akey);
  void push();
  void pop();
  void clear() { store.clear(); scope.clear(); }

  Env() {}
  Map<K,List<C> *, A> store;
  List<List<K>, A> scope;
  List<C, A> *get_bucket(K akey);
};

/* IMPLEMENTATION */

template <class K, class C, class A> inline C 
Map<K,C,A>::get(K akey) {
  MapElem<K,C> e(akey, (C)0);
  MapElem<K,C> *x = this->set_in(e);
  if (x)
    return x->value;
  return (C)0;
}

template <class K, class C, class A> inline C *
Map<K,C,A>::getp(K akey) {
  MapElem<K,C> e(akey, (C)0);
  MapElem<K,C> *x = this->set_in(e);
  if (x)
    return &x->value;
  return 0;
}

template <class K, class C, class A> inline MapElem<K,C> *
Map<K,C,A>::put(K akey, C avalue) {
  MapElem<K,C> e(akey, avalue);
  MapElem<K,C> *x = this->set_in(e);
  if (x) {
    x->value = avalue;
    return x;
  } else
    return this->set_add(e);
}

template <class K, class C, class A> inline MapElem<K,C> *
Map<K,C,A>::put(K akey) {
  MapElem<K,C> e(akey, 0);
  MapElem<K,C> *x = this->set_in(e);
  if (x)
    return x;
  else
    return this->set_add(e);
}

template <class K, class C, class A> inline void
Map<K,C,A>::get_keys(Vec<K> &keys) {
  for (int i = 0; i < n; i++)
    if (v[i].key)
      keys.add(v[i].key);
}

template <class K, class C, class A> inline void
Map<K,C,A>::get_keys_set(Vec<K> &keys) {
  for (int i = 0; i < n; i++)
    if (v[i].key)
      keys.set_add(v[i].key);
}

template <class K, class C, class A> inline void
Map<K,C,A>::get_values(Vec<C> &values) {
  for (int i = 0; i < n; i++)
    if (v[i].key)
      values.set_add(v[i].value);
  values.set_to_vec();
}

template <class K, class C, class A> inline void
Map<K,C,A>::map_union(Map<K,C> &m) {
  for (int i = 0; i < m.n; i++)
    if (m.v[i].key)
      put(m.v[i].key, m.v[i].value);
}

template <class K, class C, class A> inline int
Map<K,C,A>::some_disjunction(Map<K,C> &m) {
  for (int i = 0; i < m.n; i++)
    if (m.v[i].key && get(m.v[i].key) != m.v[i].value)
      return 1;
  for (int i = 0; i < n; i++)
    if (v[i].key && m.get(v[i].key) != v[i].value)
      return 1;
  return 0;
}

template <class K, class C, class A> inline void
map_set_add(Map<K,Vec<C,A>*,A> &m, K akey, C avalue) {
  Vec<C,A> *v = m.get(akey);
  if (!v)
    m.put(akey, (v = new Vec<C,A>));
  v->set_add(avalue);
}

template <class K, class C, class A> inline void
map_set_add(Map<K,Vec<C,A>*,A> &m, K akey, Vec<C> *madd) {
  Vec<C,A> *v = m.get(akey);
  if (!v)
    m.put(akey, (v = new Vec<C,A>));
  v->set_union(*madd);
}

template <class K, class AHashFns, class C, class A> inline C
HashSet<K, AHashFns, C, A>::get(K akey) {
  if (!n)
    return 0;
  if (n <= MAP_INTEGRAL_SIZE) {
    for (C *c = v; c < v + n; c++)
      if (c)
        if (AHashFns::equal(akey, *c))
          return *c;
    return 0;
  }
  uintptr_t h = AHashFns::hash(akey);
  h = h % n;
  for (int k = h, j = 0; j < i + 3;j++) {
    if (!v[k])
      return 0;
    else if (AHashFns::equal(akey, v[k]))
      return v[k];
    k = (k + open_hash_primes[j]) % n;
  }
  return 0;
}

template <class K, class AHashFns, class C, class A> inline C *
HashSet<K, AHashFns, C, A>::put(C avalue) {
  if (n < MAP_INTEGRAL_SIZE) {
    if (!v)
      v = e;
    for (int i = 0; i < n; i++)
      if (AHashFns::equal(avalue, v[i]))
        return &v[i];
    v[n] = avalue;
    n++;
    return &v[n-1];
  }
  if (n > MAP_INTEGRAL_SIZE) {
    uintptr_t h = AHashFns::hash(avalue);
    h = h % n;
    for (int k = h, j = 0; j < i + 3; j++) {
      if (!v[k]) {
        v[k] = avalue;
        return &v[k];
      }
      k = (k + open_hash_primes[j]) % n;
    }
  } else
    i = SET_INITIAL_INDEX-1; // will be incremented in set_expand
  HashSet<K,AHashFns,C,A> vv(*this);
  Vec<C,A>::set_expand();
  for (int i = 0; i < vv.n; i++)
    if (vv.v[i])
      put(vv.v[i]);
  return put(avalue);
}

template <class K, class AHashFns, class C, class A> inline MapElem<K,C> * 
HashMap<K,AHashFns,C,A>::get_internal(K akey) {
  if (!n)
    return 0;
  if (n <= MAP_INTEGRAL_SIZE) {
    for (MapElem<K,C> *c = v; c < v + n; c++)
      if (c->key)
        if (AHashFns::equal(akey, c->key))
          return c;
    return 0;
  }
  uintptr_t h = AHashFns::hash(akey);
  h = h % n;
  for (int k = h, j = 0; j < i + 3;j++) {
    if (!v[k].key)
      return 0;
    else if (AHashFns::equal(akey, v[k].key))
      return &v[k];
    k = (k + open_hash_primes[j]) % n;
  }
  return 0;
}

template <class K, class AHashFns, class C, class A> inline C 
HashMap<K,AHashFns,C,A>::get(K akey) {
  MapElem<K,C> *x = get_internal(akey);
  if (!x)
    return 0;
  return x->value;
}

template <class K, class AHashFns, class C, class A> inline MapElem<K,C> *
HashMap<K,AHashFns,C,A>::put(K akey, C avalue) {
  MapElem<K,C> *x = get_internal(akey);
  if (x) {
    x->value = avalue;
    return x;
  } else {
    if (n < MAP_INTEGRAL_SIZE) {
      if (!v)
        v = e;
      v[n].key = akey;
      v[n].value = avalue;
      n++;
      return &v[n-1];
    }
    if (n > MAP_INTEGRAL_SIZE) {
      uintptr_t h = AHashFns::hash(akey);
      h = h % n;
      for (int k = h, j = 0; j < i + 3; j++) {
        if (!v[k].key) {
          v[k].key = akey;
          v[k].value = avalue;
          return &v[k];
        }
        k = (k + open_hash_primes[j]) % n;
      }
    } else
      i = SET_INITIAL_INDEX-1; // will be incremented in set_expand
  }
  HashMap<K,AHashFns,C,A> vv(*this);
  Map<K,C,A>::set_expand();
  for (int i = 0; i < vv.n; i++)
    if (vv.v[i].key)
      put(vv.v[i].key, vv.v[i].value);
  return put(akey, avalue);
}

template <class K, class AHashFns, class C, class A> inline void
HashMap<K,AHashFns,C,A>::get_keys(Vec<K> &keys) { Map<K,C,A>::get_keys(keys); }

template <class K, class AHashFns, class C, class A> inline void
HashMap<K,AHashFns,C,A>::get_values(Vec<C> &values) { Map<K,C,A>::get_values(values); }

template <class C, class AHashFns, class A> C
ChainHash<C, AHashFns, A>::put(C c) {
  uintptr_t h = AHashFns::hash(c);
  List<C,A> *l;
  MapElem<uintptr_t,List<C,A> > e(h, (C)0);
  MapElem<uintptr_t,List<C,A> > *x = this->set_in(e);
  if (x)
    l = &x->value;
  else {
    l = &Map<uintptr_t, List<C,A>, A>::put(h, c)->value;
    return l->head->car;
  }
  forc_List(ChainCons, x, *l)
    if (AHashFns::equal(c, x->car))
      return x->car;
  l->push(c);
  return (C)0;
}

template <class C, class AHashFns, class A> C
ChainHash<C, AHashFns, A>::get(C c) {
  uintptr_t h = AHashFns::hash(c);
  List<C> empty;
  MapElem<uintptr_t,List<C,A> > e(h, empty);
  MapElem<uintptr_t,List<C,A> > *x = this->set_in(e);
  if (!x)
    return 0;
  List<C> *l = &x->value;
  forc_List(ChainCons, x, *l)
    if (AHashFns::equal(c, x->car))
      return x->car;
  return 0;
}

template <class C, class AHashFns, class A> C
ChainHash<C, AHashFns, A>::put_bag(C c) {
  uintptr_t h = AHashFns::hash(c);
  List<C, A> *l;
  MapElem<uintptr_t,List<C,A> > e(h, (C)0);
  MapElem<uintptr_t,List<C,A> > *x = this->set_in(e);
  if (x)
    l = &x->value;
  else {
    l = &Map<uintptr_t, List<C,A> >::put(h, c)->value;
    return l->head->car;
  }
  l->push(c);
  return (C)0;
}

template <class C, class AHashFns, class A> int
ChainHash<C, AHashFns, A>::get_bag(C c, Vec<C> &v) {
  uintptr_t h = AHashFns::hash(c);
  List<C,A> empty;
  MapElem<uintptr_t,List<C,A> > e(h, empty);
  MapElem<uintptr_t,List<C,A> > *x = this->set_in(e);
  if (!x)
    return 0;
  List<C,A> *l = &x->value;
  forc_List(C, x, *l)
    if (AHashFns::equal(c, x->car))
      v.add(x->car);
  return v.n;
}

template <class C, class AHashFns, class A> void
ChainHash<C, AHashFns, A>::get_elements(Vec<C> &elements) {
  for (int i = 0; i < n; i++) {
    List<C, A> *l = &v[i].value;
    forc_List(C, x, *l)
      elements.add(x);
  }
}

template <class C, class AHashFns, class A> int
ChainHash<C, AHashFns, A>::del(C c) {
  uintptr_t h = AHashFns::hash(c);
  List<C> *l;
  MapElem<uintptr_t,List<C,A> > e(h, (C)0);
  MapElem<uintptr_t,List<C,A> > *x = this->set_in(e);
  if (x)
    l = &x->value;
  else
    return 0;
  ConsCell<C> *last = 0;
  forc_List(ConsCell<C>, x, *l) {
    if (AHashFns::equal(c, x->car)) {
      if (!last)
        l->head = x->cdr;
      else
        last->cdr = x->cdr;
      A::free(x);
      return 1;
    }
    last = x;
  }
  return 0;
}

template <class K, class AHashFns, class C, class A>  MapElem<K,C> *
ChainHashMap<K, AHashFns, C, A>::put(K akey, C avalue) {
  uintptr_t h = AHashFns::hash(akey);
  List<MapElem<K,C>,A> empty;
  List<MapElem<K,C>,A> *l;
  MapElem<K, C> c(akey, avalue);
  MapElem<uintptr_t,List<MapElem<K,C>,A> > e(h, empty);
  MapElem<uintptr_t,List<MapElem<K,C>,A> > *x = this->set_in(e);
  if (x)
    l = &x->value;
  else {
    l = &Map<uintptr_t, List<MapElem<K,C>,A>,A>::put(h, c)->value;
    return &l->head->car;
  }
  for (ConsCell<MapElem<K,C>,A> *p  = l->head; p; p = p->cdr)
    if (AHashFns::equal(akey, p->car.key)) {
      p->car.value = avalue;
      return &p->car;
    }
  l->push(c);
  return 0;
}

template <class K, class AHashFns, class C, class A> C
ChainHashMap<K, AHashFns, C, A>::get(K akey) {
  uintptr_t h = AHashFns::hash(akey);
  List<MapElem<K,C>, A> empty;
  MapElem<uintptr_t,List<MapElem<K,C>,A> > e(h, empty);
  MapElem<uintptr_t,List<MapElem<K,C>,A> > *x = this->set_in(e);
  if (!x)
    return 0;
  List<MapElem<K,C>,A> *l = &x->value;
  if (l->head) 
    for (ConsCell<MapElem<K,C>,A> *p  = l->head; p; p = p->cdr)
      if (AHashFns::equal(akey, p->car.key))
        return p->car.value;
  return 0;
}

template <class K, class AHashFns, class C, class A> MapElem<K,C> *
ChainHashMap<K, AHashFns, C, A>::put_bag(K akey, C avalue) {
  uintptr_t h = AHashFns::hash(akey);
  List<MapElem<K,C>,A> empty;
  List<MapElem<K,C>,A> *l;
  MapElem<K, C> c(akey, avalue);
  MapElem<uintptr_t,List<MapElem<K,C>,A> > e(h, empty);
  MapElem<uintptr_t,List<MapElem<K,C>,A> > *x = this->set_in(e);
  if (x)
    l = &x->value;
  else {
    l = &Map<uintptr_t, List<MapElem<K,C>,A>,A>::put(h, c)->value;
    return &l->head->car;
  }
  for (ConsCell<MapElem<K,C>,A> *p  = l->head; p; p = p->cdr)
    if (AHashFns::equal(akey, p->car.key) && AHashFns::equal_value(avalue, p->car.value))
      return &p->car;
  l->push(c);
  return 0;
}

template <class K, class AHashFns, class C, class A> int
ChainHashMap<K, AHashFns, C, A>::get_bag(K akey, Vec<C> &v) {
  uintptr_t h = AHashFns::hash(akey);
  List<MapElem<K,C>,A> empty;
  MapElem<uintptr_t,List<MapElem<K,C>,A> > e(h, empty);
  MapElem<uintptr_t,List<MapElem<K,C>,A> > *x = this->set_in(e);
  if (!x)
    return 0;
  List<MapElem<K,C>,A> *l = &x->value;
  for (ConsCell<MapElem<K,C>,A> *p  = l->head; p; p = p->cdr)
    if (AHashFns::equal(akey, p->car.key))
      return v.add(x->car);
  return v.n;
}

template <class K, class AHashFns, class C, class A> int
ChainHashMap<K, AHashFns, C, A>::del(K akey) {
  uintptr_t h = AHashFns::hash(akey);
  List<MapElem<K,C>,A> empty;
  List<MapElem<K,C>,A> *l;
  MapElem<uintptr_t,List<MapElem<K,C>,A> > e(h, empty);
  MapElem<uintptr_t,List<MapElem<K,C>,A> > *x = this->set_in(e);
  if (x)
    l = &x->value;
  else
    return 0;
  ConsCell<MapElem<K,C>,A> *last = 0;
  for (ConsCell<MapElem<K,C>,A> *p = l->head; p; p = p->cdr) {
    if (AHashFns::equal(akey, p->car.key)) {
      if (!last)
        l->head = p->cdr;
      else
        last->cdr = p->cdr;
      return 1;
    }
    last = p;
  }
  return 0;
}

template <class K, class AHashFns, class C, class A> void
ChainHashMap<K, AHashFns, C, A>::get_keys(Vec<K> &keys) {
  for (int i = 0; i < n; i++) {
    List<MapElem<K,C> > *l = &v[i].value;
    if (l->head) 
      for (ConsCell<MapElem<K,C>,A> *p  = l->head; p; p = p->cdr)
        keys.add(p->car.key);
  }
}

template <class K, class AHashFns, class C, class A> void
ChainHashMap<K, AHashFns, C, A>::get_values(Vec<C> &values) {
  for (int i = 0; i < n; i++) {
    List<MapElem<K,C>,A> *l = &v[i].value;
    if (l->head) 
      for (ConsCell<MapElem<K,C>,A> *p  = l->head; p; p = p->cdr)
        values.add(p->car.value);
  }
}

template <class F, class A> inline cchar *
StringChainHash<F,A>::canonicalize(cchar *s, cchar *e) {
  uintptr_t h = 0;
  cchar *a = s;
  // 31 changed to 27, to avoid prime2 in vec.cpp
  if (e)
    while (a != e) h = h * 27 + (unsigned char)*a++;  
  else
    while (*a) h = h * 27 + (unsigned char)*a++;  
  MapElem<uintptr_t,List<cchar*, A> > me(h, (char*)0);
  MapElem<uintptr_t,List<cchar*, A> > *x = this->set_in(me);
  if (x) {
    List<cchar*, A> *l = &x->value;
    typedef ConsCell<cchar *, A> TT;
    forc_List(TT, x, *l) {
      a = s;
      cchar *b = x->car;
      while (1) {
        if (!*b) {
          if (a == e)
            return x->car;
          break;
        }
        if (a >= e || *a != *b)
          break;
        a++; b++;
      }
    }
  }
  s = _dupstr<A>(s, e);
  cchar *ss = ChainHash<cchar *, F, A>::put(s);
  if (ss)
    return ss;
  return s;
}

template <class K, class C, class A> inline C 
Env<K,C,A>::get(K akey) {
  MapElem<K,List<C, A> *> e(akey, 0);
  MapElem<K,List<C, A> *> *x = store.set_in(e);
  if (x)
    return x->value->first();
  return (C)0;
}

template <class K, class C, class A> inline List<C, A> *
Env<K,C,A>::get_bucket(K akey) {
  List<C, A> *bucket = store.get(akey);
  if (bucket)
    return bucket;
  bucket = new List<C>();
  store.put(akey, bucket);
  return bucket;
}

template <class K, class C, class A> inline void
Env<K,C,A>::put(K akey, C avalue) {
  scope.head->car.push(akey);
  get_bucket(akey)->push(avalue);
}

template <class K, class C, class A> inline void
Env<K,C,A>::push() {
  scope.push();
}

template <class K, class C, class A> inline void
Env<K,C,A>::pop() {
  forc_List(EnvCons, e, scope.first())
    get_bucket(e->car)->pop();
}

template <class C, class AHashFns, int N, class A> inline 
NBlockHash<C, AHashFns, N, A>::NBlockHash() : n(1), i(0) {
  memset(&e[0], 0, sizeof(e));
  v = e;
}

template <class C, class AHashFns, int N, class A> inline C*
NBlockHash<C, AHashFns, N, A>::first() {
  return &v[0];
}

template <class C, class AHashFns, int N, class A> inline C*
NBlockHash<C, AHashFns, N, A>::last() {
  return &v[n * N];
}

template <class C, class AHashFns, int N, class A> inline C
NBlockHash<C, AHashFns, N, A>::put(C c) {
  int a;
  uintptr_t h = AHashFns::hash(c);
  C *x = &v[(h % n) * N];
  for (a = 0; a < N; a++) {
    if (!x[a])
      break;
    if (AHashFns::equal(c, x[a]))
      return x[a];
  }
  if (a < N) {
    x[a] = c;
    return (C)0;
  }
  C *vv = first(), *ve = last();
  C *old_v = v;
  i = i + 1;
  size(i);
  for (;vv < ve; vv++)
    if (*vv)
      put(*vv);
  if (old_v != &e[0])
    A::free(old_v);
  return put(c);
}

template <class C, class AHashFns, int N, class A> inline void
NBlockHash<C, AHashFns, N, A>::size(int p2) {
  n = prime2[p2];
  v = (C*)A::alloc(n * sizeof(C) * N);
  memset(v, 0, n * sizeof(C) * N);
}

template <class C, class AHashFns, int N, class A> inline C
NBlockHash<C, AHashFns, N, A>::get(C c) {
  if (!n)
    return (C)0;
  uintptr_t h = AHashFns::hash(c);
  C *x = &v[(h % n) * N];
  for (int a = 0; a < N; a++) {
    if (!x[a])
      return (C)0;
    if (AHashFns::equal(c, x[a]))
      return x[a];
  }
  return (C)0;
}

template <class C, class AHashFns, int N, class A> inline C*
NBlockHash<C, AHashFns, N, A>::assoc_get(C *c) {
  if (!n)
    return (C*)0;
  uintptr_t h = AHashFns::hash(*c);
  C *x = &v[(h % n) * N];
  int a = 0;
  if (c >= x && c < x + N)
    a = c - x + 1;
  for (; a < N; a++) {
    if (!x[a])
      return (C*)0;
    if (AHashFns::equal(*c, x[a]))
      return &x[a];
  }
  return (C*)0;
}

template <class C, class AHashFns, int N, class A> inline C*
NBlockHash<C, AHashFns, N, A>::assoc_put(C *c) {
  int a;
  uintptr_t h = AHashFns::hash(*c);
  C *x = &v[(h % n) * N];
  for (a = 0; a < N; a++) {
    if (!x[a])
      break;
  }
  if (a < N) {
    x[a] = *c;
    return  &x[a];
  }
  x[i % N] = *c;
  i++;
  return &x[i % N];
}

template <class C, class AHashFns, int N, class A> inline int
NBlockHash<C, AHashFns, N, A>::del(C c) {
  int a, b;
  if (!n)
    return 0;
  uintptr_t h = AHashFns::hash(c);
  C *x = &v[(h % n) * N];
  for (a = 0; a < N; a++) {
    if (!x[a])
      return 0;
    if (AHashFns::equal(c, x[a])) {
      if (a < N - 1) {
        for (b = a + 1; b < N; b++) {
          if (!x[b])
            break;
        }
        if (b != a + 1)
          x[a] = x[b - 1];
        x[b - 1] = (C)0;
        return 1;
      } else {
        x[N - 1] = (C)0;
        return 1;
      }
    }
  }
  return 0;
}

template <class C, class AHashFns, int N, class A> inline void
NBlockHash<C, AHashFns, N, A>::clear() {
  if (v && v != e) A::free(v);
  v = e;
  n = 1;
}

template <class C, class AHashFns, int N, class A> inline void
NBlockHash<C, AHashFns, N, A>::reset() {
  if (v)
    memset(v, 0, n * N * sizeof(C));
}

template <class C, class AHashFns, int N, class A> inline int
NBlockHash<C, AHashFns, N, A>::count() {
  int nelements = 0;
  C *l = last();
  for (C *xx = first(); xx < l; xx++) 
    if (*xx)
      nelements++;
  return nelements;
}

template <class C, class AHashFns, int N, class A> inline void 
NBlockHash<C, AHashFns, N, A>::copy(const NBlockHash<C, AHashFns, N, A> &hh) {
  clear();
  n = hh.n;
  i = hh.i;
  if (hh.v == &hh.e[0]) { 
    memcpy(e, &hh.e[0], sizeof(e));
    v = e;
  } else {
    if (hh.v) {
      v = (C*)A::alloc(n * sizeof(C) * N);
      memcpy(v, hh.v, n * sizeof(C) * N);
    } else
      v = 0;
  }
}

template <class C, class AHashFns, int N, class A> inline void 
NBlockHash<C, AHashFns, N, A>::move(NBlockHash<C, AHashFns, N, A> &hh) {
  clear();
  n = hh.n;
  i = hh.i;
  v = hh.v;
  if (hh.v == &hh.e[0]) { 
    memcpy(e, &hh.e[0], sizeof(e));
    v = e;
  }
  hh.clear();
}

void test_map();

#endif
