from PyQt5 import QtGui
from PyQt5 import QtWidgets
from PyQt5.QtCore import QSize

from rdplot.Widgets import MainWindow

import pkg_resources
import sys

def main():

    app = QtWidgets.QApplication(sys.argv)

    # set icon of application
    app_icon = QtGui.QIcon()
    app_icon.addFile(pkg_resources.resource_filename(__name__,'logo/PLOT1024.png'), QSize(1024, 1024))
    app_icon.addFile(pkg_resources.resource_filename(__name__,'logo/PLOT512.png'), QSize(512, 512))
    app_icon.addFile(pkg_resources.resource_filename(__name__,'logo/PLOT256.png'), QSize(256, 256))
    app_icon.addFile(pkg_resources.resource_filename(__name__,'logo/PLOT128.png'), QSize(128, 128))
    app_icon.addFile(pkg_resources.resource_filename(__name__,'logo/PLOT64.png'), QSize(64, 64))
    app_icon.addFile(pkg_resources.resource_filename(__name__,'logo/PLOT32.png'), QSize(32, 32))
    app_icon.addFile(pkg_resources.resource_filename(__name__,'logo/PLOT16.png'), QSize(16, 16))
    app.setWindowIcon(app_icon)

    main_window = MainWindow.MainWindow()
    main_window.show()

    args = app.arguments()
    main_window.process_cmd_line_args(args)

    sys.exit(app.exec_())

if __name__ == '__main__':
    main()


"""
URL: https://leetcode.com/explore/interview/card/top-interview-questions-medium/103/array-and-strings/776/

Problem Statement:
------------------
Given an integer array nums, return all the triplets [nums[i], nums[j], nums[k]] such that i != j, i != k, and j != k, and nums[i] + nums[j] + nums[k] == 0.

Notice that the solution set must not contain duplicate triplets.

Example 1:
Input: nums = [-1,0,1,2,-1,-4]
Output: [[-1,-1,2],[-1,0,1]]

Example 2:
Input: nums = []
Output: []

Example 3:
Input: nums = [0]
Output: []

Constraints:
0 <= nums.length <= 3000
-10^5 <= nums[i] <= 10^5
"""
from typing import List

class Solution:

    def threeSum(self, nums: List[int]) -> List[List[int]]:
        nums.sort()
        ll = len(nums)
        res = []
        for i in range(ll):
            if i > 0 and nums[i] == nums[i - 1]:
                continue
            target = nums[i] * -1
            s, e = i+1, ll - 1
            while s < e:
                if nums[s] + nums[e] == target:
                    res.append([nums[i], nums[s], nums[e]])
                    s += 1
                    while s < e and nums[s] == nums[s-1]:
                        s += 1
                elif nums[s] + nums[e] < target:
                    s += 1
                else:
                    e -= 1
        return res

"""
Testing:
--------
[-2, -1, 1, 2]
-2, -1, 1
-2, -1, 2
-1, 1, 2
"""
input_list = [
    [[-1, 0, 1, 2, -1, -4]],
    [[]],
    [[0]],
    [[1, 2, 3, -2, -1]],
    [[0, 0, 0, 0]],
    [[1, 2, -2, -1]],
    [[0, 0, 0]],
    [[-2, 0, 1, 1, 2]],
]
output_list = [
    [[-1, -1, 2], [-1, 0, 1]],
    [],
    [],
    [[-2, -1, 3]],
    [[0, 0, 0]],
    [],
    [[0, 0, 0]],
    [[-2, 0, 2], [-2, 1, 1]],
]

for i in range(len(input_list)):
    result = Solution().threeSum(*input_list[i])
    print(result, output_list[i])
    assert result == output_list[i]


import cv2,numpy,os
# i have used numpy for computation of matrix from one point of our face
haar_file='Lec-7\haarcascade_frontalface_default.xml'
face_cascade=cv2.CascadeClassifier(haar_file)

datasets='Lec-7\dataset'
id=0
(images,labels,names)=([],[],{})

for(subdirs,dirs,files) in os.walk(datasets):
    # Iterate through dataset folder
    # move through different subdirectory
    for subdirs in dirs:

        names[id]=subdirs
        # assign name to directory according id
        subjectpath=os.path.join(datasets,subdirs)
        for filename in os.listdir(subjectpath):
            path=subjectpath+'/'+filename
            label=id
            # Loads the images into the images array
            images.append(cv2.imread(path,0))
            # Loads the labels into the label arrray
            labels.append(int(label))
            # print(labels)
        id+=1
(images,labels)=[numpy.array(lis) for lis in [images,labels]]
# print(images,labels)
# take images and label after place it into numpy array
(width,height)=(130,100)

model=cv2.face.LBPHFaceRecognizer_create()
# Face classifier
# model=cv2.face.FisherFaceRecognizer_create()
# 2nd classifier

model.train(images,labels)
# It will do training and processing of images and labels

webCam=cv2.VideoCapture(0)
cnt=0

#Face Detection Part
while True:
    _,img=webCam.read()
    # Converting Image to GrayImage
    grayImg=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    faces=face_cascade.detectMultiScale(grayImg,1.3,4)
    for (x,y,w,h) in faces:
        # Create Rectangle
        cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2)

        face=grayImg[y:y+h,x:x+w]
        # Make rectangle around face
        face_resize=cv2.resize(face,(width,height))

    ## Recognizer Part

        prediction=model.predict(face_resize)
        # Predict face

        cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0))

        #
        if prediction[1]<800:
        #passing the id=prediction[0] to array of name then it shows name
            cv2.putText(img,'%s - %.0f' % (names[prediction[0]],prediction[1]),(x-10,y-10),cv2.FONT_HERSHEY_SIMPLEX,2,(0,0,255))
            print(names[prediction[0]])
            cnt=0
        else:
            # Checking for 100 times if person is identified or not if not then it will go into cnt>100 condition and print unknown person
            cnt+=1
            cv2.putText(img,'Unknown',(x-10,y-10),cv2.FONT_HERSHEY_PLAIN,1,(0,0,255))
            if(cnt>100):
                print("Unknown Person")
                cv2.imwrite('input.jpg',img)
                # save unknown person image
                cnt=0
    cv2.imshow('OpenCV',img)
    key=cv2.waitKey(10)
    if key==27:
        break
webCam.release()


import pyautogui
import time
from script_random import random_around, random_point_near_center_of_rect, rsleep

def slow_click(x, y, button="left"):
    """
    Various entities won't regularly register clicks when the mouse teleports and clicks on them.
    Something about separating these two actions causes the clicks to always register.
    """
    pyautogui.moveTo(x, y)
    click(x, y, button=button, time_to_move=(0, 0))

def click(x, y, button="left", time_to_move=(0.15, 0.05)):
    """
    Params:
        x, y: The coordinates to click at.
        button: Which button, left or right, to use to click.
        time_to_move: A tuple of duration and random factor for how long to take to move to x, y.
    """
    if button == "left":
        pyautogui.click(
            x,
            y,
            # Time to move to x, y
            duration=random_around(time_to_move[0], factor=time_to_move[1]),
            button=button,
        )
    elif button == "right":
        # No idea why normal right click isn't working
        # At least on Runelite
        pyautogui.mouseDown(
            x,
            y,
            # Time to move to x, y
            duration=random_around(time_to_move[0], factor=time_to_move[1]),
            button=button,
        )
        pyautogui.mouseUp(button="right")

def press_key(key, hold=0, factor=0.1):
    hold = random_around(hold, factor)
    start = time.time()
    pyautogui.press(key)
    while time.time() - start < hold:
        pyautogui.press(key)

def click_in_rect(top_left, bottom_right, clicks=1, wait_between=0, absolute=False, delay=0):
    """Click in the rectangle, adjusted for screen size difference.

    Params:
        absolute: Whether the coordinates are relative to a screenshot MONITOR or absolute to the screen size.
    """
    if delay > 0:
        rsleep(delay)

    x, y = random_point_near_center_of_rect(top_left, bottom_right, absolute=absolute)

    for num in range(clicks):
        if num != 0 and wait_between > 0:
            rsleep(wait_between, factor=0.1)
        click(x, y)


'''
Created on Aug 26, 2016

@author: Beast
'''
import nltk

def main():
    print("you are in main")
    a = "I'm bharat. starting to study python and nltk. I will use it in Natural Language Processing"
#     tokens = nltk.word_tokenize(a)
    fd = nltk.FreqDist(a)
    for x in fd:
        print(x + "\t" + str(fd[x]))

if __name__ == '__main__':
    print("You are in text.py")
    main()


import copy

def get_move_alpha_cnt(start, end):
    diff = ord(end) - ord(start)
    if diff > 13:
        diff = 26 - diff
    return diff

def check_move(check):
    for c in check:
        if c == False:
            return False
    return True

def min_cursor(name):
    cnt_left_a = 0
    for i in range(1, len(name)):
        if name[i] == 'A':
            cnt_left_a += 1
        else:
            break

    cnt_right_a = 0
    for i in range(len(name) - 1, -1, -1):
        if name[i] == 'A':
            cnt_right_a += 1
        else:
            break
    right_cursor_straight = len(name) - 1 - cnt_right_a
    left_cursor_straight = len(name) - 1 - cnt_left_a

    #A A A J A A A A A A N
    check = []
    for n in name:
        if n == 'A':
            check.append(True)
        else:
            check.append(False)

    # right_turn
    right_turn = 1e8
    for turn in range(1, len(name) - 1, 1):
        check_copy = copy.deepcopy(check)
        # print("turn", turn)
        right_cnt = -1
        idx = 0
        idx_add = 1
        while not check_move(check_copy):
            # print("right turn", name[idx], "turn", turn, "idx", idx, check_copy, check)
            if name[idx] != 'A':
                check_copy[idx] = True

            right_cnt += 1
            if turn == idx:
                idx_add = -1
            idx += idx_add
        right_turn = min(right_turn, right_cnt)

    # left turn
    left_turn = 1e8
    for turn in range(-1, -(len(name)), -1):
        check_copy = copy.deepcopy(check)
        # print("left turn", turn)
        left_cnt = -1
        idx = 0
        idx_add = -1
        while not check_move(check_copy):
            # print("left turn", name[idx], "turn", turn, "idx", idx, check_copy, check)
            if name[idx] != 'A':
                check_copy[idx] = True

            left_cnt += 1
            if turn == idx:
                idx_add = 1
            idx += idx_add
        left_turn = min(left_turn, left_cnt)
    # print("===============cursor", right_cursor_straight, left_cursor_straight, right_turn, left_turn)
    return min(right_cursor_straight, left_cursor_straight, right_turn, left_turn)

