Napisz testy jednostkowe w Visual studio native tests dla metody TrapezoidalMethod zaimplementowanej w poniższej klasie. 
#pragma once

#include "Interval.h"
#include <vector>
#include <tuple>

class IntervalExtractor
{
    friend class IntervalExtractorPrivateTests;
public:
    /**
     * @brief Type alias for a pointer to an energy calculation method.
     *
     * This type alias represents a pointer to a member function of the `IntervalExtractor` class
     * that takes two integer parameters (`int, int`) and returns a `float`. It is used to specify
     * the method for calculating energy based on different integration methods.
     */
    using EnergyMethodFuncPtr = float (IntervalExtractor::*)(int, int) const;

    /**
     * @brief Enum for specifying the energy calculation method.
     *
     * This enum defines the available methods for energy calculation during the extraction process.
     */
    enum class EnergyMethod : uint8_t {
        LEFT_RECTANGLE_METHOD, ///< Left rectangle method for energy calculation.
        RIGHT_RECTANGLE_METHOD, ///< Right rectangle method for energy calculation.
        TRAPEZOIDAL_METHOD ///< Trapezoidal method for energy calculation.
    };

    /**
     * @brief Default constructor for the `IntervalExtractor` class.
     *
     * This constructor initializes the `IntervalExtractor` object with empty data vectors. The vectors
     * will reference a static empty vector by default, ensuring that no memory allocation occurs unless
     * the user provides specific data.
     */
    IntervalExtractor() = default;

    /**
     * @brief Constructs an `IntervalExtractor` with given data vector and optional time vector.
     *
     * If the time vector 't' is not provided, it defaults to a vector with implicit time indices (0, 1, 2, ...).
     * If the sizes of `y` and `t` differ, both vectors are reset to an empty vector, and a warning is logged.
     * This ensures that `y` and `t` always have matching sizes and consistent state.
     *
     * @param y Reference to a vector of function values (y-values).
     * @param energyMethod Energy calculation method (defaults to `TRAPEZOIDAL_METHOD`).
     * @param t Reference to a vector of time points corresponding to `y` (optional).
     * @param maxAcceptableTimeGap Maximum time gap between consecutive data points; 
     *        greater time gap is considered as data gathering failure and ends the interval
     *        (defaults to 1.0).
     */
    IntervalExtractor(std::vector<float>& y,
        EnergyMethod energyMethod = EnergyMethod::TRAPEZOIDAL_METHOD,
        std::vector<float>& t = empty,
        float maxAcceptableTimeGap = 1.0);

    /// @brief Deleted copy constructor to prevent copying.
    IntervalExtractor(const IntervalExtractor&) = delete;

    /// @brief Deleted copy assignment operator to prevent copying.
    IntervalExtractor& operator=(const IntervalExtractor&) = delete;

    /// @brief Default move constructor for efficient resource transfer.
    IntervalExtractor(IntervalExtractor&&) noexcept = default;

    /// @brief Default move assignment operator for efficient resource transfer.
    IntervalExtractor& operator=(IntervalExtractor&&) noexcept = default;

    /// @name Getters
    ///@{
    const std::vector<float>& getY() const {return y;}
    const std::vector<float>& getT() const {return t;}
    const std::vector<Interval>& getIntervals() const {return intervals;}
    float getMaxAcceptableTimeGap() const {return maxAcceptableTimeGap;}
    ///@}

    void extractIntervals(float absolute, float relative);

private:
    /// @brief Static empty vector used as the default reference for `y` and `t`.
    static std::vector<float> empty;

    /// @brief Reference to the primary data vector (e.g., measurements).
    /// Defaults to reference to `empty` if not provided.
    std::vector<float>& y = empty;

    /// @brief Reference to the secondary data vector (e.g., time values).
    /// Defaults to reference to `empty` if not provided.
    std::vector<float>& t = empty;

    std::vector<float> generatedT; ///< @brief Vector for generated time points when `t` is not provided.

    /// @brief Vector of extracted intervals from data.
    std::vector<Interval> intervals = {};

    /// @brief Pointer to the energy calculation method based on user selection (during construction).
    EnergyMethodFuncPtr calculateEnergy;

    /// @brief Maximum acceptable time gap between consecutive data points.
    /// Greater time gap is considered as data gathering failure and ends the interval.
    float maxAcceptableTimeGap = 1.0;

