/* akin_module.js
 * A Gears worker that is kicked off once and then runs continuously in the background in support of the Akinity application.
 * It handles messages from the UI or from other workers, calling akin functions.
 * It incorporates trigometry used for formatting output for the UI.
 * Akin module is called by meisosis module. It calls upon Db and synthesis modules. 
 */

function akin_module(wp) {
/* constructor
 * 
 */
	return this.run_ (wp); // kick off worker
}

akin_module.prototype.run_ = function(wp){
	/* the code to run on the worker  
	 */
	try {
		var akin_wkr_Script = 
		'google.gears.workerPool.onmessage = ' + String(this.akinWorker) 		+';'+
		'var module 				=  "akin"; ' 							+	// module global
		'var get_dissimilarity 		= ' +	String(this.get_dissimilarity) 		+';'+
		'var put_dissimilarity 		= ' +	String(this.put_dissimilarity) 		+';'+
		'var in_thresh 				= ' +	String(this.in_thresh) 				+';'+
		'var traverse 				= ' +	String(this.traverse) 				+';'+
		'var sim_distance 			= ' +	String(this.sim_distance) 			+';'+
		'var align_breadths 		= ' +	String(this.align_breadths) 		+';'+
		'var get_binding_id 		= ' +	String(this.get_binding_id) 		+';'+
		'var get_contents_id 		= ' +	String(this.get_contents_id) 		+';'+
		'var reseq 					= ' +	String(this.reseq) 					+';'+
		'var repos 					= ' +	String(this.repos) 					+';'+
		'var lower 					= ' +	String(this.lower) 					+';'+
		'var higher 				= ' +	String(this.higher) 				+';'+
//		'var trx_caller 			= ' +	String(this.trx_caller) 			+';'+
		'var lib_set_mod_state 		= ' +	String(lib_set_mod_state) 		+';'+
		'var lib_caller	 			= ' + 	String(lib_caller) 					+';'+
		'var lib_debug	 			= ' + 	String(lib_debug) 					+';';

		var akinWorkerId = wp.createWorker(akin_wkr_Script); 
		return {ID: akinWorkerId	};
	}
	catch(e){

	}
}   
	 