def solution(name):
    name_start = 'A' * len(name)
    answer = 0

    for start, end in zip(name_start, name):
        answer +=get_move_alpha_cnt(start, end)

    answer += min_cursor(name)

    return answer

#1 상하 이동, 알파벳이 아래로가는게 가까운지, 위로 가는게 가까운지 확인 후 이동시켜서 만듬.
    # a b  c  d  e  f  g  h  i  j  k  l  m  n  o  p  q  r  s  t  u  v  w  x  y  z
    # 0 1  2  3  4  5  6  7  8  9  10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
    # 0 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9  8  7  6  5  4  3  2  1

    #2 좌우 이동, A가 있다면 어떻게 가는게 최소일지. 들어갔다 나오면 어차피 최소가 아님. 왼쪽시작이냐 오른쪽시작이냐일듯.
    # AAAAAA 0
    # BAAAB  left 1
    # BBBACB 그냥 right 5
    # BACAB left 3 끝.
    # BACCCCCCCCCB
    # BAAACBCBSABB
    # idx 1 부터 연속 A의 개수 구해서 왼쪽개수에서 뺌
    # 잘못생각함. 가다가 반대로 꺾는 경우가 생김.
    # A A A J A A A A A A N

name = "JEROEN"
print(solution(name))

name = "JAAAN"
print(solution(name))

name = "AABAAAAAB"
print(solution(name))

name = "BAABA"
print(solution(name))
#4

name = "ABABA"
print(solution(name))
#5

name = "BABAB"
print(solution(name))
#6

name = "BBABAAAB"
print(solution(name))
#9

name = "AAZAAAZ"
print(solution(name))
#6


"""Define functions used to perform an analysis."""
import os
import pathlib
import random
import string
import typing
import warnings
import zipfile

import geopandas as gpd
import numpy as np
import shapely
from loguru import logger
from osmnx import (
    geocoder,
    settings,
)
from slugify import slugify

from brokenspoke_analyzer.core import (
    runner,
    utils,
)
from brokenspoke_analyzer.pyrosm import data

warnings.filterwarnings("ignore")

def osmnx_query(country: str, city: str, state: str | None) -> typing.Tuple[str, str]:
    """
    Prepare the osmnx.

    Returns: the OSMNX query and its slugified version.
    """
    query = ", ".join(filter(None, [city, state, country]))
    slug = slugify(query)
    return (query, slug)

def prepare_city_file(
    output_dir: pathlib.Path,
    region_file_path: pathlib.Path,
    boundary_file_path: pathlib.Path,
    pfb_osm_file: pathlib.Path,
) -> None:
    """
    Prepare the city OSM file.

    Use osmium to extract the content limited by the polygon file from the region file.
    """
    pfb_osm_file_path = output_dir / pfb_osm_file
    if not pfb_osm_file_path.exists():
        runner.run_osmium_extract(
            boundary_file_path.resolve(),
            region_file_path.resolve(),
            pfb_osm_file_path.resolve(),
        )

def state_info(state: str) -> tuple[str, str]:
    """
    Given a state, returns the corresponding abbreviation and FIPS code.

    The District of Columbia is also recognized as a state.

    Examples:

    >>> assert ("TX", "48") = state_info("texas")
    >>> assert ("DC", "11") = state_info("district of columbia")
    """
    # Ensure DC is considered a US state.
    # https://github.com/unitedstates/python-us/issues/67
    os.environ["DC_STATEHOOD"] = "1"
    from us import states

    # Lookup for the state name.
    if not state:
        raise ValueError("the state to lookup was not specified")
    state_map = states.mapping("name", "abbr")
    abbrev = state_map.get(state.title().replace(" Of ", " of "))
    if not abbrev:
        raise ValueError(f"cannot find state: {state}")

    # Lookup for the state info.
    st = states.lookup(abbrev)
    if not st:
        raise ValueError(f"cannot find state info for: {state}, {abbrev}")
    fips = st.fips

    return (abbrev, fips)

def derive_state_info(state: str | None) -> typing.Tuple[str, str, bool]:
    """
    Derive state information.

    Returns the state abbreviation, the state fips, and whether the job
    information can be retrieved from the census.

    Examples:
        >>> assert ("TX", "48", True) == derive_state_info("texas")
        >>> assert ("ZZ", "0", False) == derive_state_info("spain")
    """
    try:
        if not state:
            raise ValueError("no 'state' was provided")
        run_import_jobs = True
        state_abbrev, state_fips = state_info(state)
    except ValueError:
        run_import_jobs = False
        state_abbrev, state_fips = (
            runner.NON_US_STATE_ABBREV,
            runner.NON_US_STATE_FIPS,
        )
    return (state_abbrev, state_fips, run_import_jobs)

def retrieve_city_boundaries(
    output: pathlib.Path, country: str, city: str, state: typing.Optional[str] = None
) -> str:
    """
    Retrieve the city boundaries and save them as Shapefile and GeoJSON.

    :return: the slugified query used to retrieve the city boundaries.
    """
    # Prepare the query.
    query, slug = osmnx_query(country, city, state)
    logger.debug(f"Query used to retrieve the boundaries: {query}")

    # Retrieve the geodataframe.
    settings.use_cache = os.getenv("BNA_OSMNX_CACHE", "1") == "1"
    city_gdf = geocoder.geocode_to_gdf(query)
    # Remove the display_name series to ensure there are no international
    # characters in the dataframe. The import will fail if the analyzer finds
    # non US characters.
    # https://github.com/PeopleForBikes/brokenspoke-analyzer/issues/24
    city_gdf.drop("display_name", axis=1)

    # Export the boundaries.
    city_gdf.to_file(output / f"{slug}.shp", encoding="utf-8")
    city_gdf.to_file(output / f"{slug}.geojson")

    return slug

# pylint: disable=too-many-locals
def create_synthetic_population(
    area: gpd.GeoDataFrame,
    length: int,
    width: int,
    population: typing.Optional[int] = 100,
) -> gpd.GeoDataFrame:
    """
    Create a grid representing the synthetic population.

    :param GeoDataFrame area: area to grid
    :param int length: length of a cell of the grid in meters
    :param int width: width of a cell of the grid in meters
    :param int population: population to inject in each cell
    :returns: a GeoDataFrame representing the synthetic population.
    :rtype: GeoDataFrame
    """
    # Project the area into mercator.
    mercator_area = area.to_crs(utils.PSEUDO_MERCATOR_CRS)

    # Prepare the rows and columns.
    xmin, ymin, xmax, ymax = mercator_area.total_bounds
    cols = list(np.arange(xmin, xmax + width, width))
    rows = list(np.arange(ymin, ymax + length, length))

    # Extract all the boundaries.
    boundaries = mercator_area.geometry.explode(index_parts=True)

    # Compute the cells.
    cells = []
    for col in cols[:-1]:
        for row in rows[:-1]:
            # Create a new grid cell.
            cell = shapely.geometry.Polygon(
                [
                    (col, row),
                    (col + width, row),
                    (col + width, row + length),
                    (col, row + length),
                ]
            )

            # Append it if it intersects with the biggest region.
            if any(map(cell.intersects, boundaries)):
                cells.append(cell)

    # Create a geodataframe made of the cells overlapping with the area.
    # Add new columns to simulate US census data.
    BLOCKID_LEN = 15
    grid = gpd.GeoDataFrame(
        {
            "geometry": cells,
            "POP10": population,
            "BLOCKID10": [
                "".join(
                    random.choice(string.ascii_lowercase) for x in range(BLOCKID_LEN)
                )
                for _ in range(len(cells))
            ],
        },
        crs=utils.PSEUDO_MERCATOR_CRS,
    )

    return grid.to_crs(area.crs)

def change_speed_limit(
    output: pathlib.Path, city: str, state_abbrev: str, speed: int
) -> None:
    """Change the speed limit."""
    speedlimit_csv = output / "city_fips_speed.csv"
    speedlimit_csv.write_text(
        f"city,state,fips_code_city,speed\n{city},{state_abbrev.lower()},{0:07},{speed}\n"
    )

def simulate_census_blocks(
    output: pathlib.Path, synthetic_population: gpd.GeoDataFrame
) -> None:
    """Simulate census blocks."""
    tabblock = "population"
    synthetic_population_shp = output / f"{tabblock}.shp"
    synthetic_population.to_file(synthetic_population_shp)
    shapefile_parts = [
        output / f"{tabblock}.{suffix}"
        for suffix in ["cpg", "dbf", "prj", "shp", "shx"]
    ]
    # The shapefile components must be zipped at the root of one zip archive.
    # https://github.com/azavea/pfb-network-connectivity/blob/a9a4bc9546e1c798c6a6e11ee57dcca5db438f3e/src/analysis/import/import_neighborhood.sh#L112-L114
    synthetic_population_zip = output / f"{tabblock}.zip"
    with zipfile.ZipFile(synthetic_population_zip, "w") as z:
        for f in shapefile_parts:
            z.write(f, arcname=f.name)

def retrieve_region_file(region: str, output_dir: pathlib.Path) -> pathlib.Path:
    """Retrieve the region file from Geofabrik or BBike."""
    dataset = utils.normalize_unicode_name(region)
    dataset_file = data.get_data(dataset, directory=output_dir)  # type: ignore
    region_file_path: pathlib.Path = pathlib.Path(dataset_file)
    region_file_path = region_file_path.resolve(strict=True)
    if not region_file_path.exists():
        raise ValueError(f"the path `{region_file_path}` does not exist")
    return region_file_path


#!/usr/bin/env python
import rospy
from line_ros_utility.msg import LineLengthsArray
from line_detection.srv import ExtractLines

from tools.histogram_line_lengths import LineLengthHistogram

