%angel

lljwin = window {
   title = "Lossless Join Algorithm";
   content = [frameleft, frameright] };

frameleft = frame {
   side = "left";
   anchor = "n";
   borderwidth = 10;
   content = [lbltabletitle, frametable, framebuts] };

frameright = frame {
   side = "left";
   borderwidth = 10;
   content = [lblfdtitle, framefd] };
   
frametable = frame ();

framefd = frame ();

framebuts = frame {
   anchor = "w";
   pady = 10;
   content = [butreset,butresetfds,tbfdlhs,lblfdarrow,tbfdrhs,butaddfd] };

butreset = button {
   text = "Reset Table";
   side = "left" };
butresetfds = button {
   text = "Remove FDs";
   side = "left" };

lbltabletitle = label {
   text = "Decomposition Table:" };

lblfdtitle = label {
   text = "FDs:" };

tbfdlhs = textbox {
   height = 1;
   width = 2;
   side = "left" };

tbfdrhs = textbox {
   height = 1;
   width = 2;
   side = "left" };

lblfdarrow = label {
   text = "-->";
   side = "left" };

butaddfd = button {
   text = "Add";
   side = "left" };
   
   
%eden

installeddi();

%eddi

table1 (A int, B int, C int, D int, E int);
FDs ( depatt char, atts char);
table1 << [1,2,3,1,4], [1,1,5,6,7], [8,1,9,10,1], [11,12,1,1,1], [1,13,14,15,1];
FDs << ["C","A"], ["C", "B"], ["D", "C"], ["C", "DE"], ["A", "CE"];

%eden
func findindex {
	para attrname, reltable;
	auto result, listatts, count;
	listatts = tail(reltable[1]);
	count=1;
	while (listatts!=[]) {
		if (listatts[1]==attrname) result = count; 
		count++;
		shift listatts;
	}
	return result;
}
/* writeln(findindex("e", table1)); */

