/* meiosis_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, calling meiosis functions.
 * This module is called by index module. It calls upon Db, synthesis and akin modules. 
 * 
 * Note: Meiosis related functions that are run in the foreground (not by the meiosis worker) are appended to this file.
 */

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

meiosis_module.prototype.run_ = 	function(wp){
	/* the code to run on the worker  
	 */
	try {
		var meioScript = 
		'google.gears.workerPool.onmessage = ' + String(this.meioWorker) 		+ '; ' +
		'var module 				=  "meiosis"; ' 							+	// module global
		'var align_breadths 		= ' +	String(this.align_breadths) 		+';' +
		'var meiosis_main 			= ' +	String(this.meiosis_main) 			+';' +
		'var assign_bnd_value 		= ' +	String(this.assign_bnd_value) 		+';' +
		'var assign_depth 			= ' +	String(this.assign_depth) 			+';' +
		'var assign_contents 		= ' +	String(this.assign_contents) 		+';' +
		'var assign_version 		= ' +	String(this.assign_version) 		+';' +
		'var validate_cTag 			= ' +	String(this.validate_cTag) 			+';' +
		'var lower 					= ' +	String(this.lower) 					+';' +
		'var higher 				= ' +	String(this.higher) 				+';' +
		'var db_bool 				= ' +	String(this.db_bool) 				+';' +
		'var dissimilar 			= ' +	String(this.dissimilar) 			+';' +
		'var get_binding_id 		= ' +	String(this.get_binding_id) 		+';' +
		'var get_contents_id 		= ' +	String(this.get_contents_id) 		+';' +
// module shared code in mod_lib.js
		'var lib_set_mod_state 		= ' +	String(lib_set_mod_state) 			+';' +
		'var lib_set_binding 		= ' +	String(lib_set_binding) 			+';' +
		'var lib_insert_binding 	= ' + 	String(lib_insert_binding) 			+';' +
		'var lib_insert_conts 		= ' + 	String(lib_insert_conts) 			+';' +
		'var lib_activate_binding 	= ' + 	String(lib_activate_binding) 		+';' +
		'var lib_caller	 			= ' + 	String(lib_caller) 					+';' +
		'var lib_debug	 			= ' + 	String(lib_debug) 					+';';

		// the message handler that gets return results from the worker
		var meioWorkerId = wp.createWorker(meioScript);		
//		textOut(" "+meioScript)
		return {ID: meioWorkerId};	
	}
	catch(e){
	}
};   