class LineLengthHistogramBuilder:
    """ Creates a histogram of the length of the lines extracted in a dataset by
        using the service ExtractLines.

    Args:
        num_frames (int): Number of frames expected in the dataset.
    Attributes:
        histogram_displayer (LineLengthHistogram): Instance of the histogram to
            be used to store the lines.
        num_frames (int): Number of frames expected in the dataset.
        frames_received (int): Number of frames already received.
    """

    def __init__(self, num_frames):
        self.histogram_displayer = LineLengthHistogram(num_frames=num_frames)
        self.num_frames = num_frames
        self.frames_received = 0

    def callback(self, line_lengths_msg):
        # Increment the number of frames received.
        self.frames_received += 1
        # Add frame to the histogram.
        self.histogram_displayer.add_frame_to_histogram(
            list(line_lengths_msg.line_lengths))
        if (self.frames_received == self.num_frames):
            # Display histogram if all frames have been received.
            self.histogram_displayer.display_histogram()

    def start(self):
        # Start ROS node.
        rospy.init_node('histogram_line_lengths_displayer')
        # Subscribe to line lengths topic.
        rospy.Subscriber(
            '/line_lengths', LineLengthsArray, self.callback, queue_size=300)

        rospy.spin()

if __name__ == "__main__":
    histogram_builder = LineLengthHistogramBuilder(num_frames=300)
    histogram_builder.start()


# -*- coding: utf-8 -*-
"""
Created on Fri Jan 25 20:31:47 2019

@author: yufan
"""

# -*- coding: utf-8 -*-
"""
Created on Tue Jan 15 15:47:33 2019

@author: yufan
"""

import heapq
import pandas as pd
import numpy as np
import networkx as nx
import matplotlib
from Utility import MultiCluster, ConsInts
from collections import defaultdict
import time

def addIevent(e, n, s, t):
    """
    Push the infection event into the heap
    """
    heapq.heappush(e, (t, (n, s, 'I')))

def addPevent(e, n, s, t):
    """
    Push the protection event into the heap
    """
    heapq.heappush(e, (t, (n, s, 'P')))

def sStatus(node, st):
    """
    Determine if a node is susceptible
    """
    if node not in st['R'] and node not in st['I'] and node not in st['P']:
        return True
    return False

def simulation(n, t1, t2, n1, n2):
    """
    SIPR process simulation and generate genealogy for the infected nodes
    """
    status = defaultdict(set)
    generation = defaultdict(int)
    infG = nx.DiGraph()
    index = range(n)
    columns = ['Id', 'Parent', 'Generation']
    records = pd.DataFrame(index = index, columns = columns)
    source = 0
    color_map = []
    posxR, posyR, currentL, currentG = 1/n, 3/n, 1, 1
    infG.add_node(source, pos=(posxR*currentL, posyR*currentG))
    cmap = matplotlib.cm.get_cmap('tab10')
    color_map.append(cmap(source/1000))
    status['I'].add(source)
    generation[source] = 1
    cnt = 0
    records.loc[cnt] = pd.Series({'Id': source, 'Parent': np.nan,
                                 'Generation': generation[source]})
    events = []
    heapq.heapify(events)
    for neigh in n1[source]:
        if (neigh, source) in t1:
            addIevent(events, neigh, source, t1[(neigh, source)])
        elif (source, neigh) in t1:
            addIevent(events, neigh, source, t1[(source, neigh)])
    for neigh in n2[source]:
        if (neigh, source) in t2:
            addPevent(events, neigh, source, t2[(neigh, source)])
        elif (source, neigh) in t2:
            addPevent(events, neigh, source, t2[(source, neigh)])
    heapq.heappush(events, (REC, (source, source, 'R')))
    while len(events):
        t, new_event = heapq.heappop(events)
        new_end, old_source, eventType = new_event
        if eventType == 'R':
            status['R'].add(old_source)
        elif eventType == 'P':
            if old_source not in status['R']:
                if sStatus(new_end, status):
                    status['P'].add(new_end)
        elif eventType == 'I':
            if old_source not in status['R']:
                if sStatus(new_end, status):
                       status['I'].add(new_end)
                       cnt += 1
                       generation[new_end] = generation[old_source] + 1
                       records.loc[cnt] = pd.Series({'Id': new_end,
                                        'Parent': old_source,
                                        'Generation': generation[new_end]})
                       for neigh in n1[new_end]:
                           if sStatus(neigh, status):
                               if (neigh, new_end) in t1:
                                   addIevent(events, neigh, new_end,
                                             t + t1[(neigh, new_end)])
                               elif (new_end, neigh) in t1:
                                   addIevent(events, neigh, new_end,
                                             t + t1[(new_end, neigh)])
                       for neigh in n2[new_end]:
                           if sStatus(neigh, status):
                               if (neigh, new_end) in t2:
                                   addPevent(events, neigh, new_end,
                                             t + t2[(neigh, new_end)])
                               elif (new_end, neigh) in t2:
                                   addPevent(events, neigh, new_end,
                                             t + t2[(new_end, neigh)])
                       heapq.heappush(events, (t + REC,
                                               (new_end, new_end, 'R')))

    records = records.sort_values(by = ['Generation'])
    for index, row in records.iterrows():
        if not pd.isna(row['Parent']):
            if row['Generation'] != currentG:
                currentL = 1
                currentG = row['Generation']
            else:
                currentL += 1
            infG.add_node(row['Id'], pos=(posxR*currentL, posyR*currentG))
            infG.add_edge(row['Parent'], row['Id'])
            color_map.append(cmap(row['Id']/1000))
    options = {
        'node_size': 300,
        'width': 1,
        'arrowstyle': '-|>',
        'arrowsize': 10,
    }
    pos=nx.get_node_attributes(infG,'pos')
    nx.draw_networkx(infG, node_color = color_map,
                     pos=pos, arrows=True, **options, font_size = 6)

if __name__ == '__main__':
    start = time.time()
    N = 2
    Sizes = [100]*N
    Types = ['ER']*N
    TParams = [[10, 10, 1]]*N
    nlinks = 4
    TInters1 = ConsInts('line', N, nlinks, 2/N)
    TInters2 = {}
    BETA, GAMMA, REC = 0.8, 0.3, 1.0
    rate1, rate2 = -np.log(1 - BETA)/REC, -np.log(1 - GAMMA)/REC
    t1, t2, n1, n2  = MultiCluster(Sizes, Types, TParams, rate1, rate2,
                                   Depend = False,
                                   TIs1 = TInters1, TIs2 = TInters2)
    simulation(sum(Sizes), t1, t2, n1, n2)
    end = time.time()
    print(end - start)


from tkinter import *

root = Tk()
root.title("Python")
root.iconbitmap("computer.ico")
root.geometry("200x200")

def resize(e):
    x = ((e.width)/0.60*e.height)
    btn1_btn.config(font=("Calibri",int((x/1000)/10)))

btn1_btn = Button(root,text="Button 1")
btn1_btn.grid(row=0,column=0,sticky=NSEW)

Grid.rowconfigure(root,0,weight=1)
Grid.columnconfigure(root,0,weight=1)

# Binding
root.bind("<Configure>",resize)

root.mainloop()


import random

path = ""

def random_word(length):
    input = open(path + 'input' + str(length) + '.txt', 'r')
    lines = input.read().splitlines()

    output = open(path + 'word' + str(length) + '.txt', 'w')
    word = random.choice(lines)
    output.write(word)

    return word

for i in range(5, 8):
    print(random_word(i))


import datetime as dt
import decimal as d
import unittest
from http import HTTPStatus
from unittest import mock

import pytest

@pytest.mark.usefixtures("client")
class TestCreateAccountScenarios(unittest.TestCase):
    mock_models_path = "app.account.views.models"

    @mock.patch(mock_models_path)
    def test_not_call_create_person_when_id_informed(self, mock_models):
        mock_models.Person.add.return_value = "1"
        mock_models.Account.add.return_value = "2"

        resp = self.client.post(
            "/account",
            json={
                "saldo": 3323.44551,
                "flagAtivo": False,
                "limiteSaqueDiario": 444,
                "tipoConta": 33,
                "idPessoa": "665",
            },
        )

        self.assertEqual(resp.status_code, HTTPStatus.CREATED)
        self.assertEqual(resp.headers["Content-Location"], "/account/2")
        mock_models.Person.add.assert_not_called()
        mock_models.Account.add.assert_called_once_with(
            **{
                "person_id": "665",
                "active": False,
                "account_type": 33,
                "daily_withdraw_limit": d.Decimal("444"),
                "balance": d.Decimal("3323.44551"),
            }
        )

    @mock.patch(mock_models_path)
    def test_must_call_create_person_when_id_not_exists(self, mock_models):
        mock_models.Person.add.return_value = "1"
        mock_models.Account.add.return_value = "2"

        resp = self.client.post(
            "/account",
            json={
                "saldo": 3323.44551,
                "flagAtivo": False,
                "limiteSaqueDiario": 444,
                "tipoConta": 33,
                "nome": "juaozin",
                "cpf": "113.874.497-29",
                "dataNascimento": "1900-04-04",
            },
        )

        self.assertEqual(resp.status_code, HTTPStatus.CREATED)
        self.assertEqual(resp.headers["Content-Location"], "/account/2")
        mock_models.Person.add.assert_called_once_with(
            **{
                "birth": dt.datetime(1900, 4, 4).date(),
                "cpf": "113.874.497-29",
                "name": "juaozin",
            }
        )
        mock_models.Account.add.assert_called_once_with(
            **{
                "person_id": "1",
                "active": False,
                "account_type": 33,
                "daily_withdraw_limit": d.Decimal("444"),
                "balance": d.Decimal("3323.44551"),
            }
        )

    @mock.patch(mock_models_path)
    def test_content_type_must_be_json(self, mock_models):
        resp = self.client.post(
            "/account", data=b"dasda", content_type="application/x-www-form-urlencoded"
        )
        self.assertEqual(resp.status_code, HTTPStatus.BAD_REQUEST)
        self.assertEqual(resp.json, {"error": "content-type must be application/json"})
        mock_models.Person.add.assert_not_called()
        mock_models.Account.add.assert_not_called()

    @mock.patch(mock_models_path)
    def test_problem_saving_the_account(self, mock_models):
        mock_models.Account.add.side_effect = Exception(
            "Someone dropped coffee in the machine now everything is broken because it is!"
        )
        resp = self.client.post(
            "/account",
            json={
                "saldo": 3323.44551,
                "flagAtivo": False,
                "limiteSaqueDiario": 444,
                "tipoConta": 33,
                "nome": "juaozin",
                "cpf": "113.874.497-29",
                "dataNascimento": "1900-04-04",
            },
        )
        self.assertEqual(resp.status_code, HTTPStatus.INTERNAL_SERVER_ERROR)
        self.assertEqual(resp.json, {"error": "Problem creating the account"})


