#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import os
import shutil
import sys
from zipfile import ZipFile
from subprocess import Popen, PIPE

PONYSAY_VERSION = '3.0.4'

project_dir = os.path.dirname(__file__)

manpages = [('en', 'English'),  # must be first
            ('es', 'Spanish'),
            ('sv', 'Swedish'),
            ('tr', 'Turkish')]

sharedirs = [('ponies', 'xterm ponies', 'PONYDIR', True),  # must be first
             ('ttyponies', 'tty ponies', 'TTYPONYDIR', True),
             ('extraponies', 'extra xterm ponies', 'XPONYDIR', True),
             ('extrattyponies', 'extra tty ponies', 'XTTYPONYDIR', True),
             ('quotes', 'pony quotes', 'QUOTEDIR', False),
             ('balloons', 'balloon styles', 'BALLOONDIR', False)]

sharefiles = [('ucs', 'ucsmap')]

commands = ['ponysay', 'ponythink', 'ponysay-tool']

shells = [('bash', '/usr/share/bash-completion/completions/ponysay', 'GNU Bash'),
          ('fish', '/usr/share/fish/vendor_completions.d/ponysay.fish', 'Friendly interactive shell'),
          ('zsh', '/usr/share/zsh/site-functions/_ponysay', 'zsh')]

mansections = [('ponysay', '6'),
               ('cowsay', '1'),
               ('fortune', '6'),
               ('ponysay-tool', '6')]

miscfiles = [('COPYING', '/usr/share/licenses/ponysay/COPYING'),
             ('LICENSE', '/usr/share/licenses/ponysay/LICENSE'),
             ('CREDITS', '/usr/share/licenses/ponysay/CREDITS')]

ponysaysrc = [i for i in os.listdir(os.path.join(project_dir, 'src')) if i.endswith('.py') and not i.startswith('.')]

COPY = 'copy'
HARD = 'hard'
SYMBOLIC = 'symbolic'

