/********************************************************
 *  ██████╗  ██████╗████████╗██╗
 * ██╔════╝ ██╔════╝╚══██╔══╝██║
 * ██║  ███╗██║        ██║   ██║
 * ██║   ██║██║        ██║   ██║
 * ╚██████╔╝╚██████╗   ██║   ███████╗
 *  ╚═════╝  ╚═════╝   ╚═╝   ╚══════╝
 * Geophysical Computational Tools & Library (GCTL)
 *
 * Copyright (c) 2023  Yi Zhang (yizhang-geo@zju.edu.cn)
 *
 * GCTL is distributed under a dual licensing scheme. You can redistribute 
 * it and/or modify it under the terms of the GNU Lesser General Public 
 * License as published by the Free Software Foundation, either version 2 
 * of the License, or (at your option) any later version. You should have 
 * received a copy of the GNU Lesser General Public License along with this 
 * program. If not, see <http://www.gnu.org/licenses/>.
 * 
 * If the terms and conditions of the LGPL v.2. would prevent you from using 
 * the GCTL, please consider the option to obtain a commercial license for a 
 * fee. These licenses are offered by the GCTL's original author. As a rule, 
 * licenses are provided "as-is", unlimited in time for a one time fee. Please 
 * send corresponding requests to: yizhang-geo@zju.edu.cn. Please do not forget 
 * to include some description of your company and the realm of its activities. 
 * Also add information on how to contact you by electronic and paper mail.
 ******************************************************/

#ifndef _GCTL_GMSH_IO_H
#define _GCTL_GMSH_IO_H

// library's head files
#include "../core/matrix.h"
#include "../poly/node.h"
#include "../poly/sphere.h"
#include "../poly/triangle2d.h"
#include "../poly/triangle2d2o.h"
#include "../poly/rectangle2d.h"
#include "../poly/edge2d.h"
#include "../poly/block.h"
#include "../poly/prism.h"
#include "../poly/tesseroid.h"
#include "../poly/tetrahedron.h"
#include "../poly/tri_cone.h"
#include "../poly/triangle.h"
#include "../poly/edge.h"
#include "file_io.h"

#ifdef GCTL_EIGEN
#include "Eigen/Dense"
#endif // GCTL_EIGEN

namespace gctl
{	
	struct gmsh_physical_group
	{
		size_t dim_tag, phys_tag;
		std::string name;
	};

	/**
	 * @brief      Gmsh文件的IO类，主要为调用下面定义的全局函数
	 */
	class gmshio
	{
	public:
		gmshio();
		gmshio(std::string filename, file_direction_e f_direct);
		virtual ~gmshio();
		void init_file(std::string filename, file_direction_e f_direct);
		void reset_file(std::string filename, file_direction_e f_direct);
		void set_packed(index_packed_e is_packed, file_direction_e f_direct);
		bool initialized(file_direction_e f_direct);

		void read_physical_groups(array<gmsh_physical_group> &phy_groups);
		void save_physical_groups(const array<gmsh_physical_group> &phy_groups);
		int physical_name2tag(const array<gmsh_physical_group> &phy_groups, std::string name);

		template <typename A> void read_node(array<vertex<point2dc, A>> &out_nodes);
		template <typename A> void read_node(array<vertex<point3dc, A>> &out_nodes);
		template <typename E, typename A> void read_element(array<type_edge2d<E>> &out_elements, const array<vertex<point2dc, A>> &nodes, _2i_vector *ele_tag = nullptr);
		template <typename E, typename A> void read_element(array<type_triangle2d<E>> &out_elements, const array<vertex<point2dc, A>> &nodes, _2i_vector *ele_tag = nullptr);
		template <typename E, typename A> void read_element(array<type_triangle2d2o<E>> &out_elements, const array<vertex<point2dc, A>> &nodes, _2i_vector *ele_tag = nullptr);
		template <typename E, typename A> void read_element(array<type_rectangle2d<E>> &out_elements, const array<vertex<point2dc, A>> &nodes, _2i_vector *ele_tag = nullptr);
		template <typename E, typename A> void read_element(array<type_node<E>> &out_elements, const array<vertex<point3dc, A>> &nodes, _2i_vector *ele_tag = nullptr);
		template <typename E, typename A> void read_element(array<type_edge<E>> &out_elements, const array<vertex<point3dc, A>> &nodes, _2i_vector *ele_tag = nullptr);
		template <typename E, typename A> void read_element(array<type_triangle<E>> &out_elements, const array<vertex<point3dc, A>> &nodes, _2i_vector *ele_tag = nullptr);
		template <typename E, typename A> void read_element(array<type_tetrahedron<E>> &out_elements, const array<vertex<point3dc, A>> &nodes, _2i_vector *ele_tag = nullptr);
		template <typename E, typename A> void read_element(array<type_block<E>> &out_elements, const array<vertex<point3dc, A>> &nodes, _2i_vector *ele_tag = nullptr);
		template <typename E, typename A> void read_element(array<type_tricone<E>> &out_elements, const array<vertex<point3dc, A>> &nodes, _2i_vector *ele_tag = nullptr);
		template <typename E, typename A> void read_element(array<type_prism<E>> &out_elements, const array<vertex<point3dc, A>> &nodes, _2i_vector *ele_tag = nullptr);

		template <typename E, typename A> void read_mesh(array<type_triangle2d<E>> &out_elements, array<vertex<point2dc, A>> &nodes, _2i_vector *ele_tag = nullptr);
		template <typename E, typename A> void read_mesh(array<type_triangle2d2o<E>> &out_elements, array<vertex<point2dc, A>> &nodes, _2i_vector *ele_tag = nullptr);
		template <typename E, typename A> void read_mesh(array<type_rectangle2d<E>> &out_elements, array<vertex<point2dc, A>> &nodes, _2i_vector *ele_tag = nullptr);
		template <typename E, typename A> void read_mesh(array<type_node<E>> &out_elements, array<vertex<point3dc, A>> &nodes, _2i_vector *ele_tag = nullptr);
		template <typename E, typename A> void read_mesh(array<type_edge<E>> &out_elements, array<vertex<point3dc, A>> &nodes, _2i_vector *ele_tag = nullptr);
		template <typename E, typename A> void read_mesh(array<type_triangle<E>> &out_elements, array<vertex<point3dc, A>> &nodes, _2i_vector *ele_tag = nullptr);
		template <typename E, typename A> void read_mesh(array<type_tetrahedron<E>> &out_elements, array<vertex<point3dc, A>> &nodes, _2i_vector *ele_tag = nullptr);
		template <typename E, typename A> void read_mesh(array<type_block<E>> &out_elements, array<vertex<point3dc, A>> &nodes, _2i_vector *ele_tag = nullptr);
		template <typename E, typename A> void read_mesh(array<type_tricone<E>> &out_elements, array<vertex<point3dc, A>> &nodes, _2i_vector *ele_tag = nullptr);
		template <typename E, typename A> void read_mesh(array<type_prism<E>> &out_elements, array<vertex<point3dc, A>> &nodes, _2i_vector *ele_tag = nullptr);

		template <typename T> void read_data(array<T> &out_data, std::string data_name = "");
		template <typename T> void read_data(array<T> &out_data, array<size_t> &out_index, std::string data_name = "");
		template <typename T> void read_data(std::vector<std::vector<T> > &out_data, std::vector<std::string> &out_name, 
			mesh_data_type_e d_type, std::vector<std::vector<size_t> > *out_index = nullptr);

		template <typename E, typename A> void save_mesh(const array<type_triangle2d<E>> &element, const array<vertex<point2dc, A>> &node);
		template <typename E, typename A> void save_mesh(const array<type_triangle2d2o<E>> &element, const array<vertex<point2dc, A>> &node);
		template <typename E, typename A> void save_mesh(const array<type_rectangle2d<E>> &element, const array<vertex<point2dc, A>> &node);
		template <typename E, typename A> void save_mesh(const array<type_triangle<E>> &element, const array<vertex<point3dc, A>> &node, array<bool> *out_ele_tag = nullptr, array<bool> *out_node_tag = nullptr);
		template <typename E, typename A> void save_mesh(const array<type_tetrahedron<E>> &element, const array<vertex<point3dc, A>> &node, array<bool> *output_tag = nullptr);
		template <typename E, typename A> void save_mesh(const array<type_block<E>> &element, const array<vertex<point3dc, A>> &node);
		template <typename E, typename A> void save_mesh(const array<type_tricone<E>> &element, const array<vertex<point3dc, A>> &node);
		template <typename E, typename A> void save_mesh(const array<type_prism<E>> &element, const array<vertex<point3dc, A>> &node);
		template <typename E, typename A> void save_mesh(const array<type_tesseroid<E>> &element, const array<vertex<point3dc, A>> &node);

		template <typename E> void save_mesh(const array<type_tetrahedron<E>> &element);

		template <typename T> void save_data(std::string dataname, const T *in_data, size_t in_size, mesh_data_type_e datatype, const size_t *in_index = nullptr);
		template <typename T> void save_data(std::string dataname, const array<T> &in_data, mesh_data_type_e datatype, const array<size_t> *in_index = nullptr);
		template <typename T> void save_data(std::string dataname, const std::vector<T> &in_data, mesh_data_type_e datatype, const std::vector<size_t> *in_index = nullptr);
		template <typename T> void save_data(std::string dataname, const array<point3c<T>> &in_data, mesh_data_type_e datatype, const array<size_t> *in_index = nullptr);
		template <typename T> void save_data(std::string dataname, const std::vector<point3c<T>> &in_data, mesh_data_type_e datatype, const std::vector<size_t> *in_index = nullptr);

#ifdef GCTL_EIGEN

		template <typename T> void save_data(std::string dataname, const Eigen::Matrix<T, Eigen::Dynamic, 1> &in_data, mesh_data_type_e datatype, const Eigen::Matrix<T, Eigen::Dynamic, 1> *in_index = nullptr);

#endif // GCTL_EIGEN

		template <typename E> void element_register(const array<type_triangle2d<E>> &element, const _2i_matrix *tag_ptr = nullptr, array<size_t> *rgst_index = nullptr);
		template <typename E> void element_register(const array<type_triangle2d2o<E>> &element, const _2i_matrix *tag_ptr = nullptr, array<size_t> *rgst_index = nullptr);
		template <typename E> void element_register(const array<type_rectangle2d<E>> &element, const _2i_matrix *tag_ptr = nullptr, array<size_t> *rgst_index = nullptr);
		template <typename E> void element_register(const array<type_node<E>> &element, const _2i_matrix *tag_ptr = nullptr, array<size_t> *rgst_index = nullptr);
		template <typename E> void element_register(const array<type_triangle<E>> &element, const _2i_matrix *tag_ptr = nullptr, array<size_t> *rgst_index = nullptr);
		template <typename E> void element_register(const array<type_tetrahedron<E>> &element, const _2i_matrix *tag_ptr = nullptr, array<size_t> *rgst_index = nullptr);
		template <typename E> void element_register(const array<type_block<E>> &element, const _2i_matrix *tag_ptr = nullptr, array<size_t> *rgst_index = nullptr);
		template <typename E> void element_register(const array<type_tricone<E>> &element, const _2i_matrix *tag_ptr = nullptr, array<size_t> *rgst_index = nullptr);
		template <typename E> void element_register(const array<type_prism<E>> &element, const _2i_matrix *tag_ptr = nullptr, array<size_t> *rgst_index = nullptr);

		template <typename A> void save_registered_mesh(const array<vertex<point2dc, A>> &node, array<gmsh_physical_group> *phys_ptr = nullptr);
		template <typename A> void save_registered_mesh(const array<vertex<point3dc, A>> &node, array<gmsh_physical_group> *phys_ptr = nullptr);

	protected:
		std::ifstream gmsh_in; // Input stream
		std::ofstream gmsh_out; // Output stream
		index_packed_e in_packed, out_packed; // Whether the input/output node indexes are starting from zero
		std::vector<std::vector<int> > rgst_elem; // registered elements
	};

	template <typename A>
	void gmshio::read_node(array<vertex<point2dc, A>> &out_nodes)
	{
		initialized(Input);
		read_gmsh_node(gmsh_in, out_nodes, in_packed);
		return;
	}

	template <typename A>
	void gmshio::read_node(array<vertex<point3dc, A>> &out_nodes)
	{
		initialized(Input);
		read_gmsh_node(gmsh_in, out_nodes, in_packed);
		return;
	}

	template <typename E, typename A>
	void gmshio::read_element(array<type_edge2d<E>> &out_elements, const array<vertex<point2dc, A>> &nodes, _2i_vector *ele_tag)
	{
		initialized(Input);
		read_gmsh_element(gmsh_in, out_elements, nodes, in_packed, ele_tag);
		return;
	}

	template <typename E, typename A>
	void gmshio::read_element(array<type_triangle2d<E>> &out_elements, const array<vertex<point2dc, A>> &nodes, _2i_vector *ele_tag)
	{
		initialized(Input);
		read_gmsh_element(gmsh_in, out_elements, nodes, in_packed, ele_tag);
		return;
	}

	template <typename E, typename A>
	void gmshio::read_element(array<type_triangle2d2o<E>> &out_elements, const array<vertex<point2dc, A>> &nodes, _2i_vector *ele_tag)
	{
		initialized(Input);
		read_gmsh_element(gmsh_in, out_elements, nodes, in_packed, ele_tag);
		return;
	}

	template <typename E, typename A>
	void gmshio::read_element(array<type_rectangle2d<E>> &out_elements, const array<vertex<point2dc, A>> &nodes, _2i_vector *ele_tag)
	{
		initialized(Input);
		read_gmsh_element(gmsh_in, out_elements, nodes, in_packed, ele_tag);
		return;
	}

	template <typename E, typename A>
	void gmshio::read_element(array<type_node<E>> &out_elements, const array<vertex<point3dc, A>> &nodes, _2i_vector *ele_tag)
	{
		initialized(Input);
		read_gmsh_element(gmsh_in, out_elements, nodes, in_packed, ele_tag);
		return;
	}

	template <typename E, typename A>
	void gmshio::read_element(array<type_edge<E>> &out_elements, const array<vertex<point3dc, A>> &nodes, _2i_vector *ele_tag)
	{
		initialized(Input);
		read_gmsh_element(gmsh_in, out_elements, nodes, in_packed, ele_tag);
		return;
	}

	template <typename E, typename A>
	void gmshio::read_element(array<type_triangle<E>> &out_elements, const array<vertex<point3dc, A>> &nodes, _2i_vector *ele_tag)
	{
		initialized(Input);
		read_gmsh_element(gmsh_in, out_elements, nodes, in_packed, ele_tag);
		return;
	}

	template <typename E, typename A>
	void gmshio::read_element(array<type_tetrahedron<E>> &out_elements, const array<vertex<point3dc, A>> &nodes, _2i_vector *ele_tag)
	{
		initialized(Input);
		read_gmsh_element(gmsh_in, out_elements, nodes, in_packed, ele_tag);
		return;
	}

	template <typename E, typename A>
	void gmshio::read_element(array<type_block<E>> &out_elements, const array<vertex<point3dc, A>> &nodes, _2i_vector *ele_tag)
	{
		initialized(Input);
		read_gmsh_element(gmsh_in, out_elements, nodes, in_packed, ele_tag);
		return;
	}

	template <typename E, typename A>
	void gmshio::read_element(array<type_tricone<E>> &out_elements, const array<vertex<point3dc, A>> &nodes, _2i_vector *ele_tag)
	{
		initialized(Input);
		read_gmsh_element(gmsh_in, out_elements, nodes, in_packed, ele_tag);
		return;
	}

	template <typename E, typename A>
	void gmshio::read_element(array<type_prism<E>> &out_elements, const array<vertex<point3dc, A>> &nodes, _2i_vector *ele_tag)
	{
		initialized(Input);
		read_gmsh_element(gmsh_in, out_elements, nodes, in_packed, ele_tag);
		return;
	}

	template <typename E, typename A>
	void gmshio::read_mesh(array<type_triangle2d<E>> &out_elements, array<vertex<point2dc, A>> &nodes, _2i_vector *ele_tag)
	{
		read_node(nodes);
		read_element(out_elements, nodes, ele_tag);
		return;
	}

	template <typename E, typename A>
	void gmshio::read_mesh(array<type_triangle2d2o<E>> &out_elements, array<vertex<point2dc, A>> &nodes, _2i_vector *ele_tag)
	{
		read_node(nodes);
		read_element(out_elements, nodes, ele_tag);
		return;
	}

	template <typename E, typename A>
	void gmshio::read_mesh(array<type_rectangle2d<E>> &out_elements, array<vertex<point2dc, A>> &nodes, _2i_vector *ele_tag)
	{
		read_node(nodes);
		read_element(out_elements, nodes, ele_tag);
		return;
	}