meiosis_module.prototype.meioWorker = function(a, b, message) {

	var wp 			= google.gears.workerPool; 	
	var mv;			// intial module variables from parent
	var module		= 'meiosis';
	var active_flag = true;		// do not process inactive bindings
	var expanded_X = false; 
	var expanded_Y = false; 
	var exp_X_conts ;
	var exp_Y_conts ;

	var modparent	= message.sender;
/* The two init parameters are needed only when initialising a synth module process from meiosis. 
 */	
	var synthScript = message.body[1];	// module static 
	var wkrthrottle	= message.body[2];	// module static 

	try {		
		function initiate_meiosis(mv, active_flag){
			if (mv.dest.action === 'initiate')
			{
				if (!(get_binding_id(mv, mv.me.X, active_flag) && get_binding_id(mv, mv.me.Y, active_flag))) throw ("cannot retrieve active binding ");
				else if (align_breadths(mv, mv.me.X, mv.me.Y, mv.me.Z.breadth))  meiosis_main(mv); // proceed, unless expansion is underway in synth module		
			}
			else lib_caller(mv, 'unknown action: ' + mv.dest.action, 'exception', mv.init.parent);
		}

	mv = lib_set_mod_state (message, modparent, synthScript, wkrthrottle);
	mv.from.ID 		= message.sender;

	if (mv.dest.module !== 'meiosis')  throw ("meiosis: invalid module called "+mv.dest.module);
	lib_debug (mv.ba, mv.from.action, 'success','meiosis. '+mv.from.module+'.'+mv.from.action+'.'+mv.from.status);
	
	switch (mv.from.status) {
	 	case 'exception':
			throw (mv.dest.message);
	 	break;
	 	case 'error':
			throw ('meiosis error' +mv.dest.message);
	 	break;
	 	case 'success':			
			lib_debug (mv.ba, mv.from.action, 'success', 'meiosis. status=success');
			switch (mv.from.module) {
				case 'batch':
					switch (mv.dest.action) {
						case 'initiate': 
							lib_debug (mv.ba, mv.from.action, 'success', 'meiosis. initiate');
							initiate_meiosis(mv, active_flag);
						break;
					}		
				break;											
				case 'synthesis':
					switch (mv.from.action) {
			 			case 'step_out': // handle reply from up to two asynchronous synthesis module calls
		 				switch (mv.sy.binding_id) {
							case mv.me.X.binding_id: // we were X
								expanded_X = true;
								exp_X_conts = get_contents_id(mv, mv.me.X, 0, Math.pow(2, mv.me.Z.breadth) / Math.pow(2, mv.me.X.mos_breadth));
								mv.me.X.conts 	= exp_X_conts.conts;
								mv.me.X.offset 	= exp_X_conts.offset;
								break;					
							case mv.me.Y.binding_id: // we were Y
								expanded_Y = true;
								exp_Y_conts = get_contents_id(mv, mv.me.Y, 0, Math.pow(2, mv.me.Z.breadth) / Math.pow(2, mv.me.Y.mos_breadth));
								mv.me.Y.conts 	= exp_Y_conts.conts;
								mv.me.Y.offset 	= exp_Y_conts.offset;
								break;
//							default: throw ("invalid return from step_out")
						}
//						comment : proceed unless waiting for expansion of the other parent

						if (((mv.me.X.expand === false) || expanded_X) &&
							((mv.me.Y.expand === false) || expanded_Y)) {
								meiosis_main(mv);
						}
		 				break;		// step_out

//						default: throw ('unknown action. ' + mv.from.action);
					}
				case 'db':
					lib_debug (mv.ba, mv.from.action, 'success', 'meiosis. return from db');
					switch (mv.from.action) {
						case 'insert_binding': 
							lib_insert_conts (mv);
						break;
						case 'insert_conts': 
							lib_activate_binding (mv, true);
						break;
						case 'activate_binding': // comment: Db process successfully activated a binding
							lib_debug (mv.ba, mv.from.action, 'success',' batch ' +mv.ba.id +'meisosis. activated binding ' +mv.me.Z.binding_id +' return to process '+' '+mv.init.parent);
							mv.dest.module = 'batch'
							// notify caller of binding's activation
							lib_caller ( mv, 'created ID: '+mv.me.Z.binding_id, 'success', mv.ba.bawID);
					break;								
//					default: throw ('meiosis. unknown action. ' + mv.dest.action);


				}
//				default: throw ('unknown module. ' + mv.from.module);
			}
//			default: throw (' unknown status: '+mv.from.action + '. status: '+  mv.from.status);			
	 	};	

		wp.onerror = function( err ){
			lib_caller ( mv, err.message, 'error', mv.init.parent);
		};	
	}
	catch (e) { lib_debug (mv.ba, 'get_batch_id', 'exception', e); }
	finally {
		delete 	 wp, mv, active_flag, expanded_X, expanded_Y,exp_X_conts, exp_Y_conts; 	
	}
};