class Setup():
    def __init__(self):
        usage_script = '\033[34;1msetup.py\033[21;39m'
        usage_help   = '(--version | --help)'
        usage_proc   = '[\033[4mconfigurations\033[24m] ([build] | prebuilt | install | (uninstall|clean)[-old] | view)'

        usage = '%s %s\n%s %s' % (usage_script, usage_help, usage_script, usage_proc)

        usage = usage.replace('\033[', '\0')
        for sym in ('[', ']', '(', ')', '|', '...'):
            usage = usage.replace(sym, '\033[2m' + sym + '\033[22m')
            usage = usage.replace('\0', '\033[')

        opts = ArgParser(program     = 'setup.py',
                         description = 'installer for ponysay',
                         usage       = usage)

        opts.add_argumentless(alternatives = ['--help'])
        opts.add_argumentless(alternatives = ['--version'])
        opts.add_argumented  (alternatives = ['---DESTDIR'], arg="DESTDIR")
        opts.add_argumented  (alternatives = ['---PREFIX'], arg="PREFIX")

        opts.add_argumentless(help = 'Install everything that is not explicitly excluded',
                              alternatives = ['--everything', '--with-everything'])

        opts.add_argumentless(help = 'Install only the essentials\nNote that this can vary depending on the version',
                              alternatives = ['--minimal'])

        opts.add_argumentless(help = 'Install nothing (except legal documents) that is not explicitly included',
                              alternatives = ['--nothing', '--with-nothing'])

        for command in commands:
            opts.add_argumentless(help = 'Do not install the %s command' % (command),
                                  alternatives = ['--without-' + command, '--without-' + command + '-command'])

            opts.add_argumented  (help = 'Install the %s command, and set file name\nDefault = /usr/bin/%s' % (command, command),
                                  alternatives = ['--with-' + command, '--with-' + command + '-command'], arg='EXEC')

        opts.add_argumentless(help = 'Do not install a user shared cache',
                              alternatives = ['--without-shared-cache'])

        opts.add_argumented  (help = 'Install a user shared cache at CACHEDIR\nDefault = /var/cache/ponysay',
                              alternatives = [   '--with-shared-cache'], arg='CACHEDIR')

        for shell in shells:
            opts.add_argumentless(help = 'Do not install completion for ' + shell[2],
                                  alternatives = ['--without-' + shell[0], '--without-' + shell[0] + '-completion'])

            opts.add_argumented  (help = 'Set file name for the completion for ponysay in' + shell[2],
                                  alternatives = ['--with-' + shell[0], '--with-' + shell[0] + '-completion'], arg='PONYSAY_%s_FILE' % (shell[0].upper()))

        opts.add_argumentless(help = 'Only install explicitly included shell completions',
                              alternatives = ['--without-shell', '--without-shell-completion'])

        opts.add_argumented  (help = 'Set share/ directory used for shell completions\nDefault = $SHAREDIR',
                              alternatives = ['--with-shell', '--with-shell-completion'], arg='SHAREDIR')

        opts.add_argumentless(help = 'Do not install PDF manual\nDefault',
                              alternatives = ['--without-pdf', '--without-pdf-manual'])

        opts.add_argumented  (help = 'Set directory for PDF manual\nDefault = $PREFIX/doc',
                              alternatives = ['--with-pdf', '--with-pdf-manual'], arg='DOCDIR')

        opts.add_argumentless(help = 'Do not compress PDF manual\nDefault',
                              alternatives = ['--without-pdf-compression', '--without-pdf-manual-compression'])

        opts.add_argumented  (help = 'Select compression for PDF manual\nDefault = gz, xz is also recognised',
                              alternatives = ['--with-pdf-compression', '--with-pdf-manual-compression'], arg='COMPRESSION')

        opts.add_argumentless(help = 'Do not install info manual',
                              alternatives = ['--without-info', '--without-info-manual'])

        opts.add_argumented  (help = 'Set directory for info manual\nDefault = $SHARE/info',
                              alternatives = ['--with-info', '--with-info-manual'], arg='INFODIR')

        opts.add_argumentless(help = 'Do not use install-info when installing info manual',
                              alternatives = ['--without-info-install', '--without-info-manual-install'])

        opts.add_argumented  (help = 'Use install-info when installing info manual, and set description\nDefault',
                              alternatives = ['--with-info-install', '--with-info-manual-install'], arg='DESCRIPTION')

        opts.add_argumentless(help = 'Do not compress info manual',
                              alternatives = ['--without-info-compression', '--without-info-manual-compression'])

        opts.add_argumented  (help = 'Select compression for info manual\nDefault = gz, xz is also recognised',
                              alternatives = ['--with-info-compression', '--with-info-manual-compression'], arg='COMPRESSION')

        for man in manpages:
            opts.add_argumentless(help = 'Do not install %s manpage manual' % (man[1]),
                                  alternatives = ['--without-man-%s' % (man[0]), '--without-manpage-%s' % (man[0]), '--without-man-manual-%s' % (man[0]),
                                                  '--without-%s-man' % (man[0]), '--without-%s-manpage' % (man[0]), '--without-%s-man-manual' % (man[0])])

            opts.add_argumented  (help = 'Set directory for %s manpage\nDefault = $SHARE/man' % (man[1]),
                                  alternatives = ['--with-man-%s' % (man[0]), '--with-manpage-%s' % (man[0]), '--with-man-manual-%s' % (man[0]),
                                                  '--with-%s-man' % (man[0]), '--with-%s-manpage' % (man[0]), '--with-%s-man-manual' % (man[0])], arg='MANDIR')

        opts.add_argumentless(help = 'Do not install any manpages',
                              alternatives = ['--without-man', '--without-manpage', '--without-man-manual'])

        opts.add_argumented  (help = 'Set directory for all man pages\nDefault = $SHARE/man',
                              alternatives = ['--with-man', '--with-manpage', '--with-man-manual'], arg='MANDIR')

        for man in manpages:
            opts.add_argumentless(help = 'Do not compress %s manpage' % (man[1]),
                                  alternatives = ['--without-man-%s-compression' % (man[0]), '--without-manpage-%s-compression' % (man[0]), '--without-man-manual-%s-compression' % (man[0]),
                                                  '--without-%s-man-compression' % (man[0]), '--without-%s-manpage-compression' % (man[0]), '--without-%s-man-manual-compression' % (man[0])])

            opts.add_argumented  (help = 'Select compression for %s manpage\nDefault = gz, xz is also recognised' % (man[1]),
                                  alternatives = ['--with-man-%s-compression' % (man[0]), '--with-manpage-%s-compression' % (man[0]), '--with-man-manual-%s-compression' % (man[0]),
                                                  '--with-%s-man-compression' % (man[0]), '--with-%s-manpage-compression' % (man[0]), '--with-%s-man-manual-compression' % (man[0])],
                                  arg='COMPRESSION')

        opts.add_argumentless(help = 'Do not compress any installed manpage',
                              alternatives = ['--without-man-compression', '--without-manpage-compression', '--without-man-manual-compression'])

        opts.add_argumented  (help = 'Select compression for installed manpages\nDefault = gz, xz is also recognised',
                              alternatives = ['--with-man-compression', '--with-manpage-compression', '--with-man-manual-compression'], arg='COMPRESSION')

        for man in mansections:
            opts.add_argumented  (help = 'Change the section for the %s manpage\nDefault = %s' % man,
                                  alternatives = ['--man-section-%s' % (man[0]), '--%s-manpage-section' % (man[0]),
                                                  '--man-section-%s' % (man[0]), '--%s-manpage-section' % (man[0])], arg='SECTION')

        for dir in sharedirs:
            opts.add_argumentless(help = 'Do not install ' + dir[1],
                                  alternatives = ['--without-' + dir[0]])

            opts.add_argumentless(help = 'Install %s\nDefault' % dir[1],
                                  alternatives = [   '--with-' + dir[0]])

        opts.add_argumentless(help = 'Do not install UCS pony name map',
                              alternatives = ['--without-ucs', '--without-ucs-names'])

        opts.add_argumentless(help = 'Install UCS pony name map\nDefault',
                              alternatives = ['--with-ucs', '--with-ucs-names'])

        opts.add_argumentless(help = 'Let the installer set the env name for python in ponysay\nDefault',
                              alternatives = ['--without-custom-env-python'])

        opts.add_argumented  (help = 'Set the env name for python in ponysay',
                              alternatives = ['--with-custom-env-python'], arg='PYTHON')

        opts.add_argumented  (help = 'Set a prefix to all implicit directories\nDefault = /usr',
                              alternatives = ['--prefix'], arg='PREFIX')

        opts.add_argumentless(help = 'Change all implicit configurations to fit local user a installation for the current user',
                              alternatives = ['--private'])

        opts.add_argumentless(help = 'Change all implicit directories to fit installation to /opt',
                              alternatives = ['--opt'])

        opts.add_argumented  (help = 'Set the system\'s directory for command executables\nDefault = $PREFIX/bin',
                              alternatives = ['--bin-dir', '--bindir'], arg='BINDIR')

        opts.add_argumented  (alternatives = ['--sbin-dir', '--sbindir'], arg='SBINDIR')

        opts.add_argumented  (help = 'Set the system\'s directory for non-executable libraries\nDefault = $PREFIX/lib/ponysay\nNot used.',
                              alternatives = ['--lib-dir', '--libdir'], arg='LIBDIR')

        opts.add_argumented  (help = 'Set the system\'s directory for non-command executables\nDefault = $PREFIX/libexec/ponysay\nNot used.',
                              alternatives = ['--libexec-dir', '--libexecdir'], arg='LIBEXECDIR')

        opts.add_argumented  (help = 'Set the system\'s directory for resource files\nDefault = $PREFIX/share',
                              alternatives = ['--share-dir', '--sharedir'], arg='SHAREDIR')

        opts.add_argumented  (help = 'Set the system\'s local specific configuration directory\nDefault = /etc',
                              alternatives = ['--sysconf-dir', '--sysconfdir'], arg='SYSCONFDIR')

        opts.add_argumented  (help = 'Set the system\'s directory for cache directories\nDefault = /var/cache',
                              alternatives = ['--cache-dir', '--cachedir'], arg='CACHEDIR')

        opts.add_argumented  (help = 'Set off environment for installation\nEmpty by default',
                              alternatives = ['--dest-dir', '--destdir'], arg='DESTDIR')

        opts.add_argumented  (help = 'Set how to link identical files\nDefault = symbolic, copy and hard are also recognised',
                              alternatives = ['--linking'], arg='TYPE')

        opts.add_argumented  (help = 'Install all ponies or only the completely free ponies\nThis option is manditory, use strict, full, true or yes ' +
                                      'for only free ponies,\nand partial, sloppy, false or no for all ponies',
                              alternatives = ['--freedom'], arg='FREEDOM')

        opts.parse()

        self.linking = SYMBOLIC
        if opts.opts['--linking'] is not None:
            self.linking = opts.opts['--linking'][0]

        self.free = None
        if opts.opts['--freedom'] is not None:
            if opts.opts['--freedom'][0].lower() in ('strict', 'full', 'true', 'yes'):
                self.free = True
            elif opts.opts['--freedom'][0].lower() in ('partial', 'sloppy', 'false', 'no'):
                self.free = False
        def checkFreedom():
            if self.free is None:
                print()
                print('You need to select your freedom, add --freedom=strict or --freedom=partial.')
                print()
                print('--freedom=strict   will install only ponies that are completely free.')
                print('--freedom=partial  will install all ponies, even if they are not free.')
                print()
                print()
                exit(255)

        def setup_config():
            self.viewconf(conf)
            os.umask(0o022)

        if (opts.opts['---DESTDIR'] is not None) and (opts.opts['--dest-dir'] is None):
            destdir = opts.opts['---DESTDIR'][0]
            if len(destdir) > 0:
                opts.opts['--dest-dir'] = [destdir]

        if (opts.opts['---PREFIX'] is not None) and (opts.opts['--prefix'] is None):
            prefix = opts.opts['---PREFIX'][0]
            if len(prefix) > 0:
                opts.opts['--prefix'] = [prefix]

        if opts.opts['--help'] is not None:
            opts.help()
        elif opts.opts['--version'] is not None:
            print('Ponysay %s installer' % (PONYSAY_VERSION))
        elif len(opts.files) != 1:
            opts.print_fatal('A single command is expected on the command line.')
            opts.usage()
        else:
            method = opts.files[0]
            if   method == 'clean':      self.clean()
            elif method == 'clean-old':  self.cleanOld()
            else:
                conf = self.configure(opts.opts)

                if method == 'build':
                    checkFreedom()
                    setup_config()
                    self.build(conf)

                elif method == 'prebuilt':
                    checkFreedom()
                    setup_config()
                    self.applyDestDir(conf)
                    self.install(conf)

                elif method == 'install':
                    checkFreedom()
                    setup_config()
                    self.build(conf)
                    self.applyDestDir(conf)
                    self.install(conf)
                    self.clean()

                elif method == 'uninstall':
                    setup_config()
                    self.uninstall(conf)

                elif method == 'uninstall-old':
                    setup_config()
                    self.uninstallOld(conf)

                elif method == 'view':
                    setup_config()
                else:
                    opts.print_fatal('Unknown command: {}', method)
                    opts.usage()

    def viewconf(self, conf):
        '''
        Display configurations
        '''

        RED = '\033[31m%s\033[39m'
        GREEN = '%s\033[32m%s\033[39m'
        YELLOW = '\033[33m%s\033[39m'

        for command in commands:
            if conf[command] is not None:          print(GREEN  % ('Installing command ' + command + ' as ', conf[command]))
            else:                                  print(RED    % ('Skipping installion of command ' + command))
        if conf['shared-cache'] is not None:       print(GREEN  % ('Installing shared cache at ', conf['shared-cache']))
        else:                                      print(RED    % ('Skipping installation of shared cache'))
        for shell in [item[0] for item in shells]:
            if conf[shell] is not None:            print(GREEN  % ('Installing auto-completion for ' + shell + ' as ', conf[shell]))
            else:                                  print(RED    % ('Skipping installation of auto-completion for ' + shell))
        if conf['pdf'] is not None:                print(GREEN  % ('Installing PDF manual to ', conf['pdf']))
        else:                                      print(RED    % ('Skipping installation of PDF manual'))
        if conf['pdf-compression'] is not None:    print(GREEN  % ('Compressing PDF manual with ', conf['pdf-compression']))
        else:                                      print(RED    % ('Skipping compression of PDF manual'))
        if conf['info'] is not None:               print(GREEN  % ('Installing info manual to ', conf['info']))
        else:                                      print(RED    % ('Skipping installation of info manual'))
        if conf['info-install'] is not None:       print(GREEN  % ('Installing info manual with install-info with description: ', conf['info-install']))
        else:                                      print(RED    % ('Skipping installation of info manual with install-info'))
        if conf['info-compression'] is not None:   print(GREEN  % ('Compressing info manual with ', conf['info-compression']))
        else:                                      print(RED    % ('Skipping compression of info manual'))
        for man in manpages:
            key = 'man-' + man[0]
            if conf[key] is not None:              print(GREEN  % ('Installing ' + man[1] + ' manpage to ', conf[key]))
            else:                                  print(RED    % ('Skipping installation of ' + man[1] + ' manpage'))
            key += '-compression'
            if conf[key] is not None:              print(GREEN  % ('Compressing ' + man[1] + ' manpage with ', conf[key]))
            else:                                  print(RED    % ('Skipping compression of ' + man[1] + ' manpage'))
        for man in mansections:                    print(GREEN  % ('References to manpage for ' + man[0] + ' points to section ', conf['man-section-' + man[0]]))
        for dir in sharedirs:
            if conf[dir[0]] is not None:           print(GREEN  % ('Installing ' + dir[1] + ' to ', conf['share-dir'] + '/' + dir[0]))
            else:                                  print(RED    % ('Skipping installation of ' + dir[1]))
        for file in sharefiles:
            if conf[file[0]] is not None:          print(GREEN  % ('Installing ' + file[1] + ' as ', conf['share-dir'] + '/' + file[0]))
            else:                                  print(RED    % ('Skipping installation of ' + file[1]))
        if conf['custom-env-python'] is not None:  print(GREEN  % ('Using custom env reference in python script shebang: ', conf['custom-env-python']))
        else:                                      print(YELLOW % ('Looking for best env reference in python script shebang'))
        for miscfile in miscfiles:                 print(GREEN  % ('Installing ' + miscfile[0] + ' to ', conf[miscfile[0]]))
        print('Using system configuration directory: ' + conf['sysconf-dir'])
        print('Prefered linking style: ' + self.linking)
        print('Using umask: 022 (only owner can do modifications)')
        if self.free is None:                      print(YELLOW % ('\033[01m--freedom is manditory and has not be specified\033[21m'))
        elif self.free:                            print(GREEN  % ('', 'Installing only fully free parts of the package'))
        else:                                      print(RED    % ('Installing \033[1mnot\033[21m only fully free parts of the package'))

        print()

    def build(self, conf):
        '''
        Compile ponysay
        '''

        print('\033[1;34m::\033[39mCompiling...\033[21m')

        def compressCommand(ext):
            if ext == 'gz':  return 'gzip -9 -f'
            if ext == 'xz':  return 'xz -9e -f'

        def compress(source, destination, ext):
            print('%s < %s > %s' % (compressCommand(ext), source, destination))
            (fileout, filein) = (None, None)
            try:
                fileout = open(destination, 'w+')
                filein = open(source, 'r')
                Popen(compressCommand(ext).split(' '), stdout=fileout, stdin=filein).communicate()
            finally:
                if fileout is not None:  fileout.close()
                if filein  is not None:  filein .close()

        (fileout, filein) = (None, None)

        env = conf['custom-env-python']
        if env is None:
            try:
                (out, err) = Popen(['env', 'python', '--version'], stdout=PIPE, stderr=PIPE).communicate()
                out = out.decode('utf8', 'replace') + err.decode('utf8', 'replace')
                out = out.replace('\n', '')
                env = out.split(' ')[1].split('.')[0]
                if int(env) < 3:  env = 'python3'
                else:             env = 'python'
            except:
                env = 'python3'
        mane = False
        for command in commands:
            if conf[command] is not None:
                mane = True
                break
        if mane:
            for src in ponysaysrc:
                try:
                    fileout = open('src/%s.install' % src, 'wb+')
                    filein = open('src/%s' % src, 'rb')
                    data = filein.read().decode('utf-8', 'replace')

                    if '#!/usr/bin/env python3' in data:
                        data = data.replace('#!/usr/bin/env python3', '#!/usr/bin/env ' + env)
                    else:
                        data = data.replace('#!/usr/bin/env python', '#!/usr/bin/env ' + env)
                    data = data.replace('/usr/share/ponysay/', conf['share-dir'] + ('' if conf['share-dir'].endswith('/') else '/'))
                    data = data.replace('/etc/', conf['sysconf-dir'] + ('' if conf['sysconf-dir'].endswith('/') else '/'))
                    data = data.replace('\nVERSION = \'dev\'', '\nVERSION = \'%s\'' % (PONYSAY_VERSION))

                    fileout.write(data.encode('utf-8'))
                finally:
                    if fileout is not None:  fileout.close()
                    if filein  is not None:  filein .close()
            print('Creating uncompressed zip file ponysay.zip with files from src: ' + ' '.join(ponysaysrc))
            myzip = None
            try:
                myzip = ZipFile('ponysay.zip', 'w')
                for src in ponysaysrc:
                    myzip.write('src/%s.install' % src, src)
            finally:
                myzip.close()
            os.chmod('ponysay.zip', 0o755)
            try:
                fileout = open('ponysay.install', 'wb+')
                filein = open('ponysay.zip', 'rb')
                fileout.write(('#!/usr/bin/env %s\n' % env).encode('utf-8'))
                fileout.write(filein.read())
            finally:
                if fileout is not None:  fileout.close()
                if filein  is not None:  filein .close()

        for man in manpages:
            key = 'man-' + man[0]
            section = conf['man-section-ponysay']
            if man is manpages[0]:  lang = ''
            else:                   lang = '.' + man[0]
            if conf[key] is not None:
                src = 'manuals/manpage' + lang + '.6'
                dest = src + '.install'
                (fileout, filein) = (None, None)
                try:
                    fileout = open(dest, 'wb+')
                    filein = open(src, 'rb')
                    data = filein.read().decode('utf-8', 'replace')

                    data = data.replace('\n.TH PONYSAY 0', '\n.TH PONYSAY ' + conf['man-section-ponysay'])
                    for section in [item[0] for item in mansections]:
                        data = data.replace('\n.BR %s (0)' % (section), '\n.BR %s (%s)' % (section, conf['man-section-' + section]))

                    fileout.write(data.encode('utf-8'))
                finally:
                    if fileout is not None:  fileout.close()
                    if filein  is not None:  filein .close()
                src = dest
                ext = conf[key + '-compression']
                if ext is not None:
                    dest = 'manuals/manpage' + lang + '.6.' + ext
                    compress(src, dest, ext)

        if conf['info'] is not None:
            print('makeinfo manuals/ponysay.texinfo')
            os.system('makeinfo manuals/ponysay.texinfo')
            ext = conf['info-compression']
            if ext is not None:
                compress('ponysay.info', 'ponysay.info.' + ext, ext)

        if conf['pdf-compression'] is not None:
            ext = conf['pdf-compression']
            if ext is not None:
                compress('ponysay.pdf', 'ponysay.pdf.' + ext, ext)

        for command in commands:
            source = 'completion/ponysay'
            sourceed = 'completion/ponysay.%s' % (command)
            try:
                fileout = open(sourceed, 'wb+')
                filein = open(source, 'rb')
                data = filein.read().decode('utf-8', 'replace')

                if data.startswith('(ponysay\n'):
                    data = ('(%s ' % command) + data[len('(ponysay\n'):]
                elif data.startswith('(ponysay '):
                    data = ('(%s ' % command) + data[len('(ponysay '):]
                elif '\n(ponysay\n' in data:
                    edpos = data.find('\n(ponysay\n')
                    data = data[:edpos] + ('\n(%s\n' % command) + data[edpas + len('\n(ponysay\n'):]
                elif '\n(ponysay ' in data:
                    data = data[:edpos] + ('\n(%s ' % command) + data[edpas + len('\n(ponysay '):]
                else:
                    raise Exception('File %s does not look like expected' % source)

                fileout.write(data.encode('utf-8'))
            finally:
                if fileout is not None:  fileout.close()
                if filein  is not None:  filein .close()

        for shell in [item[0] for item in shells]:
            if conf[shell] is not None:
                for command in commands:
                    sourceed = 'completion/ponysay.%s' % (command)
                    generated = 'completion/%s-completion.%s' % (shell, command)
                    generatorcmd = './completion/auto-auto-complete.py %s --output %s --source %s' % (shell, generated, sourceed)
                    Popen(generatorcmd.split(' ')).communicate()
                    if conf[command] is not None:
                        dest = generated + '.install'
                        (fileout, filein) = (None, None)
                        try:
                            fileout = open(dest, 'wb+')
                            filein = open(generated, 'rb')
                            data = filein.read().decode('utf-8', 'replace')

                            data = data.replace('/usr/bin/ponysay', conf[command])
                            data = data.replace('/ponysay', '\0')
                            data = data.replace('ponysay', command)
                            data = data.replace('/usr/share/ponysay/', conf['share-dir'] if conf['share-dir'].endswith('/') else (conf['share-dir'] + '/'))
                            data = data.replace('\0', '/ponysay')

                            fileout.write(data.encode('utf-8'))
                        finally:
                            if fileout is not None:  fileout.close()
                            if filein  is not None:  filein .close()

        if conf['quotes'] is not None:
            self.removeLists([], ['quotes'])
            os.mkdir('quotes')
            ponymap = None
            try:
                ponymap = open('ponyquotes/ponies', 'rb')
                ponies = [line for line in ponymap.read().decode('utf-8', 'replace').split('\n')]
                for _ponies in ponies:
                    for pony in _ponies.split('+'):
                        if len(pony) > 0:
                            print('Generating quote files for \033[34m' + pony + '\033[39m')
                            for file in os.listdir('ponyquotes'):
                                if file.startswith(pony + '.'):
                                    if os.path.isfile('ponyquotes/' + file):
                                        shutil.copy('ponyquotes/' + file, 'quotes/' + _ponies + file[file.find('.'):])
            finally:
                if ponymap is not None:
                    ponymap.close()

        for (sharedir, hasponies) in [(sharedir[0], sharedir[3]) for sharedir in sharedirs]:
            if hasponies and os.path.isdir(sharedir):
                for toolcommand in ('--dimensions', '--metadata'):
                    if not self.free:
                        print('%s, %s, %s' % ('./src/ponysaytool.py', toolcommand, sharedir))
                        Popen(['./src/ponysaytool.py', toolcommand, sharedir], stdin=PIPE, stdout=PIPE, stderr=PIPE).communicate()
                    else:
                        params = ['./src/ponysaytool.py', toolcommand, sharedir, '--']
                        for sharefile in os.listdir(sharedir):
                            if sharefile.endswith('.pony') and (sharefile != '.pony'):
                                if not Setup.validateFreedom(sharedir + '/' + sharefile):
                                    print('Skipping metadata correction for %s/%s, did not pass validation process made by setup settings' % (sharedir, sharefile))
                                else:
                                    params.append(sharefile)
                        print('%s, %s, %s (with files)' % ('./src/ponysaytool.py', toolcommand, sharedir))
                        Popen(params, stdin=PIPE, stdout=PIPE, stderr=PIPE).communicate()

        print()

    def install(self, conf):
        '''
        Install compiled ponysay
        '''

        print('\033[1;34m::\033[39mInstalling...\033[21m')

        dests = []
        for command in commands:
            if conf[command] is not None:
                dests.append(conf[command])
        if len(dests) > 0:
            self.cp(False, 'ponysay.install', dests)
            print('Setting mode for ponysay.install copies to 755')
            if self.linking == COPY:
                for dest in dests:
                    os.chmod(dest, 0o755)
            else:
                os.chmod(dests[0], 0o755)
        if conf['shared-cache'] is not None:
            dir = conf['shared-cache']
            if not os.path.exists(dir):
                pdir = dir[:dir.rfind('/') + 1]
                if not os.path.exists(pdir):
                    print('Creating intermediate-level directories needed for ' + dir)
                    os.makedirs(pdir)
                print('Creating directory ' + dir)
                os.mkdir(dir)
                print('Setting permission mode mask for ' + dir + ' to 7777')
                Popen('chmod -R 7777 -- \'' + dir.replace('\'', '\'\\\'\'') + '\'', shell=True).wait()
                print('Setting group for ' + dir + ' users')
                Popen('chown -R :users -- \'' + dir.replace('\'', '\'\\\'\'') + '\'', shell=True).wait()
        for shell in [item[0] for item in shells]:
            if conf[shell] is not None:
                for command in commands:
                    if conf[command] is not None:
                        src = 'completion/%s-completion.%s.install' % (shell, command)
                        dest = os.path.join(
                            os.path.dirname(conf[shell]),
                            os.path.basename(conf[shell]).replace('ponysay', command))
                        self.cp(False, src, [dest])
        if conf['pdf'] is not None:
            src = 'ponysay.pdf' + ('' if conf['pdf-compression'] is None else '.' + conf['pdf-compression'])
            dest = conf['pdf'] + '/' + src
            self.cp(False, src, [dest])
        if conf['info'] is not None:
            installinfo = []
            dests = []
            ext = ('' if conf['info-compression'] is None else '.' + conf['info-compression'])
            src = 'ponysay.info' + ext
            for command in commands:
                if conf[command] is not None:
                    dest = conf['info'] + '/' + command + '.info' + ext
                    dests.append(dest)
                    if conf['info-install'] is not None:
                        cmdarr = ['install-info', '--entry=Miscellaneous', '--description=' + conf['info-install'], '--dir-file=' + conf['info'] + '/dir', dest]
                        cmd = ' '.join(['\'%s\'' % (elem.replace('\'', '\'\\\'\'')) for elem in cmdarr])
                        installinfo.append((cmd, 'Installing info manual ' + dest + ' with install-info'))
            self.cp(False, src, dests)
            for pair in installinfo:
                print(pair[1])
                os.system(pair[0])
        for man in manpages:
            key = 'man-' + man[0]
            section = conf['man-section-ponysay']
            if man is manpages[0]:  sub = 'man' + section
            else:                   sub = man[0] + '/man' + section
            if man is manpages[0]:  lang = ''
            else:                   lang = '.' + man[0]
            if conf[key] is not None:
                src = 'manuals/manpage' + lang + '.6.' + ('install' if conf[key + '-compression'] is None else conf[key + '-compression'])
                dests = []
                for command in commands:
                    if conf[command] is not None:
                        dest = '%s/%s/%s.%s%s' % (conf[key], sub, command, section, '' if conf[key + '-compression'] is None else '.' + conf[key + '-compression'])
                        dests.append(dest)
                self.cp(False, src, dests)
        ponyshare = conf['share-dir']
        if not ponyshare.endswith('/'):
            ponyshare += '/'
        for dir in sharedirs:
            if conf[dir[0]] is not None:
                self.cp(True, dir[0], [ponyshare + dir[0]], Setup.validateFreedom if self.free else None)
        for file in sharefiles:
            if conf[file[0]] is not None:
                self.cp(False, 'share/' + file[1], [ponyshare + file[0]], Setup.validateFreedom if self.free else None)
        for file in miscfiles:
            self.cp(False, file[0], [conf[file[0]]], Setup.validateFreedom if self.free else None)
        print()

    def uninstall(self, conf):
        '''
        Uninstall ponysay
        '''

        print('\033[1;34m::\033[39mUninstalling...\033[21m')

        (files, dirs, infos) = ([], [], [])

        for command in commands:
            if conf[command] is not None:
                files.append(conf[command])
        if conf['shared-cache'] is not None:
            dirs.append(conf['shared-cache'])
        for shell in [item[0] for item in shells]:
            if conf[shell] is not None:
                for command in commands:
                    if conf[command] is not None:
                        files.append(conf[shell].replace('ponysay', command))
        if conf['pdf'] is not None:
            files.append(conf['pdf'] + '/ponysay.pdf' + ('' if conf['pdf-compression'] is None else '.' + conf['pdf-compression']))
        if conf['info'] is not None:
            for command in commands:
                if conf[command] is not None:
                    file = conf['info'] + '/' + command + '.info' + ('' if conf['info-compression'] is None else '.' + conf['info-compression'])
                    files.append(file)
                    if conf['info-install'] is not None:
                        infos.append(file)
        for man in [item[0] for item in manpages]:
            key = 'man-' + man
            section = conf['man-section-ponysay']
            if man is manpages[0]:  sub = 'man' + section
            else:                   sub = man[0] + '/man' + section
            if conf[key] is not None:
                for command in commands:
                    if conf[command] is not None:
                        files.append('%s/%s/%s.%s%s' % (conf[key], sub, command, section, '' if conf[key + '-compression'] is None else '.' + conf[key + '-compression']))
        ponyshare = conf['share-dir']
        if not ponyshare.endswith('/'):
            ponyshare += '/'
        for dir in sharedirs:
            if conf[dir[0]] is not None:
                dirs.append(ponyshare + dir[0])
        for file in sharefiles:
            if conf[file[0]] is not None:
                files.append(ponyshare + file[0])
        for file in miscfiles:
            files.append(conf[file[0]])

        for info in infos:
            cmdarr = ['install-info', '--delete', '--dir-file=' + conf['info'] + '/dir', info]
            cmd = ' '.join(['\'%s\'' % (elem.replace('\'', '\'\\\'\'')) for elem in cmdarr])
            print('Uninstalling info manual ' + info + ' with install-info')
            os.system(cmd)

        self.removeLists(files, dirs)
        print()

    def uninstallOld(self, conf):
        '''
        Uninstall file ponysay no longer uses
        '''

        print('\033[1;34m::\033[39mUninstalling old files...\033[21m')

        instdir = conf['~prefix~'] + '/usr'
        files = [instdir + f for f in ['bin/ponysaylist.pl', 'bin/ponysaytruncater', 'bin/ponysay.py', 'bin/ponythink.py']]
        dirs = [instdir + d for d in ['lib/ponysay', 'share/ponies', 'share/ttyponies']]
        #$(instdir)/lib/ponysay/truncater
        #$(instdir)/lib/ponysay/list.pl
        #$(instdir)/lib/ponysay/linklist.pl
        #$(instdir)/lib/ponysay/pq4ps
        #$(instdir)/lib/ponysay/pq4ps.pl
        #$(instdir)/lib/ponysay/pq4ps-list
        #$(instdir)/lib/ponysay/pq4ps-list.pl

        self.removeLists(files, dirs)
        print()

    def clean(self):
        '''
        Remove compiled files
        '''

        print('\033[1;34m::\033[39mCleaning...\033[21m')

        files = ['ponysay.info', 'ponysay.info.gz', 'ponysay.info.xz',  'ponysay.pdf.gz', 'ponysay.pdf.xz', 'ponysay.install', 'ponysay.zip']
        files += ['src/%s.install' % file for file in ponysaysrc]
        dirs = ['quotes']
        for comp in ['install', 'gz', 'xz']:
            for man in manpages:
                if man is manpages[0]:  man = ''
                else:                   man = '.' + man[0]
                for sec in range(0, 9):
                    files.append('manuals/manpage%s.%s.%s' % (man, str(sec), comp))
        for shell in [item[0] for item in shells]:
            for command in commands:
                files.append('completion/%s-completion.%s' % (shell, command))
                files.append('completion/%s-completion.%s.install' % (shell, command))
        for sharedir in [sharedir[0] for sharedir in sharedirs]:
            for dimfile in ('widths', 'heights', 'onlyheights'):
                files.append(sharedir + '/' + dimfile)

        self.removeLists(files, dirs)
        print()

    def cleanOld(self):
        '''
        Remove compiled files ponysay is no longer compiling
        '''

        print('\033[1;34m::\033[39mCleaning old files...\033[21m')

        files = ['truncater', 'ponysaytruncater', 'ponysay.py.install', 'ponysay.install~', 'ponysay.zip']
        dirs = []
        for shell in [item[0] for item in shells]:
            files.append('completion/%s-completion.%s.install' % (shell, 'sh' if shell == 'bash' else shell))
            files.append('completion/%s-completion-think.%s'   % (shell, 'sh' if shell == 'bash' else shell))
        for shell in [item[0] for item in shells]:
            for command in commands:
                files.append('completion/%s-completion.%s.%s' % (shell, 'sh' if shell == 'bash' else shell, command))

        self.removeLists(files, dirs)
        print()

    def removeLists(self, files, dirs):
        '''
        Removes listed files and directories
        '''

        for file in files:
            if os.path.isfile(file) or os.path.islink(file):
                print('Unlinking file %s' % (file))
                os.unlink(file)
                dir = file
                while True:
                    dir = dir[:dir.rfind('/')]
                    if ('/ponysay/' in (dir + '/')) and (len(os.listdir(dir)) == 0):
                        print('Removing newly empty directory %s' % (dir))
                        os.rmdir(dir)
                    else:
                        break;
        for dir in dirs:
            if os.path.isdir(dir) or os.path.islink(dir):
                print('Cascadingly removing directory %s' % (dir))
                if os.path.islink(dir):  os.unlink(dir)
                else:                    shutil.rmtree(dir)
                while True:
                    dir = dir[:dir.rfind('/')]
                    if ('/ponysay/' in (dir + '/')) and (len(os.listdir(dir)) == 0):
                        print('Removing newly empty directory %s' % (dir))
                        os.rmdir(dir)
                    else:
                        break;

    @staticmethod
    def validateFreedom(filename):
        '''
        Check whether a file is fully free
        '''

        if not os.path.isdir(filename):
            if filename.endswith('.pony') and (filename != '.pony') and not filename.endswith('/.pony'):
                with open(filename, 'rb') as file:
                    data = file.read().decode('utf8', 'replace')
                    if data.startswith('$$$\n') and ('\n$$$\n' in data) and not data.startswith('$$$\n$$$\n'):
                        data = data[4 : data.find('\n$$$\n')].split('\n')
                        for line in data:
                            if ':' not in line:
                                continue
                            line = [item.strip() for item in line.split(':')]
                            if (len(line) == 2) and (line[0] == 'FREE'):
                                return line[1].lower() == 'yes'
                return False
        return True

    def cp(self, recursive, source, destinations, validatehook = None):
        '''
        Copys a files or directory to multiple destinations
        '''

        if validatehook is not None:
            if not validatehook(source):
                print('Ignoring installation of file %s (did not pass validation process made by setup settings)' % source)
                return
        if os.path.islink(source) and (self.linking != COPY) and os.path.isdir(os.path.realpath(source)):
            target = os.readlink(source)
            for dest in destinations:
                print('Creating symbolic link %s with target directory %s' % (dest, target))
                if os.path.exists(dest):
                    self.removeLists([], [dest])
                self.symlink(target, dest)
        if os.path.islink(source) and (self.linking != COPY) and os.path.isfile(os.path.realpath(source)):
            target = os.readlink(source)
            if self.linking == HARD:
                for dest in destinations:
                    print('Creating hard link %s with target file %s' % (dest, target))
                    if os.path.exists(dest):
                        self.removeLists([], [dest])
                    mytarget = os.path.abspath(os.path.join(os.path.dirname(dest), target))
                    if os.path.exists(mytarget):
                        os.link(mytarget, dest)
                    else:
                        print('\033[31mTarget did not exists, using symlink instead\033[39m')
                        self.symlink(target, dest)
            else:
                for dest in destinations:
                    print('Creating symbolic link %s with target file %s' % (dest, target))
                    if os.path.exists(dest):
                        self.removeLists([], [dest])
                    self.symlink(target, dest)
        for dest in destinations:
            dir = dest[:dest.rfind('/') + 1]
            if not os.path.exists(dir):
                print('Making directory ' + dir + ' with parents')
                os.makedirs(dir)
        if recursive:
            target = destinations[0]
            for dest in destinations if self.linking == COPY else [target]:
                print('Copying directory %s to %s' % (source, dest))
                if not os.path.exists(dest):
                    os.mkdir(dest)
                links = []
                for file in os.listdir(source):
                    src = source + '/' + file
                    if os.path.exists(src) and os.path.islink(src):
                        links.append((os.path.isdir(src), src, [dest + '/' + file]))
                    else:
                        self.cp(os.path.isdir(src), src, [dest + '/' + file], validatehook)
                for link in links:
                    self.cp(link[0], link[1], link[2], validatehook)
            if self.linking != COPY:
                for dest in destinations[1:]:
                    print('Creating symbolic link %s with target directory %s' % (dest, target))
                    if os.path.exists(dest):
                        self.removeLists([], [dest])
                    self.symlink(target, dest)
        else:
            target = destinations[0]
            for dest in destinations if self.linking == COPY else [target]:
                print('Copying file %s to %s' % (source, dest))
                shutil.copyfile(source, dest)
            if self.linking == HARD:
                for dest in destinations[1:]:
                    print('Creating hard link %s with target file %s' % (dest, target))
                    if os.path.exists(dest):
                        os.unlink(dest)
                    os.link(target, dest)
            elif self.linking == SYMBOLIC:
                for dest in destinations[1:]:
                    print('Creating symbolic link %s with target file %s' % (dest, target))
                    if os.path.exists(dest):
                        os.unlink(dest)
                    self.symlink(target, dest)

    def symlink(self, target, dest):
        '''
        Create a symlink with a relative path
        '''

        if target.startswith('./') or target.startswith('../'):
            os.symlink(target, dest)
        elif '/' not in target:
            try:
                os.symlink('./' + target, dest)
            except OSError as e:
                if e.errno != 17:
                    raise
        else:
            targets = target.split('/')
            dests = dest.split('/')

            while (len(targets) > 1) and (len(dests) > 1) and (targets[0] == dests[0]):
                targets = targets[1:]
                dests = dests[1:]

            if (len(dests) == 1):
                targets = ['.'] + targets
            else:
                targets = (['..'] * (len(dests) - 1)) + targets

            os.symlink('/'.join(targets), dest)

    def configure(self, opts):
        '''
        Parses configurations
        '''

        (defaults, conf) = ({}, {})

        for command in commands:
            conf[command] = '/usr/bin/' + command
        conf['shared-cache'] = '/var/cache/ponysay'
        for shell in shells:
            conf[shell[0]] = shell[1]
        conf['pdf'] = '/usr/doc'
        conf['pdf-compression'] = 'gz'
        conf['info'] = '/usr/share/info'
        conf['info-install'] = 'My Little Ponies for your terminal'
        conf['info-compression'] = 'gz'
        for manpage in manpages:
            conf['man-' + manpage[0]] = '/usr/share/man'
            conf['man-' + manpage[0] + '-compression'] = 'gz'
        conf['custom-env-python'] = 'python3'
        for sharedir in sharedirs:
            conf[sharedir[0]] = True
        for sharefile in sharefiles:
            conf[sharefile[0]] = True
        for miscfile in miscfiles:
            conf[miscfile[0]] = miscfile[1]
        conf['sysconf-dir'] = '/etc'
        conf['bin-dir'] = '/usr/bin'
        conf['lib-dir'] = '/usr/lib/ponysay'
        conf['libexec-dir'] = '/usr/libexec/ponysay'
        conf['share-dir'] = '/usr/share'

        if opts['--private'] is not None:
            if opts['--prefix'] is None:
                opts['--prefix'] = [os.environ['HOME'] + '/.local']

        prefix = '/usr'
        if opts['--prefix'] is not None:
            prefix = opts['--prefix'][0]
            for key in conf:
                if conf[key] not in (None, False, True):
                    if conf[key].startswith('/usr'):
                        conf[key] = prefix + conf[key][4:]
        conf['~prefix~'] = prefix

        if opts['--opt'] is not None:
            if opts['--bin-dir']           is None:  opts['--bin-dir']           = ['/opt/ponysay']
            if opts['--lib-dir']           is None:  opts['--lib-dir']           = ['/opt/ponysay']
            if opts['--libexec-dir']       is None:  opts['--libexec-dir']       = ['/opt/ponysay']
            if opts['--share-dir']         is None:  opts['--share-dir']         = ['/opt/ponysay']
            if opts['--with-shared-cache'] is None:  opts['--with-shared-cache'] = ['/var/opt/ponysay/cache']

        for dir in ['bin', 'lib', 'libexec', 'share']:
            key = dir + '-dir'
            if opts['--' + key] is not None:
                conf[key] = opts['--' + key][0]
            if (dir == 'share') and (opts['--opt'] is None):
                conf[key] += '/ponysay'
            if conf[key].startswith('usr/'):
                conf[key] = prefix + conf[key][3:]
        if opts['--cache-dir'] is not None:
            dir = opts['--cache-dir'][0]
            for key in conf:
                if conf[key] not in (None, False, True):
                    if conf[key].startswith('/var/cache'):
                        conf[key] = dir + conf[key][10:]

        if opts['--sysconf-dir'] is not None:
            conf['sysconf-dir'] = opts['--sysconf-dir'][0]

        for key in conf:
            defaults[key] = conf[key]

        if opts['--nothing'] is not None:
            opts['--minimal'] = opts['--nothing']

        for key in ['custom-env-python']:
            conf[key] = None

        if opts['--everything'] is None:
            for key in ['pdf', 'pdf-compression']:
                conf[key] = None

            nomanen = opts['--minimal'] is not None
            for manpage in manpages:
                if (manpage is not manpages[0]) or nomanen:
                    for key in ['man-' + manpage[0]]:
                        conf[key] = None

        if (opts['--private'] is not None) or (opts['--minimal'] is not None):
            for key in ['info-install', 'shared-cache']:
                conf[key] = None

        if opts['--minimal'] is not None:
            for key in ['info'] + [item[0] for item in shells]:
                conf[key] = None
            for sharedir in sharedirs:
                if sharedir is not sharedirs[0]:
                    conf[sharedir[0]] = None
            for sharefile in sharefiles:
                conf[sharefile[0]] = None

        if opts['--nothing'] is not None:
            for command in commands:
                conf[command] = None
            conf[sharedirs[0][0]] = None

        for coll in [['shell', '/usr/share', [item[0] for item in shells]],
                     ['man', '/usr/share/man', ['man-' + item[0] for item in manpages]],
                     ['man-compression', 'gz', ['man-' + item[0] + '-compression' for item in manpages]]
                    ]:
            if opts['--without-' + coll[0]] is not None:
                for item in coll[2]:
                    conf[item] = None
            if opts['--with-' + coll[0]] is not None:
                for item in coll[2]:
                    defaults[item] = conf[item] = defaults[item].replace(coll[1], coll[1] if opts['--with-' + coll[0]][0] is None else opts['--with-' + coll[0]][0]);

        for key in conf:
            if '--with-' + key not in opts:
                continue
            if opts['--with-' + key] is not None:
                if defaults[key] in (False, True):
                    conf[key] = True
                else:
                    conf[key] = defaults[key] if opts['--with-' + key][0] is None else opts['--with-' + key][0]
            if opts['--without-' + key] is not None:
                conf[key] = False if defaults[key] in (False, True) else None

        for mansection in mansections:
            if opts['--man-section-' + mansection[0]] is not None:
                conf['man-section-' + mansection[0]] = opts['--man-section-' + mansection[0]][0]
            else:
                conf['man-section-' + mansection[0]] = mansection[1]

        self.destDir = None if opts['--dest-dir'] is None else opts['--dest-dir'][0]

        return conf

    def applyDestDir(self, conf):
        if self.destDir is not None:
            for key in conf:
                if conf[key] not in (None, False, True):
                    if conf[key].startswith('/'):
                        conf[key] = self.destDir + conf[key]

    def unapplyDestDir(self, conf):
        if self.destDir is not None:
            for key in conf:
                if conf[key] not in (None, False, True):
                    if conf[key].startswith(self.destDir):
                        conf[key] = conf[key][len(self.destDir):]

