var _module_ = {
    exports: {}
};

var exports = _module_.exports;
/* eslint-disable eqeqeq, indent, keyword-spacing, max-len, no-mixed-spaces-and-tabs, no-unused-vars, no-var, semi, space-infix-ops */
/* TODO(csilvers): fix these lint errors (http://eslint.org/docs/rules): */
/* To fix, remove an entry above, run ka-lint, and fix errors. */
/*
 * A class for formatting number to significant digits.
 * Copyright (C) 2001 Stephen Ostermiller
 * http://ostermiller.org/contact.pl?regarding?JavaScript+Significant+Figures
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

/**
 * An object tho both scans floating points to
 * determine the number of significant figures.
 * and can display a floating point using any number
 * of significant figures.
 *
 * @param s A string representation of a floating point.
 */
export function SignificantFigures(s) {
    this.order = parseOrder(s);
    this.mantissa = parseMantissa(s);
    this.positive = parseSign(s);

    /**
     * Get the number of significant figures this object uses.
     * Leading zeros are not significant.  Traling zeros up to
     * and after the decimal point are significant.
     * Significant figures is good to know when the number is
     * used in multiplication.
     *
     * @return the number of significant figures.
     */
    this.sigFigs = this.mantissa.length;

    /**
     * Get the least significant decimal this object uses.
     * This is useful to know if a number is being used
     * in addition.
     * 400 - 2 the hundreds place (10^2) is the least significant decimal.
     * 75 - 0 the ones place (10^0) is the least significant decimal.
     * .543 - -3 the 1/1000's place (10^-3) is the least significant decimal.
     *
     * @return an integer representing the least significant decimal place.
     */
    this.sigDecs = this.order - this.mantissa.length;
}

/**
 * Format a floating point for display using the specified
 * number of significant figures and least significant decimal.
 * Scientific notation may used by this method if this
 * object is very small, very large, has many significant
 * figures, or the number of significant figures would be
 * ambiguous in the output if scientific notation were not
 * used.
 *
 * @param f A floating point number that should be displayed
 * @param sigFigs desired number of significant figures (integer).
 * @param sigDecs the least significant decimal place (integer).
 * @param scientific true iff scientific notation should always be used.
 * @return a string of this object formatted correctly.
 */
export function displaySigFigs(f, sigFigs, sigDecs, scientific) {
    var s = "" + f;
    var order = parseOrder(s);
    var mantissa = parseMantissa(s);
    var positive = parseSign(s);
    var add;
    var decAdd;
    var sigAdd;
    var zeroScientific = false;
    if (f == 0 || mantissa == "" || mantissa == "0") {
        mantissa = "";
        for (i = 0; i < sigFigs; i++) {
            mantissa += "0";
        }
        order = sigFigs + sigDecs;
        if (sigDecs < 0 && -sigDecs >= sigFigs) {
            zeroScientific = true;
        }
    } else {
        decAdd = order - mantissa.length - sigDecs;
        sigAdd = sigFigs - mantissa.length;
        add = Math.min(sigAdd, decAdd);
        if (add < 0) {
            var rounded = round(mantissa, -add);
            if (rounded.length > mantissa.length + add) {
                order++;
                if (decAdd > sigAdd) {
                    rounded = round(rounded, 1);
                }
            }
            mantissa = rounded;
        } else if (add > 0) {
            for (i = 0; i < add; i++) {
                mantissa += "0";
            }
        }
        if (mantissa == "" || mantissa == "0") {
            mantissa = "0";
            positive = true;
            order = 1 + sigDecs;
            if (order != 0) {
                zeroScientific = true;
            }
        }
    }
    var useScientific =
        scientific ||
        mantissa.length > 20 ||
        order > 4 ||
        order < -2 ||
        (order - mantissa.length > 0 && trailingZeros(mantissa) > 0) ||
        zeroScientific;
    var returnVal = "";
    if (!positive) {
        returnVal += "-";
    }
    if (useScientific) {
        returnVal += mantissa.charAt(0);
        if (mantissa.length > 1) {
            returnVal += "." + mantissa.substring(1, mantissa.length);
        }
        if (order - 1 != 0) {
            returnVal += " x 10^" + (order - 1);
        }
    } else {
        var wholePart = "";
        var fractPart = "";
        var needDot = true;
        if (order > 0) {
            if (mantissa.length > order) {
                wholePart = mantissa.substring(0, order);
                fractPart = mantissa.substring(order, mantissa.length);
            } else {
                wholePart = mantissa;
                needDot = trailingZeros(mantissa) != 0;
                for (var i = 0; i < order - mantissa.length; i++) {
                    wholePart += "0";
                }
            }
        } else {
            for (i = 0; i < -order; i++) {
                fractPart += "0";
            }
            fractPart += mantissa;
        }
        returnVal +=
            (wholePart == "" ? "0" : wholePart) +
            (needDot ? "." : "") +
            fractPart;
    }
    return returnVal;
}