meiosis_module.prototype.align_breadths = function (mv, X, Y, target_breadth) {

/* Derive the appropriate breadth for the Z child. 
 * If necessary, contract (step_in) a parent. Contract only for the duration of this transaction, not on db.
 * Alternatively, expand a synthetic parent using synth.step_out. Expand for this and subsequent transactions by updating db.
*/
	var Z = mv.me.Z; 

	function conts_or_expand(mv, source, target_breadth){
	/* source is an input binding; either mv.me.X or mv.me.Y 
	 * target has breadth and other parameters of output binding; mv.me.Z
	 */
		
		if ( (target_breadth > source.breadth) && (source.synth_flag === '1')) 
			return true; // expand breadth of synthetic parent's contents to Z.breadth 
		else {
			var endlot 		= Math.pow(2, target_breadth) / Math.pow(2,source.mos_breadth);
			var conts_res 	= get_contents_id (mv, source, 0, endlot);
			source.conts 	= conts_res.conts; 	
			source.offset 	= conts_res.offset;
			source.breadth 	= target_breadth;
			return false;
		}
	};
		
	function step_out (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 mos_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.ba.dbwID;
		mv.sy.skip_to_lot 		= source_length / mos_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 (say daisy-chain).
 */
		first_lot 				= get_contents_id (mv, mv.sy, 0, 1) 
		mv.sy.last_lot_conts 	= first_lot.conts;
		//kick off a new synth worker
		mv.ba.sywID 			= wpso.createWorker(mv.ba.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) meiosis. 
 */
 		wpso.sendMessage([mv,  mv.ba.synthScript, mv.ba.wkrthrottle, mv.ba.sywID], mv.ba.sywID);						
	} 
	catch (e) { lib_debug (mv.ba, 'step_out', 'exception', e); }
	finally {
		delete wpso; 	
		delete source_length;
		delete mos_length ;
	}
};

	try {
/* spec: Choose which breadth (a, b or c) to use for result (Z):
  
						X	x		Y	y		Z.breadth
	target_breadth param 
	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 (X.synth_flag === '0' && Y.synth_flag === '0') 	// two meis
			Z.breadth = lower(lower(X.breadth, Y.breadth), target_breadth);
		else if (X.synth_flag === '0' && Y.synth_flag === '1')  
			Z.breadth = lower(X.breadth, target_breadth);
		else if (X.synth_flag === '1' && Y.synth_flag === '0')  
			Z.breadth = lower(Y.breadth, target_breadth);
		else if (target_breadth === null) // two synths
			Z.breadth = higher(X.breadth, Y.breadth);

		X.expand = false; Y.expand = false;
		X.expand = conts_or_expand  (mv, X, Z.breadth);
		Y.expand = conts_or_expand  (mv, Y, Z.breadth);

		if (X.expand) step_out(mv, X, Z.breadth);
		if (Y.expand) step_out(mv, Y, Z.breadth);

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

meiosis_module.prototype.meiosis_main = function (mv) { 
/* Conceive a new cTag from X and Y parent bindings 
 bnd_val identifies which parent's bound_value is to be inherited by Z. 
*/
	var reversal 		= false;
	var pos				= 0;
	var create_dt;
	var X = mv.me.X, Y = mv.me.Y, Z = mv.me.Z;


	try{
	// Validate each parent object separately

	lib_debug (mv.ba, mv.from.action, 'success','meiosis_main X ' +X.binding_id +' Y ' +Y.binding_id)


	validate_cTag(mv, X) ;
	validate_cTag(mv, Y) ;

/*
	// Validate in-breeding between both parent objects 
	if (dissimilar(mv, X.conts, Y.conts, reversal) < global_var.get_min_dissimilarity()) 
			throw ("Meiosis Error: In-bred parents");

*/
	// Choose which bound_value to put in Z
	assign_bnd_value (mv, Z.bnd_val);

	// Choose which depth to give to Z
	assign_depth (mv);

	// Choose which version to give to Z
	assign_version (mv, Z);

	// Choose which polarity will Z take
	var length = mv.me.X.conts.length;
	var dissim = dissimilar(mv, mv.me.X.conts, mv.me.Y.conts, false) ;	

// note dissimilarity is the opposite of simiarity. Why are we doing this ?	
	if (dissim > length / 2) 	reversal = true; // X,Y are overall more unalike than alike in their original polarity
	else 						reversal = false;// X,Y are more alike

	lib_debug (mv.ba, mv.from.action, 'success',' reversal = '+reversal+' dissim = '+dissim+' length = '+length);

	// validate for in-breeding
	var entropy = (1- (((dissim / length) * (Math.log((dissim / length )) / Math.LN2)) + ((1-(dissim / length)) * (Math.log((1-(dissim / length ))) / Math.LN2))))/2;
	if (entropy < mv.me.Z.min_entropy) throw ("In-bred. Minimum entropy between parents = "+mv.me.Z.min_entropy+". Actual entropy = "+entropy);
	else 	lib_debug (mv.ba, mv.from.action, 'success','high enough parent entropy. ');


	// Calculate a bit and offset value for every pos in Z
	assign_contents(mv, reversal);

	// Store the new binding
	lib_insert_binding(mv);	

// wait for incoming message handler for next operation
	return mv.me.Z.binding_id;
	}
	catch (e) { lib_debug (mv.ba, 'meiosis_main', 'exception', e); }
	finally {
 		delete global_var; 
		delete reversal;
		delete pos;
		delete create_dt;
	}
};

