import logging
import logging.config
import os
import json
import argparse
import subprocess
from pathlib import Path
import requestHandler

logger = logging.getLogger(__name__)
req = requestHandler.requestHandler(api_token = "api_token")

#setting up the logging system
def setupLogging(default_path = "logging.json", default_level = logging.INFO):
    if os.path.exists(default_path):
        with open(default_path, 'rb') as file:
            config = json.load(file)
        logging.config.dictConfig(config)
    else:
        logging.basicConfig(level = default_level)

def initializeGit(path):
    subprocess.run(["git", "init", path])

#creating a command line program
def run(args):
    dirPath = args.dir_path
    req.connect()
    if os.path.exists(dirPath):
        logger.info("path exists...")
        logger.info("Checking gits repos for {dir}.....".format(dir = os.path.abspath(dirPath)))
        for dirs in Path(dirPath).iterdir():
            if not any(folder.startswith('.git') for folder in os.listdir(dirs)):
                initializeGit(dirs)
                logger.info("git initialised")
                req.create_new_repo(os.path.basename(dirs),os.path.abspath(dirs))
            else:
                logger.info("git already initialised for %s", dirs)
    else:
        logger.error("Not found")

def main():
    setupLogging()
    logger.info("Testing..")
    parser = argparse.ArgumentParser("Initialize the git repo and upload it to the ")
    parser.add_argument("-p", help = "folder path to initialize git", dest = "dir_path", type = str, required=True)
    parser.set_defaults(func = run)
    args = parser.parse_args()
    args.func(args)

if __name__ == "__main__":
        main()


