from fock_class import Fock
from math import isclose
import numpy as np
import copy
import time
import sys

"""This will probably be difficult to understand... :("""

"""The goal of this function is to calculate everything related to the hamiltonian of the
Fermi-Hubbard Model in Fock's basis. This includes:

- The ground state energy
- The ground state vector
- The ground state block (state vectors that form the ground state in their binary number form)
- The ground state block matrix

Or when manip = 'Y':
- All the block matrices (named blocks_matrix here for simplicity).
- All the blocks (state vectors in their binary form).
- The ground state block (state vectors that form the ground state in their binary number form)
- All the blocks (in their Fock object form. Said objects are defined in fock_class.py)
- The ground state vector
"""

def hubbard(N=2,t=-1,hopping_matrix=np.matrix([[0,1],[1,0]]),U=4,mu=2,spin_gs='+',manip=False,prt=False,qis_not=False):
    """Parameters
    N: Number of sites
    t: Hopping energy
    hopping_matrix : is the hopping matrix. 1 = possible jump, and 0 = impossible jump
    U: Potential energy
    mu: Chemical potential
    spin_gs: The total spin the ground state should have ('+' or '-')
    manip: Boolean parameter that dictates if the function should return way more things (list is above). This can be heavy on the memory.
    prt: Boolean parameter that dictates if the function should print some of the information in the CMD.
    qis_not: Boolean parameter that dictates if the calculation should be made using qiskit's Fock basis notation (|0up 0down 1up 1down ...>).
    """

    # The bank is a list of states that are yet to be operated on to determine which block is belongs to.
    bank = [state_num for state_num in range(4**N)]

    # Defined for later
    gs_energy = 1e5
    gs_energies = []
    gs_numerical_states = []
    gs_states = []
    gs_blocks = []

    if manip is True:
        blocks_matrix = []
        blocks_num = []
        blocks = []
        gs_blocks_matrix = []
        for x in range(2*N + 1):
            blocks_matrix.append([])
            blocks_num.append([])
            blocks.append([])

    # While the bank is not empty, we want to find all the blocks.
    while bank:
        # Initializing Block list with in it its first state we are operating on.
        block = [Fock(N,bank[0],qis_not)]
        # This will store all the hoppings made, which will determine the position of the t value in the hamitlonian,
        # as well as the phase.
        block_tuples = []

        for state in block:
            for i in range(N):
                for j in range(N):
                    if not i == j and not hopping_matrix[i,j] == 0 :
                        for spin in ['+','-']:
                            """We apply creation/annihilation operators on initial state to calculate
                            the other states in the same block."""
                            new_state = copy.deepcopy(state)
                            new_state.op('destroy',i,spin)
                            new_state.op('create',j,spin)

                            """The Fock class is supposed to turn the numpy matrix (state) into an integer of 0
                            if the state is completely destroyed by either the creation of a particle that is
                            already there or the annihilation of a void. Thus, if the state was NOT destroyed, it
                            counts as a hopping. Thus the following lines are executed to store the hopping in
                            block_tuples."""
                            if not isinstance(new_state.fock,int):
                                block_tuples.append((state,new_state))
                                if not new_state.num in [x.num for x in block]:
                                    # These lines make sure that the state added to the block list has a phase '+'.
                                    new_state_in_block = copy.deepcopy(new_state)
                                    new_state_in_block.sign = 1
                                    block.append(new_state_in_block)

        # Creation of the empty matrix depending on the size of the block
        block_matrix = np.zeros((len(block),len(block)))

        # Defining a rule to sort the block list from smallest to biggest binary number
        def num_sorting(object):
            return object.num
        block.sort(key = num_sorting)

        # We duplicate the block list to have a more readable form in which the states are represented by their binary number
        block_num = [x.num for x in block]
        for number in block_num:
            if number in bank:
                # We empty the bank of the states already calculated
                bank.remove(number)

        # Adding t values into the hamiltonian block. Extra lines compensate for the use or not of qiskit's notation.
        for tuple in block_tuples:
            hopping = tuple[0].fock - tuple[1].fock
            hopping = hopping.tolist()
            site1 = hopping[0].index(-1)
            site2 = hopping[0].index(1)

            if qis_not is False:
                if site1 > N - 1:
                    site1 = int(site1 - N)
                    site2 = int(site2 - N)
            else:
                if site1 % 2 == 0:
                    site1 = int(site1/2)
                else:
                    site1 = int((site1-1)/2)
                if site2 % 2 == 0:
                    site2 = int(site2/2)
                else:
                    site2 = int((site2-1)/2)
            block_matrix[block_num.index(tuple[0].num),block_num.index(tuple[1].num)] = t*hopping_matrix[site1,site2]*tuple[1].sign

        # We add the potential term in the hamiltonian block
        if not U == 0:
            for index,num in enumerate(block_num):
                for site in range(N):
                    new_state = Fock(N,num,qis_not)
                    for spin in ['+','-']:
                        new_state.op('destroy',site,spin)
                        new_state.op('create',site,spin)

                    if not isinstance(new_state.fock,int):
                        block_matrix[index,index] += U

                    '''
                    #This is to symetrize the hamiltonian. Don't bother.
                    new_state = Fock(N,num,qis_not)
                    new_state.op('destroy',site,'+')
                    new_state.op('create',site,'+')

                    if not isinstance(new_state.fock,int):
                        block_matrix[index,index] += -0.5*U

                    new_state = Fock(N,num,qis_not)
                    new_state.op('destroy',site,'-')
                    new_state.op('create',site,'-')

                    if not isinstance(new_state.fock,int):
                        block_matrix[index,index] += -0.5*U

                    block_matrix[index,index] += (0.5**2)*U
                    '''
        # We add the chemical potential to the hamitlonian block
        if not mu == 0:
            for index,num in enumerate(block_num):
                for site in range(N):
                    for spin in ['+','-']:
                        new_state = Fock(N,num,qis_not)
                        new_state.op('destroy',site,spin)
                        new_state.op('create',site,spin)
                        if not isinstance(new_state.fock,int):
                            block_matrix[index,index] += -mu

        # We calculate the eigen value of the now complete block matrix
        energies,numerical_states = np.linalg.eigh(block_matrix)
        gs_energy_block = min(energies)

        # This is to be able to choose which groundstate we want. The positive spin one or negative spin one.
        block_spin = block[0].total_spin

        # If multiple blocks have the same fundamental energy, but not the same ground state, this variable is declared
        # to make sure that the ground state is chosen from the biggest block.
        block_len = len(block)

        # We only consider the blocks that fit the ground state we are after. Positive total spin or Negative total spin.
        if (spin_gs == '+' and block_spin >= 0) or (spin_gs == '-' and block_spin <= 0):
            if gs_energy >= gs_energy_block:
                if gs_energy > gs_energy_block and not isclose(gs_energy,gs_energy_block,abs_tol=1e-5):
                    gs_blocks_matrix = []
                    gs_blocks_num = []
                    gs_numerical_states = []
                    gs_blocks = []
                gs_blocks_matrix.append(block_matrix)
                gs_blocks_num.append(block_num)
                gs_numerical_states.append(numerical_states[:,0].transpose())
                gs_energy = gs_energy_block
                if manip is True:
                    gs_blocks.append(block)

        if prt is True:
            print('States:',block_num)
            print(block_matrix)
            print('gs_energy_block:',gs_energy_block)
            print()

        if manip is True:
            blocks_matrix[block[0].num_electrons].append(block_matrix)
            blocks[block[0].num_electrons].append(block)

    # Rule to sort the blocks depending on their total spins
    def spin_sorting(object_list):
        return object_list[0][0].total_spin

    if manip is True:
        for index,blocks_ele in enumerate(blocks):
            blocks_matrix_ele = blocks_matrix[index]
            blocks[index] = [x for x, y in sorted(zip(blocks_ele, blocks_matrix_ele), key=spin_sorting)]
            blocks_matrix[index] = [y for x, y in sorted(zip(blocks_ele, blocks_matrix_ele), key=spin_sorting)]

        blocks_num = copy.deepcopy(blocks)
        for index1,block_group in enumerate(blocks_num):
            for index2,block in enumerate(block_group):
                for index3,fock_object in enumerate(block):
                    blocks_num[index1][index2][index3] = blocks_num[index1][index2][index3].num

    if prt is True:
        print()
        print()
        print('gs_energy:',gs_energy)
        print('gs_states:')
        for state in gs_numerical_states:
            print(state)
        print('gs_blocks_matrix:')
        for matrix in gs_blocks_matrix:
            print(matrix)
        print()

    if manip is False:
        return gs_energy,gs_numerical_states,gs_blocks_num,gs_blocks_matrix
    if manip is True:
        return blocks_matrix,blocks_num,gs_blocks,blocks,gs_numerical_states


class Solution:
    def numJewelsInStones(self, J: str, S: str) -> int:
        count = 0
        for letter in S:
            print(letter)
            if letter in J:
                print("Yes")
                count = count + 1
        return count

if __name__ == "__main__":
    J = "z"
    S = "ZZ"
    obj = Solution()
    result = obj.numJewelsInStones(J, S)
    print(result)


# _*_ coding : utf-8 _*_
# @Time : 2023/7/18 17:16
# @Author : weixing
# @FileName : pytorch-minist
# @Project : cv

'''
准备数据集：使用 MNIST 数据集作为手写数字数据集。该数据集包含 60000 张 28x28 的灰度图像作为训练数据和 10000 张图像作为测试数据。
    将数据集分为训练集和测试集，比例为 6:1。
定义模型：使用 PyTorch 的 nn.Module 类定义一个神经网络模型。
编译模型：使用 PyTorch 的 nn.CrossEntropyLoss() 函数定义损失函数，使用 optimizer 类定义优化器，如 SGD 或 Adam。
        将模型、损失函数和优化器组合在一起，使用 model.train() 和 model.eval() 方法来训练和评估模型。
测试模型：使用测试集对模型进行评估。在每个 epoch 后，使用测试集计算模型的准确率和损失函数值，并记录最佳性能指标（如准确率或损失函数值）。
预测结果：使用训练好的模型对新的手写数字图像进行预测。将图像转换为合适的张量格式，输入模型中得到预测结果，并将预测结果转换为数字输出。
'''

import torch
import torch.nn as nn
from torchvision import datasets, transforms

# 准备数据集
def minist_loadData():
    train_dataset = datasets.MNIST(root='./data', train=True, download=True, transform=transforms.ToTensor())
    test_dataset = datasets.MNIST(root='./data', train=False, download=True, transform=transforms.ToTensor())
    print("训练数据集数量：", len(train_dataset))
    print("测试数据集数量：", len(test_dataset))
    train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=64, shuffle=True)
    test_loader = torch.utils.data.DataLoader(test_dataset, batch_size=64, shuffle=True)
    return train_loader, test_loader

"""
比较两个向量中有多少数字相等
"""
def count_equal_elements(vec1, vec2):
    count = 0
    for i in range(len(vec1)):
        if vec1[i] == vec2[i]:
            count += 1
    return count

class NetWork(nn.Module):
    def __init__(self):
        super(NetWork, self).__init__()
        self.conv1 = nn.Conv2d(1, 10, kernel_size=5)
        self.conv2 = nn.Conv2d(10, 20, kernel_size=5)
        self.fc1 = nn.Linear(320, 50)
        self.fc2 = nn.Linear(50, 10)

    def forward(self, x):
        x = nn.functional.relu(nn.functional.max_pool2d(self.conv1(x), 2))
        x = nn.functional.relu(nn.functional.max_pool2d(self.conv2(x), 2))
        x = x.view(-1, 320)
        x = nn.functional.relu(self.fc1(x))
        x = self.fc2(x)
        return nn.functional.log_softmax(x, dim=1)  #log_softmax下降效果显著，但是速度很慢；softmax下降效果不理想

class Model():
    def __init__(self, network):
        self.network = network
        # 定义损失函数、优化器
        self.criterion = nn.CrossEntropyLoss()
        self.optimizer = torch.optim.SGD(self.network.parameters(), lr=0.01)

    def train(self, epoch, train_loader):
        print("start training")
        # 训练模型
        for epoch in range(epoch):
            running_loss = 0.0
            for i, (inputs, labels) in enumerate(train_loader):
                self.optimizer.zero_grad()
                outputs = self.network(inputs)  # inputs [64, 1, 28, 28]  labels [64, 1]  outputs #[64, 10]
                loss = self.criterion(outputs, labels)
                loss.backward()
                self.optimizer.step()
                running_loss += loss.item()
            print('train;[Epoch %d] Loss: %.4f' % (epoch + 1, running_loss / len(train_loader)))

        print("train end")
    def eval(self, test_loader):
        print("start test")
        # 测试模型
        correct = 0
        total = 0
        with torch.no_grad():
            for inputs, labels in test_loader:
                outputs = self.network(inputs)  # [64, 10]
                _, predicted = torch.max(outputs.data, 1)  # [64]
                correct += count_equal_elements(labels, predicted)
                total += labels.size(0)
            print("predict;测试集总个数 %d, 预测正确的个数 %d, 准确率 %.4f" % (total, correct, correct / total))

            # 保存参数
            if correct/total > 0.97:
                print("model to be saved")
                torch.save(self.network.state_dict(), "./model/torch-mnist.h5")

        print("test end")
def main():
    # 加载数据集
    train_loader, test_loader = minist_loadData()
    # 网络
    network = NetWork()
    # 模型
    model = Model(network)
    # 训练
    epoch = 10
    model.train(epoch, train_loader)
    # 测试
    model.eval(test_loader)

if __name__=='__main__':
    main()


#!/usr/bin/python3

from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
import time

opts = webdriver.FirefoxOptions()
opts.add_argument("--headless")
driver = webdriver.Firefox(firefox_options=opts)
driver.get("http://localhost/Identity/Login")