meiosis_module.prototype.assign_bnd_value = function(mv, bnd_val) {
/* Choose which bound_value to put in Z:
				Default		X		Y		result.Z.bnd_val
	Z.bnd_val
	text		any			any		any		text	
	true		any			any		any		null
	null		'X'			text	any		text	inversely 'Y'
	null		'X'			null	any		null 	inversely 'Y'	
	null		'X|Y'		text	any		text 	inversely 'Y|X'
	null		'X|Y'		null	text	text 	inversely 'Y|X' 
	null		'X|Y'		null	null	null 	inversely 'Y|X' 
(null input includes null, false, undecided)
note: could be terser code
*/
	try {
	
	mv.me.Z.oos = bnd_val;	// initialise output
		
	if (bnd_val !== null) { 	
		if (bnd_val === true)  mv.me.Z.oos = null; // boolean truth value implies null in Z
	}
	else 
		 switch (global_var.get_default_bound_parent()) {
			case 'X':
				if (mv.me.X.bnd_val === null) 	mv.me.Z.oos  = mv.me.Y.bnd_val;
			break;

			case 'Y':
				if (mv.me.Y.bnd_val === null) 	mv.me.Z.oos  = mv.me.X.bnd_val;
			break;

			case 'X/Y':
				if (mv.me.X.bnd_val === null) 	mv.me.Z.oos  = mv.me.Y.bnd_val;
				else  							mv.me.Z.oos  = mv.me.X.bnd_val
			break;

			case 'Y/X':
				if (mv.me.Y.bnd_val === null) 	mv.me.Z.oos  = mv.me.X.bnd_val;
				else  							mv.me.Z.oos  = mv.me.Y.bnd_val
			break;
			default: throw ("invalid global bind value specifier")
		}
	}
	catch (e) { lib_debug (mv.ba, 'assign_bnd_value', 'exception', e); }
	finally {
	}
}

meiosis_module.prototype.assign_depth = function(mv) {
/* Choose which depth to use for Z:
				limit	X		Y		result Z
Parameter
	null		a+b+c	a+b		b		b+1	
	null		b		c		c+a		b	
	c			a+b+c	a+b		b		b+1	
	a			a+b		a+b		a+b		a	
	c			a		c+a		a+b		a	
(a<=b<=c>0). X and Y are interchangeable. 
natural_depth is the least depth of the two parents, incremented by one for this instance of meiosis. 
An upper limit can be specified. If a depth is given as a parameter (in the Z state) then it is an alternate upper bound.
*/
	try {
		var natural_depth 	= lower(mv.me.X.depth, mv.me.Y.depth) +1;
		mv.me.Z.depth 	= 	lower(natural_depth, mv.me.Z.depth);
	}
	catch (e) { lib_debug (mv.ba, 'assign_depth', 'exception', e); }
	finally {
		delete natural_depth;
	}
}

