"""
Configuration Loader
--------------------
Various functions that assist in loading initial configuration for the
control plane.

The control plane accepts a main configuration file from the environment
variable ``SOVEREIGN_CONFIG`` which follows the format:

.. code-block:: none

   <scheme>://path[,<scheme>://path,...]

Examples:

.. code-block:: none

   # Single file
     file:///etc/sovereign.yaml

   # Multiple files (comma separated)
     file:///etc/sovereign/common.yaml,file:///etc/sovereign/dev.yaml

   # Other types of sources
     http://config.myserver.com/environments/dev.yaml

   # Other types of formats
     http+json://config.myserver.com/environments/dev.json
     http+jinja://config.myserver.com/environments/dev.j2
     http+yaml://config.myserver.com/environments/dev.yaml

"""
import os
import json
import yaml
import jinja2
import requests
import importlib
from importlib.machinery import SourceFileLoader
from pathlib import Path
from pkg_resources import resource_string

jinja_env = jinja2.Environment(enable_async=True, autoescape=True)

serializers = {
    'yaml': yaml.safe_load,
    'json': json.loads,
    'jinja': jinja_env.from_string,
    'string': str,
}

def raise_(e):
    raise e

try:
    import ujson
    serializers['ujson'] = ujson.loads
    jinja_env.policies['json.dumps_function'] = ujson.dumps  # Changes the json dumper in jinja2
except ImportError:
    # This lambda will raise an exception when the serializer is used; otherwise we should not crash
    serializers['ujson'] = lambda *a, **kw: raise_(ImportError('ujson must be installed to use in config_loaders'))

try:
    import orjson
    serializers['orjson'] = orjson.loads
    # orjson.dumps returns bytes, so we have to wrap & decode it
    jinja_env.policies['json.dumps_function'] = lambda *a, **kw: orjson.dumps(*a, **kw).decode()
    jinja_env.policies['json.dumps_kwargs'] = {'option': orjson.OPT_SORT_KEYS}  # default in jinja is to sort keys
except ImportError:
    # This lambda will raise an exception when the serializer is used; otherwise we should not crash
    serializers['orjson'] = lambda *a, **kw: raise_(ImportError('orjson must be installed to use in config_loaders'))

try:
    import boto3
except ImportError:
    boto3 = None

def load_file(path, loader):
    with open(path) as f:
        contents = f.read()
        return serializers[loader](contents)

def load_package_data(path, loader):
    pkg, pkg_file = path.split(':')
    data = resource_string(pkg, pkg_file)
    try:
        data = data.decode()
    except AttributeError:
        pass
    return serializers[loader](data)

def load_http(path, loader):
    response = requests.get(path)
    response.raise_for_status()
    data = response.text
    return serializers[loader](data)

def load_env(variable, loader=None):
    data = os.getenv(variable)
    try:
        return serializers[loader](data)
    except AttributeError as e:
        raise AttributeError(f'Unable to read environment variable {variable}: {repr(e)}')

def load_module(name, _=None):
    return importlib.import_module(name)

def load_s3(path: str, loader=None):
    if isinstance(boto3, type(None)):
        raise ImportError('boto3 must be installed to load S3 paths. Use ``pip install sovereign[boto]``')
    bucket, key = path.split('/', maxsplit=1)
    s3 = boto3.client('s3')
    response = s3.get_object(Bucket=bucket, Key=key)
    data = ''.join([chunk.decode() for chunk in response['Body']])
    return serializers[loader](data)

def load_python(path, _=None):
    p = Path(path).absolute()
    loader = SourceFileLoader(p.name, path=str(p))
    return loader.load_module(p.name)

loaders = {
    'file': load_file,
    'pkgdata': load_package_data,
    'http': load_http,
    'https': load_http,
    'env': load_env,
    'module': load_module,
    's3': load_s3,
    'python': load_python,
}

def parse_spec(spec, default_serialization='yaml'):
    serialization = default_serialization
    scheme, path = spec.split('://')
    if '+' in scheme:
        scheme, serialization = scheme.split('+')
    if 'http' in scheme:
        path = '://'.join([scheme, path])
    return scheme, path, serialization

def is_parseable(spec):
    if '://' not in spec:
        return False
    scheme, _, serialization = parse_spec(spec)
    return (
        scheme in loaders and
        serialization in serializers
    )

def load(spec, default=None):
    if '://' not in spec:
        return spec
    scheme, path, serialization = parse_spec(spec)

    try:
        return loaders[scheme](path, serialization)
    except Exception:
        if default is not None:
            return default
        raise


from django.core.management.base import BaseCommand
from django.conf import settings
from django.core.exceptions import ObjectDoesNotExist
import qrcode
import telegram
import os
from telegram import KeyboardButton, ReplyKeyboardMarkup, ReplyKeyboardRemove, InlineKeyboardMarkup, InlineKeyboardButton
from ...models import UserProfile, JobProfile
from telegram.ext import Updater, CommandHandler, MessageHandler, Filters
from telegram.utils.request import Request
from io import BytesIO
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding, rsa
from .start_keyboard import start_keyboard, start_job_keyboard
import re
from datetime import datetime
import decimal
import cv2

"""
КОД КЛИЕНТА
"""

def start(update, context):
    context.bot_data['state'] = 'start'
    user = update.effective_user
    user_id = user.id

    # Проверяем, есть ли пользователь в базе данных
    try:
        user_profile = UserProfile.objects.get(external_id=user_id)
        context.bot.send_message(chat_id=update.effective_chat.id,
                                 text='🟢 Вы уже зарегистрированы!')
    except UserProfile.DoesNotExist:
        # Отправляем запрос на номер телефона
        context.bot.send_message(chat_id=update.effective_chat.id, text='Пожалуйста, отправьте ваш номер телефона', reply_markup=ReplyKeyboardRemove(remove_keyboard=True))
        context.bot.send_message(chat_id=update.effective_chat.id, text='Для этого нажмите на кнопку "Отправить номер 📲"', reply_markup=ReplyKeyboardMarkup([[KeyboardButton('📲 Отправить номер', request_contact=True)]], resize_keyboard=True, one_time_keyboard=True))
        return
    start_keyboard(update, context)

def handle_phone_number(update, context):
    state = context.bot_data.get('state')
    if state == 'start':
        user = update.effective_user
        user_id = user.id
        phone_number = update.effective_message.contact.phone_number

        # Проверяем, есть ли пользователь в базе данных
        try:
            user_profile = UserProfile.objects.get(external_id=user_id)
            context.bot.send_message(chat_id=update.effective_chat.id, text='🟢 Вы уже зарегистрированы!')
        except UserProfile.DoesNotExist:

            # Создаем профиль пользователя
            user_profile = UserProfile.objects.create(external_id=user_id, phone_number=phone_number)
            # Создаем QR-код
            qr_data = f'🆔 ID : {user_profile.external_id}' \
                      f'📉 Скидка: {user_profile.discount_percentage}%'

            # Генерация ключевой пары RSA
            private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
            qr_code = create_signed_qr_code(user_profile, qr_data, private_key)
            user_profile.qr_code = qr_code  # Привязываем QR-код к профилю пользователя
            user_profile.save()

            # Отправляем QR-код
            context.bot.send_message(chat_id=update.effective_chat.id, text='✅ Вы успешно зарегистрированы!')
            context.bot.send_message(chat_id=update.effective_chat.id, text=f'🆔 Ваш ID: {user_profile.external_id}\n🔳 Персональный QR-код: ',
                                     reply_markup=ReplyKeyboardRemove(remove_keyboard=True))
            context.bot.send_photo(chat_id=update.effective_chat.id, photo=open(qr_code, 'rb'))
            context.bot.send_message(chat_id=update.effective_chat.id, text='📁 Данные будут доступны во вкладке "🪪 Виртуальная карта"')
            start_keyboard(update, context)
    elif state == 'start_job':
        user = update.effective_user
        user_id = user.id
        phone_number = update.effective_message.contact.phone_number

        # Проверяем, есть ли пользователь в базе данных
        try:
            job_profile = JobProfile.objects.get(external_id=user_id)
            context.bot.send_message(chat_id=update.effective_chat.id, text='🟢 Вы уже зарегистрированы!')
        except JobProfile.DoesNotExist:
            # Создаем профиль пользователя
            job_profile = JobProfile.objects.create(external_id=user_id, phone_number=phone_number)
            job_profile.save()

            # Отправляем информацию о работнике
            context.bot.send_message(chat_id=update.effective_chat.id, text='✅ Вы успешно зарегистрированы!')
            context.bot.send_message(chat_id=update.effective_chat.id, text=f'Ваш ID: {job_profile.external_id}', reply_markup=ReplyKeyboardRemove(remove_keyboard=True))
            start_job_keyboard(update, context)
    else:
        pass
def create_signed_qr_code(user_profile, data, private_key,):

    # Создание цифровой подписи
    signature = private_key.sign(
        data.encode(),
        padding.PSS(
            mgf=padding.MGF1(hashes.SHA256()),
            salt_length=padding.PSS.MAX_LENGTH
        ),
        hashes.SHA256()
    )
    user_profile.signature = signature  # Сохраняем цифровую подпись в профиле пользователя
    user_profile.save()
    qr_data = f'{data}\nЦифровая подпись: {signature.hex()}'
    qr = qrcode.QRCode(version=1,error_correction=qrcode.constants.ERROR_CORRECT_L,box_size=10,border=4)
    qr.add_data(data)
    qr.make(fit=True)
    qr_img = qr.make_image(fill_color="black", back_color="white")
    qr_bytes = BytesIO()
    qr_img.save(qr_bytes, format='PNG')
    qr_bytes.seek(0)

    # Генерируем имя файла на основе ID пользователя и номера телефона
    qr_code_filename = f"qr_code_{user_profile.external_id}.png"
    qr_code_path = os.path.join("media", "qr_codes", qr_code_filename)

    # Сохраняем QR-код в файл
    with open(qr_code_path, 'wb') as f:
        f.write(qr_bytes.read())

    return qr_code_path

def virtual_card(update, context):
    user = update.effective_user
    user_id = user.id
    user_profile = UserProfile.objects.get(external_id=user_id)
    context.bot.send_message(chat_id=update.effective_chat.id, text='🪪 Ваша виртуальная карта:')

    # Создаем QR-код
    qr_data = f'ID : {user_profile.external_id}\n Скидка: {user_profile.discount_percentage}%\n'

    # Генерация ключевой пары RSA
    private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
    qr_code = create_signed_qr_code(user_profile, qr_data, private_key)
    user_profile.qr_code = qr_code  # Привязываем QR-код к профилю пользователя
    user_profile.save()

    # Отправляем QR-код
    context.bot.send_message(chat_id=update.effective_chat.id, text=f'🆔 Ваш ID: {user_profile.external_id}\n🔳 Персональный QR-код:')
    context.bot.send_photo(chat_id=update.effective_chat.id, photo=open(qr_code, 'rb'))
    start_keyboard(update, context)

def profile(update, context):
    user = update.effective_user
    user_id = user.id
    # Проверяем, есть ли пользователь в базе данных
    try:
        user_profile = UserProfile.objects.get(external_id=user_id)
    except UserProfile.DoesNotExist:
        context.bot.send_message(chat_id=update.effective_chat.id, text='🔴 Вы не зарегистрированы!')
        return

    message = ""
    # Проверяем, является ли пользователь VIP
    if user_profile.is_special:
        message = "Статус: VIP клиент ⭐⭐⭐"
        message += "\n📉 Ваша скидка - 15%"
        message += f"\n🆔 Ваш ID: {user_profile.external_id}"
        message += f"\n💰 Общая сумма заказов: {user_profile.total_spent} руб."
        message += f"\n💵 Сумма текущего заказа: {user_profile.menu_total} руб."
    else:
        discount_percentage = user_profile.calculate_discount_percentage()
        if discount_percentage == 10:
            message = f"Cтатус: Привилегированный клиент ⭐⭐"
            message += f"\n🆔 Ваш ID: {user_profile.external_id}"
            message += "\n📉 Ваша скидка максимальная - 10%"
            message += f"\n💰 Общая сумма заказов: {user_profile.total_spent} руб."
            message += f"\n💵 Сумма текущего заказа: {user_profile.menu_total} руб."
        else:
            message = f"Cтатус: Обычный клиент ⭐"
            message += f"\n🆔 Ваш ID: {user_profile.external_id}"
            message += f"\n📉 Текущая скидка: {discount_percentage}%"
            message += f"\n💰 Общая сумма заказов: {user_profile.total_spent} руб."
            message += f"\n💵 Сумма текущего заказа: {user_profile.menu_total} руб."

    if not user_profile.is_special and discount_percentage != 10:
        # Определяем, сколько еще надо потратить для повышения скидочного процента
        thresholds = { 0: 0, 3: 1000, 5: 2000, 7: 3000, 10: 5000}
        for threshold, amount in thresholds.items():
            if user_profile.total_spent < amount:
                remaining_amount = amount - user_profile.total_spent
                message += f"\n📣 Для повышения скидки до {threshold}% необходимо потратить еще {remaining_amount} руб.\n"
                break
    context.bot.send_message(chat_id=update.effective_chat.id, text=message)

