# Source: emassanddistribution.js

    /**
     * Class to calculate Emass and Distribution for the given input
     */
    class CalculateEmassAndDistribution{

    	constructor(){
    		this.PREFIX = "PREFIX";
    		this.SUFFIX = "SUFFIX";
    		this.minintensity = 0.000001;
    		this.protonMass = 1.00727647  ;
    		this.intensityTolerance = 1 ;
    		this.toleraceMassDiff = 0.02;
    	}
    	/**
    	 * Function to calculate the emass distrubution fo the given sequence
    	 * @param {String} seq - Contains the sequence provide by the user
    	 * @param {Array} peakDataList - Contains the peak list provided by the user
    	 * @param {Float} charge - Contains the chrage of the ion
    	 * @param {String} pref_suffInd - Indicator to indiace prefix or suffix
    	 */
    	emass(seq,peakDataList,charge,pref_suffInd)
    	{
    		let AcidArray = seq ;
    		let  AcidArrayLen= AcidArray.length;
    		let totDistributionList = [] ;

    		for(let i = 0; i < AcidArrayLen ; i++)
    		{
    			let aminoAcidDist = getAminoAcidDistribution(AcidArray[i]);
    			totDistributionList = this.getMassAndIntensity(totDistributionList,aminoAcidDist) ;
    		}
    		if(pref_suffInd == this.SUFFIX)
    		{
    			let waterDist = getAminoAcidDistribution("H2O");
    			totDistributionList = this.getMassAndIntensity(totDistributionList,waterDist);
    		}
    		totDistributionList = this.getMZwithHighInte(totDistributionList,charge,peakDataList);
    		totDistributionList = this.getNormalizedIntensity(totDistributionList,peakDataList);
    		totDistributionList.sort(function(x, y){
    		   return d3.ascending(x.mz, y.mz);
    		})
    		return totDistributionList ;
    	}
    	/**
    	 * Logic to calculate distribution
    	 * @param {Array} totDistributionList - Array with current total distribution
    	 * @param {Array} aminoAcidDist - Array with existing calculated distribution of amino acid
    	 */
    	getMassAndIntensity(totDistributionList,aminoAcidDist)
    	{
    		let maxintensity = 0 ;
    		if(totDistributionList.length == 0)
    		{
    			return aminoAcidDist ;
    		}
    		else
    		{
    			let len = totDistributionList.length + aminoAcidDist.length - 1;
    	  		let completeDistributionList = new Array(len).fill(0) ;
    			for(let i=0;i<totDistributionList.length;i++)
    			{
    				for(let j=0;j<aminoAcidDist.length;j++)
    				{
    					let intensity = 0;
    					let index = i+j ;
    					let mass = totDistributionList[i].mass+aminoAcidDist[j].mass ;
    					intensity = totDistributionList[i].intensity * aminoAcidDist[j].intensity ;
    					if(completeDistributionList[index] != 0) intensity = intensity + completeDistributionList[index].intensity ;
    					completeDistributionList[index] = {mass:mass,intensity:intensity};
    					if(intensity > maxintensity) maxintensity = intensity ;
    				}
    			}
    			let completeDistributionList_temp = [];
    			for(let i = 0; i<completeDistributionList.length; i++)
    			{
    				let tempintensityVal = (completeDistributionList[i].intensity/maxintensity)*100;
    				//Taking float values till 6 decimals
    				if( tempintensityVal > this.minintensity)
    				{
    					completeDistributionList[i].intensity = Math.round(tempintensityVal * 1000000) / 1000000;//tempintensityVal//Math.round(tempintensityVal * 1000000) / 1000000; //parseFloat(((tempDistributionList[i].intensity/maxintensity)*100).toFixed(6)) ;
    					completeDistributionList_temp.push(completeDistributionList[i]);
    				}
    			}
    			completeDistributionList = completeDistributionList_temp ;
    			return completeDistributionList ;
    		}
    	}
    	/**
    	 * Code to remove the calculate MZ(mass/charge) value and remove low intensities
    	 * @param {Array} totDistributionList - Total distribution calculated
    	 * @param {Integer} charge - charge for the mass list by user
    	 * @param {Array} peakDataList - peaklist entered by the user
    	 */
    	getMZwithHighInte(totDistributionList,charge,peakDataList)
    	{
    		let len = totDistributionList.length;
    		let overaAllMaxIntensity = 0 ;
    		let onePerctInte = 0;
    		let peakListLen = peakDataList.length;
    		for(let k=0;k<peakListLen ; k++)
    		{
    			if(peakDataList[k].intensity > overaAllMaxIntensity) overaAllMaxIntensity = peakDataList[k].intensity ;
    		}
    		onePerctInte = overaAllMaxIntensity/100 ;
    		let newtotDistributionList = [];
    		for(let i=0;i<len;i++)
    		{
    			let intensity = totDistributionList[i].intensity ;
    			intensity = overaAllMaxIntensity * intensity/100 ;
    			if(intensity > onePerctInte)
    			{
    				let mz = totDistributionList[i].mass/charge + this.protonMass;
    				let intensity = totDistributionList[i].intensity ;
    				let tempdistObj = {mz:mz,intensity:intensity};
    				newtotDistributionList.push(tempdistObj);
    			}
    		}
    		return newtotDistributionList ;
    	}
    	/**
    	 * Code to normalize the Intensity.
    	 * Take the average of intensity from the peaks entered by the user.
    	 * Take the average of the calculated distribution for each Array element in the Array.
    	 * Make both of them equal and calculating the rest of the
    	 * distribution intensity based on the avg value from the peak list.
    	 * @param {Array} totDistributionList - Total distribution calculated
    	 * @param {Array} peakDataList - Peak Data entered by the user
    	 */
    	getNormalizedIntensity(totDistributionList,peakDataList)
    	{
    		let len = totDistributionList.length;
    		let peakListLen = peakDataList.length;
    		let intensity = 0;
    		let count = 0 ;
    		let distributionInte = 0;
    		for(let i=0;i<len;i++)
    		{
    			for(let j=0;j<peakListLen;j++)
    			{
    				if(Math.abs(totDistributionList[i].mz - peakDataList[j].mz) <= this.toleraceMassDiff )
    				{
    					intensity = intensity + peakDataList[j].intensity ;
    					distributionInte = distributionInte + totDistributionList[i].intensity;
    					count = count + 1;
    				}
    			}
    		}
    		let avg = intensity/count ;
    		let distributionAvgInte = distributionInte/count;
    		for(let i=0;i<len;i++)
    		{
    			totDistributionList[i].intensity = (avg * totDistributionList[i].intensity)/distributionAvgInte ;
    		}
    		return totDistributionList ;
    	}
    }