Module:Weather and Module:Weather/sandbox: Difference between pages

(Difference between pages)
Jump to navigation Jump to search
Page 1
Page 2
imported>Erutuon
m merge multi-line comments
 
imported>WOSlinker
use require('strict') instead of require('Module:No globals')
 
Line 1: Line 1:
--[[
local p = {}
Efficient (fast) functions to implement cells in tables of weather data.
 
Temperature conversion is built-in, but for simplicity, temperatures
require('strict')
are assumed to be for habitable locations (from -100 to 100 °C).
 
]]
local degree = "°" -- used by addUnitNames()
local minus = "−" -- used by makeRow() and makeTable()
local thinSpace = mw.ustring.char(0x2009) -- used by makeCell()
 
local precision, decimals
 
-- if not empty
local function ine(var)
var = tostring(var)
if var == "" then
return nil
else
return var
end
end
 
-- Error message handling
local message = ""
 
local function addMessage(newMessage)
if ine(message) then
message = message .. " " .. newMessage
else
message = "Notices: " .. newMessage
end
end


local MINUS = '' -- Unicode U+2212 MINUS SIGN
local function monospace(str)
return '<span style="background-color: #EEE; font-family: monospace;">' .. str .. '</span>'
end


local function temperature_style(palette, value, out_rgb)
-- Input and output parameters
-- Return style for a table cell based on the given value which
local function getFormat(inputParameter, outputParameter, palette, messages)
-- should be a temperature in °C.
local length, inputUnit, outputUnit, palette, show, cellFormat
local function style(bg, fg)
local min, max = unpack(palette.white or { -23, 35 })
if inputParameter == nil then
if not fg and value and (value < min or value >= max) then
error('Please provide the number of values and a unit in the input parameter')
fg = 'FFFFFF'
else
-- Find as many as two digits in the input parameter.
length = tonumber(string.match(inputParameter, "(%d%d?)"))  
if not length then
length = 13
addMessage('getFormat has not found a length value in the input parameter; length defaults to "13"')
end
-- Find C or F, but not both
if string.find(inputParameter, "C") and string.find(inputParameter, "F") then
error("Input unit must be either C (Celsius) or F (Fahrenheit)")
else
inputUnit = string.match(inputParameter, "([CF])") or error("Please provide an input unit in the input parameter: F for Fahrenheit or C for Celsius", 0)
end
end
if fg then
fg = 'color:#' .. fg .. ';'
if inputUnit == "C" then
outputUnit = "F"
else
else
fg = ''
outputUnit = "C"
end
-- Make sure nothing except C, F, numbers, or spaces is in the input parameter.
if string.find(inputParameter, "[^CF%d%s]") then
addMessage("There are extraneous characters in the " .. monospace("output") .. " parameter.")
end
end
return 'style="background:#' .. bg .. ';' .. fg .. ' font-size:100%;"'
end
end
if type(value) ~= 'number' then
return style('FFFFFF', '000000')
if outputParameter == nil then
end
-- Since there are default values, the module will still generate output with an empty output parameter.
local rgb = out_rgb or {}
addMessage("No output format has been provided in the " .. monospace("output") .. " parameter, so default values will be used.")
for i, v in ipairs(palette) do
else
local a, b, c, d = unpack(v)
cellFormat = {}
if value <= a then
for i, unit in require("Module:StringTools").imatch(outputParameter, "[CF]") do
rgb[i] = 0
cellFormat[i] = unit
elseif value < b then
if i > 2 then
rgb[i] = (value - a) * 255 / (b - a)
break
elseif value <= c then
end
rgb[i] = 255
end
elseif value < d then
local function setFormat(key, variable, value)
rgb[i] = 255 - ( (value - c) * 255 / (d - c) )
if string.find(outputParameter, key) then
cellFormat[variable] = value
else
cellFormat[variable] = not value
end
end
if cellFormat[1] then
cellFormat.first = cellFormat[1]
else
error('C or F not found in output parameter')
end
if cellFormat[2] == nil then
cellFormat["convertUnits"] = false
else
if cellFormat[2] == cellFormat[1] then
error('There should not be two of the same unit name in the output parameter.')
else
cellFormat["convertUnits"] = true
end
end
setFormat("unit", "unitNames", true)
setFormat("no ?color", "color", false)
setFormat("sort", "sortable", true)
setFormat("full ?size", "smallFont", false)
setFormat("no ?brackets", "brackets", false)
setFormat("round", "decimals", "0", "")
if string.find(outputParameter, "line break") then
cellFormat["lineBreak"] = true
elseif string.find(outputParameter, "one line") then
cellFormat["lineBreak"] = false
else
else
rgb[i] = 0
cellFormat["lineBreak"] = "auto"
end
if string.find(outputParameter, "one line") and
string.find(outputParameter, "line break") then
error('Place either "one line" or "line break" in the output parameter, not both')
end
end
end
end
return style(string.format('%02X%02X%02X', rgb[1], rgb[2], rgb[3]))
palette = palette or "cool2avg"
show = messages == "show"
return {
length = length, inputUnit = inputUnit, outputUnit = outputUnit,
cellFormat = cellFormat, show = show, palette = palette
}
end
end


