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

#
#  Roulette - Klasse  von zufall
#

#
#  This file is part of zufall
#
#
#  Copyright (c) 2019 Holger Böttcher  hbomat@posteo.de
#
#
#  Licensed under the Apache License, Version 2.0 (the "License")
#  you may not use this file except in compliance with the License
#  You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.
#

import matplotlib.pyplot as plt
import matplotlib.patches as patches

from sympy import Rational, Integer, nsimplify
from sympy.core.compatibility import iterable
from sympy.printing.latex import latex

from zufall.lib.objekte.basis import ZufallsObjekt
from zufall.lib.objekte.gleich_verteilung import GleichVerteilung
from zufall.lib.objekte.datenreihe import DatenReihe
from zufall.lib.funktionen.graf_funktionen import verlauf as verlauf_grafik

from zufall.lib.objekte.ausnahmen import ZufallError

# Roulette - Klasse
# -----------------

class Roulette(ZufallsObjekt):
    """

Roulette - Spiel

**Erzeugung**

   Roulette( )

    """

    def __new__(cls, *args, **kwargs):

        if kwargs.get("h") in (1, 2, 3, 4):
            roulette_hilfe(kwargs["h"])
            return

        cls.colonne1     = {1,4,7,10,13,16,19,22,25,28,31,34}
        cls.colonne2     = {2,5,8,11,14,17,20,23,26,29,32,35}
        cls.colonne3     = {3,6,9,12,15,18,21,24,27,30,33,36}
        cls.douze_premier = set(range(1, 13))
        cls.douze_milieu  = set(range(13, 25))
        cls.douze_dernier = set(range(25, 37))
        cls.pair         = set(range(2, 37, 2))
        cls.impair       = set(range(1, 36, 2))
        cls.rouge        = {1,3,5,7,9,12,14,16,18,19,21,23,25,27,30,32,34,36}
        cls.noir         = set(range(1, 37)).difference(cls.rouge)
        cls.manque       = set(range(1, 19))
        cls.passe        = set(range(19, 37))

        return ZufallsObjekt.__new__(cls)

    def __str__(self):
        return "Roulette"