# Публикация актуальных новостей на неделю
def afisha(update, context):
    pass

# Ссылки на источники контактов
def contacts(update, context):
    message = "📚 Контакты:\n"
    message += "\\- 💻 [Сайт компании](http://www.example.com)\n"
    message += "\\- 📧 [Email](mailto:info@example.com)\n"
    message += "\\- ☎ Телефон: \\+79132222234\n"
    context.bot.send_message(chat_id=update.effective_chat.id, text=message, parse_mode='MarkdownV2')

"""
КОД ДЛЯ РАБОТНИКА
"""

def start_job(update, context):
    context.bot_data['state'] = 'start_job'
    user = update.effective_user
    user_id = user.id
    # Проверяем, есть ли пользователь в базе данных
    try:
        job_profile = JobProfile.objects.get(external_id=user_id)
        context.bot.send_message(chat_id=update.effective_chat.id, text='Вы уже зарегистрированы!')
    except JobProfile.DoesNotExist:
        # Отправляем запрос на номер телефона
        context.bot.send_message(chat_id=update.effective_chat.id, text='Пожалуйста, отправьте ваш номер телефона', reply_markup=ReplyKeyboardRemove(remove_keyboard=True))
        context.bot.send_message(chat_id=update.effective_chat.id, text='Для этого нажмите на кнопку "📲 Отправить номер"', reply_markup=ReplyKeyboardMarkup([[KeyboardButton('📲 Отправить номер', request_contact=True)]], resize_keyboard=True, one_time_keyboard=True))
        return
    start_job_keyboard(update, context)

def scan_code(update, context):
    button_text = '🔳 Отсканировать QR-код'
    button_url = 'https://www.online-qr-scanner.com/'
    keyboard = [[InlineKeyboardButton(button_text, url=button_url)]]
    reply_markup = InlineKeyboardMarkup(keyboard)
    update.message.reply_text('Нажмите кнопку ниже, чтобы открыть сканер QR-кодов:', reply_markup=reply_markup)

def add_menu_total_button(update, context):
    keyboard = [
        [KeyboardButton('❌ Удалить сумму текущего заказа')],
        [KeyboardButton('↩ Назад')]
    ]
    reply_markup = ReplyKeyboardMarkup(keyboard, resize_keyboard=True)
    context.bot.send_message(chat_id=update.effective_chat.id, text='Пожалуйста, введите 🆔 клиента.', reply_markup=reply_markup)
    # Устанавливаем флаг ожидания ID клиента
    context.user_data['waiting_for_id'] = True

def cancel_operation(update, context):
    start_job_keyboard(update, context)
    # Сбрасываем флаги ожидания
    context.user_data['waiting_for_id'] = False
    context.user_data['waiting_for_menu_total'] = False

def handle_menu_total(update, context):
    if update.message.text == '❌ Удалить сумму текущего заказа':
        user_profile = context.user_data.get('user_profile')
        if user_profile:
            # Возврат суммы из предыдущего меню в текущую сумму меню
            user_profile.total_spent = user_profile.total_spent - decimal.Decimal(user_profile.menu_total)
            user_profile.menu_total = 0
            user_profile.save()
            update.message.reply_text(f'✅ Сумма текущего заказа успешно отменена. Текущая сумма заказа: {user_profile.menu_total} руб.')
        else:
            update.message.reply_text('🔴 Произошла ошибка. Пожалуйста, повторите попытку.')
            start_job_keyboard(update, context)

        # Сбрасываем флаги ожидания
        context.user_data['waiting_for_menu_total'] = False
        context.user_data.pop('user_profile', None)
    if context.user_data.get('waiting_for_id'):
        user_id = update.message.text
        if not user_id.isdigit():
            update.message.reply_text('ID клиента должно быть числом, повторите ввод:')
            return
        try:
            user_profile = UserProfile.objects.get(external_id=user_id)
            vip_status = "VIP-клиент ⭐⭐⭐" if user_profile.is_special else "Обычный клиент ⭐"
            # Формируем информацию о пользователе
            reply_message = f"Статус: {vip_status}\n"
            reply_message += f"🆔 ID клиента: {user_profile.external_id}\n"
            reply_message += f"📱 Номер телефона: {user_profile.phone_number}\n"
            reply_message += f"📉 Текущая скидка: {user_profile.discount_percentage}%\n"
            # Отправляем информацию о пользователе
            context.bot.send_message(chat_id=update.effective_chat.id, text=reply_message)

        except ObjectDoesNotExist:
            update.message.reply_text('Клиент с указанным ID не найден.')
            return

        context.user_data['waiting_for_id'] = False
        context.user_data['user_profile'] = user_profile
        context.bot.send_message(chat_id=update.effective_chat.id, text='Пожалуйста, введите 👆 сумму текущего заказа\n '
                                                                        'или отмените введенную ранее сумму заказа, нажав на кнопку\n'
                                                                        '"❌ Удалить сумму текущего заказа"')
        # Устанавливаем флаг ожидания суммы меню
        context.user_data['waiting_for_menu_total'] = True

    elif context.user_data.get('waiting_for_menu_total'):
        menu_total = update.message.text

        if not re.match(r'^\d+\.\d{2}$', menu_total.replace(',', '.')):
            update.message.reply_text('Сумма заказа должна содержать рубли и копейки (0.00)')
            return

        user_profile = context.user_data.get('user_profile')
        if user_profile:
            # Сохраняем предыдущую сумму меню
            user_profile.previous_menu_total = user_profile.menu_total

            # Обновляем сумму текущего меню в профиле пользователя
            user_profile.menu_total = '{:.2f}'.format(float(menu_total))
            user_profile.total_spent += decimal.Decimal(user_profile.menu_total)

            # Сохраняем дату и время обновления меню
            user_profile.menu_total_timestamp = datetime.now()
            user_profile.save()
            update.message.reply_text(f'Сумма текущего заказа {user_profile.menu_total} руб. успешно добавлена.')
            vip_status = "VIP-клиент ⭐⭐⭐" if user_profile.is_special else "Обычный клиент ⭐"
            # Формируем информацию о пользователе
            reply_message = f"Статус: {vip_status}\n"
            reply_message += f"🆔 ID клиента: {user_profile.external_id}\n"
            reply_message += f"📱 Номер телефона: {user_profile.phone_number}\n"
            reply_message += f"📉 Текущая скидка: {user_profile.discount_percentage}%\n"
            reply_message += f"💵 Сумма текущего заказа: {user_profile.menu_total} руб.\n"
            # Отправляем информацию о пользователе
            context.bot.send_message(chat_id=update.effective_chat.id, text=reply_message)
            start_job_keyboard(update, context)
        else:
            update.message.reply_text('🔴 Произошла ошибка. Пожалуйста, повторите попытку.')
        # Сбрасываем флаги ожидания
        context.user_data['waiting_for_menu_total'] = False
        context.user_data.pop('user_profile', None)
    else:
        start_job_keyboard(update, context)

def instruction(update, context):
    # Получение полного пути к файлу с инструкцией в папке media
    file_path = os.path.join('media', 'instructions', 'instruction.pdf')

    # Отправка документа и приглашение открыть файл
    with open(file_path, 'rb') as file:
        context.bot.send_document(chat_id=update.effective_chat.id,document=file,caption='Пожалуйста, откройте инструкцию для просмотра')

class Command(BaseCommand):
    help = 'Телеграм-бот'

    def handle(self, *args, **options):
        request = Request(connect_timeout=0.5,read_timeout=1.0)
        bot = telegram.Bot(request=request,token=settings.TOKEN)
        print(bot.get_me())
        updater = Updater(bot=bot,use_context=True)
        # Обработчики команд для пользователя
        dp = updater.dispatcher
        dp.add_handler(CommandHandler('start', start))
        dp.add_handler(MessageHandler(Filters.contact, handle_phone_number))
        dp.add_handler(MessageHandler(Filters.text('👨💼 Профиль'), profile))
        dp.add_handler(MessageHandler(Filters.text('📆 Афиша'), afisha))
        dp.add_handler(MessageHandler(Filters.text('📗 Контакты'),contacts))
        dp.add_handler(MessageHandler(Filters.text('🪪 Виртуальная карта'),virtual_card))

        # обработчики команд для работника
        dp.add_handler(CommandHandler('start_job', start_job))
        dp.add_handler(MessageHandler(Filters.text('🔳 Отсканировать QR-код'), scan_code))
        dp.add_handler(MessageHandler(Filters.text('📋 Инструкция'), instruction))
        dp.add_handler(MessageHandler(Filters.text('👆 Ввести ID клиента'), add_menu_total_button))
        dp.add_handler(MessageHandler(Filters.text('↩ Назад'), cancel_operation))
        dp.add_handler(MessageHandler(Filters.text, handle_menu_total))

        updater.start_polling()
        updater.idle()

if __name__ == '__main__':
    command = Command()
    command.handle()


from collections import namedtuple
from collections import defaultdict
from pprint import pprint

import argparse
import csv
import json
import re
import subprocess

instruction_strings = []
sub_instruction_strings = []
meta_instructions = []
instructions_masks = []
specialization_params = {}
endloops = {}
patterns = []
meta_mapping = defaultdict(list)
implemented_meta = 0
implemented_insn = 0
system_insn = 0
float_insn = 0
implemented_vect = 0
vectorial_meta = 0
Constant = namedtuple('Constant', ['identifier', 'bits', 'signed',
                                   'multiple', 'pc_offset', 'ranges'])
Register = namedtuple('Register', ['identifier', 'bits', 'ranges',
                                   'dot_new', 'predicate'])
Range = namedtuple('Range', ['start', 'end'])

