/********************************************************
 *  ██████╗  ██████╗████████╗██╗
 * ██╔════╝ ██╔════╝╚══██╔══╝██║
 * ██║  ███╗██║        ██║   ██║
 * ██║   ██║██║        ██║   ██║
 * ╚██████╔╝╚██████╗   ██║   ███████╗
 *  ╚═════╝  ╚═════╝   ╚═╝   ╚══════╝
 * 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_ARRAY_H
#define _GCTL_ARRAY_H

#include <random>
#include <chrono>

// library's head files
#include "../gctl_config.h"
#include "enum.h"
#include "macro.h"
#include "vector_t.h"
#include "exceptions.h"

#ifdef GCTL_EIGEN
/*The followings bypass a bug that VsCode's IntelliSense reports incorrect errors when using eigen3 library*/
#if __INTELLISENSE__
#undef __ARM_NEON
#undef __ARM_NEON__
#endif
#include "Eigen/Dense"
#include "Eigen/Sparse"
#endif // GCTL_EIGEN

#ifdef GCTL_OPENMP
#include "omp.h"
#endif // GCTL_OPENMP

namespace gctl
{
	template <typename ArrValType> class array;

	/*
	 * Here we define some commonly used array types.
	 */
	typedef array<int> _1i_array; ///< 1-D integer array
	typedef array<float> _1f_array; ///< 1-D single-precision floating-point array
	typedef array<double> _1d_array; ///< 1-D double-precision floating-point array
	typedef array<std::string> _1s_array; ///< 1-D string array
	typedef array<std::complex<float> > _1cf_array; ///< 1-D complex float array
	typedef array<std::complex<double> > _1cd_array; ///< 1-D complex double array

	/**
	 * @brief Iterator template. This could be used to enable the use of iteration algorithms for array-like objects.
	 * 
	 */
	template <typename T>
	class iterator
	{
	private:
		T *iter;

	public:
		iterator(T *para, size_t n) {iter = para + n;}
		virtual ~iterator(){}

		T &operator*() {return *iter;}
		bool operator!=(const iterator &that) {return this->iter != that.iter;}
		iterator &operator++() {++iter; return *this;}		
	};

	/**
	 * @brief      1-D array class template
	 *
	 * @tparam     ArrValType     template array
	 */
	template <typename ArrValType>
	class array
	{
	protected:
		ArrValType *val_; ///< Pointer of the array
		size_t length_; ///< Length of the array

	public:
		/**
		 * @brief      Default constructor.
		 */
		array();

		/**
		 * @brief      Construct an array with the given length.
		 *
		 * @param[in]  len   Length of the array. Must be equal to or bigger than zero.
		 */
		explicit array(size_t len);

		/**
		 * @brief      Construct an array with the given length and initial values.
		 *
		 * @param[in]  len       Length of the array. Must be equal to or bigger than zero.
		 * @param[in]  init_val  Initial value of the elements.
		 */
		array(size_t len, ArrValType init_val);

		/**
		 * @brief      Construct an array with the given length and fill elements using a native c++ array.
		 * 
		 * @note       Note that if you use an native c++ array to initialize the array template, the pointer 
		 * must be first variable and the length is last variable. This design is mainly to avoid unclear function 
		 * calls. Consider an array of pointers array<double*> A(10, nullptr) means that the initial value of all 
		 * elements is nullptr and array<double*> A((double**)B, 10) means that the element value of A is initialized 
		 * with array B If the above expression is written as array<double*> A(10, B), the compiler cannot call the 
		 * function correctly. Because it cannot be judged whether we want to use the initial value or 
		 * the array to initialize the array.
		 *
		 * @param      init_array  Pointer of the native c++ array.
		 * @param[in]  len         Length of the input c++ array. Must be equal to or bigger than zero.
		 */
		array(ArrValType *init_array, size_t len);

		/**
		 * @brief      Copy constructor.
		 *
		 * @param[in]  b     Original array.
		 */
		array(const array<ArrValType> &b);

		/**
		 * @brief Construct a new array object from std::initializer
		 * 
		 * @param init_val Initial values
		 */
		explicit array(std::initializer_list<ArrValType> init_val);

		/**
		 * @brief Construct a new array object as a sequence
		 * 
		 * @param s Size of the sequence.
		 * @param st Start value
		 * @param inc Invrease interval
		 */
		array(size_t s, ArrValType st, ArrValType inc);

		/**
		 * @brief      Overloaded assignment operator for the array template.
		 *
		 * @param[in]  b     Original array.
		 *
		 * @return     Target array.
		 */
		array<ArrValType>& operator= (const array<ArrValType> &b);

		/**
		 * @brief      Overloaded assignment operator for the array template.
		 * 
		 * @param v          Initial value.
		 * @return     Target array.
		 */
		array<ArrValType>& operator= (ArrValType v);

		/**
		 * @brief      Overloaded summation operator for the array template.
		 * 
		 * @param a    Input array
		 * @param b    Inpur array
		 * @return Target array
		 */
		array<ArrValType> operator+ (const array<ArrValType> &b);

		/**
		 * @brief      Overloaded minus operator for the array template.
		 * 
		 * @param a    Input array
		 * @param b    Inpur array
		 * @return Target array
		 */
		array<ArrValType> operator- (const array<ArrValType> &b);

		/**
		 * @brief      Overloaded multiple operator for the array template.
		 * 
		 * @param a    Input array
		 * @param b    Inpur array
		 * @return Target array
		 */
		array<ArrValType> operator* (const array<ArrValType> &b);

		/**
		 * @brief      Overloaded division operator for the array template.
		 * 
		 * @param a    Input array
		 * @param b    Inpur array
		 * @return Target array
		 */
		array<ArrValType> operator/ (const array<ArrValType> &b);

		/**
		 * @brief      Overloaded summation operator for the array template.
		 * 
		 * @param a    Input array
		 * @param b    Inpur array
		 * @return Target array
		 */
		array<ArrValType>& operator+= (const array<ArrValType> &b);

		/**
		 * @brief      Overloaded summation operator for the array template.
		 * 
		 * @param a    Input array
		 * @param b    Inpur array
		 * @return Target array
		 */
		array<ArrValType>& operator-= (const array<ArrValType> &b);

		/**
		 * @brief      Overloaded multiple operator for the array template.
		 * 
		 * @param a    Input array
		 * @param b    Inpur array
		 * @return Target array
		 */
		array<ArrValType>& operator*= (const array<ArrValType> &b);

		/**
		 * @brief      Overloaded division operator for the array template.
		 * 
		 * @param a    Input array
		 * @param b    Inpur array
		 * @return Target array
		 */
		array<ArrValType>& operator/= (const array<ArrValType> &b);

