local m=(require(script.Parent.Parent.Parent.Llama)).List local B=require(script.Parent.Monomial)local D={}local function X(B)local X=m.sort(B,function(m,B)return B>m end)local h={X[1]}for m=2,#B,1 do if X[m-1]==X[m]then h[#h]=h[#h]+X[m]else h[#h+1]=X[m]end end setmetatable(h,D)return h end local function h(m)local D={}for X=1,#m,1 do D[#D+1]=B.copy(m[X])end end local function Z(m,D,h)local Z={}for X=1,#m,1 do Z[X]=B.replace(m[X],D,h)end return X(Z)end local function n(m,D)local h={}for X=1,#D,1 do h[X]=B.scale(m,D[X])end return X(h)end local function V(m)local D={}for X=1,#m,1 do local h=B.simplify(m[X])if h.coeff~=0 then D[#D+1]=h end end return X(D)end local function Y(m)local D={}for X=1,#m,1 do D[X]=B.new({"\120"},{X-1},m[X])end return X(D)end local function W(m,D)local h={}for X=1,#m,1 do local Z=B.simplify(B.evaluate(m[X],D))if Z.coeff~=0 then h[#h+1]=Z end end return X(h)end D.__add=function(B,D)local h=m.concat(B,D)return X(h)end D.__unm=function(m)local B={}for D=1,#m,1 do B[D]=-m[D]end return X(B)end D.__sub=function(m,B)return m+-B end D.__mul=function(m,B)local D={}for X=1,#m,1 do for h=1,#B,1 do D[#D+1]=m[X]*B[h]end end return X(D)end D.__tostring=function(m)local B=tostring(m[1])for D=2,#m,1 do B=B..(" \043 "..tostring(m[D]))end return B end return{new=function(m)if getmetatable(m[1])==B then return V(X(m))else return V(Y(m))end end;copy=h,replace=Z;scale=n;simplify=V,evaluate=W}
local List = require(script.Parent.Parent.Parent.Llama).List
local Monomial = require(script.Parent.Monomial)

local Polynomial = {}

local function polynomialFromArrayOfMonomials(array)
    local preresult = List.sort(array, function(a, b) 
        return b > a
    end)

    local result = {preresult[1]}

    for i = 2, #array do
        if preresult[i - 1] == preresult[i] then
            result[#result] = result[#result] + preresult[i]
        else
            result[#result+1] = preresult[i]
        end
    end

    setmetatable(result, Polynomial)
    return result
end

local function polynomialCopy(polynomial)
    local result = {}

    for i = 1, #polynomial do
        result[#result+1] = Monomial.copy(polynomial[i])
    end
end

local function polynomialReplace(polynomial, symbolToReplace, symbol)
    local result = {}

    for i = 1, #polynomial do
        result[i] = Monomial.replace(polynomial[i], symbolToReplace, symbol)
    end

    return polynomialFromArrayOfMonomials(result)
end

local function polynomialScale(c, polynomial)
    local result = {}

    for i = 1, #polynomial do
        result[i] = Monomial.scale(c, polynomial[i])
    end

    return polynomialFromArrayOfMonomials(result)
end

local function polynomialSimplify(polynomial)
    local result = {}

    for i = 1, #polynomial do
        local simple = Monomial.simplify(polynomial[i])
        if simple["coeff"] ~= 0 then
            result[#result+1] = simple
        end
    end

    return polynomialFromArrayOfMonomials(result)
end

local function polynomialFromArray(array)
    local result = {}

    for i = 1, #array do
        result[i] = Monomial.new({"x"}, {i - 1}, array[i])
    end

    return polynomialFromArrayOfMonomials(result)
end

local function polynomialEvaluate(polynomial, rules)
    local result = {}

    for i = 1, #polynomial do
        local simple = Monomial.simplify(Monomial.evaluate(polynomial[i], rules))
        if simple["coeff"] ~= 0 then
            result[#result+1] = simple
        end
    end

    return polynomialFromArrayOfMonomials(result)
end

Polynomial.__add = function (left, right)
    local preresult = List.concat(left, right)

    return polynomialFromArrayOfMonomials(preresult)
end

Polynomial.__unm = function (polynomial)
    local result = {}

    for i = 1, #polynomial do
        result[i] = -polynomial[i]
    end

    return polynomialFromArrayOfMonomials(result)
end

Polynomial.__sub = function (left, right)
    return left + -right
end

Polynomial.__mul = function (left, right)
    local result = {}

    for i = 1, #left do
        for j = 1, #right do
            result[#result+1] = left[i] * right[j]
        end
    end

    return polynomialFromArrayOfMonomials(result)
end

Polynomial.__tostring = function (polynomial)
    local result = tostring(polynomial[1])

    for i = 2, #polynomial do
        result = result .. " + " .. tostring(polynomial[i])
    end

    return result
end

return {
    new = function(array) 
        if getmetatable(array[1]) == Monomial then
            return polynomialSimplify(polynomialFromArrayOfMonomials(array))
        else
            return polynomialSimplify(polynomialFromArray(array))
        end
    end,
    copy = polynomialCopy,
    replace = polynomialReplace,
    scale = polynomialScale,
    simplify = polynomialSimplify,
    evaluate = polynomialEvaluate
}