	template <typename E, typename A>
	void gmshio::read_mesh(array<type_node<E>> &out_elements, array<vertex<point3dc, A>> &nodes, _2i_vector *ele_tag)
	{
		read_node(nodes);
		read_element(out_elements, nodes, ele_tag);
		return;
	}

	template <typename E, typename A>
	void gmshio::read_mesh(array<type_edge<E>> &out_elements, array<vertex<point3dc, A>> &nodes, _2i_vector *ele_tag)
	{
		read_node(nodes);
		read_element(out_elements, nodes, ele_tag);
		return;
	}

	template <typename E, typename A>
	void gmshio::read_mesh(array<type_triangle<E>> &out_elements, array<vertex<point3dc, A>> &nodes, _2i_vector *ele_tag)
	{
		read_node(nodes);
		read_element(out_elements, nodes, ele_tag);
		return;
	}

	template <typename E, typename A>
	void gmshio::read_mesh(array<type_tetrahedron<E>> &out_elements, array<vertex<point3dc, A>> &nodes, _2i_vector *ele_tag)
	{
		read_node(nodes);
		read_element(out_elements, nodes, ele_tag);
		return;
	}

	template <typename E, typename A>
	void gmshio::read_mesh(array<type_block<E>> &out_elements, array<vertex<point3dc, A>> &nodes, _2i_vector *ele_tag)
	{
		read_node(nodes);
		read_element(out_elements, nodes, ele_tag);
		return;
	}

	template <typename E, typename A>
	void gmshio::read_mesh(array<type_tricone<E>> &out_elements, array<vertex<point3dc, A>> &nodes, _2i_vector *ele_tag)
	{
		read_node(nodes);
		read_element(out_elements, nodes, ele_tag);
		return;
	}

	template <typename E, typename A>
	void gmshio::read_mesh(array<type_prism<E>> &out_elements, array<vertex<point3dc, A>> &nodes, _2i_vector *ele_tag)
	{
		read_node(nodes);
		read_element(out_elements, nodes, ele_tag);
		return;
	}

	template <typename T>
	void gmshio::read_data(array<T> &out_data, std::string data_name)
	{
		initialized(Input);
		read_gmsh_data(gmsh_in, out_data, data_name);
		return;
	}

	template <typename T>
	void gmshio::read_data(array<T> &out_data, array<size_t> &out_index, std::string data_name)
	{
		initialized(Input);
		read_gmsh_data(gmsh_in, out_data, out_index, data_name, in_packed);
		return;
	}

	template <typename T>
	void gmshio::read_data(std::vector<std::vector<T> > &out_data, std::vector<std::string> &out_name, 
		mesh_data_type_e d_type, std::vector<std::vector<size_t> > *out_index)
	{
		initialized(Input);
		read_gmsh_data(gmsh_in, out_data, out_name, d_type, out_index, in_packed);
		return;
	}

	template <typename E, typename A>
	void gmshio::save_mesh(const array<type_triangle2d<E>> &element, const array<vertex<point2dc, A>> &node)
	{
		initialized(Output);
		save2gmsh(gmsh_out, element, node, out_packed);
		return;
	}

	template <typename E, typename A>
	void gmshio::save_mesh(const array<type_triangle2d2o<E>> &element, const array<vertex<point2dc, A>> &node)
	{
		initialized(Output);
		save2gmsh(gmsh_out, element, node, out_packed);
		return;
	}

	template <typename E, typename A>
	void gmshio::save_mesh(const array<type_rectangle2d<E>> &element, const array<vertex<point2dc, A>> &node)
	{
		initialized(Output);
		save2gmsh(gmsh_out, element, node, out_packed);
		return;
	}

	template <typename E, typename A>
	void gmshio::save_mesh(const array<type_triangle<E>> &element, const array<vertex<point3dc, A>> &node, 
		array<bool> *out_ele_tag, array<bool> *out_node_tag)
	{
		initialized(Output);
		save2gmsh(gmsh_out, element, node, out_packed, out_ele_tag, out_node_tag);
		return;
	}

	template <typename E, typename A>
	void gmshio::save_mesh(const array<type_tetrahedron<E>> &element, const array<vertex<point3dc, A>> &node, array<bool> *output_tag)
	{
		initialized(Output);
		save2gmsh(gmsh_out, element, node, out_packed, output_tag);
		return;
	}

	template <typename E, typename A>
	void gmshio::save_mesh(const array<type_block<E>> &element, const array<vertex<point3dc, A>> &node)
	{
		initialized(Output);
		save2gmsh(gmsh_out, element, node, out_packed);
		return;
	}

	template <typename E, typename A>
	void gmshio::save_mesh(const array<type_tricone<E>> &element, const array<vertex<point3dc, A>> &node)
	{
		initialized(Output);
		save2gmsh(gmsh_out, element, node, out_packed);
		return;
	}

	template <typename E, typename A>
	void gmshio::save_mesh(const array<type_prism<E>> &element, const array<vertex<point3dc, A>> &node)
	{
		initialized(Output);
		save2gmsh(gmsh_out, element, node, out_packed);
		return;
	}

	template <typename E, typename A>
	void gmshio::save_mesh(const array<type_tesseroid<E>> &element, const array<vertex<point3dc, A>> &node)
	{
		initialized(Output);
		save2gmsh(gmsh_out, element, node, out_packed);
		return;
	}

	template <typename E>
	void gmshio::save_mesh(const array<type_tetrahedron<E>> &element)
	{
		initialized(Output);
		save2gmsh(gmsh_out, element, out_packed);
		return;
	}

	template <typename T>
	void gmshio::save_data(std::string dataname, const T *in_data, size_t in_size, mesh_data_type_e datatype, const size_t *in_index)
	{
		initialized(Output);
		save_gmsh_data(gmsh_out, dataname, in_data, in_size, datatype, out_packed, in_index);
		return;
	}

	template <typename T>
	void gmshio::save_data(std::string dataname, const array<T> &in_data, mesh_data_type_e datatype, const array<size_t> *in_index)
	{
		initialized(Output);
		save_gmsh_data(gmsh_out, dataname, in_data, datatype, out_packed, in_index);
		return;
	}

	template <typename T>
	void gmshio::save_data(std::string dataname, const std::vector<T> &in_data, mesh_data_type_e datatype, const std::vector<size_t> *in_index)
	{
		initialized(Output);
		save_gmsh_data(gmsh_out, dataname, in_data, datatype, out_packed, in_index);
		return;
	}

	template <typename T>
	void gmshio::save_data(std::string dataname, const array<point3c<T>> &in_data, mesh_data_type_e datatype, const array<size_t> *in_index)
	{
		initialized(Output);
		save_gmsh_data(gmsh_out, dataname, in_data, datatype, out_packed, in_index);
		return;
	}

	template <typename T>
	void gmshio::save_data(std::string dataname, const std::vector<point3c<T>> &in_data, mesh_data_type_e datatype, const std::vector<size_t> *in_index)
	{
		initialized(Output);
		save_gmsh_data(gmsh_out, dataname, in_data, datatype, out_packed, in_index);
		return;
	}

#ifdef GCTL_EIGEN

	template <typename T> 
	void gmshio::save_data(std::string dataname, const Eigen::Matrix<T, Eigen::Dynamic, 1> &in_data, mesh_data_type_e datatype, const Eigen::Matrix<T, Eigen::Dynamic, 1> *in_index)
	{
		initialized(Output);
		save_gmsh_data(gmsh_out, dataname, in_data, datatype, out_packed, in_index);
		return;
	}

#endif // GCTL_EIGEN

	template <typename E>
	void gmshio::element_register(const array<type_triangle2d<E>> &element, const _2i_matrix *tag_ptr, array<size_t> *rgst_index)
	{
		if (tag_ptr != nullptr && tag_ptr->row_size() != element.size())
		{
			throw runtime_error("Tag size doesn't match with the element size. From gctl::gmshio::element_register(...)");
		}

		if (rgst_index != nullptr) rgst_index->resize(element.size());

		rgst_elem.reserve(rgst_elem.size() + element.size());

		std::vector<int> tmp_elem;
		if (tag_ptr != nullptr)
		{
			size_t c_size = tag_ptr->col_size();
			tmp_elem.resize(5 + c_size);
			tmp_elem[0] = 2; tmp_elem[1] = c_size;
			for (size_t i = 0; i < element.size(); i++)
			{
				for (size_t j = 0; j < c_size; j++)
				{
					tmp_elem[j+2] = tag_ptr->at(i, j);
				}
				
				for (size_t j = 0; j < 3; j++)
				{
					tmp_elem[j+2+c_size] = element[i].vert[j]->id;
					if (out_packed == NotPacked) tmp_elem[j+2+c_size] += 1;
				}

				if (rgst_index != nullptr) rgst_index->at(i) = rgst_elem.size();
				rgst_elem.push_back(tmp_elem);
			}
		}
		else
		{
			tmp_elem.resize(5);
			tmp_elem[0] = 2; tmp_elem[1] = 0;
			for (size_t i = 0; i < element.size(); i++)
			{
				for (size_t j = 0; j < 3; j++)
				{
					tmp_elem[j+2] = element[i].vert[j]->id;
					if (out_packed == NotPacked) tmp_elem[j+2] += 1;
				}

				if (rgst_index != nullptr) rgst_index->at(i) = rgst_elem.size();
				rgst_elem.push_back(tmp_elem);
			}
		}
		return;
	}

	template <typename E>
	void gmshio::element_register(const array<type_triangle2d2o<E>> &element, const _2i_matrix *tag_ptr, array<size_t> *rgst_index)
	{
		if (tag_ptr != nullptr && tag_ptr->row_size() != element.size())
		{
			throw runtime_error("Tag size doesn't match with the element size. From gctl::gmshio::element_register(...)");
		}

		if (rgst_index != nullptr) rgst_index->resize(element.size());

		rgst_elem.reserve(rgst_elem.size() + element.size());

		std::vector<int> tmp_elem;
		if (tag_ptr != nullptr)
		{
			size_t c_size = tag_ptr->col_size();
			tmp_elem.resize(8 + c_size);
			tmp_elem[0] = 9; tmp_elem[1] = c_size;
			for (size_t i = 0; i < element.size(); i++)
			{
				for (size_t j = 0; j < c_size; j++)
				{
					tmp_elem[j+2] = tag_ptr->at(i, j);
				}
				
				for (size_t j = 0; j < 6; j++)
				{
					tmp_elem[j+2+c_size] = element[i].vert[j]->id;
					if (out_packed == NotPacked) tmp_elem[j+2+c_size] += 1;
				}

				if (rgst_index != nullptr) rgst_index->at(i) = rgst_elem.size();
				rgst_elem.push_back(tmp_elem);
			}
		}
		else
		{
			tmp_elem.resize(8);
			tmp_elem[0] = 9; tmp_elem[1] = 0;
			for (size_t i = 0; i < element.size(); i++)
			{
				for (size_t j = 0; j < 6; j++)
				{
					tmp_elem[j+2] = element[i].vert[j]->id;
					if (out_packed == NotPacked) tmp_elem[j+2] += 1;
				}

				if (rgst_index != nullptr) rgst_index->at(i) = rgst_elem.size();
				rgst_elem.push_back(tmp_elem);
			}
		}
		return;
	}

	template <typename E>
	void gmshio::element_register(const array<type_rectangle2d<E>> &element, const _2i_matrix *tag_ptr, array<size_t> *rgst_index)
	{
		if (tag_ptr != nullptr && tag_ptr->row_size() != element.size())
		{
			throw runtime_error("Tag size doesn't match with the element size. From gctl::gmshio::element_register(...)");
		}

		if (rgst_index != nullptr) rgst_index->resize(element.size());

		rgst_elem.reserve(rgst_elem.size() + element.size());

		std::vector<int> tmp_elem;
		if (tag_ptr != nullptr)
		{
			size_t c_size = tag_ptr->col_size();
			tmp_elem.resize(6 + c_size);
			tmp_elem[0] = 3; tmp_elem[1] = c_size;
			for (size_t i = 0; i < element.size(); i++)
			{
				for (size_t j = 0; j < c_size; j++)
				{
					tmp_elem[j+2] = tag_ptr->at(i, j);
				}
				
				for (size_t j = 0; j < 4; j++)
				{
					tmp_elem[j+2+c_size] = element[i].vert[j]->id;
					if (out_packed == NotPacked) tmp_elem[j+2+c_size] += 1;
				}

				if (rgst_index != nullptr) rgst_index->at(i) = rgst_elem.size();
				rgst_elem.push_back(tmp_elem);
			}
		}
		else
		{
			tmp_elem.resize(6);
			tmp_elem[0] = 3; tmp_elem[1] = 0;
			for (size_t i = 0; i < element.size(); i++)
			{
				for (size_t j = 0; j < 4; j++)
				{
					tmp_elem[j+2] = element[i].vert[j]->id;
					if (out_packed == NotPacked) tmp_elem[j+2] += 1;
				}

				if (rgst_index != nullptr) rgst_index->at(i) = rgst_elem.size();
				rgst_elem.push_back(tmp_elem);
			}
		}
		return;
	}

	template <typename E> 
	void gmshio::element_register(const array<type_node<E>> &element, const _2i_matrix *tag_ptr, array<size_t> *rgst_index)
	{
		if (tag_ptr != nullptr && tag_ptr->row_size() != element.size())
		{
			throw runtime_error("Tag size doesn't match with the element size. From gctl::gmshio::element_register(...)");
		}

		if (rgst_index != nullptr) rgst_index->resize(element.size());

		rgst_elem.reserve(rgst_elem.size() + element.size());

		std::vector<int> tmp_elem;
		if (tag_ptr != nullptr)
		{
			size_t c_size = tag_ptr->col_size();
			tmp_elem.resize(3 + c_size);
			tmp_elem[0] = 15; tmp_elem[1] = c_size;
			for (size_t i = 0; i < element.size(); i++)
			{
				for (size_t j = 0; j < c_size; j++)
				{
					tmp_elem[j+2] = tag_ptr->at(i, j);
				}

				tmp_elem[2+c_size] = element[i].vert[0]->id;
				if (out_packed == NotPacked) tmp_elem[2+c_size] += 1;

				if (rgst_index != nullptr) rgst_index->at(i) = rgst_elem.size();
				rgst_elem.push_back(tmp_elem);
			}
		}
		else
		{
			tmp_elem.resize(3);
			tmp_elem[0] = 15; tmp_elem[1] = 0;
			for (size_t i = 0; i < element.size(); i++)
			{
				tmp_elem[2] = element[i].vert[0]->id;
				if (out_packed == NotPacked) tmp_elem[2] += 1;

				if (rgst_index != nullptr) rgst_index->at(i) = rgst_elem.size();
				rgst_elem.push_back(tmp_elem);
			}
		}
		return;
	}

	template <typename E> 
	void gmshio::element_register(const array<type_triangle<E>> &element, const _2i_matrix *tag_ptr, array<size_t> *rgst_index)
	{
		if (tag_ptr != nullptr && tag_ptr->row_size() != element.size())
		{
			throw runtime_error("Tag size doesn't match with the element size. From gctl::gmshio::element_register(...)");
		}

		if (rgst_index != nullptr) rgst_index->resize(element.size());

		rgst_elem.reserve(rgst_elem.size() + element.size());

		std::vector<int> tmp_elem;
		if (tag_ptr != nullptr)
		{
			size_t c_size = tag_ptr->col_size();
			tmp_elem.resize(5 + c_size);
			tmp_elem[0] = 2; tmp_elem[1] = c_size;
			for (size_t i = 0; i < element.size(); i++)
			{
				for (size_t j = 0; j < c_size; j++)
				{
					tmp_elem[j+2] = tag_ptr->at(i, j);
				}
				
				for (size_t j = 0; j < 3; j++)
				{
					tmp_elem[j+2+c_size] = element[i].vert[j]->id;
					if (out_packed == NotPacked) tmp_elem[j+2+c_size] += 1;
				}

				if (rgst_index != nullptr) rgst_index->at(i) = rgst_elem.size();
				rgst_elem.push_back(tmp_elem);
			}
		}
		else
		{
			tmp_elem.resize(5);
			tmp_elem[0] = 2; tmp_elem[1] = 0;
			for (size_t i = 0; i < element.size(); i++)
			{
				for (size_t j = 0; j < 3; j++)
				{
					tmp_elem[j+2] = element[i].vert[j]->id;
					if (out_packed == NotPacked) tmp_elem[j+2] += 1;
				}

				if (rgst_index != nullptr) rgst_index->at(i) = rgst_elem.size();
				rgst_elem.push_back(tmp_elem);
			}
		}
		return;
	}