time.sleep(1)
u_input = driver.find_element_by_name("Data.Email")
p_input = driver.find_element_by_name("Data.Password")
u_input.send_keys("admin@bugznet.com")
p_input.send_keys("!bugzn3t@admin")
time.sleep(1)
p_input.send_keys(Keys.RETURN)
time.sleep(1)

driver.get("http://localhost/Bugs")
print("Admin user clicking around the ui")

links = driver.find_elements(By.CSS_SELECTOR, 'a[href*="/Bugs/"]')
link_count=len(links)
for i in range(link_count):
    links = driver.find_elements(By.CSS_SELECTOR, 'a[href*="/Bugs/"]')
    driver.execute_script("arguments[0].scrollIntoView(true);", links[i])
    time.sleep(1)
    links[i].click()
    time.sleep(1)
    form = driver.find_element_by_tag_name('form')
    action = ActionChains(driver)
    action.move_to_element_with_offset(form,50,50).click().perform()
    time.sleep(1)
    driver.back()
    time.sleep(1)

time.sleep(2)
driver.close()


import json
# import re
import requests
from requests.structures import CaseInsensitiveDict
from date.date import ipaddress, headers_login

# ipaddress = "192.168.1.100"

def auth_token(ipaddress, headers_login):
    url = f"http://{ipaddress}/login"
    headers = CaseInsensitiveDict()
    headers["Content-Type"] = "application/json"
    data = headers_login
    auth = requests.post(url, headers=headers, data=data)
    token_auth = json.loads(auth.text)["sessionId"]
    return token_auth

def get_users(ipaddress, sessionId):
    url = f"http://{ipaddress}/api/settings/users/get_users"
    headers["sessionId"] = sessionId
    auth = requests.get(url, headers=headers)
    if auth.status_code == 200 :
        users = json.loads(auth.text)["get_users"]
        return users
    else:
        return None

def get_network_ip(ipaddress, sessionId):
    url = f"http://{ipaddress}/api/network/tcpip/ip"
    headers["sessionId"] = sessionId
    auth = requests.get(url, headers=headers)
    print(auth.status_code)
    print(auth.text)
    if auth.status_code == 200 :
        network_ip = json.loads(auth.text)
        return network_ip
    else:
        return None


# pylint: skip-file
import json

import joblib
import numpy as np
from azureml.core.model import Model

from package.train_pipeline.constants import (
    TrainPipelineConstants
)

def init():

    global model

    model_path = Model.get_model_path(TrainPipelineConstants.MODEL_NAME.value)
    model = joblib.load(model_path)

def run(data):

    try:
        data = json.loads(data)
        data = data['data']
        result = model.predict(np.array(data))
        return result.tolist()
    except Exception as e:
        error = str(e)
        return error


# 10 - Grâce à la liste suivante : ordi = ["apple", "asus", "dell", "samsung"], utilisez la boucle While pour afficher\
# toutes les marques d'ordinateur

ordi = ["apple", "asus", "dell", "samsung"]
i=0
while i < len(ordi):
    print(ordi)
    break

# 11 - Grâce à une boule while et a la fonction input(), demandez à l'utilisateur de saisir du texte, et quitter\
# lorsqu'il écrit le mot "exit"

texte=input("Saisissez votre texte\n")


"""Methods for pre-processing subjects, e.g., pruning."""

import logging

from typing import Iterable, List, Mapping, Set

from slub_docsa.common.subject import SimpleSubjectHierarchy, SubjectHierarchy, SubjectTargets, SubjectTuple
from slub_docsa.common.subject import SubjectUriList

logger = logging.getLogger(__name__)

def subject_ancestors_list(
    subject_uri: str,
    subject_hierarchy: SubjectHierarchy,
) -> Iterable[str]:
    """Return the list of ancestors for a subject node in a subject hierarchy (including the subject itself).

    Parameters
    ----------
    subject_uri: str
        the uri of the subject whose ancestors are being returned
    subject_hierarchy: SubjectHierarchy
        the subject hierarchy which is being searched for ancestors of the specified subject

    Returns
    -------
    Iterable[str]
        the list of ancestors (starting with the root ancestors, ending with and including the subject itself)
    """
    ancestors: List[str] = []
    next_subject = subject_uri

    while next_subject is not None:
        ancestors.insert(0, next_subject)
        next_subject = subject_hierarchy.subject_parent(next_subject)

    return ancestors

def subject_siblings_list(
    subject_uri: str,
    subject_hierarchy: SubjectHierarchy,
) -> Iterable[str]:
    """Return the list of siblings for a subject node in a subject hierarchy.

    Parameters
    ----------
    subject_uri: str
        the uri of the subject whose siblings are being returned
    subject_hierarchy: SubjectHierarchy
        the subject hierarchy that is searched for siblings of the specified subject

    Returns
    -------
    Iterable[str]
        the list of siblings in arbitrary order, including the subject itself
    """
    siblings: List[str] = []
    subject_parent = subject_hierarchy.subject_parent(subject_uri)

    for siblings_uri in iter(subject_hierarchy):
        siblings_parent = subject_hierarchy.subject_parent(siblings_uri)

        if siblings_parent == subject_parent:
            siblings.append(siblings_uri)

    return siblings

def root_subjects_from_subject_parent_map(subject_parent: Mapping[str, str]) -> Iterable[str]:
    """Extract root subjects that do not have a parent.

    Parameters
    ----------
    subject_parent : Mapping[str, str]
        the mapping from subject URIs to parent subject URIs

    Returns
    -------
    Iterable[str]
        the list of root subject that do have `None` as parent URI in the provided mapping
    """
    root_subjects = set()
    for child_uri, parent_uri in subject_parent.items():
        if parent_uri is None:
            root_subjects.add(child_uri)
    return list(root_subjects)

def subject_label_breadcrumb(
    subject_uri: str,
    subject_hierarchy: SubjectHierarchy,
) -> Iterable[Mapping[str, str]]:
    """Return a list of label mappings describing the subject hierarchy path from root to this subject.

    Parameters
    ----------
    subject_uri: str
        the uri of the subject whose ancestor path (breadcrumb) is supposed to be returned
    subject_hierarchy: SubjectHierarchy
        the subject hierarchy that is searched for ancestors of the specified subject

    Returns
    -------
    Iterable[Mapping[str, str]]
        a simple breadcrumb string (e.g. subject1 | subject2 | subject3) describing the ancestor path of the subject
    """
    subject_path = subject_ancestors_list(subject_uri, subject_hierarchy)
    return list(map(subject_hierarchy.subject_labels, subject_path))

def subject_label_as_string(
    subject_uri: str,
    lang_code: str,
    subject_hierarchy: SubjectHierarchy,
) -> str:
    """Return a string label for a subject in the given language.

    Parameters
    ----------
    subject_uri: str
        the uri of the subject whose label is requested
    lang_code: str
        the ISO 639-1 language code of the language the label is requested in
    subject_hierarchy: SubjectHierarchy
        the subject hierarchy that contains the information about the subject

    Returns
    -------
    str
        a label for the subject in the given language or a short text indicating
        that there is no label available for the subject in the requested language
    """
    labels = subject_hierarchy.subject_labels(subject_uri)
    if labels is None:
        return f"no label for subject '{subject_uri}'"
    if lang_code not in labels:
        return f"label not available in language '{lang_code}' for subject '{subject_uri}'"
    return labels[lang_code]

def subject_label_breadcrumb_as_string(
    subject_uri: str,
    lang_code: str,
    subject_hierarchy: SubjectHierarchy,
) -> Iterable[str]:
    """Return a list of label mappings describing the subject hierarchy path from root to this subject.

    Parameters
    ----------
    subject_uri: str
        the uri of the subject whose ancestor path (breadcrumb) is supposed to be returned
    lang_code: str
        the ISO 639-1 language code of the language the breadcrumb is requested in
    subject_hierarchy: SubjectHierarchy
        the subject hierarchy that is searched for ancestors of the specified subject

    Returns
    -------
    Iterable[str]
        a simple breadcrumb string (e.g. ["subject1", "subject2", "subject3"]) describing the ancestor path of the
        subject including the requested subject itself
    """
    subject_path = subject_ancestors_list(subject_uri, subject_hierarchy)
    return list(subject_label_as_string(subject_uri, lang_code, subject_hierarchy) for subject_uri in subject_path)

def subject_ancestors_for_subject_list(
    subject_list: Iterable[str],
    subject_hierarchy: SubjectHierarchy,
) -> Iterable[str]:
    """Return the set of all ancestors of a list of subjects from a subject hierarchy.

    Can be used to reduce a large subject hierarchy to a smaller set of relevant subjects.

    Parameters
    ----------
    subject_list : Iterable[str]
        the list of subjects whose ancestors are determined
    subject_hierarchy : SubjectHierarchy
        the subject hierarchy that is used to find ancestors for a subject

    Returns
    -------
    Iterable[str]
        the set of subject URIs for the provided subjects and all of their ancestors
    """
    return set(
        a_uri for s_uri in subject_list for a_uri in subject_ancestors_list(s_uri, subject_hierarchy)
    )

def prune_subject_uri_to_level(
    level: int,
    subject_uri: str,
    subject_hierarchy: SubjectHierarchy
) -> str:
    """Return the ancestor subject uri at a specific hierarchy level.

    Parameters
    ----------
    level: int
        The level at which to return a ancestor subject uri. Level 1 is the root level.
    subject_uri: str
        The uri of the subject that is supposed to be pruned, i.e., replaced by a ancestor subject
    subject_hierarchy: SubjectHierarchy
        The subject hierarchy used for pruning

    Returns
    -------
    str
        Either the ancestor subject uri of an ancestor at the specified hierarchy level, or the original subject uri
        if the requested subject is already lower (near the root) in the hierarchy than the requested level.
    """
    if level < 1:
        raise ValueError("level must be 1 or larger than 1")
    if subject_uri not in subject_hierarchy:
        raise ValueError(f"subject uri {subject_uri} not in hiearchy")

    ancestors = list(subject_ancestors_list(subject_uri, subject_hierarchy))
    if len(ancestors) <= level:
        # subject is below (closer to root) or exactly at the requested level
        return subject_uri

    # subject must be above (closer to leaf) than level
    return ancestors[level - 1]

def prune_subject_uris_to_level(
    level: int,
    subjects: SubjectUriList,
    subject_hierarchy: SubjectHierarchy,
) -> SubjectUriList:
    """Prune a list of subjects to a specified hierarchy level.

    Duplicate subjects are removed. Applies `prune_subject_uri_to_level` to every subject uri of the provided list.

    Parameters
    ----------
    level: int
        The level at which to return a ancestor subject uri. Level 1 is the root level.
    subjects: SubjectUriList
        The list of subjects to be pruned to the specified hierarchy level
    subject_hierarchy: SubjectHierarchy
        The subject hierarchy that contains the required subject relationships

    Returns
    -------
    SubjectUriList
        a list of subject uris, each pruned according to `prune_subject_uri_to_level`
    """
    return list({prune_subject_uri_to_level(level, s, subject_hierarchy) for s in subjects})

def prune_subject_targets_to_level(
    level: int,
    subject_target_list: SubjectTargets,
    subject_hierarchy: SubjectHierarchy,
) -> SubjectTargets:
    """Prune all subjects of a subject list to the specified hierarchy level.

    Duplicate subjects are removed. Applies `prune_subject_uris_to_level` to every subject uri of the provided list of
    subject lists.

    Parameters
    ----------
    level: int
        The level at which to return a ancestor subject uri. Level 1 is the root level.
    subject_target_list: SubjectTargets
        The list of subject lists to be pruned to the specified hierarchy level
    subject_hierarchy: SubjectHierarchy
        The subject hierarchy that contains the required subject relationships

    Returns
    -------
    SubjectTargets
        A list of subject uri lists, each pruned according to `prune_subject_uris_to_level`
    """
    return list(map(
        lambda subjects: prune_subject_uris_to_level(level, subjects, subject_hierarchy),
        subject_target_list
    ))

def count_number_of_samples_by_subjects(subject_targets: SubjectTargets) -> Mapping[str, int]:
    """Count the number of occurances of subjects annotations.

    Parameters
    ----------
    subject_targets: SubjectTargets
        A list of subject lists as provided by a dataset

    Returns
    -------
    Mapping[str, int]
        Occurance counts for each subject
    """
    counts = {}

    for subject_list in subject_targets:
        for subject_uri in subject_list:
            counts[subject_uri] = counts.get(subject_uri, 0) + 1

    return counts

def children_map_from_subject_parent_map(
    subject_parent: Mapping[str, str],
) -> Mapping[str, Iterable[str]]:
    """Return a set of children subjects for each parent subject in a subject hierarchy.

    Parameters
    ----------
    subject_parent: Mapping[str, str]
        The mapping of a subject to its parent subject

    Returns
    -------
    Mapping[str, Iterable[str]]
        A mapping from parent subjects to their respective set of child subjects
    """
    subject_children: Mapping[str, Set[str]] = {}
    for subject_uri, parent_uri in subject_parent.items():
        if subject_uri not in subject_children:
            subject_children[subject_uri] = set()
        if parent_uri is not None:
            if parent_uri not in subject_children:
                subject_children[parent_uri] = set()
            subject_children[parent_uri].add(subject_uri)
    return subject_children