		/**
		 * @brief      Overloaded summation operator for the array template.
		 *
		 * @param a    Input array
		 * @param b    Scale value
		 * @return Target array
		 */
		array<ArrValType> operator+ (const ArrValType &b);

		/**
		 * @brief      Overloaded minus operator for the array template.
		 *
		 * @param a    Input array
		 * @param b    Scale value
		 * @return Target array
		 */
		array<ArrValType> operator- (const ArrValType &b);

		/**
		 * @brief      Overloaded multiple operator for the array template.
		 *
		 * @param a    Input array
		 * @param b    Scale value
		 * @return Target array
		 */
		array<ArrValType> operator* (const ArrValType &b);

		/**
		 * @brief      Overloaded division operator for the array template.
		 *
		 * @param a    Input array
		 * @param b    Scale value
		 * @return Target array
		 */
		array<ArrValType> operator/ (const ArrValType &b);

		/**
		 * @brief      Overloaded summation operator for the array template.
		 *
		 * @param a    Input array
		 * @param b    Scale value
		 * @return Target array
		 *
		 */
		array<ArrValType>& operator+= (const ArrValType &b);

		/**
		 * @brief      Overloaded summation operator for the array template.
		 *
		 * @param a    Input array
		 * @param b    Scale value
		 * @return Target array
		 */
		array<ArrValType>& operator-= (const ArrValType &b);

		/**
		 * @brief      Overloaded multiple operator for the array template.
		 *
		 * @param a    Input array
		 * @param b    Scale value
		 * @return Target array
		 */
		array<ArrValType>& operator*= (const ArrValType &b);

		/**
		 * @brief      Overloaded division operator for the array template.
		 *
		 * @param a    Input array
		 * @param b    Scale value
		 * @return Target array
		 */
		array<ArrValType>& operator/= (const ArrValType &b);

		/**
		 * @brief      Compare two arraies.
		 *
		 * @return     True if the two arraies are equal, false otherwise.
		 */
		bool operator== (const array<ArrValType> &b) const;

		/**
		 * @brief      Compare two arraies.
		 *
		 * @return     True if the two arraies are not equal, false otherwise.
		 */
		bool operator!= (const array<ArrValType> &b) const;

		/**
		 * @brief      Compare two arraies.
		 * 
		 * @param b     Input array
		 * @param eps   Tolerance for flaoting point number
		 *
		 * @return     True if the two arraies are not equal, false otherwise.
		 */
		bool equals(const array<ArrValType> &b, ArrValType eps = ArrValType{}) const;

		/**
		 * @brief      Destructor
		 */
		virtual ~array();

		/**
		 * @brief      Set the length of the array to the given value.
		 * 
		 * @note       If the input length is not equal to the existing length of the array, 
		 * the previous array is destroyed and reconstructed.
		 *
		 * @param[in]  len   Length of the array. Must be equal to or bigger than zero.
		 */
		void resize(size_t len);

		/**
		 * @brief      Set the length of the array to the given value, and fill elements with initial values.
		 * 
		 * @note       If the input length is not equal to the existing length of the array, 
		 * the previous array is destroyed and reconstructed.
		 *
		 * @param[in]  len       Length of the array. Must be equal to or bigger than zero.
		 * @param[in]  init_val  Initial value of the elements.
		 */
		void resize(size_t len, ArrValType init_val);

		/**
		 * @brief      Set the length of the array to the given value, and fill elements using a native c++ array.
		 * 
		 * @note       If the input length is not equal to the existing length of the array, 
		 * the previous array is destroyed and reconstructed.
		 *
		 * @param      init_array  Pointer of the native c++ array.
		 * @param[in]  len         Length of the input c++ array. Must be equal to or bigger than zero.
		 */
		void resize(ArrValType *init_array, size_t len);

		/**
		 * @brief      Copy a array to the array.
		 * 
		 * @note       If the length of the input array is not equal to the existing length of the array, 
		 * the previous array is destroyed and reconstructed.
		 *
		 * @param[in]  b     Original array
		 */
		void resize(const array<ArrValType> &b);

		/**
		 * @brief Resize a new array object from std::initializer
		 * 
		 * @param init_val Initial values
		 */
		void resize(std::initializer_list<ArrValType> init_val);

		/**
		 * @brief Resize a new array object as a sequence
		 * 
		 * @param s Size of the sequence.
		 * @param st Start value
		 * @param inc Invrease interval
		 */
		void resize(size_t s, ArrValType st, ArrValType inc);

		/**
		 * @brief      Clear memory space and reset variables.
		 */
		void clear();

		/**
		 * @brief      Set all elements to the given value.
		 *
		 * @param[in]  in_val    Input value.
		 */
		void assign(ArrValType in_val);

		/**
		 * @brief Set value to segments of an array.
		 * 
		 * @param in_val Input value
		 * @param st Starting index
		 * @param range segment range
		 */
		void assign(ArrValType in_val, size_t st, size_t range);
		
		/**
		 * @brief Append an array to the end of the existing one
		 * 
		 * @param b Input array
		 */
		void concat(const array<ArrValType> &b);

		/**
		 * @brief Extract an array
		 * 
		 * @param b   Output array. Must be initialized before use.
		 * @param st  Start index
		 */
		void slice(array<ArrValType> &b, size_t st = 0) const;

		/**
		 * @brief Extract an array
		 * 
		 * @param b   Output array
		 * @param ids Index of the extracting elements. Must be initialized before use.
		 */
		void slice(array<ArrValType> &b, const array<size_t> &ids) const;

		/**
		 * @brief Insert data from an input array
		 * 
		 * @param b The input array
		 * @param st The inserting point. The default is zero
		 */
		void insert(array<ArrValType> &b, size_t st = 0);

		/**
		 * @brief      Get element value at the given index.
		 * 
		 * @note       This function will check if the index was out of range.
		 *
		 * @param[in]  index  index value.
		 *
		 * @return     element's value.
		 */
		ArrValType &at(size_t index);

		/**
		 * @brief      Get element value at the given index (Constant).
		 * 
		 * @note       This function will check if the index was out of range.
		 *
		 * @param[in]  index  index value.
		 *
		 * @return     element's value.
		 */
		ArrValType &at(size_t index) const;

		/**
		 * @brief      Get element value at the given index.
		 * 
		 * @note       This function could not be called by a pointer.
		 *
		 * @param[in]  index  index value.
		 *
		 * @return     element's value.
		 */
		ArrValType &operator[](size_t index) noexcept;

		/**
		 * @brief      Get element value at the given index  .
		 * 
		 * @note       This function could not be called by a pointer.
		 *
		 * @param[in]  index  index value.
		 *
		 * @return     element's value.
		 */
		ArrValType &operator[](size_t index) const noexcept;
		
