import copy
import math
import datetime

import numpy as np

from kbdiffdi.features import feature

class KBDI(object):

    def __init__(self):
        # main data
        self.temp = None # a feature.RasterCube or feature.Vector (data is a 3d array)
        self.prcp = None # a feature.RasterCube or feature.Vector (data is a 3d array)

        # first values for initializing the KBDI calculation
        self.first_KBDI = None # 2d array of data

        # additional data that is calculated further down the pipeline
        self.net_rainfall = None # 2d array (raster)
        self.mean_annual_rainfall = None # a 2d array (raster)

    def set_temp(self, newtemp):
        self.temp = newtemp

    def set_prcp(self, newprcp):
        self.prcp = newprcp

    def set_first_drought_index(self, newfirstdroughtindex):
        # initialize first drought index to match the shape of the input data
        # its date is the day before the date of the first actual value in the dataset
        d_index = feature.RasterStack()
        d_index.create_sc_stack(newfirstdroughtindex,
                                           [self.temp.datelist[0] - datetime.timedelta(days=1)],
                                           None,
                                           "standard",
                                           0,
                                           0,
                                           1,
                                           -1)
        self.first_KBDI = d_index

    def get_first_KBDI(self):
        return self.first_KBDI

    def set_mean_annual_rainfall(self, amount):
        self.mean_annual_rainfall = amount

    def get_mean_annual_rainfall(self):
        return self.mean_annual_rainfall

    def fit(self, inittemp, initprcp, initmeanannualrainfall=None, initdroughtindex=np.array([[[[0]]]])):
        self.set_temp(inittemp)
        self.set_prcp(initprcp)
        self.set_first_drought_index(initdroughtindex)
        self.set_mean_annual_rainfall(initmeanannualrainfall)
        return self.calculate_KBDI()

    def cut_first_slice(self): # cut the first slice of the temp and prcp data. Only initialize with the 1st KBDI
        self.temp.setdata(self.temp.data[1:])
        self.temp.datelist = self.temp.datelist[1:]
        self.prcp.setdata(self.prcp.data[1:])
        self.prcp.datelist = self.prcp.datelist[1:]

    def calculate_net_rainfall(self):
        threshold = 5.08 # 0.20 * 25.4 # convert inches to mm. NOTE: some literature uses 5 mm instead of 5.08

        #out = Bob.STCube(self.prcp.y,self.prcp.x,self.prcp.h,self.prcp.w,self.prcp.s,self.prcp.d)
        net_rainfall = np.zeros(shape=(len(self.prcp.data), len(self.prcp.data[0]), len(self.prcp.data[0][0]), len(self.prcp.data[0][0][0]))) # netRainfall

        #variables for the calculation
        daily_prcp = copy.deepcopy(self.prcp.data) # the precipitation data
        running_total = np.zeros(shape=(1, len(self.prcp.data[0]), len(self.prcp.data[0][0]))) # a running total for continuous rain days
        consec = np.zeros(shape=(len(self.prcp.data[0]), len(self.prcp.data[0][0])), dtype=bool) # which cells see consecutive rainfall?
        already_subtracted = np.zeros(shape=(len(self.prcp.data[0]), len(self.prcp.data[0][0])), dtype=bool) # has the 0.20 threshold been met and 0.20 subtracted?

        n=0
        while n < len(daily_prcp):
            running_total += daily_prcp[n]
            # if dailyPrcp[n] == 0:
            net_rainfall[n] = np.where(daily_prcp[n] == 0, 0, net_rainfall[n])
            running_total = np.where(daily_prcp[n] == 0, 0, running_total)
            already_subtracted = np.where(daily_prcp[n] == 0, False, already_subtracted)

            if n > 0:
                # if dailyPrcp[n] > 0 and dailyPrcp[n-1] > 0
                consec = np.where((daily_prcp[n] > 0) & (daily_prcp[n-1] > 0), True, False)

            #if dailyPrcp[n] > 0 and consec and alreadySubtracted:
            net_rainfall[n] = np.where((daily_prcp[n] > 0) & (consec == True) and (already_subtracted == True), daily_prcp[n], net_rainfall[n])
            running_total = np.where((daily_prcp[n] > 0) & (consec == True) and (already_subtracted == True), 0, running_total)

            #if dailyPrcp[n] > 0.20 and consec and not alreadySubtracted:
            net_rainfall[n] = np.where((daily_prcp[n] > threshold) & (consec == True) & (already_subtracted == False), running_total - threshold, net_rainfall[n])
            already_subtracted = np.where((daily_prcp[n] > threshold) & (consec == True) & (already_subtracted == False), True, already_subtracted)
            running_total = np.where((daily_prcp[n] > threshold) & (consec == True) & (already_subtracted == False), 0, running_total)

            # if dailyPrcp[n] > 0.20 and not consec:
            net_rainfall[n] = np.where((daily_prcp[n] > threshold) & (consec == False), daily_prcp[n] - threshold, net_rainfall[n])
            already_subtracted = np.where((daily_prcp[n] > threshold) & (consec == False), True, already_subtracted)
            running_total = np.where((daily_prcp[n] > threshold) & (consec == False), 0, running_total)

            # if dailyPrcp[n] < 0.20 and not consec:
            net_rainfall[n] = np.where((daily_prcp[n] < threshold) & (consec == False), 0, net_rainfall[n])

            #if dailyPrcp[n] < 0.20 and consec and runningTotal <= 0.20 and not alreadySubtracted:
            net_rainfall[n] = np.where((daily_prcp[n] < threshold) & (consec == True) & (running_total <= threshold) & (already_subtracted == False), 0, net_rainfall[n])

            #if dailyPrcp[n] < 0.20 and consec and runningTotal > 0.20:
            net_rainfall[n] = np.where((daily_prcp[n] < threshold) & (consec == True) & (running_total > threshold), running_total - threshold, net_rainfall[n])
            already_subtracted = np.where((daily_prcp[n] < threshold) & (consec == True) & (running_total > threshold), True, already_subtracted)
            running_total = np.where((daily_prcp[n] < threshold) & (consec == True) & (running_total > threshold), 0, running_total)
            n+=1
        net_rain = feature.RasterStack()
        net_rain.create_sc_stack(net_rainfall,
                                self.prcp.datelist,
                                None,
                                "standard",
                                0,
                                0,
                                1,
                                -1)
        self.net_rainfall = net_rain

    def get_net_rainfall(self):
        return self.net_rainfall

    # calculate mean annual rainfall for each cell using the KBDI prcp STCube and set the meanAnnualRainfall Raster.
    def calculate_mean_annual_rainfall(self):
        """
        Calculates a mean annual rainfall value for each cell in the input data.
        It is assumed that the data is daily data without missing values and starts on January 1st.

        Parameters:
        -----------
        none

        Returns:
        ---------
        out: ndarray
            A 2d array holding a mean annual rainfall value for
            every cell in the input prcp data
        """
        raindata = copy.deepcopy(self.prcp.data)
        dates = self.prcp.datelist
        running_total = 0
        yearly_sums = None
        n = 0
        cur_year = dates[n].year
        while n < len(dates):
            if dates[n].year != cur_year: # we've hit a new year. 1. store the running total in the yearlySums and restart the running total
                if yearly_sums is None:
                    yearly_sums = np.array([running_total])
                else:
                    yearly_sums = np.append(yearly_sums, [running_total], axis=0)
                cur_year = dates[n].year
                running_total = raindata[n]
            else:
                running_total += raindata[n]
            n+=1
        # make sure to include the last year of data. The while condition doesn't
        if dates[n-1].month == 12 and dates[n-1] == 31:
            yearly_sums = np.append(yearly_sums, [running_total], axis=0)
        annual_mean = np.mean(yearly_sums, axis=0)
        mean_rain = feature.Raster(np.array([annual_mean]),
                                    {0: 0},
                                   datetime.datetime(1976, 7, 4),
                                   None,
                                   "standard",
                                   "mean annual rainfall",
                                   0,
                                   0,
                                   1,
                                   -1)
        self.mean_annual_rainfall = mean_rain

    def calculate_ET(self, prev_KBDI, prev_temp):
        """
        Calculates the ET based on equation 17 in appendix A from Keetch and Byram 1968.
        (The typographical error is accounted for)

        Parameters:
        ------------
            prev_KBDI: ndarray shape=(1,1,m,n)
                KBDI yesterday (in metric equivalents)
            dailyTemp: ndarray shape=(1,m,n)
                the daily temperature (in celsius)

        Returns:
        ---------
            ET: ndarray shape=(1,m,n)
                daily evapotranspiration
        """
        numerator = (203.2 - prev_KBDI) * (0.968 * np.exp(0.0875 * prev_temp + 1.5552) - 8.30)
        denominator = 1 + 10.88 * np.exp(-0.001736 * self.get_mean_annual_rainfall().data)
        ET = 0.001 * (numerator / denominator)
        return ET

    def calculate_KBDI(self):
        """
        prcp must be in mm
        temp must be in C

        Parameters:
        ------------
        initialDroughtIndex: float or int
            the initial value to set the first drought index

        Returns:
        ---------
        out: STCube
            data structure holding KBDI values
        """
        if self.mean_annual_rainfall is None: # set the mean annual rainfall if not set yet
            self.calculate_mean_annual_rainfall()
        #self.cutFirstSlice() # to clip the first layer from the temp and prcp data.
        self.calculate_net_rainfall()
        #netRainfall[0], temp[0], prcp[0]. netRainfall[0] is the first day, self.firstKBDI is yesterday's KBDI

        kb_cube = np.ones(shape=(len(self.temp.data), len(self.temp.data[0]), len(self.temp.data[0][0]), len(self.temp.data[0][0][0])))

        prev_KBDI = self.get_first_KBDI()
        prev_kbdi_data = prev_KBDI.data

        n = 0
        while n < len(self.temp.data):
            # today's ET requires yesterday's KBDI, yesterday's temp, and mean annual rainfall.
            ET = self.calculate_ET(prev_kbdi_data, self.temp.data[n])
            # KBDI for today is calculated based on yesterday's KBDI, ET, and yesterday's effective prcp (net rainfall)
            KBDI = prev_kbdi_data + ET - self.net_rainfall.data[n]  # output is a 2d array
            KBDI = np.where(KBDI < 0, 0, KBDI) # KBDI values can't be negative
            kb_cube[n] = KBDI # set kb_cube to today's KBDI, this then becomes yesterday's KBDI at the next iteration
            prev_kbdi_data = KBDI # prev_KBDI.set_data(KBDI)
            n+=1
        out_kbdi = feature.RasterStack()
        out_kbdi.create_sc_stack(kb_cube,
                                      self.temp.datelist,
                                      None,
                                      "standard",
                                      0,
                                      0,
                                      1,
                                      -1)
        return out_kbdi


import numpy as np