def build_subject_hierarchy_from_subject_tuples(
    subject_tuples: Iterable[SubjectTuple]
) -> SubjectHierarchy:
    """Build a subject hierarchy from a list of subject tuples.

    Parameters
    ----------
    subject_tuples : Iterable[SubjectTuple]
        the list of subject tuples containing information about each subjects URI, labels, parent subject

    Returns
    -------
    SubjectHierarchy
        the generated subject hierarchy
    """
    subject_parent: Mapping[str, str] = {}
    subject_labels: Mapping[str, Mapping[str, str]] = {}
    subject_notation: Mapping[str, str] = {}

    for subject_tuple in subject_tuples:
        subject_parent[subject_tuple.subject_uri] = subject_tuple.parent_uri
        subject_labels[subject_tuple.subject_uri] = subject_tuple.labels
        subject_notation[subject_tuple.subject_uri] = subject_tuple.notation

    root_subjects = root_subjects_from_subject_parent_map(subject_parent)
    subject_children = children_map_from_subject_parent_map(subject_parent)

    return SimpleSubjectHierarchy(root_subjects, subject_labels, subject_parent, subject_children, subject_notation)

def prune_subject_uris_to_parent(
    subject_uris: SubjectUriList,
    to_be_pruned_subjects: Set[str],
    subject_hierarchy: SubjectHierarchy,
) -> SubjectUriList:
    """Replace subject uris with uri of parent subject for those that are supposed to be pruned.

    Is used by `prune_subject_targets_to_minimum_samples` to incrementally replace subjects with their parent subjects
    in case there would be insufficient training examples for that subject.

    Parameters
    ----------
    subject_uris: SubjectUriList
        the list of subjects that is scanned for subjects that are supposed to be pruned
    to_be_pruned_subjects: Set[str]
        the list of subjects that is supposed to be replaced with their parent subjects
    subject_hierarchy: SubjectHierarchy
        The subject hierarchy that contains the required subject relationships

    Returns
    -------
    SubjectUriList
        the same subject list as `subject_uris`,  where some subjects are replaced with their parent subject
    """
    new_subject_uris = []
    for subject_uri in subject_uris:
        if subject_uri in to_be_pruned_subjects:
            parent_uri = subject_hierarchy.subject_parent(subject_uri)
            if parent_uri is not None:
                new_subject_uris.append(parent_uri)
            else:
                new_subject_uris.append(subject_uri)
        else:
            new_subject_uris.append(subject_uri)
    return list(set(new_subject_uris))

def prune_subject_targets_to_parent(
    subject_targets: SubjectTargets,
    to_be_pruned_subjects: Set[str],
    subject_hierarchy: SubjectHierarchy,
) -> SubjectTargets:
    """Replace subject uris in target list with uri of parent subject for those that are supposed to be pruned.

    Is used by `prune_subject_targets_to_minimum_samples` to incrementally replace subjects with their parent subjects
    in case there would be insufficient training examples for that subject.

    Parameters
    ----------
    subject_targets: SubjectTargets
        the list of subject lists that is scanned for subjects that are supposed to be pruned
    to_be_pruned_subjects: Set[str]
        the list of subjects that is supposed to be replaced with their parent subjects
    subject_hierarchy: SubjectHierarchy
        The subject hierarchy that contains the required subject relationships

    Returns
    -------
    SubjectTargets
        the same subject target list as `subject_targets`,  where some subjects are replaced with their parent subject
    """
    return list(
        map(lambda t: prune_subject_uris_to_parent(t, to_be_pruned_subjects, subject_hierarchy), subject_targets)
    )

def prune_subject_targets_to_minimum_samples(
    minimum_samples: int,
    subject_targets: SubjectTargets,
    subject_hierarchy: SubjectHierarchy
) -> SubjectTargets:
    """Prune subject targets such that subjects with insufficient samples are replaced with their parents.

    Subjects with no parents are left as is. Therefore, it is not guaranteed that all subjects will have a minimum
    number of samples after pruning.

    Parameters
    ----------
    minimum_samples: int
        minimum number of required samples for subjects to be considered of acceptable size
    subject_targets: SubjectTargets
        subject targets that are checked for their number of samples, and modified if necessary (not in-place)
    subject_hierarchy: SubjectHierarchy
        The subject hierarchy that contains the required subject relationships

    Returns
    -------
    SubjectTargets
        the same subject target list as `subject_targets`,  where some subjects are replaced with their parent subject
        in case they do not have a sufficient number of samples
    """
    def _relevant_subject_children(relevant_subjects, subject_hierarchy):
        subject_children = {
            s_uri: [c_uri for c_uri in subject_hierarchy.subject_children(s_uri) if c_uri in relevant_subjects]
            for s_uri in relevant_subjects
        }
        return {s_uri: children for s_uri, children in subject_children.items() if children}

    pruned_subject_targets = subject_targets
    subject_counts = count_number_of_samples_by_subjects(subject_targets)
    relevant_subjects = subject_ancestors_for_subject_list(list(subject_counts.keys()), subject_hierarchy)
    subject_children = _relevant_subject_children(relevant_subjects, subject_hierarchy)
    subjects_to_be_checked = set(iter(relevant_subjects))

    i = 0
    while len(subjects_to_be_checked) > 0:
        # only consider childless subjects, whose counts can not increase further
        childless_subjects = [s for s in subjects_to_be_checked if s not in subject_children]
        logger.debug("prune to parent iteration %d considering %d childless subjects", i + 1, len(childless_subjects))

        # find subjects that need pruning
        childless_subjects_to_be_pruned = {
            s for s in childless_subjects if s in subject_counts and subject_counts[s] < minimum_samples
        }

        # do pruning
        logger.debug("prune %d subjects because of insufficient samples", len(childless_subjects_to_be_pruned))
        pruned_subject_targets = prune_subject_targets_to_parent(
            pruned_subject_targets,
            childless_subjects_to_be_pruned,
            subject_hierarchy
        )

        # check each childless subject one by one
        for s_uri in childless_subjects:
            # remove every childless subject from child set of parent,
            # such that parent can become childless in the next iterations,
            # and eventually all subjects are checked
            parent_uri = subject_hierarchy.subject_parent(s_uri)
            if parent_uri:
                subject_children[parent_uri].remove(s_uri)
                if len(subject_children[parent_uri]) == 0:
                    del subject_children[parent_uri]

        # update counts and check again
        subject_counts = count_number_of_samples_by_subjects(pruned_subject_targets)
        subjects_to_be_checked.difference_update(childless_subjects)
        i += 1

    return pruned_subject_targets


"""
Functions for reading and processing of .sdf files written by the Lare code
NOTE: these are different to more common types of .sdf file (e.g. Scientific Data Format) for which Python routines already exist
"""

import numpy as np
import struct
import os

class cfsasdf_file_properties:#TODO - None for unset properties?
	def __init__(self):
		self.endianness=0
		self.sdf_version=0
		self.sdf_revision=0
		self.code_name=""
		self.nblocks=0
		self.step=0
		self.time=0.0
		self.jobid1=0
		self.jobid2=0
		self.code_io_version=0
		self.restart_flag=0
		self.subdomain_file=0

class cfsasdf_block:#TODO: remaining block types, None for unset properties?
	def __init__(self,blocktype):
		self.blocktype=blocktype
		self.ndims=0
		self.block_id=""
		self.block_name=""
		self.data=0.0
		if blocktype==1:#c_blocktype_plain_mesh
			self.mults=0.0
			self.labels=[]
			self.units=[]
			self.geometry_type=0
			self.minval=0.0
			self.maxval=0.0
			self.dims=0
		elif blocktype==2:#c_blocktype_point_mesh
			self.mults=0.0
			self.labels=[]
			self.units=[]
			self.geometry_type=0
			self.minval=0.0
			self.maxval=0.0
			self.num_points=0
		elif blocktype==3:#c_blocktype_plain_variable
			self.mult=0.0
			self.units=""
			self.mesh_id=""
			self.dims=0
			self.stagger=0
		elif blocktype==4:#c_blocktype_point_variable
			self.mult=0.0
			self.units=""
			self.mesh_id=""
			self.num_points=0

class lare3d_dat_quantities:#TODO - None for unset properties?
	def __init__(self):
		self.nprocx=0
		self.nprocy=0
		self.nprocz=0
		self.nx=0
		self.ny=0
		self.nz=0
		self.length_x=0.0
		self.length_y=0.0
		self.length_z=0.0
		self.visc1=0.0
		self.visc2=0.0
		self.j_max=0.0
		self.eta0=0.0
		self.eta_background=0.0
		self.mass_fraction=0.0
		self.B_norm=0.0
		self.L_norm=0.0
		self.Rho_norm=0.0
		self.v_norm=0.0
		self.time_norm=0.0
		self.temp_norm=0.0

#If target_quantities==None or target_quantities=='all', return all, otherwise supply list
def read_cfsasdf_file(filename,target_quantities=None):#TODO: checks, ignore bad blocks, switch based on endianness, comments, dict?
	sdf_datatypes=[[1,'c','byte'],[4,'i','int32'],[8,'q','int64'],[4,'f','float32'],[8,'d','float64'],[16,'d','float64'],[1,'c','byte'],[1,'c','byte'],[1,'c','byte']]

	current_file=cfsasdf_file_properties()

	infile=open(filename,"rb")
	sdf1_str=infile.read(4)
	current_file.endianness=struct.unpack('i', infile.read(4))[0]
	current_file.sdf_version=struct.unpack('i', infile.read(4))[0]
	current_file.sdf_revision=struct.unpack('i', infile.read(4))[0]
	current_file.code_name=infile.read(32).decode('ascii')
	first_block_location=struct.unpack('q', infile.read(8))[0]
	next_block_location=first_block_location
	summary_location=struct.unpack('q', infile.read(8))[0]
	summary_size=struct.unpack('i', infile.read(4))[0]
	current_file.nblocks=struct.unpack('i', infile.read(4))[0]
	block_header_length=struct.unpack('i', infile.read(4))[0]
	current_file.step=struct.unpack('i', infile.read(4))[0]
	current_file.time=struct.unpack('d', infile.read(8))[0]
	current_file.jobid1=struct.unpack('i', infile.read(4))[0]
	current_file.jobid2=struct.unpack('i', infile.read(4))[0]
	string_length=struct.unpack('i', infile.read(4))[0]
	current_file.code_io_version=struct.unpack('i', infile.read(4))[0]
	current_file.restart_flag=struct.unpack('b', infile.read(1))[0]
	current_file.subdomain_file=struct.unpack('b', infile.read(1))[0]

	block_data=[]
	for idx_b in range(current_file.nblocks):
		infile.seek(next_block_location)
		current_block_location=next_block_location
		next_block_location=struct.unpack('q', infile.read(8))[0]
		data_location=struct.unpack('q', infile.read(8))[0]
		block_id=infile.read(32).decode('ascii')
		data_length=struct.unpack('q', infile.read(8))[0]
		blocktype=struct.unpack('i', infile.read(4))[0]
		datatype=struct.unpack('i', infile.read(4))[0]
		ndims=struct.unpack('i', infile.read(4))[0]
		block_name=infile.read(string_length).decode('ascii')
		block_info_length=struct.unpack('i', infile.read(4))[0]
		current_block=cfsasdf_block(blocktype)
		current_block.block_name=block_name
		current_block.ndims=ndims
		current_block.block_id=block_id
		if blocktype==1 or blocktype==2:
			infile.seek(current_block_location+block_header_length)
			current_block.mults=struct.unpack('d'*ndims, infile.read(8*ndims))
			for idx_l in range(ndims):
				current_block.labels.append(infile.read(32).decode('ascii'))
			for idx_l in range(ndims):
				current_block.units.append(infile.read(32).decode('ascii'))
			current_block.geometry_type=struct.unpack('i', infile.read(4))[0]
			current_block.minval=struct.unpack('d'*ndims, infile.read(8*ndims))
			current_block.maxval=struct.unpack('d'*ndims, infile.read(8*ndims))
			if blocktype==1:
				current_block.dims=struct.unpack('i'*ndims, infile.read(4*ndims))
			elif blocktype==2:
				current_block.num_points=struct.unpack('q', infile.read(8))[0]
		elif blocktype==3 or blocktype==4:
			infile.seek(current_block_location+block_header_length)
			current_block.mult=struct.unpack('d', infile.read(8))
			current_block.units=infile.read(32).decode('ascii')
			current_block.mesh_id=infile.read(32).decode('ascii')
			if blocktype==3:
				current_block.dims=struct.unpack('i'*ndims, infile.read(4*ndims))
				current_block.stagger=struct.unpack('i', infile.read(4))[0]
			if blocktype==4:
				current_block.num_points=struct.unpack('q', infile.read(8))[0]
		infile.seek(data_location)
		data_num_total=data_length//sdf_datatypes[datatype][0]

		if target_quantities==None:
			append_block=True
		elif type(target_quantities)==str:
			if target_quantities.lower()=='all' or target_quantities in current_block.block_id:
				append_block=True
			else:
				append_block=False
		elif hasattr(target_quantities, '__iter__'):
			append_block=False
			for elmt in target_quantities:
				if elmt in current_block.block_id:
					append_block=True
		else:
			append_block=False
		if append_block:
			current_block.data=np.array(struct.unpack(sdf_datatypes[datatype][1]*data_num_total, infile.read(data_length)),dtype=sdf_datatypes[datatype][2])
			block_data.append(current_block)
		else:
			infile.seek(data_length,1)
	infile.close()
	return current_file,block_data