"Объект - единица информации в памяти"
"Экземпляр - конкретный объект какого-то класса"
"Класс - инструкция по созданию объектов определенного типа"
"Метод - функция в классе для воздействия на объект"
"Поля и Свойства - переменные в классе"
"Атрибуты - все имена в классе: переменных и методов"

class Purse:
    def __init__(self, valuta, name='Unkown'):
        if valuta not in('USD', 'EUR'):
            raise ValueError
        self.__money = 0.00
        self.valuta = valuta
        self.name = name

    def top_up_balance(self, howmany):
        self.__money = self.__money + howmany
        return howmany

    def top_down_balance(self, howmany):
        if self.__money - howmany < 0:
            print('Недостаточно средств')
            raise ValueError ('Недостаточно средств')
        self.__money = self.__money - howmany
        return howmany

    def info(self):
        print(self.__money)

    def __del__(self):
        print('Кошелек удален')

x = Purse('USD')
y = Purse('USD', 'Bill')
y.top_up_balance(10)
x.top_up_balance(y.top_down_balance(7))
x.info()
y.info()


from nornir import InitNornir
from nornir_scrapli.tasks import send_command
from rich import print as rprint

def update_dictionary(dictionary, key, value):
    if key not in dictionary:
        dictionary[key] = value
    elif type(dictionary[key]) == list:
        dictionary[key].append(value)
    else:
        dictionary[key] = [dictionary[key], value]

nr = InitNornir(config_file="config.yaml")

def show_vlan(task):
    vlan_list = []
    int_dic = {}
    new_dic = {}
    vlan_status = task.run(task=send_command, command="show vlan")
    task.host["vlan"] = vlan_status.scrapli_response.genie_parse_output()
    vlan_nums = task.host["vlan"]["vlans"]
    for vlan in vlan_nums:
        vlan_list.append(vlan)

    interface_status = task.run(task=send_command, command="show interface")
    task.host["int"] = interface_status.scrapli_response.genie_parse_output()
    interfaces = task.host["int"]
    for int in interfaces:
        if str(int).startswith("E") or str(int).startswith("p"):
            admin_state = interfaces[int]["admin_state"]
            oper_state = interfaces[int]["oper_status"]
            int_dic[int] = [admin_state,oper_state]
    for vlan in vlan_list:
        interface_status = task.run(task=send_command, command=f"show mac address vlan {vlan}")
        task.host["mac"] = interface_status.scrapli_response.genie_parse_output()
        vlans_dic = task.host["mac"]
        mac_dic = {}
        if vlans_dic != []:
            mac_vlan = vlans_dic["mac_table"]["vlans"][vlan]["mac_addresses"]
            mac_list = []
            vlan_dic = {}
            for mac in mac_vlan:
                if mac_vlan[mac]["entry"] == "*":
                    mac_list.append(mac)
                    mac_int = mac_vlan[mac]["interfaces"]
                    for macs in mac_int:
                        interf = mac_int[macs]["interface"]
                    vlan_dic[vlan] = mac_list
                    mac_dic[interf] = vlan_dic

        if mac_dic !={}:
            for k,v in mac_dic.items():
                update_dictionary(new_dic,k,v)

    rprint(f"The list of vlans for [red][bold]{task.host}[/bold][/red] is {vlan_list}")
    rprint(f"\n\nThe status of interfaces on [red][bold]{task.host}[/bold][/red] is {int_dic}")
    rprint(f"\n\nLearnt MAC list on [red][bold]{task.host}[/bold][/red] is {new_dic}")

nr.run(task=show_vlan)


# NAME : Foosball Dynasty
# URL  : https://open.kattis.com/problems/foosball
# =============================================================================
# Problem has no performance requirements, so Python is fine. Work
# through the rules, maintaining state properly as we go.
# =============================================================================

import sys

def dbg(str):
    print(str, file=sys.stderr)

def main():
    player_count = int(input())
    players = input().split()
    result_str = input()

    dbg(f"PlayerCount:{player_count}")
    dbg(f"Players:{players}")
    dbg(f"ResultStr:{result_str}")

    sides = [
        [players[0], players[2]],
        [players[1], players[3]]
    ]
    line = players[4:]

    dbg(f"Side[0]:{sides[0]}")
    dbg(f"Side[1]:{sides[1]}")
    dbg(f"Line:{line}")

    win_tracking = [
        [" ".join(sides[0][:]), 0],
        [" ".join(sides[1][:]), 0]
    ]

    best = 0
    dynasties = []

    for c in result_str:
        winner_idx = 0 if c == "W" else 1
        loser_idx = 1 if winner_idx == 0 else 0

        winner = sides[winner_idx]
        winner_rec = win_tracking[winner_idx]
        loser = sides[loser_idx]
        loser_rec = win_tracking[loser_idx]

        winner[0], winner[1] = winner[1], winner[0]
        winner_rec[1] += 1
        dbg(f"Win -- Team:{winner_rec[0]}; Wins:{winner_rec[1]}")

        if winner_rec[1] > best:
            best = winner_rec[1]
            dynasties = [winner_rec[0]]
            dbg(f"New Best -- Team:{winner_rec[0]}, Wins:{winner_rec[1]}")
        elif winner_rec[1] == best:
            dynasties.append(winner_rec[0])
            dbg(f"New Tie -- Team:{winner_rec[0]}, Wins:{winner_rec[1]}")

        line.append(loser[1])
        loser[1] = loser[0]
        loser[0] = line.pop(0)
        win_tracking[loser_idx] = [loser[1] + " " + loser[0], 0]
        dbg(f"New Losing Team: {win_tracking[loser_idx][0]}")

    print("\n".join(dynasties))

if __name__ == "__main__":
    main()


#!/usr/bin/env python3

import uuid
import time
import signal
import random

from kombu import Connection, Producer, Exchange

def sigint_handler(signal, frame):
    print('Exiting...')
    global loop
    loop = False

signal.signal(signal.SIGINT, sigint_handler)

piid = 'pc.' + str(uuid.uuid1().node)

print("Starting with id: " + piid)

exch = Exchange('raspi.live', type='direct')

loop = True

with Connection('amqp://guest:guest@127.0.0.1') as conn:
    channel = conn.channel()
    b_exch = exch(channel)
    producer = Producer(channel, exchange=b_exch, routing_key=piid)

    while loop:
        producer.publish(
            {
                'x': 4*(random.random()-0.5),
                'y': 4*(random.random()-0.5),
                'z': 4*(random.random()-0.5),
            },
            retry=True,
        )
        time.sleep(0.05)


# Setting the search space
space = [Real(0.01, 1.0, 'uniform', name='learning_rate'),
         Integer(1, 8, name='max_depth'),
         Real(0.1, 1.0, 'uniform', name='subsample'),
         Real(0.1, 1.0, 'uniform', name='colsample_bytree'),  # subsample ratio of columns by tree
         Real(0, 100., 'uniform', name='reg_lambda'),      # L2 regularization
         Real(0, 100., 'uniform', name='reg_alpha'),       # L1 regularization
         Real(1, 30, 'uniform', name='min_child_weight'),     # minimum sum of instance weight (hessian)
]

# Setting the search space: note that we didn't put the
#n_estimators in the search space as we will use early stopping!


y = 100
yspeed = 2
x = 100
xspeed = 2

def setup():
    size(800, 600)
    fill('#0099FF')
    textSize(50)

def draw():
    global y, yspeed, x, xspeed
    background('#000000')
    y += yspeed
    x += xspeed
    text('DVD', x, y)

    if y < 0+50 or y > height:
        yspeed *= -1

    if x < 0 or x > width - textWidth('DVD'):
        xspeed *= -1


import sys

def run(line):
    ms = [int(x) for x in line.split()]
    maxdiff = 0
    first = 0
    last = 0
    for m in ms:
        if m < last:
            first += last - m
            if last - m > maxdiff:
                maxdiff = last - m
        last = m

    second = 0
    for m in ms[:-1]:
        second += min(maxdiff, m)
    return '%d %d' % (first,second)

fin = open(sys.argv[1])
T = int(fin.readline().strip())
for i in range(1,T+1):
    line = fin.readline().strip()
    line = fin.readline().strip()
    ans = run(line)
    print('Case #%d: %s' % (i, ans))


import numpy as np
from matplotlib import cm
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from scipy.signal import welch
from itertools import islice
import matplotlib as mpl
from scipy.spatial import distance
from sklearn.linear_model import LinearRegression
from scipy.ndimage import gaussian_filter1d
from src.features.helpers_vis import LinearReg

def process_inputs(psd_all_i, psd_all_c, ear='ipsi', normalization_type='sum_1', sigma_smoothing=0, sigma_gauss_norm=1):
    # filter the data
    psd_mono_c = filter_dataset(psd_all_c, normalization_type=normalization_type,
                                   sigma_smoothing=sigma_smoothing, sigma_gauss_norm=sigma_gauss_norm)
    psd_mono_i = filter_dataset(psd_all_i, normalization_type=normalization_type,
                                   sigma_smoothing=sigma_smoothing, sigma_gauss_norm=sigma_gauss_norm)

    # integrate the signals and filter
    if ear.find('contra') >= 0:
        psd_binaural = filter_dataset(
            psd_mono_c / (psd_mono_i + psd_mono_c), normalization_type=normalization_type, sigma_smoothing=0, sigma_gauss_norm=0)
    else:
        psd_binaural = filter_dataset(
            psd_mono_i / (psd_mono_i + psd_mono_c), normalization_type=normalization_type, sigma_smoothing=0, sigma_gauss_norm=0)

    # calculate different input sounds. should be 4 of them (mono,mono-mean,bin, bin-mean)
    if ear.find('contra') >= 0:
        psd_mono = psd_mono_c
    else:
        psd_mono = psd_mono_i

    psd_mono_mean = psd_mono - \
        np.transpose(np.tile(np.mean(psd_mono, axis=1), [
                     psd_mono.shape[1], 1, 1]), [1, 0, 2])
    psd_binaural = psd_binaural
    psd_binaural_mean = psd_binaural - \
        np.transpose(np.tile(np.mean(psd_binaural, axis=1), [
                     psd_binaural.shape[1], 1, 1]), [1, 0, 2])

    return psd_mono, psd_mono_mean, psd_binaural, psd_binaural_mean

