Уведомления

Группа в Telegram: присоединиться

#1 Апрель 15, 2019 13:22:15

vasyabylba
Зарегистрирован: 2019-04-15
Сообщения: 6
Репутация: +  0  -
Профиль   Отправить e-mail  

Нужна помощь в объединении файлов Python в один проект

Нужна помощь в объединении файлов о один проект. Скорее всего нужен ещё какой-то код (так называемый Main) для реализации идеи. Суть: определение являются ли два текста от одного автора.

 import numpy as np
import similarity_measures as sm
from numba import jit
@jit
def get_score(x,y,imposters,sim):
    '''Compute for how many random feature sets out of 100 sim(x,y) is greater than sim(x,z) for all z in imposters'''	
    score = 0
    sim_xy = sim(x,y) 
    for k in range(100):
        ran_el = np.sort(np.random.choice(range(len(x)), (1,round(len(x) / 2)), replace = False))        
        c_x = x[ran_el][0]
        c_y = y[ran_el][0]
        sim_xy = sim(c_x,c_y)
        for yi in imposters:
            if sim(c_x, np.take(yi,ran_el)[0]) > sim_xy:
                break
        else:
            score += 1
    return score / 100.0  # 
   
def imposters(y, universe, m = 125, n = 25):
    '''Return n imposters. Compute the m most similar files in universe and randomly select n from them '''
    minmax_vec = np.array([sm.cminmax(t,y) for t in universe])
    pot_imp_ind = minmax_vec.argsort()[:-(m+1):-1] # Last m entries
    pot_imp_ind = np.sort(np.random.choice(pot_imp_ind, n))
    return [universe[k] for k in pot_imp_ind[::-1]]  # Have the most similar imposters first, to hope for a quicker break in the get_score algorithm
    
def blog_same_author(x,y,text_corpus, threshold, nr_imposters = 25):
    '''Return true if x and y are by the same author according to the algorithm in the paper'''
    imposters_y = imposters(y, text_corpus.Y, n = nr_imposters)
    score_xy = get_score(x,y,imposters_y, sm.cminmax)
    
    imposters_x = imposters(x, text_corpus.X, n = nr_imposters)
    score_yx = get_score(y,x,imposters_x, sm.cminmax)
    
    if (score_xy + score_yx ) /2.0 > threshold:
        print("true")
        return True
    else:
        print("false")
        return False
 

Отредактировано vasyabylba (Апрель 15, 2019 13:26:56)

Прикреплённый файлы:
attachment imposter_meth.py (1,7 KБ)

Офлайн

#2 Апрель 15, 2019 13:23:35

vasyabylba
Зарегистрирован: 2019-04-15
Сообщения: 6
Репутация: +  0  -
Профиль   Отправить e-mail  

Нужна помощь в объединении файлов Python в один проект

 from numba import jit
import numpy as np
def minmax(x,y):
    return np.sum(np.minimum(x,y)) / np.sum(np.maximum(x,y))
@jit
def cminmax(x,y):
    nom = 0.0
    denom = 0
    for k in range(len(x)):
        if x[k] > y[k]:
            nom += y[k]
            denom += x[k]
        else:
            nom += x[k]
            denom += y[k]  
    return nom / denom
def cos(x,y):
    return x.dot(y) / (np.linalg.norm(x) * np.linalg.norm(y))

Отредактировано vasyabylba (Апрель 15, 2019 13:28:41)

Прикреплённый файлы:
attachment similarity_measures.py (439 байт)

Офлайн

#3 Апрель 15, 2019 13:24:01

vasyabylba
Зарегистрирован: 2019-04-15
Сообщения: 6
Репутация: +  0  -
Профиль   Отправить e-mail  

Нужна помощь в объединении файлов Python в один проект

 import data_prep as dp
import numpy as np
class corpus:
    def __init__(self, start = '', end = '', authors = ''):
        self.start, self.end, self.authors = start, end, authors
    def build_by_xml(self, path, number_of_words=500, number_of_files = 500):
        self.start, self.end, self.authors = dp.open_xml(path, max_of_files = number_of_files, n_of_words = number_of_words)
        
    def build_pairs(self):
        self.pair = dp.pair_vec(len(self.start))
        self.end = np.take(self.end, self.pair)
        
    def build_matrix(self):
        data = dp.build_matrix(np.concatenate((self.start, self.end)))
        self.X = data[:len(self.start)]
        self.Y = data[len(self.end):]
        
    def get_length(self):
        return len(self.authors)
    
    def same_author(self, k):
        return self.pair[k] == k
    
    def get_pair(self, k,num = True, text = False):
        if text and num:
            return [self.X[k], self.Y[k], self.start[k], self.end[k]]
        if text:
            return [self.start[k], self.end[k]]
        else:
            return [self.X[k], self.Y[k]]
    
    

Отредактировано vasyabylba (Апрель 15, 2019 13:28:03)

Прикреплённый файлы:
attachment corpus_class.py (1,1 KБ)

Офлайн

#4 Апрель 15, 2019 13:24:15

vasyabylba
Зарегистрирован: 2019-04-15
Сообщения: 6
Репутация: +  0  -
Профиль   Отправить e-mail  

Нужна помощь в объединении файлов Python в один проект

 import re
import string
import numpy as np
import pandas as pd
import os
import random
import xml.etree.ElementTree as ET
def get_n_words(text, number_of_words, name = None, report = False):
    '''Funktion to extract the first and last n words from a string
    long tells, if the file is too short to extract to distinct subsets of length number_of_words'''
    long = True
    
    words = text.split(' ')
    if len(words) < number_of_words*2:   # Double because I dont want that first and last have 'common' elements
        if report == True:
            print('WARNING: ' + name + ' has fewer than ' + str(number_of_words*2) + ' words! ' + str(len(words)))
        long = False
        
    # Die ersten N Wörter:
    first_words = words[:number_of_words]
    start = ' '.join(first_words)
    # Letzten N Wörter:
    last_words = words[-number_of_words:]
    end = ' '.join(last_words)
    return (start, end, long)
   
#### Test
# Kurze Test-Texte
text1 = 'Sprengstoffexperten des und Landeskriminalamtes haben den Sprengsatz untersucht. Sie gehen davon aus, dass die Ladung aus den Inhaltsstoffen sogenannter Polenböller gebaut und offenbar per Funk gezündet wurde. Die Beamten stufen den Vorfall als besorgniserregend ein, da man eine derartige Sprengkraft bei vergleichbaren Fällen noch nicht gesehen habe. Eine Spezialeinheit der Polizei zur Aufklärung extremistisch orientierter Straftaten ermittelt. Neben der Spur nach Berlin geht sie auch der These nach, dass womöglich eine bisher unbekannte Gruppe in dem Haus, das zum Abriss vorgesehen ist, einen Sprengversuch unternommen hat.'
text2 = 'Und das, obwohl die und Sache mit der Krim eigentlich klar ist Die russische Annexion der ukrainischen Halbinsel im Jahr 2014 war völkerrechtswidrig. Das sagt die Bundesregierung, das sagt die EU das sagt sogar die Linkspartei, schon 2014 festgehalten per Parteitagsbeschluss. Und trotzdem wollen manche Linke am liebsten nicht darüber sprechen.'
text3 = 'US-Präsident Donald Trump schließt eine militärische Reaktion auf die Krise in Venezuela nicht aus. Es gebe mehrere Möglichkeiten, "darunter eine militärische Option, falls nötig", sagte Trump am Freitag in New Jersey. Konkrete Pläne für ein militärisches Eingreifen in Venezuela gibt es aber offenbar nicht. Ein Pentagon-Sprecher erklärte, zum jetzigen Zeitpunkt gebe es keine entsprechenden Anweisungen aus dem Weißen Haus.'
text4 = 'Als am Freitagmorgen vergangener und Woche die Eilmeldungen zum überraschenden Fraktionswechsel der niedersächsischen Grünen-Abgeordneten Elke Twesten über die Nachrichtenagenturen liefen, wusste die Bundeskanzlerin längst Bescheid. Angela Merkel (CDU) hat einem Medienbericht zufolge vorab von der Wechsel der niedersächsischen Landtagsabgeordneten von den Grünen zur CDU erfahren. Das gehe aus einem Schreiben von Kanzleramtsstaatsminister Helge Braun an die Geschäftsführerin der SPD-Bundestagsfraktion, Christine Lambrecht hervor, berichteten die Zeitungen des Redaktionsnetzwerks Deutschland (RND). Demnach informierte der niedersächsische CDU-Landesvorsitzende Bernd Althusmann die Kanzlerin am Vortag des Wechsels telefonisch.'
vector_with_text = [text1, text2, text3, text4]
b1 = 'This is a 12385 sample'
b2 = 'this is     ano>>ther $example!!!'
vector_with_text = [b1, b2]
# Takes a path and opens all txt-files in that directory and returns a vector with the n first
# words, n last words from every text and a vector with the name of the authors, given that all
# files have the format author_title.txt
def open_text(path, number_of_words):
    vector_with_text = []
    vector_text_end = []
    authors = []
    for t in os.listdir(path):
        # Überspringe Dateien, die keine Txt sind:
        if t[-4:] != '.txt':
            continue
            
        aut = re.match(r'(\w*)_', t).group(1)
        authors.append(aut)
            
        name = path +'/'+ t
        file = open(name, 'r')   
        text = file.read()
        
        words = get_n_words(text, number_of_words, t)
        vector_with_text.append(words[0])
        vector_text_end.append(words[1])
    return vector_with_text, vector_text_end, authors
    
def open_xml(path, max_of_files = 5000, n_of_words = 500):
    ''' Takes a path and opens max_of_files xml files in that directory. From each file it extracts the
    n first and last words and gives the author-ID, if the filename is formatted as authorid.[...].xml
    remove specifies if '&' Symboles are deleted in the files'''
    text_start = []
    text_end = []
    author_id = []
    
    number_files = 0
    for f_name in os.listdir(path):
        # Just xml-Files:
        #if f_name[-4:] != '.xml':
        if not f_name.endswith('xml'):
            continue        
        
        if number_files >= max_of_files:
            break
                       
        file_path = path + '/' + f_name
        
        try:
            text = ''
            file = open(file_path, 'r')
            for line in file:
                if line.startswith('<'):
                    continue
                text += line.strip()
                
            start, end, long =  get_n_words(text, n_of_words, f_name, False)    # Gives start, end, long? (i.e. boolean if the file has fewer than n words)
            # If the document has to few words, skip this one
            if long == False:
                continue
            aut = re.match(r'(\d+).', f_name).group(1)
            author_id.append(aut)
            text_start.append(start)
            text_end.append(end)
            number_files += 1
            #print(f_name)
            
        except UnicodeDecodeError:
            #print('Encoding Error: ', f_name)
            pass
        except ValueError:
            print('Parse: file: ', f_name)
        
    return text_start, text_end, author_id
    
    
    
def old_open_xml(path, max_of_files = 5000, n_of_words = 500, remove = True):
    text_start = []
    text_end = []
    author_id = []
    
    number_files = 0
    for f_name in os.listdir(path):
        # Just xml-Files:
        #if f_name[-4:] != '.xml':
        if not f_name.endswith('xml'):
            continue        
        
        if number_files >= max_of_files:
            break
                       
        file = path + '/' + f_name
        
        # Remove &-Symbols (otherwise the xml Parser throws an error)
        if remove == True:
            f = open(file, 'r')
            text = f.read()
            f.close()
            text = text.translate({ord('&'): None})
            f_o = open(file, 'w')
            f_o.write(text)
            f_o.close()
                       
        with open(file, 'r') as xml_file:   # Umständlich damit UTF8 codierung -> Reicht nicht
            tree = ET.parse(xml_file)
#      tree = ET.parse(file)
        root = tree.getroot()
        
        text = ''
        for p in root.findall('post'): # Find all <post> entries
            text += p.text.strip()    # Build one long string without any newline characters and white spaces
        
        start, end, long =  get_n_words(text, n_of_words, f_name, False)    # Gives start, end, long? (i.e. boolean if the file has fewer than n words)
        
        # If the document has to few words, skip this one
        if long == False:
            continue
            
        aut = re.match(r'(\d+).', f_name).group(1)
        author_id.append(aut)
            
        text_start.append(start)
        text_end.append(end)
        
        number_files += 1
        
    return text_start, text_end, author_id
# Take a text and get all the n-grams with their freq as a dict
def get_freq(text, n = 4):
    '''Take a string and get all the n-grams with their freq as a dict'''
    text1 = text.lower()
#   text1 = re.sub(r'[.,-?!+"_()/$§%<>]', '', text1)   # Remove punctuation, RE slower than that:
    text1 = text1.translate({ord(char): None for char in string.punctuation + '0123456789'}) # Remove punctuation and digits
    text1 = re.sub(r'\s\s+',' ',text1)     # Remove double spaces
    words = text1.split(' ')
    
    grams = []
    # Identify all n-grams:
    for w in words:
        if len(w) < (n+1):
            grams.append(w.lower())
        else:
            for k in range(len(w)-n + 1):
                grams.append(w[k:k+n].lower())
                
    freq = {}
    for g in set(grams):
        c = grams.count(g)
        freq[g]= c
    return freq
def build_matrix(vector_with_text, k_highest_freq = 100000):
    ''' Take a array of texts and return a matrix (as a numpy array) with the tf-idf values for all n grams in the texts. Each row represents a text'''
# Matrix mit den Texten als Zeilen und den verschiedenen n-grams als Spalten. Die Einträge geben
# dann für jedes n-grams den tf-idf Wert in Bezug auf den Text an
    
    # WICHTIG:
    # total_text braucht für später die selbe Reihenfolge wie vector_with_text
    total_text = [get_freq(text) for text in vector_with_text] # Vector with all dictionaries of freq
    # Total_freq ist ein dict was für jedes in irgendeinem Text vorkommenden n-gram die Gesamt-
    # Vorkommenshäufigkeit speichert
    total_freq = {}
    for cfreq in total_text:
        for f in cfreq:
            if f in total_freq:
                total_freq[f] += cfreq[f]
            else:
                total_freq[f] = cfreq[f]  
    #print(total_freq)           
    #print(len(total_freq))
    
    
    # Get the n-grams with the k-hightest freq from all texts
    # If there are more n-grams than k_highest_freq, delate the least frequent ones
    if len(total_freq.keys()) > k_highest_freq:
        lowest_freq = sorted([x for x in total_freq.values()], reverse = True)[k_highest_freq]
        keys = total_freq.keys()
        for k in list(total_freq.keys()):
            if total_freq[k] < lowest_freq:    
                del total_freq[k]
        
    # Data_m als matrix, die für jedes n-gram die absolute Häufigkeit enthält
    data_m = np.empty((len(vector_with_text), len(total_freq.keys())))
    for t in enumerate(vector_with_text):
        freq_t = []                                     # Freq für alle n-grams für aktuellen Text, da vector erst um alle n-grams erweitert werden muss, die nicht in aktuellem Text sind
        for g in enumerate(total_freq.keys()):
            if g[1] in total_text[t[0]]:                # Wenn n-gram in aktuellem Text (toatl_text sollte die selbe Reihenfolge haben wir v_text)
                freq_t.append(total_text[t[0]][g[1]])   # Nehme aus vektor mit allen Freq, den Eintrag der zum Text entspricht und suche die Freq für das aktuelle n gram 
            else: 
                freq_t.append(0.0)
        data_m[t[0]] = freq_t                           # Neue Reihe in Dataframe mit dem index
    # Berechen Vektor mit der idf für jedes n-gram
    idf_m = np.log(data_m.shape[0] /np.array([(data_m[:,column]!= 0).sum() for column in range(data_m.shape[1])])) 
    # Berechne TF 
    data_m = data_m / np.sum(data_m, axis = 1)[:,None]
    
    # Berechne TF-IDF
    data_m = np.multiply(data_m, idf_m)
    
    #print(data_m)
    return data_m
def pair_vec(n):
    '''The index corresponds to the start index, the entry to the text in end
    A Pair is from the same author if index = entry. 
    ATTENTION: By chance it can happen, that slithly more than 50% are from the same other, but never less
    '''
    x = list(range(n))
    #random.seed(0)
    rand_ind = random.sample(range(n), round(n / 2))
    rand_match = rand_ind.copy()
    random.shuffle(rand_match)
    
    for ent in x:
        if ent in rand_ind:
            x[ent] = rand_match.pop()
    return x

Отредактировано vasyabylba (Апрель 15, 2019 13:27:37)

Прикреплённый файлы:
attachment data_prep.py (11,4 KБ)

Офлайн

#5 Апрель 15, 2019 13:24:33

vasyabylba
Зарегистрирован: 2019-04-15
Сообщения: 6
Репутация: +  0  -
Профиль   Отправить e-mail  