def read_lare3d_dat(filename):

	current_data=lare3d_dat_quantities()

	infile=open(filename,"r")
	line=infile.readline()
	line=infile.readline()
	line=infile.readline().split()
	current_data.nprocx=int(line[4])
	current_data.nprocy=int(line[5])
	current_data.nprocz=int(line[6])
	line=infile.readline().split()
	current_data.nx=int(line[4])
	current_data.ny=int(line[5])
	current_data.nz=int(line[6])
	line=infile.readline()
	line=infile.readline().split()
	current_data.length_x=float(line[2])
	line=infile.readline().split()
	current_data.length_y=float(line[2])
	line=infile.readline().split()
	current_data.length_z=float(line[2])
	line=infile.readline()
	line=infile.readline()
	line=infile.readline().split()
	current_data.visc1=float(line[4])
	line=infile.readline().split()
	current_data.visc2=float(line[4])
	line=infile.readline().split()
	current_data.j_max=float(line[2])
	line=infile.readline().split()
	current_data.eta0=float(line[2])
	line=infile.readline().split()
	current_data.eta_background=float(line[2])
	line=infile.readline().split()
	current_data.kappa=float(line[2])
	line=infile.readline()
	line=infile.readline().split()
	current_data.mass_fraction=float(line[2])
	line=infile.readline().split()
	current_data.B_norm=float(line[3])
	line=infile.readline().split()
	current_data.L_norm=float(line[3])
	line=infile.readline().split()
	current_data.Rho_norm=float(line[3])
	line=infile.readline().split()
	current_data.v_norm=float(line[3])
	line=infile.readline().split()
	current_data.time_norm=float(line[3])
	line=infile.readline().split()
	current_data.temp_norm=float(line[3])
	infile.close()

	return current_data

def unstagger_reshape(grid_x,grid_y,grid_z,current_data,current_stagger):
	nx_actual=len(grid_x)
	ny_actual=len(grid_y)
	nz_actual=len(grid_z)
	if current_stagger & 1 == 0:
		nx_actual=nx_actual-1
		grid_x=0.5*(grid_x[1:]+grid_x[:-1])
	if current_stagger & 2 == 0:
		ny_actual=ny_actual-1
		grid_y=0.5*(grid_y[1:]+grid_y[:-1])
	if current_stagger & 4 == 0:
		nz_actual=nz_actual-1
		grid_z=0.5*(grid_z[1:]+grid_z[:-1])
	current_data=current_data.reshape((nz_actual,ny_actual,nx_actual))
	return grid_x,grid_y,grid_z,current_data

def quantity_from_blocks(block_data,target_block_id):
	for idx in range(len(block_data)):
		if "grid" in block_data[idx].block_id:
			grid_dims=block_data[idx].dims
			grid_x=block_data[idx].data[:grid_dims[0]]
			grid_y=block_data[idx].data[grid_dims[0]:grid_dims[0]+grid_dims[1]]
			grid_z=block_data[idx].data[grid_dims[0]+grid_dims[1]:]
		if target_block_id in block_data[idx].block_id:
			current_data=block_data[idx].data
			current_stagger=block_data[idx].stagger

	return unstagger_reshape(grid_x,grid_y,grid_z,current_data,current_stagger)

#Memory-efficient way to read a single quantity from SDF file; TODO: checks, reject bad blocks
def read_cfsasdf_file_lean(filename,target_quantity):
	sdf_datatypes=[[1,'c','byte'],[4,'i','int32'],[8,'q','int64'],[4,'f','float32'],[8,'d','float64'],[16,'d','float64'],[1,'c','byte'],[1,'c','byte'],[1,'c','byte']]

	infile=open(filename,"rb")

	sdf1_str=infile.read(4)
	endianness=struct.unpack('i', infile.read(4))[0]
	sdf_version=struct.unpack('i', infile.read(4))[0]
	sdf_revision=struct.unpack('i', infile.read(4))[0]
	code_name=infile.read(32).decode('ascii')
	first_block_location=struct.unpack('q', infile.read(8))[0]
	next_block_location=first_block_location
	summary_location=struct.unpack('q', infile.read(8))[0]
	summary_size=struct.unpack('i', infile.read(4))[0]
	nblocks=struct.unpack('i', infile.read(4))[0]
	block_header_length=struct.unpack('i', infile.read(4))[0]
	step=struct.unpack('i', infile.read(4))[0]
	time=struct.unpack('d', infile.read(8))[0]
	jobid1=struct.unpack('i', infile.read(4))[0]
	jobid2=struct.unpack('i', infile.read(4))[0]
	string_length=struct.unpack('i', infile.read(4))[0]
	code_io_version=struct.unpack('i', infile.read(4))[0]
	restart_flag=struct.unpack('b', infile.read(1))[0]
	subdomain_file=struct.unpack('b', infile.read(1))[0]

	for idx_b in range(nblocks):
		infile.seek(next_block_location)
		current_block_location=next_block_location
		next_block_location=struct.unpack('q', infile.read(8))[0]
		data_location=struct.unpack('q', infile.read(8))[0]
		block_id=infile.read(32).decode('ascii')
		if "grid" in block_id:
			data_length=struct.unpack('q', infile.read(8))[0]
			blocktype=struct.unpack('i', infile.read(4))[0]
			datatype=struct.unpack('i', infile.read(4))[0]
			ndims=struct.unpack('i', infile.read(4))[0]
			block_name=infile.read(string_length).decode('ascii')
			block_info_length=struct.unpack('i', infile.read(4))[0]
			infile.seek(current_block_location+block_header_length)
			mults=struct.unpack('d'*ndims, infile.read(8*ndims))
			infile.seek(current_block_location+block_header_length+88*ndims+4)
			dims=struct.unpack('i'*ndims, infile.read(4*ndims))
			infile.seek(data_location)
			grid_x=np.array(struct.unpack(sdf_datatypes[datatype][1]*dims[0],infile.read(sdf_datatypes[datatype][0]*dims[0])),dtype=sdf_datatypes[datatype][2])
			grid_y=np.array(struct.unpack(sdf_datatypes[datatype][1]*dims[1],infile.read(sdf_datatypes[datatype][0]*dims[1])),dtype=sdf_datatypes[datatype][2])
			grid_z=np.array(struct.unpack(sdf_datatypes[datatype][1]*dims[2],infile.read(sdf_datatypes[datatype][0]*dims[2])),dtype=sdf_datatypes[datatype][2])
		elif target_quantity in block_id:
			data_length=struct.unpack('q', infile.read(8))[0]
			blocktype=struct.unpack('i', infile.read(4))[0]
			datatype=struct.unpack('i', infile.read(4))[0]
			ndims=struct.unpack('i', infile.read(4))[0]
			block_name=infile.read(string_length).decode('ascii')
			block_info_length=struct.unpack('i', infile.read(4))[0]
			infile.seek(current_block_location+block_header_length)
			mults=struct.unpack('d', infile.read(8))
			infile.seek(current_block_location+block_header_length+72)
			dims=struct.unpack('i'*ndims, infile.read(4*ndims))
			target_array=np.zeros((dims[2],dims[1],dims[0]),dtype=sdf_datatypes[datatype][2])
			infile.seek(data_location)
			for idx_z in range(dims[2]):
				target_array[idx_z,:,:]=np.array(struct.unpack(sdf_datatypes[datatype][1]*dims[0]*dims[1],infile.read(sdf_datatypes[datatype][0]*dims[0]*dims[1])),dtype=sdf_datatypes[datatype][2]).reshape(dims[1],dims[0])
	infile.close()
	return time,grid_x,grid_y,grid_z,target_array

def regularize_Bfield(block_data): #TODO: currently assumes "ordinary" stagger, make use of _stagger in future
	grid_found=False
	Bx_found=False
	By_found=False
	Bz_found=False
	for idx in range(len(block_data)):
		if "grid" in block_data[idx].block_id:
			grid_dims=block_data[idx].dims
			grid_x=block_data[idx].data[:grid_dims[0]]
			grid_y=block_data[idx].data[grid_dims[0]:grid_dims[0]+grid_dims[1]]
			grid_z=block_data[idx].data[grid_dims[0]+grid_dims[1]:]
			grid_found=True
		if "Bx" in block_data[idx].block_id:
			Bx=block_data[idx].data
			Bx_stagger=block_data[idx].stagger
			Bx_found=True
		if "By" in block_data[idx].block_id:
			By=block_data[idx].data
			By_stagger=block_data[idx].stagger
			By_found=True
		if "Bz" in block_data[idx].block_id:
			Bz=block_data[idx].data
			Bz_stagger=block_data[idx].stagger
			Bz_found=True
	if grid_found and Bx_found and By_found and Bz_found:
		Bx=Bx.reshape((len(grid_z)-1,len(grid_y)-1,len(grid_x)))
		By=By.reshape((len(grid_z)-1,len(grid_y),len(grid_x)-1))
		Bz=Bz.reshape((len(grid_z),len(grid_y)-1,len(grid_x)-1))

		grid_x=0.5*(grid_x[1:]+grid_x[:-1])
		grid_y=0.5*(grid_y[1:]+grid_y[:-1])
		grid_z=0.5*(grid_z[1:]+grid_z[:-1])
		B_full=np.zeros((len(grid_z),len(grid_y),len(grid_x),3))
		B_full[:,:,:,0]=0.5*(Bx[:,:,1:]+Bx[:,:,:-1])
		B_full[:,:,:,1]=0.5*(By[:,1:,:]+By[:,:-1,:])
		B_full[:,:,:,2]=0.5*(Bz[1:,:,:]+Bz[:-1,:,:])
		return grid_x,grid_y,grid_z,B_full
	else:
		print("Warning, error in Bfield regularization")
		return np.zeros((1)),np.zeros((1)),np.zeros((1)),np.zeros((1,1,1,3))

def read_Lare_en_file(en_filename):#TODO: switch based on endianness, real size
	infile_size=os.path.getsize(en_filename)
	infile=open(en_filename,"rb")
	magic=str(infile.read(3))
	version=struct.unpack('i', infile.read(4))[0]
	revision=struct.unpack('i', infile.read(4))[0]
	endianness=struct.unpack('i', infile.read(4))[0]
	header_length=struct.unpack('i', infile.read(4))[0]
	num_sz_in=struct.unpack('i', infile.read(4))[0]
	en_nvars_in=struct.unpack('i', infile.read(4))[0]

	variable_names=infile.read(header_length-3-4*6).replace(b'\x00',b'').decode("utf-8").split()
	ndump=(infile_size-header_length)//(num_sz_in*en_nvars_in)
	time=[]
	variable_sum=[]
	for idx in range(ndump):
		time.append(struct.unpack('d', infile.read(8))[0])
		variable_sum.append(struct.unpack('d'*(en_nvars_in-1), infile.read(8*(en_nvars_in-1))))

	infile.close()
	time=np.array(time)
	variable_sum=np.array(variable_sum)

	return variable_names,time,variable_sum

def calculate_dt(block_data,gamma=1.4):#TODO WORK IN PROGRESS; boris,gamma_boris, alpha
	for idx in range(len(block_data)):
		if "grid" in block_data[idx].block_id:
			grid_dims=block_data[idx].dims
			grid_x=block_data[idx].data[:grid_dims[0]]
			grid_y=block_data[idx].data[grid_dims[0]:grid_dims[0]+grid_dims[1]]
			grid_z=block_data[idx].data[grid_dims[0]+grid_dims[1]:]
		if "Bx" in block_data[idx].block_id:
			Bx=block_data[idx].data
			Bx_stagger=block_data[idx].stagger
		if "By" in block_data[idx].block_id:
			By=block_data[idx].data
			By_stagger=block_data[idx].stagger
		if "Bz" in block_data[idx].block_id:
			Bz=block_data[idx].data
			Bz_stagger=block_data[idx].stagger
		if "Vx" in block_data[idx].block_id:
			Vx=block_data[idx].data
			Vx_stagger=block_data[idx].stagger
		if "Vy" in block_data[idx].block_id:
			Vy=block_data[idx].data
			Vy_stagger=block_data[idx].stagger
		if "Vz" in block_data[idx].block_id:
			Vz=block_data[idx].data
			Vz_stagger=block_data[idx].stagger
		if "Rho" in block_data[idx].block_id:
			Rho=block_data[idx].data
			Rho_stagger=block_data[idx].stagger
		if "Pressure" in block_data[idx].block_id:
			p=block_data[idx].data
			p_stagger=block_data[idx].stagger

	dxb=grid_x[1:]-grid_x[:-1]
	dyb=grid_y[1:]-grid_y[:-1]
	dzb=grid_z[1:]-grid_z[:-1]
	dyb_mg,dzb_mg,dxb_mg=np.meshgrid(dyb,dzb,dxb)
	length=np.minimum(dxb_mg,dyb_mg,dzb_mg)

	xtemp,ytemp,ztemp,Bx=unstagger_reshape(grid_x,grid_y,grid_z,Bx,Bx_stagger)
	xtemp,ytemp,ztemp,By=unstagger_reshape(grid_x,grid_y,grid_z,By,By_stagger)
	xtemp,ytemp,ztemp,Bz=unstagger_reshape(grid_x,grid_y,grid_z,Bz,Bz_stagger)
	w1=Bx[:,:,:-1]**2+By[:,:-1,:]**2+Bz[:-1,:,:]**2

	xtemp,ytemp,ztemp,Rho=unstagger_reshape(grid_x,grid_y,grid_z,Rho,Rho_stagger)
	xtemp,ytemp,ztemp,p=unstagger_reshape(grid_x,grid_y,grid_z,p,p_stagger)
	rho0=np.clip(Rho,2.2250738585072014E-308,1E200)
	cs2=(gamma*p+w1)/rho0

	dt=length/np.sqrt(cs2)
	dt_cfl_idx=np.argmin(dt)
	print("min(Rho) =",min(Rho.flatten()))
	print(" dt(CFL) =",dt.flatten()[dt_cfl_idx])
	print()
	#print(dt.flatten()[dt_cfl_idx],length.flatten()[dt_cfl_idx],cs2.flatten()[dt_cfl_idx],p.flatten()[dt_cfl_idx],w1.flatten()[dt_cfl_idx],rho0.flatten()[dt_cfl_idx])

