Module:Radix: Difference between revisions

From Nguhcraft Wiki
Jump to navigation Jump to search
Astaryuu (talk | contribs)
No edit summary
Astaryuu (talk | contribs)
asfvdsaiodklbvhjads kjgbvjnedfskj.abg erkjdfsgvaesfgadsbgvasdfawdgaewfg
 
(36 intermediate revisions by the same user not shown)
Line 2: Line 2:


r.decimal_to = function(frame)
r.decimal_to = function(frame)
-- Get arguments from call
digits = {}
local decimal = math.abs(tonumber(frame.args[1]))
digitchars = {}
local sign = decimal > 0 and "" or "-"
-- If decimal is 1, return itself
-- Get number (in base 10)
if decimal == 1 then
decimal = tonumber(frame.args[1])
-- If decimal is 0 or 1, return decimal
if decimal == 0 or decimal == 1 then  
return tostring(decimal)
return tostring(decimal)
end
end
-- Newradix is the radix to convert to
local newradix = tonumber(frame.args[2])
-- Separator
local characters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
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
-- precision is the number of digits below 1 to use
-- if decimal is less than 1/newradix ^ precision, return 0
-- if decimal is less than 1/radix ^ precision, return 0
local precision = tonumber(frame.args[3]) or 0
precision = tonumber(frame.args[3]) or 0
local minpower = newradix ^ (precision * -1)
minpower = radix ^ (precision * -1)
if decimal < minpower then
if math.abs(decimal) < minpower then
return "0"
return "0"
end
end
-- final string to yeet everything into
local ret = "" .. sign
local maxpower = 1
-- maxpower calculation
if decimal > 0 then
maxpower = radix ^ math.ceil(math.log(math.abs(decimal + 0.0))/math.log(math.abs(radix + 0.0)))
    -- Get highest power of newradix less than decimal
-- if decimal < 0 then maxpower should be 1
    while maxpower < decimal do
    maxpower = maxpower * newradix
-- calculate digits
    end
digitval = maxpower
    maxpower = maxpower / newradix
i = 1
    -- Double loop
-- format radix and set up num of integral digits for possible formatting
    -- Outer loop: While maxpower >= minpower, add result of second loop to ret
if radix > 0 then decimal = math.abs(decimal) end
    -- Inner loop: While decimal >= maxpower, subtract maxpower from decimal
integrals = 0
    while maxpower >= minpower do
    nextdigitcarry = false
    local digit = 1
while math.abs(digitval) >= math.abs(minpower) do
    while decimal >= maxpower do
-- get floor of abs(decimal) / abs (digitval)
    decimal = decimal - maxpower
thisdigit = digitval < 0 and math.ceil(math.abs(decimal)/digitval) or math.floor(math.abs(decimal)/digitval)
    digit = digit + 1
        thisdigitsign = digitval / math.floor(digitval)
    end
        -- if thisdigit is negative, flip it and set nextdigitcarry to true
    -- digit now contains number of maxpowers, concat it onto ret
        -- otherwise, set it to false
    ret = ret .. string.sub(characters, digit, digit)
        if nextdigitcarry then
    maxpower = maxpower / newradix
            thisdigit = math.abs(thisdigit) + 1
    -- append a . if maxpower went below 1 and there is more to print
            if digitval == 1 then thisdigit = thisdigit - 1 end
    if maxpower < 1 and minpower < 1 then ret = ret .. "." end
            nextdigitcarry = true
    end
        else
    return ret
            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
else
-- Decimal is 0, also return itself
is_mixed = 1
return tostring(decimal)
-- 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
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