/**
 * Default minimum size of a run.
 */
const DEFAULT_MIN_MERGE = 32

/**
 * Minimum ordered subsequece required to do galloping.
 */
const DEFAULT_MIN_GALLOPING = 7

/**
 * Default tmp storage length. Can increase depending on the size of the
 * smallest run to merge.
 */
const DEFAULT_TMP_STORAGE_LENGTH = 256

/**
 * Pre-computed powers of 10 for efficient lexicographic comparison of
 * small integers.
 */
const POWERS_OF_TEN = [1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9]

let results

/**
 * Estimate the logarithm base 10 of a small integer.
 *
 * @param {number} x - The integer to estimate the logarithm of.
 * @return {number} - The estimated logarithm of the integer.
 */
const log10 = x => x < 1e5
  ? x < 1e2
    ? x < 1e1
      ? 0
      : 1
    : x < 1e4
      ? x < 1e3
        ? 2
        : 3
      : 4
  : x < 1e7
    ? x < 1e6
      ? 5
      : 6
    : x < 1e9
      ? x < 1e8
        ? 7
        : 8
      : 9

/**
 * Default alphabetical comparison of items.
 *
 * @param {string|object|number} a - First element to compare.
 * @param {string|object|number} b - Second element to compare.
 * @return {number} - A positive number if a.toString() > b.toString(), a
 * negative number if .toString() < b.toString(), 0 otherwise.
 */
function alphabeticalCompare (a, b) {
  if (a === b) {
    return 0
  }

  if (~ ~ a === a && ~ ~ b === b) {
    if (a === 0 || b === 0) {
      return a < b ? - 1 : 1
    }

    if (a < 0 || b < 0) {
      if (b >= 0) {
        return - 1
      }

      if (a >= 0) {
        return 1
      }

      a = - a
      b = - b
    }

    const al = log10(a)
    const bl = log10(b)

    let t = 0

    if (al < bl) {
      a *= POWERS_OF_TEN[bl - al - 1]
      b /= 10
      t = - 1
    } else if (al > bl) {
      b *= POWERS_OF_TEN[al - bl - 1]
      a /= 10
      t = 1
    }

    if (a === b) {
      return t
    }

    return a < b ? - 1 : 1
  }

  const aStr = String(a)
  const bStr = String(b)

  if (aStr === bStr) {
    return 0
  }

  return aStr < bStr ? - 1 : 1
}

/**
 * Compute minimum run length for TimSort
 *
 * @param {number} n - The size of the array to sort.
 */
function minRunLength (n) {
  let r = 0

  while (n >= DEFAULT_MIN_MERGE) {
    r |= (n & 1)
    n >>= 1
  }

  return n + r
}

/**
 * Counts the length of a monotonically ascending or strictly monotonically
 * descending sequence (run) starting at array[lo] in the range [lo, hi). If
 * the run is descending it is made ascending.
 *
 * @param {array} array - The array to reverse.
 * @param {number} lo - First element in the range (inclusive).
 * @param {number} hi - Last element in the range.
 * @param {function} compare - Item comparison function.
 * @return {number} - The length of the run.
 */
function makeAscendingRun (array, lo, hi, compare) {
  let runHi = lo + 1

  if (runHi === hi) {
    return 1
  }

  // Descending
  if (compare(array[runHi ++], array[lo]) < 0) {
    while (runHi < hi && compare(array[runHi], array[runHi - 1]) < 0) {
      runHi ++
    }

    reverseRun(array, lo, runHi)
    reverseRun(results, lo, runHi)
    // Ascending
  } else {
    while (runHi < hi && compare(array[runHi], array[runHi - 1]) >= 0) {
      runHi ++
    }
  }

  return runHi - lo
}

/**
 * Reverse an array in the range [lo, hi).
 *
 * @param {array} array - The array to reverse.
 * @param {number} lo - First element in the range (inclusive).
 * @param {number} hi - Last element in the range.
 */
function reverseRun (array, lo, hi) {
  hi --

  while (lo < hi) {
    const t = array[lo]
    array[lo ++] = array[hi]
    array[hi --] = t
  }
}

