text: const SemVer = require('../classes/semver')
  const Comparator = require('../classes/comparator')
  const {ANY} = Comparator
  const Range = require('../classes/range')
  const satisfies = require('../functions/satisfies')
  const gt = require('../functions/gt')
  const lt = require('../functions/lt')
  const lte = require('../functions/lte')
  const gte = require('../functions/gte')
  
  const outside = (version, range, hilo, options) => {
    version = new SemVer(version, options)
    range = new Range(range, options)
  
    let gtfn, ltefn, ltfn, comp, ecomp
    switch (hilo) {
      case '>':
        gtfn = gt
        ltefn = lte
        ltfn = lt
        comp = '>'
        ecomp = '>='
        break
      case '<':
        gtfn = lt
        ltefn = gte
        ltfn = gt
        comp = '<'
        ecomp = '<='
        break
      default:
        throw new TypeError('Must provide a hilo val of "<" or ">"')
    }
  
    // If it satisfies the range it is not outside
    if (satisfies(version, range, options)) {
      return false
    }
  
    // From now on, variable terms are as if we're in "gtr" mode.
    // but note that everything is flipped for the "ltr" function.
  
    for (let i = 0; i < range.set.length; ++i) {
      const comparators = range.set[i]
  
      let high = null
      let low = null
  
      comparators.forEach((comparator) => {
        if (comparator.semver === ANY) {
          comparator = new Comparator('>=0.0.0')
        }
        high = high || comparator
        low = low || comparator
        if (gtfn(comparator.semver, high.semver, options)) {
          high = comparator
        } else if (ltfn(comparator.semver, low.semver, options)) {
          low = comparator
        }
      })
  
      // If the edge version comparator has a operator then our version
      // isn't outside it
      if (high.operator === comp || high.operator === ecomp) {
        return false
      }
  
      // If the lowest version comparator has an operator and our version
      // is less than it then it isn't higher than the range
      if ((!low.operator || low.operator === comp) &&
          ltefn(version, low.semver)) {
        return false
      } else if (low.operator === ecomp && ltfn(version, low.semver)) {
        return false
      }
    }
    return true
  }
  
  module.exports = outside
  