Module:Radix

From Nguhcraft Wiki
Jump to navigation Jump to search

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 0 or 1, return decimal 
	if radix == 0 or math.abs(radix) == 1 then 
		return tostring(decimal)
	end
	
	-- characters are based on other proglangs' parsing
	characters = numerals and numerals or "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
	-- If abs(radix) > characters.len, return decimal
	if math.abs(radix) > string.len(characters) then
		return tostring(decimal)
	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 3
	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

return r