void MeshDenoisingViaL0Minimization::solveVertices(TriMesh &mesh, Eigen::MatrixXd &initial_vertices_matrix,
                                                   std::vector< std::vector<TriMesh::VertexHandle> > &edge_vertex_handle,
                                                   std::vector< std::vector<double> > &coef, std::vector<TriMesh::Point> &delta,
                                                   double alpha, double beta)
{
    Eigen::MatrixXd right_term = initial_vertices_matrix;
    Eigen::SparseMatrix<double> coef_matrix((int)mesh.n_vertices(), (int)mesh.n_vertices());

    std::vector< Eigen::Triplet<double> > triple; triple.clear();

    std::map<TriMesh::VertexHandle, double> vertex_coef;
    std::set<TriMesh::EdgeHandle> edge_handle;;
    for(TriMesh::VertexIter v_it = mesh.vertices_begin(); v_it != mesh.vertices_end(); v_it++)
    {
        edge_handle.clear();
        vertex_coef.clear();
        vertex_coef[*v_it] = 1.0;
        for(TriMesh::VertexFaceIter vf_it = mesh.vf_iter(*v_it); vf_it.is_valid(); vf_it++)
        {
            for(TriMesh::FaceEdgeIter fe_it = mesh.fe_iter(*vf_it); fe_it.is_valid(); fe_it++)
            {
                edge_handle.insert(*fe_it);
            }
        }

        TriMesh::Point right(0.0, 0.0, 0.0);
        for(std::set<TriMesh::EdgeHandle>::iterator s_it = edge_handle.begin(); s_it != edge_handle.end(); s_it++)
        {
            if(!mesh.is_boundary(*s_it))
            {
                int index = (*s_it).idx();
                TriMesh::VertexHandle v1 = edge_vertex_handle[index][0],
                        v2 = edge_vertex_handle[index][1],
                        v3 = edge_vertex_handle[index][2],
                        v4 = edge_vertex_handle[index][3];
                double coe1 = coef[index][0],
                        coe2 = coef[index][1],
                        coe3 = coef[index][2],
                        coe4 = coef[index][3];
                TriMesh::Point temp_delta = delta[index];
                if(v1 == *v_it)
                {
                    vertex_coef[v1] = vertex_coef[v1] + alpha + beta * coe1 * coe1;
                    vertex_coef[v2] = vertex_coef[v2] - alpha + beta * coe1 * coe2;
                    vertex_coef[v3] = vertex_coef[v3] + alpha + beta * coe1 * coe3;
                    vertex_coef[v4] = vertex_coef[v4] - alpha + beta * coe1 * coe4;
                    right += temp_delta * beta * coe1;
                }
                else if(v2 == *v_it)
                {
                    vertex_coef[v1] = vertex_coef[v1] - alpha + beta * coe2 * coe1;
                    vertex_coef[v2] = vertex_coef[v2] + alpha + beta * coe2 * coe2;
                    vertex_coef[v3] = vertex_coef[v3] - alpha + beta * coe2 * coe3;
                    vertex_coef[v4] = vertex_coef[v4] + alpha + beta * coe2 * coe4;
                    right += temp_delta * beta * coe2;
                }
                else if(v3 == *v_it)
                {
                    vertex_coef[v1] = vertex_coef[v1] + alpha + beta * coe3 * coe1;
                    vertex_coef[v2] = vertex_coef[v2] - alpha + beta * coe3 * coe2;
                    vertex_coef[v3] = vertex_coef[v3] + alpha + beta * coe3 * coe3;
                    vertex_coef[v4] = vertex_coef[v4] - alpha + beta * coe3 * coe4;
                    right += temp_delta * beta * coe3;
                }
                else if(v4 == *v_it)
                {
                    vertex_coef[v1] = vertex_coef[v1] - alpha + beta * coe4 * coe1;
                    vertex_coef[v2] = vertex_coef[v2] + alpha + beta * coe4 * coe2;
                    vertex_coef[v3] = vertex_coef[v3] - alpha + beta * coe4 * coe3;
                    vertex_coef[v4] = vertex_coef[v4] + alpha + beta * coe4 * coe4;
                    right += temp_delta * beta * coe4;
                }
            }
        }
        right_term(v_it->idx(), 0) += right[0];
        right_term(v_it->idx(), 1) += right[1];
        right_term(v_it->idx(), 2) += right[2];

        for(std::map<TriMesh::VertexHandle, double>::iterator m_it = vertex_coef.begin(); m_it != vertex_coef.end(); m_it++)
        {
            triple.push_back(Eigen::Triplet<double>(v_it->idx(), m_it->first.idx(), m_it->second));
        }
    }
    coef_matrix.setFromTriplets(triple.begin(), triple.end());

    Eigen::SparseLU<Eigen::SparseMatrix<double> > solver;
    solver.analyzePattern(coef_matrix);
    solver.factorize(coef_matrix);
    Eigen::MatrixXd vertices_term = solver.solve(right_term);

    for(TriMesh::VertexIter v_it = mesh.vertices_begin(); v_it != mesh.vertices_end(); v_it++)
    {
        int index = v_it->idx();
        TriMesh::Point pt = TriMesh::Point(vertices_term(index,0), vertices_term(index,1), vertices_term(index,2));
        mesh.set_point(*v_it, pt);
    }
}