# SVG Path specification parser

import re
from . import path
import xml.etree.ElementTree as ET
import re
import math

COMMANDS = set('MmZzLlHhVvCcSsQqTtAa')
UPPERCASE = set('MZLHVCSQTA')

COMMAND_RE = re.compile("([MmZzLlHhVvCcSsQqTtAa])")
FLOAT_RE = re.compile("[-+]?[0-9]*\.?[0-9]+(?:[eE][-+]?[0-9]+)?")

SVG_COLORS = {
"aliceblue": (0.941176,0.972549,1),
"antiquewhite": (0.980392,0.921569,0.843137),
"aqua": (0,1,1),
"aquamarine": (0.498039,1,0.831373),
"azure": (0.941176,1,1),
"beige": (0.960784,0.960784,0.862745),
"bisque": (1,0.894118,0.768627),
"black": (0,0,0),
"blanchedalmond": (1,0.921569,0.803922),
"blue": (0,0,1),
"blueviolet": (0.541176,0.168627,0.886275),
"brown": (0.647059,0.164706,0.164706),
"burlywood": (0.870588,0.721569,0.529412),
"cadetblue": (0.372549,0.619608,0.627451),
"chartreuse": (0.498039,1,0),
"chocolate": (0.823529,0.411765,0.117647),
"coral": (1,0.498039,0.313725),
"cornflowerblue": (0.392157,0.584314,0.929412),
"cornsilk": (1,0.972549,0.862745),
"crimson": (0.862745,0.0784314,0.235294),
"cyan": (0,1,1),
"darkblue": (0,0,0.545098),
"darkcyan": (0,0.545098,0.545098),
"darkgoldenrod": (0.721569,0.52549,0.0431373),
"darkgray": (0.662745,0.662745,0.662745),
"darkgreen": (0,0.392157,0),
"darkgrey": (0.662745,0.662745,0.662745),
"darkkhaki": (0.741176,0.717647,0.419608),
"darkmagenta": (0.545098,0,0.545098),
"darkolivegreen": (0.333333,0.419608,0.184314),
"darkorange": (1,0.54902,0),
"darkorchid": (0.6,0.196078,0.8),
"darkred": (0.545098,0,0),
"darksalmon": (0.913725,0.588235,0.478431),
"darkseagreen": (0.560784,0.737255,0.560784),
"darkslateblue": (0.282353,0.239216,0.545098),
"darkslategray": (0.184314,0.309804,0.309804),
"darkslategrey": (0.184314,0.309804,0.309804),
"darkturquoise": (0,0.807843,0.819608),
"darkviolet": (0.580392,0,0.827451),
"deeppink": (1,0.0784314,0.576471),
"deepskyblue": (0,0.74902,1),
"dimgray": (0.411765,0.411765,0.411765),
"dimgrey": (0.411765,0.411765,0.411765),
"dodgerblue": (0.117647,0.564706,1),
"firebrick": (0.698039,0.133333,0.133333),
"floralwhite": (1,0.980392,0.941176),
"forestgreen": (0.133333,0.545098,0.133333),
"fuchsia": (1,0,1),
"gainsboro": (0.862745,0.862745,0.862745),
"ghostwhite": (0.972549,0.972549,1),
"gold": (1,0.843137,0),
"goldenrod": (0.854902,0.647059,0.12549),
"gray": (0.501961,0.501961,0.501961),
"grey": (0.501961,0.501961,0.501961),
"green": (0,0.501961,0),
"greenyellow": (0.678431,1,0.184314),
"honeydew": (0.941176,1,0.941176),
"hotpink": (1,0.411765,0.705882),
"indianred": (0.803922,0.360784,0.360784),
"indigo": (0.294118,0,0.509804),
"ivory": (1,1,0.941176),
"khaki": (0.941176,0.901961,0.54902),
"lavender": (0.901961,0.901961,0.980392),
"lavenderblush": (1,0.941176,0.960784),
"lawngreen": (0.486275,0.988235,0),
"lemonchiffon": (1,0.980392,0.803922),
"lightblue": (0.678431,0.847059,0.901961),
"lightcoral": (0.941176,0.501961,0.501961),
"lightcyan": (0.878431,1,1),
"lightgoldenrodyellow": (0.980392,0.980392,0.823529),
"lightgray": (0.827451,0.827451,0.827451),
"lightgreen": (0.564706,0.933333,0.564706),
"lightgrey": (0.827451,0.827451,0.827451),
"lightpink": (1,0.713725,0.756863),
"lightsalmon": (1,0.627451,0.478431),
"lightseagreen": (0.12549,0.698039,0.666667),
"lightskyblue": (0.529412,0.807843,0.980392),
"lightslategray": (0.466667,0.533333,0.6),
"lightslategrey": (0.466667,0.533333,0.6),
"lightsteelblue": (0.690196,0.768627,0.870588),
"lightyellow": (1,1,0.878431),
"lime": (0,1,0),
"limegreen": (0.196078,0.803922,0.196078),
"linen": (0.980392,0.941176,0.901961),
"magenta": (1,0,1),
"maroon": (0.501961,0,0),
"mediumaquamarine": (0.4,0.803922,0.666667),
"mediumblue": (0,0,0.803922),
"mediumorchid": (0.729412,0.333333,0.827451),
"mediumpurple": (0.576471,0.439216,0.858824),
"mediumseagreen": (0.235294,0.701961,0.443137),
"mediumslateblue": (0.482353,0.407843,0.933333),
"mediumspringgreen": (0,0.980392,0.603922),
"mediumturquoise": (0.282353,0.819608,0.8),
"mediumvioletred": (0.780392,0.0823529,0.521569),
"midnightblue": (0.0980392,0.0980392,0.439216),
"mintcream": (0.960784,1,0.980392),
"mistyrose": (1,0.894118,0.882353),
"moccasin": (1,0.894118,0.709804),
"navajowhite": (1,0.870588,0.678431),
"navy": (0,0,0.501961),
"oldlace": (0.992157,0.960784,0.901961),
"olive": (0.501961,0.501961,0),
"olivedrab": (0.419608,0.556863,0.137255),
"orange": (1,0.647059,0),
"orangered": (1,0.270588,0),
"orchid": (0.854902,0.439216,0.839216),
"palegoldenrod": (0.933333,0.909804,0.666667),
"palegreen": (0.596078,0.984314,0.596078),
"paleturquoise": (0.686275,0.933333,0.933333),
"palevioletred": (0.858824,0.439216,0.576471),
"papayawhip": (1,0.937255,0.835294),
"peachpuff": (1,0.854902,0.72549),
"peru": (0.803922,0.521569,0.247059),
"pink": (1,0.752941,0.796078),
"plum": (0.866667,0.627451,0.866667),
"powderblue": (0.690196,0.878431,0.901961),
"purple": (0.501961,0,0.501961),
"red": (1,0,0),
"rosybrown": (0.737255,0.560784,0.560784),
"royalblue": (0.254902,0.411765,0.882353),
"saddlebrown": (0.545098,0.270588,0.0745098),
"salmon": (0.980392,0.501961,0.447059),
"sandybrown": (0.956863,0.643137,0.376471),
"seagreen": (0.180392,0.545098,0.341176),
"seashell": (1,0.960784,0.933333),
"sienna": (0.627451,0.321569,0.176471),
"silver": (0.752941,0.752941,0.752941),
"skyblue": (0.529412,0.807843,0.921569),
"slateblue": (0.415686,0.352941,0.803922),
"slategray": (0.439216,0.501961,0.564706),
"slategrey": (0.439216,0.501961,0.564706),
"snow": (1,0.980392,0.980392),
"springgreen": (0,1,0.498039),
"steelblue": (0.27451,0.509804,0.705882),
"tan": (0.823529,0.705882,0.54902),
"teal": (0,0.501961,0.501961),
"thistle": (0.847059,0.74902,0.847059),
"tomato": (1,0.388235,0.278431),
"turquoise": (0.25098,0.878431,0.815686),
"violet": (0.933333,0.509804,0.933333),
"wheat": (0.960784,0.870588,0.701961),
"white": (1,1,1),
"whitesmoke": (0.960784,0.960784,0.960784),
"yellow": (1,1,0),
"yellowgreen": (0.603922,0.803922,0.196078),
}

def _tokenize_path(pathdef):
    for x in COMMAND_RE.split(pathdef):
        if x in COMMANDS:
            yield x
        for token in FLOAT_RE.findall(x):
            yield token

def applyMatrix(matrix, z):
    return complex(z.real * matrix[0] + z.imag * matrix[1] + matrix[2],
             z.real * matrix[3] + z.imag * matrix[4] + matrix[5] )

def matrixMultiply(matrix1, matrix2):
    if matrix1 is None:
        return matrix2
    elif matrix2 is None:
        return matrix1

    m1 = [matrix1[0:3], matrix1[3:6] ] # don't need last row
    m2 = [matrix2[0:3], matrix2[3:6], [0,0,1]]

    out = []

    for i in range(2):
        for j in range(3):
            out.append( sum(m1[i][k]*m2[k][j] for k in range(3)) )

    return out