		/**
		 * @brief      Get the first element
		 * 
		 * @return     element value
		 */
		ArrValType front() const;

		/**
		 * @brief      Get the last element
		 *
		 * @return     element value
		 */
		ArrValType back() const;

		/**
		 * @brief Return the begining of the iterator
		 * 
		 * @return iterator
		 */
		iterator<ArrValType> begin();

		/**
		 * @brief Return the ending of the iterator
		 * 
		 * @return iterator
		 */
		iterator<ArrValType> end();

		/**
		 * @brief      Get the pointer to a element at the given index.
		 * 
		 * @note       This function will check if the index was out of range.
		 *
		 * @param[in]  index  index value.
		 *
		 * @return     Pointer to the element
		 */
		ArrValType *get(size_t index = 0) const;

		/**
		 * @brief      Discriminate if the array is empty.
		 *
		 * @return     True for empty. False other wise.
		 */
		bool empty() const noexcept;

		/**
		 * @brief      Return the length of the class member array.
		 *
		 * @return     Length.
		 */
		size_t size() const noexcept;

		/**
		 * @brief      Copy the array to a vector.
		 *
		 * @param      b     Target vector.
		 */
		void output(std::vector<ArrValType> &b) const;

		/**
		 * @brief      Copy the array from a vector.
		 *
		 * @param      b     Target vector.
		 */
		void input(const std::vector<ArrValType> &b);

#ifdef GCTL_EIGEN
		/**
		 * @brief      Copy the array to a Eigen3 vector.
		 *
		 * @param      b     Target vector.
		 */
		void output(Eigen::VectorXd &b) const;

		/**
		 * @brief      Copy the array from a Eigen3 vector.
		 *
		 * @param      b     Target vector.
		 */
		void input(const Eigen::VectorXd &b);
#endif // GCTL_EIGEN
		
		/**
		 * @brief 并行执行指定操作
		 * 
		 * 对数组中的每个元素并行执行指定的操作，参数为数组元素的引用。
		 * 例如：[](ArrValType &a){do something here...}
		 * 
		 * @tparam UnaryOp 一元操作类型 参数为数组元素的引用
		 * @param op 要执行的操作
		 */
		template <typename UnaryOp>
		void for_each(UnaryOp op)
		{
			#pragma omp parallel for
			for (size_t i = 0; i < length_; i++)
			{
				op(val_[i]);
			}
			return;
		}

		/**
		 * @brief 并行执行提取操作（比如提取结构体的成员变量数组）
		 * 
		 * 对数组中的每个元素并行执行给定的操作并将输出组成一个新数组，参数为数组元素的常引用。
		 * 例如：[](const ArrValType &a)->OutType{do something here...}
		 * 
		 * @tparam UnaryOp 一元操作类型 参数为数组元素的引用
		 * @param op 要执行的操作
		 */
		template <typename OutType, typename UnaryOp>
		array<OutType> extract(UnaryOp op)
		{
			array<OutType> arr(length_);
			#pragma omp parallel for
			for (size_t i = 0; i < length_; i++)
			{
				arr[i] = op(val_[i]);
			}
			return arr;
		}


		/**
		 * @brief   Display the elements.
		 * 
		 */
		void show(std::ostream &os = std::cout, char sep = ' ');

		/**
		 * @brief      Initialize the array with selected random types.
		 *
		 * @param[in]  np1          Mean (Gauss) or low bound value (Even)
		 * @param[in]  np2          Standard deviation (Gauss) or hig bound value (Even).
		 * @param[in]  mode         Random types. 'RdNormal' for Gaussian distributed numbers and 
		 * 'RdUniform' for even distributed numbers.
		 * @param[in] seed          Random seed. Input 0 to select the seed based on the current time.
		 */
		void random_float(ArrValType np1, ArrValType np2, random_type_e mode = RdNormal, unsigned int seed = 0);

		/**
		 * @brief      Initialize the array with selected random types.
		 *
		 * @param[in]  np1          Low bound value.
		 * @param[in]  np2          Hig bound value.
		 * @param[in] seed          Random seed. Input 0 to select the seed based on the current time.
		 */
		void random_int(ArrValType np1, ArrValType np2, unsigned int seed = 0);

		/**
		 * @brief Set elements' value as a sequent.
		 * 
		 * @param st_val  Start value of the sequent.
		 * @param inc Increasement of the sequent.
		 * @param st_id Start index. The default starts from the first element
		 * @param size Operating size. The default operates on all elements
		 * @param space Indexing spacing. you can skip elements when create the sequent. The default is no skipping.
		 */
		void sequence(ArrValType st_val, ArrValType inc, size_t st_id = 0, 
			size_t size = std::numeric_limits<size_t>::max(), size_t space = 0);

		/**
		 * @brief Assign values as a 2D sequence.
		 * 
		 * @param rs Start value of the row sequence
		 * @param rinc Increase value of the row sequence
		 * @param cs Start value of the column sequence
		 * @param cinc Increase value of the column sequence
		 * @param rn Row nnumber
		 * @param cn Column number
		 */
		void sequence2d(ArrValType rs, ArrValType rinc, ArrValType cs, ArrValType cinc, 
			size_t rn, size_t cn);

		/**
		 * @brief Assign values as a 3D sequence.
		 * 
		 * @param ls Start value of the layer sequence
		 * @param linc Increase value of the layer sequence
		 * @param rs Start value of the row sequence
		 * @param rinc Increase value of the row sequence
		 * @param cs Start value of the column sequence
		 * @param cinc Increase value of the column sequence
		 * @param ln Layer number
		 * @param rn Row nnumber
		 * @param cn Column number
		 */
		void sequence3d(ArrValType ls, ArrValType linc, 
			ArrValType rs, ArrValType rinc, 
			ArrValType cs, ArrValType cinc, 
			size_t ln, size_t rn, size_t cn);

		/**
		 * @brief Scale elements
		 * 
		 * @param in_val input factor
		 */
		void scale(ArrValType in_val);

		/**
		 * @brief  Dot product of two arrays.
		 * 
		 * @param a Input array. Must be the same length_.
		 * @return dot product
		 */
		ArrValType dot(const array<ArrValType> &a);

		/**
		 * @brief Return the sum of all elements.
		 * 
		 * @return sum value.
		 */
		ArrValType sum() const;

		/**
		 * @brief Return the mean value.
		 * 
		 * @return mean value.
		 */
		ArrValType mean() const;

		/**
		 * @brief Return the variance value.
		 * 
		 * @return Variance value.
		 */
		ArrValType variance() const;

