"""
Pin 0 led red
Pin 1 led green
Pin 4 dot
Pin 9 A left display
Pin 10 B left display
Pin 11 C left display
Pin 12 D left display
Pin 13 E left display
Pin 14 F left display
Pin 15 G left display
Pin 16 A right display
Pin 17 B right display
Pin 18 C right display
Pin 19 D right display
Pin 20 E right display
Pin 21 F right display
Pin 22 G right display
Pin 27 Echo
Pin 28 Trig
"""

from machine import Pin
import time
import sys
import math
import select

def handle_echo(pin: Pin) -> None:
    if pin.value():
        global pulse_start
        pulse_start = time.ticks_us()
    else:
        global pulse_end
        global waiting_for_echo
        pulse_end = time.ticks_us()
        waiting_for_echo = True

def write_distance(distance: str, displays_array: list) -> None:
    state_display_left: str = get_pins(int(distance[0]))
    state_display_right: str = get_pins(int(distance[1]))
    for index in range(len(displays_array[0])):
        displays_array[0][index].value(int(state_display_left[index]))
    for index in range(len(displays_array[1])):
        displays_array[1][index].value(int(state_display_right[index]))
    print(f"{distance}")

def write_timeout(displays_array: list) -> None:
    pins_timeout: list = ["0001110", "0011101"]
    for indexDisplay in range(len(displays_array)):
        for indexPin in range(len(displays_array[indexDisplay])):
            displays_array[indexDisplay][indexPin].value(
                int(pins_timeout[indexDisplay][indexPin])
            )

    print("00")

def get_pins(number: int) -> str:
    pins_state: list = [
        "1111110",
        "0110000",
        "1101101",
        "1111001",
        "0110011",
        "1011011",
        "1011111",
        "1110000",
        "1111111",
        "1111011",
    ]
    return pins_state[number]

def calculate_distance(pulse_start: int, pulse_end: int) -> str:
    return "%02d" % (round(time.ticks_diff(pulse_end, pulse_start) / 58, 0))

def check_alarm_and_meters(distance: str, alarm: int) -> bool:
    return int(distance) < alarm, int(distance) >= 100

def send_trigger(pin: Pin) -> None:
    pin.on()
    time.sleep_us(10)
    pin.off()

def handle_received_command(command: str) -> None:
    if "alarm=" in command:
        new_alarm_limit = int(command.split("=")[1])
        global alarm_limit
        alarm_limit = new_alarm_limit
        print(f"New alarm limit set to {alarm_limit}")

#################INIT################
led = Pin("LED", Pin.OUT, value=0)  #
led(1)  #
time.sleep(0.5)  #
led(0)  #
#####################################
pin_led_red: Pin = Pin(0, Pin.OUT, value=0)
pin_led_green: Pin = Pin(1, Pin.OUT, value=0)
pin_display_dot: Pin = Pin(2, Pin.OUT, value=0)
displays: list = [[], []]  # left, right
for i in range(7):
    displays[0].append(Pin(9 + i, Pin.OUT, value=0))
for i in range(7):
    displays[1].append(Pin(16 + i, Pin.OUT, value=0))
pin_echo: Pin = Pin(27, Pin.IN, Pin.PULL_DOWN)
pin_echo.irq(handle_echo, Pin.IRQ_FALLING | Pin.IRQ_RISING)
pin_trigger: Pin = Pin(28, Pin.OUT, value=0)
pulse_start: int = 0
pulse_end: int = 0
next_send_trigger: int = 0
next_toggle_led_red: int = 0
next_timeout_echo: int = 0
alarm_limit: int = 30
waiting_for_echo: bool = False
timeout_echo: bool = False
alarmed: bool = False
meters: bool = False

try:
    while True:
        current_timestamp: int = time.ticks_us()

        # Check if there's data to be read from the PC
        if pin_trigger.value() == 0 and select.select([sys.stdin], [], [], 0)[0]:
            received_data = sys.stdin.buffer.readline().strip()
            if received_data:
                received_command = received_data.decode().lower()
                handle_received_command(received_command)

        # Time to send another trigger
        if current_timestamp >= next_send_trigger:
            send_trigger(pin_trigger)
            next_send_trigger: int = time.ticks_us() + 1_000_000
            next_timeout_echo: int = (
                time.ticks_us() + 100_000
            )  # time max travelled by sound for 800cm (400*2)
            timeout_echo: bool = False

        # When the echo is in timeout
        if current_timestamp >= next_timeout_echo and not timeout_echo:
            timeout_echo: bool = True
            alarmed: bool = True
            write_timeout(displays)
            pin_led_red.on()
            pin_led_green.off()
            pin_display_dot.off()

        if current_timestamp >= next_toggle_led_red:
            next_toggle_led_red: int = time.ticks_us() + 500000
            pin_led_red.toggle()

        # When echo is received
        if waiting_for_echo and not timeout_echo:
            distance_cm: float = calculate_distance(pulse_start, pulse_end)
            write_distance(distance_cm, displays)
            alarmed, meters = check_alarm_and_meters(distance_cm, alarm_limit)
            waiting_for_echo: bool = False
            next_timeout_echo = math.inf
            # When the object is nearer than the limit
            if alarmed:
                next_toggle_led_red = current_timestamp
                pin_display_dot.off()
                pin_led_green.off()
            else:
                next_toggle_led_red = math.inf
                if meters:
                    pin_display_dot.on()
                else:
                    pin_display_dot.off()
                pin_led_red.off()
                pin_led_green.on()

# End Of Program
except KeyboardInterrupt:
    pin_led_red.off()
    pin_led_green.off()
    for display in displays:
        for pin in display:
            pin.off()
    pin_display_dot.off()
    pin_echo.off()
    pin_trigger.off()
    print("EOP")


import argparse
import json
import os
from enum import Enum
from functools import total_ordering
from typing import Any, Dict, List, Optional, Union

import yaml

from pydantic import BaseModel, Field, validator
from sparsify.schemas import SampledHyperparameter
from sparsify.utils import METRICS, TASK_REGISTRY

__all__ = [
    "APIArgs",
    "RunMode",
    "SparsificationTrainingConfig",
    "Metrics",
    "DEFAULT_OUTPUT_DIRECTORY",
]

DEFAULT_OUTPUT_DIRECTORY = "./output"

class RunMode(Enum):
    """
    Class defining options for auto runner modes
    """

    training_aware = "training_aware"  # full sparsification run from dense baseline
    sparse_transfer = "sparse_transfer"
    teacher_only = "teacher_only"  # only train dense upstream teacher