	template <typename E>
	void gmshio::element_register(const array<type_tetrahedron<E>> &element, const _2i_matrix *tag_ptr, array<size_t> *rgst_index)
	{
		if (tag_ptr != nullptr && tag_ptr->row_size() != element.size())
		{
			throw runtime_error("Tag size doesn't match with the element size. From gctl::gmshio::element_register(...)");
		}

		if (rgst_index != nullptr) rgst_index->resize(element.size());

		rgst_elem.reserve(rgst_elem.size() + element.size());

		std::vector<int> tmp_elem;
		if (tag_ptr != nullptr)
		{
			size_t c_size = tag_ptr->col_size();
			tmp_elem.resize(6 + c_size);
			tmp_elem[0] = 4; tmp_elem[1] = c_size;
			for (size_t i = 0; i < element.size(); i++)
			{
				for (size_t j = 0; j < c_size; j++)
				{
					tmp_elem[j+2] = tag_ptr->at(i, j);
				}
				
				for (size_t j = 0; j < 4; j++)
				{
					tmp_elem[j+2+c_size] = element[i].vert[j]->id;
					if (out_packed == NotPacked) tmp_elem[j+2+c_size] += 1;
				}

				if (rgst_index != nullptr) rgst_index->at(i) = rgst_elem.size();
				rgst_elem.push_back(tmp_elem);
			}
		}
		else
		{
			tmp_elem.resize(6);
			tmp_elem[0] = 4; tmp_elem[1] = 0;
			for (size_t i = 0; i < element.size(); i++)
			{
				for (size_t j = 0; j < 4; j++)
				{
					tmp_elem[j+2] = element[i].vert[j]->id;
					if (out_packed == NotPacked) tmp_elem[j+2] += 1;
				}

				if (rgst_index != nullptr) rgst_index->at(i) = rgst_elem.size();
				rgst_elem.push_back(tmp_elem);
			}
		}
		return;
	}

	template <typename E> 
	void gmshio::element_register(const array<type_block<E>> &element, const  _2i_matrix *tag_ptr, array<size_t> *rgst_index)
	{
		if (tag_ptr != nullptr && tag_ptr->row_size() != element.size())
		{
			throw runtime_error("Tag size doesn't match with the element size. From gctl::gmshio::element_register(...)");
		}

		if (rgst_index != nullptr) rgst_index->resize(element.size());

		rgst_elem.reserve(rgst_elem.size() + element.size());

		std::vector<int> tmp_elem;
		if (tag_ptr != nullptr)
		{
			size_t c_size = tag_ptr->col_size();
			tmp_elem.resize(10 + c_size);
			tmp_elem[0] = 5; tmp_elem[1] = c_size;
			for (size_t i = 0; i < element.size(); i++)
			{
				for (size_t j = 0; j < c_size; j++)
				{
					tmp_elem[j+2] = tag_ptr->at(i, j);
				}
				
				for (size_t j = 0; j < 8; j++)
				{
					tmp_elem[j+2+c_size] = element[i].vert[j]->id;
					if (out_packed == NotPacked) tmp_elem[j+2+c_size] += 1;
				}

				if (rgst_index != nullptr) rgst_index->at(i) = rgst_elem.size();
				rgst_elem.push_back(tmp_elem);
			}
		}
		else
		{
			tmp_elem.resize(10);
			tmp_elem[0] = 5; tmp_elem[1] = 0;
			for (size_t i = 0; i < element.size(); i++)
			{
				for (size_t j = 0; j < 8; j++)
				{
					tmp_elem[j+2] = element[i].vert[j]->id;
					if (out_packed == NotPacked) tmp_elem[j+2] += 1;
				}

				if (rgst_index != nullptr) rgst_index->at(i) = rgst_elem.size();
				rgst_elem.push_back(tmp_elem);
			}
		}
		return;
	}

	template <typename E> 
	void gmshio::element_register(const array<type_tricone<E>> &element, const _2i_matrix *tag_ptr, array<size_t> *rgst_index)
	{
		if (tag_ptr != nullptr && tag_ptr->row_size() != element.size())
		{
			throw runtime_error("Tag size doesn't match with the element size. From gctl::gmshio::element_register(...)");
		}

		if (rgst_index != nullptr) rgst_index->resize(element.size());

		rgst_elem.reserve(rgst_elem.size() + element.size());

		std::vector<int> tmp_elem;
		if (tag_ptr != nullptr)
		{
			size_t c_size = tag_ptr->col_size();
			tmp_elem.resize(6 + c_size);
			tmp_elem[0] = 4; tmp_elem[1] = c_size;
			for (size_t i = 0; i < element.size(); i++)
			{
				for (size_t j = 0; j < c_size; j++)
				{
					tmp_elem[j+2] = tag_ptr->at(i, j);
				}
				
				for (size_t j = 0; j < 4; j++)
				{
					tmp_elem[j+2+c_size] = element[i].vert[j]->id;
					if (out_packed == NotPacked) tmp_elem[j+2+c_size] += 1;
				}

				if (rgst_index != nullptr) rgst_index->at(i) = rgst_elem.size();
				rgst_elem.push_back(tmp_elem);
			}
		}
		else
		{
			tmp_elem.resize(6);
			tmp_elem[0] = 4; tmp_elem[1] = 0;
			for (size_t i = 0; i < element.size(); i++)
			{
				for (size_t j = 0; j < 4; j++)
				{
					tmp_elem[j+2] = element[i].vert[j]->id;
					if (out_packed == NotPacked) tmp_elem[j+2] += 1;
				}

				if (rgst_index != nullptr) rgst_index->at(i) = rgst_elem.size();
				rgst_elem.push_back(tmp_elem);
			}
		}
		return;
	}

	template <typename E>
	void gmshio::element_register(const array<type_prism<E>> &element, const _2i_matrix *tag_ptr, array<size_t> *rgst_index)
	{
		if (tag_ptr != nullptr && tag_ptr->row_size() != element.size())
		{
			throw runtime_error("Tag size doesn't match with the element size. From gctl::gmshio::element_register(...)");
		}

		if (rgst_index != nullptr) rgst_index->resize(element.size());

		rgst_elem.reserve(rgst_elem.size() + element.size());

		std::vector<int> tmp_elem;
		if (tag_ptr != nullptr)
		{
			size_t c_size = tag_ptr->col_size();
			tmp_elem.resize(8 + c_size);
			tmp_elem[0] = 6; tmp_elem[1] = c_size;
			for (size_t i = 0; i < element.size(); i++)
			{
				for (size_t j = 0; j < c_size; j++)
				{
					tmp_elem[j+2] = tag_ptr->at(i, j);
				}
				
				for (size_t j = 0; j < 6; j++)
				{
					tmp_elem[j+2+c_size] = element[i].vert[j]->id;
					if (out_packed == NotPacked) tmp_elem[j+2+c_size] += 1;
				}

				if (rgst_index != nullptr) rgst_index->at(i) = rgst_elem.size();
				rgst_elem.push_back(tmp_elem);
			}
		}
		else
		{
			tmp_elem.resize(8);
			tmp_elem[0] = 6; tmp_elem[1] = 0;
			for (size_t i = 0; i < element.size(); i++)
			{
				for (size_t j = 0; j < 6; j++)
				{
					tmp_elem[j+2] = element[i].vert[j]->id;
					if (out_packed == NotPacked) tmp_elem[j+2] += 1;
				}

				if (rgst_index != nullptr) rgst_index->at(i) = rgst_elem.size();
				rgst_elem.push_back(tmp_elem);
			}
		}
		return;
	}

	template <typename A> 
	void gmshio::save_registered_mesh(const array<vertex<point2dc, A>> &node, array<gmsh_physical_group> *phys_ptr)
	{
		initialized(Output);

		if (rgst_elem.empty())
		{
			throw runtime_error("No registered elemets found. From gmshio::save_registered_mesh(...)");
		}

		// save head info
		// 将文件指针重置到文件头 保险一点
		gmsh_out.clear(std::ios::goodbit);
		gmsh_out.seekp(0, std::ios::beg);

		gmsh_out << "$MeshFormat" << std::endl
		<< "2.2 0 8" << std::endl
		<< "$EndMeshFormat "<<std::endl;

		if (phys_ptr != nullptr)
		{
			gmsh_out << "$PhysicalNames\n";
			gmsh_out << phys_ptr->size() << "\n";
			for (size_t i = 0; i < phys_ptr->size(); i++)
			{
				gmsh_out << phys_ptr->at(i).dim_tag << " " << phys_ptr->at(i).phys_tag << " \"" << phys_ptr->at(i).name << "\"\n";
			}
			gmsh_out << "$EndPhysicalNames\n";
		}

		// save node
		save2gmsh_node(gmsh_out, node.get(), node.size(), out_packed);
	
		// save elements
		gmsh_out << "$Elements" << std::endl << rgst_elem.size() <<std::endl;
		// it is better to set the first vertex index to 1 to avoid a display bug in Gmsh
		for (int i = 0; i < rgst_elem.size(); i++)
		{
			if (out_packed == Packed) gmsh_out << i;
			else gmsh_out << i+1;

			for (size_t j = 0; j < rgst_elem[i].size(); j++)
			{
				gmsh_out << " " << rgst_elem[i][j];
			}
			gmsh_out << std::endl;
		}
		gmsh_out << "$EndElements"<< std::endl;
		return;
	}

	template <typename A>
	void gmshio::save_registered_mesh(const array<vertex<point3dc, A>> &node, array<gmsh_physical_group> *phys_ptr)
	{
		initialized(Output);

		if (rgst_elem.empty())
		{
			throw runtime_error("No registered elemets found. From gmshio::save_registered_mesh(...)");
		}

		// save head info
		// 将文件指针重置到文件头 保险一点
		gmsh_out.clear(std::ios::goodbit);
		gmsh_out.seekp(0, std::ios::beg);

		gmsh_out << "$MeshFormat" << std::endl
		<< "2.2 0 8" << std::endl
		<< "$EndMeshFormat "<<std::endl;

		if (phys_ptr != nullptr)
		{
			gmsh_out << "$PhysicalNames\n";
			gmsh_out << phys_ptr->size() << "\n";
			for (size_t i = 0; i < phys_ptr->size(); i++)
			{
				gmsh_out << phys_ptr->at(i).dim_tag << " " << phys_ptr->at(i).phys_tag << " \"" << phys_ptr->at(i).name << "\"\n";
			}
			gmsh_out << "$EndPhysicalNames\n";
		}

		// save node
		save2gmsh_node(gmsh_out, node.get(), node.size(), out_packed);
	
		// save elements
		gmsh_out << "$Elements" << std::endl << rgst_elem.size() <<std::endl;
		// it is better to set the first vertex index to 1 to avoid a display bug in Gmsh
		for (int i = 0; i < rgst_elem.size(); i++)
		{
			if (out_packed == Packed) gmsh_out << i;
			else gmsh_out << i+1;

			for (size_t j = 0; j < rgst_elem[i].size(); j++)
			{
				gmsh_out << " " << rgst_elem[i][j];
			}
			gmsh_out << std::endl;
		}
		gmsh_out << "$EndElements"<< std::endl;
		return;
	}

	/**
	 * @brief      Read nodes from a Gmsh file.
	 *
	 * @param[in]  infile    The input file stream
	 * @param      node      The output node array
	 * @param[in]  packed    Indicates whether the index in the node file starts from zero. The 
	 * index is deemed to be started with one if this option is false. The default value of this 
	 * variable is true.
	 * 
	 */
	template <typename A>
	void read_gmsh_node(std::ifstream &infile, array<vertex<point2dc, A>> &node, index_packed_e packed = Packed)
	{
		// 重置数据
		if (!node.empty()) node.clear();

		// 将文件指针重置到文件头
		infile.clear(std::ios::goodbit);
		infile.seekg(std::ios::beg);

		int n_size = 0;
		std::string tmp_str;
		std::stringstream tmp_ss;
		while(getline(infile,tmp_str))
		{
			if (tmp_str == "$Nodes") //读入模型空间顶点集 msh文件版本为2.2
			{
				getline(infile, tmp_str);
				gctl::str2ss(tmp_str, tmp_ss);
				tmp_ss >> n_size; //第一个数为顶点的个数
				node.resize(n_size);

				for (int i = 0; i < n_size; i++)
				{
					getline(infile, tmp_str);
					str2ss(tmp_str, tmp_ss);
					tmp_ss >> node[i].id >> node[i].x >> node[i].y;
					if (packed == NotPacked)
						node[i].id -= 1;
				}
				break;
			}
		}
		return;
	}

	/**
	 * @brief      Read nodes from a Gmsh file.
	 *
	 * @param[in]  infile    The input file stream
	 * @param      node      an array<vertex3dc> object
	 * @param[in]  packed    Indicates whether the index in the node file starts from zero. The 
	 * index is deemed to be started with one if this option is false. The default value of this 
	 * variable is true.
	 * 
	 */
	template <typename A>
	void read_gmsh_node(std::ifstream &infile, array<vertex<point3dc, A>> &node, index_packed_e packed = Packed)
	{
		// 重置数据
		if (!node.empty()) node.clear();

		// 将文件指针重置到文件头
		infile.clear(std::ios::goodbit);
		infile.seekg(std::ios::beg);

		int n_size = 0;
		std::string tmp_str;
		std::stringstream tmp_ss;
		while(getline(infile,tmp_str))
		{
			if (tmp_str == "$Nodes") //读入模型空间顶点集 msh文件版本为2.2
			{
				getline(infile,tmp_str);
				gctl::str2ss(tmp_str, tmp_ss);
				tmp_ss >> n_size; //第一个数为顶点的个数
				node.resize(n_size);

				for (int i = 0; i < n_size; i++)
				{
					getline(infile,tmp_str);
					str2ss(tmp_str, tmp_ss);
					tmp_ss >> node[i].id >> node[i].x >> node[i].y >> node[i].z;
					if (packed == NotPacked)
						node[i].id -= 1;
				}
				break;
			}
		}
		return;
	}

	/**
	 * @brief      Read element index from a Gmsh file.
	 *
	 * @param[in]  infile       The input file stream
	 * @param      element      The output element object array
	 * @param      node         The node array
	 * @param[in]  packed       Indicates whether the index in the node file starts from zero. The 
	 * index is deemed to be started with one if this option is false. The default value of this 
	 * variable is true.
	 * @param[in]  ele_tag      Return elements' tags by a 2D integer vector.
	 *
	 */
	template <typename E, typename A>
	void read_gmsh_element(std::ifstream &infile, array<type_edge2d<E>> &element, 
		const array<vertex<point2dc, A>> &node, index_packed_e packed = Packed, 
		_2i_vector *ele_tag = nullptr)
	{
		if (node.empty())
			throw runtime_error("The input array is empty. From gctl::read_gmsh_element(...)");

		// 重置数据
		if (!element.empty()) element.clear();
		// 重置标签
		if (ele_tag != nullptr && !ele_tag->empty()) ele_tag->clear();

		// 将文件指针重置到文件头
		infile.clear(std::ios::goodbit);
		infile.seekg(std::ios::beg);

		int i_size = 0, ele_count = 0;
		int tmp_int, ele_type, attri_num;
		int tmp_index[2];
		std::string tmp_str;
		std::stringstream tmp_ss;
		while(getline(infile,tmp_str))
		{
			if (tmp_str == "$Elements") //读入模型空间顶点集 msh文件版本为2.2
			{
				getline(infile,tmp_str);
				gctl::str2ss(tmp_str, tmp_ss);
				tmp_ss >> i_size; //第一个数为顶点的个数

				// 我们先用一个临时的向量来储存元素
				edge2d tmp_edge;
				std::vector<edge2d> element_vec;
				std::vector<int> tmp_tag;

				for (int i = 0; i < i_size; i++)
				{
					getline(infile,tmp_str);
					str2ss(tmp_str, tmp_ss);
					tmp_ss >> tmp_int >> ele_type >> attri_num;
					if (ele_type == 1)
					{
						tmp_edge.id = ele_count;

						tmp_tag.clear();
						for (int a = 0; a < attri_num; a++)
						{
							tmp_ss >> tmp_int;
							tmp_tag.push_back(tmp_int);
						}

						if (ele_tag != nullptr)
							ele_tag->push_back(tmp_tag);

						tmp_ss >> tmp_index[0] >> tmp_index[1];

						if (packed == NotPacked)
						{
							for (int j = 0; j < 2; j++)
								tmp_edge.vert[j] = node.get(tmp_index[j]-1);
						}
						else
						{
							for (int j = 0; j < 2; j++)
								tmp_edge.vert[j] = node.get(tmp_index[j]);
						}
						element_vec.push_back(tmp_edge);

						ele_count++;
					}
				}

				//将元素转移到向量上来
				element.resize(element_vec.size());
				for (int i = 0; i < element.size(); i++)
				{
					element[i].id = element_vec[i].id;
					for (int j = 0; j < 2; j++)
						element[i].vert[j] = element_vec[i].vert[j];
				}
				destroy_vector(element_vec);
				break;
			}
		}
		return;
	}