local function format_cell(palette, value, intext, outtext)
-- Math functions
-- Return one line of wikitext to make a cell in a table.
 
if not value then
local function round(value, decimals)
return '|\n'
value = tonumber(value)
end
if type(value) == "number" then
local text
return string.format("%." .. decimals .. "f", value)
if outtext then
text = intext .. '<br>(' .. outtext .. ')'
else
else
text = intext
error("Format was asked to operate on " .. tostring(value) .. ", which cannot be converted to a number.", 2)
return ""
end
end
return '| ' .. temperature_style(palette, value) .. ' | ' .. text .. '\n'
end
end


local function process_temperature(intext, inunit, swap)
local function convert(value, unit, decimals) -- Unit is the unit being converted from.
--[[ Convert °C to °F or vice versa, assuming the temperature is for a
if not unit then
habitable location, well inside the range -100 to 100 °C.
error("No unit supplied to convert.", 2)
That simplifies determining precision and formatting (no commas are needed).
Return (celsius_value, intext, outtext) if valid; otherwise return nil.
The returned input and output are swapped if requested.
Each returned string has a Unicode MINUS as sign, if negative. ]]
local invalue = tonumber(intext)
if not invalue then return nil end
local integer, dot, decimals = intext:match('^%s*%-?(%d+)(%.?)(%d*)%s*$')
if not integer then return nil end
if invalue < 0 then
intext = MINUS .. integer .. dot .. decimals
end
end
local outtext
if tonumber(value) then
if inunit == 'C' or inunit == 'F' then
local value = tonumber(value)
local celsius_value, outvalue
if unit == "C" then
if inunit == 'C' then
return round(value * 9/5 + 32, decimals)
outvalue = invalue * (9/5) + 32
elseif unit == "F" then
celsius_value = invalue
return round((value - 32) * 5/9, decimals)
else
else
outvalue = (invalue - 32) * (5/9)
error("Input unit not recognized", 2)
celsius_value = outvalue
end
end
local precision = dot == '' and 0 or #decimals
else
outtext = string.format('%.' .. precision .. 'f', math.abs(outvalue) + 2e-14)
-- to avoid concatenation errors
if outvalue < 0 and tonumber(outtext) ~= 0 then
return ""
-- Don't show minus if result is negative but rounds to zero.
outtext = MINUS .. outtext
end
if swap then
return celsius_value, outtext, intext
end
return celsius_value, intext, outtext
end
end
-- LATER Think about whether a no-conversion option would be useful.
return invalue, intext, outtext
end
end


local function temperature_row(palette, row, inunit, swap)
-- Stick numbers into array. Find out if any have decimals.
--[[
-- Throw an error if any are invalid.
Return 13 lines specifying the style/content of 13 table cells.
local function _makeArray(format)
Input is 13 space-separated words, each a number (°C or °F).
return function(parameter)
Any word that is not a number gives a blank cell ("M" for a missing cell).
if not parameter then
Any excess words are ignored.
return nil
end
Function  Input  Output
local array = {}
------------------------
-- If there are multiple parameters for numbers, and the first doesn't have
CtoF        C      C/F
-- decimals, the rest will have their decimals rounded off.
FfromC      C      F/C
format.precision = format.precision or parameter:find("%d%.%d") and "1" or "0"
CfromF      F      C/F
FtoC        F      F/C ]]
local numbers = mw.text.split(parameter, "%s+")
local nrcol = 13
if #numbers ~= format.length then
local results, n = {}, 0
addMessage('There are not ' .. format.length .. ' values in the ' .. parameter .. ' parameter.')
for word in row:gmatch('%S+') do
end
n = n + 1
if n > nrcol then
for i, number in ipairs(numbers) do
break
if not number:find("^%-?%d%d?%d?.?(%d?)$") then
error('The number "' .. number .. '" does not fit the expected pattern.')
end
table.insert(array, number)
end
end
results[n] = format_cell(palette, process_temperature(word, inunit, swap))
end
return array
for i = n + 1, nrcol do
results[i] = format_cell()
end
end
return table.concat(results)
end
end