DECODER_HEADER = """#ifndef HEXAGON_DECODER_H
#define HEXAGON_DECODER_H

#include "exec/log.h"
#include "qemu-common.h"
#include "tcg.h"

#define LOG_DIS(...) qemu_log_mask(CPU_LOG_TB_IN_ASM, ## __VA_ARGS__)
#define SET_USED_REG(reg_struct, reg) {\\
        if (is_conditional) \\
            reg_struct.conditional |= (uint64_t)1 << (reg); \\
        else \\
            reg_struct.written |= (uint64_t)1 << (reg); \\
        if (reg < 32) \\
            push_destination_reg(&(reg_struct.destination), reg); \\
}
#define SET_WRITTEN_PRE(dc, pre_index) dc->deps[dc->i].written |= (uint8_t)1 << (pre_index)
#define GET_WRITTEN_PRE(dc, pre_index) dc->deps[dc->i].written & (uint64_t)1 << (pre_index)
#define GET_WRITTEN_PREV_PRE(dc, pre_index)                      \\
        (dc->i == 0 ?                                            \\
        (dc->deps[1].written & (uint64_t)1 << (pre_index)) |     \\
        (dc->deps[2].written & (uint64_t)1 << (pre_index)) |     \\
        (dc->deps[3].written & (uint64_t)1 << (pre_index))       \\
        : dc->i == 1 ?                                           \\
        (dc->deps[0].written & (uint64_t)1 << (pre_index)) |     \\
        (dc->deps[2].written & (uint64_t)1 << (pre_index)) |     \\
        (dc->deps[3].written & (uint64_t)1 << (pre_index))       \\
        : dc->i == 2 ?                                           \\
        (dc->deps[0].written & (uint64_t)1 << (pre_index)) |     \\
        (dc->deps[1].written & (uint64_t)1 << (pre_index)) |     \\
        (dc->deps[3].written & (uint64_t)1 << (pre_index))       \\
        : dc->i == 3 ?                                           \\
        (dc->deps[0].written & (uint64_t)1 << (pre_index)) |     \\
        (dc->deps[1].written & (uint64_t)1 << (pre_index)) |     \\
        (dc->deps[2].written & (uint64_t)1 << (pre_index)) : -1 )
#define GET_WRITTEN_ANY_PRE(dc) \\
        (dc->deps[0].written & 0b1111) | \\
        (dc->deps[1].written & 0b1111) | \\
        (dc->deps[2].written & 0b1111) | \\
        (dc->deps[3].written & 0b1111)
#define SET_READ_PRE(dc, pre_index) dc->deps[dc->i].read |= (uint8_t)1 << (pre_index)
#define GET_USED_REG(reg_struct, reg) (reg_struct).written & (uint64_t)1 << (reg)
#define GET_COND_REG(reg_struct, reg) (reg_struct).conditional & (uint64_t)1 << (reg)
#define SET_BEGIN_COND() is_conditional = true;
#define SET_END_COND() is_conditional = false;
#define SET_JUMP_FLAG(dc) { \\
    switch (dc->jump_count) { \\
        case 0: SET_WRITTEN_PRE(dc, 4); break; \\
        case 1: SET_READ_PRE(dc, 4); break; \\
        default: assert(false && "More than two jumps in a packet!"); \\
    } \\
    dc->jump_count++; \\
}
#define ADD_IF_ZERO(x, y) {\\
        assert((x == 0 || y == 0) && "Overlapping instruction encodings!");\\
        x += y;\\
}
#define EXTRACT_P(index, dotnew) (dotnew) ? \\
        ((CR[P] & (0xff << 8 * index)) >> 8 * index) : \\
        ((CR_new[P] & (0xff << 8 * index)) >> 8 * index) : \\

#define EXTR(src, start, end) \\
(((src) >> (31 - end)) & ((1 << (end - start + 1)) - 1))
#define EXTR_2(src, start, end, start2, end2) \\
(EXTR(src, start, end) << (end2 - start2 + 1)) | EXTR(src, start2, end2)
#define EXTR_3(src, start, end, start2, end2, start3, end3) \\
(EXTR(src, start, end) << (end2 - start2 + end3 - start3 + 2)) |\\
(EXTR(src, start2, end2) << (end3 - start3 + 1)) | (EXTR(src, start3, end3));
#define EXTR_4(src, start, end, start2, end2, start3, end3, start4, end4) \\
EXTR(src, start, end) << (end2 - start2 + end3 - start3 + end4 - start4 + 3)\\
| (EXTR(src, start2, end2) << (end3 - start3 + end4 - start4 + 2))\\
| (EXTR(src, start3, end3) << (end4 - start4 + 1))\\
| EXTR(src, start4, end4)
#define EXTR_BITS(src, x) \\
((src >> x) & 1)
#define EXTR_BITS_2(src, x, y) \\
(EXTR_BITS(src, x) | (EXTR_BITS(src, y) << 1))
#define EXTR_BITS_3(src, x, y, z) \\
(EXTR_BITS_2(src, x, y) | (EXTR_BITS(src, z) << 2))
#define EXTR_BITS_4(src, x, y, z, a) \\
(EXTR_BITS_2(src, x, y) | (EXTR_BITS_2(src, z, a) << 2))
#define EXTR_BITS_5(src, x, y, z, a, b) \\
(EXTR_BITS_4(src, x, y, z, a) | (EXTR_BITS(src, b) << 4))
#define EXTR_BITS_6(src, x, y, z, a, b, c) \\
(EXTR_BITS_3(src, x, y, z) | (EXTR_BITS_3(src, a, b, c) << 3))\n

typedef struct destination_reg_node {
    int index;
    struct destination_reg_node *next;
} d_reg_node;
typedef d_reg_node * d_reg_list;

typedef struct regs {
    uint64_t written;
    uint64_t conditional;
    d_reg_list destination;
} regs_t;

typedef struct dep {
    TCGOp *begin;
    TCGOp *end;
    uint8_t written;
    uint8_t read;
} deps_t;

/* This is the state at translation time.  */
typedef struct DisasContext {
    HexagonCPU *cpu;
    target_ulong pc;
    target_ulong old_pc;
    target_ulong instruction_pc;
    target_ulong npc;

    int i;
    uint32_t ir;
    uint32_t const_ext;
    bool block_end;
    bool new_packet;
    bool extender_present;
    bool pc_written;
    bool duplex;
    bool endloop[2];
    bool is_pre_written;
    TCGOp *packet_first_op;
    int jump_count;
    regs_t regs;
    deps_t deps[4];
    deps_t * original[4];
    deps_t * ordered[4];

    struct TranslationBlock *tb;
} DisasContext;

extern TCGv GPR[32];
extern TCGv CR[32];
extern TCGv SR[64];
extern TCGv GPR_new[32];
extern TCGv CR_new[32];
extern TCGv PC_written;
extern TCGv SA[2];
extern TCGv LC[2];
extern TCGv LPCFG;

int get_destination_reg(regs_t regs, int t);
void push_destination_reg(d_reg_list* list, int index);
void register_dependency(int index, DisasContext *dc);
uint32_t decode(uint32_t ir);
uint32_t sub_decode(uint32_t ir);
regs_t execute(unsigned inst_id, DisasContext *dc);
regs_t sub_execute(unsigned inst_id, DisasContext *dc);
void endloop0(void);
void endloop01(void);
void endloop1(void);

"""
DECODER_INCLUDES = """#include "qemu/osdep.h"
#include "cpu.h"
#include "decoder.h"
#include "tcg-op.h"

static const int pred_whitening[4] = { 0xffffff00,
                                       0xffff00ff,
                                       0xff00ffff,
                                       0x00ffffff };

bool is_conditional = false;

int get_destination_reg(regs_t regs, int t) {
    d_reg_list reg_list = regs.destination;
    if (reg_list == NULL)
        assert(false && "Invalid .new instruction reference!");
    for(int i = 1; i < t; i++) {
        assert(reg_list != NULL && "Invalid .new instruction reference!");
        reg_list = reg_list->next;
    }
    return reg_list->index;
}

void push_destination_reg(d_reg_list* list, int index) {
    if (index == 29 || index == 30 || index == 31 ||
        (list != NULL && *list != NULL && (*list)->index == index)) {
        return;
    }
    d_reg_node *new_reg = (d_reg_node *)malloc(sizeof(d_reg_node));
    new_reg->next = *list;
    new_reg->index = index;
    *list = new_reg;
}

"""

def gen_macros():
    code = ""
    code += DECODER_INCLUDES
    with open(decoder_c, "w") as d:
        d.write(code)

def find_integer(string):
    for i, letter in enumerate(string):
        if not letter.isdigit():
            return i
    return len(string)

def extract_shift(string):
    if string[1:5] == ":<<N":
        return Constant(identifier="N", bits=32, signed=False,
                        multiple=1, pc_offset=False, ranges=[])
    else:
        return None

# Parse a constant from a tokenized string
def extract_const(string):
    # Extract features from constant placeholders
    identifier = "j" if string[1].islower() else "I"
    if string[1] in {"u", "U"}:
        # Unsigned N-bit immediate value
        signed = False
        pc_offset = False
    elif string[1] in {"s", "S"}:
        # Signed N-bit immediate value
        signed = True
        pc_offset = False
    elif string[1] in {"m", "M"}:
        # Signed N-bit immediate value
        signed = True
        pc_offset = False
    elif string[1] in {"r", "R"}:
        # Signed N-bit immediate value
        signed = True
        pc_offset = True
    elif string[1] == "#":
        # 32 bit value
        return Constant(bits=32)
    elif string[1] == "-" or string[1].isdigit():
        return None
    else:
        assert(False and "Constant placeholder parsing error!")

    bits_delimiter = find_integer(string[2:]) + 2
    bits = int(string[2:bits_delimiter])
    if len(string) > bits_delimiter and string[bits_delimiter] == ":":
        multiple_delimiter = find_integer(
                string[bits_delimiter + 1:]) + bits_delimiter + 1
        multiple = 2 ** int(string[bits_delimiter + 1:multiple_delimiter])
    else:
        multiple = 1
    return Constant(identifier=identifier, bits=bits, signed=signed,
                    multiple=multiple, pc_offset=pc_offset, ranges=[])

# Parse a register from a tokenized string
def extract_reg(string):
    # TODO: Handle 64bit composite registers
    if string[0] in {"R", "C", "P",
                     "M", "G", "S"}:
        if string[1] in {"e", "s", "d", "t", "u", "v", "x", "y"}:
            if len(string) >= 3 and string[2] == string[1]:
                return Register(identifier=string[1],
                                bits=64,
                                ranges=[],
                                dot_new=False,
                                predicate=string[0] == "P")
            return Register(identifier=string[1],
                            bits=32,
                            ranges=[],
                            dot_new=False,
                            predicate=string[0] == "P")
    elif string[0] in {"N"}:
        if string[1] in {"t"}:
            return Register(identifier=string[1],
                            bits=32,
                            ranges=[],
                            dot_new=True,
                            predicate=False)
        elif string[1] in {"s"}:
            return Register(identifier=string[1],
                            bits=32,
                            ranges=[],
                            dot_new=True,
                            predicate=False)

# Search operand from identifier
def find_op(identifier, operands):
    return [op for op in operands if op.identifier == identifier][0]

# Parse operands and compute format strings
def parse_op(inst_str):
    # Find operands order
    operands = []
    format_identifiers = []
    for i, letter in enumerate(inst_str):
        op = None
        # Signed and unsigned constants
        if letter == "#":
            op = extract_const(inst_str[i:])
        elif letter == "[":
            op = extract_shift(inst_str[i:])
        # Registers and predicates
        else:
            op = extract_reg(inst_str[i:])
        format_identifiers.append(op)
        if op not in operands:
            operands.append(op)
    identifiers = []
    for op in format_identifiers:
        if op is not None:
            if op.bits == 64:
                identifiers.append("(" + op.identifier + " + 1)")
                identifiers.append(op.identifier)
            else:
                identifiers.append(op.identifier)
    return ([op for op in operands if op is not None],
            identifiers)

# Operands extraction code generation
def gen_extract_op(inst_str, encoding, operands):
    code = ""
    identifiers = set([c.identifier
                       if type(c) in {Constant, Register}
                       else c
                       for c in operands])
    # Extract bit ranges from encoding scheme
    char, start = '', 0
    for i, letter in enumerate(encoding):
        if letter != char:
            if char in identifiers:
                find_op(char, operands).ranges.append(
                        Range(start=start, end=(i - 1)))
            start = i
            char = letter
        # Verify that no meaningful bits are left behind
        if letter not in {'0', '1', '-', 'P'}.union(set(identifiers)):
            print("Bit " + letter +
                  " was not parsed in instruction " + inst_str)

    if char in identifiers:
        find_op(char, operands).ranges.append(
                Range(start=start, end=(len(encoding) - 1)))

    # Verify encoding scheme coherence
    for op in operands:
        if type(op) == Constant and op.identifier != "N":
            sum = 0
            for r in op.ranges:
                sum += r.end - r.start + 1
            assert(sum == op.bits and "Non-coherent encoding scheme!")

    # Patch Nt encoding ranges
    for op in operands:
        if type(op) == Register and op.dot_new:
            old_range = op.ranges[-1]
            op.ranges[-1] = Range(start=old_range.start, end=old_range.end - 1)

    # Constand extender is not present, apply multiplier
    ext_imm = extendable_index(inst_str)
    constant_index = 0
    # Emit code for each operand
    for i, op in enumerate(operands):
        multiple = ""
        if len(op.ranges) == 1:
            code += "raw_value = EXTR(dc->ir, {}, {});\n".format(
                    op.ranges[0].start,
                    op.ranges[0].end)
        elif len(op.ranges) == 2:
            code += "raw_value = EXTR_2(dc->ir, {}, {}, {}, {});\n".format(
                            op.ranges[0].start,
                            op.ranges[0].end,
                            op.ranges[1].start,
                            op.ranges[1].end)
        elif len(op.ranges) == 3:
            code += "raw_value = EXTR_3(dc->ir, {}, {}, {}, {}, {}, {});"\
                    "\n".format(
                            op.ranges[0].start,
                            op.ranges[0].end,
                            op.ranges[1].start,
                            op.ranges[1].end,
                            op.ranges[2].start,
                            op.ranges[2].end)
        elif len(op.ranges) == 4:
            code += "raw_value = EXTR_4(dc->ir, {}, {}, {}, {}, {}, {}, {},"\
                    " {});\n".format(
                            op.ranges[0].start,
                            op.ranges[0].end,
                            op.ranges[1].start,
                            op.ranges[1].end,
                            op.ranges[2].start,
                            op.ranges[2].end,
                            op.ranges[3].start,
                            op.ranges[3].end)
        if type(op) == Constant:
            if op.multiple != 0:
                multiple = " * "+str(op.multiple)
            if op.signed:
                extended = "((0xffffffff >> {bits}) << {bits}) | "\
                        "raw_value".format(bits=op.bits)
                code += "uint32_t {op_name} = ((raw_value >> ({bits} - 1))"\
                        " ? ({extended}) : raw_value);".format(
                                op_name=op.identifier,
                                bits=op.bits,
                                extended=extended)
                # If is extendable emit conditional multiplier
                if constant_index == ext_imm:
                    code += "{op_name} = (dc->extender_present ? "\
                            "{op_name} : ({op_name}{multiple}));\n".format(
                                    op_name=op.identifier,
                                    multiple=multiple)
                # Otherwise just multiply
                else:
                    code += "{op_name} = {op_name}{multiple};\n".format(
                                    op_name=op.identifier,
                                    multiple=multiple)

            else:
                code += "uint32_t {op_name} = "\
                        "raw_value;".format(
                                op_name=op.identifier,
                                index=i,
                                bits=op.bits)
                # If is extendable emit conditional multiplier
                if constant_index == ext_imm:
                    code += "{op_name} = (dc->extender_present ? "\
                            "{op_name} : ({op_name}{multiple}));\n".format(
                                    op_name=op.identifier,
                                    multiple=multiple)
                # Otherwise just multiply
                else:
                    code += "{op_name} = {op_name}{multiple};\n".format(
                                    op_name=op.identifier,
                                    multiple=multiple)
            constant_index += 1
        else:
            code += "uint32_t "+op.identifier+" = raw_value;"
    return code