/**
 * Perform the binary sort of the array in the range [lo, hi) where start is
 * the first element possibly out of order.
 *
 * @param {array} array - The array to sort.
 * @param {number} lo - First element in the range (inclusive).
 * @param {number} hi - Last element in the range.
 * @param {number} start - First element possibly out of order.
 * @param {function} compare - Item comparison function.
 */
function binaryInsertionSort (array, lo, hi, start, compare) {
  if (start === lo) {
    start ++
  }

  for (; start < hi; start ++) {
    const pivot = array[start]
    const pivotIndex = results[start]

    // Ranges of the array where pivot belongs
    let left = lo
    let right = start

    /*
     *   pivot >= array[i] for i in [lo, left)
     *   pivot <  array[i] for i in  in [right, start)
     */
    while (left < right) {
      const mid = (left + right) >>> 1

      if (compare(pivot, array[mid]) < 0) {
        right = mid
      } else {
        left = mid + 1
      }
    }

    /*
     * Move elements right to make room for the pivot. If there are elements
     * equal to pivot, left points to the first slot after them: this is also
     * a reason for which TimSort is stable
     */
    let n = start - left
    // Switch is just an optimization for small arrays
    switch (n) {
    case 3:
      array[left + 3] = array[left + 2]
      results[left + 3] = results[left + 2]
      /* falls through */
    case 2:
      array[left + 2] = array[left + 1]
      results[left + 2] = results[left + 1]
      /* falls through */
    case 1:
      array[left + 1] = array[left]
      results[left + 1] = results[left]
      break
    default:
      while (n > 0) {
        array[left + n] = array[left + n - 1]
        results[left + n] = results[left + n - 1]
        n --
      }
    }

    array[left] = pivot
    results[left] = pivotIndex
  }
}

/**
 * Find the position at which to insert a value in a sorted range. If the range
 * contains elements equal to the value the leftmost element index is returned
 * (for stability).
 *
 * @param {number} value - Value to insert.
 * @param {array} array - The array in which to insert value.
 * @param {number} start - First element in the range.
 * @param {number} length - Length of the range.
 * @param {number} hint - The index at which to begin the search.
 * @param {function} compare - Item comparison function.
 * @return {number} - The index where to insert value.
 */
function gallopLeft (value, array, start, length, hint, compare) {
  let lastOffset = 0
  let maxOffset = 0
  let offset = 1

  if (compare(value, array[start + hint]) > 0) {
    maxOffset = length - hint

    while (
      offset < maxOffset
      && compare(value, array[start + hint + offset]) > 0
    ) {
      lastOffset = offset
      offset = (offset << 1) + 1

      if (offset <= 0) {
        offset = maxOffset
      }
    }

    if (offset > maxOffset) {
      offset = maxOffset
    }

    // Make offsets relative to start
    lastOffset += hint
    offset += hint

    // value <= array[start + hint]
  } else {
    maxOffset = hint + 1
    while (
      offset < maxOffset
      && compare(value, array[start + hint - offset]) <= 0
    ) {
      lastOffset = offset
      offset = (offset << 1) + 1

      if (offset <= 0) {
        offset = maxOffset
      }
    }
    if (offset > maxOffset) {
      offset = maxOffset
    }

    // Make offsets relative to start
    const tmp = lastOffset
    lastOffset = hint - offset
    offset = hint - tmp
  }

  /*
   * Now array[start+lastOffset] < value <= array[start+offset], so value
   * belongs somewhere in the range (start + lastOffset, start + offset]. Do a
   * binary search, with invariant array[start + lastOffset - 1] < value <=
   * array[start + offset].
   */
  lastOffset ++
  while (lastOffset < offset) {
    const m = lastOffset + ((offset - lastOffset) >>> 1)

    if (compare(value, array[start + m]) > 0) {
      lastOffset = m + 1
    } else {
      offset = m
    }
  }
  return offset
}

/**
 * Find the position at which to insert a value in a sorted range. If the range
 * contains elements equal to the value the rightmost element index is returned
 * (for stability).
 *
 * @param {number} value - Value to insert.
 * @param {array} array - The array in which to insert value.
 * @param {number} start - First element in the range.
 * @param {number} length - Length of the range.
 * @param {number} hint - The index at which to begin the search.
 * @param {function} compare - Item comparison function.
 * @return {number} - The index where to insert value.
 */