akin_module.prototype.akinWorker = function(a, b, message) {
	var wp 			= google.gears.workerPool;
	var db 			= google.gears.factory.create('beta.database');

	var mv;	
	var modparent	= message.sender;
/* The two init parameters are needed only when initialising a synthesis process from akin. 
 */	
	var synthScript = message.body[1];	
	var wkrthrottle	= message.body[2];	
	var active_flag = true;	// note: hard-coded. Ignore inactive bindings.
	var target_breadth = 12; // note: hard-coded

	function reply(mv) {
		var temp 	= mv.from;  
		mv.from 	= mv.dest;
		mv.dest 	= temp;
		return mv;
	}
	
	try {
		mv 	= lib_set_mod_state (message, modparent, synthScript, wkrthrottle);

		 // comment: pull instructions from caller off the stack
		switch (mv.dest.action) {
			case 'get_dissimilarity':
				if (!(	get_binding_id(mv, mv.ak.low, active_flag) && 
						get_binding_id(mv, mv.ak.high, active_flag))) {
					throw ("cannot retrieve a binding "+mv.ak.low.binding_id+', '+mv.ak.high.binding_id+', '+mv.cu.name);
				}
				else {	// comment: proceed, unless expansion is underway in synth module
//					if (align_breadths (db, mv, mv.ak.low, mv.ak.high, target_breadth)) {  // note : function not working here
					if (true) {  // note : function not working here

						var lot_length 	= Math.pow(2, mv.ak.low.mos_breadth)  // note: arbitrary selection
						var start_lot 	= mv.ak.result.lot;
						var end_lot 	= start_lot + (mv.ak.result.length / lot_length);

						var dissim = get_dissimilarity (db, mv, mv.ak.low, mv.ak.high, start_lot, end_lot, lot_length) ; 
						mv.ak.result.bits = dissim.bits;
						mv.ak.result.distance = sim_distance (mv, mv.ak.result.length, mv.ak.result.bits, false) 
						lib_caller (mv, ' '+dissim.bits+' of '+dissim.length, 'success', mv.init.parent);
					}
				}		
			break;
/*
			case 'get_binding_id':
				mv.ak.low = get_binding_id(mv, mv.ak.low, active_flag);
				mv = reply (mv);
				mv.from.module = 'akin';					
				mv.from.status = 'success';					
				lib_caller(mv, ' found binding ', 'success', mv.init.parent);
				lib_debug (mv.ba.dbwID, mv.from.action, mv.from.status,'got binding_id '+mv.from.module +' ' +mv.from.action+' '+mv.from.status );
			break;

*//*
			case 'get_contents_id':
				var dbgc = google.gears.factory.create('beta.database');
				mv.ak.low.conts = get_contents_id (dbgc, mv, mv.ak.low, 256, 0, 1)   
				mv = reply (mv);
				mv.from.action  = 'get_contents_id'; mv.from.module = 'akin'; 
				lib_caller(mv, ' got contents ' , 'success', mv.init.parent);
				lib_debug (mv.ba.dbwID, mv.from.action, mv.from.status,'got contents '+mv.from.module +' ' +mv.from.action+' '+mv.from.status );
			break;

*/			default:
			 	throw ("akin. unexpected action: "+mv.dest.action);
			break;
		 }

	// comment: handle return message from called worker
		wp.onmessage = function(a, b, message){
			mv = lib_set_mod_state (message, modparent, synthScript, wkrthrottle);
			mv.from.ID 		= message.sender;

			if (mv.dest.module !== 'akin')  throw ("ak: invalid module "+mv.dest.module+" "+mv.dest.action+" "+mv.dest.status+" "+mv.dest.message+" "+mv.ak.high.binding_id);
			
		 	switch (mv.from.status) {
		 		case 'exception':
		 			throw (mv.dest.message);
		 		case 'error':
		 			throw (mv.dest.message);
		 		break;
		 		case 'success':
				 	switch (mv.dest.action) {
				 		case 'insert_dissimilarity':	// notify parent that new dissim detail is available
				 		
	 					break;
/*
				 		case 'initiate':	// dissim table has been now populated for s/t
							if (in_thresh()) {	// is it within the required threshold ?
								// Do a traverse for this node.
								traverse (db, mv, active_flag, null);
							}
	 					break;

*/						case 'traverse':
								traverse(db, mv, mv.ak.low.binding_id, mv.ak.high.binding_id, mv.ak.result.distance, mv.ak.result.lot, active_flag, mv.ak.high.xpar, mv.ak.high.act_flag) ;								
						break;
						case 'get_binding_id': // same code as above
							mv.ak.low = get_binding_id(mv, mv.ak.low, active_flag);
							mv = reply (mv);
							mv.from.module = 'akin';					
							mv.from.status = 'success';					
							lib_debug (mv.ba.dbwID, mv.from.action, mv.from.status,'got binding_id '+mv.from.module +' ' +mv.from.action+' '+mv.from.status );
							lib_caller(mv, ' found binding ', 'success', mv.init.parent);
						break;

			 			case 'step_out': // handle reply from up to two async synth module calls
		 				switch (mv.sy.binding_id) {
							case mv.ak.low.binding_id: // we were low
								var expanded_low = true;
								var exp_low = get_contents_id(db, mv, mv.ak.low, Math.pow(2, mv.ak.low.mos_breadth), Math.pow(2, mv.sy.breadth) / Math.pow(2, mv.ak.low.mos_breadth));
								mv.ak.low.conts 	= exp_low.conts;
								mv.ak.low.offset 	= exp_low.offset;
								break;					
							case mv.ak.high.binding_id: // we were high
								var expanded_high = true;
								var exp_high = get_contents_id(db, mv, mv.ak.high, Math.pow(2, mv.ak.high.mos_breadth), Math.pow(2, mv.sy.breadth) / Math.pow(2, mv.ak.high.mos_breadth));
								mv.ak.high.conts 	= exp_high.conts;
								mv.ak.high.offset 	= exp_high.offset;
								break;					
							default:
								throw ("invalid return from step_out")
						}

						// comment : proceed unless waiting for expansion of the other parent
						if (((mv.ak.low.expand === false) || expanded_low) &&
							((mv.ak.high.expand === false) || expanded_high)) {
								akin_main(mv);
						}
		 				break;		// step_out

				 	default:
						throw( 'invalid action. Module : '+ mv.from.module +' Action : '+mv.from.action);		
	 			}	
				break; // normal exit
				default :
//					throw( 'invalid return status: ' + mv.from.status);		
			}
		}
	}
	catch (e) { lib_debug (mv.ba.dbwID, 'akinWorker', 'exception', e); }
	wp.onerror = function( err ){ lib_caller (mv, err.message, 'error', mv.init.parent);};	
};

akin_module.prototype.get_binding_id = function(mv, binding, active_flag) {
/* get binding on ID  
 * parameter binding is the module parameter (X,Y or Z) to populate. 
 * binding.binding_id should be populated already 
 */
	lib_debug (mv.ba.dbwID, mv.from.action, mv.from.status, 'get_binding_id '+binding.binding_id);
	if (binding.create_dt) return true; // no need to go on. created_date has a value so we know this binding.  
	
	var dbgb 		= google.gears.factory.create('beta.database');
	var rsgb; 

	try {
		dbgb.open(mv.cu.name);
		var select_string = 'select id, xparent_id, description, version, breadth, depth, synthetic_flag, id1_reversal, bound_value, created_date, active_flag ';
		if (active_flag) { // selected binding must be active
			// retrieve single binding by primary key
			rsgb = dbgb.execute(select_string + ' from binding where id = ? and active_flag = ? ', [binding.binding_id, '1']);
		}
		else {
			rsgb = dbgb.execute(select_string + ' from binding where id = ?', [binding.binding_id]);
		}
		if (rsgb.isValidRow()) {
			binding.xpar 		= rsgb.field(1);
			binding.description = rsgb.field(2);
			binding.version 	= rsgb.field(3);
			binding.breadth 	= rsgb.field(4);
			binding.depth 		= rsgb.field(5);
			binding.synth_flag 	= rsgb.field(6);
			binding.id1_reversal = rsgb.field(7);
			binding.bnd_val 	= rsgb.field(8);
			binding.create_dt 	= rsgb.field(9);
			binding.act_flag 	= rsgb.field(10);
			rsgb.close();	
			dbgb.close();
			return binding;
		 }
		 else {
		 	rsgb.close();	
			dbgb.close();
		 	return false; 
		 };
	} 
	catch (e) { lib_debug (mv.ba.dbwID, 'get_binding_id', 'exception', e); }
};