ARGUMENTLESS = 0
ARGUMENTED = 1

class ArgParser():
    '''
    Simple argument parser, a strip down of the one in ponysay and slitly modified
    '''

    def __init__(self, program, description, usage, longdescription = None):
        '''
        Constructor.
        The short description is printed on same line as the program name
        '''

        self.__program = program
        self.__description = description
        self.__usage = usage
        self.__longdescription = longdescription
        self.__arguments = []
        (self.opts, self.optmap) = ({}, {})

    def add_argumentless(self, alternatives, help = None):
        '''
        Add option that takes no arguments
        '''

        ARGUMENTLESS
        self.__arguments.append((ARGUMENTLESS, alternatives, None, help))
        (stdalt, self.opts[stdalt]) = (alternatives[0], None)
        for alt in alternatives:  self.optmap[alt] = (stdalt, ARGUMENTLESS)

    def add_argumented(self, alternatives, arg, help = None):
        '''
        Add option that takes one argument
        '''

        self.__arguments.append((ARGUMENTED, alternatives, arg, help))
        (stdalt, self.opts[stdalt]) = (alternatives[0], None)
        for alt in alternatives:  self.optmap[alt] = (stdalt, ARGUMENTED)

    def parse(self, argv = sys.argv):
        '''
        Parse arguments
        '''

        self.argcount = len(argv) - 1
        self.files = []
        (argqueue, optqueue, get) = ([], [], False)

        for arg in argv[1:]:
            if get:
                get = False
                if (arg is argv[-1]) or ((len(arg) > 2) and (arg[:2] in ('--', '++'))):
                    argqueue.append(None)
                else:
                    argqueue.append(arg)
                    continue
            if (len(arg) > 2) and (arg[:2] in ('--', '++')):
                if (arg in self.optmap) and (self.optmap[arg][1] == ARGUMENTLESS):
                    optqueue.append(arg)
                    argqueue.append(None)
                elif '=' in arg:
                    arg_opt = arg[:arg.index('=')]
                    if (arg_opt in self.optmap) and (self.optmap[arg_opt][1] == ARGUMENTED):
                        optqueue.append(arg_opt)
                        argqueue.append(arg[arg.index('=') + 1:])
                    else:
                        self.print_fatal('Unrecognized option {}. Use --help or consult the manual.', arg)
                        exit(-1)
                elif (arg in self.optmap) and (self.optmap[arg][1] == ARGUMENTED):
                    optqueue.append(arg)
                    get = True
                else:
                    self.print_fatal('Unrecognized option {}. Use --help or consult the manual.', arg)
                    exit(-1)
            else:
                self.files.append(arg)

        (i, n) = (0, len(optqueue))
        if len(argqueue) < n:
            argqueue.append(None)
        while i < n:
            (opt, arg, i) = (optqueue[i], argqueue[i], i + 1)
            opt = self.optmap[opt][0]
            if (opt not in self.opts) or (self.opts[opt] is None):
                self.opts[opt] = [arg]
            else:
                self.print_fatal('duplicate option {}', arg)
                exit(-1)

    def print_fatal(self, message, *args):
        sys.stderr.write('{}: fatal: {}\n'.format(self.__program, message.format(*args)))

    def usage(self):
        '''
        Print a short usage message.
        '''

        if self.__longdescription is not None:
            print(self.__longdescription)
            print()
        print('\n\033[1mUSAGE:\033[21m', end='')
        first = True
        for line in self.__usage.split('\n'):
            if first:  first = False
            else:      print('    or', end='')
            print('\t{}'.format(line))

        print()

    def help(self):
        '''
        Prints a colourful help message.
        '''

        # The usage should be terse so this header is only included in the help command.
        print('\033[1m{}\033[21m - {}\n'.format(self.__program, self.__description))

        self.usage()

        print('\033[1mCONFIGURATIONS:\033[21m\n')
        for opt in self.__arguments:
            (opt_type, opt_alts, opt_arg, opt_help) = opt[0:4]
            if opt_help is not None:
                for opt_alt in opt_alts:
                    if opt_alt is opt_alts[-1]:
                        print('\t%s=\033[4m%s\033[24m' % (opt_alt, opt_arg) if opt_type == ARGUMENTED else '\t' + opt_alt)
                    else:
                        print('\t\033[2m' + opt_alt + '\033[22m')
                first = True
                for line in opt_help.split('\n'):
                    print(('\t\t\033[32;1m%s\033[21;39m' if first else '\t\t%s') % (line))
                    first = False
                print()
        print()

