From 10be8548f37980d0dbcf01fb498d1ec656eb466c Mon Sep 17 00:00:00 2001
From: "Mirar (Pontus Hagland)" <pike@sort.mirar.org>
Date: Tue, 24 Feb 1998 01:47:36 +0100
Subject: [PATCH] diff[3] and transpose added

Rev: lib/modules/Array.pmod:1.4
Rev: src/array.c:1.22
Rev: src/array.h:1.6
Rev: src/builtin_functions.c:1.60
---
 lib/modules/Array.pmod  |  80 +++++++
 src/array.c             |   2 +-
 src/array.h             |   1 +
 src/builtin_functions.c | 446 +++++++++++++++++++++++++++++++++++++++-
 4 files changed, 527 insertions(+), 2 deletions(-)

diff --git a/lib/modules/Array.pmod b/lib/modules/Array.pmod
index f250f10f76..f369698187 100644
--- a/lib/modules/Array.pmod
+++ b/lib/modules/Array.pmod
@@ -1,5 +1,9 @@
 #define error(X) throw( ({ (X), backtrace()[0..sizeof(backtrace())-2] }) )
 
+constant diff = __builtin.diff;
+constant diff_longest_sequence = __builtin.diff_longest_sequence;
+constant diff_compare_table = __builtin.diff_compare_table;
+
 mixed map(mixed arr, mixed fun, mixed ... args)
 {
   int e;
@@ -166,3 +170,79 @@ array uniq(array a)
   return indices(mkmapping(a,a));
 }
 
+array transpose(array x)
+{
+  array ret=allocate(sizeof(x[0]));
+  for(int e=0;e<sizeof(x[0]);e++) ret[e]=column(x,e);
+  return ret;
+}
+
+// diff3, complement to diff (alpha stage)
+
+array(array(array(mixed))) diff3(array mid,array left,array right)
+{
+   array lmid,ldst,al;
+   array rmid,rdst,ar;
+
+   al=diff(mid,left);      lmid=al[0]; ldst=al[1];
+   ar=diff(mid,right);     rmid=ar[0]; rdst=ar[1];
+
+   array res=({});
+   int lpos=0,rpos=0;
+   int l=0,r=0,n;
+   array eq=({});
+   int x;
+
+   for (n=0; ;)
+   {
+      while (l<sizeof(lmid) && lpos>=sizeof(lmid[l]))
+      {
+	 if (sizeof(ldst[l])>lpos)
+	    res+=({({({}),ldst[l][lpos..],({})})});
+	 l++;
+	 lpos=0;
+      }
+      while (r<sizeof(rmid) && rpos>=sizeof(rmid[r])) 
+      {
+	 if (sizeof(rdst[r])>rpos)
+	    res+=({({({}),({}),rdst[r][rpos..]})});
+	 r++;
+	 rpos=0;
+      }
+
+      if (n==sizeof(mid)) break;
+
+      x=min(sizeof(lmid[l])-lpos,sizeof(rmid[r])-rpos);
+
+      if (lmid[l]==ldst[l] && rmid[r]==rdst[r])
+      {
+	 eq=lmid[l][lpos..lpos+x-1];
+	 res+=({({eq,eq,eq})});
+      }
+      else if (lmid[l]==ldst[l])
+      {
+	 eq=lmid[l][lpos..lpos+x-1];
+	 res+=({({eq,eq,rdst[r][rpos..rpos+x-1]})});
+      }
+      else if (rmid[r]==rdst[r])
+      {
+	 eq=rmid[r][rpos..rpos+x-1];
+	 res+=({({eq,ldst[l][lpos..lpos+x-1],eq})});
+      }
+      else 
+      {
+	 res+=({({lmid[l][lpos..lpos+x-1],
+		  ldst[l][lpos..lpos+x-1],
+		  rdst[r][rpos..rpos+x-1]})});
+      }
+
+//        werror(sprintf("-> %-5{%O%} %-5{%O%} %-5{%O%}"
+//  		     " x=%d l=%d:%d r=%d:%d \n",@res[-1],x,l,lpos,r,rpos));
+	 
+      rpos+=x;
+      lpos+=x;
+      n+=x;
+   }
+   
+   return transpose(res);
+}
diff --git a/src/array.c b/src/array.c
index 5057bda103..820cda81de 100644
--- a/src/array.c
+++ b/src/array.c
@@ -262,7 +262,7 @@ struct array *array_insert(struct array *v,struct svalue *s,INT32 index)
 /*
  * resize array, resize an array destructively
  */
