"""
    We study class decorators - functions that are used to modify the behavior of other functions.
        @property:
            (1) use this decorator to make a method behave like a property i.e. no parentheses needed!
    There are decorators for which we can define a setter and a deleter for our attribute
"""

class Person:

    def __init__(self, name, surname):
        self.name = name
        self.surname = surname

    @property
    def fullname(self):
        return "%s %s" % (self.name, self.surname)

    @fullname.setter
    def fullname(self, value):
        name, surname = value.split(" ", 1)
        self.name = name
        self.surname = surname

    @fullname.deleter
    def fullname(self):
        del self.name
        del self.surname

kevin = Person("Kevin", "Truong")
print(kevin.fullname)

kevin.fullname = "Kelvin Tran"
print(kevin.fullname)
print(kevin.name)
print(kevin.surname)


from aip import AipSpeech
from playsound import playsound

def get_mp3(message, mp3):
	APP_ID = '11601536'
	API_KEY = ''
	SECRET_KEY = ''

	client = AipSpeech(APP_ID, API_KEY, SECRET_KEY)

	result  = client.synthesis(message, 'zh', 1, {
		'vol': 5,
		'spd': 4,
		'plt': 6,
		'per': 4,
	})

	# 识别正确返回语音二进制 错误则返回dict 参照下面错误码
	if not isinstance(result, dict):
		with open(mp3, 'wb') as f:
			f.write(result)
	else:
		print('错误码：'+result['error_code'])
		print('错误描述：'+result['error_msg'])

if __name__ == '__main__':
	message = '我回去了'
	mp3 = 't1.mp3'

	get_mp3(message, mp3)
	# 播放MP3文件
	playsound(mp3)


# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from dateutil.tz import tzlocal
from rest_framework.viewsets import ReadOnlyModelViewSet, ModelViewSet
from rest_framework.exceptions import ParseError
from rest_framework.response import Response
from rest_framework import status

from ids.models import AttackLogs, AttackDetails
from . import ids
from core.framework.views import AbstractModuleViewSet, CachedViewSetMixin
from .serializers import BaseIdsSerializer, AttackLogsSerializer, AttackDetailsSerializer
import datetime

class IdsModuleViewSet(AbstractModuleViewSet):
    app = ids
    serializer_class = BaseIdsSerializer

    def get_data(self, module, query, request):
        section_name = request.QUERY_PARAMS.get('section')

        if request.method == 'POST':
            if query is not None:
                raise ParseError('You can not define `query` query param in POST request')

            query = request.DATA

        module_data = module.get_data(query, section_name)

        return module_data

class AttackLogsViewSet(CachedViewSetMixin, ModelViewSet):
    queryset = AttackLogs.objects.all()
    serializer_class = AttackLogsSerializer

    # tu odfiltrujeme data, len bude treba konvertovat datum.cas
    def get_queryset(self):
        queryset = AttackLogs.objects.all()
        params = self.request.QUERY_PARAMS
        if 'startTime' in params and 'endTime' in params and 'attacktype' not in params and 'probability' not in params:
            if params['startTime'] is not None and params['endTime'] is not None:
                queryset = queryset.filter(starttime__gte=datetime.datetime.fromtimestamp(float(params['startTime'])/1000.0))
                queryset = queryset.filter(endtime__lte=datetime.datetime.fromtimestamp(float(params['endTime'])/1000.0))
                queryset.order_by('startTime')
                return queryset

        elif 'startTime' in params and 'endTime' in params and 'probability' in params and 'attacktype' not in params:
            queryset = queryset.filter(starttime__gte=datetime.datetime.fromtimestamp(float(params['startTime'])/1000.0))
            queryset = queryset.filter(endtime__lte=datetime.datetime.fromtimestamp(float(params['endTime'])/1000.0))
            queryset = queryset.filter(probability__gte=params['probability'])
            queryset.order_by('starTime')
            return queryset

        elif 'startTime' in params and 'endTime' in params and 'probability' in params and 'attacktype' in params:
            queryset = queryset.filter(starttime__gte=datetime.datetime.fromtimestamp(float(params['startTime'])/1000.0))
            queryset = queryset.filter(endtime__lte=datetime.datetime.fromtimestamp(float(params['endTime'])/1000.0))
            queryset = queryset.filter(attacktype=params['attacktype'])
            queryset = queryset.filter(probability__gte=params['probability'])
            queryset.order_by('startTime')
            return queryset

        elif 'id' in params:
            queryset = queryset.filter(id=params['id'])
            queryset.order_by('id')
            return queryset

        return queryset

    # # tu odfiltrujeme data, len bude treba konvertovat datum.cas
    # def get_queryset(self):
    #     queryset = AttackLogs.objects.all()
    #     params = self.request.QUERY_PARAMS
    #     if 'startTime' in params and  'endTime' in params:
    #         if params['startTime'] is None and params['endTime'] is None: pass
    #             # queryset = queryset.filter(starttime__gte=datetime.datetime.fromtimestamp(float(params['startTime'])/1000.0))
    #             # queryset = queryset.filter(endtime__lte=datetime.datetime.fromtimestamp(float(params['endTime'])/1000.0))
    #             # queryset = queryset.filter(starttime__gte=datetime.datetime.fromtimestamp(tzlocal)
    #         elif params['startTime'] is not None and params['endTime'] is not None:
    #             queryset = queryset.filter(starttime__gte=datetime.datetime.fromtimestamp(float(params['startTime'])/1000.0))
    #             queryset = queryset.filter(endtime__lte=datetime.datetime.fromtimestamp(float(params['endTime'])/1000.0))
    #             # queryset = queryset.filter(starttime__gte=datetime.fromtimestamp(float(params['startTime'])/1000.0))
    #             # queryset = queryset.filter(endtime__lte=datetime.fromtimestamp(float(params['endTime'])/1000.0))
    #     if 'probability' in params and  'type' in params:
    #         queryset = queryset.filter(attacktype=params['type'])
    #         queryset = queryset.filter(probability=params['probability'])
    #     queryset.order_by('starttime')
    #     return queryset

    def list(self, request, *args, **kwargs):
        if self.request.user.is_admin is True:
            return super(AttackLogsViewSet, self).list(request, *args, **kwargs)
        else:
            return Response({
                "detail": "You do not have permission to perform this action."
            }, status=status.HTTP_403_FORBIDDEN)

class AttackDetailsViewSet(CachedViewSetMixin, ModelViewSet):
    queryset = AttackDetails.objects.all()
    serializer_class = AttackDetailsSerializer

    # tu odfiltrujeme data, len bude treba konvertovat datum.cas
    def get_queryset(self):
        queryset = AttackDetails.objects.all()
        params = self.request.QUERY_PARAMS
        if 'attack_id' in params:
            if params['attack_id'] is not None:
                queryset = queryset.filter(attack_id=params['attack_id'])
                queryset.order_by('attack_id')
                return queryset

        return queryset

    def list(self, request, *args, **kwargs):
        if self.request.user.is_admin is True:
            return super(AttackDetailsViewSet, self).list(request, *args, **kwargs)
        else:
            return Response({
                "detail": "You do not have permission to perform this action."
            }, status=status.HTTP_403_FORBIDDEN)


from parse import read_input_file, write_output_file
import os
import random
import math
from exp_utils import get_logger
import datetime
import numpy as np
import pickle
import cvxpy as cp

# random.seed(123)
work_dir = "./logs"
now = datetime.datetime.now()
logging = get_logger(os.path.join(work_dir, now.strftime('%Y-%m-%d %H:%M:%S') + ' log.txt'))

total_benefit = 0

def solve(tasks, input_path):
    """
    Args:
        tasks: list[Task], list of igloos to polish
    Returns:
        output: list of igloos in order of polishing
    """

    # variables: length n array of starting time
    # objective: max of sum of compute_benefit() called on all tasks
    # constraint:
    #   j.order[i] + i.order[j] == 1 for all i, j where i != j
    #   j.order[i] * (t_i.task_start_time - t_j.task_start_time - t_j.duration) + i.order[j] * (t_j.task_start_time - t_i.task_start_time - t_i.duration > 0) >= 0
    # Note: j.order[i] = 1 if j < i, j.order[i] = 0 if j > i

    def compute_benefit(task, task_start_time):
        # if task_start_time + task.duration <= task.deadline:
        #     return task.perfect_benefit
        # else:
        # return task.perfect_benefit * math.exp(-0.0170 * (task_start_time + task.duration - task.deadline))
        return 1

    # variables
    # variable_list: [t1.start_time, t2.start_time,..., tn.start_time, t1.order(t1), t1.order(t2), ..., tn.order(tn)]
    variable_list = []
    for _ in range(len(tasks) + len(tasks) * len(tasks)):
        variable_list.append(cp.Variable())

    # function
    def objective_function(variable_list):
        return sum([compute_benefit(tasks[i], variable_list[i]) for i in range(len(tasks))])

    obj = cp.Maximize(objective_function(variable_list))

    # constraints
    constraints = []

    order_lists = np.reshape(variable_list[len(tasks):], (len(tasks), len(tasks)))
    for i in range(len(tasks)):
        constraints.append(variable_list[i] >= 0)
        for j in range(len(tasks)):
            if i != j:
                # order constraints
                constraints.append(order_lists[j][i] >= 0)
                # constraints.append(order_lists[j][i] <= 1)
                constraints.append(order_lists[i][j] + order_lists[j][i] == 1)
                # start time and duration constraints
                constraints.append(order_lists[j][i] * (variable_list[i] - variable_list[j] - tasks[j].duration) + \
                                    order_lists[i][j] * (variable_list[j] - variable_list[i] - tasks[i].duration) >= 0 )

    prob = cp.Problem(obj, constraints)
    prob.solve()  # Returns the optimal value.
    print("status:", prob.status)
    print("optimal value", prob.value)
    # print("optimal var", x.value, y.value)

inputs_categories = ["large", "medium", "small"]

print(os.listdir('inputs/'))

# Load optimal output
opt_dict = {}
if os.path.exists("optimum_output.pickle"):
    with open("optimum_output.pickle", "rb") as f:
        opt_dict = pickle.load(f)

for inputs_category in inputs_categories:
    for file_name in os.listdir(os.path.join('inputs/', inputs_category)):
        if file_name[0] == ".":
            continue
        input_path = 'inputs/' + inputs_category + "/" + file_name
        # print(input_path)
        output_path = 'outputs/' + inputs_category + "/" + file_name[:-3] + '.out'
        tasks = read_input_file(input_path)
        output, benefit = solve(tasks, input_path)
        total_benefit = total_benefit + benefit
        # print(output_path)
        write_output_file(output_path, output)

logging(str(total_benefit))

with open('optimum_output.pickle', 'wb') as f:
    pickle.dump(opt_dict, f)

# Here's an example of how to run your solver.
# if __name__ == '__main__':
#     for input_path in os.listdir('inputs/'):
#         output_path = 'outputs/' + input_path[:-3] + '.out'
#         tasks = read_input_file(input_path)
#         output = solve(tasks)
#         write_output_file(output_path, output)