		/**
		 * @brief Return the standard deviation value.
		 * 
		 * @return std value.
		 */
		ArrValType std() const;

		/**
		 * @brief Return the unbiased standard deviation value.
		 * 
		 * @return std value.
		 */
		ArrValType std_unbiased() const;

		/**
		 * @brief Return the root mean square value.
		 * 
		 * @return RMS value.
		 */
		ArrValType rms() const;

		/**
		 * @brief Return the maximal value.
		 * 
		 * @return maximal value.
		 */
		ArrValType max() const;

		/**
		 * @brief Return the minimal value.
		 * 
		 * @return minimal value.
		 */
		ArrValType min() const;

		/**
		 * @brief      计算数组的模长
		 *
		 * @param[in]  n_type      模长的计算类型
		 * 
		 * @return     返回的模长
		 */
		ArrValType module(norm_type_e n_type = L2) const;

		/**
		 * @brief Normalize the array to the given module value
		 * 
		 * @param norm targeting norm-value
		 * @param n_type Norm-type
		 * 
		 * @return The module
		 */
		ArrValType normalize(ArrValType norm = 1.0, norm_type_e n_type = L2);

		/**
		 * @brief      计算一个向量相对于另一个向量的正交向量
		 *
		 * @param      b     输入的数组，计算后的正交向量以引用的形式返回
		 */
		void orth(array<ArrValType> &b);
		
		/**
		 * @brief Assign values as a power sequence.
		 * 
		 * @param base Power base
		 */
		void log2linear(const ArrValType &base);

		/**
		 * @brief Convert values to a log root.
		 * 
		 * @param base Log base
		 */
		void linear2log(ArrValType base);

		/**
		 * @brief Set values to a range.
		 * 
		 * @param min Lower bound
		 * @param max Higher bound
		 * @param rt Bounder type
		 * 
		 */
		void set2range(ArrValType min, ArrValType max, range_type_e rt = HardScale);

		/**
		 * @brief 检查是否存在等于指定值的元素
		 * 
		 * @param value 要比较的值
		 * @return 如果存在等于指定值的元素返回true，否则返回false
		 */
		bool any_of(const ArrValType& value) const;

		/**
		 * @brief 检查是否所有元素都等于指定值
		 * 
		 * @param value 要比较的值
		 * @return 如果所有元素都等于指定值返回true，否则返回false
		 */
		bool all_of(const ArrValType& value) const;

		/**
		 * @brief 统计等于指定值的元素个数
		 * 
		 * @param value 要统计的值
		 * @return 等于指定值的元素个数
		 */
		size_t count(const ArrValType& value) const;

		 /**
		 * @brief 获取数组的内存使用量
		 * 
		 * @return 数组占用的总内存量MB
		 */
		void memory_usage(std::ostream &os = std::cout) const noexcept;
	};

	template <typename ArrValType>
	array<ArrValType>::array()
	{
		length_ = 0;
		val_ = nullptr;
	}

	template <typename ArrValType>
	array<ArrValType>::array(size_t len)
	{
		length_ = 0;
		val_ = nullptr;
		resize(len);
	}

	template <typename ArrValType>
	array<ArrValType>::array(size_t len, ArrValType init_val)
	{
		length_ = 0;
		val_ = nullptr;
		resize(len, init_val);
	}

	template <typename ArrValType>
	array<ArrValType>::array(ArrValType *init_array, size_t len)
	{
		length_ = 0;
		val_ = nullptr;
		resize(init_array, len);
	}

	template <typename ArrValType>
	array<ArrValType>::array(const array<ArrValType> &b)
	{
		length_ = 0;
		val_ = nullptr;
		resize(b);
	}

	template <typename ArrValType>
	array<ArrValType>::array(std::initializer_list<ArrValType> init_val)
	{
		length_ = 0;
		val_ = nullptr;
		resize(init_val);
	}

	template <typename ArrValType>
	array<ArrValType>::array(size_t s, ArrValType st, ArrValType inc)
	{
		length_ = 0;
		val_ = nullptr;
		resize(s, st, inc);
	}

	template <typename ArrValType>
	array<ArrValType> &array<ArrValType>::operator= (const array<ArrValType> &b)
	{
		resize(b.size());
		for (size_t i = 0; i < length_; i++)
		{
			val_[i] = b[i];
		}
		return *this;
	}

	template <typename ArrValType>
	array<ArrValType> &array<ArrValType>::operator= (ArrValType v)
	{
		for (size_t i = 0; i < length_; i++)
		{
			val_[i] = v;
		}
		return *this;
	}

	template <typename ArrValType>
	array<ArrValType> array<ArrValType>::operator+ (const array<ArrValType> &b)
	{
		if (b.size() != length_)
		{
			throw std::runtime_error("[gctl::array<T>::operator+] Incompatible array sizes.");
		}

		array<ArrValType> out(length_);
		for (size_t i = 0; i < length_; i++)
		{
			out[i] = val_[i] + b[i];
		}
		return out;
	}

	template <typename ArrValType>
	array<ArrValType> array<ArrValType>::operator- (const array<ArrValType> &b)
	{
		if (b.size() != length_)
		{
			throw std::runtime_error("[gctl::array<T>::operator-] Incompatible array sizes.");
		}

		array<ArrValType> out(length_);
		for (size_t i = 0; i < length_; i++)
		{
			out[i] = val_[i] - b[i];
		}
		return out;
	}

	template <typename ArrValType>
	array<ArrValType> array<ArrValType>::operator* (const array<ArrValType> &b)
	{
		if (b.size() != length_)
		{
			throw std::runtime_error("[gctl::array<T>::operator*] Incompatible array sizes.");
		}

		array<ArrValType> out(length_);
		for (size_t i = 0; i < length_; i++)
		{
			out[i] = val_[i] * b[i];
		}
		return out;
	}

	template <typename ArrValType>
	array<ArrValType> array<ArrValType>::operator/ (const array<ArrValType> &b)
	{
		if (b.size() != length_)
		{
			throw std::runtime_error("[gctl::array<T>::operator/] Incompatible array sizes.");
		}

		array<ArrValType> out(length_);
		for (size_t i = 0; i < length_; i++)
		{
			out[i] = val_[i] / b[i];
		}
		return out;
	}

	template <typename ArrValType>
	array<ArrValType>& array<ArrValType>::operator+= (const array<ArrValType> &b)
	{
		if (b.size() != length_)
		{
			throw std::runtime_error("[gctl::array<T>::operator+=] Incompatible array sizes.");
		}

		for (size_t i = 0; i < length_; i++)
		{
			val_[i] += b[i];
		}
		return *this;
	}