meiosis_module.prototype.assign_contents = function(mv, reversal) {
/*
Reversal	X-Content	X-Offset	Y-Content	Y-Offset	Z-Content	Z-Offset
No			1			any			1			any			1			X+Y+2
Yes			1			any			0			any			(Y)			X+Y+2
Yes			1			b			1			a			0			X-Y +1
No			0			a			1			b			0			Y-X +1
No			0			a			1			a			tb(X,Y)		1
Yes			1			a			1			a			tb(X,Y)		1
zero and one are interchangeable. X and Y are interchangeable. a < b. 

The assignments in this algorithm represent the core of the meiosis algorithm.
It works like two captains each selecting their footbal team from a pool of 20 other payers.
The pool is those pos which are not effectvely the same. ('effectively' respects reversal).

In every round, each captain picks the member that they like best from all those remaining. 
There is a slight advantage to the captain who picks first (X parent).
Each parent is assigning weight relative only to ITSELF. Therefore, there is no incentive to cheat 
by increasing offset scores in the hope of beating the other parent to greater similarity with Z. 

The outcome should be fair:
	each parent shares equal similarity to Z
	ancestors acquire maximal similarity to Z, given the designed absence of omniscience (TPV) in the system
Note that in aggregate all ancestors of richer (higher depth) cTags receive only the same similarity as
all ancestors of the other cTag. Selection strategies might take this into accocunt by, 
e.g. preferring meiosis with similar depth cTags. 
*/
	var db 			= google.gears.factory.create('beta.database');
	var X_score 	= 0;
	var Y_score 	= 0;
	var Z_length 	= Math.pow(2, mv.me.Z.breadth);
	var pos 		= Z_length -1;
	mv.me.Z.conts	= new Array;
	mv.me.Z.offset	= new Array;


	try {
		db.open	(mv.cu.name);	

	// equity_rank temporary table  
//		db.execute(	'CREATE temporary TABLE if not exists equity_rank ' +
		db.execute(	'CREATE  TABLE if not exists equity_rank ' +
				'(X_id integer not null, Y_id integer not null, pos integer not null, '+
				' X_offset_score integer not null, Y_offset_score not null)' 
			);
		
		db.execute('begin');	

/* 		each parent first writes down its priority for every disputed pos.
use one db record for both X and Y scores
priority is based on existing offset for the pos.
*/
		while (pos >= 0 ) {
			// initialise outputs
			mv.me.Z.conts[pos] 		= null; 	
			mv.me.Z.offset[pos] 	= 0;

			if (	mv.me.X.conts[pos] === mv.me.Y.conts[pos]	) 
			{	// X and Y effectively the same, so the outcome is undisputed. +2 is for X and Y
				mv.me.Z.offset[pos] 	= mv.me.X.offset[pos] + mv.me.Y.offset[pos] + 2;		
				mv.me.Z.conts[pos] 		= mv.me.Y.conts[pos];	// consistently arbitrary polarity choice
			}
			else {	// effectively different, so place in the equity_rank pool. +1 is for one parent only
				X_score = 1 + mv.me.X.offset[pos] - mv.me.Y.offset[pos];
				Y_score = 1 + mv.me.Y.offset[pos] - mv.me.X.offset[pos];
				db.execute('insert into equity_rank ' +
				' (X_id, Y_id, pos, X_offset_score, Y_offset_score)' +
				' values((?),(?),(?),(?),(?))', 
				[mv.me.X.binding_id, mv.me.Y.binding_id, pos, X_score, Y_score]);  			
			}
		pos--;
		}
		
		db.execute( 'commit');		

		pos = 0;

/* open two cursors, ordered by the preferences of each parent respectively.
 * The tertiary order (pos) will be most significant in meiosis involving two or one  
 * synthetic cTag parents, because these have no offsets.
 */
		var rsx = db.execute(	
			'select pos, X_offset_score from equity_rank ' + 			
			'where X_id = (?) and Y_id = (?) '+ 
			' order by X_offset_score desc, Y_offset_score asc, pos asc',
			[mv.me.X.binding_id, mv.me.Y.binding_id]);

		var rsy = db.execute(	
			'select pos, Y_offset_score from equity_rank ' + 			
			'where X_id = (?) and Y_id = (?) '+
			' order by Y_offset_score desc, X_offset_score asc, pos asc',
			[mv.me.X.binding_id, mv.me.Y.binding_id]);

/* revisit every pos having effectively different X and Y. 
 *	In rotation, pick the best remaining X then Y, so that each parent receives equal 
 *  dissimilarity to child (equity). After allowing for reversal.
*/	
		while (rsx.isValidRow() && rsy.isValidRow()) 
		{
			while (rsx.isValidRow()) {
				pos = rsx.field(0);
				if (mv.me.Z.conts[pos] === 0 ||mv.me.Z.conts[pos] === 1) { // if not yet picked by Y
					rsx.next(); // already picked by Y so try next best choice
				}
				else {
					mv.me.Z.conts[pos] = mv.me.X.conts[pos];
					mv.me.Z.offset[pos] = rsx.field(1);
					rsx.next(); 
					break;
				}
			}
			while (rsy.isValidRow()) {
				pos = rsy.field(0);
				if (mv.me.Z.conts[pos] === 0 ||mv.me.Z.conts[pos] === 1) {
					rsy.next();
				}
				else {
					mv.me.Z.conts[pos] = mv.me.Y.conts[pos];
					mv.me.Z.offset[pos] = rsy.field(1);
					rsy.next();
					break;
				}
			}
		}
	}
	catch (e) { lib_debug (mv.ba, 'assign_contents', 'exception', e); }
	finally {
		db.close;
		rsx.close(); 
		rsy.close();
		delete X_score, Y_score, Z_length, pos, rsx, rsy;
	}
}