if __name__ == '__main__':
    Setup()


#!/usr/bin/env python
# coding: utf-8

# In[1]:

import cvxopt as cvx
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import scipy.spatial.distance as dt

# In[2]:

# read data into memory
images_data_set = np.genfromtxt("hw06_images.csv", delimiter = ",")
labels_data_set = np.genfromtxt("hw06_labels.csv", delimiter = ",").astype(int)

# In[3]:

# divide data set into two parts: training set and test set
x_training = images_data_set[0:1000, :]
x_test = images_data_set[1000:, :]
y_training = labels_data_set[0:1000]
y_test = labels_data_set[1000:]

# In[4]:

# get number of classes
K = np.max(y_training)

# get number of samples and number of features
N_training = len(y_training)
N_test = len(y_test)

# In[5]:

# define Gaussian kernel function
def gaussian_kernel(X1, X2, s):
    D = dt.cdist(X1, X2)
    K = np.exp(-D**2 / (2 * s**2))
    return(K)

# In[6]:

def SVM(Y, C):
    yyK = np.matmul(Y[:,None], Y[None,:]) * K_training

    P = cvx.matrix(yyK)
    q = cvx.matrix(-np.ones((N_training, 1)))
    G = cvx.matrix(np.vstack((-np.eye(N_training), np.eye(N_training))))
    h = cvx.matrix(np.vstack((np.zeros((N_training, 1)), C * np.ones((N_training, 1)))))
    A = cvx.matrix(1.0 * Y[None,:])
    b = cvx.matrix(0.0)

    # use cvxopt library to solve QP problems
    result = cvx.solvers.qp(P, q, G, h, A, b)
    alpha = np.reshape(result["x"], N_training)
    alpha[alpha < C * epsilon] = 0
    alpha[alpha > C * (1 - epsilon)] = C

    # find bias parameter
    support_indices, = np.where(alpha != 0)
    active_indices, = np.where(np.logical_and(alpha != 0, alpha < C))
    w0 = np.mean(Y[active_indices] * (1 - np.matmul(yyK[np.ix_(active_indices, support_indices)], alpha[support_indices])))

    return alpha, w0