	template <typename ArrValType>
	array<ArrValType>& array<ArrValType>::operator-= (const array<ArrValType> &b)
	{
		if (b.size() != length_)
		{
			throw std::runtime_error("[gctl::array<T>::operator-=] Incompatible array sizes.");
		}

		for (size_t i = 0; i < length_; i++)
		{
			val_[i] -= b[i];
		}
		return *this;
	}

	template <typename ArrValType>
	array<ArrValType>& array<ArrValType>::operator*= (const array<ArrValType> &b)
	{
		if (b.size() != length_)
		{
			throw std::runtime_error("[gctl::array<T>::operator*=] Incompatible array sizes.");
		}

		for (size_t i = 0; i < length_; i++)
		{
			val_[i] *= b[i];
		}
		return *this;
	}

	template <typename ArrValType>
	array<ArrValType>& array<ArrValType>::operator/= (const array<ArrValType> &b)
	{
		if (b.size() != length_)
		{
			throw std::runtime_error("[gctl::array<T>::operator/=] Incompatible array sizes.");
		}
		
		for (size_t i = 0; i < length_; i++)
		{
			val_[i] /= b[i];
		}
		return *this;
	}

	template <typename ArrValType>
	array<ArrValType> array<ArrValType>::operator+ (const ArrValType &b)
	{
		array<ArrValType> out(length_);
		for (size_t i = 0; i < length_; i++)
		{
			out[i] = val_[i] + b;
		}
		return out;
	}

	template <typename ArrValType>
	array<ArrValType> array<ArrValType>::operator- (const ArrValType &b)
	{
		array<ArrValType> out(length_);
		for (size_t i = 0; i < length_; i++)
		{
			out[i] = b - val_[i];
		}
		return out;
	}

	template <typename ArrValType>
	array<ArrValType> array<ArrValType>::operator* (const ArrValType &b)
	{
		array<ArrValType> out(length_);
		for (size_t i = 0; i < length_; i++)
		{
			out[i] = b*val_[i];
		}
		return out;
	}

	template <typename ArrValType>
	array<ArrValType> array<ArrValType>::operator/ (const ArrValType &b)
	{
		array<ArrValType> out(length_);
		for (size_t i = 0; i < length_; i++)
		{
			out[i] = b/val_[i];
		}
		return out;
	}

	template <typename ArrValType>
	array<ArrValType>& array<ArrValType>::operator+= (const ArrValType &b)
	{
		for (size_t i = 0; i < length_; i++)
		{
			val_[i] += b;
		}
		return *this;
	}

	template <typename ArrValType>
	array<ArrValType>& array<ArrValType>::operator-= (const ArrValType &b)
	{
		for (size_t i = 0; i < length_; i++)
		{
			val_[i] -= b;
		}
		return *this;
	}

	template <typename ArrValType>
	array<ArrValType>& array<ArrValType>::operator*= (const ArrValType &b)
	{
		for (size_t i = 0; i < length_; i++)
		{
			val_[i] *= b;
		}
		return *this;
	}

	template <typename ArrValType>
	array<ArrValType>& array<ArrValType>::operator/= (const ArrValType &b)
	{
		for (size_t i = 0; i < length_; i++)
		{
			val_[i] /= b;
		}
		return *this;
	}

	template <typename ArrValType>
	bool array<ArrValType>::operator== (const array<ArrValType> &b) const
	{
		if (length_ != b.size()) return false;

		for (size_t i = 0; i < length_; i++)
		{
			if (val_[i] != b[i]) return false;
		}
		return true;
	}

	template <typename ArrValType>
	bool array<ArrValType>::operator!= (const array<ArrValType> &b) const
	{
		return !(*this == b);
	}

	template <typename ArrValType>
	bool array<ArrValType>::equals(const array<ArrValType> &b, ArrValType eps) const
	{
		if (length_ != b.size()) return false;

		if (eps == ArrValType{})
		{
			for (size_t i = 0; i < length_; i++)
			{
				if (val_[i] != b[i]) return false;
			}
		}
		else
		{
			for (size_t i = 0; i < length_; i++)
			{
				if (std::abs(val_[i] - b[i]) > eps) return false;
			}
		}
		return true;
	}

	template <typename ArrValType>
	array<ArrValType>::~array()
	{
		clear();
	}

	template <typename ArrValType>
	void array<ArrValType>::resize(size_t len)
	{
		if (len == 0)
		{
			throw std::invalid_argument("[gctl::array<T>::resize] Invalid array size.");
		}

		if (length_ != len)
		{
			clear();

			length_ = len;
			val_ = new ArrValType[len];
		}
		return;
	}

	template <typename ArrValType>
	void array<ArrValType>::resize(size_t len, ArrValType init_val)
	{
		resize(len);
		for (size_t i = 0; i < length_; i++)
		{
			val_[i] = init_val;
		}
		return;
	}

	template <typename ArrValType>
	void array<ArrValType>::resize(ArrValType *init_array, size_t len)
	{
		if (init_array == nullptr)
		{
			throw std::domain_error("[gctl::array<T>::resize] Invalid pointer.");
		}

		resize(len);
		for (size_t i = 0; i < length_; i++)
		{
			val_[i] = init_array[i];
		}
		return;
	}

	template <typename ArrValType>
	void array<ArrValType>::resize(const array<ArrValType> &b)
	{
		if (!b.empty())
		{
			resize(b.size());
			for (size_t i = 0; i < length_; i++)
			{
				val_[i] = b[i];
			}
		}
		return;
	}

	template <typename ArrValType>
	void array<ArrValType>::resize(std::initializer_list<ArrValType> init_val)
	{
		resize(init_val.size());

		typename std::initializer_list<ArrValType>::iterator iter = init_val.begin();
		for (size_t i = 0; i < length_; i++)
		{
			val_[i] = *iter;
			iter++;
		}
		return;
	}

	template <typename ArrValType>
	void array<ArrValType>::resize(size_t s, ArrValType st, ArrValType inc)
	{
		resize(s);
		sequence(st, inc);
		return;
	}

	template <typename ArrValType>
	void array<ArrValType>::clear()
	{
		if (val_ != nullptr)
		{
			delete[] val_;
			val_ = nullptr;

			length_ = 0;
		}
		return;
	}

	template <typename ArrValType>
	void array<ArrValType>::assign(ArrValType in_val)
	{
		for (size_t i = 0; i < length_; i++)
		{
			val_[i] = in_val;
		}
		return;
	}

	template <typename ArrValType>
	void array<ArrValType>::assign(ArrValType in_val, size_t st, size_t range)
	{
		if (st + range <= length_)
		{
			for (size_t i = st; i < st + range; i++)
			{
				val_[i] = in_val;
			}
			return;
		}
		
		throw std::runtime_error("[gctl::array<T>::assign] Invalid segment range.");
		return;
	}

