c1_lua/lua/app/tools/big_num_opt.lua
2023-04-03 10:59:13 +08:00

532 lines
12 KiB
Lua

local BigNumOpt = {}
local MAX_CHAR = 26
local CHAR_NUM = 64
local DIGIT_UNIT = 1000
local DIGIT_ONE = 1000
local DIGIT_TWO = 1000000
local DIGIT_THREE = 1000000000
local DIGIT_FOUR = 1000000000000
-- local MAX_INTEGER = math.maxinteger
local MAX_INTEGER = 999999999999999
local MIN_INTEGER = 1000000
function BigNumOpt.cloneBigNum(bigBum)
return {value = bigBum.value, unit = bigBum.unit}
end
function BigNumOpt.getRemainder(unit)
return ((unit -1)%MAX_CHAR + 1)
end
function BigNumOpt.getEmptyBigNum()
local data = {}
data.value = math.tointeger(0)
data.unit = math.tointeger(0)
return data
end
function BigNumOpt.unitToNum(unit)
if unit == 1 then
return DIGIT_ONE
elseif unit == 2 then
return DIGIT_TWO
elseif unit == 3 then
return DIGIT_THREE
elseif unit == 4 then
return DIGIT_FOUR
else
return DIGIT_UNIT^unit
end
end
function BigNumOpt.numToTargetUnit(value, unit, targetUnit)
while true do
if unit == targetUnit or value == 0 then
break
end
if unit > targetUnit then
value = value * DIGIT_UNIT
unit = unit - 1
elseif unit < targetUnit then
if math.abs(value) < DIGIT_UNIT then
value = 0
else
value = value // DIGIT_UNIT
end
unit = unit + 1
end
end
return value, unit
end
function BigNumOpt.adjustDisplayBigNum(data)
if not BigNumOpt.checkDisplayBigNumData(data) then
return data
end
while true do
if data.unit >= 0 then
break
end
data.value = data.value / DIGIT_UNIT
data.unit = data.unit + 1
end
while true do
if data.unit <= 0 or data.value >= 1 then
break
end
data.value = data.value * DIGIT_UNIT
data.unit = data.unit - 1
end
while true do
if data.value < DIGIT_UNIT then
break
end
data.value = data.value / DIGIT_UNIT
data.unit = data.unit + 1
end
while true do
if data.value > -DIGIT_UNIT then
break
end
data.value = data.value / DIGIT_UNIT
data.unit = data.unit + 1
end
if data.value == 0 then
data.unit = 0
end
return data
end
function BigNumOpt.adjustRealBigNum(data)
if data.value > 0 then
while true do
if data.value <= MAX_INTEGER then
break
end
data.value = data.value // DIGIT_UNIT
data.unit = data.unit + 1
end
while true do
if data.value >= MIN_INTEGER then
break
end
data.value = data.value * DIGIT_UNIT
data.unit = data.unit - 1
end
elseif data.value < 0 then
while true do
if data.value >= -MAX_INTEGER then
break
end
data.value = data.value // DIGIT_UNIT
data.unit = data.unit + 1
end
while true do
if data.value <= -MIN_INTEGER then
break
end
data.value = data.value * DIGIT_UNIT
data.unit = data.unit - 1
end
end
if data.value == 0 then
data.unit = 0
end
return data
end
function BigNumOpt.getValidValue4Power(value)
local finalValue = 0
if value >= 100 then
finalValue = math.ceil(value)
elseif value >= 10 then
finalValue = math.ceil(value*10)/10
elseif value >= 0 then
finalValue = math.ceil(value*100)/100
elseif value >= -10 then
finalValue = math.floor(value*100)/100
elseif value >= -100 then
finalValue = math.floor(value*10)/10
else
finalValue = math.floor(value)
end
if finalValue == math.ceil(finalValue) then
return math.ceil(finalValue)
else
return finalValue
end
end
function BigNumOpt.getValidValue(value)
local finalValue = 0
if value >= 100 then
finalValue = math.floor(value)
elseif value >= 10 then
finalValue = math.floor(value*10)/10
elseif value >= 0 then
finalValue = math.floor(value*100)/100
elseif value >= -10 then
finalValue = math.ceil(value*100)/100
elseif value >= -100 then
finalValue = math.ceil(value*10)/10
else
finalValue = math.ceil(value)
end
if finalValue == math.floor(finalValue) then
return math.floor(finalValue)
else
return finalValue
end
end
function BigNumOpt.bigNum2Str4Power(bigNum)
local data = BigNumOpt.cloneBigNum(bigNum)
local tmp = BigNumOpt.adjustDisplayBigNum(data)
return BigNumOpt.getValidValue4Power(tmp.value) .. BigNumOpt.getBigNumUnit(tmp.unit)
end
-- 大数字转换
function BigNumOpt.bigNum2Str(bigNum)
local data = BigNumOpt.cloneBigNum(bigNum)
local tmp = BigNumOpt.adjustDisplayBigNum(data)
return BigNumOpt.getValidValue(tmp.value) .. BigNumOpt.getBigNumUnit(tmp.unit)
end
function BigNumOpt.bigNum2Num(bigNum)
--@TODO 2023-03-20 23:36:11
return math.floor(bigNum.value * (BigNumOpt.unitToNum(bigNum.unit)))
end
function BigNumOpt.num2BigNum(num)
local data = BigNumOpt.getEmptyBigNum()
data.value = num
BigNumOpt.adjustRealBigNum(data)
return data
end
function BigNumOpt.checkDisplayBigNumData(data)
if not data or not data.value or not data.unit then
return false
end
if not data.value then
return false
end
return true
end
function BigNumOpt.checkBigNumData(data)
if not data or not data.value or not data.unit then
return false
end
if math.type(data.value) ~= "integer" then
local value = math.floor(data.value)
data.value = math.tointeger(value)
end
if not data.value then
return false
end
return true
end
function BigNumOpt.bigNumAdd(data1, data2)
local data = BigNumOpt.getEmptyBigNum()
if not BigNumOpt.checkBigNumData(data1) or not BigNumOpt.checkBigNumData(data2) then
return data
end
local value1 = data1.value
local value2 = data2.value
local unit1 = data1.unit
local unit2 = data2.unit
if unit1 > unit2 then
-- data.value = value1*(BigNumOpt.unitToNum(unit1 - unit2)) + value2
-- data.unit = unit2
local value, unit = BigNumOpt.numToTargetUnit(value2, unit2, unit1)
data.value = value1 + value
data.unit = unit1
elseif unit1 < unit2 then
-- data.value = value1 + value2*(BigNumOpt.unitToNum(unit2 - unit1))
-- data.unit = unit1
local value, unit = BigNumOpt.numToTargetUnit(value1, unit1, unit2)
data.value = value + value2
data.unit = unit2
else
data.value = value1 + value2
data.unit = unit1
end
BigNumOpt.adjustRealBigNum(data)
return data
end
function BigNumOpt.bigNumSub(data1, data2)
local data = BigNumOpt.getEmptyBigNum()
if not BigNumOpt.checkBigNumData(data1) or not BigNumOpt.checkBigNumData(data2) then
return data
end
local value1 = data1.value
local value2 = data2.value
local unit1 = data1.unit
local unit2 = data2.unit
if unit1 > unit2 then
-- data.value = value1*(BigNumOpt.unitToNum(unit1 - unit2)) - value2
local value, unit = BigNumOpt.numToTargetUnit(value2, unit2, unit1)
data.value = value1 - value
data.unit = unit1
elseif unit1 < unit2 then
-- data.value = value1 - value2*(BigNumOpt.unitToNum(unit2 - unit1))
local value, unit = BigNumOpt.numToTargetUnit(value1, unit1, unit2)
data.value = value - value2
data.unit = unit2
else
data.value = value1 - value2
data.unit = unit1
end
BigNumOpt.adjustRealBigNum(data)
return data
end
function BigNumOpt.bigNumMultBigNum(data1, data2)
local data = BigNumOpt.getEmptyBigNum()
if not BigNumOpt.checkBigNumData(data1) or not BigNumOpt.checkBigNumData(data2) then
return data
end
local value1 = data1.value
local value2 = data2.value
local unit1 = data1.unit
local unit2 = data2.unit
if value1 > 0 then
while true do
if value1 <= MIN_INTEGER then
break
end
value1 = value1 // DIGIT_UNIT
unit1 = unit1 + 1
end
elseif value1 < 0 then
while true do
if value1 >= -MIN_INTEGER then
break
end
value1 = value1 // DIGIT_UNIT
unit1 = unit1 + 1
end
end
if value2 > 0 then
while true do
if value2 <= MIN_INTEGER then
break
end
value2 = value2 // DIGIT_UNIT
unit2 = unit2 + 1
end
elseif value2 < 0 then
while true do
if value2 >= -MIN_INTEGER then
break
end
value2 = value2 // DIGIT_UNIT
unit2 = unit2 + 1
end
end
data.value = value1*value2
data.unit = unit1 + unit2
BigNumOpt.adjustRealBigNum(data)
return data
end
function BigNumOpt.bigNumMultNum(bigNum, num)
local data = BigNumOpt.getEmptyBigNum()
if not BigNumOpt.checkBigNumData(bigNum) then
return data
end
local value = bigNum.value
data.unit = bigNum.unit
data.value = value*num
BigNumOpt.adjustRealBigNum(data)
return data
end
function BigNumOpt.bigNumDiv2Float(data1, data2)
if not BigNumOpt.checkBigNumData(data1) or not BigNumOpt.checkBigNumData(data2) then
return 0
end
local value1 = data1.value
local value2 = data2.value
local unit1 = data1.unit
local unit2 = data2.unit
if unit1 > unit2 then
return value1*(BigNumOpt.unitToNum(unit1 - unit2)) / value2
elseif unit1 < unit2 then
return value1 / (value2*(BigNumOpt.unitToNum(unit2 - unit1)))
else
return value1 / value2
end
end
-- 大数除以很小的数 用整除
function BigNumOpt.bigNumDivExactly(data1, num)
local data = BigNumOpt.getEmptyBigNum()
data.value = data1.value // num
data.unit = data1.unit
return data
end
function BigNumOpt.bigNumDiv(data1, data2)
local data = BigNumOpt.getEmptyBigNum()
if not BigNumOpt.checkBigNumData(data1) or not BigNumOpt.checkBigNumData(data2) then
return data
end
local value1 = data1.value
local value2 = data2.value
local unit1 = data1.unit
local unit2 = data2.unit
if value1 == 0 then
return data
end
local value = value1 / value2
if value >= MIN_INTEGER then
local value = math.floor(value)
data.value = math.tointeger(value)
data.unit = unit1 - unit2
else
local value = math.floor(value * DIGIT_TWO)
data.value = math.tointeger(value)
data.unit = unit1 - unit2 - 2
end
-- if data.unit < 0 then
-- local value = BigNumOpt.numToTargetUnit(data.value, data.unit, 0)
-- data.value = math.tointeger(math.floor(value))
-- data.unit = 0
-- end
-- if unit1 > unit2 then
-- data.value = value1*(BigNumOpt.unitToNum(unit1 - unit2)) // value2
-- data.unit = 0
-- elseif unit1 < unit2 then
-- data.value = value1 // (value2*(BigNumOpt.unitToNum(unit2 - unit1)))
-- data.unit = 0
-- else
-- data.value = value1 // value2
-- data.unit = 0
-- end
if EDITOR_MODE then
if value1 > 0 and value2 > 0 and data.value < 0 then
Logger.logFatal("the value is out of range")
end
end
BigNumOpt.adjustRealBigNum(data)
return data
end
function BigNumOpt.bigNumDivNum(data1, num)
local value = data1.value / num
if math.floor(value) == value then
local data = BigNumOpt.getEmptyBigNum()
data.value = data1.value // num
data.unit = data1.unit
BigNumOpt.adjustRealBigNum(data)
return data
end
local data2 = BigNumOpt.num2BigNum(num)
return BigNumOpt.bigNumDiv(data1, data2)
end
function BigNumOpt.bigNumCompare(data1, data2)
local data = BigNumOpt.getEmptyBigNum()
if not BigNumOpt.checkBigNumData(data1) or not BigNumOpt.checkBigNumData(data2) then
return
end
local value1 = data1.value
local value2 = data2.value
local unit1 = data1.unit
local unit2 = data2.unit
local data = BigNumOpt.bigNumSub(data1, data2)
if data.value > 0 then
return 1
elseif data.value < 0 then
return -1
else
return 0
end
end
function BigNumOpt.bigNumSqrt(data1)
if not BigNumOpt.checkBigNumData(data1) then
return data1
end
local data = BigNumOpt.getEmptyBigNum()
local value = data1.value
local unit = data1.unit
if value == 0 then
return data
end
if unit % 2 == 0 then
value = math.sqrt(value)
unit = unit / 2
else
value = math.sqrt(value*DIGIT_ONE)
unit = (unit - 1) / 2
end
if value >= MIN_INTEGER then
value = math.floor(value)
data.value = math.tointeger(value)
data.unit = unit
else
value = math.floor(value * DIGIT_TWO)
data.value = math.tointeger(value)
data.unit = unit - 2
end
return data
end
-- 单位转字符
function BigNumOpt.getBigNumUnit(unit)
if unit <= 0 then
return ""
end
local curDigit = 0
local unitStr = ""
while true do
local re = BigNumOpt.getRemainder(unit)
unitStr = string.char(re + CHAR_NUM) .. unitStr
if unit <= MAX_CHAR then
break
else
unit = (unit - re)/MAX_CHAR
end
end
return unitStr
end
--attr转str
-- function BigNumOpt.bigNumCfgAttr2FinalStr(cfgAttr)
-- local finalValueStr = BigNumOpt.bigNumCfgAttr2FinalValueStr(cfgAttr)
-- return GFunc.getPerStr(GConst.ATTR_TYPE[cfgAttr.type], finalValueStr)
-- end
function BigNumOpt.bigNumCfgAttr2FinalValueStr(cfgAttr)
local attrStr = GFunc.getPerStr(GConst.ATTR_TYPE[cfgAttr.type], BigNumOpt.bigNum2Str(GFunc.getFinalAttr(GConst.ATTR_TYPE[cfgAttr.type], cfgAttr.bignum)))
return attrStr
end
function BigNumOpt.bigNumCfgAttr2FinalValueStr2(type, attrValue)
local attrStr = GFunc.getPerStr(type, BigNumOpt.bigNum2Str(GFunc.getFinalAttr(type, attrValue)))
return attrStr
end
return BigNumOpt