def calculate_heatdt(block_data,gamma=5/3,dt_parab=1.0E10,kappa_0=1.0,power=2.5):#TODO nonideal
	for idx in range(len(block_data)):
		if "grid" in block_data[idx].block_id:
			grid_dims=block_data[idx].dims
			grid_x=block_data[idx].data[:grid_dims[0]]
			grid_y=block_data[idx].data[grid_dims[0]:grid_dims[0]+grid_dims[1]]
			grid_z=block_data[idx].data[grid_dims[0]+grid_dims[1]:]
		if "Temperature" in block_data[idx].block_id:
			temp=block_data[idx].data
			temp_stagger=block_data[idx].stagger
		if "Rho" in block_data[idx].block_id:
			Rho=block_data[idx].data
			Rho_stagger=block_data[idx].stagger

	dxb=grid_x[1:]-grid_x[:-1]
	dyb=grid_y[1:]-grid_y[:-1]
	dzb=grid_z[1:]-grid_z[:-1]
	dyb_mg,dzb_mg,dxb_mg=np.meshgrid(dyb,dzb,dxb)

	xtemp,ytemp,ztemp,Rho=unstagger_reshape(grid_x,grid_y,grid_z,Rho,Rho_stagger)
	xtemp,ytemp,ztemp,temp=unstagger_reshape(grid_x,grid_y,grid_z,temp,temp_stagger)
	temp2=2.0*kappa_0*temp**power

	dt1=Rho*dxb_mg**2/temp2
	dt2=Rho*dyb_mg**2/temp2
	dt3=Rho*dzb_mg**2/temp2

	dt1_idx=np.argmin(dt1)
	dt2_idx=np.argmin(dt2)
	dt3_idx=np.argmin(dt3)
	print(dt1.flatten()[dt1_idx],dt2.flatten()[dt2_idx],dt3.flatten()[dt3_idx])
	print(dzb_mg.flatten()[dt1_idx],dzb_mg.flatten()[dt2_idx],dzb_mg.flatten()[dt3_idx])

def Trilinear_interpolation(x,y,z,x0,x1,y0,y1,z0,z1,f000,f001,f010,f011,f100,f101,f110,f111):
	"""
	Must be a cuboid
	f000=f(x0,y0,z0)
	f100=f(x1,y0,z0) etc
	"""
	x_shift=x-x0
	y_shift=y-y0
	z_shift=z-z0
	delta_x=x1-x0
	delta_y=y1-y0
	delta_z=z1-z0
	a1=(f100-f000)/delta_x
	a2=(f010-f000)/delta_y
	a3=(f001-f000)/delta_z
	a4=(f110-f100-f010+f000)/delta_y/delta_x
	a5=(f110-f100-f001+f000)/delta_z/delta_x
	a6=(f011-f001-f010+f000)/delta_z/delta_y
	a7=(f111-f110-f101-f011+f001+f010+f100-f000)/delta_x/delta_y/delta_z
	return f000+a1*x_shift+a2*y_shift+a3*z_shift+a4*x_shift*y_shift+a5*x_shift*z_shift+a6*y_shift*z_shift+a7*x_shift*y_shift*z_shift

def get_grid_index(target_pos,grid,last_index):
	last_index=min(max(last_index,1),len(grid)-3)
	if grid[last_index]<=target_pos and grid[last_index+1]>=target_pos:
		return last_index
	elif grid[last_index]>target_pos:
		idx=last_index-1
		while idx>0 and grid[idx]>target_pos:
			idx-=1
		return idx
	else:
		idx=last_index+1
		while idx<len(grid)-2 and grid[idx+1]<target_pos:
			idx+=1
		return idx

#Has all components of B in same grid, B[i,j,k,0]=Bx etc; pos_in has 3 coordinates of start point
def trace_fieldline(pos_in,X,Y,Z,B,max_steps=10000,dt=0.01,periodic_X=False,periodic_Y=False,periodic_Z=False):
	r_0=np.array(pos_in)
	delta_X=X[-1]-X[0]
	delta_Y=Y[-1]-Y[0]
	delta_Z=Z[-1]-Z[0]

	idx_x=get_grid_index(r_0[0],X,0)
	idx_y=get_grid_index(r_0[1],Y,0)
	idx_z=get_grid_index(r_0[2],Z,0)

	n_forward=1
	r_forward=np.zeros((max_steps,3))
	r_forward[0,:]=r_0
	in_bounds=True
	while n_forward<max_steps and in_bounds:
		idx_x=get_grid_index(r_0[0],X,idx_x)
		idx_y=get_grid_index(r_0[1],Y,idx_y)
		idx_z=get_grid_index(r_0[2],Z,idx_z)
		B_1=Trilinear_interpolation(r_0[0],r_0[1],r_0[2],X[idx_x],X[idx_x+1],Y[idx_y],Y[idx_y+1],Z[idx_z],Z[idx_z+1],B[idx_z,idx_y,idx_x,:], B[idx_z+1,idx_y,idx_x,:], B[idx_z,idx_y+1,idx_x,:], B[idx_z+1,idx_y+1,idx_x,:], B[idx_z,idx_y,idx_x+1,:], B[idx_z+1,idx_y,idx_x+1,:], B[idx_z,idx_y+1,idx_x+1,:], B[idx_z+1,idx_y+1,idx_x+1,:])
		k_1=dt*B_1/np.linalg.norm(B_1)

		r_1=r_0+k_1*0.5
		idx_x=get_grid_index(r_1[0],X,idx_x)
		idx_y=get_grid_index(r_1[1],Y,idx_y)
		idx_z=get_grid_index(r_1[2],Z,idx_z)
		B_2=Trilinear_interpolation(r_1[0],r_1[1],r_1[2],X[idx_x],X[idx_x+1],Y[idx_y],Y[idx_y+1],Z[idx_z],Z[idx_z+1],B[idx_z,idx_y,idx_x,:], B[idx_z+1,idx_y,idx_x,:], B[idx_z,idx_y+1,idx_x,:], B[idx_z+1,idx_y+1,idx_x,:], B[idx_z,idx_y,idx_x+1,:], B[idx_z+1,idx_y,idx_x+1,:], B[idx_z,idx_y+1,idx_x+1,:], B[idx_z+1,idx_y+1,idx_x+1,:])
		k_2=dt*B_2/np.linalg.norm(B_2)

		r_2=r_0+k_2*0.5
		idx_x=get_grid_index(r_2[0],X,idx_x)
		idx_y=get_grid_index(r_2[1],Y,idx_y)
		idx_z=get_grid_index(r_2[2],Z,idx_z)
		B_3=Trilinear_interpolation(r_2[0],r_2[1],r_2[2],X[idx_x],X[idx_x+1],Y[idx_y],Y[idx_y+1],Z[idx_z],Z[idx_z+1],B[idx_z,idx_y,idx_x,:], B[idx_z+1,idx_y,idx_x,:], B[idx_z,idx_y+1,idx_x,:], B[idx_z+1,idx_y+1,idx_x,:], B[idx_z,idx_y,idx_x+1,:], B[idx_z+1,idx_y,idx_x+1,:], B[idx_z,idx_y+1,idx_x+1,:], B[idx_z+1,idx_y+1,idx_x+1,:])
		k_3=dt*B_3/np.linalg.norm(B_3)

		r_3=r_0+k_3
		idx_x=get_grid_index(r_3[0],X,idx_x)
		idx_y=get_grid_index(r_3[1],Y,idx_y)
		idx_z=get_grid_index(r_3[2],Z,idx_z)
		B_4=Trilinear_interpolation(r_3[0],r_3[1],r_3[2],X[idx_x],X[idx_x+1],Y[idx_y],Y[idx_y+1],Z[idx_z],Z[idx_z+1],B[idx_z,idx_y,idx_x,:], B[idx_z+1,idx_y,idx_x,:], B[idx_z,idx_y+1,idx_x,:], B[idx_z+1,idx_y+1,idx_x,:], B[idx_z,idx_y,idx_x+1,:], B[idx_z+1,idx_y,idx_x+1,:], B[idx_z,idx_y+1,idx_x+1,:], B[idx_z+1,idx_y+1,idx_x+1,:])
		k_4=dt*B_4/np.linalg.norm(B_4)

		r_0=r_0+(k_1+2.0*k_2+2.0*k_3+k_4)/6.0
		r_forward[n_forward,:]=r_0
		n_forward+=1
		if periodic_X:
			r_0[0]=(r_0[0]-X[0]) % delta_X + X[0]
		elif r_0[0]<X[0] or r_0[0]>X[-1]:
			in_bounds=False
		if periodic_Y:
			r_0[1]=(r_0[1]-Y[0]) % delta_Y + Y[0]
		elif r_0[1]<Y[0] or r_0[1]>Y[-1]:
			in_bounds=False
		if periodic_Z:
			r_0[2]=(r_0[2]-Z[0]) % delta_Z + Z[0]
		elif r_0[2]<Z[0] or r_0[2]>Z[-1]:
			in_bounds=False
	r_forward=r_forward[:n_forward]

	r_0=np.array(pos_in)
	n_back=0
	r_back=np.zeros((max_steps-1,3))
	in_bounds=True
	while n_back<max_steps-1 and r_0[0]>=X[0] and r_0[0]<=X[-1] and r_0[1]>=Y[0] and r_0[1]<=Y[-1] and r_0[2]>=Z[0] and r_0[2]<=Z[-1]:
		idx_x=get_grid_index(r_0[0],X,idx_x)
		idx_y=get_grid_index(r_0[1],Y,idx_y)
		idx_z=get_grid_index(r_0[2],Z,idx_z)
		B_1=Trilinear_interpolation(r_0[0],r_0[1],r_0[2],X[idx_x],X[idx_x+1],Y[idx_y],Y[idx_y+1],Z[idx_z],Z[idx_z+1],B[idx_z,idx_y,idx_x,:], B[idx_z+1,idx_y,idx_x,:], B[idx_z,idx_y+1,idx_x,:], B[idx_z+1,idx_y+1,idx_x,:], B[idx_z,idx_y,idx_x+1,:], B[idx_z+1,idx_y,idx_x+1,:], B[idx_z,idx_y+1,idx_x+1,:], B[idx_z+1,idx_y+1,idx_x+1,:])
		k_1=-dt*B_1/np.linalg.norm(B_1)

		r_1=r_0+k_1*0.5
		idx_x=get_grid_index(r_1[0],X,idx_x)
		idx_y=get_grid_index(r_1[1],Y,idx_y)
		idx_z=get_grid_index(r_1[2],Z,idx_z)
		B_2=Trilinear_interpolation(r_1[0],r_1[1],r_1[2],X[idx_x],X[idx_x+1],Y[idx_y],Y[idx_y+1],Z[idx_z],Z[idx_z+1],B[idx_z,idx_y,idx_x,:], B[idx_z+1,idx_y,idx_x,:], B[idx_z,idx_y+1,idx_x,:], B[idx_z+1,idx_y+1,idx_x,:], B[idx_z,idx_y,idx_x+1,:], B[idx_z+1,idx_y,idx_x+1,:], B[idx_z,idx_y+1,idx_x+1,:], B[idx_z+1,idx_y+1,idx_x+1,:])
		k_2=-dt*B_2/np.linalg.norm(B_2)

		r_2=r_0+k_2*0.5
		idx_x=get_grid_index(r_2[0],X,idx_x)
		idx_y=get_grid_index(r_2[1],Y,idx_y)
		idx_z=get_grid_index(r_2[2],Z,idx_z)
		B_3=Trilinear_interpolation(r_2[0],r_2[1],r_2[2],X[idx_x],X[idx_x+1],Y[idx_y],Y[idx_y+1],Z[idx_z],Z[idx_z+1],B[idx_z,idx_y,idx_x,:], B[idx_z+1,idx_y,idx_x,:], B[idx_z,idx_y+1,idx_x,:], B[idx_z+1,idx_y+1,idx_x,:], B[idx_z,idx_y,idx_x+1,:], B[idx_z+1,idx_y,idx_x+1,:], B[idx_z,idx_y+1,idx_x+1,:], B[idx_z+1,idx_y+1,idx_x+1,:])
		k_3=-dt*B_3/np.linalg.norm(B_3)

		r_3=r_0+k_3
		idx_x=get_grid_index(r_3[0],X,idx_x)
		idx_y=get_grid_index(r_3[1],Y,idx_y)
		idx_z=get_grid_index(r_3[2],Z,idx_z)
		B_4=Trilinear_interpolation(r_3[0],r_3[1],r_3[2],X[idx_x],X[idx_x+1],Y[idx_y],Y[idx_y+1],Z[idx_z],Z[idx_z+1],B[idx_z,idx_y,idx_x,:], B[idx_z+1,idx_y,idx_x,:], B[idx_z,idx_y+1,idx_x,:], B[idx_z+1,idx_y+1,idx_x,:], B[idx_z,idx_y,idx_x+1,:], B[idx_z+1,idx_y,idx_x+1,:], B[idx_z,idx_y+1,idx_x+1,:], B[idx_z+1,idx_y+1,idx_x+1,:])
		k_4=-dt*B_4/np.linalg.norm(B_4)

		r_0=r_0+(k_1+2.0*k_2+2.0*k_3+k_4)/6.0
		r_back[n_back,:]=r_0
		n_back+=1
		if periodic_X:
			r_0[0]=(r_0[0]-X[0]) % delta_X + X[0]
		elif r_0[0]<X[0] or r_0[0]>X[-1]:
			in_bounds=False
		if periodic_Y:
			r_0[1]=(r_0[1]-Y[0]) % delta_Y + Y[0]
		elif r_0[1]<Y[0] or r_0[1]>Y[-1]:
			in_bounds=False
		if periodic_Z:
			r_0[2]=(r_0[2]-Z[0]) % delta_Z + Z[0]
		elif r_0[2]<Z[0] or r_0[2]>Z[-1]:
			in_bounds=False
	r_back=r_back[:n_back]

	return np.concatenate((r_back[::-1],r_forward))

