#include <float.h>
#include <stdio.h>
#include <stdlib.h>

/*
 * The two expressions "high - low" can result in +inf if "high" and
 * "low" are have a distance exceeding DBL_MAX. Similarly, the
 * expression "oper - low" can be +inf if "oper" and "low" differs
 * by more than DBL_MAX.
 */
int width_bucket_orig(double oper, double low, double high, int count) {
  return ((double)count * (oper - low) / (high - low)) + 1;
}

int width_bucket_new(double oper, double low, double high, int count) {
  const double bucket_size = (high / 2 - low / 2);
  const double bucket_pos = (oper / 2 - low / 2);
  return count * (bucket_pos / bucket_size) + 1;
}

static struct value {
  double operand;
  double low;
  double high;
  int count;
} values[] = {
    {10.4, -DBL_MAX, DBL_MAX, 10},
    {-DBL_MAX / 2, -DBL_MAX, DBL_MAX, 10},
    {DBL_MAX / 2, -DBL_MAX, DBL_MAX, 10},
    {10.4, -DBL_MAX, DBL_MAX, 12},
    {-DBL_MAX / 2, -DBL_MAX, DBL_MAX, 12},
    {DBL_MAX / 2, -DBL_MAX, DBL_MAX, 12},
    {10.4, -DBL_MAX, DBL_MAX, 1},
    {-DBL_MAX / 2, -DBL_MAX, DBL_MAX, 1},
    {DBL_MAX / 2, -DBL_MAX, DBL_MAX, 1},
    {10.4, -DBL_MAX, DBL_MAX, 2},
    {-DBL_MAX / 2, -DBL_MAX, DBL_MAX, 2},
    {DBL_MAX / 2, -DBL_MAX, DBL_MAX, 2},
    {5.35, 0.024, 10.06, 5},
    {DBL_MIN, -2 * DBL_MIN, 2 * DBL_MIN, 4},
    {-DBL_MIN, -2 * DBL_MIN, 2 * DBL_MIN, 4},
    {DBL_MIN, -3 * DBL_MIN, 3 * DBL_MIN, 4},
    {-DBL_MIN, -3 * DBL_MIN, 3 * DBL_MIN, 4},
    {DBL_MIN, -3 * DBL_MIN, 3 * DBL_MIN, 6},
    {-DBL_MIN, -3 * DBL_MIN, 3 * DBL_MIN, 6},
};

void print_result(double oper, double low, double high, int count) {
  printf(
      "% 2.5e, low: % 2.5e, high: % 2.5e, count: % 2d --> orig: % 2d, new: % "
      "2d\n",
      oper,
      low,
      high,
      count,
      width_bucket_orig(oper, low, high, count),
      width_bucket_new(oper, low, high, count));
}

int main(int argc, char *argv[]) {
  printf("DBL_MAX: %e, DBL_MIN: %e\n", DBL_MAX, DBL_MIN);
  if (argc == 1) {
    int i;
    for (i = 0; i < sizeof(values) / sizeof(*values); ++i)
      print_result(
          values[i].operand, values[i].low, values[i].high, values[i].count);
  } else if (argc == 5) {
    print_result(strtod(argv[1], NULL),
                 strtod(argv[2], NULL),
                 strtod(argv[3], NULL),
                 strtol(argv[4], NULL, 0));
  } else {
    fprintf(stderr, "Usage: <oper> <low> <high> <count>\n");
    exit(2);
  }
}