    /**
     * @brief Finds the length and type of a monotonic run starting at a given index.
     *
     * This method detects a monotonic segment (increasing, decreasing, or steady) from the
     * `y` vector, starting at `fromIndex`. It returns the length of the segment along with
     * its monotonic type.
     *
     * @param fromIndex Index at which to start searching for a monotonic run (inclusive).
     * @return A tuple consisting of:
     *         - the length of the monotonic run (at least 1),
     *         - the monotonicity type: `Interval::IntervalType::UP`, `Interval::IntervalType::DOWN`, or `Interval::IntervalType::STEADY`,
     *         - a boolean flag indicating if the interval is a gap based on time difference.
     */
    std::tuple<int, Interval::IntervalType, bool> findMonotonicRun(int fromIndex) const;

    /**
     * @brief Creates and adds an interval to the `intervals` vector.
     *
     * This method calculates the interval based on the specified start index, segment length,
     * interval type, and whether a gap exists. The interval is then added to the `intervals` vector.
     *
     * @param startIndex The starting index of the interval.
     * @param length The length of the interval.
     * @param intervalType The type of the interval (increasing, decreasing, or steady).
     * @param isGap Flag indicating if the interval is a gap based on time difference.
     */
    void createInterval(int startIndex, int length, Interval::IntervalType intervalType, bool isGap);

    /**
     * @brief Extracts intervals from the data with a threshold for steady intervals.
     *
     * This method iterates through the data to extract intervals based on absolute and relative thresholds,
     * detecting changes that exceed the specified thresholds or are considered steady. It updates the `intervals`
     * vector with the extracted intervals.
     *
     * @param absolute The absolute threshold for steady intervals.
     * @param relative The relative threshold for steady intervals.
     */
    void extractIntervalsWithThreshold(float absolute, float relative);

    /**
     * @brief Calculates the energy using the left rectangle method.
     *
     * This method computes the energy for a given segment using the left rectangle method,
     * which approximates the integral by using the left endpoints of each subinterval.
     *
     * @param startIndex The starting index of the segment.
     * @param length The length of the segment.
     * @return The calculated energy for the segment.
     */
    float leftRectangleMethod(int startIndex, int length) const;

    /**
     * @brief Calculates the energy using the right rectangle method.
     *
     * This method computes the energy for a given segment using the right rectangle method,
     * which approximates the integral by using the right endpoints of each subinterval.
     *
     * @param startIndex The starting index of the segment.
     * @param length The length of the segment.
     * @return The calculated energy for the segment.
     */
    float rightRectangleMethod(int startIndex, int length) const;

    /**
     * @brief Calculates the energy using the trapezoidal method.
     *
     * This method computes the energy for a given segment using the trapezoidal method,
     * which approximates the integral by averaging the left and right endpoints of each subinterval.
     *
     * @param startIndex The starting index of the segment.
     * @param length The length of the segment.
     * @return The calculated energy for the segment.
     */
    float trapezoidalMethod(int startIndex, int length) const;
};

#include "IntervalExtractor.h"
#include "Logger.h"
#include "TextProvider.h"
#include <iostream>
#include <numeric>
#include <algorithm> 

using namespace std;

vector<float> IntervalExtractor::empty = {};


IntervalExtractor::IntervalExtractor(std::vector<float>& y,
	EnergyMethod energyMethod,
	std::vector<float>& t,
	float maxAcceptableTimeGap)
	: y(y), maxAcceptableTimeGap(maxAcceptableTimeGap), t(t)
{
	if (t.size() != y.size()) {
		this->t = this->y = empty;
		LOG_WARN(TextProvider::getText("different_size_t_y"));
	}
	else if (this->t == empty) {
		generatedT.resize(this->y.size());
		iota(generatedT.begin(), generatedT.end(), 0.0f);
		this->t = generatedT;
	}
	if (maxAcceptableTimeGap <= 0) {
		this->maxAcceptableTimeGap = 1.0;
		LOG_WARN(TextProvider::getText("non_positive_maxAcceptableTimeGap"));
	}
	switch (energyMethod) {
	case EnergyMethod::LEFT_RECTANGLE_METHOD:
		calculateEnergy = &IntervalExtractor::leftRectangleMethod;
		break;
	case EnergyMethod::RIGHT_RECTANGLE_METHOD:
		calculateEnergy = &IntervalExtractor::rightRectangleMethod;
		break;
	case EnergyMethod::TRAPEZOIDAL_METHOD:
		calculateEnergy = &IntervalExtractor::trapezoidalMethod;
		break;
	default:
		calculateEnergy = &IntervalExtractor::trapezoidalMethod;
		break;
	}
}


