ZNZNCOOP

Metanum

May 26th, 2016
153
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. -- Метачисла позволяют оперировать огромными значениями. Например, посчитать факториал 300 или вычислить 2^2048
  2. local metanum,m_table
  3.  
  4. --Коррекция метачисла
  5. local function correct(mn)
  6.   local e
  7.   mn.mant,e=mn.mant:match("^0*(.-)(0*)$")
  8.   mn.exp=mn.exp+#e
  9.   if mn.mant=="" then mn.neg=false mn.exp=0 end
  10.   return mn
  11. end
  12.  
  13. local function getMant(op1,op2)
  14.   local m1=op1.mant if op1.exp>op2.exp then m1=m1..string.rep("0",op1.exp-op2.exp) end
  15.   local m2=op2.mant if op2.exp>op1.exp then m2=m2..string.rep("0",op2.exp-op1.exp) end
  16.   if #m1>#m2 then m2=string.rep("0",#m1-#m2)..m2 end
  17.   if #m2>#m1 then m1=string.rep("0",#m2-#m1)..m1 end
  18.   return m1,m2
  19. end
  20.  
  21. local function division(op1,op2,divprec)
  22.   local dividend=metanum(op1)
  23.   local divisor=metanum(op2)
  24.   if divisor.mant=="" then return math.huge, 0/0 end
  25.   local res={mant="0",neg= dividend.neg~=divisor.neg,exp=#dividend.mant+dividend.exp-#divisor.mant-divisor.exp, divprec=divprec}
  26.   dividend.neg=false
  27.   divisor.neg=false
  28.   divisor.exp=divisor.exp+res.exp
  29.   setmetatable(res,m_table)
  30.  
  31.   while dividend.mant~="" do
  32.     if divisor>dividend then
  33.       res.mant=res.mant.."0"
  34.       res.exp=res.exp-1
  35.       if res.exp<0 and #res.mant>res.divprec then
  36.         if res.neg then return res+setmetatable({neg=true,mant="1",exp=res.exp+1},m_table) end
  37.         break
  38.       end
  39.       dividend.exp=dividend.exp+1
  40.     else
  41.       dividend=dividend-divisor
  42.       res.mant=res.mant:sub(1,-2)..(res.mant:byte(-1)-47)
  43.     end
  44.   end
  45.   return correct(res)
  46. end
  47.  
  48. m_table={ --Метатаблица для работы с метачислами
  49.   __index={ divprec = 32,
  50.     tonumber=function(self)  --Преобразует метачисло в обычное число (возможна потеря точности)
  51.       return tonumber(tostring(self))
  52.     end,
  53.     floor=function(self,n)     --Целая часть метачисла
  54.       local res=metanum(self)
  55.       n=n or 0
  56.       if res.exp<-n then
  57.         res.mant=res.mant:sub(1,res.exp+n-1)
  58.         res.exp=-n
  59.         if res.neg then
  60.           local one=metanum("1")
  61.           one.exp=-n
  62.           return res-one
  63.         end
  64.       end
  65.       return res
  66.     end,
  67.     abs=function(self)       --Абсолютное значение
  68.       local res=metanum(self)
  69.       res.neg=false
  70.       return res
  71.     end,
  72.     toexp=function(self)
  73.       if self.mant=="" then return 0 end
  74.       return self.mant:sub(1,1).."."..self.mant:sub(2).."e"..(#self.mant-1+math.floor(self.exp))
  75.     end,
  76.   },
  77.   __tostring=function(self)  --Преобразует метачисло в строку
  78.     local res
  79.     if self.exp>=0 then res=self.mant..string.rep("0",self.exp)
  80.     else
  81.       res=string.rep("0",1-self.exp-#self.mant)..self.mant
  82.       res=res:sub(1,self.exp-1).."."..res:sub(self.exp)
  83.     end
  84.     if res=="" then res="0" end
  85.     if self.neg then res="-"..res end
  86.     return res
  87.   end,
  88.   __unm=function(self)  --Унарный минус
  89.     local res=metanum(self)
  90.     res.neg=not res.neg
  91.     return res
  92.   end,
  93.   __add=function(op1,op2)  --Сложение метачисел
  94.     if getmetatable(op1)~=m_table then op1=metanum(op1) end
  95.     if getmetatable(op2)~=m_table then op2=metanum(op2) end
  96.     if op1.neg~=op2.neg then return op1-(-op2) end
  97.     local res=metanum() res.neg=op1.neg
  98.     local c=0
  99.     local m1,m2=getMant(op1,op2)
  100.     res.exp=math.min(op1.exp,op2.exp)
  101.     for i=#m1,1,-1 do
  102.       c=m1:byte(i)+m2:byte(i)+c-96
  103.       res.mant=c%10 .. res.mant
  104.       c=math.floor(c/10)
  105.     end
  106.     res.mant=c .. res.mant
  107.     return correct(res)
  108.   end,
  109.   __sub=function(op1,op2)  --Вычитание метачисел
  110.     if getmetatable(op1)~=m_table then op1=metanum(op1) end
  111.     if getmetatable(op2)~=m_table then op2=metanum(op2) end
  112.     if op1.neg~=op2.neg then return op1+(-op2) end
  113.     local res=metanum() res.neg=op1.neg
  114.     local c=0
  115.     local m1,m2=getMant(op1,op2)
  116.     res.exp=math.min(op1.exp,op2.exp)
  117.     if m2>m1 then m1,m2 = m2,m1 res.neg=not res.neg end
  118.     for i=#m1,1,-1 do
  119.       c=m1:byte(i)-m2:byte(i)+c
  120.       res.mant=c%10 .. res.mant
  121.       c=math.floor(c/10)
  122.     end
  123.     return correct(res)
  124.   end,
  125.   __mul=function(op1,op2)
  126.     if getmetatable(op1)~=m_table then op1=metanum(op1) end
  127.     if getmetatable(op2)~=m_table then op2=metanum(op2) end
  128.     local m1,m2=#op1.mant,#op2.mant
  129.     if m2>m1 then op1,op2 = op2,op1 m1,m2=m2,m1 end
  130.     local res=metanum()
  131.     local c=0
  132.     for i=1,m2-1 do
  133.       for j=1,i do
  134.         c=(op1.mant:byte(j-i-1) - 48)*(op2.mant:byte(-j) - 48)+c
  135.       end
  136.       res.mant=c%10 .. res.mant
  137.       c=math.floor(c/10)
  138.     end
  139.     for i=m2,m1 do
  140.       for j=1,m2 do
  141.         c=(op1.mant:byte(j-i-1) - 48)*(op2.mant:byte(-j) - 48)+c
  142.       end
  143.       res.mant=c%10 .. res.mant
  144.       c=math.floor(c/10)
  145.     end
  146.     for i=m1+1,m1+m2-1 do
  147.       for j=i-m1+1,m2 do
  148.         c=(op1.mant:byte(j-i-1) - 48)*(op2.mant:byte(-j) - 48)+c
  149.       end
  150.       res.mant=c%10 .. res.mant
  151.       c=math.floor(c/10)
  152.     end
  153.     res.mant= c .. res.mant
  154.     res.neg= op1.neg~=op2.neg
  155.     res.exp=op1.exp+op2.exp
  156.     return correct(res)
  157.   end,
  158.   __div=function(op1, op2)
  159.     return division(op1,op2,getmetatable(op1)==m_table and rawget(op1,"divprec"))
  160.   end,
  161.   __mod=function(op1, op2)
  162.     local res=division(op1,op2,0)
  163.     return op1-res*op2
  164.   end,
  165.   __pow=function(op1,op2)
  166.     op2=math.floor(op2)
  167.     if op2<0 then return metanum() end
  168.     if op2==0 then return metanum("1") end
  169.     if op2==1 then return metanum(op1) end
  170.     local res=op1^(op2/2)
  171.     if op2%2==0 then return res*res end
  172.     return res*res*op1
  173.   end,
  174.   __eq=function(op1,op2)  --  ==
  175.     if op1.neg ~= op2.neg  then return false end
  176.     if op1.mant~= op2.mant then return false end
  177.     if op1.exp ~= op2.exp  then return false end
  178.     return true
  179.   end,
  180.   __lt=function(op1,op2)  --  <
  181.     if op1.neg ~= op2.neg then return op1.neg end
  182.     local m1,m2=getMant(op1,op2)
  183.     for i=1,#m1 do
  184.       if m1:byte(i)<m2:byte(i) then return not op1.neg end
  185.       if m1:byte(i)>m2:byte(i) then return op1.neg end
  186.     end
  187.     return false
  188.   end,
  189.   __le=function(op1,op2)  --  <=
  190.     if op1.neg ~= op2.neg then return op1.neg end
  191.     local m1,m2=getMant(op1,op2)
  192.     for i=1,#m1 do
  193.       if m1:byte(i)<m2:byte(i) then return not op1.neg end
  194.       if m1:byte(i)>m2:byte(i) then return op1.neg end
  195.     end
  196.     return true
  197.   end,
  198.   __concat=function(op1, op2)
  199.     return tostring(op1)..tostring(op2)
  200.   end
  201. }
  202.  
  203. --Функция создает новое метачисло из числа, строки или другого метачисла
  204. function metanum(num,divprec)
  205.   if type(num)=="number" then
  206.     num=tostring(num)
  207.     local m,n=num:match("(.+)e(.+)")
  208.     if n then
  209.       num=metanum(m,divprec)
  210.       num.exp=num.exp+n
  211.       return num
  212.     end
  213.     return metanum(num,divprec)
  214.   end
  215.   local obj={neg=false, mant="", exp=0, divprec=divprec}
  216.   if getmetatable(num)==m_table then
  217.     obj.neg=num.neg obj.mant=num.mant obj.exp=num.exp
  218.   elseif type(num)=="string" then
  219.     local s=num:sub(1,1)
  220.     if s=="-" or s=="+" then num=num:sub(2) obj.neg=(s=="-") end
  221.     s,num=num:sub(1,1),num:sub(2)
  222.     while s>="0" and s<="9" do
  223.       obj.mant=obj.mant..s
  224.       s,num=num:sub(1,1),num:sub(2)
  225.     end
  226.     if s=="." then
  227.       s,num=num:sub(1,1),num:sub(2)
  228.       while s>="0" and s<="9" do
  229.         obj.mant=obj.mant..s
  230.         obj.exp=obj.exp-1
  231.         s,num=num:sub(1,1),num:sub(2)
  232.       end
  233.     end
  234.   end
  235.   setmetatable(obj,m_table)
  236.   return correct(obj)
  237. end
  238.  
  239. return metanum
RAW Paste Data