# Common loss class
class Loss:

    # Regularization loss calculation
    def regularization_loss(self, layer):
        # 0 by default
        regularization_loss = 0

        # L1 regularization - weights
        # calculate only when factor greater than 0
        if layer.weight_regularizer_l1 > 0:
            regularization_loss += layer.weight_regularizer_l1 * \
                                   np.sum(np.abs(layer.weights))

        # L2 regularization - weights
        if layer.weight_regularizer_l2 > 0:
            regularization_loss += layer.weight_regularizer_l2 * \
                                   np.sum(layer.weights * \
                                          layer.weights)

        # L1 regularization - biases
        # calculate only when factor greater than 0
        if layer.bias_regularizer_l1 > 0:
            regularization_loss += layer.bias_regularizer_l1 * \
                                   np.sum(np.abs(layer.biases))
        # L2 regularization - biases
        if layer.bias_regularizer_l2 > 0:
            regularization_loss += layer.bias_regularizer_l2 * \
                                   np.sum(layer.biases * \
                                          layer.biases)
        return regularization_loss

    # Calculates the data and regularization losses
    # given model output and ground truth values
    def calculate(self, output, y):

        # Calculate sample losses
        sample_losses = self.forward(output, y)

        # Calculate mean loss
        data_loss = np.mean(sample_losses)

        # Return loss
        return data_loss

# Mean Squared Error loss
class Loss_MeanSquaredError(Loss): # L2 loss

    # Forward pass
    def forward(self, y_pred, y_true):

        # Calculate loss
        sample_losses = np.mean((y_true-y_pred)**2, axis=-1)

        # Return losses
        return sample_losses

    # Backward pass
    def backward(self, dvalues, y_true):

        # Number of samples
        samples = len(dvalues)

        # Number of outputs in every sample
        # We'll use the first sample to count them
        outputs = len(dvalues[0])

        # Gradient on values
        self.dinputs = -2 * (y_true - dvalues)/outputs

        # Normalize gradient
        self.dinputs = self.dinputs/samples

# Mean Absolute Error loss
class Loss_MeanAbsoluteError(Loss):

    # Forward pass
    def forward(self, y_pred, y_true):

        # Calculate loss
        sample_losses = np.mean(np.abs(y_true - y_pred), axis=-1)

        # Return losses
        return sample_losses

    # Backward pass
    def backward(self, dvalues, y_true):

        # Number of samples
        samples = len(dvalues)
        # Number of outputs in every sample
        # We'll use the first sample to count them
        outputs = len(dvalues[0])

        # Calculate gradient
        self.dinputs = np.sign(y_true-dvalues)/outputs

        # Normalize gradient
        self.dinputs = self.dinputs/samples


pandding = '\n'

# 字典简介
alien = {'color':'green','points':5}
print(alien['color'])
print(alien['points'])
# 添加键值对
alien['position_x'] = 0
alien['position_y'] = 25
print(alien)
# 修改值
alien['color'] = 'yellow'
print(alien)
# 删除键值对
del alien['points']
print(alien)

favorite_language ={
    'jin:':'python',
    'wang':'python',
    'yang':'java',
    'lei':'javascript'
}
print(favorite_language)
print("lei's favorite language is " + favorite_language['lei'].title() + ".")
print(pandding)
# 遍历，乱序
for key in favorite_language.keys():
    print( 'key: ' + key )
print(pandding)
for value in favorite_language.values():
    print( 'value: ' + value )
print(pandding)

for key,value in favorite_language.items():
    print('\nkey: ' + key)
    print('value: ' + value)
print(pandding)
# 几个set去重
for value in set(favorite_language.values()):
    print('value: ' + value)

aliens = []
for alienNumb in range(30):
    newAlien = {'color':'green',"points":'5','speed':'slow'}
    aliens.append(newAlien)
print(len(aliens))

for alien in aliens[:5]:
    print(alien)

favorite_languages = {
    'jen': ['python', 'ruby'],
    'sarah': ['c'],
    'edward': ['ruby', 'go'],
    'phil': ['python', 'haskell'],
}

for name, languages in favorite_languages.items():
    if  len(languages) == 1:
        print( "\n" + name.title() + "'s favorite languages are:" + languages[-1].title() )
    else:
        print("\n" + name.title() + "'s favorite languages are:")
        for language in languages:
            print( "\t" + language.title())

# 嵌套
users = {
    'aeinstein': {
        'first': 'albert',
        'last': 'einstein',
        'location': 'princeton'
    },
    'mcurie': {
        "first": 'marie',
        'last': 'curie',
        'location': 'paris'
    }
}

for userName, userInfo in users.items():
    print('\nUserName: ' + userName)
    fullName = userInfo['first'] + userInfo['last']
    location = userInfo['location']

    print('\tFull name : ' + fullName.title())
    print('\nLocation: ' + location.title())

de


# Import json and requests packages
import requests
import json
# API URL
url = "https://petstore.swagger.io/v2/pet"
# read input from json file
file = open("UpdatePet.json", "r")
# call the read method and assign the result to a variable
json_input = file.read()
# call the load method
request_json = json.loads(json_input)
# Print the results read from the json file
print(request_json)
# make a put request with json input body
response = requests.put(url, request_json)
# Validating response code
assert response.status_code != 200


import traceback
from abc import ABC, abstractmethod
from argparse import ArgumentParser, Namespace

from langchain.base_language import BaseLanguageModel
from langchain.chat_models.base import BaseChatModel
from langchain.schema import BaseMessage, ChatResult, LLMResult
from procoder.functional import format_multiple_prompts
from procoder.prompt import Module as PromptModule
from toolemu.utils import convert_to_message, parse_llm_response
from toolemu.utils.my_typing import *

class BasePromptExecutor:
    """Base class for all prompt executors.

    To naturally support multithreading, the executor should be stateless.
    That is, the executor should not have any attributes that are not passed in
    through the constructor.

    args:
        llm: the language model to use
        sys_prompt_module: the prompt module for system message
        task_prompt_module: the prompt module for task message
        stop_at: stop at which stage, flexible for debugging. Can be "preprocess", "prompt", "llm"
    """

    _input_keys: List[str] = None
    _llm: BaseLanguageModel = None
    _sys_prompt_module: PromptModule = None
    _task_prompt_module: PromptModule = None
    _prompt_module_names: List[str] = ["system", "task"]

    def __init__(
        self,
        llm: BaseLanguageModel,
        stop_at: str = None,
    ):
        self._llm = llm
        self._is_chat_model = isinstance(llm, BaseChatModel)
        self._stop_at = stop_at

    @property
    def prompt_modules(self):
        return [self._sys_prompt_module, self._task_prompt_module]

    def set_prompt_module(self, prompt_module: PromptModule, prompt_type: str = "task"):
        """Set prompt modules."""
        if prompt_type == "system":
            self._sys_prompt_module = prompt_module
        elif prompt_type == "task":
            self._task_prompt_module = prompt_module
        else:
            raise ValueError(f"Unknown prompt type: {prompt_type}")

    def _validate_inputs(self, inputs: Dict[str, Any]):
        """Validate input dict."""
        if self._input_keys is None:
            # skip validation if input keys are not defined
            return
        for k in self._input_keys:
            if k not in inputs:
                raise ValueError(f"Missing input key: {k}")
        for k in inputs.keys():
            if k not in self._input_keys:
                raise ValueError(f"Unexpected input key: {k}")

    @abstractmethod
    def _preprocess_inputs(self, inputs: Dict[str, Any]) -> Dict[str, Any]:
        """Preprocess inputs to the format that can be used by the prompt."""
        pass

    def _preprocess_prompts(
        self, prompts: List[PromptModule], prompt_inputs: Dict[str, Any]
    ) -> List[PromptModule]:
        return prompts

    def _postprocess_prompts(
        self, prompts: List[str], raw_inputs: Dict[str, Any]
    ) -> List[str]:
        return prompts

    def _process_prompts(
        self, prompt_inputs: Dict[str, Any], raw_inputs: Dict[str, Any]
    ) -> List[str]:
        prompt_modules = self._preprocess_prompts(self.prompt_modules, prompt_inputs)
        include_brackets = [name != "system" for name in self._prompt_module_names]
        prompts = format_multiple_prompts(
            prompt_modules, prompt_inputs, include_brackets=include_brackets
        )
        return self._postprocess_prompts(prompts, raw_inputs)

    def _convert_to_prompt(
        self,
        system_message: str,
        conversation: List[str],
        message_types: List[str] = None,
    ) -> Union[List[BaseMessage], str]:
        """Convert system message and conversation to prompt that can be used by LangChain LLM."""
        prompt = []
        if system_message is not None:
            prompt.append(convert_to_message(system_message, "system"))
        if message_types is None:
            message_types = [
                "human" if i % 2 == 0 else "ai" for i in range(len(conversation))
            ]
        for i, msg in enumerate(conversation):
            prompt.append(convert_to_message(msg, message_types[i]))

        if not self._is_chat_model:
            prompt = "\n\n".join([t.content for t in prompt])
        return [prompt]  # need to be a list for llm.generate

    def _build_conversation(
        self, task_prompt: str, raw_inputs: Dict[str, Any]
    ) -> List[str]:
        """Build conversation using task message and raw inputs."""
        return [task_prompt]

    def _build_prompt(
        self,
        prompt_inputs: Dict[str, Any],
        raw_inputs: Dict[str, Any],
    ) -> Dict[str, Any]:
        """Build prompt using input dict."""
        if self._task_prompt_module is None:
            raise ValueError("task prompt is not defined")
        prompts = self._process_prompts(prompt_inputs, raw_inputs)
        sys_prompt, task_prompt = prompts[:2]
        conversation = self._build_conversation(task_prompt, raw_inputs)
        return {
            "prompts": prompts,
            "conversation": conversation,
            "merged_prompt": self._convert_to_prompt(sys_prompt, conversation),
        }

    @abstractmethod
    def _parse_output(
        self, output: ChatResult, raw_inputs: Dict[str, Any], **kwargs
    ) -> Any:
        """Parse the output of LLM to the desired format."""
        # TODO: supports LLMResult
        pass

    def _run(self, inputs: Dict[str, Any], **kwargs) -> Any:
        """Execute the prompt with LLM given inputs, then parse the output."""
        if not kwargs.get("disable_input_validation", False):
            self._validate_inputs(inputs)
        prompt_inputs = self._preprocess_inputs(inputs)
        if self._stop_at == "preprocess":
            return prompt_inputs
        prompt = self._build_prompt(prompt_inputs, inputs)["merged_prompt"]
        if self._stop_at == "prompt":
            return prompt
        output = self._llm.generate(prompt)
        if self._stop_at == "llm":
            return output
        return self._parse_output(output, inputs)

    def _call_impl(self, inputs: Dict[str, Any], **kwargs) -> Any:
        """Call the executor with inputs."""
        try:
            return self._run(inputs, **kwargs)
        except Exception as e:
            traceback.print_exc()
            raise e  # ! changed from "return None" to "raise e" during refacting

    __call__: Callable[..., Any] = _call_impl

    @classmethod
    def register_args(cls, parser: ArgumentParser):
        """Register arguments for the executor."""
        parser.add_argument(
            "--stop-at",
            "-sa",
            type=str,
            default=None,
            choices=["preprocess", "prompt", "llm"],
        )

    @classmethod
    def from_args(cls, args: Namespace, llm: BaseLanguageModel):
        """Create an executor from arguments."""
        return cls(llm, stop_at=args.stop_at)