class APIArgs(BaseModel):
    """
    Class containing the front-end arguments for Sparsify.Auto
    """

    task: str = Field(
        title="task",
        description="task to train the sparsified model on",
    )
    dataset: str = Field(
        title="dataset",
        description="path to dataset to train on",
    )
    save_directory: str = Field(
        title="save_directory",
        description="Absolute path to save directory",
        default=DEFAULT_OUTPUT_DIRECTORY,
    )
    optim_level: float = Field(
        title="optim_level",
        description=(
            "Preferred tradeoff between accuracy and performance. "
            "Float value in the range [0, 1]. Default 0.5"
        ),
        default=0.5,
    )
    base_model: Optional[str] = Field(
        title="base_model",
        default=None,
        description="path to base model to begin sparsification from",
    )
    recipe: Optional[str] = Field(
        title="recipe",
        default=None,
        description="file path to or zoo stub of sparsification recipe to be applied",
    )
    recipe_args: Optional[Dict[str, Any]] = Field(
        title="recipe_args",
        description="keyword args to override recipe variables with",
        default_factory=dict,
    )
    distill_teacher: str = Field(
        title="distil_teacher",
        description="teacher to use for distillation. Can be a path to a model file or "
        "zoo stub, 'off' for no distillation, and default value of 'auto' to auto-tune "
        "base model as teacher",
        default="auto",
    )
    optimizing_metric: List[str] = Field(
        title="optimizing_metric",
        description=(
            "The criterion to search model for, multiple metrics can be specified, "
            "e.g. --optimizing_metric f1 --optimizing_metric latency. Supported  "
            f"metrics are {METRICS}"
        ),
        default=None,
    )
    kwargs: Optional[Dict[str, Any]] = Field(
        title="kwargs",
        description="optional task specific arguments to add to config",
        default_factory=dict,
    )
    run_mode: RunMode = Field(
        title="run_mode",
        description=(
            "training run mode objective - 'sparse_transfer' or 'training_aware'. "
            "Default is 'sparse_transfer'"
        ),
        default=RunMode.sparse_transfer,
    )

    @validator("task")
    def task_must_be_registered(cls, v):
        for task in TASK_REGISTRY.values():
            if v == task:
                return task.name
        nl = "\n"  # backslash not allowed in f string expression
        raise ValueError(
            f"Task '{v}' is not a recognize task. List of supported domains and and "
            "their accepted task name aliases:"
            f"{nl.join([task.pretty_print() for task in TASK_REGISTRY.values()])}"
        )

    @classmethod
    def from_cli(cls, args: Optional[List[str]] = None):
        """
        :param args: optional args list to override sys.argv with. Default None
        :return: APIArgs object populated from cli args
        """
        arg_parser = argparse.ArgumentParser()
        _add_schema_to_parser(arg_parser, cls)
        parsed_args = arg_parser.parse_args(args)
        _convert_dict_args(parsed_args, cls)
        return cls(**vars(parsed_args))

class SparsificationTrainingConfig(BaseModel):
    """
    Configuration class for sparsification aware training using SparseML and its
    training integrations
    """

    task: str = Field(
        description="task to train the sparsified model on",
    )
    dataset: str = Field(
        description="path to the dataset to train the task on",
    )
    base_model: Optional[str] = Field(
        description="path to the model to be sparsified",
    )
    distill_teacher: str = Field(
        description="optional path to a distillation teacher for training",
        default="auto",
    )
    recipe: Optional[str] = Field(
        description="file path to or zoo stub of sparsification recipe to be applied",
    )
    recipe_args: Dict[str, Any] = Field(
        description="keyword args to override recipe variables with",
        default_factory=dict,
    )
    kwargs: Dict[str, Any] = Field(
        description="optional task specific arguments to add to config",
        default_factory=dict,
    )
    tuning_parameters: List[SampledHyperparameter] = Field(
        title="tuning_parameters",
        description=(
            "List of tuning hyperparameters. Values are used to overwrite respective "
            "recipe fields"
        ),
        default=[],
    )

    @classmethod
    def from_yaml(cls, config_yaml: str):
        """
        :param config_yaml: raw yaml string or file path to config yaml file to load
        :return: loaded sparsification training config
        """
        if os.path.exists(config_yaml):
            with open(config_yaml) as yaml_file:
                config_yaml = yaml_file.read()

        return cls.parse_obj(yaml.safe_load(config_yaml))

    def yaml(self, file_path: Optional[str] = None):
        """
        :param file_path: optional file path to write the generated yaml config to
        :return: this config represented as a yaml string
        """
        config_dict = self.dict()

        if file_path:
            with open(file_path, "w") as config_file:
                yaml.dump(data=config_dict, stream=config_file)

        return yaml.dump(config_dict)

@total_ordering
class Metrics(BaseModel):
    """
    Class containing metrics for a trained model. Contains all information needed to
    determine run quality by the config creator.
    """

    metrics: Dict[str, float] = Field(description="Model accuracy on validation set")
    objective_key: str = Field(
        description=(
            "keys of the accuracy dict, for the metrics used to track run quality"
        )
    )
    train_time: Optional[float] = Field(
        description="Total train time, including hyperparameter tuning", default=None
    )

    def _check_valid_operand(self, other):
        if not isinstance(other, Metrics):
            raise TypeError(
                "Comparison not supported between instances of 'Metrics' and "
                f"{type(other)}"
            )
        if self.objective_key != other.objective_key:
            raise ValueError(
                "Comparison not supported between instances of 'Metrics' with "
                f"differing 'objective_key' of '{self.objective_key}' "
                f"and '{other.objective_key}'"
            )

    def __eq__(self, other):
        self._check_valid_operand(other)
        return self.metrics[self.objective_key] == other.metrics[self.objective_key]

    def __lt__(self, other):
        self._check_valid_operand(other)
        return self.metrics[self.objective_key] < other.metrics[self.objective_key]

    @property
    def display_string(self) -> str:
        """
        :return: string representation of metrics values
        """
        string_body = "\n".join(
            [f"{metric}: {value}" for metric, value in self.metrics.items()]
        )
        return f"Post-training metrics:\n{string_body}\n"

    def finalize(self):
        """
        :return: string detailed text of results, results are also written to
            results.txt in the `model_directory`
        """
        output_string = self.output_string
        with open(os.path.join(self.model_directory, "results.txt"), "w") as f:
            f.write(output_string)
        return output_string

    @property
    def output_string(self) -> str:
        """
        :return: detailed text summarizing run results
        """
        return (
            "-------------------- Sparsify.Auto Results --------------------\n"
            f"Finished training {self.config.task} model on your dataset.\n"
            "\n"
            f"{self.metrics.display_string}"
            "\n"
            "You can find your sparsified model and everything you need to deploy "
            f"in {self.model_directory}\n"
        )