def split_fieldline(fieldline,X,Y,Z):
	delta_X=X[-1]-X[0]
	delta_Y=Y[-1]-Y[0]
	delta_Z=Z[-1]-Z[0]
	fieldine_list=[]

	for idx in range(len(fieldline[:,0])):
		...

	return fieldine_list

def shift_fieldline(fieldline,X,Y,Z):
	fieldline_new=np.copy(fieldline)
	delta_X=X[-1]-X[0]
	delta_Y=Y[-1]-Y[0]
	delta_Z=Z[-1]-Z[0]

	shift_X=0.0
	shift_Y=0.0
	shift_Z=0.0
	for idx in range(1,len(fieldline_new[:,0])):
		if abs(fieldline_new[idx,0]+shift_X-fieldline_new[idx-1,0])>0.9*delta_X:
			shift_X-=np.sign(fieldline_new[idx,0]+shift_X-fieldline_new[idx-1,0])*delta_X
		fieldline_new[idx,0]+=shift_X
		if abs(fieldline_new[idx,1]+shift_Y-fieldline_new[idx-1,1])>0.9*delta_Y:
			shift_Y-=np.sign(fieldline_new[idx,1]+shift_Y-fieldline_new[idx-1,1])*delta_Y
		fieldline_new[idx,1]+=shift_Y
		if abs(fieldline_new[idx,2]+shift_Z-fieldline_new[idx-1,2])>0.9*delta_Z:
			shift_Z-=np.sign(fieldline_new[idx,2]+shift_Z-fieldline_new[idx-1,2])*delta_Z
		fieldline_new[idx,2]+=shift_Z

	return fieldline_new

def parse_QSL_Larebinfile(filename):
	"""
	Load in QSL output file
	Note - uses special version of QSLsquasher adapted to output binary data
	"""
	filesize=os.path.getsize(filename)
	num_points=filesize//(28)
	infile=open(filename,"rb")
	x=np.zeros((num_points))
	y=np.zeros((num_points))
	z=np.zeros((num_points))
	Q=np.zeros((num_points))
	for idx in range(num_points):
		data=struct.unpack('dddf',infile.read(28))
		z[idx]=data[2]
		y[idx]=data[1]
		x[idx]=data[0]
		Q[idx]=data[3]
	infile.close()

	y_dim=1
	idx=1
	while idx<num_points:
		if x[idx]==x[0]:
			y_dim+=1
			idx+=1
		else:
			idx=num_points
	x_dim=num_points//y_dim

	z_actual=z[0]
	y=y.reshape((x_dim,y_dim))
	x=x.reshape((x_dim,y_dim))
	Q=Q.reshape((x_dim,y_dim))
	return z_actual,y,x,Q

def parse_QSL_LarebinfileExpansion(filename):
	"""
	Load in QSL output file with expansion factor calculation
	Note - uses special version of QSLsquasher adapted to output binary data
	"""
	filesize=os.path.getsize(filename)
	num_points=filesize//(44)
	infile=open(filename,"rb")
	z=np.zeros((num_points))
	y=np.zeros((num_points))
	x=np.zeros((num_points))
	Q=np.zeros((num_points))
	Bs=np.zeros((num_points))
	Be=np.zeros((num_points))
	for idx in range(num_points):
		data=struct.unpack('=dddfdd',infile.read(44))
		z[idx]=data[2]
		y[idx]=data[1]
		x[idx]=data[0]
		Q[idx]=data[3]
		Bs[idx]=data[4]
		Be[idx]=data[5]
	infile.close()

	y_dim=1
	idx=1
	while idx<num_points:
		if x[idx]==x[0]:
			y_dim+=1
			idx+=1
		else:
			idx=num_points
	x_dim=num_points//y_dim

	z_actual=z[0]
	y=y.reshape((x_dim,y_dim))
	x=x.reshape((x_dim,y_dim))
	Q=Q.reshape((x_dim,y_dim))
	Bs=Bs.reshape((x_dim,y_dim))
	Be=Be.reshape((x_dim,y_dim))
	return z_actual,y,x,Q,Bs,Be