	/**
	 * @brief      Read element index from a Gmsh file.
	 *
	 * @param[in]  infile       The input file stream
	 * @param      element      The output element object array
	 * @param      node         The node array
	 * @param[in]  packed       Indicates whether the index in the node file starts from zero. The 
	 * index is deemed to be started with one if this option is false. The default value of this 
	 * variable is true.
	 * @param[in]  ele_tag      Return elements' tags by a 2D integer vector.
	 *
	 */
	template <typename E, typename A>
	void read_gmsh_element(std::ifstream &infile, array<type_triangle2d<E>> &element, 
		const array<vertex<point2dc, A>> &node, index_packed_e packed = Packed, 
		_2i_vector *ele_tag = nullptr)
	{
		if (node.empty())
			throw runtime_error("The input array is empty. From gctl::read_gmsh_element(...)");

		// 重置数据
		if (!element.empty()) element.clear();
		// 重置标签
		if (ele_tag != nullptr && !ele_tag->empty()) ele_tag->clear();

		// 将文件指针重置到文件头
		infile.clear(std::ios::goodbit);
		infile.seekg(std::ios::beg);

		int i_size = 0, ele_count = 0;
		int tmp_int, ele_type, attri_num;
		int tmp_index[3];
		std::string tmp_str;
		std::stringstream tmp_ss;
		while(getline(infile,tmp_str))
		{
			if (tmp_str == "$Elements") //读入模型空间顶点集 msh文件版本为2.2
			{
				getline(infile,tmp_str);
				gctl::str2ss(tmp_str, tmp_ss);
				tmp_ss >> i_size; //第一个数为顶点的个数

				// 我们先用一个临时的向量来储存元素
				triangle2d tmp_tri;
				std::vector<triangle2d> element_vec;
				std::vector<int> tmp_tag;

				for (int i = 0; i < i_size; i++)
				{
					getline(infile,tmp_str);
					str2ss(tmp_str, tmp_ss);
					tmp_ss >> tmp_int >> ele_type >> attri_num;
					if (ele_type == 2)
					{
						tmp_tri.id = ele_count;

						tmp_tag.clear();
						for (int a = 0; a < attri_num; a++)
						{
							tmp_ss >> tmp_int;
							tmp_tag.push_back(tmp_int);
						}

						if (ele_tag != nullptr)
							ele_tag->push_back(tmp_tag);

						tmp_ss >> tmp_index[0] >> tmp_index[1] >> tmp_index[2];

						if (packed == NotPacked)
						{
							for (int j = 0; j < 3; j++)
								tmp_tri.vert[j] = node.get(tmp_index[j]-1);
						}
						else
						{
							for (int j = 0; j < 3; j++)
								tmp_tri.vert[j] = node.get(tmp_index[j]);
						}
						element_vec.push_back(tmp_tri);

						ele_count++;
					}
				}

				//将元素转移到向量上来
				element.resize(element_vec.size());
				for (int i = 0; i < element.size(); i++)
				{
					element[i].id = element_vec[i].id;
					for (int j = 0; j < 3; j++)
						element[i].vert[j] = element_vec[i].vert[j];
				}
				destroy_vector(element_vec);
				break;
			}
		}
		return;
	}

	/**
	 * @brief      Read element index from a Gmsh file.
	 *
	 * @param[in]  infile       The input file stream
	 * @param      element      The output element object array
	 * @param      node         The node array
	 * @param[in]  packed       Indicates whether the index in the node file starts from zero. The 
	 * index is deemed to be started with one if this option is false. The default value of this 
	 * variable is true.
	 * @param[in]  ele_tag      Return elements' tags by a 2D integer vector.
	 *
	 */
	template <typename E, typename A>
	void read_gmsh_element(std::ifstream &infile, array<type_triangle2d2o<E>> &element, 
		const array<vertex<point2dc, A>> &node, index_packed_e packed = Packed, 
		_2i_vector *ele_tag = nullptr)
	{
		if (node.empty())
			throw runtime_error("The input array is empty. From gctl::read_gmsh_element(...)");

		// 重置数据
		if (!element.empty()) element.clear();
		// 重置标签
		if (ele_tag != nullptr && !ele_tag->empty()) ele_tag->clear();

		// 将文件指针重置到文件头
		infile.clear(std::ios::goodbit);
		infile.seekg(std::ios::beg);

		int i_size = 0, ele_count = 0;
		int tmp_int, ele_type, attri_num;
		int tmp_index[6];
		std::string tmp_str;
		std::stringstream tmp_ss;
		while(getline(infile,tmp_str))
		{
			if (tmp_str == "$Elements") //读入模型空间顶点集 msh文件版本为2.2
			{
				getline(infile,tmp_str);
				gctl::str2ss(tmp_str, tmp_ss);
				tmp_ss >> i_size; //第一个数为顶点的个数

				// 我们先用一个临时的向量来储存元素
				triangle2d2o tmp_tri;
				std::vector<triangle2d2o> element_vec;
				std::vector<int> tmp_tag;

				for (int i = 0; i < i_size; i++)
				{
					getline(infile,tmp_str);
					str2ss(tmp_str, tmp_ss);
					tmp_ss >> tmp_int >> ele_type >> attri_num;
					if (ele_type == 9)
					{
						tmp_tri.id = ele_count;

						tmp_tag.clear();
						for (int a = 0; a < attri_num; a++)
						{
							tmp_ss >> tmp_int;
							tmp_tag.push_back(tmp_int);
						}

						if (ele_tag != nullptr)
							ele_tag->push_back(tmp_tag);

						tmp_ss >> tmp_index[0] >> tmp_index[1] >> tmp_index[2] 
							>> tmp_index[3] >> tmp_index[4] >> tmp_index[5];

						if (packed == NotPacked)
						{
							for (int j = 0; j < 3; j++)
								tmp_tri.vert[j] = node.get(tmp_index[j]-1);
							
							tmp_tri.vert[5] = node.get(tmp_index[3]-1);
							tmp_tri.vert[3] = node.get(tmp_index[4]-1);
							tmp_tri.vert[4] = node.get(tmp_index[5]-1);
						}
						else
						{
							for (int j = 0; j < 3; j++)
								tmp_tri.vert[j] = node.get(tmp_index[j]);
							
							tmp_tri.vert[5] = node.get(tmp_index[3]);
							tmp_tri.vert[3] = node.get(tmp_index[4]);
							tmp_tri.vert[4] = node.get(tmp_index[5]);
						}
						element_vec.push_back(tmp_tri);

						ele_count++;
					}
				}

				//将元素转移到向量上来
				element.resize(element_vec.size());
				for (int i = 0; i < element.size(); i++)
				{
					element[i].id = element_vec[i].id;
					for (int j = 0; j < 6; j++)
						element[i].vert[j] = element_vec[i].vert[j];
				}
				destroy_vector(element_vec);
				break;
			}
		}
		return;
	}

	template <typename E, typename A>
	void read_gmsh_element(std::ifstream &infile, array<type_rectangle2d<E>> &element, 
		const array<vertex<point2dc, A>> &node, index_packed_e packed = Packed, 
		_2i_vector *ele_tag = nullptr)
	{
		if (node.empty())
			throw runtime_error("The input array is empty. From gctl::read_gmsh_element(...)");

		// 重置数据
		if (!element.empty()) element.clear();
		// 重置标签
		if (ele_tag != nullptr && !ele_tag->empty()) ele_tag->clear();

		// 将文件指针重置到文件头
		infile.clear(std::ios::goodbit);
		infile.seekg(std::ios::beg);

		int i_size = 0, ele_count = 0;
		int tmp_int, ele_type, attri_num;
		int tmp_index[4];
		std::string tmp_str;
		std::stringstream tmp_ss;
		while(getline(infile,tmp_str))
		{
			if (tmp_str == "$Elements") //读入模型空间顶点集 msh文件版本为2.2
			{
				getline(infile,tmp_str);
				gctl::str2ss(tmp_str, tmp_ss);
				tmp_ss >> i_size; //第一个数为顶点的个数

				// 我们先用一个临时的向量来储存元素
				rectangle2d tmp_rect;
				std::vector<rectangle2d> element_vec;
				std::vector<int> tmp_tag;

				for (int i = 0; i < i_size; i++)
				{
					getline(infile,tmp_str);
					str2ss(tmp_str, tmp_ss);
					tmp_ss >> tmp_int >> ele_type >> attri_num;
					if (ele_type == 3)
					{
						tmp_rect.id = ele_count;

						tmp_tag.clear();
						for (int a = 0; a < attri_num; a++)
						{
							tmp_ss >> tmp_int;
							tmp_tag.push_back(tmp_int);
						}

						if (ele_tag != nullptr)
							ele_tag->push_back(tmp_tag);

						tmp_ss >> tmp_index[0] >> tmp_index[1] >> tmp_index[2] >> tmp_index[3];
						if (packed == NotPacked)
						{
							tmp_rect.dl = node.get(tmp_index[0]-1);
							tmp_rect.ur = node.get(tmp_index[2]-1);
							for (int j = 0; j < 4; j++)
							{
								tmp_rect.vert[j] = node.get(tmp_index[j]-1);
							}
						}
						else
						{
							tmp_rect.dl = node.get(tmp_index[0]);
							tmp_rect.ur = node.get(tmp_index[2]);
							for (int j = 0; j < 4; j++)
								tmp_rect.vert[j] = node.get(tmp_index[j]);
						}
						element_vec.push_back(tmp_rect);

						ele_count++;
					}
				}

				//将元素转移到向量上来
				element.resize(element_vec.size());
				for (int i = 0; i < element.size(); i++)
				{
					element[i].id = element_vec[i].id;
					element[i].dl = element_vec[i].dl;
					element[i].ur = element_vec[i].ur;
					for (int j = 0; j < 4; j++)
						element[i].vert[j] = element_vec[i].vert[j];
				}
				destroy_vector(element_vec);
				break;
			}
		}
		return;
	}

	template <typename E, typename A>
	void read_gmsh_element(std::ifstream &infile, array<type_node<E>> &element, 
		const array<vertex<point3dc, A>> &node, index_packed_e packed = Packed, 
		_2i_vector *ele_tag = nullptr)
	{
		if (node.empty())
			throw runtime_error("The input array is empty. From gctl::read_gmsh_element(...)");

		// 重置数据
		if (!element.empty()) element.clear();
		// 重置标签
		if (ele_tag != nullptr && !ele_tag->empty()) ele_tag->clear();

		// 将文件指针重置到文件头
		infile.clear(std::ios::goodbit);
		infile.seekg(std::ios::beg);

		int i_size = 0, ele_count = 0;
		int tmp_int, ele_type, attri_num;
		int tmp_index;
		std::string tmp_str;
		std::stringstream tmp_ss;
		while(getline(infile,tmp_str))
		{
			if (tmp_str == "$Elements") //读入模型空间顶点集 msh文件版本为2.2
			{
				getline(infile,tmp_str);
				gctl::str2ss(tmp_str, tmp_ss);
				tmp_ss >> i_size; //第一个数为顶点的个数

				// 我们先用一个临时的向量来储存元素
				enode tmp_node;
				std::vector<enode> element_vec;
				std::vector<int> tmp_tag;

				for (int i = 0; i < i_size; i++)
				{
					getline(infile,tmp_str);
					str2ss(tmp_str, tmp_ss);
					tmp_ss >> tmp_int >> ele_type >> attri_num;
					if (ele_type == 15)
					{
						tmp_node.id = ele_count;

						tmp_tag.clear();
						for (int a = 0; a < attri_num; a++)
						{
							tmp_ss >> tmp_int;
							tmp_tag.push_back(tmp_int);
						}

						if (ele_tag != nullptr)
							ele_tag->push_back(tmp_tag);

						tmp_ss >> tmp_index;

						if (packed == NotPacked) tmp_node.vert[0] = node.get(tmp_index-1);
						else tmp_node.vert[0] = node.get(tmp_index);

						element_vec.push_back(tmp_node);
						ele_count++;
					}
				}

				//将元素转移到向量上来
				element.resize(element_vec.size());
				for (int i = 0; i < element.size(); i++)
				{
					element[i].id = element_vec[i].id;
					element[i].vert[0] = element_vec[i].vert[0];
				}
				destroy_vector(element_vec);
				break;
			}
		}
		return;
	}

	template <typename E, typename A>
	void read_gmsh_element(std::ifstream &infile, array<type_edge<E>> &element, 
		const array<vertex<point3dc, A>> &node, index_packed_e packed = Packed, 
		_2i_vector *ele_tag = nullptr)
	{
		if (node.empty())
			throw runtime_error("The input array is empty. From gctl::read_gmsh_element(...)");

		// 重置数据
		if (!element.empty()) element.clear();
		// 重置标签
		if (ele_tag != nullptr && !ele_tag->empty()) ele_tag->clear();

		// 将文件指针重置到文件头
		infile.clear(std::ios::goodbit);
		infile.seekg(std::ios::beg);

		int i_size = 0, ele_count = 0;
		int tmp_int, ele_type, attri_num;
		int tmp_index[2];
		std::string tmp_str;
		std::stringstream tmp_ss;
		while(getline(infile,tmp_str))
		{
			if (tmp_str == "$Elements") //读入模型空间顶点集 msh文件版本为2.2
			{
				getline(infile,tmp_str);
				gctl::str2ss(tmp_str, tmp_ss);
				tmp_ss >> i_size; //第一个数为顶点的个数

				// 我们先用一个临时的向量来储存元素
				edge tmp_tri;
				std::vector<edge> element_vec;
				std::vector<int> tmp_tag;

				for (int i = 0; i < i_size; i++)
				{
					getline(infile,tmp_str);
					str2ss(tmp_str, tmp_ss);
					tmp_ss >> tmp_int >> ele_type >> attri_num;
					if (ele_type == 1)
					{
						tmp_tri.id = ele_count;

						tmp_tag.clear();
						for (int a = 0; a < attri_num; a++)
						{
							tmp_ss >> tmp_int;
							tmp_tag.push_back(tmp_int);
						}

						if (ele_tag != nullptr)
							ele_tag->push_back(tmp_tag);

						tmp_ss >> tmp_index[0] >> tmp_index[1];

						if (packed == NotPacked)
						{
							for (int j = 0; j < 2; j++)
								tmp_tri.vert[j] = node.get(tmp_index[j]-1);
						}
						else
						{
							for (int j = 0; j < 2; j++)
								tmp_tri.vert[j] = node.get(tmp_index[j]);
						}
						element_vec.push_back(tmp_tri);

						ele_count++;
					}
				}

				//将元素转移到向量上来
				element.resize(element_vec.size());
				for (int i = 0; i < element.size(); i++)
				{
					element[i].id = element_vec[i].id;
					for (int j = 0; j < 2; j++)
						element[i].vert[j] = element_vec[i].vert[j];
				}
				destroy_vector(element_vec);
				break;
			}
		}
		return;
	}

	template <typename E, typename A>
	void read_gmsh_element(std::ifstream &infile, array<type_triangle<E>> &element, 
		const array<vertex<point3dc, A>> &node, index_packed_e packed = Packed, 
		_2i_vector *ele_tag = nullptr)
	{
		if (node.empty())
			throw runtime_error("The input array is empty. From gctl::read_gmsh_element(...)");

		// 重置数据
		if (!element.empty()) element.clear();
		// 重置标签
		if (ele_tag != nullptr && !ele_tag->empty()) ele_tag->clear();

		// 将文件指针重置到文件头
		infile.clear(std::ios::goodbit);
		infile.seekg(std::ios::beg);

		int i_size = 0, ele_count = 0;
		int tmp_int, ele_type, attri_num;
		int tmp_index[3];
		std::string tmp_str;
		std::stringstream tmp_ss;
		while(getline(infile,tmp_str))
		{
			if (tmp_str == "$Elements") //读入模型空间顶点集 msh文件版本为2.2
			{
				getline(infile,tmp_str);
				gctl::str2ss(tmp_str, tmp_ss);
				tmp_ss >> i_size; //第一个数为顶点的个数

				// 我们先用一个临时的向量来储存元素
				triangle tmp_tri;
				std::vector<triangle> element_vec;
				std::vector<int> tmp_tag;

				for (int i = 0; i < i_size; i++)
				{
					getline(infile,tmp_str);
					str2ss(tmp_str, tmp_ss);
					tmp_ss >> tmp_int >> ele_type >> attri_num;
					if (ele_type == 2)
					{
						tmp_tri.id = ele_count;

						tmp_tag.clear();
						for (int a = 0; a < attri_num; a++)
						{
							tmp_ss >> tmp_int;
							tmp_tag.push_back(tmp_int);
						}

						if (ele_tag != nullptr)
							ele_tag->push_back(tmp_tag);

						tmp_ss >> tmp_index[0] >> tmp_index[1] >> tmp_index[2];

						if (packed == NotPacked)
						{
							for (int j = 0; j < 3; j++)
								tmp_tri.vert[j] = node.get(tmp_index[j]-1);
						}
						else
						{
							for (int j = 0; j < 3; j++)
								tmp_tri.vert[j] = node.get(tmp_index[j]);
						}
						element_vec.push_back(tmp_tri);

						ele_count++;
					}
				}

				//将元素转移到向量上来
				element.resize(element_vec.size());
				for (int i = 0; i < element.size(); i++)
				{
					element[i].id = element_vec[i].id;
					for (int j = 0; j < 3; j++)
						element[i].vert[j] = element_vec[i].vert[j];
				}
				destroy_vector(element_vec);
				break;
			}
		}
		return;
	}