def extendable_index(inst_str):
    inst_str = inst_str.replace(" ", "")
    extendables = []
    with open(const_ext_csv) as f:
        lines = csv.reader(f)
        for line in lines:
            extendables.append(line)
    indexes, expressions = zip(*extendables)
    regexes = map(to_extender_regex, expressions)
    patterns = map(re.compile, regexes)
    for i, pattern in enumerate(patterns):
        match = pattern.match(inst_str)
        if match is not None:
            return int(indexes[i])
    return None

# Function execution code generation
def gen_execute():
    global system_insn
    code = "regs_t execute(unsigned inst_id, DisasContext *dc) {"\
           "uint32_t raw_value = 0;"\
           "regs_t regs = { 0 };"\
           "switch (inst_id) {"
    encodings = parse_encodings(instructions_csv)
    # Count SYSTEM instructions
    for e in encodings:
        if e[0:4] == ['0', '1', '1', '0']:
            system_insn += 1
    for inst_id, inst_str in enumerate(instruction_strings):
        operands, format_identifiers = parse_op(inst_str)
        identifiers = [op.identifier for op in operands]
        code += "case {}:\n{{".format(inst_id)
        code += gen_extract_op(inst_str, encodings[inst_id], operands)
        for op in operands:
            if type(op) == Register and op.dot_new:
                code += "{id} = get_destination_reg(dc->regs, {id});\n".format(
                        id=op.identifier)
            if type(op) == Register and not op.dot_new and not op.predicate:
                sum = 0
                for r in op.ranges:
                    sum += r.end - r.start + 1
                    if sum < 5:
                        code += "if (" + op.identifier + " >= 8)\n"
                        code += op.identifier + " += 8;\n"
        # If present, apply constant extender value
        ext_imm = extendable_index(inst_str)
        if ext_imm is not None:
            constants = [op for op in operands if type(op) == Constant]
            identifier = constants[ext_imm].identifier
            code += "if (dc->extender_present) {\n"
            code += identifier + " &= 0x3f;\n"
            code += identifier + " |= dc->const_ext;\n"
            code += "}\n"
        # if inst_id == 233:
        #    pprint(inst_str)
        #    pprint(ext_imm)
        # Print LOG_DIS disassembly using format.h
        code += ("LOG_DIS({}" +
                 ", {}" * len(format_identifiers) +
                 ");\n").format(to_format_string(inst_str), *format_identifiers)
        # Call the correct semantics function
        code += gen_function_call(inst_str, identifiers)
        code += "break;\n}\n"

    code += 'default: assert(false && '\
            '"Decoding not implemented for this instruction");'

    code += "}\nreturn regs;\n}\n\n"

    with open(decoder_c, "a") as d:
        d.write(code)

# Translate regex groups to function parameters
def get_params(pattern):
    flags = {}
    params = []
    # Each group becomes a parameter
    for k, v in pattern.groupindex.items():
        flags[v - 1] = k
    for i in range(len(flags)):
        params.append(flags[i])
    return params

# Invoke semantics compiler to fill function body
def gen_function_body(pattern_index):
    global implemented_meta
    global implemented_insn
    global implemented_vect
    qemu_code = ""
    qemu_code += "regs_t regs = { 0 };\n"
    instruction_code = meta_instructions[pattern_index]["code"]
    # Patch missing semicolons
    instruction_code = instruction_code.replace(" if", "; if")
    # Parse memory operation size
    mem_args = []
    mem_size = re.findall(r'mem(.*)\(', meta_instructions[pattern_index]["str"])
    assert(len(mem_size) < 2 and "More than one memory operation!")
    if len(mem_size) == 1:
        mem_size = mem_size[0]
        if "u" in mem_size:
            mem_args.append("-u")
        if "b" in mem_size:
            mem_args.append("-b")
        elif "h" in mem_size:
            mem_args.append("-h")
        elif "w" in mem_size:
            mem_args.append("-w")
        elif "d" in mem_size:
            mem_args.append("-d")
    # Parse comparison signedness
    cmp_sign = re.findall(r'cmp.(?:gt|ge|lt|le)(.)\(',
                          meta_instructions[pattern_index]["str"])
    if len(cmp_sign) == 1 and "u" in cmp_sign:
        mem_args.append("-u")
    # Parse jump flag
    jumps = re.findall(r'jump', meta_instructions[pattern_index]["str"])
    if len(jumps) > 0:
        mem_args.append("-j")
    # Parse stop instruction
    if meta_instructions[pattern_index]["str"] == "stop(Rs)":
        mem_args.append("-s")
    args = [semantics_path] + mem_args
    proc = subprocess.Popen(" ".join(args),
                            shell=True,
                            stdout=subprocess.PIPE,
                            stderr=subprocess.PIPE,
                            stdin=subprocess.PIPE)
    proc.stdin.write(instruction_code.encode("utf-8"))
    proc.stdin.close()
    proc.wait()
    # Check bison exit code
    if proc.returncode == 0:
        qemu_code += proc.stdout.read().decode("utf-8")
        implemented_meta += 1
        implemented_insn += len(meta_mapping[pattern_index])
        if "=v" in meta_instructions[pattern_index]["str"]:
            implemented_vect += 1
    else:
        qemu_code += 'assert(false && "Instruction not implemented!");\n'
        # pprint(meta_instructions[pattern_index]["code"])
    qemu_code += "return regs;"
    return qemu_code

# Generate functions signatures
def gen_functions():
    code = ""
    regexes = map(to_regex, meta_instructions)
    patterns = map(re.compile, regexes)
    # Map meta-instructions into effective instructions
    for pattern_index, pattern in enumerate(patterns):
        meta_instruction = meta_instructions[pattern_index]["str"]
        meta_instruction = re.sub(r"\[(:<<N)\]", r"\[:<<N\]", meta_instruction)
        meta_instruction = re.sub(r"\[(:<<1)\]", r"\[:<<N\]", meta_instruction)
        operands, _ = parse_op(meta_instruction)
        identifiers = [op.identifier for op in operands]
        params = get_params(pattern)
        # Add function string comment
        code += "/* " + meta_instructions[pattern_index]["str"] + " */\n"
        code += "/* " + meta_instructions[pattern_index]["code"] + " */\n"
        # Generate function signature
        if len(identifiers) + len(params) == 0:
            code += "regs_t function_{}(DisasContext *dc) {{".format(pattern_index)
        else:
            function_str = ("regs_t function_{}(DisasContext *dc, " +
                            ", ".join(["uint32_t {}"] * len(identifiers) +
                                      ["bool {}"] * len(params)) + "){{\n")
            code += function_str.format(pattern_index,
                                        *identifiers,
                                        *params)
        code += gen_function_body(pattern_index)
        code += "}\n"
    with open(decoder_c, "a") as d:
        d.write(code)
    print("{}/{} meta instructions are vectorial!".format(vectorial_meta, len(meta_instructions)))
    print("{}/{} meta instructions have been implemented!".format(implemented_meta, len(meta_instructions)))
    print("{}/{} vectorial meta instructions have been implemented!".format(implemented_vect, vectorial_meta))
    print("{}/{} instructions have been implemented!".format(implemented_insn, len(instruction_strings) - system_insn - float_insn))

# Match instructions corresponding to a meta-instruction
def gen_pattern_matching(candidates, instruction_masks):
    if len(candidates) == 0:
        code = 'assert(false && '\
                '"Invalid instruction encoding!");'
        return code
    elif len(candidates) == 1:
        return "return {};".format(candidates[0])
    else:
        code = ""
        for candidate in candidates:
            code += "ADD_IF_ZERO(inst_index, "\
                    "(((ir & {}) ^ {}) == 0) * {});\n".format(
                        hex(int(instruction_masks[candidate][1], 2)),
                        hex(int(instruction_masks[candidate][0], 2)),
                        candidate)
        code += "return inst_index;\n"
        return code

# Generate switch case for decoding
def explore_switch(decode, instruction_masks):
    # Recursively generate switch cases
    n_bits = len(decode["bits"])
    code = ("switch (" +
            "EXTR_BITS_{}(".format(n_bits) +
            ("ir"+", {}"*n_bits+")) {{").format(*decode["bits"]))
    if "options" in decode.keys():
        for option in decode["options"]:
            code += "case {}: ".format(hex(int(option, 2)))
            code += explore_switch(decode["options"][option],
                                   instruction_masks)
            code += "break;"
        code += 'default: assert(false && '\
                '"Invalid instruction encoding!");'
    elif "instructions" in decode.keys():
        for instruction in decode["instructions"]:
            code += "case {}:".format(hex(int(instruction, 2)))
            code += gen_pattern_matching(decode["instructions"][instruction],
                                         instruction_masks)
            code += "break;"
        code += 'default: assert(false && '\
                '"Invalid instruction encoding!");'
    else:
        assert(False and "Unable to generate parsing code for:")
        pprint(decode)
    code += "}"
    return code

def gen_decoder_switch():
    with open(instruction_decoding_json) as f:
        decoding_json = json.loads(f.read())
    decode = decoding_json["decode"]
    instruction_masks = decoding_json["instructions"]

    code = "uint32_t decode(uint32_t ir) {\n"
    code += "uint64_t inst_index = 0;"
    code += explore_switch(decode, instruction_masks)
    code += "}"
    with open(decoder_c, "a") as d:
        d.write(code)

# Translate instruction string into a format string
def to_format_string(inst_str):
    code = ""
    code += "    "
    code += '"'
    format_string = re.sub("Nt", "Rt", inst_str)
    format_string = re.sub("([RCPMGNS])[esdtuvxy]{2}",
                           '\g<1>%" PRIu32 ":%" PRIu32 "', format_string)
    format_string = re.sub("([RCPMGNS])[esdtuvxy]",
                           '\g<1>%" PRIu32 "', format_string)
    format_string = re.sub("\[:<<N\]", '[:<<%" PRIi32 "]', format_string)
    format_string = re.sub("#[uU][0-9]+:[0-9]+", '#%" PRIu32 "', format_string)
    format_string = re.sub("#[uU][0-9]+", '#%" PRIu32 "', format_string)
    format_string = re.sub("#[sS][0-9]+:[0-9]+", '#%" PRIi32 "', format_string)
    format_string = re.sub("#[sS][0-9]+", '#%" PRIi32 "', format_string)
    format_string = re.sub("#m[0-9]+", '#%" PRIi32 "', format_string)
    format_string = re.sub("#r[0-9]+", '#%" PRIi32 "', format_string)
    format_string = re.sub("##", '#%" PRIi32 "', format_string)
    code += format_string
    code += '"\n'
    return code

