bool LabelledTimeSeriesClassificationSampleTrimmer::trimTimeSeries(LabelledTimeSeriesClassificationSample &timeSeries){
    
    const UINT M = timeSeries.getLength();
    const UINT N = timeSeries.getNumDimensions();
    
    if( M == 0 ){
        warningLog << "trimTimeSeries(LabelledTimeSeriesClassificationSample &timeSeries,LabelledTimeSeriesClassificationSample &trimmedTimeSeries) - can't trim data, the length of the input time series is 0!" << endl;
        return false;
    }
    
    if( N == 0 ){
        warningLog << "trimTimeSeries(LabelledTimeSeriesClassificationSample &timeSeries,LabelledTimeSeriesClassificationSample &trimmedTimeSeries) - can't trim data, the number of dimensions in the input time series is 0!" << endl;
        return false;
    }
    
    //Compute the derivative of the time series
    double maxValue = 0;
    vector< double > x(M,0);
    
    for(UINT i=1; i<M; i++){
        for(UINT j=0; j<N; j++){
            x[i] += timeSeries[i][j]-timeSeries[i-1][j];
        }
        x[i]/=N;
        if( x[i] > maxValue ) maxValue = x[i];
    }
    
    //Normalize x and at the same time search for the first time x[i] passes the trim threshold
    UINT firstIndex = 0;
    for(UINT i=1; i<M; i++){
        x[i] /= maxValue;
        
        if( x[i] > trimThreshold && firstIndex == 0 ){
            firstIndex = i;
        }
    }
    
    //Search for the last time x[i] passes the trim threshold
    UINT lastIndex = 0;
    for(UINT i=M; i>1; i--){
            
        if( x[i] < trimThreshold && lastIndex == 0 ){
            lastIndex = i;
            break;
        }
    }
    
    if( firstIndex == 0 && lastIndex == 0 ){
        warningLog << "Failed to find either the first index or the last index!";
        return false;
    }
    
    if( lastIndex == 0 ){
        warningLog << "Failed to find the last index!";
        lastIndex = M-1;
    }
    
    //Compute how long the new time series would be if we trimmed it
    UINT newM = lastIndex-firstIndex;
    
    if( (double(newM) / double(M)) * 100.0 > 100.0-maximumTrimPercentage ){
        
        Matrix< double > newTimeSeries(newM,N);
        UINT index = 0;
        for(UINT i=firstIndex; i<lastIndex; i++){
            for(UINT j=0; j<N; j++){
                newTimeSeries[index][j] = timeSeries[i][j];
            }
            index++;
        }
        
        timeSeries.setTrainingSample(timeSeries.getClassLabel(), newTimeSeries);
    }else{
        debugLog << "MAXIMUM TRIM PERCENTAGE EXCEDDED - NOT TRIMMING TIME SERIES\n";
        return false;
    }

    return true;
    
}
bool TimeSeriesClassificationSampleTrimmer::trimTimeSeries(TimeSeriesClassificationSample &timeSeries) {

    const UINT M = timeSeries.getLength();
    const UINT N = timeSeries.getNumDimensions();

    if( M == 0 ) {
        warningLog << "trimTimeSeries(TimeSeriesClassificationSample &timeSeries) - can't trim data, the length of the input time series is 0!" << endl;
        return false;
    }

    if( N == 0 ) {
        warningLog << "trimTimeSeries(TimeSeriesClassificationSample &timeSeries) - can't trim data, the number of dimensions in the input time series is 0!" << endl;
        return false;
    }

    //Compute the energy of the time series
    double maxValue = 0;
    VectorDouble x(M,0);

    for(UINT i=1; i<M; i++) {
        for(UINT j=0; j<N; j++) {
            x[i] += fabs(timeSeries[i][j]-timeSeries[i-1][j]);
        }
        x[i] /= N;
        if( x[i] > maxValue ) maxValue = x[i];
    }

    //Normalize x so that the maximum energy has a value of 1
    //At the same time search for the first time x[i] passes the trim threshold
    UINT firstIndex = 0;
    for(UINT i=1; i<M; i++) {
        x[i] /= maxValue;

        if( x[i] > trimThreshold && firstIndex == 0 ) {
            firstIndex = i;
        }
    }

    //Search for the last time x[i] passes the trim threshold
    UINT lastIndex = 0;
    for(UINT i=M-1; i>firstIndex; i--) {
        if( x[i] > trimThreshold && lastIndex == 0 ) {
            lastIndex = i;
            break;
        }
    }

    if( firstIndex == 0 && lastIndex == 0 ) {
        warningLog << "Failed to find either the first index or the last index!";
        return false;
    }

    if( firstIndex == lastIndex ) {
        warningLog << "The first index and last index are the same!";
        return false;
    }

    if( firstIndex > lastIndex ) {
        warningLog << "The first index is greater than the last index!";
        return false;
    }

    if( lastIndex == 0 ) {
        warningLog << "Failed to find the last index!";
        lastIndex = M-1;
    }

    //Compute how long the new time series would be if we trimmed it
    UINT newM = lastIndex-firstIndex;
    double trimPercentage = (double(newM) / double(M)) * 100.0;

    if( 100 - trimPercentage <= maximumTrimPercentage ) {

        MatrixDouble newTimeSeries(newM,N);
        UINT index = 0;
        for(UINT i=firstIndex; i<lastIndex; i++) {
            for(UINT j=0; j<N; j++) {
                newTimeSeries[index][j] = timeSeries[i][j];
            }
            index++;
        }

        timeSeries.setTrainingSample(timeSeries.getClassLabel(), newTimeSeries);
        return true;
    }

    warningLog << "Maximum Trim Percentage Excedded, Can't Trim Sample!";
    warningLog << " Original Timeseries Length: " << M << " Trimmed Timeseries Length: " << newM;
    warningLog << " Percentage: " << (100-trimPercentage) << " MaximumTrimPercentage: " << maximumTrimPercentage << endl;
    return false;
}