def parse_path(pathdef, current_pos=0j, matrix = None, svgState=None):
    if matrix is None:
        scaler=lambda z : z
    else:
        scaler=lambda z : applyMatrix(matrix, z)
    if svgState is None:
        svgState = path.SVGState()

    # In the SVG specs, initial movetos are absolute, even if
    # specified as 'm'. This is the default behavior here as well.
    # But if you pass in a current_pos variable, the initial moveto
    # will be relative to that current_pos. This is useful.
    elements = list(_tokenize_path(pathdef))
    # Reverse for easy use of .pop()
    elements.reverse()

    segments = path.Path(svgState = svgState)
    start_pos = None
    command = None

    while elements:

        if elements[-1] in COMMANDS:
            # New command.
            last_command = command  # Used by S and T
            command = elements.pop()
            absolute = command in UPPERCASE
            command = command.upper()
        else:
            # If this element starts with numbers, it is an implicit command
            # and we don't change the command. Check that it's allowed:
            if command is None:
                raise ValueError("Unallowed implicit command in %s, position %s" % (
                    pathdef, len(pathdef.split()) - len(elements)))
            last_command = command  # Used by S and T

        if command == 'M':
            # Moveto command.
            x = elements.pop()
            y = elements.pop()
            pos = float(x) + float(y) * 1j
            if absolute:
                current_pos = pos
            else:
                current_pos += pos

            # when M is called, reset start_pos
            # This behavior of Z is defined in svg spec:
            # http://www.w3.org/TR/SVG/paths.html#PathDataClosePathCommand
            start_pos = current_pos

            # Implicit moveto commands are treated as lineto commands.
            # So we set command to lineto here, in case there are
            # further implicit commands after this moveto.
            command = 'L'

        elif command == 'Z':
            # Close path
            if current_pos != start_pos:
                segments.append(path.Line(scaler(current_pos), scaler(start_pos)))
            if len(segments):
                segments.closed = True
            current_pos = start_pos
            start_pos = None
            command = None  # You can't have implicit commands after closing.

        elif command == 'L':
            x = elements.pop()
            y = elements.pop()
            pos = float(x) + float(y) * 1j
            if not absolute:
                pos += current_pos
            segments.append(path.Line(scaler(current_pos), scaler(pos)))
            current_pos = pos

        elif command == 'H':
            x = elements.pop()
            pos = float(x) + current_pos.imag * 1j
            if not absolute:
                pos += current_pos.real
            segments.append(path.Line(scaler(current_pos), scaler(pos)))
            current_pos = pos

        elif command == 'V':
            y = elements.pop()
            pos = current_pos.real + float(y) * 1j
            if not absolute:
                pos += current_pos.imag * 1j
            segments.append(path.Line(scaler(current_pos), scaler(pos)))
            current_pos = pos

        elif command == 'C':
            control1 = float(elements.pop()) + float(elements.pop()) * 1j
            control2 = float(elements.pop()) + float(elements.pop()) * 1j
            end = float(elements.pop()) + float(elements.pop()) * 1j

            if not absolute:
                control1 += current_pos
                control2 += current_pos
                end += current_pos

            segments.append(path.CubicBezier(scaler(current_pos), scaler(control1), scaler(control2), scaler(end)))
            current_pos = end

        elif command == 'S':
            # Smooth curve. First control point is the "reflection" of
            # the second control point in the previous path.

            if last_command not in 'CS':
                # If there is no previous command or if the previous command
                # was not an C, c, S or s, assume the first control point is
                # coincident with the current point.
                control1 = scaler(current_pos)
            else:
                # The first control point is assumed to be the reflection of
                # the second control point on the previous command relative
                # to the current point.
                control1 = 2 * scaler(current_pos) - segments[-1].control2

            control2 = float(elements.pop()) + float(elements.pop()) * 1j
            end = float(elements.pop()) + float(elements.pop()) * 1j

            if not absolute:
                control2 += current_pos
                end += current_pos

            segments.append(path.CubicBezier(scaler(current_pos), control1, scaler(control2), scaler(end)))
            current_pos = end

        elif command == 'Q':
            control = float(elements.pop()) + float(elements.pop()) * 1j
            end = float(elements.pop()) + float(elements.pop()) * 1j

            if not absolute:
                control += current_pos
                end += current_pos

            segments.append(path.QuadraticBezier(scaler(current_pos), scaler(control), scaler(end)))
            current_pos = end

        elif command == 'T':
            # Smooth curve. Control point is the "reflection" of
            # the second control point in the previous path.

            if last_command not in 'QT':
                # If there is no previous command or if the previous command
                # was not an Q, q, T or t, assume the first control point is
                # coincident with the current point.
                control = scaler(current_pos)
            else:
                # The control point is assumed to be the reflection of
                # the control point on the previous command relative
                # to the current point.
                control = 2 * scaler(current_pos) - segments[-1].control

            end = float(elements.pop()) + float(elements.pop()) * 1j

            if not absolute:
                end += current_pos

            segments.append(path.QuadraticBezier(scaler(current_pos), control, scaler(end)))
            current_pos = end

        elif command == 'A':
            radius = float(elements.pop()) + float(elements.pop()) * 1j
            rotation = float(elements.pop())
            arc = float(elements.pop())
            sweep = float(elements.pop())
            end = float(elements.pop()) + float(elements.pop()) * 1j

            if not absolute:
                end += current_pos

            segments.append(path.Arc(current_pos, radius, rotation, arc, sweep, end, scaler))
            current_pos = end

    return segments

def path_from_ellipse(x, y, rx, ry, matrix, state):
    arc = "M %.9f %.9f " % (x-rx,y)
    arc += "A %.9f %.9f 0 0 1 %.9f %.9f " % (rx, ry, x+rx,y)
    arc += "A %.9f %.9f 0 0 1 %.9f %.9f" % (rx, ry, x-rx,y)
    return parse_path(arc, matrix=matrix, svgState=state)

def path_from_rect(x,y,w,h,rx,ry, matrix,state):
    if not rx and not ry:
        rect = "M %.9f %.9f h %.9f v %.9f h %.9f Z" % (x,y,w,h,-w)
    else:
        if rx is None:
            rx = ry
        elif ry is None:
            ry = rx
        rect = "M %.9f %.9f h %.9f " % (x+rx,y,w-2*rx)
        rect += "a %.9f %.9f 0 0 1 %.9f %.9f " % (rx, ry, rx, ry)
        rect += "v %.9f " % (h-2*ry)
        rect += "a %.9f %.9f 0 0 1 %.9f %.9f " % (rx, ry, -rx, ry)
        rect += "h %.9f " % -(w-2*rx)
        rect += "a %.9f %.9f 0 0 1 %.9f %.9f " % (rx, ry, -rx, -ry)
        rect += "v %.9f " % -(h-2*ry)
        rect += "a %.9f %.9f 0 0 1 %.9f %.9f Z" % (rx, ry, rx, -ry)
    return parse_path(rect, matrix=matrix, svgState=state)

def sizeFromString(text):
    """
    Returns size in mm, if possible.
    """
    text = re.sub(r'\s',r'', text)
    try:
        return float(text)*25.4/96 # px
    except:
        if text[-1] == '%':
            return float(text[:-1]) # NOT mm
        units = text[-2:].lower()
        x = float(text[:-2])
        convert = { 'mm':1, 'cm':10, 'in':25.4, 'px':25.4/96, 'pt':25.4/72, 'pc':12*25.4/72 }
        try:
            return x * convert[units]
        except:
            return x # NOT mm

def rgbFromColor(colorName):
    colorName = colorName.strip().lower()
    if colorName == 'none':
        return None
    cmd = re.split(r'[\s(),]+', colorName)
    if cmd[0] == 'rgb':
        colors = cmd[1:4]
        outColor = []
        for c in colors:
            if c.endswith('%'):
                outColor.append(float(c[:-1]) / 100.)
            else:
                outColor.append(float(c) / 255.)
        return tuple(outColor)
    elif colorName.startswith('#'):
        if len(colorName) == 4:
            return (int(colorName[1],16)/15., int(colorName[2],16)/15., int(colorName[3],16)/15.)
        else:
            return (int(colorName[1:3],16)/255., int(colorName[3:5],16)/255., int(colorName[5:7],16)/255.)
    else:
        return SVG_COLORS[colorName]

def getPathsFromSVG(svg):
    def updateStateCommand(state,cmd,arg):
        if cmd == 'fill':
            state.fill = rgbFromColor(arg)
        elif cmd == 'fill-opacity':
            state.fillOpacity = float(arg)
        elif cmd == 'fill-rule':
            state.fillRule = arg