"""geoapi URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/3.2/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.urls import include, path
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import include, path
from django.views.generic import TemplateView
from geoapi.api import views
from rest_framework import routers
from rest_framework.permissions import AllowAny
from rest_framework.schemas import get_schema_view
from rest_framework_simplejwt.views import (
    TokenObtainPairView,
    TokenRefreshView,
)

# Wire up our API using automatic URL routing.
# Additionally, we include login URLs for the browsable API.
urlpatterns = [
    path('', views.geolocation_api_root, name='root'),
    path('admin/', admin.site.urls),
    path('api-auth/', include('rest_framework.urls', namespace='rest_framework')),
    path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
    path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
    path('geodata/', views.IpGeolocationDataList.as_view(), name='geodata-list'),
    path('geodata/<path:ip>', views.IpGeolocationDataDetail.as_view(),
         name='geodata-detail'),
    path('openapi/', get_schema_view(
        title="IP Geolocation API",
        description="API for storing geolocation data",
        version="1.0.0",
        permission_classes=[AllowAny],
        public=True,
    ), name='openapi-schema'),
    path('docs/', TemplateView.as_view(
        template_name='api/swagger-ui.html',
        extra_context={'schema_url': 'openapi-schema'}
    ), name='swagger-ui'),
]


#!/usr/bin/env python

# A script to kill all your PBS jobs on the University of Arizona cluster.
# Usage: python killjobs.py

import os
import subprocess as sp
from tqdm import tqdm

def main():
    username = os.getcwd().split('/')[5]
    output = sp.check_output(['qstat', '-u', username])
    lines = output.split('\n')[5:]
    jobs = [words[0] for words in [line.split() for line in lines] if len(words) > 1]
    for job in tqdm(jobs, desc = "Deleting PBS jobs"):
        sp.call(['qdel', job])

if __name__ == '__main__':
    main()


from __future__ import division, print_function

import itertools
import json

from opt_einsum import get_symbol, contract_expression

def save(name, eq, shapes):
    filename = 'oe_sample_{}.json'.format(name)
    print('saving {}'.format(filename))
    with open(filename, 'wb') as f:
        json.dump({'eq': eq, 'shapes': shapes}, f)

def symbol_stream():
    for i in itertools.count():
        yield get_symbol(i)

# Examples of batched Hidden Markov Model inference.
#
#  x ---> x ---> x      ---->
#  |      |      |      time
#  V      V      V
#  y      y      y
def make_hmm_example(length, latent_dim=32, observed_dim=4, batch_dim=10, query=None):
    symbols = symbol_stream()
    b = next(symbols)
    shapes = []
    inputs = []
    xs = []
    ys = []
    for t in range(length):
        xs.append(next(symbols))
        ys.append(next(symbols))

        # Add observation matrix.
        shapes.append([batch_dim, latent_dim, observed_dim])
        inputs.append(b + xs[-1] + ys[-1])

        if t >= 1:
            # Add transition matrix.
            shapes.append([batch_dim, latent_dim, latent_dim])
            inputs.append(b + xs[-2] + xs[-1])

    inputs = ','.join(inputs)
    output = '' if query is None else b + xs[query]
    eq = inputs + '->' + output
    name = 'hmm_{}_{}_{}_{}_{}'.format(
        length, latent_dim, observed_dim, batch_dim, 'total' if query is None else query)
    contract_expression(eq, *shapes, optimize='eager')  # smoke test
    save(name, eq, shapes)

# Examples of batched Dynamic Bayes Net inference.
#
#         w
#       / | \
#      /  |  \
#    /    |    \
#  x ---> x ---> x
#  |      |      |
#  y ---> y ---> y          ---->
#  |      |      |          time
#  V      V      V
#  z      z      z
def make_dbn_example(length, global_dim=2, latent_dim=32, observed_dim=4, batch_dim=10, query=None):
    symbols = symbol_stream()
    b = next(symbols)
    w = next(symbols)
    shapes = []
    inputs = []
    xs = []
    ys = []
    zs = []
    for t in range(length):
        xs.append(next(symbols))
        ys.append(next(symbols))
        zs.append(next(symbols))

        # Add vertical dependencies.
        shapes.append([batch_dim, global_dim, latent_dim])
        inputs.append(b + w + xs[-1])

        shapes.append([batch_dim, latent_dim, latent_dim])
        inputs.append(b + xs[-1] + ys[-1])

        shapes.append([batch_dim, latent_dim, latent_dim])
        inputs.append(b + ys[-1] + zs[-1])

        if t >= 1:
            # Add horizontal dependencies.
            shapes.append([batch_dim, latent_dim, latent_dim])
            inputs.append(b + xs[-2] + xs[-1])

            shapes.append([batch_dim, latent_dim, latent_dim])
            inputs.append(b + ys[-2] + ys[-1])

    inputs = ','.join(inputs)
    output = '' if query is None else b + w + xs[query] + ys[query]
    eq = inputs + '->' + output
    name = 'dbn_{}_{}_{}_{}_{}'.format(
        length, latent_dim, observed_dim, batch_dim, 'total' if query is None else query)
    contract_expression(eq, *shapes, optimize='eager')  # smoke test
    save(name, eq, shapes)

def make_all():
    for length in [10, 100]:
        for query in [None, 0, -1, length // 2]:
            make_hmm_example(length, query=query)
            make_dbn_example(length, query=query)

if __name__ == '__main__':
    make_all()


import functools
import os
import numpy as np
import matplotlib.pyplot as plt
import itertools
import sys

from globals import EXTRACTED_DATA_SET_PATH, RGB_CHANNELS, IMAGE_DIM_X, IMAGE_DIM_Y

def unpickle(file_path):
    unpickler = _get_unpickler()
    with open(file_path, 'rb') as file_handler:
        return unpickler(file_handler)

def _get_unpickler():
    if sys.version_info[0] < 3:
        import cPickle as pickle
        return pickle.load
    else:

        import pickle as pickle
        return functools.partial(pickle.load, encoding='latin1')

class DataSet(object):
    def __init__(self):
        self._meta = None
        self._data_batch = None
        self._test_batch = None

        self.training_pictures = None
        self.training_pictures_labels = None
        self.testing_pictures = None
        self.testing_pictures_labels = None

    def load_data(self, data_set_path):
        self._meta = unpickle(os.path.join(data_set_path, 'batches.meta'))
        self._data_batch = [self._get_path(data_set_path, x) for x in range(1, 6)]
        self._test_batch = unpickle(os.path.join(data_set_path, 'test_batch'))

        self.training_pictures = self._get_training_pictures()
        self.training_pictures_labels = list(itertools.chain(*[batch['labels'] for batch in self._data_batch]))
        self.testing_pictures = self._transform_batch_format(self._test_batch['data'])
        self.testing_pictures_labels = self._test_batch['labels']

    @staticmethod
    def _get_path(data_set_path, x):
        path = os.path.join(data_set_path, 'data_batch_' + str(x))
        return unpickle(path)

    def _get_training_pictures(self):
        transformed_batch_list = [self._transform_batch_format(batch['data']) for batch in self._data_batch]
        return list(itertools.chain.from_iterable(transformed_batch_list))

    def get_training_labels_text(self):
        return [self._meta['label_names'][x] for x in self.training_pictures_labels]

    def get_testing_labels_text(self):
        return [self._meta['label_names'][x] for x in self.testing_pictures_labels]

    @staticmethod
    def _transform_batch_format(x):
        return x.reshape(len(x), RGB_CHANNELS, IMAGE_DIM_X, IMAGE_DIM_Y).transpose(0, 2, 3, 1)

def get_data_set():
    loaded_data_set = DataSet()
    loaded_data_set.load_data(EXTRACTED_DATA_SET_PATH)
    return loaded_data_set

def visualize(x, y, grid_size=(4, 5)):
    fig, ax = plt.subplots(*grid_size, figsize=(12, 6))

    def plot_picture(dim_x, dim_y):
        chosen_image = np.random.choice(range(len(x)))
        ax[dim_x][dim_y].set_axis_off()
        ax[dim_x][dim_y].text(0, 36, y[chosen_image])
        ax[dim_x][dim_y].imshow(x[chosen_image])

    for i, j in itertools.product(range(grid_size[0]), range(grid_size[1])):
        plot_picture(i, j)

    plt.show()

if __name__ == "__main__":
    data_set = get_data_set()
    X = data_set.training_pictures
    Y = data_set.get_training_labels_text()
    visualize(X, Y, (4, 10))


from telegram.ext import CallbackContext,Handler,Filters,Dispatcher,Updater,CommandHandler,MessageHandler,MessageFilter,BaseFilter,Filters
from telegram import InlineQueryResultArticle, InputTextMessageContent, KeyboardButton, ReplyKeyboardMarkup,InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import CallbackQueryHandler
import os
PORT = int(os.environ.get('PORT', '8443'))
import datetime
import logging
logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
                    level=logging.INFO)

logger = logging.getLogger(__name__)
update = Updater('5591645553:AAEbvWbYTgVjFexAAAcMLnsf_No1uSD_gkk',use_context=True)

ADMINS = {
    'NARENGI':'133473427',
    'NABAT':'5182966885',
}

KEYS = {
    'NARENGI':'🍊',
    'NABAT':'🪴',
    # 'HARDO':'باهم.',
    'LAGHV':'👈🏻',
}
BANLIST = []
LASTS = {

}
LASTSADMINS = {

}
def start(update:Updater,context:CallbackContext):
    id = update.effective_chat.id
    admin = False
    admins = ''
    for i in ADMINS:
        if str(id) == ADMINS[i]:
            admin = True
            admins = i
    if admin:
        context.bot.send_message(chat_id=id,text=f'سلام {admins} .=)')
    else:
        name = ''
        # {'last_name': '\u200c\u200cAmani', 'id': 133473427, 'first_name': '\u200c\u200c\u200c\u200cMohaّmadreza', 'type': 'private'}
        # for i in ADMINS:
        name = ''
        last_name = ''
        username = ''
        url = 'tg://openmessage?user_id=%d'%id
        try:
            name = update.effective_chat.first_name
        except:
            pass
        try:
            last_name = update.effective_chat.last_name
        except:
            pass
        try:
            username = '@' + update.effective_chat.username
        except:
            pass
        text = f""" یه نفر جدید ربات رو استارت زد:
    name = {name + ' ' +last_name}
    username = {username}
    url = {url}
        """
        for i in ADMINS:
            context.bot.send_message(chat_id=ADMINS[i],text=text)
        welcome ='''
        سلام. ^^
    به ربات ما خوش اومدی، لطفا برای پیام دادن از کلیدهای زیر استفاده کن.
    🍊 برای پیام دادن به نارنگی؛
    🪴 برای پیام دادن به نبات.'''
        buttons = [
        # [KeyboardButton(KEYS['HARDO'])],
        [KeyboardButton(KEYS['NABAT']), KeyboardButton(KEYS['NARENGI'])]]
        reply_markup = ReplyKeyboardMarkup(buttons, resize_keyboard=True)
        context.bot.send_message(chat_id=id,text=welcome,reply_markup=reply_markup)
def laghv(update:Updater,context:CallbackContext,text,id):
    matn = "به صفحه اصلی برگشتی. :'("
    buttons = [
    # [KeyboardButton(KEYS['HARDO'])],
    [KeyboardButton(KEYS['NABAT']), KeyboardButton(KEYS['NARENGI'])]]
    reply_markup = ReplyKeyboardMarkup(buttons, resize_keyboard=True)
    context.bot.send_message(chat_id=id,text=matn,reply_markup=reply_markup)
def laghv_admin(update:Updater,context:CallbackContext,text,id):
    matn = "به صفحه اصلی بازگشتید."
    buttons = [
    []]
    reply_markup = ReplyKeyboardMarkup(buttons, resize_keyboard=True)
    context.bot.send_message(chat_id=id,text=matn,reply_markup=reply_markup)
def admin():
    pass
def to(update:Updater,context:CallbackContext,text,id):

    if text == '🍊':
        name = 'نارنگی🍊'
        LASTS[id] = 'NARENGI'
    else:
        name = 'نبات🪴'
        LASTS[id] = 'NABAT'
    matn = f'''
در حال پیام دادن به {name} هستی.
لطفا متن، عکس، آهنگ یا هرچیز دیگهای که دوست داری رو برام بفرست.

برای برگشت از دستور 👈🏻 استفاده کن.  ^^'''
    buttons = [
    [KeyboardButton(KEYS['LAGHV'])]]
    reply_markup = ReplyKeyboardMarkup(buttons, resize_keyboard=True)
    context.bot.send_message(chat_id=id,text=matn,reply_markup=reply_markup)
def toAdmin(update:Updater,context:CallbackContext, admin, message):
    id = ADMINS[admin]
    senderID = update.effective_chat.id
    buttons = [
    [KeyboardButton(KEYS['LAGHV'])]]
    reply_markup = ReplyKeyboardMarkup(buttons, resize_keyboard=True)
    matn = 'پیام شما ارسال شد. '
    context.bot.send_message(chat_id=senderID,text=matn,reply_markup=reply_markup)
    mid = context.bot.forward_message(chat_id=id,from_chat_id=senderID,message_id= message.message_id).message_id
    if senderID not in BANLIST:
        keyboard = [
                    [InlineKeyboardButton("نمایش اطلاعات", callback_data=f'show_{senderID}')],
                    [InlineKeyboardButton("مسدود کردن", callback_data=f'ban_{senderID}')],
                    [InlineKeyboardButton("پاسخ دادن", callback_data=f'answer_{senderID}')]
                ]
    else:
        keyboard = [
                    [InlineKeyboardButton("نمایش اطلاعات", callback_data=f'show_{senderID}')],
                    [InlineKeyboardButton("لفو مسدودیت", callback_data=f'unban_{senderID}')],
                    [InlineKeyboardButton("پاسخ دادن", callback_data=f'answer_{senderID}')]
                ]
    reply_markup = InlineKeyboardMarkup(keyboard)
    context.bot.send_message(chat_id=id,text='اطلاعات کاربر', reply_markup=reply_markup)
def from_admin(update:Updater,context:CallbackContext):
    id = update.effective_chat.id
    getterID = LASTSADMINS[str(id)]
    # LASTSADMINS.pop[str(id)]
    message = update.message.message_id
    context.bot.copyMessage(chat_id=getterID,from_chat_id=id,message_id=message)
    context.bot.send_message(chat_id=id,text='پیام شما با موفقیت ارسال شد.')
    buttons = [
    [KeyboardButton(KEYS['LAGHV'])]]
    reply_markup = ReplyKeyboardMarkup(buttons, resize_keyboard=True)

def sendMessage(update:Updater,context:CallbackContext):
    id = update.effective_chat.id
    admin = False
    for i in ADMINS:
        if str(id) == ADMINS[i]:
            admin = True
    if admin:
        text = update.message.text
        if text == '👈🏻':
            laghv_admin(update,context,text,id)
        elif str(id) not in LASTSADMINS:
            context.bot.send_message(chat_id=id,text='متوجه نشدم باید چیکار کنم.')
        else:
            from_admin(update,context)
    else:
        if id in BANLIST:
            context.bot.send_message(chat_id=id,text='شما مسدود هستید.')
            return
        text = update.message.text
        if text == '🍊' or text == '🪴':
            to(update,context,text,id)
        elif text == '👈🏻':
            laghv(update,context,text,id)
        elif id in LASTS:
            if LASTS[id] == 'NABAT':
                toAdmin(update,context, 'NABAT', update.message)
            elif LASTS[id] == 'NARENGI':
                toAdmin(update,context, 'NARENGI', update.message)
            elif str(id) not in LASTS:
                context.bot.send_message(chat_id=id,text='متوجه نشدم باید چیکار کنم.')
def press_button_callback(update:Updater,context:CallbackContext):
    data:str = update.callback_query.data
    id = update.callback_query.message.chat.id
    if data.startswith('show'):
        context.bot.send_message(chat_id=id,text='tg://openmessage?user_id='+data.split('_')[1])
    if data.startswith('ban'):
        context.bot.send_message(chat_id=id,text='یوزر مورد نظر بن شد. :(')
        BANLIST.append(data.split('_')[1])
    if data.startswith('unban'):
        context.bot.send_message(chat_id=id,text='یوزر مورد نظر از مسدودیت خارج شد.')
        BANLIST.remove(data.split('_')[1])
    if data.startswith('answer'):
        context.bot.send_message(chat_id=id,text='داری به نمیدونم کی کی پاسخ میدی. لطفا متنت رو بفرست.')
        LASTSADMINS[str(id)]=data.split('_')[1]
def main():
    dispatcher = update.dispatcher
    startHandler = CommandHandler('start',start)
    dispatcher.add_handler(startHandler)
    dispatcher.add_handler(MessageHandler(Filters.all,sendMessage))
    update.dispatcher.add_handler(CallbackQueryHandler(press_button_callback))
    update.start_webhook(
            listen="0.0.0.0",
            port=int(PORT),
            url_path='5591645553:AAEbvWbYTgVjFexAAAcMLnsf_No1uSD_gkk',
            webhook_url='https://tele2430.herokuapp.com/' + '5591645553:AAEbvWbYTgVjFexAAAcMLnsf_No1uSD_gkk'
        )

    update.idle()

if __name__ == '__main__':
    main()


#
# Test with mini cheetah
#

# Imports
import pybullet as p
import pybullet_data as pd
import time

# Connection with GUI
p.connect(p.GUI)

# Earth-like gravity
p.setGravity(0, 0, -9.8)

# Add path for data
p.setAdditionalSearchPath(pd.getDataPath())

# Load floor from URDF
floor = p.loadURDF("plane.urdf")

# Cheetah starting position
starting_position = [0, 0, 0.5]

# Load mini cheetah with starting position
robot = p.loadURDF("mini_cheetah/mini_cheetah.urdf", starting_position)

# How many joint
num_joints = p.getNumJoints(robot)

# Change Cheetah's color
p.changeVisualShape(robot, -1, rgbaColor=[1, 0, 0, 1])

# Informations
print("{} joints".format(num_joints))

# For each joints
for j in range(num_joints):
    # Change joint's color ?
    p.changeVisualShape(robot, j, rgbaColor=[1, 1, 0, 1])

    # Get joint info
    joint_info = p.getJointInfo(robot, j)

    # Print info
    print("Index : {}".format(joint_info[0]))
    print("Name : {}".format(joint_info[1]))
    print("Type : {}".format(joint_info[2]))
    print("qIndex : {}".format(joint_info[3]))
    print("uIndex : {}".format(joint_info[4]))
    print("Damping : {}".format(joint_info[6]))
    print("Friction : {}".format(joint_info[7]))
    print("")

    # Position and force
    pos = 0
    if j == 0:
        force = 200
    else:
        force = 0
    # end if

    # Change position and force
    p.setJointMotorControl2(robot, j, p.POSITION_CONTROL, pos, force=force)
# end for

# Change timestep
dt = 1.0 / 240.0
p.setTimeStep(dt)

# Infinite
while True:
    # Run on step
    p.stepSimulation()

    # Sleep for one timestep
    time.sleep(dt)
# end while


from models.arcface_model import Backbone
from models.temporal_convolutional_model import TemporalConvNet

import torch
from torch import nn

from torch.nn import Linear, BatchNorm1d, BatchNorm2d, Dropout, Sequential, Module

class Flatten(Module):
    def forward(self, input):
        return input.view(input.size(0), -1)

class my_res50(nn.Module):
    def __init__(self, num_classes=8, embedding_dim=512):
        super().__init__()
        self.backbone = Backbone(input_channels=3, num_layers=50, drop_ratio=0.4, mode="ir")

        self.backbone.output_layer = Sequential(BatchNorm2d(embedding_dim),
                                                Dropout(0.4),
                                                Flatten(),
                                                Linear(embedding_dim * 5 * 5, embedding_dim),
                                                BatchNorm1d(embedding_dim))

        self.logits = nn.Linear(in_features=embedding_dim, out_features=num_classes)

        from torch.nn.init import xavier_uniform_, constant_

        for m in self.backbone.output_layer.modules():
            if isinstance(m, nn.Linear):
                m.weight = xavier_uniform_(m.weight)
                m.bias = constant_(m.bias, 0)
            elif isinstance(m, nn.BatchNorm2d):
                m.weight.data.fill_(1)
                m.bias.data.zero_()
            elif isinstance(m, nn.BatchNorm1d):
                m.weight.data.fill_(1)
                m.bias.data.zero_()

        self.logits.weight = xavier_uniform_(self.logits.weight)
        self.logits.bias = constant_(self.logits.bias, 0)

    def forward(self, x, extract_cnn=False):
        x = self.backbone(x)

        if extract_cnn:
            return x

        x = self.logits(x)
        return x

class my_temporal(nn.Module):
    def __init__(self, model_name, visual_backbone_path,
                 num_inputs=192, cnn1d_channels=[128, 128, 128], cnn1d_kernel_size=5,
                 cnn1d_dropout_rate=0.1, modality=['video'],
                 embedding_dim=256, hidden_dim=128, lstm_dropout_rate=0.5, bidirectional=True, output_dim=1):
        super().__init__()
        self.output_dim = output_dim
        self.model_name = model_name

        # Initialize the visual backbone

        if 'video' in modality:
            spatial = my_res50()
            state_dict = torch.load(visual_backbone_path, map_location='cpu')
            spatial.load_state_dict(state_dict)

            for param in spatial.parameters():
                param.requires_grad = False

            self.spatial = spatial.backbone

        # Initialize the temporal module
        if "tcn" in model_name and "lstm" in model_name:
            raise ValueError("Please disambiguate the model name since it includes both tcn and lstm!")

        if "tcn" in model_name:
            self.temporal = TemporalConvNet(num_inputs=num_inputs, num_channels=cnn1d_channels,
                                            kernel_size=cnn1d_kernel_size, dropout=cnn1d_dropout_rate)
            self.regressor = nn.Linear(cnn1d_channels[-1], output_dim)

        elif "lstm" in model_name:
            self.temporal = nn.LSTM(input_size=num_inputs, hidden_size=hidden_dim, num_layers=2,
                                    batch_first=True, bidirectional=bidirectional, dropout=lstm_dropout_rate)
            input_dim = hidden_dim
            if bidirectional:
                input_dim = hidden_dim * 2

            self.regressor = nn.Linear(input_dim, output_dim)

    def forward(self, x):
        assert len(x.keys()) == 1, "This models is not designed for more than one modalities."

        if "video" in x:
            num_batches, length, channel, width, height = x['video'].shape
            x = x['video'].view(-1, channel, width, height)
            x = self.spatial(x)
            _, feature_dim = x.shape
            x = x.view(num_batches, length, feature_dim).contiguous()
        else:
            x = x[list(x.keys())[0]]
            x = x.squeeze(1)

        if "lstm" in self.model_name:
            x, _ = self.temporal(x)
            x = x.contiguous()
        else:
            x = x.transpose(1, 2).contiguous()
            x = self.temporal(x).transpose(1, 2).contiguous()
        feature = x

        x = self.regressor(x).contiguous()

        return x, feature

{'xavier_uniform_': 'torch.nn.init.xavier_uniform_', 'constant_': 'torch.nn.init.constant_'}


import uuid
import cv2
import uvicorn
from fastapi import FastAPI, File, UploadFile
import numpy as np
from PIL import Image
from semantic_segmentation_model import Model
from plot import CLASSES, generate_images_from_mask, generate_legend

global model, legend, legend_name

legend = generate_legend(CLASSES)
app = FastAPI()
model = Model("semantic-segmentation-adas-0001")
model.initialize_vino_model()

legend_name = "/storage/legend.png"
cv2.imwrite(legend_name, generate_legend(CLASSES)[..., ::-1])

@app.get("/")
def read_root():
    return {"message": "Welcome from the API"}

# post end point to receive image from frontend and send back predictions paths
@app.post("/predict")
def get_image(file: UploadFile = File(...)):

    image = np.array(Image.open(file.file).convert("RGB"))
    raw_mask = model.predict(image)
    mask, overlay = generate_images_from_mask(image, raw_mask, CLASSES)
    str_uuid = str(uuid.uuid4())
    mask_name = f"/storage/{str_uuid}_mask.png"
    overlay_name = f"/storage/{str_uuid}_overlay.png"
    cv2.imwrite(mask_name, mask[..., ::-1])
    cv2.imwrite(overlay_name, overlay[..., ::-1])
    return {
        "mask_name": mask_name,
        "overlay_name": overlay_name,
        "legend_name": legend_name,
    }

if __name__ == "__main__":
    uvicorn.run("main:app", host="0.0.0.0", port=8080)


from abc import ABC, ABCMeta, abstractmethod
from dataclasses import asdict, dataclass
from enum import Enum, EnumMeta, IntEnum, auto
import json
from typing import Callable, Dict, Mapping, Optional, TypeVar, Union, List

import cvc5 as pycvc5

from .sorts import *

class Kind(Enum):
    BVAdd           = auto()
    BVSub           = auto()
    BVMul           = auto()
    BVOr            = auto()
    BVAnd           = auto()
    BVNot           = auto()
    BVXor           = auto()
    BVExtract       = auto()
    BVConcat        = auto()
    BVOrr           = auto() # OR reduction
    BVXorr          = auto() # XOR reduction
    BVUle           = auto() # Unsigned less than/equal
    BVUlt           = auto() # Unsigned less than
    BVUge           = auto() # Unsigned greater than/equal
    BVUgt           = auto() # unsigned greater than
    BVZeroPad       = auto()
    BVSignExtend    = auto()
    BVSll           = auto() # Shift left (logical)
    BVSrl           = auto() # Shift right (logical)
    BVSra           = auto() # Shift right (arithmetic)
    Or              = auto()
    And             = auto()
    Not             = auto()
    Xor             = auto()
    Implies         = auto()
    Ite             = auto()
    Match           = auto()
    Lambda          = auto()
    Equal           = auto()
    Distinct        = auto()
    Exists          = auto()
    ForAll          = auto()
    Select          = auto()
    Store           = auto()

    def to_cvc5(self, _cvc5_ctx):
        try:
            return _OP_KIND_MAPPING[self]
        except KeyError:
            raise NotImplementedError(f"cannot convert kind {self}")

    @staticmethod
    def from_cvc5(cvc5_kind):
        try:
            return _OP_KIND_REV_MAPPING[cvc5_kind]
        except KeyError:
            raise NotImplementedError(f"cannot convert CVC5 kind {cvc5_kind}")

    def to_sygus2(self):
        raise NotImplementedError()

# https://cvc5.github.io/docs/api/cpp/kind.html
# Maps our Kind class to pycvc5.Kind...
_OP_KIND_MAPPING = {
    Kind.BVAdd:         pycvc5.Kind.BITVECTOR_ADD,
    Kind.BVSub:         pycvc5.Kind.BITVECTOR_SUB,
    Kind.BVMul:         pycvc5.Kind.BITVECTOR_MULT,

    Kind.BVOr:          pycvc5.Kind.BITVECTOR_OR,
    Kind.BVAnd:         pycvc5.Kind.BITVECTOR_AND,
    Kind.BVNot:         pycvc5.Kind.BITVECTOR_NOT,
    Kind.BVXor:         pycvc5.Kind.BITVECTOR_XOR,
    Kind.BVSll:         pycvc5.Kind.BITVECTOR_SHL,
    Kind.BVSrl:         pycvc5.Kind.BITVECTOR_LSHR,
    Kind.BVSra:         pycvc5.Kind.BITVECTOR_ASHR,

    Kind.BVExtract:     pycvc5.Kind.BITVECTOR_EXTRACT,
    Kind.BVConcat:      pycvc5.Kind.BITVECTOR_CONCAT,

    Kind.BVOrr:         pycvc5.Kind.BITVECTOR_REDOR,

    Kind.BVUle:         pycvc5.Kind.BITVECTOR_ULE,
    Kind.BVUlt:         pycvc5.Kind.BITVECTOR_ULT,
    Kind.BVUge:         pycvc5.Kind.BITVECTOR_UGE,
    Kind.BVUgt:         pycvc5.Kind.BITVECTOR_UGT,

    Kind.BVZeroPad:     pycvc5.Kind.BITVECTOR_ZERO_EXTEND,
    Kind.BVSignExtend:  pycvc5.Kind.BITVECTOR_SIGN_EXTEND,

    Kind.Equal:         pycvc5.Kind.EQUAL,
    Kind.Distinct:      pycvc5.Kind.DISTINCT,
    Kind.Or:            pycvc5.Kind.OR,
    Kind.And:           pycvc5.Kind.AND,
    Kind.Not:           pycvc5.Kind.NOT,
    Kind.Xor:           pycvc5.Kind.XOR,
    Kind.Implies:       pycvc5.Kind.IMPLIES,
    Kind.Ite:           pycvc5.Kind.ITE,
    Kind.Match:         pycvc5.Kind.MATCH,

    Kind.Lambda:        pycvc5.Kind.LAMBDA,
    Kind.Exists:        pycvc5.Kind.EXISTS,
    Kind.ForAll:        pycvc5.Kind.FORALL,

    Kind.Select:        pycvc5.Kind.SELECT,
    Kind.Store:         pycvc5.Kind.STORE
}
# ...and vice versa
_OP_KIND_REV_MAPPING = {v: k for k, v in _OP_KIND_MAPPING.items()}

_OP_SYGUS_SYMBOLS = {
    Kind.BVAdd: "bvadd",
    Kind.BVSub: "bvsub",
    Kind.BVMul: "bvmult",
    Kind.BVOr: "bvor",
    Kind.BVAnd: "bvand",
    Kind.BVNot: "bvnot",
    Kind.BVXor: "bvxor",
    Kind.BVSll: "bvshl",
    Kind.BVSrl: "bvashr",
    Kind.BVSra: "bvlshr",
    Kind.BVUlt: "bvult",
    Kind.BVUle: "bvule",
    Kind.BVUgt: "bvugt",
    Kind.BVUge: "bvuge",
    # extract is a special case
    Kind.BVConcat: "concat",
    Kind.Or: "or",
    Kind.And: "and",
    Kind.Not: "not",
    Kind.Xor: "xor",
    Kind.Equal: "=",
    Kind.Ite: "ite",
    Kind.Implies: "=>",
    Kind.Exists: "exists",
    Kind.ForAll: "forall",
    Kind.Select: "select",
    Kind.Store: "store",
}

# === BEGIN SMT TERMS ===

_T = TypeVar("_T")

class Term(Translatable, ABC):
    """
    Representation for an SMT term. Operator overrides perform type checking and in some cases,
    implicit type conversions between arguments; construct an `OpTerm` manually if this behavior
    is undesired.
    """

    def _binop_type_check(self, other, sext=False, zpad=True, cast_int=True) -> Tuple["Term", "Term"]:
        """
        Checks that two operands have the same sort.

        If sext or zpad are set, if both are bitvectors and one is shorter than the other,
        then the one with the smaller width is zero-padded/sign-extended to the appropriate width.
        If sext/zpad are set and one arg is a boolean but the other is a bitvector, the boolean will
        be upcasted with an ITE statement.

        When `cast_int` is set, if `other` is an int, it will automatically be wrapped in
        a constant of the appropriate sort.
        """
        assert not (zpad and sext), "zpad and sext cannot both be set"
        if cast_int and isinstance(other, int):
            if isinstance(other, BoolConst):
                other = other.value
            if self.is_bool_expr():
                assert other in (0, 1), f"cannot coerce int {other} to {self.sort}"
                return self, (BoolConst.T if other else BoolConst.F) # type: ignore
            elif self.is_bv_expr():
                bitwidth = self.c_bitwidth()
                assert other < (2 ** bitwidth), f"int constant {other} exceeds max value of bv{bitwidth}"
                return self, BVConst(other, bitwidth)
        assert isinstance(other, Term), f"cannot combine {self} with {other}"
        assert hasattr(self, "sort"), repr(self)
        assert hasattr(other, "sort"), repr(other)
        s_bw = self.c_bitwidth()
        o_bw = other.c_bitwidth()
        if s_bw == o_bw:
            return self, other
        if sext:
            if s_bw > o_bw:
                if other.is_bool_expr():
                    return self, other.ite(BVConst((1 << o_bw) - 1, o_bw), BVConst(0, o_bw))
                return self, other.sign_extend(BVConst(s_bw - o_bw, o_bw))
            else:
                if self.is_bool_expr():
                    return self.ite(BVConst((1 << o_bw) - 1, o_bw), BVConst(0, o_bw)), other
                return self.sign_extend(BVConst(o_bw - s_bw, s_bw)), other
        if zpad:
            if s_bw > o_bw:
                if other.is_bool_expr():
                    return self, other.ite(BVConst(1, o_bw), BVConst(0, o_bw))
                return self, other.zero_pad(BVConst(s_bw - o_bw, o_bw))
            else:
                if self.is_bool_expr():
                    return self.ite(BVConst(1, o_bw), BVConst(0, o_bw)), other
                return self.zero_pad(BVConst(o_bw - s_bw, s_bw)), other
        assert self.sort == other.sort, f"cannot combine value {self} of sort {self.sort} to {other} of sort {other.sort}"
        return self, other

    # === OPTIMIZATIONS AND TREE TRAVERSALS ===

    def replace_vars(self, m: Mapping["Variable", "Term"]) -> "Term":
        """
        Returns a new term tree with all references to `var` replaced by `new_term`.
        """
        if isinstance(self, Variable) and self in m:
            return m[self]
        t = self
        for i, child in enumerate(self._children):
            t = t._replace_child(child.replace_vars(m), i)
        return t

    def zpad_all_children(self, extra_bits: int) -> "Term":
        """
        Zero pads all immediate children by `extra_bits`.
        This is necessary to implement verilog carry bit idioms.
        """
        t = self
        for i, child in enumerate(self._children):
            t = t._replace_child(child.zpad(extra_bits), i)
        return t

    def preorder_visit_tree(self, visit_fn: Callable[["Term"], _T], shortcircuit=True) -> _T:
        """
        Calls `visit_fn` on this node, then recursively on all children.
        Returns whatever `visit_fn(self)` returns.

        If `shortcircuit` is True, then this function will return without visiting
        its children if  `visit_fn(self)` returns a falsey value.
        """
        rv = visit_fn(self)
        if not shortcircuit or bool(rv):
            for s in self._children:
                assert isinstance(s, Term), s
                s.preorder_visit_tree(visit_fn, shortcircuit)
        return rv

    def get_vars(self) -> List["Variable"]:
        """
        Returns the list of variables referenced in this expression tree.
        """
        visited = set()
        child_vars = []
        def visitor(term):
            if term in visited:
                return
            if isinstance(term, Variable):
                visited.add(term)
                child_vars.append(term)
        self.preorder_visit_tree(visitor, False)
        return child_vars

    def optimize(self) -> "Term":
        if isinstance(self, OpTerm):
            # This method must also traverse children!
            return self.optimize()
        t = self
        for i, child in enumerate(self._children):
            t = t._replace_child(child.optimize(), i)
        return t

    def const_fold(self) -> "Term":
        if isinstance(self, OpTerm):
            # This method must also traverse children!
            return self.const_fold()
        t = self
        for i, child in enumerate(self._children):
            t = t._replace_child(child.const_fold(), i)
        return t

    def typecheck(self) -> bool:
        """
        Performs rudimentary type checking (no scope/semantic checking).
        Returns True on success, False on failure. Short circuits.

        TODO for now, this just ensures everything in the tree is a term
        TODO error reporting
        """
        for s in self._children:
            if not isinstance(s, Term):
                print(f"type error: child term {s} is not a Term object")
                return False
            if not s.typecheck():
                return False
        return True

    def eval(self, values: Dict[str, int]) -> Union["BVConst", "BoolConst"]:
        """
        Evaluates this expression with variables replaced by the values specified in `values`.
        """
        raise NotImplementedError()

    # === PROPERTIES AND HELPER FUNCTIONS ===

    def is_const(self) -> bool:
        return isinstance(self, BVConst) or isinstance(self, BoolConst)

    def is_bool_expr(self) -> bool:
        return isinstance(self.sort, BoolSort)

    def is_bv_expr(self) -> bool:
        return isinstance(self.sort, BVSort)

    def is_array_expr(self) -> bool:
        return isinstance(self.sort, ArraySort)

    def is_bv_or_bool_expr(self) -> bool:
        return isinstance(self.sort, BVSort) or isinstance(self.sort, BoolSort)

    def c_bitwidth(self) -> int:
        """
        Gets the bitwidth of this term's sort, erroring if the sort does not have a bitwidth.
        """
        if not self.is_bv_or_bool_expr():
            raise Exception(f"cannot get bitwidth of non-bv/bool term {self}")
        return self.sort.bitwidth

    def _maybe_bool2bv1(self):
        if isinstance(self, BoolConst):
            return BVConst(int(self), 1)
        elif self.is_bool_expr():
            return self.ite(BVConst(1, 1), BVConst(0, 1))
        else:
            return self

    # === OPERATOR OVERRIDES AND SYNTACTIC SUGAR ===

    def ite(self, t_term, f_term):
        """
        Constructs an ITE expression with this variable as condition.

        If this term is of sort BV1, then an expression is automatically added to
        check if this is equal to the constant 1bv1.
        """
        if self.is_bv_expr():
            assert self.c_bitwidth() == 1
            cond = OpTerm(Kind.Equal, (self, BVConst(1, self.c_bitwidth())))
        else:
            assert self.is_bool_expr()
            cond = self
        t_term, f_term = t_term._binop_type_check(f_term)
        return OpTerm(Kind.Ite, (cond, t_term, f_term))

    def match_const(
        self,
        cases: Dict[Union[int, "BVConst"], "Term"],
        # default: Optional["Term"]=None TODO implement default
    ):
        """
        A more primitive form of an SMT match expression. SMTLIB match expressions
        perform destructuring akin to functional programming pattern matching, but
        we only support matching against constants.

        When this term takes on a certain value, the expression should evaluate to
        the corresponding value in `cases`. All keys in `cases` must be of the same
        sort as this term, or are casted if they are int.

        This match must be exhaustive, so if cases do not provide all possible values
        of this sort, then `default` must be specified. An Exception is raised if the match
        is not exhaustive.
        """
        assert self.is_bv_expr()
        sort_max = (1 << self.c_bitwidth()) - 1
        covered = 0 # Bit set representing which cases have been covered
        args = [self] # OpTerm sees this as pairs of (case, term) occurring consecutively
        for c, term in cases.items():
            if isinstance(c, int):
                c = BVConst(c, self.c_bitwidth())
            assert c.val <= sort_max
            if (covered & (1 << c.val)) != 0:
                # Check if the case was already set
                raise Exception(f"Match case {c.val} occurred multiple times")
            covered |= 1 << c.val
            args.append(c)
            args.append(term)
        all_covered = (1 << sort_max) - 1 # 1 bit for each possible value
        default = None
        if not default:
            assert covered != all_covered, "Match default was provided, but cases were already exhaustive"
        else:
            assert covered == all_covered, "Match was non-exhaustive"
        return OpTerm(Kind.Match, tuple(args))

    def implies(self, other):
        return OpTerm(Kind.Implies, self._binop_type_check(other))

    def __lt__(self, other):
        return OpTerm(Kind.BVUlt, self._binop_type_check(other))

    def __le__(self, other):
        return OpTerm(Kind.BVUle, self._binop_type_check(other))

    def op_eq(self, other):
        """
        We can't override `__eq__` without breaking a decent amount of stuff, so
        `op_eq` is syntactic sugar for an equality expression instead.
        """
        return OpTerm(Kind.Equal, self._binop_type_check(other))

    def op_ne(self, other):
        return OpTerm(Kind.Distinct, self._binop_type_check(other))

    def __gt__(self, other):
        return OpTerm(Kind.BVUgt, self._binop_type_check(other))

    def __ge__(self, other):
        return OpTerm(Kind.BVUge, self._binop_type_check(other))

    def __add__(self, other):
        return OpTerm(Kind.BVAdd, self._binop_type_check(other))

    def __and__(self, other):
        if isinstance(other, Term) and other.is_bool_expr():
            op = Kind.And
        else:
            op = Kind.BVAnd
        return OpTerm(op, self._binop_type_check(other))

    def __invert__(self):
        if self.is_bool_expr():
            op = Kind.Not
        else:
            op = Kind.BVNot
        return OpTerm(op, (self,))

    def __mul__(self, other):
        return OpTerm(Kind.BVMul, self._binop_type_check(other))

    def __neg__(self):
        raise NotImplementedError()

    def __or__(self, other):
        if isinstance(other, Term) and other.is_bool_expr():
            op = Kind.Or
        else:
            op = Kind.BVOr
        return OpTerm(op, self._binop_type_check(other))

    def __lshift__(self, other):
        return self.sll(other)

    def sll(self, other):
        # TODO typecheck all shifts
        return OpTerm(Kind.BVSll, self._binop_type_check(other))

    def __rshift__(self, other):
        # Python right shift is technically arithmetic since integers
        # don't have a fixed size
        return self.sra(other)

    def srl(self, other):
        return OpTerm(Kind.BVSrl, self._binop_type_check(other))

    def sra(self, other):
        return OpTerm(Kind.BVSra, self._binop_type_check(other))

    def __sub__(self, other):
        return OpTerm(Kind.BVSub, self._binop_type_check(other))

    def __xor__(self, other):
        if isinstance(other, Term) and other.is_bool_expr():
            op = Kind.Xor
        else:
            op = Kind.BVXor
        return OpTerm(op, self._binop_type_check(other))

    def __getitem__(self, key):
        if self.is_array_expr():
            # Array indexing
            if isinstance(key, Term):
                if not key.sort == self.sort.idx_sort:
                    raise TypeError(f"cannot index {self} (sort {self.sort}) with {key} (sort {key.sort})")
                return OpTerm(Kind.Select, (self, key))
            elif isinstance(key, int):
                # Convert key to appropriate index sort
                i_key = type(self.sort.idx_sort)(key)
                return OpTerm(Kind.Select, (self, i_key))
            else:
                raise TypeError(key)
        # Bitvector indexing
        if self.is_bool_expr():
            width = 1
        else:
            assert self.is_bv_expr(), "only BV terms support indexing, instead term was " + str(self)
            width = self.c_bitwidth()
        wrap = lambda i: BVConst(i, width)
        def shift_and_mask(term):
            """
            Encodes extracts of a variable index as a shift + mask.
            """
            if term.c_bitwidth() == width:
                shamt = term
            else:
                shamt = term.zpad(width - term.c_bitwidth())
            return OpTerm(Kind.BVAnd, (OpTerm(Kind.BVSrl, (self, shamt)), BVConst(1, width)))
        if isinstance(key, int):
            # Extracts a single bit
            # ite will auto-cast to a bool
            return OpTerm(Kind.BVExtract, (self, wrap(key), wrap(key))).ite(BoolConst.T, BoolConst.F)
        elif isinstance(key, Term):
            return shift_and_mask(key.start)
        elif isinstance(key, slice):
            if isinstance(key.start, Term) and not key.start.is_const() and key.start == key.stop:
                return shift_and_mask(key.start)
            if isinstance(key.start, int):
                hi = wrap(max(key.start, key.stop))
            else:
                hi = key.start
            if isinstance(key.stop, int):
                lo = wrap(min(key.start, key.stop))
            else:
                lo = key.stop
            if isinstance(hi, BVConst):
                assert hi.val < width, f"extract upper index {hi.val} of '{self}' exceeds bounds of bv{width}"
            if isinstance(lo, BVConst):
                assert lo.val >= 0, f"extract lower index {lo.val} of '{self}' was negative"
                if hi == lo:
                    return OpTerm(Kind.BVExtract, (self, hi, lo)).ite(BoolConst.T, BoolConst.F)
            return OpTerm(Kind.BVExtract, (self, hi, lo))
        raise TypeError(f"cannot index {self} with {key}")

    def __setitem__(self, key, value):
        if self.is_array_expr():
            if isinstance(key, type(self.sort.idx_sort)):
                pass
            elif isinstance(key, int):
                # Convert key to appropriate index sort
                key = type(self.sort.idx_sort)(key)
            else:
                raise TypeError(key)
            if isinstance(value, type(self.sort.elem_sort)):
                pass
            elif isinstance(key, int):
                value = type(self.sort.elem_sort)(key)
            else:
                raise TypeError(key)
            return OpTerm(Kind.Store, (self, key, value))
        raise TypeError(key)

    def concat(self, *others):
        if len(others) == 0:
            return self
        else:
            return OpTerm(Kind.BVConcat, (self, *others))

    def zpad(self, extra_bits: Union["BVConst", int]):
        return self.zero_pad(extra_bits)

    def zero_pad(self, extra_bits: Union["BVConst", int]):
        """Zero pads this term with an addition `extra_bits` bits."""
        if isinstance(extra_bits, int):
            extra_bits = BVConst(extra_bits, self.c_bitwidth())
        if self.is_bool_expr():
            new_width = self.c_bitwidth() + extra_bits.val
            return self.ite(BVConst(1, new_width), BVConst(0, new_width))
        return OpTerm(Kind.BVZeroPad, (self, extra_bits))

    def sext(self, extra_bits: Union["BVConst", int]):
        return self.sign_extend(extra_bits)

    def sign_extend(self, extra_bits: Union["BVConst", int]):
        """Sign extends this term with an addition `extra_bits` bits."""
        if isinstance(extra_bits, int):
            extra_bits = BVConst(extra_bits, self.c_bitwidth())
        if self.is_bool_expr():
            new_width = self.c_bitwidth() + extra_bits.val
            return self.ite(BVSort(new_width).max_bv_const(), BVConst(0, new_width))
        return OpTerm(Kind.BVSignExtend, (self, extra_bits))

    def orr(self):
        return OpTerm(Kind.BVOrr, (self,))

    def xorr(self):
        return OpTerm(Kind.BVXorr, (self,))

    # === ABSTRACT AND SHARED STATIC METHODS ===

    @staticmethod
    def from_cvc5(cvc5_term):
        from cvc5 import Kind as k
        cvc5_kind = cvc5_term.getKind()
        if cvc5_kind == k.VARIABLE:
            return Variable.from_cvc5(cvc5_term)
        elif cvc5_kind == k.LAMBDA:
            return LambdaTerm.from_cvc5(cvc5_term)
        elif cvc5_kind in (k.EXISTS, k.FORALL):
            return QuantTerm.from_cvc5(cvc5_term)
        elif cvc5_kind == k.CONST_BITVECTOR:
            return BVConst.from_cvc5(cvc5_term)
        elif cvc5_kind == k.CONST_BOOLEAN:
            return BoolConst(cvc5_term.getBooleanValue())
        elif cvc5_kind in _OP_KIND_REV_MAPPING:
            return OpTerm.from_cvc5(cvc5_term)

        # TODO what corresponds to UFTerm?
        raise NotImplementedError("Cannot convert from CVC5 term of kind " + str(cvc5_kind))

    @property
    @abstractmethod
    def sort(self):
        raise NotImplementedError()

    @property
    def _should_paren(self):
        return len(self._children) > 0 and not isinstance(self, ApplyUF) \
            and not (isinstance(self, OpTerm) and self.kind == Kind.BVExtract)

    @property
    @abstractmethod
    def _children(self):
        raise NotImplementedError()

    @abstractmethod
    def _replace_child(self, new_term, index) -> "Term":
        """
        Returns a new term with the `index`th child replaced by new_term
        """
        raise NotImplementedError()

# Needed to be able to make BoolConst sealed
class _TermMeta(ABCMeta, EnumMeta):
    pass

@dataclass(frozen=True)
class Variable(Term):
    """
    This class represents variable references. To obtain a `Translatable` for a variable
    declaration, use the `get_decl()` method.
    """

    name: str
    _sort: Sort

    def __post_init__(self):
        assert isinstance(self.name, str) and len(self.name) > 0
        assert isinstance(self._sort, Sort), f"{self._sort} is not a Sort instance"

    def __str__(self):
        return f"{self.name}"

    def to_uf(self) -> "UFTerm":
        """Converts this variable to a 0-arity `UFTerm`."""
        return UFTerm(self.name, self.sort, ())

    def get_decl(self, init_value=None) -> "VarDecl":
        """Gets a `Translatable` declaration of this variable."""
        return VarDecl(self.name, self.sort, init_value=init_value)

    def add_prefix(self, prefix) -> "Variable":
        return Variable(prefix + self.name, self.sort)

    def get_base(self) -> "Variable":
        return Variable(self.name[self.name.rfind(".") + 1:], self.sort)

    @staticmethod
    def from_cvc5(cvc5_term) -> "Variable":
        if cvc5_term.getKind() != pycvc5.Kind.VARIABLE:
            raise TypeError("Variable must be translated from pycvc5.Kind.VARIABLE, instead got " + str(cvc5_term.getKind()))
        # it seems like cvc5 will wrap the variable name in |var| if there are square brackets inside
        # so we need to strip those off
        name = cvc5_term.getSymbol()
        # technically fails if NAME is length 1, but at that point we don't care anymore
        if name[0] == "|" and name[-1] == "|":
            name = name[1:-1]
        return Variable(name, Sort.from_cvc5(cvc5_term.getSort()))

    @property
    def sort(self):
        return self._sort

    @property
    def _children(self):
        return []

    def _replace_child(self, new_term, index):
        return self

    def eval(self, values):
        assert self.name in values, f"could not evaluate variable '{self.name}' from mapping {values}"
        if self.is_bv_expr():
            return BVConst(values[self.name], self.c_bitwidth())
        elif self.is_bool_expr():
            return BoolConst(values[self.name])
        else:
            raise NotImplementedError()

    def to_target_format(self, tgt: TargetFormat, **kwargs):
        if tgt == TargetFormat.CVC5:
            cvc5_ctx = kwargs["cvc5_ctx"]
            if self in cvc5_ctx.terms:
                return cvc5_ctx.terms[self]
            else:
                v = cvc5_ctx.solver.mkVar(self.sort.to_cvc5(cvc5_ctx), self.name)
                cvc5_ctx.terms[self] = v
                return v
        elif tgt in (TargetFormat.SYGUS2, TargetFormat.VERILOG):
            return self.name
        elif tgt == TargetFormat.UCLID:
            if kwargs.get("prime_vars", False):
                return self.name + "'"
            else:
                return self.name
        elif tgt == TargetFormat.JSON:
            return json.dumps(asdict(self))
        raise NotImplementedError("cannot convert Variable to " + str(tgt))

bool_variable = lambda s, **kwargs: Variable(s, BoolSort(), **kwargs)
"""Constructs a variable of boolean sort with the given name."""

bv_variable = lambda s, w, **kwargs: Variable(s, BVSort(w), **kwargs)
"""Constructs a variable of a bitvector sort with the given name and width."""

@dataclass(frozen=True)
class VarDecl(Translatable):
    name: str
    sort: Sort
    init_value: Optional["BVConst"] = None

    def __str__(self):
        if self.init_value is None:
            return f"{self.name} : {self.sort}"
        else:
            return f"{self.name} : {self.sort} = {self.init_value}"

    def c_bitwidth(self):
        return self.get_ref().c_bitwidth()

    def get_ref(self):
        return Variable(self.name, self.sort)

    def to_target_format(self, tgt: TargetFormat, **kwargs):
        """
        Possible kwargs:
        - `cvc5_ctx`: a `rtl2model.lynth.Cvc5Ctx` object used to get a reference to a cvc5 solver
        - `is_reg`: a boolean used by verilog conversion to determine if this should be declared
                    as a reg (if false or not specified, the variable will be a wire)
        - `anyconst`: a boolean used by verilog conversion; when specified, it will add the
                    `(* anyconst *)` yosys attribute
        """
        if tgt == TargetFormat.CVC5:
            # The CVC5 interface doesn't really distnguish between declarations
            # and references of bound variables
            return self.get_ref().to_cvc5(cvc5_ctx=kwargs["cvc5_ctx"])
        elif tgt == TargetFormat.SYGUS2:
            return f"(declare-var {self.name} {self.sort.to_sygus2()})"
        elif tgt == TargetFormat.VERILOG:
            if isinstance(self.sort, ArraySort):
                raise NotImplementedError("VarDecl verilog array translation not supported yet")
            is_reg = kwargs.get("is_reg", False)
            decl = "reg" if is_reg else "wire"
            if self.c_bitwidth() != 1:
                decl += " " + self.sort.to_verilog_str()
            decl += f" {self.name}"
            if self.init_value is not None:
                decl += " = " + self.init_value.to_verilog_str()
            decl += ";"
            anyconst = kwargs.get("anyconst", False)
            if anyconst:
                return  "(* anyconst *) " + decl
            else:
                return decl
        elif tgt == TargetFormat.UCLID:
            # TODO need to identify inputs/outputs
            return f"var {self.name} : {self.sort.to_uclid()};"
        raise NotImplementedError("cannot convert VarDecl to " + str(tgt))

@dataclass(frozen=True)
class OpTerm(Term):
    kind: Kind
    args: Tuple[Term, ...]

    @property
    def sort(self):
        # TODO better type checking
        bvops = { Kind.BVAdd, Kind.BVSub, Kind.BVOr, Kind.BVAnd, Kind.BVXor, Kind.BVNot,
                  Kind.BVSll, Kind.BVSrl, Kind.BVSra }
        if self.kind in bvops:
            return self.args[0].sort
        elif self.kind == Kind.BVExtract:
            # TODO fix this hack: in the CVC5 API, BVExtract must first be turned into
            # an operator via Solver::mkOp(BVExtract, high, low)
            # As a workaround, we store the high/low parameters as BVConst arguments
            assert isinstance(self.args[1], BVConst), f"extract first index must be BVConst, instead was {self.args[1]}"
            assert isinstance(self.args[2], BVConst), f"extract second index must be BVConst, instead was {self.args[2]}"
            return BVSort(self.args[1].val - self.args[2].val + 1)
        elif self.kind in (Kind.BVZeroPad, Kind.BVSignExtend):
            assert self.args[0].is_bv_expr(), repr(self.args[0])
            assert isinstance(self.args[1], BVConst), repr(self.args[1])
            return BVSort(self.args[0].c_bitwidth() + self.args[1].val)
        elif self.kind == Kind.BVConcat:
            return BVSort(sum(a.c_bitwidth() for a in self.args))
        elif self.kind == Kind.Ite:
            return self.args[1].sort
        elif self.kind == Kind.Match:
            return self.args[2].sort # Match args are (cond, c1, v1, c2, v2, ...)
        bitops = { Kind.Or, Kind.And, Kind.Xor, Kind.Not, Kind.Equal, Kind.Distinct, Kind.Implies,
                   Kind.BVUle, Kind.BVUlt, Kind.BVUge, Kind.BVUgt}
        if self.kind in bitops:
            return BoolSort()
        if self.kind == Kind.Select:
            assert self.args[0].is_array_expr(), ArraySort
            return self.args[0].sort.elem_sort
        raise NotImplementedError(f"Cannot get Sort for kind {self.kind}")

    @property
    def _children(self):
        return list(self.args)

    def _replace_child(self, new_term, index):
        new_args = list(self.args)
        new_args[index] = new_term
        return OpTerm(self.kind, tuple(new_args))

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

    @staticmethod
    def from_cvc5(cvc5_term):
        """
        Translates the provided cvc5 Term object into an `OpTerm`.
        """
        cvc5_kind = cvc5_term.getKind()
        if cvc5_kind not in _OP_KIND_REV_MAPPING:
            raise TypeError("OpTerm cannot be translated from " + str(cvc5_kind))
        kind = Kind.from_cvc5(cvc5_kind)
        if kind == Kind.BVExtract:
            op = cvc5_term.getOp()
            base = Term.from_cvc5(cvc5_term[0])
            assert op[0].getKind() == pycvc5.Kind.CONST_RATIONAL
            upper = BVConst(op[0].getIntegerValue(), base.c_bitwidth())
            assert op[1].getKind() == pycvc5.Kind.CONST_RATIONAL
            lower = BVConst(op[1].getIntegerValue(), base.c_bitwidth())
            return OpTerm(kind, (base, upper, lower))
        if kind in (Kind.BVZeroPad, Kind.BVSignExtend):
            op = cvc5_term.getOp()
            base = Term.from_cvc5(cvc5_term[0])
            assert op[0].getKind() == pycvc5.Kind.CONST_RATIONAL
            padamt = BVConst(op[0].getIntegerValue(), base.c_bitwidth())
            return OpTerm(kind, (base, padamt))
        return OpTerm(kind, tuple([Term.from_cvc5(t) for t in cvc5_term]))

    def to_target_format(self, tgt: TargetFormat, **kwargs):
        if tgt == TargetFormat.CVC5:
            cvc5_ctx = kwargs["cvc5_ctx"]
            cvc5_kind = self.kind.to_cvc5(self)
            if self.kind == Kind.BVExtract:
                # TODO special case BVExtract, Select, and Store for from_cvc5?
                assert isinstance(self.args[1], BVConst)
                assert isinstance(self.args[2], BVConst)
                op = cvc5_ctx.solver.mkOp(cvc5_kind, self.args[1].val, self.args[2].val)
                return cvc5_ctx.solver.mkTerm(op, self.args[0].to_cvc5(cvc5_ctx))
            elif self.kind == Kind.BVConcat:
                # Special case for bool operands for a concat
                args = [o._maybe_bool2bv1().to_cvc5(cvc5_ctx) for o in self.args]
                return cvc5_ctx.solver.mkTerm(cvc5_kind, *args)
            elif self.kind in (Kind.BVZeroPad, Kind.BVSignExtend):
                assert isinstance(self.args[1], BVConst)
                op = cvc5_ctx.solver.mkOp(cvc5_kind, self.args[1].val)
                return cvc5_ctx.solver.mkTerm(op, self.args[0].to_cvc5(cvc5_ctx))
            elif self.kind == Kind.Match:
                # Jank workaround until I figure out how to pattern match vs constants
                a0 = self.args[0]
                # Last expression is the "else" case, so cond doesn't matter
                t = self.args[-1]
                for i in range(len(self.args) - 4, 0, -2):
                    t = a0.op_eq(self.args[i]).ite(self.args[i + 1], t)
                return t.to_cvc5(cvc5_ctx)
                # case_terms = []
                # for i in range(1, len(self.args), 2):
                #     case_terms.append(
                #         cvc5_ctx.solver.mkTerm(
                #             pycvc5.Kind.MatchCase,
                #             self.args[i].to_cvc5(cvc5_ctx),
                #             self.args[i + 1].to_cvc5(cvc5_ctx),
                #         )
                #     )
                # return cvc5_ctx.solver.mkTerm(cvc5_kind, self.args[0].to_cvc5(cvc5_ctx), *case_terms)
            t = cvc5_ctx.solver.mkTerm(cvc5_kind, *[v.to_cvc5(cvc5_ctx) for v in self.args])
            return t
        elif tgt == TargetFormat.SYGUS2:
            if self.kind == Kind.Match:
                # Convert to a giant ITE chain
                a0 = self.args[0]
                # Last expression is the "else" case, so cond doesn't matter
                t = self.args[-1]
                for i in range(len(self.args) - 4, 0, -2):
                    t = a0.op_eq(self.args[i]).ite(self.args[i + 1], t)
                return t.to_sygus2()
            # extract, zero_extend, and sign_extend all use decimals instead of hex
            if self.kind == Kind.BVExtract:
                assert isinstance(self.args[1], BVConst), self.args
                assert isinstance(self.args[2], BVConst), self.args
                return f"((_ extract {self.args[1].val} {self.args[2].val}) {self.args[0].to_sygus2()})"
            if self.kind == Kind.BVZeroPad:
                assert isinstance(self.args[1], BVConst), self.args
                return f"((_ zero_extend {self.args[1].val}) {self.args[0].to_sygus2()})"
            if self.kind == Kind.BVSignExtend:
                assert isinstance(self.args[1], BVConst), self.args
                return f"((_ sign_extend {self.args[1].val}) {self.args[0].to_sygus2()})"
            if self.kind == Kind.Distinct:
                return f"(not (= " +  " ".join(a.to_sygus2() for a in self.args) + "))"
            return "(" + _OP_SYGUS_SYMBOLS[self.kind] + " " + " ".join([a.to_sygus2() for a in self.args]) + ")"
        elif tgt == TargetFormat.VERILOG:
            v = self.kind
            def wrap(term):
                if term._should_paren:
                    return "(" + term.to_verilog_str() + ")"
                else:
                    return term.to_verilog_str()
            unops = {
                Kind.Not: "!",
                Kind.BVNot: "~",
                Kind.BVOrr: "|",
                Kind.BVXorr: "^",
            }
            if v in unops:
                a0_str = wrap(self.args[0])
                return f"{unops[v]}{a0_str}"
            binops = {
                Kind.BVAdd: "+",
                Kind.BVSub: "-",
                Kind.BVMul: "*",
                Kind.BVOr: "|",
                Kind.BVAnd: "&",
                Kind.BVXor: "^",
                Kind.BVSll: "<<",
                Kind.BVSra: ">>",
                Kind.BVSrl: ">>>",
                Kind.BVUlt: "<",
                Kind.BVUle: "<=",
                Kind.BVUgt: ">",
                Kind.BVUge: ">=",
                Kind.Or: "||",
                Kind.And: "&&",
                Kind.Xor: "^", # TODO check if this differs from bv xor
                Kind.Equal: "==",
                Kind.Distinct: "!=",
            }
            if v in binops:
                a0_str = wrap(self.args[0])
                a1_str = wrap(self.args[1])
                return f"{a0_str} {binops[v]} {a1_str}"
            if v == Kind.Implies:
                a0_str = wrap(self.args[0])
                a1_str = wrap(self.args[1])
                return f"!{a0_str} || {a1_str}"
            if v == Kind.Ite:
                a0_str = wrap(self.args[0])
                a1_str = wrap(self.args[1])
                a2_str = wrap(self.args[2])
                return f"{a0_str} ? {a1_str} : {a2_str}"
            if v == Kind.Match:
                # Verilog case statements aren't expressions, so just ITE it up
                a0 = self.args[0]
                # Last expression is the "else" case, so cond doesn't matter
                t = self.args[-1]
                for i in range(len(self.args) - 4, 0, -2):
                    t = a0.op_eq(self.args[i]).ite(self.args[i + 1], t)
                return t.to_verilog_str()
            if v == Kind.BVConcat:
                return "{" + ",".join(wrap(a) for a in self.args) + "}"
            if v == Kind.BVExtract:
                a0_str = wrap(self.args[0])
                if self.args[1].is_const():
                    a1_str = str(int(self.args[1].val))
                else:
                    a1_str = wrap(self.args[1])
                if self.args[2].is_const():
                    a2_str = str(int(self.args[2].val))
                else:
                    a2_str = wrap(self.args[2])
                return f"{a0_str}[{a1_str}:{a2_str}]"
            if v == Kind.Select:
                a0_str = wrap(self.args[0])
                a1_str = wrap(self.args[1])
                return f"{a0_str}[{a1_str}]"
            if v == Kind.BVSignExtend:
                # duplicate MSB, then concat
                # e.g. {{8{ext[7]}}, ext} where ext is bv8
                a0_str = self.args[0].to_verilog_str()
                msb_i = self.args[0].c_bitwidth() - 1
                # brace balancing:
                # c = outermost concat curly brace
                # r = outside repeat
                # m = outside msb
                #        c0r0                  m0                 m1r1          c1
                return f"{{{{{self.args[1].val}{{{a0_str}[{msb_i}]}}}}, {a0_str}}}"
            if v == Kind.BVZeroPad:
                a0_str = self.args[0].to_verilog_str()
                #        c0r0                     r1          c1
                return f"{{{{{self.args[1].val}'h0}}, {a0_str}}}"
            raise NotImplementedError(v)
        elif tgt == TargetFormat.UCLID:
            def wrap(term):
                if term._should_paren:
                    return "(" + term.to_uclid(**kwargs) + ")"
                else:
                    return term.to_uclid(**kwargs)
            # TODO use uclid library
            unops = {
                Kind.Not: "!",
                Kind.BVNot: "~"
            }
            if self.kind in unops:
                return unops[self.kind] + wrap(self.args[0])
            binops = {
                Kind.BVAdd: "+",
                Kind.BVSub: "-",
                Kind.BVOr: "|",
                Kind.BVAnd: "&",
                Kind.BVXor: "^",
                Kind.BVUlt: "<_u",
                Kind.BVUle: "<=_u",
                Kind.BVUgt: ">_u",
                Kind.BVUge: ">=_u",
                Kind.Equal: "==",
                Kind.Distinct: "!=",
                Kind.Or: "||",
                Kind.And: "&&",
                Kind.Xor: "^", # TODO check if this differs from bv xor
                Kind.Implies: "==>",
            }
            if self.kind in binops:
                return wrap(self.args[0]) + " " + binops[self.kind] + " " + wrap(self.args[1])
            shifts = {
                Kind.BVSll: "bv_left_shift",
                Kind.BVSra: "bv_a_right_shift",
                Kind.BVSrl: "bv_l_right_shift",
            }
            if self.kind in shifts:
                return shifts[self.kind] + "(" + self.args[0].to_uclid(**kwargs) + ", " + self.args[1].to_uclid(**kwargs) + ")"
            if self.kind == Kind.Ite:
                return "if (" + self.args[0].to_uclid(**kwargs) + ") then " + wrap(self.args[1]) + " else " + wrap(self.args[2])
            if self.kind == Kind.BVExtract:
                if not isinstance(self.args[1], BVConst):
                    raise Exception(f"non-constant BVExtract not supported ({self.args[1]})")
                if not isinstance(self.args[2], BVConst):
                    raise Exception(f"non-constant BVExtract not supported ({self.args[2]})")
                a1_str = str(self.args[1].val)
                a2_str = str(self.args[2].val)
                return wrap(self.args[0]) + "[" + a1_str + ":" + a2_str + "]"
            if self.kind == Kind.BVConcat:
                return " ++ ".join(wrap(a) for a in self.args)
            if self.kind == Kind.Select:
                return wrap(self.args[0]) + "[" + self.args[1].to_uclid(**kwargs) + "]"
            if self.kind == Kind.BVZeroPad:
                assert isinstance(self.args[1], BVConst)
                return f"bv_zero_extend({self.args[1].val}, {self.args[0].to_uclid(**kwargs)})"
            if self.kind == Kind.Match:
                # uclid case statements aren't expressions, so just ITE it up
                a0 = self.args[0]
                # Last expression is the "else" case, so cond doesn't matter
                t = self.args[-1]
                for i in range(len(self.args) - 4, 0, -2):
                    t = a0.op_eq(self.args[i]).ite(self.args[i + 1], t)
                return t.to_uclid(**kwargs)
            raise NotImplementedError(self.kind)
        raise NotImplementedError("cannot convert OpTerm to " + str(tgt))

    def optimize(self) -> Term:
        return self.const_fold()

    def const_fold(self) -> Term:
        # Optimize all children first
        t = self
        for i, child in enumerate(self._children):
            t = t._replace_child(child.const_fold(), i)
        assert isinstance(t, OpTerm), t
        assert self.kind == t.kind
        # Special case for constant folding: ITEs/match where the cond is constant
        if t.kind == Kind.Match:
            cond = t.args[0]
            if cond.is_const():
                # Return value for correct case
                # Recall that every other arg is cond, then value
                for i in range(1, len(t.args) - 1):
                    case = t.args[i]
                    if case == cond:
                        return t.args[i + 1]
            else:
                return t
        if t.kind == Kind.Ite:
            cond = t.args[0]
            if cond.is_const():
                return t.args[1] if bool(cond) else t.args[2]
            else:
                return t
        # Constant folding: all arguments must be constants
        # This technically isn't true for stuff like concats,
        # but this heuristic works for binops and unops
        args = t._children
        for child in args:
            if not child.is_const():
                return t
        return t._do_const_eval(args)

    def eval(self, values):
        # Optimize all children first
        children = [child.eval(values) for child in self._children]
        return self._do_const_eval(children)

    def _do_const_eval(self, args):
        # unary ops
        a0 = args[0]
        a0_bw = a0.c_bitwidth()
        a0_mask = (1 << a0.c_bitwidth()) - 1
        if self.kind == Kind.Not:
            return BoolConst.T if a0 == BoolConst.F else BoolConst.T
        if self.kind == Kind.BVNot:
            return BVConst((~a0.val) & a0_mask, a0_bw)
        # binary ops
        a1 = args[1]
        if self.kind == Kind.BVAdd:
            return BVConst((a0.val + a1.val) & a0_mask, a0_bw)
        if self.kind == Kind.BVSub:
            # TODO check wrapping behavior
            return BVConst((a0.val - a1.val) & a0_mask, a0_bw)
        if self.kind == Kind.BVOr:
            return BVConst(a0.val | a1.val, a0_bw)
        if self.kind == Kind.BVAnd:
            return BVConst(a0.val & a1.val, a0_bw)
        if self.kind == Kind.BVXor:
            return BVConst(a0.val ^ a1.val, a0_bw)
        if self.kind == Kind.BVUlt:
            return BoolConst(a0.val < a1.val)
        if self.kind == Kind.BVUle:
            return BoolConst(a0.val <= a1.val)
        if self.kind == Kind.BVUgt:
            return BoolConst(a0.val > a1.val)
        if self.kind == Kind.BVUge:
            return BoolConst(a0.val >= a1.val)
        if self.kind == Kind.Equal:
            return BoolConst(a0.val == a1.val)
        if self.kind == Kind.Distinct:
            return BoolConst(a0.val != a1.val)
        if self.kind == Kind.Or:
            return a0 or a1 # IntEnum provides this automatically
        if self.kind == Kind.And:
            return a0 and a1
        if self.kind == Kind.Xor:
            return BoolConst.T if a0 != a1 else BoolConst.F
        if self.kind == Kind.Implies:
            # p ==> q == !p || q
            return (a0 == BoolConst.F) or a1
        # Kind.BVSll: "bv_left_shift",
        # Kind.BVSra: "bv_a_right_shift",
        # Kind.BVSrl: "bv_l_right_shift",
        # if self.kind == Kind.Select:
        if self.kind == Kind.BVZeroPad:
            return BVConst(a0.val, a1.val + a0_bw)
        # if self.Kind == Kind.SignExtend:
        # ternary operators and higher
        a2 = args[2]
        if self.kind == Kind.BVExtract:
            # TODO check semantics for off-by-one in verilog/cvc?
            # also, what direction does the slice go?
            high = a1.val
            low = a2.val
            width = (high - low) + 1
            mask = ((1 << width) - 1) << low
            return BVConst((a0.val & mask) >> low, width)
        # if self.kind == Kind.BVConcat:
        # Ternary operator
        if self.kind == Kind.Ite:
            return a1 if bool(a0) else a2
        return t

# TODO distinguish between references and declarations
# variables and UFTerms should be referenced in the same way, but obviously declared
# in different manners
@dataclass(frozen=True)
class UFTerm(Term):
    """
    A term representing an uninterpreted function of arbitrary arity.
    """
    name: str
    _sort: Sort
    params: Tuple[Variable, ...]

    def apply(self, *args):
        return ApplyUF(self, tuple(args))

    def __str__(self):
        return self.name

    @property
    def arity(self):
        return len(self.params)

    @staticmethod
    def from_cvc5(cvc5_term):
        raise NotImplementedError("Cannot convert from CVC5 UF term")

    @property
    def sort(self):
        return self._sort

    @property
    def _children(self):
        return []

    def _replace_child(self, new_term, index):
        return self

    def to_target_format(self, tgt: TargetFormat, **kwargs):
        if tgt == TargetFormat.CVC5:
            cvc5_ctx = kwargs["cvc5_ctx"]
            return cvc5_ctx.solver.declareFun(
                self.name,
                [p.sort.to_cvc5(cvc5_ctx) for p in self.params],
                self.sort.to_cvc5(cvc5_ctx)
            )
        elif tgt == TargetFormat.UCLID:
            params_s = ",".join(f"{v.name} : {v.sort.to_uclid()}" for v in self.params)
            return f"synthesis function {self.name}({params_s}) : {self.sort.to_uclid()};"
        raise NotImplementedError("cannot convert UFTerm to " + str(tgt))

@dataclass(frozen=True)
class LambdaTerm(Term):
    params: Tuple[Variable, ...]
    body: Term

    def apply(self, *args):
        assert len(args) == len(self.params)
        # jank hack for inlining lambdas
        return self.body.replace_vars({self.params[i]: args[i] for i in range(len(args))})

    @property
    def sort(self):
        return FunctionSort(tuple([p.sort for p in self.params]), self.body.sort)

    @property
    def _children(self):
        return self.body

    def _replace_child(self, new_term, index):
        if index != 0:
            raise IndexError()
        return LambdaTerm(self.params, new_term)

    def __str__(self):
        return f"({','.join(str(s) for s in self.params)}) -> {self.body}"

    @staticmethod
    def from_cvc5(cvc5_term):
        if cvc5_term.getKind() == pycvc5.Kind.LAMBDA:
            c_params = cvc5_term[0]
            c_body = cvc5_term[1]
            return LambdaTerm(
                tuple([Variable.from_cvc5(c) for c in c_params]),
                Term.from_cvc5(c_body),
            )
        else:
            raise TypeError("LambdaTerm must be translated from pycvc5.Kind.LAMBDA, instead got " + str(cvc5_term.getKind()))

    def _to_sygus2_fn(self, name: str):
        """hacky workaround since these aren't named"""
        return f"(define-fun {name} (" + \
            " ".join([f"({p.name} {p.sort.to_sygus2()})" for p in self.params]) + ") " + \
            self.body.sort.to_sygus2() + " " + \
            self.body.to_sygus2() \
            + ")"

    def to_target_format(self, tgt: TargetFormat, **kwargs):
        if tgt == TargetFormat.CVC5:
            cvc5_ctx = kwargs["cvc5_ctx"]
            if self in cvc5_ctx.terms:
                return cvc5_ctx.terms[self]
            else:
                cvc5_kind = pycvc5.Kind.LAMBDA
                # TODO this needs to be tested
                vlist = cvc5_ctx.solver.mkTerm(pycvc5.Kind.VARIABLE_LIST, *[p.to_cvc5(cvc5_ctx) for p in self.params])
                t = cvc5_ctx.solver.mkTerm(cvc5_kind, vlist, self.body.to_cvc5(cvc5_ctx))
                cvc5_ctx.terms[self] = t
                return t
        elif tgt in (TargetFormat.SYGUS2, TargetFormat.UCLID):
            return "(define-fun (" + \
                " ".join([f"({p.name} {p.sort.to_sygus2()})" for p in self.params]) + ") " + \
                self.body.sort.to_sygus2() + " " + \
                self.body.to_sygus2() \
                + ")"
        # return f"(" + \
        #     ", ".join([f"{p.name} : {p.sort.to_uclid()}" for p in self.params]) + ") : " + \
        #     self.body.sort.to_uclid() + " = " + \
        #     self.body.to_uclid()
        raise NotImplementedError("cannot convert LambdaTerm to " + str(tgt))