	template <typename E, typename A>
	void read_gmsh_element(std::ifstream &infile, array<type_tetrahedron<E>> &element, 
		const array<vertex<point3dc, A>> &node, index_packed_e packed = Packed, 
		_2i_vector *ele_tag = nullptr)
	{
		if (node.empty())
			throw runtime_error("The input array is empty. From gctl::read_gmsh_element(...)");

		// 重置数据
		if (!element.empty()) element.clear();
		// 重置标签
		if (ele_tag != nullptr && !ele_tag->empty()) ele_tag->clear();

		// 将文件指针重置到文件头
		infile.clear(std::ios::goodbit);
		infile.seekg(std::ios::beg);

		int i_size = 0, ele_count = 0;
		int tmp_int, ele_type, attri_num;
		int tmp_index[4];
		std::string tmp_str;
		std::stringstream tmp_ss;
		while(getline(infile,tmp_str))
		{
			if (tmp_str == "$Elements") //读入模型空间顶点集 msh文件版本为2.2
			{
				getline(infile,tmp_str);
				gctl::str2ss(tmp_str, tmp_ss);
				tmp_ss >> i_size; //第一个数为顶点的个数

				// 我们先用一个临时的向量来储存元素
				tetrahedron tmp_tet;
				std::vector<tetrahedron> element_vec;
				std::vector<int> tmp_tag;

				for (int i = 0; i < i_size; i++)
				{
					getline(infile,tmp_str);
					str2ss(tmp_str, tmp_ss);
					tmp_ss >> tmp_int >> ele_type >> attri_num;
					if (ele_type == 4)
					{
						tmp_tet.id = ele_count;

						tmp_tag.clear();
						for (int a = 0; a < attri_num; a++)
						{
							tmp_ss >> tmp_int;
							tmp_tag.push_back(tmp_int);
						}

						if (ele_tag != nullptr)
							ele_tag->push_back(tmp_tag);

						tmp_ss >> tmp_index[0] >> tmp_index[1] >> tmp_index[2] >> tmp_index[3];
						if (packed == NotPacked)
						{
							for (int j = 0; j < 4; j++)
								tmp_tet.vert[j] = node.get(tmp_index[j]-1);
						}
						else
						{
							for (int j = 0; j < 4; j++)
								tmp_tet.vert[j] = node.get(tmp_index[j]);
						}
						element_vec.push_back(tmp_tet);

						ele_count++;
					}
				}

				//将元素转移到向量上来
				element.resize(element_vec.size());
				for (int i = 0; i < element.size(); i++)
				{
					element[i].id = element_vec[i].id;
					for (int j = 0; j < 4; j++)
					{
						element[i].vert[j] = element_vec[i].vert[j];
					}
					element[i].deter_vert_order();
				}
				destroy_vector(element_vec);
				break;
			}
		}
		return;
	}

	template <typename E, typename A>
	void read_gmsh_element(std::ifstream &infile, array<type_block<E>> &element, 
		const array<vertex<point3dc, A>> &node, index_packed_e packed = Packed, 
		_2i_vector *ele_tag = nullptr)
	{
		if (node.empty())
			throw runtime_error("The input array is empty. From gctl::read_gmsh_element(...)");

		// 重置数据
		if (!element.empty()) element.clear();
		// 重置标签
		if (ele_tag != nullptr && !ele_tag->empty()) ele_tag->clear();

		// 将文件指针重置到文件头
		infile.clear(std::ios::goodbit);
		infile.seekg(std::ios::beg);

		int i_size = 0, ele_count = 0;
		int tmp_int, ele_type, attri_num;
		int tmp_index[8];
		std::string tmp_str;
		std::stringstream tmp_ss;
		while(getline(infile,tmp_str))
		{
			if (tmp_str == "$Elements") //读入模型空间顶点集 msh文件版本为2.2
			{
				getline(infile,tmp_str);
				gctl::str2ss(tmp_str, tmp_ss);
				tmp_ss >> i_size; //第一个数为顶点的个数

				// 我们先用一个临时的向量来储存元素
				block tmp_block;
				std::vector<block> element_vec;
				std::vector<int> tmp_tag;

				for (int i = 0; i < i_size; i++)
				{
					getline(infile,tmp_str);
					str2ss(tmp_str, tmp_ss);
					tmp_ss >> tmp_int >> ele_type >> attri_num;
					if (ele_type == 5)
					{
						tmp_block.id = ele_count;

						tmp_tag.clear();
						for (int a = 0; a < attri_num; a++)
						{
							tmp_ss >> tmp_int;
							tmp_tag.push_back(tmp_int);
						}

						if (ele_tag != nullptr)
							ele_tag->push_back(tmp_tag);

						for (int a = 0; a < 8; a++)
							tmp_ss >> tmp_index[a];

						if (packed == NotPacked)
						{
							tmp_block.dl = node.get(tmp_index[0]-1);
							tmp_block.ur = node.get(tmp_index[6]-1);
							for (int j = 0; j < 8; j++)
							{
								tmp_block.vert[j] = node.get(tmp_index[j]-1);
							}
						}
						else
						{
							tmp_block.dl = node.get(tmp_index[0]);
							tmp_block.ur = node.get(tmp_index[6]);
							for (int j = 0; j < 8; j++)
							{
								tmp_block.vert[j] = node.get(tmp_index[j]);
							}
						}
						element_vec.push_back(tmp_block);

						ele_count++;
					}
				}

				//将元素转移到向量上来
				element.resize(element_vec.size());
				for (int i = 0; i < element.size(); i++)
				{
					element[i].id = element_vec[i].id;
					element[i].dl = element_vec[i].dl;
					element[i].ur = element_vec[i].ur;
					for (int j = 0; j < 8; j++)
						element[i].vert[j] = element_vec[i].vert[j];
				}
				destroy_vector(element_vec);
				break;
			}
		}
		return;
	}

	template <typename E, typename A>
	void read_gmsh_element(std::ifstream &infile, array<type_tricone<E>> &element, 
		const array<vertex<point3dc, A>> &node, index_packed_e packed = Packed, 
		_2i_vector *ele_tag = nullptr)
	{
		if (node.empty())
			throw runtime_error("The input array is empty. From gctl::read_gmsh_element(...)");

		// 重置数据
		if (!element.empty()) element.clear();
		// 重置标签
		if (ele_tag != nullptr && !ele_tag->empty()) ele_tag->clear();

		// 将文件指针重置到文件头
		infile.clear(std::ios::goodbit);
		infile.seekg(std::ios::beg);

		int i_size = 0, ele_count = 0;
		int tmp_int, ele_type, attri_num;
		int tmp_index[3];
		std::string tmp_str;
		std::stringstream tmp_ss;
		while(getline(infile, tmp_str))
		{
			if (tmp_str == "$Elements") //读入模型空间顶点集 msh文件版本为2.2
			{
				getline(infile,tmp_str);
				gctl::str2ss(tmp_str, tmp_ss);
				tmp_ss >> i_size; //第一个数为顶点的个数

				// 我们先用一个临时的向量来储存元素
				tri_cone tmp_cone;
				std::vector<tri_cone> element_vec;
				std::vector<int> tmp_tag;

				for (int i = 0; i < i_size; i++)
				{
					getline(infile,tmp_str);
					str2ss(tmp_str, tmp_ss);
					tmp_ss >> tmp_int >> ele_type >> attri_num;
					if (ele_type == 2)
					{
						tmp_cone.id = ele_count;

						tmp_tag.clear();
						for (int a = 0; a < attri_num; a++)
						{
							tmp_ss >> tmp_int;
							tmp_tag.push_back(tmp_int);
						}

						if (ele_tag != nullptr)
							ele_tag->push_back(tmp_tag);

						tmp_ss >> tmp_index[0] >> tmp_index[1] >> tmp_index[2];

						if (packed == NotPacked)
						{
							for (int j = 0; j < 3; j++)
							{
								tmp_cone.vert[j] = node.get(tmp_index[j]-1);
							}
						}
						else
						{
							for (int j = 0; j < 3; j++)
							{
								tmp_cone.vert[j] = node.get(tmp_index[j]);
							}
						}
						element_vec.push_back(tmp_cone);

						ele_count++;
					}
				}

				//将元素转移到向量上来
				element.resize(element_vec.size());
				for (int i = 0; i < element.size(); i++)
				{
					element[i].id = element_vec[i].id;
					for (int j = 0; j < 3; j++)
						element[i].vert[j] = element_vec[i].vert[j];
				}
				destroy_vector(element_vec);
				break;
			}
		}
		return;
	}

	template <typename E, typename A>
	void read_gmsh_element(std::ifstream &infile, array<type_prism<E>> &element, 
		const array<vertex<point3dc, A>> &node, index_packed_e packed = Packed, 
		_2i_vector *ele_tag = nullptr)
	{
		if (node.empty())
			throw runtime_error("The input array is empty. From gctl::read_gmsh_element(...)");

		// 重置数据
		if (!element.empty()) element.clear();
		// 重置标签
		if (ele_tag != nullptr && !ele_tag->empty()) ele_tag->clear();

		// 将文件指针重置到文件头
		infile.clear(std::ios::goodbit);
		infile.seekg(std::ios::beg);

		int i_size = 0, ele_count = 0;
		int tmp_int, ele_type, attri_num;
		int tmp_index[6];
		std::string tmp_str;
		std::stringstream tmp_ss;
		while(getline(infile, tmp_str))
		{
			if (tmp_str == "$Elements") //读入模型空间顶点集 msh文件版本为2.2
			{
				getline(infile,tmp_str);
				gctl::str2ss(tmp_str, tmp_ss);
				tmp_ss >> i_size; //第一个数为顶点的个数

				// 我们先用一个临时的向量来储存元素
				prism tmp_prism;
				std::vector<prism> element_vec;
				std::vector<int> tmp_tag;

				for (int i = 0; i < i_size; i++)
				{
					getline(infile,tmp_str);
					str2ss(tmp_str, tmp_ss);
					tmp_ss >> tmp_int >> ele_type >> attri_num;
					if (ele_type == 6)
					{
						tmp_prism.id = ele_count;

						tmp_tag.clear();
						for (int a = 0; a < attri_num; a++)
						{
							tmp_ss >> tmp_int;
							tmp_tag.push_back(tmp_int);
						}

						if (ele_tag != nullptr)
							ele_tag->push_back(tmp_tag);

						tmp_ss >> tmp_index[0] >> tmp_index[1] >> tmp_index[2] 
							 >> tmp_index[3] >> tmp_index[4] >> tmp_index[5];

						if (packed == NotPacked)
						{
							for (int j = 0; j < 6; j++)
							{
								tmp_prism.vert[j] = node.get(tmp_index[j]-1);
							}
						}
						else
						{
							for (int j = 0; j < 6; j++)
							{
								tmp_prism.vert[j] = node.get(tmp_index[j]);
							}
						}
						element_vec.push_back(tmp_prism);

						ele_count++;
					}
				}

				//将元素转移到向量上来
				element.resize(element_vec.size());
				for (int i = 0; i < element.size(); i++)
				{
					element[i].id = element_vec[i].id;
					for (int j = 0; j < 6; j++)
						element[i].vert[j] = element_vec[i].vert[j];
				}
				destroy_vector(element_vec);
				break;
			}
		}
		return;
	}

	/**
	 * @brief      Read data block from a Gmsh .msh file.
	 *
	 * @param      infile     The input file stream
	 * @param      out_data   Output data name
	 * @param[in]  data_name  Name of the data block that will be output. The first data block will
	 * be output if this no data name is given.
	 */
	template <typename T>
	void read_gmsh_data(std::ifstream &infile, array<T> &out_data, std::string data_name = "")
	{
		std::string requst_name = "";
		if (data_name != "")
			requst_name = "\"" + data_name + "\"";

		// 将文件指针重置到文件头
		infile.clear(std::ios::goodbit);
		infile.seekg(std::ios::beg);

		bool data_found = false;
		int tmp_int, data_size;
		std::string tmp_str;
		std::stringstream tmp_ss;
		while(getline(infile, tmp_str))
		{
			if (tmp_str == "$NodeData" || tmp_str == "$ElementData")
			{
				//先读入元素块的名称 添加到数组
				for (int i = 0; i < 2; i++)
					getline(infile, tmp_str);

				if (requst_name == "" || requst_name == tmp_str)
				{
					data_found = true;
					//跳过元素属性前面的值 最后一次为当前元素块的个数
					for (int i = 0; i < 6; i++)
						getline(infile,tmp_str);
					gctl::str2ss(tmp_str, tmp_ss);
					tmp_ss >> data_size;
					out_data.resize(data_size);

					for (int i = 0; i < data_size; i++)
					{
						getline(infile,tmp_str);
						gctl::str2ss(tmp_str, tmp_ss);
						tmp_ss >> tmp_int >> out_data[i];
					}
					break;
				}
			}
		}

		if (!data_found)
		{
			throw runtime_error("data not found for: " + data_name + ". From void gctl::read_gmsh_data(..)");
		}
		return;
	}

	/**
	 * @brief      Read data block from a Gmsh .msh file.
	 *
	 * @param      infile     The input file stream
	 * @param      out_data   Output data name
	 * @param      out_index  Output data index
	 * @param[in]  data_name  Name of the data block that will be output. The first data block will
	 * be output if this no data name is given.
	 * @param[in]  packed     Indicates whether the index in the node file starts from zero. The 
	 * index is deemed to be started with one if this option is false. The default value of this 
	 * variable is true.
	 */
	template <typename T>
	void read_gmsh_data(std::ifstream &infile, array<T> &out_data, array<size_t> &out_index, 
		std::string data_name = "", index_packed_e packed = Packed)
	{
		std::string requst_name = "";
		if (data_name != "")
			requst_name = "\"" + data_name + "\"";

		// 将文件指针重置到文件头
		infile.clear(std::ios::goodbit);
		infile.seekg(std::ios::beg);

		bool data_found = false;
		int tmp_int, data_size;
		std::string tmp_str;
		std::stringstream tmp_ss;
		while(getline(infile, tmp_str))
		{
			if (tmp_str == "$NodeData" || tmp_str == "$ElementData")
			{
				//先读入元素块的名称 添加到数组
				for (int i = 0; i < 2; i++)
					getline(infile, tmp_str);

				if (requst_name == "" || requst_name == tmp_str)
				{
					data_found = true;
					//跳过元素属性前面的值 最后一次为当前元素块的个数
					for (int i = 0; i < 6; i++)
						getline(infile,tmp_str);
					gctl::str2ss(tmp_str, tmp_ss);
					tmp_ss >> data_size;
					out_data.resize(data_size);
					out_index.resize(data_size, -1);

					for (int i = 0; i < data_size; i++)
					{
						getline(infile,tmp_str);
						gctl::str2ss(tmp_str, tmp_ss);
						tmp_ss >> out_index[i] >> out_data[i];
					}

					if (packed == NotPacked)
					{
						for (int i = 0; i < data_size; i++)
							out_index[i] -= 1;
					}
					break;
				}
			}
		}

		if (!data_found)
		{
			throw runtime_error("data not found for: " + data_name + ". From void gctl::read_gmsh_data(..)");
		}
		return;
	}

