
int my_max(int a, int b) {
  if (a > b)
    return a;
  else
    return b;
}

// biginteger routines

int make_biginteger() {
  int start;
  start = bigfloat_pool_idx;
  bigfloat_pool_idx = bigfloat_pool_idx + MAX_DIGITS + 1;
  if (bigfloat_pool_idx >= BIGFLOAT_POOL_SIZE) {
    printf("out of bigfloat memory");
    writeln();
    return 0; // TODO may not be that graceful
  }
  return start;
}

void biginteger_copy(int src, int dest) {
//for (i = 0; i < MAX_DIGITS; i++)
//  Xset_digit(dest, i, Xget_digit(src, i));
  memcpy32(bigfloat_pool, dest, bigfloat_pool, src, MAX_DIGITS);
  set_size(dest, get_size(src));
  return;
}

void initialize_biginteger(int n) {
//for (i = 0; i < MAX_DIGITS; i++)
//  Xset_digit(n, i, 0);
  memset32(bigfloat_pool, n, 0, MAX_DIGITS);
  set_size(n, 0);
  return;
}

void int_to_biginteger(int s, int n) {
  int digit;

  // s assumed to be positive

  initialize_biginteger(n);

  set_size(n, -1);

  while (s > 0) {
    set_size(n, get_size(n) + 1);
    digit = s % DIGIT_BASE;
    set_digit(n, get_size(n), digit);
    s = s / DIGIT_BASE;
  }

  return;
}

int biginteger_to_int(int n, int n_digits) {
  int size;
  int power;
  int sum;
  int i;
  size = get_size(n) + 1;
  power = 1;
  sum = 0;
  for (i = size - n_digits; i < size; i++) {
    sum = sum + power * get_digit(n, i);
    power = power * DIGIT_BASE;
  }
  return sum;
}

void zero_justify(int n) {
  while ((get_size(n) > 0) && (get_digit(n, get_size(n)) == 0))
    set_size(n, get_size(n) - 1);
  return;
}

void add_biginteger(int a, int b, int c) {
  int size;
  int carry;
  int size_a;
  int size_b;
  int digit_sum;
  int i;

  initialize_biginteger(c);

  size_a = get_size(a);
  size_b = get_size(b);
  size = my_max(size_a, size_b) + 1;
  set_size(c, size);

  carry = 0;

  for (i = 0; i <= size; i++) {
    digit_sum = carry + get_digit(a, i) + get_digit(b, i);
    if (digit_sum >= DIGIT_BASE) {
      digit_sum = digit_sum - DIGIT_BASE;
      carry = 1;
    }
    else
      carry = 0;
    set_digit(c, i, digit_sum);
  }

  zero_justify(c);

  return;
}

void subtract_biginteger(int a, int b, int c) {
  int borrow;
  int value;
  int i;
  int size_a;
  int size_b;
  int size_c;
  int a_digit;

  // only works if a >= b

  initialize_biginteger(c);

  size_a = get_size(a);
  size_b = get_size(b);
  size_c = my_max(size_a, size_b);
  set_size(c, size_c);

  borrow = 0;

  for (i = 0; i <= size_c; i++) {
    a_digit = get_digit(a, i);
    value = a_digit - borrow - get_digit(b, i);
    if (a_digit > 0)
      borrow = 0;
    if (value < 0) {
      value = value + DIGIT_BASE;
      borrow = 1;
    }

    if (value >= DIGIT_BASE)
      value = value - DIGIT_BASE;
    set_digit(c, i, value);
  }

  zero_justify(c);

  return;
}

int compare_biginteger(int a, int b) {
  int size_a;
  int size_b;

  size_a = get_size(a);
  size_b = get_size(b);

  if (size_b > size_a)
    return 1;
  else
  if (size_a > size_b)
    return -1;
  else
  if (size_a == -1) // size is 0
    return 0;
  else
    return memcmp32(bigfloat_pool, a, b, size_a);
}

void shift_right(int n, int d) { // multiply n by DIGIT_BASE^d
  int i;
  int size_n;

  size_n = get_size(n);

  if ((size_n == 0) && (get_digit(n, 0) == 0))
    return;
  else {

    for (i = size_n; i >= 0; i--)
      set_digit(n, i + d, get_digit(n, i));

    if (d == 1)
      set_digit_zero(n, 0);
    else
//for (i = 0; i < d; i++)
//  Xset_digit(n, i, 0);
      memset32(bigfloat_pool, n, 0, d);

    set_size(n, size_n + d);

    return;
  }
}

void multiply_one_digit(int a, int b, int n) {
  int i;
  int c;
  int size;
  int digit;

  size = get_size(a);
  c = 0;
  for (i = 0; i <= size; i++) {
    set_digit(b, i, n * get_digit(a, i));
    set_digit(b, i, c + get_digit(b, i));
    digit = get_digit(b, i);
    if (digit >= DIGIT_BASE) { // overflow
      c = digit / DIGIT_BASE;
      set_digit(b, i, digit % DIGIT_BASE);
    }
    else
      c = 0;
  }

  if (c == 0)
    set_size(b, size);
  else {
    set_digit(b, size + 1, c);
    set_size(b, size + 1);
  }
  return;
}

void divide_biginteger(int a, int b, int c) {
  int i;
  int size_a;
  int digit;

  initialize_biginteger(c);
  int_to_biginteger(0, row);
  initialize_biginteger(tmp);

  size_a = get_size(a);

  set_size(c, size_a);

  for (i = size_a; i >= 0; i--) {
    shift_right(row, 1);
    set_digit(row, 0, get_digit(a, i));
    digit = 0;
    while (compare_biginteger(row, b) <= 0) { // negative or 0
      digit = digit + 1;
      subtract_biginteger(row, b, tmp);
      biginteger_copy(tmp, row);
    }
    set_digit(c, i, digit);
  }

  zero_justify(c);

  return;
}

int is_biginteger_zero(int n) { // TODO should be able to just test size = -1
  int i;
  i = 0;
  while (1) {
    if (i < MAX_DIGITS) {
      if (get_digit(n, i) != 0)
        return 0; // non zero
    }
    else
      return 1; // biginteger zero
    i = i + 1;
  }
}

int digits_in_integer(int n) {
  int count;
  if (n == 0)
    return 0;
  else {
    n = abs(n);
    count = 0;
    while (n > 0) {
      n = n / DIGIT_BASE;
      count = count + 1;
    }
    return count;
  }
}