def localize_sound(psd_all, data_to_compare, metric='correlation'):

    x_test = np.zeros((psd_all.shape[0], psd_all.shape[1], 2))
    y_test = np.zeros((psd_all.shape[0], psd_all.shape[1]))

    # walk over all samples
    for i in range(psd_all.shape[0]):
        for ii in range(psd_all.shape[1]):
            data_point = np.squeeze(psd_all[i, ii, :])
            data_point = data_point[np.newaxis]

            # search for smallest distance between white noise data points and the randomly choosen sound sample
            dists = distance.cdist(data_to_compare, data_point, metric=metric)
            minimal_dist_ind = np.argmin(dists)
            y_test[i, ii] = minimal_dist_ind

            # save the data points
            x_test[i, ii, 0] = i
            x_test[i, ii, 1] = ii

    return x_test, y_test

def create_map(psd, mean_subtracted_map=True):

    # set the map
    if mean_subtracted_map:
        learned_map = np.mean(psd, axis=0)
        mean_learned_map = np.mean(learned_map, axis=0)
        learned_map = learned_map - mean_learned_map
    else:
        learned_map = np.mean(psd, axis=0)
        learned_map = learned_map

    return learned_map

def filter_dataset(dataset, normalization_type='sum_1', sigma_smoothing=0, sigma_gauss_norm=0):
    # applies filter and normalization on dataset. normalization_type can be one of the following: 'sum_1','l2','l1'
    ds = np.copy(dataset)
    # first do a smoothing
    if sigma_smoothing > 0:
        ds = gaussian_filter1d(ds, sigma=sigma_smoothing, mode='nearest', axis=2)

    # now normalize the data
    if normalization_type.find('sum_1') >= 0:
        # print('Sum 1 Normalization')
        ds = (ds / np.transpose(np.tile(np.sum(ds, axis=2), [ds.shape[2], 1, 1]), [1, 2, 0]))
    elif normalization_type.find('l1') >= 0:
        # print('L1 Normalization')
        ds = (ds - np.transpose(np.tile(np.min(ds, axis=2), [ds.shape[2], 1, 1]), [1, 2, 0]))
        ds = (ds / np.transpose(np.tile(np.max(ds, axis=2), [ds.shape[2], 1, 1]), [1, 2, 0]))
    elif normalization_type.find('l2') >= 0:
        # print('L2 Normalization')
        ds = (ds / np.transpose(np.tile(np.sqrt(np.sum(ds ** 2, axis=2)), [ds.shape[2], 1, 1]), [1, 2, 0]))
    else:
        # print('No Normalization')
        ds = ds

        # now divide the data by a gaussian convolved version of it (filter it)
    if sigma_gauss_norm > 0:
        ds = ds / (gaussian_filter1d(ds, sigma=sigma_gauss_norm, mode='nearest', axis=2))

    return ds

def get_localization_coefficients_score(x_test, y_test):
    x_test = np.reshape(x_test, (x_test.shape[0] * x_test.shape[1], 2))
    y_test = np.reshape(y_test, (y_test.shape[0] * y_test.shape[1]))

    lr_model = LinearReg(x_test, y_test)
    gain, bias = lr_model.get_coefficients()
    return gain, bias, lr_model.get_score()

def create_exp_name(values):
    # receives an array of values and returns a unique experiment name from these values
    return '_'.join([ str(float(i)) if type(i) == int  else str(i) for i in values ]) + '.npy'


from sys import stdin, setrecursionlimit

setrecursionlimit(10**6)

def main():
    H, W = map(int, stdin.readline().split())
    grid = []
    for _ in range(H):
        *X, = map(int, stdin.readline().split())
        grid.append(X)

    ruiseki_grid = []
    for row in grid:
        temp_height = 0
        temp_ruiseki = [temp_height]
        for val in row:
            temp_height += val
            temp_ruiseki.append(temp_height)
        ruiseki_grid.append(temp_ruiseki)

    Q = int(stdin.readline())
    for _ in range(Q):
        A, B, C, D = map(int, stdin.readline().split())
        ans = 0
        for r in range(A-1, C):
            ans += (ruiseki_grid[r][D] - ruiseki_grid[r][B-1])
            # print(ruiseki_grid[r][D] - ruiseki_grid[r][B-1])
        print(ans)

if __name__ == '__main__':
    main()


# Repository: https://gitlab.com/quantify-os/quantify-core
# Licensed according to the LICENCE file on the main branch
"""Module containing the MeasurementControl."""
from __future__ import annotations

import itertools
import math
import signal
import tempfile
import threading
import time
import types
from collections.abc import Iterable
from itertools import chain
from pathlib import Path
from typing import Any, Dict, Optional
from tqdm.auto import tqdm

import adaptive
import numpy as np
import xarray as xr
from filelock import FileLock
from qcodes import validators as vals
from qcodes.instrument import Instrument, InstrumentChannel
from qcodes.parameters import InstrumentRefParameter, ManualParameter

from quantify_core import __version__ as _quantify_version
from quantify_core.data.experiment import QuantifyExperiment
from quantify_core.data.handling import (
    DATASET_NAME,
    _is_uniformly_spaced_array,
    create_exp_folder,
    grow_dataset,
    initialize_dataset,
    snapshot,
    trim_dataset,
)
from quantify_core.measurement.types import Gettable, Settable, is_batched
from quantify_core.utilities.general import call_if_has_method

# Intended for plotting monitors that run in separate processes
_DATASET_LOCKS_DIR = Path(tempfile.gettempdir())

