import re
import rfc822
from datetime import datetime
try:
    from hashlib import md5
except ImportError:
    from md5 import new as md5
try:
    set = set
    frozenset = frozenset
except NameError:
    from sets import Set as set, ImmutableSet as frozenset

_accept_re = re.compile(r'([^\s;,]+)(?:[^,]*?;\s*q=(\d*(?:\.\d+)?))?')
_token_chars = frozenset("!#$%&'*+-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
                         '^_`abcdefghijklmnopqrstuvwxyz|~')
_token = '[%s]' % ''.join(_token_chars).replace('-', '\\-')
_cachecontrol_re = re.compile(r'(%s+)(?:=(?:(%s+|".*?")))?' %
                              (_token, _token))
_etag_re = re.compile(r'([Ww]/)?(?:"(.*?)"|(.*?))(?:\s*,\s*|$)')

HTTP_STATUS_CODES = {
    100:    'Continue',
    101:    'Switching Protocols',
    102:    'Processing',
    200:    'OK',
    201:    'Created',
    202:    'Accepted',
    203:    'Non Authoritative Information',
    204:    'No Content',
    205:    'Reset Content',
    206:    'Partial Content',
    207:    'Multi Status',
    226:    'IM Used',              # see RFC 3229
    300:    'Multiple Choices',
    301:    'Moved Permanently',
    302:    'Found',
    303:    'See Other',
    304:    'Not Modified',
    305:    'Use Proxy',
    307:    'Temporary Redirect',
    400:    'Bad Request',
    401:    'Unauthorized',
    402:    'Payment Required',     # unused
    403:    'Forbidden',
    404:    'Not Found',
    405:    'Method Not Allowed',
    406:    'Not Acceptable',
    407:    'Proxy Authentication Required',
    408:    'Request Timeout',
    409:    'Conflict',
    410:    'Gone',
    411:    'Length Required',
    412:    'Precondition Failed',
    413:    'Request Entity Too Large',
    414:    'Request URI Too Long',
    415:    'Unsupported Media Type',
    416:    'Requested Range Not Satisfiable',
    417:    'Expectation Failed',
    422:    'Unprocessable Entity',
    423:    'Locked',
    424:    'Failed Dependency',
    426:    'Upgrade Required',
    449:    'Retry With',           # propritary MS extension
    500:    'Internal Server Error',
    501:    'Not Implemented',
    502:    'Bad Gateway',
    503:    'Service Unavailable',
    504:    'Gateway Timeout',
    505:    'HTTP Version Not Supported',
    507:    'Insufficient Storage',
    510:    'Not Extended'
}

class Accept(list):
    """
    An `Accept` object is just a list subclass for lists of
    ``(value, quality)`` tuples.  It is automatically sorted by quality.
    """

    def __init__(self, values=()):
        if values is None:
            list.__init__(self)
            self.provided = False
        else:
            self.provided = True
            values = [(a, b) for b, a in values]
            values.sort()
            values.reverse()
            list.__init__(self, [(a, b) for b, a in values])

    def __getitem__(self, key):
        """
        Beside index lookup (getting item n) you can also pass it a string to
        get the quality for the item.  If the item is not in the list, the
        returned quality is ``0``.
        """
        if isinstance(key, basestring):
            for value in self:
                if value[0] == key:
                    return value[1]
            return 0
        return list.__getitem__(self, key)

    def __contains__(self, key):
        return self.find(key) > -1

    def __repr__(self):
        return '%s(%s)' % (
            self.__class__.__name__,
            list.__repr__(self)
        )

    def index(self, key):
        """Get the position of en entry or raise `IndexError`."""
        rv = self.find(key)
        if rv < 0:
            raise IndexError(key)
        return key

    def find(self, key):
        """Get the position of an entry or return -1"""
        if isinstance(key, basestring):
            for idx, value in enumerate(self):
                if value[0] == key:
                    return idx
            return -1
        return list.find(self, key)

    def values(self):
        """Return a list of the values, not the qualities."""
        return [x[1] for x in self]

    def itervalues(self):
        """Iterate over all values."""
        for item in self:
            yield item[0]

    def best(self):
        """The best match as value."""
        return self and self[0][0] or None
    best = property(best)