class BasePromptExecutorWithCritique(BasePromptExecutor):
    """Base class for all prompt executors with critique."""

    _critique_prompt_module: PromptModule = None
    _prompt_module_names: List[str] = ["system", "task", "critique"]

    def __init__(
        self,
        llm: BaseLanguageModel,
        critique_llm: BaseLanguageModel = None,
        stop_at: str = None,
        critique_rounds=0,
    ):
        super().__init__(llm, stop_at)
        self._critique_llm = critique_llm or llm
        self._critique_rounds = critique_rounds

    def set_prompt_modules(
        self, prompt_module: PromptModule, prompt_type: str = "task"
    ):
        """Set prompt modules."""
        if prompt_type == "critique":
            self._critique_prompt_module = prompt_module
        else:
            super().set_prompt_modules(prompt_module, prompt_type)

    @property
    def prompt_modules(self):
        return [
            self._sys_prompt_module,
            self._task_prompt_module,
            self._critique_prompt_module,
        ]

    def _run(self, inputs: Dict[str, Any], **kwargs) -> List[Any]:
        if not kwargs.get("disable_input_validation", False):
            self._validate_inputs(inputs)
        prompt_inputs = self._preprocess_inputs(inputs)
        if self._stop_at == "preprocess":
            return prompt_inputs

        prompt_dict = self._build_prompt(prompt_inputs, inputs)
        sys_prompt, task_prompt, critique_prompt = prompt_dict["prompts"]
        conversation = prompt_dict["conversation"]
        prompt = prompt_dict["merged_prompt"]
        if self._stop_at == "prompt":
            return prompt
        raw_output = self._llm.generate(prompt)
        if self._stop_at == "llm":
            return raw_output
        results = [self._parse_output(raw_output, inputs, critique_round=0)]
        for i in range(self._critique_rounds):
            try:
                if critique_prompt is None:
                    raise ValueError("critique prompt is not defined")
                conversation += [parse_llm_response(raw_output), critique_prompt]
                critique_prompt = self._convert_to_prompt(sys_prompt, conversation)
                raw_output = self._critique_llm.generate(critique_prompt)
                results.append(
                    self._parse_output(raw_output, inputs, critique_round=i + 1)
                )
            except Exception as e:
                print(f"Error encountered in critique round {i+1}: {e}")
                break
        return results

    @classmethod
    def register_args(cls, parser: ArgumentParser):
        """Register arguments for the executor."""
        super().register_args(parser)
        parser.add_argument(
            "--critique-rounds",
            "-cr",
            type=int,
            default=0,
            help="The number of critique rounds",
        )

    @classmethod
    def from_args(
        cls,
        args: Namespace,
        llm: BaseLanguageModel,
        critique_llm: BaseLanguageModel = None,
    ):
        """Create an executor from arguments."""
        return cls(
            llm,
            critique_llm,
            stop_at=args.stop_at,
            critique_rounds=args.critique_rounds,
        )


from django.urls import path
from . import views

urlpatterns = [
  path('', views.home, name="home"),
  path("about/", views.about, name="about"),

  path("tech/", views.TechList.as_view(), name="tech_index"),
  path("tech/<int:pk>/add_usage", views.add_usage, name="add_usage"),
  path("tech/<int:tech_pk>/assoc_user/<int:user_pk>/", views.assoc_user, name="assoc_user"),
  path("tech/<int:pk>/", views.tech_detail, name="tech_detail"),
  path("tech/create/", views.TechCreate.as_view(), name="tech_create"),
  path("tech/<int:pk>/update", views.TechUpdate.as_view(), name="tech_update"),
  path("tech/<int:pk>/delete", views.TechDelete.as_view(), name="tech_delete"),

  path("users/", views.UsersList.as_view(), name="users_index"),
  path("users/<int:pk>/", views.UsersDetail.as_view(), name="users_detail"),
  path("users/create/", views.UsersCreate.as_view(), name="users_create"),
  path("users/<int:pk>/update", views.UsersUpdate.as_view(), name="users_update"),
  path("users/<int:pk>/delete", views.UsersDelete.as_view(), name="users_delete"),
]


import LexicalAnalyzer
import SyntaxTree

# import sys
#
# try:
#     sys.argv[1]
#     file = open(sys.argv[1], 'r')
# except:
#     print('No file in directory')
#     exit(1)
#
# code = file.read()

code = '''program exemplo5 (input, output);
begin
   read (n);
   f1:=0; f2:=1; k:=1;
   while k<=n do
   begin
      f3:=f1+f2;
      f1:=f2;
      f2:=f3;
      k:=k+1
   end;
   write (n, f1)
end.
'''

code1 = '''
program exemplo5(input, output,);
'''

tokens = LexicalAnalyzer.get_tokens(code)

program = SyntaxTree.program

def check(object, token):
    try:
        if object.name == token[0]:
            return 1
        elif object.name == '<id>':
            return 2
        elif object.name == '<lid>':
            return 3
        elif object.name == '<dv>':
            return 4
        elif object.name == '[pdv]':
            if token[0] == '<var>':
                return 5
        elif len(object.children) != 0:
            for i in range(len(object.children)):
                x = check(object.children[i], token)
                if x == 1:
                    token = token[1:len(token)]
                elif x == 2:
                    SyntaxTree.identifiers.append(token[0])
                    token = token[1:len(token)]
                elif x == 3:
                    token = SyntaxTree.lid(token)
                elif x == 4:
                    token = SyntaxTree.pdv(token)
                elif x == 5:
                    token = token[1:len(token)] #consome o <var>
                    token = SyntaxTree.pdv(token)

        else:
            print('Error: expected {}'.format(object.name))
            exit(0)
    except IndexError:
        print('Missing arguments {}'.format(object.name))

check(program, tokens)
print('ok')


import os
import set_config
import sys
import torch
import json
from transformers import BertTokenizerFast, BertModel
from common.utils import Preprocessor, multilabel_categorical_crossentropy
from models.GlobalPointer import DataMaker, MyDataset, GlobalPointer, MetricsCalculator,EfficientGlobalPointer,Encoder_Bert_prefix,create_regularization_GlobalPointer,create_regularization_EfficientGlobalPointer
from torch.utils.data import DataLoader, Dataset
from tqdm import tqdm
import glob
from evaluate import evaluate
import time

os.environ["TOKENIZERS_PARALLELISM"] = "true"
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

#获取实体类型数
from Code.read_data.read_file import read_ner_data_span
# from Code_GlobalPointer.GlobalPointer_pytorch.datasets.read_file import read_ner_data_span
def data_generator(data_type="train"):
    # 读取数据，生成DataLoader。
    if data_type == "train":
        train_data_path = os.path.join(config["data_home"], config["exp_name"], config["train_data"])
        train_data_=read_ner_data_span(train_data_path, tokenizer, max_seq_length=512)

        valid_data_path = os.path.join(config["data_home"], config["exp_name"], config["valid_data"])
        valid_data_=read_ner_data_span(valid_data_path, tokenizer, max_seq_length=512)

        test_data_path = os.path.join(config["data_home"], config["exp_name"], config["test_data"])
        test_data_ = read_ner_data_span(test_data_path, tokenizer, max_seq_length=512)

    elif data_type == "valid":
        test_data_path = os.path.join(config["data_home"], config["exp_name"], config["test_data"])
        test_data = read_ner_data_span(test_data_path, tokenizer, max_seq_length=512)

    all_data = train_data_+valid_data_+test_data_

    # TODO:句子截取
    max_tok_num =0
    max_tok_num = max([len(f["input_ids"]) for f in all_data])
    assert max_tok_num <= hyper_parameters["max_seq_len"], f'数据文本最大token数量{max_tok_num}超过预设{hyper_parameters["max_seq_len"]}'
    max_seq_len = min(max_tok_num, hyper_parameters["max_seq_len"])  #

    data_maker = DataMaker(tokenizer)

    if data_type == "train":
        train_dataloader = DataLoader(
                                      MyDataset(train_data_),
                                      # train_data_,
                                      batch_size=hyper_parameters["batch_size"],
                                      shuffle=True,
                                      num_workers=config["num_workers"],
                                      drop_last=False,
                                      collate_fn=lambda x: data_maker.generate_batch(x, max_seq_len, ent2id),
                                      )
        valid_dataloader = DataLoader(MyDataset(valid_data_),
                                      batch_size=hyper_parameters["batch_size"],
                                      shuffle=True,
                                      num_workers=config["num_workers"],
                                      drop_last=False,
                                      collate_fn=lambda x: data_maker.generate_batch(x, max_seq_len, ent2id)
                                      )
        test_dataloader = DataLoader(MyDataset(test_data_),
                                      batch_size=hyper_parameters["batch_size"],
                                      shuffle=False,
                                      num_workers=config["num_workers"],
                                      drop_last=False,
                                      collate_fn=lambda x: data_maker.generate_batch(x, max_seq_len, ent2id)
                                      )
        return train_dataloader, valid_dataloader,test_dataloader #返回 train和dev数据

    elif data_type == "valid":
        valid_dataloader = DataLoader(MyDataset(test_data),
                                      batch_size=hyper_parameters["batch_size"],
                                      shuffle=True,
                                      num_workers=config["num_workers"],
                                      drop_last=False,
                                      collate_fn=lambda x: data_maker.generate_batch(x, max_seq_len, ent2id)
                                      )
        return valid_dataloader #返回 dev数据

metrics = MetricsCalculator() #评价指标

def loss_fun(y_true, y_pred):  # 损失函数
    """
    y_true:(batch_size, ent_type_size, seq_len, seq_len)
    y_pred:(batch_size, ent_type_size, seq_len, seq_len)
    """
    batch_size, ent_type_size = y_pred.shape[:2]
    y_true = y_true.reshape(batch_size * ent_type_size, -1)
    y_pred = y_pred.reshape(batch_size * ent_type_size, -1)
    loss = multilabel_categorical_crossentropy(y_true, y_pred)  # 多标签分类损失函数
    return loss

def train_step(batch_train, model, optimizer, criterion): #criterion损失函数
    # batch_input_ids:(batch_size, seq_len)
    # batch_labels:(batch_size, ent_type_size, seq_len, seq_len)
    batch_samples, batch_input_ids, batch_attention_mask, batch_token_type_ids, batch_labels,batch_label_token = batch_train
    batch_input_ids, batch_attention_mask, batch_token_type_ids, batch_labels,batch_label_token = (batch_input_ids.to(device),
                                                                                batch_attention_mask.to(device),
                                                                                batch_token_type_ids.to(device),
                                                                                batch_labels.to(device),
                                                                                batch_label_token.to(device)
                                                                                )
    from set_config import regularization_GlobalPointer_EfficientGlobalPointer_config
    args = regularization_GlobalPointer_EfficientGlobalPointer_config()
    if args.use_regularization: #使用 正则化
        loss,logits = model(batch_input_ids, batch_attention_mask, batch_token_type_ids, batch_labels, criterion,batch_label_token)  # [btz, head, seq, seq]
    else: #不用 正则化
        logits = model(batch_input_ids, batch_attention_mask, batch_token_type_ids) #[btz, head, seq, seq]
        loss = criterion(logits, batch_labels)

    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

    return loss.item()