	template <typename ArrValType>
	void array<ArrValType>::concat(const array<ArrValType> &b)
	{
		ArrValType *t = new ArrValType [length_ + b.size()];

		for (size_t i = 0; i < length_; i++)
		{
			t[i] = val_[i];
		}

		for (size_t i = 0; i < b.size(); i++)
		{
			t[i + length_] = b[i];
		}

		delete[] val_;
		val_ = t;

		length_ += b.size();
		return;
	}

	template <typename ArrValType>
	void array<ArrValType>::slice(array<ArrValType> &b, size_t st) const
	{
		if (b.size() + st <= length_)
		{
			for (size_t i = 0; i < b.size(); i++)
			{
				b[i] = val_[i + st];
			}
		}
		else
		{
			size_t c = 0;
			for (size_t i = st; i < length_; i++)
			{
				b[c] = val_[i];
				c++;
			}
		}
		return;
	}

	template <typename ArrValType>
	void array<ArrValType>::slice(array<ArrValType> &b, const array<size_t> &ids) const
	{
		b.resize(ids.size());
		for (size_t i = 0; i < ids.size(); i++)
		{
			b[i] = val_[ids[i]];
		}
		return;
	}

	template <typename ArrValType>
	void array<ArrValType>::insert(array<ArrValType> &b, size_t st)
	{
		if (b.size() + st <= length_)
		{
			for (size_t i = 0; i < b.size(); i++)
			{
				val_[i + st] = b[i];
			}
		}
		else
		{
			size_t c = 0;
			for (size_t i = st; i < length_; i++)
			{
				val_[i] = b[c];
				c++;
			}
		}
		return;
	}

	template <typename ArrValType>
	ArrValType *array<ArrValType>::get(size_t index) const
	{
		if (index >= length_)
			throw std::out_of_range("[gctl::array<T>::at] Invalid index.");

		return &val_[index];
	}

	template <typename ArrValType>
	ArrValType &array<ArrValType>::at(size_t index)
	{
		if (index >= length_)
			throw std::out_of_range("[gctl::array<T>::at] Invalid index.");

		return val_[index];
	}

	template <typename ArrValType>
	ArrValType &array<ArrValType>::at(size_t index) const
	{
		if (index >= length_)
			throw std::out_of_range("[gctl::array<T>::at] Invalid index.");

		return val_[index];
	}

	template <typename ArrValType>
	ArrValType &array<ArrValType>::operator[](size_t index) noexcept
	{
		return val_[index];
	}

	template <typename ArrValType>
	ArrValType &array<ArrValType>::operator[](size_t index) const noexcept
	{
		return val_[index];
	}

	template <typename ArrValType>
	ArrValType array<ArrValType>::front() const
	{
		return val_[0];
	}

	template <typename ArrValType>
	ArrValType array<ArrValType>::back() const
	{
		return val_[length_-1];
	}

	template <typename ArrValType>
	iterator<ArrValType> array<ArrValType>::begin()
	{
		return iterator<ArrValType>(this->val_, 0);
	}

	template <typename ArrValType>
	iterator<ArrValType> array<ArrValType>::end()
	{
		return iterator<ArrValType>(this->val_, length_);
	}

	template <typename ArrValType>
	bool array<ArrValType>::empty() const noexcept
	{
		if (length_ == 0) return true;
		else return false;
	}

	template <typename ArrValType>
	size_t array<ArrValType>::size() const noexcept
	{
		return length_;
	}

	template <typename ArrValType>
	void array<ArrValType>::output(std::vector<ArrValType> &b) const
	{
		if (!b.empty())
		{
			b.clear();
		}

		b.resize(length_);
		for (size_t i = 0; i < length_; i++)
		{
			b[i] = val_[i];
		}
		return;
	}

	template <typename ArrValType>
	void array<ArrValType>::input(const std::vector<ArrValType> &b)
	{
		resize(b.size());
		for (size_t i = 0; i < length_; i++)
		{
			val_[i] = b[i];
		}
		return;
	}

#ifdef GCTL_EIGEN
	template <typename ArrValType>
	void array<ArrValType>::output(Eigen::VectorXd &b) const
	{
		b.resize(length_);
		for (size_t i = 0; i < length_; i++)
		{
			b[i] = val_[i];
		}
		return;
	}

	
	template <typename ArrValType>
	void array<ArrValType>::input(const Eigen::VectorXd &b)
	{
		resize(b.size());
		for (size_t i = 0; i < length_; i++)
		{
			val_[i] = b[i];
		}
		return;
	}
#endif // GCTL_EIGEN

	template <typename ArrValType>
	void array<ArrValType>::show(std::ostream &os, char sep)
	{
		if (length_ != 0)
		{
			os << val_[0];
			for (size_t i = 1; i < length_; i++)
			{
				os << sep << val_[i];
			}
			os << std::endl;
		}
		return;
	}

	template <typename VecValType>
	std::ostream &operator <<(std::ostream &os, const array<VecValType> &a)
	{
		if (!a.empty())
		{
			os << a[0];
			for (size_t i = 1; i < a.size(); i++)
			{
				os << " " << a[i];
			}
		}
		return os;
	}

	template <typename VecValType>
	std::istream &operator >>(std::istream &os, array<VecValType> &a)
	{
		for (size_t i = 0; i < a.size(); i++)
		{
			os >> a[i];
		}
		return os;
	}

	template <typename ArrValType>
	void array<ArrValType>::random_float(ArrValType np1, ArrValType np2, 
		random_type_e mode, unsigned int seed)
	{
		static_assert(std::is_floating_point<ArrValType>::value, 
			"gctl::array<T>::random_float(...) could only be used with a float point type.");

		if (seed == 0) seed = std::chrono::system_clock::now().time_since_epoch().count();
		std::default_random_engine generator(seed);

		if (mode == RdNormal)
		{
			//添加高斯分布的随机值
			std::normal_distribution<ArrValType> dist(np1, np2);
			for (size_t i = 0; i < length_; i++)
			{
				val_[i] = dist(generator);
			}
			return;
		}

		//添加均匀分布的随机值
		std::uniform_real_distribution<ArrValType> dist(np1, np2);
		for (size_t i = 0; i < length_; i++)
		{
			val_[i] = dist(generator);
		}
		return;
	}