function gallopRight (value, array, start, length, hint, compare) {
  let lastOffset = 0
  let maxOffset = 0
  let offset = 1

  if (compare(value, array[start + hint]) < 0) {
    maxOffset = hint + 1

    while (
      offset < maxOffset
      && compare(value, array[start + hint - offset]) < 0
    ) {
      lastOffset = offset
      offset = (offset << 1) + 1

      if (offset <= 0) {
        offset = maxOffset
      }
    }

    if (offset > maxOffset) {
      offset = maxOffset
    }

    // Make offsets relative to start
    const tmp = lastOffset
    lastOffset = hint - offset
    offset = hint - tmp

    // value >= array[start + hint]
  } else {
    maxOffset = length - hint

    while (
      offset < maxOffset
      && compare(value, array[start + hint + offset]) >= 0
    ) {
      lastOffset = offset
      offset = (offset << 1) + 1

      if (offset <= 0) {
        offset = maxOffset
      }
    }

    if (offset > maxOffset) {
      offset = maxOffset
    }

    // Make offsets relative to start
    lastOffset += hint
    offset += hint
  }

  /*
   * Now array[start+lastOffset] < value <= array[start+offset], so value
   * belongs somewhere in the range (start + lastOffset, start + offset]. Do a
   * binary search, with invariant array[start + lastOffset - 1] < value <=
   * array[start + offset].
   */
  lastOffset ++

  while (lastOffset < offset) {
    const m = lastOffset + ((offset - lastOffset) >>> 1)

    if (compare(value, array[start + m]) < 0) {
      offset = m
    } else {
      lastOffset = m + 1
    }
  }

  return offset
}

class TimSort {
  constructor (array, compare) {
    this.array = array
    this.compare = compare

    const {length} = array

    this.length = length
    this.minGallop = DEFAULT_MIN_GALLOPING

    this.tmpStorageLength = length < 2 * DEFAULT_TMP_STORAGE_LENGTH
      ? length >>> 1
      : DEFAULT_TMP_STORAGE_LENGTH

    this.tmp = new Array(this.tmpStorageLength)
    this.tmpIndex = new Array(this.tmpStorageLength)

    this.stackLength = length < 120
      ? 5
      : length < 1542
        ? 10
        : length < 119151
          ? 19
          : 40

    this.runStart = new Array(this.stackLength)
    this.runLength = new Array(this.stackLength)

    this.stackSize = 0
  }

  /**
   * Push a new run on TimSort's stack.
   *
   * @param {number} runStart - Start index of the run in the original array.
   * @param {number} runLength - Length of the run;
   */
  pushRun (runStart, runLength) {
    this.runStart[this.stackSize] = runStart
    this.runLength[this.stackSize] = runLength
    this.stackSize += 1
  }

  /**
   * Merge runs on TimSort's stack so that the following holds for all i:
   * 1) runLength[i - 3] > runLength[i - 2] + runLength[i - 1]
   * 2) runLength[i - 2] > runLength[i - 1]
   */
  mergeRuns () {
    while (this.stackSize > 1) {
      let n = this.stackSize - 2

      if (
        (
          n >= 1
          && this.runLength[n - 1] <= this.runLength[n] + this.runLength[n + 1]
        )
        || (
          n >= 2
          && this.runLength[n - 2] <= this.runLength[n] + this.runLength[n - 1]
        )
      ) {
        if (this.runLength[n - 1] < this.runLength[n + 1]) {
          n --
        }
      } else if (this.runLength[n] > this.runLength[n + 1]) {
        break
      }
      this.mergeAt(n)
    }
  }

  /**
   * Merge all runs on TimSort's stack until only one remains.
   */
  forceMergeRuns () {
    while (this.stackSize > 1) {
      let n = this.stackSize - 2

      if (n > 0 && this.runLength[n - 1] < this.runLength[n + 1]) {
        n --
      }

      this.mergeAt(n)
    }
  }