-static struct array *resize_array(struct array *a, INT32 size)
+struct array *resize_array(struct array *a, INT32 size)
 {
   if(a->size == size) return a;
   if(size > a->size)
diff --git a/src/array.h b/src/array.h
index 009bccf099..adde678d11 100644
--- a/src/array.h
+++ b/src/array.h
@@ -70,6 +70,7 @@ void array_set_index(struct array *v,INT32 index, struct svalue *s);
 void simple_set_index(struct array *a,struct svalue *ind,struct svalue *s);
 struct array *array_insert(struct array *v,struct svalue *s,INT32 index);
 struct array *array_shrink(struct array *v,INT32 size);
+struct array *resize_array(struct array *a, INT32 size);
 struct array *array_remove(struct array *v,INT32 index);
 INT32 array_search(struct array *v, struct svalue *s,INT32 start);
 struct array *slice_array(struct array *v,INT32 start,INT32 end);
diff --git a/src/builtin_functions.c b/src/builtin_functions.c
index 18129a4c0f..d4c6c0e2c6 100644
--- a/src/builtin_functions.c
+++ b/src/builtin_functions.c
@@ -4,7 +4,7 @@
 ||| See the files COPYING and DISCLAIMER for more information.
 \*/
 #include "global.h"
-RCSID("$Id: builtin_functions.c,v 1.59 1998/01/29 21:13:47 grubba Exp $");
+RCSID("$Id: builtin_functions.c,v 1.60 1998/02/24 00:47:36 mirar Exp $");
 #include "interpret.h"
 #include "svalue.h"
 #include "pike_macros.h"
@@ -1561,6 +1561,446 @@ void f_glob(INT32 args)
   }
 }
 