class HeaderSet(object):
    """
    Similar to the `ETags` class this implements a set like structure.
    Unlike `ETags` this is case insensitive and used for vary, allow, and
    content-language headers.

    If not constructed using the `parse_set_header` function the instanciation
    works like this:

    >>> hs = HeaderSet(['foo', 'bar', 'baz'])
    >>> hs
    HeaderSet(['foo', 'bar', 'baz'])
    """

    def __init__(self, headers=None, on_update=None):
        self._headers = list(headers or ())
        self._set = set([x.lower() for x in self._headers])
        self.on_update = on_update

    def add(self, header):
        """Add a new header to the set."""
        self.update((header,))

    def remove(self, header):
        """
        Remove a layer from the set.  This raises an `IndexError` if the
        header is not in the set.
        """
        key = header.lower()
        if key not in self._set:
            raise IndexError(header)
        self._set.remove(key)
        for idx, key in enumerate(self._headers):
            if key.lower() == header:
                del self._headers[idx]
                break
        if self.on_update is not None:
            self.on_update(self)

    def update(self, iterable):
        """Add all the headers from the iterable to the set."""
        inserted_any = False
        for header in iterable:
            key = header.lower()
            if key not in self._set:
                self._headers.append(header)
                self._set.add(key)
                inserted_any = True
        if inserted_any and self.on_update is not None:
            self.on_update(self)

    def discard(self, header):
        """Like remove but ignores errors."""
        try:
            return self.remove(header)
        except IndexError:
            pass

    def find(self, header):
        """Return the index of the header in the set or return -1 if not found."""
        header = header.lower()
        for idx, item in enumerate(self._headers):
            if item.lower() == header:
                return idx
        return -1

    def index(self, header):
        """Return the index of the headerin the set or raise an `IndexError`."""
        rv = self.find(header)
        if rv < 0:
            raise IndexError(header)
        return rv

    def clear(self):
        """Clear the set."""
        self._set.clear()
        del self._headers[:]
        if self.on_update is not None:
            self.on_update(self)

    def as_set(self, preserve_casing=False):
        """
        Return the set as real python set structure.  When calling this all
        the items are converted to lowercase and the ordering is lost.

        If `preserve_casing` is `True` the items in the set returned will
        have the original case like in the `HeaderSet`, otherwise they will
        be lowercase.
        """
        if preserve_casing:
            return set(self._headers)
        return set(self._set)

    def to_header(self):
        """Convert the header set into an HTTP header string."""
        return ', '.join(self._headers)

    def __getitem__(self, idx):
        return self._headers[idx]

    def __delitem__(self, idx):
        rv = self._headers.pop(idx)
        self._set.remove(rv.lower())
        if self.on_update is not None:
            self.on_update(self)

    def __setitem__(self, idx, value):
        old = self._headers[idx]
        self._set.remove(old.lower())
        self._headers[idx] = value
        self._set.add(value.lower())
        if self.on_update is not None:
            self.on_update(self)

    def __contains__(self, header):
        return header.lower() in self._set

    def __len__(self):
        return len(self._set)

    def __iter__(self):
        return iter(self._headers)

    def __nonzero__(self):
        return bool(self._set)

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

    def __repr__(self):
        return '%s(%r)' % (
            self.__class__.__name__,
            self._headers
        )

class CacheControl(dict):
    """
    Subclass of a dict that stores values for a Cache-Control header.  It has
    accesors for all the cache-control directives specified in RFC 2616.  The
    class does not differentiate between request and response directives.

    Because the cache-control directives in the HTTP header use dashes the
    python descriptors use underscores for that.

    To get a header of the `CacheControl` object again you can convert the
    object into a string or call the `to_header()` function.  If you plan
    to subclass it and add your own items have a look at the sourcecode for
    that class.

    The following attributes are exposed:

    `no_cache`, `no_store`, `max_age`, `max_stale`, `min_fresh`,
    `no_transform`, `only_if_cached`, `public`, `private`, `must_revalidate`,
    `proxy_revalidate`, and `s_maxage`
    """

    def cache_property(key, default, type):
        """
        Return a new property object for a cache header.  Useful if you
        want to add support for a cache extension in a subclass.
        """
        return property(lambda x: x._get_cache_value(key, default, type),
                        lambda x, v: x._set_cache_value(key, v, type),
                        'accessor for %r' % key)

    no_cache = cache_property('no-cache', '*', bool)
    no_store = cache_property('no-store', None, bool)
    max_age = cache_property('max-age', -1, int)
    max_stale = cache_property('max-stale', '*', int)
    min_fresh = cache_property('min-fresh', '*', int)
    no_transform = cache_property('no-transform', None, None)
    only_if_cached = cache_property('only-if-cached', None, bool)
    public = cache_property('public', None, bool)
    private = cache_property('private', '*', None)
    must_revalidate = cache_property('must-revalidate', None, bool)
    proxy_revalidate = cache_property('proxy-revalidate', None, bool)
    s_maxage = cache_property('s-maxage', None, None)

    def __init__(self, values=(), on_update=None):
        self.on_update = on_update
        if values is None:
            dict.__init__(self)
            self.provided = False
        else:
            dict.__init__(self, values)
            self.provided = True

    def calls_update(f):
        def oncall(self, *args, **kw):
            rv = f(self, *args, **kw)
            if self.on_update is not None:
                self.on_update(self)
            return rv
        try:
            oncall.__name__ = f.__name__
            oncall.__module__ = f.__module__
            oncall.__doc__ = f.__doc__
        except:
            pass
        return oncall

    __setitem__ = calls_update(dict.__setitem__)
    __delitem__ = calls_update(dict.__delitem__)
    clear = calls_update(dict.clear)
    pop = calls_update(dict.pop)
    popitem = calls_update(dict.popitem)
    setdefault = calls_update(dict.setdefault)
    update = calls_update(dict.update)

    def _get_cache_value(self, key, default, type):
        """Used internally be the accessor properties."""
        if type is bool:
            return key in self
        if key in self:
            value = self[key]
            if value is None:
                return default
            elif type is not None:
                try:
                    value = type(value)
                except ValueError:
                    pass
            return value

    def _set_cache_value(self, key, value, type):
        """Used internally be the accessor properties."""
        if type is bool:
            if value:
                self[key] = None
            else:
                self.pop(key, None)
        else:
            if value is not None:
                self[key] = value
            else:
                self.pop(key, None)
    _set_cache_value = calls_update(_set_cache_value)

    def to_header(self):
        """Convert the stored values into a cache control header."""
        items = []
        for key, value in self.iteritems():
            if value is None:
                items.append(key)
            else:
                value = str(value)
                if not set(value).issubset(_token_chars):
                    value = '"%s"' % value.replace('"', "'")
                items.append('%s=%s' % (key, value))
        return ', '.join(items)

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

    def __repr__(self):
        return '%s(%s)' % (
            self.__class__.__name__,
            dict.__repr__(self)
        )

    # make cache_property a staticmethod so that subclasses of
    # `CacheControl` can use it for new properties.
    cache_property = staticmethod(cache_property)
    del calls_update

