Array1D<T> apply_function(fT (*f)(fT), const Array1D<T>& data)
{
    Array1D<T> out(data.length());
  
    for (int i=0;i<data.length();i++) 
	out[i]=static_cast<T>(f(static_cast<fT>(data[i])));
    return out;
}
void upsample(const Array1D<T>& v, int usf, Array1D<T>& u)
{
  //  it_assert1(usf >= 1, "upsample: upsampling factor must be equal or greater than one" );
  u.set_size(v.length()*usf);
  u.clear();
  for(long i=0;i<v.length();i++)
    u(i*usf)=v(i); 
}
Array1D<T> repeat(const Array1D<T>& v, int norepeats)
{
    Array1D<T> temp(v.length()*norepeats);
  
    for(int i=0;i<v.length();i++) {
	for(int j=0;j<norepeats;j++)
	    temp(i*norepeats+j)=v(i);
    }
    return temp;
}
void lininterp(const Array1D<T>& v, int usf, Array1D<T>& u)
{
  //  it_assert1(usf >= 1, "lininterp: upsampling factor must be equal or greater than one" );
  long L = (v.length()-1)*usf+1;
  u.set_size(L);
  for (long j = 0; j < L-1; j++) {
    //u(j) = (v(j/usf) + (j % usf)/((float)usf)*(v((j+usf)/usf)-v(j/usf)));  
    u(j) = (v(j/usf) + (j % usf)/((double)usf)*(v((j+usf)/usf)-v(j/usf)));  
  }
  u(L-1) = v(v.length()-1);
}
template<class T> T sum(const Array1D<T>& v)
{
    T M=0;
    for (int i=0;i<v.length();i++)
	M+=v[i];
    return M;
}
T product(const Array1D<T>& v)
{
    double lnM=0;
    for (int i=0;i<v.length();i++)
	lnM += log(static_cast<double>(v[i]));
    return static_cast<T>(exp(lnM));
}
int min_index(const Array1D<T>& in)
{
    int	minindex=0;
    for (int i=0;i<in.length();i++)
	if (in[i]<in[minindex])
	    minindex=i;
    return minindex;
}
int max_index(const Array1D<T>& in)
{
    int	maxindex=0;
    for (int i=0;i<in.length();i++) 
	if (in[i]>in[maxindex])
	    maxindex=i;
    return maxindex;
}
T min(const Array1D<T> &in)
{
    T mindata=in[0];
    for (int i=1;i<in.length();i++)
	if (in[i]<mindata)
	    mindata=in[i];
    return mindata;
}
T max(const Array1D<T>& in)
{
    T	maxdata=in[0];
    for (int i=1;i<in.length();i++)
	if (in[i]>maxdata)
	    maxdata=in[i];
    return maxdata;
}
T sum_sqr(const Array1D<T>& v)
{
    T M=0;
    
    for (int i=0; i<v.length(); i++)
	M += v[i] * v[i];
    
    return M;
}
double norm(const Array1D<T>& v, int p)
{
    int i;
    double e=0;
    
    for (i=0;i<v.length();i++)
	  e+=pow(fabs(v[i]),(double)p); // fabs() shoud be OK

    return pow(e,1.0/(double)p);
}
double norm(const Array1D<T>& v)
{
    int i;
    double e=0.0;
    
    for (i=0; i<v.length(); i++)
	e += sqr( v[i] );

    return sqrt(e);
}
ivec sort_index(const Array1D<T>& data)
{
    int N=data.length(),i;
    ivec indexlist(N);
  
    for(i=0;i<N;i++) {
	indexlist(i)=i;
    }
    QSindex(0,N-1,indexlist,data);
    return indexlist;
}
double variance(const Array1D<T>& v)
{
    int len = v.length();
    const T *p=v._data();
    double sum=0.0, sq_sum=0.0;

    for (int i=0; i<len; i++, p++) {
	sum += *p;
	sq_sum += *p * *p;
    }
    
    return (double)(sq_sum - sum*sum/len) / (len-1);
}
double geometric_mean(const Array1D<T>& v)
{
    return exp(log(product(v))/v.length());
}