def interpolation_search(key, array):
    lim_inf = 0
    lim_sup = len(array) - 1

    counter = 0

    while lim_inf < lim_sup:

        counter+=1

        mid = lim_inf + ((lim_sup - lim_inf) * ((key - array[lim_inf]) // (array[lim_sup] - array[lim_inf])))
        print(mid)

        if key == array[mid]:
            return counter
        elif key > array[mid]:
            lim_inf = mid + 1
        else:
            lim_sup = mid - 1

        return (-1)


import argparse
import math
import os

import hail as hl

META_DICT = {
    "filter": {
        "artifact_prone_site": {
            "Description": "Variant overlaps an artifact-prone site"
        }
    },
    "format": {
        "DP": {"Description": "Depth of coverage", "Number": "1", "Type": "Integer"},
        "FT": {
            "Description": "Sample-level genotype filters",
            "Number": ".",
            "Type": "String",
        },
        "HL": {"Description": "Heteroplasmy level", "Number": "1", "Type": "Float"},
        "MQ": {"Description": "Mapping quality", "Number": "1", "Type": "Float"},
        "TLOD": {
            "Description": "Log 10 likelihood ratio score of variant existing versus not existing",
            "Number": "1",
            "Type": "Float",
        },
        'AD': {"Description": "Allelic depth of REF and ALT", "Number": "R", "Type": "Integer"},
        'OriginalSelfRefAlleles': {
            'Description':'Original self-reference alleles (only if alleles were changed in Liftover repair pipeline)',
            'Number':'R',
            'Type':'String'},
        'SwappedFieldIDs': {
            'Description':'Fields remapped during liftover (only if alleles were changed in Liftover repair pipeline)',
            'Number':'1',
            'Type':'String'
        }
    },
}

def read_input_data(path):
    vcf_paths = {}

    ht = hl.import_table(path)
    ht = ht.filter(ht.path != "")

    # Add the vcf path to a dictionary with sample name as key
    df = ht.to_pandas()

    for _, row in df.iterrows():
        vcf_paths[row["s"]] = row['path']

    return vcf_paths

def multi_way_union_mts(mts: list, temp_dir: str, chunk_size: int, min_partitions: int) -> hl.MatrixTable:
    """
    Hierarchically join together MatrixTables in the provided list.

    :param mts: List of MatrixTables to join together
    :param temp_dir: Path to temporary directory for intermediate results
    :param chunk_size: Number of MatrixTables to join per chunk (the number of individual VCFs that should be combined at a time)
    :return: Joined MatrixTable
    """
    # Convert the MatrixTables to tables where entries are an array of structs
    staging = [mt.localize_entries("__entries", "__cols") for mt in mts]
    stage = 0
    while len(staging) > 1:
        # Calculate the number of jobs to run based on the chunk size
        n_jobs = int(math.ceil(len(staging) / chunk_size))
        next_stage = []

        for i in range(n_jobs):
            # Grab just the tables for the given job
            to_merge = staging[chunk_size * i : chunk_size * (i + 1)]

            # Multiway zip join will produce an __entries annotation, which is an array where each element is a struct containing the __entries annotation (array of structs) for that sample
            merged = hl.Table.multi_way_zip_join(to_merge, "__entries", "__cols")
            if min_partitions > 10:
                merged = merged.checkpoint(f"stage_{stage}_job_{i}_pre.ht", overwrite=True)
            # Flatten __entries while taking into account different entry lengths at different samples/variants (samples lacking a variant will be NA)
            merged = merged.annotate(
                __entries=hl.flatten(
                    hl.range(hl.len(merged.__entries)).map(
                        # Coalesce will return the first non-missing argument, so if the entry info is not missing, use that info, but if it is missing, create an entries struct with the correct element type for each null entry annotation (such as int32 for DP)
                        lambda i: hl.coalesce(
                            merged.__entries[i].__entries,
                            hl.range(hl.len(merged.__cols[i].__cols)).map(
                                lambda j: hl.null(
                                    merged.__entries.__entries.dtype.element_type.element_type
                                )
                            ),
                        )
                    )
                )
            )

            # Flatten col annotation from array<struct{__cols: array<struct{s: str}>} to array<struct{s: str}>
            merged = merged.annotate_globals(
                __cols=hl.flatten(merged.__cols.map(lambda x: x.__cols))
            )

            next_stage.append(
                merged.checkpoint(
                    os.path.join(temp_dir, f"stage_{stage}_job_{i}.ht"), overwrite=True
                )
            )
        stage += 1
        staging.clear()
        staging.extend(next_stage)

    # Unlocalize the entries, and unfilter the filtered entries and populate fields with missing values
    return (
        staging[0]
        ._unlocalize_entries("__entries", "__cols", list(mts[0].col_key))
        .unfilter_entries()
    )

def run_coverage(args):
    input_paths = read_input_data(args.input_tsv)
    mt_list = []
    for sample, file_path in input_paths.items():
        mt = hl.import_matrix_table(
            file_path,
            delimiter="\t",
            row_fields={"chrom": hl.tstr, "pos": hl.tint, "target": hl.tstr},
            row_key=["chrom", "pos"],
            min_partitions=args.n_read_partitions,
        )
        mt = mt.key_rows_by(*["chrom", "pos", "target"])
        mt = mt.rename({"x": "coverage"})
        mt = mt.key_cols_by(s=sample)
        mt_list.append(mt)

    cov_mt = multi_way_union_mts(mt_list, args.temp_dir, args.chunk_size, min_partitions=args.n_read_partitions)

    cov_mt.describe()
    cov_mt.show()

    cov_mt.coverage.export(args.output_flat_file)

def run_variants(args):
    input_paths = read_input_data(args.input_tsv)
    mt_list = []
    for sample, file_path in input_paths.items():
        mt = hl.import_vcf(file_path, reference_genome="GRCh38")
        fields_of_interest = {'OriginalSelfRefAlleles':'array<str>', 'SwappedFieldIDs':'str'}
        if 'GT' in mt.entry:
            mt = mt.drop('GT')
        for x, item_type in fields_of_interest.items():
            if x not in mt.entry:
                mt = mt.annotate_entries(**{x: hl.missing(item_type)})
        mt = mt.select_entries("DP", "AD", *list(fields_of_interest.keys()), HL=mt.AF[0])
        mt = mt.annotate_entries(
            MQ=hl.float(mt.info["MMQ"][1]),
            TLOD=mt.info["TLOD"][0],
            FT=hl.if_else(hl.len(mt.filters) == 0, {"PASS"}, mt.filters),
        )
        mt = mt.key_cols_by(s=sample)
        mt = mt.select_rows()
        mt_list.append(mt)

    cov_mt = multi_way_union_mts(mt_list, args.temp_dir, args.chunk_size, min_partitions=args.n_read_partitions)

    cov_mt.describe()
    cov_mt.show()
    cov_mt.entries().show()

    hl.export_vcf(cov_mt, args.output_flat_file, metadata=META_DICT)

parser = argparse.ArgumentParser()
parser.add_argument('--run-coverage', action='store_true',
                    help='If enabled, expects to merge coverage tsvs.')
parser.add_argument('--run-variants', action='store_true',
                    help='If true, expects to merge variants. Superceded by --run-coverage.')
parser.add_argument("--n-read-partitions", type=int,
                    help="The number of partitions to use when reading tsvs. This should be 1 if the files are small.", default=1)
parser.add_argument("--chunk-size",
                    help="Chunk size to use for combining VCFs (the number of individual VCFs that should be combined at a time)",
                    type=int,default=100)
parser.add_argument("--input-tsv",
                    help="Table with a 's' column for sample name and 'path' column for files to import.", required=True)
parser.add_argument("--output-flat-file",
                    help="Name of flat file to write output", required=True)
parser.add_argument("--temp-dir",
                    help="Temporary directory to use for intermediate outputs", required=True)

if __name__ == "__main__":
    args = parser.parse_args()
    if args.run_coverage:
        run_coverage(args)
    else:
        run_variants(args)


import requests
import markdown
from prime.data import MAILGUN_KEY
# from data import MAILBOX_KEY
# headers = {
#     'Authorization': MAILBOX_KEY,
#     'Content-Type': 'application/json',
# }

# cont = open('email_trial.html').read()
# cont

def send(subject='Wasssup', text='## Bingo'):
    body = markdown.markdown(text)
    data = '{"personalizations": [{"to": [{"email": "vishstar88@gmail.com"}]}], "from": {\
        "email": "sendeexampexample@example.com"}, "subject": "%s", "content": [{"type": "text/html", "value": "%s"}]}'

    print (requests.post('https://api.sendgrid.com/v3/mail/send',
                         headers=headers, data=data % (subject, body)))

def send_simple_message(subject="Your Pygeon has arrived", body="Congratulations Vishal, you just sent an email with Mailgun!  You are truly awesome!", to="Vishal <vishstar88@gmail.com>"):
    return requests.post(
        "https://api.mailgun.net/v3/sandbox37bd14349c8749d8a9b7dec8d8996261.mailgun.org/messages",
        auth=("api", MAILGUN_KEY),
        data={"from": "Pygeon master <vishalg8897@gmail.com>",
              "to": to,
              "subject": subject,
              "html": body})

# print (send())
# x = send_simple_message(body=cont)
# x.content


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

from django.db import models

from apps.contrib.models.mixins import TimeStampedModelMixin

from django.utils.translation import ugettext_lazy as _

class UserProfile(TimeStampedModelMixin):
    """This class is to customize some extra fields for users. e.g. roles."""

    user = models.OneToOneField(
        'accounts.User',
        on_delete=models.CASCADE,
        related_name='user_profile',
    )

    def __str__(self):
        return self.user.email

    class Meta:
        db_table = 'user_profiles'
        verbose_name = _('User Profile')
        verbose_name_plural = _('User Profiles')
        app_label = 'accounts'


import functions
import const

from functions import center_window

import tkinter as tk

root = tk.Tk()

def display_last_authentication_data():
    try:
        with open("user_data.txt", "r") as file:
            lines = file.readlines()
        if lines:
            last_data = lines[-1].strip()  # Получаем последнюю строку из файла
        else:
            last_data = None
    except FileNotFoundError:
        last_data = None

    if last_data:
        center_window(root, 450, 125)
        root.title("NumMaker")
        root.iconbitmap('images/logo_1.ico')
        root.configure(bg=const.BACKGROUND)
        message = tk.Label(root,
                           text=f"Хотите продолжить?\n{last_data}",
                           font=("Comic Sans MS", 13, "bold"),
                           fg="blue",
                           bg=const.BACKGROUND)
        message.pack(pady=10)
        yes_button = tk.Button(root,
                               text="Да",
                               command=lambda: open_authentication_window(last_data),
                               font=("Comic Sans MS", 10, "bold"),
                               fg="white",
                               bg='blue',
                               relief="groove",
                               bd=4,
                               width=4)
        yes_button.bind("<Enter>", lambda event: functions.on_hover_enter(event, yes_button))
        yes_button.bind("<Leave>", lambda event: functions.on_hover_leave(event, yes_button))
        no_button = tk.Button(root,
                              text="Нет",
                              command=lambda: open_authentication_window(None),
                              font=("Comic Sans MS", 10, "bold"),
                              fg="white",
                              bg='blue',
                              relief="groove",
                              bd=4,
                              width=4)
        no_button.bind("<Enter>", lambda event: functions.on_hover_enter(event, no_button))
        no_button.bind("<Leave>", lambda event: functions.on_hover_leave(event, no_button))
        yes_button.pack(side=tk.LEFT, padx=27)
        no_button.pack(side=tk.RIGHT, padx=27)
    else:
        open_authentication_window(None)

def open_authentication_window(last_data):
    root.withdraw()
    new_root = tk.Toplevel()
    new_root.title("NumMaker")
    new_root.iconbitmap('images/logo_1.ico')

    # Рассчитать ширину и высоту экрана
    screen_width = new_root.winfo_screenwidth()
    screen_height = new_root.winfo_screenheight()

    # Создание синей рамки для верхней линии
    top_line_frame = tk.Frame(new_root,
                              height=20,
                              width=screen_width,
                              bg='#32CAFB')
    top_line_frame.pack_propagate(False)  # Запретить изменение размера рамки
    top_line_frame.pack(side=tk.TOP)

    # Создание синей рамки для нижней линии
    bottom_line_frame = tk.Frame(new_root,
                                 height=20,
                                 width=screen_width,
                                 bg='#32CAFB')
    bottom_line_frame.pack_propagate(False)
    bottom_line_frame.pack(side=tk.BOTTOM)

    # Создание синей рамки для левой линии
    left_line_frame = tk.Frame(new_root,
                               height=screen_height - 2 * 40,
                               width=20, bg='#32CAFB')
    left_line_frame.pack_propagate(False)
    left_line_frame.pack(side=tk.LEFT)

    # Создание синей рамки для правой линии
    right_line_frame = tk.Frame(new_root,
                                height=screen_height - 2 * 40,
                                width=20,
                                bg='#32CAFB')
    right_line_frame.pack_propagate(False)
    right_line_frame.pack(side=tk.RIGHT)

    # Создание основной области контента
    content_frame = tk.Frame(new_root, bg=const.BACKGROUND)
    content_frame.pack()
    new_root.geometry('700x600+390+110')
    new_root.configure(bg=const.BACKGROUND)

    # Загрузка логотипа
    logo = tk.PhotoImage(file="images/origin.png")
    image_label = tk.Label(master=new_root,
                           image=logo,
                           bg=const.BACKGROUND)
    image_label.pack(pady=10)

    # Описание приложения
    description_label = tk.Label(new_root,
                                 text="Данное приложение создано для автоматического ввода\nданных о пользователе",
                                 font=const.font_for_title,
                                 fg="blue",
                                 bg=const.BACKGROUND)
    description_label.pack()

    # Пометка, что далее нужно будет вводить ссылку
    link_label = tk.Label(new_root,
                          text="Введите ссылку:",
                          font=const.custom_font_for_forms,
                          fg="blue",
                          bg="#BAF7F2")
    link_label.pack(pady=5)

    # Ввод ссылки
    link_entry = tk.Entry(new_root,
                          width=40)
    link_entry.pack()

    # Пометка, что далее нужно будет вводить имя пользователя
    name_label = tk.Label(new_root,
                          text="Введите Ваше имя:",
                          font=const.custom_font_for_forms,
                          fg="blue",
                          bg="#BAF7F2")
    name_label.pack(pady=5)

    # Ввод имени пользователя
    name_entry = tk.Entry(new_root,
                          width=40)
    name_entry.pack()
    if last_data:
        first_name = last_data.split(", ")[0][5:]
        name_entry.insert(0, first_name)
    name_entry.pack()

    last_name_label = tk.Label(new_root,
                           text="Введите Вашу фамилию:",
                           font=const.custom_font_for_forms,
                           fg="blue",
                           bg="#BAF7F2")
    last_name_label.pack(pady=5)

    # Ввод фамилии
    last_name_entry = tk.Entry(new_root,
                           width=40)
    last_name_entry.pack()

    # Пометка, что далее нужно будет вводить email-адрес
    email_label = tk.Label(new_root,
                           text="Введите Ваш email:",
                           font=const.custom_font_for_forms,
                           fg="blue",
                           bg="#BAF7F2")
    email_label.pack(pady=5)

    # Ввод email-адреса
    email_entry = tk.Entry(new_root,
                           width=40)
    email_entry.pack()
    if last_data:
        last_email = last_data.split(", ")[1].replace("Email: ", "")
        email_entry.insert(0, last_email)
    email_entry.pack()

    password_label = tk.Label(new_root,
                           text="Введите Ваш пароль:",
                           font=const.custom_font_for_forms,
                           fg="blue",
                           bg="#BAF7F2")
    password_label.pack(pady=5)

    # Ввод пароля
    password_entry = tk.Entry(new_root,
                           width=40)
    password_entry.pack()

    # Описание того, что делает кнопка "Старт"
    link_label = tk.Label(new_root,
                          text="Когда Вы нажмете кнопку 'Старт', программа автоматически сгенерирует новый"
                               "\nномер телефона, а так же заполнит данные за Вас",
                          font=("Comic Sans MS", 11),
                          fg="blue",
                          bg=const.BACKGROUND)
    link_label.pack(pady=10)

    # Вывод сгенерированного телефона
    phone_label = tk.Label(new_root,
                           text="",
                           font=const.custom_font_for_forms,
                           fg="red",
                           bg=const.BACKGROUND)
    phone_label.pack()

    # Создание отдельного блока, в который будут помещаться кнопки "Старт" и "Закончить работу программы"
    button_frame = tk.Frame(new_root, bg=const.BACKGROUND)
    button_frame.pack(pady=10)

    # Кнопка, для старта работы программы
    open_button = tk.Button(button_frame,
                            text="Старт",
                            command=lambda: functions.open_link(phone_label, link_entry, name_entry, last_name_entry, email_entry, password_entry),
                            font=("Comic Sans MS", 11, "bold"),
                            fg="white",
                            bg='blue',
                            relief="groove",
                            bd=3)
    open_button.pack(side=tk.LEFT,
                     padx=10)

    # Бинд - при наведении на кнопку, она меняет цвет
    open_button.bind("<Enter>", lambda event: functions.on_hover_enter(event, open_button))
    open_button.bind("<Leave>", lambda event: functions.on_hover_leave(event, open_button))

    # Кнопка, для окончания работы программы
    exit_button = tk.Button(button_frame,
                            text="Закончить работу программы",
                            command=lambda: functions.exit_application(root, new_root),
                            font=("Comic Sans MS", 11, "bold"),
                            fg="white",
                            bg='blue',
                            relief="groove",
                            bd=3)
    exit_button.pack(side=tk.LEFT,
                     padx=10)

    # Бинд - при наведении на кнопку, она меняет цвет
    exit_button.bind("<Enter>", lambda event: functions.on_hover_enter(event, exit_button))
    exit_button.bind("<Leave>", lambda event: functions.on_hover_leave(event, exit_button))

    # mainloop обеспечивает постоянную работу главного окна и его объектов до момента, когда оно будет закрыто
    new_root.mainloop()

if __name__ == "__main__":
    display_last_authentication_data()
    root.mainloop()


from fastapi import UploadFile
import numpy as np
import random
import pandas as pd
from imblearn.under_sampling import RandomUnderSampler

from constants import REDUCING_METHODS
from utils.numpy_encoder import NumpyEncoder

class DatasetAnalizer:
    def __init__(self, file: UploadFile):
        self.file = file
        self.x_train = None
        self.y_train = None

    async def load_train_data(self):
        contents = await self.file.read()
        as_array = np.array([i.split() for i in contents.decode('utf-8').split('\n')][:-1], dtype='float')
        self.x_train, self.y_train = np.expand_dims(as_array[:, 1:], axis=-1), as_array[:, 0]

    def dataset_metadata(self):
        """
        Gets:
            - (w, h, dimension)
            - num_classes
            - Training size
        """

        classes, classes_counts = np.unique(self.y_train, return_counts=True)
        classes_counts_original = dict(zip(classes, classes_counts))

        train_size, w, dimensions = self.x_train.shape
        h = 1

        return {
            'num_classes': classes.shape[0],
            'classes_counts': NumpyEncoder(classes_counts_original),
            'original_data_size': train_size,
            'datapoint_w': w,
            'datapoint_h': h,
            'dimensions': dimensions,
        }

    def dataset_completeness(self):
        return 1 - np.count_nonzero(np.isnan(self.x_train))/np.prod(self.x_train.shape)

    def dataset_consistency(self):
        amount_of_duplicates = np.sum(np.unique(self.x_train, axis=0, return_counts=True)[1] - 1)

        # Assuming the duplicates have different classes
        return 1 - amount_of_duplicates/self.x_train.shape[0]

    def dataset_quality(self):
        """
        Gets:
            - completeness
            - accuracy: NO
            - consistency
        """

        return {
            'completeness': self.dataset_completeness(),
            'consistency': self.dataset_consistency(),
        }

    def clean_dataset(self, dataset_percent, reducing_method: REDUCING_METHODS):

        goal_size = int(self.x_train.shape[0]*dataset_percent)

        # In order of most important DQ dimensions

        # Consistency
        current_consistency = self.dataset_consistency()
        if current_consistency < 1:
            left_to_remove = self.x_train.shape[0] - goal_size

            repeats = filter(lambda x: x.shape[0] > 1, pd.DataFrame(self.x_train).groupby([0]).indices.values())
            current_number_take_out = 0
            take_out_indices = []

            for repeat_index in repeats:
                current_number_take_out += repeat_index.shape[0]

                take_out_indices = [*take_out_indices, *repeat_index]

                if current_number_take_out > left_to_remove:
                    break

            self.x_train = np.delete(self.x_train, take_out_indices, axis=0)
            self.y_train = np.delete(self.y_train, take_out_indices, axis=0)

        # Completeness
        left_to_remove = self.x_train.shape[0] - goal_size

        nan_mask = np.squeeze(np.any(np.isnan(self.x_train), axis=1))
        number_to_drop_from_nan_mask = np.sum(nan_mask)

        if number_to_drop_from_nan_mask > left_to_remove:
            # If the number of values with nan is greater than how many we need to remove based on goal
            # Turn some of the nan mask to false, to keep some nans
            change_to_false_indices = random.sample(
                np.where(nan_mask == True)[0].tolist(),
                number_to_drop_from_nan_mask - left_to_remove)

            nan_mask[change_to_false_indices] = False

        self.x_train = self.x_train[~nan_mask]
        self.y_train = self.y_train[~nan_mask]

        # Remove till get goal
        if self.x_train.shape[0] > goal_size:

            if reducing_method == 'keep_distributions':
                # randomly remove data
                dropped_percentage = (self.x_train.shape[0] - goal_size)/self.x_train.shape[0]
                drop_mask = np.random.choice(
                    [True, False],
                    self.x_train.shape[0],
                    p=[1 - dropped_percentage, dropped_percentage])

                # Border Case - Ignore
                if not np.any(drop_mask):
                    drop_mask[0] = True

                self.x_train = self.x_train[drop_mask]
                self.y_train = self.y_train[drop_mask]
            elif reducing_method == 'balance_classes':
                total_to_drop = self.x_train.shape[0] - goal_size
                classes, classes_counts = np.unique(self.y_train, return_counts=True)
                classes_counts_original = dict(zip(classes, classes_counts))

                classes_counts = classes_counts_original.copy()

                sampling_strategy = {i: 0 for i in classes}

                while total_to_drop > 0:
                    class_to_drop = max(classes_counts, key=classes_counts.get)
                    sampling_strategy[class_to_drop] += 1
                    classes_counts[class_to_drop] -= 1
                    total_to_drop -= 1

                resulting_strategy = {}
                for class_ in classes_counts_original.keys():
                    resulting_strategy[class_] = classes_counts_original[class_] - sampling_strategy[class_]

                sampler = RandomUnderSampler(sampling_strategy=resulting_strategy)

                rebalanced_x_train, rebalanced_y_train = sampler.fit_resample(np.squeeze(self.x_train), self.y_train)

                permutations = np.random.permutation(rebalanced_x_train.shape[0])
                self.x_train = np.expand_dims(rebalanced_x_train[permutations], -1)
                self.y_train = rebalanced_y_train[permutations]

        full_dataset_train = np.concatenate((np.expand_dims(self.y_train, axis=1), np.squeeze(self.x_train)), axis=1, dtype='str')
        text_dataset = "\n".join([" ".join(i) for i in full_dataset_train])

        return {
            **self.dataset_metadata(),
            **self.dataset_quality(),
            'text_dataset': text_dataset,
        }


"""
あぴミク髪細分化
"""
from datetime import datetime
import sys
import pathlib
import os
import numpy as np
import random
from glob import glob
import math

# このソースのあるディレクトリの絶対パスを取得
current_dir = pathlib.Path(__file__).resolve().parent
# モジュールのあるパスを追加
sys.path.append(str(current_dir) + "/../")
sys.path.append(str(current_dir) + "/../src/")

from mmd.PmxReader import PmxReader  # noqa
from mmd.VmdReader import VmdReader  # noqa
from mmd.VmdWriter import VmdWriter  # noqa
from mmd.PmxWriter import PmxWriter  # noqa
from mmd.PmxData import (
    PmxModel,
    Vertex,
    Material,
    Bone,
    Morph,
    DisplaySlot,
    RigidBody,
    Joint,
    Bdef1,
    Bdef2,
    Bdef4,
)  # noqa
from mmd.VmdData import (
    VmdMotion,
    VmdBoneFrame,
    VmdCameraFrame,
    VmdInfoIk,
    VmdLightFrame,
    VmdMorphFrame,
    VmdShadowFrame,
    VmdShowIkFrame,
)  # noqa
from module.MMath import MRect, MVector2D, MVector3D, MVector4D, MQuaternion, MMatrix4x4  # noqa
from module.MOptions import MOptionsDataSet  # noqa
from module.MParams import BoneLinks  # noqa
from utils import MBezierUtils, MServiceUtils  # noqa
from utils.MException import SizingException  # noqa
from utils.MLogger import MLogger  # noqa
from service.PmxTailorExportService import read_vertices_from_file, VirtualVertex, calc_ratio

MLogger.initialize(level=MLogger.DEBUG, is_file=True)
logger = MLogger(__name__, level=MLogger.DEBUG)

def exec_joint():
    model = PmxReader(
        "E:/MMD/MikuMikuDance_v926x64/Work/202101_vroid/_報告/ラワイル1223/APmiku_nakedhair_IKx_onlyee_4_20221224_152739_20221224_165809_strong.pmx",
        is_check=False,
        is_sizing=False,
    ).read_data()
    print(model.name)

    right_bone_name1 = [
        "右髪1中心",
        "右髪1-007",
        "右髪1-011",
        "右髪1-015",
        "右髪1-019",
        "右髪1-023",
        "右髪1-027",
        "右髪1-031",
        "右髪1-035",
        # "右髪1-039",
        # "右髪1-043",
        # "右髪1-047",
        # "右髪1-051",
        # "右髪1-055",
    ]
    right_bone_name2 = [
        "右髪2中心",
        "右髪2-003",
        "右髪2-007",
        "右髪2-011",
        "右髪2-015",
        "右髪2-019",
        "右髪2-023",
        "右髪2-027",
        "右髪2-031",
        # "右髪2-035",
        # "右髪2-039",
        # "右髪2-043",
        # "右髪2-047",
        # "右髪2-051",
    ]
    right_bone_name3 = [
        "右髪3中心",
        "右髪3-007",
        "右髪3-011",
        "右髪3-015",
        "右髪3-019",
        "右髪3-023",
        "右髪3-027",
        "右髪3-031",
        "右髪3-035",
        # "右髪3-039",
        # "右髪3-043",
        # "右髪3-047",
    ]

    left_bone_name1 = [
        "左髪1中心",
        "左髪1-007",
        "左髪1-011",
        "左髪1-015",
        "左髪1-019",
        "左髪1-023",
        "左髪1-027",
        "左髪1-031",
        "左髪1-035",
        # "左髪1-039",
        # "左髪1-043",
        # "左髪1-047",
        # "左髪1-051",
        # "左髪1-055",
    ]
    left_bone_name2 = [
        "左髪2中心",
        "左髪2-005",
        "左髪2-009",
        "左髪2-013",
        "左髪2-017",
        "左髪2-021",
        "左髪2-025",
        "左髪2-029",
        "左髪2-033",
        # "左髪2-037",
        # "左髪2-041",
        # "左髪2-045",
        # "左髪2-049",
        # "左髪2-053",
    ]
    left_bone_name3 = [
        "左髪3中心",
        "左髪3-010",
        "左髪3-014",
        "左髪3-018",
        "左髪3-022",
        "左髪3-026",
        "左髪3-030",
        "左髪3-034",
        "左髪3-038",
        # "左髪3-042",
        # "左髪3-046",
        # "左髪3-050",
    ]

    for bone_names in [
        zip(right_bone_name1, right_bone_name1[1:], right_bone_name2, right_bone_name2[1:]),
        zip(right_bone_name2, right_bone_name2[1:], right_bone_name3, right_bone_name3[1:]),
        zip(right_bone_name3, right_bone_name3[1:], right_bone_name1, right_bone_name1[1:]),
        zip(left_bone_name1, left_bone_name1[1:], left_bone_name2, left_bone_name2[1:]),
        zip(left_bone_name2, left_bone_name2[1:], left_bone_name3, left_bone_name3[1:]),
        zip(left_bone_name3, left_bone_name3[1:], left_bone_name1, left_bone_name1[1:]),
    ]:
        for n, (a_bone_from_name, a_bone_to_name, b_bone_from_name, b_bone_to_name) in enumerate(bone_names):
            a_bone_from = model.bones[a_bone_from_name]
            a_bone_to = model.bones[a_bone_to_name]
            b_bone_from = model.bones[b_bone_from_name]
            b_bone_to = model.bones[b_bone_to_name]

            horizontal_joint = build_joint(model, a_bone_from, a_bone_to, b_bone_from, b_bone_to, "→", n)
            # reverse_joint = build_joint(model, b_bone_from, b_bone_to, a_bone_from, a_bone_to, "←")

            model.joints[horizontal_joint.name] = horizontal_joint
            # model.joints[reverse_joint.name] = reverse_joint

    PmxWriter().write(
        model,
        f"E:/MMD/MikuMikuDance_v926x64/Work/202101_vroid/_報告/ラワイル1223/APmiku_nakedhair_IKx_onlyee_4_20221224_152739_20221224_165809_strong_{datetime.now():%Y%m%d_%H%M%S}.pmx",
    )

def build_joint(
    model: PmxModel,
    a_bone_from: Bone,
    a_bone_to: Bone,
    b_bone_from: Bone,
    b_bone_to: Bone,
    direction_mark: str,
    limit: float,
):
    # 剛体 --------
    a_rigidbody = model.rigidbodies[a_bone_to.name]
    b_rigidbody = model.rigidbodies[b_bone_to.name]

    # 剛体のZ軸方向を求める -----
    a_x_direction_from_pos = a_y_direction_from_pos = a_bone_from.position
    a_x_direction_to_pos = a_bone_to.position
    a_y_direction_to_pos = a_y_direction_from_pos + MVector3D(1, 0, 0)

    # ボーン進行方向(x)
    a_x_direction_pos = (a_x_direction_to_pos - a_x_direction_from_pos).normalized()
    # ボーン進行方向に対しての横軸(y)
    a_y_direction_pos = (a_y_direction_to_pos - a_y_direction_from_pos).normalized()
    # ボーン進行方向に対しての縦軸(z)
    a_z_direction_pos = MVector3D.crossProduct(a_x_direction_pos, a_y_direction_pos)

    # --------
    b_x_direction_from_pos = b_y_direction_from_pos = b_bone_from.position
    b_x_direction_to_pos = b_bone_to.position
    b_y_direction_to_pos = b_y_direction_from_pos + MVector3D(1, 0, 0)

    # ボーン進行方向(x)
    b_x_direction_pos = (b_x_direction_to_pos - b_x_direction_from_pos).normalized()
    # ボーン進行方向に対しての横軸(y)
    b_y_direction_pos = (b_y_direction_to_pos - b_y_direction_from_pos).normalized()
    # ボーン進行方向に対しての縦軸(z)
    b_z_direction_pos = MVector3D.crossProduct(b_x_direction_pos, b_y_direction_pos)
    # --------

    # ボーン進行方向(x)
    x_direction_pos = (b_bone_to.position - a_bone_to.position).normalized()
    # ボーン進行方向に対しての縦軸(z)
    z_direction_pos = ((a_z_direction_pos + b_z_direction_pos) / 2).normalized()
    joint_qq = MQuaternion.fromDirection(z_direction_pos, x_direction_pos)
    joint_qq *= MQuaternion.fromEulerAngles(180, 0, 0)

    joint_name = f"{direction_mark}|{a_rigidbody.name}|{b_rigidbody.name}"

    joint_euler = joint_qq.toEulerAngles()
    joint_rotation_radians = MVector3D(
        math.radians(joint_euler.x()), math.radians(joint_euler.y()), math.radians(joint_euler.z())
    )

    return Joint(
        joint_name,
        joint_name,
        0,
        a_rigidbody.index,
        b_rigidbody.index,
        b_bone_to.position,
        joint_rotation_radians,
        MVector3D(0, -0.1 * (limit**1.2), 0),
        MVector3D(0, 0.1 * (limit**1.2), 0),
        MVector3D(),
        MVector3D(),
        MVector3D(),
        MVector3D(),
    )

def exec():
    model = PmxReader(
        "E:/MMD/MikuMikuDance_v926x64/Work/202101_vroid/_報告/ラワイル1223/APmiku_nakedhair_IKx_onlyee_start.pmx",
        is_check=False,
        is_sizing=False,
    ).read_data()
    print(model.name)

    for csv_file_path in glob("E:/MMD/MikuMikuDance_v926x64/Work/202101_vroid/_報告/ラワイル1223/*.csv"):
        abb_name = os.path.basename(csv_file_path.replace(".csv", ""))
        target_vertices = read_vertices_from_file(csv_file_path, model, "髪")

        params = {}
        # 簡易版オプションデータ -------------
        params["material_name"] = "髪"
        params["back_material_name"] = ""
        params["back_extend_material_names"] = []
        params["edge_material_name"] = ""
        params["edge_extend_material_names"] = []
        params["parent_bone_name"] = "頭"
        params["abb_name"] = abb_name
        params["direction"] = "下"
        params["exist_physics_clear"] = "すべて表面"
        params["special_shape"] = "なし"
        params["vertices_csv"] = ""
        params["vertices_edge_csv"] = ""
        params["vertices_back_csv"] = ""
        params["top_vertices_csv"] = ""
        params["mass"] = 0
        params["air_resistance"] = 0
        params["shape_maintenance"] = 0
        params["vertical_bone_density"] = 4

        vertex_map, virtual_vertices, threshold, max_pos, remaining_vertices = create_vertex_map(
            model, params, params["material_name"], target_vertices
        )

        (
            root_bone,
            virtual_vertices,
            registered_bones,
            bone_vertical_distances,
            bone_horizonal_distances,
            bone_connected,
            root_yidx,
        ) = create_bone(model, params, params["material_name"], virtual_vertices, vertex_map, threshold, max_pos)

        remaining_vertices = create_weight(
            model,
            virtual_vertices,
            vertex_map,
            registered_bones,
            bone_vertical_distances,
            root_yidx,
            remaining_vertices,
        )

        create_remaining_weight(model, virtual_vertices, vertex_map, remaining_vertices)

    PmxWriter().write(
        model,
        f"E:/MMD/MikuMikuDance_v926x64/Work/202101_vroid/_報告/ラワイル1223/APmiku_nakedhair_IKx_onlyee_{params['vertical_bone_density']}_{datetime.now():%Y%m%d_%H%M%S}.pmx",
    )

def get_rigidbody(model: PmxModel, bone_name: str):
    if bone_name not in model.bones:
        return None

    for rigidbody in model.rigidbodies.values():
        if rigidbody.bone_index == model.bones[bone_name].index:
            return rigidbody

    return None

def create_remaining_weight(
    model: PmxModel,
    virtual_vertices: dict,
    vertex_map: np.ndarray,
    remaining_vertices: dict,
):
    # ウェイト塗り終わった実頂点リスト
    weighted_vidxs = []

    # 塗り終わった頂点リスト
    weighted_vkeys = list(set(list(virtual_vertices.keys())) - set(list(remaining_vertices.keys())))

    weighted_poses = {}
    for vkey in weighted_vkeys:
        vv = virtual_vertices[vkey]
        if vv.vidxs():
            weighted_poses[vkey] = vv.position().data()
            weighted_vidxs.extend(vv.vidxs())

    # 登録済みのボーンの位置リスト
    bone_poses = {}
    for v_yidx in range(vertex_map.shape[0]):
        for v_xidx in range(vertex_map.shape[1]):
            if np.isnan(vertex_map[v_yidx, v_xidx]).any():
                continue

            v_key = tuple(vertex_map[v_yidx, v_xidx])
            vv = virtual_vertices[v_key]

            bones = [b for b in vv.map_bones.values() if b]
            if bones:
                # ボーンが登録されている場合、ボーン位置を保持
                bone_poses[bones[0].index] = bones[0].position.data()
                continue

            # ボーンが登録されてない箇所かつまだウェイトが塗られてないのは残頂点に入れる
            if v_key not in remaining_vertices and not vv.deform:
                remaining_vertices[v_key] = vv

    remain_cnt = len(remaining_vertices) * 2
    while remaining_vertices.items() and remain_cnt > 0:
        remain_cnt -= 1
        # ランダムで選ぶ
        vkey = list(remaining_vertices.keys())[random.randrange(len(remaining_vertices))]
        vv = remaining_vertices[vkey]

        if not vv.vidxs():
            # vidx がないのはそもそも対象外
            del remaining_vertices[vkey]
            continue

        # ウェイト済み頂点のうち、最も近いのを抽出
        weighted_diff_distances = np.linalg.norm(
            np.array(list(weighted_poses.values())) - vv.position().data(), ord=2, axis=1
        )

        nearest_vkey = list(weighted_poses.keys())[np.argmin(weighted_diff_distances)]
        nearest_vv = virtual_vertices[nearest_vkey]
        nearest_deform = nearest_vv.deform
        # nearest_vv.connected_vvs.extend(vv.vidxs())

        if not nearest_vv.deform:
            # デフォームの対象がない場合、他で埋まる可能性があるので、一旦据え置き
            continue

        if type(nearest_deform) is Bdef1:
            logger.debug(
                f"remaining1 nearest_vv: {nearest_vv.vidxs()}, weight_names: [{model.bone_indexes[nearest_deform.index0]}], total_weights: [1]"
            )

            for rv in vv.real_vertices:
                weighted_vidxs.append(rv.index)
                rv.deform = Bdef1(nearest_deform.index0)
                vv.deform = Bdef1(nearest_deform.index0)

            del remaining_vertices[vkey]
        elif type(nearest_deform) is Bdef2:
            vv.deform = nearest_deform.copy()
            # weight_bone1 = model.bones[model.bone_indexes[nearest_deform.index0]]
            # weight_bone2 = model.bones[model.bone_indexes[nearest_deform.index1]]

            # bone1_distance = vv.position().distanceToPoint(weight_bone1.position)
            # bone2_distance = vv.position().distanceToPoint(weight_bone2.position) if nearest_deform.weight0 < 1 else 0
            # weight_names = np.array([weight_bone1.name, weight_bone2.name])
            # if bone1_distance + bone2_distance != 0:
            #     total_weights = np.array(
            #         [
            #             bone1_distance / (bone1_distance + bone2_distance),
            #             bone2_distance / (bone1_distance + bone2_distance),
            #         ]
            #     )
            # else:
            #     total_weights = np.array([1, 0])
            #     logger.warning("残ウェイト計算で意図せぬ値が入ったため、BDEF1を設定します。: 対象頂点[%s]", vv.vidxs())
            # weights = total_weights / total_weights.sum(axis=0, keepdims=1)
            # weight_idxs = np.argsort(weights)

            # logger.debug(
            #     f"remaining2 nearest_vv: {vv.vidxs()}, weight_names: [{weight_names}], total_weights: [{total_weights}]"
            # )

            # if np.count_nonzero(weights) == 1:
            #     vv.deform = Bdef1(model.bones[weight_names[weight_idxs[-1]]].index)
            # elif np.count_nonzero(weights) == 2:
            #     vv.deform = Bdef2(
            #         model.bones[weight_names[weight_idxs[-1]]].index,
            #         model.bones[weight_names[weight_idxs[-2]]].index,
            #         weights[weight_idxs[-1]],
            #     )
            # else:
            #     vv.deform = Bdef4(
            #         model.bones[weight_names[weight_idxs[-1]]].index,
            #         model.bones[weight_names[weight_idxs[-2]]].index,
            #         model.bones[weight_names[weight_idxs[-3]]].index,
            #         model.bones[weight_names[weight_idxs[-4]]].index,
            #         weights[weight_idxs[-1]],
            #         weights[weight_idxs[-2]],
            #         weights[weight_idxs[-3]],
            #         weights[weight_idxs[-4]],
            #     )

            for rv in vv.real_vertices:
                weighted_vidxs.append(rv.index)
                rv.deform = vv.deform

            del remaining_vertices[vkey]

    return weighted_vidxs

def create_weight(
    model: PmxModel,
    virtual_vertices: dict,
    vertex_map: np.ndarray,
    registered_bones: dict,
    bone_vertical_distances: dict,
    root_yidx: int,
    remaining_vertices: dict,
):

    for v_xidx in range(vertex_map.shape[1]):
        for v_yidx in range(vertex_map.shape[0]):
            if np.isnan(vertex_map[v_yidx, v_xidx]).any():
                continue

            vkey = tuple(vertex_map[v_yidx, v_xidx])
            vv = virtual_vertices[vkey]

            if v_yidx < root_yidx:
                # 根元より上の場合、そのまま根元ウェイト
                vv.deform = Bdef1(registered_bones[root_yidx].index)

                for rv in vv.real_vertices:
                    rv.deform = vv.deform

                # 登録対象の場合、残対象から削除
                if vkey in remaining_vertices:
                    del remaining_vertices[vkey]

            elif registered_bones[v_yidx]:
                # 頂点位置にボーンが登録されている場合、BDEF1登録対象
                vv.deform = Bdef1(registered_bones[v_yidx].index)

                for rv in vv.real_vertices:
                    rv.deform = vv.deform

                # 登録対象の場合、残対象から削除
                if vkey in remaining_vertices:
                    del remaining_vertices[vkey]
            else:
                above_yidx = root_yidx
                if v_yidx > root_yidx:
                    above_yidx = np.max(np.where(registered_bones[:v_yidx])[0])
                if len(np.where(registered_bones[v_yidx + 1 :])[0]):
                    below_yidx = v_yidx + 1 + np.min(np.where(registered_bones[v_yidx + 1 :])[0])
                else:
                    below_yidx = np.max(np.where(registered_bones)[0])

                if above_yidx == below_yidx:
                    # 末端はBDEF1でウェイトを塗っておく
                    vv.deform = Bdef1(registered_bones[below_yidx].index)

                    for rv in vv.real_vertices:
                        rv.deform = vv.deform

                    # 登録対象の場合、残対象から削除
                    if vkey in remaining_vertices:
                        del remaining_vertices[vkey]

                    continue

                # 登録外のはBDEF2
                above_weight = np.nan_to_num(
                    (
                        np.sum(bone_vertical_distances[v_yidx:below_yidx, v_xidx])
                        / np.sum(bone_vertical_distances[above_yidx:below_yidx, v_xidx])
                    )
                )

                below_weight = np.nan_to_num(
                    (
                        np.sum(bone_vertical_distances[above_yidx:v_yidx, v_xidx])
                        / np.sum(bone_vertical_distances[above_yidx:below_yidx, v_xidx])
                    )
                )

                # ほぼ0のものは0に置換（円周用）
                total_weights = np.array([above_weight, below_weight])
                total_weights[np.isclose(total_weights, 0, equal_nan=True)] = 0

                if np.count_nonzero(total_weights):
                    deform_weights = total_weights / total_weights.sum(axis=0, keepdims=1)

                    vv.deform = Bdef2(
                        registered_bones[above_yidx].index,
                        registered_bones[below_yidx].index,
                        deform_weights[0],
                    )

                    for rv in vv.real_vertices:
                        rv.deform = vv.deform

                    # 登録対象の場合、残対象から削除
                    if vkey in remaining_vertices:
                        del remaining_vertices[vkey]
                else:
                    pass

    return remaining_vertices

def create_bone(
    model: PmxModel,
    param_option: dict,
    material_name: str,
    virtual_vertices: dict,
    vertex_map: dict,
    threshold: float,
    max_pos: MVector3D,
):
    logger.info("【%s:%s】ボーン生成", material_name, param_option["abb_name"], decoration=MLogger.DECORATION_LINE)

    # 中心ボーン生成

    # 略称
    abb_name = param_option["abb_name"]
    # 親ボーン
    parent_bone = model.bones[param_option["parent_bone_name"]]

    # 中心ボーン
    display_name, root_bone = create_root_bone(model, param_option, material_name, max_pos.copy())

    logger.info("【%s】頂点距離の算出", material_name)

    bone_horizonal_distances = np.zeros((vertex_map.shape[0], vertex_map.shape[1]))
    bone_vertical_distances = np.zeros((vertex_map.shape[0], vertex_map.shape[1]))
    bone_connected = np.zeros((vertex_map.shape[0], vertex_map.shape[1]), dtype=np.int)

    # 各頂点の距離（円周っぽい可能性があるため、頂点一個ずつで測る）
    for v_yidx in range(vertex_map.shape[0]):
        v_xidx = -1
        for v_xidx in range(0, vertex_map.shape[1] - 1):
            if not np.isnan(vertex_map[v_yidx, v_xidx]).any() and not np.isnan(vertex_map[v_yidx, v_xidx + 1]).any():
                now_v_vec = virtual_vertices[tuple(vertex_map[v_yidx, v_xidx])].position()
                next_v_vec = virtual_vertices[tuple(vertex_map[v_yidx, v_xidx + 1])].position()
                bone_horizonal_distances[v_yidx, v_xidx] = now_v_vec.distanceToPoint(next_v_vec)

                if tuple(vertex_map[v_yidx, v_xidx]) in virtual_vertices[
                    tuple(vertex_map[v_yidx, v_xidx + 1])
                ].connected_vvs or tuple(vertex_map[v_yidx, v_xidx]) == tuple(vertex_map[v_yidx, v_xidx + 1]):
                    # 前の仮想頂点と同じか繋がっている場合、True
                    bone_connected[v_yidx, v_xidx] = True

            if (
                v_yidx < vertex_map.shape[0] - 1
                and not np.isnan(vertex_map[v_yidx, v_xidx]).any()
                and not np.isnan(vertex_map[v_yidx + 1, v_xidx]).any()
            ):
                now_v_vec = virtual_vertices[tuple(vertex_map[v_yidx, v_xidx])].position()
                next_v_vec = virtual_vertices[tuple(vertex_map[v_yidx + 1, v_xidx])].position()
                bone_vertical_distances[v_yidx, v_xidx] = now_v_vec.distanceToPoint(next_v_vec)

        v_xidx += 1
        if (
            not np.isnan(vertex_map[v_yidx, v_xidx]).any()
            and not np.isnan(vertex_map[v_yidx, 0]).any()
            and vertex_map.shape[1] > 2
        ):
            # 輪を描いたのも入れとく(ウェイト対象取得の時に範囲指定入るからここでは強制)
            if tuple(vertex_map[v_yidx, 0]) in virtual_vertices[tuple(vertex_map[v_yidx, v_xidx])].connected_vvs:
                # 横の仮想頂点と繋がっている場合、Trueで有効な距離を入れておく
                bone_connected[v_yidx, v_xidx] = True

                now_v_vec = virtual_vertices[tuple(vertex_map[v_yidx, v_xidx])].position()
                next_v_vec = virtual_vertices[tuple(vertex_map[v_yidx, 0])].position()
                bone_horizonal_distances[v_yidx, v_xidx] = now_v_vec.distanceToPoint(next_v_vec)
            else:
                # とりあえずINT最大値を入れておく
                bone_horizonal_distances[v_yidx, v_xidx] = np.iinfo(np.int).max

    logger.debug("bone_horizonal_distances ------------")
    logger.debug(bone_horizonal_distances.tolist())
    logger.debug("bone_vertical_distances ------------")
    logger.debug(bone_vertical_distances.tolist())
    logger.debug("bone_connected ------------")
    logger.debug(bone_connected.tolist())

    # ボーン登録有無
    regist_bones = np.zeros(vertex_map.shape[0], dtype=np.int)

    root_yidx = np.where(
        np.array([not np.isnan(vertex_map[yidx, :]).any() for yidx in range(vertex_map.shape[0])]) == True
    )[0][0]

    # 間隔が頂点タイプの場合、規則的に間を空ける
    for v_yidx in list(range(root_yidx, vertex_map.shape[0], param_option["vertical_bone_density"])) + [
        vertex_map.shape[0] - 1,
    ]:
        regist_bones[v_yidx] = True

    registered_bones = np.full(vertex_map.shape[0], fill_value=None)
    prev_bone = None
    for v_yidx in range(vertex_map.shape[0]):
        if not regist_bones[v_yidx]:
            # 登録対象ではない場合、スルー
            continue

        # 親は既にモデルに登録済みのものを選ぶ
        parent_bone = root_bone if v_yidx == root_yidx else prev_bone

        bone_name = f"{abb_name}-{(v_yidx + 1):03d}"

        bone_pos = MVector3D(
            np.mean(vertex_map[v_yidx, np.unique(np.where(np.isnan(vertex_map[v_yidx, :]) == False)[0])], axis=0)
            * threshold
        )
        bone = Bone(bone_name, bone_name, bone_pos, parent_bone.index, 0, 0x0000 | 0x0002 | 0x0800)

        bone.parent_index = parent_bone.index
        bone.local_x_vector = (bone.position - parent_bone.position).normalized()
        bone.local_z_vector = MVector3D.crossProduct(bone.local_x_vector, MVector3D(1, 0, 0))

        # 登録対象の場合のみボーン保持
        bone.index = len(model.bones)
        parent_bone.tail_index = bone.index
        if v_yidx < vertex_map.shape[0] - 1:
            bone.flag |= 0x0008 | 0x0010 | 0x0001
            registered_bones[v_yidx] = bone

        model.display_slots[display_name].references.append((0, bone.index))

        logger.debug(f"tmp_all_bones: {bone.name}, pos: {bone.position.to_log()}")

        model.bones[bone_name] = bone
        model.bone_indexes[bone.index] = bone_name

        prev_bone = bone

    return (
        root_bone,
        virtual_vertices,
        registered_bones,
        bone_vertical_distances,
        bone_horizonal_distances,
        bone_connected,
        root_yidx,
    )

def get_bone_name(self, abb_name: str, v_yno: int, v_xno: int):
    return f"{abb_name}-{(v_yno):03d}-{(v_xno):03d}"

def create_root_bone(model: PmxModel, param_option: dict, material_name: str, root_pos: MVector3D):
    # 略称
    abb_name = param_option["abb_name"]
    # 表示枠名
    display_name = f"{abb_name}:{material_name}"
    # 親ボーン
    parent_bone = model.bones[param_option["parent_bone_name"]]

    # 中心ボーン
    root_bone = Bone(
        f"{abb_name}中心",
        f"{abb_name}Root",
        root_pos,
        parent_bone.index,
        0,
        0x0000 | 0x0002 | 0x0004 | 0x0008 | 0x0010,
    )

    root_bone.index = len(model.bones)
    model.bones[root_bone.name] = root_bone
    model.bone_indexes[root_bone.index] = root_bone.name

    # 表示枠
    model.display_slots[display_name] = DisplaySlot(display_name, display_name, 0, 0)
    model.display_slots[display_name].references.append((0, model.bones[root_bone.name].index))

    return display_name, root_bone

def create_ring(virtual_vertices: dict, vkey: tuple, rings: list):
    rings.append(vkey)

    vv: VirtualVertex = virtual_vertices[vkey]
    connected_vecs = {}
    for connected_vkey in vv.connected_vvs:
        if connected_vkey not in rings and connected_vkey in virtual_vertices:
            connected_vec = (vv.position() - virtual_vertices[connected_vkey].position()).normalized()
            connected_vecs[connected_vkey] = connected_vec.data()
    next_vkey = list(connected_vecs.keys())[np.argmin(np.abs(list(connected_vecs.values())), axis=0)[1]]
    if np.min(np.abs(list(connected_vecs.values())), axis=0)[1] > 0.3:
        return rings

    return create_ring(virtual_vertices, next_vkey, rings)

def create_vertex_map(
    model: PmxModel,
    param_option: dict,
    material_name: str,
    target_vertices: list,
):
    logger.info("【%s:%s】頂点マップ生成", material_name, param_option["abb_name"], decoration=MLogger.DECORATION_LINE)

    # 残頂点リスト
    remaining_vertices = {}

    parent_bone = model.bones[param_option["parent_bone_name"]]

    # 一旦全体の位置を把握
    n = 0
    vertex_positions = {}
    for index_idx in model.material_indices[material_name]:
        for v0_idx, v1_idx in zip(
            model.indices[index_idx],
            model.indices[index_idx][1:] + [model.indices[index_idx][0]],
        ):
            if v0_idx not in target_vertices or v1_idx not in target_vertices:
                continue
            vertex_positions[v0_idx] = model.vertex_dict[v0_idx].position.data()

        n += 1
        if n > 0 and n % 1000 == 0:
            logger.info("-- 全体メッシュ確認: %s個目:終了", n)

    material_mean_pos = MVector3D(np.mean(list(vertex_positions.values()), axis=0))
    logger.info("%s: 材質頂点の中心点算出: %s", material_name, material_mean_pos.to_log())

    # 各頂点の位置との差分から距離を測る
    v_distances = np.linalg.norm(
        (np.array(list(vertex_positions.values())) - parent_bone.position.data()), ord=2, axis=1
    )
    # 親ボーンに最も近い頂点
    nearest_vertex_idx = list(vertex_positions.keys())[np.argmin(v_distances)]
    # 親ボーンに最も遠い頂点
    farest_vertex_idx = list(vertex_positions.keys())[np.argmax(v_distances)]
    # 材質全体の傾き
    material_direction = (
        (model.vertex_dict[farest_vertex_idx].position - model.vertex_dict[nearest_vertex_idx].position)
        .abs()
        .normalized()
        .data()[np.where(np.abs([0, 1, 0]))]
    )[0]
    logger.info("%s: 材質頂点の傾き算出: %s", material_name, round(material_direction, 5))

    # 頂点間の距離
    # https://blog.shikoan.com/distance-without-for-loop/
    all_vertex_diffs = np.expand_dims(np.array(list(vertex_positions.values())), axis=1) - np.expand_dims(
        np.array(list(vertex_positions.values())), axis=0
    )
    all_vertex_distances = np.sqrt(np.sum(all_vertex_diffs**2, axis=-1))
    # 頂点同士の距離から閾値生成
    threshold = np.min(all_vertex_distances[all_vertex_distances > 0]) * 0.8

    logger.info("%s: 材質頂点の閾値算出: %s", material_name, round(threshold, 5))

    logger.info("%s: 仮想頂点リストの生成", material_name)

    virtual_vertices = {}
    all_poses = {}
    n = 0
    for index_idx in model.material_indices[material_name]:
        # 頂点の組み合わせから面INDEXを引く
        if (
            model.indices[index_idx][0] not in target_vertices
            or model.indices[index_idx][1] not in target_vertices
            or model.indices[index_idx][2] not in target_vertices
        ):
            # 3つ揃ってない場合、スルー
            continue

        v0_idx = model.indices[index_idx][0]
        v1_idx = model.indices[index_idx][1]
        v2_idx = model.indices[index_idx][2]

        v0 = model.vertex_dict[v0_idx]
        v1 = model.vertex_dict[v1_idx]
        v2 = model.vertex_dict[v2_idx]

        v0_key = v0.position.to_key(threshold)
        v1_key = v1.position.to_key(threshold)
        v2_key = v2.position.to_key(threshold)

        for vv0_key, vv1_key, vv2_key, vv0 in [
            (v0_key, v1_key, v2_key, v0),
            (v1_key, v2_key, v0_key, v1),
            (v2_key, v0_key, v1_key, v2),
        ]:
            # 仮想頂点登録（該当頂点対象）
            if vv0_key not in virtual_vertices:
                virtual_vertices[vv0_key] = VirtualVertex(vv0_key)
            all_poses[vv0_key] = vv0.position.data()
            virtual_vertices[vv0_key].append([vv0], [vv1_key, vv2_key], [index_idx])

            # 残頂点リストにまずは登録
            if vv0_key not in remaining_vertices:
                remaining_vertices[vv0_key] = virtual_vertices[vv0_key]

        logger.debug(
            f"☆表 index[{index_idx}], v0[{v0.index}:{v0_key}], v1[{v1.index}:{v1_key}], v2[{v2.index}:{v2_key}], pa[{parent_bone.position.to_log()}]"
        )

        n += 1

        if n > 0 and n % 500 == 0:
            logger.info("-- メッシュ確認: %s個目:終了", n)

    max_pos = np.max(list(all_poses.values()), axis=0)
    max_key = MVector3D(max_pos).to_key(threshold)

    min_pos = np.min(list(all_poses.values()), axis=0)
    min_key = MVector3D(min_pos).to_key(threshold)

    median_pos = np.median(list(all_poses.values()), axis=0)
    median_key = MVector3D(median_pos).to_key(threshold)

    median_vkey = min_vkey = max_vkey = None
    for x, y, z in all_poses.keys():
        if y == median_key[1] and not median_vkey:
            median_vkey = (x, y, z)
        if y == min_key[1] and not min_vkey:
            min_vkey = (x, y, z)
        if y == max_key[1] and not max_vkey:
            max_vkey = (x, y, z)

    min_pos = virtual_vertices[min_vkey].position()
    max_pos = virtual_vertices[max_vkey].position()

    # リング取得
    median_rings = create_ring(virtual_vertices, median_vkey, [])

    all_upper_vkeys = []
    all_lower_vkeys = []
    for mi, target_vkey in enumerate(median_rings):
        # 根元に向けてのライン
        upper_vkeys, upper_vscores = create_vertex_line_map(
            target_vkey,
            max_pos,
            target_vkey,
            virtual_vertices,
            [target_vkey],
            [],
            [],
        )

        # 髪上部の進行方向に合わせて下を検出する
        # ボーン進行方向(x)
        top_x_pos = (
            virtual_vertices[upper_vkeys[-2]].position() - virtual_vertices[upper_vkeys[-1]].position()
        ).normalized()
        # ボーン進行方向に対しての縦軸(y)
        top_y_pos = MVector3D(1, 0, 0)
        # ボーン進行方向に対しての横軸(z)
        top_z_pos = MVector3D.crossProduct(top_x_pos, top_y_pos)
        top_qq = MQuaternion.fromDirection(top_z_pos, top_y_pos)

        mat = MMatrix4x4()
        mat.setToIdentity()
        mat.translate(virtual_vertices[target_vkey].position())
        mat.rotate(top_qq)

        below_pos = mat * MVector3D(-3, 0, 0)

        lower_vkeys, lower_vscores = create_vertex_line_map(
            target_vkey,
            below_pos,
            target_vkey,
            virtual_vertices,
            [target_vkey],
            [],
            [],
        )

        all_upper_vkeys.append(upper_vkeys)
        all_lower_vkeys.append(lower_vkeys)

    # XYの最大と最小の抽出
    xu = len(all_upper_vkeys)
    upper_y = np.max([len(vk) for i, vk in enumerate(all_upper_vkeys)])
    lower_y = np.max([len(vk) for i, vk in enumerate(all_lower_vkeys)])

    # 存在しない頂点INDEXで二次元配列初期化
    vertex_map = np.full((upper_y + lower_y, xu, 3), (np.nan, np.nan, np.nan))

    # 根元から順にマップに埋めていく
    for x, vkeys in enumerate(all_upper_vkeys):
        for y, vkey in enumerate(reversed(vkeys)):
            vv = virtual_vertices[vkey]
            if not vv.vidxs():
                continue

            logger.debug(f"x: {x}, y: {y}, vv: {vkey}, vidxs: {vv.vidxs()}")

            vertex_map[upper_y - y, x] = vkey

    for x, vkeys in enumerate(all_lower_vkeys):
        for y, vkey in enumerate(reversed(vkeys)):
            vv = virtual_vertices[vkey]
            if not vv.vidxs():
                continue

            logger.debug(f"x: {x}, y: {y}, vv: {vkey}, vidxs: {vv.vidxs()}")

            vertex_map[upper_y + y, x] = vkey

    return vertex_map, virtual_vertices, threshold, max_pos, remaining_vertices

def create_vertex_line_map(
    bottom_key: tuple,
    top_pos: MVector3D,
    from_key: tuple,
    virtual_vertices: dict,
    vkeys: list,
    vscores: list,
    registed_vkeys: list,
    loop=0,
):

    if loop > 500:
        return None, None

    from_vv = virtual_vertices[from_key]
    from_pos = from_vv.position()

    bottom_vv = virtual_vertices[bottom_key]
    bottom_pos = bottom_vv.position()

    local_next_base_pos = MVector3D(1, 0, 0)

    # ボーン進行方向(x)
    top_x_pos = (top_pos - bottom_pos).normalized()
    # ボーン進行方向に対しての縦軸(y)
    top_y_pos = MVector3D(1, 0, 0)
    # ボーン進行方向に対しての横軸(z)
    top_z_pos = MVector3D.crossProduct(top_x_pos, top_y_pos)
    top_qq = MQuaternion.fromDirection(top_z_pos, top_y_pos)
    logger.debug(
        f" - top({bottom_vv.vidxs()}): x[{top_x_pos.to_log()}], y[{top_y_pos.to_log()}], z[{top_z_pos.to_log()}]"
    )

    scores = []
    prev_dots = []
    for n, to_key in enumerate(from_vv.connected_vvs):
        if to_key not in virtual_vertices:
            scores.append(0)
            prev_dots.append(0)
            logger.debug(f" - get_vertical_key({n}): from[{from_vv.vidxs()}], to[{to_key}], 対象外")
            continue

        if to_key in registed_vkeys:
            scores.append(0)
            prev_dots.append(0)
            logger.debug(f" - get_vertical_key({n}): from[{from_vv.vidxs()}], to[{to_key}], 登録済み")
            continue

        to_vv = virtual_vertices[to_key]
        to_pos = to_vv.position()

        direction_dot = MVector3D.dotProduct(top_x_pos, (to_pos - from_pos).normalized())

        if to_key in vkeys:
            # 到達済みのベクトルには行かせない
            scores.append(0)
            prev_dots.append(0)
            logger.debug(
                f" - get_vertical_key({n}): from[{from_vv.vidxs()}], to[{to_vv.vidxs()}], direction_dot[{direction_dot}], 到達済み"
            )
            continue

        if direction_dot < 0.3:
            # 反対方向のベクトルには行かせない
            scores.append(0)
            prev_dots.append(0)
            logger.debug(
                f" - get_vertical_key({n}): from[{from_vv.vidxs()}], to[{to_vv.vidxs()}], direction_dot[{direction_dot}], 反対方向"
            )
            continue

        mat = MMatrix4x4()
        mat.setToIdentity()
        mat.translate(from_pos)
        mat.rotate(top_qq)

        local_next_vpos = (mat.inverted() * to_pos).normalized()

        vec_yaw1 = (local_next_base_pos * MVector3D(1, 0, 1)).normalized()
        vec_yaw2 = (local_next_vpos * MVector3D(1, 0, 1)).normalized()
        yaw_score = calc_ratio(MVector3D.dotProduct(vec_yaw1, vec_yaw2), -1, 1, 0, 1)

        vec_pitch1 = (local_next_base_pos * MVector3D(0, 1, 1)).normalized()
        vec_pitch2 = (local_next_vpos * MVector3D(0, 1, 1)).normalized()
        pitch_score = calc_ratio(MVector3D.dotProduct(vec_pitch1, vec_pitch2), -1, 1, 0, 1)

        vec_roll1 = (local_next_base_pos * MVector3D(1, 1, 0)).normalized()
        vec_roll2 = (local_next_vpos * MVector3D(1, 1, 0)).normalized()
        roll_score = calc_ratio(MVector3D.dotProduct(vec_roll1, vec_roll2), -1, 1, 0, 1)

        # 前頂点との内積差を考慮する場合
        prev_dot = (
            MVector3D.dotProduct(
                (virtual_vertices[vkeys[0]].position() - virtual_vertices[vkeys[1]].position()).normalized(),
                (to_pos - virtual_vertices[vkeys[0]].position()).normalized(),
            )
            if len(vkeys) > 1
            else 1
        )

        if prev_dot < 0.5:
            # ズレた方向には行かせない
            scores.append(0)
            prev_dots.append(0)
            logger.debug(
                f" - get_vertical_key({n}): from[{from_vv.vidxs()}], to[{to_vv.vidxs()}], direction_dot[{direction_dot}], yaw_score: {round(yaw_score, 5)}, pitch_score: {round(pitch_score, 5)}, roll_score: {round(roll_score, 5)}, 前ズレ方向"
            )
            continue

        score = ((yaw_score * 20) + pitch_score + (roll_score * 2)) * (prev_dot**3)

        # scores.append(score * (2 if to_key in top_keys else 1))
        scores.append(score)
        prev_dots.append(prev_dot * direction_dot)

        logger.debug(
            f" - get_vertical_key({n}): from[{from_vv.vidxs()}], to[{to_vv.vidxs()}], direction_dot[{direction_dot}], prev_dot[{prev_dot}], local_next_vpos[{local_next_vpos.to_log()}], score: [{score}], yaw_score: {round(yaw_score, 5)}, pitch_score: {round(pitch_score, 5)}, roll_score: {round(roll_score, 5)}"
        )

    if np.count_nonzero(scores) == 0:
        # スコアが付けられなくなったら終了
        return vkeys, vscores

    # 最もスコアの高いINDEXを採用
    nearest_idx = np.argmax(scores)
    vertical_key = from_vv.connected_vvs[nearest_idx]

    # 前の辺との内積差を考慮する
    prev_diff_dot = (
        MVector3D.dotProduct(
            (virtual_vertices[vkeys[0]].position() - virtual_vertices[vkeys[1]].position()).normalized(),
            (virtual_vertices[vertical_key].position() - virtual_vertices[vkeys[0]].position()).normalized(),
        )
        if len(vkeys) > 1
        else 1
    )

    logger.debug(
        f"direction: from: [{virtual_vertices[from_key].vidxs()}], to: [{virtual_vertices[vertical_key].vidxs()}], prev_diff_dot[{round(prev_diff_dot, 4)}]"
    )

    vkeys.insert(0, vertical_key)
    vscores.insert(0, np.max(scores) * prev_diff_dot)

    # 髪上部の進行方向に合わせて進行方向を検出する
    # ボーン進行方向(x)
    top_x_pos = (virtual_vertices[vertical_key].position() - virtual_vertices[from_key].position()).normalized()
    # ボーン進行方向に対しての縦軸(y)
    top_y_pos = MVector3D(1, 0, 0)
    # ボーン進行方向に対しての横軸(z)
    top_z_pos = MVector3D.crossProduct(top_x_pos, top_y_pos)
    top_qq = MQuaternion.fromDirection(top_z_pos, top_y_pos)

    mat = MMatrix4x4()
    mat.setToIdentity()
    mat.translate(virtual_vertices[from_key].position())
    mat.rotate(top_qq)

    top_pos = mat * MVector3D(3, 0, 0)

    return create_vertex_line_map(
        bottom_key,
        top_pos,
        vertical_key,
        virtual_vertices,
        vkeys,
        vscores,
        registed_vkeys,
        loop + 1,
    )

def get_edge_lines(edge_line_pairs: dict, start_vkey: tuple, edge_lines: list, edge_vkeys: list, loop=0):
    if len(edge_vkeys) >= len(edge_line_pairs.keys()) or loop > 500:
        return start_vkey, edge_lines, edge_vkeys

    if not start_vkey:
        # 下: Y(降順) - X(中央揃え) - Z(降順)
        sorted_edge_line_pairs = sorted(
            list(set(edge_line_pairs.keys()) - set(edge_vkeys)), key=lambda x: (-x[1], abs(x[0]), -x[2])
        )
        start_vkey = sorted_edge_line_pairs[0]
        edge_lines.append([start_vkey])
        edge_vkeys.append(start_vkey)

    # 下: X(中央揃え) - Z(降順) - Y(降順)
    sorted_edge_line_pairs = sorted(edge_line_pairs[start_vkey], key=lambda x: (abs(x[0]), -x[2], -x[1]))

    for next_vkey in sorted_edge_line_pairs:
        if next_vkey not in edge_vkeys:
            edge_lines[-1].append(next_vkey)
            edge_vkeys.append(next_vkey)
            start_vkey, edge_lines, edge_vkeys = get_edge_lines(
                edge_line_pairs, next_vkey, edge_lines, edge_vkeys, loop + 1
            )

    return None, edge_lines, edge_vkeys

if __name__ == "__main__":
    exec_joint()


"""
This module add the support of atomicswaps to the rivine light weight client
"""
import time
import hashlib
from Jumpscale import j
from JumpscaleLib.clients.blockchain.rivine import utils
from JumpscaleLib.clients.blockchain.rivine.encoding import binary
from JumpscaleLib.clients.blockchain.rivine.errors import InvalidAtomicswapContract, AtomicSwapError
from JumpscaleLib.clients.blockchain.rivine.types.unlockconditions import ATOMICSWAP_CONDITION_TYPE, UnlockCondtionFactory, AtomicSwapCondition
from JumpscaleLib.clients.blockchain.rivine.const import HASTINGS_TFT_VALUE, ATOMICSWAP_SECRET_SIZE, MINIMUM_TRANSACTION_FEE
from JumpscaleLib.clients.blockchain.rivine.types.transaction import TransactionFactory, DEFAULT_TRANSACTION_VERSION, HASHTYPE_COINOUTPUT_ID

logger = j.logger.get(__name__)

class AtomicSwapManager:
    """
    A manager class to expose high level APIs for atomicswap operations
    """

    def __init__(self, wallet):
        """
        Initializes a new AtomicSwapManager instance

        @param wallet: An instance of RivineWallet that will be used to create tansactions
        """
        self._wallet = wallet

    def _create_atomicswap_contract(self, receiver, amount, hashed_secret, duration, refund_address):
        """
        Create an atomic swap contract
        """
        contract = {
            'amount': amount
        }
        if refund_address is None:
            refund_address = self._wallet.generate_address()
        # convert amount to hastings
        actuall_amount = int(amount * HASTINGS_TFT_VALUE)
        if type(duration) == int:
            locktime = duration
        else:
            locktime = int(time.time()) + utils.locktime_from_duration(duration=duration)
        input_results, used_addresses, minerfee, remainder = self._wallet._get_inputs(amount=actuall_amount)
        transaction = TransactionFactory.create_transaction(version=DEFAULT_TRANSACTION_VERSION)
        for input_result in input_results:
            transaction.add_coin_input(**input_result)

        if hashed_secret is None:
            # generate a secret
            secret = utils.get_secret(size=ATOMICSWAP_SECRET_SIZE)
            # hash the secret
            hashed_secret = hashlib.sha256(secret).digest().hex()
            contract['secret'] = secret.hex()
        else:
            secret = None
        contract['hashed_secret'] = hashed_secret

        ats_coin_output = transaction.add_atomicswap_output(value=actuall_amount,recipient=receiver,
                                         locktime=locktime, refund_address=refund_address,
                                         hashed_secret=hashed_secret)

        # we need to check if the sum of the inputs is more than the required fund and if so, we need
        # to send the remainder back to the original user
        if remainder > 0:
            # we have leftover fund, so we create new transaction, and pick on user key that is not used
            address = self._wallet.generate_address()
            transaction.add_coin_output(value=remainder, recipient=address)

        # add minerfee to the transaction
        transaction.add_minerfee(minerfee)

        # sign the transaction
        self._wallet._sign_transaction(transaction)

        # commit transaction
        txn_id = self._wallet._commit_transaction(transaction)
        contract['transaction_id'] = txn_id

        # retrieve the info from the transaction
        ats_output_id = None
        txn_info = self._wallet._check_address(txn_id)
        coinoutputs = txn_info.get("transaction", {}).get('rawtransaction', {}).get('data', {}).get('coinoutputs', [])
        if coinoutputs:
            for index, coin_output in enumerate(coinoutputs):
                if coin_output == ats_coin_output.json:
                    ats_output_id = txn_info['transaction']['coinoutputids'][index]

        contract['output_id'] = ats_output_id

        return contract

    def participate(self, initiator_address, amount, hashed_secret, duration='24h0m0s', refund_address=None):
        """
        Create an atomic swap contract as participant

        @param initiator_address: Address of the initiator (unlockhash) to send the money to.
        @param amount: The mount of TF tokens to use for the atomicswap contract
        @oaran hashed_secret: Hash of the secret that was created during the initiate process
        @param duration: The duration of the atomic swap contract, the amount of time the initiator has to collect (default 24h0m0s)
        @param refund_address: Address to receive the funds back if transaction is not compeleted (AUTO selected from the wallet if not provided)
        """
        return self._create_atomicswap_contract(receiver=initiator_address,
                                                amount=amount,
                                                hashed_secret=hashed_secret,
                                                duration=duration,
                                                refund_address=refund_address)

    def initiate(self, participant_address, amount, duration='48h0m0s', refund_address=None):
        """
        Create an atomic swap contract as initiator

        @param participant_address: Address of the participant (unlockhash) to send the money to.
        @param amount: The mount of TF tokens to use for the atomicswap contract
        @param duration: The duration of the atomic swap contract, the amount of time the participator has to collect (default 48h0m0s)
        @param refund_address: Address to receive the funds back if transaction is not compeleted (AUTO selected from the wallet if not provided)
        """

        return self._create_atomicswap_contract(receiver=participant_address,
                                                amount=amount,
                                                hashed_secret=None,
                                                duration=duration,
                                                refund_address=refund_address)

    def validate(self, transaction_id, amount=None, hashed_secret=None, receiver_address=None, time_left=None):
        """
        Validates that the given output id exist in the consensus as an unspent output

        @param transaction_id: Transaction id from from initiate or participate contract to validate
        @param amount: Amount to validaate against the output information
        @param hashed_secret: Validate the secret of the found atomic swap contract condition by comparing its hashed version with this secret hash
        @param receiver_address: Validate the given receiver's address (unlockhash) to the one found in the atomic swap contract condition
        @param time_left: Minimum time left for the contract, if the contract locktime is expired in less than this value the contract will be considered invalid
        """
        return self._validate(transaction_id=transaction_id,
                             amount=amount,
                             hashed_secret=hashed_secret,
                             receiver_address=receiver_address,
                             time_left=time_left)[0]

    def _validate(self, transaction_id, amount=None, hashed_secret=None, receiver_address=None, time_left=None):
        """
        An internal function that does all the validation the public validate method does, but return more info
        """
        output_id, output_result = self._get_output_info_from_id(transaction_id=transaction_id)
        if output_result:
            if amount and int(output_result['value']) != amount * HASTINGS_TFT_VALUE:
                raise InvalidAtomicswapContract("Contract amount does not match the provided amount value")
            if output_result['condition']['type'] != binary.decode(ATOMICSWAP_CONDITION_TYPE, type_=int):
                raise InvalidAtomicswapContract("Condition type is not correct")
            if receiver_address and output_result['condition']['data']['receiver'] != receiver_address:
                raise InvalidAtomicswapContract("Receiver address does not match the provided address")
            if hashed_secret and output_result['condition']['data']['hashedsecret'] != hashed_secret:
                raise InvalidAtomicswapContract("Hashed secret does not match the provided hashed secret")
            if time_left and abs(output_result['condition']['data']['timelock'] - time.time()) < time_left:
                raise InvalidAtomicswapContract("Contract will expired in less than the minimum time specified")
        else:
            raise InvalidAtomicswapContract("Could not validate atomicswap contract for transaction {}".format(transaction_id))
        return True, output_result, output_id

    def _get_output_info_from_id(self, transaction_id):
        """
        Retrieves information about an atomicswap output from a given id
        """

        txn_info = self._wallet._check_address(transaction_id)['transaction']
        for idx, coin_output in enumerate(txn_info['rawtransaction']['data']['coinoutputs']):
            condition = UnlockCondtionFactory.from_dict(coin_output['condition'])
            if isinstance(condition, AtomicSwapCondition):
                return txn_info['coinoutputids'][idx], coin_output

        return None, None

    def _spend_atomicswap_contract(self, transaction_id, secret=None):
        """
        Spends an atomicswap contract
        Can be used by both refund and redeem operations
        """
        _, output_info, output_id = self._validate(transaction_id=transaction_id)
        if secret is None:
            # spending the output as refund
            # fetch the sender address from the output_info
            address = output_info['condition']['data']['sender']
        else:
            # spend the output as redeem
            address = output_info['condition']['data']['receiver']
        if address not in self._wallet._keys:
            raise AtomicSwapError('Address found in the output does not belong to the current wallet')
        key = self._wallet._keys[address]
        value = int(output_info['value'])
        if value <= MINIMUM_TRANSACTION_FEE:
            raise AtomicSwapError("Value of the atomicswap contract is less than or equal to the minimum transaction fee.")
        # create a new transaction
        transaction = TransactionFactory.create_transaction(version=DEFAULT_TRANSACTION_VERSION)
        transaction.add_atomicswap_input(parent_id=output_id, pub_key=key.public_key,
                                        secret=secret)
        txn_value = value - MINIMUM_TRANSACTION_FEE
        transaction.add_coin_output(value=txn_value, recipient=address)
        transaction.add_minerfee(minerfee=MINIMUM_TRANSACTION_FEE)

        # sign the transaction input
        input_idx = 0
        transaction.coin_inputs[input_idx].sign(input_idx=input_idx,
                                                 transaction=transaction,
                                                 secret_key=key.secret_key
                                                 )

        # commit transaction
        txn_id = self._wallet._commit_transaction(transaction)
        return txn_id

    def refund(self, transaction_id):
        """
        Spends an atomicswap contract

        @param transaction_id: Transaction id from the atomicswap contract
        """
        txn_id = self._spend_atomicswap_contract(transaction_id=transaction_id)
        logger.info("Refund executed successfully. Transaction ID: {}".format(txn_id))
        return txn_id

    def redeem(self, transaction_id, secret):
        """
        Complete the atomicswap contract by spending a the output as the receiver of the atomicswap

        @param transaction_id: Transaction ID of the transaction to redeemed
        @param secret: Atomicswap secert
        """
        txn_id = self._spend_atomicswap_contract(transaction_id=transaction_id, secret=secret)
        logger.info("Redeem executed successfully. Transaction ID: {}".format(txn_id))
        return txn_id


from base.core import Background, Race

# Creating Human Backgrounds

criminal = Background("Criminal")
peasant = Background("Peasant")
outlander = Background("Outlander")
entertainer = Background("Entertainer")
artisan = Background("Artisan")
sailor = Background("Sailor")
soldier = Background("Soldier")
sage = Background("Sage")
acolyte = Background("Acolyte")
noble = Background("Noble")

# Creating Kits for each Background

criminal.addkit("Dagger", "Cloak")
criminal.addkit("Dagger", "Thieves' Tools")
criminal.addkit("Short Sword", "Leather Armor")

peasant.addkit("Pitchfork", "5 Rations of Vegetables")
peasant.addkit("Staff", "Rope")
peasant.addkit("Dagger", "Chicken")

outlander.addkit("Staff", "Torch")
outlander.addkit("Short Bow", "Deer Pelt")
outlander.addkit("Axe", "Herbs")

entertainer.addkit("Dagger", "Musical Instrument")
entertainer.addkit("Club", "Gambling Set")

artisan.addkit("Hammer", "Smith's Tools")
artisan.addkit("Dagger", "Nice Rug (Worth 20GP)")

sailor.addkit("Short Sword", "Rope")
sailor.addkit("Trident", "Net")

soldier.addkit("Spear", "Shield")
soldier.addkit("Longsword", "Hide Armor")

sage.addkit("Dart", "10 sheets of parchment")

acolyte.addkit("Dagger", "Holy Water")

noble.addkit("Longsword", "Signet Ring (Worth 10GP)")


# 50.042 FCS Lab 7 template
# Year 2019
# See Wan Yi Faith (1002851)

from Crypto.PublicKey import RSA
from base64 import b64encode,b64decode
import random

def square_multiply(a,x,n):
    y = 1

    coeffs = []
    binX = bin(x)           # convert x to binary
    for i in binX[2:]:      # ignore first 2 characters: 0b
        coeffs.append(int(i))

    n_b = (len(binX) - 2)   # n_b is the number of bits in x

    for i in range(n_b):
        y = y**2 % n
        if coeffs[i] == 1:
            y = a*y % n

    return y

# function to convert long int to byte string
def pack_bigint(i):
    b=bytearray()
    while i:
        b.append(i&0xFF)
        i>>=8
    return b

# function to convert byte string to long int
def unpack_bigint(b):
    b=bytearray(b)
    return sum((1<<(bi*8))* bb for (bi,bb) in enumerate(b))

if __name__=="__main__":
    pass

50.042 Foundations of Cybersecurity/Lab 7/Solution/Part I/pyrsa_sq_mul.py


"""
Задача 32: Определить индексы элементов массива (списка),
значения которых принадлежат заданному диапазону
(т.е. не меньше заданного минимума и не больше заданного максимума)
Ввод: [-5, 9, 0, 3, -1, -2, 1, 4, -2, 10, 2, 0,-9, 8, 10, -9, 0, -5, -5, 7]
Диапазон от 6 до 12
Вывод: [1, 9, 13, 14, 19]

"""

# решение 1 по условиям задачи
#         0  1  2  3   4   5  6  7   8  9  10  11 12 13  14  15  16  17  18  19
list1 = [-5, 9, 0, 3, -1, -2, 1, 4, -2, 10, 2, 0, -9, 8, 10, -9,  0, -5, -5, 7]
print(*list1)
min = 6
max = 12
array = [0]*len(list1)

for i in range(len(list1)):
    if min <= list1[i] <= max + 1:
        array.append(i)
        print(i)

# решение через рандомное заполнение массива
import random

list2 = [random.randint(-10, 11) for i in range(random.randint(10, 20))]
print(list2)
max = int(input('Введите максимум: \n'))
min = int(input('Введите минимум: \n'))
array = []
for i in range(len(list2)):
    if min <= list2[i] <= max:
        array.append(i)
print(f'Количество элементов в массиве в заданном диапазоне равно {len(array)}')
print(f'Индексы элементов в диапазоне: {array}')

# эталонное решение от ГБ - не выводит индексы
list3 = [-5, 9, 0, 3, -1, -2, 1, 4, -2, 10, 2, 0, -9, 8, 10, -9, 0, -5, -5, 7]
print(f'список 3 \n {list3}')
min_number = int(input('Введите минимум: \n'))
max_number = int(input('Введите максимум: \n'))
for i in range(len(list3)):
    if min_number <= list3[i] <= max_number:
        print(list3[i])


import cv2
import matplotlib.pyplot as MPL
import numpy as np

# image = cv2.imread("D:/Capstone Project/anh do an/anh canh tay/2021-06-11_23-07-42.jpg")
# MPL.imshow(image, cmap=MPL.cm.gray, interpolation='nearest')
# MPL.show()

# a = np.linspace(45,90, 1)
# locs = np.where(image == a)
# # pixels = image[locs]
# Uvein = np.mean(locs)
# print(Uvein)

# b = np.linspace(100, 165, 1)
# locs1 = np.where(image == b)
# # pixels = image[locs]
# Uskin = np.mean(locs1)
# print(Uskin)

# ảnh sẽ được cắt thành 1 phần nhỏ (ROI) từ ảnh xám để lấy được vùng trung bình của tĩnh mạch
imagevein = cv2.imread("Your path")
# ảnh sẽ được cắt thành 1 phần nhỏ (ROI) từ ảnh xám để lấy được vùng trung bình của da
imageskin = cv2.imread("Your path")
cv2.imshow('vein', imagevein)
cv2.waitKey(0)
cv2.imshow('skin', imageskin)
cv2.waitKey(0)

Uvein = np.mean(imagevein)
Uskin = np.mean(imageskin)

M = np.abs(Uvein - Uskin)/(Uvein + Uskin)
print(M)


import gym
import gym_plane
from pandas import read_csv
import matplotlib.pyplot as plt
import numpy as np

def run():
    info_dict = {}
    #info_dict["target_traj_yml"] = '/home/frc-ag-3/harry_ws/courses/grad_ai/final_project/trajectories/iLQR_loop/iqrl.yml'
    info_dict["target_traj_yml"] = '/home/frc-ag-3/harry_ws/courses/grad_ai/final_project/trajectories/iLQR_immelman/iqrl.yml'
    info_dict["obs_bound_factor"] = 2.0
    info_dict["pos_tol"] = 1.0

    env = gym.make("plane-v0", info_dict=info_dict)
    _ = env.reset()

    done = False
    safety_max = 1000
    safety_count = 0
    rewards = []
    positions = []
    while (not done) and (safety_count < safety_max):
        #random_action = env.action_space.sample()
        #obs, reward, done, _ = env.step(random_action)
        u = env.ilqr_control[safety_count]
        action = env.normalize_action(u)

        obs, reward, done, _ = env.step(action)
        pos = (obs[0:3]*env.pos_std) + env.pos_mean

        positions.append(pos)
        rewards.append(reward)

        safety_count += 1

    if safety_count == safety_max:
        raise RuntimeError("Safety limit reached")

    fig = plt.figure()
    ax = fig.add_subplot(projection='3d')

    positions = np.array(positions)
    rewards = np.array(rewards)

    print(rewards.shape)

    ax.scatter(positions[:, 0], positions[:, 1], positions[:, 2])
    plt.show()

if __name__ == "__main__":
    run()


import csv
import os
import requests
import datetime
import threading

path = "C:/GM1000k/"
def createFile(name, finalList):
    filename = name + ".txt"
    file = open(path + filename,'w')
    for i in finalList:
        temp = ""
        for index, j in enumerate(i):
            if index == 0:
                temp = j
            else:
                temp += "," + j
        temp +="\n"
        file.write(temp)
    file.close()

def newOrder(name, testList):
    finalList = []
    for i in testList:
        spl = i[0].split('-')
        d = spl[1]+'/'+spl[2]+'/'+spl[0]
        temp = [d,name,i[4],i[2],i[3],i[1],i[5],"stockname","sector"]
        finalList.append(temp)
    createFile(name, finalList)

def readCSV(name):
    filename = name + ".csv"
    file = open(path + filename, "r")
    reader = csv.reader(file)
    testList = []
    for index, i in enumerate(reader):
        if index > 0:
            testList.append(i)
    file.close()
    os.remove(path + filename)
    newOrder(name, testList[::-1])

def writeCSV(name):
    now = datetime.datetime.now()
    curr_year = str(now.year)
    curr_month = str(now.month -1)
    curr_day = str(now.day)
    past_year = str(now.year -5)
    newpath = ""
    firstpart = "http://chart.finance.yahoo.com/table.csv?s="
    newpath  = firstpart+name+"&a="+curr_month+"&b="+curr_day+"&c="+past_year+"&d="+curr_month+"&e="+curr_day+"&f="+curr_year+"&g=d&ignore=.csv"
    chartdata = requests.get(newpath)

    if "<html" in chartdata.text:
        print("Error Pulling Chart...")
    else:
        filename = name + ".csv"
        file = open(path + filename,'w')
        file.write(chartdata.text)
        file.close()
        readCSV(name)

def looper(f):
    for i in f:
        print(i)
        writeCSV(i.strip("\n"))

def fullList():
    bigList = []
    f = open("BelowDollar.source",'r')
    for i in f:
        bigList.append(i)
    f.close()

    threadCount = 200  #EDIT NUMBER OF THREADS HERE
    threads = []
    count = 0
    div = 1/threadCount
    for i in range(threadCount):
        if count < 1:
            t = threading.Thread(target=looper, args= (bigList[round(len(bigList)*count):round(len(bigList)*(count+div))],))
            threads.append(t)
            t.start()
            count += div

    #a = threading.Thread(target=looper, args= (bigList[:round(len(bigList)*.25)],))
    #b = threading.Thread(target=looper, args= (bigList[round(len(bigList)*.25):round(len(bigList)*.5)],))
    #c = threading.Thread(target=looper, args= (bigList[round(len(bigList)*.5):round(len(bigList)*.75)],))
    #d = threading.Thread(target=looper, args= (bigList[round(len(bigList)*.75):],))

    #a.start()
    #b.start()
    #c.start()
    #d.start()

fullList()