# Translate a meta-instruction string into a regex
def to_regex(meta_instruction):
    regex = meta_instruction["str"]
    regex = regex.replace("(", "\(")
    regex = regex.replace(")", "\)")
    regex = regex.replace("*", "\*")
    regex = regex.replace("?", "\?")
    regex = regex.replace("^", "\^")
    regex = regex.replace("\n", "")
    regex = regex.replace(" ", "")
    regex = re.sub(r"\[(:<<1)\]", r"(?P<opt_shift>\1)?", regex)
    regex = re.sub(r"\[(:<<N)\]", r"\[:<<N\]", regex)
    regex = re.sub(r"\[(:<<1)\]", r"\[:<<N\]", regex)
    regex = re.sub(r"\[([&|]+)\]", r"(?P<and_or>[\1])", regex)
    regex = re.sub(r"\[([+-]+)\]", r"(?P<plus_minus>[\1])", regex)
    regex = re.sub(r"\[([HL]+)\]", r"(?P<high_low0>[\1])", regex, count=1)
    regex = re.sub(r"(?<!>)\[([HL]+)\]", r"(?P<high_low1>[\1])", regex, count=1)
    regex = re.sub(r"\[([01]+)\]", r"(?P<zero_one0>[01])", regex, count=1)
    regex = re.sub(r"(?<!>)\[([01]+)\]", r"(?P<zero_one1>[01])", regex, count=1)
    regex = re.sub(r"\[!\]", r"(?P<not0>[!]*)?", regex, count=1)
    regex = re.sub(r"(?<!>)\[!\]", r"(?P<not1>[!]*)", regex, count=1)
    regex = re.sub(r"\[(:sat)\]", r"(?P<sat>\1)?", regex)
    regex = re.sub(r"\[(:rnd)\]", r"(?P<rnd>\1)?", regex)
    regex = re.sub(r"\[(.new)\]", r"(?P<new>\1)?", regex)
    regex = re.sub(r":<hint>", r"(?P<hint>\:n?t)?", regex)
    regex = regex.replace("+", "\+")
    regex = regex.replace("|", "\|")
    regex = regex.replace(".", "\.")
    regex = "^" + regex + "$"
    return regex

# Translate a constant extender string into a regex
def to_extender_regex(meta_instruction):
    regex = meta_instruction
    regex = regex.replace("(", "\(")
    regex = regex.replace(")", "\)")
    regex = regex.replace("?", "\?")
    regex = regex.replace("Rd[d]", "Rdd?")
    regex = regex.replace("[.new]", r"(.new)?")
    regex = regex.replace("{", "(?:")
    regex = regex.replace("}", ")")
    regex = regex.replace("*", "\*")
    regex = regex.replace("^", "\^")
    regex = regex.replace("\n", "")
    regex = regex.replace(" ", "")
    regex = regex.replace("+", "\+")
    regex = regex.replace(".", "\.")
    regex = re.sub(r"\[!\]", r"!?", regex, count=1)
    regex = regex.replace(",", "\,")
    regex = regex.replace("hint", "n?t")
    regex = regex.replace("xx", "(?:gt|ge|lt|le|eq)u?")
    regex = regex.replace("target", "#[rRuUsS][0-9]+(?:\:?[0-9]+)?")
    regex = regex.replace("#u6", "#[uUsS][0-9]+(?:\:?[0-9]+)?")
    regex = regex.replace("##u32", "#[uUsS][0-9]+(?:\:?[0-9]+)?")
    regex = regex.replace("##U32", "#[uUsS][0-9]+(?:\:?[0-9]+)?")
    regex = regex.replace("##s32", "#[rRsS][0-9]+(?:\:?[0-9]+)?")
    regex = regex.replace("##S32", "#[rRsS][0-9]+(?:\:?[0-9]+)?")
    regex = regex.replace("Pt", "P[a-z]")
    regex = regex.replace("add/sub/and/or", "(?:add|sub|and|or)")
    regex = regex.replace("asl/asr/lsr", "(?:asl|asr|lsr)")
    # pprint(regex)
    return regex

def parse_instructions(filename):
    global float_insn
    instruction_strings = []
    # Extract effective instructions from instructions.csv
    with open(filename) as f:
        inst_reader = csv.reader(f, delimiter=",", quotechar='"')
        for inst_id, inst_str in enumerate(inst_reader):
            instruction_strings.append(inst_str[-1])
            # Count floating point instructions
            if "=sf" in inst_str[-1]:
                float_insn += 1
    return instruction_strings

def parse_encodings(filename):
    instruction_strings = []
    # Extract effective instructions from instructions.csv
    with open(filename) as f:
        inst_reader = csv.reader(f, delimiter=",", quotechar='"')
        for inst_id, inst_str in enumerate(inst_reader):
            instruction_strings.append(inst_str[:32])
    return instruction_strings

def parse_meta_instructions():
    global endloops
    global vectorial_meta
    meta_instructions = []
    with open(meta_instructions_csv) as inst:
        insts = csv.reader(inst)
        instruction = {}
        for inst in insts:
            if inst[0] in {"endloop0", "endloop1", "endloop01"}:
                endloops[inst[0]] = inst[1]
                continue
            # Exclude syntactic sugar
            if "Assembler" in inst[1]:
                continue
            if len(inst[0]) != 0:
                meta_instructions.append(instruction)
                instruction = {}
                instruction["str"] = inst[0]
                if "=v" in instruction["str"]:
                    vectorial_meta += 1
                if len(inst[1]) == 0:
                    instruction["code"] = "NOP;"
                else:
                    instruction["code"] = inst[1]
            else:
                instruction["code"] += "\n"+inst[1]
        meta_instructions.append(instruction)
        del meta_instructions[0]
    return meta_instructions

def gen_function_call(inst_str, identifiers):
    inst_str = inst_str.replace(" ", "")
    arguments = identifiers
    pattern_id = None
    matched = False
    for i, pattern in enumerate(patterns):
        match = pattern.match(inst_str)
        if match is not None:
            matched = True
            for group in match.groups():
                arguments.append("true" if group in {"&", "+", "1", "!", "H",
                                                     ":sat", ":rnd", ".new",
                                                     "t"} else "false")
            pattern_id = i
            break
    if matched:
        meta_mapping[pattern_id].append(inst_str)
        return ("regs = function_{}(dc" +
                ", {}" * len(identifiers) +
                ");\n").format(pattern_id, *arguments)
    else:
        return ('assert(false && "This instruction is not implemented!");')

def gen_sub_decoder():
    global duplex_masks
    with open(sub_instruction_decoding_json) as f:
        decoding_json = json.loads(f.read())
    decode = decoding_json["decode"]
    duplex_masks = decoding_json["instructions"]

    code = "uint32_t sub_decode(uint32_t ir) {\n"
    code += "uint64_t inst_index = 0;"
    code += explore_switch(decode, duplex_masks)
    code += "}"
    with open(decoder_c, "a") as d:
        d.write(code)

# Function execution code generation
def gen_sub_execute():
    code = "regs_t sub_execute(unsigned inst_id, DisasContext *dc) {"\
           "uint32_t raw_value = 0;"\
           "regs_t regs = { 0 };"\
           "switch (inst_id) {"
    encodings = parse_encodings(sub_instructions_csv)
    for inst_id, inst_str in enumerate(sub_instruction_strings):
        operands, format_identifiers = parse_op(inst_str)
        identifiers = [op.identifier for op in operands]
        code += "case {}:\n{{".format(inst_id)
        # Implement weird operator encoding
        code += gen_extract_op(inst_str, encodings[inst_id], operands)
        for op in operands:
            if type(op) == Register:
                if op.bits == 64:
                    code += op.identifier + " *= 2;\n"
                code += "if (" + op.identifier + " >= 8)\n"
                code += op.identifier + " += 8;\n"
        # If present, apply constant extender value
        ext_imm = extendable_index(inst_str)
        if ext_imm is not None:
            constants = [op for op in operands if type(op) == Constant]
            identifier = constants[ext_imm].identifier
            code += "if (dc->extender_present) {\n"
            code += identifier + " &= 0x3f;\n"
            code += identifier + " |= dc->const_ext;\n"
            code += "dc->extender_present = false;\n"
            code += "dc->const_ext = 0;\n"
            code += "}\n"
        # Print LOG_DIS disassembly using format.h
        code += ("LOG_DIS({}" +
                 ", {}" * len(format_identifiers) +
                 ");\n").format(to_format_string(inst_str), *format_identifiers)
        # Call the correct semantics function
        code += gen_function_call(inst_str, identifiers)
        code += "break;\n}\n"

    code += 'default: assert(false && '\
            '"Decoding not implemented for this instruction");'

    code += "}\nreturn regs;\n}\n\n"

    with open(decoder_c, "a") as d:
        d.write(code)

def gen_endloop():
    code = ""
    for name, pseudocode in endloops.items():
        code += "void "+name+"(void)\n"
        code += "{\n"
        proc = subprocess.Popen([semantics_path,
                                 '-t'],
                                stdout=subprocess.PIPE,
                                stdin=subprocess.PIPE)
        proc.stdin.write(pseudocode.encode("utf-8"))
        proc.stdin.close()
        proc.wait()
        assert(proc.returncode == 0 and "Unhandled endloop instruction!")
        code += proc.stdout.read().decode("utf-8")
        code += "}\n"
    with open(decoder_c, "a") as d:
        d.write(code)

def analyse_inst_mapping():
    no_match_count = 0
    regexes = map(to_regex, meta_instructions)
    patterns = list(map(re.compile, regexes))
    for i, inst_str in enumerate(instruction_strings):
        # Patch instruction strings to make them more easily parsable
        inst_str = inst_str.replace(" ", "")
        no_match = True
        for pattern in patterns:
            match = pattern.match(inst_str)
            if match is not None:
                no_match = False
        if no_match:
            no_match_count += 1
            assert(inst_str.split("=")[1][0] == "v" and "Non-vectorial "
                   "instruction has no corresponding meta-instruction!")
    print("{}/{} instructions did not match any meta "
          "instruction!".format(no_match_count, len(instruction_strings)))

def gen_decoder_header():
    multi_reference_count = 0
    decoder_header = DECODER_HEADER
    regexes = map(to_regex, meta_instructions)
    patterns = map(re.compile, regexes)
    # Map meta-instructions into effective instructions
    for pattern_index, pattern in enumerate(patterns):
        meta_instruction = meta_instructions[pattern_index]["str"]
        meta_instruction = re.sub(r"\[(:<<N)\]", r"\[:<<N\]", meta_instruction)
        meta_instruction = re.sub(r"\[(:<<1)\]", r"\[:<<N\]", meta_instruction)
        operands, _ = parse_op(meta_instruction)
        identifiers = [op.identifier for op in operands]
        flags = {}
        params = []
        # Each group becomes a parameter
        for k, v in pattern.groupindex.items():
            flags[v - 1] = k
        for i in range(len(flags)):
            params.append(flags[i])
        # Generate function signature
        if len(identifiers) + len(params) == 0:
            decoder_header += "regs_t function_{}(DisasContext *dc);\n".format(pattern_index)
        else:
            function_str = ("regs_t function_{}(DisasContext *dc, " +
                            ", ".join(["uint32_t {}"] * len(identifiers) +
                                      ["bool {}"] * len(params)) + ");\n")
            decoder_header += function_str.format(pattern_index,
                                                  *identifiers,
                                                  *params)
    print("{}/{} instructions were referenced multiple "
          "times!".format(multi_reference_count, len(instruction_strings)))
    decoder_header += "\n#endif"
    with open(decoder_h, "w") as f:
        f.write(decoder_header)

def indent():
    # Optionally, indent
    try:
        subprocess.run("indent -linux " + decoder_c, shell=True, check=True)
    except:
        pass

def main():
    parser = argparse.ArgumentParser()
    # Support programs
    parser.add_argument("semantics", metavar="SEMANTICS")

    # Data extracted from ISA reference manual
    parser.add_argument("meta_instructions_csv", metavar="META_INSTRUCTION_CSV")
    parser.add_argument("instructions_csv", metavar="INSTRUCTION_CSV")
    parser.add_argument("sub_instructions_csv", metavar="SUB_INSTRUCTION_CSV")
    parser.add_argument("const_ext_csv", metavar="CONST_EXT_CSV")

    # Generated by best-decoding
    parser.add_argument("instruction_decoding_json", metavar="INSTRUCTION_DECODING_JSON")
    parser.add_argument("sub_instruction_decoding_json", metavar="SUB_INSTRUCTION_DECODING_JSON")

    # Output files
    parser.add_argument("decoder_c", metavar="DECODER_C")
    parser.add_argument("decoder_h", metavar="DECODER_H")

    args = parser.parse_args()

    global semantics_path
    global meta_instructions_csv
    global instructions_csv
    global sub_instructions_csv
    global const_ext_csv
    global instruction_decoding_json
    global sub_instruction_decoding_json
    global decoder_c
    global decoder_h
    semantics_path = args.semantics
    meta_instructions_csv = args.meta_instructions_csv
    instructions_csv = args.instructions_csv
    sub_instructions_csv = args.sub_instructions_csv
    const_ext_csv = args.const_ext_csv
    instruction_decoding_json = args.instruction_decoding_json
    sub_instruction_decoding_json = args.sub_instruction_decoding_json
    decoder_c = args.decoder_c
    decoder_h = args.decoder_h

    global meta_instructions
    global instruction_strings
    global sub_instruction_strings
    global patterns
    # Parse instructions and meta-instructions
    meta_instructions = parse_meta_instructions()
    instruction_strings = parse_instructions(instructions_csv)
    sub_instruction_strings = parse_instructions(sub_instructions_csv)
    regexes = map(to_regex, meta_instructions)
    patterns = list(map(re.compile, regexes))
    analyse_inst_mapping()
    # decoder.h
    gen_decoder_header()
    # decoder.c
    gen_macros()
    gen_decoder_switch()
    gen_execute()
    gen_functions()
    # Duplex instructions
    gen_sub_decoder()
    gen_sub_execute()
    gen_endloop()
    # auto-indent
    indent()