def train(model, dataloader, epoch, optimizer):
    model.train()

    #scheduler
    if hyper_parameters["scheduler"] == "CAWR":
        T_mult = hyper_parameters["T_mult"]  #hyper_parameters["T_mult"]=1
        rewarm_epoch_num = hyper_parameters["rewarm_epoch_num"] #2
        #线性预热
        scheduler = torch.optim.lr_scheduler.CosineAnnealingWarmRestarts(optimizer,len(train_dataloader) * rewarm_epoch_num,T_mult)

    elif hyper_parameters["scheduler"] == "Step": #step_scheduler模式的参数 eg "decay_rate": 0.999,"decay_steps": 100
        decay_rate = hyper_parameters["decay_rate"]
        decay_steps = hyper_parameters["decay_steps"]
        scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=decay_steps, gamma=decay_rate)

    pbar = tqdm(enumerate(dataloader), total=len(dataloader)) #枚举dataloader

    total_loss = 0.
    for batch_ind, batch_data in pbar:
        loss= train_step(batch_data, model, optimizer, loss_fun) #调用train_step 得到当前训练步数的损失

        total_loss += loss #总损失

        avg_loss = total_loss / (batch_ind + 1)
        scheduler.step()

        #设置/修改进度条的描述
        pbar.set_description(f'Project:{config["exp_name"]}, Epoch: {epoch + 1}/{hyper_parameters["epochs"]}, Step: {batch_ind + 1}/{len(dataloader)}')
        # 设置 / 修改(附加属性)
        pbar.set_postfix(loss=avg_loss, lr=optimizer.param_groups[0]["lr"])

def valid_test_step(batch_valid, model):
    batch_samples, batch_input_ids, batch_attention_mask, batch_token_type_ids, batch_labels,batch_label_token = batch_valid
    batch_input_ids, batch_attention_mask, batch_token_type_ids, batch_labels ,batch_label_token= (batch_input_ids.to(device),
                                                                                batch_attention_mask.to(device),
                                                                                batch_token_type_ids.to(device),
                                                                                batch_labels.to(device),
                                                                                batch_label_token.to(device)
                                                                                )
    with torch.no_grad():
        from set_config import regularization_GlobalPointer_EfficientGlobalPointer_config
        args = regularization_GlobalPointer_EfficientGlobalPointer_config()
        if args.use_regularization:  # 使用 正则化
            _,logits = model(batch_input_ids, batch_attention_mask, batch_token_type_ids, batch_labels, loss_fun, batch_label_token) #模型的输出
        else:
            logits = model(batch_input_ids, batch_attention_mask, batch_token_type_ids) #模型的输出

    sample_f1, sample_precision, sample_recall = metrics.get_evaluate_fpr(logits, batch_labels) #评价得分

    return sample_f1, sample_precision, sample_recall #返回结果

def valid(model, dataloader):
    model.eval()

    total_f1, total_precision, total_recall = 0., 0., 0.
    for batch_data in tqdm(dataloader, desc="deving"): #枚举dev数据
        f1, precision, recall = valid_test_step(batch_data, model) #调用valid_step函数 得到当前批次数据的 评价得分

        total_f1 += f1
        total_precision += precision
        total_recall += recall

    #取均值
    avg_f1 = total_f1 / (len(dataloader))
    avg_precision = total_precision / (len(dataloader))
    avg_recall = total_recall / (len(dataloader))

    print("******************************************")
    print('dev:  '+f'avg_precision: {avg_precision}, avg_recall: {avg_recall}, avg_f1: {avg_f1}')
    return avg_f1

def test(model, dataloader):
    model.eval()

    total_f1, total_precision, total_recall = 0., 0., 0.
    for batch_data in tqdm(dataloader, desc="testing"): #枚举dev数据
        f1, precision, recall = valid_test_step(batch_data, model) #调用valid_step函数 得到当前批次数据的 评价得分

        total_f1 += f1
        total_precision += precision
        total_recall += recall

    #取均值
    avg_f1 = total_f1 / (len(dataloader))
    avg_precision = total_precision / (len(dataloader))
    avg_recall = total_recall / (len(dataloader))

    print('test:  '+f'avg_precision: {avg_precision}, avg_recall: {avg_recall}, avg_f1: {avg_f1}')
    print("******************************************")
    print()
    return avg_f1

if __name__ == '__main__':
    config = set_config.train_config
    hyper_parameters = config["hyper_parameters"]  # 超参
    config["num_workers"] = 0 if sys.platform.startswith("linux") else 0
    torch.manual_seed(hyper_parameters["seed"])  # pytorch random seed
    model_state_dict_dir = os.path.join(config["path_to_save_model"], config["exp_name"],time.strftime("%Y-%m-%d_%H.%M.%S", time.gmtime()))
    if not os.path.exists(model_state_dict_dir):
        os.makedirs(model_state_dict_dir)
    torch.backends.cudnn.deterministic = True

    tokenizer = BertTokenizerFast.from_pretrained(config["bert_path"], add_special_tokens=True, do_lower_case=False)

    from set_config import commen_config
    args = commen_config()
    args.n_gpu = torch.cuda.device_count()

    NCBI_BC5CDR_dise_SPAN_LABEL_TO_ID = {'O': 0, 'Disease': 1} #首先将标签为Disease的 转为id的形式
    ent2id={"0":0,"1":1}

    ################ 1 ############## regularization_GlobalPointer_config use_regularization设置为False

    # encoder = BertModel.from_pretrained(config["bert_path"])  # 用bert作为encoder
    # encoder = Encoder_Bert_prefix(config["bert_path"])  # 用bert+prefix作为encoder regularization_GlobalPointer_config use_prefix设置为True

    # model = GlobalPointer(encoder, args.ent_type_size, 64)  # GlobalPointer模型
    # model = EfficientGlobalPointer(encoder, args.ent_type_size, 64)  # EfficientGlobalPointer模型

    #################################

    ################# 2 ################ regularization_GlobalPointer_config use_regularization设置为True

    model = create_regularization_GlobalPointer(args)  # GlobalPointer模型
    # model = create_regularization_EfficientGlobalPointer(args)  # EfficientGlobalPointer模型

    #################################

    model = model.to(device)

    if config["run_type"] == "train": #训练模式 进行训练和评估
        train_dataloader, valid_dataloader,test_dataloader = data_generator()

        # optimizer
        init_learning_rate = float(hyper_parameters["lr"])
        optimizer = torch.optim.Adam(model.parameters(), lr=init_learning_rate)

        max_f1 = 0.
        for epoch in range(hyper_parameters["epochs"]):
            print("===================epoch====================",epoch+1)
            train(model, train_dataloader, epoch, optimizer) #训练
            valid_f1 = valid(model, valid_dataloader) #评估
            test_f1 = test(model, test_dataloader)
            if test_f1 > max_f1:
                max_f1 = test_f1
                if test_f1 > config["f1_2_save"]:  # save the best model
                    model_state_num = len(glob.glob(model_state_dict_dir + "/model_state_dict_*.pt"))
                    torch.save(model.state_dict(),os.path.join(model_state_dict_dir, "model_state_dict_{}.pt".format(model_state_num)))
            print(f"****************** Best F1 ******************: {max_f1}")
            print()

    elif config["run_type"] == "eval": #评估模式 只进行评估
        evaluate()

if (args1.use_prefix):  # 定义encoder
    encoder = Encoder_Bert_prefix(args.model_name_or_path[i])
else:
    from transformers import BertModel
    encoder = BertModel.from_pretrained(args.model_name_or_path[i])


"""
yapp — Yet Another Python (data) Pipeline
"""
from ._version import version
from .core import InputAdapter, Inputs, Job, Monitor, OutputAdapter, Pipeline

__all__ = [
    "core",
    "adapters",
    "cli",
    "Pipeline",
    "Job",
    "Inputs",
    "InputAdapter",
    "OutputAdapter",
    "Monitor",
]

__version__ = version


from flask import render_template, redirect, url_for, send_file , Blueprint
from movie_models import movie_db ,movie_words_lists , movie_video_info , srt_detector_words

#blueprint of movie_index thats will be call and use in movie_app.py
movie_index = Blueprint('movie_index',__name__)

#create index page when clients access "/" its will return index functions
@movie_index.route("/")
def index() :
    sql1 = movie_db.session.query(movie_words_lists).count() #count number of words in database and return it to html template
    sql2 = movie_db.session.query(movie_video_info).count() #count number of video uploaded in database and return it to html template
    sql3 = movie_db.session.query(srt_detector_words).count() #count number of words founded in srt files in database and return it to html template
    return render_template('index.html' , words_num = sql1 , movies_num = sql2 , srt_num = sql3) #return to template with value of sql1 , sql2 , sql3

#create list of saved words in databse and show it
@movie_index.route("/words_list")
def words_list() :
    sql1 = movie_db.session.execute(movie_db.select(movie_words_lists)).scalars()#return all value from database and send it to tmplate to show it
    return render_template('words_list.html' , sql1 = sql1)                      # note i use (scalars()) functions to get all rows

#create list of uploades video in databse and show it
@movie_index.route("/video_list")
def video_list() :
    sql1 = movie_db.session.execute(movie_db.select(movie_video_info)).scalars()
    return render_template('videos_list.html' , sql1 = sql1)


#urls.py

from django.conf.urls import url
from places import views

urlpatterns = [
    url(r'^api/places$', views.branch_list),
    url(r'^api/placescount$', views.branch_count),
    url(r'^api/latestimport/(?P<contributor>\D+)$', views.latest_import),
    url(r'^api/partners$', views.partner_list),
    url(r'^api/partners/(?P<partner_short_name>[0-9]+)$', views.partner_detail),
    url(r'^api/places/(?P<branch_short_name>[0-9]+)$', views.branch_detail),
    url(r'^api/places/(?P<branch_short_name>[0-9]+)/(?P<contributor>\D+)$' , views.branch_from_contributor),
]


from multiprocessing import Event, Queue
from multiprocessing.connection import Connection
from typing import Callable, Sequence, Tuple

import numpy as np

from .baseoptimizer import BaseOptimizer, MinimizeResult

__all__ = ('RandomOptimizer',)