	template <typename T>
	void read_gmsh_data(std::ifstream &infile, std::vector<std::vector<T> > &out_data, 
		std::vector<std::string> &out_name, mesh_data_type_e d_type, 
		std::vector<std::vector<size_t> > *out_index = nullptr, index_packed_e packed = NotPacked)
	{
		// 将文件指针重置到文件头
		infile.clear(std::ios::goodbit);
		infile.seekg(std::ios::beg);

		std::string tar_str;
		if (d_type == NodeData) tar_str = "$NodeData";
		else if (d_type == ElemData) tar_str = "$ElementData";
		else throw std::runtime_error("Invalid mesh data type. From gctl::read_gmsh_data(...)");

		int tmp_int, data_size;
		std::string tmp_str;
		std::stringstream tmp_ss;
		std::vector<T> tmp_data;
		std::vector<size_t> tmp_index;
		while(getline(infile, tmp_str))
		{
			if (tmp_str == tar_str)
			{
				//先读入元素块的名称 添加到数组
				for (int i = 0; i < 2; i++)
					getline(infile, tmp_str);
				
				tmp_str.erase(0, 1); // 删除第一个引号
				tmp_str.erase(tmp_str.end() - 1); // 删除末尾的引号
				out_name.push_back(tmp_str);

				//跳过元素属性前面的值 最后一次为当前元素块的个数
				for (int i = 0; i < 6; i++)
					getline(infile, tmp_str);
				
				gctl::str2ss(tmp_str, tmp_ss);
				tmp_ss >> data_size;
				tmp_data.clear();
				tmp_data.resize(data_size, 0.0);

				if(out_index != nullptr)
				{
					tmp_index.clear();
					tmp_index.resize(data_size);

					for (int i = 0; i < data_size; i++)
					{
						getline(infile, tmp_str);
						gctl::str2ss(tmp_str, tmp_ss);
						tmp_ss >> tmp_index[i] >> tmp_data[i];
					}

					if (packed == NotPacked)
					{
						for (size_t i = 0; i < data_size; i++)
						{
							tmp_index[i] -= 1;
						}
					}

					out_data.push_back(tmp_data);
					out_index->push_back(tmp_index);
				}
				else
				{
					for (int i = 0; i < data_size; i++)
					{
						getline(infile,tmp_str);
						gctl::str2ss(tmp_str, tmp_ss);
						tmp_ss >> tmp_int >> tmp_data[i];
					}

					out_data.push_back(tmp_data);
				}
			}
		}
		return;
	}

	/**
	 * @brief       Save Gmsh node block.
	 * 
	 * @warning    This function is designed for internal usage. Don't call this function directly.
	 * 
	 * @tparam A          Attribute type of the vertex
	 * @param outfile     Output file stream
	 * @param node_x      Pointer of the node array
	 * @param node_size   Size of the node array
	 * @param packed      Whether the nodes' indexes start from zero (Packed) or one (NotPacked)
	 * @param output_tag  Whether to output indexed vertex. A vertex will not be output if the tag is false.
	 * Note that passing a output_tag array will reset index of associated vertexes. Use this will caution.
	 */
	template <typename A>
	void save2gmsh_node(std::ofstream &outfile, vertex<point2dc, A> *node_x, int node_size, 
		index_packed_e packed = Packed, bool *output_tag = nullptr)
	{
		// 将文件指针重置到文件尾
		outfile.clear(std::ios::goodbit);
		outfile.seekp(0, std::ios::end);

		// it is better to set the first vertex index to 1 to avoid a display bug in Gmsh
		if (packed == Packed)
		{
			if (output_tag != nullptr)
			{
				int valid_count = 0;
				for (int i = 0; i < node_size; i++)
				{
					if (output_tag[i]) valid_count++;
				}

				int valid_id = 0;
				outfile << "$Nodes" << std::endl << valid_count << std::endl;
				for (int i = 0; i < node_size; i++)
				{
					if (output_tag[i])
					{
						node_x[i].id = valid_id; valid_id++;
						outfile << node_x[i].id << " " << std::setiosflags(std::ios::scientific) << std::setprecision(14)
							<< node_x[i].x << " " << node_x[i].y << " 0.0" << std::endl;
					}
				}
			}
			else
			{
				outfile << "$Nodes" << std::endl << node_size << std::endl;
				for (int i = 0; i < node_size; i++)
				{
					outfile << node_x[i].id << " " << std::setiosflags(std::ios::scientific) << std::setprecision(14)
						<< node_x[i].x << " " << node_x[i].y << " 0.0" << std::endl;
				}
			}
		}
		else
		{
			if (output_tag != nullptr)
			{
				int valid_count = 0;
				for (int i = 0; i < node_size; i++)
				{
					if (output_tag[i]) valid_count++;
				}

				int valid_id = 0;
				outfile << "$Nodes" << std::endl << valid_count << std::endl;
				for (int i = 0; i < node_size; i++)
				{
					if (output_tag[i])
					{
						node_x[i].id = valid_id; valid_id++;
						outfile << node_x[i].id + 1 << " " << std::setiosflags(std::ios::scientific) << std::setprecision(14)
							<< node_x[i].x << " " << node_x[i].y << " 0.0" << std::endl;
					}
				}
			}
			else
			{
				outfile << "$Nodes" << std::endl << node_size << std::endl;
				for (int i = 0; i < node_size; i++)
				{
					outfile << node_x[i].id + 1 << " " << std::setiosflags(std::ios::scientific) << std::setprecision(14)
						<< node_x[i].x << " " << node_x[i].y << " 0.0" << std::endl;
				}
			}
		}
		outfile<<"$EndNodes"<<std::endl;
		return;
	}

	/**
	 * @brief       Save Gmsh node block.
	 * 
	 * @warning    This function is designed for internal usage. Don't call this function directly.
	 * 
	 * @tparam A          Attribute type of the vertex
	 * @param outfile     Output file stream
	 * @param node_x      Pointer of the node array
	 * @param node_size   Size of the node array
	 * @param packed      Whether the nodes' indexes start from zero (Packed) or one (NotPacked)
	 * @param output_tag  Whether to output indexed vertex. A vertex will not be output if the tag is false.
	 * Note that passing a output_tag array will reset index of associated vertexes. Use this will caution.
	 */
	template <typename A>
	void save2gmsh_node(std::ofstream &outfile, vertex<point3dc, A> *node_x, int node_size,
		index_packed_e packed = Packed, bool *output_tag = nullptr)
	{
		// 将文件指针重置到文件尾
		outfile.clear(std::ios::goodbit);
		outfile.seekp(0, std::ios::end);

		// it is better to set the first vertex index to 1 to avoid a display bug in Gmsh
		if (packed == Packed)
		{
			if (output_tag != nullptr)
			{
				int valid_count = 0;
				for (int i = 0; i < node_size; i++)
				{
					if (output_tag[i]) valid_count++;
				}

				int valid_id = 0;
				outfile << "$Nodes" << std::endl << valid_count << std::endl;
				for (int i = 0; i < node_size; i++)
				{
					if (output_tag[i])
					{
						node_x[i].id = valid_id; valid_id++;
						outfile << node_x[i].id << " " << std::setiosflags(std::ios::scientific) << std::setprecision(14)
							<< node_x[i].x << " " << node_x[i].y << " " << node_x[i].z << std::endl;
					}
				}
			}
			else
			{
				outfile << "$Nodes" << std::endl << node_size << std::endl;
				for (int i = 0; i < node_size; i++)
				{
					outfile << node_x[i].id << " " << std::setiosflags(std::ios::scientific) << std::setprecision(14)
						<< node_x[i].x << " " << node_x[i].y << " " << node_x[i].z << std::endl;
				}
			}
		}
		else
		{
			if (output_tag != nullptr)
			{
				int valid_count = 0;
				for (int i = 0; i < node_size; i++)
				{
					if (output_tag[i]) valid_count++;
				}

				int valid_id = 0;
				outfile << "$Nodes" << std::endl << valid_count << std::endl;
				for (int i = 0; i < node_size; i++)
				{
					if (output_tag[i])
					{
						node_x[i].id = valid_id; valid_id++;
						outfile << node_x[i].id + 1 << " " << std::setiosflags(std::ios::scientific) << std::setprecision(14)
							<< node_x[i].x << " " << node_x[i].y << " " << node_x[i].z << std::endl;
					}
				}
			}
			else
			{
				outfile << "$Nodes" << std::endl << node_size << std::endl;
				for (int i = 0; i < node_size; i++)
				{
					outfile << node_x[i].id + 1 << " " << std::setiosflags(std::ios::scientific) << std::setprecision(14)
						<< node_x[i].x << " " << node_x[i].y << " " << node_x[i].z << std::endl;
				}
			}
		}
		outfile << "$EndNodes" << std::endl;
		return;
	}

	/**
	 * @brief      Save 2D/3D meshes to a Gmsh's mesh file.
	 *
	 * @param[in]  outfile    Output file stream.
	 * @param[in]  node_x     The vertex2dc array of nodes' coordinates.
	 * @param[in]  node_size  The number of node.
	 * @param[in]  ele_index  The triangle2d array of element's index.
	 * @param[in]  ele_size   The number of element.
	 * @param[in]  pack       Indicates whether to set the starting index of nodes to zero. The 
	 * node's ordering will start with one if this option is false. The default value of this 
	 * variable is true.
	 *
	 * @return     status of the function.
	 */
	template <typename E, typename A>
	void save2gmsh(std::ofstream &outfile, vertex<point2dc, A> *node_x, int node_size, 
		const type_triangle2d<E> *ele_index, int ele_size, index_packed_e packed = Packed)
	{
		// save head info
		// 将文件指针重置到文件头 保险一点
		outfile.clear(std::ios::goodbit);
		outfile.seekp(std::ios::beg);

		outfile << "$MeshFormat" << std::endl
		<< "2.2 0 8" << std::endl
		<< "$EndMeshFormat "<<std::endl;
		// save node
		save2gmsh_node(outfile, node_x, node_size, packed);
		// save elements
		outfile << "$Elements" << std::endl << ele_size <<std::endl;
		// it is better to set the first vertex index to 1 to avoid a display bug in Gmsh
		if (packed == Packed)
		{
			for (int i = 0; i < ele_size; i++)
			{
				outfile << i << " 2 0";
				for (int j = 0; j < 3; j++)
				{
					outfile << " " << ele_index[i].vert[j]->id;
				}
				outfile << std::endl;
			}
		}
		else
		{
			for (int i = 0; i < ele_size; i++)
			{
				outfile << i + 1 << " 2 0";
				for (int j = 0; j < 3; j++)
				{
					outfile << " " << ele_index[i].vert[j]->id + 1;
				}
				outfile << std::endl;
			}
		}
		outfile << "$EndElements"<< std::endl;
		return;
	}

	/**
	 * @brief      Save 2D/3D meshes to a Gmsh's mesh file.
	 *
	 * @param[in]  outfile    Output file stream.
	 * @param[in]  node_x     The vertex2dc array of nodes' coordinates.
	 * @param[in]  node_size  The number of node.
	 * @param[in]  ele_index  The triangle2d2o array of element's index.
	 * @param[in]  ele_size   The number of element.
	 * @param[in]  pack       Indicates whether to set the starting index of nodes to zero. The 
	 * node's ordering will start with one if this option is false. The default value of this 
	 * variable is true.
	 *
	 * @return     status of the function.
	 */
	template <typename E, typename A>
	void save2gmsh(std::ofstream &outfile, vertex<point2dc, A> *node_x, int node_size, 
		const type_triangle2d2o<E> *ele_index, int ele_size, index_packed_e packed = Packed)
	{
		// save head info
		// 将文件指针重置到文件头 保险一点
		outfile.clear(std::ios::goodbit);
		outfile.seekp(std::ios::beg);

		outfile << "$MeshFormat" << std::endl
		<< "2.2 0 8" << std::endl
		<< "$EndMeshFormat "<<std::endl;
		// save node
		save2gmsh_node(outfile, node_x, node_size, packed);
		// save elements
		outfile << "$Elements" << std::endl << ele_size <<std::endl;
		// it is better to set the first vertex index to 1 to avoid a display bug in Gmsh
		if (packed == Packed)
		{
			for (int i = 0; i < ele_size; i++)
			{
				outfile << i << " 9 0";
				for (int j = 0; j < 3; j++)
				{
					outfile << " " << ele_index[i].vert[j]->id;
				}
				outfile << " " << ele_index[i].vert[5]->id;
				outfile << " " << ele_index[i].vert[3]->id;
				outfile << " " << ele_index[i].vert[4]->id;
				outfile << std::endl;
			}
		}
		else
		{
			for (int i = 0; i < ele_size; i++)
			{
				outfile << i + 1 << " 9 0";
				for (int j = 0; j < 3; j++)
				{
					outfile << " " << ele_index[i].vert[j]->id + 1;
				}
				outfile << " " << ele_index[i].vert[5]->id + 1;
				outfile << " " << ele_index[i].vert[3]->id + 1;
				outfile << " " << ele_index[i].vert[4]->id + 1;
				outfile << std::endl;
			}
		}
		outfile << "$EndElements"<< std::endl;
		return;
	}

	template <typename E, typename A>
	void save2gmsh(std::ofstream &outfile, vertex<point2dc, A> *node_x, int node_size, 
		const type_rectangle2d<E> *ele_index, int ele_size, index_packed_e packed = Packed)
	{
		// save head info
		// 将文件指针重置到文件头 保险一点
		outfile.clear(std::ios::goodbit);
		outfile.seekp(std::ios::beg);

		outfile << "$MeshFormat" << std::endl
		<< "2.2 0 8" << std::endl
		<< "$EndMeshFormat "<<std::endl;
		// save node
		save2gmsh_node(outfile, node_x, node_size, packed);
		// save elements
		outfile << "$Elements" << std::endl << ele_size << std::endl;
		if (packed == Packed)
		{
			for (int i = 0; i < ele_size; i++)
			{
				outfile << i << " 3 0";
				for (int j = 0; j < 4; j++)
				{
					outfile << " " << ele_index[i].vert[j]->id;
				}
				outfile << std::endl;
			}
		}
		else
		{
			for (int i = 0; i < ele_size; i++)
			{
				outfile << i + 1 << " 3 0";
				for (int j = 0; j < 4; j++)
				{
					outfile << " " << ele_index[i].vert[j]->id + 1;
				}
				outfile << std::endl;
			}
		}
		outfile << "$EndElements"<< std::endl;
		return;
	}