if __name__ == "__main__":
    main()


from textwrap import dedent
from unittest import mock

from django.contrib.auth.models import User
from django.test import TestCase
from django_dynamic_fixture import get

from readthedocs.config.tests.test_config import get_build_config
from readthedocs.doc_builder.environments import DockerBuildEnvironment
from readthedocs.projects.models import Project
from readthedocs.telemetry.collectors import BuildDataCollector

@mock.patch.object(BuildDataCollector, "run")
class TestBuildDataCollector(TestCase):
    def setUp(self):
        self.user = get(User)
        self.project = get(Project, slug="test", users=[self.user])
        self.version = self.project.versions.first()

        config = get_build_config({})
        config.validate()

        self.environment = DockerBuildEnvironment(
            version=self.version,
            project=self.project,
            build={"id": 1},
            config=config,
            api_client=mock.MagicMock(),
        )
        self.collector = BuildDataCollector(self.environment)

    def test_get_operating_system(self, run):
        run.return_value = (0, "Description:\tUbuntu 20.04.3 LTS", "")
        out = self.collector._get_operating_system()
        self.assertEqual(out, "Ubuntu 20.04.3 LTS")

    def test_get_python_version(self, run):
        run.return_value = (0, "Python 3.8.12", "")
        out = self.collector._get_python_version()
        self.assertEqual(out, "3.8.12")

    def test_get_all_conda_packages(self, run):
        out = dedent(
            """
            [
                {
                    "base_url": "https://conda.anaconda.org/conda-forge",
                    "build_number": 0,
                    "build_string": "py_0",
                    "channel": "conda-forge",
                    "dist_name": "alabaster-0.7.12-py_0",
                    "name": "alabaster",
                    "platform": "noarch",
                    "version": "0.7.12"
                },
                {
                    "base_url": "https://conda.anaconda.org/conda-forge",
                    "build_number": 0,
                    "build_string": "pyh9f0ad1d_0",
                    "channel": "conda-forge",
                    "dist_name": "asn1crypto-1.4.0-pyh9f0ad1d_0",
                    "name": "asn1crypto",
                    "platform": "noarch",
                    "version": "1.4.0"
                }
            ]
            """
        )
        run.return_value = (0, out, "")
        self.assertEqual(
            self.collector._get_all_conda_packages(),
            [
                {
                    "name": "alabaster",
                    "channel": "conda-forge",
                    "version": "0.7.12",
                },
                {
                    "name": "asn1crypto",
                    "channel": "conda-forge",
                    "version": "1.4.0",
                },
            ],
        )

    def test_get_user_pip_packages(self, run):
        self.collector.config = get_build_config(
            {"python": {"install": [{"requirements": "docs/requirements.txt"}]}}
        )
        self.collector.config.validate()
        out = dedent(
            """
            requests-mock==1.8.0
            requests-toolbelt==0.9.1
            rstcheck==3.3.1
            Sphinx>=5  # >= specs
            requests   # no specs
            """
        )
        run.return_value = (0, out, "")
        self.assertEqual(
            self.collector._get_user_pip_packages(),
            [
                {"name": "requests-mock", "version": "1.8.0"},
                {"name": "requests-toolbelt", "version": "0.9.1"},
                {"name": "rstcheck", "version": "3.3.1"},
                {"name": "sphinx", "version": "unknown"},  # >= specs
                {"name": "requests", "version": "undefined"},  # no specs
            ],
        )

    def test_get_all_pip_packages(self, run):
        out = dedent(
            """
            [
                {
                    "name": "requests-mock",
                    "version": "1.8.0"
                },
                {
                    "name": "requests-toolbelt",
                    "version": "0.9.1"
                },
                {
                    "name": "rstcheck",
                    "version": "3.3.1"
                }
            ]
            """
        )
        run.return_value = (0, out, "")
        self.assertEqual(
            self.collector._get_all_pip_packages(),
            [
                {"name": "requests-mock", "version": "1.8.0"},
                {"name": "requests-toolbelt", "version": "0.9.1"},
                {"name": "rstcheck", "version": "3.3.1"},
            ],
        )

    def test_get_all_apt_packages(self, run):
        out = dedent(
            """
            apt 1.6.14
            base-files 10.1ubuntu2.11
            base-passwd 3.5.44
            bash 4.4.18-2ubuntu1.2
            bsdutils 1:2.31.1-0.4ubuntu3.7
            coreutils 8.28-1ubuntu1
            """
        )
        run.return_value = (0, out, "")
        self.assertEqual(
            self.collector._get_all_apt_packages(),
            [
                {
                    "name": "apt",
                    "version": "1.6.14",
                },
                {
                    "name": "base-files",
                    "version": "10.1ubuntu2.11",
                },
                {
                    "name": "base-passwd",
                    "version": "3.5.44",
                },
                {
                    "name": "bash",
                    "version": "4.4.18-2ubuntu1.2",
                },
                {
                    "name": "bsdutils",
                    "version": "1:2.31.1-0.4ubuntu3.7",
                },
                {
                    "name": "coreutils",
                    "version": "8.28-1ubuntu1",
                },
            ],
        )

    def test_get_user_apt_packages(self, run):
        self.collector.config = get_build_config(
            {
                "build": {
                    "os": "ubuntu-22.04",
                    "tools": {
                        "python": "3",
                    },
                    "apt_packages": ["cmake", "libclang"],
                }
            }
        )
        self.collector.config.validate()
        self.assertEqual(
            self.collector._get_user_apt_packages(),
            [
                {
                    "name": "cmake",
                    "version": "",
                },
                {"name": "libclang", "version": ""},
            ],
        )


import matplotlib.pyplot as plt
from matplotlib import rc
from matplotlib.ticker import ScalarFormatter

dpi = 100
tick_size = 20
label_size = 24
line_width = 3
title_size = 15

def plot_distances_graph(timestamps, min_distances, radiations, title=""):
    # unified plotting settings

    rc('font', **{'family': 'serif', 'serif': ['Palatino']})
    plt.rc('text', usetex=True)
    fig = plt.figure(figsize=(8, 6), dpi=dpi)
    ax = fig.add_subplot(111)

    timestamps = [float(timestamp) - float(min(timestamps))
                  for timestamp in timestamps]

    for idx, source in enumerate(radiations):
        label = f"Source {idx + 1} [{', '.join([f'{coord:.1f}' for coord in source])}]"
        ax.plot(timestamps, min_distances[idx], linewidth=line_width, label=label)

    ax.legend(loc='best', fontsize=tick_size)
    ax.set_xlabel(r"Time [s]", fontsize=label_size)
    ax.set_ylabel(r"Minimum distance [m]", fontsize=label_size)
    ax.tick_params(labelsize=tick_size)
    plt.tick_params(labelsize=tick_size)

    ax.grid(which="major")
 # Set y-axis scale to logarithmic
    ax.set_yscale('log')   # set y-axis scale to logarithmic
    ax.set_yticks([0.1,1.0,10])
   # plt.ylim(1, 16,1)
    ax.yaxis.set_major_formatter(ScalarFormatter())
    ax.get_yaxis().get_major_formatter().labelOnlyBase = False

    #ax.ticklabel_format(axis='y', style='sci', scilimits=(0, 0))
    #plt.title(title, fontsize=title_size)
    plt.tight_layout()
    plt.show(block=False)

# Commented out unused code
    # ax.yaxis.set_major_formatter(plt.ScalarFormatter(useMathText=True))
    # set x-axis ticks at every 11th timestamp
    # n_ticks = 11
    # tick_indices = np.linspace(0, len(timestamps) - 1, n_ticks).astype(int)
    # tick_labels = [f"{float(timestamps[i]):.0f}" for i in tick_indices]
    # ax.set_xticks([timestamps[i] for i in tick_indices])
   # ax.set_xticklabels(tick_labels, rotation=45)

def plot_points_graph(timestamps, estimations, title=""):

    # use latex text renderer
    # count the number of points for each timestamp
    point_counts = [len(points) for points in estimations]

    # create a list of x-values and y-values for each edge
    x_edges = [float(timestamps[0])]
    y_edges = [0]
    for i in range(len(timestamps)-1):
        x_edges.append(float(timestamps[i]))
        x_edges.append(float(timestamps[i+1]))
        y_edges.append(point_counts[i])
        y_edges.append(point_counts[i])
    x_edges.append(float(timestamps[-1]))
    y_edges.append(point_counts[-1])
    x_edges.append(float(timestamps[-1]))
    y_edges.append(0)

    # use latex text renderer
    rc('font', **{'family': 'serif', 'serif': ['Palatino']})
    plt.rc('text', usetex=True)

    fig = plt.figure(figsize=(8, 6), dpi=dpi)
    ax = fig.add_subplot(111)
    ax.plot(x_edges, y_edges, linewidth=line_width)
    ax.fill_between(x_edges, y_edges, color='lightblue')
    ax.set_xlabel("Time [s]", fontsize=label_size)
    ax.set_ylabel("Estimated number of sources", fontsize=label_size)
    ax.tick_params(labelsize=tick_size)
    plt.tick_params(labelsize=tick_size)
    ax.grid(which='major')
    plt.tight_layout()
    #plt.title(title, fontsize=title_size)

    # n_ticks = 11
    # tick_indices = np.linspace(0, len(timestamps) - 1, n_ticks).astype(int)
    # tick_labels = [f"{float(timestamps[i]):.1f}" for i in tick_indices]
    # ax.set_xticks([timestamps[i] for i in tick_indices])
    # ax.set_xticklabels(tick_labels, rotation=45)
    plt.show()


#coding=utf-8
from ast import IsNot
from types import NoneType
from bs4 import BeautifulSoup
from selenium import webdriver
import time
import re
import unicodedata
from csv import DictWriter
import os
import requests
import traceback
options = webdriver.ChromeOptions()
options.add_argument('--ignore-certificate-errors')   #主要是该条
options.add_argument('--ignore-ssl-errors')
chromeOptions = webdriver.ChromeOptions()
class DoubanParser:
    driver = webdriver.Chrome()
    records = []
    records_error = []
    def get_proxy(Any):
        return requests.get("http://127.0.0.1:5010/get/").json()

    def delete_proxy(proxy,Any):
        requests.get("http://127.0.0.1:5010/delete/?proxy={}".format(proxy))