#            if state.fill is None:
#                state.fill = (0.,0.,0.)
        elif cmd == 'stroke':
            state.stroke = rgbFromColor(arg)
        elif cmd == 'stroke-opacity':
            state.strokeOpacity = rgbFromColor(arg)
        elif cmd == 'stroke-width':
            state.strokeWidth = float(arg)
        elif cmd == 'vector-effect':
            state.strokeWidthScaling = 'non-scaling-stroke' not in cmd
            # todo better scaling for non-uniform cases?

    def updateState(tree,state,matrix):
        state = state.clone()
        try:
            style = re.sub(r'\s',r'', tree.attrib['style']).lower()
            for item in style.split(';'):
                cmd,arg = item.split(':')[:2]
                updateStateCommand(state,cmd,arg)
        except:
            pass

        for item in tree.attrib:
            try:
                updateStateCommand(state,item,tree.attrib[item])
            except:
                pass

        if state.strokeWidth and state.strokeWidthScaling:
            # this won't work great for non-uniform scaling
            h = abs(applyMatrix(matrix, complex(0,state.strokeWidth)) - applyMatrix(matrix, 0j))
            w = abs(applyMatrix(matrix, complex(state.strokeWidth,0)) - applyMatrix(matrix, 0j))
            state.strokeWidth = (h+w)/2
        return state

    def reorder(a,b,c,d,e,f):
        return [a,c,e, b,d,f]

    def updateMatrix(tree, matrix):
        try:
            transformList = re.split(r'\)[\s,]+', tree.attrib['transform'].strip().lower())
        except KeyError:
            return matrix

        for transform in transformList:
            cmd = re.split(r'[,()\s]+', transform)

            updateMatrix = None

            if cmd[0] == 'matrix':
                updateMatrix = reorder(*list(map(float, cmd[1:7])))
            elif cmd[0] == 'translate':
                x = float(cmd[1])
                if len(cmd) >= 3 and cmd[2] != '':
                    y = float(cmd[2])
                else:
                    y = 0
                updateMatrix = reorder(1,0,0,1,x,y)
            elif cmd[0] == 'scale':
                x = float(cmd[1])
                if len(cmd) >= 3 and cmd[2] != '':
                    y = float(cmd[2])
                else:
                    y = x
                updateMatrix = reorder(x,0,0, y,0,0)
            elif cmd[0] == 'rotate':
                theta = float(cmd[1]) * math.pi / 180.
                c = math.cos(theta)
                s = math.sin(theta)
                updateMatrix = [c, -s, 0,  s, c, 0]
                if len(cmd) >= 4 and cmd[2] != '':
                    x = float(cmd[2])
                    y = float(cmd[3])
                    updateMatrix = matrixMultiply(updateMatrix, [1,0,-x, 0,1,-y])
                    updateMatrix = matrixMultiply([1,0,x, 0,1,y], updateMatrix)
            elif cmd[0] == 'skewX':
                theta = float(cmd[1]) * math.pi / 180.
                updateMatrix = [1, math.tan(theta), 0,  0,1,0]
            elif cmd[0] == 'skewY':
                theta = float(cmd[1]) * math.pi / 180.
                updateMatrix = [1,0,0, math.tan(theta),1,0]

            matrix = matrixMultiply(matrix, updateMatrix)

        return matrix

    def updateStateAndMatrix(tree,state,matrix):
        matrix = updateMatrix(tree,matrix)
        return updateState(tree,state,matrix),matrix

    def getPaths(paths, matrix, tree, state, savedElements):
        def getFloat(attribute,default=0.):
            try:
                return float(tree.attrib[attribute].strip())
            except KeyError:
                return default

        tag = re.sub(r'.*}', '', tree.tag).lower()
        try:
            savedElements[tree.attrib['id']] = tree
        except KeyError:
            pass

        state, matrix = updateStateAndMatrix(tree, state, matrix)
        if tag == 'path':
            path = parse_path(tree.attrib['d'], matrix=matrix, svgState=state)
            if len(path):
                paths.append(path)
        elif tag == 'circle':
            path = path_from_ellipse(getFloat('cx'), getFloat('cy'), getFloat('r'), getFloat('r'), matrix, state)
            paths.append(path)
        elif tag == 'ellipse':
            path = path_from_ellipse(getFloat('cx'), getFloat('cy'), getFloat('rx'), getFloat('ry'), matrix, state)
            paths.append(path)
        elif tag == 'line':
            x1 = getFloat('x1')
            y1 = getFloat('y1')
            x2 = getFloat('x2')
            y2 = getFloat('y2')
            p = 'M %.9f %.9f L %.9f %.9f' % (x1,y1,x2,y2)
            path = parse_path(p, matrix=matrix, svgState=state)
            paths.append(path)
        elif tag == 'polygon':
            points = re.split(r'[\s,]+', tree.attrib['points'].strip())
            p = ' '.join(['M', points[0], points[1], 'L'] + points[2:] + ['Z'])
            path = parse_path(p, matrix=matrix, svgState=state)
            paths.append(path)
        elif tag == 'polyline':
            points = re.split(r'[\s,]+', tree.attrib['points'].strip())
            p = ' '.join(['M', points[0], points[1], 'L'] + points[2:])
            path = parse_path(p, matrix=matrix, svgState=state)
            paths.append(path)
        elif tag == 'rect':
            x = getFloat('x')
            y = getFloat('y')
            w = getFloat('width')
            h = getFloat('height')
            rx = getFloat('rx',default=None)
            ry = getFloat('ry',default=None)
            path = path_from_rect(x,y,w,h,rx,ry, matrix,state)
            paths.append(path)
        elif tag == 'g' or tag == 'svg':
            for child in tree:
                getPaths(paths, matrix, child, state, savedElements)
        elif tag == 'use':
            try:
                link = None
                for tag in tree.attrib:
                    if tag.strip().lower().endswith("}href"):
                        link = tree.attrib[tag]
                        break
                if link is None or link[0] != '#':
                    raise KeyError
                source = savedElements[link[1:]]
                x = 0
                y = 0
                try:
                    x = float(tree.attrib['x'])
                except:
                    pass
                try:
                    y = float(tree.attrib['y'])
                except:
                    pass
                # TODO: handle width and height? (Inkscape does not)
                matrix = matrixMultiply(matrix, reorder(1,0,0,1,x,y))
                getPaths(paths, matrix, source, state, dict(savedElements))
            except KeyError:
                pass

    def scale(width, height, viewBox, z):
        x = (z.real - viewBox[0]) / (viewBox[2] - viewBox[0]) * width
        y = (viewBox[3]-z.imag) / (viewBox[3] - viewBox[1]) * height
        return complex(x,y)

    paths = []

    try:
        width = sizeFromString(svg.attrib['width'].strip())
    except KeyError:
        width = None
    try:
        height = sizeFromString(svg.attrib['height'].strip())
    except KeyError:
        height = None

    try:
        viewBox = list(map(float, re.split(r'[\s,]+', svg.attrib['viewBox'].strip())))
    except KeyError:
        if width is None or height is None:
            raise KeyError
        viewBox = [0, 0, width*96/25.4, height*96/25.4]

    if width is None:
        width = viewBox[2] * 25.4/96

    if height is None:
        height = viewBox[3] * 25.4/96

    viewBoxWidth = viewBox[2]
    viewBoxHeight = viewBox[3]

    viewBox[2] += viewBox[0]
    viewBox[3] += viewBox[1]

    try:
        preserve = svg.attrib['preserveAspectRatio'].strip().lower().split()
        if len(preserve[0]) != 8:
            raise KeyError
        if len(preserve)>=2 and preserve[1] == 'slice':
            if viewBoxWidth/viewBoxHeight > width/height:
                # viewbox is wider than viewport, so scale by height to ensure
                # viewbox covers the viewport
                rescale = height / viewBoxHeight
            else:
                rescale = width / viewBoxWidth
        else:
            if viewBoxWidth/viewBoxHeight > width/height:
                # viewbox is wider than viewport, so scale by width to ensure
                # viewport covers the viewbox
                rescale = width / viewBoxWidth
            else:
                rescale = height / viewBoxHeight
        matrix = [rescale, 0, 0,
                  0, rescale, 0];

        if preserve[0][0:4] == 'xmin':
            # viewBox[0] to 0
            matrix[2] = -viewBox[0] * rescale
        elif preserve[0][0:4] == 'xmid':
            # viewBox[0] to width/2
            matrix[2] = -viewBox[0] * rescale + width/2
        else: # preserve[0][0:4] == 'xmax':
            # viewBox[0] to width
            matrix[2] = -viewBox[0] * rescale + width

        if preserve[0][4:8] == 'ymin':
            # viewBox[1] to 0
            matrix[5] = -viewBox[1] * rescale
        elif preserve[0][4:8] == 'ymid':
            # viewBox[0] to width/2
            matrix[5] = -viewBox[1] * rescale + height/2
        else: # preserve[0][4:8] == 'xmax':
            # viewBox[0] to width
            matrix[5] = -viewBox[1] * rescale + height
    except:
        matrix = [ width/viewBoxWidth, 0, -viewBox[0]* width/viewBoxWidth,
                   0, -height/viewBoxHeight, viewBox[3]*height/viewBoxHeight ]

    getPaths(paths, matrix, svg, path.SVGState(), {})

    return ( paths, applyMatrix(matrix, complex(viewBox[0], viewBox[1])),
                applyMatrix(matrix, complex(viewBox[2], viewBox[3])) )

def getPathsFromSVGFile(filename):
    return getPathsFromSVG(ET.parse(filename).getroot())


import binascii
import os
import sys

import click
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.ext.serializer import dumps, loads

from flask import current_app
from flask.cli import AppGroup

from linotp.lib.audit.SQLAudit import AuditTable
from linotp.model.config import Config
from linotp.model.realm import Realm
from linotp.model.token import Token
from linotp.model.tokenRealm import TokenRealm

from . import get_backup_filename

TIME_FORMAT = "%Y-%m-%d_%H-%M"

ORM_Models = {
    "Config": Config,
    "Token": Token,
    "TokenRealm": TokenRealm,
    "Realm": Realm,
}

# -------------------------------------------------------------------------- --

# dbsnapshot commands

dbsnapshot_cmds = AppGroup(
    "dbsnapshot", help=("Manage system-independent database 'snapshots'")
)

@dbsnapshot_cmds.command(
    "create", help="Create a snapshot of the database tables."
)
def create_command():
    """Create backup file for your database tables"""

    try:
        current_app.echo("Backup database ...", v=1)
        backup_database_tables()
        current_app.echo("finished", v=1)
    except Exception as exx:
        current_app.echo("Failed to backup: %r" % exx)
        sys.exit(1)

@dbsnapshot_cmds.command(
    "restore", help="Restore a snapshot of the database tables."
)
@click.option("--file", help="Name of the snapshot file.")
@click.option(
    "--date",
    help=(
        "Restore a snapshot from a given date. "
        "'date' must be in format '%s'." % TIME_FORMAT
    ),
)
@click.option(
    "--table",
    type=click.Choice(["config", "token", "audit"], case_sensitive=False),
    help="Restore a specific table only.",
)
def restore_command(file=None, date=None, table=None):
    """restore a database snapshot

    @param file - the snapshot file name, could be absolute or relative
    @param date - select a snapshot to restore by date
    @param table - allows to restore only one database table
    """
    try:
        current_app.echo("Restoring snapshot ...", v=1)
        restore_database_tables(file, date, table)
        current_app.echo("Finished", v=1)
    except Exception as exx:
        current_app.echo(f"Failed to restore: {exx!r}")
        sys.exit(1)

@dbsnapshot_cmds.command(
    "list", help=("List available snapshots of the database tables.")
)
def list_command():
    """list available database snapshots."""
    try:
        current_app.echo("Available snapshots to restore", v=1)
        for backup_date, backup_file in list_database_backups():
            current_app.echo(f"{backup_date} {backup_file}", err=False)
        current_app.echo("Finished", v=1)
    except Exception as exx:
        current_app.echo("Failed to list snapshot files: {exx!r}")
        sys.exit(1)

# -------------------------------------------------------------------------- --

# backend implementation

def backup_database_tables() -> int:
    """
    use the sqlalchemy serializer to dump the database mapped objects
    """
    app = current_app

    backup_filename_template = "linotp_backup_%s.sqldb"

    # ---------------------------------------------------------------------- --

    # if audit is shared, it belongs to the same database, thus we make as
    # well an backup of the audit

    backup_classes = ORM_Models

    audit_db = app.config["AUDIT_DATABASE_URI"]
    if audit_db == "SHARED":
        backup_classes["AuditTable"] = AuditTable

    # ---------------------------------------------------------------------- --

    # setup db engine, session and meta from sql uri

    db = SQLAlchemy(app)

    app.echo(
        "extracting data from: %r:%r"
        % (db.engine.url.drivername, db.engine.url.database),
        v=1,
    )

    # ---------------------------------------------------------------------- --

    # setup the backup location

    backup_dir = current_app.config["BACKUP_DIR"]
    os.makedirs(backup_dir, exist_ok=True)

    # ---------------------------------------------------------------------- --

    # run the db serialisation and dump / pickle the data

    filename = get_backup_filename(backup_filename_template)
    backup_filename = os.path.join(backup_dir, filename)

    app.echo("Creating backup file: %s" % backup_filename, v=1)

    with open(backup_filename, "w") as backup_file:
        for name, model_class in backup_classes.items():
            app.echo("Saving %s" % name, v=1)

            backup_file.write("--- BEGIN %s\n" % name)

            data_query = model_class.query

            pb_file = (
                None if app.echo.verbosity > 1 else open("/dev/null", "w")
            )  # None => stdout

            with click.progressbar(
                data_query.all(), label=name, file=pb_file
            ) as all_data:
                for data in all_data:
                    backup_file.write(
                        binascii.hexlify(dumps(data)).decode("utf-8")
                    )

                app.echo(".", v=2, nl=False)

            # final newline for detail
            app.echo("", v=2)

            backup_file.write("\n--- END %s\n" % name)