  /**
   * Merge the runs on the stack at positions i and i+1. Must be always be called
   * with i=stackSize-2 or i=stackSize-3 (that is, we merge on top of the stack).
   *
   * @param {number} i - Index of the run to merge in TimSort's stack.
   */
  mergeAt (i) {
    const {compare} = this
    const {array} = this

    let start1 = this.runStart[i]
    let length1 = this.runLength[i]
    const start2 = this.runStart[i + 1]
    let length2 = this.runLength[i + 1]

    this.runLength[i] = length1 + length2

    if (i === this.stackSize - 3) {
      this.runStart[i + 1] = this.runStart[i + 2]
      this.runLength[i + 1] = this.runLength[i + 2]
    }

    this.stackSize --

    /*
     * Find where the first element in the second run goes in run1. Previous
     * elements in run1 are already in place
     */
    const k = gallopRight(array[start2], array, start1, length1, 0, compare)
    start1 += k
    length1 -= k

    if (length1 === 0) {
      return
    }

    /*
     * Find where the last element in the first run goes in run2. Next elements
     * in run2 are already in place
     */
    length2 = gallopLeft(
      array[start1 + length1 - 1],
      array,
      start2,
      length2,
      length2 - 1,
      compare
    )

    if (length2 === 0) {
      return
    }

    /*
     * Merge remaining runs. A tmp array with length = min(length1, length2) is
     * used
     */
    if (length1 <= length2) {
      this.mergeLow(start1, length1, start2, length2)
    } else {
      this.mergeHigh(start1, length1, start2, length2)
    }
  }

  /**
   * Merge two adjacent runs in a stable way. The runs must be such that the
   * first element of run1 is bigger than the first element in run2 and the
   * last element of run1 is greater than all the elements in run2.
   * The method should be called when run1.length <= run2.length as it uses
   * TimSort temporary array to store run1. Use mergeHigh if run1.length >
   * run2.length.
   *
   * @param {number} start1 - First element in run1.
   * @param {number} length1 - Length of run1.
   * @param {number} start2 - First element in run2.
   * @param {number} length2 - Length of run2.
   */
  mergeLow (start1, length1, start2, length2) {
    const {compare} = this
    const {array} = this
    const {tmp} = this
    const {tmpIndex} = this
    let i = 0

    for (i = 0; i < length1; i ++) {
      tmp[i] = array[start1 + i]
      tmpIndex[i] = results[start1 + i]
    }

    let cursor1 = 0
    let cursor2 = start2
    let dest = start1

    array[dest] = array[cursor2]
    results[dest] = results[cursor2]

    dest ++
    cursor2 ++

    if (-- length2 === 0) {
      for (i = 0; i < length1; i ++) {
        array[dest + i] = tmp[cursor1 + i]
        results[dest + i] = tmpIndex[cursor1 + i]
      }
      return
    }

    if (length1 === 1) {
      for (i = 0; i < length2; i ++) {
        array[dest + i] = array[cursor2 + i]
        results[dest + i] = results[cursor2 + i]
      }
      array[dest + length2] = tmp[cursor1]
      results[dest + length2] = tmpIndex[cursor1]
      return
    }

    let {minGallop} = this

    while (true) {
      let count1 = 0
      let count2 = 0
      let exit = false

      do {
        if (compare(array[cursor2], tmp[cursor1]) < 0) {
          array[dest] = array[cursor2]
          results[dest] = results[cursor2]
          dest ++
          cursor2 ++
          count2 ++
          count1 = 0

          if (-- length2 === 0) {
            exit = true
            break
          }
        } else {
          array[dest] = tmp[cursor1]
          results[dest] = tmpIndex[cursor1]
          dest ++
          cursor1 ++
          count1 ++
          count2 = 0
          if (-- length1 === 1) {
            exit = true
            break
          }
        }
      } while ((count1 | count2) < minGallop)

      if (exit) {
        break
      }

      do {
        count1 = gallopRight(array[cursor2], tmp, cursor1, length1, 0, compare)

        if (count1 !== 0) {
          for (i = 0; i < count1; i ++) {
            array[dest + i] = tmp[cursor1 + i]
            results[dest + i] = tmpIndex[cursor1 + i]
          }

          dest += count1
          cursor1 += count1
          length1 -= count1
          if (length1 <= 1) {
            exit = true
            break
          }
        }

        array[dest] = array[cursor2]
        results[dest] = results[cursor2]

        dest ++
        cursor2 ++

        if (-- length2 === 0) {
          exit = true
          break
        }

        count2 = gallopLeft(tmp[cursor1], array, cursor2, length2, 0, compare)

        if (count2 !== 0) {
          for (i = 0; i < count2; i ++) {
            array[dest + i] = array[cursor2 + i]
            results[dest + i] = results[cursor2 + i]
          }

          dest += count2
          cursor2 += count2
          length2 -= count2

          if (length2 === 0) {
            exit = true
            break
          }
        }
        array[dest] = tmp[cursor1]
        results[dest] = tmpIndex[cursor1]
        dest ++
        cursor1 ++

        if (-- length1 === 1) {
          exit = true
          break
        }

        minGallop --
      } while (
        count1 >= DEFAULT_MIN_GALLOPING
        || count2 >= DEFAULT_MIN_GALLOPING
      )

      if (exit) {
        break
      }

      if (minGallop < 0) {
        minGallop = 0
      }

      minGallop += 2
    }

    this.minGallop = minGallop

    if (minGallop < 1) {
      this.minGallop = 1
    }

    if (length1 === 1) {
      for (i = 0; i < length2; i ++) {
        array[dest + i] = array[cursor2 + i]
        results[dest + i] = results[cursor2 + i]
      }
      array[dest + length2] = tmp[cursor1]
      results[dest + length2] = tmpIndex[cursor1]
    } else if (length1 === 0) {
      throw new Error('mergeLow preconditions were not respected')
    } else {
      for (i = 0; i < length1; i ++) {
        array[dest + i] = tmp[cursor1 + i]
        results[dest + i] = tmpIndex[cursor1 + i]
      }
    }
  }