akin_module.prototype.get_contents_id = function(dbgc, mv, binding, lot_length, start_lot, end_lot) {
/* get contents by selecting on ID. parameter bindingID is the module parameter to select on.   
 * finish before end_lot
 * returns results to calling function
 */
	var rsgc; 
	var startpos	= lot_length * start_lot;
	var endpos		= lot_length * end_lot;
	var pos 		= 0;
	var conts 		= []; 	 
	
	try {

		dbgc.open	(mv.cu.name);
		rsgc = dbgc.execute(' select bit_value ' +
		' from contents ' +
		' where id = ? ' +
		' and pos between ? and ? ' +
		' order by pos', [binding.binding_id, startpos, endpos - 1]);

		while (pos < endpos-startpos) {
			conts[pos] 	= rsgc.field(0) ;
			rsgc.next();
		  	pos++;
		}

		dbgc.close();
		return conts ;
	} 
	catch (e) { lib_debug (mv.ba.dbwID, 'get_contents_id', 'exception', e); }
	finally {

		rsgc.close(); 
		dbgc.close();

		delete rsgc; 
		delete dbgc; 
		delete pos;
	}
}

akin_module.prototype.get_dissimilarity = function(dbgd, mv, low, high, start_lot, end_lot, lot_length) {
/* Retrieve (some of) pre-calculated dissimilarity score for the pair of active bindings,
 *  which are pre-sorted by the relative value of their ID (low/high). 
 * Return dissimilarity score and highest sequential lot number actually retrieved.
 * End before end_lot.
  * If the pair has potential for greater detail than is available and that detail is requested, 
 * then command synth module to first add detail by expanding synthetic cTag(s), then 
 * akin will populate dissimilar_lot256 if a threshold of dissimilarity is reached. 
 */
	var rsgd; 
	var lot = start_lot ||0;	
	var tally = 0;
	var result;

	lib_debug (mv.ba.dbwID, mv.from.action, mv.from.status, low.binding_id + ' '+ high.binding_id+ ' '+ start_lot+ ' '+ end_lot+ ' '+ lot_length);
	try {		
		if (low.mos_breadth === high.mos_breadth) {
			lot_length	= Math.pow(2,low.mos_breadth);
		} else throw ("inconsistent mos_breadth");

		dbgd.open	(mv.cu.name);
		rsgd = dbgd.execute(		
				' select lot, bits  from dissimilar_lot256 ' 	+ 			
				' where low_ID = ? and high_ID = ? ' 			+
				' and lot between ? and ? ' 				+
				' order by lot asc '							, 
				[low.binding_id, high.binding_id, start_lot, end_lot-1]); // stop before end_lot
			
		while (rsgd.isValidRow()) {
			if (rsgd.field(0) === lot) {
				tally += rsgd.field(1);
			}
			else {
				// comment: discontiguity in stored dissim data. Unexpected condition.
	 			// insert just one missing dissimilarity lot. include this in the return result
				
				result = put_dissimilarity(dbgd, mv, low, high, lot_length, lot);
				return { 	low : low, high : high, length: (lot * lot_length) + result.length,
							bits: tally + result.bits 				}
			}; 
			rsgd.next(); lot++;
		}
		dbgd.close();
		
		while (lot < end_lot) {	// keep discovering dissimilarity until just before end_lot.
			lib_debug (mv.ba.dbwID, mv.from.action, mv.from.status,"second put. lot "+lot+' end_lot '+end_lot);
			result = put_dissimilarity (dbgd, mv, low, high, lot_length, lot++);
			tally += result.bits;	// we can aggregate the results, despite there may be no call back from the db module yet

			if (low.conts !== null) low.conts = low.conts.concat(result.low.conts);
			else 	low.conts = result.low.conts;		
			if (high.conts !== null) high.conts = high.conts.concat(result.high.conts);
			else 	high.conts = result.high.conts;
		}
		return {low : low, high : high, length : lot * lot_length , bits : tally };
	} 
	catch (e) { lib_debug (mv.ba.dbwID, 'get_dissimilarity', 'exception', e); }
	finally {
//		dbgd.close();
	}
};

akin_module.prototype.in_thresh = function(mv, thresh, length, bits) {
	var st_dist = sim_distance (mv, length, bits, false);
	if (st_dist <= thresh) {
/*
		// this result is good, so report it directly to the calling program (UI)
		mv.ak.result.distance = st_dist;
		lib_caller(mv, '', 'success', mv.init.parent);

*/		return true;
	}
	else return false;
};

