function rgb2hsv
 local max = math.max
 local min = math.min
 local c = max - min
 
 -- calculate hue
 local h
 if c == 0 then
 h = 0
 elseif max == r then
 h = 60 * (0 + ((g - b)/c))
 elseif max == g then
 h = 60 * (2 + ((b - r)/c))
 elseif max == b then
 h = 60 * (4 + ((r - g)/c))
 end
 if h < 0 then h = h + 360 end
 
 -- calculate value
 local v = max
 
 -- calculate luminance
 -- local l = (max + min) / 2
 
 -- calculate saturation
 local s
 if c == 0 then
 s = 0
 else
 s = c/v
 end
 
 return h, s, v
end

function hsv2rgb
 local hi = math.floor(h / 60)
 local f = ((h / 60) - hi)
 
 local p = v * (1 - s)
 local q = v * (1 - s * f)
 local t = v * (1 - s * (1 - f))
 
 if hi == 1 then
 return q, v, p
 elseif hi == 2 then
 return p, v, t
 elseif hi == 3 then
 return p, q, v
 elseif hi == 4 then
 return t, p, v
 elseif hi == 5 then
 return v, p, q
 else
 return v, t, p
 end
end

function decodeRGBHex(rgb)
 local r = string.sub
 local g = string.sub
 local b = string.sub
 return tonumber, tonumber, tonumber
end

function getRGB(args)
 local r, g, b, i
 if string.len(args[1]) == 6 then
 r, g, b = decodeRGBHex(args[1])
 i = 2
 else
 r = args[1]
 g = args[2]
 b = args[3]
 i = 4
 end
 return r / 255, g / 255, b / 255, i
end

--[[toHSV
Description: Converts a RGB color to a HSV color.

Usage: or 

Output: hue [0°..360°]/saturation [0..1]/value [0..1]
]]
function p.toHSV(frame)
 local r, g, b = getRGB(frame.args)
 local h, s, v = rgb2hsv
 return h .. "/" .. s .. "/" .. v
end

--[[hue
Description: Returns the hue of a RGB color.

Usage: or 

Output: hue [0°..360°]
]]
function p.hue(frame)
 local r, g, b = getRGB(frame.args)
 local h, s, v = rgb2hsv
 return h
end

--[[saturation
Description: Returns the saturation of a RGB color.

Usage: or 

Output: saturation [0..1]
]]
function p.saturation(frame)
 local r, g, b = getRGB(frame.args)
 local h, s, v = rgb2hsv
 return s
end

--[[value
Description: Returns the hue of a RGB color.

Usage: or 

Output: value [0..1]
]]
function p.value(frame)
 local r, g, b = getRGB(frame.args)
 local h, s, v = rgb2hsv
 return v
end

--saturate
Description: Saturates a RGB color by a given factor.

Usage: or 
Optionally "factor" can be followed by another parameter with the value "hex" can be given to return the RGB color in a hexadecimal representation.

Output: saturated RGB

function p.saturate(frame)
 local r, g, b, i = getRGB(frame.args)
 -- convert to HSV
 local h, s, v = rgb2hsv
 -- saturate
 s = s * frame.args[i]
 if s < 0 then
 s = 0
 elseif s > 1 then
 s = 1
 end
 -- convert back to RGB
 local r, g, b = hsv2rgb
 r = math.floor(r * 255 + 0.5)
 g = math.floor(g * 255 + 0.5)
 b = math.floor(b * 255 + 0.5)
 -- return values
 if frame.args[i + 1] == "hex" then
 return string.format
 else
 return r .. "/" .. g .. "/" .. b
 end
end

--[[toRGB
Description: Converts a HSV color to a RGB color.

Usage: with H in [0°..360°], S in [0..1] and V in [0..1].
Optionally a 4th parameter with the value "hex" can be given to return the RGB color in a hexadecimal representation.

Output: R/G/B
]]
function p.toRGB(frame)
 local r, g, b = hsv2rgb
 -- transform from real [0..1] to integer [0..255]
 r = math.floor(r * 255 + 0.5)
 g = math.floor(g * 255 + 0.5)
 b = math.floor(b * 255 + 0.5)
 -- return values
 if frame.args[4] == "hex" then
 return string.format
 else
 return r .. "/" .. g .. "/" .. b
 end
end