  /**
   * Merge two adjacent runs in a stable way. The runs must be such that the
   * first element of run1 is bigger than the first element in run2 and the
   * last element of run1 is greater than all the elements in run2.
   * The method should be called when run1.length > run2.length as it uses
   * TimSort temporary array to store run2. Use mergeLow if run1.length <=
   * run2.length.
   *
   * @param {number} start1 - First element in run1.
   * @param {number} length1 - Length of run1.
   * @param {number} start2 - First element in run2.
   * @param {number} length2 - Length of run2.
   */
  mergeHigh (start1, length1, start2, length2) {
    const {compare} = this
    const {array} = this
    const {tmp} = this
    const {tmpIndex} = this
    let i = 0

    for (i = 0; i < length2; i ++) {
      tmp[i] = array[start2 + i]
      tmpIndex[i] = results[start2 + i]
    }

    let cursor1 = start1 + length1 - 1
    let cursor2 = length2 - 1
    let dest = start2 + length2 - 1
    let customCursor = 0
    let customDest = 0

    array[dest] = array[cursor1]
    results[dest] = results[cursor1]

    dest --
    cursor1 --

    if (-- length1 === 0) {
      customCursor = dest - (length2 - 1)

      for (i = 0; i < length2; i ++) {
        array[customCursor + i] = tmp[i]
        results[customCursor + i] = tmpIndex[i]
      }

      return
    }

    if (length2 === 1) {
      dest -= length1
      cursor1 -= length1
      customDest = dest + 1
      customCursor = cursor1 + 1

      for (i = length1 - 1; i >= 0; i --) {
        array[customDest + i] = array[customCursor + i]
        results[customDest + i] = results[customCursor + i]
      }

      array[dest] = tmp[cursor2]
      results[dest] = tmpIndex[cursor2]
      return
    }

    let {minGallop} = this

    while (true) {
      let count1 = 0
      let count2 = 0
      let exit = false

      do {
        if (compare(tmp[cursor2], array[cursor1]) < 0) {
          array[dest] = array[cursor1]
          results[dest] = results[cursor1]
          dest --
          cursor1 --
          count1 ++
          count2 = 0
          if (-- length1 === 0) {
            exit = true
            break
          }
        } else {
          array[dest] = tmp[cursor2]
          results[dest] = tmpIndex[cursor2]
          dest --
          cursor2 --
          count2 ++
          count1 = 0
          if (-- length2 === 1) {
            exit = true
            break
          }
        }
      } while ((count1 | count2) < minGallop)

      if (exit) {
        break
      }

      do {
        count1 = length1 - gallopRight(
          tmp[cursor2],
          array,
          start1,
          length1,
          length1 - 1,
          compare
        )

        if (count1 !== 0) {
          dest -= count1
          cursor1 -= count1
          length1 -= count1
          customDest = dest + 1
          customCursor = cursor1 + 1

          for (i = count1 - 1; i >= 0; i --) {
            array[customDest + i] = array[customCursor + i]
            results[customDest + i] = results[customCursor + i]
          }

          if (length1 === 0) {
            exit = true
            break
          }
        }

        array[dest] = tmp[cursor2]
        results[dest] = tmpIndex[cursor2]
        dest --
        cursor2 --

        if (-- length2 === 1) {
          exit = true
          break
        }

        count2 = length2 - gallopLeft(
          array[cursor1],
          tmp,
          0,
          length2,
          length2 - 1,
          compare
        )

        if (count2 !== 0) {
          dest -= count2
          cursor2 -= count2
          length2 -= count2
          customDest = dest + 1
          customCursor = cursor2 + 1

          for (i = 0; i < count2; i ++) {
            array[customDest + i] = tmp[customCursor + i]
            results[customDest + i] = tmpIndex[customCursor + i]
          }

          if (length2 <= 1) {
            exit = true
            break
          }
        }

        array[dest] = array[cursor1]
        results[dest] = results[cursor1]
        dest --
        cursor1 --

        if (-- length1 === 0) {
          exit = true
          break
        }

        minGallop --
      } while (
        count1 >= DEFAULT_MIN_GALLOPING
        || count2 >= DEFAULT_MIN_GALLOPING
      )

      if (exit) {
        break
      }

      if (minGallop < 0) {
        minGallop = 0
      }

      minGallop += 2
    }

    this.minGallop = minGallop

    if (minGallop < 1) {
      this.minGallop = 1
    }

    if (length2 === 1) {
      dest -= length1
      cursor1 -= length1
      customDest = dest + 1
      customCursor = cursor1 + 1

      for (i = length1 - 1; i >= 0; i --) {
        array[customDest + i] = array[customCursor + i]
        results[customDest + i] = results[customCursor + i]
      }

      array[dest] = tmp[cursor2]
      results[dest] = tmpIndex[cursor2]
    } else if (length2 === 0) {
      throw new Error('mergeHigh preconditions were not respected')
    } else {
      customCursor = dest - (length2 - 1)
      for (i = 0; i < length2; i ++) {
        array[customCursor + i] = tmp[i]
        results[customCursor + i] = tmpIndex[i]
      }
    }
  }
}