class RandomOptimizer(BaseOptimizer):
    """ Evaluates random points within the bounds for a fixed number of iterations.
    **Not** actually an optimizer. Intended for debugging.

    Parameters
    ----------
    Inherited, _opt_id _signal_pipe _results_queue _pause_flag workers backend is_log_detailed
        See :class:`.BaseOptimizer`.
    iters
        Number of function evaluations the optimizer will execute before terminating.
    """

    def __init__(self,
                 _opt_id: int = None,
                 _signal_pipe: Connection = None,
                 _results_queue: Queue = None,
                 _pause_flag: Event = None,
                 workers: int = 1,
                 backend: str = 'processes',
                 is_log_detailed: bool = False,
                 iters: int = 100):
        super().__init__(_opt_id, _signal_pipe, _results_queue, _pause_flag, workers, backend, is_log_detailed)
        self.max_iters = iters
        self.used_iters = 0
        self.result = MinimizeResult()
        self.stop_called = False
        self.logger.debug("Setup optimizer")

    def minimize(self,
                 function: Callable,
                 x0: Sequence[float],
                 bounds: Sequence[Tuple[float, float]],
                 callbacks: Callable = None, **kwargs) -> MinimizeResult:

        best_f = self.result.fx if self.is_restart else np.inf

        while self.used_iters < self.max_iters:
            if self.stop_called:
                break

            self.used_iters += 1
            vector = []
            for bnd in bounds:
                vector.append(np.random.uniform(bnd[0], bnd[1]))
            vector = np.array(vector)
            self.logger.debug("Generated vector = %s", vector)

            self.logger.debug("Evaluating function.")
            fx = function(vector)
            self.logger.debug("Function returned fx = %f", fx)

            if callbacks and callbacks():
                self.stop_called = True

            if self._results_queue:
                self.logger.debug("Checking messages")
                self.check_messages()
                self._pause_signal.wait()

            if fx < best_f:
                best_f = fx
                best_x = vector
                self.logger.debug("Updating best")
                self.result.x, self.result.fx = best_x, best_f

        self.result.success = True
        self.logger.debug("Termination successful")
        if self._results_queue:
            self.logger.debug("Messaging manager")
            self.message_manager(0, "Optimizer convergence")
            self.check_messages()

        self.logger.debug("Returning result")
        return self.result

    def callstop(self, reason: str = ""):
        self.stop_called = True


#!/usr/bin/python3 env

# Berkan ER
# https://github.com/erberkan/

# Prepared for use in test envoirements.
# Be careful for web site's Terms and Conditions before use this script to live website(s).

import sys

try:
    from bs4 import BeautifulSoup, Comment
except:
    print("[!] You will need to install BeautifulSoup")
    raise SystemExit

try:
    import urllib.request
except:
    print("[!] You will need to install urllib")
    raise SystemExit

def commentScan(x):
    soup = BeautifulSoup(urllib.request.urlopen(x), 'html.parser')
    # soup = BeautifulSoup(urllib.request.urlopen(x).read(), 'lxml')
    comments = soup.findAll(text=lambda text: isinstance(text, Comment))

    print("url: " + x)

    for c in comments:
        print("--> " + c.extract() + "\n")
        # print (type(comments))

def main():
    try:
        argv = sys.argv[1]
        commentScan("http://" + argv)
    except:
        print("--> Usage: python3 commentTagScraper.py <url> or <IP>\n")
        print("[*] Example: python3 commentTagScraper.py website.com")
        print("[*] Example: python3 commentTagScraper.py 127.0.0.1 ")

if __name__ == "__main__":
    main()


## using the following video tutorial first: https://www.youtube.com/watch?v=k8SXsT5TLxQ&t=32s

# The pattern to print -
####
####
####
####

# When having to print the same thing repeatedly, the best course of action is to try putting it into a loop.
# Having multiple print statement to do the same thing is not optimised code.

# How to print the pattern shown above:

for i in range(4): # This is to loop for each line
    for j in range(4): # This is to loop for each hash in each line

        print('#', end='')

    print() # This will add a '\n' between each iteration of the nested for loop

print()
# The next pattern to print -
#
##
###
####

for i in range(5):
    hash_list = ['#']*i

    for h in hash_list:
        print(h, end='')

    print() # Add the '\n' separator between each nested for loop iteration

# Alternative solution -

for i in range(4):
    for j in range(i+1):
        print('#', end='')

    print()

print()
# The next pattern to print -
####
###
##
#

#print(sorted(range(4), reverse=True))
print()
for i in sorted(range(4), reverse=True):
    for j in range(i+1):
        print('#', end='')

    print()

n = 5
i = 0

while i <= n:
    for j in range(n-i):
        print('#', end='')
    i += 1
    print()

###############################

# Next video tutorial being followed: https://www.youtube.com/watch?v=lsOOs5J8ycw
# All the patterns can be found here: https://github.com/kunal-kushwaha/DSA-Bootcamp-Java/blob/main/assignments/09-patterns.md

# The general trick to outputting patterns and designs in the terminal is to use nested for loops.
# First thing to look at:
# The outer-most for loop should target the number of lines that you are going to have in your pattern.
# Divide your pattern into rows and columns.
# no. of lines = no. of rows (in pattern) = no. of iterations the outer for loops will have to run.

# Second thing to look at:
# How many columns are there for each row in your pattern?
# Look at the types of items that are in each column of each row.

# Third thing to look at:
# Item being printed in each line and column.
print()
# Print the following pattern-4:
#1
#12
#123
#1234
#12345

for i in range(5):
    for j in range(i+1):
        print(f'{1+j}', end='')
    print()
print()
# Print the following, pattern-5:
#*
#**
#***
#****
#*****
#****
#***
#**
#*

n = 1
for i in range(9): # Because there are 9 rows in this pattern

    if i < 5:
        for j in range(i+1):
            print('*', end='')
        print()

    else:
        for j in range(i-n):
            print('*', end='')
        print()
        n += 2

print()
# Pattern 6:
# 6.       *
#         **
#        ***
#       ****
#      *****

# There are 5 rows, so 5 iterations for the outer loop.
# There are 5 columns in each row made up of either spaces or '*'.
# In each successive iteration, a space is replaced by a column.

n = 5
for i in range(n+1):

    #for j in range(i):
    print(f'{" "*n}', end="")
    print(f'{"*"*i}', end="")

    print()
    n -= 1

print()
# Pattern 7
# 7.   *****
#       ****
#        ***
#         **
#          *

n = 5
m = 0
for i in range(n+1):
    print(f'{" "*m}{"*"*n}', end='')
    print()
    n -= 1
    m += 1

print()
# Pattern 8
# 8.      *
#        ***
#       *****
#      *******
#     *********

# Number of rows in this pattern: 5
# Number of columns in this patter: 9
# The pattern is to replace white space with '*' in each row to create a triangle.

col = 9
row = 5
counter_space = 0
counter_ast = 1
#print(col//2) # Need to use floor divide for this pattern

for i in range(row):
    print(f'{(col//2-counter_space)*" "}{(counter_ast)*"*"}{(col//2-counter_space)*" "}', end='')
    counter_space += 1
    counter_ast += 2
    print()

print()
# Pattern 9
# 9.  *********
#      *******
#       *****
#        ***
#         *

p9_col = 9
p9_row = 5
p9_space = 0
p9_asterisk = 9

for i in range(p9_row):
    print(f'{p9_space*" "}{p9_asterisk*"*"}{p9_space*" "}', end='')
    print()
    p9_space += 1
    p9_asterisk -= 2

print()
# Pattern 10
# 10.      *
#         * *
#        * * *
#       * * * *
#      * * * * *

# This samples still contains 10 columns, based on the pattern monomer.
# The pattern is an asterisk with a space - '* '

p10_col = 10 # Including a column of white space at the end if the monomer for pattern is '* '
p10_row = 5
pattern_counter = 1
for i in range(p10_row):
    print(f'{(p10_row-pattern_counter)*" "}{pattern_counter*"* "}{(p10_row-pattern_counter)*" "}', end='')
    print()
    pattern_counter += 1
    #print()

print()
# Pattern 11
# 11.  * * * * *
#       * * * *
#        * * *
#         * *
#          *

p11_col = 10 # Including a column of white space at the end if the monomer for pattern is '* '
p11_row = 5
pattern_counter = 5
for i in range(p11_row):
    print(f'{(p11_row-pattern_counter)*" "}{pattern_counter*"* "}{(p11_row-pattern_counter)*" "}', end='')
    print()
    pattern_counter -= 1

print()
# Pattern 12
# 12.  * * * * *
#       * * * *
#        * * *
#         * *
#          *
#          *
#         * *
#        * * *
#       * * * *
#      * * * * *

# This pattern is a concatenation of the last two patterns we've done.

p12_col = 10
p12_row = 10
p12_counter = 5
for i in range(p12_row):
    if i < p12_row/2:
        print(f'{(int(p12_row/2)-p12_counter)*" "}{p12_counter*"* "}{(int(p12_row/2)-p12_counter)*" "}', end='')
        if p12_counter != 1:
            p12_counter -= 1

    else:
        print(f'{(int(p12_row/2)-p12_counter)*" "}{p12_counter*"* "}{(int(p12_row/2)-p12_counter)*" "}', end='')
        p12_counter += 1

    print()

print()
# Pattern 13
# 13.      *
#         * *
#        *   *
#       *     *
#      *********

# Algorithm to print this pattern -- print spaces unless the index is (no. of columns)/2 +- counter.
# At the +- 1 counter positions, an '*' is printed. The counter increments by 1 at each interation of the for loop.

#print(int(9/2))
p13_col = 9
p13_row = 5
p13_counter = 0

for i in range(p13_row):
    if i == (p13_row-1):
        print(f'{"*"*p13_col}', end='')
        break

    for j in range(p13_col):
        if j == (int(p13_col/2)-p13_counter) or j == (int(p13_col/2)+p13_counter):
            print('*', end='')

        else:
            print(' ', end='')
    p13_counter += 1

    print()

print()

# Patter 14
# 14.  *********
#       *     *
#        *   *
#         * *
#          *

# This pattern is the previous one in reverse.

p14_col = 9
p14_row = 5
p14_counter = 3
middle = int(p14_col/2)
#print(middle)

for i in range(p14_row):
    if i == 0:
        print(f'{"*"*p14_col}', end='')
        print()
        continue

    for j in range(p14_col):
        if j == (middle-p14_counter) or j == (middle+p14_counter):
            print('*', end='')

        else:
            print(' ', end='')
    p14_counter -= 1

    print()

print()

# Pattern 15
# 15.      *
#         * *
#        *   *
#       *     *
#      *       *
#       *     *
#        *   *
#         * *
#          *

p15_col = 9
p15_row = 9
p15_counter = 0
middle = int(p15_col/2)

for i in range(p15_row):
    for j in range(p15_col):
        if j == (middle-p15_counter) or j == (middle+p15_counter):
            print('*', end='')

        else:
            print(' ', end='')

    if i < middle:
        p15_counter += 1

    else:
        p15_counter -= 1

    print()

print()
# Pattern 16
# 16.           1
#             1   1
#           1   2   1
#         1   3   3   1
#       1   4   6   4   1

# This is the Pascal's triangle. Algorithm for getting the numbers:
# Find the number of rows you want to plot in the triangle.
# Number of columns in each row goes up by 1 each time.
# Pascal's rule: (n, k) = (n-1, k-1) + (n-1, k)
# Where n is the row index and k is the column index.