akin_module.prototype.traverse = function(dbt, mv, source, target, thresh, end_lot, active_flag, prev_child, ascend) {
/* Traverse the culture using the X-parent relation.
 * Stop when timeout has been exceeded.
 * Go both up and down from each node. 
 * Identify any cTag within the threshold directly to the calling program (UI)
 * Call the function recursively to get more nodes.
 * Look for existing dissim data.
 * Report back directly to UI.
 * Note: this function is prone to local maxima problem. It will stop at any node outside the threshold even if it connects to nodes inside it.
 * 
 * At present, there is only one instance of akin module running. Initiate messages are passed to itself. This means it does breadth first searching down the X-parent tree.
 * However, this function could easily be made to spawn new instances of akin_module to make it more parallel.
 */ 

/*	var source	= mv.ak.low.binding_id;		//	cTag at the Focus of Scope (not neceesarily lower id than target)
	var target	= mv.ak.high.binding_id;	//	cTag whose distance is measured in this instance of traverse
	var thresh	= mv.ak.result.distance;	//  maximum distance between source and target
	var end_lot	= mv.ak.result.lot; 		//  minimum number of lots required to give the required precision. Use more if already available
	var ascend	= mv.ak.high.act_flag		//	permit ascent of the xparent hierarchy
*/	
	var start_lot;
	var lot_length 	= 256;
	var akwID 		= mv.dest.ID ;


	Object.prototype.beget = function () { 
	    function F() {}
	    F.prototype = this;
	    return new F();
	};

	var mos_breadth = 8;	// hard-coded for version 0.1 of Ingendex
	var binding = {
					binding_id		: null,
					description		: null,
					xpar			: null,
					version			: null,
					breadth			: null,	
					depth			: null,
					synth_flag		: null,
					id1_reversal	: null,
					bv_type			: null,
					act_flag		: null,
					bnd_val			: null,				
					create_dt		: null,
					conts			: null,	
					offset			: null,
					mos_breadth		: mos_breadth,
					min_entropy		: null,
					def_bind_parent	: null,
					syw_base_lot	: null, 
					skip_to_lot 	: null, 
					lot				: null, 		
					steps			: null, 	
					oos				: null, 	
					dbwID			: mv.ba.dbwID, 
					sywID			: mv.ba.sywID 
				};


	var binding1 	= binding.beget();
		binding1.binding_id 	= source;
		binding1.mos_breadth 	= mos_breadth;

	function assign_record(rst, binding_p) {
			binding_p 				= binding.beget();
			binding_p.binding_id 	= rst.field(0);
			binding_p.description	= rst.field(1);
			binding_p.xpar 			= rst.field(2);
			binding_p.version 		= rst.field(3);
			binding_p.breadth 		= rst.field(4);
			binding_p.depth 		= rst.field(5);
			binding_p.synth_flag 	= rst.field(6);
			binding_p.id1_reversal	= rst.field(7);
			binding_p.bv_type 		= rst.field(8);
			binding_p.bnd_val 		= rst.field(9);
			binding_p.act_flag 		= rst.field(10);
		return binding_p;
	}

	function next_node(mv, curr_node_id, new_node_id, thresh, max_lot, ascend, akwID) {
	/* Call a new instance of traverse for a new node which is known to be in the threshold.
	 * No need to change from, dest or ak.low parameters since new a instance of this module/action which should report back to UI not to here.
	 */
		try {
			mv.ak.high.xpar 		= curr_node_id;	//xpar is a spare id on high. Not strictly parent. 
			mv.ak.high.binding_id 	= new_node_id;
			mv.ak.result.distance 	= thresh;
			mv.ak.result.lot 		= max_lot;
			mv.ak.high.act_flag 	= ascend;
			

			lib_debug (mv.ba.dbwID, mv.from.action, mv.from.status, " low.binding_id " + mv.ak.low.binding_id +
			" high.binding_id " +	mv.ak.high.binding_id +
			" thresh " 			+	mv.ak.result.distance +
			" end_lot " 		+	mv.ak.result.lot +
			" prev_child " 		+	mv.ak.high.xpar +
			" ascend " 			+	mv.ak.high.act_flag +
			" akwID " 			+	akwID);


			mv.dest.module = 'akin';
			mv.dest.action = 'traverse';
			

			if (new Date().getTime() < mv.ak.result.expiry) { // continue traversal if there is time remaining before search expiration
				lib_caller(mv, 'from next_node function', 'success', akwID); 
				}
			else lib_debug (mv.ba.dbwID, mv.from.action, mv.from.status, " timed out " + mv.ak.result.expiry )
		} 
		catch (e) {
			lib_caller(mv, "next_node " +  e, 'exception', mv.init.parent);
		}
	}
	
	function report_edge(mv, edge) {
		/* Notify UI about this edge
		 */

		var temp 			= mv.from;  
		mv.from 			= mv.dest;
		mv.dest 			= temp;	
		mv.ak.high 			= edge.high;
		mv.ak.low 			= edge.low;
		mv.ak.result.bits 	= edge.bits;
		mv.ak.result.length = edge.length;
		
		mv.from.module = 'akin'
		mv.from.action = 'traverse';

		lib_debug (mv.ba.dbwID, mv.from.action, mv.from.status,'reporting '+edge.high.binding_id+' desc '+edge.high.description+' length '+edge.length+' distance '+edge.bits);
		lib_caller(mv, " node id : "+edge.high.binding_id, 'success', mv.init.parent);		
	}

	try {	
		if (ascend) { // ascend X-parent hierarchy only if not previously descending
			dbt.open(mv.cu.name);	
			rst = dbt.execute(		
/*
				' select b.id, b.description, b.xparent_id, b.version, b.breadth, b.depth, b.synthetic_flag, b.id1_reversal, b.bv_type, b.bound_value,  b.active_flag, max(d.lot)+1, sum(d.bits) '+
				' from binding b0, binding b left outer join dissimilar_lot256 d '+
				' on ((b.id = d.high_id and d.low_id = ?) or (b.id = d.low_id and d.high_id = ?)) '+
				' where  b0.id =  ?  '+
				' and  b.id =  b0.xparent_id  '+
				' group by b.id ', [source, source, target]);

*/
				' select b.id, b.description, b.xparent_id, b.version, b.breadth, b.depth, b.synthetic_flag, b.id1_reversal, b.bv_type, b.bound_value,  b.active_flag, max(d.lot)+1, sum(d.bits) '+
				' from binding b0, binding b left outer join dissimilar_lot256 d '+
				' on ((b.id = d.high_id and d.low_id = ?) or (b.id = d.low_id and d.high_id = ?)) '+
				' where  b0.id =  ?  '+
				' and  b0.xparent_id =  b.id  '+
				' group by b.id ', [source, source, target]);
	
			if (rst.isValidRow()) {	// should always retrieve exactly one row per lot unless parent is synthetic 
				var binding2 			= assign_record(rst, binding);
				var node				= {low : binding1, high : binding2, length : null, bits : null};
				var binding_id 			= rst.field(0);
				var parent_id 			= rst.field(2);
				var max_lot 			= rst.field(11);
				var sum_bits 			= rst.field(12);
				dbt.close();	

				lib_debug (mv.ba.dbwID, mv.from.action, mv.from.status,target+ " source "+source+ " binding_id "+binding_id+ " parent_id "+parent_id+ " sum_bits "+sum_bits+" max_lot "+max_lot+" end_lot "+end_lot);				

//				if (parent_id > 0) { 
					if(max_lot === null || max_lot < end_lot)	// we don't have enough dissim data for source and parent
					 { 	// populate dissimilar_lot256 table for source and parent
						lib_debug (mv.ba.dbwID, mv.from.action, mv.from.status,'not enough dissim data '+max_lot)
						start_lot = max_lot  || 0;
						node.high.binding_id 	= binding_id;
						edge = get_dissimilarity (dbt, mv, node.low, node.high, start_lot, end_lot, lot_length);
						length = edge.length ;
						sum_bits = edge.bits;
					}
					else {
						edge = {low 	: mv.ak.low, 
								high 	: node.high, 
								length 	: max_lot  * lot_length , 
								bits 	: sum_bits };

							length = edge.length ;
						lib_debug (mv.ba.dbwID, mv.from.action, mv.from.status,' re-use parent '+ max_lot + ' ' + sum_bits + ' ' + lot_length + ' ' + edge.bits)
					}

					if (in_thresh (mv, thresh, edge.length, edge.bits)) {	// see whether it is in the threshold
						lib_debug (mv.ba.dbwID, mv.from.action, mv.from.status,'in thresh')
						report_edge (mv, edge);	// report success on parent node to UI
						if (parent_id > 0) next_node(mv, target, binding_id, thresh, edge.length / lot_length, true, akwID);	// go again, this time for the parent
					}
/*				}

				else { // report, but don't ascend from this node 
					lib_debug (mv.ba.dbwID, mv.from.action, mv.from.status,'synthetic parent')
//					report_edge (mv, edge);	
				}  

*/			}
			else 
//			throw ("no binding for id="+target);
				dbt.close();	
 
		}


	// descend X-parent hierarchy 
	
	lib_debug (mv.ba.dbwID, mv.from.action, mv.from.status," descending. source="+source+" target="+target+" prev_child="+prev_child );

		var selstr = 	
			' select b.id, b.description, b.xparent_id, b.version, b.breadth, b.depth, b.synthetic_flag, b.id1_reversal, b.bv_type, b.bound_value,  b.active_flag, d.lots, d.bits '+
			' from binding b left outer join dissimilar d '+
			' on ((b.id = d.high_id or b.id = d.low_id) and ( d.low_id = ? or d.high_id = ?)  ) '+
			' where  b.xparent_id =  ?  '
		if (prev_child !== null)  selstr = selstr + ' and  b.id !=  ? ';
		selstr = selstr +' group by b.id order by d.bits / d.lots ';

		dbt.open(mv.cu.name);	
		if (prev_child !== null)  	rst = dbt.execute( selstr , [source, source, target, prev_child]);
		else  						rst = dbt.execute( selstr , [source, source, target]);			
		
		var children 	= []; 
		var i=0;

		while (rst.isValidRow()) { // this will retrieve zero, one or more rows

			var binding2 	= assign_record(rst, binding);
			var node		= {low : binding1, high : binding2, length : null, bits : null};
			node.max_lot			= rst.field(11);
			node.sum_bits			= rst.field(12);
			children[i] = node;
			rst.next(); i++;
		}
		dbt.close();	// need to close db before moving on
		var i = children.length;
		while (--i >= 0 ) {
			var node = children[i];
			if(node.max_lot < end_lot)	// we don't yet have enough dissim for source and child
			 { 							// populate dissimilar_lot256 table for source and child
				start_lot = node.max_lot || 0;
				edge = get_dissimilarity  (dbt, mv, node.low, node.high, start_lot, end_lot, lot_length);
				if (in_thresh (mv, thresh, edge.length, edge.bits)) {	// see whether it is in the threshold
					report_edge (mv, edge);					// report success on child node to UI
					next_node(mv, source, edge.high.binding_id, thresh, end_lot, false, akwID);	// go again, this time for the child
				}
				else { // don't report this too distant node (there's a local maximum problem applicable in the turbulent region http://www.akinity.info/boundary.html)
				} 
			}
			else { // 
				edge = {low 	: mv.ak.low, 
						high 	: node.high, 
						length 	: node.max_lot * lot_length , 
						bits 	: ((node.max_lot * lot_length) /2) + node.sum_bits };
				lib_debug (mv.ba.dbwID, mv.from.action, mv.from.status,'re-use child. length '+ edge.length + '. bits '+edge.bits );
				mv.ak.result.distance = sim_distance (mv, edge.length, edge.bits, false) 
				report_edge (mv, edge);		// report success on child node to UI
				next_node(mv, source, edge.high.binding_id, thresh, end_lot, false, akwID);	// go again, this time for the child
			} 
		}
	}
	catch (e) { lib_debug (mv.ba.dbwID, 'traverse', 'exception', e); }
};