class MeasurementControl(Instrument):  # pylint: disable=too-many-instance-attributes
    """
    Instrument responsible for controlling the data acquisition loop.

    MeasurementControl (MC) is based on the notion that every experiment consists of
    the following steps:

        1. Set some parameter(s)            (settable_pars)
        2. Measure some other parameter(s)  (gettable_pars)
        3. Store the data.

    Example:

        .. code-block:: python

            meas_ctrl.settables(mw_source1.freq)
            meas_ctrl.setpoints(np.arange(5e9, 5.2e9, 100e3))
            meas_ctrl.gettables(pulsar_QRM.signal)
            dataset = meas_ctrl.run(name='Frequency sweep')

    MC exists to enforce structure on experiments. Enforcing this structure allows:

        - Standardization of data storage.
        - Providing basic real-time visualization.

    MC imposes minimal constraints and allows:

    - Iterative loops, experiments in which setpoints are processed step by step.
    - Batched loops, experiments in which setpoints are processed in batches.
    - Adaptive loops, setpoints are determined based on measured values.

    .. seealso:: :ref:`Measurement Control How-To <howto-measurement-control>`

    Parameters
    ----------
    name
        name of this instrument.
    """

    def __init__(self, name: str):
        super().__init__(name=name)

        # Parameters are attributes included in logging and which the user can change.

        self.lazy_set = ManualParameter(
            vals=vals.Bool(),
            initial_value=False,
            name="lazy_set",
            instrument=self,
        )
        """If set to ``True``, only set any settable if the setpoint differs
        from the previous setpoint. Note that this parameter is overridden by the
        ``lazy_set`` argument passed to the :meth:`.run` and :meth:`.run_adaptive`
        methods."""

        self.verbose = ManualParameter(
            vals=vals.Bool(),
            initial_value=True,
            instrument=self,
            name="verbose",
        )
        """If set to ``True``, prints to ``std_out`` during experiments."""

        self.on_progress_callback = ManualParameter(
            vals=vals.Callable(),
            instrument=self,
            name="on_progress_callback",
        )
        """A callback to communicate progress. This should be a callable accepting
        floats between 0 and 100 indicating the percentage done."""

        self.instr_plotmon = InstrumentRefParameter(
            vals=vals.MultiType(vals.Strings(), vals.Enum(None)),
            instrument=self,
            name="instr_plotmon",
        )
        """Instrument responsible for live plotting. Can be set to ``None`` to disable
        live plotting."""

        self.update_interval = ManualParameter(
            initial_value=0.5,
            vals=vals.Numbers(min_value=0.1),
            instrument=self,
            name="update_interval",
        )
        """Interval for updates during the data acquisition loop, every time more than
        :attr:`.update_interval` time has elapsed when acquiring new data points, data
        is written to file (and the live monitoring detects updated)."""

        # Add experiment_data submodule to allow user to save custom metadata
        experiment_data = InstrumentChannel(self, "experiment_data")
        self.add_submodule("experiment_data", experiment_data)

        self._soft_avg_validator = vals.Ints(1, int(1e8)).validate

        # variables that are set before the start of any experiment.
        self._settable_pars = []
        self._setpoints = []
        self._setpoints_input = []
        self._gettable_pars = []

        # variables used for book keeping during acquisition loop.
        self._soft_avg = 1
        self._nr_acquired_values = 0
        self._loop_count = 0
        self._begintime = time.time()
        self._last_upd = time.time()
        self._batch_size_last = None
        self._dataarray_cache: Optional[Dict[str, Any]] = None

        # variables used for persistence, plotting and data handling
        self._dataset = None
        self._exp_folder: Path = None
        self._experiment = None
        self._plotmon_name = ""
        # attributes named as if they are python attributes, e.g. dset.drid_2d == True
        self._plot_info = {
            "grid_2d": False,
            "grid_2d_uniformly_spaced": False,
            "1d_2_settables_uniformly_spaced": False,
        }

        # properly handling KeyboardInterrupts
        self._thread_data = threading.local()
        # counter for KeyboardInterrupts to allow forced interrupt
        self._thread_data.events_num = 0

    def __repr__full__(self):
        str_out = super().__repr__() + "\n"

        # hasattr is necessary in case the instrument was closed
        if hasattr(self, "_settable_pars"):
            settable_names = [p.name for p in self._settable_pars]
            str_out += f"    settables: {settable_names}\n"

        if hasattr(self, "_gettable_pars"):
            gettable_names = [p.name for p in self._gettable_pars]
            str_out += f"    gettables: {gettable_names}\n"

        if hasattr(self, "_setpoints_input") and self._setpoints_input is not None:
            input_shapes = [
                np.asarray(points).shape for points in self._setpoints_input
            ]
            str_out += f"    setpoints_grid input shapes: {input_shapes}\n"

        if hasattr(self, "_setpoints") and self._setpoints is not None:
            str_out += f"    setpoints shape: {np.asarray(self._setpoints).shape}\n"

        return str_out

    def __repr__(self):
        """
        Returns a string containing a summary of this object regarding settables,
        gettables and setpoints.

        Intended, for example, to give a more useful representation in interactive
        shells.
        """
        return self.__repr__full__()

    def get_idn(self) -> dict[str, str | None]:
        return {
            "vendor": "Quantify",
            "model": f"{self.__module__}.{self.__class__.__name__}",
            "serial": self.name,
            "firmware": _quantify_version,
        }

    def show(self):
        """Print short representation of the object to stdout."""
        print(self.__repr__full__())

    def set_experiment_data(
        self, experiment_data: Dict[str, Any], overwrite: bool = True
    ):
        """
        Populates the experiment_data submodule with experiment_data parameters

        Parameters
        -----------
        experiment_data:
            Dict specifying the names of the experiment_data parameters and their
            values. Follows the format:

            .. code-block:: python

                {
                    "parameter_name": {
                        "value": 10.2
                        "label": "parameter label"
                        "unit": "Hz"
                    }
                }

        overwrite:
            If True, clear all previously saved experiment_data parameters and save new
            ones.
            If False, keep all previously saved experiment_data parameters and change
            their values if necessary
        """
        if overwrite:
            self.clear_experiment_data()

        for name, parameter in experiment_data.items():
            if name not in self.experiment_data.parameters:
                self.experiment_data.add_parameter(
                    name=name, parameter_class=ManualParameter
                )

            self.experiment_data.parameters[name](parameter.get("value"))
            self.experiment_data.parameters[name].label = parameter.get("label", name)
            self.experiment_data.parameters[name].unit = parameter.get("unit", "")

    def clear_experiment_data(self):
        """
        Remove all experiment_data parameters from the experiment_data submodule
        """
        self.experiment_data.parameters = {}

    ############################################
    # Methods used to control the measurements #
    ############################################

    def _reset(self, save_data=True):
        """
        Resets all experiment specific variables for a new run.
        """
        self._nr_acquired_values = 0
        self._loop_count = 0
        self._begintime = time.time()
        self._batch_size_last = None
        # Reset KeyboardInterrupt counter
        self._thread_data.events_num = 0
        # Assign handler to interrupt signal
        signal.signal(signal.SIGINT, self._interrupt_handler)
        self._save_data = save_data
        self._dataarray_cache = None

    def _reset_post(self):
        """
        Resets specific variables that can change before `.run()`.
        """
        self._plot_info = {
            "grid_2d": False,
            "grid_2d_uniformly_spaced": False,
            "1d_2_settables_uniformly_spaced": False,
        }
        # Reset to default interrupt handler
        signal.signal(signal.SIGINT, signal.default_int_handler)

        # Make sure tqdm progress bar attribute is closed and removed if mc is interrupted and shot down gracefully
        if self.verbose() and hasattr(self, "pbar"):
            self.pbar.close()
            del self.pbar

    def _init(self, name):
        """
        Initializes MC, such as creating the Dataset, experiment folder and such.
        """
        # needs to be calculated here because we need the settables' `.batched`
        if self._setpoints is None:
            self._setpoints = grid_setpoints(self._setpoints_input, self._settable_pars)

        # initialize an empty dataset
        self._dataset = initialize_dataset(
            self._settable_pars, self._setpoints, self._gettable_pars
        )

        self._dataset.attrs["name"] = name
        # cannot add it as a separate (nested) dict so make it flat.
        self._dataset.attrs.update(self._plot_info)

        tuid = self._dataset.attrs["tuid"]

        self._experiment = QuantifyExperiment(tuid=tuid)
        if self._save_data:
            self._exp_folder = Path(create_exp_folder(tuid=tuid, name=name))
            self._safe_write_dataset()  # Write the empty dataset

            snap = snapshot(update=False, clean=True)  # Save a snapshot of all
            self._experiment.save_snapshot(snap)
        else:
            self._exp_folder = None

        if self.instr_plotmon():
            # Tell plotmon to start monitoring the new dataset
            self.instr_plotmon.get_instr().update(tuid=tuid)

    def run(
        self,
        name: str = "",
        soft_avg: int = 1,
        lazy_set: Optional[bool] = None,
        save_data: bool = True,
    ) -> xr.Dataset:
        """
        Starts a data acquisition loop.

        Parameters
        ----------
        name
            Name of the measurement. It is included in the name of the data files.
        soft_avg
            Number of software averages to be performed by the measurement control.
            E.g. if `soft_avg=3` the full dataset will be measured 3 times and the
            measured values will be averaged element-wise, the averaged dataset is then
            returned.
        lazy_set
            If ``True`` and a setpoint equals the previous setpoint, the ``.set`` method
            of the settable will not be called for that iteration.
            If this argument is ``None``, the ``.lazy_set()`` ManualParameter is used
            instead (which by default is ``False``).

            .. warning:: This feature is not available yet when running in batched mode.
        save_data
            If ``True`` that the measurement data is stored.
        """
        lazy_set = lazy_set if lazy_set is not None else self.lazy_set()
        self._soft_avg_validator(soft_avg)  # validate first
        self._soft_avg = soft_avg
        self._reset(save_data=save_data)
        self._init(name)

        self._prepare_settables()

        try:
            if self._get_is_batched():
                if self.verbose():
                    print("Starting batched measurement...")
                self._run_batched()
            else:
                if self.verbose():
                    print("Starting iterative measurement...")
                self._run_iterative(lazy_set)
        except KeyboardInterrupt:
            print("\nInterrupt signaled, exiting gracefully...")

        if self._save_data:
            self._safe_write_dataset()  # Wrap up experiment and store data
        self._finish()
        self._reset_post()

        self._check_interrupt()  # Propagate interruption

        return self._dataset

    def run_adaptive(self, name, params, lazy_set: Optional[bool] = None) -> xr.Dataset:
        """
        Starts a data acquisition loop using an adaptive function.

        .. warning ::
            The functionality of this mode can be complex - it is recommended to read
            the relevant long form documentation.

        Parameters
        ----------
        name
            Name of the measurement. This name is included in the name of the data
            files.
        params
            Key value parameters describe the adaptive function to use, and any further
            parameters for that function.
        lazy_set
            If ``True`` and a setpoint equals the previous setpoint, the ``.set`` method
            of the settable will not be called for that iteration.
            If this argument is ``None``, the ``.lazy_set()`` ManualParameter is used
            instead (which by default is ``False``).
        """
        lazy_set = lazy_set if lazy_set is not None else self.lazy_set()

        def measure(vec) -> float:
            """
            This function executes the measurement and is passed to the adaptive
            function (often a minimization algorithm) to be evaluated many times.

            Although the measure function acquires (and stores) all gettable parameters,
            only the first value is returned to match the function signature for a valid
            measurement function.
            """
            if len(self._dataset["y0"]) == self._nr_acquired_values:
                self._dataset = grow_dataset(self._dataset)

            #  1D sweeps return single values, wrap in a list
            if np.isscalar(vec):
                vec = [vec]

            self._iterative_set_and_get(vec, self._nr_acquired_values, lazy_set)
            # only y0 is returned so as to match the function signature for a valid
            # measurement function.
            ret = self._dataset["y0"].values[self._nr_acquired_values]
            self._nr_acquired_values += 1
            self._update(".")
            self._check_interrupt()
            return ret

        def subroutine():
            self._prepare_settables()
            self._prepare_gettables()

            adaptive_function = params.get("adaptive_function")
            af_pars_copy = dict(params)

            # if the adaptive function is part of the python adaptive library
            if isinstance(adaptive_function, type) and issubclass(
                adaptive_function, adaptive.learner.BaseLearner
            ):
                goal = af_pars_copy["goal"]
                unusued_pars = ["adaptive_function", "goal"]
                for unusued_par in unusued_pars:
                    af_pars_copy.pop(unusued_par, None)
                learner = adaptive_function(measure, **af_pars_copy)
                adaptive.runner.simple(learner, goal)

            # any adaptive function with the right signature
            elif isinstance(adaptive_function, types.FunctionType):
                unused_pars = ["adaptive_function"]
                for unused_par in unused_pars:
                    af_pars_copy.pop(unused_par, None)
                adaptive_function(measure, **af_pars_copy)

        self._reset()
        self.setpoints(
            np.empty((64, len(self._settable_pars)))
        )  # block out some space in the dataset
        self._init(name)

        try:
            print("Running adaptively...")
            subroutine()
        except KeyboardInterrupt:
            print("\nInterrupt signaled, exiting gracefully...")

        self._finish()
        self._dataset = trim_dataset(self._dataset)
        self._safe_write_dataset()  # Wrap up experiment and store data

        self._check_interrupt()  # Propagate interruption

        return self._dataset

    def _run_iterative(self, lazy_set: bool = False):
        while self._get_fracdone() < 1.0:
            self._prepare_gettables()

            self._dataarray_cache = {}
            for row in self._setpoints:
                self._iterative_set_and_get(row, self._curr_setpoint_idx(), lazy_set)
                self._nr_acquired_values += 1
                self._update()
                self._check_interrupt()
            self._dataarray_cache = None
            self._loop_count += 1

    def _run_batched(self):  # pylint: disable=too-many-locals
        batch_size = self._get_batch_size()
        where_batched = self._get_where_batched()
        where_iterative = self._get_where_iterative()
        batched_settables = self._get_batched_settables()
        iterative_settables = self._get_iterative_settables()

        if self.verbose():
            print(
                "Iterative settable(s) [outer loop(s)]:\n\t",
                ", ".join(par.name for par in iterative_settables) or "--- (None) ---",
                "\nBatched settable(s):\n\t",
                ", ".join(par.name for par in batched_settables),
                f"\nBatch size limit: {batch_size:d}\n",
            )
        while self._get_fracdone() < 1.0:
            setpoint_idx = self._curr_setpoint_idx()
            self._batch_size_last = batch_size
            slice_len = setpoint_idx + self._batch_size_last
            for i, spar in enumerate(iterative_settables):
                # Here ensure that all setpoints of each iterative settable are the same
                # within each batch
                val, iterator = next(
                    itertools.groupby(
                        self._setpoints[setpoint_idx:slice_len, where_iterative[i]]
                    )
                )
                spar.set(val)
                # We also determine the size of each next batch
                self._batch_size_last = min(self._batch_size_last, len(tuple(iterator)))

            slice_len = setpoint_idx + self._batch_size_last
            for i, spar in enumerate(batched_settables):
                pnts = self._setpoints[setpoint_idx:slice_len, where_batched[i]]
                spar.set(pnts)
            # Update for `print_progress`
            self._batch_size_last = min(self._batch_size_last, len(pnts))

            self._prepare_gettables()

            y_off = 0
            for gpar in self._gettable_pars:
                new_data = gpar.get()  # can return (N, M)
                # if we get a simple array, shape it to (1, M)
                if len(np.shape(new_data)) == 1:
                    new_data = new_data.reshape(1, (len(new_data)))

                for row in new_data:
                    yi_name = f"y{y_off}"
                    slice_len = setpoint_idx + len(row)  # the slice we will be updating
                    old_vals = self._dataset[yi_name].values[setpoint_idx:slice_len]
                    old_vals[
                        np.isnan(old_vals)
                    ] = 0  # will be full of NaNs on the first iteration, change to 0
                    self._dataset[yi_name].values[
                        setpoint_idx:slice_len
                    ] = self._build_data(row, old_vals)
                    y_off += 1

            self._nr_acquired_values += np.shape(new_data)[1]
            self._update()
            self._check_interrupt()

    def _build_data(self, new_data, old_data):
        if self._soft_avg == 1:
            return old_data + new_data

        return (new_data + old_data * self._loop_count) / (1 + self._loop_count)

    def _iterative_set_and_get(
        self, setpoints: np.ndarray, idx: int, lazy_set: bool = False
    ):
        """
        Processes one row of setpoints. Sets all settables, gets all gettables, encodes
        new data in dataset.

        If lazy_set==True and any setpoint equals the corresponding previous setpoint,
        that setpoint is not set in its corresponding settable.

        .. note ::

            Note: some lines in this function are redundant depending on mode (sweep vs
            adaptive). Specifically:

                - in sweep, the x dimensions are already filled
                - in adaptive, soft_avg is always 1
        """
        assert self._dataset is not None

        # set all individual setparams
        for setpar_idx, (spar, spt) in enumerate(zip(self._settable_pars, setpoints)):
            xi_name = f"x{setpar_idx}"
            if self._dataarray_cache is None:
                xi_dataarray_values = self._dataset[xi_name].values
            else:
                if not xi_name in self._dataarray_cache:
                    self._dataarray_cache[xi_name] = self._dataset[xi_name].values
                xi_dataarray_values = self._dataarray_cache[xi_name]
            xi_dataarray_values[idx] = spt
            prev_spt = xi_dataarray_values[idx - 1] if idx else None
            # if lazy_set==True and the setpoint equals the previous setpoint, do not
            # set the setpoint.
            if not (lazy_set and spt == prev_spt):
                spar.set(spt)

        # get all data points
        y_offset = 0
        for gpar in self._gettable_pars:
            new_data = gpar.get()
            # if the gettable returned a float, cast to list
            if np.isscalar(new_data):
                new_data = [new_data]
            # iterate through the data list, each element is different y for these
            # x coordinates
            for val in new_data:
                yi_name = f"y{y_offset}"
                if self._dataarray_cache is None:
                    yi_dataarray_values = self._dataset[yi_name].values
                else:
                    if not yi_name in self._dataarray_cache:
                        self._dataarray_cache[yi_name] = self._dataset[yi_name].values
                    yi_dataarray_values = self._dataarray_cache[yi_name]
                old_val = yi_dataarray_values[idx]
                if self._soft_avg == 1 or np.isnan(old_val):
                    if isinstance(val, np.ndarray) and val.size == 1:
                        # This branch avoids usage of deprecated code
                        yi_dataarray_values[idx] = val.item()
                    else:
                        # This is deprecated if val is an np.ndarray with ndim > 0
                        yi_dataarray_values[idx] = val
                else:
                    averaged = (val + old_val * self._loop_count) / (
                        1 + self._loop_count
                    )
                    yi_dataarray_values[idx] = averaged
                y_offset += 1

    ############################################
    # Methods used to control the measurements #
    ############################################

    def _interrupt_handler(self, signal, frame):
        """
        A proper handler for signal.SIGINT (a.k.a. KeyboardInterrupt).

        Used to avoid affecting any other code, e.g. instruments drivers, and be able
        safely stop the MC after an iteration finishes.
        """
        self._thread_data.events_num += 1
        if self._thread_data.events_num >= 5:
            raise KeyboardInterrupt
        print(
            f"\n\n[!!!] {self._thread_data.events_num} interruption(s) signaled. "
            "Stopping after this iteration/batch.\n"
            f"[Send {5 -  self._thread_data.events_num} more interruptions to force"
            f"stop (not safe!)].\n"
        )

    def _check_interrupt(self):
        """
        Verifies if the user has signaled the interruption of the experiment.

        Intended to be used after each iteration or after each batch of data.
        """
        if self._thread_data.events_num >= 1:
            # It is safe to raise the KeyboardInterrupt here because we are guaranteed
            # To be running MC code. The exception can be handled in a try-except
            raise KeyboardInterrupt("Measurement interrupted")

    def _update(self, print_message: str = None):
        """
        Do any updates to/from external systems, such as saving, plotting, etc.
        """
        update = (
            time.time() - self._last_upd > self.update_interval()
            or self._nr_acquired_values == self._get_max_setpoints()
        )
        if update:
            self.print_progress(print_message)

            if self._save_data:
                self._safe_write_dataset()

            self._last_upd = time.time()

    def _prepare_gettables(self) -> None:
        """
        Call prepare() on the Gettable, if prepare() exists
        """
        for getpar in self._gettable_pars:
            call_if_has_method(getpar, "prepare")

    def _prepare_settables(self) -> None:
        """
        Call prepare() on all Settable, if prepare() exists
        """
        for setpar in self._settable_pars:
            call_if_has_method(setpar, "prepare")

    def _finish(self) -> None:
        """
        Call finish() on all Settables and Gettables, if finish() exists
        """
        for par in self._gettable_pars + self._settable_pars:
            call_if_has_method(par, "finish")

    def _get_batched_mask(self):
        return tuple(is_batched(spar) for spar in self._settable_pars)

    def _get_where_batched(self):
        # Indices to select correct entries in results data
        return np.where(self._get_batched_mask())[0]

    def _get_where_iterative(self):
        return np.where(tuple(not m for m in self._get_batched_mask()))[0]

    def _get_iterative_settables(self):
        return tuple(spar for spar in self._settable_pars if not is_batched(spar))

    def _get_batched_settables(self):
        return tuple(spar for spar in self._settable_pars if is_batched(spar))

    def _get_batch_size(self):
        # np.inf is not supported by the JSON schema, but we keep the code robust
        min_with_inf = min(
            getattr(par, "batch_size", np.inf)
            for par in chain.from_iterable((self._settable_pars, self._gettable_pars))
        )
        return min(min_with_inf, len(self._setpoints))

    def _get_is_batched(self) -> bool:
        if any(
            is_batched(gpar) for gpar in chain(self._gettable_pars, self._settable_pars)
        ):
            if not all(is_batched(gpar) for gpar in self._gettable_pars):
                raise RuntimeError(
                    "Control mismatch; all Gettables must have batched Control Mode, "
                    "i.e. all gettables must have `.batched=True`."
                )
            if not any(is_batched(spar) for spar in self._settable_pars):
                raise RuntimeError(
                    "Control mismatch; At least one settable must have "
                    "`settable.batched=True`, if the gettables are batched."
                )
            return True

        return False

    def _get_max_setpoints(self) -> int:
        """
        The total number of setpoints to examine
        """
        return len(self._setpoints) * self._soft_avg

    def _curr_setpoint_idx(self) -> int:
        """
        Current position through the sweep

        Returns
        -------
        int
            setpoint_idx
        """
        acquired = self._nr_acquired_values
        setpoint_idx = acquired % len(self._setpoints)
        self._loop_count = acquired // len(self._setpoints)
        return setpoint_idx

    def _get_fracdone(self) -> float:
        """
        Returns the fraction of the experiment that is completed.
        """
        return self._nr_acquired_values / self._get_max_setpoints()

    def print_progress(self, progress_message: str = None):
        """
        Prints the provided `progress_messages` or displays tqdm progress bar; and calls the
        callback specified by `on_progress_callback`.

        NB: if called with no progress message (progress bar is used),
            `self.pbar` attribute should be closed and removed.

        Printing and progress bar display can be suppressed with `.verbose(False)`.
        """

        # There are no points initialized, progress does not make sense
        if self._get_max_setpoints() == 0:
            raise ValueError("No setpoints available, progress cannot be defined")

        # by checking if `progress_message` is None we make sure we change print behavior for adaptive run
        if self.verbose() and not hasattr(self, "pbar") and progress_message is None:
            # when you use `unit` instead of `postfix` it removes unintended comma
            # see https://github.com/tqdm/tqdm/issues/712
            custom_bar_format = "{l_bar}{bar} [ elapsed time: {elapsed} | time left: {remaining} ] {unit}"
            self.pbar = tqdm(total=100, desc="Completed", bar_format=custom_bar_format)

        progress_percent = self._get_fracdone() * 100

        if self.verbose() and progress_message is None:
            progress_diff = math.floor(progress_percent) - self.pbar.n
            if self._batch_size_last is not None:
                self.pbar.unit = " last batch size: %s" % self._batch_size_last
            else:
                # if no unit attribute provided `custom_bar_format` breaks with extra `it` output
                self.pbar.unit = ""
            self.pbar.update(progress_diff)

        if (
            self.verbose()
            and hasattr(self, "pbar")
            and math.floor(progress_percent) >= 100
        ):
            self.pbar.close()
            del self.pbar

        if self.on_progress_callback() is not None:
            self.on_progress_callback()(progress_percent)

        if self.verbose() and progress_message is not None:
            print(progress_message, end="")

    def _safe_write_dataset(self):
        """
        Uses a lock when writing the file to stay safe for multiprocessing.
        Locking files are written into a temporary dir to avoid polluting
        the experiment container.
        """
        # Multiprocess safe
        lockfile = (
            _DATASET_LOCKS_DIR / f"{self._dataset.attrs['tuid']}-{DATASET_NAME}.lock"
        )
        with FileLock(lockfile, 5):
            self._experiment.write_dataset(self._dataset)

    ####################################
    # Non-parameter get/set functions  #
    ####################################

    def settables(self, settable_pars):
        """
        Define the settable parameters for the acquisition loop.

        The :class:`.Settable` helper class defines the
        requirements for a Settable object.

        Parameters
        ---------
        settable_pars
            parameter(s) to be set during the acquisition loop, accepts a list or tuple
            of multiple Settable objects or a single Settable object.
        """
        # for native nD compatibility we treat this like a list of settables.
        if not isinstance(settable_pars, (list, tuple)):
            settable_pars = [settable_pars]

        self._settable_pars = []
        for settable in settable_pars:
            self._settable_pars.append(Settable(settable))

    def setpoints(self, setpoints: np.ndarray):
        """
        Set setpoints that determine values to be set in acquisition loop.

        .. tip::

            Use :func:`~numpy.column_stack` to reshape multiple
            1D arrays when setting multiple settables.

        Parameters
        ----------
        setpoints :
            An array that defines the values to loop over in the experiment.
            The shape of the array has to be either (N,) or (N,1) for a 1D loop;
            or (N, M) in the case of an MD loop.
        """
        if len(np.shape(setpoints)) == 1:
            setpoints = setpoints.reshape((len(setpoints), 1))
        elif len(np.shape(setpoints)) == 2:
            # used in plotmon to detect need for interpolation in 2d plot
            is_uniform = all(
                _is_uniformly_spaced_array(setpoints_i) for setpoints_i in setpoints.T
            )
            self._plot_info["1d_2_settables_uniformly_spaced"] = is_uniform

        self._setpoints = setpoints
        # `.setpoints()` and `.setpoints_grid()` cannot be used at the same time
        self._setpoints_input = None

    def setpoints_grid(self, setpoints: Iterable[np.ndarray]):
        """
        Makes a grid from the provided `setpoints` assuming each array element
        corresponds to an orthogonal dimension.
        The resulting gridded points determine values to be set in the acquisition loop.

        The gridding is such that the inner most loop corresponds to the batched
        settable with the smallest `.batch_size`.

        .. seealso:: :ref:`Measurement Control How-To <howto-measurement-control>`

        Parameters
        ----------
        setpoints
            The values to loop over in the experiment. The grid is reshaped in the same
            order.
        """
        self._setpoints = None  # assigned later in the `._init()`
        self._setpoints_input = setpoints

        if len(setpoints) == 2:
            self._plot_info["xlen"] = len(setpoints[0])
            self._plot_info["ylen"] = len(setpoints[1])
            self._plot_info["grid_2d"] = True
            is_uniform = all(  # used in plotmon to detect need for interpolation
                _is_uniformly_spaced_array(setpoints[i]) for i in (0, 1)
            )
            self._plot_info["grid_2d_uniformly_spaced"] = is_uniform

    def gettables(self, gettable_pars):
        """
        Define the parameters to be acquired during the acquisition loop.

        The :class:`.Gettable` helper class defines the
        requirements for a Gettable object.

        Parameters
        ----------
        gettable_pars
            parameter(s) to be get during the acquisition loop, accepts:
                 - list or tuple of multiple Gettable objects
                 - a single Gettable object
        """
        if not isinstance(gettable_pars, (list, tuple)):
            gettable_pars = [gettable_pars]

        self._gettable_pars = []
        for gpar in gettable_pars:
            self._gettable_pars.append(Gettable(gpar))

    def measurement_description(self) -> Dict[str, Any]:
        """Return a serializable description of the latest measurement

        Users can add additional information to the description manually.

        Returns
        -------
        :
            Dictionary with description of the measurement
        """
        experiment_description = {
            "name": self._dataset.attrs["name"],
            "settables": [str(s) for s in self._settable_pars],
        }
        experiment_description["gettables"] = [str(s) for s in self._gettable_pars]
        experiment_description["setpoints_shape"] = self._setpoints.shape
        experiment_description["soft_avg"] = self._soft_avg
        experiment_description["acquired_dataset"] = {"tuid": self._dataset.tuid}

        return experiment_description