@dataclass(frozen=True)
class QuantTerm(Term):
    kind: Kind
    bound_vars: Tuple[Variable, ...]
    body: Term

    @property
    def sort(self):
        return BoolSort()

    @property
    def _children(self):
        return self.body

    def _replace_child(self, new_term, index):
        if index != 0:
            raise IndexError()
        return QuantTerm(self.kind, self.bound_vars, new_term)

    @staticmethod
    def from_cvc5(cvc5_term):
        k = cvc5_term.getKind()
        if k not in [pycvc5.Kind.Exists, pycvc5.Kind.Forall]:
            c_params = cvc5_term[0]
            c_body = cvc5_term[1]
            return QuantTerm(
                Kind.from_cvc5(k),
                tuple([Variable.from_cvc5(c) for c in c_params]),
                Term.from_cvc5(c_body),
            )
        else:
            raise TypeError("QuantTerm must be translated from pycvc5.Kind.Exists or Forall, instead got " + str(k))

    def to_target_format(self, tgt: TargetFormat, **kwargs):
        if tgt == TargetFormat.CVC5:
            cvc5_ctx = kwargs["cvc5_ctx"]
            if self in cvc5_ctx.terms:
                return cvc5_ctx.terms[self]
            else:
                cvc5_kind = self.kind.to_cvc5(cvc5_ctx)
                # TODO this needs to be tested
                vlist = cvc5_ctx.solver.mkTerm(pycvc5.Kind.VARIABLE_LIST, [p.to_cvc5(cvc5_ctx) for p in self.bound_vars])
                t = cvc5_ctx.solver.mkTerm(cvc5_kind, vlist, [self.body.to_cvc5(cvc5_ctx)])
                cvc5_ctx.terms[self] = t
                return t
        elif tgt in (TargetFormat.SYGUS2, TargetFormat.UCLID):
            return "(" + self.kind.to_sygus2() + " (" + \
                " ".join([f"({p.name} {p.sort.to_sygus2()})" for p in self.bound_vars]) + ") " + \
                self.body.sort.to_sygus2() + " " + \
                self.body.to_sygus2() \
                + ")"
        raise NotImplementedError("cannot convert QuantTerm to " + str(tgt))