	template <typename E, typename A>
	void save2gmsh(std::ofstream &outfile, vertex<point3dc, A> *node_x, int node_size, 
		const type_triangle<E> *ele_index, int ele_size, index_packed_e packed = Packed, 
		bool *out_ele_tag = nullptr, bool *out_node_tag = nullptr)
	{
		// Set the file stream to the head
		outfile.clear(std::ios::goodbit);
		outfile.seekp(std::ios::beg);
		// Output head info
		outfile << "$MeshFormat\n2.2 0 8\n$EndMeshFormat\n";
		// Count for valid output elements if out_ele_tag is not nullptr
		if (out_ele_tag != nullptr && out_node_tag != nullptr)
		{
			array<int> index_backup(node_size); // Backup nodes' indexes
			for (int i = 0; i < node_size; i++)
			{
				index_backup[i] = node_x[i].id;
			}

			array<bool> valid_tag(node_size, false);
			for (size_t i = 0; i < node_size; i++)
			{
				valid_tag[i] = out_node_tag[i];
			}
			
			for (int i = 0; i < ele_size; i++)
			{
				if (!valid_tag[i])
				{
					for (int j = 0; j < 3; j++)
					{
						valid_tag[ele_index[i].vert[j]->id] = false;
					}
				}
			}

			// Save nodes
			save2gmsh_node(outfile, node_x, node_size, packed, valid_tag.get());

			// Save elements
			int valid_ele_count = 0;
			for (int i = 0; i < ele_size; i++)
			{
				if (out_ele_tag[i])
				{
					valid_ele_count++;
				}
			}

			int valid_id = 0;
			outfile << "$Elements" << std::endl << valid_ele_count << std::endl;
			if (packed == Packed)
			{
				for (int i = 0; i < ele_size; i++)
				{
					if (out_ele_tag[i])
					{
						outfile << valid_id << " 2 0"; valid_id++;
						for (int j = 0; j < 3; j++)
						{
							outfile << " " << ele_index[i].vert[j]->id;
						}
						outfile << std::endl;
					}
				}
			}
			else
			{
				for (int i = 0; i < ele_size; i++)
				{
					if (out_ele_tag[i])
					{
						outfile << valid_id + 1 << " 2 0"; valid_id++;
						for (int j = 0; j < 3; j++)
						{
							outfile << " " << ele_index[i].vert[j]->id + 1;
						}
						outfile << std::endl;
					}
				}
			}
			outfile << "$EndElements" << std::endl;

			// Restore nodes' indexes
			for (int i = 0; i < node_size; i++)
			{
				node_x[i].id = index_backup[i];
			}
		}
		else if (out_ele_tag != nullptr)
		{
			array<int> index_backup(node_size); // Backup nodes' indexes
			for (int i = 0; i < node_size; i++)
			{
				index_backup[i] = node_x[i].id;
			}

			array<bool> valid_tag(node_size, false);
			for (int i = 0; i < ele_size; i++)
			{
				if (out_ele_tag[i])
				{
					for (int j = 0; j < 3; j++)
					{
						valid_tag[ele_index[i].vert[j]->id] = true;
					}
				}
			}

			// Save nodes
			save2gmsh_node(outfile, node_x, node_size, packed, valid_tag.get());

			// Save elements
			int valid_ele_count = 0;
			for (int i = 0; i < ele_size; i++)
			{
				if (out_ele_tag[i])
				{
					valid_ele_count++;
				}
			}

			int valid_id = 0;
			outfile << "$Elements" << std::endl << valid_ele_count << std::endl;
			if (packed == Packed)
			{
				for (int i = 0; i < ele_size; i++)
				{
					if (out_ele_tag[i])
					{
						outfile << valid_id << " 2 0"; valid_id++;
						for (int j = 0; j < 3; j++)
						{
							outfile << " " << ele_index[i].vert[j]->id;
						}
						outfile << std::endl;
					}
				}
			}
			else
			{
				for (int i = 0; i < ele_size; i++)
				{
					if (out_ele_tag[i])
					{
						outfile << valid_id + 1 << " 2 0"; valid_id++;
						for (int j = 0; j < 3; j++)
						{
							outfile << " " << ele_index[i].vert[j]->id + 1;
						}
						outfile << std::endl;
					}
				}
			}
			outfile << "$EndElements" << std::endl;

			// Restore nodes' indexes
			for (int i = 0; i < node_size; i++)
			{
				node_x[i].id = index_backup[i];
			}
		}
		else if (out_node_tag != nullptr)
		{
			array<int> index_backup(node_size); // Backup nodes' indexes
			for (int i = 0; i < node_size; i++)
			{
				index_backup[i] = node_x[i].id;
			}

			// Save nodes
			save2gmsh_node(outfile, node_x, node_size, packed, out_node_tag);

			// Save elements
			outfile << "$Elements" << std::endl << ele_size << std::endl;
			if (packed == Packed)
			{
				for (int i = 0; i < ele_size; i++)
				{
					outfile << i << " 2 0";
					for (int j = 0; j < 3; j++)
					{
						outfile << " " << ele_index[i].vert[j]->id;
					}
					outfile << std::endl;
				}
			}
			else
			{
				for (int i = 0; i < ele_size; i++)
				{
					outfile << i + 1 << " 2 0";
					for (int j = 0; j < 3; j++)
					{
						outfile << " " << ele_index[i].vert[j]->id + 1;
					}
					outfile << std::endl;
				}
			}
			outfile << "$EndElements" << std::endl;

			// Restore nodes' indexes
			for (int i = 0; i < node_size; i++)
			{
				node_x[i].id = index_backup[i];
			}
		}
		else
		{
			// Save nodes
			save2gmsh_node(outfile, node_x, node_size, packed);
			// Save elements
			outfile << "$Elements" << std::endl << ele_size << std::endl;
			if (packed == Packed)
			{
				for (int i = 0; i < ele_size; i++)
				{
					outfile << i << " 2 0";
					for (int j = 0; j < 3; j++)
					{
						outfile << " " << ele_index[i].vert[j]->id;
					}
					outfile << std::endl;
				}
			}
			else
			{
				for (int i = 0; i < ele_size; i++)
				{
					outfile << i + 1 << " 2 0";
					for (int j = 0; j < 3; j++)
					{
						outfile << " " << ele_index[i].vert[j]->id + 1;
					}
					outfile << std::endl;
				}
			}
			outfile << "$EndElements" << std::endl;
		}
		return;
	}

	template <typename E, typename A>
	void save2gmsh(std::ofstream &outfile, vertex<point3dc, A> *node_x, int node_size, 
		const type_tetrahedron<E> *ele_index, int ele_size, index_packed_e packed = Packed, 
		bool *output_tag = nullptr)
	{
		// Set the file stream to the head
		outfile.clear(std::ios::goodbit);
		outfile.seekp(std::ios::beg);
		// Output head info
		outfile << "$MeshFormat\n2.2 0 8\n$EndMeshFormat\n";
		// Count for valid output elements if output_tag is not nullptr
		if (output_tag != nullptr)
		{
			array<int> index_backup(node_size); // Backup nodes' indexes
			for (int i = 0; i < node_size; i++)
			{
				index_backup[i] = node_x[i].id;
			}

			array<bool> valid_tag(node_size, false);
			for (int i = 0; i < ele_size; i++)
			{
				if (output_tag[i])
				{
					for (int j = 0; j < 3; j++)
					{
						valid_tag[ele_index[i].vert[j]->id] = true;
					}
				}
			}

			// Save nodes
			save2gmsh_node(outfile, node_x, node_size, packed, valid_tag.get());

			// Save elements
			int valid_ele_count = 0;
			for (int i = 0; i < ele_size; i++)
			{
				if (output_tag[i])
				{
					valid_ele_count++;
				}
			}

			int valid_id = 0;
			outfile << "$Elements" << std::endl << valid_ele_count << std::endl;
			if (packed == Packed)
			{
				for (int i = 0; i < ele_size; i++)
				{
					if (output_tag[i])
					{
						outfile << valid_id << " 4 0"; valid_id++;
						for (int j = 0; j < 4; j++)
						{
							outfile << " " << ele_index[i].vert[j]->id;
						}
						outfile << std::endl;
					}
				}
			}
			else
			{
				for (int i = 0; i < ele_size; i++)
				{
					if (output_tag[i])
					{
						outfile << valid_id + 1 << " 4 0"; valid_id++;
						for (int j = 0; j < 4; j++)
						{
							outfile << " " << ele_index[i].vert[j]->id + 1;
						}
						outfile << std::endl;
					}
				}
			}
			outfile << "$EndElements"<< std::endl;

			// Restore nodes' indexes
			for (int i = 0; i < node_size; i++)
			{
				node_x[i].id = index_backup[i];
			}
		}
		else
		{
			// save node
			save2gmsh_node(outfile, node_x, node_size, packed);
			// save elements
			outfile << "$Elements" << std::endl << ele_size << std::endl;
			if (packed == Packed)
			{
				for (int i = 0; i < ele_size; i++)
				{
					outfile << i << " 4 0";
					for (int j = 0; j < 4; j++)
					{
						outfile << " " << ele_index[i].vert[j]->id;
					}
					outfile << std::endl;
				}
			}
			else
			{
				for (int i = 0; i < ele_size; i++)
				{
					outfile << i + 1 << " 4 0";
					for (int j = 0; j < 4; j++)
					{
						outfile << " " << ele_index[i].vert[j]->id + 1;
					}
					outfile << std::endl;
				}
			}
			outfile << "$EndElements"<< std::endl;
		}
		return;
	}

	template <typename E, typename A>
	void save2gmsh(std::ofstream &outfile, vertex<point3dc, A> *node_x, int node_size, 
		const type_block<E> *ele_index, int ele_size, index_packed_e packed = Packed)
	{
		// save head info
		// 将文件指针重置到文件头 保险一点
		outfile.clear(std::ios::goodbit);
		outfile.seekp(std::ios::beg);

		outfile << "$MeshFormat" << std::endl
		<< "2.2 0 8" << std::endl
		<< "$EndMeshFormat "<<std::endl;
		// save node
		save2gmsh_node(outfile, node_x, node_size, packed);
		// save elements
		outfile << "$Elements" << std::endl << ele_size << std::endl;
		if (packed == Packed)
		{
			for (int i = 0; i < ele_size; i++)
			{
				outfile << i << " 5 0";
				for (int j = 0; j < 8; j++)
				{
					outfile << " " << ele_index[i].vert[j]->id;
				}
				outfile << std::endl;
			}
		}
		else
		{
			for (int i = 0; i < ele_size; i++)
			{
				outfile << i + 1 << " 5 0";
				for (int j = 0; j < 8; j++)
				{
					outfile << " " << ele_index[i].vert[j]->id + 1;
				}
				outfile << std::endl;
			}
		}
		outfile << "$EndElements"<< std::endl;
		return;
	}

	template <typename E, typename A>
	void save2gmsh(std::ofstream &outfile, vertex<point3dc, A> *node_x, int node_size, 
		const type_tricone<E> *ele_index, int ele_size, index_packed_e packed = Packed)
	{
		// save head info
		// 将文件指针重置到文件头 保险一点
		outfile.clear(std::ios::goodbit);
		outfile.seekp(std::ios::beg);

		outfile << "$MeshFormat" << std::endl
		<< "2.2 0 8" << std::endl
		<< "$EndMeshFormat "<<std::endl;
		// save node
		save2gmsh_node(outfile, node_x, node_size, packed);
		// save elements
		outfile << "$Elements" << std::endl << ele_size << std::endl;
		if (packed == Packed)
		{
			for (int i = 0; i < ele_size; i++)
			{
				outfile << i << " 2 0";
				for (int j = 0; j < 3; j++)
				{
					outfile << " " << ele_index[i].vert[j]->id;
				}
				outfile << std::endl;
			}
		}
		else
		{
			for (int i = 0; i < ele_size; i++)
			{
				outfile << i + 1 << " 2 0";
				for (int j = 0; j < 3; j++)
				{
					outfile << " " << ele_index[i].vert[j]->id + 1;
				}
				outfile << std::endl;
			}
		}
		outfile << "$EndElements"<< std::endl;
		return;
	}

	template <typename E, typename A>
	void save2gmsh(std::ofstream &outfile, vertex<point3dc, A> *node_x, int node_size, 
		const type_prism<E> *ele_index, int ele_size, index_packed_e packed = Packed)
	{
		// save head info
		// 将文件指针重置到文件头 保险一点
		outfile.clear(std::ios::goodbit);
		outfile.seekp(std::ios::beg);

		outfile << "$MeshFormat" << std::endl
		<< "2.2 0 8" << std::endl
		<< "$EndMeshFormat "<<std::endl;
		// save node
		save2gmsh_node(outfile, node_x, node_size, packed);
		// save elements
		outfile << "$Elements" << std::endl << ele_size << std::endl;
		if (packed == Packed)
		{
			for (int i = 0; i < ele_size; i++)
			{
				outfile << i << " 6 0";
				for (int j = 0; j < 6; j++)
				{
					outfile << " " << ele_index[i].vert[j]->id;
				}
				outfile << std::endl;
			}
		}
		else
		{
			for (int i = 0; i < ele_size; i++)
			{
				outfile << i + 1 << " 6 0";
				for (int j = 0; j < 6; j++)
				{
					outfile << " " << ele_index[i].vert[j]->id + 1;
				}
				outfile << std::endl;
			}
		}
		outfile << "$EndElements"<< std::endl;
		return;
	}

	template <typename E, typename A>
	void save2gmsh(std::ofstream &outfile, vertex<point3dc, A> *node_x, int node_size, 
		const type_tesseroid<E> *ele_index, int ele_size, index_packed_e packed = Packed)
	{
		// save head info
		// 将文件指针重置到文件头 保险一点
		outfile.clear(std::ios::goodbit);
		outfile.seekp(std::ios::beg);

		outfile << "$MeshFormat" << std::endl
		<< "2.2 0 8" << std::endl
		<< "$EndMeshFormat "<<std::endl;
		// save node
		save2gmsh_node(outfile, node_x, node_size, packed);
		// save elements
		outfile << "$Elements" << std::endl << ele_size << std::endl;
		if (packed == Packed)
		{
			for (int i = 0; i < ele_size; i++)
			{
				outfile << i << " 5 0";
				for (int j = 0; j < 8; j++)
				{
					outfile << " " << ele_index[i].vert[j]->id;
				}
				outfile << std::endl;
			}
		}
		else
		{
			for (int i = 0; i < ele_size; i++)
			{
				outfile << i + 1 << " 5 0";
				for (int j = 0; j < 8; j++)
				{
					outfile << " " << ele_index[i].vert[j]->id + 1;
				}
				outfile << std::endl;
			}
		}
		outfile << "$EndElements"<< std::endl;
		return;
	}

	template <typename E>
	void save2gmsh(std::ofstream &outfile, const type_tetrahedron<E> *ele_index, int ele_size, index_packed_e packed = Packed)
	{
		// save head info
		// 将文件指针重置到文件头 保险一点
		outfile.clear(std::ios::goodbit);
		outfile.seekp(std::ios::beg);

		outfile << "$MeshFormat" << std::endl
		<< "2.2 0 8" << std::endl
		<< "$EndMeshFormat "<<std::endl;

		// sort node
		std::vector<vertex3dc*> node_index;
		node_index.reserve(4*ele_size);
		for (int i = 0; i < ele_size; ++i)
		{
			for (int j = 0; j < 4; ++j)
			{
				node_index.push_back(ele_index[i].vert[j]);
			}
		}

		std::vector<vertex3dc*>::iterator pos;
		std::sort(node_index.begin(), node_index.end()); //对顶点序列由小到大排序
		pos = std::unique(node_index.begin(), node_index.end()); //获取重复序列开始的位置
		node_index.erase(pos, node_index.end()); //删除重复点

		outfile << "$Nodes" << std::endl << node_index.size() << std::endl;
		// it is better to set the first vertex index to 1 to avoid a display bug in Gmsh
		if (packed == Packed)
		{
			for (int i = 0; i < node_index.size(); i++)
			{
				outfile << node_index[i]->id << " " << std::setprecision(16) 
				<< node_index[i]->x << " " << node_index[i]->y << " " << node_index[i]->z << std::endl;
			}
		}
		else
		{
			for (int i = 0; i < node_index.size(); i++)
			{
				outfile << node_index[i]->id + 1 << " " << std::setprecision(16) 
				<< node_index[i]->x << " " << node_index[i]->y << " " << node_index[i]->z << std::endl;
			}
		}
		outfile<<"$EndNodes"<<std::endl;

		destroy_vector(node_index);

		// save elements
		outfile << "$Elements" << std::endl << ele_size << std::endl;
		if (packed == Packed)
		{
			for (int i = 0; i < ele_size; i++)
			{
				outfile << i << " 4 0";
				for (int j = 0; j < 4; j++)
				{
					outfile << " " << ele_index[i].vert[j]->id;
				}
				outfile << std::endl;
			}
		}
		else
		{
			for (int i = 0; i < ele_size; i++)
			{
				outfile << i + 1 << " 4 0";
				for (int j = 0; j < 4; j++)
				{
					outfile << " " << ele_index[i].vert[j]->id + 1;
				}
				outfile << std::endl;
			}
		}
		outfile << "$EndElements"<< std::endl;
		return;
	}

	/**
	 * @brief      Save 2D/3D meshes to Gmsh .msh files.
	 *
	 * @param      outfile  Output file stream.
	 * @param[in]  element  Output element array
	 * @param[in]  node     Output vertice array
	 * @param[in]  packed   Indicates whether to set the starting index to zero. The 
	 * node and element's ordering will start with one if this option is false. 
	 * The default value of this variable is true.
	 */
	template <typename E, typename A>
	void save2gmsh(std::ofstream &outfile, const array<type_triangle2d<E>> &element, 
		const array<vertex<point2dc, A>> &node, index_packed_e packed = Packed)
	{
		save2gmsh(outfile, node.get(), node.size(), element.get(), element.size(), packed);
		return;
	}

	template <typename E, typename A>
	void save2gmsh(std::ofstream &outfile, const array<type_triangle2d2o<E>> &element, 
		const array<vertex<point2dc, A>> &node, index_packed_e packed = Packed)
	{
		save2gmsh(outfile, node.get(), node.size(), element.get(), element.size(), packed);
		return;
	}

	template <typename E, typename A>
	void save2gmsh(std::ofstream &outfile, const array<type_rectangle2d<E>> &element, 
		const array<vertex<point2dc, A>> &node, index_packed_e packed = Packed)
	{
		save2gmsh(outfile, node.get(), node.size(), element.get(), element.size(), packed);
		return;
	}

	template <typename E, typename A>
	void save2gmsh(std::ofstream &outfile, const array<type_triangle<E>> &element, 
		const array<vertex<point3dc, A>> &node, index_packed_e packed = Packed, 
		array<bool> *out_ele_tag = nullptr, array<bool> *out_node_tag = nullptr)
	{
		if (out_ele_tag != nullptr && out_node_tag != nullptr)
		{
			if (element.size() != out_ele_tag->size() || node.size() != out_node_tag->size())
			{
				throw gctl::runtime_error("Incompatible tag size. From save2gmsh(...)");
				return;
			}

			save2gmsh(outfile, node.get(), node.size(), element.get(), element.size(), packed, out_ele_tag->get(), out_node_tag->get());
		}
		else if (out_ele_tag != nullptr)
		{
			if (element.size() != out_ele_tag->size())
			{
				throw gctl::runtime_error("Incompatible tag size. From save2gmsh(...)");
				return;
			}

			save2gmsh(outfile, node.get(), node.size(), element.get(), element.size(), packed, out_ele_tag->get());
		}
		else if (out_node_tag != nullptr)
		{
			if (node.size() != out_node_tag->size())
			{
				throw gctl::runtime_error("Incompatible tag size. From save2gmsh(...)");
				return;
			}

			save2gmsh(outfile, node.get(), node.size(), element.get(), element.size(), packed, nullptr, out_node_tag->get());
		}
		else
		{
			save2gmsh(outfile, node.get(), node.size(), element.get(), element.size(), packed);
		}
		return;
	}