Нужна помощь в объединении файлов Python в один проект

 {
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Determining if two documents are written by the same author"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "import pickle\n",
    "import numpy as np\n",
    "import data_prep as dp\n",
    "import similarity_measures as sm\n",
    "import imposter_meth as im\n",
    "import corpus_class as cp\n",
    "\n",
    "\n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Corpus- Class"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[array([ 0.        ,  0.        ,  0.        ,  0.23104906,  0.        ,\n",
      "        0.        ,  0.        ,  0.        ,  0.23104906,  0.        ]), array([ 0.        ,  0.        ,  0.        ,  0.        ,  0.        ,\n",
      "        0.        ,  0.        ,  0.        ,  0.        ,  0.27725887])]\n"
     ]
    }
   ],
   "source": [
    "text1 = 'First example'\n",
    "text2 = 'Next example'\n",
    "text3 = 'Third example'\n",
    "text4 = 'Four example'\n",
    "\n",
    "cor = cp.corpus([text1, text3], [text2, text4])\n",
    "cor.build_matrix()\n",
    "print(cor.get_pair(0))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Gives back the tf-idf vector representations of text 1 and 2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Imposters"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Because it takes a couple of minutes to compute the algorithm for 500 pairs, I just load the results:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "with open('/Users/alexandersauer/Anaconda_Projects/results/scores500_blog', 'rb') as f:\n",
    "    scores_blog = pickle.load(f)\n",
    "with open('/Users/alexandersauer/Anaconda_Projects/results/scores500_minmax', 'rb') as f:\n",
    "    scores_minmax = pickle.load(f)\n",
    "with open('/Users/alexandersauer/Anaconda_Projects/results/scores500_cosine', 'rb') as f:\n",
    "    scores_cos = pickle.load(f)\n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAA/EAAAIiCAYAAACAHEigAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAAPYQAAD2EBqD+naQAAIABJREFUeJzs3Xl8VNX9//HXSdhJQBCCUdbEpailAipCot9aK1prg01E\nar9WBa3aKrhUrKItikWL/SltUGutUKxWCt92KqitYheXRKoSbCmKVgmLaAyLCAlbJDm/P+5MMluS\nSXInd5b3k8c8wr1z751zPzNzz5x7NmOtRUREREREREQSX4bXCRARERERERGR2KgQLyIiIiIiIpIk\nVIgXERERERERSRIqxIuIiIiIiIgkCRXiRURERERERJKECvEiIiIiIiIiSUKFeBEREREREZEkoUK8\niIiIiIiISJJQIV5EREREREQkSagQLyIJzRjTYIwp9TodIiIiLTHG3GmMaQhbl2mMuc8Ys8UYU2+M\n8fnX9zbGPGaMqfLncw94k+rkFoi5Maa/12kR6UwqxIt0AmPMF40xfzDGbDLG7DfGbDXGrDTGXOd1\n2hKBMWa8MWa2MaaP12kRERExxlzmLxwGHvuNMR8ZY543xkw3xmRF2c0CDWHrrgBuBpYBlwLz/etv\n9y8/BFwCPBGfM+k4Y8xIfx491MM03GaMmRTlKet/iKQVFeJF4swYMwF4E/gi8ChwLfBroB6Y4WHS\nEskE4MfAYV4nRERExM8Cd+AUsq8BSv3rfg78xxjzxbDt7wZ6ha07E9hqrb3ZWvs7a+2rQev/aa39\nibX2KWvtW3E7i447HpgNDPcwDbOAaIV4kbTUxesEiKSB24HPgJOttTXBTxhjBniTpIRjPH1xY3pZ\na/d5mQYREUlIz1tr1wQtzzPGfBl4DlhujBlprT0IYK1tAOrC9s/B+Q0QLgd4261EGmMM0C2QFpcZ\n0ry22xjTw1p7wOt0iASoJl4k/vKAt8ML8ADW2h3By8aYqcaYvxljqo0xB4wxbxtjrgnfz98sf4Ux\n5n+MMW8aY/YZY9YaY/7H/3yxf3m/MWa1MeakKMc4zt/Ef6d/uzeNMd+I5YSMMTcbY8qNMTv8r73a\nGFMSts0wfxPES6Ps32CM+bH//7OB+/xPbfI/Vx/ebM8YM8kY8x9/XNYZY86JctzRxpi/GGN2G2Nq\njDF/NcaMC9sm0ETyDGPMw8aYauDDWM5bRETEWvsSTq37MJxaeiC0T3wgDwS+DJwYlLf9j3/9cOD8\n8DzPGNPNGHOXMeZ9f363xRgzzxjTLTgNgfFijDHfNsasAw4A5/ifM8aYG/x55X5jzCfGmEeMMYeF\nHSPwW6LAGPO6f9sNxpjvBG1zGU5XAICXgtJ7RnPxMU4Xwt/4j7XfOP3+F5qwfuvGmMXGmI1R9g8Z\nW8D//17A5aape8OisN36+Y+3yxjzmTFmkTGmR9hxM40xPzLGfOCP7UZjzNwosQ3EZaL/t9F+4Krm\nzlfEC6qJF4m/zcBpxpgTrLWt3XW/BlgHLAcOAd8AHjbGGGvtL4O2s8AxwO+AX+H0pZsJrDDGfA+Y\ni9PPzuA0QVsKHBfY2RhzAlAGbAXuBfYCFwFPG2OKrbXLW0nnDH8anwS6Ad8ClhljzrfW/qWVfcP5\ngGP9x7ge2Olfvz1om9OBYuBhoMb/+n8wxgy11u7yn9PxwCvAbuCnOPG7GudHxxnW2jfDXvdhYBtw\nF9C7jWkWEZH09gRwDzARWOhfF9w/eztOAf8OnDzmVpw8+R3/+p/j3EC+P7C9McYAz+B0MfsV8C5O\nV7wbcfL84rA0nIWTdz8I7AA2+dc/itPffhHwC2AEMB04yRhTYK2tD0rvMcD/+c9hMTAN+I0xZrW1\ndj1Ovlrq3/8n/jQBrG8hNmf7X3MR8AlwAk5+fDwwPmi75vqzh6+/xJ++1/3nBrAh6HmDc6OhEifO\nY4ArgWrgtqDtFuLEZRnw/4Bx/ue/AARXRFj/uqdw3odHgfdaOF+Rzmet1UMPPeL4AL6K07zuc6Ac\np4B5NtAlyrbdo6z7C/B+2LqNOH3qTw1adzbOgDq1wFFB67/r3/aMoHV/Bd4KTwNOwf7dGM6pe9hy\nJrAWeDFo3TB/ei6Nsn8D8OOg5R/40zi0mW33A8OD1n3Rv/77Qev+5N9uWNC6I3AK9f8IWneZf9+X\nAOP150MPPfTQQ4/Ee/jzinpgTAvb7AJWBy3PBurDtvkHsDbKvhuBFWHrLvH/Vhgftv4qf1pOC1rX\n4N/2uLBtC/3PTQlbH/iN8K2wNNQDE4LWDfDnpfcFrSsJ/x3RSuyi/ZaZ4j9GQdC63wCVUbaNFsca\nYFEz2zYAj4at/yOwLWh5lH+7R8K2u8+frv+JEpevev051EOP5h5qTi8SZ9bav+LceV6Ok4nMBF4A\nPgpvvm6D+rIZY/oYYw7HuQueZ4zJDjv0O9baN4KWX/f//Zu19qOw9QanWT/GmH44A+r8H9DXGHN4\n4AGsBI4xxuS2ck7B6TwM6Ae8inP3Ox5etNZuCnr9/wB7aDqnDJwfKH+y1m4O2u4TnDvphSZ0JGEL\n/Npam9Z9/EREpENqgfC8uSMuxKnh/m9Y3vwPnHz8zLDtX7LWhtcQX4jTB/9vYcd4y5/e8GO8Y619\nLbBgnW5+7+HPX9sj7DdCd//rB36LxON3gsWpMQ/2KnB4UN5/nn+7+WHb3e9P19fD1m/0/34TSUhq\nTi/SCay1FcCFxpguwJeAb+I0j/s/Y8xJ1tp3AYwxBTjNu08jdIRbC/TFuRMdsCXsNfY4LfHYGvby\nu/1/+/n/Ho2TYd2N0zQuIrk4A+5UNXc+xpjzcQbsOwnoHvRU+NQ6bonWZ30XTec0ECde/42y3Xqc\n8T+GENr8b5OL6RMRkfSThdNk2y3H4DTj3h7luUDeHGxTM8c4DKe7WCzH2BJlu+D8tc38lQV34tS+\nB79e4LdMPISfxy7/3344Ny8CrQM/CN7IWlttjPnM/3ywiL76IolEhXiRTmStPQRUABXGmPdxmpJN\nBu42xuThNHNfj1PA/xCnGf7XgRuIHIiynuiaWx8YAT5wnP+H0yIgmg+aWY8x5nScVgUvAd/DKex/\njtOP7uKgTaPWcvtrzduqtXNqj/0d2FdERNKYMeYonAJps/llO2QA/8H5DRAtfwu/oR0tH8vAubHw\n7WaOEX6DIB756//hVEbcB/wbpxCdgfObI/g3QHOt4TLb8ZqxnkesLfD0G0ESmgrxIt5Z7f8baLpe\nhDNI3DeCm8MbY85y+XUr/X8/t9b+vR37F+Nkbuf4b0oAYIy5Imy7wF3w8Lnfw+92Q8enrtkO7CNo\n8L4gI3HuvmsEehERcculOHnX8y4ecwMwylr7jw4e4yzgNevedHMx59H+LnZfAX5krZ0btP7oKJvv\nIvI3AkSfj76jvxM249xAOIagQeqMMTn+NGxuZj+RhKQ+8SJxZpz5ZKMJ9L8KjPQaKBA3fi+NMX2B\ny91Mj7V2O04t+tXGmCPCnzetz11fj5OZNt4ENMYMByaFvU4Nzmi54dPQXEtkZrzX/zdaZt4q68zN\nuxKYZIKmpjPGDMJpHfCqtba2PccWEREJZoz5Cs6o85U44664ZRkw2Bjz3Siv2cMY0yvKPtGO0QX4\ncZRjZPp/V7TVXpwa7Vjy6ECNeHgZ40Yi8/4NOGPznBiUxlzggmbS0K7fCH5/xjmHG8LW/8Cfruc6\ncGyRTqeaeJH4W+DPeP+EU2DvBhTgTAtTiTOlCziF0M+BZ40xv8IZLCcwRUpEYbuDrsUZ9OU/xphf\n+9MxCGcAvqOA0S3s+xxwE/CCMeYp/37fB97HGbgv2GPArf7XWI1ToD+GyOZtFf519xhjfo8ThxXW\n2rY0Z7sDZyaAcmPMwzg/JK7CifctYdt2pJmgiIikBwOcZ4wZifObeRBOLfPZOH2mi6y1dS6+3hM4\nvw1+aYw5E2dGm0ycFmWTcaazW9PSAay1r/h/Q9xqjDmJpt8Wx+IMejcDZ2rXtvgXTp76Q39N+0Gc\nQXR3RHn9GmPMK8At/vnXP/KneziRee/vgXk409uW4kzFdw1OTXn4AHgVwFeNMTcCH+MMPPcGMbLW\nrjXGPA5c5e+z/zLOFHOXAj5r7cuxHkskEagQLxJ/P8DJfL+GM91bN5wBWB4E5lpr9wBYa/9rjCnB\nGWzuZzhzqz6MM2/6wrBjxjq3atT11tr1xpiTcaZmuQw4HGcQnLdwBtZrlrX2H8aYaThzsc7H+SFz\nC86csOGF+Dk409Vc6I/Bn/1x2BaWntXGmDtwMu9zcO7gj8CJU6zn9I6/v/69/rRlAP8Evm2tXR1l\nXxERkZZYmvLEOuBTnD7rM4DF1tq9zewT67qQ9dZaa4yZhFNrfSlOjfQ+nBvt8wkdvLW5vBFr7feM\nMatx5mafi9PSbxPwW5wbA60eg9D8tdoYczXOnOqP4dxYOBNn9pxoLgYW4NzgNzh94b+GU/gOPu6n\nxpgLgAdwCvMbcfLvY4ksxN+EMwL93UBP4HEg5kK83xU4tf+X48T2E5z4zAnbrqW4iCQEoxmWRERE\nRERERJKD+sSLiIiIiIiIJAkV4kVERERERESShArxIiIiIiIiIklChXgRERERERGRJKFCvIiIiIiI\niEiSSJsp5owxh+NMXbUJOOBtakREROiBM3fyC9banR6nJSUorxcRkQTken6fNoV4nEz9d14nQkRE\nJMz/Ak95nYgUobxeREQSlWv5fToV4jcBPPnkk4wcOdLjpKSGG2+8kfnz53udjJSimLpL8XSfYuqe\n9evXc8kll4A/fxJXbALl9W7Sd959iqn7FFN3KZ7uikd+n06F+AMAI0eOZMyYMV6nJSX07dtXsXSZ\nYuouxdN9imlcqNm3e5TXu0zfefcppu5TTN2leMaNa/m9BraTdnvjjTe8TkLKUUzdpXi6TzEVSS/6\nzrtPMXWfYuouxTPxqRAv7TZixAivk5ByFFN3KZ7uU0xF0ou+8+5TTN2nmLpL8Ux8KsRLuw0cONDr\nJKQcxdRdiqf7FFOR9KLvvPsUU/cppu5SPBOfCvHSbhdffLHXSUg5iqm7FE/3KaYi6UXfefcppu5T\nTN2leCY+Y631Og2dwhgzBqioqKjQQA0i0mm2bNnCjh07vE6GeGTAgAEMHTo06nNr1qxh7NixAGOt\ntWs6NWEpSnm9iHhF+X36aimvh/jk9+k0Or247Omnn+aCCy7wOhkpRTF1l9fx3LJlCyNHjmTfvn2e\npUG81atXL9avX99i5i6SyLy+jqYixdR9XsdU+X168yKvVyFe2m3JkiXKhFymmLrL63ju2LGDffv2\nac7qNBWYF3bHjh0qxEvS8vo6mooUU/d5HVPl9+nLq7xehXhpt6VLl3qdhJSjmLorUeKpOatFJFkl\nynU0lSim7kuUmCq/l86ige1EREREREREkoQK8SIiIiIiIiJJQoV4ERERERERkSShQry029SpU71O\nQspRTN2leIqIdIyuo+5TTN2nmEq6USFe2m3ixIleJyHlKKbuUjzj68477yQjI4NPP/20xe2GDx/O\ntGnTOilVIuImXUfdp5i6TzGNH+X1iUmFeGm3iy++2OskpBzF1F2KZ3wZYzDGxLSdiCQnXUfdp5i6\nTzGNH+X1iUmFeBEREREREZEkoUK8iIiIiIiISJJQIV7arayszOskpBzF1F2KZ+fYvn07F110EX37\n9mXAgAHccMMNHDx4sMV9Nm7cyOTJkzn88MPp3bs348eP589//nPEdlu2bKGoqIisrCwGDRrETTfd\nxMqVK8nIyOCVV16J1ymJiJ+uo+5TTN2nmMaf8vrEokK8tNt9993ndRJSjmLqLsUz/qy1XHTRRdTV\n1fHTn/6Ur3/965SWlnL11Vc3u8+2bdsYP348L774Itdddx333HMPBw8epKioiOXLlzdut2/fPs48\n80z+/ve/c8MNN3DHHXewatUqfvjDH6rvnUgn0XXUfYqp+xTT+FJen3i6eJ0ASV6///3vvU5CylFM\n3ZVM8ayuhpISqKqC3Fzw+SAnJ/GPDZCfn4/P5wPge9/7HtnZ2fzyl7/k5ptv5sQTT4zY/t5772X7\n9u2UlZUxfvx4AK688kpGjRrFTTfdxKRJkwB45JFH2LRpE8uXL+f8888H4Oqrr+akk05yL/Ei0qJk\nuo4mC8XUfckS03jnx/E8vvL6xKKaeGm3Xr16eZ2ElKOYuiuZ4llSAuXlUFnp/C0uTo5jG2O49tpr\nQ9ZNnz4da23UJnMAf/nLXzj11FMbM3WA3r17c9VVV7Fp0ybeeecdAF544QWOOuqoxkwdoFu3bnz3\nu9917wREpEXJdB1NFoqp+5IlpvHMj+N5fOX1iUeFeBGRBFBV1fJyoh4b4Oijjw5Zzs/PJyMjg02b\nNkXdfvPmzRx33HER60eOHNn4fOBvfn5+q68nIiKSDOKdH8fz+MrrE4sK8SIiCSA3t+XlRD12NB3p\nw2atdTElIiIiiSPe+XFn5vfK672lQry028yZM71OQspRTN2VTPH0+aCgAPLynL/+bmcJf2yA999/\nP2T5gw8+oKGhgREjRkTdftiwYbz33nsR69evX48xhmHDhjVut2HDhlZfT0TiJ5muo8lCMXVfssQ0\n3vlxPI+vvD6xqBAv7TZ06FCvk5ByFFN3JVM8c3KgrAw2bHD+ujnQTTyPba3loYceCllXWlqKMYav\nfe1rUfc577zzeOONN3j99dcb1+3du5dHH32UESNGcPzxxwNwzjnn8NFHH/HMM880bnfgwAEee+wx\n905ARFqUTNfRZKGYui9ZYhrP/Diex1den3g0Or202/Tp071OQspRTN2leHaOjRs3MmnSJM4991xe\ne+01fve733HJJZdEHa0W4NZbb2XJkiWce+65zJgxg/79+7N48WI2b97cOPItOKPTPvjgg3zrW9/i\n+uuvJzc3l9/97nf07NkT6FhTPhGJja6j7lNM3aeYxp/y+sSimngREWm3jIwMli5dSvfu3bntttv4\ny1/+wowZM0LuoBtjQjLhnJwcVq1axcSJE3nwwQeZNWsWPXr04Nlnn6WoqKhxu969e/OPf/yDs846\ni9LSUn7yk59QWFjI7bffDkCPHj0670RFRETSlPL6xKOaeBERaZfZs2cze/ZsAJYtW9bsdpWVlRHr\nhg8fztKlS1t9jWHDhrFixYqQdT//+c8BGDx4cFuSKyIiIm2kvD4xqSZe2u3dd9/1OgkpRzF1l+KZ\n/A4cOBCx/Ktf/YpjjjmG3HgPsy8iuo7GgWLqPsU0uSmvbzsV4qXdbrnlFq+TkHIUU3cpnsmvuLiY\na665hkceeYSf/vSnnHzyyfz3v//lrrvu8jppImlB11H3KabuU0yTm/L6tlNzemm3Bx980OskpBzF\n1F2KZ/I799xzeeyxx3jqqaeor6/n+OOPZ+nSpVx44YVeJ00kLeg66j7F1H2KaXJTXt92KsRLuyXL\ndB7JRDF1l+KZ/GbMmMGMGTO8ToZI2tJ11H2KqfsU0+SmvL7t1JxeREREREREJEmoEC8iIiIiIiKS\nJBKiEG+MOd0Ys8IY85ExpsEYUxTDPl82xlQYYw4YY/5rjLmsM9IqTebNm+d1ElKOYuouxVMkcSiv\nT066jrpPMXWfYirpJiEK8UBv4F/A9wHb2sbGmOHAs8DfgC8BvwAeM8acHb8kSrh9+/Z5nYSUo5i6\nS/EUSSjK65OQrqPuU0zdp5hKukmIge2stc8DzwMYY0wMu3wPqLTWBuaTeM8YUwjcCLwYn1RKOE37\n4D7F1F2Kp0jiUF6fnHQddZ9i6j7FVNJNotTEt9VpwF/D1r0AjPcgLSIiIuI+5fUiIiJRJERNfDsc\nAVSHrasG+hhjultrDza3ox07ttk2fPuArDtbf3GDIatbFo9f8DiX/OkS9n3uNOHJMBkM6j2I2rpa\n+vfsT/8e/Xl/1/scOHSAbhnd2H9oP9b/6ganEsIGpWZx0WLOPeZcSpaVUFVbRW5WLo+c/whXLL+C\nf1f/G4BRg0axaNIirnn2Grbu2cqOfTvY+/leALK6ZfHsxc9yy4u38O/qf2OxdM3oSr+e/fjswGfU\nHaoDoFuXbhze83AG9xmMb4qPnN45rZ+0iIhI52p3Xn/In9db4L3crgwof4uBI06IY1JFREQ6T7LW\nxLebaeHRK8ZjWCw1dTUULytuLMADNNgGqmqrqKmrYfPuzbxV/Ra1dbUcajjEvkP7Qgrs1v8v2OUr\nLqdkWQnlH5ZTuauS8g/LmbBwAm98/AYH6w9ysP4gb378JhMWTqD8w3I2797cWIAHqK2r5azfntW4\nfV19HXs/38vWPVupraulrqGOuoY6autq2bx7M+UfllO8tLh9gQR27NjR7n0lOsXUXYpn4tq8eTMZ\nGRn89re/9TopkoK64OTrGcDIqs+pmqjK+/bSddR9iqn7FNPEpLw+fpK1EP8JMChs3SBgT0t35gHO\nA4rCHuOBp8M3/AB4KsoBngPWhK372L/t3rD1/wDKwtZ95t92e9j614GVUFVb1bSuDmofr4XNoZvu\nXbM3SoKB/4P6d+rbdB7Br7dmzRqKiooiLoSzZ8+OGPVzy5YtnHjiibz77rsh6xcsWMDMmTND1u3b\nt4+ioiLKykKDsWTJEqZOnRqRtClTpvD006EnuHLlSoqKIgcyvvbaa1m4cGHIuraeR1FRUcKcx7Rp\n01LiPIJ5eR7B8fTiPDZu3BhxbGkSW7fo1LBkyRJGjBjBSSedRFFREUVFRdx4441eJyvRuZbX/2Bz\nLePHj0/aa1mwzr6WKa93/zyU17t/HoGYJsJ5SKh0yusBJk6cGJLXFxUVceWVV7r+OsbaVgeI7VTG\nmAbgAmvtiha2+SnwNWvtl4LWPQUcZq09r5l9xgAVFcCYZo5rgYw725tydxQMKaD8w/LG5exu2dTU\n1YRsE21dQKbJpN7WR32uudcrmxZ+pyE2a9asYcyY5qIp7aGYusvreK5Zs4axY8dSUVGh9zWKuro6\nunbtmrIZfGvvf+B5YKy1Nvz2cErr7Lx+7dHZjHp/jzuJTzNeX0dTkWLqPq9jqvy+eeme1wdvg4v5\nfULUxBtjehtjvmSMOcm/Ks+/PMT//L3GmMeDdnnEv808Y8xxxpjvAxcCD7T2WraFR6yTUxgM2d2y\nefqip+nVtakRfobJ4MisI8nuls2wvsMYfcRosrpl0SWjC7269GrsBx84RvAyOH3ifVN8FAwpIK9f\nHgVDClh1xSpOPepUumd2p3tmd0458hRWXbGKgiEFDOs7jN5dezfun9Uti79f+veQ7Xt37c3gPoPJ\n6pZFt4xudMvoRla3LIb1HUbBkAJ8U3wxnnWkdL9IzZ8PxjQ9Fizo+DHTPaZuUzwTW7du3VI2U5dI\nnZnXH8LJ1xuA9bldyV25yt2TSSO6jrpPMXWfYpq4lNfHibXW8wfwPzh5bX3YY5H/+d8Afw/b5wyg\nAtgPvA98p5XXGAPYiooKK6lrypc/sa9SYD8gz1Yw2u6mt60j09aDrSPTfka2Hck6CzbiMXdu5PEe\neCByu5YeIsEqKipsKl93Zs+ebY0x9r///a/93//9X9u3b187cOBA+6Mf/chaa+2WLVvspEmTbJ8+\nfewRRxxh77///sZ9N23aZI0x9vHHH29cd9lll9msrCz70Ucf2UmTJtmsrCw7cOBAe/PNN9uGhoaI\nfe+//3770EMP2by8PNurVy87ceJEu3XrVmuttXPmzLGDBw+2PXv2tJMmTbK7du0KSfvy5cvt17/+\ndXvkkUfa7t272/z8fHv33Xfb+vr6xm3Wr19ve/bsaS+77LKQfV999VWbmZlpb7311hbj09r7H3ge\nGGMTIC+O90N5vYikqlTO75XXdyyvD97Gzfw+IWrirbUvW2szrLWZYY9p/uenWmu/ErbPK9basdba\nntbaY6y1T3iTeumoQI32IFNNhRlNvcmgwRgOGcMnPQ0NxmBN09/A/8/v8TiDTDVlppBNZji7TR8W\nvzSMQsrJp5IxvEUf9tKVejKArtTTlxpWNTM70e23R6676aZ4nrlIcgvcWZ8yZQoA8+bN47TTTmPu\n3Ln8/Oc/Z+LEiQwePJj77ruPY445hpkzZ0b0aww/XkNDA+eccw4DBw7k/vvv58tf/jIPPPAAjz76\naMT2Tz75JL/85S+ZMWMGN998My+//DKTJ0/mjjvuYOXKldx6661cffXVPPPMM9x8880h+y5evJjs\n7Gx+8IMfUFpaysknn8yPf/xjbrvttsZtvvCFL3D33XfzxBNP8OyzzwJO/8zLL7+c448/njlz5nQ4\nhulEeb2ISPJRXp+geb1bdwMS/YHuzrvusccei1j3/2Y21YT/k1PtPznFfmCG2dX9e9rd3bB1BlsP\n9nOMrQfb4H/s6YpdnfEF25Zq73qwr1LQpn0s2Dq6xFyT3sZDxyWm0n5ex7Mtd+Y/qfnEFiwssHm/\nyLMFCwtsdW21a+mI17HvvPNOa4yx3/ve9xrX1dfX2yFDhtjMzEz7s5/9rHH9Z599Znv16mWnTp1q\nrY1+d/7yyy+3GRkZdm5Ys5gxY8bYU045pXE5sO+gQYNsTU1N4/pZs2ZZY4wdPXp0yF32b3/727ZH\njx62rq6ucd2BAwcizueaa66xWVlZIds1NDTY008/3ebm5tqdO3faa6+91nbr1s2uWbOm1fioJl55\nfSrw+jqaihRT93kd01jz+3jm9fE6vvL6lqV1TbwktvvvXEfZoD5U9c5orAm3xlBx5ZVYY/i0i+H1\nIw2vH5nJ9x84orEmfBxvMI43ybebGfvpfvrUQVfrDMTQBefDF5jeL/tzOKmhbaN7GiCXqla3C7eP\nnm3eJxalpR0/xpo1aTW2VdwlUzzDp5fsyPSPnXlsYwxXXHFF43JGRgYnn3wy1tqQEZj79u3Lcccd\nR2VlZavHvPrqq0OWTz/99Kj7XXTRRWRlZTUujxs3DoDvfOc7ZGRkhKyvq6vjo48+alzXvXv3xv/X\n1tayc+dOCgsL2bdvX8hIw8YYFi9eTG1tLV/72td45JFHmDVrFqNHj271PERSQTJdR5OFYuq+ZIlp\nPPPjeB5feX3iUSFewOeDoMK5DWu+ftNdX6RwWw25+2xjodsAD/v/9q+HcVUwrqqBnrEPjB+hrUNe\nWKCK3Kgkx3QRAAAgAElEQVTP7ac7FYxmD735nEwagM/JZDfZjCf6AEdz50aua6lgXloKd93VtDxj\nRuhAd4FHWzz00ENt20FalEzxDJleMspyoh4bYOjQoSHLffv2pUePHvTv3z9i/a5du1o8Vo8ePTj8\n8MND1vXr1y/qfkOGDIk4PsDgwYOjrg8+xjvvvMM3v/lNDjvsMPr06cPAgQP5zne+A8Du3btD9s/L\ny2P27Nm8+eabnHDCCdxxxx0tnoNIKkmm62iyUEzdlywxjXd+HM/jK69PLF28ToB0snXrYMIEqKkh\neHLBQMGcsHWdqbYrvP/5SZzEvzFYGoAdPSDngJMWG5QmCxR1X8wbB7+Gj2IGs5V+fMou+rOVwbw7\n18eVs3KaDl5dTUZJCX2rqngn92rnxoW1UFICVVWQmwtFj8CYS+Ff/3Key8hg+pFHMr1gmLN9Tk5E\nmjXYprglNyuXyl2VIcvJcGyAzMzMmNYBgSbPbTpWW7dt7bV3797NGWecwWGHHcZPfvIT8vLy6NGj\nBxUVFdx66600NDRE7PvCCy9gjOHjjz9m586d5ES5HoiIiLQk3vlxPI+vvD6xqBCfyqqrnULq1q3w\n6afQv7/z/3qnujye5c/9GYa1DacAlgFmG5/138YxNfvp+TlkWmjAkIFtTENtV5j8had5Ye2kxmNk\nAEcEHdOE/f/ZxqWmwTP6AsOBwvAElZRAebnz/8pKKPY3Lwpe57+50aihwYnX1q3O9i0M0iHSUb4p\nPoqXFlNVW0VuVm6Hpn/szGMno5deeoldu3axfPlyCgoKGtdv2LAh6vaPPPIIf/vb35g7dy733HMP\nV199NX/60586K7kiIpIi4p0fK79vkup5vQrxyc7ncwqo4QYMgIMHQwulwf9vp2j31XZlwvs5ABkM\n2JtBVY+erJ6+ihvuOKHZ44T348gGXuhw6lpQVdXyMsD+/bHvL+KynN45lE2Lz42ieB47GWVmZmKt\nDbkLX1dXx8MPPxyx7caNG7nlllu48MILufXWW+nfvz/XXHMNTz75JJdccklnJltERJJcvPNj5fdN\nUj2vV5/4ZPLSS9Cli9OGu0sXuPXW6AV4gB072lxot2GPhrBlMjMxL7+MsRZjLZO+8Q2MtfQ/ZBn3\nsWXcx/Xk7/6cwuo9LRbgPZGbG7kcvq5nCwPehW/rF+hHn0M1r1LIRoazmyz204N99OCfjGPa+dti\nTmZRUVHM20rrFE8JCG7aN2HCBPr168ell17K/PnzmT9/PuPHj2+cRifYtGnT6NWrV2Omf9VVV3H2\n2Wdz/fXX88knn3Ra+kW8ouuo+xRT9ymmAumV16sQnwz8A89x5pmNTeGpr4d589p3PH8fkuAC+o6e\nsKNyXWMB3VhLRtD/jbVw6BCccUbjYa677roOnVan8vmgoADy8py/Pl/kulWrYPTopo7uGRkweDCc\neirU1cGwYdCnDwwfDoWFsG4ds/5ciM3Lpzr7GAopZzib6cNeenCQnhxkHG8w7bnYRwZNqpgmAcXT\nO9EyyfD10baJZb/Acnv379+/P8899xxHHnkkP/rRj3jggQc455xzuO+++0L2WbBgAa+88gq/+tWv\nQgbgWbhwIQ0NDXz3u9+N+loiqUTXUfcppu5TTL2hvN47prWBB1KFMWYMUFFRUcGYMWO8Tk7zvvUt\nWLrUveNlZzt94QN94gcPbnaQNmlGYWFT3/lg2dkxtXbYQB75Nnr/G0lta9asYezYsST8dUfiorX3\nP/A8MNZamxzzIyW4pMnrRSSlKL9PX7G89/HI71UTn0iqq90rwA8Y4NQuf/ABbNoEe/Y4f8vKUroA\nX11bTeGiQvJL8ylcVMi2vbE3ZW9Wc/3hW+pDH7x7lGnw5swJnYbunns6kkAREREREUkXKsR7aerU\n0JLcEUe0vk9AZibcdlvousWLnanRrIXt21O+wB5NybISyj8sp3JXJeUfllO8NPam7M1qpj98RB/6\n7Gw+O2wYe+jNAbqzn+68zqkU44uYO3727NBdb7+948kUEREREZHUp0K8lxYvbt9+Tz/t9E+/556m\nQru1cNllriav9WQ83amvF4uq2qoWl9sl0Hd+2DCnCf2wYU196IP71H/wAYft2kQfW0sPe4BeHOA0\nXmc7sd9IaUtMzz2XiJsD0R7pLBE/oyIiyUTXUfcppu5TTCXdqBCfLIJr2SdNanXzzrBkyRKvkxAh\nNyu3xeV2yclxWjWEd0s44QTn74YNrrV6CMT0ggtaL5y/ENc5+VJDIn5GRUSSia6j7lNM3aeYSrpR\nIb6z3HJLaAls1qyWty8ocPrIe1TLHoulbg7A5xLfFB8FQwrI65dHwZACfFN8zW4bl/7zHTBqFCxb\nthRjYPlyT5OSMhLxMyoikkx0HXWfYuo+xVTSjQrxnWHqVPjZz0LX3XsvXHll9O3Hjk3L/uxuyOmd\nQ9m0MjbM2MAfL/ojxUuLmy2kx6X/fJDS0qB0+eeR/4B8XqWQgUTeMPjPf1x9+Ubnnx+f44qIiIiI\nSOfr4nUCUtq6dTBhQvPTkP36185DXFNdW03JshKqaqvYvnc7NXVO7Ct3VVK8tJiyaWWN28al/3yQ\n6dOdBwCFJY3T1OVTybaCYkx5WfM7u+i55zrlZUREREREpBOoJj4efD6nyfwXvxjTPOLinuDa9UAB\nPiC8kB5L/3nXmtyHT1PX3LR17VBaGjq+oYiIiIiIpC4V4uOhpKT1bcKnh0tCU6dO9ToJEVqqTQ8v\npMfSf961Jvfh09Rt3creLn3YyPDG5vWjRgGExnTu3NACerRHY21/C4yBBQval/RkloifURGRZKLr\nqPsUU/cpppJu1JzebV27tr7NlVc608MluYkTJ3qdhAi5WblU7qpsXM7uls3A3gPJzcqNKKQH+s+3\nxLUm9z4fFBfD6tVw8CDU1dGLOoZTw3A2s62gGMrKWLJkIhdf3L6XCBgwAHbsiFw/Y0ZsBf5Ukoif\nURGRZKLrqPsUU/cpppJuVIh3y0svwVe/CvX10Z/PznbmFT/hhE5NVjxd3NHSZhz4pvgoXlpMVW1V\nY8E9p3f7BwgMvynQ7inrAtPU5edDZWXk8/7m9RExra52WnZUVTm1+T5fqwMeRivAp6tE/IyKiCQT\nXUfdp5i6TzGVdKNCvFtaKsA//XTCzO2e6mKpXW+L4JsCA3oOoK6+jvzS/PbfIMjNjV6Iz80NLbAP\nGOC0lV+71qm5B2e/YqfGvr2MgeOPh7ffbvchRERERETEQ+oT31GB+d+bK8CDCvBJLHjKuq6ZXXnz\n4zc71j/e54OCAhg2zGmdMWyYs+zzOQX48nKnsP7GG/Dmm00F+AAXBsR7550OH0LEE4sXLyYjI4Mt\nW7Z4nRQRERGJA+X1sVEhvqPC538PF0sf+SRV1oEa4WTkSv/4QLP6TZtgzx7nb1mZs76qilYjmpvr\nTF3Ypw906eI8hgyBwkLY5oycH8u4is254ALnnlRzj2QbyiHdPqOpzhiDMcbrZIikFV1H3aeYuk8x\nTR3K62OjQnxH3HJL9PWZmfDyy05z6Lq6zk1TJ7rvvvu8TkKnimVKuo69QC6tRvRf/4Jx45ypC+vr\nncfWrU4NfrHTMuAPf2h9yrnmCujLl7f88rff3uaz8lS6fUZT3aWXXsr+/fsZOnSo10kRSRu6jrpP\nMXWfYpo6lNfHRoX4jmiuFv7QITjjjM5Niwd+//vfe52EThXLlHQdewEfvz/tNMjLg1NPhVNOge7d\nQ7fZuxf27Yu+fzNN7Y8/PraXj7WAHij0J8OUden2GU11xhi6devmdTJE0oquo+5TTN2nmKYO5fWx\nUSG+Pc480ynFRJMC87/HqlevXl4noVMF948vm1bWoVHvo79ADr1WrYING+D1151+8UcdFfv+27c3\nNqkPFq9B7GbMiG27+fNDa/w7s/Cfbp9Rr3z88cdcccUVHHXUUfTo0YO8vDy+//3vc+jQIQA2btzI\n5MmTOfzww+nduzfjx4/nz3/+c8RxFixYwIknnkjv3r3p378/p5xySsgPs2j95IYPH05RURHl5eWM\nGzeOnj17kp+fzxNPPBFx/N27d3PDDTcwdOhQevTowTHHHMN9992Hba7JiojoOhoHiqn7FNP4U16f\nWFSIb4+XXmr+uWTrNCyuq66tpnBRIfml+RQuKmTb3siCdcxyozTZ793bGRQvMzN0fU1NY5P69mpr\nf/rwAvo3vxnZTP+mm0L3ibXwn3aqq52xDfLzQ8Y4SPRjV1VVccopp7Bs2TIuvvhiFixYwKWXXsor\nr7zCvn372LZtG+PHj+fFF1/kuuuu45577uHgwYMUFRWxPKj/xq9//Wuuv/56TjzxRH7xi18wZ84c\nRo8ezeuvv964TbR+csYY3n//fSZPnszEiRN54IEH6N+/P1OnTmX9+vWN2+3fv58zzjiDp556issv\nv5wFCxZQWFjIbbfdxg9+8APX4iEiItKseOb1cTy+8voEZK1NiwcwBrAVFRW2w0K7HDc9brut48eW\npFewsMByJ42PgoUF7T9YdbW1o0dba4zzGcvKsnbduqbn8/JCP4N5eXbbhv/Yfx+dbTcf3sX+++hs\nu61ynS0tbf5jG3jMndtyUlrbvy2PdFFRUWFjvu4UFIQGqaADn5tOPPall15qu3TpYtesWRP1+Rtu\nuMFmZGTY1157rXFdbW2tzcvLs3l5eY3rLrjgAvvFL36xxddavHixzcjIsJs3b25cN3z4cJuRkWHL\ny8sb123fvt326NHDzpw5s3Hd3XffbbOzs+2GDRtCjnnbbbfZrl272q1bt8Z2wm3Q2vsfeB4YYxMg\nn0yFh6t5vYhIjGLO7+OZ18fx+MrrmxfLex+P/F418W1RUNB8M3pr064WfubMmV4nISHFOop9tBr7\niJjm5MCaNdDQ4HzGamrghBMan67LGRCyeV3OAKrOmcCoD2oYuvMQoz6ooWrieKZPb71oPWtWy+dV\nWtryclt01lclqT6j4WMauDCdYLyPba1l+fLlFBUVMXr06Kjb/OUvf+HUU09l/Pjxjet69+7NVVdd\nxaZNm3jHP+fhYYcdxtatW1m9enWb03H88cczYcKExuUBAwZw3HHHUVlZ2bjuD3/4A6effjp9+/Zl\n586djY+zzjqLQ4cO8corr7T5dUXSQVJdR5OEYuq+pIlpPPP6OB1feX1iUiG+LV57Lfr6r361c9OR\nIDRqZHSxjmJfsqyE8g/LQ+adb2tMi6dYyobAhn5QNsRZPmzX/pBtwpfbK/xGwPTpQO9qmFoIM/Kd\nv71ja7bVWaPcJ9VnNLzrRLSuFAl27O3bt7Nnzx5OCLqxFG7z5s0cd9xxEetHjhzZ+DzAD3/4Q7Ky\nsjj11FM59thjue6663ituWtumGjvc79+/di1a1fj8vvvv8/zzz/PwIEDQx5nn302xhi2ud2kUSRF\nJNV1NEkopu5LmpjGM6+P0/GV1yemLl4nIOnZ1BokoS2mT5/udRISkm+Kj+KlxXy450N27d/F1j1b\nKVxUiG+KL2QwvGg19tNnODGtrq2mZFkJVbVV5GblRuwbsD5jJ6df0bScl7GTz/r1ZOjOmsZ1e7K7\ns/aYPhy2az+f9etJ7spVDBzR/IW4LUbcVsLGQ+XOQv9KDv9+MTt/ljhztSbVZ9Tnc8Y0qKpyMl2f\ni7MfxPPYLvnCF77Ae++9x7PPPsvzzz+Pz+fj4YcfZvbs2cyePbvFfTPDx4fws0HX54aGBs4++2x+\n+MMfhqwPOPbYYzt2AiIpKqmuo0lCMXVf0sQ03vlxguf3yuvdo0J8LJprQi8SRWAU+8JFhWzZvYWa\nuho2795M8dJiyqY1FXBzs3Kp3FUZshwQqKUHqNxVGbFvS8fIXbmCtRPHNxbaMw7VM+oDp1A/dGcN\nayeOZ+D7e1o+iepqZ5S74EwgJ/ImgsmugqYboHQ3/+TfR/fhhEP9yTxqMPh8mEEuj+KfqnJyoCxO\nN0DidOyBAwfSp08f1q1b1+w2w4YN47333otYHxiIZtiwYY3revbsyeTJk5k8eTKHDh3im9/8JnPn\nzuW2227r8HQz+fn51NbWcuaZZ3boOCIiIu0Wz7w+TsdXXp+Y1Jy+I9JgLnhpv9b6xrc073ys/eqj\nHWPgiBMY9f4ehu74nFHv76FPTV3IPoft2g/V1dSNH8dHA3uwOq8H5//81NBR9EtKoLwcKiudv8Gj\n3geNfPqnBdsZWNv01NIlzg2DzE2bG/drbsT79k475+WUdRLKGMMFF1zAM888w5o1a6Juc9555/HG\nG2+EjDy7d+9eHn30UUaMGMHxxx8PwKeffhqyX5cuXRg5ciTWWj7//PMOp/Wiiy5i1apVrFy5MuK5\n3bt3U19f3+HXEBERSTXK6xOTauLbK42b0Qe8++67fOELX/A6GQmrpZp2aKqxDxaIaWv7tnSMcOHN\n6/vvtfCNb9DtzTc5CjhqB9z6wJsU9wmq7W9pYJRAAR8YBfz16WzGXrqfQw2HyK0lYr8//LGaqje/\nwWFb1gKwli/xDZ5hO0019DNm+PvY+82fHzk1XXPC9w2mz2j83XPPPbz44oucccYZXHXVVYwcOZKP\nP/6YP/zhD5SXl3PrrbeyZMkSzj33XGbMmEH//v1ZvHgxmzdvxhfUzG/ixIkcccQRFBQUMGjQIN55\n5x0eeughzj//fHr37t3hdM6cOZMVK1Zw/vnnc/nllzN27Fj27t3L2rVr8fl8bNq0if79+3f4dURS\nja6j7lNM3aeYxpfy+sSjmnhpt1tuucXrJCS0lmramxOIaXv2bU7uylXU9mjqR5R1oB7Wrg3dpjas\ntr+lgVHCCvijGgYy7qhxzlNZ4S+eCyUl5G55k54cpCcHGccb+Iicz37OHBhkqikzhRTdlM+rFDKQ\njg1Aos9o/B155JG8/vrrTJ48maeeeorrr7+eJ598kq985Sv06tWLnJwcVq1axcSJE3nwwQeZNWsW\nPXr04Nlnn6WoqKjxONdccw179+5l/vz5XHfddaxYsYIbbriBJ554osXXjzafbPBzAT179uSVV17h\nlltu4eWXX+aGG25g3rx5bNiwgTlz5tC3b193AiKSYnQddZ9i6j7FNL6U1ycgt+aqS/QH7Z079q67\nos/IJSHzN4o74hbT8Pnku3cPWX51SNh89tXVztyieXnO3+rqpueizEFaXVttCxYW2FN+Msz+++hs\ne2j4sKb9wl8b7MbsTPtqTrb9wAyzr1JgB1JtwdpXCT32qxR0aN55rz+jbZonXlKO5olPorxemuX1\ndTQVKabu8zqmyu/Tl1fzxKs5fWuijZRo1ZQekmg6jyQSt5jm5jr92wO+9CXqMmD7B/+mKht+OmNU\naG1/SwOjRBn5NKRZf/j0ceGvDfQ7WM/wmhqghnw2868RxRy1sYxcQmv5x/E6r1JIMb6Q5vfBSkvh\nggtg+fJozw7l61+HZ5+NfioiItIy5fXuU0zdp5hKulEhXiQdRCl4d8vJcfrEA20q47Z15FOfj09O\nOZ++W9ZC5kHWHgGDaqFv0Hh7Rxqn8F5FLvk0Ffi7cohCyvFRzOk4r1la6vSBnzPHucc2Y0bLL//c\nc7EnVUREREQk0akQL5IO4j2lSSuvfcTmNwAoXFRI+YflvLoQhu8O2iY3l7lXQPHtPnwUM47X6cqh\nxqcL86qwG0IP28p0oiIiIiIiKUkD27Vm7tyWl9PYvHnzvE5Cykn1mAYG7Lvp6mGsPTqb+uHDoKAA\nfD5mzYJtNodCW0bXgnGhO4YPtBez1I6niEi8pXq+5AXF1H2KqaQb1cQ356WX4Ktfhfp6yMyEv/9d\n88KH2bdvn9dJSDmpHtMW+84HC2r+X5czgOIL61hfmk9uVi6+KU4f/Njs4+tf72iqRUTSV6rnS15Q\nTN2nmEq6UU18cwIFeHD+fuUr3qYnAd11111eJyHlpHNMq2urKVxUSH5pPt/43fnU1X8OwLs71vPG\nR29SuauS8g/LKV7qTE8XrZFM5Lj1d2lQOxGRDkjnfCleFFP3KaaSblQT35xAAb65ZRFxVcmyEso/\nLAfg8YWVdPvQWT8K8C2F069wlgPz2c+a5TxERERERNKJauKbk5nZ8rKIuCpQOAcYvCf0ueDl3Kym\n/vHBtfeFiwrZtndbvJMpIiIiIuIp1cQ35+9/d5rQB/eJlxA7duxgwIABXicjpaRzTHOzcqnc5Uwv\n129/6HMDDmaS129YY5/4gODa+8pdlRQvLW7qc48TzyeeGMBNNzUdKzBFXWdav359576gJAS975IK\n0jlfihfF1H2JElNd99OPV++5CvHNOeMMOHSo9e3S2LRp01ixYoXXyUgp6RxT3xQfxUuLqaqtYl/2\nVvrubJpIPmvQYDbM2BCxT3DtfbTladOm8cwzofGcMaPzCvEDBgygV69eXHLJJZ3zgpJwevXqlRA/\nLEXaK53zpXhRTN3ndUyV36c3L/J6FeLDzZ+P59V2SeLOO+/0OgkpJ51jGjJy/bJCKC9venLw4Kj7\nBNfeB5aD3XnnnTzzTOR+xjh/g7/ec+aEzj0/d27H+9wPHTqU9evXs2PHjo4dKIGsX7+ekSNHep2M\npDFgwACGDh3qdTLEA9W11ZQsK6GqtqodM2skjnTOl+JFMXWf1zFNtfxeeX3beJHXG2ttp76gV4wx\nY4CKiooKxowZ09KGkevSJEYiCWPbtsYp5hgwwPkO7tzpzBfv80GO80N4295tjbX3zf1IjvaVjpW+\n+hJPa9asYezYsQBjrbVrvE5PKog5r+8EhYsKG7v7ABQMKQjp7iMiIukhHvm9auJFJPHk5ECZ/8du\nYVCtfGWlU7gvK4N168iZMIGy/fuhZ09YtQKi1HKVljpN6EVEOlNr3X1ERETaS6PTi0hiq6qKvjxh\nAtTUOGNX1NTA+PFRd58+vWneeBGRzhLevSd8WUREpL1UiA9XWtrysjRauHCh10lIOYppFLm50Zf3\nhw1hH75MUzwDU9EdPicfphZC79anops7t12pTXn6jIrExjfFR8GQAvL65VEwpCBkZo1kou+8+xRT\n9ymm7lI8E58K8eGCq+2s1aB2LVizRl043aaYRuHzQUEB5OU5f33+H8I9e4ZuF75MUzwDU9HtbKiE\nYeUUlBZHvV8X/NXv6KB2qUqfUZHYBAbr3DBjA2XTypJyUDvQdz4eFFP3KabuUjwTnwa2E5Hk9Pbb\ncNppUFvrjF530knw/PONg94Fyy/NDxnFPq9fXtQp68JpsgqJJw1s5z7l9SIikmjikd+rJj6YMZEP\nEUlMJ5wAX/qS839r4a23nEHvomhv39TgAjxogDwRibSueh197u1D17u70ufePry97W2vkyQiIilO\nhXgRSV7NDXoXJlX6popI4pmwaAI1dTUcajhETV0N4xdGH2RTRETELZpiTkSSV26uM+1c8HJAdTWU\nlEBVFTm5uZQFzS8PzmB3JctKWpxjPppAAx01rRcRgP2H9re4LCIi4jbVxEu7FRUVeZ2ElKOYtlFz\ng94BlJRQVF7uFPLLyyOa2gcGu6vcVUn5h+UUL41sit/S5BTp2rRen1GRJtW11YSPLdSzS+Qgm8lM\n33n3KabuU0zdpXgmPhXipd2uu+46r5OQchTTNsrJgbIy2LDB+Rs8qF1VFSHRDGtqX1Xb8jJojvlo\n9BkVaVKyrIR6W9+4nGkyWXXFKg9T5L60+M5XV0NhIeTnO3+3tT4NaUekRUw7mWLqLsUz8akQHyx4\nfin9cm/VxIkTvU5CylFMXZSbS0g0t28P+YHW3sHu0p0+oyJNwm/+DTtsGCfknOBRauIjLb7zJSVO\ni61mWm61aN066NMHunZ1/r4dNrBhlBsEaRHTTqaYukvxTHwqxItIagpuap+dDTU1IT/Q2jrYXbR5\n5UUkvelmYIoIHxR19erYa+UnTHDyl0OHnL/jwwY27MgNAhGRZmhgOxFJTYGm9uD8GKupaXquqoqc\n3jmUTSuLvm/QoHjk5oLPx/TpORrITkRC+Kb4KF5aHDJApiSh8EFSDx50lisrnUJ3WTN5BcD+sIEM\na2qcPMefd8Q6i4qISFuoJl7a7emnn/Y6CSlHMXVXYzxzw2rHcnNb7gOpmpNm6TMq0iRwM3DDjA2U\nTSuLaYaLZJMW3/ngllvdu4c+11qhu2eUgQyD844o+U9axLSTKabuUjwTnwrx0m5LlizxOgkpRzF1\nV2M8o41i31JBvZ01J/PnO1PQBR4LFrh0IglEn1GR9JIW3/ngQVJPPjn0ufBCeLhVq5wuW126NM1B\nGlBVFTX/SYuYdjLF1F2KZ+Iz4VOjpCpjzBigoqKigjFjxoQ+uW6d06dp/37njuqqVXBCag1MIyJh\n8vNDm0/m5Tk/4MCpmS8vb3que3fnh13wXPNRmtybQZG1cGlyiZV2WLNmDWPHjgUYa61d43V6UkFL\nef0G3yKGXXgFmRbqDWz502LyJl3W6Wmsrq2mZFlJSBP8VKzBT1rbtjk3dYOu7SEzn7QkPO8oKIje\nFD9K/hHza4hI0olHfq+aeGh9UBIRST3RmtgHBGpOAs0qDx6MrK1Xk3uRpDLswivoYsEAXSwM/ebl\nnqSjZFkJ5R+WU7mrkvIPyyleqmtHQmlp6tLWRGv1FU20/KOTp7kTkeSmge0gclCS8GURST0+X0Rt\nS0gN2bRcXv7oCDI3bW7aJ7hZvQYrEkkqmbbl5c4SPi1d+LIkseABVVsSLf8IFOwhtgH1RCStqSYe\nIgcliTZIiYiklii1LeE1ZG93+TRkl7UZ28kvzadwUSF1OQNCj5ebq2noRBJYvWl5ubNoWjqJ2hJM\nN4ZFpA1UiIfQQUmys51ladXUqVO9TkLKUUzd1dZ4hteIXXl5/8amkWuPzuarF9Q0NYGdYiOaTU6/\nqBpbUIjNy8cWFDJ9Suo1h9RnVJLVlj8t5pABCxzy94n3gm+Kj4IhBeT1y6NgSAG+KU4roMJFhY03\nCbftTZxrR8J851OoufnUIUMim9231MVLWpUwn9MUoXgmPjWnB2cQuz17vE5F0pk4caLXSUg5iqm7\n2uFx10IAACAASURBVBrP3KxcKnc1DXbXLXdwY3PGb5bms31X01zz6zN2wh9faxqcqLgY6urgzTed\nDVK0OaQ+o5Ks8iZdBg3OQHZdgDyP0hGYli5Y4aJCyj90mlJX7qqkeGlxxDZeSZjvfAo1N59YVAQX\nXxy6MkoXL4ldwnxOU4TimfhUEy/tdnF4BiQdppi6q63xjFZDFhC1CWz44ERr14Ye0N8ccs6c0Knn\n7rmnfeeTCPQZFXFfIveTT5jvfAo1N48a044MqCeJ8zlNEYpn4lMhXkTEL1BDtmHGBsqmlYVM+xS1\ngN/aj0h/c8jZs0NX33672ykXkY7wujm7+snHQM3NRUQaqTm9iEgMojWBJTc3dK75L30JunZVc0iR\nJBMY1BK8ac7um+KjeGlxyNzxEibdmptrLnkRaYFq4qXdypK0L1oiU0zdFfd4hs8J/Mwz8Mc/No00\nXFyc1IMvRaPPqKQir5uzt9QKyGsJ851PoebmMcU02lzybovXYIEeDEKYMJ/TFKF4Jj4V4qXd7rvv\nPq+TkHIUU3fFPZ7RflRG+eE1d27obuHLyUSfUUlFas7evLh+5+NU2PO6e0RrYoppZ4wB0NYbBbG+\nX51xAyKM8iZ3KZ6JL2EK8caYa40xG40x+40x/zTGnNLK9v9rjPmXMWavMeZjY8xCY0z/zkqvwO9/\n/3uvk5ByFFN3dVY8g38wfvTe6tAnq6qYNQusbXrMmtUpyYoLfUalIxI1r29pUMt0F9fvfJwKe4Hu\nEY1Tgi6NfyGyLWKKqZtjADRX+G7rjYJY36+O3oBox80d5U3uUjwTX0IU4o0xU4D7gdnAaODfwAvG\nmAHNbF8APA78GjgeuBA4FXi0UxIsAPTq1cvrJKQcxdRdnRXP4B+MG3seDH1ywICIHyPz54eOVr9g\nQack0xX6jEp7JXJen8jN2b0W1+98nGqbve4e0ZqYYhreXasjYwA0V/hu642CWN+vjt6AaMfNHeVN\n7lI8E19CFOKBG4FfWWt/a619F7gG2AdMa2b704CN1tqHrLWbrbWvAb/CydxFRDpV8A/E4imwekT3\nph9e1ob+GDn6aO69aRs5VPMqhXxAPqNndE6fQRGPKa9PdW2tQY3TiPOtdY9I9Ob2QMtjALQ1zs0V\nvtt6oyDW9yuW47Z0Dik0naBIvHheiDfGdAXGAn8LrLPWWuCvwPhmdlsFDDHGfM1/jEHAZOC5Nr14\nfn5oddhxx7XjDEQk3QX/QNyeBTfccXLTD6+dO0M3rqnBRzF/pIRCysmnkkI6p8+giFc8zeuB+avm\nY+4yjY8F/3Sav6yrXkefe/vQ9e6u9Lm3D29ve7txn6Qo6CWattagulnbHHzYVrpHJHpz+1bFGOfA\nZ3i1/Sj0iUDhu62DBcb6fsVy3JbOQdMJirTK80I8MADIBKrD1lcDR0TbwX83/hJgqTGmDqgCdgHX\ntemVg6eGAvjvf9u0e7qbOXOm10lIOYqpuzorni3+YIzy4yOXKnKJUtPgwYi+baXPqLSTd3k9cNPK\nm0KWZ7wwA4AJiyZQU1fDoYZD1NTVMH5h0/2EpC/ouaRN3/m21qDGacT51rpHeN3cvsPX0RjjHPgM\nn1dykLIh8NGA7h27WeLm+9XSObTj5o7yJncpnokvEQrxbWaMOR74BXAnMAY4BxiB08yuReeddx5F\nRUXOAyjCqQJ4Omy7lStXUlRUFLH/tddey8KFC0PWrVmzhqKiInbs2BGyfvbs2cybNy9k3ZYtWygq\nKuLdd98NWb9gwYKIL8y+ffsoKiqKmOZhyZIlTJ06NSJtU6ZM4emnQ88knufx/PPPp8R5JNL7MXTo\n0JQ4j2BenkdwPON5Hr958DchPxgP7DzQdB4+H2RnO+cBzAR6jnCK8eC0JS4CHtv/IWsLj2msmVhS\nXs7UL34xIm1evx9Dhw5N+89Ve85jyZIljBgxgpNOOqkxD7rxxhsjjidNXMvri4rgKeAxYH3odnvf\n3es857f/0H7A+cy99+J7IdtuWr+p0z9z37rkWxGtARI6r9+1i5CzyM1NyGtAY+up14GVoa2pOuP9\n6HBe37dvyPoF1kZ9P96a/xZsdlqInX4FnPGjo1hy7bVM/eEPI9LW6e/Hrl2EfKpyc5s+V0E3C/at\nXEnRlVdGvyZ/61uNN76H/vGPsK3zvx9e5y3xOg/l9e0/j4kTJ4bk9UVFRVx55ZUR23WUcVqzecff\nxG4fUGKtXRG0fjHQ11r7zSj7/BboYa29KGhdAfAqkGutDb/TjzFmDFBRUVHBmDFjAisjE+RxPEQk\nBW3b5jQVrKpyauYDtQrFxXz03mo29jxI8RRYtRDydwXtl5fn1HhISlqzZg1jx44FGGutXeN1euLJ\n07weMHdF5vd2tqXPvX2oqatpXJfdLZs9t+0BoHBRIeUfljc+VzCkgLJpnTt3ciKkoU2iXes6UFtb\nXVtNybISqmqryM3KxTfF58qgg9v2bqN4aXGbj5sw70eMcU6Y9EbjxmelsNC58R1QUOAU/gOqq51m\n+y59HkXaKx75fRc3DtIR1trPjTEVwFnACgBjjPEvlzazWy+gLmxdA2CBKCXzZhx7bGgT+mOPjXlX\nEZGYBWoVwpWVcUZpPpW7nK49VVlhhXj1A5QU4WleD5SeU9rYhD6wDLDqilWMXzie/Yf207NLT1Zd\nsapxG98UX0RBr7N53ey7zZq71rVToDk4QOWuSoqXFrtSCA00t2+rhHk/YoxzInyGm+XGZ6W1bgWB\nfvfgdKEtLnb18yniJc8L8X4PAIv9GfwbOCPY9gIWAxhj7gWOtNZe5t/+GeBRY8w1wAvAkcB84HVr\n7Scxv+p777W+jYhIHOVm5TYW4ounwF+fzmZUw8DQGvsg8+fDTUHde0tLYfr0zkqtSId4k9cD00+b\nzvTTIr8oJ+Sc0FjzHq69BT03BV8fAsvpJGEKzX5evx9tbZmQCJ/huMrNDR3fKvzGd7RCvoe183Pm\nwOzZTctz58KsWZ3y0pKCEqJPvLV2GXAzMAd4CxgFnGOt3e7f5AhgSND2jwM3AdcC/wGW4vR0K+nE\nZKe98D4n0nGKqbuSIZ7Bg+IdO7KAI/71QYuDBt0UOj4XM2ZEbBJXyRBTSUzJlNcnysj0rY2y3hm8\n/M63NlVcZ3Pr/WhvTDXYYpigAfDeHT068sZ3tFHu2zEHvVuCC/AAt9/eaS/dZsrrE5/nfeI7S3P9\n5KT9ioqKWLFiResbSswUU3elYjy9HsojFWPqlXTqE99Z3MrrE7ovcSfz8jvf3r7rXom1pry9Mc0P\n6n4FkNcvjw0zNG4KNBPTaP3ux48Prb0PHnsmzrX0XubfbW3Fp7zeXSnZJ16S14MPPuh1ElKOYuou\nxdN9iqmkg0Rrxu0lL7/zydYcPNY+/O2NqdfN+RNZ1JhG63ffUhP8FO5DH60VX0uFeOX1iS8hmtNL\ncgqeIkXcoZi6KxXjWVra8rIb5s93agwCjwULmp5LxZiKhEu0Ztxeiud3PlG6LbilpZs/wef67b9+\nu13nGu/uFcn8fsT8OW1pDvrWBsrroLlzW15OJMrrE5+a04uISAivm+ynCzWnd59beX2yNeN2XScN\n/pVq3RZaOp9kONdkSGNctTZlXRJTvu4tNacXEUknmuNWxBPJ1ozbdZ3UrDjVui20NKVbMpxrMqQx\nrny+yD70KaK0NHQg3Hi04pPOpeb00m7z5s3zOgkpRzF1V9LH08NRdJuT9DEVkdYFNSOeF7bsplTr\nthC4+bNhxgbKppWFtN4IObeyxDzXZH4/XMmbAn3oAzPEWOvUzufnO3+3bXNuroevSwLTpzunE3i0\nNjWt8vrEp0K8tNu+ffu8TkLKUUzdlezxPPTR1pDl+rDleGmp332yx1REYhA02Nc+YLX9KC59pBNh\nCr3OEnyuQ3oOSchzTeb3Iy55U7Qb6Ql4cz0elNcnPvWJFxFJUGuP6cOoD2qalo/OZtT7ezxMkbhJ\nfeLdp7zeJf6puT56bzUbex6keApsz2q+j3SsU6uJJJX8/Mjp6OD/s3fvcXaV9d33vz8SEkgyHJPA\nQA6QYKu1D2CGQ8MMWrWNh+pUJ9ZU611MoNVKxGIb9PHwItpbfUIfpQL1Ftukaqt5hfaepmBr4dba\nPk44xMwUFDUqCRCC4+RAhAkTJgeu54+1d7L3nj2nNb896/R5v177xey115659pdc+9rXXmv9ruGX\nqAOG0YjxniPxAJBS1737LHXNl3acKXXNj+4DQMOVTit+5cfP19XXRhN4afhrpMtLq+08sFNbntqi\njk35PDqJgmluHnq/3jYgARS2A4CUmtY8T1df++Tx+63N8xJsDYCiGeu65IUviIZ8Gq7Q3ZvfLD3y\nSPTz4cPRmSsUncUk40g8Ytu3b1/STcgdMvWV9Twrr09802mX6z/+5kjixXSynimAsetc0akrzrhi\n1Guks1wQLQl5eR9N07ryDcm0ttDd3LnR7eSTpcHB6Pa970lvelMmi92NJC//RvOMSTxiW7VqVdJN\nyB0y9ZX1PCsrHd/zv6dp2oNbEy+mk/VMAWjMFbbnzpyrc759Tt1q65WyXBAtCXl5H03TZRSTmmnt\nag3f/37uit3l5d9onnE6PWJbu3Zt0k3IHTL1lac8jz69u+oN+9jTuzUlgXbkKVMgz0YsNjeOdeDH\n0ufLXzhibPLyPpqmyygmNdPm5uridrUatCTjZMrLv9E840g8YqPyrz8y9ZWnPH809Zmq+z+suT9Z\n8pQpkGcjHiWtnWSMMOmgz/vLS6ZpuoxiUjPt7JRaW6PK9K2t0iWXVD+eg2J3efk3mmdM4gEgA8qV\n6p84XXp2mnTW/oHcXHsHNMqB/3Wrgtnx24Ev3a793+zU0ZOi+0dPMu2/91+SbuZQYzzdfSQjHiWl\nwjYcFPYyitpr5e+5p3pS3zmGHBz6OIqNSTwAZEBUqV7afZp0+mFp3oFjubn2DmiUM973QZl0/HbG\ne27Q6b+zXFNDdH9qkE5/w1uSbWQ95dPdJ3CN7YhHSWuPJI5l0gHUqKzbMlLNhNwrT+rvvz+6v3Tp\n6BPzuH2cyT9KmMQjtvXr1yfdhNwhU195yrN8xGPBQE0pk+FOg23QQJ+nTFFMU8LI91NhHKe7D2fE\no6T1qm4Pgz7vj0z9pSLT2on5RRcNP/bG7eMOX/CN5tZbJbP1MpPMpNtvd/8TcMAkHrH19PQk3YTc\nIVNfecqzfMRjwUuvrH5guNNgGzTQ5ylTFNMxG/l+KtT26717q7+QG8OXdF5HSenz/sjUXyoyrZ2I\n9/cPP/bGvaTF4Qu+0Xzwg5J0Is8bbnD/E3BgIaTxK2h/ZrZEUnd3dzfFGgBk15490YeC3t5o0O/s\nrH8UbfHi6uq5ixZFR92QGj09PWppaZGklhBCCj6BZl/tWH/gS7frjPec+AT6yztv04sLF+j0N7xF\nU0I0gX/2m5t19ut+N7lG11PZz/fujSYDZa2t0X/L1eXL24apLg+kwYirJeRFW1t1v5SGH3vHOpaP\n9jca0PetzhebBZkuNkwjxnuWmAOALCmfBjua2iVwKFyFAjrzj98v/fH7T9wv//Bi9Il0qqSzJ71V\nY1DZzxcvrp7E1zvyloMlrZBv5dUSJGnngZ3q2NSR+WUJa7+Y+Oe/v1NzLlla3V+HG3vHOpbX6uwc\nOvlHIXE6PQDkEYWrgHyod9rtSKfiUvgKKZSWNeX7DvapbUObFt+2WG0b2rTn+fj9o3YZx7d+5z3S\nY4+NOvYO14YxtW0c9Sziuu22Ee7z/pIaTOIBII8mYaD/5Cd1vPCNmfTpT7v/CQD1vpAb6Uu6SSh8\nBYzXRNeU95p81068OzYN3z9G+5t1v5gYw9g7XBvG07bqhp6YWB9eeqXe9FdXHG/z3p2PjjzprjMp\nf//7o9Pny7f3v79if95fUoNJPGJrb29Pugm5Q6a+yNNfZaY331z92Ec/OsmNAYqg3qRgpImCc+Er\n3kf9FTHTia4pP9oEd6yZjueMgNH+ZtwvJoZrQ+yzFSom1tMe3KoPf+57x9vc+7qrhky6K7+c+H7b\nS+pOyofL8+jTu6vuH6u5j8nDJB6xrV69Oukm5A6Z+ipanp6nCQ6naJkCmRO36vUw6PP+ipjpRFdL\nGG2CO9ZMxzPxHu1vxv1iYrg2xD5boeaLuuaDJ34+48ChIftWfjkxc3//kMel4fP80dRnqu5vt/16\nelGbdthidVmb5toelqSbJEziEduyZcuSbkLukKmvouU56ql4DteyFS1TIHOc62HQ5/2R6fiNNsEd\na6bjmXiP9jfjfjExXBtin61Q80Vd76wTP//yzFOH7Fv5ZUTlvpW/a7g8r3v3WeqaL+04U+qaLx05\ndljnP75Fi7VTbdqiTnVULUnHZXeNQ3V6AMiJUU/FK59yJ0WnznV0TGhpmk99qvoU+k99KvavAuAl\nbtVrIMU6V3SqY1NH1RJ1cZQn3pP5N8fahvG0rUpFxfrDc2fr/1kRtOik/Wqe1azm++6U/sd7qqrZ\nN3+jQzsPRKvXdKyQvrW5SRe/OGdM1e6nNc/T1dc+efz+7juq16NrVvXnjnqX3X3kI+N/iRiKSTwA\n5ETzrObjA3P5fhXna2U/8hEGYyC2vr7oi7VxrhNdiPW2gRqxJ7gZ+5uxVHxxN03SN2ofr/lSr+rL\nifnNOndtpzSOswgqv9iYc9ERad/W448v1JN6XBdIbfNKXwiM/ntvvVX64AdP3L/ttppieqiL0+kR\n2+bNm5NuQu6Qqa+i5dm5olOXn3e5pk+ZrulTpuvIi0eqr4t3uFa2aJkCo4p7mUrMKs+xK1jHRJ/3\nR6b+yHTsxnIZwHB51j532r/co8HpTccfn6pjukBPRu9pF12kx7RY31Wb5ih6X5yroe+XlRN4SVWn\n42N4TOIR28aNG5NuQu6Qqa+i5Tl35lxNmzJNg8cGNXhsUFuf3lr9Ad/hWtmiZQqMKu6SSzHPjJns\n9bbp8/7I1B+Z+hpznnPnavr5c+o/1t9fda28JG1bwBJ1XpjEI7ZNmzYl3YTcIVNfRcxzxA/4DmvH\nFzFTYERxL1OJeWbMRNfbHi/6vD8y9UemvsaV5xjeu9oW9SoEab6eqn5g927NVZ++q7YhR+3Lbr21\nujge1e8jTOIBIEfG9AF/tNN/HarYA4UR9zKVmGfGTHS9bQBwVX4vW7hQamrSsfnzdPCUKdX7lN8X\nDxyo3v7MM/rvC5erTScq3D98YfXR+Xqn2zOhp7AdAOTKmKrp1lapv+gi6bHHThyZd65iD+RaRWXo\nsVR3Pi5mFfnMFNsCUAw172Wv2tCmn/54tzo3RWvWP392ky4uvy+edZbUX7E2/Vln6TyrPnup9v5I\nbrihuEXwOBIPADkypnVra0/37e+vvi6t9vGHHjpxRJ6j9MAJfX1DJ/AxLlMBgLzoPdirvbOkq6+V\nLvqA9Nb3zznxvjhvXtW+3z/5GW0LT1f/ghhFd4u4Hj2TeMS2cuXKpJuQO2TqizyHUW+ArJy41z5+\n9OjxAjQrL76YojRAWdyidhnC+6g/MvVHpr4mkueIl/VVXEb0/Yua9Ftv6dcblw/qofOkwakmTZ8u\nHTkiPfro8QMGT1849Dr5WvXWo887JvGIbdmyZUk3IXfI1Bd5Vus72Ke2DW264g27h79eTToxyE6t\nueKqt1dDEp3gWvNApsUtapchvI/6I1N/ZOprInmOWLejosDuW98/R3tnSXtnSUemSNOPBmlwUNq6\nVbrqquNfkJ73+Bbtae1QCNEa8pVq7xcJk3jE9o53vCPpJuQOmfoiz2rl9aW/d/RJLVp9TN+/qKl+\nUa3yIHvlldW/oLlZ73jJS6q3Pf109Wn1nG6PIolb1C5DeB/1R6b+yNTXRPIc02V9qj5C33yw5sFD\nh6rv9/aq72CfNs1s06LPL1br+jb1Hdwz4vXweS96xyQeAAqicrm5vbNK16mNtNxcverZ5W3Tp0f7\nDA5Wn0ZcgNOLgeNiVpgHgKIrH7GfPmW6emfVPHjqqdX3m5uPH4jYeWCntjy1RR2bRv58ccMNvu1N\nGybxAFAQ415fut668uVt559fvW/5NOICnF4MHFevjwAARlU+Yn/+aeerY4XUNV/acaa07cLp0j33\nSE1N0WV9TU3SnXdWHYiQogMT5csEdcNiaWWbNLP67L/aZehe//rqAnhvetNkvNLGYBKP2LpYcsod\nmfoiz2oe60sfz3S404hrt8+ezen1QIbxPuqPTP2Rqa/JzLN5VnNVNfs//dhlUWW6/v6osG5/v/Se\n99Q9EFE+Oq+zdkoLt0hvH3p0vvKI/L33Vj/2r//aiFc0OZjEI7Zbbrkl6SbkDpn6Is9qY71ObSTH\nMx3uNOLa7SFwej2QYbyP+iNTf2TqazLzrHuAofYsvm3b9F9/sVuP/EOTLp+68Ph+tUfn1VScs/8s\nhJB0GyaFmS2R1N3d3a0lS5Yk3ZxcGBgY0IwZM5JuRq6QqS/yrNZ3sE/L71qu3oO9ap7VrM4VneOe\nyI8708WLowl82fTp0an4rKmtnp4etbS0SFJLCKEn6fbkAWO9P95H/ZGpPzL1lXiebW3RF//1NDVJ\nc+ZIzc1609sO61+f/d7xh1rnt2rLtUPPIihPd82G/rrJmAo3YrznSDxi483SH5n6Is9q4y0KU8+4\nM609vX5wkKPyQIbwPuqPTP2Rqa/E86w8q69cSLesv//454jOTTbkKP7xZedm9kkr23T2JxerbUOb\n9jxf/3K+8vXxWatmzyQeAAqiXlGYhhtpIKboHQAAqFVZNPSyy4bdbdrPf6GuDdKOz0tddwxo7rmL\n9f4PnqzQdJre+aHXSwu3aP+L+axmzyQeAApi3NXpPYw0EOdwTW0AAOCo8mBAU1P1YwcOnKi789//\nLR08eLwY3hc/9UjVrtt+vm3YKvZZxCQesa1ZsybpJuQOmfoiz2r1iseUl2dZfNvIp5uVTSjTyoH4\n8sulI0eoWg+kHO+j/sjUH5n6SlWelQcDHnusunjuWWcN+7RTj1Rf7D54bFA6a6fmnr1F3515kR7T\nYn1XbZqjbH7+mJp0A5BdCxYsSLoJuUOmvsizWrk6faW2DW3R8iySdh7YqY5NHUP2qTShTMsDsVRd\ntGbnzuj6eJYIQpb19UnLl0eXieSocCPvo/7I1B+Z+kptnpWfI6Tos8STT9bddUowPfIPs3Tdu8/S\nL/p79fWvH1bzQWn289Lph/sl9WuxdupJLVC3LlOHOiVl5z2b6vQAUGCLb1usnQdOVI9fdOYi7bhh\nxyT84Zqq9YsWRd+yFwjV6f0lOtbXVlNubeWLKQBopD17ooMAvb3S6adLP/tZdEp9paYmDR46qOlH\nR57zdqlVbaEx79lUpwcAuErkOnlp6PXwXB+PrKst1EjhRgBorMpT7Xt6osr1ixZV79PfP+oEXpKa\nla33bCbxAFBg9a6Tn5w/3Fl9XVtnxd/t64uOanK9PLKEL6YAIHljeO89eMoUvaDqFXP2afbx5ebK\nt09/ulGNnDgm8Yht+/btSTchd8jUF3mOrnyd/I4bdqhrVZfmzhz5ejC3TCu/Pe/qqr52ePnyE9Vm\nWU8eWTHSF1MZxvuoPzL1R6a+Mp3nCNXsD02RuuZLr/nYPD1iv1b12MV6eEihu49+NL1ryDOJR2w3\n3XRT0k3IHTL1RZ7+JiVTTkvGBH1p25dkn7Djt0//1xgPp0zkLJDyF1P33x/dX7o0F2eS8D7qj0z9\nkamvTOdZp5r907Onq2u+tPBG6eprpWnN83TuGT+qetqpOqw2bdE9evOQX5nGNeQpbIfYdu3ald7q\nlRlFpr7I09+kZHrlldLWrSfuX3GF9NBDjf2bCaCwnb/yWK8/lnRe9WPh5jF83vEoTpezAne8j/oj\nU39k6itvee55fo86NnWo92Cvmmc1q3NFp3b9Xwt02eODQ/Y9pOmaoReGbJ/IlLkR4z1LzCG2PHXu\ntCBTX+Tpb1IyrR0pC/JlM1LA4yyQnJ1JwvuoPzL1R6a+8pZnvSV2V91wsT78ue/pqqeqT02frkF9\nV23qUKf2pnjJOU6nBwBU6TvYp7YNbVp822K1bWjTnucn+XTg/ftHvg80ikdxOgrcAUDqbfijb+jD\na1vVc/60qu0nSWrTFnWes1iamd7LoZjEAwCqLL9rubY8tUU7D+zUlqe2qGPTJBeWqzcJomI9xuF9\nl72v6v6nfvNTY3uiR3G6nBa4A4A8KR+dv6znKam1VUdrZsXNhw9Kb09vYV0m8Yht3bp1STchd8jU\nF3nG03uwd9j7k5JpvUkQFesxDte2XKtwczh++8irPjK2J460asJYefyOFOF91B+Z+iNTX4XKs/Se\n/ei86iPy+06V1HTi80/aqtRzTTxiGxgYSLoJuUOmvsgznuZZzdp5YGfV/bJJybQ8CaqUs+uMgazg\nfdQfmfojU19FzPPkKdMkHT6xwST1V58ZeMMNUn+/9JExfi/cSFSnBwBUqVfFdbT14xsuZxW/JarT\nN0JDxvq+vuhMkN7e6NKOzs7MH10HAFQ7duEFmvLEk8fv72iapotefEp6fuj7/Xinz1SnBwA0XL0q\nronr7IxOod+9W3rmmei/bW1MqNAYlRP3vXujQy9SdDlHR0fmv0ACAFSbcv48qWISv1jTNed5aW+C\nbRoJ18QDAMZt0ivYl0+xnzcvmlA9+STXxqNxKmswlCfwZVzKAQD509kpNTWduN/fr4cvTO9nDCbx\niG3fvn1JNyF3yNQXeforZ5pYBXuujcdkGOnfVcGWjON91B+Z+iNTX4XMc+5cac6cqk3nWXo/YzCJ\nR2yrVq1Kugm5Q6a+yNNfOdORKtg3FGtwYzLU/rtqairsknG8j/ojU39k6quoeR4+8/QR76cJk3jE\ntnbt2qSbkDtk6os8/ZUzraxYX+9+JddT71mDG5Oh9t/ZY4/lZsm48eJ91B+Z+iNTX0XN82f7fzri\n/TKzE7eklp2jOj0AYNzGU8G+bUObtjx1orJ86/xWn8J5Ga8aTnV6f4z1AIC4dp81VfMOHDt+UznV\n6AAAIABJREFU/4hJD81pUsfBB7R34OVj+h31ptZUpwcApMJ4Ktg37NT7cvExiarhAABgQs44VH3/\n5CC17enXk1N+Xd1zxzeZbzROpwcANNRYT70f92n3FLkDAABOTp1T//PJqceiyXznrKWT3KLhMYlH\nbOvXr0+6CblDpr7I01+cTDtXdKp1fqsWnblIrfNb1bmi/nXs4654f/bZI98HMGG8j/ojU39k6quo\neU5ZsHDEx5fu6dfjukDfVZvmqMFL646CSTxi6+nhEk5vZOqLPP3FybR86v2OG3aoa1XXsNfOj+e0\n+76Dffrhvh9XbzQbd9sAjIz3UX9k6o9MfRU2z1JR02Pz5+ngKVP0wpTqh6dIukBPqk1b9E29PpEm\nllHYDgCQCuMpgNe2oU1f+fMtWnygYuOiRVEF8YygsJ0/xnoAgJeWv5ivrk/s1qnH6j9uGjqPnqzC\ndhyJBwCkwlhPu5eio/S9s2o2smY8AABwcur5C9V93vCP/1Kn6WX64eQ1qALV6QEAqTCeivfNs5rV\nsWKnOjdJzQel589u0sWsGQ8AAJx88U1fVPuO39Df/8PzumqXVHvR3unq1wNaqjP03KS3jSPxAIDM\n6VzRqV95Wauu+fNF+tOPX66Xzn6ZtHSp1NYm7Um22AwypK8v+jezeDH/dgAAVd77jffq8WnPq22V\n9OIw+8xQ9bp0ZtKnP934tjGJR2zt7e1JNyF3yNQXefpLS6aV9Vw+9YXtmvbg1mit+C1bovXi603O\nmLCh1vLl0b+Zyn87qJKWPp8nZOqPTH2RZ6SywO7D59bfZ4qODqlU/9GPNrJVESbxiG316tVJNyF3\nyNQXefpLS6aVy9HN3N9f/WBvb/3JGRM21OrtHfk+UtPn84RM/ZGpL/KMNM86UWvnDe+SHlg4RWFK\ndcn6kyTt0oJJX3Yu1iTezKaY2bVm9nUz+5aZ/UflLebvvN7MHjezQ2b2oJldPsr+08zsU2b2hJm9\nYGY7zezdcf424lm2bFnSTcgdMvVFnv7Skmnlt+N1C9zVm5zVbnvoIY7Ij8J7vE/dWF9bDJHiiEOk\npc/nCZn6I1Nf5BnpXNGppmlNkqS9s6SrVh7TDy6cMWS/UzSoNm3RPXqTvqs2PabF6rI2zbU9DVv9\nNu6R+M+XblMkPSrpkZrbuJjZCkmflXSzpFeUfse9ZjZ7hKf9o6RXS1op6VckvUPST8b7twEA2VP5\n7XjHCun7FzVFS8y1tkbrvNabnNVuO3o0OiJ/0UWcYj88t/E+lWN9aU3gqn87AAAoKrg7Z+acqm3X\nvfssDTczv0SPqE1btFg7G76WfNzq9L8v6e0hhH9zaseNku4MIXxVkszsvZJ+R9IqSbfU7mxmr5d0\ntaRFIYRfljbvcmoLACDlOld0qmNTh3oP9qp5frPOXdspzZxbsUNndLp8b280eS9Pzjo6oiPwR4+e\n2Le/P7rt3Bk93jW2CvkF4Tnep2+snzuX/98AgGGdferZ2nlg5/H7j7zYqx8unKGXP/H8kH2n63DV\n/Uv1sCQpWiLeV9wj8YclPebRADM7WVKLpG+Xt4WoYtG3JC0d5mlvlrRN0ofMbLeZ/cTM/tLMTvFo\nE8Zm8+bNSTchd8jUF3n6S0um5eXodtywQ12rujS3cgIvnZic7dgR/Xfu3BPbrrxy+F+8e3djG549\nLuM9Y312paXP5wmZ+iNTX+R5gtUcdT/84mG9+m3PR2cALlwoNTXpCS1Ul1oVap87ZIufuJP4z0r6\ngNW+qnhmKzpNr69me5+kYeoAapGib+dfLuktkj4g6W2S/tqhPRijjRs3Jt2E3CFTX+TpLxeZVp5C\nXVOgRk8+KZ12mvTDHybTtvTxGu8Z6zMqF30+ZcjUH5n6Is8T9g3sG7Jt7yzpre+fIz3xhPTcc7rn\ntid0tbqGLEM33LJ0HqxymZ4xP8nsnxVdo/aMpB9KOlL5eAhhzCV/zaxZ0tOSloYQHqrYvk7SK0MI\nQ76hN7N7JbVJOieEcLC07a2Krp2bGUIYrPOcJZK6u7u7tWTJkrE2DwCQUn0H+7T8ruXRKfWzmtW5\nonPoEfnRXHBBNHGv1dQkPfecSzuH09PTo5boHLuWEEJPQ/9YTF7jPWM9ACCL2ja0actTW4Zsb5rW\npDkz51R9/njRTtJJFUffX5R0rvq0V7sVnYzmN97HPRL/S0n/LOm/JO2T9GzNbTz2STom6Zya7edI\n+sUwz+mV9HR5UC/5sSSTNG+kP/bGN75R7e3tVbelS5cOOW3kvvvuq7tG4vXXX6/169dXbevp6VF7\ne7v27av+pubmm2/WunXrqrbt2rVL7e3t2r59e9X222+/XWvWrKnaNjAwoPb2dnXVXK+3ceNGrVy5\nckjbVqxYwevgdfA6eB2FeR0va3uZtmyPlpnb8tQWdWzqGP/rOFI1J9WApHZJXQMDrq9j48aNuvDC\nC3XppZceH3tuvPHGIb8vhbzGe8Z6pafv8Dp4HbwOXgevY2yvo3NFp87+17M1Z9ccNU1r0sLTF6pp\nWpP6f9SvnX8dff5YcOsCtW1okylomaRLFX2WeIukl+olkq4b8nonKtaRePdGmD0o6aEQwgdK901R\n8ZrbQgh/WWf/P5J0q6S5IYSB0rbflfRPkmbx7TwA5N/i2xZXFZtZcPoCzT9t/viOzO/ZExWzu/9+\nqXI85Ei8O8Z6AEAe1H7+KDv4mZM0c7D6JPpjkqaqW2k5Ei9JMrM5ZtZWus0Z/RnD+pykPzKzPzSz\nl0r6oqQZkr5c+jufMbOvVOz/dUn7Jf2dmb3MzF6pqLLt+nqDOgAgfyqXmZOkA4cOaMtT1Ufm+w72\nqW1DmxbftlhtG9q05/maJeTKxe5+8ANpVmnBebNoyTmWmzvOabxnrAcAZF7t54+yjhvPG7JtQpPt\nEcT6vWY208w2KDrV7f8r3X5uZuvNbMZ4f18I4S5Jfy7pk5L+W9LFkl4XQthb2uVcSfMr9n9e0m9L\nOkPS9yT9vaR/UVT0BpOk3mkzmBgy9UWe/tKUaeeKTrXOb9WiMxepdX6rzjr1rKrHew/2avldy4dM\n7Ot6+culSy6Jfg5BevhhacGCwq8d7zneM9ZnU5r6fF6QqT8y9UWeIyt//pg+ZXrV9v+aubeB9eir\nxV0n/nOSXqVo+Zfylf5tkm5TVMn2T8b7C0MIX5D0hWEeG/IvKYTwU0mvG+/fgZ9ly5Yl3YTcIVNf\n5OkvTZmWl5kra9vQpiefPVGkrnlWs3oP9lY9p/Z+9YM1jw0OSlu2FH3teNfxnrE+e9LU5/OCTP2R\nqS/yHFn588ee5/eoY1OHtv18mwaPDWrw2KCCosItlb6nFl3u3Ia41en3SXpbCOE/a7a/WtJdIYSJ\nnFrfEFwnBwD5Vh5MK6+J79jUUVVVtnV+a9XEv0pbWzRpr7VoUbTevLMsXBOftfF+XGN9X5+0fHn0\n5U1zc7T04Nxxrm4AACi8hX+1ULue3SVJOvQX0inHqh/vUemKeMfxPu6R+BkautarJO0pPQYAwKSq\nPTIv6fhEvnJiP+zSdJ2d0VH3bduio/BlzfWvfSuI/I73y5ef+NJm586in3EBAIjpwKEDx39+5Bzp\nyp83/m/GncQ/IOkTZvaHIYQXJMnMTpV0c+kxAAASV29iX7nm684DO9WxqSPap1zkrlyxvvIIbXHl\nd7yvvXyi9j4AAGNw+imnq/9wvyTpze+UOjdJrU8NPa3eU9yCeR+Q1Cppt5l928y+LekpSVeJgjOF\nUbu2IiaOTH2Rp788ZFp7Xfy2n2+rrl5fnszv2BH9t9inWOd3vK89w6LYZ1wMKw99Pm3I1B+Z+iLP\n8Xn2hWeP/7x3lnT1tWp4gbtYk/gQwqOSXiLp/5b0cOn2YUkvCSH80K95SLNbbrkl6SbkDpn6Ik9/\neci0dmmYwWODo1evL6hcj/ednVJra1TzoLW16GdcDCsPfT5tyNQfmfoiz/GpXR1HauxReClmYbss\norCdv4GBAc2Yke1LItOGTH2Rp788ZFpZAO/p557W4LET178vOnORdtzgX8SuniwUtssaxnp/eejz\naUOm/sjUF3mOT+VlepI0fcp0Hfr44PGJfKKF7cysXdI3QwhHSj8PK4Rw94RbhtSjc/sjU1/k6S8P\nmVZeJ1878DbPai581XLGe1TKQ59PGzL1R6a+yHN86hXR1cfPaejfHE9hu82SzlVUkXbzCPsFSVMm\n0igAACZD3YH3dR1Fr1rOeA8AwBjVK6Lb6HPdxzyJDyGcVO9nAACyqu4lZQWvWs54DwBAurkNzmZ2\nhtfvQjasWbMm6SbkDpn6Ik9/ect0+V3LteWpLdWF7ahaPiLG+2LJW59PAzL1R6a+yDP9Yk3izexD\nZrai4v4/SnrGzJ42s0vcWodUW7BgQdJNyB0y9UWe/vKWae1yc70He6UvflFqapKmTo3+e+edCbUu\neYz3yFufTwMy9Uemvsgz/WJVpzezxyX9QQjhfjP7bUl3SVoh6e2SFoQQlvk2c+KoWAsAqHXl31yp\nrT/fevz+FedfoYe+fPKJa+KlaPmxBlwTn4Xq9Fkb7xnrAQBpEMzSUZ2+xrmSnir9/CZJd4UQ7jOz\nJyQ95NEwAAAaLdSUngkhFP6a+BqM9wAApEzca+IPSJpf+vn1kr5V+tlEpVoAQEbsP7R/6P3aa+Cf\nflpqa5P27JnElqUG4z0AACkTdxLfKenrZvZ/JJ0t6Zul7a+Q9JhHw5B+27dvT7oJuUOmvsjTX94y\nbZ7VPPR+Z2d0Cv306dHGwcHo9PqOjgRamDjG+4LLW59PAzL1R6a+yDP94k7ib5R0h6QfSfrtEMLB\n0vZmSV/waBjS76abbkq6CblDpr7I01/eMu1c0anW+a1aePpCNU1r0u7ndqvtGx3ac2+ndP751TsX\n87R6xvuCy1ufTwMy9Uemvsgz/WIVtssiit3427VrF9UrnZGpL/L0l9dM2za0actTJ4rZtc5vVdcG\nNbTAXRYK22XNmMb6Rx+VrrpKOnRIOvVU6YEHpJe/fFLbmSV57fNJIlN/ZOqLPCcuNYXtzKxd0jdD\nCEdKPw8rhHD3hFuG1KNz+yNTX+TpL6+Z1i41t+3n2/T6q+fon7qnaOZRk516amGWmsv9eH/VVVJ/\nf/Rzf7+0dKn03HPJtinF8trnk0Sm/sjUF3mm33iq029WVKV2T+nn4QRR7AYAkCHNs5q188DO4/cH\njw3qY1/brVkvlDb090vveU9DlppLoXyP94cOjXwfAICUG/M18SGEk0IIeyp+Hu6WvQEdAFBo5Wvj\nF525SNOnRAXtmg/W7FSQa+JzP96feurI9wEASLm4he0ArVu3Lukm5A6Z+iJPf3nNdO7Muepa1aUd\nN+zQZeddJknaVzu3O/vsyW8Y/D3wgNTUJE2dGv33gQeSblGq5bXPJ4lM/ZGpL/JMv1iTeDO7zcxW\n19m+2sz+auLNQhYMDAwk3YTcIVNf5OmvCJmWj8pPnzq9avsP9/1Ye54v1lrxuRzvX/7y6Br4I0ei\n/1LUbkRF6POTjUz9kakv8ky/WNXpzexpSb8TQni4ZvsSSXeHEOY5tc8N1ekBAONywQXSk08ev/vE\n6dK7PteqrlU+18VnoTp91sZ7xnoAQCqYHf+xEdXp455Of7ak/jrbn5M0O35zAABIiWeeqbo77znp\n6zc9JLW1SXsKc0Se8R4AgJQZT3X6So9JeoOkO2q2v0HSzqG7AwCQMWeddWIpMklTg7Rg/9Fo7fgF\nC6TLLpM6O9U3I2j5XcvVe7BXs0+draCg/Yf2q3lWszpXdGruzLkJvogJY7wHACBl4k7iPyfpDjOb\nI+k/StteK+nPJP2pR8OQfvv27dPs2RyI8USmvsjTX6EynTev6nT6KoODxyfzffOn6adv6dfeWapa\npm7ngZ1acOsCXXbeZVmezDPeF1yh+vwkIVN/ZOqLPNMv1un0IYQNigbwayV9p3R7l6Q/CSH8jV/z\nkGarVq1Kugm5Q6a+yNNfoTLt7NS2C6drx5nSs9OG2WdwUBc/1q/OTcM8fGxQW57aootuu0iLb1us\ntg1tmSqOx3iPQvX5SUKm/sjUF3mmX+wl5kII/6tU0OYcSaeFEBaFEL7q1zSk3dq1a5NuQu6QqS/y\n9FeoTOfO1Z9+7DJd9AHpJTdIXfOlwalWd9d5z438q/oP92vngZ3a8tQWdWzqaEBjG4fxvtgK1ecn\nCZn6I1Nf5Jl+sSfxZjbVzH5LUockK207z8xmeTUO6UblX39k6os8/RUt0/Jyc03zF+nDa1v13E9/\nILW2VlWdlaTZg1O06MxFuuL8K3T5eZdr+pTpw/zG6lPus4DxvtiK1ucnA5n6I1Nf5Jl+sa6JN7OF\nkv5d0gJJ0yX9H0XVaz9Uuv9erwYCAJCUuTPnDl1SrqtryPJzs86Zpx0r7peWL5d6e3V47iXqWBH0\n45P2D5m09x7snYSW+2C8BwAgfeIeif+8pG2SzpR0qGL7PysqeAMAQH6dc071/f37pTe8ISp2t3On\npj24Vd/YaNpxw45k2ueH8R4AgJSJO4m/WtL/DCEcrtn+hKTzJ9QiZMb69euTbkLukKkv8vRHpiUh\nVN8/eFB6+OHqbY88MuzT2za06ZlDzwz7eIow3hccfd4fmfojU1/kmX5xJ/EnSZpSZ/s8RafZoQB6\nenqSbkLukKkv8vRHpiX79w/dVjuxHxyULrhAXRtMcw5WP7TlqS1a/W+rG9c+P4z3BUef90em/sjU\nF3mmn4XaDx1jeZLZJknPhhD+2Mz6JV0saa+kf5G0K4Sw0reZE2dmSyR1d3d3U6wBADAxbW3RqfOV\nTjpJevHFurt3nyt98HXSt78qTSkNuz2SLot+bAkhpPITU9bGe8Z6AEAqVBTA7ZHUEv3oNt7HPRL/\nZ5JazexHkk6R9HWdOLXuQx4NAwAgtTo7pSuukKZPj26XXy6dd96wu1/6i2gCPzVE5d3LtwxgvAcA\nIGViVacPIew2s0skrZB0iaRZktZL+loI4dCITwYAIOvmzpUeeqh6W1ubtHt33d1PkqTxn/iWOMZ7\nAADSZ9yTeDM7WdKdkv4ihPA1SV9zbxUAAFnT2Sm9+c1RQbvBwaqHTNJRi47EZwXjPQAA6TTu0+lD\nCEckLW9AW5Ax7e3tSTchd8jUF3n6I9MRlI/Ov/CCNGNG9WMzZujZb27WUYsOyGdhLs94D4k+3whk\n6o9MfZFn+sW9Jn6zpLd4NgTZs3p1JiorZwqZ+iJPf2Q6Rlu3Sk1N0tSp0X+3btXZr/tdTX0x6DP/\n+SldfY10NOk2jg3jfcHR5/2RqT8y9UWe6Re3Ov3HFBW7+bakbknPVz4eQrjNpXWOqFgLAEiTnp4e\ntbS0SOmuTp+p8Z6xHgCQCr/6q9JPf6og6b/lX50+VmE7SddK+mWpPS01jwVJqRrUAQBALIz3AACM\n109+IkkaMGvIJXRxq9NfWP7ZLFoEL8Q5pA8AAFKL8R4AgPhmhiDr6ZFaar8Hn5i418TLzK41s0cl\nvSDpBTN71Myu82sa0m7z5s1JNyF3yNQXefoj0+JhvC82+rw/MvVHpr7IM/1iTeLN7JOSPi/pHkm/\nV7rdI+nW0mMogI0bNybdhNwhU1/k6Y9Mi4XxHvR5f2Tqj0x9kWf6xS1st1fSDSGEjTXb3yHp9hDC\nbKf2uaHYDQAgTTJS2C5T4z1jPQAgbRox3sc9nf5kSdvqbO9W/GJ5AAAgXRjvAQBImbiT+L+X9Cd1\ntv+xpK/Fbw4AAEgRxnsAAFJmIt+iX2tmyyQ9WLp/paQFkr5qZp8r7xRC+OAE/gYAAEgW4z0AACkS\n90j8r0vqkbRX0uLSbV9p269LekXpdqlDG5FSK1euTLoJuUOmvsjTH5kWDuN9wdHn/ZGpPzL1RZ7p\nF3ed+Fd7NwTZs2zZsqSbkDtk6os8/ZFpsTDegz7vj0z9kakv8ky/WNXps4iKtQCANMlCdfqsYawH\nAKRNmqrTAwAAAACAScYkHgAAAACAjGASj9i6urqSbkLukKkv8vRHpkCx0Of9kak/MvVFnunHJB6x\n3XLLLUk3IXfI1Bd5+iNToFjo8/7I1B+Z+iLP9KOwHWIbGBjQjBkzkm5GrpCpL/L0R6Z+KGznj7He\nH33eH5n6I1Nf5OmLwnZIFTq3PzL1RZ7+yBQoFvq8PzL1R6a+yDP9mMQDAAAAAJARTOIBAAAAAMgI\nJvGIbc2aNUk3IXfI1Bd5+iNToFjo8/7I1B+Z+iLP9GMSj9gWLFiQdBNyh0x9kac/MgWKhT7vj0z9\nkakv8kw/qtMDAJAAqtP7Y6wHAKQN1ekBAAAAACgwJvEAAAAAAGQEk3jEtn379qSbkDtk6os8/ZEp\nUCz0eX9k6o9MfZFn+jGJR2w33XRT0k3IHTL1RZ7+yBQoFvq8PzL1R6a+yDP9mMQjtjvuuCPpJuQO\nmfoiT39kChQLfd4fmfojU1/kmX5M4hEby0/4I1Nf5OmPTIFioc/7I1N/ZOqLPNOPSTwAAAAAABnB\nJB4AAAAAgIxgEo/Y1q1bl3QTcodMfZGnPzIFioU+749M/ZGpL/JMPybxiG1gYCDpJuQOmfoiT39k\nChQLfd4fmfojU1/kmX4WQki6DZPCzJZI6u7u7taSJUuSbg4AoOB6enrU0tIiSS0hhJ6k25MHjPUA\ngLRpxHjPkXgAAAAAADIiNZN4M7vezB43s0Nm9qCZXT7G57Wa2REz4ygGAAApxlgPAMDEpWISb2Yr\nJH1W0s2SXiHpEUn3mtnsUZ53uqSvSPpWwxuJIfbt25d0E3KHTH2Rpz8yRVyM9dlEn/dHpv7I1Bd5\npl8qJvGSbpR0ZwjhqyGE7ZLeK2lA0qpRnvdFSV+T9GCD24c6Vq0a7X8PxotMfZGnPzLFBDDWZxB9\n3h+Z+iNTX+SZfolP4s3sZEktkr5d3haianvfkrR0hOetlHShpE80uo2ob+3atUk3IXfI1Bd5+iNT\nxMFYn130eX9k6o9MfZFn+k1NugGSZkuaIqmvZnufpF+t9wQze4mkT0tqCyG8aGaNbSHqovKvPzL1\nRZ7+yBQxMdZnFH3eH5n6I1Nf5Jl+iR+JHy8zO0nRaXU3hxB2lDeP9flvfOMb1d7eXnVbunSpNm/e\nXLXffffdp/b29iHPv/7667V+/fqqbT09PWpvbx9y/cjNN9+sdevWVW3btWuX2tvbtX379qrtt99+\nu9asWVO1bWBgQO3t7erq6qravnHjRq1cuXJI21asWMHr4HXwOngdvI4Uvo6NGzfqwgsv1KWXXnp8\n7LnxxhuH/D5EGOvpO7wOXgevg9eRxdexbNmyqrG+vb1d11133ZD9JirxdeJLp9gNSFoeQri7YvuX\nJZ0eQnhrzf6nSzog6ahODOgnlX4+KmlZCOE/6/wd1o4FAKRGkdaJZ6wHABRVLteJDyEckdQt6bXl\nbRadM/daSffXecpzkn5d0qWSLindvihpe+nnhxrcZJTUfluGiSNTX+Tpj0wRB2N9dtHn/ZGpPzL1\nRZ7pl/gkvuRzkv7IzP7QzF6qaKCeIenLkmRmnzGzr0hRIZwQwo8qb5L2SHohhPDjEMKhhF5D4fT0\n5PrAUSLI1Bd5+iNTTABjfQbR5/2RqT8y9UWe6Zf46fRlZvY+STdJOkfSw5LeH0LYVnrs7yQtDCG8\nZpjn3izpd0MIw547xyl2AIA0KdLp9GWM9QCAomnEeJ+G6vSSpBDCFyR9YZjHhlYYqH78E2L5GQAA\nUo2xHgCAiUvL6fQAAAAAAGAUTOIBAAAAAMgIJvGIrd7aiJgYMvVFnv7IFCgW+rw/MvVHpr7IM/2Y\nxCO21atXJ92E3CFTX+Tpj0yBYqHP+yNTf2TqizzTLzXV6RuNirUAgDQpYnX6RmOsBwCkTSPGe47E\nAwAAAACQEUziAQAAAADICCbxiG3z5s1JNyF3yNQXefojU6BY6PP+yNQfmfoiz/RjEo/YNm7cmHQT\ncodMfZGnPzIFioU+749M/ZGpL/JMPwrbAQCQAArb+WOsBwCkDYXtAAAAAAAoMCbxAAAAAABkBJN4\nAAAAAAAygkk8Ylu5cmXSTcgdMvVFnv7IFCgW+rw/MvVHpr7IM/2YxCO2ZcuWJd2E3CFTX+Tpj0yB\nYqHP+yNTf2TqizzTj+r0AAAkgOr0/hjrAQBpQ3V6AAAAAAAKjEk8AAAAAAAZwSQesXV1dSXdhNwh\nU1/k6Y9MgWKhz/sjU39k6os8049JPGK75ZZbkm5C7pCpL/L0R6ZAsdDn/ZGpPzL1RZ7pR2E7xDYw\nMKAZM2Yk3YxcIVNf5OmPTP1Q2M4fY70/+rw/MvVHpr7I0xeF7ZAqdG5/ZOqLPP2RKVAs9Hl/ZOqP\nTH2RZ/oxiQcAAAAAICOYxAMAAAAAkBFM4hHbmjVrkm5C7pCpL/L0R6ZAsdDn/ZGpPzL1RZ7pxyQe\nsS1YsCDpJuQOmfoiT39kChQLfd4fmfojU1/kmX5UpwcAIAFUp/fHWA8ASBuq0wMAAAAAUGBM4gEA\nAAAAyAgm8Yht+/btSTchd8jUF3n6I1OgWOjz/sjUH5n6Is/0YxKP2G666aakm5A7ZOqLPP2RKVAs\n9Hl/ZOqPTH2RZ/oxiUdsd9xxR9JNyB0y9UWe/sgUKBb6vD8y9Uemvsgz/ZjEIzaWn/BHpr7I0x+Z\nAsVCn/dHpv7I1Bd5ph+TeAAAAAAAMoJJPAAAAAAAGcEkHrGtW7cu6SbkDpn6Ik9/ZAoUC33eH5n6\nI1Nf5Jl+TOIR28DAQNJNyB0y9UWe/sgUKBb6vD8y9Uemvsgz/SyEkHQbJoWZLZHU3d3drSVLliTd\nHABAwfX09KilpUWSWkIIPUm3Jw8Y6wEAadOI8Z4j8QAAAAAAZASTeAAAAAAAMoJJPGLbt29f0k3I\nHTL1RZ7+yBQoFvq8PzL1R6a+yDP9mMQjtlWrViXdhNwhU1/k6Y9MgWKhz/sjU39k6os8049JPGJb\nu3Zt0k3IHTL1RZ7+yBQoFvq8PzL1R6a+yDP9mMQjNir/+iNTX+Tpj0yBYqHP+yNTf2T4zINLAAAW\n80lEQVTqizzTj0k8AAAAAAAZwSQeAAAAAICMYBKP2NavX590E3KHTH2Rpz8yBYqFPu+PTP2RqS/y\nTD8m8Yitp6cn6SbkDpn6Ik9/ZAoUC33eH5n6I1Nf5Jl+FkJIug2TwsyWSOru7u6mWAMAIHE9PT1q\naWmRpJYQAp+YHDDWAwDSphHjPUfiAQAAAADICCbxAAAAAABkBJN4AAAAAAAygkk8Ymtvb0+6CblD\npr7I0x+ZAsVCn/dHpv7I1Bd5ph+TeMS2evXqpJuQO2Tqizz9kSlQLPR5f2Tqj0x9kWf6UZ0eAIAE\nUJ3eH2M9ACBtqE4PAAAAAECBMYkHAAAAACAjmMQjts2bNyfdhNwhU1/k6Y9MgWKhz/sjU39k6os8\n049JPGLbuHFj0k3IHTL1RZ7+yBQoFvq8PzL1R6a+yDP9KGwHAEACKGznj7EeAJA2FLYDAAAAAKDA\nmMQDAAAAAJARTOIBAAAAAMgIJvGIbeXKlUk3IXfI1Bd5+iNToFjo8/7I1B+Z+iLP9GMSj9iWLVuW\ndBNyh0x9kac/MgWKhT7vj0z9kakv8kw/qtMDAJAAqtP7Y6wHAKQN1ekBAAAAACgwJvEAAAAAAGQE\nk3jE1tXVlXQTcodMfZGnPzIFioU+749M/ZGpL/JMPybxiO2WW25Jugm5Q6a+yNMfmQLFQp/3R6b+\nyNQXeaYfhe0Q28DAgGbMmJF0M3KFTH2Rpz8y9UNhO3+M9f7o8/7I1B+Z+iJPXxS2Q6rQuf2RqS/y\n9EemQLHQ5/2RqT8y9UWe6cckHgAAAACAjEjNJN7Mrjezx83skJk9aGaXj7DvW83sPjPbY2bPmtn9\nZrZsMtsLAADGh7EeAICJS8Uk3sxWSPqspJslvULSI5LuNbPZwzzllZLuk/QGSUskfUfSPWZ2ySQ0\nFyVr1qxJugm5Q6a+yNMfmSIuxvpsos/7I1N/ZOqLPNMvFZN4STdKujOE8NUQwnZJ75U0IGlVvZ1D\nCDeGEP7fEEJ3CGFHCOGjkn4m6c2T12QsWLAg6SbkDpn6Ik9/ZIoJYKzPIPq8PzL1R6a+yDP9Eq9O\nb2YnKxrEl4cQ7q7Y/mVJp4cQ3jqG32GSnpC0LoTwhWH2oWItACA1ilSdnrEeAFBUea1OP1vSFEl9\nNdv7JJ07xt+xRtJMSXc5tgsAAPhgrAcAwMnUpBswUWb2Tkkfl9QeQtiXdHsAAIAvxnoAAE5Iw5H4\nfZKOSTqnZvs5kn4x0hPN7PclfUnS74UQvjOWP/bGN75R7e3tVbelS5dq8+bNVfvdd999am9vH/L8\n66+/XuvXr6/a1tPTo/b2du3bV/254uabb9a6deuqtu3atUvt7e3avn171fbbb799SBGJgYEBtbe3\nq6urq2r7xo0btXLlyiFtW7FixaS+jte85jW5eB1p+v9R2Y4sv45KSb6Oyt+T5ddRKenXsX379ly8\nDmly/39s3LhRF154oS699NLjY8+NN9445PflGGO9svkewFjv/zoY6/1fR/l3Zf11lCX9Ohjr47+O\nZcuWVY317e3tuu6664bsN1GJXxMvSWb2oKSHQggfKN03Sbsk3RZC+MthnvMOSX8raUUI4Rtj+Btc\nJ+esvb1dd9999+g7YszI1Bd5+iNTP0W6Jl5irM8q+rw/MvVHpr7I01cjxvu0nE7/OUlfNrNuSVsV\nVbCdIenLkmRmn5F0XgjhmtL9d5Yeu0HS98ys/M3+oRDCc5Pb9OK64447km5C7pCpL/L0R6aYAMb6\nDKLP+yNTf2TqizzTLxWT+BDCXaV1Yj+p6NS6hyW9LoSwt7TLuZLmVzzljxQVyPnr0q3sKxpmqRr4\nY/kJf2Tqizz9kSniYqzPJvq8PzL1R6a+yDP9UjGJl6TScjF1l4wJIaysuf/qSWkUAABww1gPAMDE\npaGwHQAAAAAAGAMm8YittiokJo5MfZGnPzIFioU+749M/ZGpL/JMPybxiG1gYCDpJuQOmfoiT39k\nChQLfd4fmfojU1/kmX6pWGJuMrDsDAAgTYq2xNxkYKwHAKRNI8Z7jsQDAAAAAJARTOIBAAAAAMgI\nJvGIbd++fUk3IXfI1Bd5+iNToFjo8/7I1B+Z+iLP9GMSj9hWrVqVdBNyh0x9kac/MgWKhT7vj0z9\nkakv8kw/JvGIbe3atUk3IXfI1Bd5+iNToFjo8/7I1B+Z+iLP9GMSj9io/OuPTH2Rpz8yBYqFPu+P\nTP2RqS/yTD8m8QAAAAAAZASTeAAAAAAAMoJJPGJbv3590k3IHTL1RZ7+yBQoFvq8PzL1R6a+yDP9\nmMQjtp6enqSbkDtk6os8/ZEpUCz0eX9k6o9MfZFn+lkIIek2TAozWyKpu7u7m2INAIDE9fT0qKWl\nRZJaQgh8YnLAWA8ASJtGjPcciQcAAAAAICOYxAMAAAAAkBFM4gEAAAAAyAgm8Yitvb096SbkDpn6\nIk9/ZAoUC33eH5n6I1Nf5Jl+TOIR2+rVq5NuQu6QqS/y9EemQLHQ5/2RqT8y9UWe6Ud1egAAEkB1\nen+M9QCAtKE6PQAAAAAABcYkHgAAAACAjGASj9g2b96cdBNyh0x9kac/MgWKhT7vj0z9kakv8kw/\nJvGIbePGjUk3IXfI1Bd5+iNToFjo8/7I1B+Z+iLP9KOwHQAACaCwnT/GegBA2lDYDgAAAACAAmMS\nDwAAAABARjCJBwAAAAAgI5jEI7aVK1cm3YTcIVNf5OmPTIFioc/7I1N/ZOqLPNOPSTxiW7ZsWdJN\nyB0y9UWe/sgUKBb6vD8y9Uemvsgz/ahODwBAAqhO74+xHgCQNlSnBwAAAACgwJjEAwAAAACQEUzi\nEVtXV1fSTcgdMvVFnv7IFCgW+rw/MvVHpr7IM/2YxCO2W265Jekm5A6Z+iJPf2QKFAt93h+Z+iNT\nX+SZfhS2Q2wDAwOaMWNG0s3IFTL1RZ7+yNQPhe38Mdb7o8/7I1N/ZOqLPH1R2A6pQuf2R6a+yNMf\nmQLFQp/3R6b+yNQXeaYfk3gAAAAAADKCSTwAAAAAABnBJB6xrVmzJukm5A6Z+iJPf2QKFAt93h+Z\n+iNTX+SZfkziEduCBQuSbkLukKkv8vRHpkCx0Of9kak/MvVFnulHdXoAABJAdXp/jPUAgLShOj0A\nAAAAAAXGJB4AAAAAgIxgEo/Ytm/fnnQTcodMfZGnPzIFioU+749M/ZGpL/JMPybxiO2mm25Kugm5\nQ6a+yNMfmQLFQp/3R6b+yNQXeaYfk3jEdscddyTdhNwhU1/k6Y9MgWKhz/sjU39k6os8049JPGJj\n+Ql/ZOqLPP2RKVAs9Hl/ZOqPTH2RZ/oxiQcAAAAAICOYxAMAAAAAkBFM4hHbunXrkm5C7pCpL/L0\nR6ZAsdDn/ZGpPzL1RZ7pxyQesQ0MDCTdhNwhU1/k6Y9MgWKhz/sjU39k6os8089CCEm3YVKY2RJJ\n3d3d3VqyZEnSzQEAFFxPT49aWlokqSWE0JN0e/KAsR4AkDaNGO85Eg8AAAAAQEYwiQcAAAAAICOY\nxCO2ffv2Jd2E3CFTX+Tpj0yBYqHP+yNTf2TqizzTj0k8Ylu1alXSTcgdMvVFnv7IFCgW+rw/MvVH\npr7IM/2YxCO2tWvXJt2E3CFTX+Tpj0yBYqHP+yNTf2TqizzTj0k8YqPyrz8y9UWe/sgUKBb6vD8y\n9Uemvsgz/ZjEAwAAAACQEUziAQAAAADICCbxiG39+vVJNyF3yNQXefojU6BY6PP+yNQfmfoiz/Rj\nEo/Yenp6km5C7pCpL/L0R6ZAsdDn/ZGpPzL1RZ7pZyGEpNswKcxsiaTu7u5uijUAABLX09OjlpYW\nSWoJIfCJyQFjPQAgbRox3nMkHgAAAACAjGASDwAAAABARjCJBwAAAAAgI5jEI7b29vakm5A7ZOqL\nPP2RKVAs9Hl/ZOqPTH2RZ/oxiUdsq1evTroJuUOmvsjTH5kCxUKf90em/sjUF3mmH9XpAQBIANXp\n/THWAwDShur0AAAAAAAUGJN4AAAAAAAygkk8Ytu8eXPSTcgdMvVFnv7IFCgW+rw/MvVHpr7IM/1S\nM4k3s+vN7HEzO2RmD5rZ5aPs/5tm1m1mL5jZT83smslqKyLr1q1Lugm5Q6a+yNMfmWIiGOuzhz7v\nj0z9kakv8ky/VEzizWyFpM9KulnSKyQ9IuleM5s9zP4XSPqGpG9LukTS5yX9rZn99mS0F5E5c+Yk\n3YTcIVNf5OmPTBEXY3020ef9kak/MvVFnumXikm8pBsl3RlC+GoIYbuk90oakLRqmP3/RNLOEMJN\nIYSfhBD+WtI/lX4PAABIH8Z6AAAcJD6JN7OTJbUo+qZdkhSide++JWnpME/7jdLjle4dYX8AAJAQ\nxnoAAPwkPomXNFvSFEl9Ndv7JJ07zHPOHWb/08xsum/zAADABDHWAwDgZGrSDZhEp0jSj3/846Tb\nkRtbt25VT09P0s3IFTL1RZ7+yNRPxXh0SpLtyBnGemf0eX9k6o9MfZGnr0aM92mYxO+TdEzSOTXb\nz5H0i2Ge84th9n8uhDA4zHMukKR3vetd8VqJulpaWpJuQu6QqS/y9Eem7i6QdH/SjWgwxvoMo8/7\nI1N/ZOqLPBviAjmN94lP4kMIR8ysW9JrJd0tSWZmpfu3DfO0ByS9oWbbstL24dwr6Q8kPSHphQk0\nGQAAD6coGtDvTbgdDcdYDwAoMPfx3qK6Mskys7dL+rKiSrVbFVWefZukl4YQ9prZZySdF0K4prT/\nBZJ+IOkLkjYo+hDwV5LeGEKoLYIDAAASxlgPAICPxI/ES1II4a7SOrGfVHSq3MOSXhdC2Fva5VxJ\n8yv2f8LMfkfSrZJukLRb0rUM6gAApBNjPQAAPlJxJB4AAAAAAIwuDUvMAQAAAACAMcjNJN7Mrjez\nx83skJk9aGaXj7L/b5pZt5m9YGY/NbNrJqutWTGeTM3srWZ2n5ntMbNnzex+M1s2me3NgvH+O614\nXquZHTEz1vuoEKPfTzOzT5nZE6W+v9PM3j1Jzc2EGJn+gZk9bGbPm9nPzWy9mZ01We1NMzO72szu\nNrOnzexFM2sfw3MYm0bBeO+Lsd4fY70vxnp/jPV+khrrczGJN7MVkj4r6WZJr5D0iKR7S9fe1dv/\nAknfkPRtSZdI+rykvzWz356M9mbBeDOV9EpJ9ymqJLxE0nck3WNml0xCczMhRqbl550u6SuSuA60\nQsw8/1HSqyWtlPQrkt4h6ScNbmpmxHgvbVX0b/NvJP2aoiJlV0j60qQ0OP1mKrru+32SRr12jbFp\ndIz3vhjr/THW+2Ks98dY7y6ZsT6EkPmbpAclfb7ivikqgHPTMPuvk/T9mm0bJf1b0q8lLbfxZjrM\n73hU0seSfi1pucXNtPRv8xOK3mx7kn4dabnF6Pevl/SMpDOSbntabzEy/TNJP6vZtlrSrqRfS9pu\nkl6U1D7KPoxNo+fIeJ9gnsP8DsZ6h0wZ633yZKxvSKaM9WPPdtLG+swfiTezkyW1KPo2Q5IUojS+\nJWnpME/7DQ39pvPeEfYvlJiZ1v4Ok9Sk6I208OJmamYrJV2oaGBHScw83yxpm6QPmdluM/uJmf2l\nmZ3S8AZnQMxMH5A038zeUPod50j6PUn/2tjW5hZj0wgY730x1vtjrPfFWO+PsT4VXMalzE/iJc2W\nNEVSX832PkXL1dRz7jD7n2Zm032bl0lxMq21RtHpJXc5tivLxp2pmb1E0qcl/UEI4cXGNi9z4vwb\nXSTpakkvl/QWSR9QdErYXzeojVkz7kxDCPdLepekTWZ2WFKvpAOKvqHH+DE2jYzx3hdjvT/Gel+M\n9f4Y65PnMi7lYRKPlDGzd0r6uKTfCyHsS7o9WWRmJ0n6mqSbQwg7ypsTbFIenKToNKd3hhC2hRD+\nXdIHJV3Dh/l4zOzXFF3LtVbR9bGvU3Q06c4EmwVgEjDWTxxjfUMw1jtjrE+nqUk3wME+SccknVOz\n/RxJvxjmOb8YZv/nQgiDvs3LpDiZSpLM7PcVFbp4WwjhO41pXiaNN9MmSZdJutTMyt8en6To7MXD\nkpaFEP6zQW3Ngjj/RnslPR1COFix7ceKPjDNk7Sj7rOKI06mH5a0JYTwudL9R83sfZK+a2YfDSHU\nftOMkTE2jYzx3hdjvT/Gel+M9f4Y65PnMi5l/kh8COGIpG5Jry1vK12j9VpJ9w/ztAcq9y9ZVtpe\neDEzlZm9Q9J6Sb9f+uYTJTEyfU7Sr0u6VFHlykskfVHS9tLPDzW4yakW89/oFknnmdmMim2/qugb\n+90NampmxMx0hqSjNdteVFSdlaNJ48fYNALGe1+M9f4Y630x1vtjrE8Fn3Ep6Sp+HjdJb5c0IOkP\nJb1U0ekd+yXNKT3+GUlfqdj/Akn9iqoD/qqiJQEOS/qtpF9LWm4xMn1nKcP3Kvo2qXw7LenXkpbb\neDOt83wq1k4gT0XXbT4paZOklylaKuknkr6Y9GtJyy1GptdIGiz1+wsltUraKun+pF9LGm6lf3OX\nKPqA/qKkPy3dnz9MnoxNo2fKeJ9snoz1zpnWeT5j/QTyZKxvSKaM9SPnmchYn/gLdwzwfZKekHRI\n0TcZl1U89neS/qNm/1cq+ibqkKSfSfofSb+GtN3Gk6mitWKP1bltSPp1pOk23n+nNc9lYJ9gnorW\ni71X0sHSIH+LpOlJv4403WJker2kH5Qy3a1oLdnmpF9HGm6SXlUa0Ou+LzI2xc6V8T6hPBnr/TOt\n81zG+gnmyVjfkEwZ64fPMpGx3kq/CAAAAAAApFzmr4kHAAAAAKAomMQDAAAAAJARTOIBAAAAAMgI\nJvEAAAAAAGQEk3gAAAAAADKCSTwAAAAAABnBJB4AAAAAgIxgEg8AAAAAQEYwiQcAAAAAICOYxANo\nKDN7lZm9aGanle5fY2YHkm4XAADwwVgPTC4m8QAmQxjlPgAAyDbGemCSMIkHCszMTk66DQAAoHEY\n64H8YRIPFIiZfcfMbjezW81sr6R/N7PTzexvzWyPmT1rZt8ys4trnvdmM9tqZofMbK+Z/e+Kx95l\nZt8zs+fMrNfMvmZmcyb9xQEAAMZ6oACYxAPF84eSBiVdJem9kv5R0tmSXidpiaQeSd8yszMkycx+\nR1KnpG9IulTSb0p6sOL3TZX0MUkXS/pdSQsl/d0kvA4AAFAfYz2QYxYCl6sARWFm35HUFEK4rHS/\nVdGAPTeEcKRiv59JWhdC+Fsz2yLpsRDCNWP8G5dJeqj0dwbM7FWS/kPSmSGE58zsGkm3hhDO8n11\nAACAsR7IP47EA8XTXfHzJZKaJD1jZv3lm6QLJC0q7XOpooG5LjNrMbO7zexJM/v/27l/Fj3KKIzD\nvwPBIqRKZWVIt41NIBY2FmITMFUstMonSBNYSBfBzyHYWNu5xRZqY5dAIIXtgoKYQgIhEAiTYt8i\nLLuwkXXDvHtdzQych5nTHe758zyvftmUPjrzzgGA0zDrYYtdet8NAOfuxVvnV6q/qs+qObLu383x\n5UkXmpnL1V71c/VN9U+Hn9jtVR+cUb8AwLsx62GLCfFwsT2qPqxeL8tycMKaJ9Xn1Q/H1Haqq9WD\nZVn+rJqZT/6PRgGA/8Sshy3jc3q4wJZl2a9+r36amS9m5trMfDoz383Mjc2yb6uvZ+bhzOzMzMcz\ns7upHVSvqnszc31mbne48c1RR5/8AwDnwKyH7SPEw8Vy3E6Wt6rfqu+rP6ofO/zH7e+qZVl+rb6q\nvqweV/vVzU3tWXW3ulM9rXar+6e8LwBw9sx62HJ2pwcAAICV8CYeAAAAVkKIBwAAgJUQ4gEAAGAl\nhHgAAABYCSEeAAAAVkKIBwAAgJUQ4gEAAGAlhHgAAABYCSEeAAAAVkKIBwAAgJUQ4gEAAGAlhHgA\nAABYiTeuL+hYXfgY1gAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x1179c1780>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "alg_dec = im.p_r_dic(scores_blog)\n",
    "min_dec = im.p_r_dic(scores_minmax)\n",
    "cos_dec = im.p_r_dic(scores_cos)\n",
    "\n",
    "bsame_prec = [alg_dec[sigma][0] for sigma in alg_dec.keys()]\n",
    "bsame_rec = [alg_dec[sigma][1] for sigma in alg_dec.keys()]\n",
    "bdiff_prec = [alg_dec[sigma][2] for sigma in alg_dec.keys()]\n",
    "bdiff_rec = [alg_dec[sigma][3] for sigma in alg_dec.keys()]\n",
    "\n",
    "\n",
    "msame_prec = [min_dec[sigma][0] for sigma in min_dec.keys()]\n",
    "msame_rec = [min_dec[sigma][1] for sigma in min_dec.keys()]\n",
    "mdiff_prec = [min_dec[sigma][2] for sigma in min_dec.keys()]\n",
    "mdiff_rec = [min_dec[sigma][3] for sigma in min_dec.keys()]\n",
    "\n",
    "csame_prec = [cos_dec[sigma][0] for sigma in cos_dec.keys()]\n",
    "csame_rec = [cos_dec[sigma][1] for sigma in cos_dec.keys()]\n",
    "cdiff_prec = [cos_dec[sigma][2] for sigma in cos_dec.keys()]\n",
    "cdiff_rec = [cos_dec[sigma][3] for sigma in cos_dec.keys()]\n",
    "\n",
    "plt.figure(1, figsize= (12,6))\n",
    "plt.subplot(1,2,1)\n",
    "plt.plot(bsame_rec, bsame_prec, '.', msame_rec, msame_prec, '.', csame_rec, csame_prec, '.')\n",
    "plt.legend(['blog', 'minmax', 'cosine'])\n",
    "plt.axis([0,1,0,1.1])\n",
    "plt.title('Same author')\n",
    "plt.xlabel('recall')\n",
    "plt.ylabel('precision')\n",
    "plt.grid()\n",
    "\n",
    "plt.subplot(1,2,2)\n",
    "plt.plot(bdiff_rec, bdiff_prec, '.', mdiff_rec, mdiff_prec, '.', cdiff_rec, cdiff_prec, '.')\n",
    "plt.legend(['blog', 'minmax', 'cosine'])\n",
    "plt.axis([0,1,0,1.1])\n",
    "plt.title('Different author')\n",
    "plt.xlabel('recall')\n",
    "plt.ylabel('precision')\n",
    "plt.grid()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## No difference! - WHY?!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Algorithms"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "1. Generate a set of impostors $Y_1, . . . , Y_m$.\n",
    "2. Compute $score_X(Y)$ = the number of choices of feature sets (out of 100) for which $sim(X, Y) > sim(X, Y_i)$, for all $i = 1, . . . , m$.\n",
    "3. Repeat the above with impostors $X_1, . . . , X_m$ and compute scoreY(X) in an analogous manner.\n",
    "4. If $average(score_X(Y), score_Y(X))$ is greater than a threshold $s*$, assign 〈X, Y〉 to same-author."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def blog_same_author(x,y,text_corpus, threshold, nr_imposters = 25, return_score = False):\n",
    "    '''Return true if x and y are by the same author according to the algorithm in the paper'''\n",
    "    imposters_y = imposters(y, text_corpus.Y, n = nr_imposters)\n",
    "    score_xy = get_score(x,y,imposters_y, sm.cminmax)\n",
    "    \n",
    "    imposters_x = imposters(x, text_corpus.X, n = nr_imposters)\n",
    "    score_yx = get_score(y,x,imposters_x, sm.cminmax)\n",
    "    \n",
    "    if return_score == True:\n",
    "        return (score_xy + score_yx ) /2.0\n",
    "\n",
    "    if (score_xy + score_yx ) /2.0 > threshold:\n",
    "        return True\n",
    "    else:\n",
    "        return False"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "## Create Blog-universe\n",
    "1. Compute the min-max similarity to Y of each document in the universe. Select the m most similar documents as potential impostors.\n",
    "2. Randomly select n actual impostors from among the potential impostors."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def imposters(y, universe, m = 250, n = 25):\n",
    "    '''Return n imposters. Compute the m most similar files in universe and randomly select n from them '''\n",
    "    minmax_vec = np.array([sm.cminmax(t,y) for t in universe])\n",
    "    pot_imp_ind = minmax_vec.argsort()[:-(m+1):-1] # Last m entries\n",
    "    pot_imp_ind = np.sort(np.random.choice(pot_imp_ind, n))\n",
    "    return [universe[k] for k in pot_imp_ind[::-1]]  # Have the most similar imposters first, to hope for a quicker break in the get_score algorithm\n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Compute Score\n",
    "\n",
    "Compute $score_X(Y)$ = the number of choices of feature sets (out of 100) for which $sim(X, Y) > sim(X, Y_i)$, for all $i = 1, . . . , m$."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "@jit\n",
    "def get_score(x,y,imposters,sim):\n",
    "    '''Compute for how many random feature sets out of 100 sim(x,y) is greater than sim(x,z) for all z in imposters'''\t\n",
    "    score = 0\n",
    "    sim_xy = sim(x,y) \n",
    "    for k in range(100):\n",
    "        ran_el = np.sort(np.random.choice(range(len(x)), (1,round(len(x) / 2)), replace = False))      # Sorting makes the next steps faster  \n",
    "        c_x = x[ran_el][0]\n",
    "        c_y = y[ran_el][0]\n",
    "        sim_xy = sim(c_x,c_y)\n",
    "\n",
    "\n",
    "        for yi in imposters:\n",
    "            if sim(c_x, np.take(yi,ran_el)[0]) > sim_xy:        # If there is one text in imposter with a greater score, there is no need to continue\n",
    "                break\n",
    "        else:\n",
    "            score += 1\n",
    "\n",
    "    return score / 100.0  # "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Algorithms to store the results and compute precision and recall:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def result_array(corp, sigma= None):\n",
    "    '''\n",
    "    Returns an array with tuples containing the average score given by the algorithms whether both\n",
    "    texts are by the same author and a boolean whether the pair really is by the same author    \n",
    "    '''\n",
    "    res = []\n",
    "    for k in range(corp.get_length()):\n",
    "        alg = blog_same_author(*corp.get_pair(k), corp, threshold = sigma, return_score = True)\n",
    "        same_author = corp.same_author(k)\n",
    "        res.append((alg, same_author))\n",
    "    return res"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def pre_recall(result_array, threshold):\n",
    "    ''' Return precision and recall values for the same-author and different-author classes'''\n",
    "    same_same, same_dif, dif_same, dif_dif = 0,0,0,0\n",
    "    for k in range(len(result_array)):\n",
    "        res = (result_array[k][0] > threshold )\n",
    "        same_author = result_array[k][1]\n",
    "        \n",
    "        if same_author and res:\n",
    "            same_same += 1\n",
    "        if same_author and not res:\n",
    "            same_dif +=1\n",
    "        if not same_author and res:\n",
    "            dif_same +=1\n",
    "        if not same_author and not res:\n",
    "            dif_dif +=1\n",
    "    try:\n",
    "        precision_same = same_same / (same_same + dif_same)\n",
    "        recall_same = same_same / (same_same + same_dif)\n",
    "\n",
    "        precision_dif = dif_dif / (same_dif + dif_dif)\n",
    "        recall_dif = dif_dif / (dif_dif + dif_same)\n",
    "\n",
    "        acc = (same_same + dif_dif) / len(result_array)\n",
    "    except ZeroDivisionError:\n",
    "        return None\n",
    "\n",
    "    return precision_same, recall_same, precision_dif, recall_dif, acc \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def p_r_dic(result_array):\n",
    "    '''Create a dictionary with threshold values and the decisions based on this threshold'''\n",
    "    s_range = np.linspace(0, 0.8, 1000)\n",
    "    res = {}\n",
    "    for sigma in s_range:\n",
    "        pc = pre_recall(result_array, sigma)\n",
    "        if pc == None:\n",
    "            continue\n",
    "        res[sigma] = pre_recall(result_array, sigma)\n",
    "    return res"
   ]
  }
 ],
 "metadata": {
  "anaconda-cloud": {},
  "kernelspec": {
   "display_name": "Python [default]",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.5.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}

Отредактировано vasyabylba (Апрель 15, 2019 13:29:56)

Прикреплённый файлы:
attachment Presentation.ipynb (63,6 KБ)

Офлайн

#6 Апрель 15, 2019 22:09:39

doza_and
От:
Зарегистрирован: 2010-08-15
Сообщения: 3251
Репутация: +  176  -
Профиль   Отправить e-mail  

Нужна помощь в объединении файлов Python в один проект

vasyabylba
Нужна помощь в объединении файлов о один проект
В питоне нет понятия “проект”. Следовательно файлы объединить в один проект невозможно.

Насколько я понял код не ваш. Если к коду нет документации, тестов и вменяемых примеров то смело можете выкидывать код на помойку.



Офлайн

#7 Апрель 15, 2019 22:25:24

vasyabylba
Зарегистрирован: 2019-04-15
Сообщения: 6
Репутация: +  0  -
Профиль   Отправить e-mail  

Нужна помощь в объединении файлов Python в один проект

doza_and
Это и так ясно было. Мне нужно чтобы кто-то сказал мне как это можно объединить.

Офлайн

#8 Апрель 15, 2019 23:12:28

doza_and
От:
Зарегистрирован: 2010-08-15
Сообщения: 3251
Репутация: +  176  -
Профиль   Отправить e-mail  

Нужна помощь в объединении файлов Python в один проект

vasyabylba
кто-то сказал мне как это можно объединить.
Я помоему ясно выразился- никак. Не теряйте времени и ищите нормальную библиотеку.



Офлайн

#9 Апрель 16, 2019 01:27:54

py.user.next
От:
Зарегистрирован: 2010-04-29
Сообщения: 6191
Репутация: +  556  -
Профиль   Отправить e-mail  

Нужна помощь в объединении файлов Python в один проект

vasyabylba
Скорее всего нужен ещё какой-то код (так называемый Main) для реализации идеи.
Ну вот файл main.py делаешь и в нём импортируешь свои модули и в функции main() пишешь код, который использует из модулей функции.
  
#!/usr/bin/env python3
 
def main():
    pass
 
if __name__ == '__main__':
    main()

vasyabylba
Суть: определение являются ли два текста от одного автора.
Судя по кодам, они не очень грамотно сделаны. Так что, очень может быть, эти коды и не работают вовсе.

В общем, тебе надо заново писать программу (спроектировать её сначала). Потом по проекту начинаешь делать её части. И вот если вдруг эти коды можно использовать для создания частей программы, то ты их и используешь (берёшь целиком или фрагменты из них используешь для составления части программы). То есть при разработке нужно думать так, что ты можешь написать программу с нуля, даже не имея никаких кодов. А если есть какие-то коды, которые можно использовать, то это просто облегчает написание тобой твоей программы. Но очень редко такие хреновые коды, как у тебя, можно использовать сразу. Часто и функции из них брать не стоит, а лучше новые написать. Сразу так писать коды, чтобы их можно было в любую программу потом вставить при разработке, это нужно много разных программ написать, научиться делать программы модульно. С первого раза такое не получится, да и с третьего тоже. Но это возможно. Профессиональные программы так и выглядят: они состоят из множества независимых друг от друга частей, которые полностью автономны сами по себе. Такие части не знают, в каком они проекте используются, они просто предоставляют чёткий интерфейс для работы с собой, а уж кто этот интерфейс будет использовать, для них не важно.



Отредактировано py.user.next (Апрель 16, 2019 01:30:02)

Офлайн

Board footer

Модераторировать

Powered by DjangoBB

Lo-Fi Version