/// DiffFilesWithTolerance - Compare the two files specified, returning 0 if the
/// files match, 1 if they are different, and 2 if there is a file error.  This
/// function differs from DiffFiles in that you can specify an absolete and
/// relative FP error that is allowed to exist.  If you specify a string to fill
/// in for the error option, it will set the string to an error message if an
/// error occurs, allowing the caller to distinguish between a failed diff and a
/// file system error.
///
int llvm::DiffFilesWithTolerance(const sys::PathWithStatus &FileA,
                                 const sys::PathWithStatus &FileB,
                                 double AbsTol, double RelTol,
                                 std::string *Error) {
  const sys::FileStatus *FileAStat = FileA.getFileStatus(false, Error);
  if (!FileAStat)
    return 2;
  const sys::FileStatus *FileBStat = FileB.getFileStatus(false, Error);
  if (!FileBStat)
    return 2;

  // Check for zero length files because some systems croak when you try to
  // mmap an empty file.
  size_t A_size = FileAStat->getSize();
  size_t B_size = FileBStat->getSize();

  // If they are both zero sized then they're the same
  if (A_size == 0 && B_size == 0)
    return 0;

  // If only one of them is zero sized then they can't be the same
  if ((A_size == 0 || B_size == 0)) {
    if (Error)
      *Error = "Files differ: one is zero-sized, the other isn't";
    return 1;
  }

  // Now its safe to mmap the files into memory because both files
  // have a non-zero size.
  OwningPtr<MemoryBuffer> F1;
  if (error_code ec = MemoryBuffer::getFile(FileA.c_str(), F1)) {
    if (Error)
      *Error = ec.message();
    return 2;
  }
  OwningPtr<MemoryBuffer> F2;
  if (error_code ec = MemoryBuffer::getFile(FileB.c_str(), F2)) {
    if (Error)
      *Error = ec.message();
    return 2;
  }

  // Okay, now that we opened the files, scan them for the first difference.
  const char *File1Start = F1->getBufferStart();
  const char *File2Start = F2->getBufferStart();
  const char *File1End = F1->getBufferEnd();
  const char *File2End = F2->getBufferEnd();
  const char *F1P = File1Start;
  const char *F2P = File2Start;

  // Are the buffers identical?  Common case: Handle this efficiently.
  if (A_size == B_size &&
      std::memcmp(File1Start, File2Start, A_size) == 0)
    return 0;

  // Otherwise, we are done a tolerances are set.
  if (AbsTol == 0 && RelTol == 0) {
    if (Error)
      *Error = "Files differ without tolerance allowance";
    return 1;   // Files different!
  }

  bool CompareFailed = false;
  while (1) {
    // Scan for the end of file or next difference.
    while (F1P < File1End && F2P < File2End && *F1P == *F2P)
      ++F1P, ++F2P;

    if (F1P >= File1End || F2P >= File2End) break;

    // Okay, we must have found a difference.  Backup to the start of the
    // current number each stream is at so that we can compare from the
    // beginning.
    F1P = BackupNumber(F1P, File1Start);
    F2P = BackupNumber(F2P, File2Start);

    // Now that we are at the start of the numbers, compare them, exiting if
    // they don't match.
    if (CompareNumbers(F1P, F2P, File1End, File2End, AbsTol, RelTol, Error)) {
      CompareFailed = true;
      break;
    }
  }

  // Okay, we reached the end of file.  If both files are at the end, we
  // succeeded.
  bool F1AtEnd = F1P >= File1End;
  bool F2AtEnd = F2P >= File2End;
  if (!CompareFailed && (!F1AtEnd || !F2AtEnd)) {
    // Else, we might have run off the end due to a number: backup and retry.
    if (F1AtEnd && isNumberChar(F1P[-1])) --F1P;
    if (F2AtEnd && isNumberChar(F2P[-1])) --F2P;
    F1P = BackupNumber(F1P, File1Start);
    F2P = BackupNumber(F2P, File2Start);

    // Now that we are at the start of the numbers, compare them, exiting if
    // they don't match.
    if (CompareNumbers(F1P, F2P, File1End, File2End, AbsTol, RelTol, Error))
      CompareFailed = true;

    // If we found the end, we succeeded.
    if (F1P < File1End || F2P < File2End)
      CompareFailed = true;
  }

  return CompareFailed;
}
/// DiffFilesWithTolerance - Compare the two files specified, returning 0 if the
/// files match, 1 if they are different, and 2 if there is a file error.  This
/// function differs from DiffFiles in that you can specify an absolete and
/// relative FP error that is allowed to exist.  If you specify a string to fill
/// in for the error option, it will set the string to an error message if an
/// error occurs, allowing the caller to distinguish between a failed diff and a
/// file system error.
///
int llvm::DiffFilesWithTolerance(const sys::PathWithStatus &FileA,
                                 const sys::PathWithStatus &FileB,
                                 double AbsTol, double RelTol,
                                 std::string *Error) {
    const sys::FileStatus *FileAStat = FileA.getFileStatus(false, Error);
    if (!FileAStat)
        return 2;
    const sys::FileStatus *FileBStat = FileB.getFileStatus(false, Error);
    if (!FileBStat)
        return 2;

    // Check for zero length files because some systems croak when you try to
    // mmap an empty file.
    size_t A_size = FileAStat->getSize();
    size_t B_size = FileBStat->getSize();

    // If they are both zero sized then they're the same
    if (A_size == 0 && B_size == 0)
        return 0;

    // If only one of them is zero sized then they can't be the same
    if ((A_size == 0 || B_size == 0)) {
        if (Error)
            *Error = "Files differ: one is zero-sized, the other isn't";
        return 1;
    }

    // Now its safe to mmap the files into memory becasue both files
    // have a non-zero size.
    sys::MappedFile F1;
    if (F1.open(FileA, sys::MappedFile::READ_ACCESS, Error))
        return 2;
    sys::MappedFile F2;
    if (F2.open(FileB, sys::MappedFile::READ_ACCESS, Error))
        return 2;
    if (!F1.map(Error))
        return 2;
    if (!F2.map(Error))
        return 2;

    // Okay, now that we opened the files, scan them for the first difference.
    char *File1Start = F1.charBase();
    char *File2Start = F2.charBase();
    char *File1End = File1Start+A_size;
    char *File2End = File2Start+B_size;
    char *F1P = File1Start;
    char *F2P = File2Start;

    if (A_size == B_size) {
        // Are the buffers identical?
        if (std::memcmp(File1Start, File2Start, A_size) == 0)
            return 0;

        if (AbsTol == 0 && RelTol == 0) {
            if (Error)
                *Error = "Files differ without tolerance allowance";
            return 1;   // Files different!
        }
    }

    char *OrigFile1Start = File1Start;
    char *OrigFile2Start = File2Start;

    // If the files need padding, do so now.
    PadFileIfNeeded(File1Start, File1End, F1P);
    PadFileIfNeeded(File2Start, File2End, F2P);

    bool CompareFailed = false;
    while (1) {
        // Scan for the end of file or next difference.
        while (F1P < File1End && F2P < File2End && *F1P == *F2P)
            ++F1P, ++F2P;

        if (F1P >= File1End || F2P >= File2End) break;

        // Okay, we must have found a difference.  Backup to the start of the
        // current number each stream is at so that we can compare from the
        // beginning.
        F1P = BackupNumber(F1P, File1Start);
        F2P = BackupNumber(F2P, File2Start);

        // Now that we are at the start of the numbers, compare them, exiting if
        // they don't match.
        if (CompareNumbers(F1P, F2P, File1End, File2End, AbsTol, RelTol, Error)) {
            CompareFailed = true;
            break;
        }
    }

    // Okay, we reached the end of file.  If both files are at the end, we
    // succeeded.
    bool F1AtEnd = F1P >= File1End;
    bool F2AtEnd = F2P >= File2End;
    if (!CompareFailed && (!F1AtEnd || !F2AtEnd)) {
        // Else, we might have run off the end due to a number: backup and retry.
        if (F1AtEnd && isNumberChar(F1P[-1])) --F1P;
        if (F2AtEnd && isNumberChar(F2P[-1])) --F2P;
        F1P = BackupNumber(F1P, File1Start);
        F2P = BackupNumber(F2P, File2Start);

        // Now that we are at the start of the numbers, compare them, exiting if
        // they don't match.
        if (CompareNumbers(F1P, F2P, File1End, File2End, AbsTol, RelTol, Error))
            CompareFailed = true;

        // If we found the end, we succeeded.
        if (F1P < File1End || F2P < File2End)
            CompareFailed = true;
    }

    if (OrigFile1Start != File1Start)
        delete[] (File1Start-1);   // Back up past null byte
    if (OrigFile2Start != File2Start)
        delete[] (File2Start-1);   // Back up past null byte
    return CompareFailed;
}