/**
 * Sort an array in the range [lo, hi) using TimSort.
 *
 * @param {array} array - The array to sort.
 * @param {function=} compare - Item comparison function. Default is
 *     alphabetical
 * @param {number} lo - First element in the range (inclusive).
 * @param {number} hi - Last element in the range.
 *     comparator.
 */
function sort (array, compare, lo, hi) {
  if (!Array.isArray(array)) {
    throw new TypeError(
      `The "array" argument must be an array. Received ${array}`
    )
  }

  results = []

  const {length} = array

  let i = 0

  while (i < length) {
    results[i] = i ++
  }

  /*
   * Handle the case where a comparison function is not provided. We do
   * lexicographic sorting
   */
  if (!compare) {
    compare = alphabeticalCompare
  } else if (typeof compare !== 'function') {
    hi = lo
    lo = compare
    compare = alphabeticalCompare
  }

  if (!lo) {
    lo = 0
  }
  if (!hi) {
    hi = length
  }

  let remaining = hi - lo

  // The array is already sorted
  if (remaining < 2) {
    return results
  }

  let runLength = 0
  // On small arrays binary sort can be used directly
  if (remaining < DEFAULT_MIN_MERGE) {
    runLength = makeAscendingRun(array, lo, hi, compare)
    binaryInsertionSort(array, lo, hi, lo + runLength, compare)
    return results
  }

  const ts = new TimSort(array, compare)

  const minRun = minRunLength(remaining)

  do {
    runLength = makeAscendingRun(array, lo, hi, compare)
    if (runLength < minRun) {
      let force = remaining
      if (force > minRun) {
        force = minRun
      }

      binaryInsertionSort(array, lo, lo + force, lo + runLength, compare)
      runLength = force
    }
    // Push new run and merge if necessary
    ts.pushRun(lo, runLength)
    ts.mergeRuns()

    // Go find next run
    remaining -= runLength
    lo += runLength
  } while (remaining !== 0)

  // Force merging of remaining runs
  ts.forceMergeRuns()

  return results
}

module.exports = {
  sort
}