#####
#Reading parallel surface velocity files
def read_surface_coordinates(filename):
	data_type="d"
	data_size=8

	filesize=os.path.getsize(filename)
	infile=open(filename,"rb")
	raw_data=infile.read(filesize)
	infile.close()
	return np.array(struct.unpack(data_type*(filesize//data_size),raw_data))

#timestep_label must be correct string
def get_split_surface_velocity_coords(data_dir,timestep_label):
	x_files = [f for f in os.listdir(data_dir) if os.path.isfile(os.path.join(data_dir, f)) and 'Surfx_' in f and f.count('_')==2]
	y_files = [f for f in os.listdir(data_dir) if os.path.isfile(os.path.join(data_dir, f)) and 'Surfy_' in f and f.count('_')==2]

	target_x_files=[]
	for idx in range(len(x_files)):
		if x_files[idx].replace('Surfx_','').replace('.bin','').split('_')[1]==timestep_label:
			target_x_files.append(x_files[idx])
	target_x_files.sort()

	target_y_files=[]
	for idx in range(len(y_files)):
		if y_files[idx].replace('Surfy_','').replace('.bin','').split('_')[1]==timestep_label:
			target_y_files.append(y_files[idx])
	target_y_files.sort()

	if len(target_x_files)!=len(target_y_files):
		print("Mismatch reading coordinate files")

	x_coords=[read_surface_coordinates(os.path.join(data_dir,fname)) for fname in target_x_files]
	x_unique=np.unique(np.concatenate(x_coords))
	y_coords=[read_surface_coordinates(os.path.join(data_dir,fname)) for fname in target_y_files]
	y_unique=np.unique(np.concatenate(y_coords))

	processors=[]
	x_indices=[]
	y_indices=[]
	for idx in range(len(target_x_files)):
		processors.append(target_x_files[idx].replace('Surfx_','').replace('.bin','').split('_')[0])
		x_indices.append([np.where(x_coords[idx][0]==x_unique)[0][0],np.where(x_coords[idx][-1]==x_unique)[0][0]])
		y_indices.append([np.where(y_coords[idx][0]==y_unique)[0][0],np.where(y_coords[idx][-1]==y_unique)[0][0]])

	return x_unique,y_unique,processors,np.array(x_indices,dtype=np.int32),np.array(y_indices,dtype=np.int32)

#processors and timestep_label must be correct strings
#dimension must be "x" or "y"
def get_split_surface_velocity(data_dir,dimension,processors,timestep_label,full_dims,x_indices,y_indices,ignore_ghost_cells=True,ng=2,target_steps=None):
	data_type="d"
	data_size=8

	chunk_size=(y_indices[0,1]-y_indices[0,0]+1)*(x_indices[0,1]-x_indices[0,0]+1)*data_size
	filename=os.path.join(data_dir, 'SurfV'+dimension+'_'+processors[0]+"_"+timestep_label+".bin")
	filesize=os.path.getsize(filename)
	num_steps=filesize//chunk_size
	for idx in range(1,len(processors)):
		chunk_size=(y_indices[idx,1]-y_indices[idx,0]+1)*(x_indices[idx,1]-x_indices[idx,0]+1)*data_size
		filename=os.path.join(data_dir, 'SurfV'+dimension+'_'+processors[idx]+"_"+timestep_label+".bin")
		filesize=os.path.getsize(filename)
		num_steps=min(num_steps,filesize//chunk_size)

	if target_steps==None:
		num_outputs=num_steps
	elif hasattr(target_steps, '__iter__'):
		target_steps.sort()
		num_outputs=0
		for quant in target_steps:
			if quant<=num_steps: num_outputs+=1
	else:
		target_steps=list(target_steps)
		num_outputs=1

	if ignore_ghost_cells:
		velocity=np.zeros((full_dims[1]-2*ng,full_dims[0]-2*ng,num_outputs))
	else:
		velocity=np.zeros((full_dims[1],full_dims[0],num_outputs))

	for idx in range(len(processors)):
		chunk_size=(y_indices[idx,1]-y_indices[idx,0]+1)*(x_indices[idx,1]-x_indices[idx,0]+1)*data_size
		filename=os.path.join(data_dir, 'SurfV'+dimension+'_'+processors[idx]+"_"+timestep_label+".bin")
		infile=open(filename,"rb")
		if num_outputs==num_steps:
			for idx_s in range(num_steps):
				raw_data=infile.read(chunk_size)
				raw_array=np.array(struct.unpack(data_type*(chunk_size//data_size),raw_data)).reshape((y_indices[idx,1]-y_indices[idx,0]+1,x_indices[idx,1]-x_indices[idx,0]+1))
				if ignore_ghost_cells:
					velocity[y_indices[idx,0]:y_indices[idx,1]+1-2*ng,x_indices[idx,0]:x_indices[idx,1]+1-2*ng,idx_s]=raw_array[ng:-ng,ng:-ng]
				else:
					velocity[y_indices[idx,0]:y_indices[idx,1]+1,x_indices[idx,0]:x_indices[idx,1]+1,idx_s]=raw_array
		elif num_outputs==1:
			infile.seek(chunk_size*target_steps[0],0)
			raw_data=infile.read(chunk_size)
			raw_array=np.array(struct.unpack(data_type*(chunk_size//data_size),raw_data)).reshape((y_indices[idx,1]-y_indices[idx,0]+1,x_indices[idx,1]-x_indices[idx,0]+1))
			if ignore_ghost_cells:
				velocity[y_indices[idx,0]:y_indices[idx,1]+1-2*ng,x_indices[idx,0]:x_indices[idx,1]+1-2*ng,0]=raw_array[ng:-ng,ng:-ng]
			else:
				velocity[y_indices[idx,0]:y_indices[idx,1]+1,x_indices[idx,0]:x_indices[idx,1]+1,0]=raw_array
		else:
			for idx_s in range(len(target_steps)):
				infile.seek(chunk_size*target_steps[idx_s],0)
				raw_data=infile.read(chunk_size)
				raw_array=np.array(struct.unpack(data_type*(chunk_size//data_size),raw_data)).reshape((y_indices[idx,1]-y_indices[idx,0]+1,x_indices[idx,1]-x_indices[idx,0]+1))
				if ignore_ghost_cells:
					velocity[y_indices[idx,0]:y_indices[idx,1]+1-2*ng,x_indices[idx,0]:x_indices[idx,1]+1-2*ng,idx_s]=raw_array[ng:-ng,ng:-ng]
				else:
					velocity[y_indices[idx,0]:y_indices[idx,1]+1,x_indices[idx,0]:x_indices[idx,1]+1,idx_s]=raw_array
		infile.close()

	return velocity

def merge_split_surface_velocity_coords_to_file(data_dir,ignore_ghost_cells=True,ng=2):
	coordinate_timesteps,vx_timesteps,vy_timesteps,vz_timesteps=list_split_surface_velocity_files(data_dir)
	for idx in range(len(coordinate_timesteps)):
		x_grid,y_grid,processors,x_indices,y_indices=get_split_surface_velocity_coords(data_dir,coordinate_timesteps[idx])
		outfilename=os.path.join(data_dir, 'Surfx.bin')
		outfile=open(outfilename,"wb")
		if ignore_ghost_cells:
			outfile.write(bytes(x_grid[ng:-ng]))
		else:
			outfile.write(bytes(x_grid))
		outfile.close()
		outfilename=os.path.join(data_dir, 'Surfy.bin')
		outfile=open(outfilename,"wb")
		if ignore_ghost_cells:
			outfile.write(bytes(y_grid[ng:-ng]))
		else:
			outfile.write(bytes(y_grid))
		outfile.close()

#processors and timestep_label must be correct strings
#dimension must be "x" or "y"
def merge_split_surface_velocity_to_file(data_dir,dimension,processors,timestep_label,full_dims,x_indices,y_indices,make_new,ignore_ghost_cells=True,ng=2):
	data_type="d"
	data_size=8

	if ignore_ghost_cells:
		velocity=np.zeros((full_dims[1]-2*ng,full_dims[0]-2*ng))
	else:
		velocity=np.zeros((full_dims[1],full_dims[0]))

	if not os.path.exists(os.path.join(data_dir, 'Surft.bin')):
		make_new=True

	infilename=os.path.join(data_dir, 'Surft_'+timestep_label+".bin")
	filesize=os.path.getsize(infilename)
	num_steps=filesize//data_size
	for idx in range(len(processors)):
		chunk_size=(y_indices[idx,1]-y_indices[idx,0]+1)*(x_indices[idx,1]-x_indices[idx,0]+1)*data_size
		infilename=os.path.join(data_dir, 'SurfV'+dimension+'_'+processors[idx]+"_"+timestep_label+".bin")
		filesize=os.path.getsize(infilename)
		num_steps=min(num_steps,filesize//chunk_size)

	if make_new:
		idx_start=0
		outfilename=os.path.join(data_dir, 'SurfV'+dimension+".bin")
		outfile1=open(outfilename,"wb")
		outfile2=open(os.path.join(data_dir, 'Surft.bin'),"wb")
	else: #append to existing files
		outfile2=open(os.path.join(data_dir, 'Surft.bin'),"rb")
		outfile2.seek(-data_size,2)
		final_time=struct.unpack(data_type,outfile2.read(data_size))
		outfile2.close()

		infile2=open(os.path.join(data_dir, 'Surft_'+timestep_label+'.bin'),"rb")
		for idx_start in range(num_steps):
			if struct.unpack(data_type,infile2.read(data_size))>final_time:
				break
		infile2.close()
		outfilename=os.path.join(data_dir, 'SurfV'+dimension+".bin")
		outfile1=open(outfilename,"ab")
		outfile2=open(os.path.join(data_dir, 'Surft.bin'),"ab")

	infile2=open(os.path.join(data_dir, 'Surft_'+timestep_label+'.bin'),"rb")
	infile2.seek(data_size*idx_start,0)
	for idx_s in range(idx_start,num_steps):
		outfile2.write(infile2.read(data_size))
		for idx in range(len(processors)):
			chunk_size=(y_indices[idx,1]-y_indices[idx,0]+1)*(x_indices[idx,1]-x_indices[idx,0]+1)*data_size
			infilename=os.path.join(data_dir, 'SurfV'+dimension+'_'+processors[idx]+"_"+timestep_label+".bin")
			infile1=open(infilename,"rb")
			infile1.seek(chunk_size*idx_s,0)

			raw_data=infile1.read(chunk_size)
			raw_array=np.array(struct.unpack(data_type*(chunk_size//data_size),raw_data)).reshape((y_indices[idx,1]-y_indices[idx,0]+1,x_indices[idx,1]-x_indices[idx,0]+1))
			if ignore_ghost_cells:
				velocity[y_indices[idx,0]:y_indices[idx,1]+1-2*ng,x_indices[idx,0]:x_indices[idx,1]+1-2*ng]=raw_array[ng:-ng,ng:-ng]
			else:
				velocity[y_indices[idx,0]:y_indices[idx,1]+1,x_indices[idx,0]:x_indices[idx,1]+1]=raw_array
			infile1.close()

		for idx_y in range(len(velocity[:,0])):
			outfile1.write(bytes(velocity[idx_y,:]))
	infile2.close()
	outfile1.close()
	outfile2.close()

def list_split_surface_velocity_files(data_dir):
	x_files = [f for f in os.listdir(data_dir) if os.path.isfile(os.path.join(data_dir, f)) and 'Surfx_' in f and f.count('_')==2]
	y_files = [f for f in os.listdir(data_dir) if os.path.isfile(os.path.join(data_dir, f)) and 'Surfy_' in f and f.count('_')==2]

	coordinate_timesteps_x=[]
	for idx in range(len(x_files)):
		if x_files[idx].replace('Surfx_','').replace('.bin','').split('_')[1] not in coordinate_timesteps_x:
			coordinate_timesteps_x.append(x_files[idx].replace('Surfx_','').replace('.bin','').split('_')[1])

	y_filelabels=np.zeros((len(y_files),2),dtype=np.int32)
	coordinate_timesteps_y=[]
	for idx in range(len(y_files)):
		if y_files[idx].replace('Surfy_','').replace('.bin','').split('_')[1] not in coordinate_timesteps_y:
			coordinate_timesteps_y.append(y_files[idx].replace('Surfy_','').replace('.bin','').split('_')[1])
		y_filelabels[idx,:]=y_files[idx].replace('Surfy_','').replace('.bin','').split('_')
	coordinate_timesteps=[]
	for idx in range(len(coordinate_timesteps_x)):
		if coordinate_timesteps_x[idx] in coordinate_timesteps_y:
			coordinate_timesteps.append(coordinate_timesteps_x[idx])

	vx_files = [f for f in os.listdir(data_dir) if os.path.isfile(os.path.join(data_dir, f)) and 'SurfVx_' in f and f.count('_')==2]
	vx_timesteps=[]
	for idx in range(len(vx_files)):
		if vx_files[idx].replace('SurfVx_','').replace('.bin','').split('_')[1] not in vx_timesteps:
			vx_timesteps.append(vx_files[idx].replace('SurfVx_','').replace('.bin','').split('_')[1])

	vy_files = [f for f in os.listdir(data_dir) if os.path.isfile(os.path.join(data_dir, f)) and 'SurfVy_' in f and f.count('_')==2]
	vy_timesteps=[]
	for idx in range(len(vy_files)):
		if vy_files[idx].replace('SurfVy_','').replace('.bin','').split('_')[1] not in vy_timesteps:
			vy_timesteps.append(vy_files[idx].replace('SurfVy_','').replace('.bin','').split('_')[1])

	vz_files = [f for f in os.listdir(data_dir) if os.path.isfile(os.path.join(data_dir, f)) and 'SurfVz_' in f and f.count('_')==2]
	vz_timesteps=[]
	for idx in range(len(vz_files)):
		if vz_files[idx].replace('SurfVz_','').replace('.bin','').split('_')[1] not in vz_timesteps:
			vz_timesteps.append(vz_files[idx].replace('SurfVz_','').replace('.bin','').split('_')[1])

	coordinate_timesteps.sort()
	vx_timesteps.sort()
	vy_timesteps.sort()
	vz_timesteps.sort()

	return coordinate_timesteps,vx_timesteps,vy_timesteps,vz_timesteps

def write_surface_cage_file(x,y,filename):
	np_data_type="float64"

	x_out=np.array(x,dtype=np_data_type)
	y_out=np.array(y,dtype=np_data_type)
	outfile=open(filename,"wb")
	for idx in range(min(len(x_out),len(y_out))):
		outfile.write(x_out[idx])
		outfile.write(y_out[idx])
	outfile.close()

def read_surface_cage_file(filename):
	data_type="d"
	data_size=8

	filesize=os.path.getsize(filename)
	num_g=filesize//(data_size*2)
	x=np.zeros((num_g))
	y=np.zeros((num_g))
	infile=open(filename,"rb")
	for idx in range(num_g):
		x[idx]=struct.unpack(data_type, infile.read(data_size))[0]
		y[idx]=struct.unpack(data_type, infile.read(data_size))[0]
	infile.close()
	return x,y

def save_connectivity_map(y,x,connectivity_map,connectivity_filename):
	"""
	Store connectivity map
	"""
	len_x=len(connectivity_map[:,0])
	len_y=len(connectivity_map[0,:])
	outfile=open(connectivity_filename,'wb')
	outfile.write(struct.pack('i',len_x))
	outfile.write(struct.pack('i',len_y))
	for idx_x in range(len_x):
		outfile.write(struct.pack('f',x[idx_x,0]))
	for idx_y in range(len_y):
		outfile.write(struct.pack('f',y[0,idx_y]))
	for idx_x in range(len_x):
		outfile.write(bytes(connectivity_map[idx_x,:]))
	outfile.close()

def load_connectivity_map(connectivity_filename):
	infile=open(connectivity_filename,"rb")
	len_x=struct.unpack('i',infile.read(4))[0]
	len_y=struct.unpack('i',infile.read(4))[0]
	x_grid=np.zeros((len_x))
	y_grid=np.zeros((len_y))
	connectivity_map=np.zeros((len_x,len_y),dtype=np.int32)
	for idx_x in range(len_x):
		x_grid[idx_x]=struct.unpack('f',infile.read(4))[0]
	for idx_y in range(len_y):
		y_grid[idx_y]=struct.unpack('f',infile.read(4))[0]
	y_grid,x_grid=np.meshgrid(y_grid,x_grid)
	for idx_x in range(len_x):
		connectivity_map[idx_x,:]=np.array(struct.unpack('i'*len_y,infile.read(4*len_y)))
	infile.close()
	return y_grid,x_grid,connectivity_map


import time

import pytest
import sqlite3
from lyricfetch import Song

from conftest import Nothing

def test_config(database):
    assert database._connection
    table_query = 'select name from sqlite_master where type="table"'
    assert database._execute(table_query) == {'name': 'log'}

def test_execute(database):
    values = ('a', 'b', 'c')
    insert = 'insert into log (chat_id, artist, title) values (?, ?, ?)'
    ret = database._execute(insert, values)
    assert ret is None

    select = """
    select chat_id, artist, title from log
    where chat_id=? and artist=? and title=?
    """
    ret = database._execute(select, values)
    assert tuple(ret.values()) == values

def test_execute_connection_closed(database):
    """
    Test that the database will try to connect again if there are any errors.
    """
    values = ('a', 'b', 'c')
    insert = 'insert into log (chat_id, artist, title) values (?, ?, ?)'
    database._execute(insert, values)

    database.close()

    select = """
    select chat_id, artist, title from log
    where chat_id=? and artist=? and title=?
    """
    ret = database._execute(select, values)
    assert tuple(ret.values()) == values

def test_execute_connection_retries(database):
    """
    Test that the database will try to connect again if there are any errors.
    """
    select = 'select * from log'
    database._retries = 0
    with pytest.raises(sqlite3.Error):
        database._execute(select)

    database._retries = 1
    database.close()
    with pytest.raises(sqlite3.Error):
        database._execute(select)

def test_log_result(database):
    chat_id = 'chat_id'
    song = Song('lucis absentia', 'gehenna gate')
    source = Nothing()
    source.__name__ = 'source'
    result = Nothing()
    result.song = song
    result.source = source

    database.log_result(chat_id, result)
    query = database._execute('select chat_id, source, artist, title from log')
    assert query == dict(
        chat_id=chat_id, source='source', artist=song.artist, title=song.title
    )

    source.__name__ = 'new source'
    database.log_result(chat_id, result)
    query = database._execute('select chat_id, source, artist, title from log')
    assert query == dict(
        chat_id=chat_id,
        source='new source',
        artist=song.artist,
        title=song.title,
    )

def test_get_last_res(database):
    chat_id = 'id'
    artist = 'testament'
    title = 'a day of reckoning'
    source = 'source'
    album = 'the new order'
    now = int(time.time())
    insert = """
    insert into log
        (chat_id, artist, title, source, album, date)
        values (?, ?, ?, ?, ?, ?)
    """

    # Insert a record and retrieve it
    database._execute(insert, (chat_id, artist, title, source, album, now))
    database._connection.commit()
    assert database.get_last_res(chat_id) == dict(
        source=source, artist=artist, title=title, album=album
    )

    # Update the chat id with a newer entry
    artist = 'new artist'
    now += 1
    database._execute(insert, (chat_id, artist, title, source, album, now))
    assert database.get_last_res(chat_id) == dict(
        source=source, artist=artist, title=title, album=album
    )

    # Updating with an older entry should return the new one again
    artist = 'old artist'
    now -= 2
    database._execute(insert, (chat_id, artist, title, source, album, now))
    assert database.get_last_res(chat_id) == dict(
        source=source, artist='new artist', title=title, album=album
    )

@pytest.mark.parametrize('param, expect', [(1, '1'), ("'hello'", "''hello''")])
def test_sanitize(database, param, expect):
    assert database.sanitize(param) == expect


from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QMessageBox
from PyQt5.QtWidgets import QMainWindow, QPushButton, QToolTip, QLCDNumber
from PyQt5.QtWidgets import QDesktopWidget, QAction, QLineEdit, QCheckBox
from PyQt5.QtWidgets import QProgressBar, QComboBox, QFontDialog, QStyleFactory
from PyQt5.QtGui import QIcon, QPixmap, QFont
from PyQt5.QtCore import QCoreApplication, pyqtSlot, QTime, QTimer, Qt
import matplotlib.pyplot as plt
import sys

class Window(QMainWindow):
    def __init__(self, title, startPoint, width, height):
        super(Window, self).__init__()
        QToolTip.setFont(QFont("Decorative", 10, QFont.Bold))
        self.title = title
        self.left, self.top = startPoint
        self.width = width
        self.height = height

    def initUI(self, maxWidth, maxHeight):
        self.setWindowTitle("simple app")
        self.setToolTip("main window")
        self.setGeometry(self.left, self.top, self.width, self.height)
        self.setMinimumHeight(self.height)
        self.setMaximumHeight(maxHeight)
        self.setMinimumWidth(self.width)
        self.setMaximumWidth(maxWidth)

    def initAppIcon(self, iconPath):
        appIcon = QIcon(iconPath)
        self.setWindowIcon(appIcon)

    def initMainMenu(self):
        self.statusBar()
        self.mainMenu = self.menuBar()
        self.newMenu = self.mainMenu.addMenu("new")
        self.openMenu = self.mainMenu.addMenu("open")
        self.shareMenu = self.mainMenu.addMenu("share")
        # ---------------------------------------------
        exitBtn = QAction("Exit", self)
        exitBtn.setShortcut("ctrl+Q")
        exitBtn.triggered.connect(self.close)
        self.shareMenu.addAction(exitBtn)

    def initToolBar(self):
        self.toolBar = self.addToolBar("funcy-tools")
        self.func1 = QAction(QIcon("gallery/pyLogo.png"), "func1", self)
        self.func1.triggered.connect(self.close)
        self.toolBar.addAction(self.func1)

    def initIcon(self, iconPath, iconPos):
        icon = QIcon(iconPath)
        label = QLabel("label1", self)
        label.move(*iconPos)
        # icon modes {Active, Disabled, Selected}
        pixMap = icon.pixmap(50, QIcon.Active, QIcon.On)
        label.setPixmap(pixMap)
        label.setToolTip("Python 3 Logo")

    def shutdown(self, buttonPos):
        btn = QPushButton("Quit", self)
        btn.move(*buttonPos)
        btn.clicked.connect(self.quitAssert)
        btn.setToolTip("exit the application")

    def about(self, buttonPos):
        about = QPushButton("about", self)
        about.move(*buttonPos)
        about.setToolTip("about the program")
        about.clicked.connect(self.aboutInfo)

    def aboutInfo(self):
        info = QMessageBox.about(self, "About Me", "Developer: Loay Wael\n test application")

    def textField(self, boxPos):
        self.txtBox = QLineEdit(self)
        self.txtBox.move(*boxPos)
        self.txtBox.resize(300, 25)

    def quitAssert(self):
        choice = QMessageBox.question(self, "Shutdown Confirmation",
                                      "do you want to shutdown the application?",
                                      QMessageBox.Yes | QMessageBox.No)
        if choice == QMessageBox.Yes:
            QCoreApplication.instance().quit()
        else:
            pass

    def centerWindow(self):
        qRect = self.frameGeometry()
        centerPoint = QDesktopWidget().availableGeometry().center()
        qRect.moveCenter(centerPoint)
        self.move(qRect.topLeft())

    def refresh(self, buttonPos):
        btn = QPushButton("Refresh", self)
        btn.move(*buttonPos)
        btn.resize(300, 25)
        btn.clicked.connect(self.onClick)
        btn.setToolTip("refresh the application")

    def initComboBox(self, comPos):
        self.comBox = QComboBox(self)
        self.comBox.addItem("windows-xp")
        self.comBox.addItem("windows-Vista")
        self.comBox.addItem("windows-7")
        self.comBox.move(*comPos)
        self.comBox.activated[str].connect(self.styleChoice)

    @pyqtSlot()
    def onClick(self):
        print("refresh-button is clicked")
        txtVal = self.txtBox.text()
        print("you typed: ", txtVal)
        self.txtBox.setText("")

    def initCheckBox(self, chkPos):
        self.chkBox = QCheckBox("Enlarege-Window", self)
        self.chkBox.resize(200, 50)
        self.chkBox.move(*chkPos)
        # self.chkBox.toggle()
        self.chkBox.stateChanged.connect(self.enlargeWindow)

    def enlargeWindow(self, state):
        if state == Qt.Checked:
            self.setMaximumSize(800, 600)
            self.setGeometry(100, 100, 800, 600)
        else:
            self.setMaximumSize(400, 300)

    def initProgBar(self):
        self.downBtn = QPushButton("Download", self)
        self.downBtn.move(300, 200)
        self.downBtn.clicked.connect(self.getTicks)
        self.progBar = QProgressBar(self)
        self.progBar.setGeometry(100, 150, 300, 20)

    def changeFont(self):
        self.chgFontBtn = QPushButton("change font", self)
        self.chgFontBtn.move(100, 200)
        self.screen = QLabel("None to view", self)
        self.screen.resize(300, 30)
        self.screen.move(200, 250)
        self.chgFontBtn.clicked.connect(self.fontPicker)

    def fontPicker(self):
        font, valid = QFontDialog.getFont()
        if valid:
            self.screen.setText("empty data")
            self.screen.setFont(font)

    def getTicks(self):
        comp = 0
        while comp < 100:
            comp += 0.00001
            self.progBar.setValue(comp)

    def styleChoice(self, text):
        self.screen.setText(text)
        QApplication.setStyle(QStyleFactory.create(text))

myApp = QApplication(sys.argv)
window = Window("new window", (0, 0), 500, 500)
window.initUI(500, 500)
window.centerWindow()
window.initMainMenu()
window.about((100, 300))
window.initToolBar()
window.initAppIcon("gallery/pyLogo.png")
window.initIcon("gallery/pyLogo.png", (50, 425))
window.shutdown((400, 450))
window.initProgBar()
window.initComboBox((300, 300))
window.refresh((100, 50))
window.textField((100, 100))
window.initCheckBox((150, 350))
window.changeFont()

window.show()
# img = plt.imread("gallery/pyLogo.png")
# plt.imshow(img)
# plt.show()
myApp.exec_()
sys.exit(0)


import bpy

bl_info = {
    "name": "Ngx Select",
    "author": "SolPie",
    "version": (1,0),
    "blender": (2, 68, 0),
    "location": "View3D > Specials > Ngx Select ",
    "description": "Select the verts on -x",
    "warning": "",
    "category": "Mesh"}

def sel_ng_x_verts(context):
    ob = context.active_object
    #sel_mode = bpy.context.tool_settings.mesh_select_mode
    bpy.ops.mesh.select_all(action='DESELECT')
    bpy.ops.object.mode_set(mode='OBJECT')
    #
    bpy.context.tool_settings.mesh_select_mode = [True, False, False]
    #num = 0
    for v in ob.data.vertices:
        if v.co.x < 0:
            #print('select -x:',v.co.x)
            v.select = True
            #num += 1
            #print(num)
    bpy.ops.object.mode_set(mode='EDIT')
    #bpy.context.tool_settings.mesh_select_mode = sel_mode

def set_mirror(v1, v2):
    bpy.ops.object.mode_set(mode='OBJECT')
    v2.co = v1.co
    v2.co.x = -v2.co.x
    bpy.ops.object.mode_set(mode='EDIT')

class NgxSel(bpy.types.Operator):
    bl_idname = 'mesh.ngx'
    bl_label = 'Ngx Select'
    bl_options = {'REGISTER', 'UNDO'}

    @classmethod
    def poll(cls, context):
        obj = context.active_object
        return (obj and obj.type == 'MESH')

    def execute(self, context):
        sel_ng_x_verts(context)
        return {'FINISHED'}

def menu_func(self, context):
    self.layout.operator(NgxSel.bl_idname, text="Ngx Select")

def register():
    bpy.utils.register_module(__name__)

    bpy.types.VIEW3D_MT_edit_mesh_specials.append(menu_func)
    bpy.types.VIEW3D_MT_edit_mesh_vertices.append(menu_func)

def unregister():
    bpy.utils.unregister_module(__name__)

    bpy.types.VIEW3D_MT_edit_mesh_specials.remove(menu_func)
    bpy.types.VIEW3D_MT_edit_mesh_vertices.remove(menu_func)

if __name__ == "__main__":
    register()


from abc import ABCMeta, abstractmethod

import six

@six.add_metaclass(ABCMeta)
class OrderedBinaryTree(object):
    @property
    @abstractmethod
    def root(self):
        pass

    @abstractmethod
    def delete(self, key):
        pass

    def update(self, key, val):
        node = self.search(key)
        if node is not None:
            node.val = val
        return node

    def search(self, key):
        return self._search(self._root, key)

    def _search(self, node, key):
        if node is None:
            return None
        if self._key_eq(node.key, key):
            return node
        if self._key_gt(node.key, key):
            return self._search(node.left, key)
        else:
            return self._search(node.right, key)

    def _right_rotate(self, node):
        l = node.left
        node.left = l.right
        if l.right is not None:
            l.right.parent = node
        l.right = node
        if not node.parent:
            self._root = l
        elif node is node.parent.left:
            node.parent.left = l
        else:
            node.parent.right = l
        l.parent = node.parent
        node.parent = l

    def _left_rotate(self, node):
        r = node.right
        node.right = r.left
        if r.left is not None:
            r.left.parent = node
        r.left = node
        if not node.parent:
            self._root = r
        elif node is node.parent.left:
            node.parent.left = r
        else:
            node.parent.right = r
        r.parent = node.parent
        node.parent = r


import os
import json
import binascii
import hashlib
from functools import wraps

from flask import current_app

class PollManager(object):
    """A very simple poll manager that saves its data as json."""
    def __init__(self, path):
        self.file = os.path.join(path, 'polls.json')

    def read(self):
        print("JSON path:")
        print(self.file)
        if not os.path.exists(self.file):
            return {}
        with open(self.file) as f:
            data = json.loads(f.read())
        return data

    def write(self, data):
        with open(self.file, 'w') as f:
            f.write(json.dumps(data, indent=2))

    def add_poll(self, name, title, options = [], votes = []):
        polls = self.read()
        if polls.get(name):
            return False
        new_poll = {
            'title': title,
            'options': options,
            'votes': votes
        }
        polls[name] = new_poll
        self.write(polls)
        polldata = polls.get(name)
        return Poll(self, name, polldata)

    def get_poll(self, name):
        polls = self.read()
        polldata = polls.get(name)
        if not polldata:
            return None
        return Poll(self, name, polldata)

    def delete_poll(self, name):
        polls = self.read()
        if not polls.pop(name, False):
            return False
        self.write(polls)
        return True

    def update(self, name, polldata):
        data = self.read()
        data[name] = polldata
        self.write(data)

    def get_basic_data(self, name):
        polls = self.read()
        polldata = polls.get(name)
        if not polldata:
            return None
        return Poll(self, name, polldata)

class Poll(object):
    def __init__(self, manager, name, data):
        self.manager = manager
        self.name = name
        self.data = data

    def get(self, option):
        return self.data.get(option)

    def set(self, option, value):
        self.data[option] = value
        self.save()

    def save(self):
        self.manager.update(self.name, self.data)

    def get_id(self):
        return self.name

class PollBasic(object):
    def __init__(self, name, title):
        self.name = name
        self.title = title


import threading
import time
import queue
from random import randint

class Producer(threading.Thread):
	def run(self):
		while True:
			if qu.qsize() < 5:
				a = randint(1, 99)
				print('producer', a)
				qu.put(a)

class Consumer(threading.Thread):
	def run(self):
		while True:
			if qu.qsize() > 0:
				print('consumer!!!', qu.get())

if __name__ == '__main__':
	qu = queue.Queue()
	threads = []
	for i in range(5):
		threads.append(Producer())
	threads.append(Consumer())
	for t in threads:
		t.start()


import os
import re
import platform
import subprocess
import traceback
from Analyzer import Analyzer
from helpers import first_file_name_in_path_matching_regex

class JsLintComplaint(object):
    def __init__(self, complaint):
        lines = complaint.split('\n')
        regex_match = re.search("line ([0-9]*) character ([0-9]*):(.*)", lines[0])
        if regex_match:
            self.complaint = regex_match.group(2).strip()
            for line in lines:
                self.complaint += "\n" + line
            if len(self.complaint.strip()):
                try:
                    self.line_number = int(regex_match.group(0))
                except:
                    self.line_number = -1
                try:
                    self.column_number = int(regex_match.group(1))
                except:
                    self.column_number = -1
            else:
                self.line_number = -1
                self.column_number = -1
                self.complaint = complaint
        else:
            self.line_number = -1
            self.column_number = -1
            self.complaint = complaint

    def __str__(self):
        return self.__unicode__()

    def __unicode__(self):
        if self.line_number > 0:
            if self.column_number > 0:
                return 'Line %d:%d - %s' % (self.line_number, self.column_number, self.complaint)
            else:
                return 'Line %d - %s' % (self.line_number, self.complaint)
        else:
            return self.complaint

class JSLintAnalyzer(Analyzer):

    def __init__(self, options=None):
        self._options = options or {}
        self._js_lint_proc_args = None
        self._module_path = os.path.dirname(os.path.realpath(__file__))
        self._lib_path = self._options.get('lib_path', os.path.join(self._module_path, 'lib'))
        self._jslint_script_regex = re.compile(r'^jslint.*\.js$')
        self._js_lint_script_file_path = first_file_name_in_path_matching_regex(self._lib_path, self._jslint_script_regex)
        self._rhino_jar_regex = re.compile(r'^js\.jar$')
        self._rhino_jar_file_path = first_file_name_in_path_matching_regex(self._lib_path, self._rhino_jar_regex)
        self._lib_message_list = []
        self._use_nodejs_if_available = self._options.get('use_nodejs_if_available', True)

        if self._js_lint_proc_args is None and platform.system() == 'Windows':
            # Windows Script Host has two versions, wscript.exe which pops up
            # a window and cscript.exe which does not.
            self._js_lint_proc_args = ["cscript.exe", "//I", "//Nologo", self._js_lint_script_file_path]
            self._lib_message_list.append("Using cscript.exe to run JSLint")

        if self._js_lint_proc_args is None and self._use_nodejs_if_available:
            null_file = open(os.devnull, 'w')
            try:
                subprocess.check_call(['node', '--help'], stdout=null_file, stderr=null_file)
                self._js_lint_proc_args = ["node", self._js_lint_script_file_path]
                self._lib_message_list.append("Using node.js to run JSLint")
            except Exception:
                self._lib_message_list.append("Cannot use node.js to run JSLint because it was not found on the PATH")
            finally:
                null_file.close()

        if self._js_lint_proc_args is None:
            if self._rhino_jar_file_path:
                self._js_lint_proc_args = ["java", "-jar", self._rhino_jar_file_path, self._js_lint_script_file_path]
                self._lib_message_list.append("Using Rhino to run JSLint")
            else:
                self._lib_message_list.append("Cannot use Rhino to run JSLint because js.jar could not be found in in %r" % self._lib_path)

    def analyze(self, resource):
        analysis = Analyzer.analyze(self, resource)
        analysis.add_messages(self._lib_message_list)
        if self._js_lint_proc_args is None:
            analysis.mark_as_bad()
            analysis.add_error('No suitable JSLint runner (cscript.exe, node.js or rhino) could be found.')
            return analysis

        try:
            js_lint_proc = subprocess.Popen(self._js_lint_proc_args, -1, None, subprocess.PIPE,
                subprocess.PIPE, subprocess.PIPE)
            js_lint_proc_outputs = js_lint_proc.communicate(resource.content)
        except Exception as e:
            analysis.add_error("An exception what thrown while running JsLint: %s\n%s" %
                (str(e), traceback.format_exc()))
            return analysis

        # The JSLint process returns 1 if it finds lint
        if js_lint_proc.returncode != 0 and js_lint_proc.returncode != 1:
            analysis.add_error('The JSLint process exited with return code %d\nArguments: %s\n Output: %s'
                % (js_lint_proc.returncode, self._js_lint_proc_args, js_lint_proc_outputs))
            return analysis

        analysis.mark_as_good()  # Assume that JSLint produced no complaints until parsing one from the process output

        for js_lint_proc_output in js_lint_proc_outputs:
            js_lint_complaints = js_lint_proc_output.split("Lint at ")
            for complaint in js_lint_complaints:
                if len(complaint.strip()):
                    analysis.mark_as_bad()
                    js_lint_complaint = JsLintComplaint(complaint)
                    analysis.add_error(str(js_lint_complaint))

        return analysis