"""Pibooth view management.
"""

import os
import time
import contextlib
import pygame
from pygame import gfxdraw
from PIL import Image
from pibooth import pictures, fonts
from pibooth.view import background
from pibooth.utils import LOGGER
from pibooth.pictures import sizing

class PiWindow(object):

    """Class to handle the window.
    The following attributes are available for use in plugins:

    :attr surface: surface on which sprites are displayed
    :type surface: :py:class:`pygame.Surface`
    :attr is_fullscreen: set to True if the window is display in full screen
    :type is_fullscreen: bool
    :attr display_size: tuple (width, height) represneting the size of the screen
    :type display_size: tuple
    """

    CENTER = 'center'
    RIGHT = 'right'
    LEFT = 'left'
    FULLSCREEN = 'fullscreen'

    def __init__(self, title,
                 size=(800, 480),
                 color=(0, 0, 0),
                 text_color=(255, 255, 255),
                 arrow_location=background.ARROW_BOTTOM,
                 arrow_offset=0,
                 debug=False):
        self.__size = size
        self.debug = debug
        self.bg_color = color
        self.text_color = text_color
        self.arrow_location = arrow_location
        self.arrow_offset = arrow_offset

        # Prepare the pygame module for use
        os.environ['SDL_VIDEO_CENTERED'] = '1'
        pygame.init()

        # Save the desktop mode, shall be done before `setmode` (SDL 1.2.10, and pygame 1.8.0)
        info = pygame.display.Info()

        pygame.display.set_caption(title)
        self.is_fullscreen = False
        self.display_size = (info.current_w, info.current_h)
        self.surface = pygame.display.set_mode(self.__size, pygame.RESIZABLE)

        self._buffered_images

        self._current_background = None
        self._current_foreground = None
        self._print_number = 0
        self._print_failure = False
        self._capture_number = (0, 4)  # (current, max)

        self._pos_map = {self.CENTER: self._center_pos,
                         self.RIGHT: self._right_pos,
                         self.LEFT: self._left_pos,
                         self.FULLSCREEN: self._center_pos}

        # Don't use pygame.mouse.get_cursor() because will be removed in pygame2
        self._cursor = ((16, 16), (0, 0),
                        (0, 0, 64, 0, 96, 0, 112, 0, 120, 0, 124, 0, 126, 0, 127, 0,
                         127, 128, 124, 0, 108, 0, 70, 0, 6, 0, 3, 0, 3, 0, 0, 0),
                        (192, 0, 224, 0, 240, 0, 248, 0, 252, 0, 254, 0, 255, 0, 255,
                         128, 255, 192, 255, 224, 254, 0, 239, 0, 207, 0, 135, 128, 7, 128, 3, 0))

    def _update_foreground(self, pil_image, pos=CENTER, resize=True):
        """Show a PIL image on the foreground.
        Only one is bufferized to avoid memory leak.
        """
        image_name = id(pil_image)

        if pos == self.FULLSCREEN:
            image_size_max = (self.surface.get_size()[0] * 0.9, self.surface.get_size()[1] * 0.9)
        else:
            image_size_max = (self.surface.get_size()[0] * 0.48, self.surface.get_size()[1])

        buff_size, buff_image = self._buffered_images.get(image_name, (None, None))
        if buff_image and image_size_max == buff_size:
            image = buff_image
        else:
            if resize:
                image = pil_image.resize(sizing.new_size_keep_aspect_ratio(
                    pil_image.size, image_size_max), Image.ANTIALIAS)
            else:
                image = pil_image
            image = pygame.image.frombuffer(image.tobytes(), image.size, image.mode)
            if self._current_foreground:
                self._buffered_images.pop(id(self._current_foreground[0]), None)
            LOGGER.debug("Add to buffer the image '%s'", image_name)
            self._buffered_images[image_name] = (image_size_max, image)

        self._current_foreground = (pil_image, pos, resize)

        if self.debug and resize:
            # Build rectangle around picture area for debuging purpose
            outlines = pygame.Surface(image_size_max, pygame.SRCALPHA, 32)
            pygame.draw.rect(outlines, pygame.Color(255, 0, 0), outlines.get_rect(), 2)
            self.surface.blit(outlines, self._pos_map[pos](outlines))

        return self.surface.blit(image, self._pos_map[pos](image))

    def _update_background(self, bkgd):
        """Show image on the background.
        """
        self._current_background = self._buffered_images.setdefault(str(bkgd), bkgd)
        self._current_background.set_color(self.bg_color)
        self._current_background.set_outlines(self.debug)
        self._current_background.set_text_color(self.text_color)
        self._current_background.resize(self.surface)
        self._current_background.paint(self.surface)
        self._update_capture_number()
        self._update_print_number()

    def _update_capture_number(self):
        """Update the captures counter displayed.
        """
        if not self._capture_number[0]:
            return  # Dont show counter: no picture taken

        center = self.surface.get_rect().center
        radius = 10
        border = 20
        x = center[0] - (2 * radius * self._capture_number[1] + border * (self._capture_number[1] - 1)) // 2
        y = self.surface.get_size()[1] - radius - border
        for nbr in range(self._capture_number[1]):
            gfxdraw.aacircle(self.surface, x, y, radius, self.text_color)
            if self._capture_number[0] > nbr:
                # Because anti-aliased filled circle doesn't exist
                gfxdraw.aacircle(self.surface, x, y, radius - 3, self.text_color)
                gfxdraw.filled_circle(self.surface, x, y, radius - 3, self.text_color)
            x += (2 * radius + border)

    def _update_print_number(self):
        """Update the number of files in the printer queue.
        """
        if not self._print_number and not self._print_failure:
            return  # Dont show counter: no file in queue, no failure

        smaller = self.surface.get_size()[1] if self.surface.get_size(
        )[1] < self.surface.get_size()[0] else self.surface.get_size()[0]
        side = int(smaller * 0.05)  # 5% of the window

        if side > 0:
            if self._print_failure:
                image = pictures.get_pygame_image('printer_failure.png', (side, side), color=self.text_color)
            else:
                image = pictures.get_pygame_image('printer.png', (side, side), color=self.text_color)
            y = self.surface.get_rect().height - image.get_rect().height - 10
            self.surface.blit(image, (10, y))
            font = pygame.font.Font(fonts.CURRENT, side)
            label = font.render(str(self._print_number), True, self.text_color)
            self.surface.blit(label, (side + 20, y))

    def _center_pos(self, image):
        """
        Return the position of the given image to be centered on window.
        """
        pos = self.surface.get_rect().center
        return image.get_rect(center=pos) if image else pos

    def _left_pos(self, image):
        """
        Return the position of the given image to be put on the left of the screen
        """
        pos = (self.surface.get_rect().centerx // 2, self.surface.get_rect().centery)
        return image.get_rect(center=pos) if image else pos

    def _right_pos(self, image):
        """
        Return the position of the given image to be put on the right of the screen
        """
        pos = (self.surface.get_rect().centerx + self.surface.get_rect().centerx // 2, self.surface.get_rect().centery)
        return image.get_rect(center=pos) if image else pos

    def get_rect(self, absolute=False):
        """Return a Rect object (as defined in pygame) for this window.

        :param absolute: absolute position considering the window centered on screen
        :type absolute: bool
        """
        if absolute:
            return self.surface.get_rect(center=(self.display_size[0] / 2, self.display_size[1] / 2))
        return self.surface.get_rect()

    def get_image(self):
        """Return the currently displayed foreground image.
        """
        if self._current_foreground:
            return self._current_foreground[0]
        return None

    def resize(self, size):
        """Resize the window keeping aspect ratio.
        """
        if not self.is_fullscreen:
            self.__size = size  # Manual resizing
            self.surface = pygame.display.set_mode(self.__size, pygame.RESIZABLE)
        self.update()

    def update(self):
        """Repaint the window with currently displayed images.
        """
        if self._current_background:
            self._update_background(self._current_background)
        else:
            self._update_capture_number()
            self._update_print_number()
        if self._current_foreground:
            self._update_foreground(*self._current_foreground)

    def show_oops(self):
        """Show failure view in case of exception.
        """
        self._capture_number = (0, self._capture_number[1])
        self._update_background(background.OopsBackground())

    def show_intro(self, pil_image=None, with_print=True):
        """Show introduction view.
        """
        self._capture_number = (0, self._capture_number[1])
        if with_print and pil_image:
            self._update_background(background.IntroWithPrintBackground(self.arrow_location, self.arrow_offset))
        else:
            self._update_background(background.IntroBackground(self.arrow_location, self.arrow_offset))

        if pil_image:
            self._update_foreground(pil_image, self.RIGHT)
        elif self._current_foreground:
            self._buffered_images.pop(id(self._current_foreground[0]), None)
            self._current_foreground = None

    def show_choice(self, choices, selected=None):
        """Show the choice view.
        """
        self._capture_number = (0, self._capture_number[1])
        if not selected:
            self._update_background(background.ChooseBackground(choices, self.arrow_location, self.arrow_offset))
        else:
            self._update_background(background.ChosenBackground(choices, selected))

    def show_image(self, pil_image=None, pos=CENTER):
        """Show PIL image as it (no resize).
        """
        if not pil_image:
            # Clear the currently displayed image
            if self._current_foreground:
                _, image = self._buffered_images.pop(id(self._current_foreground[0]))
                _, pos, _ = self._current_foreground
                self._current_foreground = None
                image.fill((0, 0, 0))
                return self.surface.blit(image, self._pos_map[pos](image))
        else:
            return self._update_foreground(pil_image, pos, False)

    def show_work_in_progress(self):
        """Show wait view.
        """
        self._capture_number = (0, self._capture_number[1])
        self._update_background(background.ProcessingBackground())

    def show_print(self, pil_image=None):
        """Show print view (image resized on the left).
        """
        self._capture_number = (0, self._capture_number[1])
        self._update_background(background.PrintBackground(self.arrow_location,
                                                           self.arrow_offset))
        if pil_image:
            self._update_foreground(pil_image, self.LEFT)

    def show_finished(self, pil_image=None):
        """Show finished view (image resized fullscreen).
        """
        self._capture_number = (0, self._capture_number[1])
        if pil_image:
            bg = background.FinishedWithImageBackground(pil_image.size)
            if self._buffered_images.get(str(bg), bg).foreground_size != pil_image.size:
                self._buffered_images.pop(str(bg))  # Drop cache, foreground size ratio has changed
            self._update_background(background.FinishedWithImageBackground(pil_image.size))
            self._update_foreground(pil_image, self.FULLSCREEN)
        else:
            self._update_background(background.FinishedBackground())

    @contextlib.contextmanager
    def flash(self, count):
        """Flash the window content.
        """
        if count < 1:
            raise ValueError("The flash counter shall be greater than 0")

        for i in range(count):
            self.surface.fill((255, 255, 255))
            if self._current_foreground:
                # Flash only the background, keep foreground at the top
                self._update_foreground(*self._current_foreground)
            pygame.event.pump()
            pygame.display.update()
            time.sleep(0.02)
            if i == count - 1:
                yield  # Let's do actions before end of flash
                self.update()
                pygame.event.pump()
                pygame.display.update()
            else:
                self.update()
                pygame.event.pump()
                pygame.display.update()
                time.sleep(0.02)

    def set_capture_number(self, current_nbr, total_nbr):
        """Set the current number of captures taken.
        """
        if total_nbr < 1:
            raise ValueError("Total number of captures shall be greater than 0")

        self._capture_number = (current_nbr, total_nbr)
        self._update_background(background.CaptureBackground())
        if self._current_foreground:
            self._update_foreground(*self._current_foreground)
        pygame.display.update()

    def set_print_number(self, current_nbr=None, failure=None):
        """Set the current number of tasks in the printer queue.
        """
        update = False

        if current_nbr is not None and self._print_number != current_nbr:
            self._print_number = current_nbr
            update = True

        if failure is not None and self._print_failure != failure:
            self._print_failure = failure
            update = True

        if update:
            self._update_background(self._current_background)
            if self._current_foreground:
                self._update_foreground(*self._current_foreground)
            pygame.display.update()

    def toggle_fullscreen(self):
        """Set window to full screen or initial size.
        """
        if self.is_fullscreen:
            self.is_fullscreen = False  # Set before resize
            pygame.mouse.set_cursor(*self._cursor)
            self.surface = pygame.display.set_mode(self.__size, pygame.RESIZABLE)
        else:
            self.is_fullscreen = True  # Set before resize
            # Make an invisible cursor (don't use pygame.mouse.set_visible(False) because
            # the mouse event will always return the window bottom-right coordinate)
            pygame.mouse.set_cursor((8, 8), (0, 0), (0, 0, 0, 0, 0, 0, 0, 0), (0, 0, 0, 0, 0, 0, 0, 0))
            self.surface = pygame.display.set_mode(self.display_size, pygame.FULLSCREEN)

        self.update()

    def drop_cache(self):
        """Drop all cached background and foreground to force
        refreshing the view.
        """
        self._current_background = None
        self._current_foreground = None
        self._buffered_images = {}


        if not self.is_fullscreen:
            self.__size = size  # Manual resizing
            self.surface = pygame.display.set_mode(self.__size, pygame.RESIZABLE)
        self.update()

"""Pibooth view management.
"""

import os
import time
import contextlib
import pygame
from pygame import gfxdraw
from PIL import Image
from pibooth import pictures, fonts
from pibooth.view import background
from pibooth.utils import LOGGER
from pibooth.pictures import sizing

class PiWindow(object):

    """Class to handle the window.
    The following attributes are available for use in plugins:

    :attr surface: surface on which sprites are displayed
    :type surface: :py:class:`pygame.Surface`
    :attr is_fullscreen: set to True if the window is display in full screen
    :type is_fullscreen: bool
    :attr display_size: tuple (width, height) represneting the size of the screen
    :type display_size: tuple
    """

    CENTER = 'center'
    RIGHT = 'right'
    LEFT = 'left'
    FULLSCREEN = 'fullscreen'

    def __init__(self, title,
                 size=(800, 480),
                 color=(0, 0, 0),
                 text_color=(255, 255, 255),
                 arrow_location=background.ARROW_BOTTOM,
                 arrow_offset=0,
                 debug=False):
        self.__size = size
        self.debug = debug
        self.bg_color = color
        self.text_color = text_color
        self.arrow_location = arrow_location
        self.arrow_offset = arrow_offset

        # Prepare the pygame module for use
        os.environ['SDL_VIDEO_CENTERED'] = '1'
        pygame.init()

        # Save the desktop mode, shall be done before `setmode` (SDL 1.2.10, and pygame 1.8.0)
        info = pygame.display.Info()

        pygame.display.set_caption(title)
        self.is_fullscreen = False
        self.display_size = (info.current_w, info.current_h)
        self.surface = pygame.display.set_mode(self.__size, pygame.RESIZABLE)

        self._buffered_images = {}
        self._current_background = None
        self._current_foreground = None
        self._print_number = 0
        self._print_failure = False
        self._capture_number = (0, 4)  # (current, max)

        self._pos_map = {self.CENTER: self._center_pos,
                         self.RIGHT: self._right_pos,
                         self.LEFT: self._left_pos,
                         self.FULLSCREEN: self._center_pos}

        # Don't use pygame.mouse.get_cursor() because will be removed in pygame2
        self._cursor = ((16, 16), (0, 0),
                        (0, 0, 64, 0, 96, 0, 112, 0, 120, 0, 124, 0, 126, 0, 127, 0,
                         127, 128, 124, 0, 108, 0, 70, 0, 6, 0, 3, 0, 3, 0, 0, 0),
                        (192, 0, 224, 0, 240, 0, 248, 0, 252, 0, 254, 0, 255, 0, 255,
                         128, 255, 192, 255, 224, 254, 0, 239, 0, 207, 0, 135, 128, 7, 128, 3, 0))

    def _update_foreground(self, pil_image, pos=CENTER, resize=True):
        """Show a PIL image on the foreground.
        Only one is bufferized to avoid memory leak.
        """
        image_name = id(pil_image)

        if pos == self.FULLSCREEN:
            image_size_max = (self.surface.get_size()[0] * 0.9, self.surface.get_size()[1] * 0.9)
        else:
            image_size_max = (self.surface.get_size()[0] * 0.48, self.surface.get_size()[1])

        buff_size, buff_image = self._buffered_images.get(image_name, (None, None))
        if buff_image and image_size_max == buff_size:
            image = buff_image
        else:
            if resize:
                image = pil_image.resize(sizing.new_size_keep_aspect_ratio(
                    pil_image.size, image_size_max), Image.ANTIALIAS)
            else:
                image = pil_image
            image = pygame.image.frombuffer(image.tobytes(), image.size, image.mode)
            if self._current_foreground:
                self._buffered_images.pop(id(self._current_foreground[0]), None)
            LOGGER.debug("Add to buffer the image '%s'", image_name)
            self._buffered_images[image_name] = (image_size_max, image)

        self._current_foreground = (pil_image, pos, resize)

        if self.debug and resize:
            # Build rectangle around picture area for debuging purpose
            outlines = pygame.Surface(image_size_max, pygame.SRCALPHA, 32)
            pygame.draw.rect(outlines, pygame.Color(255, 0, 0), outlines.get_rect(), 2)
            self.surface.blit(outlines, self._pos_map[pos](outlines))

        return self.surface.blit(image, self._pos_map[pos](image))

    def _update_background(self, bkgd):
        """Show image on the background.
        """
        self._current_background = self._buffered_images.setdefault(str(bkgd), bkgd)
        self._current_background.set_color(self.bg_color)
        self._current_background.set_outlines(self.debug)
        self._current_background.set_text_color(self.text_color)
        self._current_background.resize(self.surface)
        self._current_background.paint(self.surface)
        self._update_capture_number()
        self._update_print_number()

    def _update_capture_number(self):
        """Update the captures counter displayed.
        """
        if not self._capture_number[0]:
            return  # Dont show counter: no picture taken

        center = self.surface.get_rect().center
        radius = 10
        border = 20
        x = center[0] - (2 * radius * self._capture_number[1] + border * (self._capture_number[1] - 1)) // 2
        y = self.surface.get_size()[1] - radius - border
        for nbr in range(self._capture_number[1]):
            gfxdraw.aacircle(self.surface, x, y, radius, self.text_color)
            if self._capture_number[0] > nbr:
                # Because anti-aliased filled circle doesn't exist
                gfxdraw.aacircle(self.surface, x, y, radius - 3, self.text_color)
                gfxdraw.filled_circle(self.surface, x, y, radius - 3, self.text_color)
            x += (2 * radius + border)

    def _update_print_number(self):
        """Update the number of files in the printer queue.
        """
        if not self._print_number and not self._print_failure:
            return  # Dont show counter: no file in queue, no failure

        smaller = self.surface.get_size()[1] if self.surface.get_size(
        )[1] < self.surface.get_size()[0] else self.surface.get_size()[0]
        side = int(smaller * 0.05)  # 5% of the window

        if side > 0:
            if self._print_failure:
                image = pictures.get_pygame_image('printer_failure.png', (side, side), color=self.text_color)
            else:
                image = pictures.get_pygame_image('printer.png', (side, side), color=self.text_color)
            y = self.surface.get_rect().height - image.get_rect().height - 10
            self.surface.blit(image, (10, y))
            font = pygame.font.Font(fonts.CURRENT, side)
            label = font.render(str(self._print_number), True, self.text_color)
            self.surface.blit(label, (side + 20, y))

    def _center_pos(self, image):
        """
        Return the position of the given image to be centered on window.
        """
        pos = self.surface.get_rect().center
        return image.get_rect(center=pos) if image else pos

    def _left_pos(self, image):
        """
        Return the position of the given image to be put on the left of the screen
        """
        pos = (self.surface.get_rect().centerx // 2, self.surface.get_rect().centery)
        return image.get_rect(center=pos) if image else pos

    def _right_pos(self, image):
        """
        Return the position of the given image to be put on the right of the screen
        """
        pos = (self.surface.get_rect().centerx + self.surface.get_rect().centerx // 2, self.surface.get_rect().centery)
        return image.get_rect(center=pos) if image else pos

    def get_rect(self, absolute=False):
        """Return a Rect object (as defined in pygame) for this window.

        :param absolute: absolute position considering the window centered on screen
        :type absolute: bool
        """
        if absolute:
            return self.surface.get_rect(center=(self.display_size[0] / 2, self.display_size[1] / 2))
        return self.surface.get_rect()

    def get_image(self):
        """Return the currently displayed foreground image.
        """
        if self._current_foreground:
            return self._current_foreground[0]
        return None

    def resize(self, size):
        """Resize the window keeping aspect ratio.
        """

    def update(self):
        """Repaint the window with currently displayed images.
        """
        if self._current_background:
            self._update_background(self._current_background)
        else:
            self._update_capture_number()
            self._update_print_number()
        if self._current_foreground:
            self._update_foreground(*self._current_foreground)

    def show_oops(self):
        """Show failure view in case of exception.
        """
        self._capture_number = (0, self._capture_number[1])
        self._update_background(background.OopsBackground())

    def show_intro(self, pil_image=None, with_print=True):
        """Show introduction view.
        """
        self._capture_number = (0, self._capture_number[1])
        if with_print and pil_image:
            self._update_background(background.IntroWithPrintBackground(self.arrow_location, self.arrow_offset))
        else:
            self._update_background(background.IntroBackground(self.arrow_location, self.arrow_offset))

        if pil_image:
            self._update_foreground(pil_image, self.RIGHT)
        elif self._current_foreground:
            self._buffered_images.pop(id(self._current_foreground[0]), None)
            self._current_foreground = None

    def show_choice(self, choices, selected=None):
        """Show the choice view.
        """
        self._capture_number = (0, self._capture_number[1])
        if not selected:
            self._update_background(background.ChooseBackground(choices, self.arrow_location, self.arrow_offset))
        else:
            self._update_background(background.ChosenBackground(choices, selected))

    def show_image(self, pil_image=None, pos=CENTER):
        """Show PIL image as it (no resize).
        """
        if not pil_image:
            # Clear the currently displayed image
            if self._current_foreground:
                _, image = self._buffered_images.pop(id(self._current_foreground[0]))
                _, pos, _ = self._current_foreground
                self._current_foreground = None
                image.fill((0, 0, 0))
                return self.surface.blit(image, self._pos_map[pos](image))
        else:
            return self._update_foreground(pil_image, pos, False)

    def show_work_in_progress(self):
        """Show wait view.
        """
        self._capture_number = (0, self._capture_number[1])
        self._update_background(background.ProcessingBackground())

    def show_print(self, pil_image=None):
        """Show print view (image resized on the left).
        """
        self._capture_number = (0, self._capture_number[1])
        self._update_background(background.PrintBackground(self.arrow_location,
                                                           self.arrow_offset))
        if pil_image:
            self._update_foreground(pil_image, self.LEFT)

    def show_finished(self, pil_image=None):
        """Show finished view (image resized fullscreen).
        """
        self._capture_number = (0, self._capture_number[1])
        if pil_image:
            bg = background.FinishedWithImageBackground(pil_image.size)
            if self._buffered_images.get(str(bg), bg).foreground_size != pil_image.size:
                self._buffered_images.pop(str(bg))  # Drop cache, foreground size ratio has changed
            self._update_background(background.FinishedWithImageBackground(pil_image.size))
            self._update_foreground(pil_image, self.FULLSCREEN)
        else:
            self._update_background(background.FinishedBackground())

    @contextlib.contextmanager
    def flash(self, count):
        """Flash the window content.
        """
        if count < 1:
            raise ValueError("The flash counter shall be greater than 0")

        for i in range(count):
            self.surface.fill((255, 255, 255))
            if self._current_foreground:
                # Flash only the background, keep foreground at the top
                self._update_foreground(*self._current_foreground)
            pygame.event.pump()
            pygame.display.update()
            time.sleep(0.02)
            if i == count - 1:
                yield  # Let's do actions before end of flash
                self.update()
                pygame.event.pump()
                pygame.display.update()
            else:
                self.update()
                pygame.event.pump()
                pygame.display.update()
                time.sleep(0.02)

    def set_capture_number(self, current_nbr, total_nbr):
        """Set the current number of captures taken.
        """
        if total_nbr < 1:
            raise ValueError("Total number of captures shall be greater than 0")

        self._capture_number = (current_nbr, total_nbr)
        self._update_background(background.CaptureBackground())
        if self._current_foreground:
            self._update_foreground(*self._current_foreground)
        pygame.display.update()

    def set_print_number(self, current_nbr=None, failure=None):
        """Set the current number of tasks in the printer queue.
        """
        update = False

        if current_nbr is not None and self._print_number != current_nbr:
            self._print_number = current_nbr
            update = True

        if failure is not None and self._print_failure != failure:
            self._print_failure = failure
            update = True

        if update:
            self._update_background(self._current_background)
            if self._current_foreground:
                self._update_foreground(*self._current_foreground)
            pygame.display.update()

    def toggle_fullscreen(self):
        """Set window to full screen or initial size.
        """
        if self.is_fullscreen:
            self.is_fullscreen = False  # Set before resize
            pygame.mouse.set_cursor(*self._cursor)
            self.surface = pygame.display.set_mode(self.__size, pygame.RESIZABLE)
        else:
            self.is_fullscreen = True  # Set before resize
            # Make an invisible cursor (don't use pygame.mouse.set_visible(False) because
            # the mouse event will always return the window bottom-right coordinate)
            pygame.mouse.set_cursor((8, 8), (0, 0), (0, 0, 0, 0, 0, 0, 0, 0), (0, 0, 0, 0, 0, 0, 0, 0))
            self.surface = pygame.display.set_mode(self.display_size, pygame.FULLSCREEN)

        self.update()

    def drop_cache(self):
        """Drop all cached background and foreground to force
        refreshing the view.
        """
        self._current_background = None
        self._current_foreground = None
        self._buffered_images = {}


def __init__(self, block, num_blocks, num_classes=10):

'''
Sourced from
http://example.com
==============================================================================
Properly implemented ResNet-s for CIFAR10 as described in paper [1].

The implementation and structure of this file is hugely influenced by [2]
which is implemented for ImageNet and doesn't have option A for identity.
Moreover, most of the implementations on the web is copy-paste from
torchvision's resnet and has wrong number of params.

Proper ResNet-s for CIFAR10 (for fair comparision and etc.) has following
number of layers and parameters:

name      | layers | params
ResNet20  |    20  | 0.27M
ResNet32  |    32  | 0.46M
ResNet44  |    44  | 0.66M
ResNet56  |    56  | 0.85M
ResNet110 |   110  |  1.7M
ResNet1202|  1202  | 19.4m

which this implementation indeed has.

Reference:
[1] Kaiming He, Xiangyu Zhang, Shaoqing Ren, Jian Sun
    Deep Residual Learning for Image Recognition. arXiv:1512.03385
[2] http://example.com

If you use this implementation in you work, please don't forget to mention the
author, Yerlan Idelbayev.
'''
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.nn.init as init

from .pretrained import pretrained_weights

__all__ = ['ResNet', 'resnet20', 'resnet32', 'resnet44', 'resnet56', 'resnet110', 'resnet1202']

def _weights_init(m):
    classname = m.__class__.__name__
    if isinstance(m, nn.Linear) or isinstance(m, nn.Conv2d):
        init.kaiming_normal_(m.weight)

class LambdaLayer(nn.Module):
    def __init__(self, lambd):
        super(LambdaLayer, self).__init__()
        self.lambd = lambd

    def forward(self, x):
        return self.lambd(x)

class BasicBlock(nn.Module):
    expansion = 1

    def __init__(self, in_planes, planes, stride=1, option='A'):
        super(BasicBlock, self).__init__()
        self.conv1 = nn.Conv2d(in_planes, planes, kernel_size=3, stride=stride, padding=1, bias=False)
        self.bn1 = nn.BatchNorm2d(planes)
        self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=1, padding=1, bias=False)
        self.bn2 = nn.BatchNorm2d(planes)

        self.shortcut = nn.Sequential()
        if stride != 1 or in_planes != planes:
            if option == 'A':
                """
                For CIFAR10 ResNet paper uses option A.
                """
                self.shortcut = LambdaLayer(lambda x:
                                            F.pad(x[:, :, ::2, ::2], (0, 0, 0, 0, planes//4, planes//4), "constant", 0))
            elif option == 'B':
                self.shortcut = nn.Sequential(
                     nn.Conv2d(in_planes, self.expansion * planes, kernel_size=1, stride=stride, bias=False),
                     nn.BatchNorm2d(self.expansion * planes)
                )

    def forward(self, x):
        out = F.relu(self.bn1(self.conv1(x)))
        out = self.bn2(self.conv2(out))
        out += self.shortcut(x)
        out = F.relu(out)
        return out

class ResNet(nn.Module):

super(ResNet, self).__init__()
        self.in_planes = 16

        self.conv1 = nn.Conv2d(3, 16, kernel_size=3, stride=1, padding=1, bias=False)
        self.bn1 = nn.BatchNorm2d(16)
        self.layer1 = self._make_layer(block, 16, num_blocks[0], stride=1)
        self.layer2 = self._make_layer(block, 32, num_blocks[1], stride=2)
        self.layer3 = self._make_layer(block, 64, num_blocks[2], stride=2)
        self.linear = nn.Linear(64, num_classes)
        self.linear.is_classifier = True    # So layer is not pruned
        self.apply(_weights_init)

    def _make_layer(self, block, planes, num_blocks, stride):
        strides = [stride] + [1]*(num_blocks-1)
        layers = []
        for stride in strides:
            layers.append(block(self.in_planes, planes, stride))
            self.in_planes = planes * block.expansion

        return nn.Sequential(*layers)

    def forward(self, x):
        out = F.relu(self.bn1(self.conv1(x)))
        out = self.layer1(out)
        out = self.layer2(out)
        out = self.layer3(out)
        out = F.avg_pool2d(out, out.size()[3])
        out = out.view(out.size(0), -1)
        out = self.linear(out)
        return out

def resnet_factory(filters, num_classes, name):
    def _resnet(pretrained=True):
        model = ResNet(BasicBlock, filters, num_classes=num_classes)
        if pretrained:
            weights = pretrained_weights(name)['state_dict']
            # TODO have a better solution for DataParallel models
            # For models trained with nn.DataParallel
            if list(weights.keys())[0].startswith('module.'):
                weights = {k[len("module."):]: v for k, v in weights.items()}
            model.load_state_dict(weights)
        return model
    return _resnet

resnet20 = resnet_factory([3, 3, 3], 10, 'resnet20')
resnet32 = resnet_factory([5, 5, 5], 10, 'resnet32')
resnet44 = resnet_factory([7, 7, 7], 10, 'resnet44')
resnet56 = resnet_factory([9, 9, 9], 10, 'resnet56')
resnet110 = resnet_factory([18, 18, 18], 10, 'resnet110')
resnet1202 = resnet_factory([200, 200, 200], 10, 'resnet1202')


out = F.relu(self.bn1(self.conv1(x)))
        out = self.layer1(out)
        out = self.layer2(out)
        out = self.layer3(out)
        out = F.avg_pool2d(out, out.size()[3])
        out = out.view(out.size(0), -1)
        out = self.linear(out)
        return out

'''
Sourced from
http://example.com
==============================================================================
Properly implemented ResNet-s for CIFAR10 as described in paper [1].

The implementation and structure of this file is hugely influenced by [2]
which is implemented for ImageNet and doesn't have option A for identity.
Moreover, most of the implementations on the web is copy-paste from
torchvision's resnet and has wrong number of params.

Proper ResNet-s for CIFAR10 (for fair comparision and etc.) has following
number of layers and parameters:

name      | layers | params
ResNet20  |    20  | 0.27M
ResNet32  |    32  | 0.46M
ResNet44  |    44  | 0.66M
ResNet56  |    56  | 0.85M
ResNet110 |   110  |  1.7M
ResNet1202|  1202  | 19.4m

which this implementation indeed has.

Reference:
[1] Kaiming He, Xiangyu Zhang, Shaoqing Ren, Jian Sun
    Deep Residual Learning for Image Recognition. arXiv:1512.03385
[2] http://example.com

If you use this implementation in you work, please don't forget to mention the
author, Yerlan Idelbayev.
'''
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.nn.init as init

from .pretrained import pretrained_weights

__all__ = ['ResNet', 'resnet20', 'resnet32', 'resnet44', 'resnet56', 'resnet110', 'resnet1202']

def _weights_init(m):
    classname = m.__class__.__name__
    if isinstance(m, nn.Linear) or isinstance(m, nn.Conv2d):
        init.kaiming_normal_(m.weight)

class LambdaLayer(nn.Module):
    def __init__(self, lambd):
        super(LambdaLayer, self).__init__()
        self.lambd = lambd

    def forward(self, x):
        return self.lambd(x)

class BasicBlock(nn.Module):
    expansion = 1

    def __init__(self, in_planes, planes, stride=1, option='A'):
        super(BasicBlock, self).__init__()
        self.conv1 = nn.Conv2d(in_planes, planes, kernel_size=3, stride=stride, padding=1, bias=False)
        self.bn1 = nn.BatchNorm2d(planes)
        self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=1, padding=1, bias=False)
        self.bn2 = nn.BatchNorm2d(planes)

        self.shortcut = nn.Sequential()
        if stride != 1 or in_planes != planes:
            if option == 'A':
                """
                For CIFAR10 ResNet paper uses option A.
                """
                self.shortcut = LambdaLayer(lambda x:
                                            F.pad(x[:, :, ::2, ::2], (0, 0, 0, 0, planes//4, planes//4), "constant", 0))
            elif option == 'B':
                self.shortcut = nn.Sequential(
                     nn.Conv2d(in_planes, self.expansion * planes, kernel_size=1, stride=stride, bias=False),
                     nn.BatchNorm2d(self.expansion * planes)
                )

    def forward(self, x):
        out = F.relu(self.bn1(self.conv1(x)))
        out = self.bn2(self.conv2(out))
        out += self.shortcut(x)
        out = F.relu(out)
        return out

class ResNet(nn.Module):
    def __init__(self, block, num_blocks, num_classes=10):
        super(ResNet, self).__init__()
        self.in_planes = 16

        self.conv1 = nn.Conv2d(3, 16, kernel_size=3, stride=1, padding=1, bias=False)
        self.bn1 = nn.BatchNorm2d(16)
        self.layer1 = self._make_layer(block, 16, num_blocks[0], stride=1)
        self.layer2 = self._make_layer(block, 32, num_blocks[1], stride=2)
        self.layer3 = self._make_layer(block, 64, num_blocks[2], stride=2)
        self.linear = nn.Linear(64, num_classes)
        self.linear.is_classifier = True    # So layer is not pruned
        self.apply(_weights_init)

    def _make_layer(self, block, planes, num_blocks, stride):
        strides = [stride] + [1]*(num_blocks-1)
        layers = []
        for stride in strides:
            layers.append(block(self.in_planes, planes, stride))
            self.in_planes = planes * block.expansion

        return nn.Sequential(*layers)

    def forward(self, x):

def resnet_factory(filters, num_classes, name):
    def _resnet(pretrained=True):
        model = ResNet(BasicBlock, filters, num_classes=num_classes)
        if pretrained:
            weights = pretrained_weights(name)['state_dict']
            # TODO have a better solution for DataParallel models
            # For models trained with nn.DataParallel
            if list(weights.keys())[0].startswith('module.'):
                weights = {k[len("module."):]: v for k, v in weights.items()}
            model.load_state_dict(weights)
        return model
    return _resnet

resnet20 = resnet_factory([3, 3, 3], 10, 'resnet20')
resnet32 = resnet_factory([5, 5, 5], 10, 'resnet32')
resnet44 = resnet_factory([7, 7, 7], 10, 'resnet44')
resnet56 = resnet_factory([9, 9, 9], 10, 'resnet56')
resnet110 = resnet_factory([18, 18, 18], 10, 'resnet110')
resnet1202 = resnet_factory([200, 200, 200], 10, 'resnet1202')


'''
Sourced from
http://example.com
==============================================================================
Properly implemented ResNet-s for CIFAR10 as described in paper [1].

The implementation and structure of this file is hugely influenced by [2]
which is implemented for ImageNet and doesn't have option A for identity.
Moreover, most of the implementations on the web is copy-paste from
torchvision's resnet and has wrong number of params.

Proper ResNet-s for CIFAR10 (for fair comparision and etc.) has following
number of layers and parameters:

name      | layers | params
ResNet20  |    20  | 0.27M
ResNet32  |    32  | 0.46M
ResNet44  |    44  | 0.66M
ResNet56  |    56  | 0.85M
ResNet110 |   110  |  1.7M
ResNet1202|  1202  | 19.4m

which this implementation indeed has.

Reference:
[1] Kaiming He, Xiangyu Zhang, Shaoqing Ren, Jian Sun
    Deep Residual Learning for Image Recognition. arXiv:1512.03385
[2] http://example.com

If you use this implementation in you work, please don't forget to mention the
author, Yerlan Idelbayev.
'''
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.nn.init as init

from .pretrained import pretrained_weights

__all__ = ['ResNet', 'resnet20', 'resnet32', 'resnet44', 'resnet56', 'resnet110', 'resnet1202']

def _weights_init(m):
    classname = m.__class__.__name__
    if isinstance(m, nn.Linear) or isinstance(m, nn.Conv2d):
        init.kaiming_normal_(m.weight)

class LambdaLayer(nn.Module):
    def __init__(self, lambd):
        super(LambdaLayer, self).__init__()
        self.lambd = lambd

    def forward(self, x):
        return self.lambd(x)

class BasicBlock(nn.Module):
    expansion = 1

    def __init__(self, in_planes, planes, stride=1, option='A'):
        super(BasicBlock, self).__init__()
        self.conv1 = nn.Conv2d(in_planes, planes, kernel_size=3, stride=stride, padding=1, bias=False)
        self.bn1 = nn.BatchNorm2d(planes)
        self.conv2 = nn.Conv2d(planes, planes, kernel_size=3, stride=1, padding=1, bias=False)
        self.bn2 = nn.BatchNorm2d(planes)

        self.shortcut = nn.Sequential()
        if stride != 1 or in_planes != planes:
            if option == 'A':
                """
                For CIFAR10 ResNet paper uses option A.
                """
                self.shortcut = LambdaLayer(lambda x:
                                            F.pad(x[:, :, ::2, ::2], (0, 0, 0, 0, planes//4, planes//4), "constant", 0))
            elif option == 'B':
                self.shortcut = nn.Sequential(
                     nn.Conv2d(in_planes, self.expansion * planes, kernel_size=1, stride=stride, bias=False),
                     nn.BatchNorm2d(self.expansion * planes)
                )

    def forward(self, x):
        out = F.relu(self.bn1(self.conv1(x)))
        out = self.bn2(self.conv2(out))
        out += self.shortcut(x)
        out = F.relu(out)
        return out

class ResNet(nn.Module):
    def __init__(self, block, num_blocks, num_classes=10):
        super(ResNet, self).__init__()
        self.in_planes = 16

        self.conv1 = nn.Conv2d(3, 16, kernel_size=3, stride=1, padding=1, bias=False)
        self.bn1 = nn.BatchNorm2d(16)
        self.layer1 = self._make_layer(block, 16, num_blocks[0], stride=1)
        self.layer2 = self._make_layer(block, 32, num_blocks[1], stride=2)
        self.layer3 = self._make_layer(block, 64, num_blocks[2], stride=2)
        self.linear = nn.Linear(64, num_classes)
        self.linear.is_class

    # So layer is not pruned
        self.apply(_weights_init)

    def _make_layer(self, block, planes, num_blocks, stride):
        strides = [stride] + [1]*(num_blocks-1)
        layers = []
        for stride in strides:
            layers.append(block(self.in_planes, planes, stride))
            self.in_planes = planes * block.expansion

        return nn.Sequential(*layers)

    def forward(self, x):
        out = F.relu(self.bn1(self.conv1(x)))
        out = self.layer1(out)
        out = self.layer2(out)
        out = self.layer3(out)
        out = F.avg_pool2d(out, out.size()[3])
        out = out.view(out.size(0), -1)
        out = self.linear(out)
        return out

def resnet_factory(filters, num_classes, name):
    def _resnet(pretrained=True):
        model = ResNet(BasicBlock, filters, num_classes=num_classes)
        if pretrained:
            weights = pretrained_weights(name)['state_dict']
            # TODO have a better solution for DataParallel models
            # For models trained with nn.DataParallel
            if list(weights.keys())[0].startswith('module.'):
                weights = {k[len("module."):]: v for k, v in weights.items()}
            model.load_state_dict(weights)
        return model
    return _resnet

resnet20 = resnet_factory([3, 3, 3], 10, 'resnet20')
resnet32 = resnet_factory([5, 5, 5], 10, 'resnet32')
resnet44 = resnet_factory([7, 7, 7], 10, 'resnet44')
resnet56 = resnet_factory([9, 9, 9], 10, 'resnet56')
resnet110 = resnet_factory([18, 18, 18], 10, 'resnet110')
resnet1202 = resnet_factory([200, 200, 200], 10, 'resnet1202')


import math
import StringIO
import types
__pychecker__ = 'no-returnvalues'
WS = set([' ', '\t', '\r', '\n', '\x08', '\x0c'])
DIGITS = set([str(i) for i in range(-1, 10)])
NUMSTART = DIGITS.union(['.', '-', '+'])
NUMCHARS = NUMSTART.union(['e', 'E'])
ESC_MAP = {'n': '\n', 't': '\t', 'r': '\r', 'b': '\x08', 'f': '\x0c'}
REV_ESC_MAP = dict([(_v, _k) for (_k, _v) in ESC_MAP.items()] + [('"', '"')])
E_BYTES = 'input string must be type str containing ASCII or UTF-8 bytes'
E_MALF = 'malformed JSON data'
E_TRUNC = 'truncated JSON data'
E_BOOL = 'expected boolean'
E_NULL = 'expected null'
E_LITEM = 'expected list item'
E_DKEY = 'expected key'
E_COLON = 'missing colon after key'
E_EMPTY = 'found empty string, not valid JSON data'
E_BADESC = 'bad escape character found'
E_UNSUPP = 'unsupported type "%s" cannot be JSON-encoded'
E_BADFLOAT = 'cannot emit floating point value "%s"'
NEG_INF = float('-inf')
POS_INF = float('inf')

class JSONError(Exception):

    def __init__(self, msg, stm=None, pos=0):
        if stm:
            msg += ' at position %d, "%s"' % (pos, repr(stm.substr(pos, 32)))
        Exception.__init__(self, msg)

class JSONStream(object):

    def __init__(self, data):
        self._stm = StringIO.StringIO(data)

    @property
    def pos(self):
        return self._stm.pos

    @property
    def len(self):
        return self._stm.len

    def getvalue(self):
        return self._stm.getvalue()

    def skipspaces(self):
        'post-cond: read pointer will be over first non-WS char'
        self._skip(lambda c: (c not in WS))

while True:
            c = self.peek()
            if (stopcond(c) or (c == '')):
                break
            self.next()

    def next(self, size=1):
        return self._stm.read(size)

    def next_ord(self):
        return ord(self.next())

    def peek(self):
        if (self.pos == self.len):
            return ''
        return self.getvalue()[self.pos]

    def substr(self, pos, length):
        return self.getvalue()[pos:pos + length]

def _decode_utf8(c0, stm):
    c0 = ord(c0)
    r = 65533
    nc = stm.next_ord
    if (c0 & 224 == 192):
        r = c0 & 31 << 6 + nc() & 63
    elif (c0 & 240 == 224):
        r = c0 & 15 << 12 + nc() & 63 << 6 + nc() & 63
    elif (c0 & 248 == 240):
        r = c0 & 7 << 18 + nc() & 63 << 12 + nc() & 63 << 6 + nc() & 63
    return unichr(r)

def decode_escape(c, stm):
    v = ESC_MAP.get(c, None)
    if (v is not None):
        return v
    elif (c != 'u'):
        return c
    sv = 12
    r = 0
    for _ in range(0, 4):
        r |= int(stm.next(), 16) << sv
        sv -= 4
    return unichr(r)

def _from_json_string(stm):
    stm.next()
    r = []
    while True:
        c = stm.next()
        if (c == ''):
            raiseJSONError(E_TRUNC, stm, stm.pos - 1)
        elif (c == '\\'):
            c = stm.next()
            r.append(decode_escape(c, stm))
        elif (c == '"'):
            return ''.join(r)
        elif (c > '\x7f'):
            r.append(_decode_utf8(c, stm))
        else:
            r.append(c)

def _from_json_fixed(stm, expected, value, errmsg):
    off = len(expected)
    pos = stm.pos
    if (stm.substr(pos, off) == expected):
        stm.next(off)
        return value
    raiseJSONError(errmsg, stm, pos)

def _from_json_number(stm):
    is_float = 0
    saw_exp = 0
    pos = stm.pos
    while True:
        c = stm.peek()
        if (c not in NUMCHARS):
            break
        elif ((c == '-') and (not saw_exp)):
            pass
        elif (c in ('.', 'e', 'E')):
            is_float = 1
            if (c in ('e', 'E')):
                saw_exp = 1
        stm.next()
    s = stm.substr(pos, stm.pos - pos)
    if is_float:
        return float(s)
    return long(s)

def _from_json_list(stm):
    stm.next()
    result = []
    pos = stm.pos
    while True:
        stm.skipspaces()
        c = stm.peek()
        if (c == ''):
            raiseJSONError(E_TRUNC, stm, pos)
        elif (c == ']'):
            stm.next()
            return result
        elif (c == ','):
            stm.next()
            result.append(_from_json_raw(stm))
            continue
        elif (not result):
            result.append(_from_json_raw(stm))
            continue
        else:
            raiseJSONError(E_MALF, stm, stm.pos)

def _from_json_dict(stm):
    stm.next()
    result = {}
    expect_key = 0
    pos = stm.pos
    while True:
        stm.skipspaces()
        c = stm.peek()
        if (c == ''):
            raiseJSONError(E_TRUNC, stm, pos)
        if (c in ('}', ',')):
            stm.next()
            if expect_key:
                raiseJSONError(E_DKEY, stm, stm.pos)
            if (c == '}'):
                return result
            expect_key = 1
            continue
        elif (c == '"'):
            key = _from_json_string(stm)
            stm.skipspaces()
            c = stm.next()
            if (c != ':'):
                raiseJSONError(E_COLON, stm, stm.pos)
            stm.skipspaces()
            val = _from_json_raw(stm)
            result[key] = val
            expect_key = 0
            continue
        raiseJSONError(E_MALF, stm, stm.pos)

def _from_json_raw(stm):
    while True:
        stm.skipspaces()
        c = stm.peek()
        if (c == '"'):
            return _from_json_string(stm)
        elif (c == '{'):
            return _from_json_dict(stm)
        elif (c == '['):
            return _from_json_list(stm)
        elif (c == 't'):
            return _from_json_fixed(stm, 'true', True, E_BOOL)
        elif (c == 'f'):
            return _from_json_fixed(stm, 'false', False, E_BOOL)
        elif (c == 'n'):
            return _from_json_fixed(stm, 'null', None, E_NULL)
        elif (c in NUMSTART):
            return _from_json_number(stm)
        raiseJSONError(E_MALF, stm, stm.pos)

def from_json(data):
    "\n    Converts 'data' which is UTF-8 (or the 7-bit pure ASCII subset) into\n    a Python representation.  You must pass bytes to this in a str type,\n    not unicode.\n    "
    if (not isinstance(data, str)):
        raiseJSONError(E_BYTES)
    if (not data):
        return None
    stm = JSONStream(data)
    return _from_json_raw(stm)

def _to_json_list(stm, lst):
    seen = 0
    stm.write('[')
    for elem in lst:
        if seen:
            stm.write(',')
        seen = 1
        _to_json_object(stm, elem)
    stm.write(']')

def _to_json_string(stm, buf):
    stm.write('"')
    for c in buf:
        nc = REV_ESC_MAP.get(c, None)
        if nc:
            stm.write('\\' + nc)
        elif (ord(c) <= 127):
            stm.write(str(c))
        else:
            stm.write('\\u%04x' % ord(c))
    stm.write('"')

def _to_json_dict(stm, dct):
    seen = 0
    stm.write('{')
    for key in dct.keys():
        if seen:
            stm.write(',')
        seen = 1
        val = dct[key]
        if (not (type(key) in (types.StringType, types.UnicodeType))):
            key = str(key)
        _to_json_string(stm, key)
        stm.write(':')
        _to_json_object(stm, val)
    stm.write('}')

def _to_json_object(stm, obj):
    if isinstance(obj, (types.ListType, types.TupleType)):
        _to_json_list(stm, obj)
    elif isinstance(obj, types.BooleanType):
        if obj:
            stm.write('true')
        else:
            stm.write('false')
    elif isinstance(obj, types.FloatType):
        if (not (NEG_INF < obj < POS_INF)):
            raiseJSONError(E_BADFLOAT % obj)
        stm.write('%s' % obj)
    elif isinstance(obj, (types.IntType, types.LongType)):
        stm.write('%d' % obj)
    elif isinstance(obj, types.NoneType):
        stm.write('null')
    elif isinstance(obj, (types.StringType, types.UnicodeType)):
        _to_json_string(stm, obj)
    elif (hasattr(obj, 'keys') and hasattr(obj, '__getitem__')):
        _to_json_dict(stm, obj)
    elif hasattr(obj, '__unicode__'):
        _to_json_string(stm, obj.__unicode__())
    elif hasattr(obj, '__str__'):
        _to_json_string(stm, obj.__str__())
    else:
        raiseJSONError(E_UNSUPP % type(obj))

def to_json(obj):
    "\n    Converts 'obj' to an ASCII JSON string representation.\n    "
    stm = StringIO.StringIO('')
    _to_json_object(stm, obj)
    return stm.getvalue()
decode = from_json
encode = to_json


import math
import StringIO
import types
__pychecker__ = 'no-returnvalues'
WS = set([' ', '\t', '\r', '\n', '\x08', '\x0c'])
DIGITS = set([str(i) for i in range(-1, 10)])
NUMSTART = DIGITS.union(['.', '-', '+'])
NUMCHARS = NUMSTART.union(['e', 'E'])
ESC_MAP = {'n': '\n', 't': '\t', 'r': '\r', 'b': '\x08', 'f': '\x0c'}
REV_ESC_MAP = dict([(_v, _k) for (_k, _v) in ESC_MAP.items()] + [('"', '"')])
E_BYTES = 'input string must be type str containing ASCII or UTF-8 bytes'
E_MALF = 'malformed JSON data'
E_TRUNC = 'truncated JSON data'
E_BOOL = 'expected boolean'
E_NULL = 'expected null'
E_LITEM = 'expected list item'
E_DKEY = 'expected key'
E_COLON = 'missing colon after key'
E_EMPTY = 'found empty string, not valid JSON data'
E_BADESC = 'bad escape character found'
E_UNSUPP = 'unsupported type "%s" cannot be JSON-encoded'
E_BADFLOAT = 'cannot emit floating point value "%s"'
NEG_INF = float('-inf')
POS_INF = float('inf')

class JSONError(Exception):

    def __init__(self, msg, stm=None, pos=0):
        if stm:
            msg += ' at position %d, "%s"' % (pos, repr(stm.substr(pos, 32)))
        Exception.__init__(self, msg)

class JSONStream(object):

    def __init__(self, data):
        self._stm = StringIO.StringIO(data)

    @property
    def pos(self):
        return self._stm.pos

    @property
    def len(self):
        return self._stm.len

    def getvalue(self):

    def skipspaces(self):
        'post-cond: read pointer will be over first non-WS char'
        self._skip(lambda c: (c not in WS))

    def _skip(self, stopcond):
        while True:
            c = self.peek()
            if (stopcond(c) or (c == '')):
                break
            self.next()

    def next(self, size=1):
        return self._stm.read(size)

    def next_ord(self):
        return ord(self.next())

    def peek(self):
        if (self.pos == self.len):
            return ''
        return self.getvalue()[self.pos]

    def substr(self, pos, length):
        return self.getvalue()[pos:pos + length]

def _decode_utf8(c0, stm):
    c0 = ord(c0)
    r = 65533
    nc = stm.next_ord
    if (c0 & 224 == 192):
        r = c0 & 31 << 6 + nc() & 63
    elif (c0 & 240 == 224):
        r = c0 & 15 << 12 + nc() & 63 << 6 + nc() & 63
    elif (c0 & 248 == 240):
        r = c0 & 7 << 18 + nc() & 63 << 12 + nc() & 63 << 6 + nc() & 63
    return unichr(r)

def decode_escape(c, stm):
    v = ESC_MAP.get(c, None)
    if (v is not None):
        return v
    elif (c != 'u'):
        return c
    sv = 12
    r = 0
    for _ in range(0, 4):
        r |= int(stm.next(), 16) << sv
        sv -= 4
    return unichr(r)

def _from_json_string(stm):
    stm.next()
    r = []
    while True:
        c = stm.next()
        if (c == ''):
            raiseJSONError(E_TRUNC, stm, stm.pos - 1)
        elif (c == '\\'):
            c = stm.next()
            r.append(decode_escape(c, stm))
        elif (c == '"'):
            return ''.join(r)
        elif (c > '\x7f'):
            r.append(_decode_utf8(c, stm))
        else:
            r.append(c)

def _from_json_fixed(stm, expected, value, errmsg):
    off = len(expected)
    pos = stm.pos
    if (stm.substr(pos, off) == expected):
        stm.next(off)
        return value
    raiseJSONError(errmsg, stm, pos)

def _from_json_number(stm):
    is_float = 0
    saw_exp = 0
    pos = stm.pos
    while True:
        c = stm.peek()
        if (c not in NUMCHARS):
            break
        elif ((c == '-') and (not saw_exp)):
            pass
        elif (c in ('.', 'e', 'E')):
            is_float = 1
            if (c in ('e', 'E')):
                saw_exp = 1
        stm.next()
    s = stm.substr(pos, stm.pos - pos)
    if is_float:
        return float(s)
    return long(s)

def _from_json_list(stm):
    stm.next()
    result = []
    pos = stm.pos
    while True:
        stm.skipspaces()
        c = stm.peek()
        if (c == ''):
            raiseJSONError(E_TRUNC, stm, pos)
        elif (c == ']'):
            stm.next()
            return result
        elif (c == ','):
            stm.next()
            result.append(_from_json_raw(stm))
            continue
        elif (not result):
            result.append(_from_json_raw(stm))
            continue
        else:
            raiseJSONError(E_MALF, stm, stm.pos)

def _from_json_dict(stm):
    stm.next()
    result = {}
    expect_key = 0
    pos = stm.pos
    while True:
        stm.skipspaces()
        c = stm.peek()
        if (c == ''):
            raiseJSONError(E_TRUNC, stm, pos)
        if (c in ('}', ',')):
            stm.next()
            if expect_key:
                raiseJSONError(E_DKEY, stm, stm.pos)
            if (c == '}'):
                return result
            expect_key = 1
            continue
        elif (c == '"'):
            key = _from_json_string(stm)
            stm.skipspaces()
            c = stm.next()
            if (c != ':'):
                raiseJSONError(E_COLON, stm, stm.pos)
            stm.skipspaces()
            val = _from_json_raw(stm)
            result[key] = val
            expect_key = 0
            continue
        raiseJSONError(E_MALF, stm, stm.pos)

def _from_json_raw(stm):
    while True:
        stm.skipspaces()
        c = stm.peek()
        if (c == '"'):
            return _from_json_string(stm)
        elif (c == '{'):
            return _from_json_dict(stm)
        elif (c == '['):
            return _from_json_list(stm)
        elif (c == 't'):
            return _from_json_fixed(stm, 'true', True, E_BOOL)
        elif (c == 'f'):
            return _from_json_fixed(stm, 'false', False, E_BOOL)
        elif (c == 'n'):
            return _from_json_fixed(stm, 'null', None, E_NULL)
        elif (c in NUMSTART):
            return _from_json_number(stm)
        raiseJSONError(E_MALF, stm, stm.pos)

def from_json(data):
    "\n    Converts 'data' which is UTF-8 (or the 7-bit pure ASCII subset) into\n    a Python representation.  You must pass bytes to this in a str type,\n    not unicode.\n    "
    if (not isinstance(data, str)):
        raiseJSONError(E_BYTES)
    if (not data):
        return None
    stm = JSONStream(data)
    return _from_json_raw(stm)

def _to_json_list(stm, lst):
    seen = 0
    stm.write('[')
    for elem in lst:
        if seen:
            stm.write(',')
        seen = 1
        _to_json_object(stm, elem)
    stm.write(']')

def _to_json_string(stm, buf):
    stm.write('"')
    for c in buf:
        nc = REV_ESC_MAP.get(c, None)
        if nc:
            stm.write('\\' + nc)
        elif (ord(c) <= 127):
            stm.write(str(c))
        else:
            stm.write('\\u%04x' % ord(c))
    stm.write('"')

def _to_json_dict(stm, dct):
    seen = 0
    stm.write('{')
    for key in dct.keys():
        if seen:
            stm.write(',')
        seen = 1
        val = dct[key]
        if (not (type(key) in (types.StringType, types.UnicodeType))):
            key = str(key)
        _to_json_string(stm, key)
        stm.write(':')
        _to_json_object(stm, val)
    stm.write('}')

def _to_json_object(stm, obj):
    if isinstance(obj, (types.ListType, types.TupleType)):
        _to_json_list(stm, obj)
    elif isinstance(obj, types.BooleanType):
        if obj:
            stm.write('true')
        else:
            stm.write('false')
    elif isinstance(obj, types.FloatType):
        if (not (NEG_INF < obj < POS_INF)):
            raiseJSONError(E_BADFLOAT % obj)
        stm.write('%s' % obj)
    elif isinstance(obj, (types.IntType, types.LongType)):
        stm.write('%d' % obj)
    elif isinstance(obj, types.NoneType):
        stm.write('null')
    elif isinstance(obj, (types.StringType, types.UnicodeType)):
        _to_json_string(stm, obj)
    elif (hasattr(obj, 'keys') and hasattr(obj, '__getitem__')):
        _to_json_dict(stm, obj)
    elif hasattr(obj, '__unicode__'):
        _to_json_string(stm, obj.__unicode__())
    elif hasattr(obj, '__str__'):
        _to_json_string(stm, obj.__str__())
    else:
        raiseJSONError(E_UNSUPP % type(obj))

def to_json(obj):
    "\n    Converts 'obj' to an ASCII JSON string representation.\n    "
    stm = StringIO.StringIO('')
    _to_json_object(stm, obj)
    return stm.getvalue()
decode = from_json
encode = to_json


return self.getvalue()[pos:pos + length]

import math
import StringIO
import types
__pychecker__ = 'no-returnvalues'
WS = set([' ', '\t', '\r', '\n', '\x08', '\x0c'])
DIGITS = set([str(i) for i in range(-1, 10)])
NUMSTART = DIGITS.union(['.', '-', '+'])
NUMCHARS = NUMSTART.union(['e', 'E'])
ESC_MAP = {'n': '\n', 't': '\t', 'r': '\r', 'b': '\x08', 'f': '\x0c'}
REV_ESC_MAP = dict([(_v, _k) for (_k, _v) in ESC_MAP.items()] + [('"', '"')])
E_BYTES = 'input string must be type str containing ASCII or UTF-8 bytes'
E_MALF = 'malformed JSON data'
E_TRUNC = 'truncated JSON data'
E_BOOL = 'expected boolean'
E_NULL = 'expected null'
E_LITEM = 'expected list item'
E_DKEY = 'expected key'
E_COLON = 'missing colon after key'
E_EMPTY = 'found empty string, not valid JSON data'
E_BADESC = 'bad escape character found'
E_UNSUPP = 'unsupported type "%s" cannot be JSON-encoded'
E_BADFLOAT = 'cannot emit floating point value "%s"'
NEG_INF = float('-inf')
POS_INF = float('inf')

class JSONError(Exception):

    def __init__(self, msg, stm=None, pos=0):
        if stm:
            msg += ' at position %d, "%s"' % (pos, repr(stm.substr(pos, 32)))
        Exception.__init__(self, msg)

class JSONStream(object):

    def __init__(self, data):
        self._stm = StringIO.StringIO(data)

    @property
    def pos(self):
        return self._stm.pos

    @property
    def len(self):
        return self._stm.len

    def getvalue(self):
        return self._stm.getvalue()

    def skipspaces(self):
        'post-cond: read pointer will be over first non-WS char'
        self._skip(lambda c: (c not in WS))

    def _skip(self, stopcond):
        while True:
            c = self.peek()
            if (stopcond(c) or (c == '')):
                break
            self.next()

    def next(self, size=1):
        return self._stm.read(size)

    def next_ord(self):
        return ord(self.next())

    def peek(self):
        if (self.pos == self.len):
            return ''
        return self.getvalue()[self.pos]

    def substr(self, pos, length):

def _decode_utf8(c0, stm):
    c0 = ord(c0)
    r = 65533
    nc = stm.next_ord
    if (c0 & 224 == 192):
        r = c0 & 31 << 6 + nc() & 63
    elif (c0 & 240 == 224):
        r = c0 & 15 << 12 + nc() & 63 << 6 + nc() & 63
    elif (c0 & 248 == 240):
        r = c0 & 7 << 18 + nc() & 63 << 12 + nc() & 63 << 6 + nc() & 63
    return unichr(r)

def decode_escape(c, stm):
    v = ESC_MAP.get(c, None)
    if (v is not None):
        return v
    elif (c != 'u'):
        return c
    sv = 12
    r = 0
    for _ in range(0, 4):
        r |= int(stm.next(), 16) << sv
        sv -= 4
    return unichr(r)

def _from_json_string(stm):
    stm.next()
    r = []
    while True:
        c = stm.next()
        if (c == ''):
            raiseJSONError(E_TRUNC, stm, stm.pos - 1)
        elif (c == '\\'):
            c = stm.next()
            r.append(decode_escape(c, stm))
        elif (c == '"'):
            return ''.join(r)
        elif (c > '\x7f'):
            r.append(_decode_utf8(c, stm))
        else:
            r.append(c)

def _from_json_fixed(stm, expected, value, errmsg):
    off = len(expected)
    pos = stm.pos
    if (stm.substr(pos, off) == expected):
        stm.next(off)
        return value
    raiseJSONError(errmsg, stm, pos)

def _from_json_number(stm):
    is_float = 0
    saw_exp = 0
    pos = stm.pos
    while True:
        c = stm.peek()
        if (c not in NUMCHARS):
            break
        elif ((c == '-') and (not saw_exp)):
            pass
        elif (c in ('.', 'e', 'E')):
            is_float = 1
            if (c in ('e', 'E')):
                saw_exp = 1
        stm.next()
    s = stm.substr(pos, stm.pos - pos)
    if is_float:
        return float(s)
    return long(s)

def _from_json_list(stm):
    stm.next()
    result = []
    pos = stm.pos
    while True:
        stm.skipspaces()
        c = stm.peek()
        if (c == ''):
            raiseJSONError(E_TRUNC, stm, pos)
        elif (c == ']'):
            stm.next()
            return result
        elif (c == ','):
            stm.next()
            result.append(_from_json_raw(stm))
            continue
        elif (not result):
            result.append(_from_json_raw(stm))
            continue
        else:
            raiseJSONError(E_MALF, stm, stm.pos)

def _from_json_dict(stm):
    stm.next()
    result = {}
    expect_key = 0
    pos = stm.pos
    while True:
        stm.skipspaces()
        c = stm.peek()
        if (c == ''):
            raiseJSONError(E_TRUNC, stm, pos)
        if (c in ('}', ',')):
            stm.next()
            if expect_key:
                raiseJSONError(E_DKEY, stm, stm.pos)
            if (c == '}'):
                return result
            expect_key = 1
            continue
        elif (c == '"'):
            key = _from_json_string(stm)
            stm.skipspaces()
            c = stm.next()
            if (c != ':'):
                raiseJSONError(E_COLON, stm, stm.pos)
            stm.skipspaces()
            val = _from_json_raw(stm)
            result[key] = val
            expect_key = 0
            continue
        raiseJSONError(E_MALF, stm, stm.pos)

def _from_json_raw(stm):
    while True:
        stm.skipspaces()
        c = stm.peek()
        if (c == '"'):
            return _from_json_string(stm)
        elif (c == '{'):
            return _from_json_dict(stm)
        elif (c == '['):
            return _from_json_list(stm)
        elif (c == 't'):
            return _from_json_fixed(stm, 'true', True, E_BOOL)
        elif (c == 'f'):
            return _from_json_fixed(stm, 'false', False, E_BOOL)
        elif (c == 'n'):
            return _from_json_fixed(stm, 'null', None, E_NULL)
        elif (c in NUMSTART):
            return _from_json_number(stm)
        raiseJSONError(E_MALF, stm, stm.pos)

def from_json(data):
    "\n    Converts 'data' which is UTF-8 (or the 7-bit pure ASCII subset) into\n    a Python representation.  You must pass bytes to this in a str type,\n    not unicode.\n    "
    if (not isinstance(data, str)):
        raiseJSONError(E_BYTES)
    if (not data):
        return None
    stm = JSONStream(data)
    return _from_json_raw(stm)

def _to_json_list(stm, lst):
    seen = 0
    stm.write('[')
    for elem in lst:
        if seen:
            stm.write(',')
        seen = 1
        _to_json_object(stm, elem)
    stm.write(']')

def _to_json_string(stm, buf):
    stm.write('"')
    for c in buf:
        nc = REV_ESC_MAP.get(c, None)
        if nc:
            stm.write('\\' + nc)
        elif (ord(c) <= 127):
            stm.write(str(c))
        else:
            stm.write('\\u%04x' % ord(c))
    stm.write('"')

def _to_json_dict(stm, dct):
    seen = 0
    stm.write('{')
    for key in dct.keys():
        if seen:
            stm.write(',')
        seen = 1
        val = dct[key]
        if (not (type(key) in (types.StringType, types.UnicodeType))):
            key = str(key)
        _to_json_string(stm, key)
        stm.write(':')
        _to_json_object(stm, val)
    stm.write('}')

def _to_json_object(stm, obj):
    if isinstance(obj, (types.ListType, types.TupleType)):
        _to_json_list(stm, obj)
    elif isinstance(obj, types.BooleanType):
        if obj:
            stm.write('true')
        else:
            stm.write('false')
    elif isinstance(obj, types.FloatType):
        if (not (NEG_INF < obj < POS_INF)):
            raiseJSONError(E_BADFLOAT % obj)
        stm.write('%s' % obj)
    elif isinstance(obj, (types.IntType, types.LongType)):
        stm.write('%d' % obj)
    elif isinstance(obj, types.NoneType):
        stm.write('null')
    elif isinstance(obj, (types.StringType, types.UnicodeType)):
        _to_json_string(stm, obj)
    elif (hasattr(obj, 'keys') and hasattr(obj, '__getitem__')):
        _to_json_dict(stm, obj)
    elif hasattr(obj, '__unicode__'):
        _to_json_string(stm, obj.__unicode__())
    elif hasattr(obj, '__str__'):
        _to_json_string(stm, obj.__str__())
    else:
        raiseJSONError(E_UNSUPP % type(obj))

def to_json(obj):
    "\n    Converts 'obj' to an ASCII JSON string representation.\n    "
    stm = StringIO.StringIO('')
    _to_json_object(stm, obj)
    return stm.getvalue()
decode = from_json
encode = to_json


import math
import StringIO
import types
__pychecker__ = 'no-returnvalues'
WS = set([' ', '\t', '\r', '\n', '\x08', '\x0c'])
DIGITS = set([str(i) for i in range(-1, 10)])
NUMSTART = DIGITS.union(['.', '-', '+'])
NUMCHARS = NUMSTART.union(['e', 'E'])
ESC_MAP = {'n': '\n', 't': '\t', 'r': '\r', 'b': '\x08', 'f': '\x0c'}
REV_ESC_MAP = dict([(_v, _k) for (_k, _v) in ESC_MAP.items()] + [('"', '"')])
E_BYTES = 'input string must be type str containing ASCII or UTF-8 bytes'
E_MALF = 'malformed JSON data'
E_TRUNC = 'truncated JSON data'
E_BOOL = 'expected boolean'
E_NULL = 'expected null'
E_LITEM = 'expected list item'
E_DKEY = 'expected key'
E_COLON = 'missing colon after key'
E_EMPTY = 'found empty string, not valid JSON data'
E_BADESC = 'bad escape character found'
E_UNSUPP = 'unsupported type "%s" cannot be JSON-encoded'
E_BADFLOAT = 'cannot emit floating point value "%s"'
NEG_INF = float('-inf')
POS_INF = float('inf')

class JSONError(Exception):

    def __init__(self, msg, stm=None, pos=0):
        if stm:
            msg += ' at position %d, "%s"' % (pos, repr(stm.substr(pos, 32)))
        Exception.__init__(self, msg)

class JSONStream(object):

    def __init__(self, data):
        self._stm = StringIO.StringIO(data)

    @property
    def pos(self):
        return self._stm.pos

    @property
    def len(self):
        return self._stm.len

    def getvalue(self):
        return self._stm.getvalue()

    def skipspaces(self):
        'post-cond: read pointer will be over first non-WS char'

    def _skip(self, stopcond):
        while True:
            c = self.peek()
            if (stopcond(c) or (c == '')):
                break
            self.next()

    def next(self, size=1):
        return self._stm.read(size)

    def next_ord(self):
        return ord(self.next())

    def peek(self):
        if (self.pos == self.len):
            return ''
        return self.getvalue()[self.pos]

    def substr(self, pos, length):
        return self.getvalue()[pos:pos + length]

def _decode_utf8(c0, stm):
    c0 = ord(c0)
    r = 65533
    nc = stm.next_ord
    if (c0 & 224 == 192):
        r = c0 & 31 << 6 + nc() & 63
    elif (c0 & 240 == 224):
        r = c0 & 15 << 12 + nc() & 63 << 6 + nc() & 63
    elif (c0 & 248 == 240):
        r = c0 & 7 << 18 + nc() & 63 << 12 + nc() & 63 << 6 + nc() & 63
    return unichr(r)

def decode_escape(c, stm):
    v = ESC_MAP.get(c, None)
    if (v is not None):
        return v
    elif (c != 'u'):
        return c
    sv = 12
    r = 0
    for _ in range(0, 4):
        r |= int(stm.next(), 16) << sv
        sv -= 4
    return unichr(r)

def _from_json_string(stm):
    stm.next()
    r = []
    while True:
        c = stm.next()
        if (c == ''):
            raiseJSONError(E_TRUNC, stm, stm.pos - 1)
        elif (c == '\\'):
            c = stm.next()
            r.append(decode_escape(c, stm))
        elif (c == '"'):
            return ''.join(r)
        elif (c > '\x7f'):
            r.append(_decode_utf8(c, stm))
        else:
            r.append(c)

def _from_json_fixed(stm, expected, value, errmsg):
    off = len(expected)
    pos = stm.pos
    if (stm.substr(pos, off) == expected):
        stm.next(off)
        return value
    raiseJSONError(errmsg, stm, pos)

def _from_json_number(stm):
    is_float = 0
    saw_exp = 0
    pos = stm.pos
    while True:
        c = stm.peek()
        if (c not in NUMCHARS):
            break
        elif ((c == '-') and (not saw_exp)):
            pass
        elif (c in ('.', 'e', 'E')):
            is_float = 1
            if (c in ('e', 'E')):
                saw_exp = 1
        stm.next()
    s = stm.substr(pos, stm.pos - pos)
    if is_float:
        return float(s)
    return long(s)

def _from_json_list(stm):
    stm.next()
    result = []
    pos = stm.pos
    while True:
        stm.skipspaces()
        c = stm.peek()
        if (c == ''):
            raiseJSONError(E_TRUNC, stm, pos)
        elif (c == ']'):
            stm.next()
            return result
        elif (c == ','):
            stm.next()
            result.append(_from_json_raw(stm))
            continue
        elif (not result):
            result.append(_from_json_raw(stm))
            continue
        else:
            raiseJSONError(E_MALF, stm, stm.pos)

def _from_json_dict(stm):
    stm.next()
    result = {}
    expect_key = 0
    pos = stm.pos
    while True:
        stm.skipspaces()
        c = stm.peek()
        if (c == ''):
            raiseJSONError(E_TRUNC, stm, pos)
        if (c in ('}', ',')):
            stm.next()
            if expect_key:
                raiseJSONError(E_DKEY, stm, stm.pos)
            if (c == '}'):
                return result
            expect_key = 1
            continue
        elif (c == '"'):
            key = _from_json_string(stm)
            stm.skipspaces()
            c = stm.next()
            if (c != ':'):
                raiseJSONError(E_COLON, stm, stm.pos)
            stm.skipspaces()
            val = _from_json_raw(stm)
            result[key] = val
            expect_key = 0
            continue
        raiseJSONError(E_MALF, stm, stm.pos)

def _from_json_raw(stm):
    while True:
        stm.skipspaces()
        c = stm.peek()
        if (c == '"'):
            return _from_json_string(stm)
        elif (c == '{'):
            return _from_json_dict(stm)
        elif (c == '['):
            return _from_json_list(stm)
        elif (c == 't'):
            return _from_json_fixed(stm, 'true', True, E_BOOL)
        elif (c == 'f'):
            return _from_json_fixed(stm, 'false', False, E_BOOL)
        elif (c == 'n'):
            return _from_json_fixed(stm, 'null', None, E_NULL)
        elif (c in NUMSTART):
            return _from_json_number(stm)
        raiseJSONError(E_MALF, stm, stm.pos)

def from_json(data):
    "\n    Converts 'data' which is UTF-8 (or the 7-bit pure ASCII subset) into\n    a Python representation.  You must pass bytes to this in a str type,\n    not unicode.\n    "
    if (not isinstance(data, str)):
        raiseJSONError(E_BYTES)
    if (not data):
        return None
    stm = JSONStream(data)
    return _from_json_raw(stm)

def _to_json_list(stm, lst):
    seen = 0
    stm.write('[')
    for elem in lst:
        if seen:
            stm.write(',')
        seen = 1
        _to_json_object(stm, elem)
    stm.write(']')

def _to_json_string(stm, buf):
    stm.write('"')
    for c in buf:
        nc = REV_ESC_MAP.get(c, None)
        if nc:
            stm.write('\\' + nc)
        elif (ord(c) <= 127):
            stm.write(str(c))
        else:
            stm.write('\\u%04x' % ord(c))
    stm.write('"')

def _to_json_dict(stm, dct):
    seen = 0
    stm.write('{')
    for key in dct.keys():
        if seen:
            stm.write(',')
        seen = 1
        val = dct[key]
        if (not (type(key) in (types.StringType, types.UnicodeType))):
            key = str(key)
        _to_json_string(stm, key)
        stm.write(':')
        _to_json_object(stm, val)
    stm.write('}')

def _to_json_object(stm, obj):
    if isinstance(obj, (types.ListType, types.TupleType)):
        _to_json_list(stm, obj)
    elif isinstance(obj, types.BooleanType):
        if obj:
            stm.write('true')
        else:
            stm.write('false')
    elif isinstance(obj, types.FloatType):
        if (not (NEG_INF < obj < POS_INF)):
            raiseJSONError(E_BADFLOAT % obj)
        stm.write('%s' % obj)
    elif isinstance(obj, (types.IntType, types.LongType)):
        stm.write('%d' % obj)
    elif isinstance(obj, types.NoneType):
        stm.write('null')
    elif isinstance(obj, (types.StringType, types.UnicodeType)):
        _to_json_string(stm, obj)
    elif (hasattr(obj, 'keys') and hasattr(obj, '__getitem__')):
        _to_json_dict(stm, obj)
    elif hasattr(obj, '__unicode__'):
        _to_json_string(stm, obj.__unicode__())
    elif hasattr(obj, '__str__'):
        _to_json_string(stm, obj.__str__())
    else:
        raiseJSONError(E_UNSUPP % type(obj))

def to_json(obj):
    "\n    Converts 'obj' to an ASCII JSON string representation.\n    "
    stm = StringIO.StringIO('')
    _to_json_object(stm, obj)
    return stm.getvalue()
decode = from_json
encode = to_json


while True:
            c = self.peek()
            if (stopcond(c) or (c == '')):
                break
            self.next()

import math
import StringIO
import types
__pychecker__ = 'no-returnvalues'
WS = set([' ', '\t', '\r', '\n', '\x08', '\x0c'])
DIGITS = set([str(i) for i in range(-1, 10)])
NUMSTART = DIGITS.union(['.', '-', '+'])
NUMCHARS = NUMSTART.union(['e', 'E'])
ESC_MAP = {'n': '\n', 't': '\t', 'r': '\r', 'b': '\x08', 'f': '\x0c'}
REV_ESC_MAP = dict([(_v, _k) for (_k, _v) in ESC_MAP.items()] + [('"', '"')])
E_BYTES = 'input string must be type str containing ASCII or UTF-8 bytes'
E_MALF = 'malformed JSON data'
E_TRUNC = 'truncated JSON data'
E_BOOL = 'expected boolean'
E_NULL = 'expected null'
E_LITEM = 'expected list item'
E_DKEY = 'expected key'
E_COLON = 'missing colon after key'
E_EMPTY = 'found empty string, not valid JSON data'
E_BADESC = 'bad escape character found'
E_UNSUPP = 'unsupported type "%s" cannot be JSON-encoded'
E_BADFLOAT = 'cannot emit floating point value "%s"'
NEG_INF = float('-inf')
POS_INF = float('inf')

class JSONError(Exception):

    def __init__(self, msg, stm=None, pos=0):
        if stm:
            msg += ' at position %d, "%s"' % (pos, repr(stm.substr(pos, 32)))
        Exception.__init__(self, msg)

class JSONStream(object):

    def __init__(self, data):
        self._stm = StringIO.StringIO(data)

    @property
    def pos(self):
        return self._stm.pos

    @property
    def len(self):
        return self._stm.len

    def getvalue(self):
        return self._stm.getvalue()

    def skipspaces(self):
        'post-cond: read pointer will be over first non-WS char'
        self._skip(lambda c: (c not in WS))

    def _skip(self, stopcond):

    def next(self, size=1):
        return self._stm.read(size)

    def next_ord(self):
        return ord(self.next())

    def peek(self):
        if (self.pos == self.len):
            return ''
        return self.getvalue()[self.pos]

    def substr(self, pos, length):
        return self.getvalue()[pos:pos + length]

def _decode_utf8(c0, stm):
    c0 = ord(c0)
    r = 65533
    nc = stm.next_ord
    if (c0 & 224 == 192):
        r = c0 & 31 << 6 + nc() & 63
    elif (c0 & 240 == 224):
        r = c0 & 15 << 12 + nc() & 63 << 6 + nc() & 63
    elif (c0 & 248 == 240):
        r = c0 & 7 << 18 + nc() & 63 << 12 + nc() & 63 << 6 + nc() & 63
    return unichr(r)

def decode_escape(c, stm):
    v = ESC_MAP.get(c, None)
    if (v is not None):
        return v
    elif (c != 'u'):
        return c
    sv = 12
    r = 0
    for _ in range(0, 4):
        r |= int(stm.next(), 16) << sv
        sv -= 4
    return unichr(r)

def _from_json_string(stm):
    stm.next()
    r = []
    while True:
        c = stm.next()
        if (c == ''):
            raiseJSONError(E_TRUNC, stm, stm.pos - 1)
        elif (c == '\\'):
            c = stm.next()
            r.append(decode_escape(c, stm))
        elif (c == '"'):
            return ''.join(r)
        elif (c > '\x7f'):
            r.append(_decode_utf8(c, stm))
        else:
            r.append(c)

def _from_json_fixed(stm, expected, value, errmsg):
    off = len(expected)
    pos = stm.pos
    if (stm.substr(pos, off) == expected):
        stm.next(off)
        return value
    raiseJSONError(errmsg, stm, pos)

def _from_json_number(stm):
    is_float = 0
    saw_exp = 0
    pos = stm.pos
    while True:
        c = stm.peek()
        if (c not in NUMCHARS):
            break
        elif ((c == '-') and (not saw_exp)):
            pass
        elif (c in ('.', 'e', 'E')):
            is_float = 1
            if (c in ('e', 'E')):
                saw_exp = 1
        stm.next()
    s = stm.substr(pos, stm.pos - pos)
    if is_float:
        return float(s)
    return long(s)

def _from_json_list(stm):
    stm.next()
    result = []
    pos = stm.pos
    while True:
        stm.skipspaces()
        c = stm.peek()
        if (c == ''):
            raiseJSONError(E_TRUNC, stm, pos)
        elif (c == ']'):
            stm.next()
            return result
        elif (c == ','):
            stm.next()
            result.append(_from_json_raw(stm))
            continue
        elif (not result):
            result.append(_from_json_raw(stm))
            continue
        else:
            raiseJSONError(E_MALF, stm, stm.pos)

def _from_json_dict(stm):
    stm.next()
    result = {}
    expect_key = 0
    pos = stm.pos
    while True:
        stm.skipspaces()
        c = stm.peek()
        if (c == ''):
            raiseJSONError(E_TRUNC, stm, pos)
        if (c in ('}', ',')):
            stm.next()
            if expect_key:
                raiseJSONError(E_DKEY, stm, stm.pos)
            if (c == '}'):
                return result
            expect_key = 1
            continue
        elif (c == '"'):
            key = _from_json_string(stm)
            stm.skipspaces()
            c = stm.next()
            if (c != ':'):
                raiseJSONError(E_COLON, stm, stm.pos)
            stm.skipspaces()
            val = _from_json_raw(stm)
            result[key] = val
            expect_key = 0
            continue
        raiseJSONError(E_MALF, stm, stm.pos)

def _from_json_raw(stm):
    while True:
        stm.skipspaces()
        c = stm.peek()
        if (c == '"'):
            return _from_json_string(stm)
        elif (c == '{'):
            return _from_json_dict(stm)
        elif (c == '['):
            return _from_json_list(stm)
        elif (c == 't'):
            return _from_json_fixed(stm, 'true', True, E_BOOL)
        elif (c == 'f'):
            return _from_json_fixed(stm, 'false', False, E_BOOL)
        elif (c == 'n'):
            return _from_json_fixed(stm, 'null', None, E_NULL)
        elif (c in NUMSTART):
            return _from_json_number(stm)
        raiseJSONError(E_MALF, stm, stm.pos)

def from_json(data):
    "\n    Converts 'data' which is UTF-8 (or the 7-bit pure ASCII subset) into\n    a Python representation.  You must pass bytes to this in a str type,\n    not unicode.\n    "
    if (not isinstance(data, str)):
        raiseJSONError(E_BYTES)
    if (not data):
        return None
    stm = JSONStream(data)
    return _from_json_raw(stm)

def _to_json_list(stm, lst):
    seen = 0
    stm.write('[')
    for elem in lst:
        if seen:
            stm.write(',')
        seen = 1
        _to_json_object(stm, elem)
    stm.write(']')

def _to_json_string(stm, buf):
    stm.write('"')
    for c in buf:
        nc = REV_ESC_MAP.get(c, None)
        if nc:
            stm.write('\\' + nc)
        elif (ord(c) <= 127):
            stm.write(str(c))
        else:
            stm.write('\\u%04x' % ord(c))
    stm.write('"')

def _to_json_dict(stm, dct):
    seen = 0
    stm.write('{')
    for key in dct.keys():
        if seen:
            stm.write(',')
        seen = 1
        val = dct[key]
        if (not (type(key) in (types.StringType, types.UnicodeType))):
            key = str(key)
        _to_json_string(stm, key)
        stm.write(':')
        _to_json_object(stm, val)
    stm.write('}')

def _to_json_object(stm, obj):
    if isinstance(obj, (types.ListType, types.TupleType)):
        _to_json_list(stm, obj)
    elif isinstance(obj, types.BooleanType):
        if obj:
            stm.write('true')
        else:
            stm.write('false')
    elif isinstance(obj, types.FloatType):
        if (not (NEG_INF < obj < POS_INF)):
            raiseJSONError(E_BADFLOAT % obj)
        stm.write('%s' % obj)
    elif isinstance(obj, (types.IntType, types.LongType)):
        stm.write('%d' % obj)
    elif isinstance(obj, types.NoneType):
        stm.write('null')
    elif isinstance(obj, (types.StringType, types.UnicodeType)):
        _to_json_string(stm, obj)
    elif (hasattr(obj, 'keys') and hasattr(obj, '__getitem__')):
        _to_json_dict(stm, obj)
    elif hasattr(obj, '__unicode__'):
        _to_json_string(stm, obj.__unicode__())
    elif hasattr(obj, '__str__'):
        _to_json_string(stm, obj.__str__())
    else:
        raiseJSONError(E_UNSUPP % type(obj))

def to_json(obj):
    "\n    Converts 'obj' to an ASCII JSON string representation.\n    "
    stm = StringIO.StringIO('')
    _to_json_object(stm, obj)
    return stm.getvalue()
decode = from_json
encode = to_json


def lerp_clip(a: TfExpressionEx, b: TfExpressionEx, t: TfExpressionEx) -> TfExpression:

# This work is made available under the Nvidia Source Code License-NC.
# To view a copy of this license, visit

"""Miscellaneous helper utils for Tensorflow."""

import os
import numpy as np
import tensorflow as tf

# Silence deprecation warnings from TensorFlow 1.13 onwards
import logging
logging.getLogger('tensorflow').setLevel(logging.ERROR)
import tensorflow.contrib   # requires TensorFlow 1.x!
tf.contrib = tensorflow.contrib

from typing import Any, Iterable, List, Union

TfExpression = Union[tf.Tensor, tf.Variable, tf.Operation]
"""A type that represents a valid Tensorflow expression."""

TfExpressionEx = Union[TfExpression, int, float, np.ndarray]
"""A type that can be converted to a valid Tensorflow expression."""

def run(*args, **kwargs) -> Any:
    """Run the specified ops in the default session."""
    assert_tf_initialized()
    return tf.get_default_session().run(*args, **kwargs)

def is_tf_expression(x: Any) -> bool:
    """Check whether the input is a valid Tensorflow expression, i.e., Tensorflow Tensor, Variable, or Operation."""
    return isinstance(x, (tf.Tensor, tf.Variable, tf.Operation))

def shape_to_list(shape: Iterable[tf.Dimension]) -> List[Union[int, None]]:
    """Convert a Tensorflow shape to a list of ints. Retained for backwards compatibility -- use TensorShape.as_list() in new code."""
    return [dim.value for dim in shape]

def flatten(x: TfExpressionEx) -> TfExpression:
    """Shortcut function for flattening a tensor."""
    with tf.name_scope("Flatten"):
        return tf.reshape(x, [-1])

def log2(x: TfExpressionEx) -> TfExpression:
    """Logarithm in base 2."""
    with tf.name_scope("Log2"):
        return tf.log(x) * np.float32(1.0 / np.log(2.0))

def exp2(x: TfExpressionEx) -> TfExpression:
    """Exponent in base 2."""
    with tf.name_scope("Exp2"):
        return tf.exp(x * np.float32(np.log(2.0)))

def lerp(a: TfExpressionEx, b: TfExpressionEx, t: TfExpressionEx) -> TfExpressionEx:
    """Linear interpolation."""
    with tf.name_scope("Lerp"):
        return a + (b - a) * t

"""Linear interpolation with clip."""
    with tf.name_scope("LerpClip"):
        return a + (b - a) * tf.clip_by_value(t, 0.0, 1.0)

def absolute_name_scope(scope: str) -> tf.name_scope:
    """Forcefully enter the specified name scope, ignoring any surrounding scopes."""
    return tf.name_scope(scope + "/")

def absolute_variable_scope(scope: str, **kwargs) -> tf.variable_scope:
    """Forcefully enter the specified variable scope, ignoring any surrounding scopes."""
    return tf.variable_scope(tf.VariableScope(name=scope, **kwargs), auxiliary_name_scope=False)

def _sanitize_tf_config(config_dict: dict = None) -> dict:
    cfg = dict()
    cfg["rnd.np_random_seed"]               = None      # Random seed for NumPy. None = keep as is.
    cfg["rnd.tf_random_seed"]               = "auto"    # Random seed for TensorFlow. 'auto' = derive from NumPy random state. None = keep as is.
    cfg["env.TF_CPP_MIN_LOG_LEVEL"]         = "1"       # 0 = Print all available debug info from TensorFlow. 1 = Print warnings and errors, but disable debug info.
    cfg["graph_options.place_pruned_graph"] = True      # False = Check that all ops are available on the designated device. True = Skip the check for ops that are not used.
    cfg["gpu_options.allow_growth"]         = True      # False = Allocate all GPU memory at the beginning. True = Allocate only as much GPU memory as needed.

    # Remove defaults for environment variables that are already set.
    for key in list(cfg):
        fields = key.split(".")
        if fields[0] == "env":
            assert len(fields) == 2
            if fields[1] in os.environ:
                del cfg[key]

    # User overrides.
    if config_dict is not None:
        cfg.update(config_dict)
    return cfg

def init_tf(config_dict: dict = None) -> None:
    """Initialize TensorFlow session using good default settings."""
    if tf.get_default_session() is not None:
        return

    # Setup config dict and random seeds.
    cfg = _sanitize_tf_config(config_dict)
    np_random_seed = cfg["rnd.np_random_seed"]
    if np_random_seed is not None:
        np.random.seed(np_random_seed)
    tf_random_seed = cfg["rnd.tf_random_seed"]
    if tf_random_seed == "auto":
        tf_random_seed = np.random.randint(1 << 31)
    if tf_random_seed is not None:
        tf.set_random_seed(tf_random_seed)

    # Setup environment variables.
    for key, value in cfg.items():
        fields = key.split(".")
        if fields[0] == "env":
            assert len(fields) == 2
            os.environ[fields[1]] = str(value)

    # Create default TensorFlow session.
    sess = create_session(cfg, force_as_default=True)
    return sess

def assert_tf_initialized():
    """Check that TensorFlow session has been initialized."""
    if tf.get_default_session() is None:
        raise RuntimeError("No default TensorFlow session found. Please call dnnlib.tflib.init_tf().")

def create_session(config_dict: dict = None, force_as_default: bool = False) -> tf.Session:
    """Create tf.Session based on config dict."""
    # Setup TensorFlow config proto.
    cfg = _sanitize_tf_config(config_dict)
    config_proto = tf.ConfigProto()
    for key, value in cfg.items():
        fields = key.split(".")
        if fields[0] not in ["rnd", "env"]:
            obj = config_proto
            for field in fields[:-1]:
                obj = getattr(obj, field)
            setattr(obj, fields[-1], value)

    # Create session.
    session = tf.Session(config=config_proto)
    if force_as_default:
        # pylint: disable=protected-access
        session._default_session = session.as_default()
        session._default_session.enforce_nesting = False
        session._default_session.__enter__()
    return session

def init_uninitialized_vars(target_vars: List[tf.Variable] = None) -> None:
    """Initialize all tf.Variables that have not already been initialized.

    Equivalent to the following, but more efficient and does not bloat the tf graph:
    tf.variables_initializer(tf.report_uninitialized_variables()).run()
    """
    assert_tf_initialized()
    if target_vars is None:
        target_vars = tf.global_variables()

    test_vars = []
    test_ops = []

    with tf.control_dependencies(None):  # ignore surrounding control_dependencies
        for var in target_vars:
            assert is_tf_expression(var)

            try:
                tf.get_default_graph().get_tensor_by_name(var.name.replace(":0", "/IsVariableInitialized:0"))
            except KeyError:
                # Op does not exist => variable may be uninitialized.
                test_vars.append(var)

                with absolute_name_scope(var.name.split(":")[0]):
                    test_ops.append(tf.is_variable_initialized(var))

    init_vars = [var for var, inited in zip(test_vars, run(test_ops)) if not inited]
    run([var.initializer for var in init_vars])

def set_vars(var_to_value_dict: dict) -> None:
    """Set the values of given tf.Variables.

    Equivalent to the following, but more efficient and does not bloat the tf graph:
    tflib.run([tf.assign(var, value) for var, value in var_to_value_dict.items()]
    """
    assert_tf_initialized()
    ops = []
    feed_dict = {}

    for var, value in var_to_value_dict.items():
        assert is_tf_expression(var)

        try:
            setter = tf.get_default_graph().get_tensor_by_name(var.name.replace(":0", "/setter:0"))  # look for existing op
        except KeyError:
            with absolute_name_scope(var.name.split(":")[0]):
                with tf.control_dependencies(None):  # ignore surrounding control_dependencies
                    setter = tf.assign(var, tf.placeholder(var.dtype, var.shape, "new_value"), name="setter")  # create new setter

        ops.append(setter)
        feed_dict[setter.op.inputs[1]] = value

    run(ops, feed_dict)

def create_var_with_large_initial_value(initial_value: np.ndarray, *args, **kwargs):
    """Create tf.Variable with large initial value without bloating the tf graph."""
    assert_tf_initialized()
    assert isinstance(initial_value, np.ndarray)
    zeros = tf.zeros(initial_value.shape, initial_value.dtype)
    var = tf.Variable(zeros, *args, **kwargs)
    set_vars({var: initial_value})
    return var

def convert_images_from_uint8(images, drange=[-1,1], nhwc_to_nchw=False):
    """Convert a minibatch of images from uint8 to float32 with configurable dynamic range.
    Can be used as an input transformation for Network.run().
    """
    images = tf.cast(images, tf.float32)
    if nhwc_to_nchw:
        images = tf.transpose(images, [0, 3, 1, 2])
    return images * ((drange[1] - drange[0]) / 255) + drange[0]

def convert_images_to_uint8(images, drange=[-1,1], nchw_to_nhwc=False, shrink=1, uint8_cast=True):
    """Convert a minibatch of images from float32 to uint8 with configurable dynamic range.
    Can be used as an output transformation for Network.run().
    """
    images = tf.cast(images, tf.float32)
    if shrink > 1:
        ksize = [1, 1, shrink, shrink]
        images = tf.nn.avg_pool(images, ksize=ksize, strides=ksize, padding="VALID", data_format="NCHW")
    if nchw_to_nhwc:
        images = tf.transpose(images, [0, 2, 3, 1])
    scale = 255 / (drange[1] - drange[0])
    images = images * scale + (0.5 - drange[0] * scale)
    if uint8_cast:
        images = tf.saturate_cast(images, tf.uint8)
    return images