# Eigenschaften + Methoden
# ------------------------

    @property
    def omega(self):
        """Ergebnismenge"""
        return set(range(37))

    @property
    def regeln(self):
        """Regeln für Roulettespiel"""

        print("\nRoulette - Glücksspiel\n")
        print("Eine Kugel wird in eine sich drehende Scheibe geworfen. Sie landet in ")
        print("einem der Felder 0 bis 36, die auf der Scheibe in bunter Reihenfolge")
        print("angeordnet sind (Gewinnfeld)\n")
        print("Auf dem Spielbrett setzt man Spielmarken (Chips) und gewinnt, wenn die ")
        print("Vorhersage eintrifft, das heißt, wenn das Gewinnfeld durch die getrof-")
        print("fene Wahl erfaßt wird; zu den Setzmöglichkeiten siehe Hilfeseite\n")
        print("Wenn die Vorhersage nicht eintrifft, ist der gesetzte Chip verloren ")
        print("Das entspricht einer Gewinnquote von -1 : 1\n")
        return

    @property
    def brett(self):
        """Spielbrett / -tisch (Abbildung)"""

        def pline(x, y):
            return plt.plot(x, y, color=(0,0,0), lw=0.8)

        def prot(x, y, t):
            return ax.text(x, y, t, fontsize=9, horizontalalignment='center',
                  verticalalignment='center', color=(1,0,0),
                  fontname='Times New Roman')

        def pblack(x, y, t):
            return ax.text(x, y, t, fontsize=9, horizontalalignment='center',
                   verticalalignment='center', color=(0,0,0),
                   fontname='Times New Roman')

        def punt(x, y):
            ax.text(x, y, '12', fontsize=6, horizontalalignment='center',
                  verticalalignment='center', color=(0,0,0),
                   fontname='Times New Roman')

        dx, dy = 1.5, 1.5
        fig = plt.figure(figsize=(3, 4))
        ax = fig.add_subplot(1, 1, 1)
        ax.spines['top'].set_visible(False)
        ax.spines['bottom'].set_visible(False)
        ax.spines['right'].set_visible(False)
        ax.spines['left'].set_visible(False)
        ax.set_xticks([])
        plt.axes().xaxis.set_ticks_position('none')
        ax.set_yticks([])
        plt.axes().yaxis.set_ticks_position('none')
        plt.xlim(0, 10*dx)
        plt.ylim(-0.1, 15*dy)
        pline([3*dx, 6*dx, 6*dx, 3*dx, 3*dx], [0, 0, 14*dy, 14*dy, 0])
        pline([4*dx, 4*dx], [dy, 13*dy])
        pline([5*dx, 5*dx], [dy, 13*dy])
        for i in range(1, 14):
            pline([3*dx, 6*dx], [i*dy, i*dy])
        pline([0, 0], [2*dy, 12*dy])
        pline([9*dx, 9*dx], [2*dy, 12*dy])
        pline([3*dx, 0], [dy, 2*dy])
        pline([3*dx, 0], [2*dy, 3*dy])
        pline([6*dx, 9*dx], [dy, 2*dy])
        pline([6*dx, 9*dx], [2*dy, 3*dy])
        pline([0, 3*dx], [12*dy, 13*dy])
        pline([9*dx, 6*dx], [12*dy, 13*dy])
        pline([0, 9*dx], [5*dy, 5*dy])
        pline([0, 9*dx], [9*dy, 9*dy])
        pline([2*dx, 2*dx], [1.35*dy, 2.3*dy])
        pline([7*dx, 7*dx], [1.35*dy, 2.3*dy])
        pline([dx, dx], [1.7*dy, 2.65*dy])
        pline([8*dx, 8*dx], [1.7*dy, 2.65*dy])
        ax.add_patch(patches.RegularPolygon(
            (1.7*dx, 3.7*dy), 4, 0.6*dx, color=(0,0,0)))
        ax.add_patch(patches.RegularPolygon(
            (7.4*dx, 3.7*dy), 4, 0.6*dx, facecolor=(1,0,0)))
        ax.text(4.5*dx, 13.4*dy, '0', fontsize=9, horizontalalignment='center', \
               verticalalignment='center', color=(0,1,0))
        prot(3.5*dx, 12.4*dy, '1')
        pblack(4.5*dx, 12.4*dy, '2')
        prot(5.5*dx, 12.4*dy, '3')
        pblack(3.5*dx, 11.4*dy, '4')
        prot(4.5*dx, 11.4*dy, '5')
        pblack(5.5*dx, 11.4*dy, '6')
        prot(3.5*dx, 10.4*dy, '7')
        pblack(4.5*dx, 10.4*dy, '8')
        prot(5.5*dx, 10.4*dy, '9')
        pblack(3.5*dx, 9.4*dy, '10')
        pblack(4.5*dx, 9.4*dy, '11')
        prot(5.5*dx, 9.4*dy, '12')
        pblack(3.5*dx, 8.4*dy, '13')
        prot(4.5*dx, 8.4*dy, '14')
        pblack(5.5*dx, 8.4*dy, '15')
        prot(3.5*dx, 7.4*dy, '16')
        pblack(4.5*dx, 7.4*dy, '17')
        prot(5.5*dx, 7.4*dy, '18')
        prot(3.5*dx, 6.4*dy, '19')
        pblack(4.5*dx, 6.4*dy, '20')
        prot(5.5*dx, 6.4*dy, '21')
        pblack(3.5*dx, 5.4*dy, '22')
        prot(4.5*dx, 5.4*dy, '23')
        pblack(5.5*dx, 5.4*dy, '24')
        prot(3.5*dx, 4.4*dy, '25')
        pblack(4.5*dx, 4.4*dy, '26')
        prot(5.5*dx, 4.4*dy, '27')
        pblack(3.5*dx, 3.4*dy, '28')
        pblack(4.5*dx, 3.4*dy, '29')
        prot(5.5*dx, 3.4*dy, '30')
        pblack(3.5*dx, 2.4*dy, '31')
        prot(4.5*dx, 2.4*dy, '32')
        pblack(5.5*dx, 2.4*dy, '33')
        prot(3.5*dx, 1.4*dy, '34')
        pblack(4.5*dx, 1.4*dy, '35')
        prot(5.5*dx, 1.4*dy, '36')
        pblack(0.5*dx, 2.4*dy, 'P')
        pblack(8.5*dx, 2.4*dy, 'P')
        punt(0.7*dx, 2.13*dy)
        punt(8.7*dx, 2.13*dy)
        pblack(1.35*dx, 2.07*dy, 'M')
        pblack(7.35*dx, 2.07*dy, 'M')
        punt(1.72*dx, 1.85*dy)
        punt(7.72*dx, 1.85*dy)
        pblack(2.45*dx, 1.75*dy, 'D')
        pblack(6.45*dx, 1.75*dy, 'D')
        punt(2.75*dx, 1.48*dy)
        punt(6.75*dx, 1.48*dy)
        pblack(1.5*dx, 10.5*dy, 'Passe')
        pblack(7.5*dx, 10.5*dy, 'Manque')
        pblack(1.5*dx, 7*dy, 'Pair')
        pblack(7.5*dx, 7*dy, 'Impair')

        plt.show()

    tisch = brett

    @property
    def formeln(self):
        """Berechnungsformeln"""

        def dm(x):
            return display(Math(x))

        print(' ')
        dm('\mathrm{Gewinnerwartung\;beim\; Roulette - Spiel}')
        print(' ')
        dm('\mathrm{Gewinnerwartung\; bei\; einer\; Setzmöglichkeit\; (Chance):}')
        dm('\\qquad GewinnQuote \cdot GewinnWahrscheinlichkeit\, + \, (-1) \cdot VerlustWahrscheinlichkeit')
        dm('\\qquad \mathrm{(\,-1 = Verlustquote\;[gesetzter\;Chip]\,)}')
        dm('\mathrm{Die\; Wahrscheinlichkeiten\; der\; Chancen\; werden\; so\; berechnet:}')
        dm('\qquad P( Chance ) = (Anzahl\; der\; Zahlen\; in\; der\; Chance)\; / \;37')
        print(' ')
        dm('\mathrm{ Erwartung\; bei\; den\; einzelnen\; Chancen: \\qquad (s.a.\; Hilfeseite)}')
        dm('\mathrm{ Chance \\qquad\\qquad\;\; Anzahl \\qquad Erwartung \;\\qquad\\qquad\\quad	\, in\; \% \;zum}')
        dm('\mathrm{\\qquad\\qquad\\qquad\\quad\, Zahlen	\\qquad\\qquad\\qquad\\qquad\\qquad\\qquad	Einsatz}')
        dm('\mathrm{plein} \;\,\\qquad\\qquad\\quad\\quad\\quad 1 \\quad\\quad	35 \\cdot \\frac{1}{37} - \\frac{36}{37} = -\\frac{1}{37} \\quad\\quad -2.7 \%')
        dm('\mathrm{a\; cheval} \;\\qquad\\qquad\\quad\\quad 2 \\quad\\quad	17 \\cdot \\frac{2}{37} - \\frac{35}{37} = -\\frac{1}{37} \\quad\\quad -2.7 \%')
        dm('\mathrm{transversale\; plein} \\quad\\qquad 3 \\quad\\quad	11 \\cdot \\frac{3}{37} - \\frac{34}{37} = -\\frac{1}{37} \\quad\\quad -2.7 \%')
        dm('\mathrm{carre} \;\,\\qquad\\qquad\\quad\\quad\\quad 4 \\quad\\quad	8 \\cdot \\frac{4}{37} - \\frac{33}{37} = -\\frac{1}{37} \;\,\\quad\\quad -2.7 \%')
        dm('\mathrm{transversale\; simple} \,\;\\qquad 6 \\quad\\quad	5 \\cdot \\frac{6}{37} - \\frac{31}{37} = -\\frac{1}{37} \;\\quad\\quad -2.7 \%')
        dm('\mathrm{Kolonne,\; Dutzend} \,\,\\qquad 12 \\quad\\quad	2 \\cdot \\frac{12}{37} - \\frac{25}{37} = -\\frac{1}{37} \;\\quad\\quad -2.7 \%')
        dm('\mathrm{einfache\; Chancen} \;\;\\qquad 18 \\quad\\quad	1 \\cdot \\frac{18}{37} - \\frac{19}{37} = -\\frac{1}{37} \;\\quad\\quad -2.7 \%')
        print(' ')

    def P(self, *args, **kwargs):
        """Wahrscheinlichkeit eines Ereignisses"""

        if kwargs.get('h'):
            print("\nWahrscheinlichkeit eines Ereignisses\n")
            print("Aufruf   r . P( e )\n")
            print("                r    Roulette-Objekt")
            print("                e    Zahl aus {0,...,36} | Menge/Liste/Tupel von Zahlen |")
            print("                     Chance beim einmaligen Spielen (s. Hilfeseite)\n")
            print("  oder   r . P( e, e1 )\n")
            print("Bei der Angabe von zwei Ereignissen wird die bedingte Wahrscheinlich-")
            print("keit P( e | e1 ) berechnet\n")
            print("Beispiele")
            print("r.P( 4 )   r.P( {12} )   r.P( r.colonne1 )")
            print("r.P( { 28, 29, 30 } )    r.P( [28, 29, 30] )")
            print("r.P( 'r.rouge und r.pair' )")
            print("r.P( 12, r.rouge )   (bedingte Wahrscheinlichkeit)\n")
            return

        if len(args) not in (1, 2):
            print('zufall: ein oder zwei Ereignisse angeben')
            return

        def teil(x):
            if isinstance(x, set):
                return all([y in omega for y in x])
            else:
                if x in omega:
                    return True
                return False

        omega = self.omega

        if len(args) == 1:
            e = args[0]
            if e == 0:
                pp = Rational(1, 37)
            elif not e:
                pp = 0
            elif not iterable(e) and e in omega:
                pp = Rational(1, 37)
            elif iterable(e):
                if not all([x in omega for x in e]):
                    print('zufall: Elemente der Ergebnismenge angeben')
                    return
                if not len(e) in (1, 2, 3, 4, 6, 12, 18):
                    print('zufall: 1,2,3,4 oder 6 Zahlen oder benannte Chance angeben')
                    return
                if len(e) == 2:
                    if not set(e) in _a_cheval:
                        print('zufall: keine a_cheval-Chance')
                        return
                elif len(e) == 3:
                    if not set(e) in _transversale_plein:
                        print('zufall: keine transversale_plein-Chance')
                        return
                elif len(e) == 4:
                    if not set(e) in _carre:
                        print('zufall: keine carre-Chance')
                        return
                elif len(e) == 6:
                    if not set(e) in _transversale_simple:
                        print('zufall: keine transversale_simple-Chance')
                        return
                elif len(e) == 12:
                     if not set(e) in [self.colonne1, self.colonne2, self.colonne3, self.douze_premier, \
				           self.douze_milieu, self.douze_dernier]:
                         print('zufall: keine Chance mit 12 Zahlen')
                         return
                elif len(e) == 18:
                     if not set(e) in [self.pair, self.impair, self.rouge, self.noir, self.manque, \
                         self.passe]:
                         print('zufall: keine Chance mit 18 Zahlen')
                         return
                pp = Rational(len(e), 37)
            elif isinstance(e, str):
                uu, oo, nn = e.find('und'), e.find('oder'), e.find('nicht')
                try:
                    if uu >= 0:
                        a, b = e[:uu].strip(), e[uu+3:].strip()
                        ia, ib = a.find('.'), b.find('.')
                        a, b = a[ia+1:], b[ib+1:]
                        a, b = 'self.' + a, 'self.' + b
                        a, b = eval(a), eval(b)
                        if not (teil(a) and teil(b)):
                            print('zufall: Ereignisse bei Roulette angeben')
                            return
                        if isinstance(a, set):
                            if isinstance(b, set):
                                ee = a.intersection(b)
                            else:
                                ee = a.intersection(set([b]))
                        else:
                            if isinstance(b, set):
                                ee = set([a]).intersection(b)
                            else:
                                ee = set([a]).intersection(set([b]))
                        pp = Rational(len(ee), 37)
                    elif oo > 0:
                        a, b = e[:oo].strip(), e[oo+3:].strip()
                        ia, ib = a.find('.'), b.find('.')
                        a, b = a[ia+1:], b[ib+1:]
                        a, b = 'self.' + a, 'self.' + b
                        a, b = eval(a), eval(b)
                        if not (teil(a) and teil(b)):
                            print('zufall: Ereignisse bei Roulette angeben')
                            return
                        if isinstance(a, set):
                            if isinstance(b, set):
                                ee = a.union(b)
                            else:
                                ee = a.union(set([b]))
                        else:
                            if isinstance(b, set):
                                ee = set([a]).union(b)
                            else:
                                ee = set([a]).union(set([b]))
                        pp = Rational(len(ee), 37)
                    elif nn >= 0:
                        a = e[nn+5:].strip()
                        ia = a.find('.')
                        a = a[ia+1:]
                        a = 'self.' + a
                        a = eval(a)
                        if not teil(a):
                            print('zufall: Ereignis bei Roulette angeben')
                            return
                        if isinstance(a, set):
                           ee = omega.difference(a)
                        else:
                            ee = omega.difference(set([a]))
                        pp = Rational(len(ee), 37)
                    else:
                        ee = eval(e)
                        pp = Rational(len(ee), 37)
                except:
                    print('zufall:', 'Ausdruck überprüfen')
                    return
            else:
                print('zufall: Ereignis bei Roulette angeben')
                return

        elif len(args) == 2:
            e1, e2 = args
            if iterable(e1):
                e1 = set(e1)
            if iterable(e2):
                e2 = set(e2)
            try:
                if isinstance(e1, set):
                    if isinstance(e2, set):
                        ee = e1.intersection(e2)
                    else:
                        ee = e1.intersection(set([e2]))
                else:
                    if isinstance(e2, set):
                        ee = set([e1]).intersection(e2)
                    else:
                        ee = set([e1]).intersection(set([e2]))
                pp1, pp2 = self.P(ee), self.P(e2)
                pp = nsimplify(pp1 / pp2, rational=True)
            except:
                print('zufall: die Angaben bitte überprüfen')
                return

        return pp

    def spiel(self, *args, **kwargs):
        """Spiel"""

        if kwargs.get('h'):
            print("\nSpiel     ( Roulette )\n")
            print("Aufruf    r . spiel( chance /[, m ] )\n")
            print("              r        Roulette-Objekt")
            print("              chance   gesetzte Chance - einzelne Zahl oder Menge von ")
            print("                       1-6 Zahlen aus {0,1,...,36} | r.colonne1 |")
            print("                       r.colonne2 | r.colonne3 | r.douze_premier |")
            print("                       r.douze_milieu | r.douze_dernier | r.pair |")
            print("                       r.impair | r.rouge | r.noir | r.manque |")
            print("                       r.passe")
            print("              m        Anzahl Spiele; Standard=1\n")
            print("Zusatz   g=ja   Grafik des Gewinn-Verlaufes")
            print("         m=ja   Grafik des Verlaufes des mittleren Gewinns")
            print("         d=ja   Bei Angabe von m > 1 Rückgabe einer DatenReihe mit dem")
            print("                Gewinn/Verlust je Spiel")
            print("         gd=ja  Gewinnverlauf + DatenReihe")
            print("         md=ja  Mittlerer Gewinn + DatenReihe\n")
            return

        try:
            if len(args) not in (1, 2):
                raise ZufallError("ein oder zwei Argumente angeben")
            chance = args[0]
            if not (iterable(chance) or isinstance(chance, (int, Integer))):
                raise ZufallError("einzelne Zahl oder Menge von Zahlen angeben")
            if isinstance(chance, (int, Integer)):
                if chance not in self.omega:
                    raise ZufallError("Element der Ergebnismenge angeben")
                chance = set([chance])
            else:
                chance = set(chance)
                if not all([x in self.omega for x in chance]):
                    raise ZufallError("Element der Ergebnismenge angeben")
                if len(chance) == 1:
                    if list(chance)[0] not in self.omega:
                        raise ZufallError("keine gültige Chance")
                elif len(chance) == 2:
                    if chance not in _a_cheval:
                        raise ZufallError("keine gültige a_cheval-Chance")
                elif len(chance) == 3:
                   if chance not in _transversale_plein:
                        raise ZufallError("keine gültige transversale_plein-Chance")
                elif len(chance) == 4:
                    if chance not in _carre:
                        raise ZufallError("keine gültige carre-Chance")
                elif len(chance) == 6:
                    if chance not in _transversale_simple:
                        raise ZufallError("keine gültige transversale_simple-Chance")
                elif len(chance) == 12:
                    if chance not in [self.colonne1, self.colonne2, self.colonne3,
                        self.douze_premier, self.douze_milieu, self.douze_dernier]:
                        raise ZufallError("keine gültige Chance")
                elif len(chance) == 18:
                    if chance not in [self.pair, self.impair, self.rouge, self.noir,
                        self.manque, self.passe]:
                        raise ZufallError("keine gültige Chance")
                else:
                    raise ZufallError("keine gültige Chance")

            m = 1
            if len(args) == 2:
                m = args[1]
                if not (isinstance(m, (int, Integer)) and m > 0):
                    raise ZufallError("für m ganze Zahl > 0 angebem")

        except ZufallError as e:
            print('zufall:', str(e))
            return

        def dm(x):
            return display(Math(x))

        vv = GleichVerteilung(37)
        if len(args) == 1 or len(args) == 2 and m == 1:
            erg = vv.versuch - 1   # die Gleichverteilung ist auf {1,2,...,37} definiert
            print(' ')
            dm('\mathrm{Gesetzt\;\, :\;\; }' + latex(chance))
            dm('\mathrm{Ergebnis:\;\; }' + str(erg))
            txt = '\mathrm{Spiel\;gewonnen}' if erg in chance else '\mathrm{Spiel\;verloren}'
            dm(txt)
            print(' ')
            return
        else:
            gewinn = dict([(1, 35), (2, 17), (3, 11), (4, 8), (6, 5), (12, 2), (18, 1)])
            sp = vv.stich_probe(m)
            sp = [x-1 for x in sp]
            dr = [ (gewinn[len(chance)] if x in  chance else -1) for x in sp]

            if kwargs.get('g') or kwargs.get('gd'):
                print(' ')
                dm('\\qquad\mathrm{Verlauf\;des\;Gewinns,\;aktueller\;Gewinn}')
                verlauf_grafik(dr, art='summe', xlabel='Anzahl Spiele')
            elif kwargs.get('m') or kwargs.get('md'):
                pass
                print(' ')
                dm('\\qquad\mathrm{Verlauf\;des\; mittleren\;Spielgewinnes}')
                dm('\\qquad\mathrm{grün-theoretischer\; Erwartungswert\;bei\;einem\;Spiel\; (-1/37)}')
                verlauf_grafik(dr, art='mittel', vergl=float(-1/37), xlabel='Anzahl Spiele')

            if not kwargs:
                L = len(dr)
                G = dr.count(1)
                V = L - G
                p = float((G-V)/L * 100)
                print(' ')
                dm('\mathrm{Gesetzt\;\,\; :  \;\; }' + latex(chance))
                dm('\mathrm{Gewonnen\;(1):  \;\; }' + str(G))
                dm('\mathrm{Verloren\;(-1):\;\; }' + str(V))
                dm('\mathrm{Gesamt\;\\quad\\quad\,:\; }' + str(G-V) + '=' + '{0:.2f}'.format(p) + '\%')
            if kwargs.get('d') or kwargs.get('gd') or kwargs.get('md'):
                dm('\mathrm{Rückgabe\; einer\; DatenReihe\; mit \;dem\;Gewinn\,/\,Verlust\;je\;Spiel}')
                print(' ')
                return DatenReihe(dr)
            print(' ')

    @property
    def hilfe(self):
        """Bezeichner der Eigenschaften und Methoden"""
        roulette_hilfe(3)

    h = hilfe