p16_n = 5

pascal_arr = [[1],[1,1]]

for i in range(2, p16_n):
    temp_list = [1]
    for j in range(1, i):

        num = pascal_arr[i-1][j] + pascal_arr[i-1][j-1]
        #print(num)
        temp_list.append(num)

    temp_list.append(1)
    pascal_arr.append(temp_list)

print(pascal_arr)

counter = 1

for arr in pascal_arr:
    print(f'{(p16_n-counter)*" "}', end='')

    for item in arr:
        print(f'{item} ', end='')
    counter += 1

    print()

print()
# Pattern 17
# 17.      1
#         212
#        32123
#       4321234
#        32123
#         212
#          1

# In this instace, lets define a variable n = 4
# No of rows for this pattern is 2n-1
# No of columns is also 2n-1
# The number to print before and after the central '1' determined by the variable n.

p17_n = 4
p17_rows = 2*p17_n - 1
p17_cols = 2*p17_n - 1
p17_middle = int((p17_cols+1)/2)
#print(p17_middle)
p17_counter = 1
p17_num_counter = 1

for i in range(p17_rows):
    temp = int((p17_num_counter+1)/2)

    print(f'{(p17_middle-p17_counter)*" "}', end='')
    #temp = int((p17_num_counter+1)/2)
    for num in range(p17_num_counter):

        print(temp, end='')
        if num >= int((p17_num_counter+1)/2)-1:
            temp += 1

        else:
            temp -= 1

    if i >= p17_n-1:
        p17_counter -= 1
        p17_num_counter -= 2

    else:
        p17_counter += 1
        p17_num_counter += 2

    print()

print()
# Pattern 28
# 28.      *
#         * *
#        * * *
#       * * * *
#      * * * * *
#       * * * *
#        * * *
#         * *
#          *

p28_col = 9
p28_row = 9
p28_middle = int(9/2)
p28_counter = 1

for i in range(p28_row):
    print(f'{(p28_middle-p28_counter+1)*" "}{p28_counter*"* "}', end='')

    if i < p28_middle:
        p28_counter += 1

    else:
        p28_counter -= 1

    print()

print()


from typing import List, Tuple

from flask_wtf import FlaskForm
from wtforms import SelectField, StringField, SubmitField, TextAreaField
from wtforms.validators import DataRequired, Length, ValidationError

class NewCourseForm(FlaskForm):
    """Subclass of :class: `flask_wtf.Flask-Form`.
    Represents form for adding a new `Course`

    :attr title: StringField

    :attr description: TextAreaField

    :attr submit: SubmitField
    """
    title = StringField(
        label="Title",
        validators=[
            DataRequired(),
            Length(max=32, message="Title should be 32 characters max")
            ],
        render_kw={"class": "form-control", "placeholder": "Title"}
        )
    description = TextAreaField(
        label="Description",
        validators=[
            DataRequired(),
            Length(
                max=2000,
                message="Description should be 2000 characters max"
                )
            ],
        render_kw={
            "class": "form-control",
            "placeholder": "Description", "rows": 5}
    )
    topics = StringField(
        label="topics",
        render_kw={
            "class": "form-control",
            "placeholder": "Indicate topics separated by space"
            }
        )
    submit = SubmitField(
        label="Create course and save it to a draft",
        render_kw={"class": "btn btn-primary btn-lg mb-4 w-100"}
        )

    def validate_title_chars(self, title: str) -> ValidationError:
        """Validates `title` by checking whether all characters
        except the whitespace is alphanumeric (letters(a-z) and/or digits(0-9))

        :param title: field of :class:`NewCourseForm` to be validated
        :type title: str

        :raises ValidationError: if `title` contains
        non-alphanumeric characters

        :returns: None
        :rtype: None
        """
        title = title.replace(" ", "")
        if not title.isalnum():
            msg = "Title should contain only letters(a-z) and/or digits(0-9)"
            raise ValidationError(msg)

class FilterByTutorAndTopicForm(FlaskForm):
    """Subclass of :class: `flask_wtf.Flask-Form`.
    Represents form for filtering courses by two fields:
    `tutors` and `topics`.

    :attr filter_by_tutor: SelectField with integer values

    :attr filter_by_topic: SelectField with integer values

    :attr submit: SubmitField
    Represents 'Show' submit button when filtering courses
    """
    filter_by_tutor = SelectField(
        label="Filter by tutor",
        coerce=int
        )
    filter_by_topic = SelectField(
        label="Filter by topic",
        coerce=int
        )
    submit = SubmitField(
        label="Show",
        render_kw={"class": "btn btn-primary btn-sm"}
    )

    @staticmethod
    def _get_all_tutor_choices(tutors: list) -> List[Tuple]:
        """Repacks list with `User()` instances - `tutors` -
        into list of tuples (`tutor.id`, `tutor.username`).

        :param tutors: unique list of Users that are tutors
        :type: list
        :return: list of tuple pairs (tutor.id, tutor.username)
        :rtype: List[Tuple]
        """
        choices = [(0, "all")]
        for tutor in tutors:
            choices.append((tutor.id, tutor.username))
        return choices

    @staticmethod
    def _get_all_topic_choices(topics: list) -> List[Tuple]:
        """Repacks list with `Topic()` instances - `topics` -
        into list of tuples (`topic.id`, `topic.name`).

        :param topics: unique list of Topics
        :type: list
        :return: list of tuple pairs (topic.id, topic.name)
        :rtype: List[Tuple]
        """
        choices = [(0, "all")]
        for topic in topics:
            choices.append((topic.id, topic.name))
        return choices

    def load_choices(self, tutors: list, topics: list) -> None:
        """Loads choices for two fields in a `FilterByTutorAndTopicForm`.

        :param tutors: list of tuples (`tutor.id`, `tutor.username`)
        :type: List[Tuple]

        :param topics: list of tuples (`topic.id`, `topic.name`)
        :type: List[Tuple]

        :return: None
        :rtype: None
        """
        self.filter_by_tutor.choices = self._get_all_tutor_choices(tutors)
        self.filter_by_topic.choices = self._get_all_topic_choices(topics)


import torch
import torch.nn.functional as F
from torch import nn
import torchvision.models as models
from .backboneforafnet import resnet4thermal,resnet4rgb,Bottleneck,Bottleneck2
import numpy as np
resnet4rgb = resnet4rgb(Bottleneck, [3, 4, 6, 3])
resnet4thermal = resnet4thermal(Bottleneck2, [3, 4, 6, 3])

# from .backbone import build_backbone

class afnet(nn.Module):
    """ This is the DETR module that performs object detection """
    def __init__(self, n_class):
        super(afnet, self).__init__()
        """ Initializes the model.

        """

        # self.n_class=n
        self.upsample=nn.Upsample(scale_factor=2,mode='bilinear', align_corners=True)
        self.conv1=nn.Conv2d(in_channels=2048,out_channels=512,kernel_size=3,stride=1,padding=1,dilation=1)
        self.conv2=nn.Conv2d(in_channels=512,out_channels=128,kernel_size=3,stride=1,padding=1,dilation=1)
        self.conv3=nn.Conv2d(in_channels=128,out_channels=n_class,kernel_size=3,stride=1,padding=1,dilation=1)
        #5类

        resnet50=models.resnet50(pretrained=True)
        resnet50forthermal=models.resnet50(pretrained=True)
        pretrained_dict = resnet50.state_dict()
        pretrained_dict2 = resnet50forthermal.state_dict()
        model_dict = resnet4rgb.state_dict()
        model_dict2 = resnet4thermal.state_dict()
        pretrained_dict = {k: v for k, v in pretrained_dict.items() if k in model_dict}
        pretrained_dict2 = {k: v for k, v in pretrained_dict2.items() if k in model_dict2}
        model_dict.update(pretrained_dict)
        model_dict2.update(pretrained_dict2)
        resnet4rgb.load_state_dict(model_dict)
        resnet4thermal.load_state_dict(model_dict2)
        # self.mo
        # resnet4rgb = resnet4rgb(Bottleneck, [3, 4, 6, 3])
        # resnet4thermal = resnet4thermal(Bottleneck, [3, 4, 6, 3])

        self.model4rgb = resnet4rgb
        self.model4thermal=resnet4thermal

    def forward(self, input):
        rgb = input[:, :3]
        thermal = input[:, 3:]

        image=self.model4rgb(rgb)
        thermal=torch.cat((thermal,thermal,thermal),dim=1)
        thermal=self.model4thermal(thermal)
        b, c, h, w = image.size()

        image_mat = image.contiguous().view(image.size(0), image.size(1), -1) #c*t
        thermal_mat = thermal.contiguous().view(thermal.size(0), thermal.size(1), -1)## #c*t
        image_transpose = torch.transpose(image_mat,1,2) #t*c
        thermal_transpose = torch.transpose(thermal_mat, 1, 2)#t*c

        image_similarity = torch.bmm(F.normalize(image_transpose, dim=2), F.normalize(thermal_mat, dim=1))
        thermal_similarity = torch.bmm(F.normalize(thermal_transpose, dim=2), F.normalize(image_mat, dim=1))

        rgb_base_attention = F.softmax(image_similarity,dim=2)
        thermal_base_attention = F.softmax(thermal_similarity,dim=2)

        # rgb_base_enhanced = torch.bmm(thermal_mat, rgb_base_attention.transpose(1,2))
        # thermal_base_enhanced = torch.bmm(image_mat, thermal_base_attention.transpose(1,2))
        rgb_base_enhanced = torch.bmm(thermal_mat, rgb_base_attention.transpose(1, 2))
        thermal_base_enhanced = torch.bmm(image_mat, thermal_base_attention.transpose(1, 2))
        rgb_base_enhanced = rgb_base_enhanced.contiguous().view(b, c, h, w)
        thermal_base_enhanced = thermal_base_enhanced.contiguous().view(b, c, h, w)
        rgb_enhanced = rgb_base_enhanced + image
        thermal_enhanced = thermal_base_enhanced + thermal
        output = rgb_enhanced + thermal_enhanced
        output = self.upsample(output)
        output = self.conv1(output)
        output = self.upsample(output)
        output = self.conv2(output)
        output = self.upsample(output)
        output = self.conv3(output)

        return output


# -*- coding: utf-8 -*-
import asyncio
from functools import wraps

import httpx
from loguru import logger

from .exceptions import RateLimitError

async def paginate(f, *args, **kwargs):
    cursor = ""
    while cursor is not None:
        res = await f(*args, **kwargs, cursor=cursor)
        cursor = res.next
        yield res

async def linear_retry(self, make_coro):
    for i in range(1, 10000):
        coro = make_coro()
        await self._check_rate_limit()
        try:
            res = await coro
            # TODO check for status_codes instead of items

            res = res.json()
            # DEBUG
            # logger.error(res)

            is_throttled = "detail" in res and "Request was throttled" in res["detail"]
            has_failed = "success" in res and not res["success"]
            if is_throttled:
                await asyncio.sleep(i * 0.25)
                continue

            return None if has_failed else res
        except (
            httpx.ConnectError,
            httpx.ConnectTimeout,
            httpx.ReadError,
            httpx.ReadTimeout,
        ):
            retry_in = i * 0.25
            logger.debug(f"Failed request, retry in: {retry_in}")
            await asyncio.sleep(retry_in)