# your spider code

    def getHtml(self,url):
    # ....
        retry_count = 5
        proxy = self.get_proxy().get("proxy")
        chromeOptions.add_argument("--proxy-server=http://"+proxy)
        browser = webdriver.Chrome(chrome_options = chromeOptions)
        while retry_count > 0:
            try:
                self.driver.get(url)
                # 使用代理访问
                html = self.driver.page_source
                return html
            except Exception:
                retry_count -= 1
        # 删除代理池中代理
        self.delete_proxy(proxy)
        return None

    def parse(self,page_url):
        try:
            html = self.getHtml(page_url)
            while(html == None):
                html = self.getHtml(page_url)
            page_soup = BeautifulSoup(html,features='lxml')
            movie_titles = page_soup.find('div',{'id':'wrapper'})
            title = movie_titles.h1.span.string##title : #wrapper > h1 > span

            book_rating = page_soup.find('div',{'class':'rating_self clearfix'})##interest_sectl > div > div.rating_self.clearfix > strong
            rating =  book_rating.strong.get_text();

            author = page_soup.find('div',{'id':'info'}).select('span')[0].a##info > span:nth-child(1) > a
            book_summary = page_soup.find('div',{'id':'link-report'})
            summary = ""
            if book_summary.find('span',{'class':'all hidden'}) is not None:
                for string in book_summary.find('span',{'class':'all hidden'}).stripped_strings:
                    summary += string #link-report > span.all.hidden > div > div > p
            elif book_summary.find('span',{'class':'short'}) is not None:
                 #link-report > span.short > div > p:nth-child(1)
                for string in book_summary.find('span',{'class':'short'}).stripped_strings:
                    summary += string;
            else:
                #link-report > div > div > p
                for string in book_summary.stripped_strings:
                    summary += string
            #link-report > div > div > p:nth-child(1)
            #summary = ''.join(summary.split())##剔除空格与换行符
            #summary = summary.replace([' ','\n','\u3000'],'')
            author_summary=""
            preauthor = page_soup.find('div',{'class':'related_info'})
            q = preauthor.select('div')[4]
            if preauthor.select('div')[4].find('span',{'class':'all hidden'}) is not None:
                for string in preauthor.select('div')[4].find('span',{'class':'all hidden'}).stripped_strings:
                    author_summary += string#content > div > div.article > div.related_info > div:nth-child(5) > span.all.hidden > div > p
            elif preauthor.select('div')[4].find('span',{'class':'short'}) is not None:
                #content > div > div.article > div.related_info > div:nth-child(5) > span.short > div > p
                for string in  preauthor.select('div')[4].find('span',{'class':'short'}).stripped_strings:
                    author_summary += string
            else:
                #content > div > div.article > div.related_info > div:nth-child(5) > div > div > p:nth-child(1)
                for string in preauthor.select('div')[4].stripped_strings:
                    author_summary += string
            movie_dict = {'book':title,'rating':rating,'link':page_url,'book summary':summary,'author summary':author_summary}##content > div > div.article > div.related_info > div:nth-child(5) > div > div > p
            self.records.append(movie_dict)
        except:
            print(traceback.format_exc())
            #movie_dict = {'book':"ERROR",'rating':"ERROR",'link':page_url,'book summary':traceback.format_exc(),'author summary':"ERROR"}
            #self.records_error.append(movie_dict)
    def read_txt(self):
        num = 1
        f = open("D:/360MoveData/Users/admin/Desktop/coursework/web_info/lab/lab1/Book_id.txt")
        byt = f.readlines()
        for line in byt:
            url = "https://book.douban.com/subject/"+line
            self.parse(url)
            print(num,"has been resolved")
            num =num + 1
        '''url = "https://book.douban.com/subject/1400707"'''
        self.parse(url)
        if os.path.exists("douban_book.csv"):
            os.remove("douban_book.csv")
        with open('douban_book.csv','w', newline='', encoding='utf-8-sig') as file:
            headers = [key for key in self.records[0].keys()]
            csv_writer = DictWriter(file, fieldnames=headers)
            csv_writer.writeheader()
            for record in self.records:
                csv_writer.writerow(record)
        if self.records_error != []:
            with open('douban_book_ERROR.csv','w', newline='', encoding='utf-8-sig') as file:
                headers = [key for key in self.records_error[0].keys()]
                csv_writer = DictWriter(file, fieldnames=headers)
                csv_writer.writeheader()
                for record in self.records_error:
                    csv_writer.writerow(record)

parser=DoubanParser()
parser.read_txt()


import numpy as np
import opt_einsum as oe
import torch

from .antenna_arrays import UniformLinearArray
from .ofdmchannel import MultiPathChannelConfig, OFDMBeamSpaceChannel, OFDMConfig
from .prepare_sim import get_prior_cov, get_scale_matrix_J
from .utils import db2lin, herm

def fim_c_xi(
    mpc_configs: MultiPathChannelConfig,
    ofdm_configs: OFDMConfig,
    tx_array: UniformLinearArray,
    rx_array: UniformLinearArray,
    w: torch.Tensor,
    channel_mat: torch.Tensor,
    sig2_noise: float = 1,
    wavelength: float = 1,
) -> tuple[torch.Tensor, torch.Tensor]:
    num_ant_rx, num_ant_tx, num_carriers, num_symbols = channel_mat.shape
    assert (num_ant_rx == rx_array.num_antennas) and (num_ant_tx == tx_array.num_antennas)
    if not isinstance(sig2_noise, torch.Tensor):
        sig2_noise = torch.ones((num_carriers, num_symbols), dtype=torch.complex128) * sig2_noise
    assert sig2_noise.shape == (num_carriers, num_symbols)

    At = torch.from_numpy(tx_array.steering_matrix(grid=mpc_configs.aods, wavelength=wavelength, axis=1)).clone()
    Dt = torch.from_numpy(tx_array.steering_mat_der(grid=mpc_configs.aods, wavelength=wavelength, axis=1)).clone()

    Ar = torch.from_numpy(rx_array.steering_matrix(grid=mpc_configs.aoas, wavelength=wavelength, axis=1)).clone()
    Dr = torch.from_numpy(rx_array.steering_mat_der(grid=mpc_configs.aoas, wavelength=wavelength, axis=1)).clone()

    At = At.conj()
    Dt = Dt.conj()

    tgrid, fgrid = torch.meshgrid(
        torch.arange(num_symbols) * ofdm_configs.symbol_time,
        torch.arange(num_carriers) * ofdm_configs.subcarrier_spacing,
        indexing="xy",
    )
    tgrid_ = oe.contract("nk,l->nkl", tgrid, torch.as_tensor(mpc_configs.doppler_shifts))
    fgrid_ = oe.contract("nk,l->nkl", fgrid, torch.as_tensor(mpc_configs.path_delays))
    Omega_nk = torch.exp(1j * 2 * torch.pi * (tgrid_ - fgrid_))
    G_nk = oe.contract("...nk,...nkl->...nkl", -1j * 2 * torch.pi * fgrid, Omega_nk)
    F_nk = oe.contract("...nk,...nkl->...nkl", 1j * 2 * torch.pi * tgrid, Omega_nk)
    path_gains = torch.as_tensor(mpc_configs.path_gains)

    C_x = w @ herm(w)

    BG_nk = oe.contract("nkl, l -> nkl", G_nk, path_gains)
    BF_nk = oe.contract("nkl, l -> nkl", F_nk, path_gains)
    BO_nk = oe.contract("nkl, l -> nkl", Omega_nk, path_gains)

    Lambda_nk = torch.concat((Omega_nk, 1j * Omega_nk, BG_nk, BF_nk, BO_nk, BO_nk), dim=-1)

    T = torch.concat((At, At, At, At, At, Dt), dim=1)
    R = torch.concat((Ar, Ar, Ar, Ar, Dr, Ar), dim=1)

    term1 = oe.contract(
        "nk, nkl, lt, ti, ij, nkj -> lj",
        2 / sig2_noise,
        Lambda_nk.conj(),
        herm(T),
        C_x.conj(),
        T,
        Lambda_nk,
    )
    term2 = herm(R) @ R
    fim_mat = torch.mul(term1, term2).real

    return fim_mat

def fim_c_s(w, HH, sig2_noise):
    nt, ns = w.shape
    M = np.prod(HH.shape[2:])
    wHHw = oe.contract("rm, rtnk, ts -> msnk", w.conj(), HH, w).reshape(ns, ns, -1)
    fim_complex = wHHw.permute(2, 0, 1) / sig2_noise * 2
    fim_real = torch.zeros((M, 2 * ns, 2 * ns))
    for i in range(M):
        temp = fim_complex[i]
        fim_real[i, :ns, :ns] = temp.real
        fim_real[i, :ns, ns:] = (1j * temp).real
        fim_real[i, ns:, :ns] = (-1j * temp).real
        fim_real[i, ns:, ns:] = temp.real

    return fim_complex, fim_real

def fim_p(C_xi, C_s):
    Gamma = torch.linalg.inv(C_xi)
    I_s = torch.linalg.inv(C_s)
    return Gamma, I_s

def obj_bfim(X, params_list, mpc_configs, cfg):
    ofdm_configs = OFDMConfig(
        subcarrier_spacing=cfg.subcarrier_spacing,
        num_guard_carriers=(0, 0),
        Nfft=cfg.num_carriers,
    )

    snrlin = db2lin(cfg.snrdb)
    power_noise = cfg.power_signal / snrlin
    noise_sigma2 = power_noise

    tx_array = UniformLinearArray(num_antennas=cfg.num_ant_tx)
    rx_array = UniformLinearArray(num_antennas=cfg.num_ant_rx)

    w = X * np.sqrt(cfg.power_signal)
    expec_HH = 0
    expec_fimc_xi = 0

    new_br, new_bi, new_tau, new_fD, new_aoas, new_aods = params_list
    new_tau = abs(new_tau)  # if sampled delay is negative, make it positive
    num_samples = len(new_br)
    new_b = new_br + 1j * new_bi

    scale_matrix_J = get_scale_matrix_J(cfg)

    for i in range(num_samples):
        newmpc = MultiPathChannelConfig(
            path_gains=new_b[i].numpy(),
            path_delays=new_tau[i].numpy(),
            doppler_shifts=new_fD[i].numpy(),
            aoas=new_aoas[i].numpy(),
            aods=new_aods[i].numpy(),
        )
        ofdmbschann = OFDMBeamSpaceChannel(
            mpc_configs=newmpc,
            ofdm_config=ofdm_configs,
            tx_array=tx_array,
            rx_array=rx_array,
        )
        Hf = ofdmbschann.get_channel(num_carriers=cfg.num_carriers, num_symbols=cfg.num_symbols)
        Hf_tensor = torch.from_numpy(Hf).clone()
        Hf_tensor = torch.fft.ifftshift(Hf_tensor, dim=-2)
        expec_HH = expec_HH + torch.einsum("rt..., rm... -> tm...", Hf_tensor.conj(), Hf_tensor)

        fimc_xi = fim_c_xi(
            mpc_configs=newmpc,
            ofdm_configs=ofdm_configs,
            tx_array=tx_array,
            rx_array=rx_array,
            w=w,
            channel_mat=Hf_tensor,
            sig2_noise=noise_sigma2,
        )
        expec_fimc_xi = expec_fimc_xi + fimc_xi

    if not num_samples:
        ofdmbschann = OFDMBeamSpaceChannel(
            mpc_configs=mpc_configs,
            ofdm_config=ofdm_configs,
            tx_array=tx_array,
            rx_array=rx_array,
        )
        Hf = ofdmbschann.get_channel(num_carriers=cfg.num_carriers, num_symbols=cfg.num_symbols)
        Hf_tensor = torch.from_numpy(Hf).clone()
        Hf_tensor = torch.fft.ifftshift(Hf_tensor, dim=-2)
        expec_HH = torch.einsum("rt..., rm... -> tm...", Hf_tensor.conj(), Hf_tensor)
        fimc_xi = fim_c_xi(
            mpc_configs=mpc_configs,
            ofdm_configs=ofdm_configs,
            tx_array=tx_array,
            rx_array=rx_array,
            w=w,
            channel_mat=Hf_tensor,
            sig2_noise=noise_sigma2,
        )
        expec_fimc_xi = fimc_xi
        num_samples = 1

    C_xi, C_s = get_prior_cov(cfg)
    Gamma, I_s = fim_p(C_xi, C_s)
    expec_fim_xi = expec_fimc_xi / num_samples + Gamma
    expec_HH = expec_HH / num_samples
    fim_complex, fim_real = fim_c_s(w, expec_HH, noise_sigma2)

    num_re = cfg.num_carriers * cfg.num_symbols
    term1 = torch.logdet(herm(scale_matrix_J) @ expec_fim_xi @ scale_matrix_J)
    term2 = torch.linalg.slogdet(fim_complex + I_s)[1].real.sum() * 2
    return term1, term2, (cfg.multi_obj_factor * term1 + (1 - cfg.multi_obj_factor) / num_re * term2)


from turtle import Screen
from Scoreboard import Scoreboard
from paddle import Paddle
from ball import Ball
import time

canvas = Screen()
canvas.screensize(500, 500)
canvas.bgcolor("black")
canvas.title("Pong")
canvas.tracer(0)
player1 = Paddle(posx=-350, posy=0, player="Player1")
player2 = Paddle(posx=350, posy=0, player="Player2")
ball = Ball()
scoreboard = Scoreboard()
game_state = True

canvas.listen()
canvas.onkeypress(key="Up", fun=player1.moveUp)
canvas.onkeypress(key="Down", fun=player1.moveDown)
canvas.onkeypress(key="w", fun=player2.moveUp)
canvas.onkeypress(key="s", fun=player2.moveDown)