# Benutzerhilfe für Roulette
# --------------------------

def roulette_hilfe(h):

    if h == 1:
        print("h=2 - Erzeugung")
        print("h=3 - Eigenschaften und Methoden")
        print("h=4 - Ereignisse")
        return

    if h == 2:
        print(""" \

Roulette - Objekt

Erzeugung    Roulette( )

Zuweisung    r = Roulette()   (r - freier Bezeichner)
	   """)
        return

    if h == 3:
        print(""" \

Eigenschaften und Methoden (M) für Roulette

r.hilfe          Bezeichner der Eigenschaften und Methoden
r.brett          Spielbrett (Abbildung)
r.formeln        Berechnungsformeln
r.omega          Ergebnismenge
r.P(...)      M  Wahrscheinlichkeiten beim einmaligen Spielen
r.regeln         Spielregeln
r.spiel(...)  M  Spiel
r.tisch          = s.brett

Synonymer Bezeichner

hilfe   h
""")
        return

    if h == 4:
        print(""" \

Ereignisse (Setzmöglichkeiten, Chancen) beim einmaligen Roulette-
Spiel

(Unbenannte) Ereignisse, die über die Menge der Zahlen angegeben
werden:
                                                                   Anzahl   Gewinn-
Name                Beschreibung                                   Zahlen    quote

plein               einzelne Zahl               z.B. 3 bzw. {3}         1   35 : 1
a_cheval            zwei angrenzende Zahlen     z.B. {13,16}            2   17 : 1
transversale_plein  Querreihe von drei Zahlen   z.B. {28,29,30}         3   11 : 1
carre               vier Zahlen, deren Felder   z.B. {14,15,17,18}      4    8 : 1
                    in einem Punkt zusammensto-
                    ßen bzw. die ersten vier
                    Zahlen
transversale_simple zwei benachbarte Querreihen z.B. {7,8 9,10,11,12}   6    5 : 1

Kolonne und Dutzend:
r.colonne1          die linke Reihe             {1,4,7,..,34}          12    2 : 1
r.colonne2          die mittlere Reihe          {2,5,8,..,35}
r.colonne3          die rechte Reihe            {3,6,9,..,36}
r.douze_premier     das erste Dutzend           {1,2,..,12}
r.douze_milieu      das mittlere Dutzend        {13,14,..,24}
r.douze_dernier     das letzte Dutzend          {25,26,..,36}

Einfache Chancen:
r.pair              die geraden Zahlen außer 0  {2,4,6,..,36}          18    1 : 1
r.impair            die ungeraden Zahlen        {1,3,5,..,35}
r.rouge             die roten Zahlen            {1,3,7,..36}
r.noir              die schwarzen Zahlen        {2,4,6,..35}
r.manque            die erste Hälfte            {1,2,..,18}
r.passe             die zweite Hälfte           {19,20,..,36}
	""")
        return

# 'Unbenannte' Chancen außer plein
# --------------------------------
_a_cheval = [{0, 1}, {0, 2}, {0, 3}, {1, 2}, {2, 3}, {1, 4}, {2, 5}, {4, 5}, {3, 6}, \
                  {5, 6}, {4, 7}, {5, 8}, {6, 9}, {7, 8}, {8, 9}, {7, 10}, {8, 11}, {10, 11}, \
                  {9, 12}, {11, 12}, {10, 13}, {11, 14}, {12, 15}, {13, 14}, {13, 16}, \
                  {14, 15}, {14, 17}, {15, 18}, {16, 17}, {17, 18}, {16, 19}, {17, 20}, \
                  {19, 20}, {18, 21}, {19, 22}, {20, 21}, {20, 23}, {22, 23}, {21, 24}, \
                  {22, 25}, {23, 24}, {23, 26}, {25, 26}, {24, 27}, {25, 28}, {26, 27}, \
                  {26, 29}, {28, 29}, {27, 30}, {28, 31}, {29, 30}, {29, 32}, {30, 33}, \
                  {31, 32}, {32, 33}, {31, 34}, {32, 35}, {33, 36}, {34, 35}, {35, 36}]
_transversale_plein = [{1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {10, 11, 12}, {13, 14, 15}, \
                  {16, 17, 18}, {19, 20, 21}, {22, 23, 24}, {25, 26, 27}, {28, 29, 30}, \
                  {31, 32, 33}, {34, 35, 36}]
_carre = [{1, 2, 3, 4}, {1, 2, 4, 5}, {2, 3, 5, 6}, {4, 5, 7, 8}, {5, 6, 8, 9}, \
                  {7, 8, 10, 11}, {8, 9, 11, 12}, {10, 11, 13, 14}, {11, 12, 14, 15}, \
                  {13, 14, 16, 17}, {14, 15, 17, 18}, {16, 17, 19, 20}, {17, 18, 20, 21},
                  {19, 20, 22, 23}, {20, 21, 23, 24}, {22, 23, 25, 26}, {23, 24, 26, 27}, \
                  {25, 26, 28, 29}, {26, 27, 29, 30}, {28, 29, 31, 32}, {29, 30, 32, 33}, \
                  {31, 32, 34, 35}, {32, 33, 35, 36}]
_transversale_simple = [{1, 2, 3, 4, 5, 6}, {4, 5, 6, 7, 8, 9}, {7, 8, 9, 10, 11, 12}, \
                  {10, 11, 12, 13, 14, 15}, {13, 14, 15, 16, 17, 18}, {16, 17, 18, 19, 20, 21}, \
                  {19, 20, 21, 22, 23, 24}, {22, 23, 24, 25, 26, 27}, {25, 26, 27, 28, 29, 30}, \
                  {28, 29, 30, 31, 32, 33}, {31, 32, 33, 34, 35, 36}]


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

# In[5]:

import os
from sympy import *
import pandas as pd
import numpy as np
import scipy.fftpack
import matplotlib as mpl
import matplotlib.pyplot as plt
plt.style.use("seaborn-paper")

def find_nearest(array, value):
    array = np.asarray(array)
    idx = (np.abs(array - value)).argmin()
    return idx

# ## Canvas palette

# In[6]:

#Canvas for single plot
x = np.linspace(0,10,100)
y = np.sin(x)
plt.figure(figsize=[14,6])
plt.grid(True)
plt.title("Change-me!",fontsize=20)
plt.plot(x,y,label="testvalue")
plt.legend(fontsize=16)
plt.xlabel("XLABEL (unit)",fontsize=18)
plt.ylabel("YLABEL (unit)",fontsize=18)
plt.show()

# In[7]:

#Canvas for side by side
fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(14,6))
fig.suptitle("test",y=1.05,fontsize=20)

axes[0].grid(True)
axes[0].plot(x,y,label="testvalue")
axes[0].legend(fontsize=16)
axes[0].set_title("TESTTITLE",fontsize=18)
axes[0].set_xlabel("XLABEL (unit)",fontsize=18)
axes[0].set_ylabel("YLABEL (unit)",fontsize=18)
axes[0].legend(fontsize=16)
axes[0].tick_params(axis='both', which='major', labelsize=15)

axes[1].grid(True)
axes[1].plot(x,y,label="testvalue")
axes[1].legend(fontsize=16)
axes[1].set_title("TESTTITLE",fontsize=18)
axes[1].set_xlabel("XLABEL (unit)",fontsize=18)
axes[1].set_ylabel("YLABEL (unit)",fontsize=18)
axes[1].legend(fontsize=16)
axes[1].tick_params(axis='both', which='major', labelsize=15)

fig.tight_layout()
plt.show()

# In[8]:

#Canvas for side by side
fig, axes = plt.subplots(nrows=2, ncols=4, figsize=(14,6))
fig.suptitle("test",y=1.05,fontsize=20)

axes[0,0].grid(True)
axes[0,0].plot(x,y,label="testvalue")
axes[0,0].legend(fontsize=16)
axes[0,0].set_title("TESTTITLE",fontsize=18)
axes[0,0].set_xlabel("XLABEL (unit)",fontsize=18)
axes[0,0].set_ylabel("YLABEL (unit)",fontsize=18)
axes[0,0].legend(fontsize=16)
axes[0,0].tick_params(axis='both', which='major', labelsize=15)

axes[0,1].grid(True)
axes[0,1].plot(x,y,label="testvalue")
axes[0,1].legend(fontsize=16)
axes[0,1].set_title("TESTTITLE",fontsize=18)
axes[0,1].set_xlabel("XLABEL (unit)",fontsize=18)
axes[0,1].set_ylabel("YLABEL (unit)",fontsize=18)
axes[0,1].legend(fontsize=16)
axes[0,1].tick_params(axis='both', which='major', labelsize=15)

fig.tight_layout()
plt.show()

# ## Read data

# In[11]:

#Folder and paths definitions
main_path  = os.getcwd()
datafolder_path = main_path+"/results"
results_dir = "/output_py"
output_dir = main_path+results_dir
try:
    os.mkdir(output_dir)
except OSError:
    print ("Creation of the directory %s failed" % results_dir)
else:
    print ("Successfully created the directory %s " % results_dir)

# In[ ]:

# In[81]:

#Simulation parameters
N = 1000
T = 10000
n_runs = 10
dt = .01
freq = "ufreq"
fixed_plot = False #--> fixed phase and variable freqs, False viceversa
gamma = .5
MF = "MF"

if(freq =="gfreq"):
    freq_plot="$\\vec{\\omega_{0}} = \\mathcal{N}(0,1)$"
else:
    freq_plot="$\\vec{\\omega_{0}} = U(-%.1f,%.1f)$"%(gamma,gamma)
if(MF =="MF"):
    MF_plot="MeanField"
else:
    MF_plot="non-meanField"

if(fixed_plot==True):
    fixed_plot = "FixedPhase"
else:
    fixed_plot = "FixedFreq"

# In[82]:

#OutputFileNames
#S/N --> |r(t)|/sigma(r(t))
sn_name = "S_N"
#(Mod&Phase)(t)
modphase_name = "ModPhase_t"
#(Mod)(t)
mod_name = "Mod_t"
#Spectrum
spectrum_name = "Spectrum"
#r_inf
rinf_name = "r_inf"
#Configuration-specific name
if(freq!="gfreq"):
    config_name= "/N%d_nruns%d_freq=%s_gamma=%.2f"%(N,n_runs,freq,gamma)
else:
    config_name= "/N%d_nruns%d_freq=%s_"%(N,n_runs,freq)

# In[ ]:

# In[ ]:

# In[84]:

#Create dataframe dictionary. For each entry, first value is the K of the dataframe (second value)
data = []
for i in range(0,n_runs):
    filename = datafolder_path + "/PART2_ufreq_uphase_N1000_NOMF_T10000_dt0.0100_nruns10_K1.000_NO_Fphase_Ffreq_RUN%d.tsv"%(i)
    #cols refers to timestep, mod, phase, (of order parameter)
    df = pd.read_csv(filename,sep="\t",header=None)
    data.append(["n_run=%d"%(i),df])

# In[ ]:

# ## Plots

# In[ ]:

# In[85]:

#Plot settings
alph = 1
tmax = 100

fig, axes = plt.subplots(nrows=1, ncols=1, figsize=(14,6))

fig.suptitle("N = %d, dt = %.3f, %s, K=1"%(N,dt,freq_plot),y=.95,fontsize=20)

plt.grid(True)

#for i in range(0,2):
for i in range(0,n_runs):
    plt.plot(data[i][1][0],data[i][1][1],ls='--',linewidth=.8,markersize=.05,label="n_run=%d"%(i+1),alpha=alph)