def _add_schema_to_parser(parser: argparse.ArgumentParser, model: BaseModel):
    # populate ArgumentParser args from pydantic model
    fields = model.__fields__
    for name, field in fields.items():
        argument_kwargs = {}

        is_dict = field.default_factory and isinstance(field.default_factory(), dict)
        is_union = getattr(field.type_, "__origin__", None) is Union
        # Support for CLI args that can be repeated
        is_list = getattr(field.outer_type_, "_name", None) == "List"

        if is_dict or is_union:
            argument_kwargs["type"] = str if is_dict else _str_number_union_parser

        if field.required:
            argument_kwargs["required"] = True
        else:
            argument_kwargs["default"] = (
                field.default if not is_dict else str(field.default_factory())
            )

        if is_list:
            argument_kwargs["action"] = "append"
        elif field.type_ == bool:
            argument_kwargs["action"] = "store_false" if field.default else "store_true"

        parser.add_argument(
            f"--{name}", dest=name, help=field.field_info.description, **argument_kwargs
        )

def _str_number_union_parser(value: str):
    # type conversion function for "Union" type support with argparse. Supports string
    # and numeric values only
    if value.isdigit():
        return int(value)
    try:
        return float(value)
    except ValueError:
        return value

def _convert_dict_args(args: argparse.ArgumentParser, model: BaseModel):
    # convert argparse args that should be dicts from str
    fields = model.__fields__
    for name, field in fields.items():
        is_dict = field.default_factory and isinstance(field.default_factory(), dict)
        if not is_dict or not hasattr(args, name):
            # field is not a dict field or field not parsed
            continue
        field_val_str = getattr(args, name).replace("'", '"')  # use double quotes
        setattr(args, name, json.loads(field_val_str))


from pytest import mark
from lacp_lib import (
    associate_interface_to_lag,
    associate_vlan_to_l2_interface,
    associate_vlan_to_lag,
    create_lag_active,
    create_vlan,
    LOCAL_STATE,
    REMOTE_STATE,
    set_lacp_rate_fast,
    turn_on_interface,
    verify_connectivity_between_hosts,
    verify_state_sync_lag,
    verify_turn_on_interfaces
)

TOPOLOGY = """
# +-------+                                 +-------+
# |       |     +-------+     +-------+     |       |
# |  hs1  <----->  sw1  <----->  sw2  <----->  hs2  |
# |       |     +-------+     +-------+     |       |
# +-------+                                 +-------+

# Nodes
[type=openswitch name="Switch 1"] sw1
[type=openswitch name="Switch 2"] sw2
[type=host name="Host 1"] hs1
[type=host name="Host 2"] hs2

# Links
hs1:1 -- sw1:3
sw1:1 -- sw2:1
sw1:2 -- sw2:2
sw2:3 -- hs2:1
"""

@mark.platform_incompatible(['docker'])
def test_lag_shutdown_by_default(topology, step):
    """Test LAG with shutdown by default ('no shutdown').

    By default a new LAG will be configured with 'no shutdown'. IPv4 pings
    from both clients must be successful.
    """

    step('\n############################################')
    step('Test lag shutdown (By default)')
    step('############################################')

    sw1 = topology.get('sw1')
    sw2 = topology.get('sw2')
    hs1 = topology.get('hs1')
    hs2 = topology.get('hs2')

    step('### Verifying switches are not None ###')
    assert sw1 is not None, 'Topology failed getting object sw1'
    assert sw2 is not None, 'Topology failed getting object sw2'

    step('### Verifying hosts are not None ###')
    assert hs1 is not None, 'Topology failed getting object hs1'
    assert hs2 is not None, 'Topology failed getting object hs2'

    sw1_lag_id = '100'
    sw2_lag_id = '200'
    h1_ip_address = '10.0.0.1'
    h2_ip_address = '10.0.0.2'
    vlan = '100'
    mask = '/24'

    ports_sw1 = list()
    ports_sw2 = list()
    port_labels = ['1', '2', '3']

    step("### Mapping interfaces from Docker ###")
    for port in port_labels:
        ports_sw1.append(sw1.ports[port])
        ports_sw2.append(sw2.ports[port])

    step("Sorting the port list")
    ports_sw1.sort()
    ports_sw2.sort()
    step("### Turning on all interfaces used in this test ###")
    for port in ports_sw1:
        turn_on_interface(sw1, port)

    for port in ports_sw2:
        turn_on_interface(sw2, port)

    step("#### Validate interfaces are turned on ####")
    verify_turn_on_interfaces(sw1, ports_sw1)
    verify_turn_on_interfaces(sw2, ports_sw2)

    # Changing hardcoded interfaces for dynamic assignation
    mac_addr_sw1 = sw1.libs.vtysh.show_interface('1')['mac_address']
    mac_addr_sw2 = sw2.libs.vtysh.show_interface('1')['mac_address']
    assert mac_addr_sw1 != mac_addr_sw2,\
        'Mac address of interfaces in sw1 is equal to mac address of ' +\
        'interfaces in sw2. This is a test framework problem. Dynamic ' +\
        'LAGs cannot work properly under this condition. Refer to Taiga ' +\
        'issue #1251.'

    step("### Create LAG in both switches ###")
    create_lag_active(sw1, sw1_lag_id)
    create_lag_active(sw2, sw2_lag_id)

    step("### Set LACP rate to fast ###")
    set_lacp_rate_fast(sw1, sw1_lag_id)
    set_lacp_rate_fast(sw2, sw2_lag_id)

    step("### Configure vlan and switch interfaces ###")
    create_vlan(sw1, vlan)
    create_vlan(sw2, vlan)

    step("### Associate vlan with lag in both switches ###")
    associate_vlan_to_lag(sw1, vlan, sw1_lag_id)
    associate_vlan_to_lag(sw2, vlan, sw2_lag_id)

    step("### Associate vlan with l2 interfaces in both switches ###")
    associate_vlan_to_l2_interface(sw1, vlan, ports_sw1[2])
    associate_vlan_to_l2_interface(sw2, vlan, ports_sw2[2])

    step("### Associate interfaces [1,2] to lag in both switches ###")
    for intf in ports_sw1[0:2]:
        associate_interface_to_lag(sw1, intf, sw1_lag_id)

    for intf in ports_sw2[0:2]:
        associate_interface_to_lag(sw2, intf, sw2_lag_id)

    step("### Verify if LAG is synchronized ###")
    verify_state_sync_lag(sw1, ports_sw1[0:2], LOCAL_STATE, 'active')
    verify_state_sync_lag(sw1, ports_sw1[0:2], REMOTE_STATE, 'active')

    step("### Configure IP and bring UP in host 1 ###")
    hs1.libs.ip.interface('1', addr=(h1_ip_address + mask), up=True)

    step("### Configure IP and bring UP in host 2 ###")
    hs2.libs.ip.interface('1', addr=(h2_ip_address + mask), up=True)

    verify_connectivity_between_hosts(hs1, h1_ip_address, hs2, h2_ip_address,
                                      True)

    step('\n############################################')
    step('Test lag shutdown (By default) DONE')
    step('############################################')