def list_database_backups() -> list:
    """
    find all backup files in the backup directory

    @return list of backup dates
    """
    app = current_app

    filename_template = "linotp_backup_"

    # ---------------------------------------------------------------------- --

    # setup the backup location

    backup_dir = app.config["BACKUP_DIR"]

    if not os.path.exists(backup_dir):
        app.echo("no backup directory found: %s" % backup_dir, v=2)
        return

    # ---------------------------------------------------------------------- --

    # lookup for all files in the directory that match the template

    for backup_file in os.listdir(backup_dir):
        # backup files match the "template" + "%s.sqldb" format

        if backup_file.startswith(filename_template) and backup_file.endswith(
            ".sqldb"
        ):
            backup_date, _, _ext = backup_file[
                len(filename_template) :
            ].rpartition(".")

            yield backup_date, backup_file

# -------------------------------------------------------------------------- --

# restore

def _get_restore_filename(
    template: str, filename: str = None, date: str = None
) -> str or None:
    """
    helper for restore, to determin a filename from a given date or file name

    @param template - the file name template to search for
    @param filename - the absolute or relative backup file name
    @param date - find a backup file by date
    @return the matching filename or None
    """
    app = current_app

    backup_filename = None
    backup_dir = app.config["BACKUP_DIR"]

    if date:
        backup_filename = os.path.join(backup_dir, template % date)

    elif filename:
        # check if file is absolute or relative to the backup directory

        if os.path.isfile(filename):
            backup_filename = filename
        else:
            backup_filename = os.path.join(backup_dir, filename)

    # ---------------------------------------------------------------------- --

    # no file or data parameter was provided

    if not filename and not date:
        app.echo(
            "failed to restore - no date or file name parameter provided", v=1
        )
        raise ValueError("no date or file name parameter provided!")

    # ---------------------------------------------------------------------- --

    # verify that the file to restore from exists

    if not os.path.isfile(backup_filename):
        app.echo(
            "Failed to restore %s - not found or not accessible"
            % backup_filename
        )
        raise FileNotFoundError(
            "failed to restore %s - not found or not"
            " accessible" % backup_filename
        )

    return backup_filename

def restore_database_tables(
    filename: str = None, date: str = None, table: str = None
) -> int:
    """
    restore the database tables from a file or for a given date
       optionally restore only one table

    @param filename - the absolute or relative backup file name
    @param date - find a backup file by date
    @param table - restore only one database table e.g. tokens
    """
    app = current_app

    restore_names = list(ORM_Models.keys())

    audit_uri = app.config["AUDIT_DATABASE_URI"]
    if audit_uri == "SHARED":
        restore_names.append("AuditTable")

    # ---------------------------------------------------------------------- --

    # determine which table should be restored specified by the table parameter
    #  If the token table should be restored we require to restore Realm and
    #  TokenRealm as well, as the have an n:m relationship

    if table:
        if table.lower() == "config":
            restore_names = ["Config"]

        elif table.lower() == "audit":
            restore_names = ["AuditTable"]

        elif table.lower() == "token":
            restore_names = ["Token", "TokenRealm", "Realm"]

        else:
            app.echo(
                f"selected table {table} is not in the set "
                "of supported tables",
                v=1,
            )
            raise ValueError(
                f"selected table {table} is not in the set "
                "of supported tables"
            )

    # ---------------------------------------------------------------------- --

    # determine the backup file for the database restore

    backup_filename = _get_restore_filename(
        "linotp_backup_%s.sqldb", filename, date
    )

    # ---------------------------------------------------------------------- --

    db = SQLAlchemy(app)

    # ---------------------------------------------------------------------- --

    # restore the sqlalchemy dump from file

    with open(backup_filename, "r") as backup_file:
        for line in backup_file:
            line = line.strip()

            if line.startswith("--- END "):
                name = None

            elif line.startswith("--- BEGIN "):
                name = line[len("--- BEGIN ") :]

            elif line and name in restore_names:
                # unhexlify the serialized data first

                data = binascii.unhexlify(line.encode("utf-8"))

                # use sqlalchemy loads to de-serialize the data objects

                restore_query = loads(data, db.metadata, db.session)

                # merge the objects into the current session

                db.session.merge(restore_query)

                app.echo("Restoring %r" % name, v=1)

    # finally commit all de-serialized objects

    db.session.commit()


from django.contrib import messages
from django.shortcuts import render
from rest_framework.response import Response
from rest_framework.views import APIView

from .forms import CustomerForm, OrderForm
from .models import Customer, Order

def create_customer(request):

    if request.method == "POST":
        form = CustomerForm(request.POST, request.FILES)
        if form.is_valid():
            obj = Customer.objects.create(
                **form.cleaned_data
            )
            obj.save()
    else:
        form = CustomerForm()
    return render(request, 'orders/create.html', {'form': form})

def create_order(request):
    if request.method == "POST":
        form = OrderForm(request.POST)
        if form.is_valid():
            obj = Order.objects.create(
                **form.cleaned_data
            )
            obj.save()
    else:
        form = OrderForm()
    return render(request, 'orders/create.html', {'form': form})

class OrderAPIViewSet(APIView):

    def get(self, request, *args, **kwargs):
        pk = self.kwargs['pk']

        if Order.objects.filter(customer_id=pk).exists():
            orders_price = [order.price for order in Order.objects.filter(customer_id=pk)]
            price = sum(orders_price)
            count = len(orders_price)
            avg = price/count
            context = {
                'price': price,
                'count': count,
                'avg': avg
            }
            return Response(context)
        else:
            return Response('No orders')


from spacy.language import Language
from spacy.tokens import Doc

@Language.component("sub_text")
def create_subtexts(doc):
    # restore「,」index list
    punct_idxs = [0]
    subtexts = []
    for token in doc:
        if token.is_punct:
            punctype = token.morph.get("PunctType")
            punctype = "".join(punctype)
            if punctype == "Comm":
                punct_idxs.append(token.i + 1)
                subtexts.append(doc[punct_idxs[-2]:  punct_idxs[-1]])
    if subtexts:
        Doc.set_extension("sub_texts", default=subtexts, force=True)
    return doc


from django.urls import path
from hunt.views import *

app_name = 'hunt'
urlpatterns = [
    path('', home,name='home'),
    path('scanner/', scanner,name='scanner'),
    path('manage_qr/', manage_qr,name='manage_qr'),
    path('add_qr/', add_qr,name='add_qr'),
    path('detail_qr/<int:qr_id>', detail_qr,name='qr_detail'),
    path('delete_qr/<int:qr_id>', delete_qr,name='delete_qr'),
    path('edit_qr/<int:qr_id>', edit_qr,name='edit_qr'),
]


from matplotlib import pyplot as plt
import numpy as np
import cv2
import torch
import os
import random
from PIL import Image

def count_parameters(model):
    return sum(p.numel() for p in model.parameters() if p.requires_grad)

def imshow_tensor(image, ax=None, title=None):
    """Imshow for Tensor."""

    if ax is None:
        fig, ax = plt.subplots()

    # Set the color channel as the third dimension
    image = image.numpy().transpose((1, 2, 0))

    # Reverse the preprocessing steps
    mean = np.array([0.485, 0.456, 0.406])
    std = np.array([0.229, 0.224, 0.225])
    image = std * image + mean

    # Clip the image pixel values
    image = np.clip(image, 0, 1)

    ax.imshow(image)
    plt.axis('off')

    return ax, image

#Convert a img source to tensor
def image_to_tensor(img_path, use_gpu):
    image = cv2.imread(img_path)
    image = torch.tensor((np.asarray(image).T/255.).astype('float32'))
    image = image.unsqueeze(0)
    if use_gpu:
        image = image.cuda()
    return image

#Plot an image from path
def plot_image(img_path):
    image = plt.imread(img_path)
    plt.imshow(image)
    plt.title("Test original image")

#Plot image and show the predicted class
def plot_image_and_class(model, img_path):
    image = plt.imread(img_path)
    plt.imshow(image)
    tensor_image = image_to_tensor(img_path, True)
    tensor_model_image = model(tensor_image)
    sm, detected_label, accuracy = detect_class(tensor_model_image)
    plt.title('Accuracy: '+ str(accuracy*100)[0:5] + '%')
    plt.suptitle(detected_label)

#Return the class of a Tensor image
def detect_class(tensor_image):
    classes = list('ABCDEFHIKLMNOPQRTUVWY')
    sm = torch.softmax(tensor_image, dim=1).detach().cpu().numpy()
    max_value = sm.max()
    arg_max = sm.argmax()
    return sm[0], classes[arg_max], max_value

#Print results of an image path
def print_results(model, tensor_image):
    tensor_model_image = model(tensor_image)
    sm, detected_label, accuracy = detect_class(tensor_model_image)
    print("Softmax")
    classes = list('ABCDEFHIKLMNOPQRTUVWY')
    for i, acc in enumerate(sm):
        print(classes[i], '\t', str(acc*100)[0:5] + '%')
    print("Detected label: "+detected_label)
    print('Accuracy: '+ str(accuracy*100)[0:5] + '%')

#Get feature maps from a lenet model
def get_lenet_feat_maps(lenet_model, tensor_image):
    f_map_1 = lenet_model.activation(lenet_model.conv1(tensor_image))
    f_map_2 = lenet_model.activation(lenet_model.conv2(f_map_1))
    adapool = lenet_model.adapool(f_map_2)
    view = adapool.view(adapool.size(0), -1)
    f_conn1 = lenet_model.activation(lenet_model.fc1(view))
    f_conn2 = lenet_model.activation(lenet_model.fc2(f_conn1))
    output = lenet_model.fc3(f_conn2)
    return f_map_1, f_map_2#, adapool, f_conn1

