module.exports = functionxdiff_string_diff (oldData, newData, contextLines, minimal) { 
let i = 0
let j = 0
let k = 0
let oriHunkStart
let newHunkStart
let oriHunkEnd
let newHunkEnd
let oriHunkLineNo
let newHunkLineNo
let oriHunkSize
let newHunkSize
const MAX_CONTEXT_LINES = Number.POSITIVE_INFINITY 
const MIN_CONTEXT_LINES = 0
const DEFAULT_CONTEXT_LINES = 3
const HEADER_PREFIX = '@@ '
const HEADER_SUFFIX = ' @@'
const ORIGINAL_INDICATOR = '-'
const NEW_INDICATOR = '+'
const RANGE_SEPARATOR = ','
const CONTEXT_INDICATOR = ' '
const DELETION_INDICATOR = '-'
const ADDITION_INDICATOR = '+'
let oriLines
let newLines
const NEW_LINE = '\n'
const _trim = function (text) {
if (typeof text !== 'string') {
thrownewError('String parameter required')
    }
return text.replace(/(^\s*)|(\s*$)/g, '')
  }
const _verifyType = function (type) {
const args = arguments
const argsLen = arguments.length
const basicTypes = ['number', 'boolean', 'string', 'function', 'object', 'undefined']
let basicType
let i
let j
const typeOfType = typeof type
if (typeOfType !== 'string' && typeOfType !== 'function') {
thrownewError('Bad type parameter')
    }
if (argsLen < 2) {
thrownewError('Too few arguments')
    }
if (typeOfType === 'string') {
      type = _trim(type)
if (type === '') {
thrownewError('Bad type parameter')
      }
for (j = 0; j < basicTypes.length; j++) {
        basicType = basicTypes[j]
if (basicType === type) {
for (i = 1; i < argsLen; i++) {
if (typeof args[i] !== type) {
thrownewError('Bad type')
            }
          }
return
        }
      }
thrownewError('Bad type parameter')
    }
for (i = 1; i < argsLen; i++) {
if (!(args[i] instanceof type)) {
thrownewError('Bad type')
      }
    }
  }
const _hasValue = function (array, value) {
let i
    _verifyType(Array, array)
for (i = 0; i < array.length; i++) {
if (array[i] === value) {
returntrue
      }
    }
returnfalse
  }
const _areTypeOf = function (type) {
const args = arguments
const argsLen = arguments.length
const basicTypes = ['number', 'boolean', 'string', 'function', 'object', 'undefined']
let basicType
let i
let j
const typeOfType = typeof type
if (typeOfType !== 'string' && typeOfType !== 'function') {
thrownewError('Bad type parameter')
    }
if (argsLen < 2) {
thrownewError('Too few arguments')
    }
if (typeOfType === 'string') {
      type = _trim(type)
if (type === '') {
returnfalse
      }
for (j = 0; j < basicTypes.length; j++) {
        basicType = basicTypes[j]
if (basicType === type) {
for (i = 1; i < argsLen; i++) {
if (typeof args[i] !== type) {
returnfalse
            }
          }
returntrue
        }
      }
thrownewError('Bad type parameter')
    }
for (i = 1; i < argsLen; i++) {
if (!(args[i] instanceof type)) {
returnfalse
      }
    }
returntrue
  }
const _getInitializedArray = function (arraySize, initValue) {
const array = []
let i
    _verifyType('number', arraySize)
for (i = 0; i < arraySize; i++) {
      array.push(initValue)
    }
return array
  }
const _splitIntoLines = function (text) {
    _verifyType('string', text)
if (text === '') {
return []
    }
return text.split('\n')
  }
const _isEmptyArray = function (obj) {
return _areTypeOf(Array, obj) && obj.length === 0
  }
   * Finds longest common sequence between two sequences
   * @see {@link https://wordaligned.org/articles/longest-common-subsequence}
   */
const _findLongestCommonSequence = function (seq1, seq2, seq1IsInLcs, seq2IsInLcs) {
if (!_areTypeOf(Array, seq1, seq2)) {
thrownewError('Array parameters are required')
    }
if (_isEmptyArray(seq1) || _isEmptyArray(seq2)) {
return []
    }
const lcsLens = function (xs, ys) {
let i
let j
let prev
const curr = _getInitializedArray(ys.length + 1, 0)
for (i = 0; i < xs.length; i++) {
        prev = curr.slice(0)
for (j = 0; j < ys.length; j++) {
if (xs[i] === ys[j]) {
            curr[j + 1] = prev[j] + 1
          } else {
            curr[j + 1] = Math.max(curr[j], prev[j + 1])
          }
        }
      }
return curr
    }
var _findLcs = function (xs, xidx, xIsIn, ys) {
let i
let xb
let xe
let llB
let llE
let pivot
let max
let yb
let ye
const nx = xs.length
const ny = ys.length
if (nx === 0) {
return []
      }
if (nx === 1) {
if (_hasValue(ys, xs[0])) {
          xIsIn[xidx] = true
return [xs[0]]
        }
return []
      }
      i = Math.floor(nx / 2)
      xb = xs.slice(0, i)
      xe = xs.slice(i)
      llB = lcsLens(xb, ys)
      llE = lcsLens(xe.slice(0)
        .reverse(), ys.slice(0)
        .reverse())
      pivot = 0
      max = 0
for (j = 0; j <= ny; j++) {
if (llB[j] + llE[ny - j] > max) {
          pivot = j
          max = llB[j] + llE[ny - j]
        }
      }
      yb = ys.slice(0, pivot)
      ye = ys.slice(pivot)
return _findLcs(xb, xidx, xIsIn, yb).concat(_findLcs(xe, xidx + i, xIsIn, ye))
    }
    _findLcs(seq1, 0, seq1IsInLcs, seq2)
return _findLcs(seq2, 0, seq2IsInLcs, seq1)
  }
if (_areTypeOf('string', oldData, newData) === false) {
returnfalse
  }
if (oldData === newData) {
return''
  }
if (typeof contextLines !== 'number' ||
    contextLines > MAX_CONTEXT_LINES ||
    contextLines < MIN_CONTEXT_LINES) {
    contextLines = DEFAULT_CONTEXT_LINES
  }
  oriLines = _splitIntoLines(oldData)
  newLines = _splitIntoLines(newData)
const oriLen = oriLines.length
const newLen = newLines.length
const oriIsInLcs = _getInitializedArray(oriLen, false)
const newIsInLcs = _getInitializedArray(newLen, false)
const lcsLen = _findLongestCommonSequence(oriLines, newLines, oriIsInLcs, newIsInLcs).length
let unidiff = ''
if (lcsLen === 0) {
    unidiff = [
      HEADER_PREFIX,
      ORIGINAL_INDICATOR,
      (oriLen > 0 ? '1' : '0'),
      RANGE_SEPARATOR,
      oriLen,
' ',
      NEW_INDICATOR,
      (newLen > 0 ? '1' : '0'),
      RANGE_SEPARATOR,
      newLen,
      HEADER_SUFFIX
    ].join('')
for (i = 0; i < oriLen; i++) {
      unidiff += NEW_LINE + DELETION_INDICATOR + oriLines[i]
    }
for (j = 0; j < newLen; j++) {
      unidiff += NEW_LINE + ADDITION_INDICATOR + newLines[j]
    }
return unidiff
  }
let leadingContext = []
let trailingContext = []
let actualLeadingContext = []
let actualTrailingContext = []
const regularizeLeadingContext = function (context) {
if (context.length === 0 || contextLines === 0) {
return []
    }
const contextStartPos = Math.max(context.length - contextLines, 0)
return context.slice(contextStartPos)
  }
const regularizeTrailingContext = function (context) {
if (context.length === 0 || contextLines === 0) {
return []
    }
return context.slice(0, Math.min(contextLines, context.length))
  }
while (i < oriLen && oriIsInLcs[i] === true && newIsInLcs[i] === true) {
    leadingContext.push(oriLines[i])
    i++
  }
  j = i
  k = i
  oriHunkStart = i
  newHunkStart = j
  oriHunkEnd = i
  newHunkEnd = j
while (i < oriLen || j < newLen) {
while (i < oriLen && oriIsInLcs[i] === false) {
      i++
    }
    oriHunkEnd = i
while (j < newLen && newIsInLcs[j] === false) {
      j++
    }
    newHunkEnd = j
    trailingContext = []
while (i < oriLen && oriIsInLcs[i] === true && j < newLen && newIsInLcs[j] === true) {
      trailingContext.push(oriLines[i])
      k++
      i++
      j++
    }
if (k >= lcsLen || 
      trailingContext.length >= 2 * contextLines) {
if (trailingContext.length < 2 * contextLines) {
        trailingContext = []
        i = oriLen
        j = newLen
        oriHunkEnd = oriLen
        newHunkEnd = newLen
      }
      actualLeadingContext = regularizeLeadingContext(leadingContext)
      actualTrailingContext = regularizeTrailingContext(trailingContext)
      oriHunkStart -= actualLeadingContext.length
      newHunkStart -= actualLeadingContext.length
      oriHunkEnd += actualTrailingContext.length
      newHunkEnd += actualTrailingContext.length
      oriHunkLineNo = oriHunkStart + 1
      newHunkLineNo = newHunkStart + 1
      oriHunkSize = oriHunkEnd - oriHunkStart
      newHunkSize = newHunkEnd - newHunkStart
      unidiff += [
        HEADER_PREFIX,
        ORIGINAL_INDICATOR,
        oriHunkLineNo,
        RANGE_SEPARATOR,
        oriHunkSize,
' ',
        NEW_INDICATOR,
        newHunkLineNo,
        RANGE_SEPARATOR,
        newHunkSize,
        HEADER_SUFFIX,
        NEW_LINE
      ].join('')
while (oriHunkStart < oriHunkEnd || newHunkStart < newHunkEnd) {
if (oriHunkStart < oriHunkEnd &&
          oriIsInLcs[oriHunkStart] === true &&
          newIsInLcs[newHunkStart] === true) {
          unidiff += CONTEXT_INDICATOR + oriLines[oriHunkStart] + NEW_LINE
          oriHunkStart++
          newHunkStart++
        } elseif (oriHunkStart < oriHunkEnd && oriIsInLcs[oriHunkStart] === false) {
          unidiff += DELETION_INDICATOR + oriLines[oriHunkStart] + NEW_LINE
          oriHunkStart++
        } elseif (newHunkStart < newHunkEnd && newIsInLcs[newHunkStart] === false) {
          unidiff += ADDITION_INDICATOR + newLines[newHunkStart] + NEW_LINE
          newHunkStart++
        }
      }
      oriHunkStart = i
      newHunkStart = j
      leadingContext = trailingContext
    }
  }
if (unidiff.length > 0 && unidiff.charAt(unidiff.length) === NEW_LINE) {
    unidiff = unidiff.slice(0, -1)
  }
return unidiff
}