/**
 * Count the significant trailing zeros on this object.
 *
 * @return the number of trailing zeros
 */
function trailingZeros(mantissa) {
    var zeros = 0;
    for (var i = mantissa.length - 1; i >= 0; i--) {
        var c = mantissa.charAt(i);
        if (c == "0") {
            zeros++;
        } else {
            return zeros;
        }
    }
    return zeros;
}

/**
 * Parse a string representation of a floating point
 * and pull out the sign.
 *
 * @param s the string representation of a floating point.
 * @return true iff this is a positive number
 */
function parseSign(s) {
    var beginning = true;
    var seenDot = false;
    var seenSomething = false;
    var zeros = "";
    var leadZeros = "";
    var all = "";
    var decPlaces = 0;
    var totalDecs = 0;
    var pos = true;
    for (var i = 0; i < s.length; i++) {
        var c = s.charAt(i);
        if (c >= "1" && c <= "9") {
            all += zeros + c;
            zeros = "";
            seenSomething = true;
            if (!seenDot) {
                totalDecs++;
                decPlaces++;
            }
            beginning = false;
        } else if (c == "0") {
            if (seenDot) {
                if (seenSomething) {
                    all += zeros + c;
                    zeros = "";
                } else {
                    leadZeros += c;
                    decPlaces--;
                }
            } else {
                totalDecs++;
                if (seenSomething) {
                    leadZeros += c;
                    decPlaces++;
                    zeros += c;
                } else {
                    leadZeros += c;
                }
            }
            beginning = false;
        } else if (!seenDot && c == ".") {
            all += zeros;
            zeros = "";
            seenDot = true;
            beginning = false;
        } else if (c == "e" || (c == "E" && i + 1 < s.length)) {
            var raised = parseInt(s.substring(i + 1, s.length));
            decPlaces += raised;
            totalDecs += raised;
            i = s.length;
        } else if (beginning && (c == "+" || c == "-")) {
            if (c == "-") {
                pos = !pos;
            }
        }
    }
    if (all == "") {
        return true;
    } else {
        return pos;
    }
}

/**
 * Parse a string representation of a floating point
 * and pull out the mantissa.
 *
 * @param s the string representation of a floating point.
 * @return the mantissa of this number.
 */
function parseMantissa(s) {
    var beginning = true;
    var seenDot = false;
    var seenSomething = false;
    var zeros = "";
    var leadZeros = "";
    var all = "";
    var decPlaces = 0;
    var totalDecs = 0;
    var pos = true;
    for (var i = 0; i < s.length; i++) {
        var c = s.charAt(i);
        if (c >= "1" && c <= "9") {
            all += zeros + c;
            zeros = "";
            seenSomething = true;
            if (!seenDot) {
                totalDecs++;
                decPlaces++;
            }
            beginning = false;
        } else if (c == "0") {
            if (seenDot) {
                if (seenSomething) {
                    all += zeros + c;
                    zeros = "";
                } else {
                    leadZeros += c;
                    decPlaces--;
                }
            } else {
                totalDecs++;
                if (seenSomething) {
                    leadZeros += c;
                    decPlaces++;
                    zeros += c;
                } else {
                    leadZeros += c;
                }
            }
            beginning = false;
        } else if (!seenDot && c == ".") {
            all += zeros;
            zeros = "";
            seenDot = true;
            beginning = false;
        } else if (c == "e" || (c == "E" && i + 1 < s.length)) {
            var raised = parseInt(s.substring(i + 1, s.length));
            decPlaces += raised;
            totalDecs += raised;
            i = s.length;
        } else if (beginning && (c == "+" || c == "-")) {
            if (c == "-") {
                pos = !pos;
            }
        }
    }
    if (all == "") {
        return leadZeros;
    } else {
        return all;
    }
}