	template <typename ArrValType>
	void array<ArrValType>::random_int(ArrValType np1, ArrValType np2, unsigned int seed)
	{
		static_assert(std::is_integral<ArrValType>::value, 
			"gctl::array<T>::random_int(...) could only be used with an integral type.");

		if (seed == 0) seed = std::chrono::system_clock::now().time_since_epoch().count();
		std::default_random_engine generator(seed);

		//添加均匀分布的随机值
		std::uniform_int_distribution<ArrValType> dist(np1, np2);
		for (size_t i = 0; i < length_; i++)
		{
			val_[i] = dist(generator);
		}
		return;
	}

	template <typename ArrValType>
	void array<ArrValType>::sequence(ArrValType st_val, ArrValType inc, 
		size_t st_id, size_t size, size_t space)
	{
		if (st_id >= length_) throw std::runtime_error("[gctl::array<T>::sequence] Invalid index.");
		size = GCTL_MIN(size, length_);

		if (size > 0)
		{
			val_[st_id] = st_val;
			size--;

			size_t id = st_id;
			size_t id2 = st_id + space + 1;
			while (id2 < length_ && size > 0)
			{
				val_[id2] = val_[id] + inc;

				id = id2;
				id2 += space + 1;
				size--;
			}
		}
		return;
	}

	template <typename ArrValType>
	void array<ArrValType>::sequence2d(ArrValType rs, ArrValType rinc, 
		ArrValType cs, ArrValType cinc, size_t rn, size_t cn)
	{
		if (rn*cn != length_)
		{
			throw std::invalid_argument("[gctl::array<T>::sequence2d] Invalid sequence sizes.");
		}

		array<ArrValType> out_x(cn), out_y(rn);
		out_x.sequence(cs, cinc);
        out_y.sequence(rs, rinc);

		for (size_t i = 0; i < rn; i++)
        {
            for (size_t j = 0; j < cn; j++)
            {
                val_[j + i*cn] = out_x[j] + out_y[i];
            }
        }
		return;
	}

	template <typename ArrValType>
	void array<ArrValType>::sequence3d(ArrValType ls, ArrValType linc, 
		ArrValType rs, ArrValType rinc, ArrValType cs, ArrValType cinc, 
		size_t ln, size_t rn, size_t cn)
	{
		if (ln*rn*cn != length_)
		{
			throw std::invalid_argument("[gctl::array<T>::sequence3d] Invalid sequence sizes.");
		}

		array<ArrValType> out_x(cn), out_y(rn), out_z(ln);
		out_x.sequence(cs, cinc);
        out_y.sequence(rs, rinc);
		out_z.sequence(ls, linc);

		for (size_t k = 0; k < ln; k++)
        {
			for (size_t i = 0; i < rn; i++)
			{
				for (size_t j = 0; j < cn; j++)
				{
					val_[j + i*cn + k*rn*cn] = out_x[j] + out_y[i] + out_z[k];
				}
			}
		}
		return;
	}

	template <typename ArrValType>
	void array<ArrValType>::scale(ArrValType in_val)
	{
		static_assert(std::is_arithmetic<ArrValType>::value, 
			"gctl::array<T>::scale(...) could only be used with an arithmetic type.");

		for (size_t i = 0; i < length_; i++)
		{
			val_[i] = val_[i]*in_val;
		}
		return;
	}

	template <typename ArrValType>
	ArrValType array<ArrValType>::dot(const array<ArrValType> &a)
	{
		static_assert(std::is_arithmetic<ArrValType>::value, 
			"gctl::array<T>::dot(...) could only be used with an arithmetic type.");

		if (length_ != a.size())
		{
			throw std::runtime_error("Incompatible array sizes. gctl::array<T>::dot(...)");
		}

		ArrValType s = (ArrValType) 0;
		for (size_t i = 0; i < length_; i++)
		{
			s += val_[i]*a[i];
		}
		return s;
	}

	template <typename ArrValType>
	ArrValType array<ArrValType>::sum() const
	{
		static_assert(std::is_arithmetic<ArrValType>::value, 
			"gctl::array<T>::sum(...) could only be used with an arithmetic type.");

		if (length_ == 0) return ArrValType{};

		ArrValType s = val_[0];
		#pragma omp parallel for reduction(+:s)
		for (size_t i = 1; i < length_; i++)
		{
			s += val_[i];
		}
		return s;
	}

	template <typename ArrValType>
	ArrValType array<ArrValType>::mean() const
	{
		static_assert(std::is_arithmetic<ArrValType>::value, 
			"gctl::array<T>::mean(...) could only be used with an arithmetic type.");

		if (length_ == 0) return ArrValType{};
		return sum()/static_cast<ArrValType>(length_);
	}

	template <typename ArrValType>
	ArrValType array<ArrValType>::variance() const
	{
		static_assert(std::is_arithmetic<ArrValType>::value, 
			"gctl::array<T>::variance(...) could only be used with an arithmetic type.");

		if (length_ == 0 || val_ == nullptr) return ArrValType{};

		ArrValType m = mean();
		ArrValType d = ArrValType(0);
		for (size_t i = 0; i < length_; i++)
		{
			d += (val_[i] - m)*(val_[i] - m);
		}
		return d/static_cast<ArrValType>(length_);
	}

	template <typename ArrValType>
	ArrValType array<ArrValType>::std() const
	{
		static_assert(std::is_arithmetic<ArrValType>::value, 
			"gctl::array<T>::std(...) could only be used with an arithmetic type.");

		if (length_ == 0 || val_ == nullptr) return ArrValType{};

		ArrValType m = mean(), s = ArrValType(0);
		for (size_t i = 0; i < length_; i++)
		{
			s += (val_[i] - m)*(val_[i] - m);
		}
		return sqrt(s/static_cast<ArrValType>(length_));
	}

	template <typename ArrValType>
	ArrValType array<ArrValType>::rms() const
	{
		static_assert(std::is_arithmetic<ArrValType>::value, 
			"gctl::array<T>::rms(...) could only be used with an arithmetic type.");

		if (length_ == 0) return ArrValType{};

		ArrValType m = 0;
		for (size_t i = 0; i < length_; i++)
		{
			m += val_[i]*val_[i];
		}
		return sqrt(m/static_cast<ArrValType>(length_));
	}

	template <typename ArrValType>
	ArrValType array<ArrValType>::max() const
	{
		static_assert(std::is_arithmetic<ArrValType>::value, 
			"gctl::array<T>::max(...) could only be used with an arithmetic type.");

		if (length_ == 0) return ArrValType{};

		ArrValType m = val_[0];
		for (size_t i = 1; i < length_; i++)
		{
			m = std::max(val_[i], m);
		}
		return m;
	}