class ETags(object):
    """
    A set that can be used to check if one etag is present in a collection
    of etags.
    """

    def __init__(self, strong_etags=None, weak_etags=None, star_tag=False):
        self._strong = frozenset(not star_tag and strong_etags or ())
        self._weak = frozenset(weak_etags or ())
        self.star_tag = star_tag

    def as_set(self, include_weak=False):
        """
        Convert the `ETags` object into a python set.  Per default all the
        weak etags are not part of this set.
        """
        rv = set(self._strong)
        if include_weak:
            rv.update(self._weak)
        return rv

    def is_weak(self, etag):
        """Check if an etag is weak."""
        return etag in self._weak

    def contains_weak(self, etag):
        """Check if an etag is part of the set including weak and strong tags."""
        return self.is_weak(etag) or self.contains(etag)

    def contains(self, etag):
        """Check if an etag is part of the set ignoring weak tags."""
        if self.star_tag:
            return True
        return etag in self._strong

    def contains_raw(self, etag):
        """
        When passed a quoted tag it will check if this tag is part of the set.
        If the tag is weak it is checked against weak and strong tags, otherwise
        weak only.
        """
        etag, weak = unquote_etag(etag)
        if weak:
            return self.contains_weak(etag)
        return self.contains(etag)

    def to_header(self):
        """Convert the etags set into a HTTP header string."""
        if self.star_tag:
            return '*'
        return ', '.join(['"%s"' % item for item in self.as_set(True)])

    def __call__(self, etag=None, data=None, include_weak=False):
        if [etag, data].count(None) != 1:
            raise TypeError('either tag or data required, but at least one')
        if etag is None:
            etag = generate_etag(data)
        if include_weak:
            if etag in self._weak:
                return True
        return etag in self._strong

    def __nonzero__(self):
        return bool(self.star_tag or self._strong)

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

    def __iter__(self):
        return iter(self._strong)

    def __contains__(self, etag):
        return self.contains(etag)

    def __repr__(self):
        return '<%s %r>' % (self.__class__.__name__, str(self))

def parse_accept_header(value):
    """
    Parses an HTTP Accept-* header.  This does not implement a complete valid
    algorithm but one that supports at least value and quality extraction.

    Returns a new `Accept` object (basicly a list of ``(value, quality)``
    tuples sorted by the quality with some additional accessor methods)
    """
    if not value:
        return Accept(None)
    result = []
    for match in _accept_re.finditer(value):
        quality = match.group(2)
        if not quality:
            quality = 1
        else:
            quality = max(min(float(quality), 1), 0)
        result.append((match.group(1), quality))
    return Accept(result)

def parse_cache_control_header(value, on_update=None):
    """
    Parse a cache control header.  The RFC differs between response and
    request cache control, this method does not.  It's your responsibility
    to not use the wrong control statements.
    """
    if not value:
        return CacheControl(None, on_update)
    result = {}
    for match in _cachecontrol_re.finditer(value):
        name, value = match.group(1, 2)
        if value and value[0] == value[-1] == '"':
            value = value[1:-1]
        result[name] = value
    return CacheControl(result, on_update)

def parse_set_header(value, on_update=None):
    """
    Parse a set like header and return a `HeaderSet` object.  The return
    value is an object that treats the items case insensitive and keeps the
    order of the items.
    """
    if not value:
        return HeaderSet(None, on_update)
    return HeaderSet([x.strip() for x in value.split(',')], on_update)

def quote_etag(etag, weak=False):
    """Quote an etag."""
    if '"' in etag:
        raise ValueError('invalid etag')
    etag = '"%s"' % etag
    if weak:
        etag = 'w/' + etag
    return etag

def unquote_etag(etag):
    """Unquote a single etag.  Return a ``(etag, weak)`` tuple."""
    if not etag:
        return None, None
    etag = etag.strip()
    weak = False
    if etag[:2] in ('w/', 'W/'):
        weak = True
        etag = etag[2:]
    if etag[:1] == etag[-1:] == '"':
        etag = etag[1:-1]
    return etag, weak

def parse_etags(value):
    """Parse and etag header.  Returns an `ETags` object."""
    if not value:
        return ETags()
    strong = []
    weak = []
    end = len(value)
    pos = 0
    while pos < end:
        match = _etag_re.match(value, pos)
        if match is None:
            break
        is_weak, quoted, raw = match.groups()
        if raw == '*':
            return ETags(star_tag=True)
        elif quoted:
            raw = quoted
        if is_weak:
            weak.append(raw)
        else:
            strong.append(raw)
        pos = match.end()
    return ETags(strong, weak)

def generate_etag(data):
    """Generate an etag for some data."""
    return md5(data).hexdigest()

def parse_date(value):
    """
    Parse one of the following date formats into a datetime object:

    .. sourcecode:: text

        Sun, 06 Nov 1994 08:49:37 GMT  ; RFC 822, updated by RFC 1123
        Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
        Sun Nov  6 08:49:37 1994       ; ANSI C's asctime() format

    If parsing fails the return value is `None`.
    """
    if value:
        t = rfc822.parsedate_tz(value)
        if t is not None:
            return datetime.utcfromtimestamp(rfc822.mktime_tz(t))

