diff --git a/src/modules/Image/colortable.c b/src/modules/Image/colortable.c
index c509b4a08f1c55a1584eda9052b978da8eccf678..bfc230818b26ff3e9393b9943a16803386d2ca1a 100644
--- a/src/modules/Image/colortable.c
+++ b/src/modules/Image/colortable.c
@@ -1,11 +1,11 @@
 #include <config.h>
 
-/* $Id: colortable.c,v 1.4 1997/10/12 21:12:31 mirar Exp $ */
+/* $Id: colortable.c,v 1.5 1997/10/15 00:40:32 mirar Exp $ */
 
 /*
 **! module Image
 **! note
-**!	$Id: colortable.c,v 1.4 1997/10/12 21:12:31 mirar Exp $<br>
+**!	$Id: colortable.c,v 1.5 1997/10/15 00:40:32 mirar Exp $<br>
 **! class colortable
 **!
 **!	This object keeps colortable information,
@@ -46,7 +46,15 @@ typedef unsigned long nct_weight_t;
 #define WEIGHT_NEEDED (nct_weight_t)(0x10000000)
 #define WEIGHT_REMOVE (nct_weight_t)(0x10000001)
 
-#define COLORLOOKUPCACHESIZE 5
+#define COLORLOOKUPCACHEHASHSIZE 207
+#define COLORLOOKUPCACHEHASHR 7
+#define COLORLOOKUPCACHEHASHG 17
+#define COLORLOOKUPCACHEHASHB 1
+#define COLORLOOKUPCACHEHASHVALUE(r,g,b) \
+               (((COLORLOOKUPCACHEHASHR*(int)(r))+ \
+		 (COLORLOOKUPCACHEHASHG*(int)(g))+ \
+		 (COLORLOOKUPCACHEHASHB*(int)(b)))% \
+		COLORLOOKUPCACHEHASHSIZE) 
 
 #define SQ(x) ((x)*(x))
 static INLINE int sq(int x) { return x*x; }
@@ -63,9 +71,10 @@ struct nct_scale
    struct nct_scale *next;
    rgb_group low,high;
    rgbl_group vector; /* high-low */
-   INT32 sqvector; /* |vector|� */
+   float invsqvector; /* |vector|� */
    INT32 realsteps;
    int steps;
+   float mqsteps;     /* 1.0/(steps-1) */
    int no[1];  /* actually no[steps] */
 };
 
@@ -100,12 +109,12 @@ struct neo_colortable
       } cube;
    } u;
 
