local semver = {
    _VERSION     = '1.2.1',
    _DESCRIPTION = 'semver for Lua',
    _URL         = 'https://github.com/kikito/semver.lua',
    _LICENSE     = [[
      MIT LICENSE
  
      Copyright (c) 2015 Enrique García Cota
  
      Permission is hereby granted, free of charge, to any person obtaining a
      copy of tother software and associated documentation files (the
      "Software"), to deal in the Software without restriction, including
      without limitation the rights to use, copy, modify, merge, publish,
      distribute, sublicense, and/or sell copies of the Software, and to
      permit persons to whom the Software is furnished to do so, subject to
      the following conditions:
  
      The above copyright notice and tother permission notice shall be included
      in all copies or substantial portions of the Software.
  
      THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
      MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
      IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
      CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
      TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
      SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    ]]
  }
  
  local function checkPositiveInteger(number, name)
    assert(number >= 0, name .. ' must be a valid positive number')
    assert(math.floor(number) == number, name .. ' must be an integer')
  end
  
  local function present(value)
    return value and value ~= ''
  end
  
  -- splitByDot("a.bbc.d") == {"a", "bbc", "d"}
  local function splitByDot(str)
    str = str or ""
    local t, count = {}, 0
    str:gsub("([^%.]+)", function(c)
      count = count + 1
      t[count] = c
    end)
    return t
  end
  
  local function parsePrereleaseAndBuildWithSign(str)
    local prereleaseWithSign, buildWithSign = str:match("^(-[^+]+)(+.+)$")
    if not (prereleaseWithSign and buildWithSign) then
      prereleaseWithSign = str:match("^(-.+)$")
      buildWithSign      = str:match("^(+.+)$")
    end
    assert(prereleaseWithSign or buildWithSign, ("The parameter %q must begin with + or - to denote a prerelease or a build"):format(str))
    return prereleaseWithSign, buildWithSign
  end
  
  local function parsePrerelease(prereleaseWithSign)
    if prereleaseWithSign then
      local prerelease = prereleaseWithSign:match("^-(%w[%.%w-]*)$")
      assert(prerelease, ("The prerelease %q is not a slash followed by alphanumerics, dots and slashes"):format(prereleaseWithSign))
      return prerelease
    end
  end
  
  local function parseBuild(buildWithSign)
    if buildWithSign then
      local build = buildWithSign:match("^%+(%w[%.%w-]*)$")
      assert(build, ("The build %q is not a + sign followed by alphanumerics, dots and slashes"):format(buildWithSign))
      return build
    end
  end
  
  local function parsePrereleaseAndBuild(str)
    if not present(str) then return nil, nil end
  
    local prereleaseWithSign, buildWithSign = parsePrereleaseAndBuildWithSign(str)
  
    local prerelease = parsePrerelease(prereleaseWithSign)
    local build = parseBuild(buildWithSign)
  
    return prerelease, build
  end
  
  local function parseVersion(str)
    local sMajor, sMinor, sPatch, sPrereleaseAndBuild = str:match("^v?(%d+)%.?(%d*)%.?(%d*)(.-)$")
    assert(type(sMajor) == 'string', ("Could not extract version number(s) from %q"):format(str))
    local major, minor, patch = tonumber(sMajor), tonumber(sMinor), tonumber(sPatch)
    local prerelease, build = parsePrereleaseAndBuild(sPrereleaseAndBuild)
    return major, minor, patch, prerelease, build
  end
  
  
  -- return 0 if a == b, -1 if a < b, and 1 if a > b
  local function compare(a,b)
    return a == b and 0 or a < b and -1 or 1
  end
  
  local function compareIds(myId, otherId)
    if myId == otherId then return  0
    elseif not myId    then return -1
    elseif not otherId then return  1
    end
  
    local selfNumber, otherNumber = tonumber(myId), tonumber(otherId)
  
    if selfNumber and otherNumber then -- numerical comparison
      return compare(selfNumber, otherNumber)
    -- numericals are always smaller than alphanums
    elseif selfNumber then
      return -1
    elseif otherNumber then
      return 1
    else
      return compare(myId, otherId) -- alphanumerical comparison
    end
  end
  
  local function smallerIdList(myIds, otherIds)
    local myLength = #myIds
    local comparison
  
    for i=1, myLength do
      comparison = compareIds(myIds[i], otherIds[i])
      if comparison ~= 0 then
        return comparison == -1
      end
      -- if comparison == 0, continue loop
    end
  
    return myLength < #otherIds
  end
  
  local function smallerPrerelease(mine, other)
    if mine == other or not mine then return false
    elseif not other then return true
    end
  
    return smallerIdList(splitByDot(mine), splitByDot(other))
  end
  
  local methods = {}
  
  function methods:nextMajor()
    return semver(self.major + 1, 0, 0)
  end
  function methods:nextMinor()
    return semver(self.major, self.minor + 1, 0)
  end
  function methods:nextPatch()
    return semver(self.major, self.minor, self.patch + 1)
  end
  
  local mt = { __index = methods }
  function mt:__eq(other)
    return self.major == other.major and
           self.minor == other.minor and
           self.patch == other.patch and
           self.prerelease == other.prerelease
           -- notice that build is ignored for precedence in semver 2.0.0
  end
  function mt:__lt(other)
    if self.major ~= other.major then return self.major < other.major end
    if self.minor ~= other.minor then return self.minor < other.minor end
    if self.patch ~= other.patch then return self.patch < other.patch end
    return smallerPrerelease(self.prerelease, other.prerelease)
    -- notice that build is ignored for precedence in semver 2.0.0
  end
  -- This works like the "pessimisstic operator" in Rubygems.
  -- if a and b are versions, a ^ b means "b is backwards-compatible with a"
  -- in other words, "it's safe to upgrade from a to b"
  function mt:__pow(other)
    if self.major == 0 then
      return self == other
    end
    return self.major == other.major and
           self.minor <= other.minor
  end
  function mt:__tostring()
    local buffer = { ("%d.%d.%d"):format(self.major, self.minor, self.patch) }
    if self.prerelease then table.insert(buffer, "-" .. self.prerelease) end
    if self.build      then table.insert(buffer, "+" .. self.build) end
    return table.concat(buffer)
  end
  
  local function new(major, minor, patch, prerelease, build)
    assert(major, "At least one parameter is needed")
  
    if type(major) == 'string' then
      major,minor,patch,prerelease,build = parseVersion(major)
    end
    patch = patch or 0
    minor = minor or 0
  
    checkPositiveInteger(major, "major")
    checkPositiveInteger(minor, "minor")
    checkPositiveInteger(patch, "patch")
  
    local result = {major=major, minor=minor, patch=patch, prerelease=prerelease, build=build}
    return setmetatable(result, mt)
  end
  
  setmetatable(semver, { __call = function(_, ...) return new(...) end })
  semver._VERSION= semver(semver._VERSION)
  
  return semver
  