@dataclass(frozen=True)
class ApplyUF(Term):
    """
    Term representing application of an uninterpreted function on the specified inputs.
    """
    fun: UFTerm
    # without making cvc5 unhappy
    input_values: Tuple[Term, ...]

    # def __post_init__(self):
    #     assert self.fun.arity == len(self.input_values)

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

    @property
    def sort(self):
        return self.fun.sort

    @property
    def _children(self):
        return list(self.input_values)

    def _replace_child(self, new_term, index):
        return ApplyUF(self.fun, tuple(t._replace_child(new_term, index) for t in self.input_values))

    @staticmethod
    def from_cvc5(cvc5_term):
        if cvc5_term.getKind() == pycvc5.Kind.APPLY_UF:
            raise NotImplementedError()
        else:
            raise TypeError("ApplyUF must be translated from pycvc5.Kind.APPLY_UF, instead got " + str(cvc5_term.getKind()))

    def to_target_format(self, tgt: TargetFormat, **kwargs):
        if tgt == TargetFormat.CVC5:
            cvc5_ctx = kwargs["cvc5_ctx"]
            cvc5_kind = pycvc5.Kind.APPLY_UF
            # Distinguish between ordinary UFs and synthfuns
            if self.fun.name in cvc5_ctx.synthfuns:
                cvc5_fun = cvc5_ctx.synthfuns[self.fun.name]
            else:
                cvc5_fun = self.fun.to_cvc5(cvc5_ctx)
            t = cvc5_ctx.solver.mkTerm(cvc5_kind, cvc5_fun, *[v.to_cvc5(cvc5_ctx) for v in self.input_values])
            return t
        elif tgt == TargetFormat.SYGUS2:
            return f"({self.fun.name} " + " ".join(a.to_sygus2() for a in self.input_values) + ")"
        elif tgt == TargetFormat.UCLID:
            args = ", ".join(i.to_uclid(**kwargs) for i in self.input_values)
            return self.fun.name + "(" + args + ")"
        raise NotImplementedError("cannot convert ApplyUF to " + str(tgt))