akin_module.prototype.reseq = function(mos_breadth) {
/* Use a pseudo-random generator to create an array containing an alternate sequence of pos.
 * 
 * Purpose is to avoid consistently presenting, through a 2 Dimensional UI, such arbitrary correlations
 *  as between neighbouring pos. A spurious correlation, if consistently presented to the user, 
 *  could feed back into actual correlation, via their input to meiosis from the UI.
 * e.g. pos# 172 and 173, after reseq are as likely to be presented far apart as close by.
 * Output array sequence is invariably different for any different Source cTag input.
 * Output array sequence is invariably different when the same Source cTag input is used more than once.
 * All Target cTags (to Akin) must also follow the same alternate sequence as Source; so that 
 * dissimilarity between Source and Target is preserved perfectly.
 * 
 * The output of reseq is used as input to repos, when resequencing Source and Target cTags.
 * 
 * Non-deterministic Reseq is a presentational feature, not a component of stored data.
 * Reseq is implementation-specific. It has no part in the general specification of Akinity.
 */

 var wprs 	= google.gears.workerPool; 	
 var module = 'akin_module';
 var action = 'reseq';
 var orig_arr  = new Array[mos_breadth];
 var reseq_arr = new Array[mos_breadth];
 
	try {
			for (var i = 0; i < mos_breadth; i++) {
				orig_arr[i] = i;
			}
			for ( i = 0; i < mos_breadth; i++) {
				reseq_arr.concat( orig_arr.splice(Math.round(Math.random()* i ), 1) );

			}
			return reseq_arr;
	}
	catch (e) { lib_debug (mv.ba.dbwID, 'reseq', 'exception', e); }
	finally {
 		delete wprs; 	
 		delete orig_arr;
 		delete reseq_arr;
		delete module;
		delete action;
	}
};