local palettes = {
-- Color generation
-- A background color entry in a palette is a table of four numbers,
 
-- say { 11, 22, 33, 44 } (values in °C).
p.palettes = {
-- That means the color is 0 below 11 and above 44, and is 255 from 22 to 33.
--[[
-- The color rises from 0 to 255 between 11 and 22, and falls between 33 and 44.
The first three arrays in each palette defines background color using a
table of four numbers, say { 11, 22, 33, 44 } (values in °C).
That means that, on the scale from 0 (black) to 255 (saturated), the color
is 0 below 11°C and above 44°C, and is 255 from 22°C to 33°C.
The color rises from 0 to 255 between 11°C and 22°C, and falls from 255 to 0
between 33°C and 44°C.
]]
cool = {
cool = {
{ -42.75,  4.47, 41.5, 60  },
{ -42.75,  4.47, 41.5, 60  }, -- red
{ -42.75,  4.47,  4.5, 41.5 },
{ -42.75,  4.47,  4.5, 41.5 }, -- green
{ -90  , -42.78,  4.5, 23  },
{ -90  , -42.78,  4.5, 23  }, -- blue
white = { -23.3, 37.8 },
white = { -23.3, 37.8 }, -- background
},
},
cool2 = {
cool2 = {
Line 149: Line 219:
}
}


local function temperatures(frame, inunit, swap)
--[[ Return style for a table cell based on the given value which
local palette = palettes[frame.args.palette] or palettes.cool
should be a temperature in °C. ]]
return temperature_row(palette, frame.args[1], inunit, swap)
local function temperatureColor(palette, value, outRGB)
local backgroundColor, textColor
value = tonumber(value)
if not value then
backgroundColor, textColor = 'FFF', '000'
addMessage("Value supplied to " .. monospace("temperatureColor") .. " is not recognized.")
else
local min, max = unpack(palette.white or { -23, 35 })
if value < min or value >= max then
textColor = 'FFF'
-- Else nil.
-- This assumes that black text color is the default for most readers.
end
 
local backgroundRGB = outRGB or {}
for i, v in ipairs(palette) do
local a, b, c, d = unpack(v)
if value <= a then
backgroundRGB[i] = 0
elseif value < b then
backgroundRGB[i] = (value - a) * 255 / (b - a)
elseif value <= c then
backgroundRGB[i] = 255
elseif value < d then
backgroundRGB[i] = 255 - ( (value - c) * 255 / (d - c) )
else
backgroundRGB[i] = 0
end
end
backgroundColor = string.format('%02X%02X%02X', unpack(backgroundRGB))
end
return backgroundColor, textColor
end
 
local function colorCSS(backgroundColor, textColor)
if backgroundColor and textColor then
return 'background: #' .. backgroundColor .. '; color: #' .. textColor .. ';'
elseif backgroundColor then
return 'background: #' .. backgroundColor .. ';'
else
return ''
end
end
end


local function CtoF(frame)
local function temperatureColorCSS(palette, value, outRGB)
return temperatures(frame, 'C')
return colorCSS(temperatureColor(palette, value, outRGB))
end
end


local function CfromF(frame)
local function temperatureCSS(value, unit, palette)
return temperatures(frame, 'F', true)
local palette = p.palettes[palette] or p.palettes.cool
local value = tonumber(value)
if value == nil then
error("The function " .. monospace("temperatureCSS") .. " is receiving a nil value")
else
if unit == 'F' then
value = convert(value, 'F', decimals)
elseif unit ~= 'C' then
unitError(unit or "nil")
end
return colorCSS(temperatureColor(palette, value))
end
end
end


local function FtoC(frame)
local function styleAttribute(palette, value, outRGB)
return temperatures(frame, 'F')
local fontSize = "font-size: 85%;"
local color = temperatureColorCSS(palette, value, outRGB)
return 'style=\"' .. color .. ' ' .. fontSize .. '\"'
end
end


local function FfromC(frame)
local style_attribute = styleAttribute
return temperatures(frame, 'C', true)
 
--[=[
Used by {{Average temperature table/row/C/sandbox}},
{{Average temperature table/row/F/sandbox}},
{{Average temperature table/row/C/sandbox}},
{{Template:Avg temp row F/sandbox2}},
{{Template:Avg temp row C/sandbox2}}.
]=]
function p.temperatureStyle(frame)
local palette = p.palettes[frame.args.palette] or p.palettes.cool
local unit = frame.args.unit or 'C'
local value = tonumber(frame.args[1])
if unit == 'F' then
value = convert(value, 'F', 1)
elseif unit ~= 'C' then
error('Unrecognized unit: ' .. unit)
end
return styleAttribute(palette, value)
end
end
p.temperature_style = p.temperatureStyle
--[[ ==== Cell, row, table generation ==== ]]
local outputFormats = {
high_low_average_F =
{ first = "F",
convertUnits = true,
unitNames = false,
color = true,
smallFont = true,
sortable = true,
decimals = "0",
brackets = true,
lineBreak = "auto", },
high_low_average_C =
{ first = "C",
convertUnits = true,
unitNames = false,
color = true,
smallFont = true,
sortable = true,
decimals = "0",
brackets = true,
lineBreak = "auto", },
high_low_F =
{ first = "F",
convertUnits = true,
unitNames = false,
color = false,
smallFont = true,
sortable = false,
decimals = "",
brackets = true,
lineBreak = "auto", },
high_low_C =
{ first = "C",
convertUnits = true,
unitNames = false,
color = false,
smallFont = true,
sortable = false,
decimals = "0",
brackets = true,
lineBreak = "auto", },
average_F =
{ first = "F",
convertUnits = true,
unitNames = false,
color = true,
smallFont = true,
sortable = false,
decimals = "0",
brackets = true,
lineBreak = "auto", },
average_C =
{ first = "C",
convertUnits = true,
unitNames = false,
color = true,
smallFont = true,
sortable = false,
decimals = "0",
brackets = true,
lineBreak = "auto", },
}
local outputFormat
local function addUnitNames(value, yesOrNo, unit)
if not unit then
error("No unit supplied as argument 3 to addUnitNames", 2)
end
-- Don't add a unit name to an empty string
value = yesOrNo == true and ine(value) and value .. "&nbsp;" .. degree .. unit or value
return value
end
local function ifYes(parameter, realization1, realization2)
local result
if realization1 then
if realization2 then
result = parameter == true and { realization1, realization2 } or { "", "" }
else
result = parameter == true and realization1 or ""
end
else
result = ""
addMessage(monospace("ifYes") .. " needs at least one realization.")
end
return result
end
local function makeCell(outputFormat, a, b, c, format)
local cell, cellContent = "", ""
local colorCSS, otherCSS, titleAttribute, sortkey, attributeSeparator, convertedUnitsSeparator =
"", "", "", "", "", "", ""
-- Distinguish styleAttribute variable from styleAttribute function above.
local styleAttribute, highLowSeparator, brackets, values, convertedUnits =
{"", ""}, {"", ""}, {"", ""}, {"", ""}, {"", ""}
-- Precision is 1 if any number has one or more decimals.
decimals = tonumber(outputFormat.decimals) and outputFormat.decimals or format.precision
if tonumber(b) and tonumber(a) then
values, highLowSeparator = { round(a, decimals), round(b, decimals) },
{ thinSpace .. "/" .. thinSpace, ifYes(outputFormat.convertUnits, thinSpace .. "/" .. thinSpace) }
elseif tonumber(a) then
values = { round(a, decimals), "" }
elseif tonumber(c) then
values = { round(c, decimals), "" }
end
if outputFormat.first == format.inputUnit then
if outputFormat.convertUnits == true then
convertedUnits = { addUnitNames(convert(values[1], format.inputUnit, decimals), outputFormat.unitNames, format.outputUnit), addUnitNames(convert(values[2], format.inputUnit, decimals), outputFormat.unitNames, format.outputUnit) }
end
values = { addUnitNames(values[1], outputFormat.unitNames, format.inputUnit), addUnitNames(values[2], outputFormat.unitNames, format.inputUnit) }
elseif outputFormat.first == "C" or outputFormat.first == "F" then
if outputFormat.convertUnits == true then
convertedUnits = { addUnitNames(values[1], outputFormat.unitNames, format.inputUnit), addUnitNames(values[2], outputFormat.unitNames, format.inputUnit) }
end
values = { addUnitNames(convert(values[1], format.inputUnit, decimals), outputFormat.unitNames, format.outputUnit), addUnitNames(convert(values[2], format.inputUnit, decimals), outputFormat.unitNames, format.outputUnit) }
else
addMessage(monospace(tostring(outputFormat.first)) .. ", the value for " .. monospace("first") .. " in " .. monospace("outputFormat") .. " is not recognized.")
end
--[[
Regarding line breaks:
If there are two values, there will be at least three characters: 9/1.
If there is one decimal, numbers will be three to five characters long
and there will be 3 to 10 characters total even without unit conversion:
1.1, 116.5/88.0.
If there are units, that adds three characters per number: 25 °C/20 °C.
In each of these cases, a line break is needed so that table cells are not too wide;
even more so when more than one of these things are true.
]]
if outputFormat.convertUnits == true then
brackets = outputFormat.brackets == true and { "(", ")" } or { "", "" }
if outputFormat.lineBreak == "auto" then
convertedUnitsSeparator = ( ine(values[2]) or decimals ~= "0" or outputFormat.showUnits == true ) and "<br>" or "&nbsp;"
else
convertedUnitsSeparator = outputFormat.lineBreak == true and "<br>" or outputFormat.lineBreak == false and "&nbsp;" or error('Value for lineBreak not recognized')
end
end
cellContent = values[1] .. highLowSeparator[1] .. values[2] .. convertedUnitsSeparator .. brackets[1] .. convertedUnits[1] .. highLowSeparator[2] .. convertedUnits[2] .. brackets[2]
if tonumber(c) then
colorCSS = outputFormat.color == true and temperatureCSS(c, format.inputUnit, format.palette, format.inputUnit) or ""
if tonumber(b) and tonumber(a) then
local attributeValue = outputFormat.first == format.inputUnit and c or convert(c, format.inputUnit, decimals)
sortkey = outputFormat.sortable == true and " data-sort-value=\"" .. attributeValue .. "\"" or ""
titleAttribute = " title=\"Average temperature: " .. attributeValue .. " " .. degree .. outputFormat.first .. "\""
end
elseif tonumber(b) then
colorCSS = ""
elseif tonumber(a) then
colorCSS = outputFormat.color == true and temperatureCSS(a, format.inputUnit, format.palette) or ""
else
addMessage('Neither a nor b nor c are strings.')
end
otherCSS = outputFormat.smallFont == true and "font-size: 85%;" or ""
if ine(colorCSS) or ine(otherCSS) then
styleAttribute = { "style=\"", "\"" }
end
if ine(otherCSS) or ine(colorCSS) or ine(titleAttribute) or ine(sortkey) then
attributeSeparator = " | "
end
cell = "\n| " .. styleAttribute[1] .. colorCSS .. otherCSS .. styleAttribute[2] .. titleAttribute .. sortkey .. attributeSeparator .. cellContent
return cell
end
--[[
Replaces hyphens that have a punctuation or space character before them and a number after them,
making sure that hyphens in "data-sort-type" are not replaced with minuses.
If Lua had (?<=), a capture would not be necessary.
]]
local function hyphenToMinus(str)
return str:gsub("([%p%s])-(%d)", "%1" .. minus .. "%2")
end
function p.makeRow(frame)
local args = frame.args
local format = getFormat(args.input, args.output, args.palette, args.messages)
local makeArray = _makeArray(format)
local a, b, c = makeArray(args.a), makeArray(args.b), makeArray(args.c)
local output = {}
if args[1] then
table.insert(output, "\n|-")
table.insert(output, "\n! " .. args[1])
if args[2] then
table.insert(output, " !! " .. args[2])
end
end
if format.cellFormat then
outputFormat = format.cellFormat
end
-- Assumes that if c is defined, b and a are, and if b is defined, a is.
if c then
if not outputFormat then
outputFormat = outputFormats.high_low_average_F
end
for i = 1, format.length do
table.insert(output, makeCell(outputFormat, a[i], b[i], c[i], format))
end
elseif b then
if not outputFormat then
outputFormat = outputFormats.high_low_F
end
for i = 1, format.length do
table.insert(output, makeCell(outputFormat, a[i], b[i], nil, format))
end
elseif a then
if not outputFormat then
outputFormat = outputFormats.average_F
end
for i = 1, format.length do
table.insert(output, makeCell(outputFormat, a[i], nil, nil, format))
end
end
output = table.concat(output)
output = hyphenToMinus(output)
return output
end
function p.makeTable(frame)
local args = frame.args
local format = getFormat(args.input, args.output, args.palette, args.messages)
local makeArray = _makeArray(format)
local a, b, c = makeArray(args.a), makeArray(args.b), makeArray(args.c)
local output = { "{| class=\"wikitable center nowrap\"" }
if format.cellFormat then
outputFormat = format.cellFormat
end
-- Assumes that if c is defined, b and a are, and if b is defined, a is.
if c then
for i = 1, format.length do
if not outputFormat then
outputFormat = outputFormats.high_low_average_F
end
table.insert(output, makeCell(outputFormat, a[i], b[i], c[i], format))
end
elseif b then
for i = 1, format.length do
if not outputFormat then
outputFormat = outputFormats.high_low_F
end
table.insert(output, makeCell(outputFormat, a[i], b[i], nil, format))
end
elseif a then
for i = 1, format.length do
if not outputFormat then
outputFormat = outputFormats.average_F
end
table.insert(output, makeCell(outputFormat, a[i], nil, nil, format))
end
end
table.insert(output, "\n|}")
if format.show then
table.insert(output, "\n\n<span style=\"color: red; font-size: 80%; line-height: 100%;\">" .. message .. "</span>")
end
output = table.concat(output)
output = hyphenToMinus(output)
return output
end


local chart = [[
local chart = [[
Line 183: Line 595:
]]
]]


local function show(frame)
function p.show(frame)
--[[ For testing, return wikitext to show graphs of how the red/green/blue colors
-- For testing, return wikitext to show graphs of how the red/green/blue colors
vary with temperature, and a table of the resulting colors. ]]
-- vary with temperature, and a table of the resulting colors.
local function collection()
local function collection()
-- Return a table to hold items.
-- Return a table to hold items.
Line 191: Line 603:
n = 0,
n = 0,
add = function (self, item)
add = function (self, item)
self.n = self.n + 1
if item then
self[self.n] = item
self.n = self.n + 1
self[self.n] = item
end
end,
end,
join = function (self, sep)
join = function (self, sep)
Line 209: Line 623:
local function with_minus(value)
local function with_minus(value)
if value < 0 then
if value < 0 then
return MINUS .. tostring(-value)
return minus .. tostring(-value)
end
end
return tostring(value)
return tostring(value)
Line 216: Line 630:
local first = args[1] or -90
local first = args[1] or -90
local last = args[2] or 59
local last = args[2] or 59
local palette = palettes[args.palette] or palettes.cool
local palette = p.palettes[args.palette] or p.palettes.cool
local xvals, reds, greens, blues = collection(), collection(), collection(), collection()
local xvals, reds, greens, blues = collection(), collection(), collection(), collection()
local wikitext = collection()
local wikitext = collection()
wikitext:add(
wikitext:add('{| class="wikitable"\n|-\n')
[[
{| class="wikitable"
|-
]]
)
local columns = 0
local columns = 0
for celsius = first, last do
for celsius = first, last do
local rgb = {}
local backgroundRGB = {}
local style = temperature_style(palette, celsius, rgb)
local style = styleAttribute(palette, celsius, backgroundRGB)
local R = math.floor(rgb[1])
local R = math.floor(backgroundRGB[1])
local G = math.floor(rgb[2])
local G = math.floor(backgroundRGB[2])
local B = math.floor(rgb[3])
local B = math.floor(backgroundRGB[3])
xvals:add(celsius)
xvals:add(celsius)
reds:add(R)
reds:add(R)
Line 250: Line 659:
end
end


return {
return p
CtoF = CtoF,
CfromF = CfromF,
FtoC = FtoC,
FfromC = FfromC,
show = show,
}