class BoolConst(Term, IntEnum, metaclass=_TermMeta):
    """SMT boolean constants for true and false. Can be coerced to/from an `int`."""
    F = 0
    T = 1

    @property
    def val(self) -> int:
        return int(self.value)

    @property
    def sort(self):
        return BoolSort()

    @property
    def _children(self):
        return []

    def _replace_child(self, new_term, index):
        return self

    def __str__(self):
        return "true" if self == self.T else "false"

    def eval(self, values):
        return self

    def to_target_format(self, tgt: TargetFormat, **kwargs):
        if tgt == TargetFormat.CVC5:
            cvc5_ctx = kwargs["cvc5_ctx"]
            if self == self.F:
                return cvc5_ctx.solver.mkFalse()
            else:
                return cvc5_ctx.solver.mkTrue()
        elif tgt == TargetFormat.VERILOG:
            return "1" if self == self.T else "0"
        elif tgt in (TargetFormat.SYGUS2, TargetFormat.UCLID):
            return "true" if self == self.T else "false"
        raise NotImplementedError("cannot convert BoolConst to " + str(tgt))

@dataclass(frozen=True)
class BVConst(Term):
    val: int
    width: int

    def __post_init__(self):
        assert isinstance(self.val, int), type(self.val)
        assert isinstance(self.width, int), type(self.width)

    @property
    def sort(self):
        return BVSort(self.width)

    @property
    def _children(self):
        return []

    def _replace_child(self, new_term, index):
        return self

    def __str__(self):
        return f"{self.val}bv{self.width}"

    @staticmethod
    def from_cvc5(cvc5_term):
        kind = cvc5_term.getKind()
        if kind != pycvc5.Kind.CONST_BITVECTOR:
            raise TypeError("BVConst must be translated from pycvc5.Kind.CONST_BITVECTOR, instead got " + str(kind))
        return BVConst(int(cvc5_term.getBitVectorValue(base=10)), cvc5_term.getSort().getBitVectorSize())

    def eval(self, values):
        return self

    def to_target_format(self, tgt: TargetFormat, **kwargs):
        if tgt == TargetFormat.CVC5:
            cvc5_ctx = kwargs["cvc5_ctx"]
            if BVSort(self.width) in cvc5_ctx.sorts:
                return cvc5_ctx.sorts[self]
            new_sort = cvc5_ctx.solver.mkBitVector(self.width, self.val)
            return new_sort
        elif tgt == TargetFormat.SYGUS2:
            # sygus literal lengths determine their type
            if self.width % 4 == 0:
                return f"#x{self.val:0{self.width // 4}x}"
            else:
                return f"#b{self.val:0{self.width}b}"
        elif tgt == TargetFormat.VERILOG:
            return "{}'h{:x}".format(self.width, self.val)
        elif tgt == TargetFormat.UCLID:
            return "0x{:x}bv{}".format(self.val, self.width)
        raise NotImplementedError("cannot convert BVConst to " + str(tgt))