akin_module.prototype.aggregate_segments = function(source_conts, target_conts, reseq_arr, mos_breadth ) {
/* Calculate a single result vector from an array of Target conts which have already been 
 * breadth normalised, polarity normalised and repositioned with respect to Source. 
 * 
 * Source conts represents the item of interest at the center of the UI. 
 * Source conts' bit values are each canonical for their particular repositioned segment. 
 * Target bit values which concur with source are included in the Target aggregation calucation. 
 * Those bit values which differ are excluded. 
 *    
 * The relationship between the real pos and the segment is not relevant to this function, 
 * which is concerned only with repositioned conts, for consumption by UI.
 * 
 * Each included segment in the Target is considered a single vector. 
 * The aggregate of all included vectors is calculated. 
 * 
 * The direction in the resultant vector is used by UI to plot the Target cTag.
 * The magnitude of this vector is not used directly, but it is retained in case the scope of 
 * the UI is later extended to include more cTags.
 */

 var wpas 	= google.gears.workerPool; 	
 var module = 'akin_module';
 var action = 'aggregate_segments';
 var result_vector;

	try {
		for (var i =0;i<source_conts.length;i++) {
			
		}

		return target_segment;
	}
	catch (e) { lib_debug (mv.ba.dbwID, 'aggregate_segments', 'exception', e); }
	finally {
 		delete wpas; 	
 		delete target_segment;
 		delete module;
		delete action;
	}
};

akin_module.prototype.repos = function(source_conts, reseq_arr) {
/* Reposition an array of source conts (length Mos_breadth) 
 * according to an alternate pos sequence given by reseq_arr.
 * Return the repositioned array of conts
 */

 var wprp 	= google.gears.workerPool; 	
 var module = 'akin_module';
 var action = 'repos';
 var target_conts  = new Array[mos_breadth];
 
	try {
			for (var i = 0; i < mos_breadth; i++) {
				target_conts[i] = source_conts[reseq_arr[i]];
			}
			return target_conts;
	}
	catch(e){
		wprp.sendMessage([e, 'exception', action, module], message.sender);
	}
	finally {
		delete module;
		delete action;
 		delete target_conts;
 		delete wprp; 	
	}
};