#Get feature maps from a alexnet model
def get_alexnet_feat_maps(alexnet_model, tensor_image):
    #f_map = (model.features[1](tensor_image)).detach().cpu().numpy()
    cv1 = (alexnet_model.features[0](tensor_image))
    ac1 = (alexnet_model.features[1](cv1))
    pool1 = (alexnet_model.features[2](ac1))
    cv2 = (alexnet_model.features[3](pool1))
    ac2 = (alexnet_model.features[4](cv2))
    pool2 = (alexnet_model.features[5](ac2))
    cv3 = (alexnet_model.features[6](pool2))
    ac3 = (alexnet_model.features[7](cv3))
    cv4 = (alexnet_model.features[8](ac3))
    ac4 = (alexnet_model.features[9](cv4))
    cv5 = (alexnet_model.features[10](ac4))
    ac5 = (alexnet_model.features[11](cv5))
    pool3 = (alexnet_model.features[12](ac5))
    #print(ac5.shape)
    #print(pool3.shape)
    avgpool = (alexnet_model.avgpool(pool3))
    #print(avgpool.shape)
    #print(pool3)
    view = avgpool.view(avgpool.size(0), 256 * 6 * 6)
    drop1 = alexnet_model.classifier[0](view)
    linear1 = alexnet_model.classifier[1](drop1)
    ac6 = alexnet_model.classifier[2](linear1)
    drop2 = alexnet_model.classifier[3](ac6)
    linear2 = alexnet_model.classifier[4](drop2)
    ac7 = alexnet_model.classifier[5](linear2)
    output = alexnet_model.classifier[6](ac7)

    return pool3

#Plot feature map from a model
def plot_feat_map(f_map):
    feat_map_number = f_map.shape[1]
    print("Feature output maps number: ", feat_map_number)
    divisions = 16
    fig, axes = plt.subplots(nrows=divisions, ncols=divisions, figsize=(20, 20))
    for i in range(0, divisions):
        for j in range(0, divisions):
            index = j + (divisions * i)
            axes[i,j].imshow(f_map[0, index].detach().cpu().numpy())
            axes[i,j].axis('off')
    plt.show()

def show_layer_output(layer):
    #print(layer)
    print("Output layer shape: ", layer.shape)
    #print(layer.argmax(dim = 1))

def test_image_from_path(model, test_img_path, img_name, use_gpu, transform):
    if(img_name == ''):
        r = random.choice(os.listdir(test_img_path)) #change dir name to whatever
        img_path = test_img_path + r

    pil_image = Image.open(img_path)
    transformed_image = transform_test_image(pil_image, transform)
    print("Transform image shape: ", transformed_image.shape)
    if use_gpu:
        transformed_image = transformed_image.cuda()

    print_results(model, transformed_image)
    plot_image(img_path)
    if(model.name != 'vgg16'):
        if model.name == 'alexnet': feat_maps = get_alexnet_feat_maps(model, transformed_image)
        if model.name == 'lenet': feat_maps = get_lenet_feat_maps(model, transformed_image)
        #print("Feature maps shape: ", feat_maps.shape)
        #plot_feat_map(feat_maps)

def test_image_from_dataset(model, use_gpu, transform, dataset):
    r = random.randint(0, len(dataset))
    img_path = 'balanced/' + dataset[r]

    pil_image = Image.open(img_path)
    transformed_image = transform_test_image(pil_image, transform)
    #print("Transform shape ", transformed_image.shape)
    if use_gpu:
        transformed_image = transformed_image.cuda()

    print_results(model, transformed_image)
    plot_image(img_path)

    if(model.name != 'vgg16'):
        if model.name == 'alexnet': feat_maps = get_alexnet_feat_maps(model, transformed_image)
        if model.name == 'lenet': feat_maps = get_lenet_feat_maps(model, transformed_image)
        #print("Feature maps shape: ", feat_maps.shape)
        #plot_feat_map(feat_maps)

def transform_test_image(pil_image, transform):
    transformed_image = transform(pil_image)
    numpy_image = transformed_image.numpy()[0]
    plt.imshow(numpy_image)
    plt.title("Resized image transform")
    transformed_image = transformed_image.unsqueeze(0)
    return transformed_image


from itertools import  product
n,m,x=map(int,input().split())
ca=[list(map(int,input().split())) for _ in range(n)]
for i in range(1,m+1):
    count=0
    for j in range(n):
        count+=ca[j][i]
    if count<x:
        print(-1)
        exit()

ans=10**9

for z in product((0, 1), repeat=n):#bit全探索のくみあわせを勝手に作ってくれるので使うとよい
    count=0
    count2=[0]*m
    for i in range(n):
        if z[i]==1:
            count+=ca[i][0]
            for j in range(1,m+1):
                count2[j-1]+=ca[i][j]

    for i in range(m):
        if count2[i]<x:
            break
    else:
        ans=min(count,ans)
print(ans)


import telegram
import requests
from bs4 import BeautifulSoup
import schedule
import time

bot = telegram.Bot(token = "5433279502:AAF1snTj1bPTAjLwYj5BFfvgsZKDvN7tT84")
id = '@AlarmBotmadeEunbae'
eun_id = 5085254544

def create_soup(url) :
    res = requests.get(url)
    res.raise_for_status()
    soup = BeautifulSoup(res.text, "lxml")
    return soup

def weather() :
    url = "https://search.naver.com/search.naver?where=nexearch&sm=top_sug.asiw&fbm=1&acr=1&acq=%EC%84%9C%EC%9A%B8&qdt=0&ie=utf8&acir=1&query=%EC%84%9C%EC%9A%B8+%EB%82%A0%EC%94%A8"
    soup = create_soup(url)

    # 어제보다 OO' 높아요, 맑음
    cast = soup.find("p", attrs={"class" : "summary"}).get_text()
    cast = cast.replace("  "," ")

    # 현재 온도, OO'C
    temps = soup.find("div", attrs={"class" : "temperature_text"}).get_text()

    # 최고 / 최저 온도
    temps_rate = soup.find("span", attrs={"class" : "temperature_inner"}).get_text().strip()

    # 강수량
    cell_weather = soup.find("div", attrs={"class" : "cell_weather"}).get_text().strip()
    cell_weather = cell_weather.replace("   ","/")

    # 미세먼지
    air = soup.find("li", attrs={"class" : "item_today level2"}).get_text().strip()
    bot.send_message(id, text=
                    "[오늘의 날씨 알려드립니다.] \n"  +
                    (f"{temps[1:6]} : {temps[7:]}") +
                    (f"[{temps_rate}] \n") +
                    (f"  :  {cast} \n") +
                    (f"  :  {air} \n") +
                    ("[강수량 정보] \n") +
                    (f"{cell_weather}")
    )

def scrap_news() :
    url = "https://media.naver.com/press/001"
    soup = create_soup(url)

    main_news = soup.find("ul", attrs={"class" : "press_news_list as_bottom"}).find_all("li")
    for index, news in enumerate(main_news) :
        title = news.find("a").get_text().strip()
        link = news.find("a")["href"]
        bot.send_message(id, text=
                            "[오늘의 뉴스] \n" +
                            (f"{index + 1}. {title} \n") +
                            (f"    (링크 : {link})")
        )
        time.sleep(10)

def baseball_match() :
    url = "https://sports.news.naver.com/kbaseball/index"
    soup = create_soup(url)

    kboMatch = soup.find_all("div", id="_tab_box_kbo")[0]
    kboMatchItems = kboMatch.find("div", class_="hmb_list").find_all("li", class_="hmb_list_items")
    bot.send_message(id, "[야구 경기 예정]\n")
    for item in kboMatchItems :
            leftItemBox = item.find(class_="vs_list vs_list1").find(class_="inner")
            global leftScore
            try :
                leftScore = leftItemBox.find("div", class_="score").stripped_strings
                leftScore = ("".join(leftScore))
            except :
                leftScore = " "
            leftName = leftItemBox.find("span", class_="name").text
            leftPitcher = leftItemBox.find_all("span")[2].text

            rightItemBox = item.find(class_="vs_list vs_list2").find(class_="inner")
            global rightScore
            try :
                rightScore = rightItemBox.find("div", class_="score").stripped_strings
                rightScore = ("".join(rightScore))
            except :
                rightScore = " "
            rightName = rightItemBox.find("span", class_="name").text
            rightPitcher = rightItemBox.find_all("span")[2].text
            bot.send_message(id, f"{leftName}\t (선발 : {leftPitcher})\t {leftScore}\n \tvs\n{rightName}\t (선발 : {rightPitcher})\t {rightScore}")
            time.sleep(3)

def baseball_result() :
    url = "https://sports.news.naver.com/kbaseball/index"
    soup = create_soup(url)

    kboMatch = soup.find_all("div", id="_tab_box_kbo")[0]
    kboMatchItems = kboMatch.find("div", class_="hmb_list").find_all("li", class_="hmb_list_items")
    bot.send_message(id, "[야구 경기 결과]\n")
    for item in kboMatchItems :
            leftItemBox = item.find(class_="vs_list vs_list1").find(class_="inner")
            global leftScore
            try :
                leftScore = leftItemBox.find("div", class_="score").stripped_strings
                leftScore = ("".join(leftScore))
            except :
                leftScore = " "
            leftName = leftItemBox.find("span", class_="name").text

            rightItemBox = item.find(class_="vs_list vs_list2").find(class_="inner")
            global rightScore
            try :
                rightScore = rightItemBox.find("div", class_="score").stripped_strings
                rightScore = ("".join(rightScore))
            except :
                rightScore = " "
            rightName = rightItemBox.find("span", class_="name").text
            bot.send_message(id, f"{leftName}\t : {leftScore} \tvs\t {rightName}\t : {rightScore}")
            time.sleep(3)