std::tuple<int, Interval::IntervalType, bool> IntervalExtractor::findMonotonicRun(int fromIndex) const {
	const int n = static_cast<int>(y.size());
	int i = fromIndex + 1;
	bool isGap = false;
	while (i < n && y[i] > y[i - 1] && (isGap = (t[i] - t[i - 1] <= maxAcceptableTimeGap)))
		++i;
	if (i > fromIndex + 1)
		return { i - fromIndex - 1, Interval::IntervalType::UP, isGap };
	while (i < n && y[i] < y[i - 1] && (isGap = (t[i] - t[i - 1] <= maxAcceptableTimeGap)))
		++i;
	if (i > fromIndex + 1)
		return { i - fromIndex - 1, Interval::IntervalType::DOWN, isGap };
	return { 1, Interval::IntervalType::STEADY, isGap };
}


void IntervalExtractor::extractIntervalsWithThreshold(float absolute, float relative) {
	const int n = static_cast<int>(y.size());
	int fromIndex = 0;
	while (fromIndex < n - 1) {
		auto [segmentLength, type, isGap] = findMonotonicRun(fromIndex);
		float thresh = absolute + y[fromIndex] * relative;
		// check if steady interval or small change within threshold
		if (!isGap && (type == Interval::IntervalType::STEADY || std::fabs(y[fromIndex + segmentLength] - y[fromIndex]) <= thresh)) {
			type = Interval::IntervalType::STEADY;
			int i;
			float minVal = y[fromIndex];
			float maxVal = y[fromIndex];
			for (i = fromIndex + 1; i < n && maxVal - minVal <= thresh; ++i) {
				minVal = min(minVal, y[i]);
				maxVal = max(maxVal, y[i]);
			}
			segmentLength = i - fromIndex - (i < n ? 2 : 1);
			if (i == n) isGap = true;
		}
		createInterval(fromIndex, segmentLength, type, isGap);
		fromIndex += segmentLength;
	}
}


void IntervalExtractor::createInterval(int startIndex, int length, Interval::IntervalType intervalType, bool isGap) {
	float duration = t[startIndex + length] - t[startIndex];
	float startTime = t[startIndex];
	auto beginIt = y.begin() + startIndex;
	auto endIt = beginIt + length;
	float minY, maxY;
	if (intervalType == Interval::IntervalType::STEADY) {
		auto [minIt, maxIt] = std::minmax_element(beginIt, endIt);
		minY = *minIt;
		maxY = *maxIt;
	}
	else {
		minY = min(y[startIndex], y[startIndex + length]);
		maxY = max(y[startIndex], y[startIndex + length]);
	}
	float energy = (this->*calculateEnergy)(startIndex, length);
	intervals.push_back(Interval(energy, duration, minY, maxY, startTime, startIndex, length, intervalType, isGap));
}


float IntervalExtractor::leftRectangleMethod(int startIndex, int length) const {
	float energy = 0.0;
	for (int i = startIndex + 1; i <= startIndex + length; i++) {
		energy += y[i - 1] * (t[i] - t[i - 1]);
	}
	return energy;
}


float IntervalExtractor::rightRectangleMethod(int startIndex, int length) const {
	float energy = 0.0;
	for (int i = startIndex + 1; i <= startIndex + length; i++) {
		energy += y[i] * (t[i] - t[i - 1]);
	}
	return energy;
}


float IntervalExtractor::trapezoidalMethod(int startIndex, int length) const {
	float energy = 0.0;
	for (int i = startIndex + 1; i <= startIndex + length; i++) {
		energy += (y[i - 1] + y[i]) * (t[i] - t[i - 1]);
	}
	return 0.5f * energy;
}


void IntervalExtractor::extractIntervals(float absolute, float relative) {
	extractIntervalsWithThreshold(absolute, relative);
}