# Was möchte ich tun: Ich möchte einen Array einlesen mit den letzen X ecg datenpunkten (ca 100) und zusätzlich eine Info
# pber die sportart dazu dann gebe ich die aktivität mit und predicte wie die nächsten n ecg datenpunkte aussehen.
# Wäre optimal vorzutrainieren was ein ecg ist und dann nur noch optimizen ?
import csv

# Look into stock market prediction, they try to do the same.
# Possible data sources for algo: https://towardsdatascience.com/the-complete-guide-to-time-series-analysis-and-forecasting-70d476bfe775
# Transformer for time series https://arxiv.org/pdf/2205.13504.pdf

# Ich möchte die abstände zwischen einzelnen beats predicten nicht die höhe oder sowas. Sondern die frequenz => deshlab
# benutz ich die Fourier transofmraion zum beispiel oder die wavelet decomposition ? will ich dannach zurück transfomieren ?

# Current approch: Transform data into Furnier => train a classifier, with transform and label predict next fourier
# inverse fournier and try to get the ecg signal in this way.

import numpy as np
import torch
from matplotlib import pyplot as plt
from torch import nn

# build batches without crossections between themself. Just cut the array in pieces of the same length.
def split_to_batches(ecg_file):
    data_array_action = []
    # split in a multi dim array. ech list element is a tuple a list of the ecg of a action and the lable
    with open(ecg_file, "r") as f:
        reader = csv.reader(f)
        data_array = []
        target_length = 1200  # ca 20 beats in theorie but it around 3-6
        length_counter = 0
        # score_cash = 0
        for row in reader:
            if length_counter <= target_length:
                data_array.append((row[0], row[1]))
                # score_cash += float(row[1])
                length_counter += 1
            else:
                data_array_action.append(data_array)
                data_array = [(row[0], row[1])]
                # score_cash = float(row[1])
                length_counter = 1

    return data_array_action

# read in batches make fft and fft back and compare the results ?
def plot_data_fft(data_array_action):
    # Prepare the data
    # Extract input data and labels
    signal_array_strings = np.array([data for data, _ in data_array_action[0]])
    # score = np.array([label for _, label in data_array_action[0]])

    signal_array = np.array(signal_array_strings, dtype=np.float32)

    # Perform Fourier Transform
    fourier_transform = np.fft.fft(signal_array)
    frequencies = np.fft.fftfreq(len(fourier_transform))

    # Plot the original signal and its Fourier transform
    plt.figure(figsize=(10, 6))

    plt.subplot(2, 2, 1)
    plt.plot(signal_array)
    plt.xlabel('Time (samples)')
    plt.ylabel('Amplitude')
    plt.title('Original Signal')

    plt.subplot(2, 2, 2)
    plt.plot(frequencies, np.abs(fourier_transform))
    plt.xlabel('Frequency (Hz)')
    plt.ylabel('Magnitude')
    plt.title('Fourier Transform')

    # Inverse Fourier Transform
    inverted_signal = np.fft.ifft(fourier_transform).real

    # Plot the inverted signal
    plt.subplot(2, 2, 3)
    plt.plot(inverted_signal)
    plt.xlabel('Time (samples)')
    plt.ylabel('Amplitude')
    plt.title('Inverted Signal')

    # Plot the differences between original and inverted signals
    plt.subplot(2, 2, 4)
    plt.plot(signal_array - inverted_signal)
    plt.xlabel('Time (samples)')
    plt.ylabel('Difference')
    plt.title('Difference (Original - Inverted)')

    plt.tight_layout()
    plt.show()

def plot_prediciton(pred, groud_truth):
    print("pred", inverse_img_number(pred))
    print("ground", inverse_img_number(groud_truth))

    inverted_signal_pred = np.fft.ifft(inverse_img_number(pred))
    inverted_signal_ground = np.fft.ifft(inverse_img_number(groud_truth))

    # Plot the original signal and its Fourier transform
    plt.figure(figsize=(10, 6))

    plt.subplot(2, 2, 1)
    plt.plot(inverted_signal_ground)
    plt.xlabel('Time (samples)')
    plt.ylabel('Amplitude')
    plt.title('Original Signal ground_truth')

    # plt.subplot(2, 2, 2)
    # plt.plot(frequencies, np.abs(fourier_transform))
    # plt.xlabel('Frequency (Hz)')
    # plt.ylabel('Magnitude')
    # plt.title('Fourier Transform')

    # Plot the inverted signal
    plt.subplot(2, 2, 3)
    plt.plot(inverted_signal_pred)
    plt.xlabel('Time (samples)')
    plt.ylabel('Amplitude')
    plt.title('Inverted Signal prediction')

    # Plot the differences between original and inverted signals
    plt.subplot(2, 2, 4)
    plt.plot(inverted_signal_ground - inverted_signal_pred)
    plt.xlabel('Time (samples)')
    plt.ylabel('Difference')
    plt.title('Difference (Ground truth - predicted)')

    plt.tight_layout()
    plt.show()