plt.title("Re[r(t)], fixed $\\vec{\\omega}(0)$",fontsize=18)
plt.xlabel("t",fontsize=18)
plt.ylabel("",fontsize=18)
plt.legend(fontsize=16,ncol=2)
plt.xlim(0,tmax)
plt.tick_params(axis='both', which='major', labelsize=15)

fig.tight_layout()
plt.xlim(0,20)
plt.subplots_adjust(top=.825)
plt.savefig(output_dir+config_name+mod_name+fixed_plot+"real.png")
plt.show()

# In[86]:

#Plot settings
alph = 1
tmax = 100

fig, axes = plt.subplots(nrows=1, ncols=1, figsize=(14,6))

fig.suptitle("N = %d, dt = %.3f, %s, K=1"%(N,dt,freq_plot),y=.95,fontsize=20)

plt.grid(True)

#for i in range(0,2):
for i in range(0,n_runs):
    plt.plot(data[i][1][0],data[i][1][2],ls='--',linewidth=.8,markersize=.05,label="n_run=%d"%(i+1),alpha=alph)

plt.title("Im[r(t)], fixed $\\vec{\\omega}(0)$",fontsize=18)
plt.xlabel("t",fontsize=18)
plt.ylabel("",fontsize=18)
plt.legend(fontsize=16,ncol=2)
plt.xlim(0,tmax)
plt.tick_params(axis='both', which='major', labelsize=15)

fig.tight_layout()
plt.xlim(0,20)
plt.subplots_adjust(top=.825)
plt.savefig(output_dir+config_name+mod_name+fixed_plot+"imag.png")
plt.show()

# In[70]:

fig, axes = plt.subplots(nrows=2, ncols=5, figsize=(14,6))
fig.suptitle("$\\mathcal{F} (r(t))$\nN = %d, dt = %.3f, %s, n_runs = %d"%(N,dt,freq_plot, n_runs),y=1,fontsize=20)
counter1 = 0
counter2 = 0
#for i in range(0,2):
for i in range(0,n_runs):
    if(counter1<n_runs/2):
        axes[0,counter1].grid(True)
        im = axes[0,counter1].specgram(data[i][1][1],Fs=1/dt)
        axes[0,counter1].set_title("K = %s"%(data[i][0]),fontsize=18)
        axes[0,counter1].set_xlabel("t",fontsize=18)
        axes[0,counter1].set_ylabel("Freq.",fontsize=18)
        axes[0,counter1].tick_params(axis='both', which='major', labelsize=15)
        counter1 = counter1+1
    else:
        axes[1,counter2].grid(True)
        im = axes[1,counter2].specgram(data[i][1][1],Fs=1/dt)
        axes[1,counter2].set_title("K = %s"%(data[i][0]),fontsize=18)
        axes[1,counter2].set_xlabel("t",fontsize=18)
        axes[1,counter2].set_ylabel("Freq.",fontsize=18)
        axes[1,counter2].tick_params(axis='both', which='major', labelsize=15)
        counter2 = counter2+1

fig.tight_layout()
plt.subplots_adjust(top=.825)

plt.savefig(output_dir+config_name+spectrum_name+".png")

plt.show()

# In[58]:

#for r_inf evaluation
Kval_list = []
r_inf = []
r_inf_err = []
last_percent = .9
for i in range(0,len(Kvalues)):
    r_inf.append([np.mean(data[i][1][1][int(len(data[i][1][1])*last_percent):])])
    r_inf_err.append([np.std(data[i][1][1][int(len(data[i][1][1])*last_percent):])])
    Kval_list.append([Kvalues[i]])

# # ADD ERRORBARS

# In[59]:

fig = plt.figure(figsize=[14,6])
plt.grid(True)
plt.title("N = %d, dt = %.3f, %s, n_runs = %d"%(N,dt,freq_plot, n_runs),y=1,fontsize=20)
#plt.errorbar(Kval_list,r_inf,y_err=r_inf_err,label="testvalue")
plt.errorbar(Kval_list,r_inf,ls='--',linewidth=.5,fmt='.',markersize=5, elinewidth=.5, capthick=.5,label="Average on last %d steps"%((1-last_percent)*T+1))
plt.legend(fontsize=16)
plt.xlabel("K",fontsize=18)
plt.ylabel("$r_{\\infty}$",fontsize=18,rotation=0)
fig.tight_layout()
plt.savefig(output_dir+config_name+rinf_name+".png")
plt.show()

# In[ ]:

# In[ ]:


# -*- coding: utf-8 -*-
"""Swapnil Tamrakar Lab 7: Feature Engineering - Creating Synthetic Features.ipynb

Automatically generated by Colaboratory.

Original file is located at
    https://colab.research.google.com/drive/1Pd5yAJ6CxvLGYIN-2RInnOEBKaBdOvxH

#### Copyright 2017 Google LLC.
"""

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""# Lab 7: Feature Engineering - Creating Synthetic Features
**Learning Objectives:**
  * Gain more experience with the `LinearRegressor` class in TensorFlow by using it to predict median housing price, at the granularity of city blocks
  * Use a validation data set and test set to make sure that our model will generalize and is not overfitting the training data.
  * Use test data only after tuning hyperparameters as a measure of how the model will generalize to new data
  * Create synthetic features from the existing features (e.g., taking a ratio of two other features)
  * More practice with feature transformations including identifying and clipping (removing) outliers out of the input data to obtain the best model

### Standard Set-up

We begin with the same set-up as in the last lab.
"""

import math

from IPython import display
from matplotlib import cm
from matplotlib import gridspec
from matplotlib import pyplot as plt
import numpy as np
import pandas as pd
from mpl_toolkits.mplot3d import Axes3D
from sklearn import metrics
import tensorflow as tf
from tensorflow.contrib.learn.python.learn import learn_io, estimator

# This line increases the amount of logging when there is an error. You can
# remove it if you want less logging.
tf.logging.set_verbosity(tf.logging.ERROR)

# Set the output display to have two digits for decimal places, for display
# readability only and limit it to printing 15 rows.
pd.options.display.float_format = '{:.2f}'.format
pd.options.display.max_rows = 15

"""Read the data and randomize the order."""

california_housing_dataframe = pd.read_csv("https://storage.googleapis.com/ml_universities/california_housing_train.csv", sep=",")

california_housing_dataframe = california_housing_dataframe.reindex(
    np.random.permutation(california_housing_dataframe.index))

california_housing_dataframe.describe()

"""##Prepare Features

As our learning models get more sophisticated we will want to do some computation on the features and even generate new features from the existing features. You will change this later in the lab. For now this method will just make a copy of the portion of the dataframe we plan to use, and re-scale the median-house value (to make it a bit easier to work with).
"""

def prepare_features(dataframe):
  """Prepares the features for provided dataset.

  Args:
    dataframe: A Pandas DataFrame expected to contain data from the
      desired data set.
  Returns:
    A new dataFrame that contains the features to be used for the model.
  """
  processed_features = dataframe.copy()

  # Modifying median_house_value to be in scale of $1000.  So a value of 14.0
  # will correspond to $14,000.  This will make it a bit easier to work with.
  processed_features["median_house_value"] /= 1000.0

  return processed_features

"""###Divide the provided data for training our model into a training and validation set

As in the last lab we use the first 14000 examples (after randomization) for the ***training set*** and the remaining 3000 examples for the ***validation set***.
"""

training_examples = prepare_features(california_housing_dataframe.head(14000))
validation_examples = prepare_features(california_housing_dataframe.tail(3000))

"""### Load the Test Data

As in the last lab we load the test data from [here](https://storage.googleapis.com/ml_universities/california_housing_test.csv).
"""

california_housing_test_data = pd.read_csv(
    "https://storage.googleapis.com/ml_universities/california_housing_test.csv",
    sep=",")

test_examples = prepare_features(california_housing_test_data)

"""### Compute Loss

Here is a simple method to compute the loss on the given input function and targets.
"""

def compute_loss(model, input_fn, targets):
  """ Computes the loss (RMSE) for linear regression.

  Args:
    model: the trained model to use for making the predictions.
    input_fn: the input_fn to use to make the predictions.
    targets: a list of the target values being predicted that must be the
             same length as predictions.

  Returns:
    The RMSE for the provided predictions and targets.
  """
  predictions = list(model.predict(input_fn=input_fn))
  return math.sqrt(metrics.mean_squared_error(predictions, targets))

"""### Setting Up the Feature Columns and Input Function for TensorFlow
As in the last lab we define `input_fn` to create a real-valued feature for each provided
numerical column, and then define `train_input_fn` to use the training data, `eval_input_fn` to use the validation data, and `test_input_fn` to use the test data.
"""

CATEGORICAL_COLUMNS = []
NUMERICAL_COLUMNS = ["latitude", "longitude", "housing_median_age",
                     "total_rooms", "total_bedrooms", "population",
                     "households", "median_income"]
def input_fn(dataframe):
  """Constructs a dictionary for the feature columns.

  Args:
    dataframe: The Pandas DataFrame to use for the input.
  Returns:
    The feature columns and the associated labels for the provided input.
  """
  # Creates a dictionary mapping each numeric feature column name (k) to
  # the values of that column stored in a constant Tensor.
  numerical_cols = {k: tf.constant(dataframe[k].values)
                     for k in NUMERICAL_COLUMNS}
  # Creates a dictionary mapping each categorical feature column name (k)
  # to the values of that column stored in a tf.SparseTensor.
  categorical_cols = {k: tf.SparseTensor(
      indices=[[i, 0] for i in range(dataframe[k].size)],
      values=dataframe[k].values,
      dense_shape=[dataframe[k].size, 1])
                      for k in CATEGORICAL_COLUMNS}
  # Merges the two dictionaries into one.
  feature_cols = dict(numerical_cols.items() + categorical_cols.items())
  # Converts the label column into a constant Tensor.
  label = tf.constant(dataframe[LABEL].values)
  # Returns the feature columns and the label.
  return feature_cols, label

def train_input_fn():
  return input_fn(training_examples)

def eval_input_fn():
  return input_fn(validation_examples)

def test_input_fn():
  return input_fn(test_examples)

"""### Functions to help visualize our results

We will use our functions from the last lab to generate a calibration plot and learning curve (with both training and validation losses).
"""

def make_calibration_plot(predictions, targets):
  """ Creates a calibration plot.

  Args:
    predictions: a list of values predicted by the model being visualized
    targets: a list of the target values being predicted that must be the
             same length as predictions.
  """
  calibration_data = pd.DataFrame()
  calibration_data["predictions"] = pd.Series(predictions)
  calibration_data["targets"] = pd.Series(targets)
  calibration_data.describe()
  min_val = calibration_data["predictions"].min()
  max_val = calibration_data["predictions"].max()
  plt.ylabel("target")
  plt.xlabel("prediction")
  plt.scatter(predictions, targets, color='black')
  plt.plot([min_val, max_val], [min_val, max_val])

def plot_learning_curve(training_losses, validation_losses):
  """ Plot the learning curve.

  Args:
    training_loses: a list of training losses to plot.
    validation_losses: a list of validation losses to plot.
  """
  plt.ylabel('Loss')
  plt.xlabel('Training Steps')
  plt.plot(training_losses, label="training")
  plt.plot(validation_losses, label="validation")
  plt.legend(loc=1)

"""### Defining the features, linear regression model, and function to train the model

These functions are just like the last lab except now we include all of the available numerical features.
"""

NUMERICAL_FEATURES = ["latitude", "longitude", "housing_median_age",
                     "total_rooms", "total_bedrooms", "population",
                     "households"]
LABEL = "median_income"

def construct_feature_columns():
  """Construct TensorFlow Feature Columns for features.

  Returns:
    A set of feature columns.
  """
  feature_set = set([tf.contrib.layers.real_valued_column(feature)
                     for feature in NUMERICAL_FEATURES])
  return feature_set

def define_linear_regression_model(learning_rate):
  """ Defines a linear regression model of one feature to predict the target.

  Args:
    learning_rate: A `float`, the learning rate.

  Returns:
    A linear regressor created with the given parameters.
  """
  linear_regressor = tf.contrib.learn.LinearRegressor(
    feature_columns=construct_feature_columns(),
    optimizer=tf.train.GradientDescentOptimizer(learning_rate=learning_rate),
    gradient_clip_norm=5.0
  )
  return linear_regressor

def train_model(linear_regressor, steps):
  """Trains a linear regression model.

  Args:
    linear_regressor: The regressor to train.
    steps: A non-zero `int`, the total number of training steps.

  Returns:
    The trained regressor.
  """
  # In order to see how the model evolves as we train it, we divide the
  # steps into periods and show the model after each period.
  periods = 10
  steps_per_period = steps / periods

  # Train the model, but do so inside a loop so that we can periodically assess
  # loss metrics.  We store the training and validation losses so we can
  # generate a learning curve.
  print "Training model..."
  training_losses = []
  validation_losses = []

  for period in range (0, periods):
    # Call fit to train the regressor for steps_per_period steps.
    linear_regressor.fit(input_fn=train_input_fn, steps=steps_per_period)

    # Compute the loss between the predictions and the correct labels, append
    # the training and validation loss to the list of losses used to generate
    # the learning curve after training is complete and print the current
    # training loss.
    training_loss = compute_loss(linear_regressor, train_input_fn,
                                 training_examples[LABEL])
    validation_loss = compute_loss(linear_regressor, eval_input_fn,
                                   validation_examples[LABEL])
    training_losses.append(training_loss)
    validation_losses.append(validation_loss)
    print "  Training loss after period %02d : %0.3f" % (period, training_loss)

  # Now that training is done print the final training and validation losses.
  print "Final Training Loss (RMSE): %0.3f" % training_loss
  print "Final Validation Loss (RMSE): %0.3f" % validation_loss

  # Generate a figure with the learning curve on the left and a
  # calibration plot on the right.
  plt.figure(figsize=(10, 5))
  plt.subplot(1, 2, 1)
  plt.title("Learning Curve (RMSE vs time)")
  plot_learning_curve(training_losses, validation_losses)

  plt.subplot(1, 2, 2)
  plt.tight_layout(pad=1.1, w_pad=3.0, h_pad=3.0)
  plt.title("Calibration Plot on Validation Data")
  validation_predictions = np.array(list(linear_regressor.predict(
      input_fn=eval_input_fn)))
  make_calibration_plot(validation_predictions, validation_examples[LABEL])

  return linear_regressor

"""### Training a model with one feature.

In the last lab, you trained a model to predict the `median_house_value` from a single feature. Before we explore what can be done by introducing additional features, let's just train a good model to use a single feature.  Feel free to change the below, but as is it should give a pretty good result (given the constraint of using a single feature).
"""

NUMERICAL_FEATURES = ["median_income"]
LABEL = "median_house_value"

LEARNING_RATE = 0.1
STEPS = 250

linear_regressor = define_linear_regression_model(learning_rate = LEARNING_RATE)
linear_regressor = train_model(linear_regressor, steps=STEPS)

"""Once you've adjusted the hyperparameters (if you'd like to see if you can reduce the validation loss), let's check the loss on the test data."""

