text: import { floatTwo } from './helpers';
  
  function normalize(x) {
  	// Calculates mantissa and exponent of a number
  	// Returns normalized number and exponent
  	// https://stackoverflow.com/q/9383593/6495043
  
  	if(x===0) {
  		return [0, 0];
  	}
  	if(isNaN(x)) {
  		return {mantissa: -6755399441055744, exponent: 972};
  	}
  	var sig = x > 0 ? 1 : -1;
  	if(!isFinite(x)) {
  		return {mantissa: sig * 4503599627370496, exponent: 972};
  	}
  
  	x = Math.abs(x);
  	var exp = Math.floor(Math.log10(x));
  	var man = x/Math.pow(10, exp);
  
  	return [sig * man, exp];
  }
  
  function getChartRangeIntervals(max, min=0) {
  	let upperBound = Math.ceil(max);
  	let lowerBound = Math.floor(min);
  	let range = upperBound - lowerBound;
  
  	let noOfParts = range;
  	let partSize = 1;
  
  	// To avoid too many partitions
  	if(range > 5) {
  		if(range % 2 !== 0) {
  			upperBound++;
  			// Recalc range
  			range = upperBound - lowerBound;
  		}
  		noOfParts = range/2;
  		partSize = 2;
  	}
  
  	// Special case: 1 and 2
  	if(range <= 2) {
  		noOfParts = 4;
  		partSize = range/noOfParts;
  	}
  
  	// Special case: 0
  	if(range === 0) {
  		noOfParts = 5;
  		partSize = 1;
  	}
  
  	let intervals = [];
  	for(var i = 0; i <= noOfParts; i++){
  		intervals.push(lowerBound + partSize * i);
  	}
  	return intervals;
  }
  
  function getChartIntervals(maxValue, minValue=0) {
  	let [normalMaxValue, exponent] = normalize(maxValue);
  	let normalMinValue = minValue ? minValue/Math.pow(10, exponent): 0;
  
  	// Allow only 7 significant digits
  	normalMaxValue = normalMaxValue.toFixed(6);
  
  	let intervals = getChartRangeIntervals(normalMaxValue, normalMinValue);
  	intervals = intervals.map(value => value * Math.pow(10, exponent));
  	return intervals;
  }
  
  export function calcChartIntervals(values, withMinimum=false) {
  	//*** Where the magic happens ***
  
  	// Calculates best-fit y intervals from given values
  	// and returns the interval array
  
  	let maxValue = Math.max(...values);
  	let minValue = Math.min(...values);
  
  	// Exponent to be used for pretty print
  	let exponent = 0, intervals = []; // eslint-disable-line no-unused-vars
  
  	function getPositiveFirstIntervals(maxValue, absMinValue) {
  		let intervals = getChartIntervals(maxValue);
  
  		let intervalSize = intervals[1] - intervals[0];
  
  		// Then unshift the negative values
  		let value = 0;
  		for(var i = 1; value < absMinValue; i++) {
  			value += intervalSize;
  			intervals.unshift((-1) * value);
  		}
  		return intervals;
  	}
  
  	// CASE I: Both non-negative
  
  	if(maxValue >= 0 && minValue >= 0) {
  		exponent = normalize(maxValue)[1];
  		if(!withMinimum) {
  			intervals = getChartIntervals(maxValue);
  		} else {
  			intervals = getChartIntervals(maxValue, minValue);
  		}
  	}
  
  	// CASE II: Only minValue negative
  
  	else if(maxValue > 0 && minValue < 0) {
  		// `withMinimum` irrelevant in this case,
  		// We'll be handling both sides of zero separately
  		// (both starting from zero)
  		// Because ceil() and floor() behave differently
  		// in those two regions
  
  		let absMinValue = Math.abs(minValue);
  
  		if(maxValue >= absMinValue) {
  			exponent = normalize(maxValue)[1];
  			intervals = getPositiveFirstIntervals(maxValue, absMinValue);
  		} else {
  			// Mirror: maxValue => absMinValue, then change sign
  			exponent = normalize(absMinValue)[1];
  			let posIntervals = getPositiveFirstIntervals(absMinValue, maxValue);
  			intervals = posIntervals.map(d => d * (-1));
  		}
  
  	}
  
  	// CASE III: Both non-positive
  
  	else if(maxValue <= 0 && minValue <= 0) {
  		// Mirrored Case I:
  		// Work with positives, then reverse the sign and array
  
  		let pseudoMaxValue = Math.abs(minValue);
  		let pseudoMinValue = Math.abs(maxValue);
  
  		exponent = normalize(pseudoMaxValue)[1];
  		if(!withMinimum) {
  			intervals = getChartIntervals(pseudoMaxValue);
  		} else {
  			intervals = getChartIntervals(pseudoMaxValue, pseudoMinValue);
  		}
  
  		intervals = intervals.reverse().map(d => d * (-1));
  	}
  
  	return intervals;
  }
  
  export function getZeroIndex(yPts) {
  	let zeroIndex;
  	let interval = getIntervalSize(yPts);
  	if(yPts.indexOf(0) >= 0) {
  		// the range has a given zero
  		// zero-line on the chart
  		zeroIndex = yPts.indexOf(0);
  	} else if(yPts[0] > 0) {
  		// Minimum value is positive
  		// zero-line is off the chart: below
  		let min = yPts[0];
  		zeroIndex = (-1) * min / interval;
  	} else {
  		// Maximum value is negative
  		// zero-line is off the chart: above
  		let max = yPts[yPts.length - 1];
  		zeroIndex = (-1) * max / interval + (yPts.length - 1);
  	}
  	return zeroIndex;
  }
  
  export function getRealIntervals(max, noOfIntervals, min = 0, asc = 1) {
  	let range = max - min;
  	let part = range * 1.0 / noOfIntervals;
  	let intervals = [];
  
  	for(var i = 0; i <= noOfIntervals; i++) {
  		intervals.push(min + part * i);
  	}
  
  	return asc ? intervals : intervals.reverse();
  }
  
  export function getIntervalSize(orderedArray) {
  	return orderedArray[1] - orderedArray[0];
  }
  
  export function getValueRange(orderedArray) {
  	return orderedArray[orderedArray.length-1] - orderedArray[0];
  }
  
  export function scale(val, yAxis) {
  	return floatTwo(yAxis.zeroLine - val * yAxis.scaleMultiplier);
  }
  
  export function calcDistribution(values, distributionSize) {
  	// Assume non-negative values,
  	// implying distribution minimum at zero
  
  	let dataMaxValue = Math.max(...values);
  
  	let distributionStep = 1 / (distributionSize - 1);
  	let distribution = [];
  
  	for(var i = 0; i < distributionSize; i++) {
  		let checkpoint = dataMaxValue * (distributionStep * i);
  		distribution.push(checkpoint);
  	}
  
  	return distribution;
  }
  
  export function getMaxCheckpoint(value, distribution) {
  	return distribution.filter(d => d < value).length;
  }
  