func makestrlist {
	para str;
	auto result, i;
	result = [];
	for (i=1; i<=str#; i++)
		result = result // [substr(str,i,i)];
	return result;
}

func distproject {
        auto i,j,k,attr;

        /* project takes as its 2nd argument a single attribute name
                                or an (attribute name, rename) pair  */

        /* writeln("Project called with pars ", $1, " ", $2); */
        if ($#<1)
        {
                writeln("EDEN: ERROR: table not provided for project.");
                return @;
        } else {
                if ($1==@)
                {
                        writeln("EDEN: ERROR: project has received an "//
                        "undefined table.");
                        return @;
                } else
                    if (!inclist(tail($1[1]), extractattrs($2))) {
                        writeln("EDEN: ERROR: invalid attribute "//
                                        "in projection.");
                        return @;
                    }

          }

        tlist = [[[]]];

        /* For renaming attrs: c_list can contain [col_name,new_name] - ME*/
        for (i=1; i<=$2#; i++)
                if (type($2[i]) == "list")
                {
                        append tlist[1],$2[i][2];
                        $2[i] = $2[i][1];
                }
                else
                        append tlist[1],$2[i];

        for (i=2; i<=$1#; i++)
                append tlist,[];

        for (i=1; i<=$2#; i++)
        {
                addcol = getcolumn($1,$2[i]);
                append tlist[1][1], $1[1][1][addcol];
                for (j=2; j<=$1#; j++)
                        append tlist[j], $1[j][addcol];
        }
        return tlist;
}

currentFD = 0;

currentFDlhs is (currentFD!=0) ? makestrlist(FDs[currentFD+1][2]) : [];
currentFDrhs is (currentFD!=0) ? [FDs[currentFD+1][1]] : [];
currentFDlhs_id is find_indexes(currentFDlhs, table1);
currentFDrhs_id is find_indexes(currentFDrhs, table1);

func find_indexes {
   para indexlist, table;
   auto idlist, i, j;
   idlist=[];
   for (i=1; i<=indexlist#; i++) {
      for (j=2; j<=table[1]#; j++) {
         if (indexlist[i] == table[1][j]) {
	    append idlist, j-1;
	    break;
	 }
      }
   }
   return idlist;
}

func member_of {
   para element, list;
   if (type(list)!="list")
      return 0;
   while (list# > 0) {
      if (list[1] == element)
         return 1;
      shift list;
   }
   return 0;
}

projtable1 is distproject(table1, currentFDlhs);

/* compute a representative index for each block of equal tuples */

func iddups {
	para tuplelist;
	auto result, j, i, currtuple, eltresult;
	result = [];
	for (i=1; i<= tuplelist#; i++) {
		currtuple = tuplelist[i];
		for (j=tuplelist#; j>0; j--)
			if (currtuple==tuplelist[j]) eltresult = j;
		result = result // [eltresult];
	}	
	return result;
}


/* find the indices of elements with a specified value in a list */

func indicesn {
	para n, listidx;
	auto i, result;
	result = [];
	for (i=1; i<=listidx#; i++)
		if (listidx[i]==n) result = result // [i];
	return result;
}

/* compute the minimal value in a column with index in a specifed list */

func minmapcol {
	para col, ixlist;
	auto result;
	result = col[ixlist[1]];
	shift ixlist;
	while (ixlist!=[]) {
		result = min(result, col[ixlist[1]]);
		shift ixlist; 
	}
	return result;
}

/* transform a column by replacing all values to be made equal by the
	smallest of them */

func transformcol {
	para col, ixlist;
	auto i; 
	for (i=1; i<=col#; i++)
		col[i] = minmapcol(col, indicesn(ixlist[i], ixlist));
	return col;
}

	
proj2table1 is distproject(table1, currentFDrhs);

/* make a single column relation into a list */

func makelistcol {
	para rel;
	auto result;
	result = [];
	rel = tail(rel);
	while (rel!=[]) {
		result = result // rel[1];
		shift rel;
	}
	return result;
} 

newcol is transformcol(makelistcol(proj2table1), iddups(tail(projtable1)));


func applycurrFDtable1 {
	para currtable1;
	auto result, j;
	result = currtable1;
	i = findindex(FDs[currentFD+1][1], table1);
	for (j=1; j<=tail(currtable1)#; j++) 
		result[j+1][i] = newcol[j];
	return result;
}

/* the following procedures trace the execution of the algorithm */

table0=table1; /* recording initial state */

/* assign currentFD each value, and update the variable table1 accordingly */

proc updatetable1 : currentFD {
   if (currentFD > 0) {
	oldtable = table1;
	table1 = applycurrFDtable1(table1);
   }
}

func get_header {
   para table;
   if (type(table) == "list") {
      table = table[1];
      shift table;
      return table;
   }
   return @;
}

func get_rows {
   para table;
   if (type(table) == "list") {
      shift table;
      return table;
   }
   return @;
}

resulthead is get_header(table1);
resultset is get_rows(table1);
resultsetrows is resultset#;
resultsetcols is resultset# > 0 ? resultset[1]# : 0;

cellbg="white";
cellrelief="sunken";
cellheadbg="grey";
cellheadrelief="groove";
cellbg2="yellow";
cellbg3="green";

proc update_resultset : resultsetrows, resultsetcols {
   auto i, j, framecontent, colcontent;
   
   if (resultsetrows != resultsetrows_bak || resultsetcols != resultsetcols_bak) {

   framecontent = [];

   for (i=1; i<=resultsetcols; i++) {
   
      colcontent = [];
      
      ## create header cell
      execute("%angel\n cellc"//str(i)//"head = label { width=8;
	 background=cellheadbg; relief=cellheadrelief; fill=\"x\" };");
      if (i<=resulthead#)
         `"cellc"//str(i)//"head_text"` = resulthead[i];
      append colcontent, "cellc"//str(i)//"head";
      
      ## create cells
      for (j=1; j<=resultsetrows; j++) {
         execute("%angel\n cellc"//str(i)//"r"//str(j)//" = label {
	    background=member_of("//str(i)//",currentFDlhs_id)?cellbg2:(member_of("//str(i)//",currentFDrhs_id)?cellbg3:cellbg);
	    relief=cellrelief; fill=\"x\";
	    text=resultset["//str(j)//"]["//str(i)//"] };");
	 append colcontent, "cellc"//str(i)//"r"//str(j);
      }
      
      ## create column containing cells
      execute("%angel\n framecol"//str(i)//" = frame {side=\"left\"};");
      `"framecol"//str(i)//"_content"` = colcontent;
      append framecontent, "framecol"//str(i);
   }
   
   ## place columns in the frame
   frametable_content = framecontent;

   resultsetrows_bak = resultsetrows;
   resultsetcols_bak = resultsetcols;
   }
}

fdset is get_rows(FDs);
fdsetrows is fdset#;

proc update_fdset : fdsetrows {
   auto i, framecontent;
   framecontent = [];

   ## create buttons
   for (i=1; i<=fdsetrows; i++) {
      execute("%angel\n fdr"//str(i)//" = button {
	    fill=\"x\"; activebackground=\"white\";
	    relief = (currentFD=="//str(i)//") ? \"sunken\" : \"raised\";
	    text=fdsetrows>="//str(i)//"?fdset["//str(i)//"][2]//\" --> \"//fdset["//str(i)//"][1]:\"\" };");
      `"fdr"//str(i)//"_mouseclick"` = @;
      execute("
         proc fdr"//str(i)//"_trigger : fdr"//str(i)//"_mouseclick {
            if (fdr"//str(i)//"_mouseclick == 0)
               currentFD = "//str(i)//";
         }
      ");
      append framecontent, "fdr"//str(i);
   }
   
   ## place columns in the frame
   framefd_content = framecontent;
}

proc butreset_trigger : butreset_mouseclick {
   if (butreset_mouseclick == 0) {
      currentFD = 0;
      table1 = table0;
   }
}

proc butresetfds_trigger : butresetfds_mouseclick {
   if (butresetfds_mouseclick == 0) {
      currentFD = 0;
      execute("%eddi\n~FDs;\n");
   }
}

proc butaddfd_trigger : butaddfd_mouseclick {
   if (butaddfd_mouseclick == 0) {
      execute("%eddi\nFDs << [\""// tbfdrhs_text //"\",\""// tbfdlhs_text //"\"];\n");
   }
}