/**
 * Parse a string representation of a floating point
 * and pull out the exponent.
 *
 * @param s the string representation of a floating point.
 * @return (integer) the number after the e.
 */
function parseOrder(s) {
    var beginning = true;
    var seenDot = false;
    var seenSomething = false;
    var zeros = "";
    var leadZeros = "";
    var all = "";
    var decPlaces = 0;
    var totalDecs = 0;
    var pos = true;
    for (var i = 0; i < s.length; i++) {
        var c = s.charAt(i);
        if (c >= "1" && c <= "9") {
            all += zeros + c;
            zeros = "";
            seenSomething = true;
            if (!seenDot) {
                totalDecs++;
                decPlaces++;
            }
            beginning = false;
        } else if (c == "0") {
            if (seenDot) {
                if (seenSomething) {
                    all += zeros + c;
                    zeros = "";
                } else {
                    leadZeros += c;
                    decPlaces--;
                }
            } else {
                totalDecs++;
                if (seenSomething) {
                    leadZeros += c;
                    decPlaces++;
                    zeros += c;
                } else {
                    leadZeros += c;
                }
            }
            beginning = false;
        } else if (!seenDot && c == ".") {
            all += zeros;
            zeros = "";
            seenDot = true;
            beginning = false;
        } else if (c == "e" || (c == "E" && i + 1 < s.length)) {
            var raised = parseInt(s.substring(i + 1, s.length));
            decPlaces += raised;
            totalDecs += raised;
            i = s.length;
        } else if (beginning && (c == "+" || c == "-")) {
            if (c == "-") {
                pos = !pos;
            }
        }
    }
    if (all == "") {
        return totalDecs;
    } else {
        return decPlaces;
    }
}

/**
 * Remove the specified number of digits from string by
 * rounding.  Proper rounding rules for scientific purposes
 * are followed.
 * This method may cause an extra significant figure
 * to be added to the number.  For example, if 999999
 * were rounded, A one would carry over and become
 * a significant figure.  Those who call this method
 * should check for this and call round again if needed.
 *
 * @param mantissa A string representing an whole number of arbitrary length.
 * @param digits A number of digits to remove
 * @return A string represted the rounded version of mantissa
 */
function round(mantissa, digits) {
    var last = mantissa.length - digits - 1;
    if (last < 0) {
        return "";
    } else if (last >= mantissa.length - 1) {
        return mantissa;
    } else {
        var nextToLast = mantissa.charAt(last + 1);
        var lastChar = mantissa.charAt(last);
        var roundUp = false;
        if (nextToLast > "5") {
            roundUp = true;
        } else if (nextToLast == "5") {
            for (var j = last + 2; j < mantissa.length; j++) {
                if (mantissa.charAt(j) != "0") {
                    roundUp = true;
                }
            }
            if (lastChar % 2 == 1) {
                roundUp = true;
            }
        }
        var result = "";
        for (var i = last; i >= 0; i--) {
            var c = mantissa.charAt(i);
            if (roundUp) {
                var nextChar;
                if (c == "9") {
                    nextChar = "0";
                } else {
                    switch (c) {
                        case "0":
                            nextChar = "1";
                            break;
                        case "1":
                            nextChar = "2";
                            break;
                        case "2":
                            nextChar = "3";
                            break;
                        case "3":
                            nextChar = "4";
                            break;
                        case "4":
                            nextChar = "5";
                            break;
                        case "5":
                            nextChar = "6";
                            break;
                        case "6":
                            nextChar = "7";
                            break;
                        case "7":
                            nextChar = "8";
                            break;
                        case "8":
                            nextChar = "9";
                            break;
                    }
                    roundUp = false;
                }
                result = nextChar + result;
            } else {
                result = c + result;
            }
        }
        if (roundUp) {
            result = "1" + result;
        }
        return result;
    }
}

export default {
    SignificantFigures,
    displaySigFigs,
};