print "loss on test data is", compute_loss(
    linear_regressor, test_input_fn, test_examples[LABEL])

"""## Task 1: Introduce Synthetic Features (1 Point)
Both the total_rooms and population features count totals for a given city block.
But what if one city block were more densely populated than another? Then just using `total_rooms` and `population` directly might not be very useful.  Instead what we really want is the **quadratic feature** obtain by dividing `total_rooms` by the `population` to give the average number of rooms per person for that city block.

We've got you started by creating a feature called rooms_per_person.  Create others, and use them along with whatever other features you think are useful to train a model.
"""

def prepare_features(dataframe):
  """Prepares the features for provided dataset.

  Args:
    dataframe: A Pandas DataFrame expected to contain data from the
      desired data set.
  Returns:
    A new DataFrame that contains the features to be used for the model.
  """
  processed_features = dataframe.copy()

  # Modifying median_house_value to be in scale of $1000.  So a value of 14.0
  # will correspond to $14,000.  This will make it a bit easier to work with.
  processed_features["median_house_value"] /= 1000.0

  # Add your synthetic features here. We've got you started by defining
  # rooms_per_person
  processed_features["rooms_per_person"] = (
    dataframe["total_rooms"] / dataframe["population"])
  processed_features["bedrooms_per_person"] = (dataframe["total_bedrooms"] / dataframe["population"])
  processed_features["rooms_per_household"] = (dataframe["total_rooms"] / dataframe["households"])
  processed_features["bedrooms_per_household"] = (dataframe["total_bedrooms"] / dataframe["households"])

  return processed_features

# Generate the training, validation and test examples
training_examples = prepare_features(california_housing_dataframe.head(14000))
validation_examples = prepare_features(california_housing_dataframe.tail(3000))
test_examples = prepare_features(california_housing_test_data)

validation_examples.describe()

"""Since we added new features, we need to add them to `NUMERICAL_COLUMNS` so that they are included in the dictionary created by `input_fn`."""

# Add any other synthetic features you create to this list.
NUMERICAL_COLUMNS = ["latitude", "longitude", "housing_median_age",
                     "total_rooms", "total_bedrooms", "population",
                     "households", "median_income", "rooms_per_person", "bedrooms_per_person", "rooms_per_household", "bedrooms_per_household"]

"""##Task 2 -- Add a Clip Feature Transformation (1 point)

Recall that there are two characteristics we'd like of numerical features when used together to train a linear model:
* The range of the features is roughly the same.
* To the extent possible the histogram of the features kind of resembles a bell curve.  Sometimes the data will fit this very well and other times it won't.

Below are the methods to perform linear scaling and log scaling.  For this data set it will also be useful to have a feature transformation to cap the features to within a minimum and/or maximum value.  Most likely you will want to then linearly scale or log scale the feature after clipping it.
"""

# Linearly rescales to the range [0, 1]
def linear_scale(series):
  min_val = series.min()
  max_val = series.max()
  scale = 1.0 * (max_val - min_val)
  return series.apply(lambda x:((x - min_val) / scale))

# Perform log scaling
def log_scale(series):
  return series.apply(lambda x:math.log(x+1.0))

# Clip all features to given min and max
def clip(series, clip_to_min, clip_to_max):
  # You need to modify this to actually do the clipping versus just returning
  # the series unchanged.
  return series.apply(lambda x: np.clip(x, clip_to_min, clip_to_max))

"""You can use this function to draw a histogram to help decide what kind of scaling is best to use for `households` and also to confirm your implementation of `clip` works as you intended."""

clip_min = -np.inf
clip_max = np.inf

def draw_histograms(dataframe, feature_name,
                    clip_min = -np.inf, clip_max = np.inf):
  plt.figure(figsize=(20, 4))
  plt.subplot(1, 3, 1)
  plt.title(feature_name)
  histogram = dataframe[feature_name].hist(bins=50)

  plt.subplot(1, 3, 2)
  plt.title("linear_scaling")
  scaled_features = pd.DataFrame()
  scaled_features[feature_name] = linear_scale(
      clip(dataframe[feature_name], clip_min, clip_max))
  histogram = scaled_features[feature_name].hist(bins=50)

  plt.subplot(1, 3, 3)
  plt.title("log scaling")
  log_normalized_features = pd.DataFrame()
  log_normalized_features[feature_name] = log_scale(dataframe[feature_name])
  histogram = log_normalized_features[feature_name].hist(bins=50)

draw_histograms(training_examples, 'households')
draw_histograms(training_examples, 'median_income')
draw_histograms(training_examples, 'rooms_per_person')
draw_histograms(training_examples, 'bedrooms_per_person')
draw_histograms(training_examples, 'rooms_per_household')
draw_histograms(training_examples, 'bedrooms_per_household')

print "Now let's clip between 0 and 1000 before scaling."
draw_histograms(training_examples, 'households', 0, 3000)
draw_histograms(training_examples, 'median_income', 0, 15)
draw_histograms(training_examples, 'rooms_per_person', 0, 10)
draw_histograms(training_examples, 'bedrooms_per_person', 0, 2)
draw_histograms(training_examples, 'rooms_per_household', 0, 10)
draw_histograms(training_examples, "bedrooms_per_household", 0, 5)

"""The leftmost histogram is unchanged since that just shows the raw feature.  You should see a very visible change when you clip the feature before applying linear scaling. As you will often (but not always) find, for this particular feature log scaling worked well without the need to first clip the data.

**Run the above code with features other than `households` to help decide what feature normalization to use for each feature.**  Feel free to duplicate the code box that calls `draw_histogram` if you'd like to show the histograms for multiple features.

## Task 3: Train the Best Model You Can (3 points)

We expect you to take some time exploring the feature transformations and hyperparameters.

Select any number of the provided features, create synthetic features you think will be informative, and modify the hyperparmaters to get a better model. You will want to edit preprocess_features to do some feature normalization like you saw in the [Using Multiple Numerical Features and Feature Scaling](https://colab.research.google.com/notebook#fileId=/v2/external/notebooks/intro_to_ml_semester_course/Lab_3__Using_Multiple_Numerical_Features_and_Feature_Scaling.ipynb). See how well you are able to do.

**DO NOT APPLY ANY ADDITIONAL FEATURE TRANSFORMATION TO THE TARGET `median_price` since that would change the scale for RMSE.**

* Summarize the changes you made that were the most important.

* Once you find a model, try training 20 times more steps.  Does overfitting occur if you do that?
"""

def prepare_features(dataframe):
  """Prepares the features for provided dataset.

  Args:
    dataframe: A Pandas DataFrame expected to contain data from the
      desired data set.
  Returns:
    A new dataFrame that contains the features to be used for the model.
  """
  processed_features = dataframe.copy()

  # Modifying median_house_value to be in scale of $1000.  So a value of 14.0
  # will correspond to $14,000.  This will make it a bit easier to work with.
  processed_features["median_house_value"] /= 1000.0

  # Perform your feature scaling here
  processed_features["households"] = linear_scale(dataframe["households"])
  processed_features["median_income"] = linear_scale(dataframe["median_income"])

  # Add your synthetic features here along with the feature scaling you'd like
  # to them. As a starting point linear scaling is used for rooms_per_person.
  # You are encouraged to experiment with different scaling options.
  processed_features["rooms_per_person"] = linear_scale(dataframe["total_rooms"] / dataframe["population"])
  processed_features["bedrooms_per_person"] = linear_scale(dataframe["total_bedrooms"] / dataframe["population"])
  processed_features["rooms_per_household"] = linear_scale(dataframe["total_rooms"] / dataframe["households"])
  processed_features["bedrooms_per_household"] = linear_scale(dataframe["total_bedrooms"] / dataframe["households"])

  return processed_features

# Generate the training, validation and test examples
california_housing_dataframe = california_housing_dataframe.sample(frac=1).reset_index(drop=True)
training_examples = prepare_features(california_housing_dataframe.head(14000))
validation_examples = prepare_features(california_housing_dataframe.tail(3000))
test_examples = prepare_features(california_housing_test_data)

training_examples.describe()

"""Here's the method to train the model.  You'l need to fill in the features you want to use."""

# Fill in the features you want to use
NUMERICAL_FEATURES = ["rooms_per_person", "bedrooms_per_person", "housing_median_age", "median_income","rooms_per_household", "bedrooms_per_household"]
LABEL = "median_house_value"

LEARNING_RATE = 0.025
STEPS = 100

linear_regressor = define_linear_regression_model(learning_rate = LEARNING_RATE)
linear_regressor = train_model(linear_regressor, steps=STEPS)

"""You can look at the weights of the trained model."""

# Let's also look at the weights and bias
for feature in NUMERICAL_FEATURES:
  print "weight for", feature, ":", linear_regressor.get_variable_value(
    "linear/" + feature + "/weight")[0]