def baseball_rank() :
    rank_url = "https://sports.news.naver.com/kbaseball/record/index?category=kbo"
    soup = create_soup(rank_url)

    selected = soup.find_all(id="regularTeamRecordList_table")[0]

    rows = selected.find_all("tr")
    bot.send_message(id, "[야구 순위]\n")
    for item in rows :
        # tr
        bot.send_message(id,
                item.find_all("th")[0].text + "위 : "  # rank
                +item.find_all("div")[0].text # team name
                +"("
                +"경기수 : "+item.find_all("td")[1].text
                +"\t승 : "+item.find_all("td")[2].text
                +"\t패 : "+item.find_all("td")[3].text
                +"\t무 : "+item.find_all("td")[4].text
                +"\t승률 : "+item.find_all("td")[5].text
                +"\t게임차 : "+item.find_all("td")[6].text
                +"\t연승/연패 : "+item.find_all("td")[7].text
                +"\t최근10경기 : "+item.find_all("td")[10].text
                +")\n"
        )
        time.sleep(15)

def football() :
    url = "https://sports.news.naver.com/wfootball/index"
    soup = create_soup(url)
    day = soup.find("span", attrs={"class" : "inner"}).find_all("span", limit=1)
    for days in day :
        bot.send_message(eun_id, f"{days.get_text()} 경기 일정") # 08.06(토)

    Match = soup.find("div", class_ = "hmb_list").find_all("li", class_="hmb_list_items")
    for Matchs in Match :
        LeftBox = Matchs.find(class_ ="vs_list vs_list1").find(class_="inner")
        global LeftScore
        try :
            LeftScore = LeftBox.find("div", class_="score").stripped_strings
            LeftScore = ("".join(LeftScore))
        except :
            LeftScore = " "
        LeftTeam = LeftBox.find("span", class_ = "name").text

        RightBox = Matchs.find(class_="vs_list vs_list2").find(class_="inner")
        global RightScore
        try :
            RightScore = RightBox.find("div", class_="score").stripped_strings
            RightScore = ("".join(RightScore))
        except :
            RightScore = " "
        RightTeam = RightBox.find("span", class_ = "name").text
        Matchtime = Matchs.find(class_="state").text
        Matchtime = Matchtime.replace("\n", "")
        bot.send_message(eun_id, text =
                        f"(경기시간 : {Matchtime}) \n" +
                        f"{LeftTeam} : {LeftScore} vs {RightScore} : {RightTeam}")
        time.sleep(10)

def sports_news() :
    url = "https://sports.news.naver.com/wfootball/index"
    world_football_url = "https://sports.news.naver.com/"
    soup = create_soup(url)

    sports = soup.find("ul", attrs={"class" : "home_news_list"}).find_all("li")
    for index, news in enumerate(sports) :
        title = news.find("a").get_text().strip()
        link = world_football_url + news.find("a")['href']
        bot.send_message(eun_id, text =
                         "[오늘의 해외축구] \n" +
                         (f"{index+1}. {title}\n") +
                         (f"    (링크 : {link})")
        )
        time.sleep(10)

schedule.every().day.at("05:50:00").do(weather)
schedule.every().day.at("07:30:00").do(scrap_news)
schedule.every().day.at("08:00:00").do(sports_news)
schedule.every().day.at("16:00:00").do(baseball_match)
schedule.every().day.at("19:30:00").do(football)
schedule.every().day.at("22:15:00").do(baseball_result)
schedule.every().sunday.at("22:30:30").do(baseball_rank)

while True :
    schedule.run_pending()
    time.sleep(10)


from ..io import get_input

class Question(object):

    def __init__(self, name, series=None, message=None, default=None, key=None,
                 update_function=None, do_not_ask=False,
                 quote_user_input=True):

        self.name = name
        self._series = series
        if series is not None:
            self.series = series

        self.user_input = None
        self.default = default

        if message is not None:
            self.message = message
        else:
            self.message = self._get_message()

        self.key = key
        self.update_function = update_function
        self.do_not_ask = do_not_ask
        self.quote_user_input = quote_user_input

    def _get_message(self):

        if self.default:
            return "{} ({}): ".format(self.name, self.default)
        else:
            return "{}: ".format(self.name)

    @property
    def series(self):
        return self._series

    @series.setter
    def series(self, s):
        if s and self not in s and self.name not in [x.name for x in s]:
            s.append(self)
        self._series = s

    def ask(self):

        if not self.do_not_ask and self.message:
            self.user_input = get_input(self.message)

        self.process()

    def process(self):

        if not self.user_input and self.default:
            self.user_input = self.default

        if self.user_input and self.series and \
                self.key and self.series.template_vars:
            if self.quote_user_input:
                self.user_input = '"{}"'.format(self.user_input)

            if hasattr(self.update_function, "__call__"):
                self.update_function(self)
            else:
                value = self.series.template_vars.get(self.key)

                if isinstance(value, list):
                    value.append(self.user_input)
                    self.series.template_vars[self.key] = value
                else:
                    self.series.template_vars[self.key] = self.user_input


from dgl.data import DGLDataset
import json
import dgl
import torch
import os
from dgl import save_graphs, load_graphs

class NestDataset(DGLDataset):
    """ 用于在DGL中自定义图数据集的模板：

    Parameters
    ----------
    file_graph_path : str, 文件级图。
    func_graph_path : str, 函数级图。
    name: 使用数据集的类型
    type:训练或测试
    ratio:训练/测试的比例

    url:
    raw_dir : str
        指定下载数据的存储目录或已下载数据的存储目录。默认: ~/.dgl/
    save_dir : str
        处理完成的数据集的保存目录。默认：raw_dir指定的值
    force_reload : bool
        是否重新导入数据集。默认：False
    verbose : bool
        是否打印进度信息。
    """

    def __init__(self, file_graph_path, func_graph_path, name, type = 'train',ratio=1,url=None, raw_dir=None, save_dir=None, force_reload=True,
                 verbose=False):
        self.file_graph_path = file_graph_path
        self.func_graph_path = func_graph_path
        self.type = type
        self.ratio = ratio
        self.file_graphs = []
        self.func_graphs = []
        self.num_funcs = []
        self.labels = []

        # super(path_no_v, path_v).__init__(path_no_v=path_no_v,
        #                                   path_v=path_v,
        #                                   name='dataset_name',
        #                                   url=url,
        #                                   raw_dir=raw_dir,
        #                                   save_dir=save_dir,
        #                                   force_reload=force_reload,
        #                                   verbose=verbose)
        # super().__init__(name, url, raw_dir, save_dir, force_reload, verbose)
        super().__init__(name, url, raw_dir, save_dir, force_reload, verbose)

    def download(self):
        # 将原始数据下载到本地磁盘
        pass

    def process(self):
        print("loading data")
        f = open(self.file_graph_path,'r')
        ff = open(self.func_graph_path,'r')

        if self.type == 'train':
            # generate file_g_list
            file_data = json.load(f)
            print("number of files:",len(file_data))
            num_train = int(self.ratio * len(file_data))
            num_funcs = 0
            for i in range(num_train):
                data = file_data[i]

                #num_funs***********************************
                self.num_funcs.append(data[0])
                self.labels.append(data[1])
                num_funcs += len(data[1])
                if(data[0]!=len(data[1])):
                    print('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!++++' + str(i))
                g = dgl.graph((data[2], data[3]))
                g = g.to('cuda:0')
                self.file_graphs.append(g)
            print(num_funcs)
            # generate func_g_list
            func_data = json.load(ff)
            # print(num_funcs,len(func_data))
            for i in range(num_funcs):
                data = func_data[i]
                g = dgl.graph((data[0], data[1]))
                g.ndata['n_attr'] = torch.Tensor(data[2])
                g.edata['e_attr'] = torch.Tensor(data[3])
                # g = dgl.add_self_loop(g)
                g = g.to('cuda:0')
                self.func_graphs.append(g)
            # print(num_funcs,len(self.func_graphs))
        elif self.type == 'test':
            #skip train data
            file_data = json.load(f)
            num_train = int(self.ratio * len(file_data))
            func_begin = 0
            num_funs = 0
            for i in range(num_train):
                data = file_data[i]
                func_begin += len(data[1])

            # generate file_g_list
            for i in range(num_train,len(file_data)):
                data = file_data[i]
                self.num_funcs.append(data[0])
                self.labels.append(data[1])
                num_funs += len(data[1])
                if(data[0]!=len(data[1])):
                    print('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!++++' + str(i))
                g = dgl.graph((data[2], data[3]))
                g = g.to('cuda:0')
                self.file_graphs.append(g)
                # print(len(g.nodes()),data[0])

            # generate func_g_list
            func_data = json.load(ff)
            for i in range(func_begin,len(func_data)):
                data = func_data[i]
                g = dgl.graph((data[0], data[1]))
                g.ndata['n_attr'] = torch.Tensor(data[2])
                g.edata['e_attr'] = torch.Tensor(data[3])
                # g = dgl.add_self_loop(g)
                g = g.to('cuda:0')
                self.func_graphs.append(g)
            # n_nodes = 0
            # for g in self.file_graphs:
            #     n_nodes += len(g.nodes())
            # print(n_nodes,len(self.func_graphs))
        print("successfully load data")
        # print(len(self.file_graphs))

    def __getitem__(self, idx):
        # 通过idx得到与之对应的一个样本, label with no meaning
        return self.file_graphs[idx],1

    def __len__(self):
        # 数据样本的数量
        # l = 0
        # with open(self.path_v,'r') as f:
        #     vul_data = json.load(f)
        #     l += len(vul_data)
        # with open(self.path_v, 'r') as f:
        #     no_vul_data = json.load(f)
        #     l += len(no_vul_data)
        return len(self.file_graphs)

    def save(self):
        # 将处理后的数据保存至 `self.save_path` _dgl_graph.bin
        # graph_path = self.save_dir
        # labels = torch.tensor(self.labels)
        # save_graphs(graph_path, self.graphs, {'labels': labels})
        # 在Python字典里保存其他信息
        # info_path = os.path.join(self.save_path, self.mode + '_info.pkl')
        # save_info(info_path, {'num_classes': self.num_classes})
        pass
    def load(self):
        # 从 `self.save_path` 导入处理后的数据
        graph_path = self.save_dir
        self.graphs, label_dict = load_graphs(graph_path)
        for g in self.graphs:
            g.to("cuda:0")
        self.labels = label_dict['labels']

    def has_cache(self):
        # 检查在 `self.save_path` 中是否存有处理后的数据
        # graph_path = os.path.join(self.save_path)
        # info_path = os.path.join(self.save_path, self.mode + '_info.pkl')
        # return os.path.exists(self.save_dir)
        return False


