Creating a factoring calculator

Hey Codea,

I’m attempting to code a program that will factor polynomials. Firstly, though, I have created a program that finds the factors of the inputted number. If you know how to factor, this basically will be part of the “x-box” method. This is just the beginning of a big project I have in mind.

-- Factor

function setup()
    
        parameter.integer("n", 0, 300, 0) 
        parameter.action("Factors", factor)
        print("Factors of "..n.." are: ")
        factorsList = { }
end

function factor()
    
    i = 1
    while(i <= n) do
        if(n%i==0) then
            print(i)
            table.insert(factorsList, i)
        end
        i = i + 1
    end
    drawFactors = true
end

function draw()
   
    background(40, 40, 50)
    fill(172, 22, 22, 255)
    rect(WIDTH/4, HEIGHT/4, 500, 400)
    fill(255, 255, 255, 255)
    fontSize(50)
    text(n, WIDTH/2+50, 550)
    for i = 1, #factorsList do
        text(factorsList[i], WIDTH/2-50, HEIGHT/2)
    end
end

I would like to draw the factors in a list format (with commas and such), however, they are overlapping each other. Also, in the print command, I want to have it in pairs. I’m trying to do for i,v in pairs(factorsList) do print(i,v) end but v doesn’t have any value. Is there a way to have pairs with a single variable like i? So if the input was 10, the output (print and/or text) should read 1 10, 2 5.

@Zacharias Here’s your code modified a little.

-- Factor

function setup()
    parameter.integer("n", 0, 300, 0,factor) 
    factorsList = {}
end

function factor()
    factorsList = {}
    i = 1
    while(i <= n) do
        if(n%i==0) then
            table.insert(factorsList, i)
        end
        i = i + 1
    end
end

function draw()
    background(40, 40, 50)
    fill(172, 22, 22, 255)
    fontSize(25)
    text(n,WIDTH/2,HEIGHT-30)
    for i = 1, #factorsList do
        text(factorsList[i], WIDTH/2, HEIGHT-30*i-60)
    end
end

@dave1707 Thanks a lot! It serves its purpose well. Now I just have to figure how to use this with factoring polynomials. Should be fun :tongue:

@Zacharias - your loop only needs to run from 2 (1 is always a factor) to the square root of n. That should make it much faster.

For example, take the number 24. Loop from 2 to 4 (integer of square root of 5).

2 is a factor, so 12 must be as well (since 24/2=12).

3 is a factor, so 8 must be too.

4 is a factor, so 6 must be too

Factors are 1, 2, 3, 4, 6, 8, 12

NB if you are going to want to factor primes, there are faster methods.

@Zacharias I believe a good way to do is something I learned last year in math class, I don’t know if it is the best way or it even works, but search up the Gausse-Jordan method. It deals with matrices and solving and factoring polynomials. That’s how I know graphing calculators are able to do that.

@Ignatz I see what you mean. When I figure out how to do the rest of the factoring and what not, I’ll come back and optimize it.

@CamelCoder Thanks for the suggestion. It looks complex, and I’d like to use my already obtained knowledge of math for what I’m coding. Feel free to try it out, though.

[resolved]

[resolved]

Alright thanks @Ceres and @Ignatz. I’ll try and use those instructions and implement them into the code. Will update code here.

So I added pseudo code to clarify parts and added some aesthetic (not much) to the program. I’m confused with changing the loop to 2, math.sqrt(ac) and what to do inside, so I left it for later. Here is what I have:

-- Factor

function setup()
    --DISPLAY
    displayMode(STANDARD)
    supportedOrientations(LANDSCAPE_ANY)
    --PARAMETERS
    parameter.integer("a", 1, 1, 1)
    parameter.integer("b", 0, 50, 5)
    parameter.integer("c", 0, 50, 6)
    parameter.text("term", "x")
    parameter.action("Factor", factor)
    --LISTS
    factorsList = {}
    drawFactors = {}
    --VARIABLES
    local ac
    local factor1
    local factor2
end