akin_module.prototype.lower = function(s, t){
	if (s < t) return s; 
	else return t;
};

akin_module.prototype.higher = function(s, t){
	if (s > t) return s; 
	else return t;
};

akin_module.prototype.align_breadths = function (dbab, mv, low, high, target_breadth) {
/* Derive the appropriate breadth for the comparison. 
 * If necessary, contract (step_in) one or both inputs. Contract only for the duration of this transaction, not on db.
 * Alternatively, expand a synthetic input using synth.step_out. Expand for this and subsequent transactions by updating db.
 * Note: this function is essentially identical in meiosis. Either meiosis or akin could offer it to the other as a service.
*/
	var result = mv.ak.result; 
	function conts_or_expand(mv, source, target_breadth){
	/* source is an input binding; either mv.ak.low or mv.ak.high
	 * target breadth specifies the required breadth of output result
	 */
		if ( (target_breadth > source.breadth) && (source.synth_flag === '1')) 
			return true; // expand breadth of synthetic parent's contents to Z.breadth 
		else {
			var lot_length 	= Math.pow(2,source.mos_breadth)
			var endlot 		= Math.pow(2, target_breadth) / lot_length;
			source.conts 	= get_contents_id (dbab, mv, source, lot_length, 0, endlot);
			source.breadth 	= target_breadth;
			return false;
		}
	};

	function step_out (dbso, mv, source, target_breadth) {
	/* Command Synthesis module to expand a synthetic cTag binding.
 * Upon completion, it will reply back to this module as 'step_out'
*/


	var wpso 			= google.gears.workerPool; 	
	var source_length	= Math.pow(2, source.breadth);
	var lot_length 		= Math.pow(2, source.mos_breadth);
	var first_lot;

	try {		
 		mv.from.action		= 'step_out';
		mv.dest.module		= 'synthesis';
		mv.dest.action 		= 'step_out';

		// Prepare sy object state for synthesis module. First copy source state ...
		mv.sy 					= source;	
		// then some state variables required by step-out 
		mv.sy.lot 				= 1;	// we start after the first lot (zero)
		mv.sy.syw_base_lot		= mv.sy.lot;
		mv.sy.breadth			= target_breadth;
		mv.sy.steps				= target_breadth -9; // 8 is for min breadth. +1 is for the first step.
//		mv.sy.dbwID 			= mv.ak.dbwID;
		mv.sy.skip_to_lot 		= source_length / lot_length;	// process, but do not insert until skip_to_lot
/* 		Expand from the very first lot because Pattern of Expansion requires it. 
 * There can be some redundant processing for step_out due to this 
 * (no more than 50% inefficiency, since just a single step doubles conts size).
 * Neverthelss, PoE makes all conts creation (not just step_out) quicker through processing in parallel. 
 * So PoE is to be preferred to a single threaded expansion (eg daisy-chain).
 */
		first_lot 				= get_contents_id (dbso, mv, mv.sy, lot_length, 0, 1) 
		mv.sy.last_lot_conts 	= first_lot.conts;
		//kick off a new synth worker
		mv.ba.sywID 			= wpso.createWorker(mv.init.synthScript); 
		
	// note: binding should have been de-activated before now	
	
/* comment : initialise a synthesis process to do expansion of binding's contents.
 * This is a permanent expansion. The binding's stored breadth will be greater than before.
 * The new process is parented by (this worker of) akin. 
 */
 		wpso.sendMessage([mv,  mv.init.synthScript, mv.init.wkrthrottle, mv.ba.sywID], mv.ba.sywID);						
	} 
	catch (e) { lib_debug (mv.ba.dbwID, 'step_out', 'exception', e); }
	finally {
		delete wpso; 	
		delete source_length;
		delete lot_length ;
	}
};

	try {
		/* Choose which breadth (a, b or c) to target for Z:
 * 
						X	x		Y	y		result.Z.breadth
	spec.breadth (Z)
	null				a			b			a	
	a					b			c			a	
	b						any		a			a
	a						any		b			a
	null				b				a		b
	null					a			b		b
	b						a			c		b
	c						a			b		c
(a<b<c)
X and Y are logically equivalent. X is meiotic, x is synthetic 
*/
		if (low.synth_flag === '0' && high.synth_flag === '0') 	// two meis
			target_breadth = lower(lower(low.breadth, high.breadth), target_breadth);
		else if (low.synth_flag === '0' && high.synth_flag === '1')  
			target_breadthh = lower(low.breadth, target_breadth);
		else if (low.synth_flag === '1' && high.synth_flag === '0')  
			target_breadth = lower(high.breadth, target_breadth);
		else if (target_breadth === null) // two synths
			target_breadth = higher(low.breadth, high.breadth);

		low.expand = false; high.expand = false;
		low.expand = conts_or_expand  (mv, low, target_breadth);
		high.expand = conts_or_expand  (mv, high, target_breadth);

		if (low.expand) step_out(dbab, mv, low, target_breadth);
		if (high.expand) step_out(dbab, mv, high, target_breadth);

		// discontinue processing if step_out has been invoked for low and/or high
		if (mv.ak.low.expand || mv.ak.high.expand) return false;
		else return true ;
	}
		catch (e) { lib_debug (mv.ba.dbwID, 'step_out', 'exception', e); }
	finally {
	}
};