def get(response_model):
    ResponseModel = response_model

    def deco(f):
        @wraps(f)
        async def wrapper(self, *args, **kwargs):
            url_and_maybe_params = f(self, *args, **kwargs)
            # with params
            if len(url_and_maybe_params) == 2:
                url, params = url_and_maybe_params
                params.pop("self")
                params.pop("url")
                params = {k: v for k, v in params.items() if v is not None}
            # without params
            else:
                url, params = url_and_maybe_params, {}

            async with self._sema:
                async with httpx.AsyncClient() as client:
                    res = await linear_retry(
                        self,
                        lambda: client.get(url, params=params, headers=self._headers),
                    )

            if res is None:
                return res

            try:
                return ResponseModel(**res)
            except Exception as e:
                logger.error(e)
                logger.exception(e)
                logger.error(res)
                raise e

        return wrapper

    return deco

# FIXME: broke atm
def post(response_model):
    def deco(f):
        @wraps(f)
        async def wrapper(self, *args, **kwargs):
            url, body = f(self, *args, **kwargs)

            async with self._sema:
                await self._check_rate_limit()
                res = await self.client.post(url, headers=self._headers)

            if "success" in res and not res["success"]:
                return None

            if "Request was throttled" in res:
                raise RateLimitError()

            # res = key(res)
            # return response_model(**res)

            return res

        return wrapper

    return deco


from django.middleware.locale import LocaleMiddleware
from django.utils.translation import activate

from apps.authentication.models import UserProfile
from apps.config.models import SiteConfiguration
from apps.config.views import maintenance_view

class CustomLocaleMiddleware(LocaleMiddleware):
    """
    Override Django LocaleMiddleware in order to read user preferences.
    """

    @staticmethod
    def __userHasLanguagePreference(request):
        if request.user.is_authenticated:
            profile = UserProfile.objects.get_or_create(user=request.user)[0]
            if profile.language:
                return True
        return False

    @staticmethod
    def __activateUserFavoriteLanguage(request):
        activate(request.user.userprofile.language)
        request.LANGUAGE_CODE = request.user.userprofile.language

    def process_request(self, request):
        if self.__userHasLanguagePreference(request):
            self.__activateUserFavoriteLanguage(request)
        else:
            super().process_request(request)

class MaintenanceModeMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        path = request.META.get("PATH_INFO")
        if path not in ("", "/") and not path.startswith("/admin"):
            is_closed = SiteConfiguration.get_solo().close_for_maintenance
            if is_closed:
                return maintenance_view(request)

        response = self.get_response(request)
        return response


#run in python 2 the division in python truncates instead of converts to float
#othwerwise it won't work as written
import networkx as nx
import csv

def read_into_graph(inputfile) :
     with open(inputfile) as f:
        data = [list(map(int,row)) for row in csv.reader(f, delimiter = ',')]

     G = nx.DiGraph()
     for i in range(len(data)):
         G.add_edge(data[i][0],data[i][1],{'u' : data[i][2], 'x' : data[i][3]})
     return G

def find_augmenting_path(G, s, t, phase):
    # returns an augmenting path P and its capacity Delta
    # if there is no augmenting path, Delta is 0 and P contains S*, the s-side of a minimum s-t cut
    marked = set()
    marked.add(s)
    pred = {}
    pred[s] = 0
    LIST = [s]
    while LIST:
        i = LIST[0]
        # scan forward arcs out of i with capacity >= phase
        for j in G.successors(i):
             if j not in marked and j not in LIST and G[i][j]['u'] - G[i][j]['x'] >= phase:
                  pred[j] = i
                  LIST.append(j)

        # scan backward arcs out of i with capacity >= phase
        for j in G.predecessors(i):
             if j not in marked and j not in LIST and G[j][i]['x'] >= phase:
                  pred[j] = i
                  LIST.append(j)

        LIST.pop(0)
        marked.add(i)

    if t not in marked :
        P = marked
        Delta = 0
    else:
        cur = t
        P = [cur]
        Delta = float('inf')
        while P[-1] != s:
            prev = cur
            cur = pred[prev]
            P.append(cur)
            if G.has_edge(cur, prev):
                r = G[cur][prev]['u'] - G[cur][prev]['x']
            else:
                r = G[prev][cur]['x']
            Delta = min(Delta, r)
        P.reverse()

    return [Delta, P]

def max_flow(G,s,t):
    # G is a directed graph with s,t nodes in G. Each arc in G has an attribute G[i][j]['u'] indicating its capacity and may have an attribute G[i][j]['x'] indicating its current flow

    for (i,j) in G.edges():
        try :
            uij = G[i][j]['u']
        except :
            uij = float.inf()
            G[i][j]['u'] = uij
        try :
            xij = G[i][j]['x']
        except :
            xij = 0
            G[i][j]['x'] = xij

    #find the max edge capacity
    U = nx.get_edge_attributes(G,'u')
    U = max(U)
    U = G[U[0]][U[1]]['u']

    #find the initial phase
    phase = U
    while phase != 0:
        Delta, P  = find_augmenting_path(G,s,t, phase)
        if Delta > 0:
          for j in range(0,len(P) - 1):
               if G.has_edge(P[j], P[j+1]):
                    G[P[j]][P[j+1]]['x'] = G[P[j]][P[j+1]]['x'] + Delta

               else:
                    G[P[j+1]][P[j]]['x'] = G[P[j+1]][P[j]]['x']  - Delta
        else:
             phase = phase / 2

    return G, P

#test code
# G = nx.DiGraph()
# G.add_edges_from([(1,2,{'u':8,'x':0}),(1,4,{'u': 9,'x':0}),(2,3,{'u':4,'x':0}),(2,5,{'u':5,'x':0}),(3,6,{'u':6,'x':0}), (4,3,{'u': 3,'x':0}), (4,5,{'u':2,'x':0}),(4,2,{'u':1,'x':0}), (5,3,{'u':2,'x': 0}), (5,6,{'u':10,'x':0}), (6,5,{'u':10,'x':0})])

# print(max_flow(G, 1, 6)[1])
# G[1][4]['u'] = 6
# G[4][5]['u'] = 3

#print(max_flow(G,1,6)[1])


#!/usr/bin/python3
import os
import subprocess

# Reformats files in src and tests folder
# Use to change coding style

BASE_DIR = os.path.dirname(os.path.abspath(__file__)) + '/'
SRC_DIR = BASE_DIR + "../src"
TEST_DIR = BASE_DIR + "../tests"
STYLE_FILE = BASE_DIR + "../.clang-format"

list_of_files = []

def get_files_and_append_to_list(dir, list):
    for root, dirs, files in os.walk(dir):
        for file in files:
            if file.endswith(".hpp") or file.endswith(".cpp"):
                list_of_files.append(os.path.join(root,file))

get_files_and_append_to_list(SRC_DIR, list_of_files)
get_files_and_append_to_list(TEST_DIR, list_of_files)

args = ["clang-format", "-i", "--style=file"]
args.extend(list_of_files)
print
subprocess.call(args, cwd=BASE_DIR)


import time
import warnings
from typing import Callable, Iterable, Optional, Tuple, Union, cast

import torch
from torch import nn, optim
from torch.optim.lbfgs import LBFGS
from torch.optim.lr_scheduler import _LRScheduler as LRScheduler
from torch.optim.optimizer import Optimizer
from torch.utils.data import DataLoader

import pystiche
from pystiche.image import extract_aspect_ratio, extract_image_size
from pystiche.misc import build_deprecation_message
from pystiche.pyramid import ImagePyramid
from pystiche.pyramid.level import PyramidLevel

from .log import (
    OptimLogger,
    default_epoch_header_fn,
    default_image_optim_log_fn,
    default_pyramid_level_header,
    default_transformer_optim_log_fn,
)

def default_image_optimizer(input_image: torch.Tensor) -> LBFGS:
    return LBFGS([input_image.requires_grad_(True)], lr=1.0, max_iter=1)

def default_image_optim_loop(
    input_image: torch.Tensor,
    criterion: nn.Module,
    get_optimizer: Optional[Callable[[torch.Tensor], Optimizer]] = None,
    num_steps: Union[int, Iterable[int]] = 500,
    preprocessor: Optional[nn.Module] = None,
    postprocessor: Optional[nn.Module] = None,
    quiet: bool = False,
    logger: Optional[OptimLogger] = None,
    log_fn: Optional[
        Callable[[int, Union[torch.Tensor, pystiche.LossDict]], None]
    ] = None,
) -> torch.Tensor:
    if get_optimizer is None:
        get_optimizer = default_image_optimizer

    if isinstance(num_steps, int):
        num_steps = range(1, num_steps + 1)

    if logger is None:
        logger = OptimLogger()

    if log_fn is None:
        log_fn = default_image_optim_log_fn(optim_logger=logger)

    if preprocessor:
        with torch.no_grad():
            input_image = preprocessor(input_image)

    optimizer = get_optimizer(input_image)

    for step in num_steps:

        def closure() -> float:
            optimizer.zero_grad()

            loss = criterion(input_image)
            loss.backward()

            if not quiet:
                # See https://github.com/pmeier/pystiche/pull/264#discussion_r430205029
                log_fn(step, loss)  # type: ignore[misc]

            return cast(float, loss.item())

        optimizer.step(closure)

    if postprocessor:
        with torch.no_grad():
            input_image = postprocessor(input_image)

    return input_image.detach()

def default_image_pyramid_optim_loop(
    input_image: torch.Tensor,
    criterion: nn.Module,
    pyramid: ImagePyramid,
    get_optimizer: Optional[Callable[[torch.Tensor], Optimizer]] = None,
    preprocessor: Optional[nn.Module] = None,
    postprocessor: Optional[nn.Module] = None,
    quiet: bool = False,
    logger: Optional[OptimLogger] = None,
    get_pyramid_level_header: Optional[
        Callable[[int, PyramidLevel, Tuple[int, int]], str]
    ] = None,
    log_fn: Optional[
        Callable[[int, Union[torch.Tensor, pystiche.LossDict]], None]
    ] = None,
) -> torch.Tensor:
    aspect_ratio = extract_aspect_ratio(input_image)
    if get_optimizer is None:
        get_optimizer = default_image_optimizer

    if logger is None:
        logger = OptimLogger()

    if get_pyramid_level_header is None:
        get_pyramid_level_header = default_pyramid_level_header

    output_image = input_image
    for num, level in enumerate(pyramid, 1):

        def image_optim_loop(input_image: torch.Tensor) -> torch.Tensor:
            return default_image_optim_loop(
                input_image,
                criterion,
                get_optimizer=get_optimizer,
                num_steps=iter(level),
                preprocessor=preprocessor,
                postprocessor=postprocessor,
                quiet=quiet,
                logger=logger,
                log_fn=log_fn,
            )

        with torch.no_grad():
            input_image = level.resize_image(output_image, aspect_ratio=aspect_ratio)

        if quiet:
            output_image = image_optim_loop(input_image)
        else:
            input_image_size = extract_image_size(input_image)
            header = get_pyramid_level_header(num, level, input_image_size)
            with logger.environment(header):
                output_image = image_optim_loop(input_image)

    return output_image