# In[7]:

s = 10
C = 10
epsilon = 1e-3

# calculate Gaussian kernel
K_training = gaussian_kernel(x_training, x_training, s)

SVM_alphas = []
SVM_w0s = []
for i in range (K):
    y_adjusted = np.ones(y_training.shape[0])
    y_adjusted[y_training != i+1] = -1
    alpha, w0 = SVM(y_adjusted, C)
    SVM_alphas.append(alpha)
    SVM_w0s.append(w0)
SVM_alphas = np.array(SVM_alphas)
SVM_w0s = np.array(SVM_w0s)

# In[8]:

# calculate predictions on training samples
scores = []
for i in range(K):
    y_adjusted = np.ones(y_training.shape[0])
    y_adjusted[y_training != i+1] = -1
    f_predicted = np.matmul(K_training, y_adjusted[:,None] * SVM_alphas[i][:,None]) + SVM_w0s[i]
    f_predicted = np.squeeze(f_predicted)
    scores.append(f_predicted)
scores = np.array(scores)

y_predicted = np.argmax(scores, axis=0)
y_predicted = y_predicted + 1

# calculate confusion matrix
confusion_matrix = pd.crosstab(np.reshape(y_predicted, N_training), y_training, rownames = ['y_predicted'], colnames = ['y_train'])
print(confusion_matrix)

# In[9]:

K_test = gaussian_kernel(x_test, x_training, s)

# calculate predictions on test samples
scores = []
for i in range(K):
    y_adjusted = np.ones(y_training.shape[0])
    y_adjusted[y_training != i+1] = -1
    f_predicted = np.matmul(K_test, y_adjusted[:,None] * SVM_alphas[i][:,None]) + SVM_w0s[i]
    f_predicted = np.squeeze(f_predicted)
    scores.append(f_predicted)
scores = np.array(scores)

y_predicted = np.argmax(scores, axis=0)
y_predicted = y_predicted + 1

# calculate confusion matrix
confusion_matrix = pd.crosstab(np.reshape(y_predicted, N_test), y_test, rownames = ['y_predicted'], colnames = ['y_test'])
print(confusion_matrix)