# Define the RNN model
class RNN(nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
        super(RNN, self).__init__()
        self.rnn = nn.RNN(input_size, hidden_size, batch_first=True)
        self.fc = nn.Linear(hidden_size, output_size)

    def forward(self, x, h):
        out, h = self.rnn(x, h)
        out = self.fc(out)
        return out, h

# Basic baseline
class NeuralNetwork(nn.Module):
    def __init__(self, input_size, hidden_size, hidden_size_2, output_size):
        super(NeuralNetwork, self).__init__()
        self.fc1 = nn.Linear(input_size, hidden_size)
        self.relu = nn.ReLU()
        self.fc2 = nn.Linear(hidden_size, hidden_size_2)
        self.relu = nn.ReLU()
        self.fc3 = nn.Linear(hidden_size_2, output_size)

    def forward(self, x):
        x = self.fc1(x)
        x = self.relu(x)
        x = self.fc2(x)
        x = self.relu(x)
        x = self.fc3(x)
        return x

def split_img_values(value_array):
    # Create a new array with twice the length
    transformed_array = np.zeros(len(value_array) * 2)

    # Fill the new array with real and imaginary parts alternately
    for i, num in enumerate(value_array):
        transformed_array[i * 2] = np.real(num)  # Real part
        transformed_array[i * 2 + 1] = np.imag(num)  # Imaginary part

    return transformed_array

def inverse_img_number(values_array):
    # Create an array of complex numbers
    complex_numbers = []
    for i in range(0, len(values_array), 2):
        real_part = values_array[i]
        imag_part = values_array[i + 1]
        complex_numbers.append(complex(real_part, imag_part))

    complex_numbers = np.array(complex_numbers)
    return complex_numbers

def train_model_2(batches):
    # Data overview:
    print("batch size: 1201 1200 ecg datapoints + 1 score")
    print("Feature: FFT")
    print("batches: ", len(batches))
    print("test_size: 20% not implemented yet")

    # strategics, i need the score from the next batch to train because the score is the only thing that indicates
    # how it will shifts, the look into the furture.
    x_train = []
    x_test = []
    y_train = []
    y_test = []

    # TODO to get the test files, get 20 % random indices and if this index is chosen add the pair to test instead of train
    array_length = len(batches)
    percentage_of_ones = 0.2  # 20%
    # Calculate the number of ones and zeros
    num_ones = int(array_length * percentage_of_ones)
    num_zeros = array_length - num_ones
    # Create an array with ones and zeros
    array_index = np.concatenate((np.ones(num_ones), np.zeros(num_zeros)))
    # Shuffle the array randomly
    np.random.shuffle(array_index)

    for i in range(len(batches) - 1):

        signal_array_strings_curr = np.array([data for data, _ in data_array_action[i]])
        signal_array_strings_next = np.array([data for data, _ in data_array_action[i + 1]])
        label_curr = np.array([label for _, label in data_array_action[i]])
        label_next = np.array([label for _, label in data_array_action[i + 1]])

        signal_array_strings_curr = signal_array_strings_curr.astype(np.float32)
        signal_array_strings_next = signal_array_strings_next.astype(np.float32)
        label_curr = label_curr.astype(np.float32)
        label_next = label_next.astype(np.float32)

        # print(batches[i])
        # connect scor to signal davor brauchst du die Transformation

        # fourier_transform = np.fft.fft(signal_array_strings_curr).real
        fourier_transform = np.fft.fft(signal_array_strings_curr)
        frequencies = np.fft.fftfreq(len(fourier_transform))

        # Brauche den score form nächsten nciht vom jetzigen.
        temp = np.concatenate((split_img_values(fourier_transform), label_curr, label_next), axis=0)

        # fourier_transform2 = np.fft.fft(signal_array_strings_next).real
        fourier_transform2 = np.fft.fft(signal_array_strings_next)
        frequencies2 = np.fft.fftfreq(len(fourier_transform2))

        if array_index[i] == 0:
            x_train.append(temp)
            y_train.append(split_img_values(fourier_transform2))
        else:
            x_test.append(temp)
            y_test.append(split_img_values(fourier_transform2))

    # print(x_train, y_train)

    # Hyperparameters
    input_size = 4804  # Size of each input vector (e.g., window size)
    hidden_size = 300  # Number of hidden units in the RNN
    hidden_size_2 = 1000  # Number of hidden units in the RNN
    output_size = 2402  # Size of the predicted output vector

    # Convert data to PyTorch tensors
    x_train_tensor = torch.tensor(x_train, dtype=torch.float32)
    y_train_tensor = torch.tensor(y_train, dtype=torch.float32)
    x_test_tensor = torch.tensor(x_test, dtype=torch.float32)
    y_test_tensor = torch.tensor(y_test, dtype=torch.float32)

    # Create an instance of the neural network
    neural_net = NeuralNetwork(input_size, hidden_size, hidden_size_2, output_size)

    # Loss function and optimizer
    criterion = nn.MSELoss()
    optimizer = torch.optim.Adam(neural_net.parameters(), lr=0.001)

    print("Start training!")
    # Training loop
    num_epochs = 100
    for epoch in range(num_epochs):
        # Forward pass
        outputs = neural_net(x_train_tensor)

        # Compute the loss
        loss = criterion(outputs, y_train_tensor)

        # Backpropagation and optimization
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        print(f'Epoch [{epoch + 1}/{num_epochs}], Loss: {loss.item():.4f}')

    print("Training finished.")

    print("start testing")
    with torch.no_grad():
        predictions = neural_net(x_test_tensor)

    for wave, ground_truth in zip(predictions, y_test):
        plot_prediciton(wave, ground_truth)

    """
    # Create an instance of the neural network
    rnn = RNN(input_size, hidden_size, output_size)

    # Loss function and optimizer
    criterion = nn.MSELoss()
    optimizer = torch.optim.Adam(rnn.parameters(), lr=0.001)

    # Convert data to PyTorch tensors
    x_train_tensor = torch.tensor(x_train, dtype=torch.float32)
    y_train_tensor = torch.tensor(y_train, dtype=torch.float32)

    # Training loop
    num_epochs = 100
    for epoch in range(num_epochs):
        # Initialize hidden state
        h = torch.zeros(1, x_train_tensor.size(0), hidden_size)

        # Forward pass
        outputs, h = rnn(x_train_tensor, h)

        # Compute the loss
        loss = criterion(outputs, y_train_tensor)

        # Backpropagation and optimization
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        print(f'Epoch [{epoch + 1}/{num_epochs}], Loss: {loss.item():.4f}')

    print("Training finished.")
    """

if __name__ == "__main__":
    print("NN_model_2")
    # Output file name
    ecg_file = "../data/DHM 2/Andrei/transformed_data.csv"
    data_array_action = split_to_batches(ecg_file)
    # plot_data_fft(data_array_action)
    train_model_2(data_array_action)
    print("end")


#!/usr/bin/env python3

import yaml

from slack_cleaner2 import *

with open("config.yml", "r") as configfile:
  cfg = yaml.load(configfile, Loader=yaml.BaseLoader)

f = is_not_pinned()
before = a_while_ago(days=7)

for token in cfg["tokens"]:
  s = SlackCleaner(token)
  s.sleep_for = 1.0

  with s.log.group("files"):
    for entry in filter(f, s.files(before=before)):
      entry.delete()

  for c in s.conversations:
    with s.log.group(c.name):
      for msg in filter(f, c.msgs(before=before)):
        if (c.type.value != 4) or (msg.user == s.myself):
          msg.delete(as_user=True, files=True, replies=True)

  s.log.summary()


"""
This example is inspired by https://realpython.com/python-gil/ and designed to address issues with performance bottlenecks associated with multithreading when data collection and analysis are done using the same process hence sharing Python Interpretter and are subject to GIL.

"""

from numpy import random

# multi_threaded.py
import time
from threading import Thread
results = [None]*2
def generate_random_array(size = (10,1000,1000), dtype = 'uint16'):
    from numpy import random
    return random.randint(0,4095,size = size).astype(dtype)

def save_to_drive(arr,filename = '1.h5py'):
    from h5py import File
    from tempfile import gettempdir
    with File(gettempdir()+'/'+filename,'a') as f:
        f.create_dataset('data', data = arr, chunks= (1,1000,1000))
arr1 = generate_random_array(size = (100,1000,1000))
arr2 = generate_random_array(size = (100,1000,1000))
arr3 = generate_random_array(size = (100,1000,1000))
# t1 = Thread(target=save_to_drive, args=(arr1,str(time.time())+'_1.h5py'))
# t2 = Thread(target=save_to_drive, args=(arr2,str(time.time())+'_2.h5py'))
# t3 = Thread(target=save_to_drive, args=(arr3,str(time.time())+'_3.h5py'))
# start = time.time()
# t1.start()
# t2.start()
# #t3.start()
# t1.join()
# t2.join()
# #t3.join()
# end = time.time()
#
# print('Time taken in seconds -', end - start)

start = time.time()
save_to_drive(arr1,str(time.time())+'_1.h5py')
save_to_drive(arr2,str(time.time())+'_2.h5py')
#save_to_drive(arr3,str(time.time())+'_3.h5py')
end = time.time()

print('Time taken in seconds -', end - start)


import gzip
import json
import argparse
from tqdm import tqdm

if __name__=="__main__":
    parser = argparse.ArgumentParser(description='Create bilingual pretraining file for tevatron.')
    parser.add_argument('--input', dest='input', required=True)
    parser.add_argument('--output', dest='output', required=True)

    args = parser.parse_args()

    with open(args.input) as f, open(args.output, "w") as g:
        for line in tqdm(f):
            query, text = line.strip().split("\t")
            query = query.strip().lower()
            text = text.strip().lower()

            temp = {}
            temp["query"] = query
            temp["positives"] = [pos_text]
            temp["negatives"] = []

            g.write(json.dumps(temp)+"\n")


from collections import OrderedDict
from transformers.transformer_base import TransformerBase

class ListReturnsTransformer(TransformerBase):

    RENAME_DATA = OrderedDict([
        ("EFiledlist.ret_prd", "return_period"),
        ("EFiledlist.mof", "method_of_filing"),
        ("EFiledlist.dof", "date_of_filing"),
        ("EFiledlist.rtntype", "return_type"),
    ])

    def rearrange_transformation(self):
        self.data = self.data['EFiledlist']


import random
from datetime import datetime

now = datetime.now()

current_time = now.strftime("%H:%M:%S")

def shuffle(arr):
  start = datetime.now()
  l = len(arr)
  a = []
  for i in arr:
    a.insert(random.randrange(l-1), i)
    i += 1
  end = datetime.now()
  total = end-start
  print("shuffle", total)
  return a

def reverse(arr):
  start = datetime.now()
  l = len(arr)
  a = []
  for i in arr:
    a.insert(0, i)
  end = datetime.now()
  total = end-start
  print("reverse", total)
  return a

def sort(arr):
  start = datetime.now()
  for i in range (len(arr) - 1):
    if arr[0] > arr[0+1] == True:
      arr.append(i)
      arr.remove(i)
    else:
      arr.append(i+1)
      arr.remove(i+1)

  if arr[0] > arr[1] == True:
    arr.append(arr[0])
    arr.remove(arr[0])

  end = datetime.now()
  total = end-start
  print("sort", total)
  return arr


# Program function that two list and retur true if they have
# one common member

def com_data(list1 , list2):
    result = False

    #traversing in first list
    for x in list1:
        #traversing in list2
        for y in list2:

            #if one common
            if x == y:
                result = True
                return result
        return result

#take list as user input
a = [x for x in input("Enter list 1:").split()]
b = [x for x in input("Enter list 2:").split()]

print(com_data(a,b))


import json
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.cm as cm

#print("\nCréation d'un fichier json qui contient les parcelles levées en se basant sur les observations.")
file_config = open("config.json", "r")
config = json.load(file_config)
file_config.close()
path = config["pathToDataDirectory"]

levees = {}

file1 = pd.read_csv(path+"14072016_observations.csv")
file2 = pd.read_csv(path+"28092016_observations.csv")
file3 = pd.read_csv(path+"03112016_observations.csv")
file4 = pd.read_csv(path+"21112016_observations.csv")
file5 = pd.read_csv(path+"02122016_observations.csv")

for id, row in file1.iterrows():
    if("croissance" in row["Culture"] and row["CODE_2016_"] not in levees):
        levees[row["CODE_2016_"]]= "20160806"
for id, row in file2.iterrows():
    if("croissance" in row["Culture"] and row["CODE_2016_"] not in levees):
        levees[row["CODE_2016_"]]="20161003"
for id, row in file3.iterrows():
    if("croissance" in row["Culture"] and row["CODE_2016_"] not in levees):
        levees[row["CODE_2016_"]]="20161102"
for id, row in file4.iterrows():
    if("croissance" in row["Culture"] and row["CODE_2016_"] not in levees):
        levees[row["CODE_2016_"]]="20161122"
for id, row in file5.iterrows():
    if("croissance" in row["Culture"] and row["CODE_2016_"] not in levees):
        levees[row["CODE_2016_"]]="20161202"

file = open(path+"parcelles_levees.json","w")
json.dump(levees, file, indent=1)
file.close()


from sklearn.naive_bayes import GaussianNB
import preparedata.load_data as ld
import csv
import numpy as np

def PredictBySkBayes():
    original_x,original_y = ld.GetBayesTrainset(10000,10000)
    x = np.array(original_x)
    y = np.array(original_y)
    clf = GaussianNB().fit(x,y)
    needtest_key,needtest_data = ld.GetBayesNeedtest()
    f = open("result/sk_bayes_result.csv", "wb")
    write = csv.writer(f)
    write.writerow(["user_id", "item_id"])
    for i in range(len(needtest_key)):
        judge = clf.predict(needtest_data[i])
        if judge[0] == 1:
            write.writerow([needtest_key[i][0],needtest_key[i][1]])
    f.close()


#!/usr/bin/env python
########################################################################
# Given a stream of elements too large to store in memory, pick a random
# element from the stream with uniform probability.
########################################################################

import random

def rand_from_stream(stream):
    num, val = 0, 0
    for n in stream:
        rand = random.random()
        if rand > val:
            num = n
            val = rand
    print(num)

def reservoir_sample(population, sample_size):
    sample = []
    for i, n in enumerate(population):
        if i < sample_size:
            sample.append(n)
        else:
            j = random.randint(0, i + 1)
            if j < sample_size:
                sample[j] = n
    return sample

if __name__ == "__main__":
    rand_from_stream([2, 4, 5, 6, 7, 3, 5])
    print(reservoir_sample([2, 4, 5, 6, 7, 3, 5], 2))


def parse_files(self, files=None, sections=None):
        """Parse configfiles.
        files <list str>, <str> or None:
            What files to parse. None means use self.configfiles. New
            values override old ones. A string value will be interpreted
            as a list of one item.
        sections <list str>, <str> or None:
            Which sections to parse from the files. None means use
            self.sections. A string value will be interpreted as a list
            of one item. The [DEFAULT]
            section is always read, and it is read as if its contents were
            copied to the beginning of all other sections.

        """
        files = _list(files, self.configfiles)
        sections = _list(sections, self.sections)
        for file in files:
            parser = StrictConfigParser()
            parser.read(file)
            for section in sections:
                if not parser.has_section(section):
                    continue
                for unused in parser.unusedoptions(section):
                    if unused not in self.options and unused not in self.ignore:
                        templ = "The option %r in section [%s] of file %s does not exist."
                        raise InvalidOption(unused, message=templ % (unused, section, file))
                for name in parser.options(section):
                    if name in self.options:
                        if self.options[name].reserved:
                            templ = "The option %s in section [%s] of file %s is reserved for command line use."
                            raise ReservedOptionError(message=templ % (unused, section, file))
                        value = parser.get(section, name)
                        self.options[name].parsestr(value, name, '%s [%s]' % (file, section))


# -*- coding: utf-8 -*-

# -*- coding: utf-8 -*-

import matplotlib.pyplot as plt # For plotting.
import numpy as np # To create matrices.
def f(x):
    """ReLU returns 1 if x>0, else 0."""
    return np.maximum(0,x)

print ("f(1): ",f(1))
print ("f(3): ",f(3))
print ("f(-1): ",f(-1))
print ("f(-3): ",f(-3))

print ("Relu Function:  ")
X = np.arange(-4,5,1)
print (X)

Y = f(X)
# All negative values are 0.
print (Y)

plt.plot(X,Y,'o-')
plt.ylim(-1,5)
plt.grid()

plt.xlabel('$x$', fontsize=22)
plt.ylabel('$f(x)$', fontsize=22)
plt.figure(figsize=(7,7))

X_neg = np.arange(-4,1,1) # Negative numbers.
plt.plot(X_neg,f(X_neg),'.-', label='$f\'(x) =0$'); # Plot negative x, f(x)
X_pos = np.arange(0,5,1) # Positive numbers
plt.plot(X_pos, f(X_pos), '.-g',label='$f\'(x)=1$') # Plot positive x, f(x)
plt.plot(0,f(0),'or',label='$f \'(x)=$undefined but set to 0') # At 0.

plt.ylim(-1,5)
plt.grid()
plt.xlabel('$x$', fontsize=22)
plt.ylabel('$f(x)$', fontsize=22) # Make plot look nice.
plt.legend(loc='best', fontsize=16)


# -*- coding: utf-8 -*-

from convenient import start_async
import six
import multiprocessing
import numpy as np

import alexnet
import datasets
import loops
import model_io
import normalizers
import settings

# logging
from logging import getLogger, NullHandler
logger = getLogger(__name__)
logger.addHandler(NullHandler())

GPU = settings.GPU
STAGE = None
JOINT_IDX = None

# Shared variables
new_params = None
mat_que = None
base_joint_que = None
face_normalizer = None
out_que = None

# Load model func
def load_model_func():
    return model_io.load_best_model(STAGE - 1, JOINT_IDX, gpu=GPU >= 0,
                                    train=False)

# First pose convert function
def first_conv_func(img, joint, param, tag):
    facial_rect = param
    width, height = alexnet.IMG_WIDTH, alexnet.IMG_HEIGHT
    mat = face_normalizer.calc_matrix(width, height, facial_rect)
    # Send matrix
    mat_que.put(mat)
    # Apply
    img, dummy_joint = normalizers.transform_pose(img, joint, mat,
                                                  width, height)
    return img, dummy_joint

# First pose register function
def first_register_loop():
    while True:
        data = out_que.get()
        if data is None:
            break
        pred_joints = data
        # First pose register
        for joint in pred_joints:
            mat = mat_que.get()
            inv_mat = np.linalg.inv(mat)
            joint = normalizers.transform_joint(joint, inv_mat)
            # Register
            new_params.append(joint)

# Subsequent pose convert function
def subseq_conv_func(img, joint, param, tag):
    pred_joint = param
    width, height = alexnet.IMG_WIDTH, alexnet.IMG_HEIGHT
    # Crop pose at current joint index
    center = pred_joint[JOINT_IDX]
    mat = normalizers.calc_cropping_matrix(width, height, center,
                                           pred_joint,
                                           sigma=settings.BBOX_SIGMA)
    # Send matrix
    mat_que.put(mat)
    # Apply
    new_img = normalizers.transform_img(img, mat, width, height)
    # Send base joint
    base_joint_que.put(pred_joint)
    # Send the difference as teacher (dummy)
    dummy_diff = np.asarray([0, 0])
    return new_img, dummy_diff

# Subsequent pose convert function
def subseq_register_loop():
    while True:
        data = out_que.get()
        if data is None:
            break
        diff_pts = data
        # Subsequent pose register function
        for diff_pt in diff_pts:
            assert(len(diff_pt) == 1)
            diff_pt = diff_pt[0]
            mat = mat_que.get()
            inv_mat = np.linalg.inv(mat)
            diff_pt = normalizers.transform_joint_pt(diff_pt, inv_mat)
            # Receive base joint
            base_joint = base_joint_que.get()
            # Merge
            base_joint[JOINT_IDX] += diff_pt
            # Register
            new_params.append(base_joint)

def precompute_params(xp, stage, joint_idx, prev_loader, normalizer=None,
                      detector=None):
    # Set variables to global
    global STAGE, JOINT_IDX
    global new_params, mat_que, base_joint_que, face_normalizer, out_que
    STAGE = stage
    JOINT_IDX = joint_idx
    face_normalizer = normalizer

    # First stage
    if stage == 0:
        new_params = list()
        valid_idx = list()
        logger.info('Precompute facial rects')
        failed_cnt = 0
        for i in six.moves.xrange(prev_loader.get_size()):
            # show progress
            if i % 10 == 0:
                logger.info('  %d / %d (failed: %d)',
                            i, prev_loader.get_size(), failed_cnt)
            # load image
            img, joint = prev_loader.get_data(i)
            # get param
            facial_rect = detector.detect_joint_valid_face(img, joint)
            if facial_rect is None:
                failed_cnt += 1
                continue
            # append
            new_params.append(facial_rect)
            valid_idx.append(i)

        # Create new loader
        loader = datasets.PoseDataLoader()
        loader.set_from_raw(np.asarray(prev_loader.img_paths)[valid_idx],
                            np.asarray(prev_loader.joints)[valid_idx],
                            new_params)
        return loader

    # Subsequent stage
    else:
        logger.info('Precompute predicted joints')
        new_params = list()

        # Train data load event and queue
        data_que = multiprocessing.Queue(settings.N_BUFFERING_BATCH)
        load_evt = multiprocessing.Event()
        # Predicted joint queue
        out_que = multiprocessing.Queue()
        # Matrix queue
        mat_que = multiprocessing.Queue()
        base_joint_que = multiprocessing.Queue()  # for stage >= 2

        # Stage selection
        if stage - 1 == 0:
            conv_func = first_conv_func
            register_loop = first_register_loop
            joint_scale_mode = '+'
        else:
            conv_func = subseq_conv_func
            register_loop = subseq_register_loop
            joint_scale_mode = '+-'

        # Data loader thread
        start_async(loops.load_pose_loop, 'predict', data_que, load_evt,
                      prev_loader, settings.BATCH_SIZE, conv_func, False)
        # Prediction loop process
        start_async(loops.predict_pose_loop, xp, data_que, load_evt,
                      out_que, load_model_func, joint_scale_mode)

        # Receive predicted joints and register params
        register_loop()

        # Register predicted joints and matrix as parameters
        loader = datasets.PoseDataLoader()
        loader.set_from_raw(prev_loader.img_paths, prev_loader.joints,
                            new_params)
        return loader


import os, sys
fpath = os.path.join(os.path.dirname(__file__), '../')
sys.path.append(fpath)
from src.WellsFargoReader import WellsFargoReader as wfr
from src.DiscoverReader import DiscoverReader as dcr
from src.TransactionReaderFactory import TransactionReaderFactory
import unittest

class TransactionReaderTests(unittest.TestCase):

    def test_initialize(self):
        result = TransactionReaderFactory.get_reader("fjskdWF.csv")
        self.assertIsInstance(result, wfr)
        self.assertEqual("Credit", result.get_card())

    def test_initialize_discover(self):
        result = TransactionReaderFactory.get_reader("DCfjkdls.fsd")
        self.assertIsInstance(result, dcr)
        self.assertEqual("Credit", result.get_card())

if __name__ == "__main__":
    unittest.main()


# -*- coding: utf-8 -*-
from warnings import warn

import numpy as np

from ..misc import NeuroKitWarning
from ..signal import signal_detrend, signal_interpolate
from .intervals_utils import (
    _intervals_sanitize,
    _intervals_time_to_sampling_rate,
    _intervals_time_uniform,
)

def intervals_process(
    intervals,
    intervals_time=None,
    interpolate=False,
    interpolation_rate=100,
    detrend=None,
    **kwargs
):
    """**Interval preprocessing**

    R-peak intervals preprocessing.

    Parameters
    ----------
    intervals : list or array
        List or numpy array of intervals, in milliseconds.
    intervals_time : list or array, optional
        List or numpy array of timestamps corresponding to intervals, in seconds.
    interpolate : bool, optional
        Whether to interpolate the interval signal. The default is False.
    interpolation_rate : int, optional
        Sampling rate (Hz) of the interpolated interbeat intervals. Should be at least twice as
        high as the highest frequency in vhf. By default 100. To replicate Kubios defaults, set
        to 4.
    detrend : str
        Can be one of ``"polynomial"`` (traditional detrending of a given order) or
        ``"tarvainen2002"`` to use the smoothness priors approach described by Tarvainen (2002)
        (mostly used in HRV analyses as a lowpass filter to remove complex trends), ``"loess"`` for
        LOESS smoothing trend removal or ``"locreg"`` for local linear regression (the *'runline'*
        algorithm from chronux). By default None such that there is no detrending.
    **kwargs
        Keyword arguments to be passed to :func:`.signal_interpolate`.

    Returns
    -------
    np.ndarray
        Preprocessed intervals, in milliseconds.
    np.ndarray
        Preprocessed timestamps corresponding to intervals, in seconds.
    int
        Sampling rate (Hz) of the interpolated interbeat intervals.

    Examples
    --------
    **Example 1**: With interpolation and detrending
    .. ipython:: python

      import neurokit2 as nk
      import matplotlib.pyplot as plt

      plt.rc('font', size=8)

      # Download data
      data = nk.data("bio_resting_5min_100hz")

      # Clean signal and find peaks
      ecg_cleaned = nk.ecg_clean(data["ECG"], sampling_rate=100)
      peaks, info = nk.ecg_peaks(ecg_cleaned, sampling_rate=100, correct_artifacts=True)

      # Convert peaks to intervals
      rri = np.diff(peaks) / sampling_rate * 1000
      rri_time = np.array(peaks[1:]) / sampling_rate

      # Compute HRV indices
      @savefig p_intervals_process1.png scale=100%
      plt.figure()
      plt.plot(intervals_time, intervals, label="Original intervals")
      intervals, intervals_time = intervals_process(rri,
                                                    intervals_time=rri_time,
                                                    interpolate=True,
                                                    interpolation_rate=100,
                                                    detrend="tarvainen2002")
      plt.plot(intervals_time, intervals, label="Processed intervals")
      plt.xlabel("Time (seconds)")
      plt.ylabel("Interbeat intervals (milliseconds)")
      @suppress
      plt.close()

    """
    # Sanitize input
    intervals, intervals_time, _ = _intervals_sanitize(intervals, intervals_time=intervals_time)

    if interpolate is False:
        interpolation_rate = None

    if interpolation_rate is not None:

        # Rate should be at least 1 Hz (due to Nyquist & frequencies we are interested in)
        # We considered an interpolation rate 4 Hz by default to match Kubios
        # but in case of some applications with high heart rates we decided to make it 100 Hz
        # See https://github.com/neuropsychology/NeuroKit/pull/680 for more information
        # and if you have any thoughts to contribute, please let us know!
        if interpolation_rate < 1:
            warn(
                "The interpolation rate of the R-R intervals is too low for "
                " computing the frequency-domain features."
                " Consider increasing the interpolation rate to at least 1 Hz.",
                category=NeuroKitWarning,
            )
        # Compute x-values of interpolated interval signal at requested sampling rate.
        x_new = np.arange(
            start=intervals_time[0],
            stop=intervals_time[-1] + 1 / interpolation_rate,
            step=1 / interpolation_rate,
        )

        intervals = signal_interpolate(intervals_time, intervals, x_new=x_new, **kwargs)
        intervals_time = x_new
    else:
        # check if intervals appear to be already interpolated
        if _intervals_time_uniform(intervals_time):
            # get sampling rate used for interpolation
            interpolation_rate = _intervals_time_to_sampling_rate(intervals_time)

    if detrend is not None:
        intervals = signal_detrend(intervals, method=detrend, sampling_rate=interpolation_rate)
    return intervals, intervals_time, interpolation_rate