// Copyright 2021 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/bigint/vector-arithmetic.h"

#include "src/bigint/bigint-internal.h"
#include "src/bigint/digit-arithmetic.h"

namespace v8 {
namespace bigint {

digit_t AddAndReturnOverflow(RWDigits Z, Digits X) {
  X.Normalize();
  if (X.len() == 0) return 0;
  // Here and below: callers are careful to pass sufficiently large result
  // storage Z. If that ever goes wrong, then something is corrupted; could
  // be a concurrent-mutation attack. So we harden against that with Release-
  // mode CHECKs.
  CHECK(Z.len() >= X.len());
  digit_t carry = 0;
  uint32_t i = 0;
  for (; i < X.len(); i++) {
    Z[i] = digit_add3(Z[i], X[i], carry, &carry);
  }
  for (; i < Z.len() && carry != 0; i++) {
    Z[i] = digit_add2(Z[i], carry, &carry);
  }
  return carry;
}

digit_t SubAndReturnBorrow(RWDigits Z, Digits X) {
  X.Normalize();
  if (X.len() == 0) return 0;
  CHECK(Z.len() >= X.len());
  digit_t borrow = 0;
  uint32_t i = 0;
  for (; i < X.len(); i++) {
    Z[i] = digit_sub2(Z[i], X[i], borrow, &borrow);
  }
  for (; i < Z.len() && borrow != 0; i++) {
    Z[i] = digit_sub(Z[i], borrow, &borrow);
  }
  return borrow;
}

void Add(RWDigits Z, Digits X, Digits Y) {
  if (X.len() < Y.len()) std::swap(X, Y);  // Now X.len() >= Y.len().
  CHECK(Z.len() >= X.len());
  uint32_t i = 0;
  digit_t carry = 0;
  for (; i < Y.len(); i++) {
    Z[i] = digit_add3(X[i], Y[i], carry, &carry);
  }
  for (; i < X.len(); i++) {
    Z[i] = digit_add2(X[i], carry, &carry);
  }
  for (; i < Z.len(); i++) {
    Z[i] = carry;
    carry = 0;
  }
}

void Subtract(RWDigits Z, Digits X, Digits Y) {
  X.Normalize();
  Y.Normalize();
  CHECK(Z.len() >= X.len() && X.len() >= Y.len());
  uint32_t i = 0;
  digit_t borrow = 0;
  for (; i < Y.len(); i++) {
    Z[i] = digit_sub2(X[i], Y[i], borrow, &borrow);
  }
  for (; i < X.len(); i++) {
    Z[i] = digit_sub(X[i], borrow, &borrow);
  }
  DCHECK(borrow == 0);
  for (; i < Z.len(); i++) Z[i] = 0;
}

digit_t AddAndReturnCarry(RWDigits Z, Digits X, Digits Y) {
  CHECK(Z.len() >= Y.len() && X.len() >= Y.len());
  digit_t carry = 0;
  for (uint32_t i = 0; i < Y.len(); i++) {
    Z[i] = digit_add3(X[i], Y[i], carry, &carry);
  }
  return carry;
}

digit_t SubtractAndReturnBorrow(RWDigits Z, Digits X, Digits Y) {
  CHECK(Z.len() >= Y.len() && X.len() >= Y.len());
  digit_t borrow = 0;
  for (uint32_t i = 0; i < Y.len(); i++) {
    Z[i] = digit_sub2(X[i], Y[i], borrow, &borrow);
  }
  return borrow;
}

bool AddSigned(RWDigits Z, Digits X, bool x_negative, Digits Y,
               bool y_negative) {
  if (x_negative == y_negative) {
    Add(Z, X, Y);
    return x_negative;
  }
  if (GreaterThanOrEqual(X, Y)) {
    Subtract(Z, X, Y);
    return x_negative;
  }
  Subtract(Z, Y, X);
  return !x_negative;
}

bool SubtractSigned(RWDigits Z, Digits X, bool x_negative, Digits Y,
                    bool y_negative) {
  if (x_negative != y_negative) {
    Add(Z, X, Y);
    return x_negative;
  }
  if (GreaterThanOrEqual(X, Y)) {
    Subtract(Z, X, Y);
    return x_negative;
  }
  Subtract(Z, Y, X);
  return !x_negative;
}

void AddOne(RWDigits Z, Digits X) {
  digit_t carry = 1;
  uint32_t i = 0;
  for (; carry > 0 && i < X.len(); i++) Z[i] = digit_add2(X[i], carry, &carry);
  if (carry > 0) Z[i++] = carry;
  for (; i < X.len(); i++) Z[i] = X[i];
  for (; i < Z.len(); i++) Z[i] = 0;
}

void SubtractOne(RWDigits Z, Digits X) {
  digit_t borrow = 1;
  uint32_t i = 0;
  for (; borrow > 0; i++) Z[i] = digit_sub(X[i], borrow, &borrow);
  for (; i < X.len(); i++) Z[i] = X[i];
  for (; i < Z.len(); i++) Z[i] = 0;
}

}  // namespace bigint
}  // namespace v8
