const bitBase = 8;
const suffixes = {
  bit: 'b',
  b: 'B',
  kb: 'KB',
  mb: 'MB',
  gb: 'GB',
  tb: 'TB',
};
const multipliers = {
  bit: {
    toBitHr: 1,
    toB: 1 / bitBase,
    toKB: 1 / (bitBase * 1e3),
    toMB: 1 / (bitBase * 1e6),
    toGB: 1 / (bitBase * 1e9),
    toTB: 1 / (bitBase * 1e12),
  },
  B: {
    toBit: bitBase,
    toBHr: 1,
    toKB: 1 / 1e3,
    toMB: 1 / 1e6,
    toGB: 1 / 1e9,
    toTB: 1 / 1e12,
  },
  KB: {
    toBit: 1 / (bitBase * 1e3),
    toB: 1e3,
    toKBHr: 1,
    toMB: 1 / 1e3,
    toGB: 1 / 1e6,
    toTB: 1 / 1e9,
  },
  MB: {
    toBit: bitBase * 1e6,
    toB: 1e6,
    toKB: 1e3,
    toMBHr: 1,
    toGB: 1 / 1e3,
    toTB: 1 / 1e6,
  },
  GB: {
    toBit: bitBase * 1e9,
    toB: 1e9,
    toKB: 1e6,
    toMB: 1e3,
    toGBHr: 1,
    toTB: 1 / 1e3,
  },
  TB: {
    toBit: bitBase * 1e12,
    toB: 1e12,
    toKB: 1e9,
    toMB: 1e6,
    toGB: 1e3,
    toTBHr: 1,
  },
};
const round = (num, decimalPlaces) => {
  const strNum = num.toString();
  const isExp = strNum.includes('e');
  if (isExp) {
    return Number(num.toPrecision(decimalPlaces + 1));
  }
  return Number(
    `${Math.round(Number(`${num}e${decimalPlaces}`))}e${decimalPlaces * -1}`,
  );
};
function conv(
  value,
  hr,
  rnd,
  multiplier,
  suffix,
) {
  let val = value * multiplier;
  if ((value * multiplier) > Number.MAX_SAFE_INTEGER) {
    val = Number.MAX_SAFE_INTEGER;
  }
  if (val < Number.MIN_VALUE) val = 0;
  if ((rnd || rnd === 0) && val < Number.MAX_SAFE_INTEGER) {
    val = round(val, rnd);
  }
  if (hr) return `${val}${suffix}`;
  return val;
}
const MemConv = (function _() {
  return {
    bit(value) {
      return {
        toBitHr(opts = {}) {
          return conv(
            value,
            true,
            opts.round || false,
            multipliers.bit.toBitHr,
            suffixes.bit,
          );
        },
        toB(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.bit.toB,
            suffixes.b,
          );
        },
        toKB(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.bit.toKB,
            suffixes.kb,
          );
        },
        toMB(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.bit.toMB,
            suffixes.mb,
          );
        },
        toGB(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.bit.toGB,
            suffixes.gb,
          );
        },
        toTB(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.bit.toTB,
            suffixes.tb,
          );
        },
      };
    },
    B(value) {
      return {
        toBit(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.B.toBit,
            suffixes.bit,
          );
        },
        toBHr(opts = {}) {
          return conv(
            value,
            true,
            opts.round || false,
            multipliers.B.toBHr,
            suffixes.b,
          );
        },
        toKB(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.B.toKB,
            suffixes.kb,
          );
        },
        toMB(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.B.toMB,
            suffixes.mb,
          );
        },
        toGB(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.B.toGB,
            suffixes.gb,
          );
        },
        toTB(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.B.toTB,
            suffixes.tb,
          );
        },
      };
    },
    KB(value) {
      return {
        toBit(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.KB.toBit,
            suffixes.bit,
          );
        },
        toB(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.KB.toB,
            suffixes.b,
          );
        },
        toKBHr(opts = {}) {
          return conv(
            value,
            true,
            opts.round || false,
            multipliers.KB.toKBHr,
            suffixes.kb,
          );
        },
        toMB(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.KB.toMB,
            suffixes.mb,
          );
        },
        toGB(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.KB.toGB,
            suffixes.gb,
          );
        },
        toTB(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.KB.toTB,
            suffixes.tb,
          );
        },
      };
    },
    MB(value) {
      return {
        toBit(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.MB.toBit,
            suffixes.bit,
          );
        },
        toB(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.MB.toB,
            suffixes.b,
          );
        },
        toKB(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.MB.toKB,
            suffixes.kb,
          );
        },
        toMBHr(opts = {}) {
          return conv(
            value,
            true,
            opts.round || false,
            multipliers.MB.toMBHr,
            suffixes.mb,
          );
        },
        toGB(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.MB.toGB,
            suffixes.gb,
          );
        },
        toTB(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.MB.toTB,
            suffixes.tb,
          );
        },
      };
    },
    GB(value) {
      return {
        toBit(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.GB.toBit,
            suffixes.bit,
          );
        },
        toB(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.GB.toB,
            suffixes.b,
          );
        },
        toKB(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.GB.toKB,
            suffixes.kb,
          );
        },
        toMB(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.GB.toMB,
            suffixes.mb,
          );
        },
        toGBHr(opts = {}) {
          return conv(
            value,
            true,
            opts.round || false,
            multipliers.GB.toGBHr,
            suffixes.gb,
          );
        },
        toTB(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.GB.toTB,
            suffixes.tb,
          );
        },
      };
    },
    TB(value) {
      return {
        toBit(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.TB.toBit,
            suffixes.bit,
          );
        },
        toB(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.TB.toB,
            suffixes.b,
          );
        },
        toKB(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.TB.toKB,
            suffixes.kb,
          );
        },
        toMB(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.TB.toMB,
            suffixes.mb,
          );
        },
        toGB(opts = {}) {
          return conv(
            value,
            opts.hr || false,
            opts.round || false,
            multipliers.TB.toGB,
            suffixes.gb,
          );
        },
        toTBHr(opts = {}) {
          return conv(
            value,
            true,
            opts.round || false,
            multipliers.TB.toTBHr,
            suffixes.tb,
          );
        },
      };
    },
  };
}());
const testCases = [1, 10, 150, 1000, 74839.67346];
const HRSuffixes = Object.values(suffixes);
const roundDecimals = 2;
const precision = Number(`0.${'0'.repeat(roundDecimals)}5`);
const SCIENTIFIC_NOT_NUMBER_REGXP = /[-+]?[0-9]*.?[0-9]+([eE][-+]?[0-9]+)?/g;
const SUFFIX_REGXP = /[a-z]+$/i;
const CONVERSION_TO_REGXP = /(?<=to).*(?=hr+$)|(?<=to).*(?=hr+$)?/i;
for (const conversionFrom of (Object.keys(MemConv))) {
  for (const tCase of testCases) {
    const convFunc = MemConv[conversionFrom](tCase);
    for (const [conversionToFn, f] of Object.entries(convFunc)) {
      const conversionTo = (conversionToFn.match(CONVERSION_TO_REGXP) || [conversionToFn])[0];
      const result = f();
      const humanReadable = f({ hr: true });
      const rounded = f({ round: roundDecimals });
      const roundedAndHumanReadable = f({ hr: true, round: roundDecimals });
      console.log({
        value: tCase,
        from: conversionFrom,
        to: conversionTo,
        result,
        humanReadable,
        rounded,
        roundedAndHumanReadable,
      });
    }
  }
}