	template <typename E, typename A>
	void save2gmsh(std::ofstream &outfile, const array<type_tetrahedron<E>> &element, 
		const array<vertex<point3dc, A>> &node, index_packed_e packed = Packed, 
		array<bool> *output_tag = nullptr)
	{
		if (output_tag != nullptr)
		{
			if (element.size() != output_tag->size())
			{
				throw gctl::runtime_error("Incompatible tag size. From save2gmsh(...)");
				return;
			}

			save2gmsh(outfile, node.get(), node.size(), element.get(), element.size(), packed, output_tag->get());
		}
		else
		{
			save2gmsh(outfile, node.get(), node.size(), element.get(), element.size(), packed);
		}
		return;
	}

	template <typename E, typename A>
	void save2gmsh(std::ofstream &outfile, const array<type_block<E>> &element, 
		const array<vertex<point3dc, A>> &node, index_packed_e packed = Packed)
	{
		save2gmsh(outfile, node.get(), node.size(), element.get(), element.size(), packed);
		return;
	}

	template <typename E, typename A>
	void save2gmsh(std::ofstream &outfile, const array<type_tricone<E>> &element, 
		const array<vertex<point3dc, A>> &node, index_packed_e packed = Packed)
	{
		save2gmsh(outfile, node.get(), node.size(), element.get(), element.size(), packed);
		return;
	}

	template <typename E, typename A>
	void save2gmsh(std::ofstream &outfile, const array<type_prism<E>> &element, 
		const array<vertex<point3dc, A>> &node, index_packed_e packed = Packed)
	{
		save2gmsh(outfile, node.get(), node.size(), element.get(), element.size(), packed);
		return;
	}

	template <typename E, typename A>
	void save2gmsh(std::ofstream &outfile, const array<type_tesseroid<E>> &element, 
		const array<vertex<point3dc, A>> &node, index_packed_e packed = Packed)
	{
		save2gmsh(outfile, node.get(), node.size(), element.get(), element.size(), packed);
		return;
	}

	template <typename E>
	void save2gmsh(std::ofstream &outfile, const array<type_tetrahedron<E>> &element, 
		index_packed_e packed = Packed)
	{
		save2gmsh(outfile, element.get(), element.size(), packed);
		return;
	}

	/**
	 * @brief      Save a Gmsh data block to a given file.
	 *
	 * @param[in]  filename   The filename of the file that in_data is going to be added into.
	 * @param[in]  dataname   Name of the data that is going to be saved.
	 * @param[in]  in_data    Pointer of the double array of the input data.
	 * @param[in]  d_size     Number of the input data.
	 * @param[in]  in_index   Pointer of the int array of the input data's index. The size of in_index 
	 * must be equal to in_data.
	 * @param[in]  data_type  The data type to be save. This enum could be equals to NODE_DATA 
	 * or ELEMENT_DATA.
	 * @param[in]  pack       Indicates whether to set the starting index of data to zero. 
	 * The data ordering will start with one if this option is false. The default value of 
	 * this variable is true.
	 *
	 * @return     status of the function
	 */
	template <typename T>
	void save_gmsh_data(std::ofstream &outfile, std::string dataname, const T *in_data, 
		int d_size, mesh_data_type_e datatype, index_packed_e packed = Packed, const size_t *in_index = nullptr)
	{
		if (in_data == nullptr)
		{
			throw runtime_error("Null data pointer. From void gctl::save_gmsh_data(...)");
		}

		if (d_size <= 0)
		{
			throw runtime_error("Invalid array size. From void gctl::save_gmsh_data(...)");
		}

		// 将文件指针重置到文件尾
		outfile.clear(std::ios::goodbit);
		outfile.seekp(0, std::ios::end);

		if (datatype == NodeData)
			outfile<<"$NodeData"<<std::endl;
		else if (datatype == ElemData || datatype == ElemData2D || datatype == ElemData3D)
			outfile<<"$ElementData"<<std::endl;

		int valid_size = 0;
		for (int i = 0; i < d_size; i++)
		{
			if (!std::isnan(in_data[i])) valid_size++;
		}

		outfile<<1<<std::endl
		<<"\"" << dataname << "\"" <<std::endl
		<<1<<std::endl<<0.0<<std::endl
		<<3<<std::endl<<0<<std::endl
		<<1<<std::endl<<valid_size<<std::endl;

		if (packed == Packed && in_index != nullptr)
		{
			for (int j = 0; j < d_size; j++)
			{
				if (!std::isnan(in_data[j]))
					outfile << in_index[j] << " " << std::setiosflags(std::ios::scientific) <<  std::setprecision(14) << in_data[j] << std::endl;
			}
		}
		else if (packed == NotPacked && in_index != nullptr)
		{
			for (int j = 0; j < d_size; j++)
			{
				if (!std::isnan(in_data[j]))
					outfile << in_index[j]+1 << " " << std::setiosflags(std::ios::scientific) <<  std::setprecision(14) << in_data[j] << std::endl;
			}
		}
		else if (packed == Packed && in_index == nullptr)
		{
			for (int j = 0; j < d_size; j++)
			{
				if (!std::isnan(in_data[j]))
					outfile << j << " " << std::setiosflags(std::ios::scientific) <<  std::setprecision(14) << in_data[j] << std::endl;
			}
		}
		else
		{
			for (int j = 0; j < d_size; j++)
			{
				if (!std::isnan(in_data[j]))
					outfile << j+1 << " " << std::setiosflags(std::ios::scientific) <<  std::setprecision(14) << in_data[j] << std::endl;
			}
		}

		if (datatype == NodeData)
			outfile << "$EndNodeData" << std::endl;
		else
			outfile << "$EndElementData"<< std::endl;
		return;
	}

	/**
	 * @brief      保存Gmsh模型数据单元
	 *
	 * @param      outfile   输出文件流
	 * @param[in]  dataname  模型数据名称
	 * @param[in]  in_data   模型数据数组
	 * @param[in]  datatype  模型数据类型
	 * @param[in]  in_index  模型数据索引数组的指针，为空时则依序储存模型数据数组
	 * @param[in]  pack      输出模型数组索引是否从0开始，为假时索引值从1开始
	 */
	template <typename T>
	void save_gmsh_data(std::ofstream &outfile, std::string dataname, const array<T> &in_data, 
		mesh_data_type_e datatype, index_packed_e packed = Packed, const array<size_t> *in_index = nullptr)
	{
		if (in_index != nullptr)
		{
			save_gmsh_data(outfile, dataname, in_data.get(), in_data.size(), datatype, packed, in_index->get());
		}
		else save_gmsh_data(outfile, dataname, in_data.get(), in_data.size(), datatype, packed);
		return;
	}
	
	/**
	 * @brief      保存Gmsh模型数据单元
	 *
	 * @param      outfile   输出文件流
	 * @param[in]  dataname  模型数据名称
	 * @param[in]  in_data   模型数据数组
	 * @param[in]  datatype  模型数据类型
	 * @param[in]  in_index  模型数据索引数组的指针，为空时则依序储存模型数据数组
	 * @param[in]  pack      输出模型数组索引是否从0开始，为假时索引值从1开始
	 */
	template <typename T>
	void save_gmsh_data(std::ofstream &outfile, std::string dataname, const std::vector<T> &in_data, 
		mesh_data_type_e datatype, index_packed_e packed = Packed, const std::vector<size_t> *in_index = nullptr)
	{
		if (in_index != nullptr)
		{
			save_gmsh_data(outfile, dataname, in_data.data(), in_data.size(), datatype, packed, in_index->data());
		}
		else save_gmsh_data(outfile, dataname, in_data.data(), in_data.size(), datatype, packed);
		return;
	}

	/**
	 * @brief      Save a Gmsh data block to a given file.
	 *
	 * @param[in]  filename   The filename of the file that in_data is going to be added into.
	 * @param[in]  dataname   Name of the data that is going to be saved.
	 * @param[in]  in_data    Pointer of the point3dc array of the input data.
	 * @param[in]  d_size     Number of the input data.
	 * @param[in]  in_index   Pointer of the int array of the input data's index. The size of in_index 
	 * must be equal to in_data.
	 * @param[in]  data_type  The data type to be save. This enum could be equals to NODE_DATA 
	 * or ELEMENT_DATA.
	 * @param[in]  pack       Indicates whether to set the starting index of data to zero. 
	 * The data ordering will start with one if this option is false. The default value of 
	 * this variable is true.
	 *
	 * @return     status of the function
	 */
	template <typename T>
	void save_gmsh_data(std::ofstream &outfile, std::string dataname, const point3c<T> *in_data, 
		int d_size, mesh_data_type_e datatype, index_packed_e packed = Packed, const size_t *in_index = nullptr)
	{
		if (in_data == nullptr)
		{
			throw runtime_error("Null data pointer. From void gctl::save_gmsh_data(...)");
		}

		if (d_size <= 0)
		{
			throw runtime_error("Invalid array size. From void gctl::save_gmsh_data(...)");
		}

		// 将文件指针重置到文件尾
		outfile.clear(std::ios::goodbit);
		outfile.seekp(0, std::ios::end);

		if (datatype == NodeData)
			outfile<<"$NodeData"<<std::endl;
		else if (datatype == ElemData || datatype == ElemData2D || datatype == ElemData3D)
			outfile<<"$ElementData"<<std::endl;

		int valid_size = 0;
		for (int i = 0; i < d_size; i++)
		{
			if (in_data[i].valid()) valid_size++;
		}

		outfile<<1<<std::endl
		<<"\"" << dataname << "\"" <<std::endl
		<<1<<std::endl<<0.0<<std::endl
		<<3<<std::endl<<0<<std::endl
		<<3<<std::endl<<valid_size<<std::endl;

		if (packed == Packed && in_index != nullptr)
		{
			for (int j = 0; j < d_size; j++)
			{
				if (in_data[j].valid())
				{
					outfile << in_index[j] << " " << std::setiosflags(std::ios::scientific) <<  std::setprecision(14) 
						<< in_data[j].x << " " << in_data[j].y << " " << in_data[j].z << std::endl;
				}
			}
		}
		else if (packed == NotPacked && in_index != nullptr)
		{
			for (int j = 0; j < d_size; j++)
			{
				if (in_data[j].valid())
				{
					outfile << in_index[j]+1 << " " << std::setiosflags(std::ios::scientific) <<  std::setprecision(14) 
						<< in_data[j].x << " " << in_data[j].y << " " << in_data[j].z << std::endl;
				}
			}
		}
		else if (packed == Packed && in_index == nullptr)
		{
			for (int j = 0; j < d_size; j++)
			{
				if (in_data[j].valid())
				{
					outfile << j << " " << std::setiosflags(std::ios::scientific) <<  std::setprecision(14) 
						<< in_data[j].x << " " << in_data[j].y << " " << in_data[j].z << std::endl;
				}
			}
		}
		else
		{
			for (int j = 0; j < d_size; j++)
			{
				if (in_data[j].valid())
				{
					outfile << j+1 << " " << std::setiosflags(std::ios::scientific) <<  std::setprecision(14) 
						<< in_data[j].x << " " << in_data[j].y << " " << in_data[j].z << std::endl;
				}
			}
		}

		if (datatype == NodeData)
			outfile << "$EndNodeData" << std::endl;
		else
			outfile << "$EndElementData"<< std::endl;
		return;
	}

	/**
	 * @brief      保存Gmsh模型数据单元
	 *
	 * @param      outfile   输出文件流
	 * @param[in]  dataname  模型数据名称
	 * @param[in]  in_data   模型数据数组
	 * @param[in]  datatype  模型数据类型
	 * @param[in]  in_index  模型数据索引数组的指针，为空时则依序储存模型数据数组
	 * @param[in]  pack      输出模型数组索引是否从0开始，为假时索引值从1开始
	 */
	template <typename T>
	void save_gmsh_data(std::ofstream &outfile, std::string dataname, const array<point3c<T>> &in_data, 
		mesh_data_type_e datatype, index_packed_e packed = Packed, const array<size_t> *in_index = nullptr)
	{
		if (in_index != nullptr)
		{
			save_gmsh_data(outfile, dataname, in_data.get(), in_data.size(), datatype, packed, in_index->get());
		}
		else save_gmsh_data(outfile, dataname, in_data.get(), in_data.size(), datatype, packed);
		return;
	}

	/**
	 * @brief      保存Gmsh模型数据单元
	 *
	 * @param      outfile   输出文件流
	 * @param[in]  dataname  模型数据名称
	 * @param[in]  in_data   模型数据数组
	 * @param[in]  datatype  模型数据类型
	 * @param[in]  in_index  模型数据索引数组的指针，为空时则依序储存模型数据数组
	 * @param[in]  pack      输出模型数组索引是否从0开始，为假时索引值从1开始
	 */
	template <typename T>
	void save_gmsh_data(std::ofstream &outfile, std::string dataname, const std::vector<point3c<T>> &in_data, 
		mesh_data_type_e datatype, index_packed_e packed = Packed, const std::vector<size_t> *in_index = nullptr)
	{
		if (in_index != nullptr)
		{
			save_gmsh_data(outfile, dataname, in_data.data(), in_data.size(), datatype, packed, in_index->data());
		}
		else save_gmsh_data(outfile, dataname, in_data.data(), in_data.size(), datatype, packed);
		return;
	}

#ifdef GCTL_EIGEN

	template <typename T> 
	void save_gmsh_data(std::ofstream &outfile, std::string dataname, const Eigen::Matrix<T, Eigen::Dynamic, 1> &in_data, 
		mesh_data_type_e datatype, index_packed_e packed = Packed, const Eigen::Matrix<T, Eigen::Dynamic, 1> *in_index = nullptr)
	{
		size_t d_size = in_data.size();
		if (d_size <= 0)
		{
			throw runtime_error("Invalid array size. From void gctl::save_gmsh_data(...)");
		}

		// 将文件指针重置到文件尾
		outfile.clear(std::ios::goodbit);
		outfile.seekp(0, std::ios::end);

		if (datatype == NodeData)
			outfile<<"$NodeData"<<std::endl;
		else if (datatype == ElemData || datatype == ElemData2D || datatype == ElemData3D)
			outfile<<"$ElementData"<<std::endl;

		int valid_size = 0;
		for (int i = 0; i < d_size; i++)
		{
			if (!std::isnan(in_data[i])) valid_size++;
		}

		outfile<<1<<std::endl
		<<"\"" << dataname << "\"" <<std::endl
		<<1<<std::endl<<0.0<<std::endl
		<<3<<std::endl<<0<<std::endl
		<<1<<std::endl<<valid_size<<std::endl;

		if (packed == Packed && in_index != nullptr)
		{
			for (int j = 0; j < d_size; j++)
			{
				if (!std::isnan(in_data[j]))
					outfile << in_index->coeff(j) << " " << std::setiosflags(std::ios::scientific) <<  std::setprecision(14) << in_data[j] << std::endl;
			}
		}
		else if (packed == NotPacked && in_index != nullptr)
		{
			for (int j = 0; j < d_size; j++)
			{
				if (!std::isnan(in_data[j]))
					outfile << in_index->coeff(j)+1 << " " << std::setiosflags(std::ios::scientific) <<  std::setprecision(14) << in_data[j] << std::endl;
			}
		}
		else if (packed == Packed && in_index == nullptr)
		{
			for (int j = 0; j < d_size; j++)
			{
				if (!std::isnan(in_data[j]))
					outfile << j << " " << std::setiosflags(std::ios::scientific) <<  std::setprecision(14) << in_data[j] << std::endl;
			}
		}
		else
		{
			for (int j = 0; j < d_size; j++)
			{
				if (!std::isnan(in_data[j]))
					outfile << j+1 << " " << std::setiosflags(std::ios::scientific) <<  std::setprecision(14) << in_data[j] << std::endl;
			}
		}

		if (datatype == NodeData)
			outfile << "$EndNodeData" << std::endl;
		else
			outfile << "$EndElementData"<< std::endl;
		return;
	}

#endif // GCTL_EIGEN

};

#endif //_GCTL_GMSH_IO_H