meiosis_module.prototype.assign_version = function(mv, Z) {
/* note: a proper cTag version decider is required here. 
 * mos_breadth is tied to version
 */	
 	try {
 		Z.version		=	mv.me.version;
		Z.mos_breadth	=	mv.me.mos_breadth;
	}
	catch (e) { lib_debug (mv.ba, 'assign_version', 'exception', e); }
	finally {
	}
}

meiosis_module.prototype.validate_cTag = function (mv, binding) {

	try { 
		if (binding.act_flag === false) 
			throw ("cTag validation error: parent inactive or non-existent");			
		if (Math.pow(2, binding.breadth) !==  binding.conts.length)
			throw ('cTag validation error: breadth and contents mismatch');			
		if (binding.synth_flag === '1' && binding.depth !== 0)
			throw ("cTag validation error: parent synthetic tag's depth must be 0");				

// note: reinstate version validation code
/*
		var supptary = global_var.get_supported_cTag_version(); 
		for (var i=0 ; i < supptary.length; i++) 
			{ if (binding.version === supptary[i]) break;
				else if (i === supptary.length -1)
					throw ("cTag validation error: unsupported version");
			}

*/		}
	catch (e) { lib_debug (mv.ba, 'validate_cTag', 'exception', e); }
}

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

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

meiosis_module.prototype.db_bool = function(s){
	if (s) return '1'; 
	else return '0';
};

meiosis_module.prototype.dissimilar = function(mv, source, target, reversal){	
/* Compare the contents of a pair of bindings. 
 * If reversal is specified, return their dissimilarity. Else their unalikeness.
 * source/target are assigned arbitrarily. they are just a pair of cTags.
 */
	var tally 			= 0;
	var pos				= source.length -1;
	try {
		while (pos >=0) {
			if (source[pos] !== target[pos]) tally++;
			pos--;
		}

		if (reversal && (tally > source.length / 2) ) 		
			return source.length - tally; 	
		else return tally;
	}
	catch (e) { lib_debug (mv.ba, 'dissimilar', 'exception', e); }
	finally {
		delete tally, pos;
	}
};