# === END SMT TERMS ===


import torch
import numpy as np
sigma = 2
rg = 0.1
lr = 0.01
iteration = 10000

def criterion(x, A):
    pairwise_norm = torch.norm(x-x[:, None], dim=2, p=2)
    push_near = torch.sum(pairwise_norm * A)
    push_far = torch.exp(((pairwise_norm * (1-A)) ** 2) * (-1 / sigma))
    push_far = torch.sum(push_far)
    regularization = torch.sum(x ** 2) * rg
    return push_near + push_far + regularization

edgelist = []
with open("./facebook_edgelist") as f:
    for line in f:
        words = line.rstrip().split(",")
        edgelist.append([int(words[0]), int(words[1])])
edgelist = np.array(edgelist) - 1
print(edgelist.shape)
adjlist = np.full(shape=(53918, 53918), fill_value=0)
adjlist[tuple(edgelist.T)] = 1
edgelist.T[[0, 1]] = edgelist.T[[1, 0]]
adjlist[tuple(edgelist.T)] = 1
adjlist = torch.from_numpy(adjlist)
adjlist = adjlist.float().to("cuda")
emb = torch.randn((53918, 64)).float().to("cuda")
emb.requires_grad_()
for _ in range(10000):
    loss = criterion(emb, adjlist)
    loss.backward()
    emb.data = emb.data - lr * emb.grad
    emb.grad = None