# In[10]:

def calc_accuracy(C):
    SVM_alphas = []
    SVM_w0s = []
    for i in range (K):
        y_adjusted = np.ones(y_training.shape[0])
        y_adjusted[y_training != i+1] = -1
        alpha, w0 = SVM(y_adjusted, C)
        SVM_alphas.append(alpha)
        SVM_w0s.append(w0)
    SVM_alphas = np.array(SVM_alphas)
    SVM_w0s = np.array(SVM_w0s)

    # calculate predictions on training samples
    scores = []
    for i in range(K):
        y_adjusted = np.ones(y_training.shape[0])
        y_adjusted[y_training != i+1] = -1
        f_predicted = np.matmul(K_training, y_adjusted[:,None] * SVM_alphas[i][:,None]) + SVM_w0s[i]
        f_predicted = np.squeeze(f_predicted)
        scores.append(f_predicted)
    scores = np.array(scores)

    y_predicted = np.argmax(scores, axis=0)
    y_predicted = y_predicted + 1

    train_accuracy = np.sum(y_predicted == y_training) / len(y_training)

    # calculate predictions on test samples
    scores = []
    for i in range(K):
        y_adjusted = np.ones(y_training.shape[0])
        y_adjusted[y_training != i+1] = -1
        f_predicted = np.matmul(K_test, y_adjusted[:,None] * SVM_alphas[i][:,None]) + SVM_w0s[i]
        f_predicted = np.squeeze(f_predicted)
        scores.append(f_predicted)
    scores = np.array(scores)

    y_predicted = np.argmax(scores, axis=0)
    y_predicted = y_predicted + 1

    test_accuracy = np.sum(y_predicted == y_test) / len(y_test)

    return train_accuracy, test_accuracy

# In[11]:

training_accuracy = []
test_accuracy = []
for C in [1e-1, 1e0, 1e1, 1e2, 1e3]:
    training, test = calc_accuracy(C)
    training_accuracy.append(training)
    test_accuracy.append(test)

# In[12]:

fig = plt.figure(figsize = (8,6))

plt.plot([1e-1, 1e0, 1e1, 1e2, 1e3], training_accuracy, "b.-", label = "training", markersize = 10)
plt.plot([1e-1, 1e0, 1e1, 1e2, 1e3], test_accuracy, "r.-", label = "test", markersize = 10)

plt.xscale("log")
plt.xlabel("Regularization parameter (C)")
plt.ylabel("Accuracy")
plt.legend(loc = "upper left")
plt.show()


from django.conf.urls import url, include
import xadmin

from apps.home import views

urlpatterns = [
    # url('admin/', admin.site.urls),
    #  替换admin为xadmin
    url('xadmin/', xadmin.site.urls),
    # 定位home
    url('home/', include('apps.home.urls')),
    # 定义首页
    url('^$', views.index, name='index'),
    # 搜寻模块
    url('search/', include('apps.search.urls')),
    url('cars/', include('apps.cars.urls')),
    url('account/', include('apps.account.urls')),
    url('order/', include('apps.order.urls')),
]


"""foxconn URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/2.1/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.urls import include, path
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path,re_path
#导入图集views函数
from picture   import views as pic_views
#导入文学views函数
from story  import views as sto_views
#导入文件views函数
from file import views as file_views

urlpatterns = [

    #图片墙功能路由集合-------------------------------------------------------------------------------------------
    path('admin/', admin.site.urls),
    #配置图片网址的首页地址路由
    # re_path('^$',pic_views.pic_index),
    re_path('$^',file_views.index),
    re_path('pic_index.html$',pic_views.pic_index),
    #配置用户注册路由
    re_path('register.html$',pic_views.register),
    #配置用户名验证路由
    re_path('check_user.html$',pic_views.check_user),
    # 配置用户登录路由
    re_path('login.html$', pic_views.login),
    # 配置用户退出路由
    re_path('logout.html$', pic_views.logout),
    #后台首页路由-图片墙
    re_path('my_admin',pic_views.my_admin),
    #我要上传图片
    re_path('pic_upload.html$',pic_views.pic_upload),
    #处理图片上传路由
    re_path('pic_upload_data.html$',pic_views.pic_upload_data),
    #编辑图片路由
    re_path('pic_edit.html$',pic_views.pic_edit),
    #删除图片路由
    re_path('pic_del.html$',pic_views.pic_del),
    #图集详细信息列表
    re_path('picd_list.html$',pic_views.picd_list),
    #图集详细信息添加路由
    re_path('picd_add.html$',pic_views.picd_add),
    # 图集详细信息数据添加路由
    re_path('picd_add_data.html$', pic_views.picd_add_data),
    # 图集详细信息数据添加路由
    re_path('picd_edit.html$', pic_views.picd_edit),
    # 删除详情图片路由
    re_path('picd_del.html$', pic_views.picd_del),
    # 图集评论表路由
    re_path('pic_index_new.html$', pic_views.pic_index_new),
    #图集评论表删除功能
    re_path('com_del.html$',pic_views.com_del),
    #图集评论入库功能
    re_path('com_data.html',pic_views.com_data),
    #图集点击自动增加路由
    re_path('pic_hot_add.html$',pic_views.pic_hot_add),
    #我的个人资料路由
    re_path('my_info.html$',pic_views.my_info),
    #用户资料编辑
    re_path('user_edit.html$',pic_views.user_edit),

    #文学表路由控制集合------------------------------------------------------------------------------------------------
    #文学网首页路由
    re_path('story_index.html',sto_views.story_index),
    #文章点击自动增加
    re_path('sto_hot_add.html',sto_views.sto_hot_add),
    # 文章首页详情页面1
    re_path('story_index_list.html', sto_views.story_index_list),
    #文章首页详情页面
    re_path('story_index_detail.html',sto_views.story_index_detail),
    #文学网后台管理首页
    re_path('my_story.html$',sto_views.my_story),
    #文学添加界面
    re_path('story_add.html$',sto_views.story_add),
    # 文学添加数据处理
    re_path('story_add_data.html$', sto_views.story_add_data),
    #文学编辑界面
    re_path('story_edit.html$',sto_views.story_edit),
    #文学详情展示界面
    re_path('story_detail_list.html',sto_views.story_detail_list),
    # 文学详情添加界面
    re_path('story_detail_add.html', sto_views.story_detail_add),
    #文学章节添加数据处理
    re_path('story_detail_add_data.html',sto_views.story_detail_add_data),
    #文章章节编辑界面展示
    re_path('story_detail_edit.html',sto_views.story_detail_edit),
    #文章章节删除
    re_path('story_detail_del.html',sto_views.story_detail_del),
    #文章删除
    re_path('story_del.html',sto_views.story_del),

#文件表路由控制集合------------------------------------------------------------------------------------------------
    re_path('index.html$', file_views.index),  # 定义文件馆首页路由
    re_path('download.html$', file_views.download),
    re_path('upload.html$', file_views.upload),
    re_path('file_del.html$',file_views.file_del),

#网络爬虫测试，自动登录、自动评论功能实现
    re_path('pachong',file_views.pachong),
    re_path('py_requests',file_views.py_requests),

]


# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def pathSum(self, root, sum):
        """
        :type root: TreeNode
        :type sum: int
        :rtype: int
        """
        def helper(root, prev, sum):
            if not root:
                return 0
            curr = prev + root.val
            if curr == sum:
                return 1 + helper(root.left,curr,sum) + helper(root.right,curr,sum)
            else:
                return helper(root.left,curr,sum) + helper(root.right,curr,sum)

        if not root:
            return 0
        return helper(root,0,sum) + self.pathSum(root.left,sum) + self.pathSum(root.right,sum)


class Queue:
    __a = []
    def __init__(self):
        print('object created')
    def enqueue(self,x):
        self.__a.append(x)
    def dequeue(self):
        if len(self.__a) == 0:
            return 'under flow'
        else:
            x = self.__a[0]
            self.__a.pop(0)
            return x
    def isEmpty(self):
        if len(self.__a) == 0:
            return True
        return False
class Stack:
    __counter = 0
    __q1 = Queue()
    __q2 = Queue()
    def __init__(self):
        print('object is created')
    def isEmpty(self):
        return self.__q1.isEmpty()
    def push(self,x):
        self.__q1.enqueue(x)
        self.__counter += 1
    def pop(self):
        if self.__counter == 0:
            pass
        else:
            for i in range(self.__counter):
                self.__q2.enqueue(self.__q1.dequeue())
            for i in range(self.__counter-1):
                self.__q1.enqueue(self.__q2.dequeue())
            self.__counter -= 1
            return self.__q2.dequeue()
#I wrote it in order to check if queue is working
s1 = Stack()
for i in range(10):
    s1.push(i**2)
for i in range(9):
    print(s1.pop())
s1.push(10)
s1.push(20)
s1.push(30)
print(s1.pop())
print(s1.pop())
print(s1.pop())
print(s1.pop())


from django.urls import path

from .views import *

urlpatterns = [
    path('', store, name='store'),
    path('cart/', cart, name='cart'),
    path('checkout/', checkout, name='checkout'),
    path('categories/', CategoryView.as_view(), name='categories'),
    path('categories/create/', CategoryCreateView.as_view(), name='create_category'),
    path('products/by_weight/', ProductByWeightView.as_view(), name='products_by_weight'),
    path('products/by_weight/create/', ProductByWeightCreateView.as_view(), name='create_product_by_weight'),
    path('products/by_quantity/', ProductByQuantityView.as_view(), name='products_by_quantity'),
    path('products/by_quantity/create/', ProductByQuantityCreateView.as_view(), name='create_product_by_quantity'),
    path('products/other/', ProductView.as_view(), name='products_other'),
    path('products/other/create/', ProductCreateView.as_view(), name='create_products_other'),
    path('customers/', CustomerView.as_view(), name='customers'),
    path('create_account/', CustomerCreateView.as_view(), name='create_customer'),
    path('orders/', OrderView.as_view(), name='orders'),
    path('orders/<pk>/items/', OrderItemView.as_view(), name='order_items'),
]


import model
import torch
import numpy as np
from itertools import count
from settings import DEVICE,EPOCHS,GAME,EPISODE_LIFE,CLIP_REWARDS,SCALE,FRAME_STACK,STEPS
from atari_wrappers import wrap_deepmind,make_atari

import time
import matplotlib.pyplot as plt
from utils import log

env=make_atari(GAME)
env=wrap_deepmind(env,episode_life=EPISODE_LIFE,clip_rewards=CLIP_REWARDS,frame_stack=FRAME_STACK,scale=SCALE)

#
np.random.seed(1)
env.seed(0)

log_reward=log(abs_dir_path="F:/github/RL/Atari_DQN/log")
log_epic_step=log(abs_dir_path="F:/github/RL/Atari_DQN/log")
log_loss=log(abs_dir_path="F:/github/RL/Atari_DQN/log")

agent = model.Agent(DEVICE)

total_count=0
start_time=time.time()

done_=False

for i in count():
    if done_:
        break

    state = env.reset()

    epic_reward=0
    for t in count():
        total_count+=1

        if total_count%10000==0:
            end_time=time.time()
            print("1 w frame time: {}".format(end_time-start_time))
            start_time=end_time
            if total_count==STEPS:
                done_=True
                break

        action=agent.select_action(state)

        state_,reward,done,_=env.step(action.item())

        epic_reward+=reward

        reward=torch.tensor([reward],device=DEVICE)

        agent.remember(state,action,state_,reward)

        state=state_

        agent.learn()

        if done:
            log_reward.add(epic_reward)
            log_epic_step.add(t+1)

            print("episode: {} reward: {} step: {} memory: {}".format(i,epic_reward,t+1,len(agent.memory)))

            break

log_loss.add(agent.loss_hist)

fig=plt.figure()

ax1=fig.add_subplot(221)
ax2=fig.add_subplot(222)
ax3=fig.add_subplot(223)
ax1.plot(np.arange(0,len(log_loss.buf)),log_loss.buf)
ax2.plot(np.arange(0,len(log_reward.buf)),log_reward.buf)
ax3.plot(np.arange(0,len(log_epic_step.buf)),log_epic_step.buf)

plt.show()

log_epic_step.write(file_name="epic_step_3m.csv")
log_reward.write(file_name="reward_3m.csv")
log_loss.write(file_name="loss_3m.csv")


import numpy as np
import torch
import torch.nn as nn
from ..networks.generic_network import GenericNetwork
from ..networks.lstm import LSTMNetwork
from ..networks.multilayer_perceptron import MLPNetwork
from torch.distributions.normal import Normal

class ContinuousActor(GenericNetwork):
    def __init__(self, device_id: int = 0):
        super().__init__(device_id=device_id)

    def __new__(cls, *args, **kwargs):
        if cls is ContinuousActor:
            raise TypeError(
                f"'{cls.__name__}' should not be directly instantiated. "
                + f"Try '{cls.__name__}MLP' or '{cls.__name__}LSTM' instead."
            )
        return object.__new__(cls)

    def set_up_parameters(
        self,
        shape: tuple,
        learning_rate: float,
        starting_std_dev: float,
        clip_epsilon: float,
        entropy_coefficient: float,
    ):
        self.clip_epsilon = clip_epsilon
        self.entropy_coefficient = entropy_coefficient
        num_actions = shape[-1]
        self.log_standard_deviation = nn.Parameter(
            torch.ones((1, num_actions), device=self.device) * np.log(starting_std_dev)
        )
        self.create_optimizer(learning_rate)

    def get_distribution(
        self, inputs: torch.Tensor, should_detach: bool = False
    ) -> Normal:
        means = self.forward(inputs)
        if should_detach:
            means = means.detach()
        standard_deviations = torch.exp(self.log_standard_deviation)
        return Normal(means, standard_deviations)

    def gradient_ascent_step(
        self,
        states: torch.Tensor,
        actions: torch.Tensor,
        log_probs: torch.Tensor,
        advantages: torch.Tensor,
    ) -> None:
        self.optimizer.zero_grad()
        loss = self.compute_actor_loss(states, actions, log_probs, advantages)
        loss.backward()
        self.optimizer.step()

    def compute_actor_loss(
        self,
        states: torch.Tensor,
        actions: torch.Tensor,
        log_probs: torch.Tensor,
        advantages: torch.Tensor,
    ) -> torch.Tensor:
        distribution = self.get_distribution(states)
        new_log_probs = distribution.log_prob(actions)
        prob_ratios = torch.exp(new_log_probs - log_probs)
        first_term = prob_ratios * advantages
        second_term = (
            torch.clamp(prob_ratios, 1 - self.clip_epsilon, 1 + self.clip_epsilon)
            * advantages
        )
        clip_objective = torch.minimum(first_term, second_term)
        mean_clipped_objective = clip_objective.mean().mean()
        entropy = distribution.entropy().mean()
        actor_objective = mean_clipped_objective + self.entropy_coefficient * entropy
        return -actor_objective

class ContinuousActorMLP(MLPNetwork, ContinuousActor):
    def __init__(
        self,
        shape: tuple,
        learning_rate: float = 3e-4,
        starting_std_dev: float = 1.0,
        clip_epsilon: float = 0.2,
        entropy_coefficient: float = 0.01,
        layer_activation=nn.ELU,
        output_activation=nn.Tanh,
        device_id: int = 0,
    ):
        super().__init__(
            shape=shape,
            layer_activation=layer_activation,
            output_activation=output_activation,
            device_id=device_id,
        )
        self.set_up_parameters(
            shape, learning_rate, starting_std_dev, clip_epsilon, entropy_coefficient
        )

class ContinuousActorLSTM(LSTMNetwork, ContinuousActor):
    def __init__(
        self,
        shape: tuple,
        sequence_length: int,
        learning_rate: float = 3e-4,
        starting_std_dev: float = 1.0,
        clip_epsilon: float = 0.2,
        entropy_coefficient: float = 0.01,
        output_activation=nn.Tanh,
        device_id: int = 0,
    ):
        super().__init__(
            shape=shape,
            sequence_length=sequence_length,
            output_activation=output_activation,
            device_id=device_id,
        )
        self.set_up_parameters(
            shape, learning_rate, starting_std_dev, clip_epsilon, entropy_coefficient
        )


# -*- coding: utf-8 -*-
from __future__ import print_function

__author__= "Luis C. Pérez Tato (LCPT) and Ana Ortega (AO_O)"
__copyright__= "Copyright 2015, LCPT and AO_O"
__license__= "GPL"
__version__= "3.0"
__email__= "l.pereztato@ciccp.es ana.ortega@ciccp.es"

import geom

# Define sliding vector system
svd1=geom.SlidingVectorsSystem3d()

# Defina a sliding vector.
ptoAplic=geom.Pos3d(1,1,0)
testVector=geom.Vector3d(0,0,1)
vec= geom.SlidingVector3d(ptoAplic,testVector)
svd1+=vec # add it to the system.

# Another sliding vector.
ptoAplic=geom.Pos3d(-1,1,0)
vec=geom.SlidingVector3d(ptoAplic,testVector)
svd1+=vec # add it to the system.

# And another one
ptoAplic=geom.Pos3d(-1,-1,0)
vec=geom.SlidingVector3d(ptoAplic,testVector)
svd1+=vec # add it to the system

# Again...
ptoAplic=geom.Pos3d(1,-1,0)
vec=geom.SlidingVector3d(ptoAplic,testVector)
svd1+=vec # add it to the system

Res= svd1.getResultant()
Mom= svd1.getMoment()

#It seems there is a bug in svd.getResultant() it returns a SlidingVectorsSystem3d instead of a Vector3d.
#print('type<Res>= ', type(Res))
#print('Res= ', Res)

import os
from misc_utils import log_messages as lmsg
fname= os.path.basename(__file__)
if Res.x == 0 and Res.y == 0 and Res.z == 4 and Mom.x == 0 and Mom.y == 0 and Mom.z == 0:
    print('test: '+fname+': ok.')
else:
    lmsg.error('test: '+fname+' ERROR.')


# -*- coding: utf-8 -*-

import pygame
from pygame.sprite import Sprite

class Bullet(Sprite):
    """管理飞船所发射的子弹的类"""

    def __init__(self, ai_game):
        """在飞船当前位置创建一个子弹对象"""

        # 继承 Sprite 类
        super().__init__()

        # 指向 AlienInvasion 的 screen 对象
        self.screen = ai_game.screen

        # 实现对 Settings 的访问
        self.settings = ai_game.settings
        self.color = ai_game.settings.bullet_color

        # 在（0, 0）处创建一个表示子弹的矩形
        self.rect = pygame.Rect(0, 0, self.settings.bullet_width, self.settings.bullet_height)

        # 再设置子弹的正确位置，即飞船中间顶部
        self.rect.midtop = ai_game.ship.rect.midtop

        # 存储用小数表示的子弹位置
        self.y = float(self.rect.y)

    def update(self):
        """向上移动子弹"""

        # 更新表示子弹位置的小数
        self.y -= self.settings.bullet_speed

        # 更新表示子弹的 rect 的位置
        self.rect.y = self.y

    def draw_bullet(self):
        """在屏幕上绘制子弹"""

        pygame.draw.rect(self.screen, self.color, self.rect)


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Mar 18 09:45:20 2020

@author: Jian Wang
"""