def grid_setpoints(setpoints: Iterable, settables: Iterable = None) -> np.ndarray:
    """
    Makes gridded setpoints. If `settables` is provided, the gridding is such that the
    inner most loop corresponds to the batched settable with the smallest `.batch_size`.

    .. warning ::

        Using this method typecasts all values into the same type.
        This may lead to validator errors when setting
        e.g., a `float` instead of an `int`.

    Parameters
    ----------
    setpoints
        A list of arrays that defines the values to loop over in the experiment for each
        orthogonal dimension. The grid is reshaped in the same order.
    settables
        A list of settable objects to which the elements in the `setpoints` correspond
        to. Used to correctly grid data when mixing batched and iterative settables.

    Returns
    -------
    :class:`~numpy.ndarray`
        An array where the first numpy axis correspond to individual setpoints.
    """

    if settables is None:
        settables = [None] * len(setpoints)

    coordinates_names = [f"x{i}" for i, pnts in enumerate(setpoints)]
    dataset_coordinates = xr.Dataset(coords=dict(zip(coordinates_names, setpoints)))
    coordinates_batched = [
        name for name, spar in zip(coordinates_names, settables) if is_batched(spar)
    ]
    coordinates_iterative = sorted(
        (
            name
            for name, spar in zip(coordinates_names, settables)
            if not is_batched(spar)
        ),
        reverse=True,
    )

    if len(coordinates_batched):
        batch_sizes = [
            getattr(spar, "batch_size", np.inf)
            for spar in settables
            if is_batched(spar)
        ]
        coordinates_batched_set = set(coordinates_batched)
        inner_coord_name = coordinates_batched[np.argmin(batch_sizes)]
        coordinates_batched_set.remove(inner_coord_name)
        # The inner most coordinate must correspond to the batched settable with
        # min `.batch_size`
        stack_order = (
            coordinates_iterative
            + sorted(coordinates_batched_set, reverse=True)
            + [inner_coord_name]
        )
    else:
        stack_order = coordinates_iterative + sorted(coordinates_batched, reverse=True)

    # Internally the xarray's stack mechanism is used to achieve the desired grid
    stacked_dset = dataset_coordinates.stack(dim_0=stack_order)

    # Return numpy array in the original order
    return np.column_stack([stacked_dset[name].values for name in coordinates_names])


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Thu Nov 18 10:01:06 2021