def is_resource_modified(environ, etag=None, data=None, last_modified=None):
    """Convenience method for conditional requests."""
    if etag is None and data is not None:
        etag = generate_etag(data)
    elif data is not None:
        raise TypeError('both data and etag given')
    if environ['REQUEST_METHOD'] not in ('GET', 'HEAD'):
        return False

    unmodified = False
    if isinstance(last_modified, basestring):
        last_modified = parse_date(last_modified)
    modified_since = parse_date(environ.get('HTTP_IF_MODIFIED_SINCE'))

    if modified_since and last_modified and last_modified <= modified_since:
        unmodified = True
    if etag:
        if_none_match = parse_etags(environ.get('HTTP_IF_NONE_MATCH'))
        if if_none_match:
            unmodified = if_none_match.contains_raw(etag)

    return not unmodified


'''
Project         : Global-Reasoned Multi-Task Surgical Scene Understanding
Lab             : MMLAB, National University of Singapore
contributors    : Lalithkumar Seenivasan, Sai Mitheran, Mobarakol Islam, Hongliang Ren
Note            : Code adopted and modified from Visual-Semantic Graph Attention Networks and Dual attention network for scene segmentation
'''

import cv2
import numpy as np
from PIL import Image

import torch
import torchvision
import torch.nn as nn