import bitset as bst
from util import Utility as utl
from builtins import staticmethod
import numpy as np

class SpnCipher:

    SBoxTbl = [14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7]
    SBoxInvTbl = [14, 3, 4, 8, 1, 12, 10, 15, 7, 13, 9, 6, 11, 2, 0, 5]
    PermuteTbl = [0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15]

    def __init__(self):
        pass

    def getSBox(self, bits4):
        # idx = utl.toInteger(bSet, 4)
        idx = int(bits4)
        return utl.toBitSet(self.SBoxTbl[idx], 4)

    def getSBoxInv(self, bits4):
        # idx = utl.toInteger(bSet, 4)
        idx = int(bits4)
        return utl.toBitSet(self.SBoxInvTbl[idx], 4)

    def getSBoxGroup(self, bits16):
        result = bst.Bitset(0, 16)

        for i in range(4):
            toSBox = bst.Bitset(0, 4)
            for j in range(4):
                # toSBox.set(j, bSet.get(i * 4 + j))
                toSBox[j] = bits16[i * 4 + j]

            fromSBox = self.getSBox(toSBox)
            for j in range(4):
                result[i * 4 + j] = fromSBox[j]

        return result

    def getSBoxInvGroup(self, bits16):
        result = bst.Bitset(0, 16);
        for i in range(4):
            toSBox = bst.Bitset(0, 4)
            for j in range(4):
                toSBox[j] = bits16[i * 4 + j]

            fromSBox = self.getSBoxInv(toSBox)
            for j in range(4):
                result[i * 4 + j] = fromSBox[j]

        return result

    def permuteBits(self, bits16):
        bSetPerm = bst.Bitset(0, 16)
        for i in range(16):
            bSetPerm[self.PermuteTbl[i]] = bits16[i]

        return bSetPerm

    def EncRound(self, ptBits, keyBits, permute=False):
        ptBits ^= keyBits
        ctBits = self.getSBoxGroup(ptBits)
        if permute:
            ctBits = self.permuteBits(ctBits)
        return ctBits

    def DecRound(self, ctBits, keyBits, permute=False):
        if permute:
            ctBits = self.permuteBits(ctBits)
        ptBits = self.getSBoxInvGroup(ctBits)

        ptBits ^= keyBits
        return ptBits;

    def Encrypt(self, ptBits, roundKeys):
        #ptBits = utl.getCopy(ptBits, 16)
        for i in range(4):
            ptBits = self.EncRound(ptBits, roundKeys[i], i != 3)
        ptBits ^= roundKeys[4]
        return ptBits

    def Decrypt(self, ctBits, roundKeys):
        #ctBits = utl.getCopy(ctBits, 16);
        ctBits ^= roundKeys[4]
        for i in range(3, -1, -1):
            ctBits = self.DecRound(ctBits, roundKeys[i], i != 3)
        return ctBits

    def partialEncrypt(self, ctBits, roundKey=0, roundNo=3):
        #ctBits ^= roundKey;
        ctBits = self.EncRound(ctBits, roundKey, roundNo != 3)
        return ctBits

    def partialDecrypt(self, ctBits, roundKey, roundNo):
        #ctBits ^= roundKey;
        ctBits = self.DecRound(ctBits, roundKey, roundNo != 3)
        return ctBits

    #get Diff Pair in 4 bits
    def getDiffPair4(self):
        X = np.arange(16, dtype=bst.Bitset)
        Y = np.zeros(16, dtype=bst.Bitset)
        for i in range(16):
            X[i] = utl.toBitSet(i, 4)
            Y[i] = self.getSBox(X[i])

        # Difference Pair Matrix
        dpm = np.zeros(16*16, dtype=int).reshape(16,16)
        dp = np.zeros(16, dtype=int)
        for i in range(16):
            for j in range(16):
                dx = utl.toBitSet(j, 4)
                Xprm = X[i] ^ dx
                Yprm = Y[i] ^ Y[int(Xprm)]
                #jj = int(dx)
                val = int(Yprm)
                dpm[i][j] = val
            # stat the high prob

        for j in range(16):
            #dp[j] = np.max(dpm[:,j])
            col = dpm[:,j]
            counts = np.bincount(col)   #count occurrence of 0..array_max (e.g. bincount([0, 1, 1])=[0:1, 1:2, 2:0])
            mx = np.argmax(counts)
            dp[j] = mx

        #get DDT (Diff Distribution Table) of SBox
        ddt = np.zeros(16*16, dtype=int).reshape(16,16)
        for i in range(16):
            #unique, counts = np.unique(dpm[:,i], return_counts=True)
            #counts = np.pad(counts, (0,16-counts.shape[0]))
            counts = np.bincount(dpm[:,i])
            counts = np.pad(counts, (0,16-counts.shape[0]))
            ddt[i, :] = counts

        return dp, dpm, ddt

    #Diff Pair in 16 bits
    def getDiffPair16(self, seed16, dp):
        X = utl.toBitSet(0xF000, 16)
        Y = utl.toBitSet(seed16, 16)
        Z = utl.toBitSet(0, 16)
        for i in range(4):
            sb = int(X & Y) >> (4-1 - i) * 4
            sb = int(dp[sb])
            sb = sb << (4-1 - i) * 4
            Xpm = utl.toBitSet(sb, 16)
            Z = Xpm | Z
            X = X >> 4

        return Z

    def getLastRoundDiffPair(self, dp, seed):
        print('delta P =', "{0:#0{1}X}".format(seed,6))

        print('       ____....____....')
        print('U1 =', "{0:#0{1}b}".format(int(seed),18), "= {0:#0{1}X}".format(seed,6))
        z = self.getDiffPair16(seed, dp)
        print('V1 =', "{0:#0{1}b}".format(int(z),18), "= {0:#0{1}X}".format(int(z),6))   #bin(int(z)))

        z = self.permuteBits(z)
        print('U2 =', "{0:#0{1}b}".format(int(z),18), "= {0:#0{1}X}".format(int(z),6))   #bin(int(seed)))
        z = self.getDiffPair16(int(z), dp)
        print('V2 =', "{0:#0{1}b}".format(int(z),18), "= {0:#0{1}X}".format(int(z),6))   #bin(int(z)))

        z = self.permuteBits(z)
        print('U3 =', "{0:#0{1}b}".format(int(z),18), "= {0:#0{1}X}".format(int(z),6))   #bin(int(seed)))
        z = self.getDiffPair16(int(z), dp)
        print('V3 =', "{0:#0{1}b}".format(int(z),18), "= {0:#0{1}X}".format(int(z),6))   #bin(int(z)))

        z = self.permuteBits(z)
        print('U4 =', "{0:#0{1}b}".format(int(z),18), "= {0:#0{1}X}".format(int(z),6))   #bin(int(seed)))

    def test1(self):
        ptext = utl.toBitSet(0x2345, 16)
        sub1 = self.getSBoxGroup(ptext)
        print('substitution:', utl.toInteger(sub1, 16), '-', int(sub1))

        print('permutation: ', int(self.permuteBits(ptext)))

        sub1 = self.getSBoxGroup(utl.toBitSet(0x1234, 16))
        sub2 = self.getSBoxGroup(utl.toBitSet(0x5678, 16))
        subinv3 = self.getSBoxInvGroup(utl.toBitSet(0x9abc, 16))
        print('permutation: ', int(sub1), " - ", int(sub2))
        print('subinv: ', int(subinv3))

    #Test encrypt/decrypt
    def test2(self):
        spnKey = [bst.Bitset(0x1234, 16), bst.Bitset(0x5678, 16), bst.Bitset(0x9ABC, 16),
              bst.Bitset(0xDEF0, 16), bst.Bitset(0x1357, 16)]
        pt = 0x1234
        ctenc = self.Encrypt(utl.toBitSet(pt, 16), spnKey)
        #ctenc = utl.toBitSet(0x52C0, 16)
        ptdec = self.Decrypt(ctenc, spnKey)
        print('Step1: Encrypt(0x1234) = ', hex(int(ctenc)),
              '\nStep2:          Decrypt(',hex(int(ctenc)),') = ', hex(int(ptdec)),
              '\nStep3: The original plaintext = ', hex(pt), ', it equals to the result of Step2')

    #Test encrypt/decrypt
    def test3(self):
        spnKey = [bst.Bitset(0x1234, 16), bst.Bitset(0x5678, 16), bst.Bitset(0x9ABC, 16),
              bst.Bitset(0xDEF0, 16), bst.Bitset(0x1357, 16)]
        # process of encrypting
        #deltaX = utl.toBitSet(0x0b00, 16)
        deltaX = utl.toBitSet(0x1234, 16)
        for i in range(4):
            deltaY = self.partialEncrypt(deltaX, spnKey[i], i)
            print('y', i+1, '=', hex(int(deltaY)))
            deltaX = deltaY
        deltaY ^= spnKey[4]
        print('y5 = ', hex(int(deltaY)))

        # process of encrypting
        deltaY ^= spnKey[4]
        print('x4 = ', hex(int(deltaY)))
        for i in range(3, -1, -1):
            deltaX = self.partialDecrypt(deltaY, spnKey[i], i)
            print('x', i, '=', hex(int(deltaX)))
            deltaY = deltaX

    def test4(self):
        deltaX = utl.toBitSet(0x0b00, 16)

        dY = self.getSBoxGroup(deltaX)
        print(hex(int(dY)))

        print('dX = ', hex(int(deltaX)))
        for i in range(4):
            deltaY = self.partialEncrypt(deltaX)
            print('y', i+1, '=', hex(int(deltaY)))
            deltaX = deltaY

    def test5(self):
        dp, dpm, ddt = self.getDiffPair4()
        print('Diff Pair = \n', dp)
        #print(dpm)
        print('\nDDT (Difference Distribution Table) = \n', ddt)

    def test6(self):
        dp = self.getDiffPair4()
