Whitemambaa

Arbitrary Degree Polynomial Root Finder (real numbers only)

Aug 8th, 2014
354
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 15.01 KB | None | 0 0
  1. --By xXxMoNkEyMaNxXx
  2. local select=select
  3.  
  4. local abs=math.abs
  5. local sqrt=math.sqrt
  6. local cos,sin=math.cos,math.sin
  7. local atan2=math.atan2
  8.  
  9. local sort=table.sort
  10. local remove=table.remove
  11. local unpack=unpack or table.unpack
  12.  
  13. local sign=function(x)
  14.     if x==0 then
  15.         return 0
  16.     elseif x>0 then
  17.         return 1
  18.     else
  19.         return -1
  20.     end
  21. end
  22.  
  23. local function cpow(re,im,p) --Raise a complex number to an arbitrary real power
  24.     local magnitude=(re*re+im*im)^(p/2)
  25.     local theta=atan2(im,re)*p
  26.     return magnitude*cos(theta),magnitude*sin(theta)
  27. end
  28.  
  29. local function csqrt(re,im) -- Square root of a complex number with no trig! :>
  30.     if im and im~=0 then
  31.         local m=sqrt(re*re+im*im)
  32.         return sqrt((m+re)/2),re>=0 and (sqrt(m+im)-sqrt(m-im))/2 or im>=0 and (sqrt(m+im)+sqrt(m-im))/2 or (sqrt(m+im)+sqrt(m-im))/-2 --shut up this is the fastest way
  33.     else
  34.         return re>0 and sqrt(re) or 0,re<0 and sqrt(-re) or 0
  35.     end
  36. end
  37.  
  38. local function cbrt(x) --wow. just wow.
  39.     return x<0 and -(-x)^(1/3) or x^(1/3)
  40. end
  41.  
  42. local acc=1e-10 --floating point error tolerance
  43. local r3=0.75^0.5 -- common constant
  44. local function Zeroes(a0,a1,a2,a3,a4)
  45.     local z={}
  46.     if not a4 or abs(a4)<acc then
  47.         if not a3 or abs(a3)<acc then
  48.             if not a2 or abs(a2)<acc then
  49.                 z[1]=-a0/a1
  50.             else
  51.                 local p1=a1/(-2*a2)
  52.                 local p2=a1*a1-4*a0*a2
  53.                 if p2>acc then
  54.                     local p3=sqrt(p2)/(2*a2)
  55.                     if p3>0 then
  56.                         z[1]=p1-p3
  57.                         z[2]=p1+p3
  58.                     else
  59.                         z[1]=p1+p3
  60.                         z[2]=p1-p3
  61.                     end
  62.                 elseif p2>=0 then
  63.                     z[1]=p1
  64.                 end
  65.             end
  66.         else -- No guarantees for sorting from now on
  67.             local p1=a2*a2-3*a3*a1
  68.             local p2=a2*a2*a2-4.5*a3*a2*a1+13.5*a3*a3*a0
  69.             local p3=p2*p2-p1*p1*p1
  70.             if abs(p3)<acc then
  71.                 if abs(p1)<acc then
  72.                     z[1]=a2/(-3*a3)
  73.                 else
  74.                     z[1]=(9*a3*a0-a2*a1)/(2*p1)
  75.                     z[2]=(4*a2*a1-9*a3*a0-a2*a2*a2/a3)/p1
  76.                 end
  77.             else
  78.                 local p5r,p5i
  79.                 local p6r,p6i
  80.                 if p3<0 then
  81.                     local p4i=sqrt(-p3)
  82.                     p5r,p5i=cpow(p2,p4i,1/3)
  83.                     p6r,p6i=cpow(p2,-p4i,1/3)
  84.                 else
  85.                     local p4=sqrt(p3)
  86.                     p5r,p5i=cbrt(p2+p4),0
  87.                     p6r,p6i=cbrt(p2-p4),0
  88.                 end
  89.                 local z1i,z2i,z3i=(p5i+p6i)/(-3*a3),((p5r-p6r)*r3-(p5i+p6i)/2)/(-3*a3),((p6r-p5r)*r3-(p5i+p6i)/2)/(-3*a3)
  90.                 if abs(z1i)<acc then
  91.                     z[#z+1]=(a2+p5r+p6r)/(-3*a3)
  92.                 end
  93.                 if abs(z2i)<acc then -- wow I actually have to do this?????? ok FPU
  94.                     z[#z+1]=(a2-(p5r+p6r)/2+(p6i-p5i)*r3)/(-3*a3)
  95.                 end
  96.                 if abs(z3i)<acc then
  97.                     z[#z+1]=(a2-(p5r+p6r)/2+(p5i-p6i)*r3)/(-3*a3)
  98.                 end
  99.             end
  100.         end
  101.     else
  102.         local p1=a2*a2-3*a3*a1+12*a4*a0
  103.         local p2=a2*a2*a2-4.5*a3*a2*a1+13.5*(a3*a3*a0+a4*a1*a1)-36*a4*a2*a0
  104.         local p3=2*a2/a4-3*a3*a3/(4*a4*a4)
  105.         local p4=(a3*a3*a3-4*a4*a3*a2+8*a4*a4*a1)/(4*a4*a4*a4)
  106.         local p5=p2*p2-p1*p1*p1
  107.         --print(p1,p2,p3,p4,p5)
  108.         local p6
  109.         if abs(p1)<acc then
  110.             p6=cbrt(2*p2)
  111.         elseif p5<0 then
  112.             p6=2*(p2*p2-p5)^(1/6)*cos(atan2(sqrt(-p5),p2)/3) -- r u mad
  113.         else
  114.             local p5r=sqrt(p5)
  115.             p6=cbrt(p2+p5r)+cbrt(p2-p5r)
  116.         end
  117.         local p7=(p6/a4-p3)/3
  118.         if p7<-acc then --This part does not seem to ever run, probably needs more imagination
  119.             local p7i=sqrt(-p7)
  120.             local p8r,p8i=csqrt(-p7-p3,p4*p7i)
  121.             --local p9r,p9i=csqrt(-p7-p3,p4*p7i)
  122.             if abs(-p8i-p7i)/2<acc then
  123.                 z[#z+1]=a3/(-4*a4)+p8r/2
  124.             end
  125.             if abs(p8i-p7i)/2<acc then
  126.                 z[#z+1]=a3/(-4*a4)-p8r/2
  127.             end
  128.             if abs(p7i+p8i)/2<acc then
  129.                 z[#z+1]=a3/(-4*a4)+p8r/2
  130.             end
  131.             if abs(p7i-p8i)/2<acc then
  132.                 z[#z+1]=a3/(-4*a4)-p8r/2
  133.             end
  134.         elseif p7>acc then
  135.             local p7r=sqrt(p7)
  136.             local p8=p4/p7r-p7-p3
  137.             local p9=-p4/p7r-p7-p3
  138.             if p8>acc then
  139.                 local p8r=sqrt(p8)/2
  140.                 z[#z+1]=a3/(-4*a4)-p7r/2+p8r
  141.                 z[#z+1]=a3/(-4*a4)-p7r/2-p8r
  142.             elseif p8>=0 then
  143.                 z[#z+1]=a3/(-4*a4)-p7r/2
  144.             end
  145.             if p9>acc then
  146.                 local p9r=sqrt(p9)/2
  147.                 z[#z+1]=a3/(-4*a4)+p7r/2+p9r
  148.                 z[#z+1]=a3/(-4*a4)+p7r/2-p9r
  149.             elseif p9>=0 then
  150.                 z[#z+1]=a3/(-4*a4)+p7r/2
  151.             end
  152.         else
  153.             local p1=a2/(-2*a4)
  154.             local p2=a2*a2-4*a0*a4
  155.             if p2>acc then
  156.                 local p3=sqrt(p2)/(2*a4)
  157.                 local p4=p1+p3
  158.                 local p5=p1-p3
  159.                 if p4>acc then
  160.                     local p4r=sqrt(p4)
  161.                     z[#z+1]=p4r
  162.                     z[#z+1]=-p4r
  163.                 elseif p4>=0 then
  164.                     z[#z+1]=0
  165.                 end
  166.                 if p5>acc then
  167.                     local p5r=sqrt(p5)
  168.                     z[#z+1]=p5r
  169.                     z[#z+1]=-p5r
  170.                 elseif p5>=0 then
  171.                     z[#z+1]=0
  172.                 end
  173.             elseif abs(p2)<acc then
  174.                 if p1>acc then
  175.                     local p1r=sqrt(p1)
  176.                     z[#z+1]=p1r
  177.                     z[#z+1]=-p1r
  178.                 elseif p1>=0 then
  179.                     z[#z+1]=0
  180.                 end
  181.             end
  182.         end
  183.     end
  184.     --sort(z)
  185.     return z
  186. end
  187.  
  188. local function dumpPoly(coefficients)
  189.     local s=""
  190.     for i=0,#coefficients do
  191.         local v=coefficients[i]
  192.         if v~=0 then
  193.             s=s..((v<0 or #s==0) and "" or "+")..v..(i==0 and "" or i==1 and " x" or " x^"..(i>9 and "{"..i.."}" or i))
  194.         end
  195.     end
  196.     return (s:gsub("e(%-?%d%d%d)"," 10^{%1}"))
  197. end
  198.  
  199. local function Evaluate(coefficients,x)
  200.     local Result=coefficients[0]
  201.     local Multiplier=1
  202.     for Coefficient=1,#coefficients do
  203.         Multiplier=Multiplier*x
  204.         Result=Result+coefficients[Coefficient]*Multiplier
  205.     end
  206.     return Result
  207. end
  208.  
  209. local function EvaluateWithDerivative(coefficients,x)
  210.     local Result,Derivative=coefficients[0],0
  211.     local Multiplier=1
  212.     for Coefficient=1,#coefficients do
  213.         local c=coefficients[Coefficient]
  214.         Derivative=Derivative+Coefficient*c*Multiplier
  215.         Multiplier=Multiplier*x
  216.         Result=Result+c*Multiplier
  217.     end
  218.     return Result,Derivative
  219. end
  220.  
  221. local flag=false
  222. --Searches for 0 starting from <value> in the direction of <interval>.  Pass unBounded as true if the zero may be out of the interval.  This is pretty slow compared to the other functions...
  223. local function BinarySearch(coefficients,value,interval,unBounded)
  224.     local LastResult=Evaluate(coefficients,value)
  225.     local InitialSign=sign(LastResult)
  226.     local Guess=0.5
  227.     local Multiplier=0.5
  228.     for i=1,75 do
  229.         local Result=Evaluate(coefficients,value+Guess*interval)
  230.         local ResultSign=sign(Result)
  231.         if ResultSign==0 or Result==LastResult then
  232.             return value+Guess*interval,Result
  233.         else
  234.             if unBounded then
  235.                 Guess=Guess+InitialSign*ResultSign*Multiplier
  236.                 if InitialSign==ResultSign then
  237.                     Multiplier=Multiplier*2
  238.                 else
  239.                     unBounded=false
  240.                 end
  241.             else
  242.                 Multiplier=Multiplier/2
  243.                 Guess=Guess+InitialSign*ResultSign*Multiplier
  244.             end
  245.             LastResult=Result
  246.         end
  247.     end
  248.     print("What the hell?",unBounded,value,unBounded and interval or value+interval,value+Guess*interval,LastResult,dumpPoly(coefficients))
  249.     flag=true
  250.     return value+Guess*interval,LastResult
  251. end
  252.  
  253. local function IntervalSearch(coefficients,lowerBound,upperBound)
  254.     local LowerResult,UpperResult=Evaluate(coefficients,lowerBound),Evaluate(coefficients,upperBound)
  255.     local AverageSlope=(UpperResult-LowerResult)/(upperBound-lowerBound)
  256.     local LastGuess=lowerBound-LowerResult/AverageSlope
  257.     local LastResult,LastDerivative=EvaluateWithDerivative(coefficients,LastGuess)
  258.     for i=1,25 do
  259.         local Guess=LastGuess-LastResult/LastDerivative
  260.         if Guess<=lowerBound or upperBound<=Guess then
  261.             Guess=LastGuess-LastResult/AverageSlope -- Should be guaranteed to move the guess towards the right answer...
  262.         end
  263.         local Result,Derivative=EvaluateWithDerivative(coefficients,Guess)
  264.         if Result==0 or Result==LastResult then
  265.             return Guess,Result
  266.         else
  267.             LastGuess,LastResult,LastDerivative=Guess,Result,Derivative
  268.         end
  269.     end
  270.     --print("Only the worst polynomials have to use this code.",#coefficients)
  271.     return BinarySearch(coefficients,lowerBound,upperBound-lowerBound)
  272. end
  273.  
  274. --[[ It was such a good idea though :c
  275. --side = 1:Integrate[D[a x^n, x] Exp[x0 - x], {x, x0, Infinity}]/Integrate[Exp[x0 - x], {x, x0, Infinity}]
  276. --side =-1:Integrate[D[a x^n, x] Exp[x - x0], {x,-Infinity, x0}]/Integrate[Exp[x - x0], {x,-Infinity, x0}]
  277. local function GetWeightedSlopeIntegral(coefficients,x0,side)
  278.     local WeightedSlope=coefficients[1]
  279.     local GammaCoefficients={1}
  280.     for d=2,#coefficients do
  281.         local GammaResult=0
  282.         local Multiplier=1
  283.         local d1=d-1
  284.         for i=1,d1 do
  285.             local gci=GammaCoefficients[i]*d1*side
  286.             GammaCoefficients[i]=gci
  287.             GammaResult=GammaResult+gci*Multiplier
  288.             Multiplier=Multiplier*x0
  289.         end
  290.         GammaCoefficients[d]=1
  291.         WeightedSlope=WeightedSlope+d*coefficients[d]*(GammaResult+Multiplier)
  292.     end
  293.     return WeightedSlope
  294. end
  295.  
  296. local function OneSidedSearch(coefficients,bound,side)
  297.     local BoundResult=Evaluate(coefficients,bound)
  298.     local WeightedSlope=GetWeightedSlopeIntegral(coefficients,bound,side)
  299.     local LastGuess=bound-BoundResult/WeightedSlope
  300.     local LastResult,LastDerivative=EvaluateWithDerivative(coefficients,LastGuess)
  301.     for i=1,30 do
  302.         local Guess=LastGuess-LastResult/LastDerivative
  303.         if (Guess-bound)/side<0 then
  304.             Guess=LastGuess-LastResult/WeightedSlope -- Not sure if it will be in bounds for certain...
  305.             if (Guess-bound)/side<0 then
  306.                 print("Your polynomial is truly ugly.")
  307.                 return BinarySearch(coefficients,bound,side,true)
  308.             end
  309.         end
  310.         local Result,Derivative=EvaluateWithDerivative(coefficients,Guess)
  311.         if Result==0 or Result==LastResult then
  312.             return Guess,Result
  313.         else
  314.             LastGuess,LastResult,LastDerivative=Guess,Result,Derivative
  315.         end
  316.     end
  317.     return BinarySearch(coefficients,bound,side,true)
  318. end
  319. --]]
  320.  
  321. local function OneSidedSearch(coefficients,bound,side)
  322.     local LastGuess=bound+side*abs(Evaluate(coefficients,bound)/coefficients[#coefficients])^(1/#coefficients)
  323.     local LastResult,LastDerivative=EvaluateWithDerivative(coefficients,LastGuess)
  324.     for i=1,25 do
  325.         local Guess=LastGuess-LastResult/LastDerivative
  326.         if (Guess-bound)/side<0 then
  327.             Guess=LastGuess-LastResult/side -- Not sure if it will be in bounds for certain...
  328.             if (Guess-bound)/side<0 then
  329.                 --print("Your polynomial is truly ugly.",#coefficients)
  330.                 return BinarySearch(coefficients,bound,side,true)
  331.             end
  332.         end
  333.         local Result,Derivative=EvaluateWithDerivative(coefficients,Guess)
  334.         if Result==0 or Result==LastResult then
  335.             return Guess,Result
  336.         else
  337.             LastGuess,LastResult,LastDerivative=Guess,Result,Derivative
  338.         end
  339.     end
  340.     --print("Your polynomial sucks.",#coefficients)
  341.     return BinarySearch(coefficients,bound,side,true)
  342. end
  343.  
  344. local function FindRoots(...) --Found roots are guaranteed to be in ascending order.
  345.     local Coefficients={[0]=(...),select(2,...)}
  346.     local Degree=#Coefficients
  347.     while Coefficients[Degree]==0 do
  348.         remove(Coefficients)
  349.         Degree=Degree-1
  350.     end
  351.     if Degree>4 then
  352.         local DifferentiatedCoefficients={}
  353.         for Coefficient=1,Degree do
  354.             DifferentiatedCoefficients[Coefficient-1]=Coefficient*Coefficients[Coefficient]
  355.         end
  356.         local Roots={}
  357.         local Extremities=FindRoots(unpack(DifferentiatedCoefficients,0))
  358.         if #Extremities>0 then
  359.             local LeadingCoefficient=Coefficients[Degree]
  360.             local LastExtremity=Extremities[1]
  361.             local LastExtremityResult=Evaluate(Coefficients,LastExtremity)
  362.             if LastExtremityResult==0 then
  363.                 Roots[#Roots+1]=LastExtremity
  364.             elseif LastExtremityResult*LeadingCoefficient*(1-Degree%2*2)<0 then
  365.                 Roots[#Roots+1]=OneSidedSearch(Coefficients,LastExtremity,-1)
  366.             end
  367.             for ExtremityIndex=2,#Extremities do
  368.                 local Extremity=Extremities[ExtremityIndex]
  369.                 local ExtremityResult=Evaluate(Coefficients,Extremity)
  370.                 if ExtremityResult==0 then
  371.                     Roots[#Roots+1]=Extremity
  372.                 elseif ExtremityResult*LastExtremityResult<0 then
  373.                     Roots[#Roots+1]=IntervalSearch(Coefficients,LastExtremity,Extremity)
  374.                 end
  375.                 LastExtremity=Extremity
  376.                 LastExtremityResult=ExtremityResult
  377.             end
  378.             if LastExtremityResult*LeadingCoefficient<0 then
  379.                 Roots[#Roots+1]=OneSidedSearch(Coefficients,LastExtremity,1)
  380.             end
  381.         else--if Degree%2==1 then -- Degree cannot be even if there are no extremities - this test is useless
  382.             local Intercept=Coefficients[0]
  383.             if Intercept==0 then
  384.                 Roots[#Roots+1]=0
  385.             else
  386.                 Roots[#Roots+1]=OneSidedSearch(Coefficients,0,-sign(Intercept)*sign(Coefficients[Degree]))
  387.             end
  388.         end
  389.         return Roots
  390.     elseif Degree>2 then
  391.         local Roots=Zeroes(...)
  392.         sort(Roots)
  393.         return Roots
  394.     elseif Degree>0 then
  395.         return Zeroes(...)
  396.     end
  397. end
  398.  
  399. --[[
  400. do
  401.     local c={}
  402.     local f=1
  403.     for i=1,100,2 do
  404.         c[i]=f
  405.         c[i-1]=0
  406.         f=-f/((i+1)*(i+2))
  407.     end
  408.     print(dumpPoly(c))
  409.     print(unpack(FindRoots(unpack(c,0))))
  410. end
  411.  
  412. print(unpack(FindRoots(0,0,3,-2)))
  413. --]]
  414.  
  415. --[[ sad time testing -- took a long time to find little mistakes like forgot to multiply by 2
  416. print(unpack(Zeroes(6,5,4,3,-2)))
  417. print(unpack(Zeroes(0.5,5,-6,-1,2)))
  418. print(unpack(Zeroes(2,0,-3,0,3)))
  419. print(unpack(Zeroes(4.1,-1,-4,-4.6,5)))
  420. print(unpack(Zeroes(-1,0,0,0,1)))
  421. print(unpack(Zeroes(1.9208,-6.86,8.82,-4.9,1)))
  422. print(unpack(Zeroes(0.5,5,-2.8,-3.5,2)))
  423. print(unpack(Zeroes(6,5,4,3)))
  424. print(unpack(Zeroes(5,4,3)))
  425. print(unpack(Zeroes(4,3)))
  426. --]]
  427.  
  428. local tau=2*math.pi
  429. local log=math.log
  430.  
  431. math.randomseed(os.time())
  432. math.random()
  433.  
  434. local rand=math.random
  435.  
  436. local s
  437. local function r()
  438.     if s then
  439.         local r=s
  440.         s=nil
  441.         return r
  442.     else
  443.         local theta=tau*rand()
  444.         local r2
  445.         repeat r2=rand() until r2~=0
  446.         local magnitude=log(r2)
  447.         local r
  448.         r,s=magnitude*cos(theta),magnitude*sin(theta)
  449.         return r
  450.     end
  451. end
  452.  
  453. --[[
  454. do
  455.     local c={}
  456.     local s=""
  457.     for i=0,100 do
  458.         local v=r()
  459.         c[i]=v
  460.         s=s..((v<0 or i==0) and "" or "+")..v..(i==0 and "" or i==1 and " x" or " x^"..(i>9 and "{"..i.."}" or i))
  461.     end
  462.     print(s)
  463.     print(unpack(FindRoots(unpack(c,0))))
  464. end
  465. --]]
  466.  
  467. --[[
  468. for d=1,100 do
  469.     local c={}
  470.     local s=""
  471.     for i=0,d do
  472.         local v=r()
  473.         c[i]=v
  474.         s=s..((v<0 or i==0) and "" or "+")..v..(i==0 and "" or i==1 and " x" or " x^"..(i>9 and "{"..i.."}" or i))
  475.     end
  476.     print(s)
  477.     print(unpack(FindRoots(unpack(c,0))))
  478. end
  479. --]]
  480.  
  481. ---[[
  482. local n=100000
  483. local n2=1000000
  484. local rtime
  485. do
  486.     local t0=os.clock()
  487.     for i=1,n2 do
  488.         r()
  489.     end
  490.     local t1=os.clock()
  491.     rtime=(t1-t0)/n2
  492.     print(rtime)
  493. end
  494. for d=1,10 do
  495.     local t0=os.clock()
  496.     local q=1+math.floor(n*rand())
  497.     for _=1,n do
  498.         local c={}
  499.         for i=0,d do
  500.             c[i]=r()
  501.         end
  502.         local Roots=FindRoots(unpack(c,0))
  503.         if flag or _==q then
  504.             print(dumpPoly(c))
  505.             print(unpack(Roots))
  506.             flag=false
  507.         end
  508.     end
  509.     local t1=os.clock()
  510.     print(d,1/((t1-t0)/n-(d+1)*rtime))
  511. end
  512. --]]
  513.  
  514. --[[ Extra cases that were handled by a more general block of code
  515. elseif p3>0 then
  516.     local p4=sqrt(p3)/2
  517.     local p5=p2+p4
  518.     local p6=p2-p4
  519.     local c1,c2=p5>=0,p6>=0
  520.     if c1 and c2 then
  521.         --uhhh they'll always be imaginary... ok
  522.         local p7,p8=p5^(1/3),p6^(1/3)
  523.         z[1]=(a2+p7+p8)/(-3*a3)
  524.         print(r3*(p7-p8)/(-3*a3),r3*(p8-p7)/(-3*a3))--imaginary parts should not be zero
  525.     elseif c2 then
  526.         local p7r,p7i=cpow(p5,0,1/3)
  527.         local p8=p6^(1/3)
  528.         local z1r,z1i=(a2+p7r+p8)/(-3*a3),p7i/(-3*a3) -- not zero o.o
  529.         local z2r,z2i=(a2-(p7r+p8)/2-p7i*r3)/(-3*a3),((p7r-p8)*r3-p7i/2)/(-3*a3)
  530.         local z3r,z3i=(a2-(p7r+p8)/2+p7i*r3)/(-3*a3),((p8-p7r)*r3-p7i/2)/(-3*a3)
  531.         print(z1i,z2i,z3i)
  532.     elseif c1 then
  533.         local p7=p5^(1/3)
  534.         local p8r,p8i=cpow(p5,0,1/3)
  535.         local z1r,z1i=(a2+p7+p8r)/(-3*a3),p8i/(-3*a3)
  536.     else
  537.         --
  538.     end
  539.     local p4=sqrt(p3)/2
  540.     local p5r,p5i=cpow(p2+p4,0,1/3)
  541.     local p6r,p6i=cpow(p2-p4,0,1/3)
  542.     z[1]=(a2+p5r+p6r)/(-3*a3)
  543.     z[2]=(a2-(p5r+p6r)/2+(p6i-p5i)*r3)/(-3*a3)
  544.     z[3]=(a2-(p5r+p6r)/2+(p5i-p6i)*r3)/(-3*a3)
  545.     print((p5i+p6i)/(-3*a3),((p5r-p6r)*r3-(p5i+p6i)/2)/(-3*a3),((p6r-p5r)*r3-(p5i+p6i)/2)/(-3*a3))
  546. --]]
Advertisement
Add Comment
Please, Sign In to add comment