class mtl_model(nn.Module):
    '''
    Multi-task model : Graph Scene Understanding and segmentation
    Forward uses features from feature_extractor
    '''

    def __init__(self, feature_encoder, scene_graph, seg_gcn_block, seg_decoder, seg_mode = None):
        super(mtl_model, self).__init__()
        self.feature_encoder = feature_encoder
        self.gcn_unit = seg_gcn_block
        self.seg_mode = seg_mode
        self.seg_decoder = seg_decoder
        self.scene_graph = scene_graph
        self.avgpool = nn.AdaptiveAvgPool2d((1, 1))
        self.transform = torchvision.transforms.Compose([torchvision.transforms.ToTensor()])

    def model_type1_insert(self):
        self.sg_avgpool = nn.AdaptiveAvgPool1d(1)
        self.sg_linear = nn.Linear(1040, 128)
        self.sg_feat_s1d1 = nn.Conv1d(1, 1, kernel_size=1, padding=0, stride=1, groups=1, bias=True)

    def model_type2_insert(self):
        self.sg2_linear = nn.Linear(1040, 128)

    def model_type3_insert(self):
        # self.sf_avgpool = nn.AdaptiveAvgPool2d((1, 1))
        self.sf_avgpool = nn.AdaptiveAvgPool1d(1)
        #self.sf_linear = nn.Linear(256, 128)

    def set_train_test(self, model_type):
        ''' train Feature extractor for scene graph '''
        # if model_type == 'stl-s' or model_type == 'amtl-t0' or model_type == 'amtl-t3' or model_type == 'stl-sg':
        if model_type == 'stl-s' or model_type == 'stl-sg' or model_type == 'amtl-t0' or model_type == 'amtl-t3':
            self.train_FE_SG = False
        else:
            self.train_FE_SG = True

        ''' train feature extractor for segmentation '''
        # if model_type == 'stl-sg' or model_type == 'amtl-t0' or model_type == 'amtl-t3':
        if model_type == 'stl-sg' or model_type == 'stl-sg-wfe' or model_type == 'amtl-t0' or model_type == 'amtl-t3':# or model_type == 'amtl-t1':
            self.Train_FE_SEG = False
        else:
            self.Train_FE_SEG = True

        ''' train scene graph'''
        # set train flag for scene graph
        if model_type == 'stl-s':
            self.Train_SG = False
        else:
            self.Train_SG = True

        ''' train segmentation GR-unit (Global-Reasoniing unit) '''
        # if model_type == 'stl-sg' or model_type == 'amtl-t0' or model_type == 'amtl-t3':
        if model_type == 'stl-sg' or model_type == 'stl-sg-wfe' or model_type == 'amtl-t0' or model_type == 'amtl-t3':
            self.Train_SEG_GR = False
        else:
            self.Train_SEG_GR = True

        ''' train segmentation decoder '''
        # set train flag for segmentation decoder
        # if model_type == 'stl-sg' or model_type == 'amtl-t0' or model_type == 'amtl-t3':
        if model_type == 'stl-sg' or model_type == 'stl-sg-wfe' or model_type == 'amtl-t0' or model_type == 'amtl-t3':
            self.Train_SG_DECODER = False
        else:
            self.Train_SG_DECODER = True

        self.model_type = model_type

    def forward(self, img, img_dir, det_boxes_all, node_num, spatial_feat, word2vec, roi_labels, validation=False):

        gsu_node_feat = None
        seg_inputs = None
        interaction = None
        imsize = img.size()[2:]

        # ====================================================== Extract node features for Scene graph ==============================================================
        if not self.train_FE_SG:
            ''' skip training the feature extractor for scene graph '''
            with torch.no_grad():
                for index, img_loc in enumerate(img_dir):
                    _img = Image.open(img_loc).convert('RGB')
                    _img = np.array(_img)
                    img_stack = None
                    for bndbox in det_boxes_all[index]:
                        roi = np.array(bndbox).astype(int)
                        roi_image = _img[roi[1]:roi[3] + 1, roi[0]:roi[2] + 1, :]
                        roi_image = self.transform(cv2.resize(roi_image, (224, 224), interpolation=cv2.INTER_LINEAR))
                        roi_image = torch.autograd.Variable(roi_image.unsqueeze(0))
                        # stack nodes images per image
                        img_stack = roi_image if img_stack == None else torch.cat((img_stack, roi_image))

                    img_stack = img_stack.cuda(non_blocking=True)
                    _, _, _, img_stack = self.feature_encoder(img_stack)

                    img_stack = self.avgpool(img_stack)
                    img_stack = img_stack.view(img_stack.size(0), -1)

                    # # prepare graph node features
                    gsu_node_feat = img_stack if gsu_node_feat == None else torch.cat((gsu_node_feat, img_stack))

        else:
            # print('node_info grad enabled')
            for index, img_loc in enumerate(img_dir):
                _img = Image.open(img_loc).convert('RGB')
                _img = np.array(_img)
                img_stack = None
                for bndbox in det_boxes_all[index]:
                    roi = np.array(bndbox).astype(int)
                    roi_image = _img[roi[1]:roi[3] + 1, roi[0]:roi[2] + 1, :]
                    roi_image = self.transform(cv2.resize(roi_image, (224, 224), interpolation=cv2.INTER_LINEAR))
                    roi_image = torch.autograd.Variable(roi_image.unsqueeze(0))
                    # stack nodes images per image
                    img_stack = roi_image if img_stack == None else torch.cat((img_stack, roi_image))

                img_stack = img_stack.cuda(non_blocking=True)
                _, _, _, img_stack = self.feature_encoder(img_stack)
                img_stack = self.avgpool(img_stack)
                img_stack = img_stack.view(img_stack.size(0), -1)
                # prepare graph node features
                gsu_node_feat = img_stack if gsu_node_feat == None else torch.cat((gsu_node_feat, img_stack))
        # ================================================================================================================================================================
        # ===================================================== Segmentation feature extractor ===========================================================================
        if not self.Train_FE_SEG:
            ''' Skip training feature encoder for segmentation task '''
            with torch.no_grad():
                s1, s2, s3, seg_inputs = self.feature_encoder(img)
                fe_feat = seg_inputs
        else:
            # print('segment encoder enabled')
            s1, s2, s3, seg_inputs = self.feature_encoder(img)
            fe_feat = seg_inputs
        # ================================================================================================================================================================
        # ================================================= Scene graph and segmentation GR (Global Reasoning) unit ======================================================
        if self.model_type == 'amtl-t1' or self.model_type == 'mtl-t1':
            '''
            In type 1, interaction features are passed to segmentation GR (Global Reasoning) module.
            inside GR unit, (x = x + h + avg((x)T) * sg_feat[1x128])
            Here interation is called before GR unit.
            '''
            ''' ==== scene graph ==== '''
            # print('inside mtl-1')
            interaction, sg_feat = self.scene_graph(node_num, gsu_node_feat, spatial_feat, word2vec, roi_labels, validation= validation)

            ''' ==== GR (Global Reasoning) ==== '''
            edge_sum = 0
            batch_sg_feat = None
            for n in node_num:
                active_edges = n-1 if n >1 else n
                if batch_sg_feat == None:
                    batch_sg_feat = self.sg_linear(self.sg_avgpool(sg_feat[edge_sum:edge_sum+active_edges, :].unsqueeze(0).permute(0,2,1)).permute(0,2,1))
                else:
                    batch_sg_feat = torch.cat((batch_sg_feat, self.sg_linear(self.sg_avgpool(sg_feat[edge_sum:edge_sum+active_edges, :].unsqueeze(0).permute(0,2,1)).permute(0,2,1))))
                edge_sum += active_edges
            batch_sg_feat = self.sg_feat_s1d1(batch_sg_feat)
            s1, s2, s3, seg_inputs, _ = self.gcn_unit(seg_inputs, s1=s1, s2=s2, s3=s3, scene_feat = batch_sg_feat, seg_mode = self.seg_mode, model_type = self.model_type)

        elif self.model_type == 'amtl-t2' or self.model_type == 'mtl-t2':
            '''
            In type 2, interaction features are passed to segmentation GR module. Replace
            inside GR, GCN is replaced with x = x * sg_feat [128 x 128]
            Here interation is called before GR unit.
            '''
            ''' ==== scene graph ==== '''
            interaction, sg_feat = self.scene_graph(node_num, gsu_node_feat, spatial_feat, word2vec, roi_labels, validation= validation)

            ''' ==== GR (Global Reasoning) ==== '''
            edge_sum = 0
            batch_sg_feat = None
            for n in node_num:
                active_edges = n-1 if n >1 else n
                if batch_sg_feat == None:
                    batch_sg_feat = torch.matmul(self.sg2_linear(sg_feat[edge_sum:edge_sum+active_edges, :]).permute(1, 0), \
                                        self.sg2_linear(sg_feat[edge_sum:edge_sum+active_edges, :])).unsqueeze(0)
                else:
                    batch_sg_feat = torch.cat((batch_sg_feat, torch.matmul(self.sg2_linear(sg_feat[edge_sum:edge_sum+active_edges, :]).permute(1, 0), \
                                        self.sg2_linear(sg_feat[edge_sum:edge_sum+active_edges, :])).unsqueeze(0)))
                edge_sum += active_edges
            s1, s2, s3, seg_inputs, _ = self.gcn_unit(seg_inputs, s1=s1, s2=s2, s3=s3, scene_feat = batch_sg_feat, seg_mode = self.seg_mode, model_type = self.model_type)

        else:
            '''
            If it's not type 1 & 2, then GR is processed before interaction.
            '''
            ''' ==== GR (Global Reasoning) ==== '''
            if not self.Train_SEG_GR:
                ''' skip GR unit training '''
                with torch.no_grad():
                    s1, s2, s3, seg_inputs, gi_feat = self.gcn_unit(seg_inputs, s1=s1, s2=s2, s3=s3, seg_mode = self.seg_mode, model_type = self.model_type)
            else:
                # print('segment gcn enabled')
                s1, s2, s3, seg_inputs, gi_feat = self.gcn_unit(seg_inputs, s1=s1, s2=s2, s3=s3, seg_mode = self.seg_mode, model_type = self.model_type)

            ''' ==== scene graph ==== '''
            if self.model_type == 'amtl-t3' or self.model_type == 'mtl-t3':
                gr_int_feat = self.sf_avgpool(gi_feat).view(gi_feat.size(0), 128)

                edge_sum = 0
                global_spatial_feat = None

                for b_i, n in enumerate(node_num):
                    active_edges = (n*(n-1)) if n >1 else n
                    if global_spatial_feat == None:
                        global_spatial_feat = torch.cat((spatial_feat[edge_sum:edge_sum+active_edges, :], gr_int_feat[b_i,:].repeat(active_edges,1)),1)
                    else:
                        global_spatial_feat = torch.cat((global_spatial_feat, torch.cat((spatial_feat[edge_sum:edge_sum+active_edges, :], gr_int_feat[b_i,:].repeat(active_edges,1)),1)))
                    edge_sum += active_edges
                interaction, _ = self.scene_graph(node_num, gsu_node_feat, global_spatial_feat, word2vec, roi_labels, validation= validation)
            elif not self.Train_SG:
                ''' skip scene graph training '''
                with torch.no_grad():
                    global_spatial_feat = spatial_feat
                    interaction, _ = self.scene_graph(node_num, gsu_node_feat, global_spatial_feat, word2vec, roi_labels, validation= True)
            else:
                # print('interaction encoder enabled')
                global_spatial_feat = spatial_feat
                interaction, _ = self.scene_graph(node_num, gsu_node_feat, global_spatial_feat, word2vec, roi_labels, validation= validation)

        # ================================================================================================================================================================
        # ================================================= Scene graph and segmentation GR Unit =========================================================================

        ''' ============== Segmentation decoder =============='''
        if not self.Train_SG_DECODER:
            ''' skip segmentation decoder '''
            with torch.no_grad():
                seg_inputs = self.seg_decoder(seg_inputs, s1 = s1, s2 = s2, s3 =s3, imsize =  imsize, seg_mode = self.seg_mode)

        else:
            # print('segment_decoder_enabled')
            seg_inputs = self.seg_decoder(seg_inputs, s1 = s1, s2 = s2, s3 =s3, imsize =  imsize, seg_mode = self.seg_mode)
        # ================================================================================================================================================================

        return interaction, seg_inputs, fe_feat