#        print('delta P = 0xB000')
#        self.getLastRoundDiffPair(dp, 0xB000)  # 0b0000 1011 0000 1011
        self.getLastRoundDiffPair(dp, 0x0B00)  # 0b0000 0110 0000 0110
#         print('seed = 0x00B0')
#         self.getLastRoundDiffPair(dp, 0x00B0)  # 0b0000 0101 0000 0101
#         print('seed = 0x000B')
#         self.getLastRoundDiffPair(dp, 0x000B)  # 0b0000 1010 0000 1010

#         print('seed = 0xD000')
#         self.getLastRoundDiffPair(dp, 0xD000)  # 0b1011 0000 1011 0000
#         print('seed = 0x0D00')
#         self.getLastRoundDiffPair(dp, 0x0D00)  # 0b0110 0000 0110 0000
#         print('seed = 0x00D0')
#         self.getLastRoundDiffPair(dp, 0x00D0)  # 0b0101 0000 0101 0000
        print('\n')
        self.getLastRoundDiffPair(dp, 0x000D)  # 0b1010 0000 1010 0000

#         print('seed = 0x1000')
#         self.getLastRoundDiffPair(dp, 0x1000)
#         print('seed = 0x0100')
#         self.getLastRoundDiffPair(dp, 0x0100)
#         print('seed = 0x0010')
#         self.getLastRoundDiffPair(dp, 0x0010)
#         print('seed = 0x0001')
#         self.getLastRoundDiffPair(dp, 0x0001)
#
#         print('seed = 0x2000')
#         self.getLastRoundDiffPair(dp, 0x2000)
#         print('seed = 0x0200')
#         self.getLastRoundDiffPair(dp, 0x0200)
#         print('seed = 0x0020')
#         self.getLastRoundDiffPair(dp, 0x0020)
#         print('seed = 0x0002')
#         self.getLastRoundDiffPair(dp, 0x0002)
#
#         print('seed = 0x3000')
#         self.getLastRoundDiffPair(dp, 0x3000)
#         print('seed = 0x0300')
#         self.getLastRoundDiffPair(dp, 0x0300)
#         print('seed = 0x0030')
#         self.getLastRoundDiffPair(dp, 0x0030)
#         print('seed = 0x0003')
#         self.getLastRoundDiffPair(dp, 0x0003)
#
#         print('seed = 0x4000')
#         self.getLastRoundDiffPair(dp, 0x4000)
#         print('seed = 0x0400')
#         self.getLastRoundDiffPair(dp, 0x0400)
#         print('seed = 0x0040')
#         self.getLastRoundDiffPair(dp, 0x0040)
#         print('seed = 0x0004')
#         self.getLastRoundDiffPair(dp, 0x0004)
#
#         print('seed = 0x0005')
#         self.getLastRoundDiffPair(dp, 0x0005)
#         print('seed = 0x0006')
#         self.getLastRoundDiffPair(dp, 0x0006)
#         print('seed = 0x0007')
#         self.getLastRoundDiffPair(dp, 0x0007)
#         print('seed = 0x0008')
#         self.getLastRoundDiffPair(dp, 0x0008)
#         print('seed = 0x0009')
#         self.getLastRoundDiffPair(dp, 0x0009)
#         print('seed = 0x000A')
#         self.getLastRoundDiffPair(dp, 0x000A)
#         print('seed = 0x000C')
#         self.getLastRoundDiffPair(dp, 0x000C)
#         print('seed = 0x000D')
#         self.getLastRoundDiffPair(dp, 0x000D)     #*********** [1010 0000 1010 0000]
#         print('seed = 0x000E')
#         self.getLastRoundDiffPair(dp, 0x000E)

#SpnCipher().test1()
#SpnCipher().test2()
#SpnCipher().test3()
#SpnCipher().test4()
#SpnCipher().test5()
#SpnCipher().test6()

#SpnCipher().test5()


import sys
import synonyms
def nearbyWords(keyword):
    words = synonyms.nearby(keyword)
    if words != None:
        return words[0]
    else:
        return None

if __name__ == '__main__':
    a = []
    for i in range(1, len(sys.argv)):
        a.append(sys.argv[i])
    print(nearbyWords(a[0]))


from  bs4 import BeautifulSoup
import requests

r = requests.get("https://sports.news.naver.com/wfootball/record/index?category=primera&league=650&tab=player")
html = r.text # 텍스트 형식으로 데이터 추출

soup = BeautifulSoup(html, 'html.parser')
# print(soup)
num = soup.select('#wfootballPlayerRecordBody > table > tbody > tr > td.num')
align_l = soup.select('#wfootballPlayerRecordBody > table > tbody > tr > td.align_l')
# print(txt)
for i in num:
    print(i.get_text())

for i in align_l:
    print(i.get_text().strip())

list = []
for i in range(len(align_l)):
    print(align_l[i].get_text().strip())
print(list)

# txt = soup.select('#wfootballPlayerRecordBody > table > tbody > tr')

# for i in txt:
#     print(i.get_text().strip())

# for i in range(len(txt)):
#     print((i+1), '위 :', txt[i].get_text().strip())