print(emb)
embnum = emb.data.cpu().numpy()
index = 1
with open("out4.out", 'w') as f:
    f.write("53918 64\n")
    for line in embnum:
        f.write("{} ".format(index))
        index = index + 1
        for item in line:
            f.write("{} ".format(item))
        f.write("\n")


import logging
import datetime
from django.urls import reverse_lazy
from django.shortcuts import redirect
from django.views.generic import CreateView
from django.contrib.auth.mixins import (
    UserPassesTestMixin, LoginRequiredMixin, PermissionRequiredMixin
)
from django.contrib import messages
from patient_ms.models import DoctorAppointment
from patient_ms.forms import DoctorAppointmentForm
logger = logging.getLogger(__name__)

class DoctorAppointment(
    UserPassesTestMixin,
    LoginRequiredMixin,
    CreateView
):
    model = DoctorAppointment
    form_class = DoctorAppointmentForm
    template_name = 'appointment/index.html'

    def test_func(self):
        """Tests if the user is active"""
        return self.request.user.is_active  # any active user

    def form_valid(self, form):
        appointment_day = form.cleaned_data.get("appointment_day").strftime("%Y-%m-%d")
        doctor = form.cleaned_data.get("doctor")
        serial = 1
        try:
            save_object = self.model.objects.filter(
                appointment_day__contains=appointment_day,
                doctor=doctor
            ).last()
            if save_object:
                if save_object.serial_number > 0:
                    serial = serial + save_object.serial_number
        except Exception as e:
            logger.debug(self.request, f"Unable to get Doctor as {e}")
            redirect(self.get_error_url())

        save_form = form.save(commit=False)
        save_form.patient = self.request.user
        save_form.serial_number = serial
        save_form.save()
        return super(DoctorAppointment, self).form_valid(form)

    def get_success_url(self):
        messages.success(
            self.request,
            "Successfully Appointment Taken, "
            "Please Download the appointment letter"
        )
        logger.debug("Successfully Updated")
        return reverse_lazy(
                    "patient_ms:appointment_confirmation",
                    kwargs={'pk': self.object.pk}
        )

    def get_error_url(self):
        messages.warning(self.request, "Unable to take Appointment")
        logger.debug("Unable to get Appointment")
        return reverse_lazy("index")


