Module:Radix: Difference between revisions
Jump to navigation
Jump to search
No edit summary |
asfvdsaiodklbvhjads kjgbvjnedfskj.abg erkjdfsgvaesfgadsbgvasdfawdgaewfg |
||
| (20 intermediate revisions by the same user not shown) | |||
| Line 2: | Line 2: | ||
r.decimal_to = function(frame) | r.decimal_to = function(frame) | ||
digits = {} | |||
digitchars = {} | |||
-- Get number (in base 10) | -- Get number (in base 10) | ||
decimal = tonumber(frame.args[1]) | decimal = tonumber(frame.args[1]) | ||
| Line 26: | Line 29: | ||
-- characters are based on other proglangs' parsing | -- characters are based on other proglangs' parsing | ||
characters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" | characters = numerals and numerals or "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" | ||
-- precision is the number of digits below 1 to use | -- precision is the number of digits below 1 to use | ||
| Line 32: | Line 35: | ||
precision = tonumber(frame.args[3]) or 0 | precision = tonumber(frame.args[3]) or 0 | ||
minpower = radix ^ (precision * -1) | minpower = radix ^ (precision * -1) | ||
if decimal < minpower then | if math.abs(decimal) < minpower then | ||
return "0" | return "0" | ||
end | end | ||
-- | -- maxpower calculation | ||
maxpower = radix ^ math.ceil(math.log(math.abs(decimal + 0.0))/math.log(math.abs(radix + 0.0))) | |||
-- if decimal < 0 then maxpower should be 1 | |||
-- calculate digits | |||
digitval = maxpower | |||
i = 1 | |||
-- format radix and set up num of integral digits for possible formatting | |||
if radix > 0 then decimal = math.abs(decimal) end | |||
integrals = 0 | |||
nextdigitcarry = false | |||
while math.abs(digitval) >= math.abs(minpower) do | |||
-- get floor of abs(decimal) / abs (digitval) | |||
thisdigit = digitval < 0 and math.ceil(math.abs(decimal)/digitval) or math.floor(math.abs(decimal)/digitval) | |||
thisdigitsign = digitval / math.floor(digitval) | |||
-- if thisdigit is negative, flip it and set nextdigitcarry to true | |||
-- otherwise, set it to false | |||
if nextdigitcarry then | |||
thisdigit = math.abs(thisdigit) + 1 | |||
if digitval == 1 then thisdigit = thisdigit - 1 end | |||
nextdigitcarry = true | |||
else | |||
nextdigitcarry = false | |||
end | |||
-- if i is 1 and thisdigit is 0: | |||
-- if radix is negative and digitval is positive, add 1 to thisdigit | |||
-- if radix is positive and maxpower isn't 1, skip | |||
add_digit = true | |||
if thisdigit == 0 and i == 1 then | |||
if radix < 0 and digitval > 0 then | |||
thisdigit = 1 | |||
nextdigitcarry = true | |||
else | |||
if math.abs(maxpower) > 1.0 then | |||
add_digit = false | |||
end | |||
end | |||
end | |||
if add_digit then | |||
-- add thisdigit to digits | |||
digits[i] = thisdigit | |||
-- subtract thisdigit * digitval from decimal | |||
decimal = decimal - thisdigit * digitval | |||
if digitval >= 1 then integrals = integrals + 1 end | |||
-- add this digit to assembled string | |||
i = i + 1 | |||
end | |||
digitval = digitval / radix | |||
end | |||
-- if sign is -1, append a negative sign to the front | |||
signchar = sign == -1 and "-" or "" | |||
ret = "" .. signchar | |||
-- assemble digits | |||
separators = 0 | |||
distance = math.abs(radix) <= 6 and 4 or math.abs(radix) <= 20 and 3 or 2 | |||
for j,digit in ipairs(digits) do | |||
digitchars[j+separators] = string.sub(characters, digit+1, digit+1) | |||
-- when integrals - j is a positive multiple of distance | |||
if format_output then | |||
if j < integrals and (integrals - j) % distance == 0 then | |||
-- add "," and 1 to separators | |||
separators = separators + 1 | |||
digitchars[j+separators] = thousands | |||
end | |||
end | |||
-- when j equals integrals and precision > 0 | |||
if j == integrals and precision > 0 then | |||
-- add "." and 1 to separators | |||
separators = separators + 1 | |||
digitchars[j+separators] = point | |||
end | |||
end | |||
-- assemble final number | |||
for k,character in ipairs(digitchars) do | |||
ret = ret .. character | |||
end | |||
return ret | |||
end | |||
-- The following function is decimal_to but with the args all inline | |||
-- Some checks are removed or changed, as this function is intended for use in translation to other positional notation systems | |||
r.decimal_to_inline = function(frame) | |||
digits = {} | digits = {} | ||
digitchars = {} | digitchars = {} | ||
-- Get number (in base 10) | |||
decimal = tonumber(frame.args[1]) | |||
-- Separator | |||
format_output = frame.args[5] and frame.args[5] or true | |||
thousands = frame.args[6] and frame.args[6] or "," | |||
point = frame.args[7] and frame.args[7] or "." | |||
-- Get radix to convert to | |||
radix = tonumber(frame.args[2]) | |||
-- sign is equal to sign(decimal) if radix is positive, and 1 otherwise | |||
sign = radix > 0 and decimal / math.abs(decimal) or 1 | |||
-- If radix is not an integer: Round to nearest integer for now | |||
radix = radix < 0 and math.ceil(radix) or math.floor(radix) | |||
-- characters are based on other proglangs' parsing | |||
characters = frame.args[4] and frame.args[4] or "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" | |||
-- If abs(radix) is 1, 0, or > characters length, set radix to 10 | |||
if radix == 0 or math.abs(radix) == 1 or math.abs(radix) > string.len(characters) then | |||
radix = 10 | |||
end | |||
-- precision is the number of digits below 1 to use | |||
-- if decimal is less than 1/radix ^ precision, return 0 | |||
precision = tonumber(frame.args[3]) or 0 | |||
minpower = radix ^ (precision * -1) | |||
if math.abs(decimal) < minpower then | |||
return "0" | |||
end | |||
-- maxpower calculation | -- maxpower calculation | ||
maxpower = | maxpower = radix ^ math.ceil(math.log(math.abs(decimal + 0.0))/math.log(math.abs(radix + 0.0))) | ||
-- if decimal < 0 then maxpower should be 1 | -- if decimal < 0 then maxpower should be 1 | ||
| Line 53: | Line 168: | ||
if radix > 0 then decimal = math.abs(decimal) end | if radix > 0 then decimal = math.abs(decimal) end | ||
integrals = 0 | integrals = 0 | ||
nextdigitcarry = false | |||
while math.abs(digitval) >= math.abs(minpower) do | while math.abs(digitval) >= math.abs(minpower) do | ||
-- get floor of abs(decimal) / abs (digitval) | -- get floor of abs(decimal) / abs (digitval) | ||
thisdigit = math. | thisdigit = digitval < 0 and math.ceil(math.abs(decimal)/digitval) or math.floor(math.abs(decimal)/digitval) | ||
thisdigitsign = digitval / math.floor(digitval) | |||
-- if radix is negative, add 1 to thisdigit | -- if thisdigit is negative, flip it and set nextdigitcarry to true | ||
-- otherwise, set it to false | |||
if nextdigitcarry then | |||
thisdigit = math.abs(thisdigit) + 1 | |||
if digitval == 1 then thisdigit = thisdigit - 1 end | |||
nextdigitcarry = true | |||
else | |||
nextdigitcarry = false | |||
end | |||
-- if i is 1 and thisdigit is 0: | |||
-- if radix is negative and digitval is positive, add 1 to thisdigit | |||
-- if radix is positive and maxpower isn't 1, skip | -- if radix is positive and maxpower isn't 1, skip | ||
add_digit = true | add_digit = true | ||
if thisdigit == 0 and i == 1 then | if thisdigit == 0 and i == 1 then | ||
if radix < 0 and digitval > 0 then | |||
thisdigit = 1 | |||
nextdigitcarry = true | |||
else | |||
if math.abs(maxpower) > 1.0 then | |||
add_digit = false | |||
end | |||
end | end | ||
end | end | ||
| Line 71: | Line 200: | ||
digits[i] = thisdigit | digits[i] = thisdigit | ||
-- subtract thisdigit * digitval from decimal | -- subtract thisdigit * digitval from decimal | ||
decimal = decimal - | decimal = decimal - thisdigit * digitval | ||
if digitval >= 1 then integrals = integrals + 1 end | if digitval >= 1 then integrals = integrals + 1 end | ||
-- add this digit to assembled string | |||
i = i + 1 | |||
end | end | ||
digitval = digitval / radix | |||
end | end | ||
-- if sign is -1, append a negative sign to the front | |||
signchar = sign == -1 and "-" or "" | |||
ret = "" .. signchar | |||
-- assemble digits | -- assemble digits | ||
separators = 0 | separators = 0 | ||
distance = math.abs(radix) <= 6 and 3 or | distance = math.abs(radix) <= 6 and 4 or math.abs(radix) <= 20 and 3 or 2 | ||
for j,digit in ipairs(digits) do | for j,digit in ipairs(digits) do | ||
digitchars[j+separators] = string.sub(characters, digit+1, digit+1) | digitchars[j+separators] = string.sub(characters, digit+1, digit+1) | ||
| Line 91: | Line 225: | ||
end | end | ||
end | end | ||
-- when j equals integrals | -- when j equals integrals and precision > 0 | ||
if j == integrals then | if j == integrals and precision > 0 then | ||
-- add "." and 1 to separators | -- add "." and 1 to separators | ||
separators = separators + 1 | separators = separators + 1 | ||
| Line 99: | Line 233: | ||
end | end | ||
-- assemble final number | -- assemble final number | ||
for k,character in ipairs(digitchars) do | for k,character in ipairs(digitchars) do | ||
ret = ret .. character | ret = ret .. character | ||
end | end | ||
return ret | return ret | ||
end | |||
local function unpack_ipairs(t) | |||
local i = 0 | |||
return function() | |||
i = i + 1 | |||
if t[i] then | |||
return i, unpack(t[i]) | |||
end | |||
end | |||
end | |||
-- To Kardonian shorthand; not great but it should hopefully work | |||
r.to_kard = function(frame) | |||
number = tonumber(frame.args[1]) | |||
-- Make sure 0 is not the result | |||
if number == 0 then | |||
return "" | |||
end | |||
truncated_number = number < 0 and math.ceil(number) or math.floor(number) | |||
is_mixed = 0 | |||
frame0 = {} | |||
frame0.args = {number, 64, 0, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", false} | |||
frame1 = {} | |||
frame1.args = {number, 64, 1, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", false} | |||
frame2 = {} | |||
frame2.args = {number, 64, 2, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", false} | |||
if number == truncated_number then | |||
-- base 64 | |||
base64encoded = r.decimal_to_inline(frame0) | |||
else | |||
is_mixed = 1 | |||
-- is it at most 2 digits long? | |||
new_truncated_number = number < 0 and math.ceil(number/64) or math.floor(number/64) | |||
if number == new_truncated_number then | |||
base64encoded = r.decimal_to_inline(frame1) | |||
else | |||
is_mixed = 2 | |||
-- assume 4 digits is enough | |||
base64encoded = r.decimal_to_inline(frame2) | |||
end | |||
end | |||
final_string = "" .. base64encoded | |||
-- reencode to base 8 | |||
for i, stringin64, stringin8 in unpack_ipairs { | |||
{"A",""},{"B",""},{"C",""},{"D",""},{"E",""},{"F",""},{"G",""},{"H",""}, | |||
{"I",""},{"J",""},{"K",""},{"L",""},{"M",""},{"N",""},{"O",""},{"P",""}, | |||
{"Q",""},{"R",""},{"S",""},{"T",""},{"U",""},{"V",""},{"W",""},{"X",""}, | |||
{"Y",""},{"Z",""},{"a",""},{"b",""},{"c",""},{"d",""},{"e",""},{"f",""}, | |||
{"g",""},{"h",""},{"i",""},{"j",""},{"k",""},{"l",""},{"m",""},{"n",""}, | |||
{"o",""},{"p",""},{"q",""},{"r",""},{"s",""},{"t",""},{"u",""},{"v",""}, | |||
{"w",""},{"x",""},{"y",""},{"z",""},{"0",""},{"1",""},{"2",""},{"3",""}, | |||
{"4",""},{"5",""},{"6",""},{"7",""},{"8",""},{"9",""},{"+",""},{"/",""}, | |||
{"\\.",""},{",",""} | |||
} do | |||
-- find stringin64 and replace with stringin8 | |||
final_string = string.gsub(final_string,stringin64,stringin8) | |||
end | |||
-- TODO: convert above to fully base 8 if after the decimal point | |||
return final_string | |||
end | end | ||
return r | return r | ||
Latest revision as of 01:41, 9 October 2026
Documentation for this module may be created at Module:Radix/doc
local r = {}
r.decimal_to = function(frame)
digits = {}
digitchars = {}
-- Get number (in base 10)
decimal = tonumber(frame.args[1])
-- If decimal is 0 or 1, return decimal
if decimal == 0 or decimal == 1 then
return tostring(decimal)
end
-- Separator
format_output = format_output and format_output or true
thousands = thousands and thousands or ","
point = point and point or "."
-- Get radix to convert to
radix = tonumber(frame.args[2])
-- sign is equal to sign(decimal) if radix is positive, and 1 otherwise
sign = radix > 0 and decimal / math.abs(decimal) or 1
-- If radix is not an integer: Round to nearest integer for now
radix = radix < 0 and math.ceil(radix) or math.floor(radix)
-- If abs(radix) is 1, 0, or > 36, return decimal
if radix == 0 or math.abs(radix) == 1 or math.abs(radix) > 36 then
return tostring(decimal)
end
-- characters are based on other proglangs' parsing
characters = numerals and numerals or "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
-- precision is the number of digits below 1 to use
-- if decimal is less than 1/radix ^ precision, return 0
precision = tonumber(frame.args[3]) or 0
minpower = radix ^ (precision * -1)
if math.abs(decimal) < minpower then
return "0"
end
-- maxpower calculation
maxpower = radix ^ math.ceil(math.log(math.abs(decimal + 0.0))/math.log(math.abs(radix + 0.0)))
-- if decimal < 0 then maxpower should be 1
-- calculate digits
digitval = maxpower
i = 1
-- format radix and set up num of integral digits for possible formatting
if radix > 0 then decimal = math.abs(decimal) end
integrals = 0
nextdigitcarry = false
while math.abs(digitval) >= math.abs(minpower) do
-- get floor of abs(decimal) / abs (digitval)
thisdigit = digitval < 0 and math.ceil(math.abs(decimal)/digitval) or math.floor(math.abs(decimal)/digitval)
thisdigitsign = digitval / math.floor(digitval)
-- if thisdigit is negative, flip it and set nextdigitcarry to true
-- otherwise, set it to false
if nextdigitcarry then
thisdigit = math.abs(thisdigit) + 1
if digitval == 1 then thisdigit = thisdigit - 1 end
nextdigitcarry = true
else
nextdigitcarry = false
end
-- if i is 1 and thisdigit is 0:
-- if radix is negative and digitval is positive, add 1 to thisdigit
-- if radix is positive and maxpower isn't 1, skip
add_digit = true
if thisdigit == 0 and i == 1 then
if radix < 0 and digitval > 0 then
thisdigit = 1
nextdigitcarry = true
else
if math.abs(maxpower) > 1.0 then
add_digit = false
end
end
end
if add_digit then
-- add thisdigit to digits
digits[i] = thisdigit
-- subtract thisdigit * digitval from decimal
decimal = decimal - thisdigit * digitval
if digitval >= 1 then integrals = integrals + 1 end
-- add this digit to assembled string
i = i + 1
end
digitval = digitval / radix
end
-- if sign is -1, append a negative sign to the front
signchar = sign == -1 and "-" or ""
ret = "" .. signchar
-- assemble digits
separators = 0
distance = math.abs(radix) <= 6 and 4 or math.abs(radix) <= 20 and 3 or 2
for j,digit in ipairs(digits) do
digitchars[j+separators] = string.sub(characters, digit+1, digit+1)
-- when integrals - j is a positive multiple of distance
if format_output then
if j < integrals and (integrals - j) % distance == 0 then
-- add "," and 1 to separators
separators = separators + 1
digitchars[j+separators] = thousands
end
end
-- when j equals integrals and precision > 0
if j == integrals and precision > 0 then
-- add "." and 1 to separators
separators = separators + 1
digitchars[j+separators] = point
end
end
-- assemble final number
for k,character in ipairs(digitchars) do
ret = ret .. character
end
return ret
end
-- The following function is decimal_to but with the args all inline
-- Some checks are removed or changed, as this function is intended for use in translation to other positional notation systems
r.decimal_to_inline = function(frame)
digits = {}
digitchars = {}
-- Get number (in base 10)
decimal = tonumber(frame.args[1])
-- Separator
format_output = frame.args[5] and frame.args[5] or true
thousands = frame.args[6] and frame.args[6] or ","
point = frame.args[7] and frame.args[7] or "."
-- Get radix to convert to
radix = tonumber(frame.args[2])
-- sign is equal to sign(decimal) if radix is positive, and 1 otherwise
sign = radix > 0 and decimal / math.abs(decimal) or 1
-- If radix is not an integer: Round to nearest integer for now
radix = radix < 0 and math.ceil(radix) or math.floor(radix)
-- characters are based on other proglangs' parsing
characters = frame.args[4] and frame.args[4] or "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
-- If abs(radix) is 1, 0, or > characters length, set radix to 10
if radix == 0 or math.abs(radix) == 1 or math.abs(radix) > string.len(characters) then
radix = 10
end
-- precision is the number of digits below 1 to use
-- if decimal is less than 1/radix ^ precision, return 0
precision = tonumber(frame.args[3]) or 0
minpower = radix ^ (precision * -1)
if math.abs(decimal) < minpower then
return "0"
end
-- maxpower calculation
maxpower = radix ^ math.ceil(math.log(math.abs(decimal + 0.0))/math.log(math.abs(radix + 0.0)))
-- if decimal < 0 then maxpower should be 1
-- calculate digits
digitval = maxpower
i = 1
-- format radix and set up num of integral digits for possible formatting
if radix > 0 then decimal = math.abs(decimal) end
integrals = 0
nextdigitcarry = false
while math.abs(digitval) >= math.abs(minpower) do
-- get floor of abs(decimal) / abs (digitval)
thisdigit = digitval < 0 and math.ceil(math.abs(decimal)/digitval) or math.floor(math.abs(decimal)/digitval)
thisdigitsign = digitval / math.floor(digitval)
-- if thisdigit is negative, flip it and set nextdigitcarry to true
-- otherwise, set it to false
if nextdigitcarry then
thisdigit = math.abs(thisdigit) + 1
if digitval == 1 then thisdigit = thisdigit - 1 end
nextdigitcarry = true
else
nextdigitcarry = false
end
-- if i is 1 and thisdigit is 0:
-- if radix is negative and digitval is positive, add 1 to thisdigit
-- if radix is positive and maxpower isn't 1, skip
add_digit = true
if thisdigit == 0 and i == 1 then
if radix < 0 and digitval > 0 then
thisdigit = 1
nextdigitcarry = true
else
if math.abs(maxpower) > 1.0 then
add_digit = false
end
end
end
if add_digit then
-- add thisdigit to digits
digits[i] = thisdigit
-- subtract thisdigit * digitval from decimal
decimal = decimal - thisdigit * digitval
if digitval >= 1 then integrals = integrals + 1 end
-- add this digit to assembled string
i = i + 1
end
digitval = digitval / radix
end
-- if sign is -1, append a negative sign to the front
signchar = sign == -1 and "-" or ""
ret = "" .. signchar
-- assemble digits
separators = 0
distance = math.abs(radix) <= 6 and 4 or math.abs(radix) <= 20 and 3 or 2
for j,digit in ipairs(digits) do
digitchars[j+separators] = string.sub(characters, digit+1, digit+1)
-- when integrals - j is a positive multiple of distance
if format_output then
if j < integrals and (integrals - j) % distance == 0 then
-- add "," and 1 to separators
separators = separators + 1
digitchars[j+separators] = thousands
end
end
-- when j equals integrals and precision > 0
if j == integrals and precision > 0 then
-- add "." and 1 to separators
separators = separators + 1
digitchars[j+separators] = point
end
end
-- assemble final number
for k,character in ipairs(digitchars) do
ret = ret .. character
end
return ret
end
local function unpack_ipairs(t)
local i = 0
return function()
i = i + 1
if t[i] then
return i, unpack(t[i])
end
end
end
-- To Kardonian shorthand; not great but it should hopefully work
r.to_kard = function(frame)
number = tonumber(frame.args[1])
-- Make sure 0 is not the result
if number == 0 then
return ""
end
truncated_number = number < 0 and math.ceil(number) or math.floor(number)
is_mixed = 0
frame0 = {}
frame0.args = {number, 64, 0, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", false}
frame1 = {}
frame1.args = {number, 64, 1, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", false}
frame2 = {}
frame2.args = {number, 64, 2, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", false}
if number == truncated_number then
-- base 64
base64encoded = r.decimal_to_inline(frame0)
else
is_mixed = 1
-- is it at most 2 digits long?
new_truncated_number = number < 0 and math.ceil(number/64) or math.floor(number/64)
if number == new_truncated_number then
base64encoded = r.decimal_to_inline(frame1)
else
is_mixed = 2
-- assume 4 digits is enough
base64encoded = r.decimal_to_inline(frame2)
end
end
final_string = "" .. base64encoded
-- reencode to base 8
for i, stringin64, stringin8 in unpack_ipairs {
{"A",""},{"B",""},{"C",""},{"D",""},{"E",""},{"F",""},{"G",""},{"H",""},
{"I",""},{"J",""},{"K",""},{"L",""},{"M",""},{"N",""},{"O",""},{"P",""},
{"Q",""},{"R",""},{"S",""},{"T",""},{"U",""},{"V",""},{"W",""},{"X",""},
{"Y",""},{"Z",""},{"a",""},{"b",""},{"c",""},{"d",""},{"e",""},{"f",""},
{"g",""},{"h",""},{"i",""},{"j",""},{"k",""},{"l",""},{"m",""},{"n",""},
{"o",""},{"p",""},{"q",""},{"r",""},{"s",""},{"t",""},{"u",""},{"v",""},
{"w",""},{"x",""},{"y",""},{"z",""},{"0",""},{"1",""},{"2",""},{"3",""},
{"4",""},{"5",""},{"6",""},{"7",""},{"8",""},{"9",""},{"+",""},{"/",""},
{"\\.",""},{",",""}
} do
-- find stringin64 and replace with stringin8
final_string = string.gsub(final_string,stringin64,stringin8)
end
-- TODO: convert above to fully base 8 if after the decimal point
return final_string
end
return r