meiosis_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 
 */
	var dbgb 		= google.gears.factory.create('beta.database');
	var rsgb; 

	try {
		dbgb.open(mv.cu.name);
		
		if (active_flag) { // selected binding must be active
			rsgb = dbgb.execute( // retrieve single binding by primary key
			' select 	ID, xparent_id, description, version, breadth, depth, synthetic_flag, ' +
			' bound_value, created_date, active_flag from binding ' +
			' where ID = (?) and active_flag=(?) ', [binding.binding_id, '1']);
		}
		else {
			rsgb = dbgb.execute( 
			'select 	ID, xparent_id, description, version, breadth, depth, synthetic_flag, ' +
			' bound_value, created_date, active_flag 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.bnd_val 	= rsgb.field(7);
			binding.act_flag 	= rsgb.field(9);
			return true;
		} else return false;
	} 
	catch (e) { lib_debug (mv.ba, 'get_binding_id', 'exception', e); }
	finally {
		rsgb.close(); 
		dbgb.close();
//		delete rsgb; 
		delete active_flag;
	}
}
	
meiosis_module.prototype.get_contents_id = function(mv, binding, start_lot, end_lot) {
/* get contents on ID  
 * parameter binding is the module parameter (X,Y or Z) to populate. 
 * binding.binding_id should be populated already 
 * finish before end_lot
 * returns results to calling function
 */
	var dbgc 		= google.gears.factory.create('beta.database');
	var rsgc; 
	var mos_length	= Math.pow(2, binding.mos_breadth);
	var startpos	= mos_length * start_lot;
	var endpos		= mos_length * end_lot;
	var pos 		= 0;

	var conts 	= []; 	var offset	= []; // initialise target arrays

	try {
		dbgc.open(mv.cu.name);
		rsgc = dbgc.execute(	
 			' select bit_value, offset '	+
 			' from contents ' 				+
 			' where id = ? ' 				+
			' and pos >= ? ' 				+
 			' and pos <  ? ' 				+
 			' order by pos', [binding.binding_id, startpos, endpos]);
	
		while (pos < endpos-startpos) {
			conts[pos] 	= rsgc.field(0) ;
			offset[pos] = rsgc.field(1) ;
			rsgc.next();
		  	pos++;
		}
		return {conts : conts, offset : offset}
	} 
	catch (e) { lib_debug (mv.ba, 'get_contents_id', 'exception', e); }
	finally {
		rsgc.close(); 
		dbgc.close();
		delete rsgc; 
		delete pos;
	}
}


/* Foreground functions
 * 
 */

	function meiosis(culture, organelle, breadth, X_ID, Y_ID){
		/* This function is called from the custom menu, which is located in the settings file config/amradar_settings.xml
		 * 
		 */
	try {
		var mv = init_mv();	// initialise module variable structure. Assign default and UI values

		// comment: fire up the main meiosis worker (if needed) and store its ID in a global Text field
	     if(meiworker === 0) meiworker = new meiosis_module(wp);
	
		// localise module parameters 
		var create_dt 		= new Date().getTime();
// globalised		var wkrthrottle		= +document.getElementById("GearsWorkers").value;	
		var maxBreadth 		= +document.getElementById("ConcMaxBreadth").value;	
		var Z_breadth 		= +document.getElementById("ConcDefBreadth").value;
		var Z_depth 		= +document.getElementById("ConcMaxDepth").value;
		var MaxInBred 		= +document.getElementById("ConcMaxInBred").value;
		var def_bind_parent = document.getElementById("ConcDefBindPar").value;
		var version			= document.getElementById("ConcAkinityVers").value;
		var organelle		= document.getElementById("organelle").value;
		var mos_breadth		= document.getElementById("ConcMosBreadth").value;

		// set parameters for new binding
		mv.me.Z = lib_set_binding (version, Z_breadth, Z_depth, '0', null, organelle, mos_breadth, MaxInBred, def_bind_parent, 0, mv.ba.sywID);

		mv.cu.name 				= culture;
		mv.me.X.binding_id 		= X_ID; 
		mv.me.Y.binding_id 		= Y_ID; 
		mv.dest = {
			module: 'meiosis',
			action: 'initiate',
			status: null,
			message: null,
			ID: meiworker.ID }

		// start meiosis 
		trx_caller (wp, mv, synworker.Script, wkrthrottle, mv.dest.ID);
	}
	catch (e){
		alert('meiosis exception : '+ e && e.message);
	}
}