akin_module.prototype.sim_distance = function  (mv, length, score, reversal) {
	/* Convert the number of bits of similarity score from the number of opportunities (length)
	 * into a distance score of units in entropy. 
	 * reversal is boolean, tells whether the score has been subjected to reversal or not.
	 * Function called be called from outside or inside the akin module
	 */

	try {
		var distance;
		/* standard calculation for entropy */
		if (reversal) {
			distance = (1 - (score / length) * (Math.log((score / length)) / Math.LN2)) ;
		}
		else {
            distance = -
			/* polarity0 */     ((    score / length)  * (Math.log((     score / length))  / Math.LN2)  +
			/* polarity1 */     (1 - (score / length)) * (Math.log((1 - (score / length))) / Math.LN2))           

		}
		
		mv.ak.result.distance = distance;
		return mv.ak.result.distance;
	}
		catch (e) { lib_debug (mv.ba.dbwID, 'sim_distance', 'exception', e); }
};

akin_module.prototype.put_dissimilarity = function(dbpd, mv, low, high, lot_length, lot) {
/* Generate a single record for the dissimilarity table and request it to be inserted by db module.
  Give result for specified polarity only.(no reversal). 
 * Because the dissimilar function is working on a lot, not the whole cTag
 */

 lib_debug (mv.ba.dbwID, mv.from.action, mv.from.status,"put_dissimilarity " + " lot_length "+ lot_length+ " lot "+ lot);

	function dissimilar(mv, source_conts, target_conts) {
	/* Measure dissimilarity of contents two (partial) cTags. 
 * Source and target need not be the same length, but target must be at least as long as source.
 * Takes as input (partial) conts of two cTags having normalised breadth.
 * Return the length of conts compared and their relative dissimilarity score.
 */
		var tally		= 0;
		var startpos	= 0 ;
		var endpos		= lot_length-1;
		var pos			= endpos;	

 
		try {
			while (pos >= startpos) { // count down from end of desired lot
				if (target_conts[pos] === source_conts[pos]) {
					tally++;
				}
				pos--;
			}
			return {length : source_conts.length, bits : tally};
		}
		catch (e) { lib_debug (mv.ba.dbwID, 'dissimilar', 'exception', e); }
	};
	
	function insert_dissimilarity (mv) {
		mv.dest.module 		= 'db';
		mv.dest.action 		= 'insert_dissimilar';
		try {
			// comment: command insert dissimilarity by Db module worker  
			lib_caller (mv, null, null, mv.ak.dbwID);
		}
		catch (e) { lib_debug (mv.ba.dbwID, 'insert_dissimilarity', 'exception', e); }
	};

	var put_low	;
	var put_high;
	var startpos	= lot * lot_length;
	var endpos		= (lot +1) * lot_length;

	try {

		if (low.conts === null) 	
				put_low = get_contents_id (dbpd, mv, low, lot_length, lot, lot+1);
		else 	put_low	= low.conts.slice (startpos, endpos);
		
		if (high.conts === null) 	
				put_high 	= get_contents_id (dbpd, mv, high, lot_length, lot, lot+1);
		else	put_high	= high.conts.slice (startpos, endpos);

			// calculate dissimilarity score

			var result = dissimilar(mv, put_low, put_high);

			mv.ak.result.length = result.length;
			mv.ak.result.bits = result.bits;
			mv.ak.result.lot = lot;

			// prepare parameters for insert
			mv.ak.low 	= low;
			mv.ak.high 	= high;

			// request db module for insert
			insert_dissimilarity(mv);

		return {bits : mv.ak.result.bits, low : {conts : put_low}, high : {conts : put_high}};
	}
		catch (e) { lib_debug (mv.ba.dbwID, 'dissimilar', 'exception', e); }
	finally {
	}
};

/*
akin_module.prototype.debug = function(dbwID, action, status, msg) {

	try {	// comment: Request debug record by Db module worker  
		var wp 			= google.gears.workerPool; 	
		var mv = {
			from : {
				module 	: 'akin',
				action 	: action,
				status 	: status	},
			dest : {
				module 	: 'db',
				action 	: 'insert_debug',
				message : msg	}
		};
		wp.sendMessage([mv], dbwID);
	}
	catch(e){
	}
};

*/
/*
akin_module.prototype.trx_caller = function(mv, msg, status, dest_ID){

// 	mv.from.module = 'akin';
	try {	 
		var wp 		= google.gears.workerPool; 	
	
		mv.dest.ID = dest_ID;
		mv.from.module= 'akin';
	
		if (msg) {
			mv.from.message = msg;
			mv.dest.message = msg;
		}
		if (status) mv.from.status 	= status;
	
		wp.sendMessage([mv], mv.dest.ID);
		delete wp;
	}
	catch(e){
		mv.dest.message = 'trx_caller failed';
		wp.sendMessage([mv], 0);
	}
};	 
*/

