hstructure Matrix33
    _11 : number
    _12 : number
    _13 : number
    _21 : number
    _22 : number
    _23 : number
    _31 : number
    _32 : number
    _33 : number
end

function zo_setToIdentityMatrix33(m : Matrix33)
    m._11 = 1
    m._12 = 0
    m._13 = 0
    m._21 = 0
    m._22 = 1
    m._23 = 0
    m._31 = 0
    m._32 = 0
    m._33 = 1
end

function zo_setToRotationMatrix2D(m : Matrix33, radians)
    local cosResult = math.cos(radians)
    local sinResult = math.sin(radians)

    m._11 = cosResult
    m._12 = sinResult
    m._13 = 0
    m._21 = -sinResult
    m._22 = cosResult
    m._23 = 0
    m._31 = 0
    m._32 = 0
    m._33 = 1
end

function zo_setToTranslationMatrix2D(m : Matrix33, x, y)
    m._11 = 1
    m._12 = 0
    m._13 = x
    m._21 = 0
    m._22 = 1
    m._23 = -y
    m._31 = 0
    m._32 = 0
    m._33 = 1
end

function zo_setToScaleMatrix2D(m : Matrix33, scale)
    m._11 = scale
    m._12 = 0
    m._13 = 0
    m._21 = 0
    m._22 = scale
    m._23 = 0
    m._31 = 0
    m._32 = 0
    m._33 = 1
end

function zo_invertMatrix33(m : Matrix33, result : Matrix33)
    local determinant = m._11 * (m._22 * m._33 - m._32 * m._23) -
                        m._12 * (m._21 * m._33 - m._23 * m._31) +
                        m._13 * (m._21 * m._32 - m._22 * m._31)
    local inverseDeterminant = 1 / determinant

    -- store values in locals so we can reuse m as our result matrix: this way we aren't editing the input as we record our output
    local _11 = (m._22 * m._33 - m._32 * m._23) * inverseDeterminant
    local _12 = (m._13 * m._32 - m._12 * m._33) * inverseDeterminant
    local _13 = (m._12 * m._23 - m._13 * m._22) * inverseDeterminant
    local _21 = (m._23 * m._31 - m._21 * m._33) * inverseDeterminant
    local _22 = (m._11 * m._33 - m._13 * m._31) * inverseDeterminant
    local _23 = (m._21 * m._13 - m._11 * m._23) * inverseDeterminant
    local _31 = (m._21 * m._32 - m._31 * m._22) * inverseDeterminant
    local _32 = (m._31 * m._12 - m._11 * m._32) * inverseDeterminant
    local _33 = (m._11 * m._22 - m._21 * m._12) * inverseDeterminant

    result._11 = _11
    result._12 = _12
    result._13 = _13
    result._21 = _21
    result._22 = _22
    result._23 = _23
    result._31 = _31
    result._32 = _32
    result._33 = _33
end

function zo_matrixMultiply33x33(a : Matrix33, b : Matrix33, result : Matrix33)
    -- store values in locals so we can reuse a or b as our result matrix: this way we aren't editing the input as we record our output
    local _11 = a._11 * b._11 + a._12 * b._21 + a._13 * b._31
    local _12 = a._11 * b._12 + a._12 * b._22 + a._13 * b._32
    local _13 = a._11 * b._13 + a._12 * b._23 + a._13 * b._33
    local _21 = a._21 * b._11 + a._22 * b._21 + a._23 * b._31
    local _22 = a._21 * b._12 + a._22 * b._22 + a._23 * b._32
    local _23 = a._21 * b._13 + a._22 * b._23 + a._23 * b._33
    local _31 = a._31 * b._11 + a._32 * b._21 + a._33 * b._31
    local _32 = a._31 * b._12 + a._32 * b._22 + a._33 * b._32
    local _33 = a._31 * b._13 + a._32 * b._23 + a._33 * b._33

    result._11 = _11
    result._12 = _12
    result._13 = _13
    result._21 = _21
    result._22 = _22
    result._23 = _23
    result._31 = _31
    result._32 = _32
    result._33 = _33
end

function zo_matrixTransformPoint(m : Matrix33, pointX, pointY)
    local outX = m._11 * pointX + m._12 * pointY + m._13
    local outY = m._21 * pointX + m._22 * pointY + m._23
    return outX, outY
end