import os
from os import path
import logging
import datetime
import sys

def get_local_time():
    return datetime.datetime.now().strftime('%b-%d-%Y_%H-%M-%S')

def log_exceptions(func):
    def wrapper(*args, **kwargs):
        logger = logging.getLogger('train_logger')
        try:
            return func(*args, **kwargs)
        except Exception as e:
            logger.exception(e)
            raise e
    return wrapper

class Logger(object):
    def __init__(self, args, log_configs=True):
        self.logger = logging.getLogger('train_logger')
        self.logger.setLevel(logging.INFO)
        self.args = args

        data_name = args.data_path.split("/")[-1]

        save_dir = path.join(args.save_path, data_name, args.exp_name)
        if not path.exists(save_dir):
            os.makedirs(save_dir)
        args.save_dir = save_dir

        if args.log:
            cur_time = get_local_time()
            args.exp_time = cur_time
            log_file = logging.FileHandler(path.join(save_dir, 'train_log_{}.txt'.format(cur_time)))
            formatter = logging.Formatter('%(asctime)s - %(message)s')
            log_file.setFormatter(formatter)
            self.logger.addHandler(log_file)

        self.log(f"DESC: {args.desc}")
        self.log(f"PID: {os.getpid()}")

        # log command that runs the code
        s = ""
        for arg in sys.argv:
            s = s + arg + " "
        self.log(os.path.basename(sys.executable) + " " + s)

        if log_configs:
            self.log(args)

    def info(self, message):
        self.logger.info(message)

    def log(self, message, save_to_log=True, print_to_console=True):
        if save_to_log:
            self.logger.info(message)
        if print_to_console:
            print(message)

    def log_loss(self, epoch_idx, loss_log_dict, save_to_log=True, print_to_console=True):
        epoch = self.args.num_epochs
        message = '[Epoch {:3d} / {:3d} Training Time: {:.2f}s ] '.format(epoch_idx, epoch, loss_log_dict['train_time'])
        for loss_name in loss_log_dict:
            if loss_name == 'train_time':
                continue
            message += '{}: {:.4f} '.format(loss_name, loss_log_dict[loss_name])
        if save_to_log:
            self.logger.info(message)
        if print_to_console:
            print(message)

    def log_eval(self, eval_result, k, save_to_log=True, print_to_console=True):
        message = 'Eval Time: {:.2f}s '.format(eval_result['eval_time'])
        for metric in eval_result:
            if metric == 'eval_time':
                continue
            message += '['
            for i in range(len(k)):
                message += '{}@{}: {:.4f} '.format(metric, k[i], eval_result[metric][i])
            message += '] '
        if save_to_log:
            self.logger.info(message)
        if print_to_console:
            print(message)


import dash_blueprint
import dash
from dash.dependencies import Input, Output
import dash_html_components as html
import dash_core_components as dcc

import arrow

app = dash.Dash(__name__)

app.scripts.config.serve_locally = True
app.css.config.serve_locally = True

app.layout = html.Div(
    className="bp3-dadrk",
    children=[
        dash_blueprint.DatePicker(
            id="datepicker", defaultValue=arrow.now().shift(years=-4).datetime
        ),
        dash_blueprint.DateInput(
            id="dateinput", defaultValue=arrow.now().shift(years=-4).date()
        ),
        html.Div(id="output"),
        dash_blueprint.DateRangePicker(
            id="daterangepicker",
            # start_date=arrow.now().replace(years=-4).date()
            minDate=arrow.now().shift(days=-1).date(),
            maxDate=arrow.now().shift(days=14).date(),
        ),
        html.Div(id="date-range-output"),
        dash_blueprint.DateRangeInput(
            id="daterangeinput",
            # start_date=arrow.now().replace(years=-4).date()
        ),
        html.Div(id="date-range-input-output"),
    ],
)

@app.callback(
    Output("date-range-input-output", "children"),
    [Input("daterangeinput", "start_date"), Input("daterangeinput", "end_date")],
)
def update_date(start_date, end_date):
    print(start_date, end_date)
    return "{} - {}".format(start_date, end_date)

@app.callback(
    Output("date-range-output", "children"),
    [Input("daterangepicker", "start_date"), Input("daterangepicker", "end_date")],
)
def update_date(start_date, end_date):
    print(start_date, end_date)
    return "{} - {}".format(start_date, end_date)

@app.callback(Output("output", "children"), [Input("dateinput", "date")])
def update_date(date):
    print(date)
    return date

if __name__ == "__main__":
    app.run_server(debug=False)


"""""
import cv2

video = cv2.VideoCapture(0, cv2.CAP_DSHOW)

a = 1

while True:
    a = a+1
    check,frame = video.read()
    print(frame)
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    cv2.imshow('Dhodis',gray)
    key = cv2.waitKey(1)
    if key == ord('q'):
        break

print(a)
video.release()
cv2.destroyAllWindows()

import cv2

video = cv2.VideoCapture(0)

while True:
    check,frame = video.read()
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    cv2.imshow('dhodis', gray)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

video.release()
cv2.destroyAllWindows()

"""""

import cv2
from datetime import datetime

video = cv2.VideoCapture(0)
# print(video.get(cv2.CAP_PROP_FRAME_HEIGHT))
# print(video.get(cv2.CAP_PROP_FRAME_WIDTH))

video.set(3, 700000)
video.set(4, 700000)

# print(video.get(3))
# print(video.get(4))

while video.isOpened():
    check,frame = video.read()
    if check:
        frame = cv2.flip(frame, 1)
        font = cv2.FONT_HERSHEY_SIMPLEX
        text = 'Width: '+ str(int(video.get(3))) + '    Height: '+ str(int(video.get(4)))
        datet = datetime.now()
        dateo = date_time = datet.strftime("%d/%m/%Y, %H:%M:%S")
        frame = cv2.putText(frame, dateo, (10,50), font, 1, (0,255,255), 2, cv2.LINE_AA)
        cv2.imshow('dhodi', frame)

        if cv2.waitKey(1) & 0XFF == ord('q'):
            break
    else:
        break

video.release()
cv2.destroyAllWindows()