print "bias:",  linear_regressor.get_variable_value("linear/bias_weight")

"""Check the loss on the test data after you are done selecting the learning rate and number of steps to run."""

print "loss on test data is", compute_loss(
    linear_regressor, test_input_fn, test_examples[LABEL])

"""Here's a code box for you to train your model with the same learning rate that worked best but to see what happens if you train it 20 times longer."""

# Fill in the features you want to use
NUMERICAL_FEATURES = ["rooms_per_person", "bedrooms_per_person", "housing_median_age", "median_income","rooms_per_household", "bedrooms_per_household"]
LABEL = "median_house_value"

LEARNING_RATE = 0.025
STEPS = 2000

linear_regressor = define_linear_regression_model(learning_rate = LEARNING_RATE)
linear_regressor = train_model(linear_regressor, steps=STEPS)

"""
Here's a place for you to answer these questions:

A) Summarize the changes you made that were the most important.

    Answer:

    *Selected the new features because usually when a person looks to buy a house, they look at the room available for a person so I added synthetic features rooms_per_person, bedroom_per_person.**
    **I also added rooms_per_household as it is important and used linear scaling for all of them as the histograms clearly show that linear scaling provided better data estimation**

B) Once you find a model, try training 20 times more steps.  Does overfitting
   occur if you do that?

  Answer:

   Yes overfitting occurs if you try training 20 times more steps. This can be seen from the differece in the training and validation losses between the first and the second graphs.
   The difference is greater in the second graph.
"""


########################################################################3456789
########################################################################72
from examsage import Macros # Import the parent class for Parameters
import numpy as np

class Parameters(Macros):
    def __init__(self):
        ## General note about variable names in Python
        # - `self` refers to this problem object
        # - `self.variable_name = value` is a variable that is attached to
        # this problem and will keep its value as long as the problem object
        # exists in memory. These are called object attributes.
        # - `variable_name = value` is a temporary variable useful for local
        # calculations.
        # - `def method_name(self, arguments)` is a function that is
        # attached to this object. These are called object methods. Methods
        # are usually named without double underscores. Methods named with
        # double underscores play a special role in Python.

        ## Note about variable names in ExamSage
        # - All TeX parameters **must** use the `self.` prefix
        # - Problem objects **must** define the methods __init__ and __next__.
        # - Helper methods should be named **without** double underscores

        ## Required attributes
        # List the variables names that must be converted into tex macros
        # as strings without the `self.` prefix
        # EXAMPLE:
        #     self.par_names = ['constant_par_name', 'varying_par_name']
        self.par_names = []

        # Set the minimum height for this problem.
        # Minimum heights should be strings with units supported by LaTeX
        #     https://www.overleaf.com/learn/latex/Lengths_in_LaTeX
        self.min_height = '2in'

        ## OPTIONAL: Generate constant parameters
        # In order to generate problems of similar difficulty, it is
        # sometimes helpful to have parameters that are constant
        # between versions. Parameters that are generated inside of
        # this initialization method, will be generated when the problem
        # object is created
        #    `problem = examsage.Problem()`
        # but will not be affected by
        #    `next(problem)`
        # EXAMPLE:
        #     self.constant_par_name = parameter_value
        pass

        ## Don't edit below this line
        # Generate varying parameters
        next(self)

    def __next__(self):
        ## REQUIRED: Generate varying parameters
        # Set the values of the TeX parameters
        # EXAMPLE:
        #     self.varying_par_name = parameter_value

        ## Don't edit below this line
        return self


# -*- coding: utf-8 -*-
# ---
# jupyter:
#   jupytext:
#     cell_metadata_filter: -all
#     formats: py:light,notebooks//ipynb
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.6.0
#   kernelspec:
#     display_name: Python 3
#     language: python
#     name: python3
# ---

# # Jupyter
#
# ## Installation
#
# Pour construire note site, nous avez besoin d'une distribution Python. [Miniconda](https://conda.io/miniconda.html) est une solution légère permettant d'installer tous les packages nécessaires.
#
# Une fois installer miniconda en ayant suivi les instructions, ouvrez un terminal sur Linux/Macos ou un anaconda prompt sur Windows.
#

# ### Environnement conda
#
# ```bash
# git clone https://github.com/M2MAS-AGROCAMPUS/git-markdown-docker
# # cd git-markdown-docker
# conda env create -n agrocampus
# ```
#
# [documentation](https://conda.io/docs/using/envs.html).
#
# L'environnement est créé a partir d'un fichier nommé `environment.yml` qui contient la liste des packages dont nous avons besoin.

# ### Activer l'environnement conda
#
# Lorsque vous activer l'environement conda, votre configuration du terminal est modifiée et la version
# de python qui sera disponible sera celle ou tous nos packages seront installés.
# <pre>
# $ conda activate agrocampus
# (agrocampus) $ python
# Python 3.6.2 (default, Jul 17 2017, 16:44:45)
# [GCC 4.2.1 Compatible Apple LLVM 8.1.0 (clang-802.0.42)] on darwin
# Type "help", "copyright", "credits" or "license" for more information.
# >>> quit()
# </pre>
#

# ## Jupyter Notebook
#
# - Le calepin Jupyter est un outil de rédaction qui permet de partager une analyse mathématique.
# - Rassembler du code informatique, du texte, des images et des formules mathématiques dans un seul document.
# - Le code informatique est modifiable et exécutable.
# - Excellent support pour travailler et surtout pour partager.
# - Jupyter comporte de nombreuses extensions et supporte [un nombre important de langages](https://github.com/jupyter/jupyter/wiki/Jupyter-kernels).
# - Logiciel libre, ouvert et totalement gratuit qui fonctionne sur tous les systèmes d'exploitation existants.
#
# Jupyter est un acronyme des 3 langages supportés à l'origine du projet: **JU**lia, **PYT**hon, et **R**

# ```bash
# conda install -c conda-forge jupyter
# ```

# ## Raccoucis clavier
#
# - Pour afficher les commandes disponibles: `Cmd + Shift + P`
# - Exécuter une cellule
#     - 'Cmd-Enter' exécute la cellule courante
#     - 'Shift-Enter' exécute la cellule courante et passe à la suivante
#     - 'Alt-Enter' exécute la cellule et crée une nouvelle en dessous.
#
#
# - `Esc` permet de basculer en mode "commande" et vous pouvez naviguer dans le document avec les flèches de votre clavier. En mode "commande":
#    - `A` permet d'insérer une nouvelle cellule **au dessus** de la cellule active
#    - `B` permet d'insérer une nouvelle cellule **en dessous** de la cellule active
#    - `M` bacule en mode texte (markdown), `Y` pour revenir au code
#    - `D + D` en pressant deux fois ce caractère vous effacez la cellule courante
# - "Shift-Ctrl-M" permet de couper une cellule en deux au niveau du curseur.

# ## Installation de packages Python dans Jupyter
#
# ### Avec conda
#
# Pour installer `numpy` du canal *conda-forge*
# ```ipython
# # %conda install -c conda-forge numpy
# ```
#
# ### Avec pip
#
# ```ipython
# # %pip install numpy
# ```

# ## Documentation
#
# - Shift + Tab donne accès à la documentation des fonctions

dict

# Si vous passez la cellule suivante en *Code* et l'exécutez, la documentation apparaît dans le *pager*

# ?dict

# ## Graphique (en python)
#
# la première ligne de la cellule suivante permet de tracer vos graphiques juste après l"éxécution de la céllule.

# %matplotlib inline
# %config InlineBackend.figure_format = 'retina'
import matplotlib.pyplot as plt
import numpy as np

# +
plt.rcParams['figure.figsize'] = (10,6)
fig, ax = plt.subplots()
np.random.seed(0)
x, y = np.random.normal(size=(2, 200))
color, size = np.random.random((2, 200))

ax.scatter(x, y, c=color, s=500 * size, alpha=0.3)
ax.grid(color='lightgray', alpha=0.7)
# -

# ## Les commandes magiques

# %lsmagic

# %ls

# +
# %%file sample.txt

write the cell content to the file sample.txt.
The file is created when you run this cell.
# -

# %cat sample.txt

# +
# %%file fibonacci.py

f1, f2 = 1, 1
for n in range(10):
    print(f1, end=',')
    f1, f2 = f2, f1+f2
# -

# %run fibonacci.py

# +
# # %load fibonacci.py

f1, f2 = 1, 1
for n in range(10):
    print(f1, end=',')
    f1, f2 = f2, f1+f2

# -

# %%time
f1, f2 = 1, 1
for n in range(10):
    print(f1, end=',')
    f1, f2 = f2, f1+f2
print()

# %who int

import numpy as np
# %timeit np.random.normal(size=100)

from time import sleep
def fibonacci(n):
    f1, f2 = 1, 1
    res = []
    for i in range(n):
        sleep(0.1)
        f1, f2 = f2, f1+f2
        res.append(f1)
    return res

import IPython.core
IPython.core.page = print

# %prun -q -T prof.txt fibonacci(10)

# %cat prof.txt

# %load_ext heat

# +
# %%heat

def fibonacci(n):
    f1, f2 = 1, 1
    res = []
    for i in range(n):
        f1, f2 = f2, f1+f2
        res.append(f1)
    return res

fibonacci(100)

# +
from tqdm.notebook import tqdm
from time import sleep

n = 10
res = [1]

for x in tqdm(range(2, n)):
    sleep(0.5)
    for i in range(2, x):
        if (x % i) == 0:
            break
        else:
            res.append(x)
            break

res
# -

# ## Interactivité

# +
from ipywidgets import interact

@interact(x=True, y=1.0)
def g(x, y):
    return (x, y)

# -

@interact(tau=(0.01, 0.2, 0.01))
def f(tau):
    plt.figure(2)
    t = np.linspace(0, 1, num=1000)
    plt.plot(t, 1 - np.exp(-t/tau), t,  np.exp(-t/tau))
    plt.xlim(0, 1)
    plt.ylim(0, 1)

# -

# ## Remarques importantes
#
# - Un calepin Jupyter n'est pas vraiment un programme Python
# - Il s'agit d'une suite d'instructions exécutées dans un ordre particulier avec éventuellement des répétitions.
# - Avant de partager un notebook, il est préférable d'aller dans l'onglet `Kernel` et cliquer sur `Restart & Run All` et vérifier que tout ce passe bien.
#
# ![](images/joelgrus_tweet.png)
#
# [@joelgrus](https://twitter.com/joelgrus/status/1290072502060740610?s=20)