-   struct lookupcachenode
+   struct lookupcache
    {
-      unsigned long rgb_as_long;
-      rgb_group rgb;
-   } lookupcache[COLORLOOKUPCACHESIZE]; /* about 5 is optimal */
-   int cacheslotsused;
+      rgb_group src;
+      rgb_group dest;
+      int index;
+   } lookupcachehash[COLORLOOKUPCACHEHASHSIZE];
 
    union /* of pointers!! */
    {
@@ -182,10 +191,13 @@ static void free_colortable_struct(struct neo_colortable *nct)
 
 static void init_colortable_struct(struct object *o)
 {
+   int i;
    THIS->type=NCT_NONE;
    THIS->lookup_mode=NCT_CUBICLES;
    THIS->lu.tree=NULL;
-   THIS->cacheslotsused=0;
+   
+   for (i=0; i<COLORLOOKUPCACHEHASHSIZE; i++)
+      THIS->lookupcachehash[i].index=-1;
 }
 
 static void exit_colortable_struct(struct object *obj)
@@ -195,11 +207,38 @@ static void exit_colortable_struct(struct object *obj)
 
 /***************** internal stuff ******************************/
 
+#if 1
+#include <sys/resource.h>
+#define CHRONO(X) chrono(X);
+
+static void chrono(char *x)
+{
+   struct rusage r;
+   static struct rusage rold;
+   getrusage(RUSAGE_SELF,&r);
+   fprintf(stderr,"%s: %ld.%06ld - %ld.%06ld\n",x,
+	   r.ru_utime.tv_sec,r.ru_utime.tv_usec,
+
+	   ((r.ru_utime.tv_usec-rold.ru_utime.tv_usec<0)?-1:0)
+	   +r.ru_utime.tv_sec-rold.ru_utime.tv_sec,
+           ((r.ru_utime.tv_usec-rold.ru_utime.tv_usec<0)?1000000:0)
+           + r.ru_utime.tv_usec-rold.ru_utime.tv_usec
+	   );
+
+   rold=r;
+}
+#else
+#define CHRONO(X)
+#endif
+
 static void _img_copy_colortable(struct neo_colortable *dest,
 				 struct neo_colortable *src)
 {
-   struct nct_scale *s,*d;
-   dest->cacheslotsused=0;
+   struct nct_scale *s,*d,**np;
+   int i;
+   for (i=0; i<COLORLOOKUPCACHEHASHSIZE; i++)
+      dest->lookupcachehash[i].index=-1;
+
    dest->lookup_mode=src->lookup_mode;
    dest->lu.tree=NULL;
    switch (src->type)
@@ -219,14 +258,16 @@ static void _img_copy_colortable(struct neo_colortable *dest,
       case NCT_CUBE:
          *dest=*src;
 	 dest->u.cube.firstscale=NULL;
+	 np=&(dest->u.cube.firstscale);
 	 s=src->u.cube.firstscale;
 	 while (s)
 	 {
 	    d=(struct nct_scale*)
 	       xalloc(sizeof(struct nct_scale)+s->steps*sizeof(int));
 	    memcpy(d,s,sizeof(struct nct_scale)+s->steps*sizeof(int));
-	    d->next=dest->u.cube.firstscale;
-	    dest->u.cube.firstscale=d;
+	    d->next=NULL;
+	    *np=d;
+	    np=&(d->next); /* don't change order */
 	    s=s->next;
 	 }
          return; /* done */
@@ -815,9 +856,9 @@ static INLINE void _find_cube_dist(struct nct_cube cube,rgb_group rgb,
       b.g=((INT32)rgb.g)-s->low.g;
       b.b=((INT32)rgb.b)-s->low.b;
 
-      n=(s->steps*(b.r*s->vector.r+
-		   b.g*s->vector.g+
-		   b.b*s->vector.b))/s->sqvector;
+      n=(int)((s->steps*(b.r*s->vector.r+
+			 b.g*s->vector.g+
+			 b.b*s->vector.b))*s->invsqvector);
 
       if (n<0) n=0; else if (n>=s->steps) n=s->steps-1;
 
@@ -845,7 +886,7 @@ static INLINE void _find_cube_dist(struct nct_cube cube,rgb_group rgb,
 static struct nct_cube _img_get_cube_from_args(INT32 args)
 {
    struct nct_cube cube;
-   struct nct_scale *s;
+   struct nct_scale *s,**np;
    int no,i;
    int osteps,o2steps;
    
@@ -875,6 +916,7 @@ static struct nct_cube _img_get_cube_from_args(INT32 args)
    ap=3;
 
    cube.firstscale=NULL;
+   np=&(cube.firstscale);
 
    while (args>=ap+3)
    {
@@ -958,9 +1000,10 @@ static struct nct_cube _img_get_cube_from_args(INT32 args)
       s->vector.r=high.r-(INT32)low.r;
       s->vector.g=high.g-(INT32)low.g;
       s->vector.b=high.b-(INT32)low.b;
-      s->sqvector=SQ(s->vector.r)+SQ(s->vector.g)+SQ(s->vector.b);
+      s->invsqvector=1.0/(SQ(s->vector.r)+SQ(s->vector.g)+SQ(s->vector.b));
       s->steps=steps;
       s->realsteps=isteps;
+      s->mqsteps=1.0/(steps-1);
 
 #ifdef COLORTABLE_DEBUG
       fprintf(stderr,"COLORTABLE %d steps, %d live, trig=%d\n",s->steps,s->realsteps,cube.disttrig);
@@ -987,15 +1030,16 @@ static struct nct_cube _img_get_cube_from_args(INT32 args)
 	    s->no[i]=dummyno;
       }
       
-      s->next=cube.firstscale;
-      cube.firstscale=s;
+      *np=s;
+      s->next=NULL;
+      np=&(s->next);
    }
    cube.numentries=no;
 
    return cube;
 }
 
-static struct nct_flat _img_colortable_cube_to_flat(struct nct_cube cube)
+static struct nct_flat _img_nct_cube_to_flat(struct nct_cube cube)
 {
    struct nct_flat flat;
    int no;
@@ -1066,15 +1110,18 @@ static void _img_add_colortable(struct neo_colortable *rdest,
       return;
    }
 
+   for (i=0; i<COLORLOOKUPCACHEHASHSIZE; i++)
+      dest->lookupcachehash[i].index=-1;
+
    if (src->type==NCT_CUBE)
    {
-      tmp1.u.flat=_img_colortable_cube_to_flat(src->u.cube);
+      tmp1.u.flat=_img_nct_cube_to_flat(src->u.cube);
       tmp1.type=NCT_FLAT;
       src=&tmp1;
    }
    if (dest->type==NCT_CUBE)
    {
-      tmp2.u.flat=_img_colortable_cube_to_flat(dest->u.cube);
+      tmp2.u.flat=_img_nct_cube_to_flat(dest->u.cube);
       tmp2.type=NCT_FLAT;
       dest=&tmp2;
    }
@@ -1245,15 +1292,18 @@ static void _img_sub_colortable(struct neo_colortable *rdest,
       return;
    }
 
+   for (i=0; i<COLORLOOKUPCACHEHASHSIZE; i++)
+      dest->lookupcachehash[i].index=-1;
+
    if (src->type==NCT_CUBE)
    {
-      tmp1.u.flat=_img_colortable_cube_to_flat(src->u.cube);
+      tmp1.u.flat=_img_nct_cube_to_flat(src->u.cube);
       tmp1.type=NCT_FLAT;
       src=&tmp1;
    }
    if (dest->type==NCT_CUBE)
    {
-      tmp2.u.flat=_img_colortable_cube_to_flat(dest->u.cube);
+      tmp2.u.flat=_img_nct_cube_to_flat(dest->u.cube);
       tmp2.type=NCT_FLAT;
       dest=&tmp2;
    }
@@ -1623,7 +1673,7 @@ void image_colortable_reduce(INT32 args)
       case NCT_NONE: pop_n_elems(args); push_object(o); return;
       case NCT_CUBE:
          nct->type=NCT_FLAT;
-         nct->u.flat=_img_colortable_cube_to_flat(THIS->u.cube);
+         nct->u.flat=_img_nct_cube_to_flat(THIS->u.cube);
 	 break;
       case NCT_FLAT:
          _img_copy_colortable(nct,THIS);
@@ -1737,7 +1787,7 @@ void image_colortable_cast_to_array(INT32 args)
    }
 
    if (THIS->type==NCT_CUBE)
-      flat=_img_colortable_cube_to_flat(THIS->u.cube);
+      flat=_img_nct_cube_to_flat(THIS->u.cube);
    else
       flat=THIS->u.flat;
 
@@ -1888,32 +1938,204 @@ void image_colortable_cubicles(INT32 args)
 
 /*
 **! method object map(object image)
+**! method object `*(object image)
+**! method object ``*(object image)
 **!	Map colors in an image object to the colors in 
 **!     the colortable, and creates a new image with the
 **!     closest colors. 
 **!
 **! returns a new image object
 **!
-**! arg int r
-**! arg int g
-**! arg int b
-**!     Size, ie how much the colorspace is divided.
-**!     Note that the size of each cubicle is at least about 8b,
-**!     and that it takes time to calculate them. The number of
-**!     cubicles are <tt>r*g*b</tt>, and default is r=18, g=20, b=12,
-**!     ie 4320 cubicles.
-**!
 **! note
-**!     this method doesn't figure out the cubicles, this is 
-**!     done on the first use of the colortable
+**!     Flat (not cube) and not '<ref>full</ref>' method: 
+**!     this method does figure out the data needed for
+**!	the lookup method, which may take time the first
+**!	use of the colortable - the second use is quicker.
 **!
-**!     Not applicable to colorcube types of colortable.
+**! see also
+**!     cubicle, tree, full
 **/
 
+static void _img_nct_map_to_cube(rgb_group *s,
+				 rgb_group *d,
+				 int n,
+				 struct neo_colortable *nct)
+{
+   int red,green,blue;
+   int hred,hgreen,hblue;
+   int redm,greenm,bluem;
+   float redf,greenf,bluef;
+   struct nct_cube *cube=&(nct->u.cube);
+
+   red=cube->r; 	hred=red/2;      redm=red-1;
+   green=cube->g;	hgreen=green/2;  greenm=green-1;
+   blue=cube->b; 	hblue=blue/2;    bluem=blue-1;
+
+   redf=255.0/redm;
+   greenf=255.0/greenm;
+   bluef=255.0/bluem;
+
+   if (!cube->firstscale && red && green && blue)
+   {
+      while (n--)
+      {
+	 d->r=((int)(((s->r*red+hred)>>8)*redf));
+	 d->g=((int)(((s->g*green+hgreen)>>8)*greenf));
+	 d->b=((int)(((s->b*blue+hblue)>>8)*bluef));
+	 
+	 d++;
+	 s++;
+      }
+   }
+   else
+   {
+      while (n--) /* similar to _find_cube_dist() */
+      {
+	 struct nct_scale *sc;
+	 int mindist;
+	 int i;
+	 char *n;
+	 int nc;
+	 int rgbr,rgbg,rgbb;
+	 int drgbr,drgbg,drgbb;
+	 struct lookupcache *lc;
+	 rgbr=s->r;
+	 rgbg=s->g;
+	 rgbb=s->b;
+
+	 lc=nct->lookupcachehash+COLORLOOKUPCACHEHASHVALUE(rgbr,rgbg,rgbb);
+	 if (lc->index!=-1 &&
+	     lc->src.r==rgbr &&
+	     lc->src.g==rgbg &&
+	     lc->src.b==rgbb)
+	 {
+	    *(d++)=lc->dest;
+	    s++;
+	    continue;
+	 }
+
+	 lc->src=*s;
+
+	 if (red && green && blue)
+	 {
+	    lc->dest.r=d->r=((int)(((rgbr*red+hred)>>8)*redf));
+	    lc->dest.g=d->g=((int)(((rgbg*green+hgreen)>>8)*greenf));
+	    lc->dest.b=d->b=((int)(((rgbb*blue+hblue)>>8)*bluef));
+
+	    i=((rgbr*red+hred)>>8)+
+	       ((rgbg*green+hgreen)>>8)*red+
+	       ((rgbb*blue+hblue)>>8)*red*green;
+
+	    mindist=SQ(rgbr-d->r)+SQ(rgbg-d->g)+SQ(rgbb-d->b);
+	 }
+	 else
+	 {
+	    mindist=10000000;
+	    i=0;
+	 }
+
+	 if (mindist>=cube->disttrig)
+	 {
+	    /* check scales to get better distance if possible */
+
+	    nc=cube->r*cube->g*cube->b;
+	    sc=cube->firstscale;
+	    while (sc)
+	    {
+	       /* what step is closest? project... */
+
+	       i=(int)
+		  (( sc->steps *
+		     ( ((int)rgbr-sc->low.r)*sc->vector.r +
+		       ((int)rgbg-sc->low.g)*sc->vector.g +
+		       ((int)rgbb-sc->low.b)*sc->vector.b ) ) *
+		   sc->invsqvector);
+
+	       if (i<0) i=0; else if (i>=sc->steps) i=sc->steps-1;
+	       if (sc->no[i]>=nc) 
+	       {
+		  float f=i*sc->mqsteps;
+		  int drgbr=sc->low.r+(int)(sc->vector.r*f);
+		  int drgbg=sc->low.g+(int)(sc->vector.g*f);
+		  int drgbb=sc->low.b+(int)(sc->vector.b*f);
+
+		  int ldist=SQ(rgbr-drgbr)+SQ(rgbg-drgbg)+SQ(rgbb-drgbb);
+
+	       
+		  if (ldist<mindist)
+		  {
+		     lc->dest.r=d->r=(unsigned char)drgbr;
+		     lc->dest.g=d->g=(unsigned char)drgbg;
+		     lc->dest.b=d->b=(unsigned char)drgbb;
+		     lc->index=i;
+		     mindist=ldist;
+		  }
+	       }
+	    
+	       nc+=sc->realsteps;
+	    
+	       sc=sc->next;
+	    }
+	 }
+	 
+	 d++;
+	 s++;
+      }
+   }
+}
+
+static void _img_nct_map_to_flat(rgb_group *s,
+				 rgb_group *d,
+				 int n,
+				 struct neo_colortable *nct)
+{
+   error("colortable->map(): map to flat not implemented\n");
+}
 
 void image_colortable_map(INT32 args)
 {
-   
+   struct image *src;
+   struct image *dest;
+   struct object *o;
+
+   if (args<1)
+      error("too few arguments to colortable->map()\n");
+   if (sp[-args].type!=T_OBJECT ||
+       ! (src=(struct image*)get_storage(sp[-args].u.object,image_program)))
+      error("illegal argument 1 to colortable->map(), expecting image object\n");
+
+   if (!src->img) 
+      error("colortable->map(): source image is empty\n");
+
+   o=clone_object(image_program,0);
+   dest=(struct image*)(o->storage);
+   *dest=*src;
+
+   dest->img=malloc(sizeof(rgb_group)*src->xsize*src->ysize +1);
+   if (!dest->img)
+   {
+      free_object(o);
+      error("colortable->map(): out of memory\n");
+   }
+
+   switch (THIS->type)
+   {
+      case NCT_CUBE:
+	 _img_nct_map_to_cube(src->img,dest->img,
+			      src->xsize*src->ysize,THIS);
+	 pop_n_elems(args);
+	 push_object(o);
+	 return;
+      case NCT_FLAT:
+	 _img_nct_map_to_flat(src->img,dest->img,
+			      src->xsize*src->ysize,THIS);
+	 pop_n_elems(args);
+	 push_object(o);
+	 return;
+      default:
+	 free_object(o);
+	 error("colortable->map(): called colortable is not initiated\n");
+   }
 }
 
 /***************** global init etc *****************************/
@@ -1922,7 +2144,7 @@ void image_colortable_map(INT32 args)
 void init_colortable_programs(void)
 {
    start_new_program();
-   add_storage(sizeof(struct colortable*));
+   add_storage(sizeof(struct neo_colortable));
 
    set_init_callback(init_colortable_struct);
 
@@ -1944,7 +2166,7 @@ void init_colortable_programs(void)
    /* operators */
    add_function("`+",image_colortable_operator_plus,
 		"function(object:object)",0);
-   add_function("`-",image_colortable_operator_minus,
+   add_function("``+",image_colortable_operator_plus,
 		"function(object:object)",0);
 
    /* cast to array */
@@ -1962,6 +2184,10 @@ void init_colortable_programs(void)
    /* map image */
    add_function("map",image_colortable_map,
 		"function(object:object)",0);
+   add_function("`*",image_colortable_map,
+		"function(object:object)",0);
+   add_function("``*",image_colortable_map,
+		"function(object:object)",0);
 
    set_exit_callback(exit_colortable_struct);