# Search for restriction enzyme cut sites within a given amino acid sequence
# Input: Amino acid query sequence
# Output: Restriction enzymes and where they cut in the translated DNA sequence
# Dennis R. Goulet
# First upload to Github: 17 May 2022

import itertools

aa_seq = "GARC"

enzymes = {
	"AatII":"GACGTC",
	"AclI":"AACGTT",
	"AfeI":"AGCGCT",
	"AflII":"CTTAAG",
	"AgeI":"ACCGGT",
	"ApaI":"GGGCCC",
	"ApaLI":"GTGCAC",
	"AscI":"GGCGCGCC",
	"AseI":"ATTAAT",
	"AsiSI":"GCGATCGC",
	"AvrII":"CCTAGG",
	"BamHI":"GGATCC",
	"BclI":"TGATCA",
	"BglII":"AGATCT",
	"BsiWI":"CGTACG",
	"BspEI":"TCCGGA",
	"BspHI":"TCATGA",
	"BsrGI":"TGTACA",
	"BssHII":"GCGCGC",
	"BstBI":"TTCGAA",
	"BstZ17I":"GTATAC",
	"ClaI":"ATCGAT",
	"DraI":"TTTAAA",
	"EagI":"CGGCCG",
	"EcoRI":"GAATTC",
	"EcoRV":"GATATC",
	"FseI":"GGCCGGCC",
	"FspI":"TGCGCA",
	"HindIII":"AAGCTT",
	"HpaI":"GTTAAC",
	"KasI":"GGCGCC",
	"KpnI":"GGTACC",
	"MfeI":"CAATTG",
	"MluI":"ACGCGT",
	"MscI":"TGGCCA",
	"NcoI":"CCATGG",
	"NdeI":"CATATG",
	"NheI":"GCTAGC",
	"NotI":"GCGGCCGC",
	"NruI":"TCGCGA",
	"NsiI":"ATGCAT",
	"PacI":"TTAATTAA",
	"PciI":"ACATGT",
	"PmeI":"GTTTAAAC",
	"PmlI":"CACGTG",
	"PsiI":"TTATAA",
	"PstI":"CTGCAG",
	"PvuI":"CGATCG",
	"PvuII":"CAGCTG",
	"SacI":"GAGCTC",
	"SalI":"GTCGAC",
	"SbfI":"CCTGCAGG",
	"ScaI":"AGTACT",
	"SnaBI":"TACGTA",
	"SpeI":"ACTAGT",
	"SphI":"GCATGC",
	"SrfI":"GCCCGGGC",
	"SspI":"AATATT",
	"StuI":"AGGCCT",
	"SwaI":"ATTTAAAT",
	"XbaI":"TCTAGA",
	"XhoI":"CTCGAG",
	"XmaI":"CCCGGG"
}

