{ "metadata": { "language": "Julia", "name": "" }, "nbformat": 3, "nbformat_minor": 0, "worksheets": [ { "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "Julia Tutorial\n", "==============\n", "\n", "Welcome to [Julia](http://www.julialang.org)!\n", "\n", "Julia is a new language focused on technical and numerical computing, and similar to systems such as Octave (or MATLAB), IDL, SciLab, and Python+NumPy+SciPy.\n", "\n", "Although Julia's current focus is technical computing, due to a well-considered design and an extensive mathematical library, Julia has also been used to write such things as [web servers](https://github.com/hackerschool/HttpServer.jl), and [microframeworks](https://github.com/hackerschool/Morsel.jl), [raytracers](https://github.com/jakebolewski/rays) and a [Quake rendering engine](https://github.com/jayschwa/Quake2.jl).\n", "\n", "This tutorial is organized as follows:\n", "\n", "- Why Julia?\n", "- Community and contribution\n", "- Basics: introduction to the language\n", "- Numbers, Arithmetic, and Arrays\n", "- Types and Multiple Dispatch" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Why Julia?\n", "\n", "Julia has been designed from the ground up for the needs of modern technical computing.\n", "\n", "* Julia's efficient front-end and LLVM-based JIT compiler provide [compelling performance](http://julialang.org/#High-Performance.JIT.Compiler) within 2x of C on a range of benchmarks.\n", "* Therefore, the transition from \"correct\" to \"fast\" can be accomplished within a *single language*, drastically simplifying the development experience.\n", "* Optional type annotations and multiple dispatch combine to form a powerful paradigm for both mathematical and general programming.\n", "* Scalable computing is included from the start, with powerful distributed array primitives and interfaces to a growing number of computation platforms.\n", "* Julia is fun: express powerful ideas in concise code with judicious use of anonymous functions, operator overloading, Unicode operators, duck-typing, excellent shell support, and much more!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Community and contribution\n", "\n", "As a young, open-source language and community, there are nearly unlimited opportunities for contribution. The community strives to be welcoming to new contributors in any capacity. Some suggested areas to pursue contribution:\n", "\n", "- **documentation**: as with any open-source project, good documentation is critical for continued growth (and as such, contributions are deeply appreciated!)\n", "\n", "- **packages**: there are a huge number of \"greenfield\" packages just waiting to be written (or implemented in pure Julia). Come build something new, just the way you've always wanted it!\n", "\n", "- **library**: Because Julia's library is written in the language itself, the barrier-to-entry for contributions to the core repository is much lower than other projects written in a mix of languages. [Pull requests](https://help.github.com/articles/using-pull-requests) are always welcome! [Here](https://github.com/julialang/julia/issues?labels=up+for+grabs%2Cfeature&page=1&state=open) is one list of potential areas to contribute.\n", "\n", "- **core**: the core of Julia is written in high-quality, terse, highly-readable C. Julia's code generation is written in C++ (with `extern C` entry points) in order to interface with the [LLVM](http://llvm.org/) JIT engine. There are many [optimizations](https://github.com/JuliaLang/julia/issues/3440) left to be written (for even better performance), and anyone with the skills and interest is welcome and encouraged to contribute.\n", "\n", "## Julia Activity Hubs\n", "\n", "- #### [The Julia Language project on GitHub](https://github.com/julialang/julia): source code, issue tracker\n", "\n", "- #### [julia-users mailing list](https://groups.google.com/forum/#!forum/julia-users) usage questions and general discussions\n", "- #### [julia-dev mailing list](https://groups.google.com/forum/#!forum/julia-dev) core language and standard library discussions (note: both mailing lists are open to public)\n", "\n", "- #### [Packages](http://docs.julialang.org/en/latest/packages/packagelist/) and the [package manager](http://docs.julialang.org/en/latest/manual/packages/), fostering collaborative ecosystem development." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Basics" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Hello, world!\n", "\n", "\n", "We start with the canonical introductory program:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "println(\"Hello, world!\")" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "Hello, world!\n" ] } ], "prompt_number": 3 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Simple enough, right? Now, let's make a function:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "function sayhello(name)\n", " println(\"Hello, \", name)\n", "end" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 1, "text": [ "sayhello (generic function with 1 method)" ] } ], "prompt_number": 1 }, { "cell_type": "markdown", "metadata": {}, "source": [ "...and call it:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "sayhello(\"friend!\")" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "Hello, friend!" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\n" ] } ], "prompt_number": 2 }, { "cell_type": "markdown", "metadata": {}, "source": [ "The \"Hello, world\" example could be written on the command line as:\n", "\n", "```\n", "julia -e 'println(\"Hello, world\")\n", "```\n", "\n", "Or as a script, by placing the command in `hello.jl` and running:\n", "\n", "```\n", "> julia hello.jl\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To write text without appending a newline, use `print()`:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "print(1)\n", "print(\" 2\")" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "1" ] }, { "output_type": "stream", "stream": "stdout", "text": [ " 2" ] } ], "prompt_number": 6 }, { "cell_type": "markdown", "metadata": {}, "source": [ "IJulia Notes\n", "------------\n", "\n", "This tutorial can be run in IJulia, and viewed statically using the IPython notebook viewer (IJulia is built on the remarkable [IPython](www.ipython.org) software)\n", "\n", "In IJulia, each **`In [#]`** line denotes an individual **cell** containing one or more lines of Julia code, which may be executed by pressing `Shift-Enter`. Program output or errors will be displayed immediately below the input cell, and the return value of the last command in the cell will be printed in **`Out[#]`** box.\n", "\n", "```\n", "In [1]: println(\"Hello, world!\")\n", "Hello, world! <-- standard output is captured and printed sans Out[] prefix.\n", "\n", "In [3]: \"Hello, friend!\"\n", "Out[3]: \"Hello, friend!\" <-- expression values are printed in the Out[] cell\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Variables\n", "\n", "Variables allow storage and reuse of the results of earlier calculations. There are very few limits on variable names, and variable names may be written in Unicode, as will be demonstrated shortly.\n", "\n", "### Naming variables and Assigning them values\n", "Use a single equal `=` sign to assign variables." ] }, { "cell_type": "code", "collapsed": false, "input": [ "i = 2.0" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 1, "text": [ "2.0" ] } ], "prompt_number": 1 }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can define complex variables using the special symbol **`im`** denoting the _imaginary unit_:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "e = 1+1im" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 2, "text": [ "1 + 1im" ] } ], "prompt_number": 2 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Julia contains a set of built-in constants, which may be used in computation or assigned to other variable names:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "supercalifragilisticexpialidocious = pi\n", "\n", "supercalifragilisticexpialidocious/2" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 3, "text": [ "1.5707963267948966" ] } ], "prompt_number": 3 }, { "cell_type": "markdown", "metadata": {}, "source": [ "The cell above calculates `pi/2` using the special variable *supercalifragilisticexpialidocious* that we have set as equal `pi`, and prints the result to `Out [ ]`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can even use names of built-in variables and functions, if you so choose. (This is usually not a good idea since it is very easy to write confusing code; Julia will **warn** you against overwriting built-ins, but will not stop you in this case)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You *cannot*, however, use the names of Julia keywords for your variable names." ] }, { "cell_type": "code", "collapsed": false, "input": [ "end=0.5im" ], "language": "python", "metadata": {}, "outputs": [ { "ename": "LoadError", "evalue": "syntax: unexpected end\nat In[13]:1", "output_type": "pyerr", "traceback": [ "syntax: unexpected end\nat In[13]:1" ] } ], "prompt_number": 13 }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Comparison\n", "\n", "Julia supports the standard set of comparison operators:\n", "\n", "```\n", "< > == <= >=\n", "```" ] }, { "cell_type": "code", "collapsed": false, "input": [ "supercalifragilisticexpialidocious == pi" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 10, "text": [ "true" ] } ], "prompt_number": 10 }, { "cell_type": "code", "collapsed": false, "input": [ "pi == 3.2 # Sorry, Indiana" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 3, "text": [ "false" ] } ], "prompt_number": 3 }, { "cell_type": "code", "collapsed": false, "input": [ "pi < 3.2" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 5, "text": [ "true" ] } ], "prompt_number": 5 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Inexact comparisons should use `isapprox`" ] }, { "cell_type": "code", "collapsed": false, "input": [ "isapprox(exp(im * pi) + 1, 0)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 16, "text": [ "true" ] } ], "prompt_number": 16 }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Characters and strings\n", "\n", "A single character is denoted with single-quote marks, and characters may be input by ASCII or Unicode value using the `char` function:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "'a' == char(97) # ASCII 97 is 'a'" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 17, "text": [ "true" ] } ], "prompt_number": 17 }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Strings and escaped characters\n", "\n", "Strings are denoted by double-quote signs, as in many other languages. Some characters may only be printed as **escape sequences**. An escape sequence starts with a backslash `\\` and ends with a single character that follows it.\n", "\n", "Here are two very common examples:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "println(\"1\\n2\") #Newline" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "1\n", "2\n" ] } ], "prompt_number": 20 }, { "cell_type": "code", "collapsed": false, "input": [ "println(\"1\\t2\") #Tab" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "1\t2\n" ] } ], "prompt_number": 21 }, { "cell_type": "markdown", "metadata": {}, "source": [ "To print the baskslash character itself, it must be escaped with another backslash." ] }, { "cell_type": "code", "collapsed": false, "input": [ "println(\"1\\\\2\") #backslash" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "1\\2\n" ] } ], "prompt_number": 17 }, { "cell_type": "markdown", "metadata": {}, "source": [ "The dollar sign has special meaning in Julia and must be escaped to print correctly." ] }, { "cell_type": "code", "collapsed": false, "input": [ "println(\"\\$\")" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "$\n" ] } ], "prompt_number": 18 }, { "cell_type": "markdown", "metadata": {}, "source": [ "What happens if we don't escape it?" ] }, { "cell_type": "code", "collapsed": false, "input": [ "println(\"$pi\")" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "\u03c0 = 3.1415926535897...\n" ] } ], "prompt_number": 19 }, { "cell_type": "markdown", "metadata": {}, "source": [ "The dollar sign performs **string interpolation**, splicing the value of the variable name after the `$` into the output. Attempting to splice a non-existent variable results in an error:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "println(\"$\u03b1\")" ], "language": "python", "metadata": {}, "outputs": [ { "ename": "LoadError", "evalue": "\u03b1 not defined\nat In[20]:1", "output_type": "pyerr", "traceback": [ "\u03b1 not defined\nat In[20]:1" ] } ], "prompt_number": 20 }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Unicode support (or: fancy characters)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Julia supports Unicode for string contents *as well as* variable names. So variables:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "\u03b1 = 7.29735257E-3" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 21, "text": [ "0.00729735257" ] } ], "prompt_number": 21 }, { "cell_type": "markdown", "metadata": {}, "source": [ "and formulae:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "z = 3+4im\n", "\u03be = 1/z" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 22, "text": [ "0.09090909090909091" ] } ], "prompt_number": 22 }, { "cell_type": "markdown", "metadata": {}, "source": [ "May be written with any appropriate character.\n", "\n", "Or even using non-Latin alphabets!" ] }, { "cell_type": "code", "collapsed": false, "input": [ "\u30a2\u30eb\u30b3\u30fc\u30eb = 0.1337\n", "\u30a2\u30eb\u30b3\u30fc\u30eb^2" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 23, "text": [ "0.017875690000000003" ] } ], "prompt_number": 23 }, { "cell_type": "markdown", "metadata": {}, "source": [ "On some operating systems and browsers, you may even be able to print pizza!:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "print(char(0x1f355))\n", "print(\" = \")\n", "print('\\U263A')" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "\ud83c\udf55" ] }, { "output_type": "stream", "stream": "stdout", "text": [ " = \u263a" ] } ], "prompt_number": 24 }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Regular expressions\n", "\n", "Julia includes a Perl-compatible regular expression support. A regular expression is declared using the special syntax: `r` prefixed immediately before the regex string:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "re = r\".*(brown fox).*(lazy dog)\"" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 25, "text": [ "r\".*(brown fox).*(lazy dog)\"" ] } ], "prompt_number": 25 }, { "cell_type": "code", "collapsed": false, "input": [ "m = match(re, \"The quick brown fox jumped over the lazy dog.\")" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 26, "text": [ "RegexMatch(\"The quick brown fox jumped over the lazy dog\", 1=\"brown fox\", 2=\"lazy dog\")" ] } ], "prompt_number": 26 }, { "cell_type": "code", "collapsed": false, "input": [ "m.captures[1]" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 27, "text": [ "\"brown fox\"" ] } ], "prompt_number": 27 }, { "cell_type": "code", "collapsed": false, "input": [ "m.captures[2]" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 28, "text": [ "\"lazy dog\"" ] } ], "prompt_number": 28 }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Control Flow\n", "\n", "### if/else\n", "\n", "Parentheses are optional:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "if false\n", " println(\"Twilight zone...\")\n", " \n", "elseif ( 1 == 2 )\n", " println(\"We can actually do math, right?\")\n", " \n", "else\n", " println(\"All is well. Both conditions are false.\")\n", "end" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "All is well. Both conditions are false." ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\n" ] } ], "prompt_number": 29 }, { "cell_type": "markdown", "metadata": {}, "source": [ "### for loops" ] }, { "cell_type": "code", "collapsed": false, "input": [ "for i in 1:10\n", " print(i, \" \")\n", "end" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "1 2 3 4 5 6 7 8 9 10 " ] } ], "prompt_number": 30 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Breaking it down:\n", "\n", "- the expression **`i in 1:10`** returns **an iterator** over the range 1 to 10\n", "- for loop executes the enclosing codeblock for all values of *i*\n", "- instead of `println`, we use simply `print`, which does not add a newline after each output\n", "\n", "We can also skip elements using the `continue` keyword:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "for i in 1:10\n", " (i > 5 && i < 9) && continue\n", " \n", " print(i, \" \")\n", "end" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "1 2 3 4 5 9 10 " ] } ], "prompt_number": 31 }, { "cell_type": "markdown", "metadata": {}, "source": [ "[Julia also supports `while` loops, expression blocks, `try-catch` statements for error handling, short-circuited evaluation, and coroutines.](http://docs.julialang.org/en/latest/manual/control-flow/)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### *Exercise*\n", "\n", "Write the for loop above as a while loop instead." ] }, { "cell_type": "code", "collapsed": false, "input": [], "language": "python", "metadata": {}, "outputs": [] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Input and output\n", "\n", "So how do we get data out of and into Julia? Here is the simplest possible example:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "# opening a file:\n", "\n", "vecfile = open(\"/tmp/myvector.txt\", \"w\")" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 119, "text": [ "IOStream()" ] } ], "prompt_number": 119 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notice that the `open` function returns a variable with the type *IOStream*. This is important, because the behavior of other functions will be influenced by the type of this variable.\n", "\n", "### *Exercise*\n", "\n", "Write a function that prints the comma-separated numbers from 1 to 5, in increments of .5, to the file handle `vecfile` defined above.\n", "\n", "- HINT 1: the `print` function is overloaded, and will accept an *IOStream* as the **first** argument.\n", "- HINT 2: the range syntax accepts a middle argument specifying step-size: *start*:*step*:*end*\n", "- Bonus: don't print the final comma." ] }, { "cell_type": "code", "collapsed": false, "input": [ "function writenumbers(fhandle)\n", " # implementation goes here\n", "end" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 23, "text": [ "writenumbers (generic function with 1 method)" ] } ], "prompt_number": 23 }, { "cell_type": "code", "collapsed": false, "input": [ "writenumbers(vecfile)" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 121 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Be sure to clean up the file handle:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "close(vecfile)" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 122 }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Reading it back\n", "\n", "Now that we have a file, let's read the data back in:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "f_in = open(\"/tmp/myvector.txt\", \"r\")\n", "myvec1 = split( readall(f_in), \",\" )" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 26, "text": [ "10-element Array{String,1}:\n", " \"1.0\"\n", " \"1.5\"\n", " \"2.0\"\n", " \"2.5\"\n", " \"3.0\"\n", " \"3.5\"\n", " \"4.0\"\n", " \"4.5\"\n", " \"5.0\"\n", " \"\" " ] } ], "prompt_number": 26 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Since this is a CSV file, we could also use the built-in `readcsv` function:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "seekstart(f_in)\n", "myvec1 = readcsv( f_in )" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 27, "text": [ "1x9 Array{Float64,2}:\n", " 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0" ] } ], "prompt_number": 27 }, { "cell_type": "markdown", "metadata": {}, "source": [ "We have used the built-in function `seekstart` to go back to the beginning of the file, so that we don't have to open it again." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Data structures\n", "\n", "Julia includes a number of useful data structures; here are two important examples:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Lists\n", "\n", "Here we define a list containing a strange, heterogenous grab bag of stuff:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "allmixedup = { 1, pi, \"foo\", myvec1, \"biggles\", sayhello }" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 28, "text": [ "6-element Array{Any,1}:\n", " 1 \n", " \u03c0 = 3.1415926535897... \n", " \"foo\" \n", " 1x9 Array{Float64,2}:\n", " 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0\n", " \"biggles\" \n", " sayhello " ] } ], "prompt_number": 28 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Square brackets, `[ ]`, are used to retrieve the contents at a specific index.\n", "\n", "**Note: Julia uses 1-based indexing just like Fortran. Everything will be ok.**" ] }, { "cell_type": "code", "collapsed": false, "input": [ "allmixedup[1]" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 30, "text": [ "1" ] } ], "prompt_number": 30 }, { "cell_type": "code", "collapsed": false, "input": [ "allmixedup[5]" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 31, "text": [ "\"biggles\"" ] } ], "prompt_number": 31 }, { "cell_type": "code", "collapsed": false, "input": [ "allmixedup[6]" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 38, "text": [ "sayhello (generic function with 1 method)" ] } ], "prompt_number": 38 }, { "cell_type": "markdown", "metadata": {}, "source": [ "As demonstrated above, can store a reference to a function like any other object!\n", "\n", "List elements can be set by assigning to an index:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "push!(allmixedup, \"another random string\")" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 126, "text": [ "7-element Array{Any,1}:\n", " 1 \n", " \u03c0 = 3.1415926535897... \n", " \"foo\" \n", " [\"\"] \n", " \"biggles\" \n", " sayhello \n", " \"another random string\"" ] } ], "prompt_number": 126 }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### *Exercise*\n", "call the `sayhello` function by using the reference stored in *allmixedup*." ] }, { "cell_type": "code", "collapsed": false, "input": [], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 32 }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Dictionaries (aka: maps or associative arrays)\n", "\n", "Julia provides an efficient *associative array* implementation and, like Python, there is a compact comprehension syntax to define a mapping set: " ] }, { "cell_type": "code", "collapsed": false, "input": [ "mymap = { 1 => \"one\", \"two\" => 2, \"sayhello\" => sayhello, sayhello => \"function called `sayhello`\"}" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 41, "text": [ "{\"sayhello\"=>sayhello,sayhello=>\"function called `sayhello`\",1=>\"one\",\"two\"=>2}" ] } ], "prompt_number": 41 }, { "cell_type": "markdown", "metadata": {}, "source": [ "(`mymap` above accepts any type of object, but as will be seen, dictionary key and value types may be restricted)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Key-value pairs may also be assigned individually:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "mymap[7.3] = \"seven point 3\"" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 42, "text": [ "\"seven point 3\"" ] } ], "prompt_number": 42 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Map indexing is similar to lists:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "mymap[1]" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 43, "text": [ "\"one\"" ] } ], "prompt_number": 43 }, { "cell_type": "code", "collapsed": false, "input": [ "mymap[\"two\"]" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 44, "text": [ "2" ] } ], "prompt_number": 44 }, { "cell_type": "code", "collapsed": false, "input": [ "mymap[\"sayhello\"]" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 45, "text": [ "sayhello (generic function with 1 method)" ] } ], "prompt_number": 45 }, { "cell_type": "code", "collapsed": false, "input": [ "mymap[sayhello]" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 46, "text": [ "\"function called `sayhello`\"" ] } ], "prompt_number": 46 }, { "cell_type": "code", "collapsed": false, "input": [ "mymap[7.3]" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 47, "text": [ "\"seven point 3\"" ] } ], "prompt_number": 47 }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Numbers, Arithmetic, and Arrays" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Numbers" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Numbers are the soul of Julia, so it is fitting that **all numeric types are defined in the language itself**. For example, the 64-bit integer is declared as follows (in *src/base/base.jl*, for the curious):" ] }, { "cell_type": "code", "collapsed": false, "input": [ "bitstype 64 Int64 <: Signed" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 48 }, { "cell_type": "markdown", "metadata": {}, "source": [ "This means that **you can define your own data types in pure Julia, and expect the same performance profile as \"core\" types!**\n", "\n", "*One goal of Julia is to be fast enough that moving to another language for performance should never be necessary.*" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Binary Representations" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can write numeric literals in binary representation:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "0b10 == 2 # There are 10 kinds of programmers..." ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 49, "text": [ "true" ] } ], "prompt_number": 49 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Julia is also quite willing to expose the *when you want to see them*. Binary representations are one example, and can be easily inspected:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "[ 0 bits(0); 1 bits(1); 2 bits(2); 3 bits(3); 7 bits(7); 8 bits(8);\n", " 16 bits(16); 17 bits(17); 32 bits(32); 33 bits(33); 35 bits(35); 64 bits(64);]" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 50, "text": [ "12x2 Array{Any,2}:\n", " 0 \"0000000000000000000000000000000000000000000000000000000000000000\"\n", " 1 \"0000000000000000000000000000000000000000000000000000000000000001\"\n", " 2 \"0000000000000000000000000000000000000000000000000000000000000010\"\n", " 3 \"0000000000000000000000000000000000000000000000000000000000000011\"\n", " 7 \"0000000000000000000000000000000000000000000000000000000000000111\"\n", " 8 \"0000000000000000000000000000000000000000000000000000000000001000\"\n", " 16 \"0000000000000000000000000000000000000000000000000000000000010000\"\n", " 17 \"0000000000000000000000000000000000000000000000000000000000010001\"\n", " 32 \"0000000000000000000000000000000000000000000000000000000000100000\"\n", " 33 \"0000000000000000000000000000000000000000000000000000000000100001\"\n", " 35 \"0000000000000000000000000000000000000000000000000000000000100011\"\n", " 64 \"0000000000000000000000000000000000000000000000000000000001000000\"" ] } ], "prompt_number": 50 }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Integers" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Literal values without a decimal point are interpreted as integers:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "x = 1" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 51, "text": [ "1" ] } ], "prompt_number": 51 }, { "cell_type": "code", "collapsed": false, "input": [ "typeof(x)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 52, "text": [ "Int64" ] } ], "prompt_number": 52 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Julia has both signed and unsigned Integer types for **8, 16, 32, 64, and 128**-bits.\n", "\n", "Each Integer type can hold a specific and finite range of values:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "[ typemin(Uint8) typemax(Uint8) ;\n", " typemin(Int8) typemax(Int8) ;\n", " typemin(Int64) typemax(Int64) ; ]" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 33, "text": [ "3x2 Array{Int64,2}:\n", " 0 255\n", " -128 127\n", " -9223372036854775808 9223372036854775807" ] } ], "prompt_number": 33 }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Floating point" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Literals entered with a decimal point are intepreted as floating-point numbers:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "y = 2.0" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 54, "text": [ "2.0" ] } ], "prompt_number": 54 }, { "cell_type": "code", "collapsed": false, "input": [ "[ 2. typeof(2.);\n", " .5 typeof(.5); ]" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 55, "text": [ "2x2 Array{Any,2}:\n", " 2.0 Float64\n", " 0.5 Float64" ] } ], "prompt_number": 55 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that `0 == -0`, but they do not have the same binary representation:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "0 == -0" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 56, "text": [ "true" ] } ], "prompt_number": 56 }, { "cell_type": "code", "collapsed": false, "input": [ "[ \" 0\" bits(0.0) ;\n", " \"-0\" bits(-0.0) ; ]" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 57, "text": [ "2x2 Array{ASCIIString,2}:\n", " \" 0\" \"0000000000000000000000000000000000000000000000000000000000000000\"\n", " \"-0\" \"1000000000000000000000000000000000000000000000000000000000000000\"" ] } ], "prompt_number": 57 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Julia does arithmetic using machine numbers which can represent only finite ranges and with finite precision (as compared to the idealized $\\mathbb{R}$eal numbers). The `eps` function returns the smallest difference that can be represented by a given type:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "\n", "[ 1.0 bits(1.0)\n", " repr(1.0 + eps(1.0)) bits(1.0 + eps(Float64)) ]" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 34, "text": [ "2x2 Array{Any,2}:\n", " 1.0 \u2026 \"0011111111110000000000000000000000000000000000000000000000000000\"\n", " \"1.0000000000000002\" \"0011111111110000000000000000000000000000000000000000000000000001\"" ] } ], "prompt_number": 34 }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Wrap around\n", "\n", "Exceeding the maximum representable type (`typemax`) for a given type results in wrap-around:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "typemax(Int64)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 40, "text": [ "9223372036854775807" ] } ], "prompt_number": 40 }, { "cell_type": "code", "collapsed": false, "input": [ "typemax(Int64) + 1" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 41, "text": [ "-9223372036854775808" ] } ], "prompt_number": 41 }, { "cell_type": "markdown", "metadata": {}, "source": [ "For more information on numerical representations and accurate computation with machine math, please see the excellent [discussion in the Julia manual](http://docs.julialang.org/en/latest/manual/integers-and-floating-point-numbers/) (and links therein)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Complex numbers\n", "\n", "As seen earlie, the imaginary unit is called `im` in Julia." ] }, { "cell_type": "code", "collapsed": false, "input": [ "z=3+4im" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 4, "text": [ "3 + 4im" ] } ], "prompt_number": 4 }, { "cell_type": "markdown", "metadata": {}, "source": [ "After defining a complex variable, addition and multiplication \"just work\":" ] }, { "cell_type": "code", "collapsed": false, "input": [ "z+z" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 5, "text": [ "6 + 8im" ] } ], "prompt_number": 5 }, { "cell_type": "code", "collapsed": false, "input": [ "z*z' # z' denotes the conjugate of z (complex, of course)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 6, "text": [ "25 + 0im" ] } ], "prompt_number": 6 }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Arithmetic" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Operators and conversions" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Operations on heterogenous types result in promotion to the most representative common type:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "x + y" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 64, "text": [ "3.0" ] } ], "prompt_number": 64 }, { "cell_type": "code", "collapsed": false, "input": [ "typeof(x + y)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 65, "text": [ "Float64" ] } ], "prompt_number": 65 }, { "cell_type": "code", "collapsed": false, "input": [ "0x1 + 2" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 66, "text": [ "3" ] } ], "prompt_number": 66 }, { "cell_type": "code", "collapsed": false, "input": [ "[ typeof(0x1) typeof(0x1 + 2) ]" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 67, "text": [ "1x2 Array{DataType,2}:\n", " Uint8 Int64" ] } ], "prompt_number": 67 }, { "cell_type": "markdown", "metadata": {}, "source": [ "For the sake of completeness, here is a review of all of the standard arithmetic operators:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "rtmc = [ 1+1 -2 1/2 2/3 3\\2 2^3 x%y y^4 ]" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 68, "text": [ "1x8 Array{Float64,2}:\n", " 2.0 -2.0 0.5 0.666667 0.666667 8.0 1.0 16.0" ] } ], "prompt_number": 68 }, { "cell_type": "markdown", "metadata": {}, "source": [ "The `rtmc` assignment above is an example of *array declaration*, with array elements computed in place using the specified inputs and operations.\n", "\n", "Again, Julia converts all operations to the most representative type for given arguments. For example, division between integers results in a floating point number:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "[ 2/3 typeof(2/3) ]" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 69, "text": [ "1x2 Array{Any,2}:\n", " 0.666667 Float64" ] } ], "prompt_number": 69 }, { "cell_type": "markdown", "metadata": {}, "source": [ "This rule is applied consistently *even if* the result could be represented exactly by the common type of the input variables:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "[ 4/2 typeof(4/2) ]" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 70, "text": [ "1x2 Array{Any,2}:\n", " 2.0 Float64" ] } ], "prompt_number": 70 }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Array creation\n", "Let's look at several compact ways to create an array, noting in particular the use of **array comprehension**:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "a = ones(5)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 71, "text": [ "5-element Array{Float64,1}:\n", " 1.0\n", " 1.0\n", " 1.0\n", " 1.0\n", " 1.0" ] } ], "prompt_number": 71 }, { "cell_type": "code", "collapsed": false, "input": [ "b = [ 5:9 ] # 5-9, inclusive" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 72, "text": [ "5-element Array{Int64,1}:\n", " 5\n", " 6\n", " 7\n", " 8\n", " 9" ] } ], "prompt_number": 72 }, { "cell_type": "code", "collapsed": false, "input": [ "c = eye(4) # returns the 4x4 identity matrix" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 73, "text": [ "4x4 Array{Float64,2}:\n", " 1.0 0.0 0.0 0.0\n", " 0.0 1.0 0.0 0.0\n", " 0.0 0.0 1.0 0.0\n", " 0.0 0.0 0.0 1.0" ] } ], "prompt_number": 73 }, { "cell_type": "code", "collapsed": false, "input": [ "d = ones(4,4) # returns a 4x4 matrix of ones" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 74, "text": [ "4x4 Array{Float64,2}:\n", " 1.0 1.0 1.0 1.0\n", " 1.0 1.0 1.0 1.0\n", " 1.0 1.0 1.0 1.0\n", " 1.0 1.0 1.0 1.0" ] } ], "prompt_number": 74 }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Array arithmetic" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ " We can do arithmetic with array elements:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "a[1] + b[1]" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 75, "text": [ "6.0" ] } ], "prompt_number": 75 }, { "cell_type": "markdown", "metadata": {}, "source": [ "And with entire arrays, such as addition:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "a + b" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 76, "text": [ "5-element Array{Float64,1}:\n", " 6.0\n", " 7.0\n", " 8.0\n", " 9.0\n", " 10.0" ] } ], "prompt_number": 76 }, { "cell_type": "markdown", "metadata": {}, "source": [ "... scalar multiplication:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "5 * b" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 77, "text": [ "5-element Array{Int64,1}:\n", " 25\n", " 30\n", " 35\n", " 40\n", " 45" ] } ], "prompt_number": 77 }, { "cell_type": "markdown", "metadata": {}, "source": [ "... and exponentiation:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "b .^ 2" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 78, "text": [ "5-element Array{Int64,1}:\n", " 25\n", " 36\n", " 49\n", " 64\n", " 81" ] } ], "prompt_number": 78 }, { "cell_type": "markdown", "metadata": {}, "source": [ "This last one is important: the `.^` operator denotes *element-wise* operation. As a general convention in Julia, element-wise operators are denoted with a dot prefixed to the operator symbol." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Types and Multiple Dispatch" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Julia has a powerful type system, and variables carry type information that is automatically inferred at the time of assignment (and helpfully printed by IJulia):" ] }, { "cell_type": "code", "collapsed": false, "input": [ "rtmc = [ 1 1+1 -2 1/2 2/3 3\\2 2^3 x%y y^4 ]" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 79, "text": [ "1x9 Array{Float64,2}:\n", " 1.0 2.0 -2.0 0.5 0.666667 0.666667 8.0 1.0 16.0" ] } ], "prompt_number": 79 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Although it is fully possible to program in Julia without explicit type declarations, the type system is fundamental to higher-level and generic programming. Let's make a quick review of some types encountered so far:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "typeof('a')" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 80, "text": [ "Char" ] } ], "prompt_number": 80 }, { "cell_type": "code", "collapsed": false, "input": [ "typeof(\"Quick brown fox\")" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 81, "text": [ "ASCIIString (constructor with 1 method)" ] } ], "prompt_number": 81 }, { "cell_type": "code", "collapsed": false, "input": [ "typeof(1)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 82, "text": [ "Int64" ] } ], "prompt_number": 82 }, { "cell_type": "code", "collapsed": false, "input": [ "typeof(1.0)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 83, "text": [ "Float64" ] } ], "prompt_number": 83 }, { "cell_type": "code", "collapsed": false, "input": [ "typeof([1.0 2.0 3.0])" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 84, "text": [ "Array{Float64,2}" ] } ], "prompt_number": 84 }, { "cell_type": "code", "collapsed": false, "input": [ "typeof([ 1 => \"2\", 2 => \"2\"])" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 85, "text": [ "Dict{Int64,ASCIIString} (constructor with 2 methods)" ] } ], "prompt_number": 85 }, { "cell_type": "code", "collapsed": false, "input": [ "typeof(allmixedup)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 86, "text": [ "Array{Any,1}" ] } ], "prompt_number": 86 }, { "cell_type": "markdown", "metadata": {}, "source": [ "This last one is special: **the `Any` type is the root of the type hierarchy in Julia**." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Example: typed dictionaries\n", "\n", "We can explicitly specify the accepted key-value types in a dictionary constructor:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "int_to_string = Dict{Int,String}()" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 87, "text": [ "Dict{Int64,String}()" ] } ], "prompt_number": 87 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Or, we can ask Julia to select inferred types by using square bracket `[ ]` comprehension instead of the curly bracket `{ }` comprehension used earlier:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "int_to_string = [ 1 => \"1\", 2 => \"2\"]" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 88, "text": [ "[2=>\"2\",1=>\"1\"]" ] } ], "prompt_number": 88 }, { "cell_type": "code", "collapsed": false, "input": [ "typeof(int_to_string)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 89, "text": [ "Dict{Int64,ASCIIString} (constructor with 2 methods)" ] } ], "prompt_number": 89 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Assigning an Integer-String pair works:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "int_to_string[3] = \"foo\"" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 90, "text": [ "\"foo\"" ] } ], "prompt_number": 90 }, { "cell_type": "markdown", "metadata": {}, "source": [ "But, unsurprisingly, assigning a String-String pair does not:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "int_to_string[\"item3\"] = \"baz\"" ], "language": "python", "metadata": {}, "outputs": [ { "ename": "LoadError", "evalue": "no method convert(Type{Int64},ASCIIString)\nat In[91]:1", "output_type": "pyerr", "traceback": [ "no method convert(Type{Int64},ASCIIString)\nat In[91]:1", " in setindex! at dict.jl:412" ] } ], "prompt_number": 91 }, { "cell_type": "markdown", "metadata": {}, "source": [ "These last lines failed becase `int_to_string` expects only Integers as keys, and only strings as values." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Composite types\n", "\n", "Julia provides a mechanism to define custom composite (aggregate) types, reminescent of structs in C, and plain-old-data classes in C++." ] }, { "cell_type": "code", "collapsed": false, "input": [ "type LP\n", " c # Types are optional\n", " A::Matrix{Float64}\n", " b::Vector{Float64}\n", "end\n", "\n", "randlp(n,m) = LP(rand(n),rand(n,m),rand(m))\n", "\n", "mylp = randlp(10,5)\n", "\n", "println(mylp.c)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ ".41658206875707227\n", ".5017688328770391\n", ".4950271332072449\n", ".47894020363889234\n", ".4810285428763976\n", ".5129626979895638\n", ".4894621516145241\n", ".04926848192609756\n", ".35015060687983346\n", ".57393984978873\n" ] }, { "output_type": "stream", "stream": "stderr", "text": [ "Warning: imported binding for Int64 overwritten in module Main\n" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\n" ] } ], "prompt_number": 92 }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Parametric Types\n", "\n", "Parametric types take one (or more) type arguments which are used in the final construction of constituent fields:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "type LP2{T}\n", " c::Vector{T}\n", " A::Matrix{T}\n", " b::Vector{T}\n", "end" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 93 }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can construct derived types by specifying the element type in the constructor, between `{ }` curly brackets following the type name:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "lp = LP2{Float64}(mylp.c,mylp.A,mylp.b) # dbl precision" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 94, "text": [ "LP2{Float64}([0.41658206875707227,0.5017688328770391,0.4950271332072449,0.47894020363889234,0.4810285428763976,0.5129626979895638,0.4894621516145241,0.04926848192609756,0.35015060687983346,0.57393984978873],10x5 Array{Float64,2}:\n", " 0.31356 0.421467 0.855788 0.184812 0.522469 \n", " 0.523827 0.000162772 0.80338 0.990524 0.435689 \n", " 0.903068 0.412541 0.208929 0.249097 0.0109994\n", " 0.73804 0.00372301 0.880434 0.287483 0.962761 \n", " 0.41663 0.475956 0.716943 0.748487 0.642943 \n", " 0.561952 0.594682 0.408093 0.375726 0.554211 \n", " 0.428398 0.390269 0.264798 0.655475 0.473286 \n", " 0.759091 0.60214 0.232885 0.922757 0.946042 \n", " 0.179187 0.204381 0.995682 0.0101273 0.592077 \n", " 0.674353 0.806467 0.180198 0.677089 0.871963 ,[0.599648906039072,0.5608212204969416,0.9780278280294874,0.11913449426512002,0.2961395431294518])" ] } ], "prompt_number": 94 }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Multiple dispatch" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Recall the IJulia output after the definition of `sayhello` in the first section:\n", "\n", "```\n", "In [146]: function sayhello(name)\n", " println(\"Hello, \", name)\n", " end\n", "Out[146]: sayhello (generic function with 1 method)\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notice the line `(generic function with 1 method)`.\n", "\n", "What happens if we define another version of this function with a different argument type?" ] }, { "cell_type": "code", "collapsed": false, "input": [ "sayhello(friendnumber::Number) = println(\"Hello, numerical friend \", friendnumber)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 95, "text": [ "sayhello (generic function with 2 methods)" ] } ], "prompt_number": 95 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Aha! No error. Instead, IJulia tells us that `sayhello` now refers to a function with two **methods**. Compare the output when we call this function with different types of arguments:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "sayhello(\"Bob\")" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "Hello, Bob\n" ] } ], "prompt_number": 96 }, { "cell_type": "code", "collapsed": false, "input": [ "sayhello(3)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "Hello, numerical friend " ] }, { "output_type": "stream", "stream": "stdout", "text": [ "3\n" ] } ], "prompt_number": 97 }, { "cell_type": "code", "collapsed": false, "input": [ "sayhello(4.0)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "Hello, numerical friend 4.0\n" ] } ], "prompt_number": 98 }, { "cell_type": "markdown", "metadata": {}, "source": [ "When we call `sayhello` with a String argument, the original definition with *unspecified type* is used. When we call `sayhello` with any numerical type, the new Number-specific version is called.\n", "\n", "We can make another, even more specific definition; compare the output now with different argument types:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "sayhello(fpfriend::FloatingPoint) = println(\"Hello, floating point friend \", fpfriend)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 99, "text": [ "sayhello (generic function with 3 methods)" ] } ], "prompt_number": 99 }, { "cell_type": "code", "collapsed": false, "input": [ "sayhello(4)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "Hello, numerical friend 4\n" ] } ], "prompt_number": 100 }, { "cell_type": "code", "collapsed": false, "input": [ "sayhello(5.0)" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "Hello, floating point friend 5.0\n" ] } ], "prompt_number": 101 }, { "cell_type": "markdown", "metadata": {}, "source": [ "Why does this work? Julia's multiple dispatch chooses the *most specific method* for a given argument. `Float64` is a subtype of `Number` (twice removed), so the `Float64` version is used for a floating point argument, but the earlier `Number` version is still used for other numeric types.\n", "\n", "`Float64` is a *subtype* of the abstract `FloatingPoint` type:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "super(Float64)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 102, "text": [ "FloatingPoint" ] } ], "prompt_number": 102 }, { "cell_type": "markdown", "metadata": {}, "source": [ "which is in turn a subtype of `Number`:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "issubtype(Float64, Number)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 103, "text": [ "true" ] } ], "prompt_number": 103 }, { "cell_type": "markdown", "metadata": {}, "source": [ "### *Exercise*\n", "\n", "Write a function to print the type hierarchy for a given type. Hint: use the `super` function as shown above." ] }, { "cell_type": "code", "collapsed": false, "input": [ "function hierarchy(t::Type)\n", "\n", "end" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 26, "text": [ "hierarchy (generic function with 1 method)" ] } ], "prompt_number": 26 }, { "cell_type": "code", "collapsed": false, "input": [ "hierarchy(Int64)" ], "language": "python", "metadata": {}, "outputs": [], "prompt_number": 27 }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Expected output:*\n", "```\n", "In[]: hierarchy(Int64)\n", "\n", "Signed\n", "Integer\n", "Real\n", "Number\n", "Any\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Array functions\n", "\n", "We can use type annotated functions to define different behavior for various types of arrays:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "function array_sum(x::Array{Float64, 1})\n", " y = 0\n", " for i in 1:length(x)\n", " y += x[i] + 2\n", " end\n", " return y\n", "end\n", "\n", "function array_sum(x::Array{Int64, 1})\n", " y = 0\n", " for i in 1:length(x)\n", " y += x[i] + 1\n", " end\n", " return y\n", "end" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 1, "text": [ "array_sum (generic function with 2 methods)" ] } ], "prompt_number": 1 }, { "cell_type": "code", "collapsed": false, "input": [ "array_sum([ 1, 2, 3, 4, 5 ]) # array of integers" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 2, "text": [ "20" ] } ], "prompt_number": 2 }, { "cell_type": "code", "collapsed": false, "input": [ "array_sum([ 1.0, 2, 3, 4, 5 ]) # array of floats" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 107, "text": [ "25.0" ] } ], "prompt_number": 107 }, { "cell_type": "markdown", "metadata": {}, "source": [ "### *Exercise*\n", "\n", "Write a function that takes a vector and normalizes it in place by its [L2 norm](http://mathworld.wolfram.com/L2-Norm.html):\n", "\n", "$\\|x\\|_2 = \\sqrt{\\sum\\limits_{i=1}^n{|x_i|^2}}$\n", "\n", "Hints:\n", "\n", "- Use for loops. \n", "- square root function: `sqrt`\n", "- use element-wise operators to calculate the norm" ] }, { "cell_type": "code", "collapsed": false, "input": [ "function vec_norm(ar::Array{Float64,1})\n", "\n", "end" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 28, "text": [ "vec_norm (generic function with 1 method)" ] } ], "prompt_number": 28 }, { "cell_type": "markdown", "metadata": {}, "source": [ "*Expected output*\n", "```\n", "In [8]: vec_norm([1.0, 1.0, 1.0])\n", "Out[8]:\n", "3-element Array{Float64,1}:\n", " 0.57735\n", " 0.57735\n", " 0.57735\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Extra Topics" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Calling C code\n", "\n", "Julia provides a facility to call C (and Fortran!) libraries with no runtime overhead." ] }, { "cell_type": "code", "collapsed": false, "input": [ "path = ccall( (:getenv, \"libc\"), Ptr{Uint8}, (Ptr{Uint8},), \"PATH\")" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 109, "text": [ "Ptr{Uint8} @0x00007fff4dc88553" ] } ], "prompt_number": 109 }, { "cell_type": "code", "collapsed": false, "input": [ "bytestring(path)" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 110, "text": [ "\"/home/isaiah/bin:/usr/lib/lightdm/lightdm:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games\"" ] } ], "prompt_number": 110 }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Plotting\n", "\n", "There are several plotting options available for Julia.\n", "[Gadfly](https://github.com/dcjones/Gadfly.jl)\n", "[Winston](https://github.com/nolta/Winston.jl)\n", "[PyPlot](https://github.com/stevengj/PyPlot.jl)\n", "\n", "Here is a demonstration of PyPlot, as integrated with the IJulia notebook:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "using PyPlot\n", "x = -3pi:.01:3pi\n", "plot(x, sin(x))" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "display_data", "png": "iVBORw0KGgoAAAANSUhEUgAAArQAAAILCAYAAAAZog96AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAPYQAAD2EBqD+naQAAIABJREFUeJzs3XlwVdedJ/Dv04IEYhcgBALpPQkhIcRqt4ljx/FC7MR2T2y3TTNxJ16meibT3VPMpJKpniTuZNI1FWe6MiQz052ZOG3iNpHdIWY6TrwFL7ETG9uYTQIJLe9JIHYkNoHQ+uaPX66F0PaWe++555zvp4pSIqP3fkg69/zO9juheDweBxERERGRpjJUB0BERERElA4mtERERESkNSa0RERERKQ1JrREREREpDUmtERERESkNSa0RERERKQ1JrREREREpDUmtERERESkNSa0RERERKQ1zxParq4ufO1rX8NnPvMZzJ07FxkZGfj2t7+d8NefOnUKjzzyCObOnYu8vDzceOONeOONNzyMmIiIiIh04nlCe+bMGfz4xz9GX18f7rvvPgBAKBRK6Gt7enpw++23480338QPf/hD/PKXv0RBQQHuuusuvP32216GTURERESayPL6DUpKSnD27FkAQEdHB5566qmEv/YnP/kJDhw4gPfeew833HADAODTn/40Vq5cia997WvYuXOnJzETERERkT583UMbj8eT+vvbt29HRUXFx8ksAGRmZuLhhx/GBx98gOPHj7sdIhERERFpxvMZ2nTU1dXhlltuGfH56upqAMCBAwdQWFg47L+dOXMGr776KkpKSjB58mRf4iQiIiKixHV3d6O1tRV33nkn5syZk/brBTqh7ezsxOzZs0d83vlcR0fHiP/26quv4uGHH/Y8NiIiIiJKz7PPPosvfOELab9OoBPaVITDYQDyDaqsrFQcDZlu06ZN2Lx588f/v7MT+A//AWhsBP70T4HbbwcmTQJ27gSeeUb+9//+30BZmcKgSUvX/q4FXUMD8Jd/CcTjwJe+BFx/PdDTA/zmN8DPfw4sWwb84AfAjBmqI6Vr6fa7FnQvvwx861vAokXSFioqgNOngRdeAN58E/hX/wr4+teBzEzVkfqrvr4eDz/88Md5W7oCndDm5+ejs7NzxOedz+Xn54/4b7m5uQCAyspKrFmzxtsAyXozZ878+PessxN45BH5uHMncN11Q3/vC18A/vqvgbvvBv79vwfefls6dKJEXf27FnT79wN/8RfAkiXAiy8CBQVD/+2xx4D/+B+Bz30O+E//SdrCtGnqYqWRdPpdC7rnngOeeAJ4+GHg//wf4A8pCgAZ8P30p8Djj0sbeOYZIMEiUEbJvfqbkoZAX6xQXV2N/fv3j/h8bW0tAGD58uV+h0Q0qsFB4ItfBI4eBd54Y3gy61iwAHj9dWD+fOC++4ALF/yPk8hrZ8/K73dJiczGXp3MOv7oj2RmqqUFePRRmcUlMs2+ffL7/a//NfD008OTWceXvgRs3Qo8+yzw3/+7/zGaJNAJ7X333YeGhgZ88MEHH3+uv78fzz77LNatW4f58+crjI5oyA9+APz61/JQGm/mdfZsWWY6cQL4q7/yLz4iv/y7fydJ7QsvjL+doLpaZqR+8QuZuSIySXc38NBDsr3g//5fIGOcbGvDBlnB+y//Bdi9278YTeNLQvvyyy9j27ZtePHFFwFIdYJt27Zh27Zt6O7uBgA8/vjjyM7OxpEjRz7+usceewxVVVV48MEHUVNTgx07duChhx5CU1MTnnzyST9CJ5rQ4cPAN74he2c/+9mJ/355ObB5s3Tmr7/ufXxEfvn1r4F//mfgf/0vIJFtcZ//PPBv/g3wn/+zrG4QmeK//TcgFgNqaoBECi59+9vA8uWy/aCvz/v4jBT3QUlJSTwUCsVDoVA8IyNj2P9ua2uLx+Px+COPPDLs/ztOnjwZ/9KXvhTPz8+PT548OX7jjTfGX3/99THf66OPPooDiH/00Uee/puI4vF4/Gc/+1n8gQfi8QUL4vHz5xP/usHBePyWW+Lx8vJ4vK/Ps/DIID/72c9UhzCunp54PByOx++8U36/E9XZGY8XFMTjX/iCd7FRcoL+uxZ0zc3xeHZ2PP7EE8l93YcfxuOhUDz+93/vTVxB43a+ForHzdq9tHv3bqxduxYfffQRN7WT5z76SPbL/vSnsoc2GXv3AqtXAz/+scxSEensH/5BDoLV1gJVVcl97Y9+JIcl9+4FVqzwJj4iv3zxi8COHbJHPNly+F/6EvDqq0BzMzB1qjfxBYXb+Vqg99ASBd3f/A2wdKls+k/WqlVS2uvb3wauXHE/NiK/dHcD3/mOVPNINpkFZJm1rEy27hDp7OBBOUvxjW8kn8wCwH/9r7IH/X/+T/djMx0TWqIUffCB7Bn8m78BslIsgPetbwHt7bLPikhXTz0FnDolv8+pyM6WOpwvvigJAZGu/vZvpd7s44+n9vXFxVL+8Qc/4ERHspjQEqXof/wPoLRUTrKmaulS4N57ge9/n6WLSE+Dg8APfwg8+KC0h1Rt3Cil7b7/ffdiI/JTe7scivzKV4CcnNRf5ytfkQHis8+6F5sNmNASpeDYMWDbNim9le7tLl/5ClBXB7z2mjuxEfnppZdkv9+mTem9zqRJUinkn/5JytoR6ebv/x6YMkVmWNNRXi4VQP7u72TASIlhQkuUgh/9SEbg6T64AOBTnwLWrOGeKdLT5s3AunXADTek/1r/9t/KAPEf/zH91yLyU3e31Jt97DFg+vT0X2/TJuDQIblJjxLDhJYoSf398uD60pfcuYc+FJKO/OWXWYuT9BKNSi3lv/gLd15v5kzZwvOTn3BmivTywgtAR4d7beHmm2Wm9sc/duf1bMCElihJr70GnDyZ+qb/0WzYIDO+zzzj3msSee2nP5XZqPvvd+81H39cEuXf/ta91yTy2jPPSBK6ZIk7rxcKSTnHX/wC6Ox05zVNx4SWKEn/9E9yve3q1e695owZcqjmH/+Rh8NID4ODktBu2CD7Bt1y000yM/XUU+69JpGXjh6VurPJ1iKfyBe/CAwMAFu3uvu6pmJCS5SECxeA//f/gD/7MxlBu+mxx+Rwzbvvuvu6RF546y2grc2dfeRXC4WARx8Ftm8HurrcfW0iL/zsZ3Ko8cEH3X3dggLgc5+T16eJMaElSsIvfgH09EgBebfdfLOULfrnf3b/tYncVlMjlyF84hPuv/aGDXLI5le/cv+1idwUj8tKxec/786Zimv96Z8CO3cCsZj7r20aJrRESXj2WeDWW6VwttsyMmSE//OfyzITUVD198sM6p/8ifsrFQAQDkvVhOefd/+1idxUVwccOAA8/LA3r3/vvXLjGCc6JsaElihBHR1yUCWdixQmsmEDcPw48Pvfe/ceROn67W+lPfzJn3j3Hhs2SOWP8+e9ew+idG3fLgcj16/35vWnTpWkloO7iTGhJUrQr34lB2H++I+9e48bbpDZXz68KMi2bQNKSqR+slcefFC29/zyl969B1G6XngBuPtu2UPrlQ0bgD175IwFjY0JLVGCtm+XAvKFhd69R0YG8MADwL/8C6sdUDANDEgn7tV2A0dRkQzwmNBSUMViwL597patG82dd0pZxxdf9PZ9dMeEligBly9L/dn77vP+ve69V8rA7N3r/XsRJet3v5N75r3cbuC45x7g1VeB3l7v34soWdu3S6J5113evk9eHnD77RzcTYQJLVECXn1VTl1//vPev9dNN8meLI7GKYh+/Wtg/nzg+uu9f6977wUuXuQlCxRML7wAfOYzss/Va/feC7zzDnD2rPfvpSsmtEQJ+Jd/kcsU3LoFZjyTJsmInyWLKIheekl+PzN86D1WrJA95RzcUdB0dEjNcD8mOQBZrRgYkIOSNDomtEQTiMdlhvbuu/17z3vuAT78EDhxwr/3JJrI4cNSouhzn/Pn/UIhmZl68UXuKadg+c1v5HfS6+0GjqIiOYTJwd3YmNASTWD/fkks/XpwAcBnPyszYL/+tX/vSTSRl18GMjO9K1E0mnvvBVpbJZEmCopXXgGqq+UyHL/ce6+0wf5+/95TJ0xoiSbwyityV/0nP+nfe86ZIxUVXnrJv/ckmsjLLwM33gjMnOnfe37600BurhzKJAoCZ9XOz0kOQN7v/HlZvaORmNASTeDVV+V2sJwcf993/XrgzTd5axgFQ08PsGOHf9sNHLm5ci30b37j7/sSjUXFqh0AXHedXK/LtjA6JrRE4+jqkjJFfj+4AOCOO+RE6549/r830bXeew+4dElNW1i/Hnj7bUmqiVRTsWoHAFlZMrnChHZ0TGiJxvHmm0BfnxS29tsNN0g5mB07/H9vomu98QaQny+VB/x2xx1SC/q99/x/b6JrvfIKcNtt/q/aATK427lTytnRcExoicaxY4dc8VlW5v97Z2cDt9zChJaC4c03ZT+rH+W6rrVypewrZ1sg1a5ckYHV7beref/16+VQGGszj8SElmgcv/2tdOJeXvE5nvXrZctDd7ea9ycCZKvB++/LrJQKGRmSQHCplVTbuVO2vtx6q5r3LysDiovZFkbDhJZoDJ2dsvn/059WF8Mdd8jD83e/UxcD0e9/L1tvVHXigLSFXbt4UxKp9dZbwKxZUrJLhVBIJjq4WjESE1qiMbzzjpRnueUWdTEsWwYUFMj+RSJV3nhDrrutqFAXw223AYODHNyRWm+9JX2Ciq03jltuAQ4eBM6cURdDEDGhJRrDW2/J0k5JiboYQiEpWfTOO+piIHrzTZmdVbX1BgDCYSliz7ZAqly5IlsOVK7aAcCnPiUfObgbjgkt0Rh++1u1s7OOm2+WQtpXrqiOhGx0/rws9avcbgBwcEfqOftnVSe0ixfLZMvbb6uNI2iY0BKN4uxZYO9e9Q8uQDrx3l7ggw9UR0I2+t3vZKlfdUILyMzUrl1SwovIb6r3z16Ng7uRmNASjeJ3v1O/f9axYgUwfTofXqTG734nS/2lpaojkU68v19myoj8FoT9s45PfQrYvZv1aK8WgB8LUfC89RawaJHs21MtMxO48UYmtKTG738vv38q9886qqpkhoxtgfzW0yMDqSBMcgCS0A4O8rKRqzGhJRrF738vs0FB6MQBieXdd4GBAdWRkE16e2X/tt9XfI4lI0NiYUJLftu9W5Lam25SHYkoLwfmzeM+2qsxoSW6xpUr8vD6xCdURzLk5ptlaWnfPtWRkE327JH2cOONqiMZ8qlPyaxUX5/qSMgm770HTJ4st9YFgXNIkgntECa0RNf46CPpLIOU0F5/PTBpEsu0kL9+/3sgNxdYtUp1JENuvlkOhe3erToSssl77wHXXSdXkgfFzTfLYeGeHtWRBAMTWqJrvPceMGWKHMYKitxcSWqZ0JKf3n0X+KM/ksFUUKxZA+TkcO8g+Scel7YQpJUKQCZdenq4cudgQkt0jffek+QxSCNxAFi3Dnj/fdVRkC3i8aEDYUEyaRKwdi3bAvnnyBHg2LFgrdoBsnKSk8OqHw4mtERXiccloQ3agwuQhPbwYeDECdWRkA1iMfldC8qBsKvdcAM7cfLPu+/Kx6D1C5MmyYoFB3eCCS3RVQ4fBo4fD96DC5BOHODDi/wR1E4ckMFdaytw8qTqSMgG770ndZjnzVMdyUgc3A1hQkt0FacTX7dObRyjKSoCCgv58CJ/vPeelAbKz1cdyUgc3JGfgrpqB0hfFY0Cp0+rjkQ9JrREVwnySDwUko6cnTj54cMP5UBYEC1eDMyfz7ZA3uvulvJ1QdtL7uDgbggTWqKr7NwZ3JE4IA+vDz/kBQvkrd5eOTl9/fWqIxkdB3fkl9275bplJ3EMmuJioKCAK3cAE1qij/X0AHv3BvfBBcjyUlcXUF+vOhIyWW2tJLVBTWgBaacffMDBHXlr1y6pJLB8uepIRhcKSb/AhJYJLdHH6urkQoXrrlMdydiuu06u/+TMFHnpww+BrKxgXahwrXXr5Pa8hgbVkZDJdu2S28GCVIv5WhzcCSa0RH/w0UdAZmZwrjYczdSpQFUVE1ry1ocfyozU5MmqIxnbddfJ7BRnpshLu3YFe5IDkIT24kXg0CHVkajFhJboD3btkmQxyJ04MDQaJ/LKhx8Ge7sBAEybBixbJu2WyAsXLkiSGPSEdu1a+fjRR2rjUI0JLdEf6DASB+ThdeAAcOWK6kjIRJcuye9X0BNaQIrK796tOgoy1Z49ctlO0NvCjBlAWRkTWia0RJDksLZWn4S2v1/iJXLbnj3A4KA+bWHfPtn7TuS2XbuAKVOAigrVkUxs7VomtExoiSDJYX//0NJNkFVXy15fzkyRF3btAnJzg3uq+2pr1kh1Elb9IC/s2gWsXi0HJINuzZqhwaitmNASQR5cWVnAihWqI5lYbq7s9bV9NE7e+PBDqW6Qna06komtWiUHw9gWyAu6bEMDZDLm0iWgsVF1JOowoSWCPLiqqyVZ1AH3DpJXdDgQ5pg2Ta7nZVsgt509CzQ365PQrlkjH20e3DGhJYJeI3FARuNO8Xsit5w/DzQ16dcWbO7EyRvOIEmXtjBrFhAO2z24Y0JL1uvullPdujy4ABmN9/ZK3ERu2b9fPq5erTaOZKxZIzf82V5Unty1a5fU/S4vVx1J4mwf3DGhJevt2yedoQ4HwhwrV8reQZtH4+S+PXvkmk8dTnU71q6VQSlvDCM3ffSRDJYyNMqS1q6VPsHWg2Ea/aiIvLFnjxwI0+FUtyMvT5IOJrTkpr17pR3ocCDM4cwmsy2Qm/buDfbVz6NZu1ZuDGtpUR2JGkxoyXr79gGVlTIzpRPbl5fIfTp24iwqT267dEkOhAX5GvTR2H4wjAktWU/HThyQh9e+fVI/lyhdvb1AXZ2+bYEztOSW2lq5IUy3tpCfDxQXM6ElstLAgDy8dBuJA9KJX7nCovLkjvp6uXFLpwNhDhaVJzft3SuX1yxbpjqS5K1eLfHbiAktWa2lBbh8Wc+E1pk92LdPbRxkBqcT1OFykWutWgV0dQGxmOpIyAT79skZBV3qkl9t5cqhaiW2YUJLVnOSQR0T2hkzgJISJrTkjr17ZS/qtGmqI0mek4Tb2pGTu/bt02+7gWPFCuDUKeDECdWR+I8JLVlt3z6gsBCYO1d1JKlZsYKdOLljzx49txsAwPz5wJw5HNxR+gYH5Zmq4yQHMBS3jf0CE1qy2t69+j64ACa05I54XN/DkYDUZLZ5qZXc09IiVQ507RfCYbkQwsbBHRNasprOS0uAJLQnTsgSE1Gq2trk2lvd2wITWkqXztvQALkIorrazrbAhJas1dkJtLfr++AChmKvrVUbB+ltzx75qHNCu3KlzK5dvKg6EtLZvn2yhaWgQHUkqVuxgjO0RFbRfSQOAKWlwOTJdj68yD379sk+8sJC1ZGkzjkYVlenNg7Sm+7b0ACJv75eakvbhAktWWvfPinLsmSJ6khSl5kpV5XauLxE7qmtlWXKUEh1JKmrrJT2wLZA6dB9Gxogg7v+fvtqlDOhJWvt2yfJYFaW6kjSw72DlK66OklodZabK7VDuVpBqersBI4c0X+G1mnLtvULTGjJWjqf6r7aypXAgQO8ApdS090t99YvX646kvRxcEfpMGEbGgBMny7VDmwb3DGhJSv19QEHD+r/4AKkE+/tBRobVUdCOqqvl9qbus/QAkOlu+Jx1ZGQjvbvB3JygPJy1ZGkz8YydkxoyUqNjZIE6njN57Wcf4Nto3Fyh3OISsd766+1YoVUOWhtVR0J6aiuTtqB7tvQADsrHTChJSsdOCAfq6rUxuGGWbOARYvsG42TO+rq5AplHa+8vZbNtyRR+g4cMKNPAKQt2HYFLhNaslJdndQazM9XHYk7bByNkzucCgcmKCwEZs9mXWZKXjwuCa0Je8mBoX+HM3ljAya0ZCWTRuKAPLxsenCRe+rqzOnEQyFp12wLlKz2duDCBXP6hdJS2Q9sU1tgQktWMmkkDshD+PBh3pJEyTl3TjpyU2ZoASa0lBpnL7kp/UJmppSxs6ktMKEl61y5AjQ1mTMSB4YO9Bw8qDYO0otpnTgg7frQIZaxo+TU1QF5ecDixaojcY9tgzsmtGSdQ4ekTJFJCW1lpSy3MqGlZNTVyYnupUtVR+KeqiqpYNLcrDoS0omzDS3DoKzISWhtKWNn0I+OKDEmVThwTJkihbRtGo1T+mprJZmdNEl1JO5x2jXbAiWjrs6sPgGQf8+5c8Dx46oj8QcTWrJOXR1QVATMmKE6EnfZtrxE6TPpQJhj3jxgzhy2BUrc4KCsbpnWFmwb3DGhJeuYVuHAwYSWkhGPS0Jr0oEwB9sCJSMWkyugTesXwmEgN9eetsCElqxjWoUDR1UVcOSIlJ4hmsjx40Bnp7ltwZZOnNLn/K6Y1hYyM+V8hS1tgQktWeXyZSAaNW8kDrDSASXHxAoHjqoqud66r091JKSDujrZgrZggepI3FdVNdTWTceElqxSXy9LrSZ24hUVUunAltE4pae+XpYjS0pUR+K+qipJZpuaVEdCOnBW7UIh1ZG4r6pKJjlsqHTAhJas4iR7lZVq4/DClClAJMIZWkpMfb1UOMjMVB2J+2w7DEPpMbHCgaOqSrahtberjsR7TGjJKnV1MiM1darqSLzBvYOUqPp6Mwd2gFQ5mDePbYEm1t8PNDSYuWoH2DW4Y0JLVjH1QJiDCS0lyuSEFmBboMQ0N8tFHKbO0JaUyOqdDW2BCS1ZxdSSXY6qKllaOn9edSQUZGfOAKdPm53QLltmRydO6amvl4/OoVrTZGTYU+mACS1Z4/JloK3N/E4c4D5aGp/TiZvcFqqq5FBYb6/qSCjIGhqAmTOBggLVkXhn+XImtERGOXRIPlZUqI3DSxUVMiK34eFFqauvl9+TJUtUR+KdqirZH9nYqDoSCrKGhqEKMaZatsyOSgdMaMkaDQ3y0eSEdvJkVjqgidXXA6WlQE6O6ki846xWOO2eaDT19Wb3CYCsxHR1AceOqY7EW0xoyRoNDUBhoRTQNlllJTtxGl99vbl7Bh1z5gD5+WwLNLZ4XH4/TN56Awwl7Ka3BSa0ZA1nacl0FRXmP7goPaZXOHCwLdB4jh8HLl40v18Ih4HsbPPbAhNasoYtCe3SpUBrK3DliupIKIi6uoDDh5nQEjmHI03vF7KyZL+86W2BCS1ZYWBADoWZ/uAC5N8Yj/PaTxqdczjSpoTW9MMwlJqGBpm5jERUR+I9GwZ3TGjJCm1tQE+PPQktYP7Di1Jjy6wUIP/GS5eAo0dVR0JB1NAgM5dZWaoj8R4TWiJD2FDhwJGfLwdiTH94UWoOHgSKioBp01RH4j0O7mg8tmxDA+Tf2d4ue4ZNxYSWrNDQINf/FRWpjsQfNozGKTW2HAgD5NrPSZPYFmh0NrUFJ3E3uS4zE1qygjMSz7DkN54JLY3Fpk7clsMwlLyLF2Urii0ztEuXykeT24Il3TvZzobi2VdzEtrBQdWRUJD09gLNzfYktAAHdzQ6G26OvNr06cCCBWa3BSa0ZAWb9koB8m+9fJmHYWi45map+MGElmzn/E44M5c2qKgYOhRqIia0ZLwzZ+SPTQmtDctLlDynM7MtoT161OzDMJS8+np7Dkc6TB/cMaEl49m2tATwMAyN7tAhYNYsYO5c1ZH4x2n3znOACLBv1Q6Qf29TE9DfrzoSbzChJeM1NAChkBwOsYVzGIadOF2tsVFm70Mh1ZH4h6sVNBpbE9reXrlJ0kRMaMl49fVyl3VurupI/GX68hIl79AhoLxcdRT+mjYNWLiQbYGG9PfLTKVNW28A8+syM6El4zU02PfgApjQ0kiNjfYltADbAg0XjQJ9ffbN0BYVAXl55rYFJrRkPBuXlgAehqHhOjqAzk67TnU7mNDS1Wy6OfJqoZDZbYEJLRntyhUgFrPvwQXwMAwN5/we2DpDa/JhGEpOQ4NsRSksVB2J/5jQEmmqqUkuF7AxoeVhGLqac+VlWZnaOFQw/TAMJcdZtbPpcKTD5Fq0viS0XV1d2LRpExYuXIjJkydj9erVeP755yf8ui1btiAjI2PUP6dOnfIhctKdMytl4zIrD8PQ1RobgcWLgSlTVEfiP6f9m9qRU3Kcah82WrpUth51dKiOxH1ZfrzJ/fffj127duHJJ59EeXk5tm7dio0bN2JwcBAbN26c8Ou3bNmCimum2GbPnu1VuGSQxkZg5kxgzhzVkaixdCkTWhI2VjhwLFwoiXxTk+pIKAgaG4HPfEZ1FGo45SsbG4FPfEJtLG7zPKF96aWXsGPHDtTU1GDDhg0AgFtuuQVtbW346le/ig0bNiAjY/yJ4uXLl2PNmjVeh0oGamqSTtzGpSVA/u3vvqs6CgqCxkbgU59SHYUaGRmy1YIJLZ07B5w+be/gztly1NRkXkLr+ZaD7du3Y9q0aXjwwQeHff7RRx/FsWPH8P7770/4GvF43KvwyHCNjXZdqHCtJUuA5mbZR0z2GhwcGtzZqrx8aB8x2csZ1NjaL0ydKisWJrYFzxPauro6VFZWjpiFra6uBgAcOHBgwte45557kJWVhfz8fDzwwAMJfQ0RwE68vBy4fBk4dkx1JKTS4cNAT4+9+wYBSWBM7MQpObYntID8201crfB8y0FHRwfKRjlW6+yB7RhnZ3JhYSG+8Y1vYN26dZg+fTr279+P7373u1i3bh3efffdj5Pi0WzatAkzZ84c9rmNGzcmtGeXzOAsLdn+4ALk4VVUpDYWUsdJ5Gwf3LW3ywDPxoNxJBobgYICYPp01ZGoU14OfPCBv+9ZU1ODmpqaYZ87d+6cq+/hy6GwVN1555248847P/7/N910E+6++25UV1fjiSeewPbt28f82s2bN3PfreWcEajNnXg4DGRmykP81ltVR0OqNDYCkyYBxcWqI1HHGdw1NwMrVqiNhdSxfdUOkLawdSsQj/t3vmS0CcXdu3dj7dq1rr2H51sO8vPzR52F7ezs/Pi/J6O4uBif/OQnsXPnTlfiI3NxaUmSmJISM5eXKHGHDslhkMxM1ZGo4yQxbAt2s/X656uVlwOXLgHHj6uOxF2eJ7QrVqxAfX09Bq85lVJbWwtAKhikImT9wKO3AAAgAElEQVTrsXVKGJeWBPcOEjtxKd03cybbgs3icR4UBoaeBaa1Bc8T2vvuuw9dXV3Ytm3bsM9v2bIFCxcuxA033JDU60WjUbzzzjv4hGn1Jsh1TU18cAHy8OKslN2Y0MrSqqmHYSgxp08DFy6wLUQiUsrOtLbg+R7au+66C+vXr8eXv/xlXLhwAaWlpaipqcFrr72GrVu3fjzT+vjjj+OZZ55BNBrFokWLAADr16/HbbfdhqqqKkydOhW1tbX43ve+h6ysLHznO9/xOnTSXGMj98oB0on/wz/IPfZZgd41T17o7gba2uyucOBg6S67OT972yc6nK1oprUFX7q3F154AV//+tfxxBNPoLOzE5WVlXjuuefw0EMPffx3BgcHMTg4OKzmbHV1NbZu3YojR46gu7sb8+bNwx133IFvfvObo1ZOIHLE4zL6fOAB1ZGoV14O9PVJ6aZIRHU05LeWFmkPts9KAZLI/OY3qqMgVZqaZKa+tFR1JOqZuFrhS0Kbl5eHzZs3Y/PmzWP+naeffhpPP/30sM99//vf9zo0MtTp08D58xyJA8OvOmRCax+W7BpSXg6cOiXPhhkzVEdDfmtsBBYtAiZPVh2JeuXlwI4dqqNwl+d7aIlUYMmuIYsXyxKTaaNxSsyhQ3IYau5c1ZGod3VdZrIPS3YNKS+X1ZuBAdWRuIcJLRnJ6bC4tCSlmkpL2YnbyjkQxsIww1cryD6scDBkyRKgt1e2opmCCS0ZyVla4o1Agodh7MUKB0NmzADmzePgzkaDg3KpBtuCMLF0FxNaMhJLdg1n4gEASkxzM9vC1Ti4s9PRo1Lxg21BmLgVjQktGYmzUsOVlwOtrbLERPa4cEEOQbEozBAO7uzEw5HDOVvRTBrcMaEl4zhLSxyJD1myRL4v0ajqSMhPzc3ykQntEGeG9qoKkWSBpiZJ4kpKVEcSHKZdusOEloxz7Bhw+TJH4lczcb8UTYwJ7UhLlkjZrjNnVEdCfnLKFmZnq44kOEy7Fp0JLRnHGXFyhnZIYSGQl2fWaJwm1twMzJoFzJ6tOpLg4ODOTjxXMZJpW9GY0JJxGhtlaSkcVh1JcDj32LMTtwu33ozklPLj4M4uPFcxkmlb0ZjQknGammSf1KRJqiMJFh6GsU9zM7cbXGvKFCnpx8GdPfr7JWljQjucaasVTGjJOLwNZnScobUPE9rRsXSXXVpbJanlasVwpm1FY0JLxuFtMKMrL5dajJcuqY6E/HDpEnD8OBPa0ZSVDR2YI/OxZNfoQiHZgtPSojoSdzChJaMMDMjSEhPakZzExpT9UjQ+p5NiQjtSWZl8f1i6yw7NzUBODlBUpDqS4CktNWdwx4SWjHLkiJzYZEI7knMYxpTROI2PJbvGVloKdHXJpRNkvpYWOSScwYxnBGdwZwL+eMkoTsN0kjcaUlAg+6VMGY3T+JqbgenTgTlzVEcSPE6Sb0pHTuPjXvKxlZaaU7qLCS0ZpblZSnYVF6uOJHhM2y9F43M68VBIdSTBE4nIRw7u7NDSwkmOsZSVSemutjbVkaSPCS0ZpaVFklneBjM6HoaxB2elxpaXJye8Obgz38AAEIuxLYzFpNUKJrRklOZmjsTHwxlae/BmpPGZdBiGxtbeLsvp7BdGV1QkE0AmtAUmtGQULi2Nr6xMlpZM2C9FY+vulo6cs1JjM+kwDI2N5yrGl5kpW3BMaAtMaMkY8bg0SnbiYystNWe/FI3NKc3GtjA2ztDaoaVFqhuUlKiOJLhMaQtMaMkYJ09KMXmOxMdm0n4pGhtLdk2srAzo6ADOnVMdCXmpuRlYvJhXoY/HlNUKJrRkDC4tTcyk/VI0tuZmOfhUUKA6kuBiXWY7cNVuYqWlsqozOKg6kvQwoSVjOB2TU5KHRjJpvxSNjSW7JsbVCjvwoPDEysqAnh65Gl1nTGjJGM3NUoonL091JMFmyn4pGhtLdk1s1iz5w7ZgLudcBRPa8TnfH93bAhNaMgYfXIlh6S7zMaFNjCl7B2l0p0/LFcdsC+MrKZGDc0xoiQKCe6USU1Zmxn4pGl1PD3D4MNtCIrhaYTbnZ8uJjvHl5ACLFuk/uGNCS8bgXqnElJaasV+KRtfaKoMVJrQT4wyt2XhQOHEm3CLJhJaMcO6clODhg2tiTqKj+8OLRuf8XHlL2MRKS2Vg192tOhLyQksLMH8+z1UkwoStaExoyQhOQ+Ss1MRKSuT0u+4PLxpdUxMwebIckKTxOc8L5yIKMgtX7RLnzNDG46ojSR0TWjICl5YSl5MjhcY5Q2smpxPP4NN9Qqac7qbR8VxF4srK5ADd6dOqI0kdH3lkhJYWYOZMYPZs1ZHowYTlJRodKxwkbv58YMoUtgVTcYY2cSYM7pjQkhHYiSfHhAMANDqWr0tcKMRKB6a6cAE4c4b9QqKY0BIFBDvx5DgztDrvl6KRBgaAtjbelpcMVjowE7ehJScvT1YsdG4LTGjJCExok1NaCly8qPd+KRrp6FGgrw8Ih1VHog/O0JqJNWiTp/vKHRNa0l53N9DezqWlZPAeezM5p/U5Q5u4sjKZ1e7rUx0JuYnnKpKn+9kKJrSkvVhMPnIknjgn4dH54UUjxWKyL7S4WHUk+igtHdqqQeZwVu1CIdWR6IMztESKca9U8qZNAwoK9H540UjRKLBgAZCbqzoSffCiETPxoHDySkvlgqJz51RHkhomtKS95mYWkk+F7stLNFI0yu0GyVq0CMjOZlswDc9VJE/3rWhMaEl7LS3SibOQfHJ0X16ikWIxHghLVmamfM/YFsxx5Yqcq2BCmxzn+8WElkgRFs9ODWdozcMZ2tSw0oFZYjEpScgtB8mZPRuYNUvftsCElrTH6w1TU1YmZbvOn1cdCbnh8mXg5EnO0KaitHSoQgTpj+cqUheJDB201g0TWtJafz/Q2soHVyqcmTxdH140nPNz5Axt8pxOnBeNmKG5WQ5G8lxF8iIRfQd3TGhJa4cPS1LLhDZ5TuKj68OLhmNCm7pIROpZnzypOhJyg3MgjOcqkseElkgRZ2mJWw6SN3euXHeo68OLhotGgZwcub6SksPBnVmcg8KUvEhEJop0vGiECS1pLRqVU8qLF6uORD+hkN6jcRouGpX9s5yVSp6z75htwQyxGFftUhWJAIODktTqho8+0lo0OlRHkpKn8wEAGo4lu1I3dSowbx4TWhMMDrItpEPn1QomtKS1WIxLS+ngDK05WLIrPWwLZjhxAujpYVtI1aJFsuqpY1tgQktac5ZZKTWRiFSJGBhQHQmlIx7nrFS6mNCawfkZsi2kJjtbtvDp2BaY0JLWOEObnkgE6O0Fjh1THQml4/Rp4NIltoV0MKE1g7OFqqREaRha07UtMKElbZ0/D3R2ciSeDp33S9EQluxKXyQCHD0q16aSvqJRoKBAKrhQapjQEvnM6cSZ0KbOmcXQ8eFFQ7jMmj5nMNDaqjQMShO33qSPCS2Rz5wGx1mp1OXmAgsW6PnwoiHRKJCfD0yfrjoSfXG1wgw8HJm+SAQ4dw44e1Z1JMlhQkvaisWAKVPkggBKna6jcRrCWan0LVggB2LYFvTGtpA+XesyM6ElbTkHwkIh1ZHojQmt/jgrlb7MTNmCw7agr54e2QfNtpAeXVcrmNCStliyyx1MaPXHWSl3sC3ora1NStixLaRn9mzZvqRbW2BCS9piyS53RCLAqVNS9on009cn11SyLaSPCa3eWO3DHbpei86ElrTE6w3d4zz8eQWuno4ckfbATjx9zlXQ8bjqSCgV0SiQlQUUFamORH9MaIl8wusN3aPrfikSLNnlnkgE6OoCzpxRHQmlIhYDiotlPzSlhwktkU/Yibtn/nwp36Xbw4tENApkZMh1lZQeDu70xnMV7olEZE9yf7/qSBLHhJa0xOsN3aPrfikSsRiwaJGUnKL06FquiATPVbgnEgEGBoD2dtWRJI4JLWkpGgXmzQOmTlUdiRmY0OqLJbvcM2OGXFDBtqAnztC6R8fVCia0pCUeCHMXE1p9sS24i21BT2fPyu1WbAvuKC6W1Tud2gITWtISl5bc5ZzuHhxUHQklizO07mJCqyeW7HLXpEmylUmntsCElrTEpSV3hcPAlStSPYL0ceEC0NHBTtxNTGj15CS07Bfco1tbYEJL2uH1hu7Tcb8UsRP3QiQitX17e1VHQsmIRoFp02QPNLmDCS2Rx3i9oft4ultPzs+Lgzv3RCLyfGlrUx0JJcPZSx4KqY7EHExoiTzGvVLuy8sDCgp4W5huYjFgyhRg7lzVkZiDqxV64rkK90UisqXp/HnVkSSGCS1pJxqVm2B4vaG7dBuN09CBMM5KuaeoSJ4vbAt64bkK9+l2LToTWtJOLCa3ImVlqY7ELExo9cOSXe7LypKSRWwL+hgcBFpbOUPrNt1WK5jQknZYpsgbTGj1w7bgDbYFvRw7Jof4OLhz15w5cnmRLm2BCS1ph7NS3ohEpGPo7lYdCSVicJD7Br3i1GUmPfBchTd0uxadCS1ph524N5zvaWur0jAoQSdOSAk7Du7cF4kALS1S7YCCz0m4SkqUhmEkJrREHjl3Tq44ZCfuPt32S9mOJbu8E4nIpRVnz6qOhBIRiwHz5wOTJ6uOxDxMaIk8wqUl7yxYINcd6vLwsp3TFjgr5T4O7vTCveTeiURk1W5gQHUkE2NCS1pxOhjO0LovI0OSI3bieohGpXZwXp7qSMzDhFYvPFfhnUgE6OuT2zmDjgktaSUWk1OXc+aojsRMOi0v2Y6duHdmzQJmzmRb0AVnaL2j0+COCS1pxSmezULy3uDpbn2wE/cWB3d6uHJFqrNwcOeN4mLpb3VoC0xoSSuclfKW04nzdHfwMaH1FhNaPbS1yUe2BW/k5sr5Ch3aAhNa0gpLdnkrEgEuXQJOn1YdCY2Hs1LeY0KrB56r8J5Txi7omNCSNpxC8nxweUen/VI2a2uTWXQO7rwTiQCHD8uBGAquWAzIzgYWLlQdibl02YrGhJa0cfy4XG/ITtw7zmBBh9G4zZzOhYM774TDUqroyBHVkdB4olHZ55mZqToSc4XDTGiJXMWlJe9Nnw7k5+vx8LJZNApkZQFFRaojMZczcGZbCDZuQ/NeOAycOiXb0YKMCS1pg7NS/tBleclmsRhnpby2eLE+p7tt5lS+Ie8439+gX4vOhJa04RSSnzJFdSRm42GY4GOFA+9NmgQsWsTBXZDF42wLftBltYIJLWmDS0v+CIeZ0AYdO3F/cHAXbGfPAhcucIbWa4WFQE5O8NsCE1rSBpeW/BGJAO3tcgCPgseZlWJb8B4Hd8HGbWj+yMiQLU6coSVyCUt2+SMclhJphw+rjoRG48xKcYbWe9xPHmzOYINtwXs6VDpgQktacArJ88HlPV32S9mKs1L+CYeBM2eAixdVR0KjicWkMsusWaojMR8TWiKXOIXk2Yl7b9EiWWLiUmswcVbKPxzcBZtzriIUUh2J+ZzViiBfi86ElrTgdCjsxL2XnS0li9iJB1M0ylkpv/DmvGDjXnL/hMOyUtHZqTqSsTGhJS2wkLy/eLo7uDgr5Z9586RMINtCMLHyjX+cgUOQ2wITWtICC8n7i6e7g4slu/wTCumxd9BGAwNS6J8ztP5wvs9BbgtMaEkLXFryF093BxerffiLg7tgOnYM6Ovj4M4vs2bJVqcg9wtMaEkL7MT9FQ7LXqnz51VHQlcbGJADkuzE/cPBXTA5gwz2C/4IhYLfFpjQkha4zOovnu4OpqNHZVaKnbh/nE58cFB1JHQ159lUUqI0DKsEffsNE1oKvLNnZaaQnbh/eLo7mFiyy3/hsNTBPnFCdSR0tWgUWLAAyM1VHYk9gr79hgktBR5LdvlvzhwgLy/Yo3EbOZ1JcbHaOGzC1Ypg4jY0/4XDsuVpYEB1JKNjQkuBx71S/nP2SwV5NG6jWAxYuJCzUn5ylrTZFoKF29D8Fw7Llqdjx1RHMjomtBR4sRgwbRqQn686ErsEfXnJRqz24b+pU6UeLWdog4UztP4LeukuJrQUeE4nzkLy/gr6iVYbsZC8GlytCJbubuD4cbYFvzmrFUHtF5jQUuCxE1fDOdHK093BwWVWNbhaESytrfKRM7T+mjIFmD8/uG2BCS0FHpdZ1YhEgN5emQkh9S5fBk6eZFtQgasVwcKDwuoEuXQXE1oKNKeQPDtx/7F0V7CwE1cnHJYawD09qiMhQJ5JkyZJ2S7yFxNaohQdOyazhOzE/cfT3cHCah/qRCJAPC6Da1IvFpPnUwYzGN8xoSVKkdNw2In7z9kvFdSHl21iMSAnBygsVB2JfbhaESzchqZOJCITTVeuqI5kJCa0FGi83lAtHoYJjmiUs1KqFBUBWVlsC0HBg8LqhMPBXa3go5ECLRqVWcIpU1RHYicehgkOduLqZGbK7WxsC+rF45yhVSnItWg9T2i7urqwadMmLFy4EJMnT8bq1avx/PPPJ/S1p06dwiOPPIK5c+ciLy8PN954I9544w2PI6YgYSeuFutvBgdLdqnF1Ypg6OwELl5kW1ClqEgGeFYmtPfffz+eeeYZfOtb38Irr7yC66+/Hhs3bkRNTc24X9fT04Pbb78db775Jn74wx/il7/8JQoKCnDXXXfh7bff9jpsCgiOxNUKh4O7X8omnJVSj6sVwcDDkWplZQGLFwezLWR5+eIvvfQSduzYgZqaGmzYsAEAcMstt6CtrQ1f/epXsWHDBmSMsSHsJz/5CQ4cOID33nsPN9xwAwDg05/+NFauXImvfe1r2Llzp5ehU0DEYsBtt6mOwl7OLEhrK1BRoTQUq50+LXVoOSulTiQCJLi4SB7iQWH1gjq483SGdvv27Zg2bRoefPDBYZ9/9NFHcezYMbz//vvjfm1FRcXHySwAZGZm4uGHH8YHH3yA46z2brzubpkd5INLHed7z6VWtTgrpV44DJw/D5w9qzoSu8ViwMyZwKxZqiOx16pVwOTJqqMYydOEtq6uDpWVlSNmYaurqwEABw4cGPdrV6xYMeLziXytGxoagD17PH0LmoBzipKduDoLFwLZ2cEcjduEs1LqsXRXMHDrjXp/93fAM8+ojmIkT7ccdHR0oKysbMTnZ8+e/fF/H0tnZ+fHfy/ZrwWATZs2YebMmcM+t3HjRmzcuHHCuAHgr/9aZghfeSWhv04e4M1I6jmnu9mJqxWNArNnAzNmqI7EXlevVqxdqzYWm/GgsJ5qampGnJ06d+6cq+/haUKr0ubNm7FmzZqUvz4SAX71KxcDoqRFozI7uHCh6kjsFtT9UjZhJ67e7NnA9OlsC6pFo8B996mOgpI12oTi7t27sdbF0aGnWw7y8/NHnUnt7Oz8+L+P97XO30v2a90QDstBmMFBT9+GxhGLyexgZqbqSOzG0l3qsWSXeqEQ24JqAwOyFY1tgUbjaUK7YsUK1NfXY/CarLC2thYAsHz58jG/trq6Gvv37x/x+US+1g2RCNDbK4eSSA3ulQoG5+7ueFx1JPZiWwiGIN9jb4P2dqC/n22BRudpQnvfffehq6sL27ZtG/b5LVu2YOHChcMqGIz2tQ0NDfjggw8+/lx/fz+effZZrFu3DvPnz/csboCnu4OAy6zBEIkAFy5IQXPyX18fcOQI20IQcIZWLZ6roPF4mtDeddddWL9+Pb785S/jqaeewptvvok///M/x2uvvYbvfe97CIVCAIDHH38c2dnZOHLkyMdf+9hjj6GqqgoPPvggampqsGPHDjz00ENoamrCk08+6WXYAOTOdICjcVVYSD44OLhT6/Bh2frEtqBeOCxL3gMDqiOxUzQqWz+Ki1VHQkHk+U1hL7zwAv7sz/4MTzzxBD772c/iww8/xHPPPTdsc/Dg4CAGBwcRv2pNc9KkSXj99ddx66234q/+6q/wx3/8xzh58iRefvll3HzzzV6HjcmTgcJCduKqnD0rs4Iciavn/Aw4uFODs1LBEYnIjPnRo6ojsVMsJoeEc3JUR0JB5HmVg7y8PGzevBmbN28e8+88/fTTePrpp0d8ft68ediyZYuH0Y2P+6XUYSH54Jg1SwqZc3CnRjQKZGTIdZOk1tW1aPnz8B9X7Wg8ns/Q6oz7pdRhIflg4eBOnVgMWLRIStiRWs5SN9uCGrEY+wQaGxPacbATVycWk5qPo9ytQQpwcKcOS3YFR26uLHmzLajBg8I0Hia044hEpGxXd7fqSOzjLC394dwgKcbBnTpcZg2WcJgJrQqXLwMnTrAt0NiY0I7DaThtbWrjsBFH4sESiUg76O9XHYl92BaChTfnqdHaKh/ZFmgsTGjHwdPd6nBWKljCYUlm29tVR2KXCxeAjg62hSDh9hs1eFCYJsKEdhwLFshBDD68/MXrDYOHgzs1WLIreMJh4ORJWQIn/8RiUq6rsFB1JBRUTGjHkZkpFyywE/fX0aNS65Ej8eAoLpb9zBzc+YuzUsHDwZ0a0aj0xxnMWmgM/NWYAA8A+I+zUsGTkyOnu9mJ+ysWA6ZMAebNUx0JOXhznhrcS04TYUI7AR4A8J/TUTjXD1MwcO+g/5ySXaz2ERyFhTLAY7/gL56roIkwoZ2AM0N71a285LFYTDqN3FzVkdDVWLrLf+zEgycjgyt3fovHOUNLE2NCO4FIRE4anz2rOhJ78MEVTJyh9R/bQjBxcOevM2eAri4O7mh8TGgnwP1S/uPNSMEUiQCnTknHQt4bHORVn0HFwZ2/eK6CEsGEdgI80eo/LrMGk/MzcQqck7dOnAB6etiJBxG3ovmL1T4oEUxoJzBrFjBjBkfjfnGuN2QnHjzOz4RtwR/sxIMrEpFn1enTqiOxQywGzJ4tfTHRWJjQJoCVDvzjzP6xEw+e+fPloB7bgj+Y0AYXB3f+4qodJYIJbQJ4otU/3CsVXKEQ24KfYjGgoADIy1MdCV3LSa44uPMHz1VQIpjQJoAnWv0TjQKTJsm1wxQ8TGj9w1mp4Jo+HcjPZ1vwCw9HUiKY0CYgEgHa2oCBAdWRmC8W4/WGQcbtN/5hya5g40SHP/r7gcOH2RZoYkwbEhAOA319wNGjqiMxH5eWgs1JaHm623ucoQ02lu7yx5EjMpnEfoEmwoQ2ATwA4B924sEWDsvp7lOnVEdititXgGPH2IkHGRNaf/BwJCWKCW0CiovlI5eXvMXrDYOPgzt/tLVJe2AnHlzhsMwe9vWpjsRssZhsQVu8WHUkFHRMaBOQmwssXMhO3Gu83jD4eLrbH86zhoO74IpE5Da3w4dVR2K2aBQoKpLDwkTjYUKbIB4A8B5LdgXftGnAnDkc3HktFgOysqQjp2Di4M4fPFdBiWJCmyDul/IeZ6X0wLbgvWhUtjplZqqOhMayeLEshbMteIsluyhRTGgTxBla78ViQ1cNU3CxLXiPnXjwZWdLUsuE1lucoaVEMaFNUCQCnDghJ7zJG3xw6YEztN6LRoHSUtVR0EQ4uPPWxYtytoKDO0oEE9oEOQ2qtVVpGEZjyS49hMNAezvQ26s6EjPF4xzc6YKDO2/xXAUlgwltgpwGxdG4d1iySw883e2tzk7gwgW2BR1whtZbzveWEx2UCCa0CSosBHJyOBr3Cq831AdPd3uLheT1EYkAHR3A+fOqIzFTNApMngwUFKiOhHTAhDZBGRlASQk7ca841xuyEw++RYvk9D0Hd95gtQ99cOXOW86qXSikOhLSARPaJITD7MS9wk5cHzzd7a1oFJg5Uyp+ULBxtcJbPFdByWBCm4RIhA8ur8RiMgrn9YZ64N5B7/BAmD7mzgXy8ji48wrbAiWDCW0SnBnaeFx1JOaJRmUpm9cb6oGnu73Dw5H6CIU4uPNKPM56zJQcJrRJiESAri45BEDu4oNLL+zEvcNZKb1wcOeNEyeAK1fYFihxTGiT4CRcfHi5j524XiIRKS/F093u6utjtQ/dMKH1Bkt2UbKY0CaBJ1q9w4RWL2wL3nCqfbAt6CMclgt3BgdVR2IWlq+jZDGhTcKMGXLymKNxd/F6Q/1wtcIbrPahn0gE6OkBjh9XHYlZYjE5dDd1qupISBdMaJPEvYPu4/WG+pkzRzoaJrTuikal5jWrfeiDpbu8wVU7ShYT2iRxv5T7uFdKPzzd7Y1oVJLZ7GzVkVCiuFrhDdagpWQxoU0SO3H38XpDPXFw5z7OSulnyhRg/ny2BbexfB0liwltkiIRoK0N6O9XHYk5eL2hnji4cx9npfTEtuCunh6gvZ1tgZLDhDZJ4bCcQm5vVx2JOdiJ68m5OY+nu93DGVo9cbXCXYcPy8UKbAuUDCa0SXIaGB9e7mEnrqdIBOjtBY4dUx2JGc6elT9sC/rhDK27WO2DUsGENkmLF8vSOB9e7uD1hvri6W53sdqHviIR4OhRudmK0heLAZmZQFGR6khIJ0xok5STI42Mnbg7eL2hvkpK5CNXK9zBWSl9OT+z1lalYRgjGgWKi4GsLNWRkE6Y0KYgHGYn7haW7NKXc7qbgzt3xGLAtGlAfr7qSChZXK1wF89VUCqY0KbAOQxD6eP1hnrjYRj3OHvJWe1DPwsXSu1gtgV3sGQXpYIJbQo4Q+ueWAyYN4/XG+qKgzv38HCkvjIzZYmcbcEdnKGlVDChTUEkApw6BVy6pDoS/fHBpTcO7tzDhFZvXK1wx9mzwLlzbAuUPCa0KeB+KfewE9dbJCJlu7q7VUeit4EBOVDEtqAvrla4g+cqKFVMaFPgdDp8eKWPJbv05vzs2trUxqG79na5fZAJrb6c1Yp4XHUkemP5OkoVE9oUzJ8P5OZyeSldzvWGfHDpixeNuIMlu/QXiQAXLgCdnaoj0Vs0KmcqWO2DksWENgWhEG+GcQOvN9TfggVyupttIT3RqDxXiotVR0Kp4lY0d7DaB6WKCW2KeBgmfSzZpb/MTLlggW0hPdGolH7KyVEdCaWKqxXu4DY0ShUT2hTxAED6olG5CYbXG+qNbSF9PBypv1mzgN4L37QAACAASURBVJkz2RbS1dIClJaqjoJ0xIQ2RTwAkL6WFl5vaAKuVqSPCa0Z2BbS098v1T6Y0FIqmNCmKBIBLl+WerSUGo7EzeDU3+TgLnVMaM3AWrTpOXJEklr2C5QKJrQpcjqflha1ceiMCa0ZwmHg4kWe7k7VxYvAmTNMaE3Aw8LpcfpT9guUCia0KXIaHBPa1MTjMpPBB5f+eBgmPay7aY5IRGoyDwyojkRPLS1D1wgTJYsJbYry8qQeLRPa1Jw8KVcHM6HVHy8aSQ9r0JojEpEl8/Z21ZHoqaUFWLxYSgESJYsJbRpKS5nQpopLS+aYOVP+cIY2NdEoMGUKMG+e6kgoXU65KbaF1HAbGqWDCW0amNCmzvm+cVbKDCzdlToWkjdHcbH8HJnQpoYJLaWDCW0aSkuB5mbVUeippUW2bOTlqY6E3MByRaljhQNz5OTIBRkc3CUvHmdCS+lhQpuG0lLg9Gk5pUzJ4YPLLCxXlDomtGZhW0jN6dNAVxf7BUodE9o0lJXJR247SB4TWrOEw8Dhw3IghhI3OMirPk3D7Tep4bkKShcT2jSwdFfqmNCahae7U9PeDvT2Dg2OSX/cfpManqugdDGhTUN+PjB9OhPaZF28KMtLTGjNwdJdqeGslHkiEblBsqtLdSR6aWmRSh/TpqmOhHTFhDYNoRArHaSCnbh5Fi/m6e5UNDcDGRlASYnqSMgtzvaR1lalYWiHq3aULia0aWKlg+QxoTVPTg5QVMSENlktLcCiRfL9IzPw5rzUMKGldDGhTRNnaJPX0iLLSnPmqI6E3MR77JPX3Mz9s6aZPx/IzWVbSBYTWkoXE9o0lZYCR47IwQ5KjPPgYiF5s7BcUfLYiZsnFOLBsGR1dcl16GwLlA4mtGkqK5PSO9wvlTh24mZiuaLkxOOcoTUVB3fJcZ4b7BcoHUxo08TSXcljQmumcJinu5PBQvLm4vab5LBkF7mBCW2aFi4EJk1iQpuo3l4pwM9O3Dws3ZUc5zApZ2jN46xWxOOqI9FDS4tcg15QoDoS0hkT2jRlZsponAltYtraZIsGE1rz8HR3cjgrZa5IBLh8WVYsaGItLfI947kKSgcTWhewdFfiWLLLXAUFwNSpbAuJam6WE/FTp6qOhNzm1KLlREdiuA2N3MCE1gUs3ZW4lhYgO1tqb5JZQiFZPmdCmxh24ubi2YrksC2QG5jQuqCsTJZZBwdVRxJ8LS1yK1JmpupIyAtlZUBTk+oo9NDSwv2zpsrLAwoLObhLRH+/bEVjQkvpYkLrgtJSoKcHOHZMdSTBx5G42ThDm7jmZrYFky1ZwsFdIg4flqSWbYHSxYTWBVxeShwTWrOVlUkH1dOjOpJgO38eOHOGM7Qm4+AuMTxXQW5hQuuCcFj2DzKhHV88Llsz+OAyV1mZ/JxZumt87MTNx4Q2MS0tsgVt8WLVkZDumNC6ICcHKCriw2six48D3d3sxE3mzDiyLYzPSWg5Q2uusjLg7Fmgs1N1JMHW0gIUF8thYaJ0MKF1CSsdTIyzUuYrLAQmT2ZCO5HmZmDmTGD2bNWRkFecwQr30Y6P29DILUxoXcKEdmIsJG++jAzWZU4EKxyYj6sViWFCS25hQuuSsjImtBNpaQEWLJAZPDLXkiXsxCfCCgfmmzZNLhthWxhbPM6EltzDhNYlpaXAuXPcLzUePrjswMMwE+MMrR3YFsZ36hRw6RL7BXIHE1qXsHTXxDgrZYeyMqC1FejrUx1JMHV3A+3tbAs24EUj43O+N0uWqI2DzMCE1iVO58TR+Niam/ngskFZGTAwILf/0EhOSTPO0JqP22/G5yS0HNyRG5jQumTGDCA/nzO0Y+nokBI2TGjNx9Pd43MSHHbi5isrG3r20UhNTcCiRTxXQe5gQusiVjoYG5eW7FFUJLWZOTM1upYW6cALC1VHQl5zBnfsF0bX1MQ+gdzDhNZFPAAwNieh5TKr+TIypDQb28LonL3koZDqSMhr3Io2Pia05CYmtC5asoTLrGNpapIZqalTVUdCfuDgbmyscGCPmTOBOXPYL4wmHpdnBNsCuYUJrYuWLAFOngQuXFAdSfBwJG4XJrRjY7UPu/Bg2OhOnJCSXewXyC1MaF1UXi4fORofiQmtXZYskdP8/f2qIwmWvj6p/sCE1h4c3I2O5yrIbUxoXeQ0TCa0w8XjTGhtU1YmyduRI6ojCZbWVknyncEvmY8J7eiammQfOa9CJ7cwoXUR90uN7vRp2YbBhNYevMd+dJyVsk9ZmdyIxa1owzU1AYsXA7m5qiMhUzChdVl5ORPaa7ETt8+iRUB2NhPaazU2SgdeVKQ6EvKL89xjWxiOq3bkNia0LluyRDotGsLbYOyTlQWEw+zEr9XUJDN2GXzyWoOrFaNjQktu42PVZSzdNVJTk8xITZmiOhLyE/cOjtTYyP2ztpk1C5g9m23haoODLNlF7mNC67LycqCzU647JMGRuJ3Kyji4uxbbgp3YFoY7dgzo7mZbIHcxoXUZKx2MxE7cTmVlconAwIDqSILhyhXg8GHO0NqIqxXDOd8L9gvkJia0LnOWUJjQCpbssldZGdDbCxw9qjqSYGhpkfbAtmAfXq4wXFPT0BXZRG5hQuuyqVOBBQuY0Dp4G4y9uFoxnHNYlDO09ikrk2dhV5fqSIKhqQkoLgYmTVIdCZmECa0HWOlgCEt22au4WKodMKEVTU3AtGnAvHmqIyG/sdLBcFy1Iy8wofUAKx0M4W0w9srOllJthw6pjiQYnAoHoZDqSMhvXK0YjgkteYEJrQecyxXicdWRqMfbYOxWXs7VCgc7cXvl50vpLg7upGRXSwtLdpH7mNB6YMkS4OJFue7Qds3N7MRtVl7OTtzBGrR2W7qUgzsAaG+Xih/sF8htnie0XV1d2LRpExYuXIjJkydj9erVeP755xP62i1btiAjI2PUP6cCnC06DZUPL85K2W7pUiAWk2oHNrt4UQ4FsS3Ya+lSDu4Anqsg72R5/Qb3338/du3ahSeffBLl5eXYunUrNm7ciMHBQWzcuDGh19iyZQsqKiqGfW727NlehOuK0lLZJ9fUBNx8s+po1InHZYb2kUdUR0KqlJfLEmM0ClzThK3idOKcobVXeTmwfbs8F23eR93UBGRmytXYRG7yNKF96aWXsGPHDtTU1GDDhg0AgFtuuQVtbW346le/ig0bNiAjgUvNly9fjjVr1ngZqqtyc2XfqO0HAI4dAy5f5kjcZkuXysdDh5jQAmwLNlu6FDh/Hjh92u5KF83NQEmJHBolcpOnWw62b9+OadOm4cEHHxz2+UcffRTHjh3D+++/n9DrxDU8XcXSXezECSgokFJVtreFxkZgzhxg1izVkZAqzuy87dsOuA2NvOJpQltXV4fKysoRs7DV1dUAgAMHDiT0Ovfccw+ysrKQn5+PBx54IOGvU4mlu4Zug+HSkr1CIR4MA9iJk5zqD4U4uGtsZFsgb3i65aCjowNlo9TmcPa/dnR0jPv1hYWF+MY3voF169Zh+vTp2L9/P7773e9i3bp1ePfddz9OjEezadMmzJw5c9jnNm7cmPC+3XSVlwNbtsj+wQR2VRipsVGWlngbjN14ulv+/c72C7JTbq5cNmLz4K6/X0p2/eVfqo6E/FZTU4Oampphnzt37pyr75FwQvvWW2/htttuS+jv7t27FytWrEg5KMedd96JO++88+P/f9NNN+Huu+9GdXU1nnjiCWzfvn3Mr928ebPSfbdLlgDd3XKP/aJFysJQqqHB7n2TJMrLgddfVx2FWk1NwL33qo6CVLN9cBeLAX197BdsNNqE4u7du7F27VrX3iPhhLaiogJPPfVUQn938eLFAID8/PxRZ2E7Ozs//u/JKi4uxic/+Uns3Lkz6a/109U3w9ia0B46BNxzj+ooSLXycuDkSTkQM2OG6mj819EBdHZymZWkLfzmN6qjUKehQT5ytYK8kHBCO3/+fDz22GNJvfiKFStQU1ODwcHBYftoa2trAUj1glSFAl73JByW0iSNjUCCE9tG6e1lqSYSTufV2Ahcf73aWFRgyS5yLF0K/OhHsvSe5XnRzOA5dAjIywMWLlQdCZnI092d9913H7q6urBt27Zhn9+yZQsWLlyIG264IenXjEajeOedd/CJT3zCrTA94dxjb+vyUksLMDDAkTgNzUzaunfQeQbwqk8qL5cl99ZW1ZGoceiQfA8CPh9FmvJ0jHjXXXdh/fr1+PKXv4wLFy6gtLQUNTU1eO2117B169Zhs6yPP/44nnnmGUSjUSz6wxr9+vXrcdttt6GqqgpTp05FbW0tvve97yErKwvf+c53vAzdFRUVQ0sstnGSFya0NG0asGCBvYO7Q4eAoiJg6lTVkZBqV69W2DjAsb0eNXnL80WPF154AV//+tfxxBNPoLOzE5WVlXjuuefw0EMPDft7g4ODGBwcHFZztrq6Glu3bsWRI0fQ3d2NefPm4Y477sA3v/nNUasnBE1FBfDzn6uOQo2GBtkvWVCgOhIKgvJyexNaHo4kR1ERMHmyJHaf+5zqaPx36BBw++2qoyBTeZ7Q5uXlYfPmzdi8efO4f+/pp5/G008/Pexz3//+970MzXMVFbK01N0tDzGbHDoksxFcWiJAEtoPPlAdhRoNDcCtt6qOgoIgI0O24Ni4/ebsWeDUKa7akXcsrZDqj4oKubfbxgsWnISWCBgqV6ThpX9p6e+X9s8ZWnLYulrBbWjkNSa0HnIarm37aONxLrPScOXlwOXLwLFjqiPxF+tu0rWWLrVzhtb5N7PaB3mFCa2HZs8G5s61L6E9c0aWlzgSJ4et99g7/14mtORYulQGdl1dqiPx16FDUpM9L091JGQqJrQes7HSAZeW6FrhsNTdtG2ptaGBdTdpOGdwZ2NbYJ9AXmJC6zEbE9qGBjn8oEEhCvJJdjYQidg3Q+tsveHhSHLYmtDyXAV5jQmtxyoqpCEPDqqOxD+HDgElJUBurupIKEhs3DvIveR0rVmzZCuaTW1hYABobmZCS95iQuuxigo5DNPerjoS/7ATp9FUVgL19aqj8BfbAo3GtsFda6tch862QF5iQusxpwHbtO2AS0s0mspKoK1NBng2OHMG6OhgJ04jVVTYNbjjuQryAxNajxUXAzk59iS0vb1ANMoHF41UWSkl3WyZmXLaPBNaulZlpV1b0Roa5HKhoiLVkZDJmNB6LDNTDgHYktC2tMh+KXbidC3nd+LgQbVx+IWHI2ksy5bJDZJtbaoj8cehQ9IPZjDjIA/x18sHNlU64NISjWXGDGDBAnuWWhsapFwZD0fStSor5aMtg7tDhzjJQd5jQusD2xLa6dOBggLVkVAQ2XQwjAfCaCzOBQM2tQVOcpDXmND6oKICOH4cOH9edSTeq69n3U0a27JldnXiTGhpNBkZ9hwM6+wETp6Utk/kJSa0PnA6NRsOwxw8yAcXja2yEmhqAvr6VEfirStXgFiMCS2NrbLSji0HTtLOfoG8xoTWB7bcYx+PM6Gl8VVWAv39cnjQZM3NcoKdCS2NxVmtiMdVR+KtgwdlRtrpB4m8woTWB1Onyp4p0/fRHjkCXLoEVFWpjoSCyjkMY/pSqzN4ZUJLY6mslG1oJ06ojsRbBw9KpY+cHNWRkOmY0PrEhoNhzvIZZ2hpLPPmydWfpi+11tcDs2cDc+aojoSCypZKB1y1I78wofXJ0qXmz0odPAhMmQIsXqw6EgqqUMiOSgfsxGkipaVAdjbbApFbmND6ZNkyOQzT26s6Eu8cPCjJCotn03hsSGgPHODWGxpfVpbsKzW5LVy4ALS3M6ElfzD18Mny5XIYprFRdSTe4UicErFsmWy/MfXaz/5++fcxoaWJmF7pgBUOyE9MaH3idG4HDqiNwyuscECJqqwELl+WQ4QmammRlRgmtDQR0+syHzwo24x4qQL5gQmtT2bPBubPNzehdS6OYEJLEzG90oHTxpnQ0kQqK+XSgc5O1ZF44+BBuf55yhTVkZANmND6qKoKqKtTHYU3nE6cCS1NZPFi6eBMTmjz86WiA9F4TB/ccdWO/MSE1kfLl5s7Q3vwoNQZDIdVR0JBl5FhdtWPAwekE+f1zzSR8nJpD6a2BSa05CcmtD6qqpIbhK5cUR2J+w4elFq7mZmqIyEdmHwYhhUOKFGTJ8skgIkJ7aVLQGsrE1ryDxNaH1VVycluE6/A5UickrF8uWy/Me3az74+ad9MaClRy5aZObhzLhJiv0B+YULrI6dhm7aPNh4fWmYlSkR1tRwiPHpUdSTuammRpJYJLSXK1LMVTpLO65/JL0xofTRzJlBUZN4+2lOngLNnmdBS4pYvl4+1tWrjcBsrHFCyli+XywfOnVMdibsOHpQDoNOmqY6EbMGE1mdVVeYltM5InAktJWrxYmDqVPNmpg4cAObMYYUDSlx1tXw0sS04VRyI/MCE1mcmJrR1dcCkSXI3OVEiMjJkZsrEGVrOzlIyKirkGlzT2kJtLbBiheooyCZMaH1WVQVEo3JTkilqa2Uknp2tOhLSiXMwzCRMaClZkyZJGTuTEtqLF6XCgTP7TOQHJrQ+W75cDlGZVKZl/36OxCl51dWyXaW/X3Uk7ujrAxobmdBS8kwb3Dn/Fia05CcmtD5z9pmasu1gcFAeXnxwUbKWLwd6eqQygAmamljhgFJTXS0ztKaUsdu/X2qScw8t+YkJrc+mTgWKi81JaGMxKaDNGVpKlmmVDljhgFJVXS1VDkwpY1dbK9socnJUR0I2YUKrgEl1B51khDO0lKx58+SPKW1h3z6gsFCqHBAlw3l+mjK427+ffQL5jwmtAsuXmzNDW1sL5OdLR06ULJMqHezfD6xcqToK0lFxMZCXZ8bgLh6XNs2ElvzGhFaB5cuBtja5KUl3zkg8FFIdCemoutqMThzg4UhKnUll7I4ele0TbAvkNya0CjizOPv3q43DDaw1SOlYvhxobga6u1VHkp5z52SQyrZAqXIOhunO6dc4Q0t+Y0KrQEWF1B7ct091JOnp7paT3XxwUaqqq6VShu5l7JxEhAktpaq6WtqB7mXsamvlutviYtWRkG2Y0CowaZKU79I9oT14UJIRduKUKqeMne7bDvbvl4tFKipUR0K6csrYNTerjiQ9tbXyb+E2NPIbE1pFVq7UP6Hdv18eWixTRKmaNg0oKdF/+82+fZKc87Y8SpUplQ64l5xUYUKryMqV8uDSeXmpthYoLZXTuUSpWrXKjMEdO3FKx9y5QEGB3gltXx/Q0MBtaKQGE1pFVq0CrlyRPai6Yq1BcsOqVcDevfrekjQ4yMOR5A7dV+4OHZKklv0CqcCEVhGn0oHODy924uSGVauAM2f0vSWppQW4fJk1aCl9q1YBe/aojiJ1rHBAKjGhVWT2bKCoSN+E9tQp+cMHF6Vr9Wr5uHev2jhS5XTiHNxRulavBo4cATo6VEeSmv37pV+bNUt1JGQjJrQKOUutOnJmETgrRelatEg6QF3bwv79coVvQYHqSEh3ug/udu8G1qxRHQXZigmtQjrvl9q9G5gxQw6FEaUjFNJ7qXXfPs7OkjvKyoApU/RsC/G4xO0k5UR+Y0Kr0MqVwPHjwOnTqiNJ3p49koSw1iC5YfVqfWel9u7lSgW5IzNTfpd0TGjb22UvPGdoSRUmtArpfDCMS0vkplWrgGgUOH9edSTJ6eiQK2/XrlUdCZlC18Gdk4RzhpZUYUKrkFPDVbeH1/nzcrKbDy5yi/O7pNvgbvdu+ciEltyyapXUcr18WXUkydmzB5gzRw6FEanAhFahzEypEqBbJ+4k4JyhJbcsXQrk5Og3uNu9W247KytTHQmZYvXqodrGOtm9W2LnNjRShQmtYqtWDc3y6GL3bmDyZElCiNyQnS33v+u2d/Cjj6QTz+CTlFyyfLlMdug2uNuzh5McpBYfw4pdd50sL3V1qY4kcbt3y6nurCzVkZBJdNw7yL3k5LbcXKCyUq/B3ZkzUj+X29BIJSa0il13nSwv6dSRsxMnL6xaBRw4APT0qI4kMefOyV5ytgVy2+rVeiW0TqxsC6QSE1rF/n979x+UVZX/Afz9PChKSigIYpbpGA6KmAj4A3VFUCDJndDIda1Wcbddt3a1X2wzO2O5O5PO7tpO7trO2lqmuKRYbGpqpoiKP0AlE03zR4Tmj0SwFPwJ3O8fn3k0vqg94L33nAvv18wzOA8853xqnnvu5/y45/TpIz3y3btVR+KdS5dkRJk9cTJbdLScA++UtYOemzgfCCOz9e8vB3bU1KiOxDueteTcl5xUYkKrWOvW0ng5JaHdt09GlNkTJ7M9/LAsY9m1S3Uk3ikulk3wuZaczBYdDVy5Ahw8qDoS73j2JedaclKJXz8NxMQ4J6EtLpako29f1ZFQc+PnJ98rpyS0e/bITdzHR3Uk1NxER0tyWFSkOhLv8IQw0gETWg1ERwNffglcuKA6kh9XXAxERMgWS0Rmi411TkLLteRklfbt5cEwJ1wL338PHD7Ma4HUY0KrgZgY+emEhwB27ZKkg8gKsbHAF18A1dWqI7mzixflJs71s2SVgQOdMULrmV0cNEhtHERMaDUQHi5r8XRfdlBVBezfLw0tkRViY2WNtu57M+/dCxgGR6XIOrGx8oDk5cuqI7mzoiLg3nuBXr1UR0ItHRNaDbRqJeuPdE9oi4sl2WBPnKwSESG7fug+1VpYKJ3QPn1UR0LN1cCBssuB7ls6FhVJ8s0Hwkg1fgU14YQHw4qKeBMna7VuLZ07JyS0MTE8XISsExkJ+PrqfS0YhlwLnLUjHTCh1URMDHD0KHD+vOpIbq+wUNYM8iZOVnLCg2E7d3Kmgqzl6yudO53X0Z48CZw+zYSW9MCEVhOeB8N0HqUtKuJNnKwXGysncFVWqo7k1k6dAr75Bhg8WHUk1Nzp3rnzJNu8L5AOmNBqolcvoEMHYMcO1ZHc2pkzwPHj7ImT9Ty7aOjauSsslJ+8iZPVBg6U3TS++051JLdWVATcfz/QpYvqSIiY0GrD7QaGDNE3ofWMEjChJauFhQEBAfqOTO3cCXTtKi8iK+neuSsq4j2B9MGEViNDhsjNsq5OdSQNFRYCnTsD3bqpjoSaO7dbbpK6du4KCzk6S/bo1Uu2xNJxHW1trSTaTGhJF0xoNTJkiEwtHTqkOpKGPD1xl0t1JNQSDB0KbN+uX+eupkZu4lw/S3Zwu6XztH276kgaOnhQDhhhQku6YEKrkYEDpQHTbWSqtlZGjnkTJ7sMHSo7fnz5pepI6jtwQE4x4wgt2WXYMD07d9u2AT4+TGhJH0xoNXLvvUDfvvoltCUl0hMfPlx1JNRSDBoknbtt21RHUt/OnXIT55G3ZBdP5+7gQdWR1FdQICfltWunOhIiwYRWM0OG6De9VFAgeyJ6HlAgspq/P9Cvn57XQv/+vImTfQYNkk6Ubp27ggJJtol0wYRWM3Fx0hPX6YCFggLZJ7dtW9WRUEsydKh+N/GtWzlTQfZq3x54+GG9roWTJ4Gvv5blEES6YEKrmSFD5OfOnWrj8DAMuYmz4SK7xcXJHpzl5aojESdOAGVlwE9+ojoSaml069x5YuEILemECa1mHnoICAmRUVEdlJXJyUhMaMlunpulLmvKt26Vn7wWyG7DhsnpeWfOqI5EFBQAPXsCoaGqIyG6iQmtZlwuYMQIID9fdSTCk1jHxamNg1qebt3k8AJdRqa2bgXCw4HgYNWRUEvj6dzpci0UFLBjR/phQquhESPklKTqatWRSMPVpw8QFKQ6EmppXC65aW7ZojoSsWUL18+SGl27Ag8+qMfM3cWLwOefc7kB6YcJrYbi44Hr1/WYamVPnFQaOVI6dxcvqo2jogL44gsmtKTO8OF6dO527JA9cZnQkm6Y0GqoTx+gUyf1yw7OnpWN5HkTJ1VGjpSDPVSPTHnq57VAqiQkAJ99BlRWqo1j0yZ5zqN3b7VxEP1/TGg15FlHu3mz2jg2bZKfCQlq46CWKyxMplvz8tTGsXUrcP/9Mu1LpEJCguw6o3qgIy9PYuEx6KQbJrSaio8HCguBS5fUxbBxo/TC77tPXQzUsrlcMkqrOqHduJE3cVLrwQdlZwGV18L33wO7d3OQg/TEhFZTOqyj9fTEiVTyTLWqOmykvBzYuxcYNUpN/UQeCQlqE9rNm2X9bGKiuhiIbocJraZUr6MtK5N9D9lwkWqeqVZVS3A2bpSfvBZItcREOUny9Gk19eflyUhxjx5q6ie6Eya0mnK75Ua+fr2a+jduvLmWl0glzw3Us6bbbhs2ABERXHpD6sXHy09Vo7RcP0s6Y0KrseRk2bKoosL+uvPygAEDgMBA++sm+v8SEm6OlNrJMIBPP+VyA9JD585A375qEtqzZ4GSEs5UkL6Y0GosOfnmDdVOdXVSJxsu0kVSkmwhd+KEvfUeOwYcP86ElvSRmCjts2HYW+8nn9ysn0hHTGg11rUrEBl5syGxy5490hsfM8beeoluZ/RowMcHWLvW3no//RRo1YpLb0gfKSnSsTtwwN5616wBYmKA0FB76yXyFhNazSUnA+vW2dsbX7MGCAgA4uLsq5PoTjp2lO/jmjX21rt6tZyU5+9vb71EtxMfD9xzD/Dxx/bVWVMjAyuPPGJfnUSNxYRWcykpwJkzwL599tX58ceSSLdubV+dRD/mkUfkAa2rV+2pr7pa1u2OHWtPfUTeaNtWpv3t7NwVFsq2eZy1I50xodXcsGHSG7er8fr2W3kQjQ0X6WbMGEky7ToGd+NGSZ6Z0JJuUlOBbdvs25t5zRogKAiIjbWnPqKmYEKruTZtZJQ2N9ee+tatky1ZOLVEuunXT7bOsmuqddUqoFcvOX6XSCdjxgC1tfZt67hmjdyHfHzsqY+oKZjQOkBamoyafvON9XWtWiUL/0NCrK+LqDFcLhmZWrnS+jXldXWSOHN0lnT0wAPSwVu92vq6Efag5gAADctJREFUjh+Xk/JSU62vi+huMKF1gNRUedL6o4+srae6Wnri48ZZWw9RUz3+uGyl9fnn1tZTXCynMTGhJV099pgMQFi9pvyDD2SmkAkt6Y4JrQN07AiMHGn9soO1a4HLl4H0dGvrIWqqkSPlesjJsbaeFStkzeDQodbWQ9RU6enA99/Lg5JWysmRh4TvvdfaeojuFhNah0hLA/LzgcpK6+rIyQGiooCePa2rg+hutG4tI1M5OdYtOzAMYNkyYPx4mRkh0lFEBNC7N7B8uXV1fPMNsGOHzIwQ6Y4JrUM89pis67NqlPbSJVmPxdFZ0l16OnDkCLB/vzXl79oFfP01MGGCNeUTmcHlkmvho4+sW3bwwQfSifzpT60pn8hMTGgdoksX2XtwyRJryl+7VpJaJrSku8REOfjDqpGpZcvkoUieDka6e+IJWXZg1fHoOTly7HRAgDXlE5mJCa2DPP00sHmzjB6ZbelSYMAA4KGHzC+byEy+vjIFumSJzFqYqbZWEtrHH+cWRaS/iAigTx/gv/81v+xjx2Sv25/9zPyyiazAhNZB0tKAdu2ArCxzyz17Vp6WnTLF3HKJrDJlClBWJh08M61fD5w8CUyebG65RFaZPBn48EPzD1lYtEgeBOOuN+QUTGgdpH17eVBlyRJzH4jJygLcbuDnPzevTCIrxcXJgQfvvmtuuQsXApGRshczkRM89RRQUwNkZ5tXZm0t8N57so78nnvMK5fISkxoHebpp4HDh807/tMw5Cb+2GNAYKA5ZRJZzeWSkakVK2QNoRnKy+XQhqlTpXwiJwgNBR59VNpxs+TlASdOABkZ5pVJZDUmtA6TkACEhwP/+Ic55RUUAF98ITdxIieZPBm4ft28UdpFiySRffJJc8ojssvUqXIYyK5d5pT3z3/K+txBg8wpj8gOTGgdxuUCnntO1kydOHH35c2dKw8VjB5992UR2em++2RKdN48mSK9G9evSzmTJsmBCkROMmaM7B/+xht3X9bRo/JMxYwZnKkgZ2FC60BPPy0Ph7311t2Vc+SITLE+/zwbLnKmGTOA0lK5Ad+N5ctlE/kXXjAnLiI7+fjItZCTAxw/fndlzZsnnbpJk8yJjcguTGgdyN8feOYZYP58oKKi6eW88QbQqRMbLnKumBhg2DBgzpymPyhpGDJTkZwM9O1rbnxEdpk8We4Nf/9708soLwfeeQeYNg3w8zMtNCJbMKF1qMxM2YNz7tymff7rr+UhguefZ8NFzvbqq0BhoZx01xS5ucBnnwGvvGJuXER2at8emD4d+Ne/mr4c7S9/kR1vpk83NzYiOzChdajgYOB3v5PpobNnG//5116TXQ1+/3vTQyOyVWIiMHIk8Mc/Nv6ghZoa+VxSEhAfb0l4RLZ58UXZO/bVVxv/2ZMn5WGw55/nOnJyJia0DvbSS3JqUmZm4z63Y4fsMfjqq7IWl5ou28zNH6lJXC7g9deBkhLgP/9p3Gfnzwe+/FI+rzt+1+jH+PsDM2dK+753b+M++8ILkgy/8AK/a+RMlia0VVVVyMzMRFJSEoKDg+F2uzFr1qxGlXH27FlMnjwZwcHBaNeuHeLi4pCXl2dRxM4SFCRTRO+9J/sGeuPaNeDXv5a1h888Y218LQEbfj0MHix7ZmZmAqdOefeZ48dldHbaNCA62tr4zMDvGnnjmWdk55qMDNm9wxvr1smDkXPnAgEB/K6RM1ma0J47dw5vv/02rl+/jrS0NACAqxGP01+9ehWJiYnYtGkT5s2bh5UrV6Jz585ISUnBli1brArbUTIyZKr0qae8W3rw8svAoUPAggU8q56al7/+FWjb9ubJSXdy7RowcSLQoYMzRmeJvOXrK3sz79sno7U/5vRp4Be/kGU3fECYnMzShLZ79+44f/48Nm3ahNmzZzf68wsXLsSBAwewfPlyTJw4EYmJiVixYgV69eqFzMbOszdTbjewdKncwB9/HKiuvv3fLlwoa27feAOIirIvRiI7BAYC778PbN4sWxjdbteDujrZy3nXLtnmKCDA3jiJrBYTA8yeLbt/ZGXd/u8uXADS0mRwY8kSbt9IzmbbGlqjCXvq5ObmIjw8HIN+cFyJj48PnnzySRQVFeH06dNmhuhY990H/O9/8qT2I48AZ87U/71hAG++CfzqVzK9+uyzauIkslp8vKyLnT8f+M1vgKtX6//+8mXgl7+UtbYLFgBDhigJk8hyL70ETJkio6/z5zfs4J0+DaSkyIzdypVASIiaOInM0kp1AHeyf/9+jBgxosH7kZGRAIADBw6gS5cu9X535coVAMDBgwetD1AjbdpI0pqZKUfjPvGEHF14/rw0Vp99JtNJGRnybzLHd999h+LiYtVh0A/ExspU6+uvA598AqSnAw8+KAcwLF8OfPst8Kc/Af36yXGhTsHvGjXWb38rnbjnnpP9ZceOlZmMzz+Xa6FtW5m1c7vrXwv8rpEdPHna5cuXzSnQsEl5ebnhcrmMWbNmef0ZX19fY9q0aQ3e3759u+FyuYz333+/we+ysrIMAHzxxRdffPHFF198af7Kysq6q/zSw+sR2vz8fCQkJHj1t3v37kW/fv28LdpUycnJyMrKQvfu3eHHEwOIiIiItHPlyhWUlpYiOTnZlPK8TmjDw8PxHy83eXzggQeaHNAPBQUFobKyssH7nveCbrH7c6dOnTCJj2oSERERaS0uLs60srxOaENDQ5GRkWFaxd6IjIzEvn37GrxfUlICAOjLg9eJiIiIWjytTwpLS0vDoUOHUFRUdOO9mpoaZGVlYfDgwQgNDVUYHRERERHpwPJdDtauXYvq6mpcvHgRgOxMsGLFCgBAamrqjXWuU6dOxeLFi/HVV1/dWLKQkZGB+fPnIz09HXPmzEFwcDDeeustHDlyBBs2bLA6dCIiIiJyAJdhNGGD2Ebo0aMHysrKpDKX68Z+tC6XC6WlpejWrRsAYMqUKVi8eHG99wA5+jYzMxOrV6/GpUuXEBUVhT//+c9eP6BGRERERM2b5UsOSktLUVdXh7q6OtTW1tb79w8T13fffbfBewAQEhKCRYsW4dy5c7h06RK2bdt2y2S2qqoKmZmZSEpKQnBwMNxuN2bNmnXbuIqLizFq1Cj4+/ujY8eOGD9+PEpLS837D6cWJz8/H263+5avHy6bIfJWVVUVZsyYga5du8LPzw9RUVFYtmyZ6rComWHbRVZoTF5mRk6m9Rraxjh37hzefvttXL9+HWlpaQBkFPhWDh06hPj4eNTU1CAnJwfvvPMODh8+jOHDh+PcuXN2hk3N0OzZs7Fz5856r4iICNVhkQONGzcOixcvxmuvvYZ169YhNjYWEydORHZ2turQqBli20Vm8jYvMysn0/qksMbo3r07zp8/DwCoqKi44xZjM2fOhJ+fH1avXo327dsDAKKjoxEWFoa//e1vmDNnji0xU/MUFhaGgQMHqg6DHG7NmjXYsGEDsrOzMWHCBADAiBEjUFZWhpdffhkTJkyA291sxiRIA2y7yEze5mVm5WTNsjW807LgmpoarF69GuPHj7/xPw4AunXrhpEjRyI3N9eOEKkZs3hZOrUQubm58Pf3R3p6er33p0yZglOnTqGwsFBRZNRcse0iq9zuu2VmTtYsE9o7OXbsGK5cuXLLk8wiIyNx9OhRXLt2TUFk1Fw8++yzaN26NQICApCSkoJt27apDokcaP/+/ejdu3eDUdjIyEgAsmMMkZnYdpHdzMzJWlxCW1FRAQAIDAxs8LvAwEAYhnFjiJyoMTp06IAZM2ZgwYIFyM/Px5tvvokTJ04gPj4e69evVx0eOUxFRcVt2ynP74nMwLaLVDEzJ9NyDW1+fr7X23Lt3bv3lpk90d1oynewf//+6N+//433hw4dirS0NERGRuIPf/gDkpKSrAqXiKjJ2HZRc6BlQhseHn7Hh7p+yHMIg7eCgoIAAJWVlQ1+V1lZCZfLhY4dOzaqTGp+zPoOBgQEIDU1Ff/+979x9epVtGnTxqwQqZkLCgq65Sisp+3ytGVEVmDbRXYwMyfTMqENDQ1FRkaGJWX37NkTfn5+2LdvX4PflZSUICwsDL6+vpbUTc5hxXfwdtvIEd1Kv379kJ2djbq6unrraEtKSgAAffv2VRUatTBsu8gqZuZkLW4NbatWrTB27Fh8+OGHqKqquvH+8ePHsWnTJowbN05hdNTcnD9/HqtWrUJUVBQ7StQoaWlpqKqqunFUuMeiRYvQtWtXDBo0SFFk1BKw7SI7mJmTaTlC21Rr165FdXU1Ll68CECeAvbcDFJTU+Hn5wcAmDVrFmJjY/Hoo4/ilVdeweXLlzFz5kyEhITgxRdfVBY/OdukSZPQo0cPDBgwAIGBgThy5Ajmzp2L8vJyLF68WHV45DApKSkYPXo0pk2bhgsXLqBnz57Izs7G+vXrsXTpUo6akWnYdpFVvMnLTMvJjGake/fuhsvlMlwul+F2u+v9u6ysrN7f7tmzxxg1apTRrl07IyAgwBg3bpzx1VdfKYqcmoM5c+YYUVFRRocOHYxWrVoZISEhxvjx443du3erDo0cqqqqypg+fbrRpUsXo02bNkb//v2NZcuWqQ6Lmhm2XWQVb/MyM3Iyl2FwJ2UiIiIicq4Wt4aWiIiIiJoXJrRERERE5GhMaImIiIjI0ZjQEhEREZGjMaElIiIiIkdjQktEREREjsaEloiIiIgcjQktERERETna/wFB0EeTkfB/ggAAAABJRU5ErkJggg==", "svg": [ "\n", "\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "\n" ], "text": [ "Figure(PyObject )" ] }, { "metadata": {}, "output_type": "pyout", "prompt_number": 111, "text": [ "1-element Array{Any,1}:\n", " PyObject " ] } ], "prompt_number": 111 }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Code blocks" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Julia supports two kinds of code blocks. The first is delimited by:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "begin \n", " # and\n", " \"hello\"\n", "end" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 112, "text": [ "\"hello\"" ] } ], "prompt_number": 112 }, { "cell_type": "markdown", "metadata": {}, "source": [ "And the second is delimted by parentheses, with expressions separated by a semi-colon `;` between each expression:" ] }, { "cell_type": "code", "collapsed": false, "input": [ "expression = 1\n", "( this = 1; is = 2; println(a[1]); valid = expression )" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "1.0" ] }, { "output_type": "stream", "stream": "stdout", "text": [ "\n" ] }, { "metadata": {}, "output_type": "pyout", "prompt_number": 113, "text": [ "1" ] } ], "prompt_number": 113 }, { "cell_type": "markdown", "metadata": {}, "source": [ "The final expression in a code block determines the resulting value, if any. Code blocking style can be used to great effect " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Generated Code\n", "\n", "Julia provides access to several levels of transformed code between the initial input and the executable, including type-annotated expressions, LLVM Intermediate Representation, and finally the resulting machine code for any function." ] }, { "cell_type": "code", "collapsed": false, "input": [ "function plus(x::Float64, y::Float64)\n", " return x + y\n", "end" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 114, "text": [ "plus (generic function with 1 method)" ] } ], "prompt_number": 114 }, { "cell_type": "code", "collapsed": false, "input": [ "code_typed(plus, (Float64, Float64))" ], "language": "python", "metadata": {}, "outputs": [ { "metadata": {}, "output_type": "pyout", "prompt_number": 115, "text": [ "1-element Array{Any,1}:\n", " :($(Expr(:lambda, {:x,:y}, {{},{{:x,Float64,0},{:y,Float64,0}},{}}, quote # In[114], line 2:\n", " return top(box)(Float64,top(add_float)(x::Float64,y::Float64))::Float64\n", " end)))" ] } ], "prompt_number": 115 }, { "cell_type": "code", "collapsed": false, "input": [ "code_llvm(plus, (Float64, Float64))" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "\n", "define double @julia_plus(double, double) {\n", "top:\n", " %2 = fadd double %0, %1, !dbg !15456\n", " ret double %2, !dbg !15456\n", "}\n" ] } ], "prompt_number": 116 }, { "cell_type": "code", "collapsed": false, "input": [ "code_native(plus, (Float64, Float64))" ], "language": "python", "metadata": {}, "outputs": [ { "output_type": "stream", "stream": "stdout", "text": [ "\t.text\n", "Filename: In[114]\n", "Source line: 2\n", "\tpush\tRBP\n", "\tmov\tRBP, RSP\n", "Source line: 2\n", "\tvaddsd\tXMM0, XMM0, XMM1\n", "\tpop\tRBP\n", "\tret\n" ] } ], "prompt_number": 117 } ], "metadata": {} } ] }