def selection_3():

    # Library import
    import numpy
    import matplotlib
    import matplotlib.pyplot   as plt
    import matplotlib.gridspec as gridspec

    # Library version
    matplotlib_version = matplotlib.__version__
    numpy_version      = numpy.__version__

    # Histo binning
    xBinning = numpy.linspace(-3.2,3.2,65,endpoint=True)

    # Creating data sequence: middle of each bin
    xData = numpy.array([-3.15,-3.05,-2.95,-2.85,-2.75,-2.65,-2.55,-2.45,-2.35,-2.25,-2.15,-2.05,-1.95,-1.85,-1.75,-1.65,-1.55,-1.45,-1.35,-1.25,-1.15,-1.05,-0.95,-0.85,-0.75,-0.65,-0.55,-0.45,-0.35,-0.25,-0.15,-0.05,0.05,0.15,0.25,0.35,0.45,0.55,0.65,0.75,0.85,0.95,1.05,1.15,1.25,1.35,1.45,1.55,1.65,1.75,1.85,1.95,2.05,2.15,2.25,2.35,2.45,2.55,2.65,2.75,2.85,2.95,3.05,3.15])

    # Creating weights for histo: y4_PT_0
    y4_PT_0_weights = numpy.array([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])

    # Creating weights for histo: y4_PT_1
    y4_PT_1_weights = numpy.array([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])

    # Creating weights for histo: y4_PT_2
    y4_PT_2_weights = numpy.array([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])

    # Creating weights for histo: y4_PT_3
    y4_PT_3_weights = numpy.array([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])

    # Creating weights for histo: y4_PT_4
    y4_PT_4_weights = numpy.array([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])

    # Creating weights for histo: y4_PT_5
    y4_PT_5_weights = numpy.array([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])

    # Creating weights for histo: y4_PT_6
    y4_PT_6_weights = numpy.array([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])

    # Creating weights for histo: y4_PT_7
    y4_PT_7_weights = numpy.array([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])

    # Creating weights for histo: y4_PT_8
    y4_PT_8_weights = numpy.array([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])

    # Creating weights for histo: y4_PT_9
    y4_PT_9_weights = numpy.array([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])

    # Creating weights for histo: y4_PT_10
    y4_PT_10_weights = numpy.array([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])

    # Creating weights for histo: y4_PT_11
    y4_PT_11_weights = numpy.array([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])

    # Creating weights for histo: y4_PT_12
    y4_PT_12_weights = numpy.array([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])

    # Creating weights for histo: y4_PT_13
    y4_PT_13_weights = numpy.array([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])

    # Creating weights for histo: y4_PT_14
    y4_PT_14_weights = numpy.array([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])

    # Creating weights for histo: y4_PT_15
    y4_PT_15_weights = numpy.array([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])

    # Creating weights for histo: y4_PT_16
    y4_PT_16_weights = numpy.array([0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0])

    # Creating a new Canvas
    fig   = plt.figure(figsize=(12,6),dpi=80)
    frame = gridspec.GridSpec(1,1,right=0.7)
    pad   = fig.add_subplot(frame[0])

    # Creating a new Stack
    pad.hist(x=xData, bins=xBinning, weights=y4_PT_0_weights+y4_PT_1_weights+y4_PT_2_weights+y4_PT_3_weights+y4_PT_4_weights+y4_PT_5_weights+y4_PT_6_weights+y4_PT_7_weights+y4_PT_8_weights+y4_PT_9_weights+y4_PT_10_weights+y4_PT_11_weights+y4_PT_12_weights+y4_PT_13_weights+y4_PT_14_weights+y4_PT_15_weights+y4_PT_16_weights,\
             label="$bg\_dip\_1600\_inf$", rwidth=1.0,\
             color=None, edgecolor="#e5e5e5", linewidth=1, linestyle="solid",\
             bottom=None, cumulative=False, normed=False, align="mid", orientation="vertical")

    pad.hist(x=xData, bins=xBinning, weights=y4_PT_0_weights+y4_PT_1_weights+y4_PT_2_weights+y4_PT_3_weights+y4_PT_4_weights+y4_PT_5_weights+y4_PT_6_weights+y4_PT_7_weights+y4_PT_8_weights+y4_PT_9_weights+y4_PT_10_weights+y4_PT_11_weights+y4_PT_12_weights+y4_PT_13_weights+y4_PT_14_weights+y4_PT_15_weights,\
             label="$bg\_dip\_1200\_1600$", rwidth=1.0,\
             color=None, edgecolor="#f2f2f2", linewidth=1, linestyle="solid",\
             bottom=None, cumulative=False, normed=False, align="mid", orientation="vertical")

    pad.hist(x=xData, bins=xBinning, weights=y4_PT_0_weights+y4_PT_1_weights+y4_PT_2_weights+y4_PT_3_weights+y4_PT_4_weights+y4_PT_5_weights+y4_PT_6_weights+y4_PT_7_weights+y4_PT_8_weights+y4_PT_9_weights+y4_PT_10_weights+y4_PT_11_weights+y4_PT_12_weights+y4_PT_13_weights+y4_PT_14_weights,\
             label="$bg\_dip\_800\_1200$", rwidth=1.0,\
             color=None, edgecolor="#ccc6aa", linewidth=1, linestyle="solid",\
             bottom=None, cumulative=False, normed=False, align="mid", orientation="vertical")

    pad.hist(x=xData, bins=xBinning, weights=y4_PT_0_weights+y4_PT_1_weights+y4_PT_2_weights+y4_PT_3_weights+y4_PT_4_weights+y4_PT_5_weights+y4_PT_6_weights+y4_PT_7_weights+y4_PT_8_weights+y4_PT_9_weights+y4_PT_10_weights+y4_PT_11_weights+y4_PT_12_weights+y4_PT_13_weights,\
             label="$bg\_dip\_600\_800$", rwidth=1.0,\
             color=None, edgecolor="#ccc6aa", linewidth=1, linestyle="solid",\
             bottom=None, cumulative=False, normed=False, align="mid", orientation="vertical")

    pad.hist(x=xData, bins=xBinning, weights=y4_PT_0_weights+y4_PT_1_weights+y4_PT_2_weights+y4_PT_3_weights+y4_PT_4_weights+y4_PT_5_weights+y4_PT_6_weights+y4_PT_7_weights+y4_PT_8_weights+y4_PT_9_weights+y4_PT_10_weights+y4_PT_11_weights+y4_PT_12_weights,\
             label="$bg\_dip\_400\_600$", rwidth=1.0,\
             color=None, edgecolor="#c1bfa8", linewidth=1, linestyle="solid",\
             bottom=None, cumulative=False, normed=False, align="mid", orientation="vertical")

    pad.hist(x=xData, bins=xBinning, weights=y4_PT_0_weights+y4_PT_1_weights+y4_PT_2_weights+y4_PT_3_weights+y4_PT_4_weights+y4_PT_5_weights+y4_PT_6_weights+y4_PT_7_weights+y4_PT_8_weights+y4_PT_9_weights+y4_PT_10_weights+y4_PT_11_weights,\
             label="$bg\_dip\_200\_400$", rwidth=1.0,\
             color=None, edgecolor="#bab5a3", linewidth=1, linestyle="solid",\
             bottom=None, cumulative=False, normed=False, align="mid", orientation="vertical")

    pad.hist(x=xData, bins=xBinning, weights=y4_PT_0_weights+y4_PT_1_weights+y4_PT_2_weights+y4_PT_3_weights+y4_PT_4_weights+y4_PT_5_weights+y4_PT_6_weights+y4_PT_7_weights+y4_PT_8_weights+y4_PT_9_weights+y4_PT_10_weights,\
             label="$bg\_dip\_100\_200$", rwidth=1.0,\
             color=None, edgecolor="#b2a596", linewidth=1, linestyle="solid",\
             bottom=None, cumulative=False, normed=False, align="mid", orientation="vertical")

    pad.hist(x=xData, bins=xBinning, weights=y4_PT_0_weights+y4_PT_1_weights+y4_PT_2_weights+y4_PT_3_weights+y4_PT_4_weights+y4_PT_5_weights+y4_PT_6_weights+y4_PT_7_weights+y4_PT_8_weights+y4_PT_9_weights,\
             label="$bg\_dip\_0\_100$", rwidth=1.0,\
             color=None, edgecolor="#b7a39b", linewidth=1, linestyle="solid",\
             bottom=None, cumulative=False, normed=False, align="mid", orientation="vertical")

    pad.hist(x=xData, bins=xBinning, weights=y4_PT_0_weights+y4_PT_1_weights+y4_PT_2_weights+y4_PT_3_weights+y4_PT_4_weights+y4_PT_5_weights+y4_PT_6_weights+y4_PT_7_weights+y4_PT_8_weights,\
             label="$bg\_vbf\_1600\_inf$", rwidth=1.0,\
             color=None, edgecolor="#ad998c", linewidth=1, linestyle="solid",\
             bottom=None, cumulative=False, normed=False, align="mid", orientation="vertical")

    pad.hist(x=xData, bins=xBinning, weights=y4_PT_0_weights+y4_PT_1_weights+y4_PT_2_weights+y4_PT_3_weights+y4_PT_4_weights+y4_PT_5_weights+y4_PT_6_weights+y4_PT_7_weights,\
             label="$bg\_vbf\_1200\_1600$", rwidth=1.0,\
             color=None, edgecolor="#9b8e82", linewidth=1, linestyle="solid",\
             bottom=None, cumulative=False, normed=False, align="mid", orientation="vertical")

    pad.hist(x=xData, bins=xBinning, weights=y4_PT_0_weights+y4_PT_1_weights+y4_PT_2_weights+y4_PT_3_weights+y4_PT_4_weights+y4_PT_5_weights+y4_PT_6_weights,\
             label="$bg\_vbf\_800\_1200$", rwidth=1.0,\
             color=None, edgecolor="#876656", linewidth=1, linestyle="solid",\
             bottom=None, cumulative=False, normed=False, align="mid", orientation="vertical")

    pad.hist(x=xData, bins=xBinning, weights=y4_PT_0_weights+y4_PT_1_weights+y4_PT_2_weights+y4_PT_3_weights+y4_PT_4_weights+y4_PT_5_weights,\
             label="$bg\_vbf\_600\_800$", rwidth=1.0,\
             color=None, edgecolor="#afcec6", linewidth=1, linestyle="solid",\
             bottom=None, cumulative=False, normed=False, align="mid", orientation="vertical")

    pad.hist(x=xData, bins=xBinning, weights=y4_PT_0_weights+y4_PT_1_weights+y4_PT_2_weights+y4_PT_3_weights+y4_PT_4_weights,\
             label="$bg\_vbf\_400\_600$", rwidth=1.0,\
             color=None, edgecolor="#84c1a3", linewidth=1, linestyle="solid",\
             bottom=None, cumulative=False, normed=False, align="mid", orientation="vertical")

    pad.hist(x=xData, bins=xBinning, weights=y4_PT_0_weights+y4_PT_1_weights+y4_PT_2_weights+y4_PT_3_weights,\
             label="$bg\_vbf\_200\_400$", rwidth=1.0,\
             color=None, edgecolor="#89a8a0", linewidth=1, linestyle="solid",\
             bottom=None, cumulative=False, normed=False, align="mid", orientation="vertical")

    pad.hist(x=xData, bins=xBinning, weights=y4_PT_0_weights+y4_PT_1_weights+y4_PT_2_weights,\
             label="$bg\_vbf\_100\_200$", rwidth=1.0,\
             color=None, edgecolor="#829e8c", linewidth=1, linestyle="solid",\
             bottom=None, cumulative=False, normed=False, align="mid", orientation="vertical")

    pad.hist(x=xData, bins=xBinning, weights=y4_PT_0_weights+y4_PT_1_weights,\
             label="$bg\_vbf\_0\_100$", rwidth=1.0,\
             color=None, edgecolor="#adbcc6", linewidth=1, linestyle="solid",\
             bottom=None, cumulative=False, normed=False, align="mid", orientation="vertical")

    pad.hist(x=xData, bins=xBinning, weights=y4_PT_0_weights,\
             label="$signal$", rwidth=1.0,\
             color=None, edgecolor="#7a8e99", linewidth=1, linestyle="solid",\
             bottom=None, cumulative=False, normed=False, align="mid", orientation="vertical")

    # Axis
    plt.rc('text',usetex=False)
    plt.xlabel(r"\phi [ j_{1} ] ",\
               fontsize=16,color="black")
    plt.ylabel(r"$\mathrm{Events}$ $(\mathcal{L}_{\mathrm{int}} = 40.0\ \mathrm{fb}^{-1})$ ",\
               fontsize=16,color="black")

    # Boundary of y-axis
    ymax=(y4_PT_0_weights+y4_PT_1_weights+y4_PT_2_weights+y4_PT_3_weights+y4_PT_4_weights+y4_PT_5_weights+y4_PT_6_weights+y4_PT_7_weights+y4_PT_8_weights+y4_PT_9_weights+y4_PT_10_weights+y4_PT_11_weights+y4_PT_12_weights+y4_PT_13_weights+y4_PT_14_weights+y4_PT_15_weights+y4_PT_16_weights).max()*1.1
    ymin=0 # linear scale
    #ymin=min([x for x in (y4_PT_0_weights+y4_PT_1_weights+y4_PT_2_weights+y4_PT_3_weights+y4_PT_4_weights+y4_PT_5_weights+y4_PT_6_weights+y4_PT_7_weights+y4_PT_8_weights+y4_PT_9_weights+y4_PT_10_weights+y4_PT_11_weights+y4_PT_12_weights+y4_PT_13_weights+y4_PT_14_weights+y4_PT_15_weights+y4_PT_16_weights) if x])/100. # log scale
    plt.gca().set_ylim(ymin,ymax)

    # Log/Linear scale for X-axis
    plt.gca().set_xscale("linear")
    #plt.gca().set_xscale("log",nonposx="clip")

    # Log/Linear scale for Y-axis
    plt.gca().set_yscale("linear")
    #plt.gca().set_yscale("log",nonposy="clip")

    # Legend
    plt.legend(bbox_to_anchor=(1.05,1), loc=2, borderaxespad=0.)

    # Saving the image
    plt.savefig('../../HTML/MadAnalysis5job_0/selection_3.png')
    plt.savefig('../../PDF/MadAnalysis5job_0/selection_3.png')
    plt.savefig('../../DVI/MadAnalysis5job_0/selection_3.eps')