	template <typename ArrValType>
	ArrValType array<ArrValType>::min() const
	{
		static_assert(std::is_arithmetic<ArrValType>::value, 
			"gctl::array<T>::min(...) could only be used with an arithmetic type.");

		if (length_ == 0) return ArrValType{};

		ArrValType m = val_[0];
		for (size_t i = 1; i < length_; i++)
		{
			m = std::min(val_[i], m);
		}
		return m;
	}

	template <typename ArrValType>
	ArrValType array<ArrValType>::module(norm_type_e n_type) const
    {
        static_assert(std::is_arithmetic<ArrValType>::value, 
			"gctl::array<T>::module(...) could only be used with an arithmetic type.");

        ArrValType norm_sum = 0.0;

		if (n_type == L0)
		{
			int n = 0;
			for (size_t i = 0; i < length_; i++)
			{
				if (val_[i] != 0.0)
				{
					n++;
				}
			}
			norm_sum = (ArrValType) n;
		}

		if (n_type == L1)
		{
			for (size_t i = 0; i < length_; i++)
			{
				norm_sum += GCTL_FABS(val_[i]);
			}
		}

		if (n_type == L2)
		{
			for (size_t i = 0; i < length_; i++)
			{
				norm_sum += val_[i] * val_[i];
			}
			norm_sum = sqrt(norm_sum);
		}

		if (n_type == Linf)
		{
			norm_sum = val_[0];
			for (int i = 1; i < length_; i++)
			{
				norm_sum = GCTL_MAX(norm_sum, val_[i]);
			}
		}

		return norm_sum;
    }

	template <typename ArrValType>
	ArrValType array<ArrValType>::normalize(ArrValType norm, norm_type_e n_type)
	{
		static_assert(std::is_arithmetic<ArrValType>::value, 
			"gctl::array<T>::normalize(...) could only be used with an arithmetic type.");

		ArrValType norm_sum = module(n_type);

		if (norm_sum <= GCTL_ZERO) return 0;

		for (size_t i = 0; i < length_; i++)
		{
			val_[i] = val_[i]*norm/norm_sum;
		}
		return norm_sum;
	}

	template <typename ArrValType>
	void array<ArrValType>::orth(array<ArrValType> &b)
    {
        static_assert(std::is_arithmetic<ArrValType>::value, 
			"gctl::array<T>::orth(...) could only be used with an arithmetic type.");

        if (length_ != b.size())
        {
            throw std::runtime_error("[gctl::array<T>::orth] Incompatible array size.");
        }

        ArrValType product = dot(b);
        for (int i = 0; i < length_; i++)
        {
            b[i] = b[i] - product*val_[i];
        }
        return;
    }

    template <typename ArrValType>
	void array<ArrValType>::log2linear(const ArrValType &base)
    {
        static_assert(std::is_arithmetic<ArrValType>::value, 
			"gctl::array<T>::logspace(...) could only be used with an arithmetic type.");

        for (size_t i = 0; i < length_; i++)
        {
            val_[i] = pow(base, val_[i]);
        }
        return;
    }

    template <typename ArrValType>
	void array<ArrValType>::linear2log(ArrValType base)
    {
        static_assert(std::is_arithmetic<ArrValType>::value, 
			"gctl::array<T>::linear2log(...) could only be used with an arithmetic type.");

        if (length_ == 0 || val_ == nullptr) return;

        for (size_t i = 0; i < length_; i++)
        {
            if (val_[i] <= ArrValType(0)) continue;  // 跳过非正值
            val_[i] = std::log(val_[i])/std::log(base);
        }
    }

    template <typename ArrValType>
	ArrValType array<ArrValType>::std_unbiased() const
    {
        static_assert(std::is_arithmetic<ArrValType>::value, 
			"gctl::array<T>::std_unbiased(...) could only be used with an arithmetic type.");

		if (length_ < 2) return 0;

        ArrValType mn = mean();
        ArrValType d = (ArrValType) 0;
        for (size_t i = 0; i < length_; i++)
        {
            d = d + (val_[i] - mn)*(val_[i] - mn);
        }
        return sqrt(d/(length_ - 1));
    }

    template <typename ArrValType>
	void array<ArrValType>::set2range(ArrValType min, ArrValType max, range_type_e rt)
	{
		static_assert(std::is_arithmetic<ArrValType>::value, 
			"gctl::array<T>::set2range(...) could only be used with an arithmetic type.");

        ArrValType amin = val_[0], amax = val_[0];
        for (size_t i = 1; i < length_; i++)
        {
            amin = amin<val_[i]?amin:val_[i];
            amax = amax>val_[i]?amax:val_[i];
        }

        if (rt == HardScale)
        {
            for (size_t i = 0; i < length_; i++)
            {
                val_[i] = (max - min)*(val_[i] - amin)/(amax - amin) + min;
            }
            return;
        }

        if (amin >= min && amax <= max) return; // aleardy in the range, nothing to be done

        if (rt == CutOff)
        {
            for (size_t i = 0; i < length_; i++)
            {
                if (val_[i] > max) val_[i] = max;
                if (val_[i] < min) val_[i] = min;
            }
            return;
        }

        // Soft Scale
        ArrValType min2 = amin>min?amin:min;
        ArrValType max2 = amax<max?amax:max;
        for (size_t i = 0; i < length_; i++)
        {
            val_[i] = (max2 - min2)*(val_[i] - amin)/(amax - amin) + min2;
        }
        return;
	}

	template <typename ArrValType>
	bool array<ArrValType>::any_of(const ArrValType& value) const
	{
		bool result = false;
        #pragma omp parallel for reduction(|:result)
        for (size_t i = 0; i < length_; i++)
		{
            result = result || (val_[i] == value);
        }
        return result;
	}

	template <typename ArrValType>
	bool array<ArrValType>::all_of(const ArrValType& value) const
	{
		bool result = true;
        #pragma omp parallel for reduction(&:result)
        for (size_t i = 0; i < length_; i++)
		{
            result = result && (val_[i] == value);
        }
        return result;
	}

	template <typename ArrValType>
	size_t array<ArrValType>::count(const ArrValType& value) const
	{
        size_t result = 0;
        #pragma omp parallel for reduction(+:result)
        for (size_t i = 0; i < length_; i++)
		{
            if (val_[i] == value) ++result;
        }
        return result;
	}

	template <typename ArrValType>
	void array<ArrValType>::memory_usage(std::ostream &os) const noexcept
	{
		size_t byte = sizeof(*this) + length_ * sizeof(ArrValType);
		if (byte/1073741824 > 0) os << byte/1073741824 << " GB\n";
		else if (byte/1048576 > 0) os << byte/1048576 << " MB\n";
		else if (byte/1024 > 0) os << byte/1024 << " KB\n";
		else os << byte << " B\n";
		return;
	}
};

#endif // _GCTL_ARRAY_H