codon_table = {
'A': ('GCT', 'GCC', 'GCA', 'GCG'),
'C': ('TGT', 'TGC'),
'D': ('GAT', 'GAC'),
'E': ('GAA', 'GAG'),
'F': ('TTT', 'TTC'),
'G': ('GGT', 'GGC', 'GGA', 'GGG'),
'H': ('CAT', 'CAC'),
'I': ('ATT', 'ATC', 'ATA'),
'K': ('AAA', 'AAG'),
'L': ('TTA', 'TTG', 'CTT', 'CTC', 'CTA', 'CTG'),
'M': ('ATG',),
'N': ('AAT', 'AAC'),
'P': ('CCT', 'CCC', 'CCA', 'CCG'),
'Q': ('CAA', 'CAG'),
'R': ('CGT', 'CGC', 'CGA', 'CGG', 'AGA', 'AGG'),
'S': ('TCT', 'TCC', 'TCA', 'TCG', 'AGT', 'AGC'),
'T': ('ACT', 'ACC', 'ACA', 'ACG'),
'V': ('GTT', 'GTC', 'GTA', 'GTG'),
'W': ('TGG',),
'Y': ('TAT', 'TAC'),
}

dna_seq = []
index = 0

for aa in aa_seq:
	for codon in codon_table[aa]:
		dna_seq.append(codon)

dna_list = list(itertools.product(*[list(codon_table[key]) for key in aa_seq]))
dna_list_flat = [''.join(a) for a in dna_list ]

blank_seq = []
for j in range(0,3*len(aa_seq)):
	blank_seq.append('-')
tuple(blank_seq)
blank_str = ''.join(blank_seq)
final_list = []
new_seq_str = ''

for seq in dna_list_flat:
	for enzyme, recog in enzymes.items():
		result = seq.find(recog)
		if result == -1:
			pass
		else:
			new_seq = blank_seq[:]
			for i in range(0,len(seq)):
					if seq[i:i+len(recog)] == recog:
						res = i
						break
			for k in range(res,res+len(recog)):
				new_seq[k] = seq[k]
			new_seq_str = ''.join(new_seq)

			if new_seq_str in final_list:
				pass
			else:
				print(f"The enzyme {enzyme} cuts over '{recog}' in the sequence {new_seq_str}.")
				final_list.append(new_seq_str)
				new_seq_str = ''


#!/usr/bin/python
# -*- coding: utf-8 -*-
#利用DFS的分支定界算法解决背包问题
from collections import namedtuple
from functools import cmp_to_key
#物体类
Item = namedtuple("Item", ['index', 'value', 'weight'])
#自定义比较函数
def mycmp(item1, item2):
    if(item1.value*1.0/item1.weight > item2.value*1.0/item2.weight): #value/weight大的排前边
        return -1
    else:
        return 0
#节点类
class Node:
    def __init__(self, level, curvalue, room, bestvalue, taken, capacity): #成员变量
        self.level = level
        self.curvalue = curvalue
        self.room = room
        self.bestvalue = bestvalue
        self.path = taken
        self.capacity = capacity

    def show(self):
        print(self.level , ",", self.curvalue, ",", self.room, "," , self.bestvalue)
    #所求的bound值
    def bound(self, items):
        weight = 0
        value = 0
        if self.level == -1:
            for i in range(len(items)):
                if weight + items[i].weight <= self.capacity:
                    value += items[i].value
                    weight += items[i].weight
                else:
                    value += (self.capacity - weight) * 1.0 / items[i].weight * items[i].value
                    break
        else:
            value += self.curvalue
            weight += self.capacity - self.room
            for i in range(self.level+1, len(items), 1):
                if weight + items[i].weight <= self.capacity:
                    value += items[i].value
                    weight += items[i].weight
                else:
                    value += (self.capacity - weight) * 1.0 / items[i].weight * items[i].value
                    break
        return value

def solve_it(input_data):
    # Modify this code to run your optimization algorithm

    # parse the input
    lines = input_data.split('\n')

    firstLine = lines[0].split()
    item_count = int(firstLine[0]) #物体数目
    capacity = int(firstLine[1]) #背包容量
    items = []

    for i in range(1, item_count+1):
        line = lines[i]
        parts = line.split()
        items.append(Item(i-1, int(parts[0]), int(parts[1]))) #物体初始化

    value = 0
    weight = 0
    taken = [0]*len(items)
    empty = [0]*len(items)
    #关于python3的自定义比较函数用法
    items.sort(key=cmp_to_key(lambda a, b : mycmp(a,b)))

  #  for item in items:
   #     print (str(item.index) + "," + str(item.value) + "," + str(item.weight))

    stack = [] #深度优先用栈实现，python中list代替
    u = Node(-1, 0, capacity, 0, empty, capacity)
    temp = u.bound(items)
    u.bestvalue = temp
   # print("curvalue:", u.curvalue)
    #print("bound:", u.bestvalue)
    stack.append(u)
    max_profit = 0
    while(len(stack) != 0):
        #弹出末尾的节点
        t = stack.pop()
        v = Node(-1, 0, capacity, 0, empty, capacity)
        if t.level == -1:
            v.level = 0
        if t.level == item_count-1:
            continue
        #not choose this item
        v.level = t.level + 1
        v.room = t.room
        v.curvalue = t.curvalue
        v.bestvalue = v.bound(items)
        v.path = t.path.copy() #注意Python中list为引用，故不能直接赋值，而是用copy()方法
        if v.bestvalue > max_profit:
            stack.append(v)
            if v.level == item_count -1:
                max_profit = v.curvalue #保留最大profit
                taken = v.path #保留最优解

        #choose this item
        v1 = Node(-1, 0, capacity, 0, empty, capacity)
        v1.level = t.level + 1
        v1.room = t.room - items[v1.level].weight
        v1.curvalue = t.curvalue + items[v1.level].value