@author: davidhealy
"""
import matplotlib.pyplot as plt
from datetime import datetime

#   read in file of fault dips, if required
fnAQ = '2021-11-12-211118092446Edited.csv'
iLine = 0
#   open the file
with open(fnAQ, 'r') as reader:
    dtList = []
    fPM10List = []
    fNOList = []
    fNO2List = []
    fNOXasNO2List = []
    fO3List = []
    fPM25List = []
    for line in reader:
        iLine += 1
        if iLine >= 7:
            sLineTokens = line.split(',')
            print(sLineTokens)
            for i, s in enumerate(sLineTokens):
                if i == 1 and len(s) > 0:
                    if "24:" in s[0:3]:
                        s = s.replace("24:", "00:")
                        sDate = sLineTokens[i-1]
                        day = int(sDate[0:2])
                        sLineTokens[i-1] = sDate.replace(str(day), str(day+1))
                    if "\n" in s[8:10]:
                        s = s.replace("\n", "")
                        sDateTime = sLineTokens[i-1] + ' ' + s
                        dDateTime = datetime.strptime(sDateTime, "%d/%m/%Y %H:%M:%S")
                        dtList.append(dDateTime)
                        fPM10List.append(float('nan'))
                        fNOList.append(float('nan'))
                        fNO2List.append(float('nan'))
                        fNOXasNO2List.append(float('nan'))
                        fO3List.append(float('nan'))
                        fPM25List.append(float('nan'))
                        continue
                    else:
                        sDateTime = sLineTokens[i-1] + ' ' + s
                        dDateTime = datetime.strptime(sDateTime, "%d/%m/%Y %H:%M:%S")
                        dtList.append(dDateTime)
                elif i == 2:
                    if len(s) > 0:
                        fPM10List.append(float(s))
                    else:
                        fPM10List.append(float('nan'))
                elif i == 4:
                    if len(s) > 0:
                        fNOList.append(float(s))
                    else:
                        fNOList.append(float('nan'))
                elif i == 6:
                    if len(s) > 0:
                        fNO2List.append(float(s))
                    else:
                        fNO2List.append(float('nan'))
                elif i == 8:
                    if len(s) > 0:
                        fNOXasNO2List.append(float(s))
                    else:
                        fNOXasNO2List.append(float('nan'))
                elif i == 10:
                    if len(s) > 0:
                        fO3List.append(float(s))
                    else:
                        fO3List.append(float('nan'))
                elif i == 12:
                    if len(s) > 0:
                        fPM25List.append(float(s))
                    else:
                        fPM25List.append(float('nan'))
                else:
                    continue

print(iLine)

fig = plt.figure(figsize=(12,4))
plt.plot(dtList, fPM10List, label='PM$_{10}$')
plt.plot(dtList, fPM25List, label='PM$_{2.5}$')
plt.plot(dtList, fNOList, label='NO')
plt.plot(dtList, fNO2List, label='NO$_2$')
#plt.plot(dtList, fNOXasNO2List, label='NO$_x$ as NO$_2$')
#plt.plot(dtList, fO3List, label='O$_3$')
plt.grid(True)
plt.xlabel('Date & time')
plt.ylabel('Concentration, $\mu$g $m^{-3}$')
plt.title('Air quality from DEFRA Salford M60')
plt.legend()
plt.savefig('plotAirQualityTimeWindow.png', dpi=300)