def default_transformer_optimizer(transformer: nn.Module) -> Optimizer:
    return optim.Adam(transformer.parameters(), lr=1e-3)

def default_transformer_optim_loop(
    image_loader: DataLoader,
    # FIXME: make device optional and extract from transformer if not given
    device: torch.device,
    transformer: nn.Module,
    criterion: nn.Module,
    criterion_update_fn: Callable[[torch.Tensor, nn.Module], None],
    optimizer: Optional[Optimizer] = None,
    get_optimizer: Optional[Callable[[nn.Module], Optimizer]] = None,
    quiet: bool = False,
    logger: Optional[OptimLogger] = None,
    log_fn: Optional[
        Callable[[int, Union[torch.Tensor, pystiche.LossDict], float, float], None]
    ] = None,
) -> nn.Module:
    if get_optimizer is not None:
        msg = build_deprecation_message(
            "The parameter get_optimizer",
            "0.4.0",
            info="You can achieve the same functionality by passing optimizer=get_optimizer(transformer).",
            url="https://github.com/pmeier/pystiche/pull/96",
        )
        warnings.warn(msg)
        optimizer = get_optimizer(transformer)

    if optimizer is None:
        optimizer = default_transformer_optimizer(transformer)

    if logger is None:
        logger = OptimLogger()

    if log_fn is None:
        log_fn = default_transformer_optim_log_fn(logger, len(image_loader))

    loading_time_start = time.time()
    for batch, input_image in enumerate(image_loader, 1):
        input_image = input_image.to(device)

        criterion_update_fn(input_image, criterion)

        loading_time = time.time() - loading_time_start

        def closure() -> float:
            processing_time_start = time.time()

            # See https://github.com/pmeier/pystiche/pull/264#discussion_r430205029
            optimizer.zero_grad()  # type: ignore[union-attr]

            output_image = transformer(input_image)
            loss = criterion(output_image)
            loss.backward()

            processing_time = time.time() - processing_time_start

            if not quiet:
                batch_size = input_image.size()[0]
                image_loading_velocity = batch_size / max(loading_time, 1e-6)
                image_processing_velocity = batch_size / max(processing_time, 1e-6)
                # See https://github.com/pmeier/pystiche/pull/264#discussion_r430205029
                log_fn(batch, loss, image_loading_velocity, image_processing_velocity)  # type: ignore[misc]

            return cast(float, loss.item())

        optimizer.step(closure)
        loading_time_start = time.time()

    return transformer

def default_transformer_epoch_optim_loop(
    image_loader: DataLoader,
    transformer: nn.Module,
    criterion: nn.Module,
    criterion_update_fn: Callable[[torch.Tensor, nn.Module], None],
    epochs: int,
    device: Optional[torch.device] = None,
    optimizer: Optional[Optimizer] = None,
    lr_scheduler: Optional[LRScheduler] = None,
    quiet: bool = False,
    logger: Optional[OptimLogger] = None,
    get_epoch_header: Optional[
        Callable[[int, Optimizer, Optional[LRScheduler]], str]
    ] = None,
    log_fn: Optional[
        Callable[[int, Union[torch.Tensor, pystiche.LossDict], float, float], None]
    ] = None,
) -> nn.Module:
    if device is None:
        device = next(transformer.parameters()).device

    if optimizer is None:
        if lr_scheduler is None:
            optimizer = default_transformer_optimizer(transformer)
        else:
            # Every LRScheduler has optimizer as a valid attribute
            # https://pytorch.org/docs/stable/optim.html#how-to-adjust-learning-rate
            # but this is not reflected in the torch type hints
            optimizer = lr_scheduler.optimizer  # type: ignore[attr-defined]

    if get_epoch_header is None:
        get_epoch_header = default_epoch_header_fn

    def transformer_optim_loop(transformer: nn.Module) -> nn.Module:
        # See https://github.com/pmeier/pystiche/pull/264#discussion_r430205029
        return default_transformer_optim_loop(
            image_loader,
            device,  # type: ignore[arg-type]
            transformer,
            criterion,
            criterion_update_fn,
            optimizer,
            quiet=quiet,
            logger=logger,
            log_fn=log_fn,
        )

    for epoch in range(epochs):
        if quiet:
            transformer = transformer_optim_loop(transformer)
        else:
            header = get_epoch_header(epoch, optimizer, lr_scheduler)
            # See https://github.com/pmeier/pystiche/pull/264#discussion_r430205029
            with logger.environment(header):  # type: ignore[union-attr]
                transformer = transformer_optim_loop(transformer)

        if lr_scheduler is not None:
            lr_scheduler.step()

    return transformer


import logging

import torch
import torch.nn.functional as F

class CTC(torch.nn.Module):
    """CTC module.

    Args:
        odim: dimension of outputs
        encoder_output_size: number of encoder projection units
        dropout_rate: dropout rate (0.0 ~ 1.0)
        ctc_type: builtin or warpctc
        reduce: reduce the CTC loss into a scalar
    """

    def __init__(
        self,
        odim: int,
        encoder_output_size: int,
        dropout_rate: float = 0.0,
        ctc_type: str = "builtin",
        reduce: bool = True,
        ignore_nan_grad: bool = True,
    ):
        super().__init__()
        eprojs = encoder_output_size
        self.dropout_rate = dropout_rate
        self.ctc_lo = torch.nn.Linear(eprojs, odim)
        self.ctc_type = ctc_type
        self.ignore_nan_grad = ignore_nan_grad

        if self.ctc_type == "builtin":
            self.ctc_loss = torch.nn.CTCLoss(reduction="none")
        elif self.ctc_type == "warpctc":
            import warpctc_pytorch as warp_ctc

            if ignore_nan_grad:
                logging.warning("ignore_nan_grad option is not supported for warp_ctc")
            self.ctc_loss = warp_ctc.CTCLoss(size_average=True, reduce=reduce)
        else:
            raise ValueError(
                f'ctc_type must be "builtin" or "warpctc": {self.ctc_type}'
            )

        self.reduce = reduce

    def loss_fn(self, th_pred, th_target, th_ilen, th_olen) -> torch.Tensor:
        if self.ctc_type == "builtin":
            th_pred = th_pred.log_softmax(2)
            loss = self.ctc_loss(th_pred, th_target, th_ilen, th_olen)

            if loss.requires_grad and self.ignore_nan_grad:
                # ctc_grad: (L, B, O)
                ctc_grad = loss.grad_fn(torch.ones_like(loss))
                ctc_grad = ctc_grad.sum([0, 2])
                indices = torch.isfinite(ctc_grad)
                size = indices.long().sum()
                if size == 0:
                    # Return as is
                    logging.warning(
                        "All samples in this mini-batch got nan grad."
                        " Returning nan value instead of CTC loss"
                    )
                elif size != th_pred.size(1):
                    logging.warning(
                        f"{th_pred.size(1) - size}/{th_pred.size(1)}"
                        " samples got nan grad."
                        " These were ignored for CTC loss."
                    )

                    # Create mask for target
                    target_mask = torch.full(
                        [th_target.size(0)],
                        1,
                        dtype=torch.bool,
                        device=th_target.device,
                    )
                    s = 0
                    for ind, le in enumerate(th_olen):
                        if not indices[ind]:
                            target_mask[s : s + le] = 0
                        s += le

                    # Calc loss again using maksed data
                    loss = self.ctc_loss(
                        th_pred[:, indices, :],
                        th_target[target_mask],
                        th_ilen[indices],
                        th_olen[indices],
                    )
            else:
                size = th_pred.size(1)

            if self.reduce:
                # Batch-size average
                loss = loss.sum() / size
            else:
                loss = loss / size
            return loss

        elif self.ctc_type == "warpctc":
            # warpctc only supports float32
            th_pred = th_pred.to(dtype=torch.float32)

            th_target = th_target.cpu().int()
            th_ilen = th_ilen.cpu().int()
            th_olen = th_olen.cpu().int()
            loss = self.ctc_loss(th_pred, th_target, th_ilen, th_olen)
            if self.reduce:
                # NOTE: sum() is needed to keep consistency since warpctc
                # return as tensor w/ shape (1,)
                # but builtin return as tensor w/o shape (scalar).
                loss = loss.sum()
            return loss

        elif self.ctc_type == "gtnctc":
            log_probs = torch.nn.functional.log_softmax(th_pred, dim=2)
            return self.ctc_loss(log_probs, th_target, th_ilen, 0, "none")

        else:
            raise NotImplementedError

    def forward(self, hs_pad, hlens, ys_pad, ys_lens):
        """Calculate CTC loss.

        Args:
            hs_pad: batch of padded hidden state sequences (B, Tmax, D)
            hlens: batch of lengths of hidden state sequences (B)
            ys_pad: batch of padded character id sequence tensor (B, Lmax)
            ys_lens: batch of lengths of character sequence (B)
        """
        # hs_pad: (B, L, NProj) -> ys_hat: (B, L, Nvocab)
        ys_hat = self.ctc_lo(F.dropout(hs_pad, p=self.dropout_rate))

        if self.ctc_type == "gtnctc":
            # gtn expects list form for ys
            ys_true = [y[y != -1] for y in ys_pad]  # parse padded ys
        else:
            # ys_hat: (B, L, D) -> (L, B, D)
            ys_hat = ys_hat.transpose(0, 1)
            # (B, L) -> (BxL,)
            ys_true = torch.cat([ys_pad[i, :l] for i, l in enumerate(ys_lens)])

        loss = self.loss_fn(ys_hat, ys_true, hlens, ys_lens).to(
            device=hs_pad.device, dtype=hs_pad.dtype
        )

        return loss

    def softmax(self, hs_pad):
        """softmax of frame activations

        Args:
            Tensor hs_pad: 3d tensor (B, Tmax, eprojs)
        Returns:
            torch.Tensor: softmax applied 3d tensor (B, Tmax, odim)
        """
        return F.softmax(self.ctc_lo(hs_pad), dim=2)

    def log_softmax(self, hs_pad):
        """log_softmax of frame activations

        Args:
            Tensor hs_pad: 3d tensor (B, Tmax, eprojs)
        Returns:
            torch.Tensor: log softmax applied 3d tensor (B, Tmax, odim)
        """
        return F.log_softmax(self.ctc_lo(hs_pad), dim=2)

    def argmax(self, hs_pad):
        """argmax of frame activations

        Args:
            torch.Tensor hs_pad: 3d tensor (B, Tmax, eprojs)
        Returns:
            torch.Tensor: argmax applied 2d tensor (B, Tmax)
        """
        return torch.argmax(self.ctc_lo(hs_pad), dim=2)