#        print("curvalue:", v1.curvalue)
        #copy(), 不能直接赋值，因为都是引用
        v1.path = t.path.copy()
        v1.path[items[v1.level].index] = 1
        v1.bestvalue = t.bestvalue
#        print("v1.path:", v1.path)
        if (v1.room >= 0) and (v1.bestvalue > max_profit):
   #         print(taken)
            #满足则加入stack
            stack.append(v1)
            if v1.level == item_count-1:
                max_profit = v1.curvalue #保留最大profit
                taken = v1.path #保留最优解集
              #  print(taken)
    value = max_profit

    #prepare the solution in the specified output format
    output_data = str(value) + ' ' + str(0) + '\n'
    output_data += ' '.join(map(str, taken))
    return output_data

if __name__ == '__main__':
    import sys
    if len(sys.argv) > 1:
        file_location = sys.argv[1].strip()
        with open(file_location, 'r') as input_data_file:
            input_data = input_data_file.read()
        print(solve_it(input_data))
    else:
        print('This test requires an input file.  Please select one from the data directory. (i.e. python solver.py ./data/ks_4_0)')


# -*- coding: utf-8 -*-
"""
Created on Sat Jun  6 12:40:32 2020

@author: Ankita
"""

import numpy as np
import itertools as itr

arr=np.array(['A','B','C','D'])
arr1= list(itr.permutations(arr,2))
for i in arr1:
    print(i)

arr2= list( itr.combinations(arr, 2))
for i in arr2:
    print(i)

print(len(arr1))
print(len(arr2))


import multiprocessing
import os
import random

from sklearn.grid_search import ParameterGrid

from rec_si.train_eval_save import train_eval_save
from rec_si.utils import merge_dicts, easy_parallize
from train_mfnn import train_mfnn

def local_train_mfnn(args):
    config, q = args
    train_eval_save(config, train_mfnn)

    if q is not None:
        q.put(1)

if __name__ == '__main__':

    # make local dir the working dir, st paths are working
    abspath = os.path.abspath(__file__)
    dname = os.path.dirname(abspath)
    os.chdir(dname)

    train_in_parallel = False
    grid_search = False
    nb_random_samples = 1

    theano = True

    params = {
        'lr': [0.01, 0.03],
        'lr_decay': [2e-2],
        'reg_lambda': [0.01],
        'nb_latent_f': [96, 128],
        'binarize': [True],
        'use_avg_rating': [False],
        'zero_sample_factor': [5],
        'd2v_model': ['doc2vec-models/2016-04-14_17.36.08_20e_pv-dbow_size50_lr0.025_window8_neg5']
    }

    if theano:
        params['user_pref_lr'] = [0.003, 0.01, 0.03]
        params['user_pref_lr_decay'] = [2e-2]
        params['user_pref_reg_lambda'] = [0.003, 0.01]
        params['user_pref_hidden_dim'] = [[4], [10], [100]]

    else:
        params['nn_reg_lambda'] = [0.003, 0.01]
        params['nb_hidden_neurons'] = [4, 10, 100]

    param_comb = list(ParameterGrid(params))

    if not grid_search and nb_random_samples < len(param_comb): # random search
        param_comb = random.sample(param_comb, nb_random_samples)

    experiment_name = 'mfnn_ml-100k_e{}_tt-0.7_task-{}'

    config = {}
    config['nb_epochs'] = 20
    config['ratings_path'] = 'data/splits/ml-100k/ratings.csv'
    config['sparse_item'] = True
    config['train_test_split'] = 0.7
    config['train_path'] = 'data/splits/ml-100k/sparse-item/0.7-train.csv'
    config['test_path'] = 'data/splits/ml-100k/sparse-item/0.7-test.csv'
    config['test'] = True
    config['val'] = False
    if config['val']:
        config['train_val_split'] = 0.8
        config['val_path'] = 'data/splits/ml-100k/sparse-item/0.7-0.8-val.csv'

    config['adagrad'] = True
    if config['adagrad']:
        config['ada_eps'] = 1e-6

    config['model_save_dir'] = 'models/mfnn'
    config['metrics_save_dir'] = 'metrics/mfnn'

    config['user_pref_input_movie_d2v'] = True

    config['run_eval'] = True
    config['precision_recall_at_n'] = 20
    config['verbose'] = 1
    config['hit_threshold'] = 4
    config['top_n_predictions'] = 100
    config['run_movie_metrics'] = True
    config['eval_in_parallel'] = False

    all_configs = []
    for i, p in enumerate(param_comb):
        curr_config = merge_dicts(config, p)

        if curr_config['binarize']:
            curr_config['binarize_threshold'] = 1
            curr_config['binarize_pos'] = 1
            curr_config['binarize_neg'] = 0

        curr_config['experiment_name'] = experiment_name.format(curr_config['nb_epochs'], i)

        all_configs.append(curr_config)

    if train_in_parallel:
        nb_concurrent_jobs = 2  # multiprocessing.cpu_count()
        easy_parallize(local_train_mfnn, all_configs, p=nb_concurrent_jobs)
    else:
        for c in all_configs:
            c['eval_in_parallel'] = True
            c['pool_size'] = multiprocessing.cpu_count()
            train_eval_save(c, train_mfnn)