+/**** diff ************************************************************/
+
+static struct array* diff_compare_table(struct array *a,struct array *b)
+{
+   struct array *res;
+   struct mapping *map;
+   struct svalue *pval;
+   int i;
+
+   map=allocate_mapping(256);
+   push_mapping(map); /* in case of out of memory */
+
+   for (i=0; i<b->size; i++)
+   {
+      pval=low_mapping_lookup(map,b->item+i);
+      if (!pval)
+      {
+	 struct svalue val;
+	 val.type=T_ARRAY;
+	 val.u.array=low_allocate_array(1,1);
+	 val.u.array->item[0].type=T_INT;
+	 val.u.array->item[0].subtype=NUMBER_NUMBER;
+	 val.u.array->item[0].u.integer=i;
+	 mapping_insert(map,b->item+i,&val);
+	 free_svalue(&val);
+      }
+      else
+      {
+	 pval->u.array=resize_array(pval->u.array,pval->u.array->size+1);
+	 pval->u.array->item[pval->u.array->size-1].type=T_INT;
+	 pval->u.array->item[pval->u.array->size-1].subtype=NUMBER_NUMBER;
+	 pval->u.array->item[pval->u.array->size-1].u.integer=i;
+      }
+   }
+
+   res=low_allocate_array(a->size,0);
+
+   for (i=0; i<a->size; i++)
+   {
+      pval=low_mapping_lookup(map,a->item+i);
+      if (!pval)
+      {
+	 res->item[i].type=T_ARRAY;
+	 (res->item[i].u.array=&empty_array)->refs++;
+      }
+      else
+      {
+	 assign_svalue(res->item+i,pval);
+      }
+   }
+
+   pop_stack();
+   return res;
+}
+
+struct diff_magic_link
+{ 
+   int x;
+   int refs;
+   struct diff_magic_link *prev;
+};
+
+struct diff_magic_link_pool
+{
+   struct diff_magic_link *firstfree;
+   struct diff_magic_link_pool *next;
+   int firstfreenum;
+   struct diff_magic_link dml[1];
+};
+
+#define DMLPOOLSIZE 16384
+
+static int dmls=0;
+
+static INLINE struct diff_magic_link_pool*
+         dml_new_pool(struct diff_magic_link_pool **pools)
+{
+   struct diff_magic_link_pool *new;
+
+   new=malloc(sizeof(struct diff_magic_link_pool)+
+	      sizeof(struct diff_magic_link)*DMLPOOLSIZE);
+   if (!new) return NULL; /* fail */
+
+   new->firstfreenum=0;
+   new->firstfree=NULL;
+   new->next=*pools;
+   *pools=new;
+   return *pools;
+}
+
+static INLINE struct diff_magic_link* 
+       dml_new(struct diff_magic_link_pool **pools)
+{
+   struct diff_magic_link *new;
+   struct diff_magic_link_pool *pool;
+
+   dmls++;
+
+   if ( *pools && (new=(*pools)->firstfree) )
+   {
+      (*pools)->firstfree=new->prev;
+      new->prev=NULL;
+      return new;
+   }
+
+   pool=*pools;
+   while (pool)
+   {
+      if (pool->firstfreenum<DMLPOOLSIZE)
+	 return pool->dml+(pool->firstfreenum++);
+      pool=pool->next;
+   }
+
+   if ( (pool=dml_new_pool(pools)) )
+   {
+      pool->firstfreenum=1;
+      return pool->dml;
+   }
+   
+   return NULL;
+}	
+
+static INLINE void dml_free_pools(struct diff_magic_link_pool *pools)
+{
+   struct diff_magic_link_pool *pool;
+
+   while (pools)
+   {
+      pool=pools->next;
+      free(pools);
+      pools=pool;
+   }
+}
+
+static INLINE void dml_delete(struct diff_magic_link_pool *pools,
+			      struct diff_magic_link *dml)
+{
+   if (dml->prev && !--dml->prev->refs) dml_delete(pools,dml->prev);
+   dmls--;
+   dml->prev=pools->firstfree;
+   pools->firstfree=dml;
+}
+
+static INLINE int diff_ponder_stack(int x,
+				    struct diff_magic_link **dml,
+				    int top)
+{
+   int middle,a,b;
+   
+   a=0; 
+   b=top;
+   while (b>a)
+   {
+      middle=(a+b)/2;
+      if (dml[middle]->x<x) a=middle+1;
+      else if (dml[middle]->x>x) b=middle;
+      else return middle;
+   }
+   if (a<top && dml[a]->x<x) a++;
+   return a;
+}
+
+static INLINE int diff_ponder_array(int x,
+				    struct svalue *arr,
+				    int top)
+{
+   int middle,a,b;
+   
+   a=0; 
+   b=top;
+   while (b>a)
+   {
+      middle=(a+b)/2;
+      if (arr[middle].u.integer<x) a=middle+1;
+      else if (arr[middle].u.integer>x) b=middle;
+      else return middle;
+   }
+   if (a<top && arr[a].u.integer<x) a++;
+   return a;
+}
+
+static struct array* diff_longest_sequence(struct array *cmptbl)
+{
+   int i,j,top=0,lsize=0;
+   struct array *a;
+   struct diff_magic_link_pool *pools=NULL;
+   struct diff_magic_link *dml;
+   struct diff_magic_link **stack;
+
+   stack=malloc(sizeof(struct diff_magic_link*)*cmptbl->size);
+
+   if (!stack) error("out of memory\n");
+
+   for (i=0; i<cmptbl->size; i++)
+   {
+      struct svalue *inner=cmptbl->item[i].u.array->item;
+
+      for (j=cmptbl->item[i].u.array->size; j--;)
+      {
+	 int x=inner[j].u.integer;
+	 int pos;
+
+	 if (top && x<=stack[top-1]->x)
+	    pos=diff_ponder_stack(x,stack,top);
+	 else
+	    pos=top;
+
+	 if (pos && j)
+	 {
+	    if (stack[pos-1]->x+1 < inner[j].u.integer)
+	    {
+	       j=diff_ponder_array(stack[pos-1]->x+1,inner,j);
+	       x=inner[j].u.integer;
+	    }
+	 }
+	 else
+	 {
+	    j=0;
+	    x=inner->u.integer;
+	 }
+	 if (pos==top)
+	 {
+	    if (! (dml=dml_new(&pools)) )
+	    {
+	       dml_free_pools(pools);
+	       free(stack);
+	       error("out of memory\n");
+	    }
+
+	    dml->x=x;
+	    dml->refs=1;
+
+	    if (pos)
+	       (dml->prev=stack[pos-1])->refs++;
+	    else
+	       dml->prev=NULL;
+
+	    top++;
+	    
+	    stack[pos]=dml;
+	 }
+	 else if (stack[pos]->x!=x)
+	    if (pos && 
+		stack[pos]->refs==1 &&
+		stack[pos-1]==stack[pos]->prev)
+	    {
+	       stack[pos]->x=x;
+	    }
+	    else
+	    {
+	       if (! (dml=dml_new(&pools)) )
+	       {
+		  dml_free_pools(pools);
+		  free(stack);
+		  error("out of memory\n");
+	       }
+
+	       dml->x=x;
+	       dml->refs=1;
+
+	       if (pos)
+		  (dml->prev=stack[pos-1])->refs++;
+	       else
+		  dml->prev=NULL;
+
+	       if (!--stack[pos]->refs)
+		  dml_delete(pools,stack[pos]);
+	    
+	       stack[pos]=dml;
+	    }
+      }
+   }
+
+   /* FIXME(?) memory unfreed upon error here */
+   a=low_allocate_array(top,0); 
+   if (top)
+   {
+       dml=stack[top-1];
+       while (dml)
+       {
+	  a->item[--top].u.integer=dml->x;
+	  dml=dml->prev;
+       }
+   }
+
+   free(stack);
+   dml_free_pools(pools);
+   return a;
+}
+
+static struct array* diff_build(struct array *a,
+				struct array *b,
+				struct array *seq)
+{
+   struct array *ad,*bd;
+   int bi,ai,lbi,lai,i,eqstart;
+
+   b->refs++;
+   a->refs++;  /* protect from getting optimized... */
+
+   /* FIXME(?) memory unfreed upon error here (and later) */
+   ad=low_allocate_array(0,32);
+   bd=low_allocate_array(0,32);
+   
+   eqstart=0;
+   lbi=bi=ai=-1;
+   for (i=0; i<seq->size; i++)
+   {
+      bi=seq->item[i].u.integer;
+
+      if (bi!=lbi+1 || !is_equal(a->item+ai+1,b->item+bi))
+      {
+	 /* insert the equality */
+	 if (lbi>=eqstart)
+	 {
+	    struct array *eq;
+	    eq=slice_array(b,eqstart,lbi+1);
+	    push_array(eq); sp--;
+	    ad=resize_array(ad,ad->size+1);
+	    bd=resize_array(bd,bd->size+1);
+
+	    sp->u.refs[0]++;
+	    ad->item[ad->size-1] = bd->item[bd->size-1] = *sp;
+	 }
+	 /* insert the difference */
+	 lai=ai;
+	 ai=array_search(a,b->item+bi,ai+1)-1;
+
+	 bd=resize_array(bd,bd->size+1);
+	 ad=resize_array(ad,ad->size+1);
+
+	 push_array(slice_array(b,lbi+1,bi));
+	 bd->item[bd->size-1]=*--sp;
+
+	 push_array(slice_array(a,lai+1,ai+1));
+	 ad->item[ad->size-1]=*--sp;
+
+	 eqstart=bi;
+      }
+      ai++;
+      lbi=bi;
+   }
+
+   if (lbi>=eqstart)
+   {
+      struct array *eq;
+      eq=slice_array(b,eqstart,lbi+1);
+      push_array(eq); sp--;
+      ad=resize_array(ad,ad->size+1);
+      bd=resize_array(bd,bd->size+1);
+      
+      sp->u.refs[0]++;
+      ad->item[ad->size-1] = bd->item[bd->size-1] = *sp;
+   }
+
+   if (b->size>bi+1 || a->size>ai+1)
+   {
+      ad=resize_array(ad,ad->size+1);
+      bd=resize_array(bd,bd->size+1);
+
+      push_array(slice_array(b,lbi+1,b->size));
+      bd->item[bd->size-1]=*--sp;
+      
+      push_array(slice_array(a,ai+1,a->size));
+      ad->item[ad->size-1]=*--sp;
+   }
+
+   b->refs--;
+   a->refs--; /* i know what i'm doing... */
+
+   push_array(ad);
+   push_array(bd);
+   f_aggregate(2);
+   sp--;
+   return sp->u.array;
+}
+
+void f_diff(INT32 args)
+{
+   struct array *seq;
+   struct array *cmptbl;
+   struct array *diff;
+
+   if (args<2)
+      error("Too few arguments to diff().\n");
+
+   if (sp[-args].type!=T_ARRAY ||
+       sp[1-args].type!=T_ARRAY)
+      error("Illegal arguments to diff().\n");
+
+   cmptbl=diff_compare_table(sp[-args].u.array,sp[1-args].u.array);
+   push_array(cmptbl);
+   seq=diff_longest_sequence(cmptbl);
+   push_array(seq); 
+   
+   diff=diff_build(sp[-2-args].u.array,sp[1-2-args].u.array,seq);
+
+   pop_n_elems(2+args);
+   push_array(diff);
+}
+
+void f_diff_compare_table(INT32 args)
+{
+   struct array *cmptbl;
+
+   if (args<2)
+      error("Too few arguments to diff().\n");
+
+   if (sp[-args].type!=T_ARRAY ||
+       sp[1-args].type!=T_ARRAY)
+      error("Illegal arguments to diff().\n");
+
+   cmptbl=diff_compare_table(sp[-args].u.array,sp[1-args].u.array);
+
+   pop_n_elems(args);
+   push_array(cmptbl);
+}
+
+void f_diff_longest_sequence(INT32 args)
+{
+   struct array *seq;
+   struct array *cmptbl;
+   struct array *diff;
+
+   if (args<2)
+      error("Too few arguments to diff().\n");
+
+   if (sp[-args].type!=T_ARRAY ||
+       sp[1-args].type!=T_ARRAY)
+      error("Illegal arguments to diff().\n");
+
+   cmptbl=diff_compare_table(sp[-args].u.array,sp[1-args].u.array);
+   push_array(cmptbl);
+   seq=diff_longest_sequence(cmptbl);
+   pop_n_elems(args+1);
+   push_array(seq); 
+}
+
+/**********************************************************************/
+
 static struct callback_list memory_usage_callback;
 
 struct callback *add_memory_usage_callback(callback_func call,
@@ -1896,5 +2336,9 @@ void init_builtin_efuns(void)
   add_efun("encode_value", f_encode_value, "function(mixed:string)", OPT_TRY_OPTIMIZE);
   add_efun("decode_value", f_decode_value, "function(string:mixed)", OPT_TRY_OPTIMIZE);
   add_efun("object_variablep", f_object_variablep, "function(object,string:int)", OPT_EXTERNAL_DEPEND);
+
+  add_function("diff",f_diff,"function(array,array:array(array))",OPT_TRY_OPTIMIZE);
+  add_function("diff_longest_sequence",f_diff_longest_sequence,"function(array,array:array(int))",OPT_TRY_OPTIMIZE);
+  add_function("diff_compare_table",f_diff_compare_table,"function(array,array:array(array))",OPT_TRY_OPTIMIZE);
 }
 
-- 
GitLab