while game_state:
    canvas.update()
    ball.update()

    if ball.ycor() == 400 or ball.ycor() == -400:
        ball.reverse_y_direction()
    if ball.distance(player2) < 50 and ball.xcor() > 330:
        ball.reverse_x_direction()
    if ball.distance(player1) < 50 and ball.xcor() < -330:
        ball.reverse_x_direction()

    if ball.xcor() > 400:
        ball.restart()
        scoreboard.update_score_left()
    elif ball.xcor() < -400:
        ball.restart()
        scoreboard.update_score_right()

    time.sleep(ball.current_speed)

canvas.exitonclick()


import sys
import cv2
import math
from skimage import draw
import numpy as np
from typing import List
from pathlib import Path
import logging
import argparse

def alpha_blur(img, alpha_mask, kernel_size=10):
    """
    Blur image proportional to the given mask
    :param img:
    :param alpha_mask:
    :param kernel_size:
    :return:
    """
    # apply morphology open to smooth the outline
    # kernel_size = max(2, (10 // (i + 1)))
    # kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (kernel_size, kernel_size))
    # blurred_img = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)
    # kernel_size = max(20, (150 // (i + 1)))

    #blurred_img = cv2.bilateralFilter(img, 9, 5, 5)
    blurred_img = cv2.GaussianBlur(img, (21, 21), 11)

    if alpha_mask is not None:
        if alpha_mask.ndim == 3 and alpha_mask.shape[-1] == 3:
            alpha = alpha_mask / 255.0
        else:
            alpha = cv2.cvtColor(alpha_mask, cv2.COLOR_GRAY2BGR) / 255.0
        blurred_img = cv2.convertScaleAbs(blurred_img * (1 - alpha) + img * alpha)

    return blurred_img

def positive_sharpen(i, overblur=False, coeff=8.):
    # emphasize the edges
    blurred = cv2.blur(i,(5,5))
    sharpened = i + (i - blurred) * coeff
    if overblur:
        return cv2.blur(np.maximum(sharpened,i),(11,11))
    return cv2.blur(np.maximum(sharpened,i),(3,3))

def sample_color(img, x, y, neighbor_size):
    # sample color from image => converges faster.
    color = img[max(0, y - neighbor_size):y + neighbor_size,
               max(0, x - neighbor_size):x + neighbor_size].mean(axis=(0,1))

    return color

def get_phase_and_magnitude(img, sobel_kernel_size=7, magnitude_power=0.3):
    """
    Calculate phase/rotation angle from image gradient
    :param img: image to compute phase from
    :param sobel_kernel_size:
    :return: phase in float32 radian
    """
    # grayify
    img_gray = img.astype('float32') #cv2.cvtColor(img, cv2.COLOR_BGR2GRAY).astype('float32')

    # gradient (along x and y axis)
    xg = cv2.Sobel(img_gray, cv2.CV_32F, 1, 0, ksize=sobel_kernel_size)
    yg = - cv2.Sobel(img_gray, cv2.CV_32F, 0, 1, ksize=sobel_kernel_size)

    # calculates the rotation angle of the 2D vectors gradients
    phase = cv2.phase(xg, yg)

    # calculates the magnitude of the 2D vectors gradients
    magnitude = cv2.magnitude(xg, yg)
    magnitude = magnitude / magnitude.max()  # normalize to [0, 1] range

    # make magnitude more uniform
    magnitude = np.power(magnitude, magnitude_power)

    return phase, magnitude

def get_point_angle_and_magnitude(x, y, phase_map, magnitude_map,
                                  phase_neighbor_size: int, magnitude_neighbor_size: int):
    # get gradient orientation info from phase map (phase should be between [0,2pi))
    # compute an average phase around the point, for an area proportional to brush size
    phase = phase_map[max(0, y - phase_neighbor_size):y + phase_neighbor_size,
            max(0, x - phase_neighbor_size):x + phase_neighbor_size].mean()
    # choose direction perpendicular to gradient
    angle = (((phase / math.pi) * 180) + 90) % 360

    magnitude = magnitude_map[max(0, y - magnitude_neighbor_size):y + magnitude_neighbor_size,
                max(0, x - magnitude_neighbor_size):x + magnitude_neighbor_size].mean()

    return angle, magnitude

def get_edges(img, img_blur_size=5, min_hyst_val=100, max_hyst_val=200, edges_blur_size=5):
    """
    Detect image edges
    :param img:
    :param img_blur_size: kernel for gaussian-blur on image
    :param min_hyst_val: hysteresis min threshold (canny edge detection)
    :param max_hyst_val: hysteresis max threshold (canny edge detection)
    :param edges_blur_size: blur size applied to edge results
    :return: norm blurred edges and uint8 original edges images
    """
    # remove noise to improve edge detection results
    blurred_img = cv2.GaussianBlur(img, (img_blur_size, img_blur_size), 0)

    # canny edge detection
    edges = cv2.Canny((blurred_img * 255).astype('uint8'), min_hyst_val, max_hyst_val)

    # blur edges
    blurred_edges = cv2.blur(edges, (edges_blur_size, edges_blur_size)).astype('float32') / 255

    norm_edges = blurred_edges / blurred_edges.sum()  # normalize to probabilities

    return norm_edges, edges

def get_distance_map(src_img):
    """
    Get distance values for given image. Distance is the closest zero pixel for each pixel of the source image.
    :param src_img: grayscale image
    :return: distance map
    """
    # use simple euclidean distance and a 3×3 mask for a fast, coarse distance estimation
    dist = cv2.distanceTransform(255 - src_img, cv2.DIST_L2, 3)

    # normalize distance image between 0.0 and 1.0
    cv2.normalize(dist, dist, 0, 1.0, cv2.NORM_MINMAX)

    return dist

def get_min_radius_to_edge(dist_img, start_pos, end_pos, dist_threshold=0.01):
    # get lines coordinates
    line = np.transpose(np.array(draw.line(start_pos[0], start_pos[1], end_pos[0], end_pos[1])))
    line = np.array([[x, y] for [x, y] in line if (0 <= x < dist_img.shape[1] and 0<= y < dist_img.shape[0])])
    # get dist values overlapping the line
    data = dist_img[line[:, 1], line[:, 0]]

    # find first index below threshold
    radius = np.argmax(data < dist_threshold)

    return radius

def add_border_to_img(img, border_size: int):
    border_img = cv2.copyMakeBorder(img, border_size, border_size, border_size, border_size, cv2.BORDER_CONSTANT,
                                    value=[255] * 3)
    border_img = border_img.astype('float32') / 255  # convert to float32

    img = cv2.copyMakeBorder(img, border_size // 2, border_size // 2, border_size // 2, border_size // 2,
                             cv2.BORDER_CONSTANT, value=[255] * 3)
    img = cv2.resize(img, border_img.shape[:2][::-1])
    img = img.astype('float32') / 255  # convert to float32

    return img, border_img

def combine_salience_images(input_dir: Path, salience_paths: List[Path], weights: List[float], output_dir: Path):
    main_imgs = list(input_dir.glob('*.png')) + list(input_dir.glob('*.jpg'))

    output_dir.mkdir(exist_ok=True, parents=True)

    # for each image in out target input folder, compute composed salience image
    for img_path in main_imgs:
        main_img = cv2.imread(str(img_path), cv2.IMREAD_GRAYSCALE).astype('float32') / 255
        composed_salience_img = np.zeros(main_img.shape, dtype='float32')

        salience_img_name = img_path.stem
        for i, salience_path in enumerate(salience_paths):
            salience_img_ext = None
            for ext in ['.jpg', '.png']:
                if (salience_path / (salience_img_name + ext)).exists():
                    salience_img_ext = ext
                    break
            if salience_img_ext is None:
                continue

            salience_img_path = str(salience_path / (salience_img_name + ext))
            salience_img = cv2.imread(str(salience_img_path), cv2.IMREAD_GRAYSCALE)
            salience_img = salience_img.astype('float32') / 255  # convert to float32
            salience_img = salience_img.clip(0.)
            composed_salience_img = composed_salience_img + (weights[i] * salience_img)

        composed_salience_img = composed_salience_img / composed_salience_img.max()

        cv2.imwrite(str(output_dir / f'{salience_img_name}.png'), composed_salience_img * 255)

def main(_=None):
    logging.getLogger().setLevel(logging.INFO)

    parser = argparse.ArgumentParser(description='Image Utils')

    parser.add_argument('-i', '--input-path', required=True)
    parser.add_argument('-o', '--output-path', required=True)
    parser.add_argument('-s', '--salience-paths', type=str, nargs='+',)

    args = parser.parse_args()
    input_path = Path(args.input_path)
    output_path = Path(args.output_path)
    salience_paths = [Path(p) for p in args.salience_paths]
    weights = [1. for p in salience_paths]

    combine_salience_images(input_path, salience_paths=salience_paths, weights=weights, output_dir=output_path)

if __name__ == "__main__":
    main(sys.argv[1:])


# Rosen Gradient Projection Method
# Ref:《最优化理论与算法》陈宝林，Chapter 12
# Author: Stella

import numpy as np
from scipy.optimize import minimize_scalar

#minmize problem
#min 2*x[0]**2 + 2*x[1]**2 -2*x[0]*x[1] - 4*x[0] - 6*x[1]
#s.t.
# -x[0]-x[1]>=-2,
# -x[0]-5*x[1]>=-5,
# x[0]>=0, x[1]>=0

n=2
#Coefficient matrix
A = np.array([[-1, -1], [-1, -5], [1, 0], [0, 1]])
b = np.array([-2, -5, 0, 0]).reshape((1, len(A)))

#Objective function
def fun(x):
    ff = 2*x[0]**2 + 2*x[1]**2 -2*x[0]*x[1] - 4*x[0] - 6*x[1]
    return ff

#left hand side
def func_eval(x, A, b):
    con_left = A @ x - b
    return con_left

def gradient(x):
    g_1 = 4*x[0]-2*x[1]-4
    g_2 = 4*x[1]-2*x[0]-6
    gg  = np.array([g_1,g_2])
    return gg

def func(lambda_o):
    find_min_f = fun(x + lambda_o * d)
    find_min_f = np.int(find_min_f)
    return find_min_f

x=np.array([0,0])

number = 1
while number < 10000:
    con_left = func_eval(x,A,b)
    suoyin_1 = np.where(con_left == 0)[1]
    suoyin_2 = np.where(con_left >0)[1]
    A1 = A[suoyin_1]
    A2 = A[suoyin_2]
    b1 = np.transpose(np.transpose(b)[suoyin_1])
    b2 = np.transpose(np.transpose(b)[suoyin_2])

    N = A1.T @ (np.linalg.inv(A1 @ A1.T)) @ A1
    I = np.eye(len(N))
    P = I - N
    G = gradient(x)
    d = -1 * P @ G

    count = 1
    while count < 100:
        if d.any() == 0 or all([v < 1e-5 for v in d]):
            W = (np.linalg.inv(A1 @ A1.T)) @ A1 @ G
            if (W>=0).all():
                print('The K-T point is', x)
                judge = 1
                break
            else:
                print('Continue searching')
                judge = 0
                suoyin_3 = np.argmin(W)
                A1 = np.delete(A1, suoyin_3, axis=0)
                N = A1.T @ (np.linalg.inv(A1 @ A1.T)) @ A1
                I = np.eye(len(N))
                P = I - N
                G = gradient(x)
                d = -1 * P @ G
                count = count + 1
        else:
            break
        d = d

    b_hat = b2 - A2 @ x
    d_hat = A2 @ d
    d_hat_u = d_hat.reshape((1, len(d_hat)))
    d_hat_0 = np.array(np.where(d_hat < 0))
    lieshu = (d_hat_0.shape)[1]
    lamb = []
    for i in range(lieshu):
        k = d_hat_0[0, i]
        lam = b_hat[0, k] / d_hat_u[0, k]
        lamb.append(lam)
    lamb = np.array(lamb, dtype=float)
    lambda_max = np.min(lamb)
    a_bound = 0

    ## if the objective function is not concave,convex or expressed:
    # search = 50000
    # fi = []
    # for i in range(search):
    #     ki = i * 1/search
    #     fii = fun(x+ki*lambda_max*d)
    #     fi.append(fii)
    #     i = i+1
    # fi = np.array(fi, dtype = float)
    # suoyin_4 = np.argmin(fi)
    # lambda_op = (1+suoyin_4) * (1/search) *lambda_max

    #if the objective function is normal
    res = minimize_scalar(fun, bracket=(a_bound, lambda_max), bounds=(a_bound, lambda_max), args=(),
                                method='bounded', tol=None, options=None)
    lambda_op = res.x

    if judge ==1:
        break
        print('The K-T point is', x)
    else:
        number = number+1
        x = x + lambda_op * d

print('The optimal solution is',x)