function factor()
    
    ac = a * c
    factorsList = {} -- list of factors
    drawFactors = {} -- list of factors to draw
    
    --DETERMING FACTORS OF AC AND INPUTTING FACTORS IN A LIST
    i = 1
    while(i <= ac) do
        if(ac%i==0) then
            table.insert(factorsList, i)
            table.insert(drawFactors, i)
        end
        i = i + 1
    end
    --DETERMINING FACTORS FOR FACTORED FORM
    for i=1, #drawFactors do 
        factor1 = tonumber(factorsList[#factorsList])
        factor2 = tonumber(factorsList[#factorsList-(#factorsList-1)])
        
        if((factor1 + factor2 == b) and (factor1 * factor2 == ac)) then
            return
        else
            table.remove(factorsList, #factorsList)
            table.remove(factorsList, #factorsList-(#factorsList-1))
        end
    end
end

function draw()
    background(40, 40, 50)
    
    for i = 1, #drawFactors do
        --LIST OF FACTORS
        fill(236, 236, 236 , 255)
        fontSize(25)
        text("Factors of "..ac, WIDTH/2-225, HEIGHT/2+350)
        text(drawFactors[i], WIDTH/2-225, HEIGHT-30*i-60)
        --AC AND B IN X, AND POLYNOMIAL/FACTORED FORMS AT BOTTOM
        fill(172, 22, 22, 255)
        fontSize(50)
        text(""..ac.."", WIDTH/2, HEIGHT/2+75) --in the X 
        text(""..b.."", WIDTH/2, HEIGHT/2-75)  -- in the X
        fontSize(35)
        text(""..term.."^2 + "..b..term.." + "..c.."", WIDTH/2, HEIGHT/2-225) -- polynomial
        text("("..term.." + "..factor1..")("..term.." + "..factor2..")", WIDTH/2, HEIGHT/2-300) --factored
        --FACTORS FOR FACTORED FORM OF POLYNOMIAL
        fill(41, 184, 20, 255)
        fontSize(50)
        text(""..factor1.."", WIDTH/2-75, HEIGHT/2) -- in the X
        text(""..factor2.."", WIDTH/2+75, HEIGHT/2) -- in the X
    end
    
    --"POLYNOMIAL:/FACTORED:" AT BOTTOM
    fill(236, 236, 236, 255)
    fontSize(35)
    text("Your polynomial: ", WIDTH/2-225, HEIGHT/2-225)
    text("Factored: ", WIDTH/2-173, HEIGHT/2-300)
    --X LINES IN MIDDLE
    lineCapMode(ROUND)
    smooth()
    strokeWidth(7)
    line(WIDTH/2+100, HEIGHT/2+150, WIDTH/2-100, HEIGHT/2-150)
    line(WIDTH/2-100, HEIGHT/2+150, WIDTH/2+100, HEIGHT/2-150)
end

Going back to Ignatz’s idea, I’d go from 2 up to the square root on n, which gives you the first half of the factors, and then take that list and work backwards through it, dividing n by each factor, to get the second half of the list. Much, much faster than going all the way up to n if n is large.

I also need to do complex polynomial so where a > 1. That program can only solve simple polynomials.

I was sort of getting a headache from making my game, i really do hate 3D. I thought I would take a break and make my own version of this calculator. I added a UI just because I didn’t like the parameters. The UI is sort of bad because I in all spent about an hour on this program, so it will probably have errors. I would like to post the code, but I do not know how to post code on this forum. Can u please tell me what to do so I can post it

@CamelCoder To post code in the forum, just select all of your code then copy it. After that, just paste the code in the forum. Put 3 ~'s on a line before and after your code so it formats correctly.

That's what I did, but it said that my body (the post) had too many characters in it

@CamelCoder If your code is too big then you can post it in github, or split your code into 2 or more posts.

@CamelCoder - but there is little point in posting your code if it is rushed and has errors and doesn’t work properly. Why would we want to look at it?

Here’s an example that uses the quadratic equation to solve for X. Just change the X2, X1, X0 values and see the results as you change the values. An example is shown when the program is started.

EDIT: Corrected an error.

supportedOrientations(PORTRAIT_ANY)

function setup()
    fontSize(30)
    parameter.text("X2",0)
    parameter.text("X1",0)
    parameter.text("X0",0)
    X2=8    -- for example only
    X1=2
    X0=-15
end

function draw()
    background(0)
    fill(255)       
    X2=tonumber(X2)
    if X2=="" or X2==nil then
        X2=0
    end   
    strA=X2.."x^2"
    X1=tonumber(X1)
    if X1=="" or X1==nil then
        X1=0
    end   
    strB=X1.."x"
    if X1>=0 then
        strB="+"..X1.."x"
    end    
    X0=tonumber(X0)
    if X0=="" or X0==nil then
        X0=0
    end
    strC=X0
    if X0>=0 then
        strC="+"..X0
    end  
    solve()  
    text(strA..strB..strC.." = 0",WIDTH/2,HEIGHT-100)
    text(str1,WIDTH/2,HEIGHT-200)
    text(str2,WIDTH/2,HEIGHT-250)
end

function solve()
    if X2=="" or X1=="" or X0=="" then
        str1,str2="",""    
    else
        str1="x = "..(-X1+math.sqrt(X1*X1-4*X2*X0))/(2*X2)
        str2="x = "..(-X1-math.sqrt(X1*X1-4*X2*X0))/(2*X2)
    end
end

@Ignatz Although it was rushed, I found it to have few errors. I don’t know the cause because I didn’t look into it that much. It still works great for the most part. [Edit] I fixed the error it had, now I hope you think it is worth looking at.
Here is the first part:

--# Main
-- MyFactorCalc
displayMode(FULLSCREEN)
supportedOrientations(ANY)
function setup()
    vara,varb,varc,var=readLocalData("vara",1),readLocalData("varb",2),readLocalData("varc",1),""
    txt,bpos,cpos,counter="",readLocalData("bpos",true),readLocalData("cpos",true),readLocalData("counter",120)
    arrows={upArrow={},downArrow={}}
    for i=1,6 do table.insert(arrows.upArrow,Arrow{size=vec2(70,35)})
    table.insert(arrows.downArrow,Arrow{size=vec2(70,35),angle=180}) end
    factorButton=TextButton{text=string.upper("Factor"),pos=vec2(WIDTH/2,HEIGHT/8),
    fill=color(255, 0, 186, 255),font="Baskerville-Bold",fontSize=80}
    establishArrowFuncs()
end
function draw()
    var=string.char(counter)
    if cpos then varc=math.abs(varc) else varc=-math.abs(varc)end
    if bpos then varb=math.abs(varb) else varb=-math.abs(varb)end
    background(255, 255, 255, 255)
    textMode(CENTER)
    textAlign(CENTER)
    fill(0, 45, 255, 255)
    textWrapWidth(500)
    fontSize(30)
    text("Modify the variables, then click factor to factor the formula.",WIDTH/2,HEIGHT-100)
    fontSize(50)
    font("AmericanTypewriter-Bold")
    fill(0)
    local s=getString(vara,varb,varc,var)
    text(s,WIDTH/2,HEIGHT*3/4)
    textWrapWidth(-1)
    text(txt,WIDTH/2,HEIGHT/4)
    drawSelections()
    for i,v in pairs(arrows) do
        for n,m in ipairs(v) do
            if m.counter and m.counter>1.2 then
                if math.floor(m.counter*25)%2==0 then m.func(BEGAN) end
            end
        end
    end
    factorButton:draw()
end
function factor(a,b,c)
    txt="Factoring..."
    local signb,signc=sign(b),sign(c)
    local mm=math.greatestCommonFactor(a,b)
    local m=math.abs(math.greatestCommonFactor(mm,c))
    a,b,c=a/m,b/m,c/m
    local ac=a*c
    local factors=math.findAllFactors(ac)
    local sol
    for i,v in ipairs(factors) do
        for n,m in ipairs(factors) do
            if i~=n or #factors==1 then
                if m+v==b and m*v==ac then
                    sol=vec2(m,v)
                elseif -m+v==b and -m*v==ac then
                    sol=vec2(-m,v)
                elseif m-v==b and m*-v==ac then
                    sol=vec2(m,-v)
                elseif -m-v==b and -m*-v==ac then
                    sol=vec2(-m,-v)
                end
            end
        end
    end
    if sol then
        local function func(a,b,sol,s)
            local gcf=math.greatestCommonFactor
            local aa=gcf(a,sol.x)
            local bb=gcf(b,sol.y)
            local cc=gcf(a,sol.y)
            local dd=gcf(b,sol.x)
            local ab=math.abs(aa)+math.abs(bb)
            local cd=math.abs(cc)+math.abs(dd)
            local soll=vec2(aa,bb)
            if not s or s==1 then
            if ab<cd and sign(cc)~=-1 or sign(aa)==-1 then soll=vec2(cc,dd) end elseif s==2 then
            soll=vec2(cc,dd) end
            return soll:unpack()
        end
        local tt
        ::place::
        local aa,cc=func(a,c,vec2(sol.y,sol.x),tt)
        local f=a/aa
        local ff=sol.x/aa
        if cc*ff~=c and ff==-1 then
            ff=2
        end
        if sign(aa)==-1 then
            aa=-aa
            f=-f
            ff=-ff
        end
        if not (aa*f==a and (cc*f)+(aa*ff)==b and cc*ff==c) then
            tt=2
            goto place
        end
        txt=getFactoredString(aa,cc,f,ff,m)
    else txt="NOT FACTORABLE"
        if m~=1 then
            txt=tostring(m).."("..getString(a,b,c,var)..")"
        end
    end
end

function drawSelections()
    local tab={
    {txt="a",tag="math.floor(vara)"},{txt="+/-",tag="stringSign(varb)"},
    {txt="b",tag="math.floor(math.abs(varb))"},{txt="+/-",tag="stringSign(varc)"},
    {txt="c",tag="math.floor(math.abs(varc))"},{txt="var.",tag="var"}}
    for i,v in ipairs(tab) do
        pushStyle()
        local a=color(255,0,0)
        if i==2 or i==3 then a=color(0,0,255) 
        elseif i==4 or i==5 then a=color(67, 255, 0, 255) 
        elseif i==6 then a=color(0, 255, 223, 255) end
        fill(0)
        fontSize(30)
        textMode(CENTER)
        local pos=vec2(WIDTH*i/(#tab+1),HEIGHT/2+150)
        text(tostring(v.txt),pos:unpack())
        pos.y=HEIGHT/2-30
        fill(a)
        rectMode(CENTER)
        rect(pos.x,pos.y,100,100)
        fill(255)
        font("Baskerville-SemiBold")
        fontSize(55)
        text(tostring(loadstring("return "..v.tag)()),pos:unpack())
        arrows.upArrow[i].pos.x=pos.x
        arrows.upArrow[i].pos.y=pos.y+90
        arrows.upArrow[i]:draw()
        arrows.downArrow[i].pos.x=pos.x
        arrows.downArrow[i].pos.y=pos.y-90
        arrows.downArrow[i]:draw()
        popStyle()
    end
end

function establishArrowFuncs()
    local s=ENDED
    arrows.upArrow[1].func=function(a)if a==s then vara=vara+1;saveLocalData("vara",vara) end end
    arrows.downArrow[1].func=function(a)if a==s then vara=math.max(vara-1,1)saveLocalData("vara",vara) end end
    arrows.upArrow[2].func=function(a)if a==s then bpos=not bpos;saveLocalData("bpos",bpos) end end
    arrows.downArrow[2].func=function(a)if a==s then bpos=not bpos;saveLocalData("bpos",bpos) end end
    arrows.upArrow[3].func=function(a)if a==s then varb=math.abs(varb)+1;saveLocalData("varb",varb) end end
    arrows.downArrow[3].func=function(a)if a==s then varb=math.max(math.abs(varb)-1,0)saveLocalData("varb",varb) end end
    arrows.upArrow[4].func=function(a)if a==s then cpos=not cpos;saveLocalData("cpos",cpos) end end
    arrows.downArrow[4].func=function(a)if a==s then cpos=not cpos;saveLocalData("cpos",cpos) end end
    arrows.upArrow[5].func=function(a)if a==s then varc=math.abs(varc)+1;saveLocalData("varc",varc) end end
    arrows.downArrow[5].func=function(a)if a==s then varc=math.max(math.abs(varc)-1,0)saveLocalData("varc",varc) end end
    arrows.downArrow[6].func=function(a)if a==s then
            counter=counter+1
            if counter>122 then counter=97 end
            saveLocalData("counter",counter)
        end
    end
    arrows.upArrow[6].func=function(a)if a==s then
            counter=counter-1
            if counter<97 then counter=122 end
            saveLocalData("counter",counter)
        end
    end
end

function touched(t)
    for i,v in pairs(arrows) do
        for n,m in ipairs(v) do
            if m:touched(t) then txt="" end
        end
    end
    factorButton:touched(t)
    if factorButton.state==ENDED then
        factor(vara,varb,varc)
    end
end

function orientationChanged(o)
    if factorButton then factorButton.pos=vec2(WIDTH/2,HEIGHT/8) end
end

Here is the second part:

--# Math
function math.findAllFactors(n)
    local factors={}
    for i=1,math.abs(n) do
        local a=n/i
        if a==math.floor(a) then
            table.insert(factors,math.floor(i*sign(a)))
            if a==i then
                table.insert(factors,math.floor(i*sign(a)))
            end
        end
    end
    return factors
end

function math.greatestCommonFactor(a,b)
    local ans=1
    for i,v in pairs(math.findAllFactors(a)) do
        for n,m in pairs(math.findAllFactors(b)) do
            if v==m or -v==-m then
                ans=v
            elseif -v==m or v==-m then
                ans=-v
            end
        end
    end
    return ans
end

function sign(x)
    if x==x and x~=0 then
        return x/math.abs(x)
    end
    return(0)
end
--# String
function stringSign(a) if a<0 then return "-" end return "+" end

function getString(a,b,c,x)
    local space=string.rep(" ",2)
    local s=""
    local function concatVals(n,pow)
        if n~=0 then
            local a=tostring(math.floor(math.abs(n)))
            local p=""
            local t=stringSign(n)
            if pow>=1 then
            if a=="1" then a="" end
            p=x if pow==2 then p=p.."²"t="" end end
            s=s..t..space..a..p..space
        end
    end
    concatVals(a,2)
    concatVals(b,1)
    concatVals(c,0)
    return s
end

function getFactoredString(fo,fi,li,lo,m)
    local space=string.rep(" ",2)
    local s=""
    if m~=1 then s=s..tostring(m) end
    if fi~=0 then s=s.."(" end
    if fo~=1 then s=s..tostring(math.floor(fo)) end
    s=s..var
    if fi~=0 then s=s..space..stringSign(fi)..space..tostring(math.floor(math.abs(fi)))..")" end
    if lo~=0 then s=s.."(" end
    if li~=1 then s=s..tostring(math.floor(li)) end
    s=s..var
    if lo~=0 then s=s..space..stringSign(lo)..space..tostring(math.floor(math.abs(lo)))..")" end
    if string.sub(s,1+string.len(m),math.ceil(string.len(s)/2))
        ==string.sub(s,math.ceil(string.len(s)/2)+1,string.len(s)) then
        s=string.sub(s,1,math.ceil(string.len(s)/2)).."²"
    end
    return s
end
--# Arrow
Arrow=class()

function Arrow:init(args)args=args or {}
    self.strokeWidth=args.strokeWidth or 20
    self.stroke=args.stroke or color(0, 0, 0, 255)
    self.ang=args.angle or 0
    self.size=args.size or vec2(200,200)
    self.pos=args.pos or vec2(0,0)
    self.lineCapMode=args.lineCapMode or ROUND
    self.func=args.func or function()end
    self.counter=nil
end

function Arrow:draw()
    pushMatrix()
    pushStyle()
    translate(self.pos.x,self.pos.y)
    rotate(self.ang)
    lineCapMode(self.lineCapMode)
    strokeWidth(self.strokeWidth)
    stroke(self.stroke)
    for i=-1,1,2 do
        line(self.size.x/2*i,-self.size.y/2,0,self.size.y/2)
    end
    if self.counter then self.counter = self.counter + DeltaTime end
    popStyle()
    popMatrix()
end

function Arrow:touched(touch)
    local shift=vec2(0,10)
    if touch.x>self.pos.x-self.size.x/2-shift.x and touch.y>self.pos.y-self.size.y/2-shift.y and touch.x<self.pos.x+self.size.x/2+shift.x
        and touch.y<self.pos.y+self.size.y/2+shift.y then
        if touch.state==BEGAN then self.counter=0 elseif touch.state==ENDED then self.counter=nil end
        self.func(touch.state)
        return true
    end
    return false
end
--# TextButton
TextButton=class()

function TextButton:init(args)args=args or {}
    self.font=args.font or nil
    self.fontSize=args.fontSize or 50
    self.fill=args.fill or color(0)
    self.txt=args.text or ""
    self.pos=args.pos or vec2(0,0)
    self.w,self.h=0,0
end

function TextButton:draw()
    pushStyle()
    textMode(CENTER)
    textAlign(CENTER)
    fill(self.fill)
    if self.font then font(self.font) end
    fontSize(self.fontSize)
    self.w,self.h=textSize(self.txt)
    text(self.txt,self.pos:unpack())
    popStyle()
end

function TextButton:touched(t)
    if t.x>self.pos.x-self.w/2 and t.x<self.pos.x+self.w/2 and t.y>self.pos.y-self.h/2 and t.y<self.pos.y+self.h/2 then
        self.state=t.state
    else
        self.state=nil
    end
end