from __future__ import print_function
import os, sys, time, weakref, binascii
import traceback
import collections
import six
from twisted.python import log as twisted_log
from twisted.python import failure
from foolscap import eventual
from foolscap.logging.interfaces import IIncidentReporter
from foolscap.logging.incident import IncidentQualifier, IncidentReporter
from foolscap.logging import app_versions, flogfile
from foolscap.util import ensure_dict_str_keys

from foolscap.logging.levels import NOISY, OPERATIONAL, UNUSUAL, \
     INFREQUENT, CURIOUS, WEIRD, SCARY, BAD

llmap = {}
try:
    import logging as py_logging
    from twisted.logger import LogLevel # added in Twisted-15.2.0
    # twisted.logger._stdlib.toStdlibLogLevelMapping is private, alas
    llmap = {
        LogLevel.debug: py_logging.DEBUG, # == NOISY
        LogLevel.info: py_logging.INFO, # == OPERATIONAL
        LogLevel.warn: py_logging.WARNING, # == WEIRD
        LogLevel.error: py_logging.ERROR, # == BAD,
        LogLevel.critical: py_logging.CRITICAL, # == BAD+10
        }
except ImportError:
    pass # Twisted < 15.2.0

# hush pyflakes, these are imported to be available to other callers
_unused = [NOISY, OPERATIONAL, UNUSUAL, INFREQUENT, CURIOUS, WEIRD, SCARY, BAD]

def format_message(e):
    # the dict might have come from json.loads (so unicode everywhere), or
    # over the wire from a py2 program (so bytes), or from a py3 program (so
    # unicode). Do our best to produce text.
    e = ensure_dict_str_keys(e)
    try:
        if "format" in e:
            fmt = six.ensure_str(e['format'])
            args = e
        elif "args" in e:
            assert "message" in e
            fmt = six.ensure_str(e['message'])
            args = e['args']
        elif "message" in e:
            fmt = "%(message)s"
            assert isinstance(e['message'], (six.binary_type, six.text_type))
            args = {"message": six.ensure_str(e['message'])}
            # i.e. just return e['message']
        else:
            fmt = ""
            args = {}
        assert isinstance(fmt, (six.binary_type, six.text_type))
        return six.ensure_text(fmt % args)
    except (ValueError, TypeError):
        return six.ensure_text(e.get('message', "[no message]")) + " [formatting failed]"

class Count:
    """A fixed version of itertools.count .

    This class counts up from zero, just like the Python 2.5.2 docs claim
    that itertools.count() does, but this class does not overflow with an
    error like itertools.count() does:

      File 'foolscap/logging/log.py', line 137, in msg
          num = self.seqnum.next()
      exceptions.OverflowError: cannot count beyond PY_SSIZE_T_MAX
    """

    def __init__(self, firstval=0):
        self.n = firstval - 1

    def next(self):
        self.n += 1
        return self.n

class FoolscapLogger:
    DEFAULT_SIZELIMIT = 100
    DEFAULT_THRESHOLD = NOISY
    MAX_RECORDED_INCIDENTS = 20 # records filenames of incident logfiles

    def __init__(self):
        self.incarnation = self.get_incarnation()
        self.seqnum = Count()
        self.facility_explanations = {}
        self.buffer_sizes = {} # k: facility or None, v: dict(level->sizelimit)
        self.buffer_sizes[None] = {}
        self.buffers = {} # k: facility or None, v: dict(level->deque)
        self.thresholds = {}
        self._observers = []
        self._immediate_observers = []
        self._immediate_incident_observers = []
        self.logdir = None # nowhere to put our incidents
        self.inactive_incident_qualifier = IncidentQualifier()
        self.active_incident_qualifier = None
        self.incident_reporter_factory = IncidentReporter
        self.active_incident_reporter_weakref = None
        self.incidents_declared = 0
        self.incidents_recorded = 0
        self.recent_recorded_incidents = []

    def get_incarnation(self):
        unique = binascii.b2a_hex(os.urandom(8))
        # b2a_hex yields bytes on both py2/py3
        if sys.version_info.major != 2:
            unique = unique.decode("ascii")
        # 'unique' is native string
        sequential = None
        return (unique, sequential)

    def addObserver(self, observer):
        self._observers.append(observer)
    def removeObserver(self, observer):
        self._observers.remove(observer)

    def addImmediateObserver(self, observer):
        # by using this, you solemly swear that your observer will not raise
        # an exception, nor will it recurse or cause more log messages to be
        # emitted. Immediate Observers are notified without an eventual-send.
        self._immediate_observers.append(observer)
    def removeImmediateObserver(self, observer):
        self._immediate_observers.remove(observer)

    def setLogDir(self, directory):
        # TODO: change self.incarnation to reflect next seqnum
        self.logdir = os.path.abspath(os.path.expanduser(directory))
        if not os.path.isdir(self.logdir):
            os.makedirs(self.logdir)
        self.activate_incident_qualifier()

    def setIncidentQualifier(self, iq):
        assert iq.event
        self.deactivate_incident_qualifier()
        self.inactive_incident_qualifier = iq
        if self.logdir:
            self.activate_incident_qualifier()

    def deactivate_incident_qualifier(self):
        if self.active_incident_qualifier:
            self.active_incident_qualifier.set_handler(None)
            self.active_incident_qualifier = None

    def activate_incident_qualifier(self):
        self.active_incident_qualifier = self.inactive_incident_qualifier
        self.active_incident_qualifier.set_handler(self)

    def setIncidentReporterFactory(self, ir):
        assert IIncidentReporter.implementedBy(ir)
        self.incident_reporter_factory = ir

    def addImmediateIncidentObserver(self, observer):
        self._immediate_incident_observers.append(observer)
    def removeImmediateIncidentObserver(self, observer):
        self._immediate_incident_observers.remove(observer)

    def explain_facility(self, facility, description):
        self.facility_explanations[facility] = description

    def set_buffer_size(self, level, sizelimit, facility=None):
        if facility not in self.buffer_sizes:
            self.buffer_sizes[facility] = {}
        self.buffer_sizes[facility][level] = sizelimit

    def set_generation_threshold(self, level, facility=None):
        self.thresholds[facility] = level
    def get_generation_threshold(self, facility=None):
        return self.thresholds.get(facility, self.DEFAULT_THRESHOLD)

    def msg(self, *args, **kwargs):
        """
        @param parent: the event number of the most direct parent of this
                       event
        @param facility: the slash-joined facility name, or None
        @param level: the numeric severity level, like NOISY or SCARY
        @param stacktrace: a string stacktrace, or True to generate one
        @returns: the event number for this logevent, intended to be passed
                  to parent= in a subsequent call to msg()
        """

        if "num" not in kwargs:
            num = self.seqnum.next()
            kwargs['num'] = num
        else:
            num = kwargs['num']

        try:
            self._msg(*args, **kwargs)
        except Exception as e:
            try:
                errormsg = ("internal error in log._msg,"
                            " args=%r, kwargs=%r, exception=%r"
                            % (args, kwargs, e))
                self._msg(errormsg, num=num, level=WEIRD,
                          facility="foolscap/internal-error")
            except:
                pass # bummer
        return num

    def _msg(self, *args, **kwargs):
        facility = kwargs.get('facility')
        if "level" not in kwargs:
            kwargs['level'] = OPERATIONAL
        level = kwargs["level"]
        threshold = self.get_generation_threshold(facility)
        if level < threshold:
            return # not worth logging

        event = kwargs
        # kwargs always has 'num'

        if "format" in event:
            pass
        elif "message" in event:
            event['message'] = str(event['message'])
        elif args:
            event['message'], posargs = str(args[0]), args[1:]
            if posargs:
                event['args'] = posargs
        else:
            event['message'] = ""

        if "time" not in event:
            event['time'] = time.time()

        if event.get('stacktrace', False) is True:
            event['stacktrace'] = traceback.format_stack()
        event['incarnation'] = self.incarnation
        self.add_event(facility, level, event)

    def err(self, _stuff=None, _why=None, **kw):
        """
        Write a failure to the log.
        """
        if _stuff is None:
            _stuff = failure.Failure()
        if isinstance(_stuff, failure.Failure):
            return self.msg(failure=_stuff, why=_why, isError=1, **kw)
        elif isinstance(_stuff, Exception):
            return self.msg(failure=failure.Failure(_stuff), why=_why,
                            isError=1, **kw)
        else:
            return self.msg(repr(_stuff), why=_why, isError=1, **kw)

    def add_event(self, facility, level, event):
        # send to observers
        for o in self._immediate_observers:
            o(event)
        for o in self._observers:
            eventual.eventually(o, event)

        # buffer locally
        d1 = self.buffers.get(facility)
        if not d1:
            d1 = self.buffers[facility] = {}
        buffer = d1.get(level)
        if not buffer:
            buffer = d1[level] = collections.deque()
        buffer.append(event)

        # enforce size limits on local buffers
        d2 = self.buffer_sizes.get(facility)
        if d2:
            sizelimit = d2.get(level, self.DEFAULT_SIZELIMIT)
        else:
            sizelimit = self.DEFAULT_SIZELIMIT
        while len(buffer) > sizelimit:
            buffer.popleft()

        # check with incident reporter. This is done synchronously rather
        # than via the usual eventual-send to allow the application to do:
        #  log.msg("abandon ship", level=log.BAD)
        #  sys.exit(1)
        #
        # This means the IncidentReporter will do most of its work right
        # here. The reporter is not allowed to make any foolscap calls, and
        # the call to incident_recorded() is required to pass through an
        # eventual-send.

        if self.active_incident_qualifier:
            # this might call declare_incident
            self.active_incident_qualifier.event(event)

    def declare_incident(self, triggering_event):
        self.incidents_declared += 1
        ir = self.get_active_incident_reporter()
        if ir:
            ir.new_trigger(triggering_event)
            return
        if self.logdir: # just in case
            ir = self.incident_reporter_factory(self.logdir, self, "local")
            self.active_incident_reporter_weakref = weakref.ref(ir)
            ir.incident_declared(triggering_event) # this takes a few seconds

    def incident_recorded(self, filename, name, trigger):
        # 'name' is incident-TIMESTAMP-UNIQUE, whereas filename is an
        # absolute pathname to the NAME.flog.bz2 file.
        self.incidents_recorded += 1
        self.recent_recorded_incidents.append(filename)
        while len(self.recent_recorded_incidents) > self.MAX_RECORDED_INCIDENTS:
            self.recent_recorded_incidents.pop(0)
        # publish these to interested parties
        for o in self._immediate_incident_observers:
            o(name, trigger)

    def get_active_incident_reporter(self):
        if self.active_incident_reporter_weakref:
            ir = self.active_incident_reporter_weakref()
            if ir and ir.is_active():
                return ir
        return None

    def setLogPort(self, logport):
        self._logport = logport
    def getLogPort(self):
        return self._logport

    def get_buffered_events(self):
        # iterates over all current log events in no particular order. The
        # caller should sort them by event number. If this isn't iterated
        # quickly enough, more events may arrive.
        for facility,b1 in self.buffers.items():
            for level,q in b1.items():
                for event in q:
                    yield event