# Running!
if __name__ == '__main__':
    selection_3()


"""
@file
@brief Helpers to compile C.
"""
import os
import sys
import shutil
import numpy

_header_c_float = """
void concat_float_float(float* xy, float x, float y)
{
    xy[0] = x;
    xy[1] = y;
}

void adot_float_float(float* res, float* vx, float* vy, int dim)
{
    *res = 0;
    for(; dim > 0; --dim, ++vx, ++vy)
        *res += *vx * *vy;
}

void aadd_float(float* res, float* vx, float* vy, int dim)
{
    for(; dim > 0; --dim, ++vx, ++vy, ++res)
        *res = *vx + *vy;
}

void asub_float_float(float* res, float* vx, float* vy, int dim)
{
    for(; dim > 0; --dim, ++vx, ++vy, ++res)
        *res = *vx - *vy;
}

void amul_float_float(float* res, float* vx, float* vy, int dim)
{
    for(; dim > 0; --dim, ++vx, ++vy, ++res)
        *res = *vx * *vy;
}

void adiv_float_float(float* res, float* vx, float* vy, int dim)
{
    for(; dim > 0; --dim, ++vx, ++vy, ++res)
        *res = *vx / *vy;
}

void sign_float(float* res, float x)
{
    *res = x >= 0 ? (float)1 : (float)0 ;
}

void atake_float_int(float* res, float * vx, int p, int dim)
{
    *res = vx[p];
}

void atake_int_int(int* res, int* vx, int p, int dim)
{
    *res = vx[p];
}

typedef int bool;

"""

_header_c_double = _header_c_float.replace("float", "double")

class CompilationError(Exception):
    """
    Raised when a compilation error was detected.
    """
    pass

def compile_c_function(code_c, nbout, dtype=numpy.float32, add_header=True,
                       suffix="", additional_paths=None, tmpdir='.', fLOG=None):
    """
    Compiles a C function with :epkg:`cffi`.
    It takes one features vector.

    @param      nbout               number of expected outputs
    @param      code_c              code C
    @param      dtype               numeric type to use
    @param      add_header          add common function before compiling
    @param      suffix              avoid avoid the same compiled module name
    @param      additional_paths    additional paths to add to the module
    @param      tmpdir              see below
    @param      fLOG                logging function
    @return     compiled            function

    The function assumes the first line is the signature.
    If you are using Windows with Visual Studio 2017, make sure
    you are using :epkg:`Python` 3.6.3+
    (see `Issue 30389 <https://bugs.python.org/issue30389>`_).
    Parameter *tmpdir* is used by function `compile
    <http://cffi.readthedocs.io/en/latest/cdef.html?
    highlight=compile#ffibuilder-compile-etc-compiling-out-of-line-modules>`_.
    """
    if sys.platform.startswith("win"):
        if "VS140COMNTOOLS" not in os.environ:  # pragma: no cover
            raise CompilationError(
                "Visual Studio is not installed.\n{0}".format(
                    "\n".join("{0}={1}".format(k, v) for k, v in sorted(os.environ.items()))))

    sig = code_c.split("\n")[0].strip() + ";"
    name = sig.split()[1]
    include_paths = []
    lib_paths = []
    if additional_paths is None:
        additional_paths = []

    # ~ if len(additional_paths) == 0 and sys.platform.startswith("win") and \
    # ~ 'VSSDK140Install' not in os.environ:  # last condition is for the installed VisualStudio.
    # ~ if fLOG:
    #~ fLOG("[compile_c_function] fix PATH for VS2017 on Windows")
    # ~ # Update environment variables.
    # ~ adds = [r"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin\amd64",
    # ~ r"C:\Program Files (x86)\Windows Kits\10\bin\10.0.15063.0\x64"]
    # ~ vcvars64 = os.path.join(adds[0], 'vcvars64.bat')
    #~ subprocess.run(vcvars64)

    # ~ # Add paths for VS2017.
    # ~ includes = [r'C:\Program Files (x86)\Windows Kits\10\Include\10.0.15063.0\shared',
    #~ r'C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include',
    # ~ r'C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\SDK\ScopeCppSDK\SDK\include\ucrt']
    # ~ libs = [r'C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\lib\amd64',
    #~ r'C:\Program Files (x86)\Windows Kits\10\Lib\10.0.15063.0\um\x64',
    # ~ r'C:\Program Files (x86)\Windows Kits\10\Lib\10.0.15063.0\ucrt\x64']
    # ~ opaths = os.environ['PATH'].split(';')
    # ~ for add in adds:
    # ~ if os.path.exists(add) and add not in opaths:
    #~ additional_paths.append(add)
    # ~ oinc = os.environ.get('INCLUDE', '').split(';')
    # ~ for inc in includes:
    # ~ if os.path.exists(inc) and inc not in oinc:
    #~ include_paths.append(inc)
    # ~ for lib in libs:
    # ~ if os.path.exists(lib):
    #~ lib_paths.append(lib)

    if additional_paths:
        if fLOG:  # pragma: no cover
            for p in additional_paths:
                fLOG("[compile_c_function] PATH += '{0}'".format(p))
        os.environ["PATH"] += ";" + ";".join(additional_paths)

    if lib_paths and sys.platform.startswith("win"):  # pragma: no cover
        libs = ['msvcrt.lib', 'oldnames.lib', 'kernel32.lib', 'vcruntime.lib',
                'ucrt.lib']
        libs = {k: False for k in libs}
        for lib in lib_paths:
            for name in list(libs):
                if libs[name]:
                    continue
                msv = os.path.join(lib, name)
                if os.path.exists(msv):
                    dst = os.getcwd()
                    msvd = os.path.join(dst, name)
                    if not os.path.exists(msvd):
                        shutil.copy(msv, dst)
                        if fLOG:
                            fLOG("[compile_c_function] copy '{0}'".format(msv))
                    libs[name] = True
        copied = len([k for k, v in libs.items() if v])
        if copied < len(libs):
            raise CompilationError('Unable to find those libraries ({0}<{1}) {2} in\n{3}'.format(
                copied, len(libs), ','.join(sorted(libs)), '\n'.join(lib_paths)))

    if include_paths:
        if fLOG:  # pragma: no cover
            for p in include_paths:
                fLOG("[compile_c_function] INCLUDE += '{0}'".format(p))
        if 'INCLUDE' in os.environ:
            os.environ["INCLUDE"] += ";" + ";".join(include_paths)
        else:
            os.environ["INCLUDE"] = ";".join(include_paths)

    is_float = dtype == numpy.float32
    header = _header_c_float if is_float else _header_c_double
    code = code_c if not add_header else (header + code_c)

    from cffi import FFI
    ffibuilder = FFI()
    try:
        ffibuilder.cdef(sig)
    except Exception as e:  # pragma: no cover
        raise CompilationError(
            "Signature is wrong\n{0}\ndue to\n{1}".format(sig, e)) from e
    ffibuilder.set_source("_" + name + suffix, code)
    try:
        ffibuilder.compile(verbose=False, tmpdir=tmpdir)
    except Exception as e:  # pragma: no cover
        raise CompilationError(
            "Compilation failed \n{0}\ndue to\n{1}".format(sig, e)) from e
    mod = __import__("_{0}{1}".format(name, suffix))
    fct = getattr(mod.lib, name)

    def wrapper(features, output, cast_type, dtype):
        "wrapper for a vector of features"
        if len(features.shape) != 1:
            raise TypeError(  # pragma: no cover
                "Only one dimension for the features not {0}.".format(
                    features.shape))
        if output is None:
            output = numpy.zeros((nbout,), dtype=dtype)
        else:
            if len(output.shape) != 1:
                raise TypeError(  # pragma: no cover
                    "Only one dimension for the output not {0}.".format(
                        output.shape))
            if output.shape[0] != nbout:
                raise TypeError(  # pragma: no cover
                    "Dimension mismatch {0} != {1} (expected).".format(
                        output.shape, nbout))
            if output.dtype != dtype:
                raise TypeError(  # pragma: no cover
                    "Type mismatch {0} != {1} (expected).".format(
                        output.dtype, dtype))
        ptr = features.__array_interface__['data'][0]
        cptr = mod.ffi.cast(cast_type, ptr)
        optr = output.__array_interface__['data'][0]
        cout = mod.ffi.cast(cast_type, optr)
        fct(cout, cptr)
        return output

    def wrapper_double(features, output=None):
        "wrapper for double"
        return wrapper(features, output, "double*", numpy.float64)

    def wrapper_float(features, output=None):
        "wrapper for float"
        return wrapper(features, output, "float*", numpy.float32)

    return wrapper_float if is_float else wrapper_double


#!/usr/bin/env python3

import unittest
from unittest.mock import patch
import numpy as np
import pandas as pd
from tmc import points

from tmc.utils import load, get_stdout, patch_helper

module_name="src.cycling_weather"
cycling_weather = load(module_name, "cycling_weather")
main = load(module_name, "main")
ph = patch_helper(module_name)

@points('p05-02.1')
class CyclingWeather(unittest.TestCase):

    # @classmethod
    # def setUpClass(cls):
    #     cls.df = cycling_weather()

    def setUp(self):
        self.df = cycling_weather()

    def test_shape(self):
        self.assertEqual(self.df.shape, (8760, 28),
                         msg="Incorrect shape returned by cycling_weather function!")

    def test_column_names(self):
        cols=['Year', 'Precipitation amount (mm)', 'Snow depth (cm)',
        'Air temperature (degC)', 'Weekday', 'Day', 'Month', 'Hour',
        'Auroransilta', 'Eteläesplanadi', 'Huopalahti (asema)',
        'Kaisaniemi/Eläintarhanlahti', 'Kaivokatu', 'Kulosaaren silta et.',
        'Kulosaaren silta po. ', 'Kuusisaarentie', 'Käpylä, Pohjoisbaana',
        'Lauttasaaren silta eteläpuoli', 'Merikannontie',
        'Munkkiniemen silta eteläpuoli', 'Munkkiniemi silta pohjoispuoli',
        'Heperian puisto/Ooppera', 'Pitkäsilta itäpuoli',
        'Pitkäsilta länsipuoli', 'Lauttasaaren silta pohjoispuoli',
        'Ratapihantie', 'Viikintie', 'Baana']
        self.assertCountEqual(self.df.columns, cols, msg="Incorrect column names!")

    def test_calls(self):
        with patch(ph("cycling_weather"), wraps=cycling_weather) as pcw,\
            patch(ph("pd.read_csv"), wraps=pd.read_csv) as prc,\
            patch(ph("pd.merge"), wraps=pd.merge) as pmerge:
            main()
            pcw.assert_called_once()
            pmerge.assert_called_once()
            self.assertEqual(prc.call_count, 2,
                             msg="You should have called pd.read_csv exactly twice")

if __name__ == '__main__':
    unittest.main()