theLogger = FoolscapLogger()

# def msg(stuff):
msg = theLogger.msg
err = theLogger.err
setLogDir = theLogger.setLogDir
explain_facility = theLogger.explain_facility
set_buffer_size = theLogger.set_buffer_size
set_generation_threshold = theLogger.set_generation_threshold
get_generation_threshold = theLogger.get_generation_threshold

# code to bridge twisted.python.log.msg() to foolscap

class TwistedLogBridge:
    def __init__(self, tubID=None, foolscap_logger=theLogger):
        self.tubID = tubID
        self.logger = foolscap_logger

    # we currently depend on Twisted >= 10.1.0, so we can use
    # t.p.log.textFromEventDict . However we cannot add ourselves as a
    # new-style observer (t.l.globalLogPublisher.addObserver()) because that
    # wasn't added until 15.2.0. So even on newer Twisteds, we'll be wrapped
    # by t.l._legacy.LegacyLogObserverWrapper

    def observer(self, d):
        # Twisted will remove this for us if it fails.
        if "from-foolscap" in d:
            return

        # Twisted-8.2.0's ILogObserver tends to give these keys:
        #  log.msg(): message=*args, system, time, isError=False
        #  log.err() adds: isError=True, failure, why
        # plus any kwargs provided to msg()/err(), like format=

        # With Twisted-15.2.0 we are wrapped by
        # t.l._legacy.LegacyLogObserverWrapper , so we still get those keys,
        # but we'll also see some log_* keys that the new logging system
        # adds. Some of the new keys are non-serializable.

        # So we stringify the Twisted event right now, and produce a new
        # event with a small set of known keys.

        message = twisted_log.textFromEventDict(d)
        kwargs = {'tubID': self.tubID, 'from-twisted': True}

        # log_level was added in 15.2.0
        if "log_level" in d:
            # d["log_level"] might be a non-serializable ConstantString.
            # Transform it into the corresponding (integer) Foolscap log
            # level.
            log_level = d.pop("log_level")
            new_log_level = llmap.get(log_level, log_level)
            if not isinstance(new_log_level,
                              six.integer_types +
                              (six.binary_type, six.text_type, bool)):
                # it was something weird: just stringify it in-place
                new_log_level = str(new_log_level)
            kwargs["level"] = new_log_level # foolscap level, not twisted

        # d["isError"]=1 for pre-15.2.0 calls to t.p.log.err(), and is
        # synthesized by the LegacyLogObserverWrapper for post-15.2.0 calls
        # when the event includes a Failure or a log_level of "error" or
        # "critical". In post-15.2.0 calls, "time" and "system" are copied
        # from log_time and log_system, and "log_namespace" seems pretty
        # useful.
        for k in ["isError", "why", "time", "system", "log_namespace"]:
            if k in d:
                kwargs[k] = d[k]
        # we don't copy d["failure"] or d["why"], because its text should
        # already be copied into "message".

        self.logger.msg(message, **kwargs)

_bridges = {} # maps (twisted_logger,foolscap_logger) to TwistedLogBridge

def bridgeLogsFromTwisted(tubID=None,
                          twisted_logger=twisted_log.theLogPublisher,
                          foolscap_logger=theLogger):
    """Called without arguments, this arranges for all twisted log messages
    to be bridged into the default foolscap logger.

    I can also be called with a specific twisted and/or foolscap logger,
    mostly for unit tests that don't want to modify the default instances.
    For their benefit, I return the bridge.

    I only add one bridge per (twisted_logger,foolscap_logger) pair, even if
    called multiple times with different TubIDs, so multiple Tubs in a single
    process that all call tub.setOption(bridge-twisted-logs) will only see
    one foolscap copy of each twisted event, with the first Tub's tubID.
    """
    key = (twisted_logger, foolscap_logger)
    if key not in _bridges:
        tlb = TwistedLogBridge(tubID, foolscap_logger)
        _bridges[key] = tlb
        twisted_logger.addObserver(tlb.observer)
    return _bridges[key]

def unbridgeLogsFromTwisted(twisted_logger, tlb):
    # for tests
    foolscap_logger = tlb.logger
    key = (twisted_logger, foolscap_logger)
    del _bridges[key]
    twisted_logger.removeObserver(tlb.observer)

def bridgeLogsToTwisted(filter=None,
                        foolscap_logger=theLogger,
                        twisted_logger=twisted_log):
    # foolscap_logger and twisted_logger are for testing purposes
    def non_foolscap_operational_or_better(e):
        if e.get("facility","").startswith("foolscap"):
            return False
        if e['level'] < OPERATIONAL:
            return False
        return True
    if not filter:
        filter = non_foolscap_operational_or_better
    def _to_twisted(event):
        if "from-twisted" in event:
            return
        if not filter(event):
            return
        args = {"from-foolscap": True,
                "num": event["num"],
                "level": event["level"],
                }
        twisted_logger.msg(format_message(event), **args)
    foolscap_logger.addObserver(_to_twisted)

class LogFileObserver:
    def __init__(self, filename, level=OPERATIONAL):
        if filename.endswith(".bz2"):
            # py3: bz2file ignores "b", only accepts bytes, not str
            import bz2
            f = bz2.BZ2File(filename, "w")
        else:
            f = open(filename, "wb")
        self._logFile = f # todo: line_buffering=True ?
        self._level = level
        self._logFile.write(flogfile.MAGIC)
        flogfile.serialize_header(self._logFile,
                                  "log-file-observer",
                                  versions=app_versions.versions,
                                  pid=os.getpid(),
                                  threshold=level)

    def stop_on_shutdown(self):
        from twisted.internet import reactor
        reactor.addSystemEventTrigger("after", "shutdown", self._stop)

    def msg(self, event):
        threshold = self._level
        #if event.get('facility', '').startswith('foolscap'):
        #    threshold = UNUSUAL
        if event['level'] >= threshold:
            flogfile.serialize_wrapper(self._logFile, event,
                                       from_="local", rx_time=time.time())

    def _stop(self):
        self._logFile.close()
        del self._logFile

# remove the key, so any child processes won't try to log to (and thus
# clobber) the same file. This doesn't always seem to work reliably
# (allmydata.test.test_runner.RunNode.test_client uses os.system and the
# child process still has $FLOGFILE set).

_flogfile = os.environ.pop("FLOGFILE", None)
if _flogfile:
    try:
        _floglevel = int(os.environ.get("FLOGLEVEL", str(OPERATIONAL)))
        lfo = LogFileObserver(_flogfile, _floglevel)
        lfo.stop_on_shutdown()
        theLogger.addObserver(lfo.msg)
        #theLogger.set_generation_threshold(UNUSUAL, "foolscap.negotiation")
    except IOError:
        print("FLOGFILE: unable to write to %s, ignoring" % \
              (_flogfile,), file=sys.stderr)

if "FLOGTWISTED" in os.environ:
    bridgeLogsFromTwisted()

if "FLOGTOTWISTED" in os.environ:
    _floglevel = int(os.environ.get("FLOGLEVEL", str(OPERATIONAL)))
    def non_foolscap_FLOGLEVEL_or_better(e):
        if e.get("facility","").startswith("foolscap"):
            return False
        if e['level'] < _floglevel:
            return False
        return True
    bridgeLogsToTwisted(filter=non_foolscap_FLOGLEVEL_or_better)

{'bz2': 'bz2', 'reactor': 'twisted.internet.reactor'}


import os, argparse
import sys
import scipy
import timeit
import gzip
import torch
from numpy import save
import numpy as np
from sys import stdout
import pickle as pkl
import scipy.io as scio
import torchvision
import torch.nn as nn
from torch.utils.data import TensorDataset, DataLoader, Dataset

def to_numpy(x):
	if x.is_cuda:
		return x.data.cpu().numpy()
	else:
		return x.data.numpy()

path = 'stl10_matlab/'
data=scio.loadmat(path+'train.mat')
train_X = data['X']
train_Y = data['y'].squeeze()
data=scio.loadmat(path+'test.mat')
test_X = data['X']
test_Y = data['y'].squeeze()
X = []
Y = []
X.append(train_X)
X.append(test_X)
Y.append(train_Y)
Y.append(test_Y)
X = np.concatenate(X,axis=0)
Y = np.concatenate(Y,axis=0)
X = np.reshape(X,(-1,3,96,96))
X = np.transpose(X,(0,1,3,2))
image_train = X.astype('float32')/255
image_train[:,0,:,:] = (image_train[:,0,:,:] - 0.485)/0.229
image_train[:,1,:,:] = (image_train[:,1,:,:] - 0.456)/0.224
image_train[:,2,:,:] = (image_train[:,2,:,:] - 0.406)/0.225
label_train = Y.astype('float32')-1

res50_model = torchvision.models.resnet50(pretrained=True)
res50_conv = nn.Sequential(*list(res50_model.children())[:-2])
res50_conv.eval()
data = torch.from_numpy(image_train)
dataloader = DataLoader(TensorDataset(data),batch_size=200,shuffle=False)
res50_conv = res50_conv.cuda()
total_output = []
for batch_idx, batch in enumerate(dataloader):
    inputs = batch[0].cuda()
    output = res50_conv(inputs)
    total_output.append(output.data)
total_output = torch.cat(total_output,dim=0)

feature_train = torch.sum(torch.sum(total_output,dim=-1),dim=-1)/9

image_train = to_numpy(feature_train)

save('stl_features.npy', image_train)
save('stl_label.npy', label_train)